aboutsummaryrefslogtreecommitdiff
path: root/vendor/gioui.org
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gioui.org')
-rw-r--r--vendor/gioui.org/LICENSE4
-rw-r--r--vendor/gioui.org/app/Gio.java (renamed from vendor/gioui.org/app/internal/window/Gio.java)0
-rw-r--r--vendor/gioui.org/app/GioActivity.java (renamed from vendor/gioui.org/app/internal/window/GioActivity.java)15
-rw-r--r--vendor/gioui.org/app/GioView.java480
-rw-r--r--vendor/gioui.org/app/app.go4
-rw-r--r--vendor/gioui.org/app/app_android.go48
-rw-r--r--vendor/gioui.org/app/d3d11_windows.go135
-rw-r--r--vendor/gioui.org/app/datadir.go1
-rw-r--r--vendor/gioui.org/app/datadir_android.go41
-rw-r--r--vendor/gioui.org/app/egl_android.go66
-rw-r--r--vendor/gioui.org/app/egl_wayland.go (renamed from vendor/gioui.org/app/internal/window/egl_wayland.go)43
-rw-r--r--vendor/gioui.org/app/egl_windows.go (renamed from vendor/gioui.org/app/internal/window/egl_windows.go)27
-rw-r--r--vendor/gioui.org/app/egl_x11.go59
-rw-r--r--vendor/gioui.org/app/framework_ios.h (renamed from vendor/gioui.org/app/internal/window/framework_ios.h)0
-rw-r--r--vendor/gioui.org/app/gl_ios.go (renamed from vendor/gioui.org/app/internal/window/gl_ios.go)93
-rw-r--r--vendor/gioui.org/app/gl_ios.m (renamed from vendor/gioui.org/app/internal/window/gl_ios.m)6
-rw-r--r--vendor/gioui.org/app/gl_js.go69
-rw-r--r--vendor/gioui.org/app/gl_macos.go121
-rw-r--r--vendor/gioui.org/app/gl_macos.m74
-rw-r--r--vendor/gioui.org/app/internal/d3d11/backend_windows.go856
-rw-r--r--vendor/gioui.org/app/internal/glimpl/gl.go541
-rw-r--r--vendor/gioui.org/app/internal/glimpl/gl_js.go339
-rw-r--r--vendor/gioui.org/app/internal/glimpl/gl_windows.go406
-rw-r--r--vendor/gioui.org/app/internal/log/log_android.go24
-rw-r--r--vendor/gioui.org/app/internal/log/log_ios.go9
-rw-r--r--vendor/gioui.org/app/internal/log/log_ios.m11
-rw-r--r--vendor/gioui.org/app/internal/srgb/srgb.go195
-rw-r--r--vendor/gioui.org/app/internal/window/GioView.java255
-rw-r--r--vendor/gioui.org/app/internal/window/d3d11_windows.go102
-rw-r--r--vendor/gioui.org/app/internal/window/egl_android.go54
-rw-r--r--vendor/gioui.org/app/internal/window/egl_x11.go50
-rw-r--r--vendor/gioui.org/app/internal/window/gl_js.go98
-rw-r--r--vendor/gioui.org/app/internal/window/gl_macos.go90
-rw-r--r--vendor/gioui.org/app/internal/window/gl_macos.m143
-rw-r--r--vendor/gioui.org/app/internal/window/os_android.c96
-rw-r--r--vendor/gioui.org/app/internal/window/os_android.go684
-rw-r--r--vendor/gioui.org/app/internal/window/os_darwin.m22
-rw-r--r--vendor/gioui.org/app/internal/window/os_macos.go460
-rw-r--r--vendor/gioui.org/app/internal/window/os_unix.go39
-rw-r--r--vendor/gioui.org/app/internal/window/window.go102
-rw-r--r--vendor/gioui.org/app/internal/windows/windows.go157
-rw-r--r--vendor/gioui.org/app/internal/xkb/xkb_unix.go15
-rw-r--r--vendor/gioui.org/app/loop.go157
-rw-r--r--vendor/gioui.org/app/metal_darwin.go172
-rw-r--r--vendor/gioui.org/app/metal_ios.go48
-rw-r--r--vendor/gioui.org/app/metal_macos.go49
-rw-r--r--vendor/gioui.org/app/os.go220
-rw-r--r--vendor/gioui.org/app/os_android.go1283
-rw-r--r--vendor/gioui.org/app/os_darwin.go (renamed from vendor/gioui.org/app/internal/window/os_darwin.go)76
-rw-r--r--vendor/gioui.org/app/os_darwin.m12
-rw-r--r--vendor/gioui.org/app/os_ios.go (renamed from vendor/gioui.org/app/internal/window/os_ios.go)162
-rw-r--r--vendor/gioui.org/app/os_ios.m (renamed from vendor/gioui.org/app/internal/window/os_ios.m)90
-rw-r--r--vendor/gioui.org/app/os_js.go (renamed from vendor/gioui.org/app/internal/window/os_js.go)342
-rw-r--r--vendor/gioui.org/app/os_macos.go663
-rw-r--r--vendor/gioui.org/app/os_macos.m (renamed from vendor/gioui.org/app/internal/window/os_macos.m)191
-rw-r--r--vendor/gioui.org/app/os_unix.go50
-rw-r--r--vendor/gioui.org/app/os_wayland.c (renamed from vendor/gioui.org/app/internal/window/os_wayland.c)4
-rw-r--r--vendor/gioui.org/app/os_wayland.go (renamed from vendor/gioui.org/app/internal/window/os_wayland.go)328
-rw-r--r--vendor/gioui.org/app/os_windows.go (renamed from vendor/gioui.org/app/internal/window/os_windows.go)486
-rw-r--r--vendor/gioui.org/app/os_x11.go (renamed from vendor/gioui.org/app/internal/window/os_x11.go)420
-rw-r--r--vendor/gioui.org/app/runmain.go (renamed from vendor/gioui.org/app/internal/window/runmain.go)3
-rw-r--r--vendor/gioui.org/app/sigpipe_darwin.go18
-rw-r--r--vendor/gioui.org/app/vulkan.go210
-rw-r--r--vendor/gioui.org/app/vulkan_android.go90
-rw-r--r--vendor/gioui.org/app/vulkan_wayland.go81
-rw-r--r--vendor/gioui.org/app/vulkan_x11.go81
-rw-r--r--vendor/gioui.org/app/wayland_text_input.c (renamed from vendor/gioui.org/app/internal/window/wayland_text_input.c)40
-rw-r--r--vendor/gioui.org/app/wayland_text_input.h (renamed from vendor/gioui.org/app/internal/window/wayland_text_input.h)21
-rw-r--r--vendor/gioui.org/app/wayland_xdg_decoration.c (renamed from vendor/gioui.org/app/internal/window/wayland_xdg_decoration.c)20
-rw-r--r--vendor/gioui.org/app/wayland_xdg_decoration.h (renamed from vendor/gioui.org/app/internal/window/wayland_xdg_decoration.h)12
-rw-r--r--vendor/gioui.org/app/wayland_xdg_shell.c (renamed from vendor/gioui.org/app/internal/window/wayland_xdg_shell.c)107
-rw-r--r--vendor/gioui.org/app/wayland_xdg_shell.h (renamed from vendor/gioui.org/app/internal/window/wayland_xdg_shell.h)191
-rw-r--r--vendor/gioui.org/app/window.go734
-rw-r--r--vendor/gioui.org/cpu/LICENSE63
-rw-r--r--vendor/gioui.org/cpu/README.md25
-rw-r--r--vendor/gioui.org/cpu/abi.h91
-rw-r--r--vendor/gioui.org/cpu/driver.go86
-rw-r--r--vendor/gioui.org/cpu/driver_nosupport.go64
-rw-r--r--vendor/gioui.org/cpu/embed.go11
-rw-r--r--vendor/gioui.org/cpu/go.mod3
-rw-r--r--vendor/gioui.org/cpu/go.sum0
-rw-r--r--vendor/gioui.org/cpu/init.sh23
-rw-r--r--vendor/gioui.org/cpu/runtime.c245
-rw-r--r--vendor/gioui.org/cpu/runtime.h45
-rw-r--r--vendor/gioui.org/f32/affine.go79
-rw-r--r--vendor/gioui.org/f32/f32.go20
-rw-r--r--vendor/gioui.org/font/opentype/opentype.go196
-rw-r--r--vendor/gioui.org/gesture/gesture.go101
-rw-r--r--vendor/gioui.org/gpu/api.go40
-rw-r--r--vendor/gioui.org/gpu/backend/backend.go204
-rw-r--r--vendor/gioui.org/gpu/caches.go17
-rw-r--r--vendor/gioui.org/gpu/clip.go24
-rw-r--r--vendor/gioui.org/gpu/compute.go2219
-rw-r--r--vendor/gioui.org/gpu/cpu.go129
-rw-r--r--vendor/gioui.org/gpu/gen.go5
-rw-r--r--vendor/gioui.org/gpu/gl/backend.go835
-rw-r--r--vendor/gioui.org/gpu/gl/types.go27
-rw-r--r--vendor/gioui.org/gpu/gl/types_js.go29
-rw-r--r--vendor/gioui.org/gpu/gpu.go1131
-rw-r--r--vendor/gioui.org/gpu/internal/d3d11/d3d11.go5
-rw-r--r--vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go859
-rw-r--r--vendor/gioui.org/gpu/internal/driver/api.go127
-rw-r--r--vendor/gioui.org/gpu/internal/driver/driver.go237
-rw-r--r--vendor/gioui.org/gpu/internal/metal/metal.go5
-rw-r--r--vendor/gioui.org/gpu/internal/metal/metal_darwin.go1141
-rw-r--r--vendor/gioui.org/gpu/internal/opengl/opengl.go1357
-rw-r--r--vendor/gioui.org/gpu/internal/opengl/srgb.go176
-rw-r--r--vendor/gioui.org/gpu/internal/vulkan/vulkan.go1121
-rw-r--r--vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go5
-rw-r--r--vendor/gioui.org/gpu/pack.go86
-rw-r--r--vendor/gioui.org/gpu/path.go254
-rw-r--r--vendor/gioui.org/gpu/shaders.go657
-rw-r--r--vendor/gioui.org/gpu/timer.go12
-rw-r--r--vendor/gioui.org/internal/byteslice/byteslice.go45
-rw-r--r--vendor/gioui.org/internal/cocoainit/cocoa_darwin.go (renamed from vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go)0
-rw-r--r--vendor/gioui.org/internal/d3d11/d3d11_windows.go (renamed from vendor/gioui.org/app/internal/d3d11/d3d11_windows.go)910
-rw-r--r--vendor/gioui.org/internal/egl/egl.go (renamed from vendor/gioui.org/app/internal/egl/egl.go)74
-rw-r--r--vendor/gioui.org/internal/egl/egl_unix.go (renamed from vendor/gioui.org/app/internal/egl/egl_unix.go)7
-rw-r--r--vendor/gioui.org/internal/egl/egl_windows.go (renamed from vendor/gioui.org/app/internal/egl/egl_windows.go)13
-rw-r--r--vendor/gioui.org/internal/f32color/rgba.go128
-rw-r--r--vendor/gioui.org/internal/gl/gl.go (renamed from vendor/gioui.org/gpu/gl/gl.go)115
-rw-r--r--vendor/gioui.org/internal/gl/gl_js.go458
-rw-r--r--vendor/gioui.org/internal/gl/gl_unix.go1222
-rw-r--r--vendor/gioui.org/internal/gl/gl_windows.go502
-rw-r--r--vendor/gioui.org/internal/gl/types.go77
-rw-r--r--vendor/gioui.org/internal/gl/types_js.go90
-rw-r--r--vendor/gioui.org/internal/gl/util.go (renamed from vendor/gioui.org/gpu/gl/util.go)34
-rw-r--r--vendor/gioui.org/internal/opconst/ops.go84
-rw-r--r--vendor/gioui.org/internal/ops/ops.go471
-rw-r--r--vendor/gioui.org/internal/ops/reader.go121
-rw-r--r--vendor/gioui.org/internal/scene/scene.go251
-rw-r--r--vendor/gioui.org/internal/stroke/stroke.go742
-rw-r--r--vendor/gioui.org/internal/unsafe/unsafe.go46
-rw-r--r--vendor/gioui.org/internal/vk/vulkan.go2081
-rw-r--r--vendor/gioui.org/internal/vk/vulkan_android.go45
-rw-r--r--vendor/gioui.org/internal/vk/vulkan_wayland.go46
-rw-r--r--vendor/gioui.org/internal/vk/vulkan_x11.go46
-rw-r--r--vendor/gioui.org/io/clipboard/clipboard.go37
-rw-r--r--vendor/gioui.org/io/key/key.go105
-rw-r--r--vendor/gioui.org/io/key/mod.go1
-rw-r--r--vendor/gioui.org/io/pointer/doc.go64
-rw-r--r--vendor/gioui.org/io/pointer/pointer.go184
-rw-r--r--vendor/gioui.org/io/profile/profile.go6
-rw-r--r--vendor/gioui.org/io/router/clipboard.go57
-rw-r--r--vendor/gioui.org/io/router/key.go157
-rw-r--r--vendor/gioui.org/io/router/pointer.go830
-rw-r--r--vendor/gioui.org/io/router/router.go270
-rw-r--r--vendor/gioui.org/io/semantic/semantic.go93
-rw-r--r--vendor/gioui.org/io/system/system.go43
-rw-r--r--vendor/gioui.org/io/transfer/transfer.go109
-rw-r--r--vendor/gioui.org/layout/context.go20
-rw-r--r--vendor/gioui.org/layout/doc.go2
-rw-r--r--vendor/gioui.org/layout/flex.go106
-rw-r--r--vendor/gioui.org/layout/layout.go126
-rw-r--r--vendor/gioui.org/layout/list.go139
-rw-r--r--vendor/gioui.org/layout/stack.go18
-rw-r--r--vendor/gioui.org/op/clip/clip.go464
-rw-r--r--vendor/gioui.org/op/clip/doc.go16
-rw-r--r--vendor/gioui.org/op/clip/shapes.go185
-rw-r--r--vendor/gioui.org/op/op.go242
-rw-r--r--vendor/gioui.org/op/paint/doc.go9
-rw-r--r--vendor/gioui.org/op/paint/paint.go104
-rw-r--r--vendor/gioui.org/shader/LICENSE63
-rw-r--r--vendor/gioui.org/shader/README.md18
-rw-r--r--vendor/gioui.org/shader/gio/blit.frag15
-rw-r--r--vendor/gioui.org/shader/gio/blit.vert27
-rw-r--r--vendor/gioui.org/shader/gio/common.h35
-rw-r--r--vendor/gioui.org/shader/gio/copy.frag24
-rw-r--r--vendor/gioui.org/shader/gio/copy.vert26
-rw-r--r--vendor/gioui.org/shader/gio/cover.frag20
-rw-r--r--vendor/gioui.org/shader/gio/cover.vert31
-rw-r--r--vendor/gioui.org/shader/gio/gen.go5
-rw-r--r--vendor/gioui.org/shader/gio/input.vert15
-rw-r--r--vendor/gioui.org/shader/gio/intersect.frag15
-rw-r--r--vendor/gioui.org/shader/gio/intersect.vert26
-rw-r--r--vendor/gioui.org/shader/gio/material.frag32
-rw-r--r--vendor/gioui.org/shader/gio/material.vert25
-rw-r--r--vendor/gioui.org/shader/gio/shaders.go796
-rw-r--r--vendor/gioui.org/shader/gio/simple.frag11
-rw-r--r--vendor/gioui.org/shader/gio/stencil.frag81
-rw-r--r--vendor/gioui.org/shader/gio/stencil.vert57
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.0.dxbcbin0 -> 628 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.0.glsl100es18
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.0.glsl15017
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.0.metallibiosbin0 -> 2679 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.0.metallibiossimulatorbin0 -> 3057 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.0.metallibmacosbin0 -> 2695 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.0.spirvbin0 -> 624 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.1.dxbcbin0 -> 848 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.1.glsl100es19
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.1.glsl15018
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.1.metallibiosbin0 -> 2935 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.1.metallibiossimulatorbin0 -> 3361 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.1.metallibmacosbin0 -> 2951 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.1.spirvbin0 -> 964 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.2.dxbcbin0 -> 660 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.2.glsl100es13
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.2.glsl15012
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.2.metallibiosbin0 -> 2711 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.2.metallibiossimulatorbin0 -> 3121 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.2.metallibmacosbin0 -> 2775 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.frag.2.spirvbin0 -> 596 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.vert.0.dxbcbin0 -> 1184 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.vert.0.glsl100es37
-rw-r--r--vendor/gioui.org/shader/gio/zblit.vert.0.glsl15037
-rw-r--r--vendor/gioui.org/shader/gio/zblit.vert.0.metallibiosbin0 -> 3044 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.vert.0.metallibiossimulatorbin0 -> 3438 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.vert.0.metallibmacosbin0 -> 3092 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zblit.vert.0.spirvbin0 -> 1600 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.frag.0.dxbcbin0 -> 1256 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.frag.0.glsl100es27
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.frag.0.glsl15026
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiosbin0 -> 2951 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiossimulatorbin0 -> 3361 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.frag.0.metallibmacosbin0 -> 2999 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.frag.0.spirvbin0 -> 1252 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.vert.0.dxbcbin0 -> 1056 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.vert.0.glsl100es35
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.vert.0.glsl15035
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiosbin0 -> 3028 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiossimulatorbin0 -> 3438 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.vert.0.metallibmacosbin0 -> 3060 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcopy.vert.0.spirvbin0 -> 1304 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.0.dxbcbin0 -> 932 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.0.glsl100es23
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.0.glsl15022
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.0.metallibiosbin0 -> 3191 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.0.metallibiossimulatorbin0 -> 3665 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.0.metallibmacosbin0 -> 3207 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.0.spirvbin0 -> 1092 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.1.dxbcbin0 -> 1152 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.1.glsl100es24
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.1.glsl15023
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.1.metallibiosbin0 -> 3367 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.1.metallibiossimulatorbin0 -> 3921 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.1.metallibmacosbin0 -> 3367 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.1.spirvbin0 -> 1416 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.2.dxbcbin0 -> 984 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.2.glsl100es17
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.2.glsl15016
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.2.metallibiosbin0 -> 2967 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.2.metallibiossimulatorbin0 -> 3441 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.2.metallibmacosbin0 -> 3015 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.frag.2.spirvbin0 -> 1000 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.vert.0.dxbcbin0 -> 1332 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.vert.0.glsl100es41
-rw-r--r--vendor/gioui.org/shader/gio/zcover.vert.0.glsl15041
-rw-r--r--vendor/gioui.org/shader/gio/zcover.vert.0.metallibiosbin0 -> 3156 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.vert.0.metallibiossimulatorbin0 -> 3598 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.vert.0.metallibmacosbin0 -> 3204 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zcover.vert.0.spirvbin0 -> 1972 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zinput.vert.0.dxbcbin0 -> 588 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zinput.vert.0.glsl100es22
-rw-r--r--vendor/gioui.org/shader/gio/zinput.vert.0.glsl15022
-rw-r--r--vendor/gioui.org/shader/gio/zinput.vert.0.metallibiosbin0 -> 2579 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zinput.vert.0.metallibiossimulatorbin0 -> 2989 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zinput.vert.0.metallibmacosbin0 -> 2627 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zinput.vert.0.spirvbin0 -> 800 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.frag.0.dxbcbin0 -> 776 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.frag.0.glsl100es13
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.frag.0.glsl15012
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiosbin0 -> 2791 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiossimulatorbin0 -> 3233 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.frag.0.metallibmacosbin0 -> 2823 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.frag.0.spirvbin0 -> 736 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.vert.0.dxbcbin0 -> 1048 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.vert.0.glsl100es35
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.vert.0.glsl15035
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiosbin0 -> 2964 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiossimulatorbin0 -> 3358 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.vert.0.metallibmacosbin0 -> 3012 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zintersect.vert.0.spirvbin0 -> 1368 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.frag.0.dxbcbin0 -> 1476 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl100es37
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl15036
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiosbin0 -> 3367 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiossimulatorbin0 -> 3761 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibmacosbin0 -> 3383 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.frag.0.spirvbin0 -> 1624 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.vert.0.dxbcbin0 -> 1000 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl100es34
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl15034
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiosbin0 -> 2996 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiossimulatorbin0 -> 3406 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibmacosbin0 -> 3028 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zmaterial.vert.0.spirvbin0 -> 1188 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zsimple.frag.0.dxbcbin0 -> 476 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zsimple.frag.0.glsl100es9
-rw-r--r--vendor/gioui.org/shader/gio/zsimple.frag.0.glsl1509
-rw-r--r--vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiosbin0 -> 2359 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiossimulatorbin0 -> 2721 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zsimple.frag.0.metallibmacosbin0 -> 2391 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zsimple.frag.0.spirvbin0 -> 396 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.frag.0.dxbcbin0 -> 2652 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.frag.0.glsl100es38
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.frag.0.glsl15037
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiosbin0 -> 3159 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiossimulatorbin0 -> 3713 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.frag.0.metallibmacosbin0 -> 3175 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.frag.0.spirvbin0 -> 2868 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.vert.0.dxbcbin0 -> 2120 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.vert.0.glsl100es64
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.vert.0.glsl15064
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiosbin0 -> 3535 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiossimulatorbin0 -> 4041 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.vert.0.metallibmacosbin0 -> 3567 bytes
-rw-r--r--vendor/gioui.org/shader/gio/zstencil.vert.0.spirvbin0 -> 2724 bytes
-rw-r--r--vendor/gioui.org/shader/go.mod5
-rw-r--r--vendor/gioui.org/shader/go.sum2
-rw-r--r--vendor/gioui.org/shader/piet/abi.h91
-rw-r--r--vendor/gioui.org/shader/piet/annotated.h225
-rw-r--r--vendor/gioui.org/shader/piet/backdrop.comp109
-rw-r--r--vendor/gioui.org/shader/piet/backdrop_abi.c23
-rw-r--r--vendor/gioui.org/shader/piet/backdrop_abi.go35
-rw-r--r--vendor/gioui.org/shader/piet/backdrop_abi.h17
-rw-r--r--vendor/gioui.org/shader/piet/backdrop_abi_nosupport.go22
-rw-r--r--vendor/gioui.org/shader/piet/backdrop_linux_amd64.sysobin0 -> 16384 bytes
-rw-r--r--vendor/gioui.org/shader/piet/backdrop_linux_arm.sysobin0 -> 12248 bytes
-rw-r--r--vendor/gioui.org/shader/piet/backdrop_linux_arm64.sysobin0 -> 9432 bytes
-rw-r--r--vendor/gioui.org/shader/piet/binning.comp148
-rw-r--r--vendor/gioui.org/shader/piet/binning_abi.c23
-rw-r--r--vendor/gioui.org/shader/piet/binning_abi.go35
-rw-r--r--vendor/gioui.org/shader/piet/binning_abi.h17
-rw-r--r--vendor/gioui.org/shader/piet/binning_abi_nosupport.go22
-rw-r--r--vendor/gioui.org/shader/piet/binning_linux_amd64.sysobin0 -> 19976 bytes
-rw-r--r--vendor/gioui.org/shader/piet/binning_linux_arm.sysobin0 -> 15688 bytes
-rw-r--r--vendor/gioui.org/shader/piet/binning_linux_arm64.sysobin0 -> 11096 bytes
-rw-r--r--vendor/gioui.org/shader/piet/bins.h31
-rw-r--r--vendor/gioui.org/shader/piet/coarse.comp426
-rw-r--r--vendor/gioui.org/shader/piet/coarse_abi.c23
-rw-r--r--vendor/gioui.org/shader/piet/coarse_abi.go35
-rw-r--r--vendor/gioui.org/shader/piet/coarse_abi.h17
-rw-r--r--vendor/gioui.org/shader/piet/coarse_abi_nosupport.go22
-rw-r--r--vendor/gioui.org/shader/piet/coarse_linux_amd64.sysobin0 -> 64736 bytes
-rw-r--r--vendor/gioui.org/shader/piet/coarse_linux_arm.sysobin0 -> 50164 bytes
-rw-r--r--vendor/gioui.org/shader/piet/coarse_linux_arm64.sysobin0 -> 35544 bytes
-rw-r--r--vendor/gioui.org/shader/piet/elements.comp413
-rw-r--r--vendor/gioui.org/shader/piet/elements_abi.c23
-rw-r--r--vendor/gioui.org/shader/piet/elements_abi.go43
-rw-r--r--vendor/gioui.org/shader/piet/elements_abi.h19
-rw-r--r--vendor/gioui.org/shader/piet/elements_abi_nosupport.go30
-rw-r--r--vendor/gioui.org/shader/piet/elements_linux_amd64.sysobin0 -> 157712 bytes
-rw-r--r--vendor/gioui.org/shader/piet/elements_linux_arm.sysobin0 -> 143196 bytes
-rw-r--r--vendor/gioui.org/shader/piet/elements_linux_arm64.sysobin0 -> 96456 bytes
-rw-r--r--vendor/gioui.org/shader/piet/gen.go5
-rw-r--r--vendor/gioui.org/shader/piet/gencpu.sh44
-rw-r--r--vendor/gioui.org/shader/piet/kernel4.comp248
-rw-r--r--vendor/gioui.org/shader/piet/kernel4_abi.c23
-rw-r--r--vendor/gioui.org/shader/piet/kernel4_abi.go43
-rw-r--r--vendor/gioui.org/shader/piet/kernel4_abi.h19
-rw-r--r--vendor/gioui.org/shader/piet/kernel4_abi_nosupport.go30
-rw-r--r--vendor/gioui.org/shader/piet/kernel4_linux_amd64.sysobin0 -> 44120 bytes
-rw-r--r--vendor/gioui.org/shader/piet/kernel4_linux_arm.sysobin0 -> 28548 bytes
-rw-r--r--vendor/gioui.org/shader/piet/kernel4_linux_arm64.sysobin0 -> 23312 bytes
-rw-r--r--vendor/gioui.org/shader/piet/mem.h147
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse.comp294
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse_abi.c23
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse_abi.go35
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse_abi.h17
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse_abi_nosupport.go22
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse_linux_amd64.sysobin0 -> 26320 bytes
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse_linux_arm.sysobin0 -> 16308 bytes
-rw-r--r--vendor/gioui.org/shader/piet/path_coarse_linux_arm64.sysobin0 -> 12392 bytes
-rw-r--r--vendor/gioui.org/shader/piet/pathseg.h100
-rw-r--r--vendor/gioui.org/shader/piet/ptcl.h278
-rw-r--r--vendor/gioui.org/shader/piet/runtime.h45
-rw-r--r--vendor/gioui.org/shader/piet/scene.h313
-rw-r--r--vendor/gioui.org/shader/piet/setup.h51
-rw-r--r--vendor/gioui.org/shader/piet/shaders.go268
-rw-r--r--vendor/gioui.org/shader/piet/state.h73
-rw-r--r--vendor/gioui.org/shader/piet/support.c27
-rw-r--r--vendor/gioui.org/shader/piet/tile.h150
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc.comp108
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc_abi.c23
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc_abi.go35
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc_abi.h17
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc_abi_nosupport.go22
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc_linux_amd64.sysobin0 -> 20864 bytes
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc_linux_arm.sysobin0 -> 15652 bytes
-rw-r--r--vendor/gioui.org/shader/piet/tile_alloc_linux_arm64.sysobin0 -> 10856 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbackdrop.comp.0.dxbcbin0 -> 3312 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiosbin0 -> 4407 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiossimulatorbin0 -> 4977 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibmacosbin0 -> 4407 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbackdrop.comp.0.spirvbin0 -> 5188 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbinning.comp.0.dxbcbin0 -> 4164 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiosbin0 -> 5319 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiossimulatorbin0 -> 6209 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbinning.comp.0.metallibmacosbin0 -> 5351 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zbinning.comp.0.spirvbin0 -> 6924 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zcoarse.comp.0.dxbcbin0 -> 17824 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiosbin0 -> 9783 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiossimulatorbin0 -> 10913 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibmacosbin0 -> 9783 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zcoarse.comp.0.spirvbin0 -> 23204 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zelements.comp.0.dxbcbin0 -> 59800 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zelements.comp.0.metallibiosbin0 -> 24263 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zelements.comp.0.metallibiossimulatorbin0 -> 26033 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zelements.comp.0.metallibmacosbin0 -> 23575 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zelements.comp.0.spirvbin0 -> 67752 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zkernel4.comp.0.dxbcbin0 -> 42832 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiosbin0 -> 8199 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiossimulatorbin0 -> 9201 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibmacosbin0 -> 8167 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zkernel4.comp.0.spirvbin0 -> 15320 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zpath_coarse.comp.0.dxbcbin0 -> 12712 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiosbin0 -> 8247 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiossimulatorbin0 -> 9649 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibmacosbin0 -> 8119 bytes
-rw-r--r--vendor/gioui.org/shader/piet/zpath_coarse.comp.0.spirvbin0 -> 16276 bytes
-rw-r--r--vendor/gioui.org/shader/piet/ztile_alloc.comp.0.dxbcbin0 -> 3476 bytes
-rw-r--r--vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiosbin0 -> 4967 bytes
-rw-r--r--vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiossimulatorbin0 -> 5745 bytes
-rw-r--r--vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibmacosbin0 -> 4919 bytes
-rw-r--r--vendor/gioui.org/shader/piet/ztile_alloc.comp.0.spirvbin0 -> 6004 bytes
-rw-r--r--vendor/gioui.org/shader/shader.go65
-rw-r--r--vendor/gioui.org/text/lru.go10
-rw-r--r--vendor/gioui.org/text/shaper.go95
-rw-r--r--vendor/gioui.org/text/text.go91
419 files changed, 32012 insertions, 10033 deletions
diff --git a/vendor/gioui.org/LICENSE b/vendor/gioui.org/LICENSE
index 818b5da..81f4733 100644
--- a/vendor/gioui.org/LICENSE
+++ b/vendor/gioui.org/LICENSE
@@ -1,5 +1,5 @@
-This project is dual-licensed under the UNLICENSE or
-the MIT license with the SPDX identifier:
+This project is provided under the terms of the UNLICENSE or
+the MIT license denoted by the following SPDX identifier:
SPDX-License-Identifier: Unlicense OR MIT
diff --git a/vendor/gioui.org/app/internal/window/Gio.java b/vendor/gioui.org/app/Gio.java
index 33e1a68..33e1a68 100644
--- a/vendor/gioui.org/app/internal/window/Gio.java
+++ b/vendor/gioui.org/app/Gio.java
diff --git a/vendor/gioui.org/app/internal/window/GioActivity.java b/vendor/gioui.org/app/GioActivity.java
index 260d4b6..4565e38 100644
--- a/vendor/gioui.org/app/internal/window/GioActivity.java
+++ b/vendor/gioui.org/app/GioActivity.java
@@ -3,12 +3,8 @@
package org.gioui;
import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Build;
import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
+import android.content.res.Configuration;
public final class GioActivity extends Activity {
private GioView view;
@@ -16,13 +12,8 @@ public final class GioActivity extends Activity {
@Override public void onCreate(Bundle state) {
super.onCreate(state);
- Window w = getWindow();
-
this.view = new GioView(this);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- this.view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- }
- this.view.setLayoutParams(new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT));
+
setContentView(view);
}
@@ -48,7 +39,7 @@ public final class GioActivity extends Activity {
@Override public void onLowMemory() {
super.onLowMemory();
- view.lowMemory();
+ GioView.onLowMemory();
}
@Override public void onBackPressed() {
diff --git a/vendor/gioui.org/app/GioView.java b/vendor/gioui.org/app/GioView.java
new file mode 100644
index 0000000..14639b0
--- /dev/null
+++ b/vendor/gioui.org/app/GioView.java
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package org.gioui;
+
+import java.lang.Class;
+import java.lang.IllegalAccessException;
+import java.lang.InstantiationException;
+import java.lang.ExceptionInInitializerError;
+import java.lang.SecurityException;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.Editable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.WindowInsets;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.view.Window;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.EditorInfo;
+import android.text.InputType;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import java.io.UnsupportedEncodingException;
+
+public final class GioView extends SurfaceView {
+ private static boolean jniLoaded;
+
+ private final SurfaceHolder.Callback surfCallbacks;
+ private final View.OnFocusChangeListener focusCallback;
+ private final InputMethodManager imm;
+ private final float scrollXScale;
+ private final float scrollYScale;
+ private int keyboardHint;
+ private AccessibilityManager accessManager;
+
+ private long nhandle;
+
+ public GioView(Context context) {
+ this(context, null);
+ }
+
+ public GioView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+ setLayoutParams(new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT));
+
+ // Late initialization of the Go runtime to wait for a valid context.
+ Gio.init(context.getApplicationContext());
+
+ // Set background color to transparent to avoid a flickering
+ // issue on ChromeOS.
+ setBackgroundColor(Color.argb(0, 0, 0, 0));
+
+ ViewConfiguration conf = ViewConfiguration.get(context);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ scrollXScale = conf.getScaledHorizontalScrollFactor();
+ scrollYScale = conf.getScaledVerticalScrollFactor();
+
+ // The platform focus highlight is not aware of Gio's widgets.
+ setDefaultFocusHighlightEnabled(false);
+ } else {
+ float listItemHeight = 48; // dp
+ float px = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ listItemHeight,
+ getResources().getDisplayMetrics()
+ );
+ scrollXScale = px;
+ scrollYScale = px;
+ }
+
+ accessManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ nhandle = onCreateView(this);
+ imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ focusCallback = new View.OnFocusChangeListener() {
+ @Override public void onFocusChange(View v, boolean focus) {
+ GioView.this.onFocusChange(nhandle, focus);
+ }
+ };
+ setOnFocusChangeListener(focusCallback);
+ surfCallbacks = new SurfaceHolder.Callback() {
+ @Override public void surfaceCreated(SurfaceHolder holder) {
+ // Ignore; surfaceChanged is guaranteed to be called immediately after this.
+ }
+ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ onSurfaceChanged(nhandle, getHolder().getSurface());
+ }
+ @Override public void surfaceDestroyed(SurfaceHolder holder) {
+ onSurfaceDestroyed(nhandle);
+ }
+ };
+ getHolder().addCallback(surfCallbacks);
+ }
+
+ @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (nhandle != 0) {
+ onKeyEvent(nhandle, keyCode, event.getUnicodeChar(), event.getEventTime());
+ }
+ return false;
+ }
+
+ @Override public boolean onGenericMotionEvent(MotionEvent event) {
+ dispatchMotionEvent(event);
+ return true;
+ }
+
+ @Override public boolean onTouchEvent(MotionEvent event) {
+ // Ask for unbuffered events. Flutter and Chrome do it
+ // so assume it's good for us as well.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ requestUnbufferedDispatch(event);
+ }
+
+ dispatchMotionEvent(event);
+ return true;
+ }
+
+ private void setCursor(int id) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+ return;
+ }
+ PointerIcon pointerIcon = PointerIcon.getSystemIcon(getContext(), id);
+ setPointerIcon(pointerIcon);
+ }
+
+ private void setOrientation(int id, int fallback) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ id = fallback;
+ }
+ ((Activity) this.getContext()).setRequestedOrientation(id);
+ }
+
+ private void setFullscreen(boolean enabled) {
+ int flags = this.getSystemUiVisibility();
+ if (enabled) {
+ flags |= SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ flags |= SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ flags |= SYSTEM_UI_FLAG_FULLSCREEN;
+ flags |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ } else {
+ flags &= ~SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ flags &= ~SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ flags &= ~SYSTEM_UI_FLAG_FULLSCREEN;
+ flags &= ~SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ }
+ this.setSystemUiVisibility(flags);
+ }
+
+ private enum Bar {
+ NAVIGATION,
+ STATUS,
+ }
+
+ private void setBarColor(Bar t, int color, int luminance) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return;
+ }
+
+ Window window = ((Activity) this.getContext()).getWindow();
+
+ int insetsMask;
+ int viewMask;
+
+ switch (t) {
+ case STATUS:
+ insetsMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+ viewMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ window.setStatusBarColor(color);
+ break;
+ case NAVIGATION:
+ insetsMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+ viewMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ window.setNavigationBarColor(color);
+ break;
+ default:
+ throw new RuntimeException("invalid bar type");
+ }
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return;
+ }
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ int flags = this.getSystemUiVisibility();
+ if (luminance > 128) {
+ flags |= viewMask;
+ } else {
+ flags &= ~viewMask;
+ }
+ this.setSystemUiVisibility(flags);
+ return;
+ }
+
+ WindowInsetsController insetsController = window.getInsetsController();
+ if (insetsController == null) {
+ return;
+ }
+ if (luminance > 128) {
+ insetsController.setSystemBarsAppearance(insetsMask, insetsMask);
+ } else {
+ insetsController.setSystemBarsAppearance(0, insetsMask);
+ }
+ }
+
+ private void setStatusColor(int color, int luminance) {
+ this.setBarColor(Bar.STATUS, color, luminance);
+ }
+
+ private void setNavigationColor(int color, int luminance) {
+ this.setBarColor(Bar.NAVIGATION, color, luminance);
+ }
+
+ @Override protected boolean dispatchHoverEvent(MotionEvent event) {
+ if (!accessManager.isTouchExplorationEnabled()) {
+ return super.dispatchHoverEvent(event);
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ // Fall through.
+ case MotionEvent.ACTION_HOVER_MOVE:
+ onTouchExploration(nhandle, event.getX(), event.getY());
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ onExitTouchExploration(nhandle);
+ break;
+ }
+ return true;
+ }
+
+ void sendA11yEvent(int eventType, int viewId) {
+ if (!accessManager.isEnabled()) {
+ return;
+ }
+ AccessibilityEvent event = obtainA11yEvent(eventType, viewId);
+ getParent().requestSendAccessibilityEvent(this, event);
+ }
+
+ AccessibilityEvent obtainA11yEvent(int eventType, int viewId) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ event.setPackageName(getContext().getPackageName());
+ event.setSource(this, viewId);
+ return event;
+ }
+
+ boolean isA11yActive() {
+ return accessManager.isEnabled();
+ }
+
+ void sendA11yChange(int viewId) {
+ if (!accessManager.isEnabled()) {
+ return;
+ }
+ AccessibilityEvent event = obtainA11yEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, viewId);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ }
+ getParent().requestSendAccessibilityEvent(this, event);
+ }
+
+ private void dispatchMotionEvent(MotionEvent event) {
+ if (nhandle == 0) {
+ return;
+ }
+ for (int j = 0; j < event.getHistorySize(); j++) {
+ long time = event.getHistoricalEventTime(j);
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ onTouchEvent(
+ nhandle,
+ event.ACTION_MOVE,
+ event.getPointerId(i),
+ event.getToolType(i),
+ event.getHistoricalX(i, j),
+ event.getHistoricalY(i, j),
+ scrollXScale*event.getHistoricalAxisValue(MotionEvent.AXIS_HSCROLL, i, j),
+ scrollYScale*event.getHistoricalAxisValue(MotionEvent.AXIS_VSCROLL, i, j),
+ event.getButtonState(),
+ time);
+ }
+ }
+ int act = event.getActionMasked();
+ int idx = event.getActionIndex();
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int pact = event.ACTION_MOVE;
+ if (i == idx) {
+ pact = act;
+ }
+ onTouchEvent(
+ nhandle,
+ pact,
+ event.getPointerId(i),
+ event.getToolType(i),
+ event.getX(i), event.getY(i),
+ scrollXScale*event.getAxisValue(MotionEvent.AXIS_HSCROLL, i),
+ scrollYScale*event.getAxisValue(MotionEvent.AXIS_VSCROLL, i),
+ event.getButtonState(),
+ event.getEventTime());
+ }
+ }
+
+ @Override public InputConnection onCreateInputConnection(EditorInfo editor) {
+ editor.inputType = this.keyboardHint;
+ editor.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
+ return new InputConnection(this);
+ }
+
+ void setInputHint(int hint) {
+ if (hint == this.keyboardHint) {
+ return;
+ }
+ this.keyboardHint = hint;
+ imm.restartInput(this);
+ }
+
+ void showTextInput() {
+ GioView.this.requestFocus();
+ imm.showSoftInput(GioView.this, 0);
+ }
+
+ void hideTextInput() {
+ imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+
+ @Override protected boolean fitSystemWindows(Rect insets) {
+ if (nhandle != 0) {
+ onWindowInsets(nhandle, insets.top, insets.right, insets.bottom, insets.left);
+ }
+ return true;
+ }
+
+ @Override protected void onDraw(Canvas canvas) {
+ if (nhandle != 0) {
+ onFrameCallback(nhandle);
+ }
+ }
+
+ int getDensity() {
+ return getResources().getDisplayMetrics().densityDpi;
+ }
+
+ float getFontScale() {
+ return getResources().getConfiguration().fontScale;
+ }
+
+ public void start() {
+ if (nhandle != 0) {
+ onStartView(nhandle);
+ }
+ }
+
+ public void stop() {
+ if (nhandle != 0) {
+ onStopView(nhandle);
+ }
+ }
+
+ public void destroy() {
+ if (nhandle != 0) {
+ onDestroyView(nhandle);
+ }
+ }
+
+ protected void unregister() {
+ setOnFocusChangeListener(null);
+ getHolder().removeCallback(surfCallbacks);
+ nhandle = 0;
+ }
+
+ public void configurationChanged() {
+ if (nhandle != 0) {
+ onConfigurationChanged(nhandle);
+ }
+ }
+
+ public boolean backPressed() {
+ if (nhandle == 0) {
+ return false;
+ }
+ return onBack(nhandle);
+ }
+
+ static private native long onCreateView(GioView view);
+ static private native void onDestroyView(long handle);
+ static private native void onStartView(long handle);
+ static private native void onStopView(long handle);
+ static private native void onSurfaceDestroyed(long handle);
+ static private native void onSurfaceChanged(long handle, Surface surface);
+ static private native void onConfigurationChanged(long handle);
+ static private native void onWindowInsets(long handle, int top, int right, int bottom, int left);
+ static public native void onLowMemory();
+ static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, float scrollX, float scrollY, int buttons, long time);
+ static private native void onKeyEvent(long handle, int code, int character, long time);
+ static private native void onFrameCallback(long handle);
+ static private native boolean onBack(long handle);
+ static private native void onFocusChange(long handle, boolean focus);
+ static private native AccessibilityNodeInfo initializeAccessibilityNodeInfo(long handle, int viewId, int screenX, int screenY, AccessibilityNodeInfo info);
+ static private native void onTouchExploration(long handle, float x, float y);
+ static private native void onExitTouchExploration(long handle);
+ static private native void onA11yFocus(long handle, int viewId);
+ static private native void onClearA11yFocus(long handle, int viewId);
+
+ private static class InputConnection extends BaseInputConnection {
+ private final Editable editable;
+
+ InputConnection(View view) {
+ // Passing false enables "dummy mode", where the BaseInputConnection
+ // attempts to convert IME operations to key events.
+ super(view, false);
+ editable = Editable.Factory.getInstance().newEditable("");
+ }
+
+ @Override public Editable getEditable() {
+ return editable;
+ }
+ }
+
+ @Override public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ return new AccessibilityNodeProvider() {
+ private final int[] screenOff = new int[2];
+
+ @Override public AccessibilityNodeInfo createAccessibilityNodeInfo(int viewId) {
+ AccessibilityNodeInfo info = null;
+ if (viewId == View.NO_ID) {
+ info = AccessibilityNodeInfo.obtain(GioView.this);
+ GioView.this.onInitializeAccessibilityNodeInfo(info);
+ } else {
+ info = AccessibilityNodeInfo.obtain(GioView.this, viewId);
+ info.setPackageName(getContext().getPackageName());
+ info.setVisibleToUser(true);
+ }
+ GioView.this.getLocationOnScreen(screenOff);
+ info = GioView.this.initializeAccessibilityNodeInfo(nhandle, viewId, screenOff[0], screenOff[1], info);
+ return info;
+ }
+
+ @Override public boolean performAction(int viewId, int action, Bundle arguments) {
+ if (viewId == View.NO_ID) {
+ return GioView.this.performAccessibilityAction(action, arguments);
+ }
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+ GioView.this.onA11yFocus(nhandle, viewId);
+ GioView.this.sendA11yEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, viewId);
+ return true;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+ GioView.this.onClearA11yFocus(nhandle, viewId);
+ GioView.this.sendA11yEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, viewId);
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+}
diff --git a/vendor/gioui.org/app/app.go b/vendor/gioui.org/app/app.go
index d21224e..e00298a 100644
--- a/vendor/gioui.org/app/app.go
+++ b/vendor/gioui.org/app/app.go
@@ -5,8 +5,6 @@ package app
import (
"os"
"strings"
-
- "gioui.org/app/internal/window"
)
// extraArgs contains extra arguments to append to
@@ -44,5 +42,5 @@ func DataDir() (string, error) {
// require control of the main thread of the program for
// running windows.
func Main() {
- window.Main()
+ osMain()
}
diff --git a/vendor/gioui.org/app/app_android.go b/vendor/gioui.org/app/app_android.go
deleted file mode 100644
index 58e40d0..0000000
--- a/vendor/gioui.org/app/app_android.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package app
-
-import (
- "gioui.org/app/internal/window"
-)
-
-// JavaVM returns the global JNI JavaVM.
-func JavaVM() uintptr {
- return window.JavaVM()
-}
-
-// AppContext returns the global Application context as a JNI
-// jobject.
-func AppContext() uintptr {
- return window.AppContext()
-}
-
-// Do invokes the function with a JNI jobject handle to the underlying
-// Android View. The function is invoked on the main thread, and the
-// handle is invalidated after the function returns.
-//
-// Note: Do may deadlock if called from the same goroutine that receives from
-// Events.
-func (w *Window) Do(f func(view uintptr)) {
- type androidDriver interface {
- Do(f func(view uintptr)) bool
- }
- success := make(chan bool)
- for {
- driver := make(chan androidDriver, 1)
- // two-stage process: first wait for a valid driver...
- w.driverDo(func() {
- driver <- w.driver.(androidDriver)
- })
- // .. then run the function on the main thread using the
- // driver. The driver Do method returns false if the
- // view was invalidated while switching to the main thread.
- window.RunOnMain(func() {
- d := <-driver
- success <- d.Do(f)
- })
- if <-success {
- break
- }
- }
-}
diff --git a/vendor/gioui.org/app/d3d11_windows.go b/vendor/gioui.org/app/d3d11_windows.go
new file mode 100644
index 0000000..ab04ee4
--- /dev/null
+++ b/vendor/gioui.org/app/d3d11_windows.go
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package app
+
+import (
+ "fmt"
+ "unsafe"
+
+ "gioui.org/gpu"
+ "gioui.org/internal/d3d11"
+)
+
+type d3d11Context struct {
+ win *window
+ dev *d3d11.Device
+ ctx *d3d11.DeviceContext
+
+ swchain *d3d11.IDXGISwapChain
+ renderTarget *d3d11.RenderTargetView
+ width, height int
+}
+
+const debug = false
+
+func init() {
+ drivers = append(drivers, gpuAPI{
+ priority: 1,
+ initializer: func(w *window) (context, error) {
+ hwnd, _, _ := w.HWND()
+ var flags uint32
+ if debug {
+ flags |= d3d11.CREATE_DEVICE_DEBUG
+ }
+ dev, ctx, _, err := d3d11.CreateDevice(
+ d3d11.DRIVER_TYPE_HARDWARE,
+ flags,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("NewContext: %v", err)
+ }
+ swchain, err := d3d11.CreateSwapChain(dev, hwnd)
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(ctx), ctx.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release)
+ return nil, err
+ }
+ return &d3d11Context{win: w, dev: dev, ctx: ctx, swchain: swchain}, nil
+ },
+ })
+}
+
+func (c *d3d11Context) API() gpu.API {
+ return gpu.Direct3D11{Device: unsafe.Pointer(c.dev)}
+}
+
+func (c *d3d11Context) RenderTarget() (gpu.RenderTarget, error) {
+ return gpu.Direct3D11RenderTarget{
+ RenderTarget: unsafe.Pointer(c.renderTarget),
+ }, nil
+}
+
+func (c *d3d11Context) Present() error {
+ err := c.swchain.Present(1, 0)
+ if err == nil {
+ return nil
+ }
+ if err, ok := err.(d3d11.ErrorCode); ok {
+ switch err.Code {
+ case d3d11.DXGI_STATUS_OCCLUDED:
+ // Ignore
+ return nil
+ case d3d11.DXGI_ERROR_DEVICE_RESET, d3d11.DXGI_ERROR_DEVICE_REMOVED, d3d11.D3DDDIERR_DEVICEREMOVED:
+ return gpu.ErrDeviceLost
+ }
+ }
+ return err
+}
+
+func (c *d3d11Context) Refresh() error {
+ var width, height int
+ _, width, height = c.win.HWND()
+ if c.renderTarget != nil && width == c.width && height == c.height {
+ return nil
+ }
+ c.releaseFBO()
+ if err := c.swchain.ResizeBuffers(0, 0, 0, d3d11.DXGI_FORMAT_UNKNOWN, 0); err != nil {
+ return err
+ }
+ c.width = width
+ c.height = height
+
+ backBuffer, err := c.swchain.GetBuffer(0, &d3d11.IID_Texture2D)
+ if err != nil {
+ return err
+ }
+ texture := (*d3d11.Resource)(unsafe.Pointer(backBuffer))
+ renderTarget, err := c.dev.CreateRenderTargetView(texture)
+ d3d11.IUnknownRelease(unsafe.Pointer(backBuffer), backBuffer.Vtbl.Release)
+ if err != nil {
+ return err
+ }
+ c.renderTarget = renderTarget
+ return nil
+}
+
+func (c *d3d11Context) Lock() error {
+ c.ctx.OMSetRenderTargets(c.renderTarget, nil)
+ return nil
+}
+
+func (c *d3d11Context) Unlock() {}
+
+func (c *d3d11Context) Release() {
+ c.releaseFBO()
+ if c.swchain != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(c.swchain), c.swchain.Vtbl.Release)
+ }
+ if c.ctx != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(c.ctx), c.ctx.Vtbl.Release)
+ }
+ if c.dev != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(c.dev), c.dev.Vtbl.Release)
+ }
+ *c = d3d11Context{}
+ if debug {
+ d3d11.ReportLiveObjects()
+ }
+}
+
+func (c *d3d11Context) releaseFBO() {
+ if c.renderTarget != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(c.renderTarget), c.renderTarget.Vtbl.Release)
+ c.renderTarget = nil
+ }
+}
diff --git a/vendor/gioui.org/app/datadir.go b/vendor/gioui.org/app/datadir.go
index 31e5453..500a59a 100644
--- a/vendor/gioui.org/app/datadir.go
+++ b/vendor/gioui.org/app/datadir.go
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build !android
// +build !android
package app
diff --git a/vendor/gioui.org/app/datadir_android.go b/vendor/gioui.org/app/datadir_android.go
deleted file mode 100644
index 450e6cf..0000000
--- a/vendor/gioui.org/app/datadir_android.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build android
-
-package app
-
-import "C"
-
-import (
- "os"
- "path/filepath"
- "sync"
-
- "gioui.org/app/internal/window"
-)
-
-var (
- dataDirOnce sync.Once
- dataPath string
-)
-
-func dataDir() (string, error) {
- dataDirOnce.Do(func() {
- dataPath = window.GetDataDir()
- // Set XDG_CACHE_HOME to make os.UserCacheDir work.
- if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists {
- cachePath := filepath.Join(dataPath, "cache")
- os.Setenv("XDG_CACHE_HOME", cachePath)
- }
- // Set XDG_CONFIG_HOME to make os.UserConfigDir work.
- if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists {
- cfgPath := filepath.Join(dataPath, "config")
- os.Setenv("XDG_CONFIG_HOME", cfgPath)
- }
- // Set HOME to make os.UserHomeDir work.
- if _, exists := os.LookupEnv("HOME"); !exists {
- os.Setenv("HOME", dataPath)
- }
- })
- return dataPath, nil
-}
diff --git a/vendor/gioui.org/app/egl_android.go b/vendor/gioui.org/app/egl_android.go
new file mode 100644
index 0000000..2c31130
--- /dev/null
+++ b/vendor/gioui.org/app/egl_android.go
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package app
+
+/*
+#include <android/native_window_jni.h>
+#include <EGL/egl.h>
+*/
+import "C"
+
+import (
+ "unsafe"
+
+ "gioui.org/internal/egl"
+)
+
+type androidContext struct {
+ win *window
+ eglSurf egl.NativeWindowType
+ width, height int
+ *egl.Context
+}
+
+func init() {
+ newAndroidGLESContext = func(w *window) (context, error) {
+ ctx, err := egl.NewContext(nil)
+ if err != nil {
+ return nil, err
+ }
+ return &androidContext{win: w, Context: ctx}, nil
+ }
+}
+
+func (c *androidContext) Release() {
+ if c.Context != nil {
+ c.Context.Release()
+ c.Context = nil
+ }
+}
+
+func (c *androidContext) Refresh() error {
+ c.Context.ReleaseSurface()
+ if err := c.win.setVisual(c.Context.VisualID()); err != nil {
+ return err
+ }
+ win, width, height := c.win.nativeWindow()
+ c.eglSurf = egl.NativeWindowType(unsafe.Pointer(win))
+ c.width, c.height = width, height
+ return nil
+}
+
+func (c *androidContext) Lock() error {
+ // The Android emulator creates a broken surface if it is not
+ // created on the same thread as the context is made current.
+ if c.eglSurf != nil {
+ if err := c.Context.CreateSurface(c.eglSurf, c.width, c.height); err != nil {
+ return err
+ }
+ c.eglSurf = nil
+ }
+ return c.Context.MakeCurrent()
+}
+
+func (c *androidContext) Unlock() {
+ c.Context.ReleaseCurrent()
+}
diff --git a/vendor/gioui.org/app/internal/window/egl_wayland.go b/vendor/gioui.org/app/egl_wayland.go
index b4878b4..c2406a0 100644
--- a/vendor/gioui.org/app/internal/window/egl_wayland.go
+++ b/vendor/gioui.org/app/egl_wayland.go
@@ -1,20 +1,22 @@
// SPDX-License-Identifier: Unlicense OR MIT
-// +build linux,!android,!nowayland freebsd
+//go:build ((linux && !android) || freebsd) && !nowayland
+// +build linux,!android freebsd
+// +build !nowayland
-package window
+package app
import (
"errors"
"unsafe"
- "gioui.org/app/internal/egl"
+ "gioui.org/internal/egl"
)
/*
#cgo linux pkg-config: egl wayland-egl
#cgo freebsd openbsd LDFLAGS: -lwayland-egl
-#cgo CFLAGS: -DMESA_EGL_NO_X11_HEADERS
+#cgo CFLAGS: -DEGL_NO_X11
#include <EGL/egl.h>
#include <wayland-client.h>
@@ -22,22 +24,24 @@ import (
*/
import "C"
-type context struct {
+type wlContext struct {
win *window
*egl.Context
eglWin *C.struct_wl_egl_window
}
-func (w *window) NewContext() (Context, error) {
- disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
- ctx, err := egl.NewContext(disp)
- if err != nil {
- return nil, err
+func init() {
+ newWaylandEGLContext = func(w *window) (context, error) {
+ disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
+ ctx, err := egl.NewContext(disp)
+ if err != nil {
+ return nil, err
+ }
+ return &wlContext{Context: ctx, win: w}, nil
}
- return &context{Context: ctx, win: w}, nil
}
-func (c *context) Release() {
+func (c *wlContext) Release() {
if c.Context != nil {
c.Context.Release()
c.Context = nil
@@ -48,7 +52,7 @@ func (c *context) Release() {
}
}
-func (c *context) MakeCurrent() error {
+func (c *wlContext) Refresh() error {
c.Context.ReleaseSurface()
if c.eglWin != nil {
C.wl_egl_window_destroy(c.eglWin)
@@ -64,12 +68,13 @@ func (c *context) MakeCurrent() error {
}
c.eglWin = eglWin
eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin)))
- if err := c.Context.CreateSurface(eglSurf, width, height); err != nil {
- return err
- }
- return c.Context.MakeCurrent()
+ return c.Context.CreateSurface(eglSurf, width, height)
}
-func (c *context) Lock() {}
+func (c *wlContext) Lock() error {
+ return c.Context.MakeCurrent()
+}
-func (c *context) Unlock() {}
+func (c *wlContext) Unlock() {
+ c.Context.ReleaseCurrent()
+}
diff --git a/vendor/gioui.org/app/internal/window/egl_windows.go b/vendor/gioui.org/app/egl_windows.go
index 654d279..3a95450 100644
--- a/vendor/gioui.org/app/internal/window/egl_windows.go
+++ b/vendor/gioui.org/app/egl_windows.go
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: Unlicense OR MIT
-package window
+package app
import (
- "gioui.org/app/internal/egl"
+ "golang.org/x/sys/windows"
+
+ "gioui.org/internal/egl"
)
type glContext struct {
@@ -12,9 +14,9 @@ type glContext struct {
}
func init() {
- backends = append(backends, gpuAPI{
+ drivers = append(drivers, gpuAPI{
priority: 2,
- initializer: func(w *window) (Context, error) {
+ initializer: func(w *window) (context, error) {
disp := egl.NativeDisplayType(w.HDC())
ctx, err := egl.NewContext(disp)
if err != nil {
@@ -32,9 +34,13 @@ func (c *glContext) Release() {
}
}
-func (c *glContext) MakeCurrent() error {
+func (c *glContext) Refresh() error {
c.Context.ReleaseSurface()
- win, width, height := c.win.HWND()
+ var (
+ win windows.Handle
+ width, height int
+ )
+ win, width, height = c.win.HWND()
eglSurf := egl.NativeWindowType(win)
if err := c.Context.CreateSurface(eglSurf, width, height); err != nil {
return err
@@ -43,9 +49,14 @@ func (c *glContext) MakeCurrent() error {
return err
}
c.Context.EnableVSync(true)
+ c.Context.ReleaseCurrent()
return nil
}
-func (c *glContext) Lock() {}
+func (c *glContext) Lock() error {
+ return c.Context.MakeCurrent()
+}
-func (c *glContext) Unlock() {}
+func (c *glContext) Unlock() {
+ c.Context.ReleaseCurrent()
+}
diff --git a/vendor/gioui.org/app/egl_x11.go b/vendor/gioui.org/app/egl_x11.go
new file mode 100644
index 0000000..7234808
--- /dev/null
+++ b/vendor/gioui.org/app/egl_x11.go
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build ((linux && !android) || freebsd || openbsd) && !nox11
+// +build linux,!android freebsd openbsd
+// +build !nox11
+
+package app
+
+import (
+ "unsafe"
+
+ "gioui.org/internal/egl"
+)
+
+type x11Context struct {
+ win *x11Window
+ *egl.Context
+}
+
+func init() {
+ newX11EGLContext = func(w *x11Window) (context, error) {
+ disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
+ ctx, err := egl.NewContext(disp)
+ if err != nil {
+ return nil, err
+ }
+ return &x11Context{win: w, Context: ctx}, nil
+ }
+}
+
+func (c *x11Context) Release() {
+ if c.Context != nil {
+ c.Context.Release()
+ c.Context = nil
+ }
+}
+
+func (c *x11Context) Refresh() error {
+ c.Context.ReleaseSurface()
+ win, width, height := c.win.window()
+ eglSurf := egl.NativeWindowType(uintptr(win))
+ if err := c.Context.CreateSurface(eglSurf, width, height); err != nil {
+ return err
+ }
+ if err := c.Context.MakeCurrent(); err != nil {
+ return err
+ }
+ c.Context.EnableVSync(true)
+ c.Context.ReleaseCurrent()
+ return nil
+}
+
+func (c *x11Context) Lock() error {
+ return c.Context.MakeCurrent()
+}
+
+func (c *x11Context) Unlock() {
+ c.Context.ReleaseCurrent()
+}
diff --git a/vendor/gioui.org/app/internal/window/framework_ios.h b/vendor/gioui.org/app/framework_ios.h
index 18e5a02..18e5a02 100644
--- a/vendor/gioui.org/app/internal/window/framework_ios.h
+++ b/vendor/gioui.org/app/framework_ios.h
diff --git a/vendor/gioui.org/app/internal/window/gl_ios.go b/vendor/gioui.org/app/gl_ios.go
index 0099189..2dc2dbf 100644
--- a/vendor/gioui.org/app/internal/window/gl_ios.go
+++ b/vendor/gioui.org/app/gl_ios.go
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: Unlicense OR MIT
-// +build darwin,ios
+//go:build darwin && ios && nometal
+// +build darwin,ios,nometal
-package window
+package app
/*
+@import UIKit;
+
#include <CoreFoundation/CoreFoundation.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
@@ -14,32 +17,34 @@ __attribute__ ((visibility ("hidden"))) int gio_presentRenderbuffer(CFTypeRef ct
__attribute__ ((visibility ("hidden"))) int gio_makeCurrent(CFTypeRef ctx);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createContext(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLLayer(void);
+
+static CFTypeRef getViewLayer(CFTypeRef viewRef) {
+ @autoreleasepool {
+ UIView *view = (__bridge UIView *)viewRef;
+ return CFBridgingRetain(view.layer);
+ }
+}
+
*/
import "C"
import (
"errors"
"fmt"
+ "runtime"
- "gioui.org/app/internal/glimpl"
- "gioui.org/gpu/backend"
- "gioui.org/gpu/gl"
+ "gioui.org/gpu"
+ "gioui.org/internal/gl"
)
type context struct {
- owner *window
- c *glimpl.Functions
- ctx C.CFTypeRef
- layer C.CFTypeRef
- init bool
- frameBuffer gl.Framebuffer
- colorBuffer, depthBuffer gl.Renderbuffer
-}
-
-func init() {
- layerFactory = func() uintptr {
- return uintptr(C.gio_createGLLayer())
- }
+ owner *window
+ c *gl.Functions
+ ctx C.CFTypeRef
+ layer C.CFTypeRef
+ init bool
+ frameBuffer gl.Framebuffer
+ colorBuffer gl.Renderbuffer
}
func newContext(w *window) (*context, error) {
@@ -47,17 +52,30 @@ func newContext(w *window) (*context, error) {
if ctx == 0 {
return nil, fmt.Errorf("failed to create EAGLContext")
}
+ api := contextAPI()
+ f, err := gl.NewFunctions(api.Context, api.ES)
+ if err != nil {
+ return nil, err
+ }
c := &context{
ctx: ctx,
owner: w,
- layer: C.CFTypeRef(w.contextLayer()),
- c: new(glimpl.Functions),
+ layer: C.getViewLayer(w.contextView()),
+ c: f,
}
return c, nil
}
-func (c *context) Backend() (backend.Device, error) {
- return gl.NewBackend(c.c)
+func contextAPI() gpu.OpenGL {
+ return gpu.OpenGL{}
+}
+
+func (c *context) RenderTarget() gpu.RenderTarget {
+ return gpu.OpenGLRenderTarget(c.frameBuffer)
+}
+
+func (c *context) API() gpu.API {
+ return contextAPI()
}
func (c *context) Release() {
@@ -67,7 +85,6 @@ func (c *context) Release() {
C.gio_renderbufferStorage(c.ctx, 0, C.GLenum(gl.RENDERBUFFER))
c.c.DeleteFramebuffer(c.frameBuffer)
c.c.DeleteRenderbuffer(c.colorBuffer)
- c.c.DeleteRenderbuffer(c.depthBuffer)
C.gio_makeCurrent(0)
C.CFRelease(c.ctx)
c.ctx = 0
@@ -77,10 +94,6 @@ func (c *context) Present() error {
if c.layer == 0 {
panic("context is not active")
}
- // Discard depth buffer as recommended in
- // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html
- c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer)
- c.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT)
c.c.BindRenderbuffer(gl.RENDERBUFFER, c.colorBuffer)
if C.gio_presentRenderbuffer(c.ctx, C.GLenum(gl.RENDERBUFFER)) == 0 {
return errors.New("presentRenderBuffer failed")
@@ -88,23 +101,30 @@ func (c *context) Present() error {
return nil
}
-func (c *context) Lock() {}
+func (c *context) Lock() error {
+ // OpenGL contexts are implicit and thread-local. Lock the OS thread.
+ runtime.LockOSThread()
-func (c *context) Unlock() {}
+ if C.gio_makeCurrent(c.ctx) == 0 {
+ return errors.New("[EAGLContext setCurrentContext] failed")
+ }
+ return nil
+}
+
+func (c *context) Unlock() {
+ C.gio_makeCurrent(0)
+}
-func (c *context) MakeCurrent() error {
+func (c *context) Refresh() error {
if C.gio_makeCurrent(c.ctx) == 0 {
- C.CFRelease(c.ctx)
- c.ctx = 0
return errors.New("[EAGLContext setCurrentContext] failed")
}
if !c.init {
c.init = true
c.frameBuffer = c.c.CreateFramebuffer()
c.colorBuffer = c.c.CreateRenderbuffer()
- c.depthBuffer = c.c.CreateRenderbuffer()
}
- if !c.owner.isVisible() {
+ if !c.owner.visible {
// Make sure any in-flight GL commands are complete.
c.c.Finish()
return nil
@@ -114,14 +134,9 @@ func (c *context) MakeCurrent() error {
if C.gio_renderbufferStorage(c.ctx, c.layer, C.GLenum(gl.RENDERBUFFER)) == 0 {
return errors.New("renderbufferStorage failed")
}
- w := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)
- h := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)
- c.c.BindRenderbuffer(gl.RENDERBUFFER, c.depthBuffer)
- c.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h)
c.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB)
c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer)
c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, c.colorBuffer)
- c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, c.depthBuffer)
if st := c.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
return fmt.Errorf("framebuffer incomplete, status: %#x\n", st)
}
diff --git a/vendor/gioui.org/app/internal/window/gl_ios.m b/vendor/gioui.org/app/gl_ios.m
index 065ea97..8e50753 100644
--- a/vendor/gioui.org/app/internal/window/gl_ios.m
+++ b/vendor/gioui.org/app/gl_ios.m
@@ -1,12 +1,16 @@
// SPDX-License-Identifier: Unlicense OR MIT
-// +build darwin,ios
+// +build darwin,ios,nometal
@import UIKit;
@import OpenGLES;
#include "_cgo_export.h"
+Class gio_layerClass(void) {
+ return [CAEAGLLayer class];
+}
+
int gio_renderbufferStorage(CFTypeRef ctxRef, CFTypeRef layerRef, GLenum buffer) {
EAGLContext *ctx = (__bridge EAGLContext *)ctxRef;
CAEAGLLayer *layer = (__bridge CAEAGLLayer *)layerRef;
diff --git a/vendor/gioui.org/app/gl_js.go b/vendor/gioui.org/app/gl_js.go
new file mode 100644
index 0000000..e3aee8d
--- /dev/null
+++ b/vendor/gioui.org/app/gl_js.go
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package app
+
+import (
+ "errors"
+ "syscall/js"
+
+ "gioui.org/gpu"
+ "gioui.org/internal/gl"
+)
+
+type glContext struct {
+ ctx js.Value
+ cnv js.Value
+}
+
+func newContext(w *window) (*glContext, error) {
+ args := map[string]interface{}{
+ // Enable low latency rendering.
+ // See https://developers.google.com/web/updates/2019/05/desynchronized.
+ "desynchronized": true,
+ "preserveDrawingBuffer": true,
+ }
+ ctx := w.cnv.Call("getContext", "webgl2", args)
+ if ctx.IsNull() {
+ ctx = w.cnv.Call("getContext", "webgl", args)
+ }
+ if ctx.IsNull() {
+ return nil, errors.New("app: webgl is not supported")
+ }
+ c := &glContext{
+ ctx: ctx,
+ cnv: w.cnv,
+ }
+ return c, nil
+}
+
+func (c *glContext) RenderTarget() (gpu.RenderTarget, error) {
+ return gpu.OpenGLRenderTarget{}, nil
+}
+
+func (c *glContext) API() gpu.API {
+ return gpu.OpenGL{Context: gl.Context(c.ctx)}
+}
+
+func (c *glContext) Release() {
+}
+
+func (c *glContext) Present() error {
+ if c.ctx.Call("isContextLost").Bool() {
+ return errors.New("context lost")
+ }
+ return nil
+}
+
+func (c *glContext) Lock() error {
+ return nil
+}
+
+func (c *glContext) Unlock() {}
+
+func (c *glContext) Refresh() error {
+ return nil
+}
+
+func (w *window) NewContext() (context, error) {
+ return newContext(w)
+}
diff --git a/vendor/gioui.org/app/gl_macos.go b/vendor/gioui.org/app/gl_macos.go
new file mode 100644
index 0000000..7511bbd
--- /dev/null
+++ b/vendor/gioui.org/app/gl_macos.go
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build darwin && !ios && nometal
+// +build darwin,!ios,nometal
+
+package app
+
+import (
+ "errors"
+ "runtime"
+
+ "unsafe"
+
+ "gioui.org/gpu"
+ "gioui.org/internal/gl"
+)
+
+/*
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <AppKit/AppKit.h>
+#include <dlfcn.h>
+
+__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLContext(void);
+__attribute__ ((visibility ("hidden"))) void gio_setContextView(CFTypeRef ctx, CFTypeRef view);
+__attribute__ ((visibility ("hidden"))) void gio_makeCurrentContext(CFTypeRef ctx);
+__attribute__ ((visibility ("hidden"))) void gio_updateContext(CFTypeRef ctx);
+__attribute__ ((visibility ("hidden"))) void gio_flushContextBuffer(CFTypeRef ctx);
+__attribute__ ((visibility ("hidden"))) void gio_clearCurrentContext(void);
+__attribute__ ((visibility ("hidden"))) void gio_lockContext(CFTypeRef ctxRef);
+__attribute__ ((visibility ("hidden"))) void gio_unlockContext(CFTypeRef ctxRef);
+
+typedef void (*PFN_glFlush)(void);
+
+static void glFlush(PFN_glFlush f) {
+ f();
+}
+*/
+import "C"
+
+type glContext struct {
+ c *gl.Functions
+ ctx C.CFTypeRef
+ view C.CFTypeRef
+
+ glFlush C.PFN_glFlush
+}
+
+func newContext(w *window) (*glContext, error) {
+ clib := C.CString("/System/Library/Frameworks/OpenGL.framework/OpenGL")
+ defer C.free(unsafe.Pointer(clib))
+ lib, err := C.dlopen(clib, C.RTLD_NOW|C.RTLD_LOCAL)
+ if err != nil {
+ return nil, err
+ }
+ csym := C.CString("glFlush")
+ defer C.free(unsafe.Pointer(csym))
+ glFlush := C.PFN_glFlush(C.dlsym(lib, csym))
+ if glFlush == nil {
+ return nil, errors.New("gl: missing symbol glFlush in the OpenGL framework")
+ }
+ view := w.contextView()
+ ctx := C.gio_createGLContext()
+ if ctx == 0 {
+ return nil, errors.New("gl: failed to create NSOpenGLContext")
+ }
+ C.gio_setContextView(ctx, view)
+ c := &glContext{
+ ctx: ctx,
+ view: view,
+ glFlush: glFlush,
+ }
+ return c, nil
+}
+
+func (c *glContext) RenderTarget() (gpu.RenderTarget, error) {
+ return gpu.OpenGLRenderTarget{}, nil
+}
+
+func (c *glContext) API() gpu.API {
+ return gpu.OpenGL{}
+}
+
+func (c *glContext) Release() {
+ if c.ctx != 0 {
+ C.gio_clearCurrentContext()
+ C.CFRelease(c.ctx)
+ c.ctx = 0
+ }
+}
+
+func (c *glContext) Present() error {
+ // Assume the caller already locked the context.
+ C.glFlush(c.glFlush)
+ return nil
+}
+
+func (c *glContext) Lock() error {
+ // OpenGL contexts are implicit and thread-local. Lock the OS thread.
+ runtime.LockOSThread()
+
+ C.gio_lockContext(c.ctx)
+ C.gio_makeCurrentContext(c.ctx)
+ return nil
+}
+
+func (c *glContext) Unlock() {
+ C.gio_clearCurrentContext()
+ C.gio_unlockContext(c.ctx)
+}
+
+func (c *glContext) Refresh() error {
+ c.Lock()
+ defer c.Unlock()
+ C.gio_updateContext(c.ctx)
+ return nil
+}
+
+func (w *window) NewContext() (context, error) {
+ return newContext(w)
+}
diff --git a/vendor/gioui.org/app/gl_macos.m b/vendor/gioui.org/app/gl_macos.m
new file mode 100644
index 0000000..fe83743
--- /dev/null
+++ b/vendor/gioui.org/app/gl_macos.m
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// +build darwin,!ios,nometal
+
+@import AppKit;
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <OpenGL/OpenGL.h>
+#include "_cgo_export.h"
+
+CALayer *gio_layerFactory(void) {
+ @autoreleasepool {
+ return [CALayer layer];
+ }
+}
+
+CFTypeRef gio_createGLContext(void) {
+ @autoreleasepool {
+ NSOpenGLPixelFormatAttribute attr[] = {
+ NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
+ NSOpenGLPFAColorSize, 24,
+ NSOpenGLPFAAccelerated,
+ // Opt-in to automatic GPU switching. CGL-only property.
+ kCGLPFASupportsAutomaticGraphicsSwitching,
+ NSOpenGLPFAAllowOfflineRenderers,
+ 0
+ };
+ NSOpenGLPixelFormat *pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
+
+ NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:pixFormat shareContext: nil];
+ return CFBridgingRetain(ctx);
+ }
+}
+
+void gio_setContextView(CFTypeRef ctxRef, CFTypeRef viewRef) {
+ NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
+ NSView *view = (__bridge NSView *)viewRef;
+ [view setWantsBestResolutionOpenGLSurface:YES];
+ [ctx setView:view];
+}
+
+void gio_clearCurrentContext(void) {
+ @autoreleasepool {
+ [NSOpenGLContext clearCurrentContext];
+ }
+}
+
+void gio_updateContext(CFTypeRef ctxRef) {
+ @autoreleasepool {
+ NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
+ [ctx update];
+ }
+}
+
+void gio_makeCurrentContext(CFTypeRef ctxRef) {
+ @autoreleasepool {
+ NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
+ [ctx makeCurrentContext];
+ }
+}
+
+void gio_lockContext(CFTypeRef ctxRef) {
+ @autoreleasepool {
+ NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
+ CGLLockContext([ctx CGLContextObj]);
+ }
+}
+
+void gio_unlockContext(CFTypeRef ctxRef) {
+ @autoreleasepool {
+ NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
+ CGLUnlockContext([ctx CGLContextObj]);
+ }
+}
diff --git a/vendor/gioui.org/app/internal/d3d11/backend_windows.go b/vendor/gioui.org/app/internal/d3d11/backend_windows.go
deleted file mode 100644
index 48ff175..0000000
--- a/vendor/gioui.org/app/internal/d3d11/backend_windows.go
+++ /dev/null
@@ -1,856 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package d3d11
-
-import (
- "errors"
- "fmt"
- "image"
- "math"
- "unsafe"
-
- "gioui.org/gpu/backend"
- gunsafe "gioui.org/internal/unsafe"
- "golang.org/x/sys/windows"
-)
-
-const debug = false
-
-type Device struct {
- dev *_ID3D11Device
- ctx *_ID3D11DeviceContext
- featLvl uint32
- floatFormat uint32
- depthStates map[depthState]*_ID3D11DepthStencilState
- blendStates map[blendState]*_ID3D11BlendState
-}
-
-type Backend struct {
- // Temporary storage to avoid garbage.
- clearColor [4]float32
- viewport _D3D11_VIEWPORT
- depthState depthState
- blendState blendState
- prog *Program
-
- dev *Device
- caps backend.Caps
-
- // fbo is the currently bound fbo.
- fbo *Framebuffer
-}
-
-type blendState struct {
- enable bool
- sfactor backend.BlendFactor
- dfactor backend.BlendFactor
-}
-
-type depthState struct {
- enable bool
- mask bool
- fn backend.DepthFunc
-}
-
-type Texture struct {
- backend *Backend
- format uint32
- bindings backend.BufferBinding
- tex *_ID3D11Texture2D
- sampler *_ID3D11SamplerState
- resView *_ID3D11ShaderResourceView
- width int
- height int
-}
-
-type Program struct {
- backend *Backend
-
- vert struct {
- shader *_ID3D11VertexShader
- uniforms *Buffer
- }
- frag struct {
- shader *_ID3D11PixelShader
- uniforms *Buffer
- }
-}
-
-type Framebuffer struct {
- dev *Device
- format uint32
- resource *_ID3D11Resource
- renderTarget *_ID3D11RenderTargetView
- depthView *_ID3D11DepthStencilView
- foreign bool
-}
-
-type Buffer struct {
- backend *Backend
- bind uint32
- buf *_ID3D11Buffer
- immutable bool
-}
-
-type InputLayout struct {
- dev *Device
- layout *_ID3D11InputLayout
-}
-
-type SwapChain struct {
- swchain *_IDXGISwapChain
- fbo *Framebuffer
-}
-
-func NewDevice() (*Device, error) {
- var flags uint32
- if debug {
- flags |= _D3D11_CREATE_DEVICE_DEBUG
- }
- d3ddev, d3dctx, featLvl, err := _D3D11CreateDevice(
- _D3D_DRIVER_TYPE_HARDWARE,
- flags,
- )
- if err != nil {
- return nil, fmt.Errorf("NewContext: %v", err)
- }
- dev := &Device{dev: d3ddev, ctx: d3dctx, featLvl: featLvl}
- if featLvl < _D3D_FEATURE_LEVEL_9_1 {
- _IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release)
- _IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release)
- return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
- }
- floatFormat, ok := detectFloatFormat(d3ddev)
- if !ok {
- _IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release)
- _IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release)
- return nil, fmt.Errorf("d3d11: no available floating point formats")
- }
- dev.floatFormat = floatFormat
- dev.depthStates = make(map[depthState]*_ID3D11DepthStencilState)
- dev.blendStates = make(map[blendState]*_ID3D11BlendState)
- return dev, nil
-}
-
-func detectFloatFormat(dev *_ID3D11Device) (uint32, bool) {
- formats := []uint32{
- _DXGI_FORMAT_R16_FLOAT,
- _DXGI_FORMAT_R32_FLOAT,
- _DXGI_FORMAT_R16G16_FLOAT,
- _DXGI_FORMAT_R32G32_FLOAT,
- // These last two are really wasteful, but c'est la vie.
- _DXGI_FORMAT_R16G16B16A16_FLOAT,
- _DXGI_FORMAT_R32G32B32A32_FLOAT,
- }
- for _, format := range formats {
- need := uint32(_D3D11_FORMAT_SUPPORT_TEXTURE2D | _D3D11_FORMAT_SUPPORT_RENDER_TARGET)
- if support, _ := dev.CheckFormatSupport(format); support&need == need {
- return format, true
- }
- }
- return 0, false
-}
-
-func (d *Device) CreateSwapChain(hwnd windows.Handle) (*SwapChain, error) {
- dxgiDev, err := _IUnknownQueryInterface(unsafe.Pointer(d.dev), d.dev.vtbl.QueryInterface, &_IID_IDXGIDevice)
- if err != nil {
- return nil, fmt.Errorf("NewContext: %v", err)
- }
- adapter, err := (*_IDXGIDevice)(unsafe.Pointer(dxgiDev)).GetAdapter()
- _IUnknownRelease(unsafe.Pointer(dxgiDev), dxgiDev.vtbl.Release)
- if err != nil {
- return nil, fmt.Errorf("NewContext: %v", err)
- }
- dxgiFactory, err := (*_IDXGIObject)(unsafe.Pointer(adapter)).GetParent(&_IID_IDXGIFactory)
- _IUnknownRelease(unsafe.Pointer(adapter), adapter.vtbl.Release)
- if err != nil {
- return nil, fmt.Errorf("NewContext: %v", err)
- }
- d3dswchain, err := (*_IDXGIFactory)(unsafe.Pointer(dxgiFactory)).CreateSwapChain(
- (*_IUnknown)(unsafe.Pointer(d.dev)),
- &_DXGI_SWAP_CHAIN_DESC{
- BufferDesc: _DXGI_MODE_DESC{
- Format: _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
- },
- SampleDesc: _DXGI_SAMPLE_DESC{
- Count: 1,
- },
- BufferUsage: _DXGI_USAGE_RENDER_TARGET_OUTPUT,
- BufferCount: 1,
- OutputWindow: hwnd,
- Windowed: 1,
- SwapEffect: _DXGI_SWAP_EFFECT_DISCARD,
- },
- )
- _IUnknownRelease(unsafe.Pointer(dxgiFactory), dxgiFactory.vtbl.Release)
- if err != nil {
- return nil, fmt.Errorf("NewContext: %v", err)
- }
- return &SwapChain{swchain: d3dswchain, fbo: &Framebuffer{}}, nil
-}
-
-func (s *SwapChain) Framebuffer(d *Device) (*Framebuffer, error) {
- if s.fbo.renderTarget != nil {
- return s.fbo, nil
- }
- desc, err := s.swchain.GetDesc()
- if err != nil {
- return nil, err
- }
- backBuffer, err := s.swchain.GetBuffer(0, &_IID_ID3D11Texture2D)
- if err != nil {
- return nil, err
- }
- texture := (*_ID3D11Resource)(unsafe.Pointer(backBuffer))
- renderTarget, err := d.dev.CreateRenderTargetView(texture)
- _IUnknownRelease(unsafe.Pointer(backBuffer), backBuffer.vtbl.Release)
- if err != nil {
- return nil, err
- }
- depthView, err := createDepthView(d.dev, int(desc.BufferDesc.Width), int(desc.BufferDesc.Height), 24)
- if err != nil {
- _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release)
- return nil, err
- }
- s.fbo.renderTarget = renderTarget
- s.fbo.depthView = depthView
- s.fbo.dev = d
- return s.fbo, nil
-}
-
-func (d *Device) Release() {
- _IUnknownRelease(unsafe.Pointer(d.ctx), d.ctx.vtbl.Release)
- _IUnknownRelease(unsafe.Pointer(d.dev), d.dev.vtbl.Release)
- d.ctx = nil
- d.dev = nil
- for _, state := range d.depthStates {
- _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release)
- }
- d.depthStates = nil
- for _, state := range d.blendStates {
- _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release)
- }
- d.blendStates = nil
-}
-
-func (s *SwapChain) Resize() error {
- if s.fbo.renderTarget != nil {
- s.fbo.Release()
- }
- return s.swchain.ResizeBuffers(0, 0, 0, _DXGI_FORMAT_UNKNOWN, 0)
-}
-
-func (s *SwapChain) Release() {
- _IUnknownRelease(unsafe.Pointer(s.swchain), s.swchain.vtbl.Release)
-}
-
-func (s *SwapChain) Present() error {
- return s.swchain.Present(1, 0)
-}
-
-func NewBackend(d *Device) (*Backend, error) {
- caps := backend.Caps{
- MaxTextureSize: 2048, // 9.1 maximum
- }
- switch {
- case d.featLvl >= _D3D_FEATURE_LEVEL_11_0:
- caps.MaxTextureSize = 16384
- case d.featLvl >= _D3D_FEATURE_LEVEL_9_3:
- caps.MaxTextureSize = 4096
- }
- b := &Backend{dev: d, caps: caps}
- // Disable backface culling to match OpenGL.
- state, err := b.dev.dev.CreateRasterizerState(&_D3D11_RASTERIZER_DESC{
- CullMode: _D3D11_CULL_NONE,
- FillMode: _D3D11_FILL_SOLID,
- DepthClipEnable: 1,
- })
- // Enable depth mask to match OpenGL.
- b.depthState.mask = true
- if err != nil {
- return nil, err
- }
- b.dev.ctx.RSSetState(state)
- _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release)
- return b, nil
-}
-
-func (b *Backend) BeginFrame() {
-}
-
-func (b *Backend) EndFrame() {
-}
-
-func (b *Backend) Caps() backend.Caps {
- return b.caps
-}
-
-func (b *Backend) NewTimer() backend.Timer {
- panic("timers not supported")
-}
-
-func (b *Backend) IsTimeContinuous() bool {
- panic("timers not supported")
-}
-
-func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, minFilter, magFilter backend.TextureFilter, bindings backend.BufferBinding) (backend.Texture, error) {
- var d3dfmt uint32
- switch format {
- case backend.TextureFormatFloat:
- d3dfmt = b.dev.floatFormat
- case backend.TextureFormatSRGB:
- d3dfmt = _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
- default:
- return nil, fmt.Errorf("unsupported texture format %d", format)
- }
- tex, err := b.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
- Width: uint32(width),
- Height: uint32(height),
- MipLevels: 1,
- ArraySize: 1,
- Format: d3dfmt,
- SampleDesc: _DXGI_SAMPLE_DESC{
- Count: 1,
- Quality: 0,
- },
- BindFlags: convBufferBinding(bindings),
- })
- if err != nil {
- return nil, err
- }
- var (
- sampler *_ID3D11SamplerState
- resView *_ID3D11ShaderResourceView
- )
- if bindings&backend.BufferBindingTexture != 0 {
- var filter uint32
- switch {
- case minFilter == backend.FilterNearest && magFilter == backend.FilterNearest:
- filter = _D3D11_FILTER_MIN_MAG_MIP_POINT
- case minFilter == backend.FilterLinear && magFilter == backend.FilterLinear:
- filter = _D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT
- default:
- _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release)
- return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
- }
- var err error
- sampler, err = b.dev.dev.CreateSamplerState(&_D3D11_SAMPLER_DESC{
- Filter: filter,
- AddressU: _D3D11_TEXTURE_ADDRESS_CLAMP,
- AddressV: _D3D11_TEXTURE_ADDRESS_CLAMP,
- AddressW: _D3D11_TEXTURE_ADDRESS_CLAMP,
- MaxAnisotropy: 1,
- MinLOD: -math.MaxFloat32,
- MaxLOD: math.MaxFloat32,
- })
- if err != nil {
- _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release)
- return nil, err
- }
- resView, err = b.dev.dev.CreateShaderResourceViewTEX2D(
- (*_ID3D11Resource)(unsafe.Pointer(tex)),
- &_D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D{
- _D3D11_SHADER_RESOURCE_VIEW_DESC: _D3D11_SHADER_RESOURCE_VIEW_DESC{
- Format: d3dfmt,
- ViewDimension: _D3D11_SRV_DIMENSION_TEXTURE2D,
- },
- Texture2D: _D3D11_TEX2D_SRV{
- MostDetailedMip: 0,
- MipLevels: ^uint32(0),
- },
- },
- )
- if err != nil {
- _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release)
- _IUnknownRelease(unsafe.Pointer(sampler), sampler.vtbl.Release)
- return nil, err
- }
- }
- return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, bindings: bindings, width: width, height: height}, nil
-}
-
-func (b *Backend) CurrentFramebuffer() backend.Framebuffer {
- renderTarget := b.dev.ctx.OMGetRenderTargets()
- if renderTarget != nil {
- // Assume someone else is holding on to it.
- _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release)
- }
- if renderTarget == b.fbo.renderTarget {
- return b.fbo
- }
- return &Framebuffer{dev: b.dev, renderTarget: renderTarget, foreign: true}
-}
-
-func (b *Backend) NewFramebuffer(tex backend.Texture, depthBits int) (backend.Framebuffer, error) {
- d3dtex := tex.(*Texture)
- if d3dtex.bindings&backend.BufferBindingFramebuffer == 0 {
- return nil, errors.New("the texture was created without BufferBindingFramebuffer binding")
- }
- resource := (*_ID3D11Resource)(unsafe.Pointer(d3dtex.tex))
- renderTarget, err := b.dev.dev.CreateRenderTargetView(resource)
- if err != nil {
- return nil, err
- }
- fbo := &Framebuffer{dev: b.dev, format: d3dtex.format, resource: resource, renderTarget: renderTarget}
- if depthBits > 0 {
- depthView, err := createDepthView(b.dev.dev, d3dtex.width, d3dtex.height, depthBits)
- if err != nil {
- _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release)
- return nil, err
- }
- fbo.depthView = depthView
- }
- return fbo, nil
-}
-
-func createDepthView(d *_ID3D11Device, width, height, depthBits int) (*_ID3D11DepthStencilView, error) {
- depthTex, err := d.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
- Width: uint32(width),
- Height: uint32(height),
- MipLevels: 1,
- ArraySize: 1,
- Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
- SampleDesc: _DXGI_SAMPLE_DESC{
- Count: 1,
- Quality: 0,
- },
- BindFlags: _D3D11_BIND_DEPTH_STENCIL,
- })
- if err != nil {
- return nil, err
- }
- depthView, err := d.CreateDepthStencilViewTEX2D(
- (*_ID3D11Resource)(unsafe.Pointer(depthTex)),
- &_D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D{
- Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
- ViewDimension: _D3D11_DSV_DIMENSION_TEXTURE2D,
- },
- )
- _IUnknownRelease(unsafe.Pointer(depthTex), depthTex.vtbl.Release)
- return depthView, err
-}
-
-func (b *Backend) NewInputLayout(vertexShader backend.ShaderSources, layout []backend.InputDesc) (backend.InputLayout, error) {
- if len(vertexShader.Inputs) != len(layout) {
- return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
- }
- descs := make([]_D3D11_INPUT_ELEMENT_DESC, len(layout))
- for i, l := range layout {
- inp := vertexShader.Inputs[i]
- cname, err := windows.BytePtrFromString(inp.Semantic)
- if err != nil {
- return nil, err
- }
- var format uint32
- switch l.Type {
- case backend.DataTypeFloat:
- switch l.Size {
- case 1:
- format = _DXGI_FORMAT_R32_FLOAT
- case 2:
- format = _DXGI_FORMAT_R32G32_FLOAT
- case 3:
- format = _DXGI_FORMAT_R32G32B32_FLOAT
- case 4:
- format = _DXGI_FORMAT_R32G32B32A32_FLOAT
- default:
- panic("unsupported float data size")
- }
- case backend.DataTypeShort:
- switch l.Size {
- case 1:
- format = _DXGI_FORMAT_R16_SINT
- case 2:
- format = _DXGI_FORMAT_R16G16_SINT
- default:
- panic("unsupported float data size")
- }
- default:
- panic("unsupported data type")
- }
- descs[i] = _D3D11_INPUT_ELEMENT_DESC{
- SemanticName: cname,
- SemanticIndex: uint32(inp.SemanticIndex),
- Format: format,
- AlignedByteOffset: uint32(l.Offset),
- }
- }
- l, err := b.dev.dev.CreateInputLayout(descs, vertexShader.HLSL)
- if err != nil {
- return nil, err
- }
- return &InputLayout{dev: b.dev, layout: l}, nil
-}
-
-func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer, error) {
- if typ&backend.BufferBindingUniforms != 0 {
- if typ != backend.BufferBindingUniforms {
- return nil, errors.New("uniform buffers cannot have other bindings")
- }
- if size%16 != 0 {
- return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size)
- }
- }
- bind := convBufferBinding(typ)
- buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{
- ByteWidth: uint32(size),
- BindFlags: bind,
- }, nil)
- if err != nil {
- return nil, err
- }
- return &Buffer{backend: b, buf: buf, bind: bind}, nil
-}
-
-func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (backend.Buffer, error) {
- if typ&backend.BufferBindingUniforms != 0 {
- if typ != backend.BufferBindingUniforms {
- return nil, errors.New("uniform buffers cannot have other bindings")
- }
- if len(data)%16 != 0 {
- return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", len(data))
- }
- }
- bind := convBufferBinding(typ)
- buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{
- ByteWidth: uint32(len(data)),
- Usage: _D3D11_USAGE_IMMUTABLE,
- BindFlags: bind,
- }, data)
- if err != nil {
- return nil, err
- }
- return &Buffer{backend: b, buf: buf, bind: bind, immutable: true}, nil
-}
-
-func (b *Backend) NewProgram(vertexShader, fragmentShader backend.ShaderSources) (backend.Program, error) {
- vs, err := b.dev.dev.CreateVertexShader(vertexShader.HLSL)
- if err != nil {
- return nil, err
- }
- ps, err := b.dev.dev.CreatePixelShader(fragmentShader.HLSL)
- if err != nil {
- return nil, err
- }
- p := &Program{backend: b}
- p.vert.shader = vs
- p.frag.shader = ps
- return p, nil
-}
-
-func (b *Backend) Clear(colr, colg, colb, cola float32) {
- b.clearColor = [4]float32{colr, colg, colb, cola}
- b.dev.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor)
-}
-
-func (b *Backend) ClearDepth(depth float32) {
- if b.fbo.depthView != nil {
- b.dev.ctx.ClearDepthStencilView(b.fbo.depthView, _D3D11_CLEAR_DEPTH|_D3D11_CLEAR_STENCIL, depth, 0)
- }
-}
-
-func (b *Backend) Viewport(x, y, width, height int) {
- b.viewport = _D3D11_VIEWPORT{
- TopLeftX: float32(x),
- TopLeftY: float32(y),
- Width: float32(width),
- Height: float32(height),
- MinDepth: 0.0,
- MaxDepth: 1.0,
- }
- b.dev.ctx.RSSetViewports(&b.viewport)
-}
-
-func (b *Backend) DrawArrays(mode backend.DrawMode, off, count int) {
- b.prepareDraw(mode)
- b.dev.ctx.Draw(uint32(count), uint32(off))
-}
-
-func (b *Backend) DrawElements(mode backend.DrawMode, off, count int) {
- b.prepareDraw(mode)
- b.dev.ctx.DrawIndexed(uint32(count), uint32(off), 0)
-}
-
-func (b *Backend) prepareDraw(mode backend.DrawMode) {
- if p := b.prog; p != nil {
- b.dev.ctx.VSSetShader(p.vert.shader)
- b.dev.ctx.PSSetShader(p.frag.shader)
- if buf := p.vert.uniforms; buf != nil {
- b.dev.ctx.VSSetConstantBuffers(buf.buf)
- }
- if buf := p.frag.uniforms; buf != nil {
- b.dev.ctx.PSSetConstantBuffers(buf.buf)
- }
- }
- var topology uint32
- switch mode {
- case backend.DrawModeTriangles:
- topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
- case backend.DrawModeTriangleStrip:
- topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
- default:
- panic("unsupported draw mode")
- }
- b.dev.ctx.IASetPrimitiveTopology(topology)
-
- depthState, ok := b.dev.depthStates[b.depthState]
- if !ok {
- var desc _D3D11_DEPTH_STENCIL_DESC
- if b.depthState.enable {
- desc.DepthEnable = 1
- }
- if b.depthState.mask {
- desc.DepthWriteMask = _D3D11_DEPTH_WRITE_MASK_ALL
- }
- switch b.depthState.fn {
- case backend.DepthFuncGreater:
- desc.DepthFunc = _D3D11_COMPARISON_GREATER
- case backend.DepthFuncGreaterEqual:
- desc.DepthFunc = _D3D11_COMPARISON_GREATER_EQUAL
- default:
- panic("unsupported depth func")
- }
- var err error
- depthState, err = b.dev.dev.CreateDepthStencilState(&desc)
- if err != nil {
- panic(err)
- }
- b.dev.depthStates[b.depthState] = depthState
- }
- b.dev.ctx.OMSetDepthStencilState(depthState, 0)
-
- blendState, ok := b.dev.blendStates[b.blendState]
- if !ok {
- var desc _D3D11_BLEND_DESC
- t0 := &desc.RenderTarget[0]
- t0.RenderTargetWriteMask = _D3D11_COLOR_WRITE_ENABLE_ALL
- t0.BlendOp = _D3D11_BLEND_OP_ADD
- t0.BlendOpAlpha = _D3D11_BLEND_OP_ADD
- if b.blendState.enable {
- t0.BlendEnable = 1
- }
- scol, salpha := toBlendFactor(b.blendState.sfactor)
- dcol, dalpha := toBlendFactor(b.blendState.dfactor)
- t0.SrcBlend = scol
- t0.SrcBlendAlpha = salpha
- t0.DestBlend = dcol
- t0.DestBlendAlpha = dalpha
- var err error
- blendState, err = b.dev.dev.CreateBlendState(&desc)
- if err != nil {
- panic(err)
- }
- b.dev.blendStates[b.blendState] = blendState
- }
- b.dev.ctx.OMSetBlendState(blendState, nil, 0xffffffff)
-}
-
-func (b *Backend) DepthFunc(f backend.DepthFunc) {
- b.depthState.fn = f
-}
-
-func (b *Backend) SetBlend(enable bool) {
- b.blendState.enable = enable
-}
-
-func (b *Backend) SetDepthTest(enable bool) {
- b.depthState.enable = enable
-}
-
-func (b *Backend) DepthMask(mask bool) {
- b.depthState.mask = mask
-}
-
-func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) {
- b.blendState.sfactor = sfactor
- b.blendState.dfactor = dfactor
-}
-
-func (t *Texture) Upload(img *image.RGBA) {
- b := img.Bounds()
- w := b.Dx()
- if img.Stride != w*4 {
- panic("unsupported stride")
- }
- start := (b.Min.X + b.Min.Y*w) * 4
- end := (b.Max.X + (b.Max.Y-1)*w) * 4
- pixels := img.Pix[start:end]
- res := (*_ID3D11Resource)(unsafe.Pointer(t.tex))
- t.backend.dev.ctx.UpdateSubresource(res, uint32(img.Stride), uint32(len(pixels)), pixels)
-}
-
-func (t *Texture) Release() {
- _IUnknownRelease(unsafe.Pointer(t.tex), t.tex.vtbl.Release)
- t.tex = nil
- if t.sampler != nil {
- _IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.vtbl.Release)
- t.sampler = nil
- }
- if t.resView != nil {
- _IUnknownRelease(unsafe.Pointer(t.resView), t.resView.vtbl.Release)
- t.resView = nil
- }
-}
-
-func (b *Backend) BindTexture(unit int, tex backend.Texture) {
- t := tex.(*Texture)
- b.dev.ctx.PSSetSamplers(uint32(unit), t.sampler)
- b.dev.ctx.PSSetShaderResources(uint32(unit), t.resView)
-}
-
-func (b *Backend) BindProgram(prog backend.Program) {
- b.prog = prog.(*Program)
-}
-
-func (p *Program) Release() {
- _IUnknownRelease(unsafe.Pointer(p.vert.shader), p.vert.shader.vtbl.Release)
- _IUnknownRelease(unsafe.Pointer(p.frag.shader), p.frag.shader.vtbl.Release)
- p.vert.shader = nil
- p.frag.shader = nil
-}
-
-func (p *Program) SetVertexUniforms(buf backend.Buffer) {
- p.vert.uniforms = buf.(*Buffer)
-}
-
-func (p *Program) SetFragmentUniforms(buf backend.Buffer) {
- p.frag.uniforms = buf.(*Buffer)
-}
-
-func (b *Backend) BindVertexBuffer(buf backend.Buffer, stride, offset int) {
- b.dev.ctx.IASetVertexBuffers(buf.(*Buffer).buf, uint32(stride), uint32(offset))
-}
-
-func (b *Backend) BindIndexBuffer(buf backend.Buffer) {
- b.dev.ctx.IASetIndexBuffer(buf.(*Buffer).buf, _DXGI_FORMAT_R16_UINT, 0)
-}
-
-func (b *Buffer) Upload(data []byte) {
- b.backend.dev.ctx.UpdateSubresource((*_ID3D11Resource)(unsafe.Pointer(b.buf)), 0, 0, data)
-}
-
-func (b *Buffer) Release() {
- _IUnknownRelease(unsafe.Pointer(b.buf), b.buf.vtbl.Release)
- b.buf = nil
-}
-
-func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
- if f.resource == nil {
- return errors.New("framebuffer does not support ReadPixels")
- }
- w, h := src.Dx(), src.Dy()
- tex, err := f.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
- Width: uint32(w),
- Height: uint32(h),
- MipLevels: 1,
- ArraySize: 1,
- Format: f.format,
- SampleDesc: _DXGI_SAMPLE_DESC{
- Count: 1,
- Quality: 0,
- },
- Usage: _D3D11_USAGE_STAGING,
- CPUAccessFlags: _D3D11_CPU_ACCESS_READ,
- })
- if err != nil {
- return fmt.Errorf("ReadPixels: %v", err)
- }
- defer _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release)
- res := (*_ID3D11Resource)(unsafe.Pointer(tex))
- f.dev.ctx.CopySubresourceRegion(
- res,
- 0, // Destination subresource.
- 0, 0, 0, // Destination coordinates (x, y, z).
- f.resource,
- 0, // Source subresource.
- &_D3D11_BOX{
- left: uint32(src.Min.X),
- top: uint32(src.Min.Y),
- right: uint32(src.Max.X),
- bottom: uint32(src.Max.Y),
- front: 0,
- back: 1,
- },
- )
- resMap, err := f.dev.ctx.Map(res, 0, _D3D11_MAP_READ, 0)
- if err != nil {
- return fmt.Errorf("ReadPixels: %v", err)
- }
- defer f.dev.ctx.Unmap(res, 0)
- srcPitch := w * 4
- dstPitch := int(resMap.RowPitch)
- mapSize := dstPitch * h
- data := gunsafe.SliceOf(resMap.pData)[:mapSize:mapSize]
- width := w * 4
- for r := 0; r < h; r++ {
- pixels := pixels[r*srcPitch:]
- copy(pixels[:width], data[r*dstPitch:])
- }
- return nil
-}
-
-func (b *Backend) BindFramebuffer(fbo backend.Framebuffer) {
- b.fbo = fbo.(*Framebuffer)
- b.dev.ctx.OMSetRenderTargets(b.fbo.renderTarget, b.fbo.depthView)
-}
-
-func (f *Framebuffer) Invalidate() {
-}
-
-func (f *Framebuffer) Release() {
- if f.foreign {
- panic("cannot release Framebuffer from CurrentFramebuffer")
- }
- if f.renderTarget != nil {
- _IUnknownRelease(unsafe.Pointer(f.renderTarget), f.renderTarget.vtbl.Release)
- f.renderTarget = nil
- }
- if f.depthView != nil {
- _IUnknownRelease(unsafe.Pointer(f.depthView), f.depthView.vtbl.Release)
- f.depthView = nil
- }
-}
-
-func (b *Backend) BindInputLayout(layout backend.InputLayout) {
- b.dev.ctx.IASetInputLayout(layout.(*InputLayout).layout)
-}
-
-func (l *InputLayout) Release() {
- _IUnknownRelease(unsafe.Pointer(l.layout), l.layout.vtbl.Release)
- l.layout = nil
-}
-
-func convBufferBinding(typ backend.BufferBinding) uint32 {
- var bindings uint32
- if typ&backend.BufferBindingVertices != 0 {
- bindings |= _D3D11_BIND_VERTEX_BUFFER
- }
- if typ&backend.BufferBindingIndices != 0 {
- bindings |= _D3D11_BIND_INDEX_BUFFER
- }
- if typ&backend.BufferBindingUniforms != 0 {
- bindings |= _D3D11_BIND_CONSTANT_BUFFER
- }
- if typ&backend.BufferBindingTexture != 0 {
- bindings |= _D3D11_BIND_SHADER_RESOURCE
- }
- if typ&backend.BufferBindingFramebuffer != 0 {
- bindings |= _D3D11_BIND_RENDER_TARGET
- }
- return bindings
-}
-
-func toBlendFactor(f backend.BlendFactor) (uint32, uint32) {
- switch f {
- case backend.BlendFactorOne:
- return _D3D11_BLEND_ONE, _D3D11_BLEND_ONE
- case backend.BlendFactorOneMinusSrcAlpha:
- return _D3D11_BLEND_INV_SRC_ALPHA, _D3D11_BLEND_INV_SRC_ALPHA
- case backend.BlendFactorZero:
- return _D3D11_BLEND_ZERO, _D3D11_BLEND_ZERO
- case backend.BlendFactorDstColor:
- return _D3D11_BLEND_DEST_COLOR, _D3D11_BLEND_DEST_ALPHA
- default:
- panic("unsupported blend source factor")
- }
-}
diff --git a/vendor/gioui.org/app/internal/glimpl/gl.go b/vendor/gioui.org/app/internal/glimpl/gl.go
deleted file mode 100644
index 5b23e4e..0000000
--- a/vendor/gioui.org/app/internal/glimpl/gl.go
+++ /dev/null
@@ -1,541 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build darwin linux freebsd openbsd
-
-package glimpl
-
-import (
- "runtime"
- "strings"
- "unsafe"
-
- "gioui.org/gpu/gl"
-)
-
-/*
-#cgo CFLAGS: -Werror
-#cgo linux,!android pkg-config: glesv2
-#cgo linux freebsd LDFLAGS: -ldl
-#cgo freebsd openbsd android LDFLAGS: -lGLESv2
-#cgo freebsd CFLAGS: -I/usr/local/include
-#cgo freebsd LDFLAGS: -L/usr/local/lib
-#cgo openbsd CFLAGS: -I/usr/X11R6/include
-#cgo openbsd LDFLAGS: -L/usr/X11R6/lib
-#cgo darwin,!ios CFLAGS: -DGL_SILENCE_DEPRECATION
-#cgo darwin,!ios LDFLAGS: -framework OpenGL
-#cgo darwin,ios CFLAGS: -DGLES_SILENCE_DEPRECATION
-#cgo darwin,ios LDFLAGS: -framework OpenGLES
-
-#include <stdlib.h>
-
-#ifdef __APPLE__
- #include "TargetConditionals.h"
- #if TARGET_OS_IPHONE
- #include <OpenGLES/ES3/gl.h>
- #else
- #include <OpenGL/gl3.h>
- #endif
-#else
-#define __USE_GNU
-#include <dlfcn.h>
-#include <GLES2/gl2.h>
-#include <GLES3/gl3.h>
-#endif
-
-static void (*_glBindBufferBase)(GLenum target, GLuint index, GLuint buffer);
-static GLuint (*_glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName);
-static void (*_glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
-static void (*_glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments);
-
-static void (*_glBeginQuery)(GLenum target, GLuint id);
-static void (*_glDeleteQueries)(GLsizei n, const GLuint *ids);
-static void (*_glEndQuery)(GLenum target);
-static void (*_glGenQueries)(GLsizei n, GLuint *ids);
-static void (*_glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params);
-static const GLubyte* (*_glGetStringi)(GLenum name, GLuint index);
-
-// The pointer-free version of glVertexAttribPointer, to avoid the Cgo pointer checks.
-__attribute__ ((visibility ("hidden"))) void gio_glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uintptr_t offset) {
- glVertexAttribPointer(index, size, type, normalized, stride, (const GLvoid *)offset);
-}
-
-// The pointer-free version of glDrawElements, to avoid the Cgo pointer checks.
-__attribute__ ((visibility ("hidden"))) void gio_glDrawElements(GLenum mode, GLsizei count, GLenum type, const uintptr_t offset) {
- glDrawElements(mode, count, type, (const GLvoid *)offset);
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glBindBufferBase(GLenum target, GLuint index, GLuint buffer) {
- _glBindBufferBase(target, index, buffer);
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) {
- _glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
-}
-
-__attribute__ ((visibility ("hidden"))) GLuint gio_glGetUniformBlockIndex(GLuint program, const GLchar *uniformBlockName) {
- return _glGetUniformBlockIndex(program, uniformBlockName);
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glInvalidateFramebuffer(GLenum target, GLenum attachment) {
- // gl.Framebuffer invalidation is just a hint and can safely be ignored.
- if (_glInvalidateFramebuffer != NULL) {
- _glInvalidateFramebuffer(target, 1, &attachment);
- }
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glBeginQuery(GLenum target, GLenum attachment) {
- _glBeginQuery(target, attachment);
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glDeleteQueries(GLsizei n, const GLuint *ids) {
- _glDeleteQueries(n, ids);
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glEndQuery(GLenum target) {
- _glEndQuery(target);
-}
-
-__attribute__ ((visibility ("hidden"))) const GLubyte* gio_glGetStringi(GLenum name, GLuint index) {
- if (_glGetStringi == NULL) {
- return NULL;
- }
- return _glGetStringi(name, index);
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glGenQueries(GLsizei n, GLuint *ids) {
- _glGenQueries(n, ids);
-}
-
-__attribute__ ((visibility ("hidden"))) void gio_glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) {
- _glGetQueryObjectuiv(id, pname, params);
-}
-
-__attribute__((constructor)) static void gio_loadGLFunctions() {
-#ifdef __APPLE__
- #if TARGET_OS_IPHONE
- _glInvalidateFramebuffer = glInvalidateFramebuffer;
- _glBeginQuery = glBeginQuery;
- _glDeleteQueries = glDeleteQueries;
- _glEndQuery = glEndQuery;
- _glGenQueries = glGenQueries;
- _glGetQueryObjectuiv = glGetQueryObjectuiv;
- #endif
- _glBindBufferBase = glBindBufferBase;
- _glGetUniformBlockIndex = glGetUniformBlockIndex;
- _glUniformBlockBinding = glUniformBlockBinding;
- _glGetStringi = glGetStringi;
-#else
- // Load libGLESv3 if available.
- dlopen("libGLESv3.so", RTLD_NOW | RTLD_GLOBAL);
- _glBindBufferBase = dlsym(RTLD_DEFAULT, "glBindBufferBase");
- _glGetUniformBlockIndex = dlsym(RTLD_DEFAULT, "glGetUniformBlockIndex");
- _glUniformBlockBinding = dlsym(RTLD_DEFAULT, "glUniformBlockBinding");
- _glInvalidateFramebuffer = dlsym(RTLD_DEFAULT, "glInvalidateFramebuffer");
- _glGetStringi = dlsym(RTLD_DEFAULT, "glGetStringi");
- // Fall back to EXT_invalidate_framebuffer if available.
- if (_glInvalidateFramebuffer == NULL) {
- _glInvalidateFramebuffer = dlsym(RTLD_DEFAULT, "glDiscardFramebufferEXT");
- }
-
- _glBeginQuery = dlsym(RTLD_DEFAULT, "glBeginQuery");
- if (_glBeginQuery == NULL)
- _glBeginQuery = dlsym(RTLD_DEFAULT, "glBeginQueryEXT");
- _glDeleteQueries = dlsym(RTLD_DEFAULT, "glDeleteQueries");
- if (_glDeleteQueries == NULL)
- _glDeleteQueries = dlsym(RTLD_DEFAULT, "glDeleteQueriesEXT");
- _glEndQuery = dlsym(RTLD_DEFAULT, "glEndQuery");
- if (_glEndQuery == NULL)
- _glEndQuery = dlsym(RTLD_DEFAULT, "glEndQueryEXT");
- _glGenQueries = dlsym(RTLD_DEFAULT, "glGenQueries");
- if (_glGenQueries == NULL)
- _glGenQueries = dlsym(RTLD_DEFAULT, "glGenQueriesEXT");
- _glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuiv");
- if (_glGetQueryObjectuiv == NULL)
- _glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuivEXT");
-#endif
-}
-*/
-import "C"
-
-type Functions struct {
- // gl.Query caches.
- uints [100]C.GLuint
- ints [100]C.GLint
-}
-
-func (f *Functions) ActiveTexture(texture gl.Enum) {
- C.glActiveTexture(C.GLenum(texture))
-}
-
-func (f *Functions) AttachShader(p gl.Program, s gl.Shader) {
- C.glAttachShader(C.GLuint(p.V), C.GLuint(s.V))
-}
-
-func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) {
- C.gio_glBeginQuery(C.GLenum(target), C.GLenum(query.V))
-}
-
-func (f *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) {
- cname := C.CString(name)
- defer C.free(unsafe.Pointer(cname))
- C.glBindAttribLocation(C.GLuint(p.V), C.GLuint(a), cname)
-}
-
-func (f *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) {
- C.gio_glBindBufferBase(C.GLenum(target), C.GLuint(index), C.GLuint(b.V))
-}
-
-func (f *Functions) BindBuffer(target gl.Enum, b gl.Buffer) {
- C.glBindBuffer(C.GLenum(target), C.GLuint(b.V))
-}
-
-func (f *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) {
- C.glBindFramebuffer(C.GLenum(target), C.GLuint(fb.V))
-}
-
-func (f *Functions) BindRenderbuffer(target gl.Enum, fb gl.Renderbuffer) {
- C.glBindRenderbuffer(C.GLenum(target), C.GLuint(fb.V))
-}
-
-func (f *Functions) BindTexture(target gl.Enum, t gl.Texture) {
- C.glBindTexture(C.GLenum(target), C.GLuint(t.V))
-}
-
-func (f *Functions) BlendEquation(mode gl.Enum) {
- C.glBlendEquation(C.GLenum(mode))
-}
-
-func (f *Functions) BlendFunc(sfactor, dfactor gl.Enum) {
- C.glBlendFunc(C.GLenum(sfactor), C.GLenum(dfactor))
-}
-
-func (f *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) {
- var p unsafe.Pointer
- if len(src) > 0 {
- p = unsafe.Pointer(&src[0])
- }
- C.glBufferData(C.GLenum(target), C.GLsizeiptr(len(src)), p, C.GLenum(usage))
-}
-
-func (f *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum {
- return gl.Enum(C.glCheckFramebufferStatus(C.GLenum(target)))
-}
-
-func (f *Functions) Clear(mask gl.Enum) {
- C.glClear(C.GLbitfield(mask))
-}
-
-func (f *Functions) ClearColor(red float32, green float32, blue float32, alpha float32) {
- C.glClearColor(C.GLfloat(red), C.GLfloat(green), C.GLfloat(blue), C.GLfloat(alpha))
-}
-
-func (f *Functions) ClearDepthf(d float32) {
- C.glClearDepthf(C.GLfloat(d))
-}
-
-func (f *Functions) CompileShader(s gl.Shader) {
- C.glCompileShader(C.GLuint(s.V))
-}
-
-func (f *Functions) CreateBuffer() gl.Buffer {
- C.glGenBuffers(1, &f.uints[0])
- return gl.Buffer{uint(f.uints[0])}
-}
-
-func (f *Functions) CreateFramebuffer() gl.Framebuffer {
- C.glGenFramebuffers(1, &f.uints[0])
- return gl.Framebuffer{uint(f.uints[0])}
-}
-
-func (f *Functions) CreateProgram() gl.Program {
- return gl.Program{uint(C.glCreateProgram())}
-}
-
-func (f *Functions) CreateQuery() gl.Query {
- C.gio_glGenQueries(1, &f.uints[0])
- return gl.Query{uint(f.uints[0])}
-}
-
-func (f *Functions) CreateRenderbuffer() gl.Renderbuffer {
- C.glGenRenderbuffers(1, &f.uints[0])
- return gl.Renderbuffer{uint(f.uints[0])}
-}
-
-func (f *Functions) CreateShader(ty gl.Enum) gl.Shader {
- return gl.Shader{uint(C.glCreateShader(C.GLenum(ty)))}
-}
-
-func (f *Functions) CreateTexture() gl.Texture {
- C.glGenTextures(1, &f.uints[0])
- return gl.Texture{uint(f.uints[0])}
-}
-
-func (f *Functions) DeleteBuffer(v gl.Buffer) {
- f.uints[0] = C.GLuint(v.V)
- C.glDeleteBuffers(1, &f.uints[0])
-}
-
-func (f *Functions) DeleteFramebuffer(v gl.Framebuffer) {
- f.uints[0] = C.GLuint(v.V)
- C.glDeleteFramebuffers(1, &f.uints[0])
-}
-
-func (f *Functions) DeleteProgram(p gl.Program) {
- C.glDeleteProgram(C.GLuint(p.V))
-}
-
-func (f *Functions) DeleteQuery(query gl.Query) {
- f.uints[0] = C.GLuint(query.V)
- C.gio_glDeleteQueries(1, &f.uints[0])
-}
-
-func (f *Functions) DeleteRenderbuffer(v gl.Renderbuffer) {
- f.uints[0] = C.GLuint(v.V)
- C.glDeleteRenderbuffers(1, &f.uints[0])
-}
-
-func (f *Functions) DeleteShader(s gl.Shader) {
- C.glDeleteShader(C.GLuint(s.V))
-}
-
-func (f *Functions) DeleteTexture(v gl.Texture) {
- f.uints[0] = C.GLuint(v.V)
- C.glDeleteTextures(1, &f.uints[0])
-}
-
-func (f *Functions) DepthFunc(v gl.Enum) {
- C.glDepthFunc(C.GLenum(v))
-}
-
-func (f *Functions) DepthMask(mask bool) {
- m := C.GLboolean(C.GL_FALSE)
- if mask {
- m = C.GLboolean(C.GL_TRUE)
- }
- C.glDepthMask(m)
-}
-
-func (f *Functions) DisableVertexAttribArray(a gl.Attrib) {
- C.glDisableVertexAttribArray(C.GLuint(a))
-}
-
-func (f *Functions) Disable(cap gl.Enum) {
- C.glDisable(C.GLenum(cap))
-}
-
-func (f *Functions) DrawArrays(mode gl.Enum, first int, count int) {
- C.glDrawArrays(C.GLenum(mode), C.GLint(first), C.GLsizei(count))
-}
-
-func (f *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) {
- C.gio_glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(ty), C.uintptr_t(offset))
-}
-
-func (f *Functions) Enable(cap gl.Enum) {
- C.glEnable(C.GLenum(cap))
-}
-
-func (f *Functions) EndQuery(target gl.Enum) {
- C.gio_glEndQuery(C.GLenum(target))
-}
-
-func (f *Functions) EnableVertexAttribArray(a gl.Attrib) {
- C.glEnableVertexAttribArray(C.GLuint(a))
-}
-
-func (f *Functions) Finish() {
- C.glFinish()
-}
-
-func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) {
- C.glFramebufferRenderbuffer(C.GLenum(target), C.GLenum(attachment), C.GLenum(renderbuffertarget), C.GLuint(renderbuffer.V))
-}
-
-func (f *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) {
- C.glFramebufferTexture2D(C.GLenum(target), C.GLenum(attachment), C.GLenum(texTarget), C.GLuint(t.V), C.GLint(level))
-}
-
-func (c *Functions) GetBinding(pname gl.Enum) gl.Object {
- return gl.Object{uint(c.GetInteger(pname))}
-}
-
-func (f *Functions) GetError() gl.Enum {
- return gl.Enum(C.glGetError())
-}
-
-func (f *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int {
- C.glGetRenderbufferParameteriv(C.GLenum(target), C.GLenum(pname), &f.ints[0])
- return int(f.ints[0])
-}
-
-func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int {
- C.glGetFramebufferAttachmentParameteriv(C.GLenum(target), C.GLenum(attachment), C.GLenum(pname), &f.ints[0])
- return int(f.ints[0])
-}
-
-func (f *Functions) GetInteger(pname gl.Enum) int {
- C.glGetIntegerv(C.GLenum(pname), &f.ints[0])
- return int(f.ints[0])
-}
-
-func (f *Functions) GetProgrami(p gl.Program, pname gl.Enum) int {
- C.glGetProgramiv(C.GLuint(p.V), C.GLenum(pname), &f.ints[0])
- return int(f.ints[0])
-}
-
-func (f *Functions) GetProgramInfoLog(p gl.Program) string {
- n := f.GetProgrami(p, gl.INFO_LOG_LENGTH)
- buf := make([]byte, n)
- C.glGetProgramInfoLog(C.GLuint(p.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0])))
- return string(buf)
-}
-
-func (f *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint {
- C.gio_glGetQueryObjectuiv(C.GLuint(query.V), C.GLenum(pname), &f.uints[0])
- return uint(f.uints[0])
-}
-
-func (f *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int {
- C.glGetShaderiv(C.GLuint(s.V), C.GLenum(pname), &f.ints[0])
- return int(f.ints[0])
-}
-
-func (f *Functions) GetShaderInfoLog(s gl.Shader) string {
- n := f.GetShaderi(s, gl.INFO_LOG_LENGTH)
- buf := make([]byte, n)
- C.glGetShaderInfoLog(C.GLuint(s.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0])))
- return string(buf)
-}
-
-func (f *Functions) GetStringi(pname gl.Enum, index int) string {
- str := C.gio_glGetStringi(C.GLenum(pname), C.GLuint(index))
- if str == nil {
- return ""
- }
- return C.GoString((*C.char)(unsafe.Pointer(str)))
-}
-
-func (f *Functions) GetString(pname gl.Enum) string {
- switch {
- case runtime.GOOS == "darwin" && pname == gl.EXTENSIONS:
- // macOS OpenGL 3 core profile doesn't support glGetString(GL_EXTENSIONS).
- // Use glGetStringi(GL_EXTENSIONS, <index>).
- var exts []string
- nexts := f.GetInteger(gl.NUM_EXTENSIONS)
- for i := 0; i < nexts; i++ {
- ext := f.GetStringi(gl.EXTENSIONS, i)
- exts = append(exts, ext)
- }
- return strings.Join(exts, " ")
- default:
- str := C.glGetString(C.GLenum(pname))
- return C.GoString((*C.char)(unsafe.Pointer(str)))
- }
-}
-
-func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint {
- cname := C.CString(name)
- defer C.free(unsafe.Pointer(cname))
- return uint(C.gio_glGetUniformBlockIndex(C.GLuint(p.V), cname))
-}
-
-func (f *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform {
- cname := C.CString(name)
- defer C.free(unsafe.Pointer(cname))
- return gl.Uniform{int(C.glGetUniformLocation(C.GLuint(p.V), cname))}
-}
-
-func (f *Functions) InvalidateFramebuffer(target, attachment gl.Enum) {
- C.gio_glInvalidateFramebuffer(C.GLenum(target), C.GLenum(attachment))
-}
-
-func (f *Functions) LinkProgram(p gl.Program) {
- C.glLinkProgram(C.GLuint(p.V))
-}
-
-func (f *Functions) PixelStorei(pname gl.Enum, param int32) {
- C.glPixelStorei(C.GLenum(pname), C.GLint(param))
-}
-
-func (f *Functions) Scissor(x, y, width, height int32) {
- C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
-}
-
-func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) {
- var p unsafe.Pointer
- if len(data) > 0 {
- p = unsafe.Pointer(&data[0])
- }
- C.glReadPixels(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p)
-}
-
-func (f *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) {
- C.glRenderbufferStorage(C.GLenum(target), C.GLenum(internalformat), C.GLsizei(width), C.GLsizei(height))
-}
-
-func (f *Functions) ShaderSource(s gl.Shader, src string) {
- csrc := C.CString(src)
- defer C.free(unsafe.Pointer(csrc))
- strlen := C.GLint(len(src))
- C.glShaderSource(C.GLuint(s.V), 1, &csrc, &strlen)
-}
-
-func (f *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width int, height int, format gl.Enum, ty gl.Enum, data []byte) {
- var p unsafe.Pointer
- if len(data) > 0 {
- p = unsafe.Pointer(&data[0])
- }
- C.glTexImage2D(C.GLenum(target), C.GLint(level), C.GLint(internalFormat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(ty), p)
-}
-
-func (f *Functions) TexSubImage2D(target gl.Enum, level int, x int, y int, width int, height int, format gl.Enum, ty gl.Enum, data []byte) {
- var p unsafe.Pointer
- if len(data) > 0 {
- p = unsafe.Pointer(&data[0])
- }
- C.glTexSubImage2D(C.GLenum(target), C.GLint(level), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p)
-}
-
-func (f *Functions) TexParameteri(target, pname gl.Enum, param int) {
- C.glTexParameteri(C.GLenum(target), C.GLenum(pname), C.GLint(param))
-}
-
-func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) {
- C.gio_glUniformBlockBinding(C.GLuint(p.V), C.GLuint(uniformBlockIndex), C.GLuint(uniformBlockBinding))
-}
-
-func (f *Functions) Uniform1f(dst gl.Uniform, v float32) {
- C.glUniform1f(C.GLint(dst.V), C.GLfloat(v))
-}
-
-func (f *Functions) Uniform1i(dst gl.Uniform, v int) {
- C.glUniform1i(C.GLint(dst.V), C.GLint(v))
-}
-
-func (f *Functions) Uniform2f(dst gl.Uniform, v0 float32, v1 float32) {
- C.glUniform2f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1))
-}
-
-func (f *Functions) Uniform3f(dst gl.Uniform, v0 float32, v1 float32, v2 float32) {
- C.glUniform3f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2))
-}
-
-func (f *Functions) Uniform4f(dst gl.Uniform, v0 float32, v1 float32, v2 float32, v3 float32) {
- C.glUniform4f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3))
-}
-
-func (f *Functions) UseProgram(p gl.Program) {
- C.glUseProgram(C.GLuint(p.V))
-}
-
-func (f *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride int, offset int) {
- var n C.GLboolean = C.GL_FALSE
- if normalized {
- n = C.GL_TRUE
- }
- C.gio_glVertexAttribPointer(C.GLuint(dst), C.GLint(size), C.GLenum(ty), n, C.GLsizei(stride), C.uintptr_t(offset))
-}
-
-func (f *Functions) Viewport(x int, y int, width int, height int) {
- C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
-}
diff --git a/vendor/gioui.org/app/internal/glimpl/gl_js.go b/vendor/gioui.org/app/internal/glimpl/gl_js.go
deleted file mode 100644
index e4291d0..0000000
--- a/vendor/gioui.org/app/internal/glimpl/gl_js.go
+++ /dev/null
@@ -1,339 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package glimpl
-
-import (
- "errors"
- "strings"
- "syscall/js"
-
- "gioui.org/gpu/gl"
-)
-
-type Functions struct {
- Ctx js.Value
- EXT_disjoint_timer_query js.Value
- EXT_disjoint_timer_query_webgl2 js.Value
-
- // Cached JS arrays.
- byteBuf js.Value
- int32Buf js.Value
-}
-
-func (f *Functions) Init(version int) error {
- if version < 2 {
- f.EXT_disjoint_timer_query = f.getExtension("EXT_disjoint_timer_query")
- if f.getExtension("OES_texture_half_float").IsNull() && f.getExtension("OES_texture_float").IsNull() {
- return errors.New("gl: no support for neither OES_texture_half_float nor OES_texture_float")
- }
- if f.getExtension("EXT_sRGB").IsNull() {
- return errors.New("gl: EXT_sRGB not supported")
- }
- } else {
- // WebGL2 extensions.
- f.EXT_disjoint_timer_query_webgl2 = f.getExtension("EXT_disjoint_timer_query_webgl2")
- if f.getExtension("EXT_color_buffer_half_float").IsNull() && f.getExtension("EXT_color_buffer_float").IsNull() {
- return errors.New("gl: no support for neither EXT_color_buffer_half_float nor EXT_color_buffer_float")
- }
- }
- return nil
-}
-
-func (f *Functions) getExtension(name string) js.Value {
- return f.Ctx.Call("getExtension", name)
-}
-
-func (f *Functions) ActiveTexture(t gl.Enum) {
- f.Ctx.Call("activeTexture", int(t))
-}
-func (f *Functions) AttachShader(p gl.Program, s gl.Shader) {
- f.Ctx.Call("attachShader", js.Value(p), js.Value(s))
-}
-func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) {
- if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
- f.Ctx.Call("beginQuery", int(target), js.Value(query))
- } else {
- f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query))
- }
-}
-func (f *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) {
- f.Ctx.Call("bindAttribLocation", js.Value(p), int(a), name)
-}
-func (f *Functions) BindBuffer(target gl.Enum, b gl.Buffer) {
- f.Ctx.Call("bindBuffer", int(target), js.Value(b))
-}
-func (f *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) {
- f.Ctx.Call("bindBufferBase", int(target), index, js.Value(b))
-}
-func (f *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) {
- f.Ctx.Call("bindFramebuffer", int(target), js.Value(fb))
-}
-func (f *Functions) BindRenderbuffer(target gl.Enum, rb gl.Renderbuffer) {
- f.Ctx.Call("bindRenderbuffer", int(target), js.Value(rb))
-}
-func (f *Functions) BindTexture(target gl.Enum, t gl.Texture) {
- f.Ctx.Call("bindTexture", int(target), js.Value(t))
-}
-func (f *Functions) BlendEquation(mode gl.Enum) {
- f.Ctx.Call("blendEquation", int(mode))
-}
-func (f *Functions) BlendFunc(sfactor, dfactor gl.Enum) {
- f.Ctx.Call("blendFunc", int(sfactor), int(dfactor))
-}
-func (f *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) {
- f.Ctx.Call("bufferData", int(target), f.byteArrayOf(src), int(usage))
-}
-func (f *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum {
- return gl.Enum(f.Ctx.Call("checkFramebufferStatus", int(target)).Int())
-}
-func (f *Functions) Clear(mask gl.Enum) {
- f.Ctx.Call("clear", int(mask))
-}
-func (f *Functions) ClearColor(red, green, blue, alpha float32) {
- f.Ctx.Call("clearColor", red, green, blue, alpha)
-}
-func (f *Functions) ClearDepthf(d float32) {
- f.Ctx.Call("clearDepth", d)
-}
-func (f *Functions) CompileShader(s gl.Shader) {
- f.Ctx.Call("compileShader", js.Value(s))
-}
-func (f *Functions) CreateBuffer() gl.Buffer {
- return gl.Buffer(f.Ctx.Call("createBuffer"))
-}
-func (f *Functions) CreateFramebuffer() gl.Framebuffer {
- return gl.Framebuffer(f.Ctx.Call("createFramebuffer"))
-}
-func (f *Functions) CreateProgram() gl.Program {
- return gl.Program(f.Ctx.Call("createProgram"))
-}
-func (f *Functions) CreateQuery() gl.Query {
- return gl.Query(f.Ctx.Call("createQuery"))
-}
-func (f *Functions) CreateRenderbuffer() gl.Renderbuffer {
- return gl.Renderbuffer(f.Ctx.Call("createRenderbuffer"))
-}
-func (f *Functions) CreateShader(ty gl.Enum) gl.Shader {
- return gl.Shader(f.Ctx.Call("createShader", int(ty)))
-}
-func (f *Functions) CreateTexture() gl.Texture {
- return gl.Texture(f.Ctx.Call("createTexture"))
-}
-func (f *Functions) DeleteBuffer(v gl.Buffer) {
- f.Ctx.Call("deleteBuffer", js.Value(v))
-}
-func (f *Functions) DeleteFramebuffer(v gl.Framebuffer) {
- f.Ctx.Call("deleteFramebuffer", js.Value(v))
-}
-func (f *Functions) DeleteProgram(p gl.Program) {
- f.Ctx.Call("deleteProgram", js.Value(p))
-}
-func (f *Functions) DeleteQuery(query gl.Query) {
- if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
- f.Ctx.Call("deleteQuery", js.Value(query))
- } else {
- f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query))
- }
-}
-func (f *Functions) DeleteShader(s gl.Shader) {
- f.Ctx.Call("deleteShader", js.Value(s))
-}
-func (f *Functions) DeleteRenderbuffer(v gl.Renderbuffer) {
- f.Ctx.Call("deleteRenderbuffer", js.Value(v))
-}
-func (f *Functions) DeleteTexture(v gl.Texture) {
- f.Ctx.Call("deleteTexture", js.Value(v))
-}
-func (f *Functions) DepthFunc(fn gl.Enum) {
- f.Ctx.Call("depthFunc", int(fn))
-}
-func (f *Functions) DepthMask(mask bool) {
- f.Ctx.Call("depthMask", mask)
-}
-func (f *Functions) DisableVertexAttribArray(a gl.Attrib) {
- f.Ctx.Call("disableVertexAttribArray", int(a))
-}
-func (f *Functions) Disable(cap gl.Enum) {
- f.Ctx.Call("disable", int(cap))
-}
-func (f *Functions) DrawArrays(mode gl.Enum, first, count int) {
- f.Ctx.Call("drawArrays", int(mode), first, count)
-}
-func (f *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) {
- f.Ctx.Call("drawElements", int(mode), count, int(ty), offset)
-}
-func (f *Functions) Enable(cap gl.Enum) {
- f.Ctx.Call("enable", int(cap))
-}
-func (f *Functions) EnableVertexAttribArray(a gl.Attrib) {
- f.Ctx.Call("enableVertexAttribArray", int(a))
-}
-func (f *Functions) EndQuery(target gl.Enum) {
- if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
- f.Ctx.Call("endQuery", int(target))
- } else {
- f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target))
- }
-}
-func (f *Functions) Finish() {
- f.Ctx.Call("finish")
-}
-func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) {
- f.Ctx.Call("framebufferRenderbuffer", int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer))
-}
-func (f *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) {
- f.Ctx.Call("framebufferTexture2D", int(target), int(attachment), int(texTarget), js.Value(t), level)
-}
-func (f *Functions) GetError() gl.Enum {
- return gl.Enum(f.Ctx.Call("getError").Int())
-}
-func (f *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int {
- return paramVal(f.Ctx.Call("getRenderbufferParameteri", int(pname)))
-}
-func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int {
- return paramVal(f.Ctx.Call("getFramebufferAttachmentParameter", int(target), int(attachment), int(pname)))
-}
-func (f *Functions) GetBinding(pname gl.Enum) gl.Object {
- return gl.Object(f.Ctx.Call("getParameter", int(pname)))
-}
-func (f *Functions) GetInteger(pname gl.Enum) int {
- return paramVal(f.Ctx.Call("getParameter", int(pname)))
-}
-func (f *Functions) GetProgrami(p gl.Program, pname gl.Enum) int {
- return paramVal(f.Ctx.Call("getProgramParameter", js.Value(p), int(pname)))
-}
-func (f *Functions) GetProgramInfoLog(p gl.Program) string {
- return f.Ctx.Call("getProgramInfoLog", js.Value(p)).String()
-}
-func (f *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint {
- if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
- return uint(paramVal(f.Ctx.Call("getQueryParameter", js.Value(query), int(pname))))
- } else {
- return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname))))
- }
-}
-func (f *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int {
- return paramVal(f.Ctx.Call("getShaderParameter", js.Value(s), int(pname)))
-}
-func (f *Functions) GetShaderInfoLog(s gl.Shader) string {
- return f.Ctx.Call("getShaderInfoLog", js.Value(s)).String()
-}
-func (f *Functions) GetString(pname gl.Enum) string {
- switch pname {
- case gl.EXTENSIONS:
- extsjs := f.Ctx.Call("getSupportedExtensions")
- var exts []string
- for i := 0; i < extsjs.Length(); i++ {
- exts = append(exts, "GL_"+extsjs.Index(i).String())
- }
- return strings.Join(exts, " ")
- default:
- return f.Ctx.Call("getParameter", int(pname)).String()
- }
-}
-func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint {
- return uint(paramVal(f.Ctx.Call("getUniformBlockIndex", js.Value(p), name)))
-}
-func (f *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform {
- return gl.Uniform(f.Ctx.Call("getUniformLocation", js.Value(p), name))
-}
-func (f *Functions) InvalidateFramebuffer(target, attachment gl.Enum) {
- fn := f.Ctx.Get("invalidateFramebuffer")
- if !fn.IsUndefined() {
- if f.int32Buf.IsUndefined() {
- f.int32Buf = js.Global().Get("Int32Array").New(1)
- }
- f.int32Buf.SetIndex(0, int32(attachment))
- f.Ctx.Call("invalidateFramebuffer", int(target), f.int32Buf)
- }
-}
-func (f *Functions) LinkProgram(p gl.Program) {
- f.Ctx.Call("linkProgram", js.Value(p))
-}
-func (f *Functions) PixelStorei(pname gl.Enum, param int32) {
- f.Ctx.Call("pixelStorei", int(pname), param)
-}
-func (f *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) {
- f.Ctx.Call("renderbufferStorage", int(target), int(internalformat), width, height)
-}
-func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) {
- f.resizeByteBuffer(len(data))
- f.Ctx.Call("readPixels", x, y, width, height, int(format), int(ty), f.byteBuf)
- js.CopyBytesToGo(data, f.byteBuf)
-}
-func (f *Functions) Scissor(x, y, width, height int32) {
- f.Ctx.Call("scissor", x, y, width, height)
-}
-func (f *Functions) ShaderSource(s gl.Shader, src string) {
- f.Ctx.Call("shaderSource", js.Value(s), src)
-}
-func (f *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width, height int, format, ty gl.Enum, data []byte) {
- f.Ctx.Call("texImage2D", int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), f.byteArrayOf(data))
-}
-func (f *Functions) TexSubImage2D(target gl.Enum, level int, x, y, width, height int, format, ty gl.Enum, data []byte) {
- f.Ctx.Call("texSubImage2D", int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data))
-}
-func (f *Functions) TexParameteri(target, pname gl.Enum, param int) {
- f.Ctx.Call("texParameteri", int(target), int(pname), int(param))
-}
-func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) {
- f.Ctx.Call("uniformBlockBinding", js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding))
-}
-func (f *Functions) Uniform1f(dst gl.Uniform, v float32) {
- f.Ctx.Call("uniform1f", js.Value(dst), v)
-}
-func (f *Functions) Uniform1i(dst gl.Uniform, v int) {
- f.Ctx.Call("uniform1i", js.Value(dst), v)
-}
-func (f *Functions) Uniform2f(dst gl.Uniform, v0, v1 float32) {
- f.Ctx.Call("uniform2f", js.Value(dst), v0, v1)
-}
-func (f *Functions) Uniform3f(dst gl.Uniform, v0, v1, v2 float32) {
- f.Ctx.Call("uniform3f", js.Value(dst), v0, v1, v2)
-}
-func (f *Functions) Uniform4f(dst gl.Uniform, v0, v1, v2, v3 float32) {
- f.Ctx.Call("uniform4f", js.Value(dst), v0, v1, v2, v3)
-}
-func (f *Functions) UseProgram(p gl.Program) {
- f.Ctx.Call("useProgram", js.Value(p))
-}
-func (f *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride, offset int) {
- f.Ctx.Call("vertexAttribPointer", int(dst), size, int(ty), normalized, stride, offset)
-}
-func (f *Functions) Viewport(x, y, width, height int) {
- f.Ctx.Call("viewport", x, y, width, height)
-}
-
-func (f *Functions) byteArrayOf(data []byte) js.Value {
- if len(data) == 0 {
- return js.Null()
- }
- f.resizeByteBuffer(len(data))
- js.CopyBytesToJS(f.byteBuf, data)
- return f.byteBuf
-}
-
-func (f *Functions) resizeByteBuffer(n int) {
- if n == 0 {
- return
- }
- if !f.byteBuf.IsUndefined() && f.byteBuf.Length() >= n {
- return
- }
- f.byteBuf = js.Global().Get("Uint8Array").New(n)
-}
-
-func paramVal(v js.Value) int {
- switch v.Type() {
- case js.TypeBoolean:
- if b := v.Bool(); b {
- return 1
- } else {
- return 0
- }
- case js.TypeNumber:
- return v.Int()
- default:
- panic("unknown parameter type")
- }
-}
diff --git a/vendor/gioui.org/app/internal/glimpl/gl_windows.go b/vendor/gioui.org/app/internal/glimpl/gl_windows.go
deleted file mode 100644
index 4fe1d91..0000000
--- a/vendor/gioui.org/app/internal/glimpl/gl_windows.go
+++ /dev/null
@@ -1,406 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package glimpl
-
-import (
- "math"
- "runtime"
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-
- "gioui.org/gpu/gl"
- gunsafe "gioui.org/internal/unsafe"
-)
-
-var (
- LibGLESv2 = windows.NewLazyDLL("libGLESv2.dll")
- _glActiveTexture = LibGLESv2.NewProc("glActiveTexture")
- _glAttachShader = LibGLESv2.NewProc("glAttachShader")
- _glBeginQuery = LibGLESv2.NewProc("glBeginQuery")
- _glBindAttribLocation = LibGLESv2.NewProc("glBindAttribLocation")
- _glBindBuffer = LibGLESv2.NewProc("glBindBuffer")
- _glBindBufferBase = LibGLESv2.NewProc("glBindBufferBase")
- _glBindFramebuffer = LibGLESv2.NewProc("glBindFramebuffer")
- _glBindRenderbuffer = LibGLESv2.NewProc("glBindRenderbuffer")
- _glBindTexture = LibGLESv2.NewProc("glBindTexture")
- _glBlendEquation = LibGLESv2.NewProc("glBlendEquation")
- _glBlendFunc = LibGLESv2.NewProc("glBlendFunc")
- _glBufferData = LibGLESv2.NewProc("glBufferData")
- _glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus")
- _glClear = LibGLESv2.NewProc("glClear")
- _glClearColor = LibGLESv2.NewProc("glClearColor")
- _glClearDepthf = LibGLESv2.NewProc("glClearDepthf")
- _glDeleteQueries = LibGLESv2.NewProc("glDeleteQueries")
- _glCompileShader = LibGLESv2.NewProc("glCompileShader")
- _glGenBuffers = LibGLESv2.NewProc("glGenBuffers")
- _glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers")
- _glGetUniformBlockIndex = LibGLESv2.NewProc("glGetUniformBlockIndex")
- _glCreateProgram = LibGLESv2.NewProc("glCreateProgram")
- _glGenRenderbuffers = LibGLESv2.NewProc("glGenRenderbuffers")
- _glCreateShader = LibGLESv2.NewProc("glCreateShader")
- _glGenTextures = LibGLESv2.NewProc("glGenTextures")
- _glDeleteBuffers = LibGLESv2.NewProc("glDeleteBuffers")
- _glDeleteFramebuffers = LibGLESv2.NewProc("glDeleteFramebuffers")
- _glDeleteProgram = LibGLESv2.NewProc("glDeleteProgram")
- _glDeleteShader = LibGLESv2.NewProc("glDeleteShader")
- _glDeleteRenderbuffers = LibGLESv2.NewProc("glDeleteRenderbuffers")
- _glDeleteTextures = LibGLESv2.NewProc("glDeleteTextures")
- _glDepthFunc = LibGLESv2.NewProc("glDepthFunc")
- _glDepthMask = LibGLESv2.NewProc("glDepthMask")
- _glDisableVertexAttribArray = LibGLESv2.NewProc("glDisableVertexAttribArray")
- _glDisable = LibGLESv2.NewProc("glDisable")
- _glDrawArrays = LibGLESv2.NewProc("glDrawArrays")
- _glDrawElements = LibGLESv2.NewProc("glDrawElements")
- _glEnable = LibGLESv2.NewProc("glEnable")
- _glEnableVertexAttribArray = LibGLESv2.NewProc("glEnableVertexAttribArray")
- _glEndQuery = LibGLESv2.NewProc("glEndQuery")
- _glFinish = LibGLESv2.NewProc("glFinish")
- _glFramebufferRenderbuffer = LibGLESv2.NewProc("glFramebufferRenderbuffer")
- _glFramebufferTexture2D = LibGLESv2.NewProc("glFramebufferTexture2D")
- _glGenQueries = LibGLESv2.NewProc("glGenQueries")
- _glGetError = LibGLESv2.NewProc("glGetError")
- _glGetRenderbufferParameteri = LibGLESv2.NewProc("glGetRenderbufferParameteri")
- _glGetFramebufferAttachmentParameteri = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteri")
- _glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv")
- _glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv")
- _glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog")
- _glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv")
- _glGetShaderiv = LibGLESv2.NewProc("glGetShaderiv")
- _glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog")
- _glGetString = LibGLESv2.NewProc("glGetString")
- _glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation")
- _glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer")
- _glLinkProgram = LibGLESv2.NewProc("glLinkProgram")
- _glPixelStorei = LibGLESv2.NewProc("glPixelStorei")
- _glReadPixels = LibGLESv2.NewProc("glReadPixels")
- _glRenderbufferStorage = LibGLESv2.NewProc("glRenderbufferStorage")
- _glScissor = LibGLESv2.NewProc("glScissor")
- _glShaderSource = LibGLESv2.NewProc("glShaderSource")
- _glTexImage2D = LibGLESv2.NewProc("glTexImage2D")
- _glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D")
- _glTexParameteri = LibGLESv2.NewProc("glTexParameteri")
- _glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding")
- _glUniform1f = LibGLESv2.NewProc("glUniform1f")
- _glUniform1i = LibGLESv2.NewProc("glUniform1i")
- _glUniform2f = LibGLESv2.NewProc("glUniform2f")
- _glUniform3f = LibGLESv2.NewProc("glUniform3f")
- _glUniform4f = LibGLESv2.NewProc("glUniform4f")
- _glUseProgram = LibGLESv2.NewProc("glUseProgram")
- _glVertexAttribPointer = LibGLESv2.NewProc("glVertexAttribPointer")
- _glViewport = LibGLESv2.NewProc("glViewport")
-)
-
-type Functions struct {
- // gl.Query caches.
- int32s [100]int32
-}
-
-func (c *Functions) ActiveTexture(t gl.Enum) {
- syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0)
-}
-func (c *Functions) AttachShader(p gl.Program, s gl.Shader) {
- syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0)
-}
-func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) {
- syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0)
-}
-func (c *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) {
- cname := cString(name)
- c0 := &cname[0]
- syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0)))
- issue34474KeepAlive(c)
-}
-func (c *Functions) BindBuffer(target gl.Enum, b gl.Buffer) {
- syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0)
-}
-func (c *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) {
- syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V))
-}
-func (c *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) {
- syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0)
-}
-func (c *Functions) BindRenderbuffer(target gl.Enum, rb gl.Renderbuffer) {
- syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0)
-}
-func (c *Functions) BindTexture(target gl.Enum, t gl.Texture) {
- syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0)
-}
-func (c *Functions) BlendEquation(mode gl.Enum) {
- syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0)
-}
-func (c *Functions) BlendFunc(sfactor, dfactor gl.Enum) {
- syscall.Syscall(_glBlendFunc.Addr(), 2, uintptr(sfactor), uintptr(dfactor), 0)
-}
-func (c *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) {
- if n := len(src); n == 0 {
- syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), 0, 0, uintptr(usage), 0, 0)
- } else {
- s0 := &src[0]
- syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(n), uintptr(unsafe.Pointer(s0)), uintptr(usage), 0, 0)
- issue34474KeepAlive(s0)
- }
-}
-func (c *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum {
- s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0)
- return gl.Enum(s)
-}
-func (c *Functions) Clear(mask gl.Enum) {
- syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0)
-}
-func (c *Functions) ClearColor(red, green, blue, alpha float32) {
- syscall.Syscall6(_glClearColor.Addr(), 4, uintptr(math.Float32bits(red)), uintptr(math.Float32bits(green)), uintptr(math.Float32bits(blue)), uintptr(math.Float32bits(alpha)), 0, 0)
-}
-func (c *Functions) ClearDepthf(d float32) {
- syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0)
-}
-func (c *Functions) CompileShader(s gl.Shader) {
- syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0)
-}
-func (c *Functions) CreateBuffer() gl.Buffer {
- var buf uintptr
- syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
- return gl.Buffer{uint(buf)}
-}
-func (c *Functions) CreateFramebuffer() gl.Framebuffer {
- var fb uintptr
- syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0)
- return gl.Framebuffer{uint(fb)}
-}
-func (c *Functions) CreateProgram() gl.Program {
- p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0)
- return gl.Program{uint(p)}
-}
-func (f *Functions) CreateQuery() gl.Query {
- var q uintptr
- syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0)
- return gl.Query{uint(q)}
-}
-func (c *Functions) CreateRenderbuffer() gl.Renderbuffer {
- var rb uintptr
- syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0)
- return gl.Renderbuffer{uint(rb)}
-}
-func (c *Functions) CreateShader(ty gl.Enum) gl.Shader {
- s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0)
- return gl.Shader{uint(s)}
-}
-func (c *Functions) CreateTexture() gl.Texture {
- var t uintptr
- syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
- return gl.Texture{uint(t)}
-}
-func (c *Functions) DeleteBuffer(v gl.Buffer) {
- syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0)
-}
-func (c *Functions) DeleteFramebuffer(v gl.Framebuffer) {
- syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
-}
-func (c *Functions) DeleteProgram(p gl.Program) {
- syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0)
-}
-func (f *Functions) DeleteQuery(query gl.Query) {
- syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0)
-}
-func (c *Functions) DeleteShader(s gl.Shader) {
- syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0)
-}
-func (c *Functions) DeleteRenderbuffer(v gl.Renderbuffer) {
- syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
-}
-func (c *Functions) DeleteTexture(v gl.Texture) {
- syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
-}
-func (c *Functions) DepthFunc(f gl.Enum) {
- syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0)
-}
-func (c *Functions) DepthMask(mask bool) {
- var m uintptr
- if mask {
- m = 1
- }
- syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0)
-}
-func (c *Functions) DisableVertexAttribArray(a gl.Attrib) {
- syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
-}
-func (c *Functions) Disable(cap gl.Enum) {
- syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0)
-}
-func (c *Functions) DrawArrays(mode gl.Enum, first, count int) {
- syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count))
-}
-func (c *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) {
- syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0)
-}
-func (c *Functions) Enable(cap gl.Enum) {
- syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0)
-}
-func (c *Functions) EnableVertexAttribArray(a gl.Attrib) {
- syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
-}
-func (f *Functions) EndQuery(target gl.Enum) {
- syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0)
-}
-func (c *Functions) Finish() {
- syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0)
-}
-func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) {
- syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0)
-}
-func (c *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) {
- syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0)
-}
-func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint {
- cname := cString(name)
- c0 := &cname[0]
- u, _, _ := syscall.Syscall(_glGetUniformBlockIndex.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0)
- issue34474KeepAlive(c0)
- return uint(u)
-}
-func (c *Functions) GetBinding(pname gl.Enum) gl.Object {
- return gl.Object{uint(c.GetInteger(pname))}
-}
-func (c *Functions) GetError() gl.Enum {
- e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0)
- return gl.Enum(e)
-}
-func (c *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int {
- p, _, _ := syscall.Syscall(_glGetRenderbufferParameteri.Addr(), 2, uintptr(target), uintptr(pname), 0)
- return int(p)
-}
-func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int {
- p, _, _ := syscall.Syscall(_glGetFramebufferAttachmentParameteri.Addr(), 3, uintptr(target), uintptr(attachment), uintptr(pname))
- return int(p)
-}
-func (c *Functions) GetInteger(pname gl.Enum) int {
- syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
- return int(c.int32s[0])
-}
-func (c *Functions) GetProgrami(p gl.Program, pname gl.Enum) int {
- syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
- return int(c.int32s[0])
-}
-func (c *Functions) GetProgramInfoLog(p gl.Program) string {
- n := c.GetProgrami(p, gl.INFO_LOG_LENGTH)
- buf := make([]byte, n)
- syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
- return string(buf)
-}
-func (c *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint {
- syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
- return uint(c.int32s[0])
-}
-func (c *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int {
- syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
- return int(c.int32s[0])
-}
-func (c *Functions) GetShaderInfoLog(s gl.Shader) string {
- n := c.GetShaderi(s, gl.INFO_LOG_LENGTH)
- buf := make([]byte, n)
- syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
- return string(buf)
-}
-func (c *Functions) GetString(pname gl.Enum) string {
- s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0)
- return gunsafe.GoString(gunsafe.SliceOf(s))
-}
-func (c *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform {
- cname := cString(name)
- c0 := &cname[0]
- u, _, _ := syscall.Syscall(_glGetUniformLocation.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0)
- issue34474KeepAlive(c0)
- return gl.Uniform{int(u)}
-}
-func (c *Functions) InvalidateFramebuffer(target, attachment gl.Enum) {
- addr := _glInvalidateFramebuffer.Addr()
- if addr == 0 {
- // InvalidateFramebuffer is just a hint. Skip it if not supported.
- return
- }
- syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment)))
-}
-func (c *Functions) LinkProgram(p gl.Program) {
- syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0)
-}
-func (c *Functions) PixelStorei(pname gl.Enum, param int32) {
- syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0)
-}
-func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) {
- d0 := &data[0]
- syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0)
- issue34474KeepAlive(d0)
-}
-func (c *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) {
- syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0)
-}
-func (c *Functions) Scissor(x, y, width, height int32) {
- syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
-}
-func (c *Functions) ShaderSource(s gl.Shader, src string) {
- var n uintptr = uintptr(len(src))
- psrc := &src
- syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0)
- issue34474KeepAlive(psrc)
-}
-func (c *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width, height int, format, ty gl.Enum, data []byte) {
- if len(data) == 0 {
- syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0)
- } else {
- d0 := &data[0]
- syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)))
- issue34474KeepAlive(d0)
- }
-}
-func (c *Functions) TexSubImage2D(target gl.Enum, level int, x, y, width, height int, format, ty gl.Enum, data []byte) {
- d0 := &data[0]
- syscall.Syscall9(_glTexSubImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)))
- issue34474KeepAlive(d0)
-}
-func (c *Functions) TexParameteri(target, pname gl.Enum, param int) {
- syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param))
-}
-func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) {
- syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding))
-}
-func (c *Functions) Uniform1f(dst gl.Uniform, v float32) {
- syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0)
-}
-func (c *Functions) Uniform1i(dst gl.Uniform, v int) {
- syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0)
-}
-func (c *Functions) Uniform2f(dst gl.Uniform, v0, v1 float32) {
- syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)))
-}
-func (c *Functions) Uniform3f(dst gl.Uniform, v0, v1, v2 float32) {
- syscall.Syscall6(_glUniform3f.Addr(), 4, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), 0, 0)
-}
-func (c *Functions) Uniform4f(dst gl.Uniform, v0, v1, v2, v3 float32) {
- syscall.Syscall6(_glUniform4f.Addr(), 5, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), uintptr(math.Float32bits(v3)), 0)
-}
-func (c *Functions) UseProgram(p gl.Program) {
- syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0)
-}
-func (c *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride, offset int) {
- var norm uintptr
- if normalized {
- norm = 1
- }
- syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset))
-}
-func (c *Functions) Viewport(x, y, width, height int) {
- syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
-}
-
-func cString(s string) []byte {
- b := make([]byte, len(s)+1)
- copy(b, s)
- return b
-}
-
-// issue34474KeepAlive calls runtime.KeepAlive as a
-// workaround for golang.org/issue/34474.
-func issue34474KeepAlive(v interface{}) {
- runtime.KeepAlive(v)
-}
diff --git a/vendor/gioui.org/app/internal/log/log_android.go b/vendor/gioui.org/app/internal/log/log_android.go
index 7936911..1245598 100644
--- a/vendor/gioui.org/app/internal/log/log_android.go
+++ b/vendor/gioui.org/app/internal/log/log_android.go
@@ -40,16 +40,22 @@ type androidLogWriter struct {
}
func (w *androidLogWriter) Write(data []byte) (int, error) {
- // Truncate the buffer, leaving space for the '\0'.
- if max := len(w.buf) - 1; len(data) > max {
- data = data[:max]
+ n := 0
+ for len(data) > 0 {
+ msg := data
+ // Truncate the buffer, leaving space for the '\0'.
+ if max := len(w.buf) - 1; len(msg) > max {
+ msg = msg[:max]
+ }
+ buf := w.buf[:len(msg)+1]
+ copy(buf, msg)
+ // Terminating '\0'.
+ buf[len(msg)] = 0
+ C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0])))
+ n += len(msg)
+ data = data[len(msg):]
}
- buf := w.buf[:len(data)+1]
- copy(buf, data)
- // Terminating '\0'.
- buf[len(data)] = 0
- C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0])))
- return len(data), nil
+ return n, nil
}
func logFd(fd uintptr) {
diff --git a/vendor/gioui.org/app/internal/log/log_ios.go b/vendor/gioui.org/app/internal/log/log_ios.go
index e49f425..cad4e19 100644
--- a/vendor/gioui.org/app/internal/log/log_ios.go
+++ b/vendor/gioui.org/app/internal/log/log_ios.go
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build darwin && ios
// +build darwin,ios
package log
@@ -7,7 +8,11 @@ package log
/*
#cgo CFLAGS: -Werror -fmodules -fobjc-arc -x objective-c
-__attribute__ ((visibility ("hidden"))) void nslog(char *str);
+@import Foundation;
+
+static void nslog(char *str) {
+ NSLog(@"%@", @(str));
+}
*/
import "C"
@@ -17,7 +22,7 @@ import (
"log"
"unsafe"
- _ "gioui.org/app/internal/cocoainit"
+ _ "gioui.org/internal/cocoainit"
)
func init() {
diff --git a/vendor/gioui.org/app/internal/log/log_ios.m b/vendor/gioui.org/app/internal/log/log_ios.m
deleted file mode 100644
index 201bc36..0000000
--- a/vendor/gioui.org/app/internal/log/log_ios.m
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build darwin,ios
-
-@import Foundation;
-
-#include "_cgo_export.h"
-
-void nslog(char *str) {
- NSLog(@"%@", @(str));
-}
diff --git a/vendor/gioui.org/app/internal/srgb/srgb.go b/vendor/gioui.org/app/internal/srgb/srgb.go
deleted file mode 100644
index c148b04..0000000
--- a/vendor/gioui.org/app/internal/srgb/srgb.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package srgb
-
-import (
- "fmt"
- "runtime"
- "strings"
-
- "gioui.org/app/internal/glimpl"
- "gioui.org/gpu/gl"
- "gioui.org/internal/unsafe"
-)
-
-// FBO implements an intermediate sRGB FBO
-// for gamma-correct rendering on platforms without
-// sRGB enabled native framebuffers.
-type FBO struct {
- c *glimpl.Functions
- width, height int
- frameBuffer gl.Framebuffer
- depthBuffer gl.Renderbuffer
- colorTex gl.Texture
- blitted bool
- quad gl.Buffer
- prog gl.Program
- gl3 bool
-}
-
-func New(f *glimpl.Functions) (*FBO, error) {
- var gl3 bool
- glVer := f.GetString(gl.VERSION)
- ver, _, err := gl.ParseGLVersion(glVer)
- if err != nil {
- return nil, err
- }
- if ver[0] >= 3 {
- gl3 = true
- } else {
- exts := f.GetString(gl.EXTENSIONS)
- if !strings.Contains(exts, "EXT_sRGB") {
- return nil, fmt.Errorf("no support for OpenGL ES 3 nor EXT_sRGB")
- }
- }
- s := &FBO{
- c: f,
- gl3: gl3,
- frameBuffer: f.CreateFramebuffer(),
- colorTex: f.CreateTexture(),
- depthBuffer: f.CreateRenderbuffer(),
- }
- f.BindTexture(gl.TEXTURE_2D, s.colorTex)
- f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
- f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
- f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
- f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
- return s, nil
-}
-
-func (s *FBO) Blit() {
- if !s.blitted {
- prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"})
- if err != nil {
- panic(err)
- }
- s.prog = prog
- s.c.UseProgram(prog)
- s.c.Uniform1i(gl.GetUniformLocation(s.c, prog, "tex"), 0)
- s.quad = s.c.CreateBuffer()
- s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad)
- s.c.BufferData(gl.ARRAY_BUFFER,
- unsafe.BytesView([]float32{
- -1, +1, 0, 1,
- +1, +1, 1, 1,
- -1, -1, 0, 0,
- +1, -1, 1, 0,
- }),
- gl.STATIC_DRAW)
- s.blitted = true
- }
- s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{})
- s.c.UseProgram(s.prog)
- s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
- s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad)
- s.c.VertexAttribPointer(0 /* pos */, 2, gl.FLOAT, false, 4*4, 0)
- s.c.VertexAttribPointer(1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2)
- s.c.EnableVertexAttribArray(0)
- s.c.EnableVertexAttribArray(1)
- s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
- s.c.BindTexture(gl.TEXTURE_2D, gl.Texture{})
- s.c.DisableVertexAttribArray(0)
- s.c.DisableVertexAttribArray(1)
- s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer)
- s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
- s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT)
- // The Android emulator requires framebuffer 0 bound at eglSwapBuffer time.
- // Bind the sRGB framebuffer again in afterPresent.
- s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{})
-}
-
-func (s *FBO) AfterPresent() {
- s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer)
-}
-
-func (s *FBO) Refresh(w, h int) error {
- s.width, s.height = w, h
- if w == 0 || h == 0 {
- return nil
- }
- s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
- if s.gl3 {
- s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil)
- } else /* EXT_sRGB */ {
- s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB_ALPHA_EXT, w, h, gl.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE, nil)
- }
- currentRB := gl.Renderbuffer(s.c.GetBinding(gl.RENDERBUFFER_BINDING))
- s.c.BindRenderbuffer(gl.RENDERBUFFER, s.depthBuffer)
- s.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h)
- s.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB)
- s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer)
- s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.colorTex, 0)
- s.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, s.depthBuffer)
- if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
- return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError())
- }
-
- if runtime.GOOS == "js" {
- // With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8
- // texture result in twice gamma corrected colors. Using a plain RGBA
- // texture seems to work.
- s.c.ClearColor(.5, .5, .5, 1.0)
- s.c.Clear(gl.COLOR_BUFFER_BIT)
- var pixel [4]byte
- s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:])
- if pixel[0] == 128 { // Correct sRGB color value is ~188
- s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil)
- if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
- return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError())
- }
- }
- }
-
- return nil
-}
-
-func (s *FBO) Release() {
- s.c.DeleteFramebuffer(s.frameBuffer)
- s.c.DeleteTexture(s.colorTex)
- s.c.DeleteRenderbuffer(s.depthBuffer)
- if s.blitted {
- s.c.DeleteBuffer(s.quad)
- s.c.DeleteProgram(s.prog)
- }
- s.c = nil
-}
-
-const (
- blitVSrc = `
-#version 100
-
-precision highp float;
-
-attribute vec2 pos;
-attribute vec2 uv;
-
-varying vec2 vUV;
-
-void main() {
- gl_Position = vec4(pos, 0, 1);
- vUV = uv;
-}
-`
- blitFSrc = `
-#version 100
-
-precision mediump float;
-
-uniform sampler2D tex;
-varying vec2 vUV;
-
-vec3 gamma(vec3 rgb) {
- vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055);
- vec3 lin = rgb * vec3(12.92);
- bvec3 cut = lessThan(rgb, vec3(0.0031308));
- return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b);
-}
-
-void main() {
- vec4 col = texture2D(tex, vUV);
- vec3 rgb = col.rgb;
- rgb = gamma(rgb);
- gl_FragColor = vec4(rgb, col.a);
-}
-`
-)
diff --git a/vendor/gioui.org/app/internal/window/GioView.java b/vendor/gioui.org/app/internal/window/GioView.java
deleted file mode 100644
index be90c50..0000000
--- a/vendor/gioui.org/app/internal/window/GioView.java
+++ /dev/null
@@ -1,255 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package org.gioui;
-
-import java.lang.Class;
-import java.lang.IllegalAccessException;
-import java.lang.InstantiationException;
-import java.lang.ExceptionInInitializerError;
-import java.lang.SecurityException;
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Build;
-import android.text.Editable;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Choreographer;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.WindowInsets;
-import android.view.Surface;
-import android.view.SurfaceView;
-import android.view.SurfaceHolder;
-import android.view.inputmethod.BaseInputConnection;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.EditorInfo;
-
-import java.io.UnsupportedEncodingException;
-
-public final class GioView extends SurfaceView implements Choreographer.FrameCallback {
- private final static Object initLock = new Object();
- private static boolean jniLoaded;
-
- private final SurfaceHolder.Callback surfCallbacks;
- private final View.OnFocusChangeListener focusCallback;
- private final InputMethodManager imm;
- private final float scrollXScale;
- private final float scrollYScale;
-
- private long nhandle;
-
- public GioView(Context context) {
- this(context, null);
- }
-
- public GioView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- // Late initialization of the Go runtime to wait for a valid context.
- Gio.init(context.getApplicationContext());
-
- ViewConfiguration conf = ViewConfiguration.get(context);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- scrollXScale = conf.getScaledHorizontalScrollFactor();
- scrollYScale = conf.getScaledVerticalScrollFactor();
- } else {
- float listItemHeight = 48; // dp
- float px = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- listItemHeight,
- getResources().getDisplayMetrics()
- );
- scrollXScale = px;
- scrollYScale = px;
- }
-
- nhandle = onCreateView(this);
- imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
- setFocusable(true);
- setFocusableInTouchMode(true);
- focusCallback = new View.OnFocusChangeListener() {
- @Override public void onFocusChange(View v, boolean focus) {
- GioView.this.onFocusChange(nhandle, focus);
- }
- };
- setOnFocusChangeListener(focusCallback);
- surfCallbacks = new SurfaceHolder.Callback() {
- @Override public void surfaceCreated(SurfaceHolder holder) {
- // Ignore; surfaceChanged is guaranteed to be called immediately after this.
- }
- @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- onSurfaceChanged(nhandle, getHolder().getSurface());
- }
- @Override public void surfaceDestroyed(SurfaceHolder holder) {
- onSurfaceDestroyed(nhandle);
- }
- };
- getHolder().addCallback(surfCallbacks);
- }
-
- @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
- onKeyEvent(nhandle, keyCode, event.getUnicodeChar(), event.getEventTime());
- return false;
- }
-
- @Override public boolean onGenericMotionEvent(MotionEvent event) {
- dispatchMotionEvent(event);
- return true;
- }
-
- @Override public boolean onTouchEvent(MotionEvent event) {
- // Ask for unbuffered events. Flutter and Chrome does it
- // so I assume its good for us as well.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- requestUnbufferedDispatch(event);
- }
-
- dispatchMotionEvent(event);
- return true;
- }
-
- private void dispatchMotionEvent(MotionEvent event) {
- for (int j = 0; j < event.getHistorySize(); j++) {
- long time = event.getHistoricalEventTime(j);
- for (int i = 0; i < event.getPointerCount(); i++) {
- onTouchEvent(
- nhandle,
- event.ACTION_MOVE,
- event.getPointerId(i),
- event.getToolType(i),
- event.getHistoricalX(i, j),
- event.getHistoricalY(i, j),
- scrollXScale*event.getHistoricalAxisValue(MotionEvent.AXIS_HSCROLL, i, j),
- scrollYScale*event.getHistoricalAxisValue(MotionEvent.AXIS_VSCROLL, i, j),
- event.getButtonState(),
- time);
- }
- }
- int act = event.getActionMasked();
- int idx = event.getActionIndex();
- for (int i = 0; i < event.getPointerCount(); i++) {
- int pact = event.ACTION_MOVE;
- if (i == idx) {
- pact = act;
- }
- onTouchEvent(
- nhandle,
- pact,
- event.getPointerId(i),
- event.getToolType(i),
- event.getX(i), event.getY(i),
- scrollXScale*event.getAxisValue(MotionEvent.AXIS_HSCROLL, i),
- scrollYScale*event.getAxisValue(MotionEvent.AXIS_VSCROLL, i),
- event.getButtonState(),
- event.getEventTime());
- }
- }
-
- @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- return new InputConnection(this);
- }
-
- void showTextInput() {
- post(new Runnable() {
- @Override public void run() {
- GioView.this.requestFocus();
- imm.showSoftInput(GioView.this, 0);
- }
- });
- }
-
- void hideTextInput() {
- post(new Runnable() {
- @Override public void run() {
- imm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
- });
- }
-
- @Override protected boolean fitSystemWindows(Rect insets) {
- onWindowInsets(nhandle, insets.top, insets.right, insets.bottom, insets.left);
- return true;
- }
-
- void postFrameCallback() {
- Choreographer.getInstance().removeFrameCallback(this);
- Choreographer.getInstance().postFrameCallback(this);
- }
-
- @Override public void doFrame(long nanos) {
- onFrameCallback(nhandle, nanos);
- }
-
- int getDensity() {
- return getResources().getDisplayMetrics().densityDpi;
- }
-
- float getFontScale() {
- return getResources().getConfiguration().fontScale;
- }
-
- void start() {
- onStartView(nhandle);
- }
-
- void stop() {
- onStopView(nhandle);
- }
-
- void destroy() {
- setOnFocusChangeListener(null);
- getHolder().removeCallback(surfCallbacks);
- onDestroyView(nhandle);
- nhandle = 0;
- }
-
- void configurationChanged() {
- onConfigurationChanged(nhandle);
- }
-
- void lowMemory() {
- onLowMemory();
- }
-
- boolean backPressed() {
- return onBack(nhandle);
- }
-
- static private native long onCreateView(GioView view);
- static private native void onDestroyView(long handle);
- static private native void onStartView(long handle);
- static private native void onStopView(long handle);
- static private native void onSurfaceDestroyed(long handle);
- static private native void onSurfaceChanged(long handle, Surface surface);
- static private native void onConfigurationChanged(long handle);
- static private native void onWindowInsets(long handle, int top, int right, int bottom, int left);
- static private native void onLowMemory();
- static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, float scrollX, float scrollY, int buttons, long time);
- static private native void onKeyEvent(long handle, int code, int character, long time);
- static private native void onFrameCallback(long handle, long nanos);
- static private native boolean onBack(long handle);
- static private native void onFocusChange(long handle, boolean focus);
-
- private static class InputConnection extends BaseInputConnection {
- private final Editable editable;
-
- InputConnection(View view) {
- // Passing false enables "dummy mode", where the BaseInputConnection
- // attempts to convert IME operations to key events.
- super(view, false);
- editable = Editable.Factory.getInstance().newEditable("");
- }
-
- @Override public Editable getEditable() {
- return editable;
- }
- }
-}
diff --git a/vendor/gioui.org/app/internal/window/d3d11_windows.go b/vendor/gioui.org/app/internal/window/d3d11_windows.go
deleted file mode 100644
index f1e06a6..0000000
--- a/vendor/gioui.org/app/internal/window/d3d11_windows.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package window
-
-import (
- "gioui.org/app/internal/d3d11"
- "gioui.org/gpu/backend"
-)
-
-type d3d11Context struct {
- win *window
- swchain *d3d11.SwapChain
- fbo *d3d11.Framebuffer
- backend backend.Device
- *d3d11.Device
- width, height int
-}
-
-func init() {
- backends = append(backends, gpuAPI{
- priority: 1,
- initializer: func(w *window) (Context, error) {
- hwnd, _, _ := w.HWND()
- dev, err := d3d11.NewDevice()
- if err != nil {
- return nil, err
- }
- swchain, err := dev.CreateSwapChain(hwnd)
- if err != nil {
- dev.Release()
- return nil, err
- }
- return &d3d11Context{win: w, Device: dev, swchain: swchain}, nil
- },
- })
-}
-
-func (c *d3d11Context) Backend() (backend.Device, error) {
- backend, err := d3d11.NewBackend(c.Device)
- if err != nil {
- return nil, err
- }
- c.backend = backend
- c.backend.BindFramebuffer(c.fbo)
- return backend, nil
-}
-
-func (c *d3d11Context) Present() error {
- if err := c.swchain.Present(); err != nil {
- if err, ok := err.(d3d11.ErrorCode); ok {
- switch err.Code {
- case d3d11.DXGI_STATUS_OCCLUDED:
- // Ignore
- return nil
- case d3d11.DXGI_ERROR_DEVICE_RESET, d3d11.DXGI_ERROR_DEVICE_REMOVED, d3d11.D3DDDIERR_DEVICEREMOVED:
- return ErrDeviceLost
- }
- }
- }
- return nil
-}
-
-func (c *d3d11Context) MakeCurrent() error {
- _, width, height := c.win.HWND()
- if c.fbo != nil && width == c.width && height == c.height {
- c.backend.BindFramebuffer(c.fbo)
- return nil
- }
- if c.fbo != nil {
- c.fbo.Release()
- c.fbo = nil
- }
- if err := c.swchain.Resize(); err != nil {
- return err
- }
- c.width = width
- c.height = height
- fbo, err := c.swchain.Framebuffer(c.Device)
- if err != nil {
- return err
- }
- c.fbo = fbo
- if c.backend != nil {
- c.backend.BindFramebuffer(c.fbo)
- }
- return nil
-}
-
-func (c *d3d11Context) Lock() {}
-
-func (c *d3d11Context) Unlock() {}
-
-func (c *d3d11Context) Release() {
- if c.fbo != nil {
- c.fbo.Release()
- }
- c.swchain.Release()
- c.Device.Release()
- c.fbo = nil
- c.swchain = nil
- c.Device = nil
-}
diff --git a/vendor/gioui.org/app/internal/window/egl_android.go b/vendor/gioui.org/app/internal/window/egl_android.go
deleted file mode 100644
index 19c8d63..0000000
--- a/vendor/gioui.org/app/internal/window/egl_android.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package window
-
-/*
-#include <EGL/egl.h>
-*/
-import "C"
-
-import (
- "unsafe"
-
- "gioui.org/app/internal/egl"
-)
-
-type context struct {
- win *window
- *egl.Context
-}
-
-func (w *window) NewContext() (Context, error) {
- ctx, err := egl.NewContext(nil)
- if err != nil {
- return nil, err
- }
- return &context{win: w, Context: ctx}, nil
-}
-
-func (c *context) Release() {
- if c.Context != nil {
- c.Context.Release()
- c.Context = nil
- }
-}
-
-func (c *context) MakeCurrent() error {
- c.Context.ReleaseSurface()
- win, width, height := c.win.nativeWindow(c.Context.VisualID())
- if win == nil {
- return nil
- }
- eglSurf := egl.NativeWindowType(unsafe.Pointer(win))
- if err := c.Context.CreateSurface(eglSurf, width, height); err != nil {
- return err
- }
- if err := c.Context.MakeCurrent(); err != nil {
- return err
- }
- return nil
-}
-
-func (c *context) Lock() {}
-
-func (c *context) Unlock() {}
diff --git a/vendor/gioui.org/app/internal/window/egl_x11.go b/vendor/gioui.org/app/internal/window/egl_x11.go
deleted file mode 100644
index ffe69c6..0000000
--- a/vendor/gioui.org/app/internal/window/egl_x11.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build linux,!android,!nox11 freebsd openbsd
-
-package window
-
-import (
- "unsafe"
-
- "gioui.org/app/internal/egl"
-)
-
-type x11Context struct {
- win *x11Window
- *egl.Context
-}
-
-func (w *x11Window) NewContext() (Context, error) {
- disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
- ctx, err := egl.NewContext(disp)
- if err != nil {
- return nil, err
- }
- return &x11Context{win: w, Context: ctx}, nil
-}
-
-func (c *x11Context) Release() {
- if c.Context != nil {
- c.Context.Release()
- c.Context = nil
- }
-}
-
-func (c *x11Context) MakeCurrent() error {
- c.Context.ReleaseSurface()
- win, width, height := c.win.window()
- eglSurf := egl.NativeWindowType(uintptr(win))
- if err := c.Context.CreateSurface(eglSurf, width, height); err != nil {
- return err
- }
- if err := c.Context.MakeCurrent(); err != nil {
- return err
- }
- c.Context.EnableVSync(true)
- return nil
-}
-
-func (c *x11Context) Lock() {}
-
-func (c *x11Context) Unlock() {}
diff --git a/vendor/gioui.org/app/internal/window/gl_js.go b/vendor/gioui.org/app/internal/window/gl_js.go
deleted file mode 100644
index 0adbdf4..0000000
--- a/vendor/gioui.org/app/internal/window/gl_js.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package window
-
-import (
- "errors"
- "syscall/js"
-
- "gioui.org/app/internal/glimpl"
- "gioui.org/app/internal/srgb"
- "gioui.org/gpu/backend"
- "gioui.org/gpu/gl"
-)
-
-type context struct {
- ctx js.Value
- cnv js.Value
- f *glimpl.Functions
- srgbFBO *srgb.FBO
-}
-
-func newContext(w *window) (*context, error) {
- args := map[string]interface{}{
- // Enable low latency rendering.
- // See https://developers.google.com/web/updates/2019/05/desynchronized.
- "desynchronized": true,
- "preserveDrawingBuffer": true,
- }
- version := 2
- ctx := w.cnv.Call("getContext", "webgl2", args)
- if ctx.IsNull() {
- version = 1
- ctx = w.cnv.Call("getContext", "webgl", args)
- }
- if ctx.IsNull() {
- return nil, errors.New("app: webgl is not supported")
- }
- f := &glimpl.Functions{Ctx: ctx}
- if err := f.Init(version); err != nil {
- return nil, err
- }
- c := &context{
- ctx: ctx,
- cnv: w.cnv,
- f: f,
- }
- return c, nil
-}
-
-func (c *context) Backend() (backend.Device, error) {
- return gl.NewBackend(c.f)
-}
-
-func (c *context) Release() {
- if c.srgbFBO != nil {
- c.srgbFBO.Release()
- c.srgbFBO = nil
- }
-}
-
-func (c *context) Present() error {
- if c.srgbFBO != nil {
- c.srgbFBO.Blit()
- }
- if c.srgbFBO != nil {
- c.srgbFBO.AfterPresent()
- }
- if c.ctx.Call("isContextLost").Bool() {
- return errors.New("context lost")
- }
- return nil
-}
-
-func (c *context) Lock() {}
-
-func (c *context) Unlock() {}
-
-func (c *context) MakeCurrent() error {
- if c.srgbFBO == nil {
- var err error
- c.srgbFBO, err = srgb.New(c.f)
- if err != nil {
- c.Release()
- c.srgbFBO = nil
- return err
- }
- }
- w, h := c.cnv.Get("width").Int(), c.cnv.Get("height").Int()
- if err := c.srgbFBO.Refresh(w, h); err != nil {
- c.Release()
- return err
- }
- return nil
-}
-
-func (w *window) NewContext() (Context, error) {
- return newContext(w)
-}
diff --git a/vendor/gioui.org/app/internal/window/gl_macos.go b/vendor/gioui.org/app/internal/window/gl_macos.go
deleted file mode 100644
index e4e293d..0000000
--- a/vendor/gioui.org/app/internal/window/gl_macos.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build darwin,!ios
-
-package window
-
-import (
- "gioui.org/app/internal/glimpl"
- "gioui.org/gpu/backend"
- "gioui.org/gpu/gl"
-)
-
-/*
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreGraphics/CoreGraphics.h>
-#include <AppKit/AppKit.h>
-#include <OpenGL/gl3.h>
-
-__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLView(void);
-__attribute__ ((visibility ("hidden"))) CFTypeRef gio_contextForView(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) void gio_makeCurrentContext(CFTypeRef ctx);
-__attribute__ ((visibility ("hidden"))) void gio_flushContextBuffer(CFTypeRef ctx);
-__attribute__ ((visibility ("hidden"))) void gio_clearCurrentContext(void);
-__attribute__ ((visibility ("hidden"))) void gio_lockContext(CFTypeRef ctxRef);
-__attribute__ ((visibility ("hidden"))) void gio_unlockContext(CFTypeRef ctxRef);
-*/
-import "C"
-
-type context struct {
- c *glimpl.Functions
- ctx C.CFTypeRef
- view C.CFTypeRef
-}
-
-func init() {
- viewFactory = func() C.CFTypeRef {
- return C.gio_createGLView()
- }
-}
-
-func newContext(w *window) (*context, error) {
- view := w.contextView()
- ctx := C.gio_contextForView(view)
- c := &context{
- ctx: ctx,
- c: new(glimpl.Functions),
- view: view,
- }
- return c, nil
-}
-
-func (c *context) Backend() (backend.Device, error) {
- return gl.NewBackend(c.c)
-}
-
-func (c *context) Release() {
- c.Lock()
- defer c.Unlock()
- C.gio_clearCurrentContext()
- // We could release the context with [view clearGLContext]
- // and rely on [view openGLContext] auto-creating a new context.
- // However that second context is not properly set up by
- // OpenGLContextView, so we'll stay on the safe side and keep
- // the first context around.
-}
-
-func (c *context) Present() error {
- // Assume the caller already locked the context.
- C.glFlush()
- return nil
-}
-
-func (c *context) Lock() {
- C.gio_lockContext(c.ctx)
-}
-
-func (c *context) Unlock() {
- C.gio_unlockContext(c.ctx)
-}
-
-func (c *context) MakeCurrent() error {
- c.Lock()
- defer c.Unlock()
- C.gio_makeCurrentContext(c.ctx)
- return nil
-}
-
-func (w *window) NewContext() (Context, error) {
- return newContext(w)
-}
diff --git a/vendor/gioui.org/app/internal/window/gl_macos.m b/vendor/gioui.org/app/internal/window/gl_macos.m
deleted file mode 100644
index 0d7f772..0000000
--- a/vendor/gioui.org/app/internal/window/gl_macos.m
+++ /dev/null
@@ -1,143 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build darwin,!ios
-
-@import AppKit;
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <OpenGL/OpenGL.h>
-#include <OpenGL/gl3.h>
-#include "_cgo_export.h"
-
-static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) {
- NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil];
- if (!event.hasPreciseScrollingDeltas) {
- // dx and dy are in rows and columns.
- dx *= 10;
- dy *= 10;
- }
- gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, p.y, dx, dy, [event timestamp], [event modifierFlags]);
-}
-
-@interface GioView : NSOpenGLView
-@end
-
-@implementation GioView
-- (instancetype)initWithFrame:(NSRect)frameRect
- pixelFormat:(NSOpenGLPixelFormat *)format {
- return [super initWithFrame:frameRect pixelFormat:format];
-}
-- (void)prepareOpenGL {
- [super prepareOpenGL];
- // Bind a default VBA to emulate OpenGL ES 2.
- GLuint defVBA;
- glGenVertexArrays(1, &defVBA);
- glBindVertexArray(defVBA);
- glEnable(GL_FRAMEBUFFER_SRGB);
-}
-- (BOOL)isFlipped {
- return YES;
-}
-- (void)update {
- [super update];
- [self setNeedsDisplay:YES];
-}
-- (void)drawRect:(NSRect)r {
- gio_onDraw((__bridge CFTypeRef)self);
-}
-- (void)mouseDown:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0);
-}
-- (void)mouseUp:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_UP, 0, 0);
-}
-- (void)middleMouseDown:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0);
-}
-- (void)middletMouseUp:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_UP, 0, 0);
-}
-- (void)rightMouseDown:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0);
-}
-- (void)rightMouseUp:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_UP, 0, 0);
-}
-- (void)mouseMoved:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_MOVE, 0, 0);
-}
-- (void)mouseDragged:(NSEvent *)event {
- handleMouse(self, event, GIO_MOUSE_MOVE, 0, 0);
-}
-- (void)scrollWheel:(NSEvent *)event {
- CGFloat dx = -event.scrollingDeltaX;
- CGFloat dy = -event.scrollingDeltaY;
- handleMouse(self, event, GIO_MOUSE_SCROLL, dx, dy);
-}
-- (void)keyDown:(NSEvent *)event {
- NSString *keys = [event charactersIgnoringModifiers];
- gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags]);
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
-}
-- (void)insertText:(id)string {
- const char *utf8 = [string UTF8String];
- gio_onText((__bridge CFTypeRef)self, (char *)utf8);
-}
-- (void)doCommandBySelector:(SEL)sel {
- // Don't pass commands up the responder chain.
- // They will end up in a beep.
-}
-@end
-
-CFTypeRef gio_createGLView(void) {
- @autoreleasepool {
- NSOpenGLPixelFormatAttribute attr[] = {
- NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
- NSOpenGLPFAColorSize, 24,
- NSOpenGLPFADepthSize, 16,
- NSOpenGLPFAAccelerated,
- // Opt-in to automatic GPU switching. CGL-only property.
- kCGLPFASupportsAutomaticGraphicsSwitching,
- NSOpenGLPFAAllowOfflineRenderers,
- 0
- };
- id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
-
- NSRect frame = NSMakeRect(0, 0, 0, 0);
- GioView* view = [[GioView alloc] initWithFrame:frame pixelFormat:pixFormat];
-
- [view setWantsBestResolutionOpenGLSurface:YES];
- [view setWantsLayer:YES]; // The default in Mojave.
-
- return CFBridgingRetain(view);
- }
-}
-
-void gio_setNeedsDisplay(CFTypeRef viewRef) {
- NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef;
- [view setNeedsDisplay:YES];
-}
-
-CFTypeRef gio_contextForView(CFTypeRef viewRef) {
- NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef;
- return (__bridge CFTypeRef)view.openGLContext;
-}
-
-void gio_clearCurrentContext(void) {
- [NSOpenGLContext clearCurrentContext];
-}
-
-void gio_makeCurrentContext(CFTypeRef ctxRef) {
- NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
- [ctx makeCurrentContext];
-}
-
-void gio_lockContext(CFTypeRef ctxRef) {
- NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
- CGLLockContext([ctx CGLContextObj]);
-}
-
-void gio_unlockContext(CFTypeRef ctxRef) {
- NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
- CGLUnlockContext([ctx CGLContextObj]);
-}
diff --git a/vendor/gioui.org/app/internal/window/os_android.c b/vendor/gioui.org/app/internal/window/os_android.c
deleted file mode 100644
index 8a2c62d..0000000
--- a/vendor/gioui.org/app/internal/window/os_android.c
+++ /dev/null
@@ -1,96 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-#include <jni.h>
-#include "_cgo_export.h"
-
-jint gio_jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
- return (*vm)->GetEnv(vm, (void **)env, version);
-}
-
-jint gio_jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) {
- return (*env)->GetJavaVM(env, jvm);
-}
-
-jint gio_jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
- return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
-}
-
-jint gio_jni_DetachCurrentThread(JavaVM *vm) {
- return (*vm)->DetachCurrentThread(vm);
-}
-
-jobject gio_jni_NewGlobalRef(JNIEnv *env, jobject obj) {
- return (*env)->NewGlobalRef(env, obj);
-}
-
-void gio_jni_DeleteGlobalRef(JNIEnv *env, jobject obj) {
- (*env)->DeleteGlobalRef(env, obj);
-}
-
-jclass gio_jni_GetObjectClass(JNIEnv *env, jobject obj) {
- return (*env)->GetObjectClass(env, obj);
-}
-
-jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
- return (*env)->GetMethodID(env, clazz, name, sig);
-}
-
-jmethodID gio_jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
- return (*env)->GetStaticMethodID(env, clazz, name, sig);
-}
-
-jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
- return (*env)->CallFloatMethod(env, obj, methodID);
-}
-
-jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
- return (*env)->CallIntMethod(env, obj, methodID);
-}
-
-void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) {
- (*env)->CallStaticVoidMethodA(env, cls, methodID, args);
-}
-
-void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
- (*env)->CallVoidMethodA(env, obj, methodID, args);
-}
-
-jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) {
- return (*env)->GetByteArrayElements(env, arr, NULL);
-}
-
-void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) {
- (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT);
-}
-
-jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr) {
- return (*env)->GetArrayLength(env, arr);
-}
-
-jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) {
- return (*env)->NewString(env, unicodeChars, len);
-}
-
-jsize gio_jni_GetStringLength(JNIEnv *env, jstring str) {
- return (*env)->GetStringLength(env, str);
-}
-
-const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str) {
- return (*env)->GetStringChars(env, str, NULL);
-}
-
-jthrowable gio_jni_ExceptionOccurred(JNIEnv *env) {
- return (*env)->ExceptionOccurred(env);
-}
-
-void gio_jni_ExceptionClear(JNIEnv *env) {
- (*env)->ExceptionClear(env);
-}
-
-jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
- return (*env)->CallObjectMethodA(env, obj, method, args);
-}
-
-jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
- return (*env)->CallStaticObjectMethodA(env, cls, method, args);
-}
diff --git a/vendor/gioui.org/app/internal/window/os_android.go b/vendor/gioui.org/app/internal/window/os_android.go
deleted file mode 100644
index 3458b26..0000000
--- a/vendor/gioui.org/app/internal/window/os_android.go
+++ /dev/null
@@ -1,684 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package window
-
-/*
-#cgo CFLAGS: -Werror
-#cgo LDFLAGS: -landroid
-
-#include <android/native_window_jni.h>
-#include <android/configuration.h>
-#include <android/keycodes.h>
-#include <android/input.h>
-#include <stdlib.h>
-
-__attribute__ ((visibility ("hidden"))) jint gio_jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version);
-__attribute__ ((visibility ("hidden"))) jint gio_jni_GetJavaVM(JNIEnv *env, JavaVM **jvm);
-__attribute__ ((visibility ("hidden"))) jint gio_jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args);
-__attribute__ ((visibility ("hidden"))) jint gio_jni_DetachCurrentThread(JavaVM *vm);
-
-__attribute__ ((visibility ("hidden"))) jobject gio_jni_NewGlobalRef(JNIEnv *env, jobject obj);
-__attribute__ ((visibility ("hidden"))) void gio_jni_DeleteGlobalRef(JNIEnv *env, jobject obj);
-__attribute__ ((visibility ("hidden"))) jclass gio_jni_GetObjectClass(JNIEnv *env, jobject obj);
-__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
-__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
-__attribute__ ((visibility ("hidden"))) jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID);
-__attribute__ ((visibility ("hidden"))) jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID);
-__attribute__ ((visibility ("hidden"))) void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args);
-__attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
-__attribute__ ((visibility ("hidden"))) jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr);
-__attribute__ ((visibility ("hidden"))) void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes);
-__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr);
-__attribute__ ((visibility ("hidden"))) jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
-__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetStringLength(JNIEnv *env, jstring str);
-__attribute__ ((visibility ("hidden"))) const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str);
-__attribute__ ((visibility ("hidden"))) jthrowable gio_jni_ExceptionOccurred(JNIEnv *env);
-__attribute__ ((visibility ("hidden"))) void gio_jni_ExceptionClear(JNIEnv *env);
-__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
-__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args);
-*/
-import "C"
-
-import (
- "errors"
- "fmt"
- "image"
- "reflect"
- "runtime"
- "runtime/debug"
- "sync"
- "time"
- "unicode/utf16"
- "unsafe"
-
- "gioui.org/f32"
- "gioui.org/io/key"
- "gioui.org/io/pointer"
- "gioui.org/io/system"
- "gioui.org/unit"
-)
-
-type window struct {
- callbacks Callbacks
-
- view C.jobject
-
- dpi int
- fontScale float32
- insets system.Insets
-
- stage system.Stage
- started bool
-
- mu sync.Mutex
- win *C.ANativeWindow
- animating bool
-
- // Cached Java methods.
- mgetDensity C.jmethodID
- mgetFontScale C.jmethodID
- mshowTextInput C.jmethodID
- mhideTextInput C.jmethodID
- mpostFrameCallback C.jmethodID
-}
-
-type jvalue uint64 // The largest JNI type fits in 64 bits.
-
-var dataDirChan = make(chan string, 1)
-
-var android struct {
- // mu protects all fields of this structure. However, once a
- // non-nil jvm is returned from javaVM, all the other fields may
- // be accessed unlocked.
- mu sync.Mutex
- jvm *C.JavaVM
-
- // appCtx is the global Android App context.
- appCtx C.jobject
- // gioCls is the class of the Gio class.
- gioCls C.jclass
-
- mwriteClipboard C.jmethodID
- mreadClipboard C.jmethodID
- mwakeupMainThread C.jmethodID
-}
-
-var views = make(map[C.jlong]*window)
-
-var mainWindow = newWindowRendezvous()
-
-var mainFuncs = make(chan func(env *C.JNIEnv), 1)
-
-func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
- m := C.CString(method)
- defer C.free(unsafe.Pointer(m))
- s := C.CString(sig)
- defer C.free(unsafe.Pointer(s))
- jm := C.gio_jni_GetMethodID(env, class, m, s)
- if err := exception(env); err != nil {
- panic(err)
- }
- return jm
-}
-
-func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
- m := C.CString(method)
- defer C.free(unsafe.Pointer(m))
- s := C.CString(sig)
- defer C.free(unsafe.Pointer(s))
- jm := C.gio_jni_GetStaticMethodID(env, class, m, s)
- if err := exception(env); err != nil {
- panic(err)
- }
- return jm
-}
-
-//export Java_org_gioui_Gio_runGoMain
-func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) {
- initJVM(env, class, context)
- dirBytes := C.gio_jni_GetByteArrayElements(env, jdataDir)
- if dirBytes == nil {
- panic("runGoMain: GetByteArrayElements failed")
- }
- n := C.gio_jni_GetArrayLength(env, jdataDir)
- dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
- dataDirChan <- dataDir
- C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
-
- runMain()
-}
-
-func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) {
- android.mu.Lock()
- defer android.mu.Unlock()
- if res := C.gio_jni_GetJavaVM(env, &android.jvm); res != 0 {
- panic("gio: GetJavaVM failed")
- }
- android.appCtx = C.gio_jni_NewGlobalRef(env, ctx)
- android.gioCls = C.jclass(C.gio_jni_NewGlobalRef(env, C.jobject(gio)))
- android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V")
- android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;")
- android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V")
-}
-
-func JavaVM() uintptr {
- jvm := javaVM()
- return uintptr(unsafe.Pointer(jvm))
-}
-
-func javaVM() *C.JavaVM {
- android.mu.Lock()
- defer android.mu.Unlock()
- return android.jvm
-}
-
-func AppContext() uintptr {
- android.mu.Lock()
- defer android.mu.Unlock()
- return uintptr(android.appCtx)
-}
-
-func GetDataDir() string {
- return <-dataDirChan
-}
-
-//export Java_org_gioui_GioView_onCreateView
-func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
- view = C.gio_jni_NewGlobalRef(env, view)
- w := &window{
- view: view,
- mgetDensity: getMethodID(env, class, "getDensity", "()I"),
- mgetFontScale: getMethodID(env, class, "getFontScale", "()F"),
- mshowTextInput: getMethodID(env, class, "showTextInput", "()V"),
- mhideTextInput: getMethodID(env, class, "hideTextInput", "()V"),
- mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"),
- }
- wopts := <-mainWindow.out
- w.callbacks = wopts.window
- w.callbacks.SetDriver(w)
- handle := C.jlong(view)
- views[handle] = w
- w.loadConfig(env, class)
- w.setStage(system.StagePaused)
- return handle
-}
-
-//export Java_org_gioui_GioView_onDestroyView
-func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := views[handle]
- w.callbacks.SetDriver(nil)
- delete(views, handle)
- C.gio_jni_DeleteGlobalRef(env, w.view)
- w.view = 0
-}
-
-//export Java_org_gioui_GioView_onStopView
-func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := views[handle]
- w.started = false
- w.setStage(system.StagePaused)
-}
-
-//export Java_org_gioui_GioView_onStartView
-func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := views[handle]
- w.started = true
- if w.aNativeWindow() != nil {
- w.setVisible()
- }
-}
-
-//export Java_org_gioui_GioView_onSurfaceDestroyed
-func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := views[handle]
- w.mu.Lock()
- w.win = nil
- w.mu.Unlock()
- w.setStage(system.StagePaused)
-}
-
-//export Java_org_gioui_GioView_onSurfaceChanged
-func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) {
- w := views[handle]
- w.mu.Lock()
- w.win = C.ANativeWindow_fromSurface(env, surf)
- w.mu.Unlock()
- if w.started {
- w.setVisible()
- }
-}
-
-//export Java_org_gioui_GioView_onLowMemory
-func Java_org_gioui_GioView_onLowMemory() {
- runtime.GC()
- debug.FreeOSMemory()
-}
-
-//export Java_org_gioui_GioView_onConfigurationChanged
-func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) {
- w := views[view]
- w.loadConfig(env, class)
- if w.stage >= system.StageRunning {
- w.draw(true)
- }
-}
-
-//export Java_org_gioui_GioView_onFrameCallback
-func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong, nanos C.jlong) {
- w, exist := views[view]
- if !exist {
- return
- }
- if w.stage < system.StageRunning {
- return
- }
- w.mu.Lock()
- anim := w.animating
- w.mu.Unlock()
- if anim {
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- callVoidMethod(env, w.view, w.mpostFrameCallback)
- })
- w.draw(false)
- }
-}
-
-//export Java_org_gioui_GioView_onBack
-func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
- w := views[view]
- ev := &system.CommandEvent{Type: system.CommandBack}
- w.callbacks.Event(ev)
- if ev.Cancel {
- return C.JNI_TRUE
- }
- return C.JNI_FALSE
-}
-
-//export Java_org_gioui_GioView_onFocusChange
-func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
- w := views[view]
- w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE})
-}
-
-//export Java_org_gioui_GioView_onWindowInsets
-func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) {
- w := views[view]
- w.insets = system.Insets{
- Top: unit.Px(float32(top)),
- Right: unit.Px(float32(right)),
- Bottom: unit.Px(float32(bottom)),
- Left: unit.Px(float32(left)),
- }
- if w.stage >= system.StageRunning {
- w.draw(true)
- }
-}
-
-func (w *window) setVisible() {
- win := w.aNativeWindow()
- width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win)
- if width == 0 || height == 0 {
- return
- }
- w.setStage(system.StageRunning)
- w.draw(true)
-}
-
-func (w *window) setStage(stage system.Stage) {
- if stage == w.stage {
- return
- }
- w.stage = stage
- w.callbacks.Event(system.StageEvent{stage})
-}
-
-func (w *window) nativeWindow(visID int) (*C.ANativeWindow, int, int) {
- win := w.aNativeWindow()
- var width, height int
- if win != nil {
- if C.ANativeWindow_setBuffersGeometry(win, 0, 0, C.int32_t(visID)) != 0 {
- panic(errors.New("ANativeWindow_setBuffersGeometry failed"))
- }
- w, h := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win)
- width, height = int(w), int(h)
- }
- return win, width, height
-}
-
-func (w *window) aNativeWindow() *C.ANativeWindow {
- w.mu.Lock()
- defer w.mu.Unlock()
- return w.win
-}
-
-func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) {
- dpi := int(C.gio_jni_CallIntMethod(env, w.view, w.mgetDensity))
- w.fontScale = float32(C.gio_jni_CallFloatMethod(env, w.view, w.mgetFontScale))
- switch dpi {
- case C.ACONFIGURATION_DENSITY_NONE,
- C.ACONFIGURATION_DENSITY_DEFAULT,
- C.ACONFIGURATION_DENSITY_ANY:
- // Assume standard density.
- w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM
- default:
- w.dpi = int(dpi)
- }
-}
-
-func (w *window) SetAnimating(anim bool) {
- w.mu.Lock()
- w.animating = anim
- w.mu.Unlock()
- if anim {
- runOnMain(func(env *C.JNIEnv) {
- if w.view == 0 {
- // View was destroyed while switching to main thread.
- return
- }
- callVoidMethod(env, w.view, w.mpostFrameCallback)
- })
- }
-}
-
-func (w *window) draw(sync bool) {
- win := w.aNativeWindow()
- width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win)
- if width == 0 || height == 0 {
- return
- }
- const inchPrDp = 1.0 / 160
- ppdp := float32(w.dpi) * inchPrDp
- w.callbacks.Event(FrameEvent{
- FrameEvent: system.FrameEvent{
- Now: time.Now(),
- Size: image.Point{
- X: int(width),
- Y: int(height),
- },
- Insets: w.insets,
- Metric: unit.Metric{
- PxPerDp: ppdp,
- PxPerSp: w.fontScale * ppdp,
- },
- },
- Sync: sync,
- })
-}
-
-type keyMapper func(devId, keyCode C.int32_t) rune
-
-func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
- if jvm == nil {
- panic("nil JVM")
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- var env *C.JNIEnv
- if res := C.gio_jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
- if res != C.JNI_EDETACHED {
- panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
- }
- if C.gio_jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
- panic(errors.New("runInJVM: AttachCurrentThread failed"))
- }
- defer C.gio_jni_DetachCurrentThread(jvm)
- }
-
- f(env)
-}
-
-func convertKeyCode(code C.jint) (string, bool) {
- var n string
- switch code {
- case C.AKEYCODE_DPAD_UP:
- n = key.NameUpArrow
- case C.AKEYCODE_DPAD_DOWN:
- n = key.NameDownArrow
- case C.AKEYCODE_DPAD_LEFT:
- n = key.NameLeftArrow
- case C.AKEYCODE_DPAD_RIGHT:
- n = key.NameRightArrow
- case C.AKEYCODE_FORWARD_DEL:
- n = key.NameDeleteForward
- case C.AKEYCODE_DEL:
- n = key.NameDeleteBackward
- default:
- return "", false
- }
- return n, true
-}
-
-//export Java_org_gioui_GioView_onKeyEvent
-func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) {
- w := views[handle]
- if n, ok := convertKeyCode(keyCode); ok {
- w.callbacks.Event(key.Event{Name: n})
- }
- if r != 0 {
- w.callbacks.Event(key.EditEvent{Text: string(rune(r))})
- }
-}
-
-//export Java_org_gioui_GioView_onTouchEvent
-func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) {
- w := views[handle]
- var typ pointer.Type
- switch action {
- case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
- typ = pointer.Press
- case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
- typ = pointer.Release
- case C.AMOTION_EVENT_ACTION_CANCEL:
- typ = pointer.Cancel
- case C.AMOTION_EVENT_ACTION_MOVE:
- typ = pointer.Move
- case C.AMOTION_EVENT_ACTION_SCROLL:
- typ = pointer.Scroll
- default:
- return
- }
- var src pointer.Source
- var btns pointer.Buttons
- if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 {
- btns |= pointer.ButtonLeft
- }
- if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 {
- btns |= pointer.ButtonRight
- }
- if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 {
- btns |= pointer.ButtonMiddle
- }
- switch tool {
- case C.AMOTION_EVENT_TOOL_TYPE_FINGER:
- src = pointer.Touch
- case C.AMOTION_EVENT_TOOL_TYPE_MOUSE:
- src = pointer.Mouse
- case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
- // For example, triggered via 'adb shell input tap'.
- // Instead of discarding it, treat it as a touch event.
- src = pointer.Touch
- default:
- return
- }
- w.callbacks.Event(pointer.Event{
- Type: typ,
- Source: src,
- Buttons: btns,
- PointerID: pointer.ID(pointerID),
- Time: time.Duration(t) * time.Millisecond,
- Position: f32.Point{X: float32(x), Y: float32(y)},
- Scroll: f32.Pt(float32(scrollX), float32(scrollY)),
- })
-}
-
-func (w *window) ShowTextInput(show bool) {
- if w.view == 0 {
- return
- }
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- if show {
- callVoidMethod(env, w.view, w.mshowTextInput)
- } else {
- callVoidMethod(env, w.view, w.mhideTextInput)
- }
- })
-}
-
-func javaString(env *C.JNIEnv, str string) C.jstring {
- if str == "" {
- return 0
- }
- utf16Chars := utf16.Encode([]rune(str))
- return C.gio_jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars)))
-}
-
-// Do invokes the function with a global JNI handle to the view. If
-// the view is destroyed, Do returns false and does not invoke the
-// function.
-//
-// NOTE: Do must be invoked on the Android main thread.
-func (w *window) Do(f func(view uintptr)) bool {
- if w.view == 0 {
- return false
- }
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- view := C.gio_jni_NewGlobalRef(env, w.view)
- defer C.gio_jni_DeleteGlobalRef(env, view)
- f(uintptr(view))
- })
- return true
-}
-
-func varArgs(args []jvalue) *C.jvalue {
- if len(args) == 0 {
- return nil
- }
- return (*C.jvalue)(unsafe.Pointer(&args[0]))
-}
-
-func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error {
- C.gio_jni_CallStaticVoidMethodA(env, cls, method, varArgs(args))
- return exception(env)
-}
-
-func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
- res := C.gio_jni_CallStaticObjectMethodA(env, cls, method, varArgs(args))
- return res, exception(env)
-}
-
-func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error {
- C.gio_jni_CallVoidMethodA(env, obj, method, varArgs(args))
- return exception(env)
-}
-
-func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) {
- res := C.gio_jni_CallObjectMethodA(env, obj, method, varArgs(args))
- return res, exception(env)
-}
-
-// exception returns an error corresponding to the pending
-// exception, or nil if no exception is pending. The pending
-// exception is cleared.
-func exception(env *C.JNIEnv) error {
- thr := C.gio_jni_ExceptionOccurred(env)
- if thr == 0 {
- return nil
- }
- C.gio_jni_ExceptionClear(env)
- cls := getObjectClass(env, C.jobject(thr))
- toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;")
- msg, err := callObjectMethod(env, C.jobject(thr), toString)
- if err != nil {
- return err
- }
- return errors.New(goString(env, C.jstring(msg)))
-}
-
-func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass {
- if obj == 0 {
- panic("null object")
- }
- cls := C.gio_jni_GetObjectClass(env, C.jobject(obj))
- if err := exception(env); err != nil {
- // GetObjectClass should never fail.
- panic(err)
- }
- return cls
-}
-
-// goString converts the JVM jstring to a Go string.
-func goString(env *C.JNIEnv, str C.jstring) string {
- if str == 0 {
- return ""
- }
- strlen := C.gio_jni_GetStringLength(env, C.jstring(str))
- chars := C.gio_jni_GetStringChars(env, C.jstring(str))
- var utf16Chars []uint16
- hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars))
- hdr.Data = uintptr(unsafe.Pointer(chars))
- hdr.Cap = int(strlen)
- hdr.Len = int(strlen)
- utf8 := utf16.Decode(utf16Chars)
- return string(utf8)
-}
-
-func Main() {
-}
-
-func NewWindow(window Callbacks, opts *Options) error {
- mainWindow.in <- windowAndOptions{window, opts}
- return <-mainWindow.errs
-}
-
-func (w *window) WriteClipboard(s string) {
- runOnMain(func(env *C.JNIEnv) {
- jstr := javaString(env, s)
- callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard,
- jvalue(android.appCtx), jvalue(jstr))
- })
-}
-
-func (w *window) ReadClipboard() {
- runOnMain(func(env *C.JNIEnv) {
- c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard,
- jvalue(android.appCtx))
- if err != nil {
- return
- }
- content := goString(env, C.jstring(c))
- w.callbacks.Event(system.ClipboardEvent{Text: content})
- })
-}
-
-// Close the window. Not implemented for Android.
-func (w *window) Close() {}
-
-// RunOnMain is the exported version of runOnMain without a JNI
-// environement.
-func RunOnMain(f func()) {
- runOnMain(func(_ *C.JNIEnv) {
- f()
- })
-}
-
-// runOnMain runs a function on the Java main thread.
-func runOnMain(f func(env *C.JNIEnv)) {
- go func() {
- mainFuncs <- f
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread)
- })
- }()
-}
-
-//export Java_org_gioui_Gio_scheduleMainFuncs
-func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
- for {
- select {
- case f := <-mainFuncs:
- f(env)
- default:
- return
- }
- }
-}
diff --git a/vendor/gioui.org/app/internal/window/os_darwin.m b/vendor/gioui.org/app/internal/window/os_darwin.m
deleted file mode 100644
index dc4f00b..0000000
--- a/vendor/gioui.org/app/internal/window/os_darwin.m
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-@import Dispatch;
-@import Foundation;
-
-#include "_cgo_export.h"
-
-void gio_wakeupMainThread(void) {
- dispatch_async(dispatch_get_main_queue(), ^{
- gio_dispatchMainFuncs();
- });
-}
-
-NSUInteger gio_nsstringLength(CFTypeRef cstr) {
- NSString *str = (__bridge NSString *)cstr;
- return [str length];
-}
-
-void gio_nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc, NSUInteger length) {
- NSString *str = (__bridge NSString *)cstr;
- [str getCharacters:chars range:NSMakeRange(loc, length)];
-}
diff --git a/vendor/gioui.org/app/internal/window/os_macos.go b/vendor/gioui.org/app/internal/window/os_macos.go
deleted file mode 100644
index ef95b70..0000000
--- a/vendor/gioui.org/app/internal/window/os_macos.go
+++ /dev/null
@@ -1,460 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build darwin,!ios
-
-package window
-
-import (
- "errors"
- "image"
- "runtime"
- "time"
- "unicode"
- "unicode/utf16"
- "unsafe"
-
- "gioui.org/f32"
- "gioui.org/io/key"
- "gioui.org/io/pointer"
- "gioui.org/io/system"
- "gioui.org/unit"
-
- _ "gioui.org/app/internal/cocoainit"
-)
-
-/*
-#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c
-
-#include <AppKit/AppKit.h>
-
-#define GIO_MOUSE_MOVE 1
-#define GIO_MOUSE_UP 2
-#define GIO_MOUSE_DOWN 3
-#define GIO_MOUSE_SCROLL 4
-
-__attribute__ ((visibility ("hidden"))) void gio_main(void);
-__attribute__ ((visibility ("hidden"))) CGFloat gio_viewWidth(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) CGFloat gio_viewHeight(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) CGFloat gio_getViewBackingScale(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) CGFloat gio_getScreenBackingScale(void);
-__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
-__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
-__attribute__ ((visibility ("hidden"))) void gio_setNeedsDisplay(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
-__attribute__ ((visibility ("hidden"))) void gio_makeKeyAndOrderFront(CFTypeRef windowRef);
-__attribute__ ((visibility ("hidden"))) NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft);
-__attribute__ ((visibility ("hidden"))) void gio_close(CFTypeRef windowRef);
-*/
-import "C"
-
-func init() {
- // Darwin requires that UI operations happen on the main thread only.
- runtime.LockOSThread()
-}
-
-type window struct {
- view C.CFTypeRef
- window C.CFTypeRef
- w Callbacks
- stage system.Stage
- displayLink *displayLink
-
- scale float32
-}
-
-// viewMap is the mapping from Cocoa NSViews to Go windows.
-var viewMap = make(map[C.CFTypeRef]*window)
-
-var viewFactory func() C.CFTypeRef
-
-// launched is closed when applicationDidFinishLaunching is called.
-var launched = make(chan struct{})
-
-// nextTopLeft is the offset to use for the next window's call to
-// cascadeTopLeftFromPoint.
-var nextTopLeft C.NSPoint
-
-// mustView is like lookoupView, except that it panics
-// if the view isn't mapped.
-func mustView(view C.CFTypeRef) *window {
- w, ok := lookupView(view)
- if !ok {
- panic("no window for view")
- }
- return w
-}
-
-func lookupView(view C.CFTypeRef) (*window, bool) {
- w, exists := viewMap[view]
- if !exists {
- return nil, false
- }
- return w, true
-}
-
-func deleteView(view C.CFTypeRef) {
- delete(viewMap, view)
-}
-
-func insertView(view C.CFTypeRef, w *window) {
- viewMap[view] = w
-}
-
-func (w *window) contextView() C.CFTypeRef {
- return w.view
-}
-
-func (w *window) ReadClipboard() {
- runOnMain(func() {
- content := nsstringToString(C.gio_readClipboard())
- w.w.Event(system.ClipboardEvent{Text: content})
- })
-}
-
-func (w *window) WriteClipboard(s string) {
- u16 := utf16.Encode([]rune(s))
- runOnMain(func() {
- var chars *C.unichar
- if len(u16) > 0 {
- chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
- }
- C.gio_writeClipboard(chars, C.NSUInteger(len(u16)))
- })
-}
-
-func (w *window) ShowTextInput(show bool) {}
-
-func (w *window) SetAnimating(anim bool) {
- if anim {
- w.displayLink.Start()
- } else {
- w.displayLink.Stop()
- }
-}
-
-func (w *window) Close() {
- runOnMain(func() {
- // Make sure the view is still valid. The window might've been closed
- // during the switch to the main thread.
- if w.view != 0 {
- C.gio_close(w.window)
- }
- })
-}
-
-func (w *window) setStage(stage system.Stage) {
- if stage == w.stage {
- return
- }
- w.stage = stage
- w.w.Event(system.StageEvent{Stage: stage})
-}
-
-//export gio_onKeys
-func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) {
- str := C.GoString(cstr)
- kmods := convertMods(mods)
- w := mustView(view)
- for _, k := range str {
- if n, ok := convertKey(k); ok {
- w.w.Event(key.Event{
- Name: n,
- Modifiers: kmods,
- })
- }
- }
-}
-
-//export gio_onText
-func gio_onText(view C.CFTypeRef, cstr *C.char) {
- str := C.GoString(cstr)
- w := mustView(view)
- w.w.Event(key.EditEvent{Text: str})
-}
-
-//export gio_onMouse
-func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) {
- var typ pointer.Type
- switch cdir {
- case C.GIO_MOUSE_MOVE:
- typ = pointer.Move
- case C.GIO_MOUSE_UP:
- typ = pointer.Release
- case C.GIO_MOUSE_DOWN:
- typ = pointer.Press
- case C.GIO_MOUSE_SCROLL:
- typ = pointer.Scroll
- default:
- panic("invalid direction")
- }
- var btns pointer.Buttons
- if cbtns&(1<<0) != 0 {
- btns |= pointer.ButtonLeft
- }
- if cbtns&(1<<1) != 0 {
- btns |= pointer.ButtonRight
- }
- if cbtns&(1<<2) != 0 {
- btns |= pointer.ButtonMiddle
- }
- t := time.Duration(float64(ti)*float64(time.Second) + .5)
- w := mustView(view)
- xf, yf := float32(x)*w.scale, float32(y)*w.scale
- dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale
- w.w.Event(pointer.Event{
- Type: typ,
- Source: pointer.Mouse,
- Time: t,
- Buttons: btns,
- Position: f32.Point{X: xf, Y: yf},
- Scroll: f32.Point{X: dxf, Y: dyf},
- Modifiers: convertMods(mods),
- })
-}
-
-//export gio_onDraw
-func gio_onDraw(view C.CFTypeRef) {
- w := mustView(view)
- w.draw()
-}
-
-//export gio_onFocus
-func gio_onFocus(view C.CFTypeRef, focus C.BOOL) {
- w := mustView(view)
- w.w.Event(key.FocusEvent{Focus: focus == C.YES})
-}
-
-//export gio_onChangeScreen
-func gio_onChangeScreen(view C.CFTypeRef, did uint64) {
- w := mustView(view)
- w.displayLink.SetDisplayID(did)
-}
-
-func (w *window) draw() {
- w.scale = float32(C.gio_getViewBackingScale(w.view))
- wf, hf := float32(C.gio_viewWidth(w.view)), float32(C.gio_viewHeight(w.view))
- if wf == 0 || hf == 0 {
- return
- }
- width := int(wf*w.scale + .5)
- height := int(hf*w.scale + .5)
- cfg := configFor(w.scale)
- w.setStage(system.StageRunning)
- w.w.Event(FrameEvent{
- FrameEvent: system.FrameEvent{
- Now: time.Now(),
- Size: image.Point{
- X: width,
- Y: height,
- },
- Metric: cfg,
- },
- Sync: true,
- })
-}
-
-func configFor(scale float32) unit.Metric {
- return unit.Metric{
- PxPerDp: scale,
- PxPerSp: scale,
- }
-}
-
-//export gio_onClose
-func gio_onClose(view C.CFTypeRef) {
- w := mustView(view)
- w.displayLink.Close()
- deleteView(view)
- w.w.Event(system.DestroyEvent{})
- C.CFRelease(w.view)
- w.view = 0
- C.CFRelease(w.window)
- w.window = 0
-}
-
-//export gio_onHide
-func gio_onHide(view C.CFTypeRef) {
- w := mustView(view)
- w.setStage(system.StagePaused)
-}
-
-//export gio_onShow
-func gio_onShow(view C.CFTypeRef) {
- w := mustView(view)
- w.setStage(system.StageRunning)
-}
-
-//export gio_onAppHide
-func gio_onAppHide() {
- for _, w := range viewMap {
- w.setStage(system.StagePaused)
- }
-}
-
-//export gio_onAppShow
-func gio_onAppShow() {
- for _, w := range viewMap {
- w.setStage(system.StageRunning)
- }
-}
-
-//export gio_onFinishLaunching
-func gio_onFinishLaunching() {
- close(launched)
-}
-
-func NewWindow(win Callbacks, opts *Options) error {
- <-launched
- errch := make(chan error)
- runOnMain(func() {
- w, err := newWindow(opts)
- if err != nil {
- errch <- err
- return
- }
- screenScale := float32(C.gio_getScreenBackingScale())
- cfg := configFor(screenScale)
- width := cfg.Px(opts.Width)
- height := cfg.Px(opts.Height)
- // Window sizes is in unscaled screen coordinates, not device pixels.
- width = int(float32(width) / screenScale)
- height = int(float32(height) / screenScale)
- minWidth := cfg.Px(opts.MinWidth)
- minHeight := cfg.Px(opts.MinHeight)
- minWidth = int(float32(minWidth) / screenScale)
- minHeight = int(float32(minHeight) / screenScale)
- maxWidth := cfg.Px(opts.MaxWidth)
- maxHeight := cfg.Px(opts.MaxHeight)
- maxWidth = int(float32(maxWidth) / screenScale)
- maxHeight = int(float32(maxHeight) / screenScale)
- title := C.CString(opts.Title)
- defer C.free(unsafe.Pointer(title))
- errch <- nil
- win.SetDriver(w)
- w.w = win
- w.window = C.gio_createWindow(w.view, title, C.CGFloat(width), C.CGFloat(height),
- C.CGFloat(minWidth), C.CGFloat(minHeight), C.CGFloat(maxWidth), C.CGFloat(maxHeight))
- if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
- // cascadeTopLeftFromPoint treats (0, 0) as a no-op,
- // and just returns the offset we need for the first window.
- nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft)
- }
- nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft)
- C.gio_makeKeyAndOrderFront(w.window)
- })
- return <-errch
-}
-
-func newWindow(opts *Options) (*window, error) {
- view := viewFactory()
- if view == 0 {
- return nil, errors.New("CreateWindow: failed to create view")
- }
- scale := float32(C.gio_getViewBackingScale(view))
- w := &window{
- view: view,
- scale: scale,
- }
- dl, err := NewDisplayLink(func() {
- runOnMain(func() {
- if w.view != 0 {
- C.gio_setNeedsDisplay(w.view)
- }
- })
- })
- w.displayLink = dl
- if err != nil {
- C.CFRelease(view)
- return nil, err
- }
- insertView(view, w)
- return w, nil
-}
-
-func Main() {
- C.gio_main()
-}
-
-func convertKey(k rune) (string, bool) {
- var n string
- switch k {
- case 0x1b:
- n = key.NameEscape
- case C.NSLeftArrowFunctionKey:
- n = key.NameLeftArrow
- case C.NSRightArrowFunctionKey:
- n = key.NameRightArrow
- case C.NSUpArrowFunctionKey:
- n = key.NameUpArrow
- case C.NSDownArrowFunctionKey:
- n = key.NameDownArrow
- case 0xd:
- n = key.NameReturn
- case 0x3:
- n = key.NameEnter
- case C.NSHomeFunctionKey:
- n = key.NameHome
- case C.NSEndFunctionKey:
- n = key.NameEnd
- case 0x7f:
- n = key.NameDeleteBackward
- case C.NSDeleteFunctionKey:
- n = key.NameDeleteForward
- case C.NSPageUpFunctionKey:
- n = key.NamePageUp
- case C.NSPageDownFunctionKey:
- n = key.NamePageDown
- case C.NSF1FunctionKey:
- n = "F1"
- case C.NSF2FunctionKey:
- n = "F2"
- case C.NSF3FunctionKey:
- n = "F3"
- case C.NSF4FunctionKey:
- n = "F4"
- case C.NSF5FunctionKey:
- n = "F5"
- case C.NSF6FunctionKey:
- n = "F6"
- case C.NSF7FunctionKey:
- n = "F7"
- case C.NSF8FunctionKey:
- n = "F8"
- case C.NSF9FunctionKey:
- n = "F9"
- case C.NSF10FunctionKey:
- n = "F10"
- case C.NSF11FunctionKey:
- n = "F11"
- case C.NSF12FunctionKey:
- n = "F12"
- case 0x09, 0x19:
- n = key.NameTab
- case 0x20:
- n = "Space"
- default:
- k = unicode.ToUpper(k)
- if !unicode.IsPrint(k) {
- return "", false
- }
- n = string(k)
- }
- return n, true
-}
-
-func convertMods(mods C.NSUInteger) key.Modifiers {
- var kmods key.Modifiers
- if mods&C.NSAlternateKeyMask != 0 {
- kmods |= key.ModAlt
- }
- if mods&C.NSControlKeyMask != 0 {
- kmods |= key.ModCtrl
- }
- if mods&C.NSCommandKeyMask != 0 {
- kmods |= key.ModCommand
- }
- if mods&C.NSShiftKeyMask != 0 {
- kmods |= key.ModShift
- }
- return kmods
-}
diff --git a/vendor/gioui.org/app/internal/window/os_unix.go b/vendor/gioui.org/app/internal/window/os_unix.go
deleted file mode 100644
index baaebb5..0000000
--- a/vendor/gioui.org/app/internal/window/os_unix.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build linux,!android freebsd openbsd
-
-package window
-
-import (
- "errors"
-)
-
-func Main() {
- select {}
-}
-
-// instead of creating files with build tags for each combination of wayland +/- x11
-// let each driver initialize these variables with their own version of createWindow.
-var wlDriver, x11Driver func(Callbacks, *Options) error
-
-func NewWindow(window Callbacks, opts *Options) error {
- var errFirst, err error
- if wlDriver != nil {
- if err = wlDriver(window, opts); err == nil {
- return nil
- }
- errFirst = err
- }
- if x11Driver != nil {
- if err = x11Driver(window, opts); err == nil {
- return nil
- }
- if errFirst == nil {
- errFirst = err
- }
- }
- if errFirst != nil {
- return errFirst
- }
- return errors.New("app: no window driver available")
-}
diff --git a/vendor/gioui.org/app/internal/window/window.go b/vendor/gioui.org/app/internal/window/window.go
deleted file mode 100644
index 30ba360..0000000
--- a/vendor/gioui.org/app/internal/window/window.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// Package window implements platform specific windows
-// and GPU contexts.
-package window
-
-import (
- "errors"
-
- "gioui.org/gpu/backend"
- "gioui.org/io/event"
- "gioui.org/io/system"
- "gioui.org/unit"
-)
-
-type Options struct {
- Width, Height unit.Value
- MinWidth, MinHeight unit.Value
- MaxWidth, MaxHeight unit.Value
- Title string
-}
-
-type FrameEvent struct {
- system.FrameEvent
-
- Sync bool
-}
-
-type Callbacks interface {
- SetDriver(d Driver)
- Event(e event.Event)
-}
-
-type Context interface {
- Backend() (backend.Device, error)
- Present() error
- MakeCurrent() error
- Release()
- Lock()
- Unlock()
-}
-
-// ErrDeviceLost is returned from Context.Present when
-// the underlying GPU device is gone and should be
-// recreated.
-var ErrDeviceLost = errors.New("GPU device lost")
-
-// Driver is the interface for the platform implementation
-// of a window.
-type Driver interface {
- // SetAnimating sets the animation flag. When the window is animating,
- // FrameEvents are delivered as fast as the display can handle them.
- SetAnimating(anim bool)
- // ShowTextInput updates the virtual keyboard state.
- ShowTextInput(show bool)
- NewContext() (Context, error)
-
- // ReadClipboard requests the clipboard content.
- ReadClipboard()
- // WriteClipboard requests a clipboard write.
- WriteClipboard(s string)
-
- // Close the window.
- Close()
-}
-
-type windowRendezvous struct {
- in chan windowAndOptions
- out chan windowAndOptions
- errs chan error
-}
-
-type windowAndOptions struct {
- window Callbacks
- opts *Options
-}
-
-func newWindowRendezvous() *windowRendezvous {
- wr := &windowRendezvous{
- in: make(chan windowAndOptions),
- out: make(chan windowAndOptions),
- errs: make(chan error),
- }
- go func() {
- var main windowAndOptions
- var out chan windowAndOptions
- for {
- select {
- case w := <-wr.in:
- var err error
- if main.window != nil {
- err = errors.New("multiple windows are not supported")
- }
- wr.errs <- err
- main = w
- out = wr.out
- case out <- main:
- }
- }
- }()
- return wr
-}
diff --git a/vendor/gioui.org/app/internal/windows/windows.go b/vendor/gioui.org/app/internal/windows/windows.go
index 9006326..65a01d3 100644
--- a/vendor/gioui.org/app/internal/windows/windows.go
+++ b/vendor/gioui.org/app/internal/windows/windows.go
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build windows
// +build windows
package windows
@@ -54,14 +55,44 @@ type MinMaxInfo struct {
PtMaxTrackSize Point
}
+type WindowPlacement struct {
+ length uint32
+ flags uint32
+ showCmd uint32
+ ptMinPosition Point
+ ptMaxPosition Point
+ rcNormalPosition Rect
+ rcDevice Rect
+}
+
+type MonitorInfo struct {
+ cbSize uint32
+ Monitor Rect
+ WorkArea Rect
+ Flags uint32
+}
+
const (
+ TRUE = 1
+
CS_HREDRAW = 0x0002
CS_VREDRAW = 0x0001
CS_OWNDC = 0x0020
CW_USEDEFAULT = -2147483648
- IDC_ARROW = 32512
+ GWL_STYLE = ^(uint32(16) - 1) // -16
+ HWND_TOPMOST = ^(uint32(1) - 1) // -1
+
+ HTCLIENT = 1
+
+ IDC_ARROW = 32512
+ IDC_IBEAM = 32513
+ IDC_HAND = 32649
+ IDC_CROSS = 32515
+ IDC_SIZENS = 32645
+ IDC_SIZEWE = 32644
+ IDC_SIZEALL = 32646
INFINITE = 0xFFFFFFFF
@@ -77,6 +108,13 @@ const (
SW_SHOWDEFAULT = 10
+ SWP_FRAMECHANGED = 0x0020
+ SWP_NOMOVE = 0x0002
+ SWP_NOOWNERZORDER = 0x0200
+ SWP_NOSIZE = 0x0001
+ SWP_NOZORDER = 0x0004
+ SWP_SHOWWINDOW = 0x0040
+
USER_TIMER_MINIMUM = 0x0000000A
VK_CONTROL = 0x11
@@ -142,14 +180,17 @@ const (
WM_MBUTTONUP = 0x0208
WM_MOUSEMOVE = 0x0200
WM_MOUSEWHEEL = 0x020A
+ WM_MOUSEHWHEEL = 0x020E
WM_PAINT = 0x000F
WM_CLOSE = 0x0010
WM_QUIT = 0x0012
+ WM_SETCURSOR = 0x0020
WM_SETFOCUS = 0x0007
WM_KILLFOCUS = 0x0008
WM_SHOWWINDOW = 0x0018
WM_SIZE = 0x0005
WM_SYSKEYDOWN = 0x0104
+ WM_SYSKEYUP = 0x0105
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205
WM_TIMER = 0x0113
@@ -159,6 +200,7 @@ const (
WS_CLIPCHILDREN = 0x00010000
WS_CLIPSIBLINGS = 0x04000000
+ WS_MAXIMIZE = 0x01000000
WS_VISIBLE = 0x10000000
WS_OVERLAPPED = 0x00000000
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
@@ -185,6 +227,19 @@ const (
GHND = 0x0042
CF_UNICODETEXT = 13
+ IMAGE_BITMAP = 0
+ IMAGE_ICON = 1
+ IMAGE_CURSOR = 2
+
+ LR_CREATEDIBSECTION = 0x00002000
+ LR_DEFAULTCOLOR = 0x00000000
+ LR_DEFAULTSIZE = 0x00000040
+ LR_LOADFROMFILE = 0x00000010
+ LR_LOADMAP3DCOLORS = 0x00001000
+ LR_LOADTRANSPARENT = 0x00000020
+ LR_MONOCHROME = 0x00000001
+ LR_SHARED = 0x00008000
+ LR_VGACOLOR = 0x00000080
)
var (
@@ -207,12 +262,20 @@ var (
_GetClientRect = user32.NewProc("GetClientRect")
_GetClipboardData = user32.NewProc("GetClipboardData")
_GetDC = user32.NewProc("GetDC")
+ _GetDpiForWindow = user32.NewProc("GetDpiForWindow")
_GetKeyState = user32.NewProc("GetKeyState")
_GetMessage = user32.NewProc("GetMessageW")
_GetMessageTime = user32.NewProc("GetMessageTime")
+ _GetMonitorInfo = user32.NewProc("GetMonitorInfoW")
+ _GetWindowLong = user32.NewProc("GetWindowLongPtrW")
+ _GetWindowLong32 = user32.NewProc("GetWindowLongW")
+ _GetWindowPlacement = user32.NewProc("GetWindowPlacement")
_KillTimer = user32.NewProc("KillTimer")
_LoadCursor = user32.NewProc("LoadCursorW")
+ _LoadImage = user32.NewProc("LoadImageW")
_MonitorFromPoint = user32.NewProc("MonitorFromPoint")
+ _MonitorFromWindow = user32.NewProc("MonitorFromWindow")
+ _MoveWindow = user32.NewProc("MoveWindow")
_MsgWaitForMultipleObjectsEx = user32.NewProc("MsgWaitForMultipleObjectsEx")
_OpenClipboard = user32.NewProc("OpenClipboard")
_PeekMessage = user32.NewProc("PeekMessageW")
@@ -224,11 +287,17 @@ var (
_ScreenToClient = user32.NewProc("ScreenToClient")
_ShowWindow = user32.NewProc("ShowWindow")
_SetCapture = user32.NewProc("SetCapture")
+ _SetCursor = user32.NewProc("SetCursor")
_SetClipboardData = user32.NewProc("SetClipboardData")
_SetForegroundWindow = user32.NewProc("SetForegroundWindow")
_SetFocus = user32.NewProc("SetFocus")
_SetProcessDPIAware = user32.NewProc("SetProcessDPIAware")
_SetTimer = user32.NewProc("SetTimer")
+ _SetWindowLong = user32.NewProc("SetWindowLongPtrW")
+ _SetWindowLong32 = user32.NewProc("SetWindowLongW")
+ _SetWindowPlacement = user32.NewProc("SetWindowPlacement")
+ _SetWindowPos = user32.NewProc("SetWindowPos")
+ _SetWindowText = user32.NewProc("SetWindowTextW")
_TranslateMessage = user32.NewProc("TranslateMessage")
_UnregisterClass = user32.NewProc("UnregisterClassW")
_UpdateWindow = user32.NewProc("UpdateWindow")
@@ -343,7 +412,7 @@ func getDpiForMonitor(hmonitor syscall.Handle, dpiType uint32) int {
// GetSystemDPI returns the effective DPI of the system.
func GetSystemDPI() int {
- // Check for getDpiForMonitor, introduced in Windows 8.1.
+ // Check for GetDpiForMonitor, introduced in Windows 8.1.
if _GetDpiForMonitor.Find() == nil {
hmon := monitorFromPoint(Point{}, MONITOR_DEFAULTTOPRIMARY)
return getDpiForMonitor(hmon, MDT_EFFECTIVE_DPI)
@@ -377,6 +446,66 @@ func GetMessageTime() time.Duration {
return time.Duration(r) * time.Millisecond
}
+// GetWindowDPI returns the effective DPI of the window.
+func GetWindowDPI(hwnd syscall.Handle) int {
+ // Check for GetDpiForWindow, introduced in Windows 10.
+ if _GetDpiForWindow.Find() == nil {
+ dpi, _, _ := _GetDpiForWindow.Call(uintptr(hwnd))
+ return int(dpi)
+ } else {
+ return GetSystemDPI()
+ }
+}
+
+func GetWindowPlacement(hwnd syscall.Handle) *WindowPlacement {
+ var wp WindowPlacement
+ wp.length = uint32(unsafe.Sizeof(wp))
+ _GetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&wp)))
+ return &wp
+}
+
+func GetMonitorInfo(hwnd syscall.Handle) MonitorInfo {
+ var mi MonitorInfo
+ mi.cbSize = uint32(unsafe.Sizeof(mi))
+ v, _, _ := _MonitorFromWindow.Call(uintptr(hwnd), MONITOR_DEFAULTTOPRIMARY)
+ _GetMonitorInfo.Call(v, uintptr(unsafe.Pointer(&mi)))
+ return mi
+}
+
+func GetWindowLong(hwnd syscall.Handle) (style uintptr) {
+ if runtime.GOARCH == "386" {
+ style, _, _ = _GetWindowLong32.Call(uintptr(hwnd), uintptr(GWL_STYLE))
+ } else {
+ style, _, _ = _GetWindowLong.Call(uintptr(hwnd), uintptr(GWL_STYLE))
+ }
+ return
+}
+
+func SetWindowLong(hwnd syscall.Handle, idx uint32, style uintptr) {
+ if runtime.GOARCH == "386" {
+ _SetWindowLong32.Call(uintptr(hwnd), uintptr(idx), style)
+ } else {
+ _SetWindowLong.Call(uintptr(hwnd), uintptr(idx), style)
+ }
+}
+
+func SetWindowPlacement(hwnd syscall.Handle, wp *WindowPlacement) {
+ _SetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wp)))
+}
+
+func SetWindowPos(hwnd syscall.Handle, hwndInsertAfter uint32, x, y, dx, dy int32, style uintptr) {
+ _SetWindowPos.Call(uintptr(hwnd), uintptr(hwndInsertAfter),
+ uintptr(x), uintptr(y),
+ uintptr(dx), uintptr(dy),
+ style,
+ )
+}
+
+func SetWindowText(hwnd syscall.Handle, title string) {
+ wname := syscall.StringToUTF16Ptr(title)
+ _SetWindowText.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wname)))
+}
+
func GlobalAlloc(size int) (syscall.Handle, error) {
r, _, err := _GlobalAlloc.Call(GHND, uintptr(size))
if r == 0 {
@@ -417,6 +546,22 @@ func LoadCursor(curID uint16) (syscall.Handle, error) {
return syscall.Handle(h), nil
}
+func LoadImage(hInst syscall.Handle, res uint32, typ uint32, cx, cy int, fuload uint32) (syscall.Handle, error) {
+ h, _, err := _LoadImage.Call(uintptr(hInst), uintptr(res), uintptr(typ), uintptr(cx), uintptr(cy), uintptr(fuload))
+ if h == 0 {
+ return 0, fmt.Errorf("LoadImageW failed: %v", err)
+ }
+ return syscall.Handle(h), nil
+}
+
+func MoveWindow(hwnd syscall.Handle, x, y, width, height int32, repaint bool) {
+ var paint uintptr
+ if repaint {
+ paint = TRUE
+ }
+ _MoveWindow.Call(uintptr(hwnd), uintptr(x), uintptr(y), uintptr(width), uintptr(height), paint)
+}
+
func monitorFromPoint(pt Point, flags uint32) syscall.Handle {
r, _, _ := _MonitorFromPoint.Call(uintptr(pt.X), uintptr(pt.Y), uintptr(flags))
return syscall.Handle(r)
@@ -500,6 +645,10 @@ func SetClipboardData(format uint32, mem syscall.Handle) error {
return nil
}
+func SetCursor(h syscall.Handle) {
+ _SetCursor.Call(uintptr(h))
+}
+
func SetTimer(hwnd syscall.Handle, nIDEvent uintptr, uElapse uint32, timerProc uintptr) error {
r, _, err := _SetTimer.Call(uintptr(hwnd), uintptr(nIDEvent), uintptr(uElapse), timerProc)
if r == 0 {
@@ -530,6 +679,10 @@ func UpdateWindow(hwnd syscall.Handle) {
_UpdateWindow.Call(uintptr(hwnd))
}
+func (p WindowPlacement) Rect() Rect {
+ return p.rcNormalPosition
+}
+
// issue34474KeepAlive calls runtime.KeepAlive as a
// workaround for golang.org/issue/34474.
func issue34474KeepAlive(v interface{}) {
diff --git a/vendor/gioui.org/app/internal/xkb/xkb_unix.go b/vendor/gioui.org/app/internal/xkb/xkb_unix.go
index 3525692..68b1329 100644
--- a/vendor/gioui.org/app/internal/xkb/xkb_unix.go
+++ b/vendor/gioui.org/app/internal/xkb/xkb_unix.go
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build (linux && !android) || freebsd || openbsd
// +build linux,!android freebsd openbsd
// Package xkb implements a Go interface for the X Keyboard Extension library.
@@ -150,7 +151,7 @@ func (x *Context) Modifiers() key.Modifiers {
return mods
}
-func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) {
+func (x *Context) DispatchKey(keyCode uint32, state key.State) (events []event.Event) {
if x.state == nil {
return
}
@@ -163,6 +164,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) {
cmd := key.Event{
Name: name,
Modifiers: x.Modifiers(),
+ State: state,
}
// Ensure that a physical backtab key is translated to
// Shift-Tab.
@@ -185,7 +187,10 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) {
C.xkb_compose_state_reset(x.compState)
str = x.utf8Buf[:size]
case C.XKB_COMPOSE_NOTHING:
- str = x.charsForKeycode(kc)
+ mod := x.Modifiers()
+ if mod&(key.ModCtrl|key.ModAlt|key.ModSuper) == 0 {
+ str = x.charsForKeycode(kc)
+ }
}
// Report only printable runes.
var n int
@@ -198,7 +203,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) {
str = str[:len(str)-s]
}
}
- if len(str) > 0 {
+ if state == key.Press && len(str) > 0 {
events = append(events, key.EditEvent{Text: string(str)})
}
return
@@ -230,7 +235,7 @@ func convertKeysym(s C.xkb_keysym_t) (string, bool) {
if 'a' <= s && s <= 'z' {
return string(rune(s - 'a' + 'A')), true
}
- if ' ' <= s && s <= '~' {
+ if ' ' < s && s <= '~' {
return string(rune(s)), true
}
var n string
@@ -288,7 +293,7 @@ func convertKeysym(s C.xkb_keysym_t) (string, bool) {
case C.XKB_KEY_Tab, C.XKB_KEY_KP_Tab, C.XKB_KEY_ISO_Left_Tab:
n = key.NameTab
case 0x20, C.XKB_KEY_KP_Space:
- n = "Space"
+ n = key.NameSpace
default:
return "", false
}
diff --git a/vendor/gioui.org/app/loop.go b/vendor/gioui.org/app/loop.go
deleted file mode 100644
index 0b2195a..0000000
--- a/vendor/gioui.org/app/loop.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package app
-
-import (
- "image"
- "runtime"
-
- "gioui.org/app/internal/window"
- "gioui.org/gpu"
- "gioui.org/op"
-)
-
-type renderLoop struct {
- summary string
- drawing bool
- err error
-
- frames chan frame
- results chan frameResult
- refresh chan struct{}
- refreshErr chan error
- ack chan struct{}
- stop chan struct{}
- stopped chan struct{}
-}
-
-type frame struct {
- viewport image.Point
- ops *op.Ops
-}
-
-type frameResult struct {
- profile string
- err error
-}
-
-func newLoop(ctx window.Context) (*renderLoop, error) {
- l := &renderLoop{
- frames: make(chan frame),
- results: make(chan frameResult),
- refresh: make(chan struct{}),
- refreshErr: make(chan error),
- // Ack is buffered so GPU commands can be issued after
- // ack'ing the frame.
- ack: make(chan struct{}, 1),
- stop: make(chan struct{}),
- stopped: make(chan struct{}),
- }
- if err := l.renderLoop(ctx); err != nil {
- return nil, err
- }
- return l, nil
-}
-
-func (l *renderLoop) renderLoop(ctx window.Context) error {
- // GL Operations must happen on a single OS thread, so
- // pass initialization result through a channel.
- initErr := make(chan error)
- go func() {
- defer close(l.stopped)
- runtime.LockOSThread()
- // Don't UnlockOSThread to avoid reuse by the Go runtime.
-
- if err := ctx.MakeCurrent(); err != nil {
- initErr <- err
- return
- }
- b, err := ctx.Backend()
- if err != nil {
- initErr <- err
- return
- }
- g, err := gpu.New(b)
- if err != nil {
- initErr <- err
- return
- }
- defer ctx.Release()
- initErr <- nil
- loop:
- for {
- select {
- case <-l.refresh:
- l.refreshErr <- ctx.MakeCurrent()
- case frame := <-l.frames:
- ctx.Lock()
- g.Collect(frame.viewport, frame.ops)
- // Signal that we're done with the frame ops.
- l.ack <- struct{}{}
- g.BeginFrame()
- var res frameResult
- res.err = ctx.Present()
- g.EndFrame()
- res.profile = g.Profile()
- ctx.Unlock()
- l.results <- res
- case <-l.stop:
- break loop
- }
- }
- }()
- return <-initErr
-}
-
-func (l *renderLoop) Release() {
- // Flush error.
- l.Flush()
- close(l.stop)
- <-l.stopped
- l.stop = nil
-}
-
-func (l *renderLoop) Flush() error {
- if l.drawing {
- st := <-l.results
- l.setErr(st.err)
- if st.profile != "" {
- l.summary = st.profile
- }
- l.drawing = false
- }
- return l.err
-}
-
-func (l *renderLoop) Summary() string {
- return l.summary
-}
-
-func (l *renderLoop) Refresh() {
- if l.err != nil {
- return
- }
- // Make sure any pending frame is complete.
- l.Flush()
- l.refresh <- struct{}{}
- l.setErr(<-l.refreshErr)
-}
-
-// Draw initiates a draw of a frame. It returns a channel
-// than signals when the frame is no longer being accessed.
-func (l *renderLoop) Draw(viewport image.Point, frameOps *op.Ops) <-chan struct{} {
- if l.err != nil {
- l.ack <- struct{}{}
- return l.ack
- }
- l.Flush()
- l.frames <- frame{viewport, frameOps}
- l.drawing = true
- return l.ack
-}
-
-func (l *renderLoop) setErr(err error) {
- if l.err == nil {
- l.err = err
- }
-}
diff --git a/vendor/gioui.org/app/metal_darwin.go b/vendor/gioui.org/app/metal_darwin.go
new file mode 100644
index 0000000..06b89f3
--- /dev/null
+++ b/vendor/gioui.org/app/metal_darwin.go
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build !nometal
+// +build !nometal
+
+package app
+
+import (
+ "errors"
+
+ "gioui.org/gpu"
+)
+
+/*
+#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc
+
+@import Metal;
+@import QuartzCore.CAMetalLayer;
+
+#include <CoreFoundation/CoreFoundation.h>
+
+static CFTypeRef createMetalDevice(void) {
+ @autoreleasepool {
+ id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
+ return CFBridgingRetain(dev);
+ }
+}
+
+static void setupLayer(CFTypeRef layerRef, CFTypeRef devRef) {
+ @autoreleasepool {
+ CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef;
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ layer.device = dev;
+ // Package gpu assumes an sRGB-encoded framebuffer.
+ layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
+ if (@available(iOS 11.0, *)) {
+ // Never let nextDrawable time out and return nil.
+ layer.allowsNextDrawableTimeout = NO;
+ }
+ }
+}
+
+static CFTypeRef nextDrawable(CFTypeRef layerRef) {
+ @autoreleasepool {
+ CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef;
+ return CFBridgingRetain([layer nextDrawable]);
+ }
+}
+
+static CFTypeRef drawableTexture(CFTypeRef drawableRef) {
+ @autoreleasepool {
+ id<CAMetalDrawable> drawable = (__bridge id<CAMetalDrawable>)drawableRef;
+ return CFBridgingRetain(drawable.texture);
+ }
+}
+
+static void presentDrawable(CFTypeRef queueRef, CFTypeRef drawableRef) {
+ @autoreleasepool {
+ id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef;
+ id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
+ id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer];
+ [cmdBuffer presentDrawable:drawable];
+ [cmdBuffer commit];
+ }
+}
+
+static CFTypeRef newCommandQueue(CFTypeRef devRef) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ return CFBridgingRetain([dev newCommandQueue]);
+ }
+}
+*/
+import "C"
+
+type mtlContext struct {
+ dev C.CFTypeRef
+ view C.CFTypeRef
+ layer C.CFTypeRef
+ queue C.CFTypeRef
+ drawable C.CFTypeRef
+ texture C.CFTypeRef
+}
+
+func newMtlContext(w *window) (*mtlContext, error) {
+ dev := C.createMetalDevice()
+ if dev == 0 {
+ return nil, errors.New("metal: MTLCreateSystemDefaultDevice failed")
+ }
+ view := w.contextView()
+ layer := getMetalLayer(view)
+ if layer == 0 {
+ C.CFRelease(dev)
+ return nil, errors.New("metal: CAMetalLayer construction failed")
+ }
+ queue := C.newCommandQueue(dev)
+ if layer == 0 {
+ C.CFRelease(dev)
+ C.CFRelease(layer)
+ return nil, errors.New("metal: [MTLDevice newCommandQueue] failed")
+ }
+ C.setupLayer(layer, dev)
+ c := &mtlContext{
+ dev: dev,
+ view: view,
+ layer: layer,
+ queue: queue,
+ }
+ return c, nil
+}
+
+func (c *mtlContext) RenderTarget() (gpu.RenderTarget, error) {
+ if c.drawable != 0 || c.texture != 0 {
+ return nil, errors.New("metal:a previous RenderTarget wasn't Presented")
+ }
+ c.drawable = C.nextDrawable(c.layer)
+ if c.drawable == 0 {
+ return nil, errors.New("metal: [CAMetalLayer nextDrawable] failed")
+ }
+ c.texture = C.drawableTexture(c.drawable)
+ if c.texture == 0 {
+ return nil, errors.New("metal: CADrawable.texture is nil")
+ }
+ return gpu.MetalRenderTarget{
+ Texture: uintptr(c.texture),
+ }, nil
+}
+
+func (c *mtlContext) API() gpu.API {
+ return gpu.Metal{
+ Device: uintptr(c.dev),
+ Queue: uintptr(c.queue),
+ PixelFormat: int(C.MTLPixelFormatBGRA8Unorm_sRGB),
+ }
+}
+
+func (c *mtlContext) Release() {
+ C.CFRelease(c.queue)
+ C.CFRelease(c.dev)
+ C.CFRelease(c.layer)
+ if c.drawable != 0 {
+ C.CFRelease(c.drawable)
+ }
+ if c.texture != 0 {
+ C.CFRelease(c.texture)
+ }
+ *c = mtlContext{}
+}
+
+func (c *mtlContext) Present() error {
+ C.CFRelease(c.texture)
+ c.texture = 0
+ C.presentDrawable(c.queue, c.drawable)
+ C.CFRelease(c.drawable)
+ c.drawable = 0
+ return nil
+}
+
+func (c *mtlContext) Lock() error {
+ return nil
+}
+
+func (c *mtlContext) Unlock() {}
+
+func (c *mtlContext) Refresh() error {
+ resizeDrawable(c.view, c.layer)
+ return nil
+}
+
+func (w *window) NewContext() (context, error) {
+ return newMtlContext(w)
+}
diff --git a/vendor/gioui.org/app/metal_ios.go b/vendor/gioui.org/app/metal_ios.go
new file mode 100644
index 0000000..860ba1a
--- /dev/null
+++ b/vendor/gioui.org/app/metal_ios.go
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build !nometal
+// +build !nometal
+
+package app
+
+/*
+#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc
+
+@import UIKit;
+
+@import QuartzCore.CAMetalLayer;
+
+#include <CoreFoundation/CoreFoundation.h>
+
+Class gio_layerClass(void) {
+ return [CAMetalLayer class];
+}
+
+static CFTypeRef getMetalLayer(CFTypeRef viewRef) {
+ @autoreleasepool {
+ UIView *view = (__bridge UIView *)viewRef;
+ return CFBridgingRetain(view.layer);
+ }
+}
+
+static void resizeDrawable(CFTypeRef viewRef, CFTypeRef layerRef) {
+ @autoreleasepool {
+ UIView *view = (__bridge UIView *)viewRef;
+ CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef;
+ layer.contentsScale = view.contentScaleFactor;
+ CGSize size = layer.bounds.size;
+ size.width *= layer.contentsScale;
+ size.height *= layer.contentsScale;
+ layer.drawableSize = size;
+ }
+}
+*/
+import "C"
+
+func getMetalLayer(view C.CFTypeRef) C.CFTypeRef {
+ return C.getMetalLayer(view)
+}
+
+func resizeDrawable(view, layer C.CFTypeRef) {
+ C.resizeDrawable(view, layer)
+}
diff --git a/vendor/gioui.org/app/metal_macos.go b/vendor/gioui.org/app/metal_macos.go
new file mode 100644
index 0000000..7db06a9
--- /dev/null
+++ b/vendor/gioui.org/app/metal_macos.go
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build darwin && !ios && !nometal
+// +build darwin,!ios,!nometal
+
+package app
+
+/*
+#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc
+
+@import AppKit;
+
+@import QuartzCore.CAMetalLayer;
+
+#include <CoreFoundation/CoreFoundation.h>
+
+CALayer *gio_layerFactory(void) {
+ @autoreleasepool {
+ return [CAMetalLayer layer];
+ }
+}
+
+static CFTypeRef getMetalLayer(CFTypeRef viewRef) {
+ @autoreleasepool {
+ NSView *view = (__bridge NSView *)viewRef;
+ return CFBridgingRetain(view.layer);
+ }
+}
+
+static void resizeDrawable(CFTypeRef viewRef, CFTypeRef layerRef) {
+ @autoreleasepool {
+ NSView *view = (__bridge NSView *)viewRef;
+ CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef;
+ CGSize size = layer.bounds.size;
+ size.width *= layer.contentsScale;
+ size.height *= layer.contentsScale;
+ layer.drawableSize = size;
+ }
+}
+*/
+import "C"
+
+func getMetalLayer(view C.CFTypeRef) C.CFTypeRef {
+ return C.getMetalLayer(view)
+}
+
+func resizeDrawable(view, layer C.CFTypeRef) {
+ C.resizeDrawable(view, layer)
+}
diff --git a/vendor/gioui.org/app/os.go b/vendor/gioui.org/app/os.go
new file mode 100644
index 0000000..fc5c266
--- /dev/null
+++ b/vendor/gioui.org/app/os.go
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// package app implements platform specific windows
+// and GPU contexts.
+package app
+
+import (
+ "errors"
+ "image"
+ "image/color"
+
+ "gioui.org/io/key"
+
+ "gioui.org/gpu"
+ "gioui.org/io/pointer"
+ "gioui.org/io/system"
+ "gioui.org/unit"
+)
+
+type size struct {
+ Width unit.Value
+ Height unit.Value
+}
+
+// errOutOfDate is reported when the GPU surface dimensions or properties no
+// longer match the window.
+var errOutOfDate = errors.New("app: GPU surface out of date")
+
+// Config describes a Window configuration.
+type Config struct {
+ // Size is the window dimensions (Width, Height).
+ Size image.Point
+ // MaxSize is the window maximum allowed dimensions.
+ MaxSize image.Point
+ // MinSize is the window minimum allowed dimensions.
+ MinSize image.Point
+ // Title is the window title displayed in its decoration bar.
+ Title string
+ // WindowMode is the window mode.
+ Mode WindowMode
+ // StatusColor is the color of the Android status bar.
+ StatusColor color.NRGBA
+ // NavigationColor is the color of the navigation bar
+ // on Android, or the address bar in browsers.
+ NavigationColor color.NRGBA
+ // Orientation is the current window orientation.
+ Orientation Orientation
+ // CustomRenderer is true when the window content is rendered by the
+ // client.
+ CustomRenderer bool
+}
+
+// ConfigEvent is sent whenever the configuration of a Window changes.
+type ConfigEvent struct {
+ Config Config
+}
+
+func (c *Config) apply(m unit.Metric, options []Option) {
+ for _, o := range options {
+ o(m, c)
+ }
+}
+
+type wakeupEvent struct{}
+
+// WindowMode is the window mode (WindowMode.Option sets it).
+//
+// Supported platforms are macOS, X11, Windows, Android and JS.
+type WindowMode uint8
+
+const (
+ // Windowed is the normal window mode with OS specific window decorations.
+ Windowed WindowMode = iota
+ // Fullscreen is the full screen window mode.
+ Fullscreen
+)
+
+func (m WindowMode) Option() Option {
+ return func(_ unit.Metric, cnf *Config) {
+ cnf.Mode = m
+ }
+}
+
+func (m WindowMode) String() string {
+ switch m {
+ case Windowed:
+ return "windowed"
+ case Fullscreen:
+ return "fullscreen"
+ }
+ return ""
+}
+
+// Orientation is the orientation of the app (Orientation.Option sets it).
+//
+// Supported platforms are Android and JS.
+type Orientation uint8
+
+const (
+ // AnyOrientation allows the window to be freely orientated.
+ AnyOrientation Orientation = iota
+ // LandscapeOrientation constrains the window to landscape orientations.
+ LandscapeOrientation
+ // PortraitOrientation constrains the window to portrait orientations.
+ PortraitOrientation
+)
+
+func (o Orientation) Option() Option {
+ return func(_ unit.Metric, cnf *Config) {
+ cnf.Orientation = o
+ }
+}
+
+func (o Orientation) String() string {
+ switch o {
+ case AnyOrientation:
+ return "any"
+ case LandscapeOrientation:
+ return "landscape"
+ case PortraitOrientation:
+ return "portrait"
+ }
+ return ""
+}
+
+type frameEvent struct {
+ system.FrameEvent
+
+ Sync bool
+}
+
+type context interface {
+ API() gpu.API
+ RenderTarget() (gpu.RenderTarget, error)
+ Present() error
+ Refresh() error
+ Release()
+ Lock() error
+ Unlock()
+}
+
+// Driver is the interface for the platform implementation
+// of a window.
+type driver interface {
+ // SetAnimating sets the animation flag. When the window is animating,
+ // FrameEvents are delivered as fast as the display can handle them.
+ SetAnimating(anim bool)
+
+ // ShowTextInput updates the virtual keyboard state.
+ ShowTextInput(show bool)
+
+ SetInputHint(mode key.InputHint)
+
+ NewContext() (context, error)
+
+ // ReadClipboard requests the clipboard content.
+ ReadClipboard()
+ // WriteClipboard requests a clipboard write.
+ WriteClipboard(s string)
+
+ // Configure the window.
+ Configure([]Option)
+
+ // SetCursor updates the current cursor to name.
+ SetCursor(name pointer.CursorName)
+
+ // Raise the window at the top.
+ Raise()
+
+ // Close the window.
+ Close()
+
+ // Wakeup wakes up the event loop and sends a WakeupEvent.
+ Wakeup()
+
+ // Maximize will make the window as large as possible, but keep the frame decorations.
+ Maximize()
+ // Center will place the window at monitor center.
+ Center()
+}
+
+type windowRendezvous struct {
+ in chan windowAndConfig
+ out chan windowAndConfig
+ errs chan error
+}
+
+type windowAndConfig struct {
+ window *callbacks
+ options []Option
+}
+
+func newWindowRendezvous() *windowRendezvous {
+ wr := &windowRendezvous{
+ in: make(chan windowAndConfig),
+ out: make(chan windowAndConfig),
+ errs: make(chan error),
+ }
+ go func() {
+ var main windowAndConfig
+ var out chan windowAndConfig
+ for {
+ select {
+ case w := <-wr.in:
+ var err error
+ if main.window != nil {
+ err = errors.New("multiple windows are not supported")
+ }
+ wr.errs <- err
+ main = w
+ out = wr.out
+ case out <- main:
+ }
+ }
+ }()
+ return wr
+}
+
+func (wakeupEvent) ImplementsEvent() {}
+func (ConfigEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/app/os_android.go b/vendor/gioui.org/app/os_android.go
new file mode 100644
index 0000000..51d8289
--- /dev/null
+++ b/vendor/gioui.org/app/os_android.go
@@ -0,0 +1,1283 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package app
+
+/*
+#cgo CFLAGS: -Werror
+#cgo LDFLAGS: -landroid
+
+#include <android/native_window_jni.h>
+#include <android/configuration.h>
+#include <android/keycodes.h>
+#include <android/input.h>
+#include <stdlib.h>
+
+static jint jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
+ return (*vm)->GetEnv(vm, (void **)env, version);
+}
+
+static jint jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) {
+ return (*env)->GetJavaVM(env, jvm);
+}
+
+static jint jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
+ return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
+}
+
+static jint jni_DetachCurrentThread(JavaVM *vm) {
+ return (*vm)->DetachCurrentThread(vm);
+}
+
+static jobject jni_NewGlobalRef(JNIEnv *env, jobject obj) {
+ return (*env)->NewGlobalRef(env, obj);
+}
+
+static void jni_DeleteGlobalRef(JNIEnv *env, jobject obj) {
+ (*env)->DeleteGlobalRef(env, obj);
+}
+
+static jclass jni_GetObjectClass(JNIEnv *env, jobject obj) {
+ return (*env)->GetObjectClass(env, obj);
+}
+
+static jmethodID jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
+ return (*env)->GetMethodID(env, clazz, name, sig);
+}
+
+static jmethodID jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
+ return (*env)->GetStaticMethodID(env, clazz, name, sig);
+}
+
+static jfloat jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
+ return (*env)->CallFloatMethod(env, obj, methodID);
+}
+
+static jint jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
+ return (*env)->CallIntMethod(env, obj, methodID);
+}
+
+static void jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) {
+ (*env)->CallStaticVoidMethodA(env, cls, methodID, args);
+}
+
+static void jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
+ (*env)->CallVoidMethodA(env, obj, methodID, args);
+}
+
+static jboolean jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
+ return (*env)->CallBooleanMethodA(env, obj, methodID, args);
+}
+
+static jbyte *jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) {
+ return (*env)->GetByteArrayElements(env, arr, NULL);
+}
+
+static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) {
+ (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT);
+}
+
+static jsize jni_GetArrayLength(JNIEnv *env, jbyteArray arr) {
+ return (*env)->GetArrayLength(env, arr);
+}
+
+static jstring jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) {
+ return (*env)->NewString(env, unicodeChars, len);
+}
+
+static jsize jni_GetStringLength(JNIEnv *env, jstring str) {
+ return (*env)->GetStringLength(env, str);
+}
+
+static const jchar *jni_GetStringChars(JNIEnv *env, jstring str) {
+ return (*env)->GetStringChars(env, str, NULL);
+}
+
+static jthrowable jni_ExceptionOccurred(JNIEnv *env) {
+ return (*env)->ExceptionOccurred(env);
+}
+
+static void jni_ExceptionClear(JNIEnv *env) {
+ (*env)->ExceptionClear(env);
+}
+
+static jobject jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
+ return (*env)->CallObjectMethodA(env, obj, method, args);
+}
+
+static jobject jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
+ return (*env)->CallStaticObjectMethodA(env, cls, method, args);
+}
+
+static jclass jni_FindClass(JNIEnv *env, char *name) {
+ return (*env)->FindClass(env, name);
+}
+
+static jobject jni_NewObjectA(JNIEnv *env, jclass cls, jmethodID cons, jvalue *args) {
+ return (*env)->NewObjectA(env, cls, cons, args);
+}
+*/
+import "C"
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "image/color"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "runtime/debug"
+ "sync"
+ "time"
+ "unicode/utf16"
+ "unsafe"
+
+ "gioui.org/internal/f32color"
+
+ "gioui.org/f32"
+ "gioui.org/io/clipboard"
+ "gioui.org/io/key"
+ "gioui.org/io/pointer"
+ "gioui.org/io/router"
+ "gioui.org/io/semantic"
+ "gioui.org/io/system"
+ "gioui.org/unit"
+)
+
+type window struct {
+ callbacks *callbacks
+
+ view C.jobject
+
+ dpi int
+ fontScale float32
+ insets system.Insets
+
+ stage system.Stage
+ started bool
+ animating bool
+
+ win *C.ANativeWindow
+ config Config
+
+ semantic struct {
+ hoverID router.SemanticID
+ rootID router.SemanticID
+ focusID router.SemanticID
+ diffs []router.SemanticID
+ }
+}
+
+// gioView hold cached JNI methods for GioView.
+var gioView struct {
+ once sync.Once
+ getDensity C.jmethodID
+ getFontScale C.jmethodID
+ showTextInput C.jmethodID
+ hideTextInput C.jmethodID
+ setInputHint C.jmethodID
+ postInvalidate C.jmethodID // requests draw, called from non-UI thread
+ invalidate C.jmethodID // requests draw, called from UI thread
+ setCursor C.jmethodID
+ setOrientation C.jmethodID
+ setNavigationColor C.jmethodID
+ setStatusColor C.jmethodID
+ setFullscreen C.jmethodID
+ unregister C.jmethodID
+ sendA11yEvent C.jmethodID
+ sendA11yChange C.jmethodID
+ isA11yActive C.jmethodID
+}
+
+// ViewEvent is sent whenever the Window's underlying Android view
+// changes.
+type ViewEvent struct {
+ // View is a JNI global reference to the android.view.View
+ // instance backing the Window. The reference is valid until
+ // the next ViewEvent is received.
+ // A zero View means that there is currently no view attached.
+ View uintptr
+}
+
+type jvalue uint64 // The largest JNI type fits in 64 bits.
+
+var dataDirChan = make(chan string, 1)
+
+var android struct {
+ // mu protects all fields of this structure. However, once a
+ // non-nil jvm is returned from javaVM, all the other fields may
+ // be accessed unlocked.
+ mu sync.Mutex
+ jvm *C.JavaVM
+
+ // appCtx is the global Android App context.
+ appCtx C.jobject
+ // gioCls is the class of the Gio class.
+ gioCls C.jclass
+
+ mwriteClipboard C.jmethodID
+ mreadClipboard C.jmethodID
+ mwakeupMainThread C.jmethodID
+
+ // android.view.accessibility.AccessibilityNodeInfo class.
+ accessibilityNodeInfo struct {
+ cls C.jclass
+ // addChild(View, int)
+ addChild C.jmethodID
+ // setBoundsInScreen(Rect)
+ setBoundsInScreen C.jmethodID
+ // setText(CharSequence)
+ setText C.jmethodID
+ // setContentDescription(CharSequence)
+ setContentDescription C.jmethodID
+ // setParent(View, int)
+ setParent C.jmethodID
+ // addAction(int)
+ addAction C.jmethodID
+ // setClassName(CharSequence)
+ setClassName C.jmethodID
+ // setCheckable(boolean)
+ setCheckable C.jmethodID
+ // setSelected(boolean)
+ setSelected C.jmethodID
+ // setChecked(boolean)
+ setChecked C.jmethodID
+ // setEnabled(boolean)
+ setEnabled C.jmethodID
+ // setAccessibilityFocused(boolean)
+ setAccessibilityFocused C.jmethodID
+ }
+
+ // android.graphics.Rect class.
+ rect struct {
+ cls C.jclass
+ // (int, int, int, int) constructor.
+ cons C.jmethodID
+ }
+
+ strings struct {
+ // "android.view.View"
+ androidViewView C.jstring
+ // "android.widget.Button"
+ androidWidgetButton C.jstring
+ // "android.widget.CheckBox"
+ androidWidgetCheckBox C.jstring
+ // "android.widget.EditText"
+ androidWidgetEditText C.jstring
+ // "android.widget.RadioButton"
+ androidWidgetRadioButton C.jstring
+ // "android.widget.Switch"
+ androidWidgetSwitch C.jstring
+ }
+}
+
+// view maps from GioView JNI refenreces to windows.
+var views = make(map[C.jlong]*window)
+
+var windows = make(map[*callbacks]*window)
+
+var mainWindow = newWindowRendezvous()
+
+var mainFuncs = make(chan func(env *C.JNIEnv), 1)
+
+var (
+ dataDirOnce sync.Once
+ dataPath string
+)
+
+var (
+ newAndroidVulkanContext func(w *window) (context, error)
+ newAndroidGLESContext func(w *window) (context, error)
+)
+
+// AccessibilityNodeProvider.HOST_VIEW_ID.
+const HOST_VIEW_ID = -1
+
+const (
+ // AccessibilityEvent constants.
+ TYPE_VIEW_HOVER_ENTER = 128
+ TYPE_VIEW_HOVER_EXIT = 256
+)
+
+const (
+ // AccessibilityNodeInfo constants.
+ ACTION_ACCESSIBILITY_FOCUS = 64
+ ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128
+ ACTION_CLICK = 16
+)
+
+func (w *window) NewContext() (context, error) {
+ funcs := []func(w *window) (context, error){newAndroidVulkanContext, newAndroidGLESContext}
+ var firstErr error
+ for _, f := range funcs {
+ if f == nil {
+ continue
+ }
+ c, err := f(w)
+ if err != nil {
+ if firstErr == nil {
+ firstErr = err
+ }
+ continue
+ }
+ return c, nil
+ }
+ if firstErr != nil {
+ return nil, firstErr
+ }
+ return nil, errors.New("x11: no available GPU backends")
+}
+
+func dataDir() (string, error) {
+ dataDirOnce.Do(func() {
+ dataPath = <-dataDirChan
+ // Set XDG_CACHE_HOME to make os.UserCacheDir work.
+ if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists {
+ cachePath := filepath.Join(dataPath, "cache")
+ os.Setenv("XDG_CACHE_HOME", cachePath)
+ }
+ // Set XDG_CONFIG_HOME to make os.UserConfigDir work.
+ if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists {
+ cfgPath := filepath.Join(dataPath, "config")
+ os.Setenv("XDG_CONFIG_HOME", cfgPath)
+ }
+ // Set HOME to make os.UserHomeDir work.
+ if _, exists := os.LookupEnv("HOME"); !exists {
+ os.Setenv("HOME", dataPath)
+ }
+ })
+ return dataPath, nil
+}
+
+func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
+ m := C.CString(method)
+ defer C.free(unsafe.Pointer(m))
+ s := C.CString(sig)
+ defer C.free(unsafe.Pointer(s))
+ jm := C.jni_GetMethodID(env, class, m, s)
+ if err := exception(env); err != nil {
+ panic(err)
+ }
+ return jm
+}
+
+func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
+ m := C.CString(method)
+ defer C.free(unsafe.Pointer(m))
+ s := C.CString(sig)
+ defer C.free(unsafe.Pointer(s))
+ jm := C.jni_GetStaticMethodID(env, class, m, s)
+ if err := exception(env); err != nil {
+ panic(err)
+ }
+ return jm
+}
+
+//export Java_org_gioui_Gio_runGoMain
+func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) {
+ initJVM(env, class, context)
+ dirBytes := C.jni_GetByteArrayElements(env, jdataDir)
+ if dirBytes == nil {
+ panic("runGoMain: GetByteArrayElements failed")
+ }
+ n := C.jni_GetArrayLength(env, jdataDir)
+ dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
+ dataDirChan <- dataDir
+ C.jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
+
+ runMain()
+}
+
+func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) {
+ android.mu.Lock()
+ defer android.mu.Unlock()
+ if res := C.jni_GetJavaVM(env, &android.jvm); res != 0 {
+ panic("gio: GetJavaVM failed")
+ }
+ android.appCtx = C.jni_NewGlobalRef(env, ctx)
+ android.gioCls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(gio)))
+
+ cls := findClass(env, "android/view/accessibility/AccessibilityNodeInfo")
+ android.accessibilityNodeInfo.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls)))
+ android.accessibilityNodeInfo.addChild = getMethodID(env, cls, "addChild", "(Landroid/view/View;I)V")
+ android.accessibilityNodeInfo.setBoundsInScreen = getMethodID(env, cls, "setBoundsInScreen", "(Landroid/graphics/Rect;)V")
+ android.accessibilityNodeInfo.setText = getMethodID(env, cls, "setText", "(Ljava/lang/CharSequence;)V")
+ android.accessibilityNodeInfo.setContentDescription = getMethodID(env, cls, "setContentDescription", "(Ljava/lang/CharSequence;)V")
+ android.accessibilityNodeInfo.setParent = getMethodID(env, cls, "setParent", "(Landroid/view/View;I)V")
+ android.accessibilityNodeInfo.addAction = getMethodID(env, cls, "addAction", "(I)V")
+ android.accessibilityNodeInfo.setClassName = getMethodID(env, cls, "setClassName", "(Ljava/lang/CharSequence;)V")
+ android.accessibilityNodeInfo.setCheckable = getMethodID(env, cls, "setCheckable", "(Z)V")
+ android.accessibilityNodeInfo.setSelected = getMethodID(env, cls, "setSelected", "(Z)V")
+ android.accessibilityNodeInfo.setChecked = getMethodID(env, cls, "setChecked", "(Z)V")
+ android.accessibilityNodeInfo.setEnabled = getMethodID(env, cls, "setEnabled", "(Z)V")
+ android.accessibilityNodeInfo.setAccessibilityFocused = getMethodID(env, cls, "setAccessibilityFocused", "(Z)V")
+
+ cls = findClass(env, "android/graphics/Rect")
+ android.rect.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls)))
+ android.rect.cons = getMethodID(env, cls, "<init>", "(IIII)V")
+ android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V")
+ android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;")
+ android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V")
+
+ intern := func(s string) C.jstring {
+ ref := C.jni_NewGlobalRef(env, C.jobject(javaString(env, s)))
+ return C.jstring(ref)
+ }
+ android.strings.androidViewView = intern("android.view.View")
+ android.strings.androidWidgetButton = intern("android.widget.Button")
+ android.strings.androidWidgetCheckBox = intern("android.widget.CheckBox")
+ android.strings.androidWidgetEditText = intern("android.widget.EditText")
+ android.strings.androidWidgetRadioButton = intern("android.widget.RadioButton")
+ android.strings.androidWidgetSwitch = intern("android.widget.Switch")
+}
+
+// JavaVM returns the global JNI JavaVM.
+func JavaVM() uintptr {
+ jvm := javaVM()
+ return uintptr(unsafe.Pointer(jvm))
+}
+
+func javaVM() *C.JavaVM {
+ android.mu.Lock()
+ defer android.mu.Unlock()
+ return android.jvm
+}
+
+// AppContext returns the global Application context as a JNI jobject.
+func AppContext() uintptr {
+ android.mu.Lock()
+ defer android.mu.Unlock()
+ return uintptr(android.appCtx)
+}
+
+//export Java_org_gioui_GioView_onCreateView
+func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
+ gioView.once.Do(func() {
+ m := &gioView
+ m.getDensity = getMethodID(env, class, "getDensity", "()I")
+ m.getFontScale = getMethodID(env, class, "getFontScale", "()F")
+ m.showTextInput = getMethodID(env, class, "showTextInput", "()V")
+ m.hideTextInput = getMethodID(env, class, "hideTextInput", "()V")
+ m.setInputHint = getMethodID(env, class, "setInputHint", "(I)V")
+ m.postInvalidate = getMethodID(env, class, "postInvalidate", "()V")
+ m.invalidate = getMethodID(env, class, "invalidate", "()V")
+ m.setCursor = getMethodID(env, class, "setCursor", "(I)V")
+ m.setOrientation = getMethodID(env, class, "setOrientation", "(II)V")
+ m.setNavigationColor = getMethodID(env, class, "setNavigationColor", "(II)V")
+ m.setStatusColor = getMethodID(env, class, "setStatusColor", "(II)V")
+ m.setFullscreen = getMethodID(env, class, "setFullscreen", "(Z)V")
+ m.unregister = getMethodID(env, class, "unregister", "()V")
+ m.sendA11yEvent = getMethodID(env, class, "sendA11yEvent", "(II)V")
+ m.sendA11yChange = getMethodID(env, class, "sendA11yChange", "(I)V")
+ m.isA11yActive = getMethodID(env, class, "isA11yActive", "()Z")
+ })
+ view = C.jni_NewGlobalRef(env, view)
+ wopts := <-mainWindow.out
+ w, ok := windows[wopts.window]
+ if !ok {
+ w = &window{
+ callbacks: wopts.window,
+ }
+ windows[wopts.window] = w
+ }
+ if w.view != 0 {
+ w.detach(env)
+ }
+ w.view = view
+ w.callbacks.SetDriver(w)
+ handle := C.jlong(view)
+ views[handle] = w
+ w.loadConfig(env, class)
+ w.Configure(wopts.options)
+ w.setStage(system.StagePaused)
+ w.callbacks.Event(ViewEvent{View: uintptr(view)})
+ return handle
+}
+
+//export Java_org_gioui_GioView_onDestroyView
+func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
+ w := views[handle]
+ w.detach(env)
+}
+
+//export Java_org_gioui_GioView_onStopView
+func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
+ w := views[handle]
+ w.started = false
+ w.setStage(system.StagePaused)
+}
+
+//export Java_org_gioui_GioView_onStartView
+func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
+ w := views[handle]
+ w.started = true
+ if w.win != nil {
+ w.setVisible(env)
+ }
+}
+
+//export Java_org_gioui_GioView_onSurfaceDestroyed
+func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) {
+ w := views[handle]
+ w.win = nil
+ w.setStage(system.StagePaused)
+}
+
+//export Java_org_gioui_GioView_onSurfaceChanged
+func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) {
+ w := views[handle]
+ w.win = C.ANativeWindow_fromSurface(env, surf)
+ if w.started {
+ w.setVisible(env)
+ }
+}
+
+//export Java_org_gioui_GioView_onLowMemory
+func Java_org_gioui_GioView_onLowMemory(env *C.JNIEnv, class C.jclass) {
+ runtime.GC()
+ debug.FreeOSMemory()
+}
+
+//export Java_org_gioui_GioView_onConfigurationChanged
+func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) {
+ w := views[view]
+ w.loadConfig(env, class)
+ if w.stage >= system.StageRunning {
+ w.draw(env, true)
+ }
+}
+
+//export Java_org_gioui_GioView_onFrameCallback
+func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong) {
+ w, exist := views[view]
+ if !exist {
+ return
+ }
+ if w.stage < system.StageRunning {
+ return
+ }
+ if w.animating {
+ w.draw(env, false)
+ // Schedule the next draw immediately after this one. Since onFrameCallback runs
+ // on the UI thread, View.invalidate can be used here instead of postInvalidate.
+ callVoidMethod(env, w.view, gioView.invalidate)
+ }
+}
+
+//export Java_org_gioui_GioView_onBack
+func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
+ w := views[view]
+ ev := &system.CommandEvent{Type: system.CommandBack}
+ w.callbacks.Event(ev)
+ if ev.Cancel {
+ return C.JNI_TRUE
+ }
+ return C.JNI_FALSE
+}
+
+//export Java_org_gioui_GioView_onFocusChange
+func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
+ w := views[view]
+ w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE})
+}
+
+//export Java_org_gioui_GioView_onWindowInsets
+func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) {
+ w := views[view]
+ w.insets = system.Insets{
+ Top: unit.Px(float32(top)),
+ Right: unit.Px(float32(right)),
+ Bottom: unit.Px(float32(bottom)),
+ Left: unit.Px(float32(left)),
+ }
+ if w.stage >= system.StageRunning {
+ w.draw(env, true)
+ }
+}
+
+//export Java_org_gioui_GioView_initializeAccessibilityNodeInfo
+func Java_org_gioui_GioView_initializeAccessibilityNodeInfo(env *C.JNIEnv, class C.jclass, view C.jlong, virtID, screenX, screenY C.jint, info C.jobject) C.jobject {
+ w := views[view]
+ semID := w.semIDFor(virtID)
+ sem, found := w.callbacks.LookupSemantic(semID)
+ if found {
+ off := f32.Pt(float32(screenX), float32(screenY))
+ if err := w.initAccessibilityNodeInfo(env, sem, off, info); err != nil {
+ panic(err)
+ }
+ }
+ return info
+}
+
+//export Java_org_gioui_GioView_onTouchExploration
+func Java_org_gioui_GioView_onTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong, x, y C.jfloat) {
+ w := views[view]
+ semID, _ := w.callbacks.SemanticAt(f32.Pt(float32(x), float32(y)))
+ if w.semantic.hoverID == semID {
+ return
+ }
+ // Android expects ENTER before EXIT.
+ if semID != 0 {
+ callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_ENTER, jvalue(w.virtualIDFor(semID)))
+ }
+ if prevID := w.semantic.hoverID; prevID != 0 {
+ callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(prevID)))
+ }
+ w.semantic.hoverID = semID
+}
+
+//export Java_org_gioui_GioView_onExitTouchExploration
+func Java_org_gioui_GioView_onExitTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong) {
+ w := views[view]
+ if w.semantic.hoverID != 0 {
+ callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(w.semantic.hoverID)))
+ w.semantic.hoverID = 0
+ }
+}
+
+//export Java_org_gioui_GioView_onA11yFocus
+func Java_org_gioui_GioView_onA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) {
+ w := views[view]
+ if semID := w.semIDFor(virtID); semID != w.semantic.focusID {
+ w.semantic.focusID = semID
+ // Android needs invalidate to refresh the TalkBack focus indicator.
+ callVoidMethod(env, w.view, gioView.invalidate)
+ }
+}
+
+//export Java_org_gioui_GioView_onClearA11yFocus
+func Java_org_gioui_GioView_onClearA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) {
+ w := views[view]
+ if w.semantic.focusID == w.semIDFor(virtID) {
+ w.semantic.focusID = 0
+ }
+}
+
+func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem router.SemanticNode, off f32.Point, info C.jobject) error {
+ for _, ch := range sem.Children {
+ err := callVoidMethod(env, info, android.accessibilityNodeInfo.addChild, jvalue(w.view), jvalue(w.virtualIDFor(ch.ID)))
+ if err != nil {
+ return err
+ }
+ }
+ if sem.ParentID != 0 {
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setParent, jvalue(w.view), jvalue(w.virtualIDFor(sem.ParentID))); err != nil {
+ return err
+ }
+ b := sem.Desc.Bounds.Add(off)
+ rect, err := newObject(env, android.rect.cls, android.rect.cons,
+ jvalue(b.Min.X),
+ jvalue(b.Min.Y),
+ jvalue(b.Max.X),
+ jvalue(b.Max.Y),
+ )
+ if err != nil {
+ return err
+ }
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setBoundsInScreen, jvalue(rect)); err != nil {
+ return err
+ }
+ }
+ d := sem.Desc
+ if l := d.Label; l != "" {
+ jlbl := javaString(env, l)
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setText, jvalue(jlbl)); err != nil {
+ return err
+ }
+ }
+ if d.Description != "" {
+ jd := javaString(env, d.Description)
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setContentDescription, jvalue(jd)); err != nil {
+ return err
+ }
+ }
+ addAction := func(act C.jint) {
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.addAction, jvalue(act)); err != nil {
+ panic(err)
+ }
+ }
+ if d.Gestures&router.ClickGesture != 0 {
+ addAction(ACTION_CLICK)
+ }
+ clsName := android.strings.androidViewView
+ selectMethod := android.accessibilityNodeInfo.setChecked
+ checkable := false
+ switch d.Class {
+ case semantic.Button:
+ clsName = android.strings.androidWidgetButton
+ case semantic.CheckBox:
+ checkable = true
+ clsName = android.strings.androidWidgetCheckBox
+ case semantic.Editor:
+ clsName = android.strings.androidWidgetEditText
+ case semantic.RadioButton:
+ selectMethod = android.accessibilityNodeInfo.setSelected
+ clsName = android.strings.androidWidgetRadioButton
+ case semantic.Switch:
+ checkable = true
+ clsName = android.strings.androidWidgetSwitch
+ }
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setClassName, jvalue(clsName)); err != nil {
+ panic(err)
+ }
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setCheckable, jvalue(javaBool(checkable))); err != nil {
+ panic(err)
+ }
+ if err := callVoidMethod(env, info, selectMethod, jvalue(javaBool(d.Selected))); err != nil {
+ panic(err)
+ }
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setEnabled, jvalue(javaBool(!d.Disabled))); err != nil {
+ panic(err)
+ }
+ isFocus := w.semantic.focusID == sem.ID
+ if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setAccessibilityFocused, jvalue(javaBool(isFocus))); err != nil {
+ panic(err)
+ }
+ if isFocus {
+ addAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS)
+ } else {
+ addAction(ACTION_ACCESSIBILITY_FOCUS)
+ }
+ return nil
+}
+
+func (w *window) virtualIDFor(id router.SemanticID) C.jint {
+ // TODO: Android virtual IDs are 32-bit Java integers, but childID is a int64.
+ if id == w.semantic.rootID {
+ return HOST_VIEW_ID
+ }
+ return C.jint(id)
+}
+
+func (w *window) semIDFor(virtID C.jint) router.SemanticID {
+ if virtID == HOST_VIEW_ID {
+ return w.semantic.rootID
+ }
+ return router.SemanticID(virtID)
+}
+
+func (w *window) detach(env *C.JNIEnv) {
+ callVoidMethod(env, w.view, gioView.unregister)
+ w.callbacks.Event(ViewEvent{})
+ w.callbacks.SetDriver(nil)
+ delete(views, C.jlong(w.view))
+ C.jni_DeleteGlobalRef(env, w.view)
+ w.view = 0
+}
+
+func (w *window) setVisible(env *C.JNIEnv) {
+ width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
+ if width == 0 || height == 0 {
+ return
+ }
+ w.setStage(system.StageRunning)
+ w.draw(env, true)
+}
+
+func (w *window) setStage(stage system.Stage) {
+ if stage == w.stage {
+ return
+ }
+ w.stage = stage
+ w.callbacks.Event(system.StageEvent{stage})
+}
+
+func (w *window) setVisual(visID int) error {
+ if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 {
+ return errors.New("ANativeWindow_setBuffersGeometry failed")
+ }
+ return nil
+}
+
+func (w *window) nativeWindow() (*C.ANativeWindow, int, int) {
+ width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
+ return w.win, int(width), int(height)
+}
+
+func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) {
+ dpi := int(C.jni_CallIntMethod(env, w.view, gioView.getDensity))
+ w.fontScale = float32(C.jni_CallFloatMethod(env, w.view, gioView.getFontScale))
+ switch dpi {
+ case C.ACONFIGURATION_DENSITY_NONE,
+ C.ACONFIGURATION_DENSITY_DEFAULT,
+ C.ACONFIGURATION_DENSITY_ANY:
+ // Assume standard density.
+ w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM
+ default:
+ w.dpi = int(dpi)
+ }
+}
+
+func (w *window) SetAnimating(anim bool) {
+ w.animating = anim
+ if anim {
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ callVoidMethod(env, w.view, gioView.postInvalidate)
+ })
+ }
+}
+
+func (w *window) draw(env *C.JNIEnv, sync bool) {
+ size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win)))
+ if size != w.config.Size {
+ w.config.Size = size
+ w.callbacks.Event(ConfigEvent{Config: w.config})
+ }
+ if size.X == 0 || size.Y == 0 {
+ return
+ }
+ const inchPrDp = 1.0 / 160
+ ppdp := float32(w.dpi) * inchPrDp
+ w.callbacks.Event(frameEvent{
+ FrameEvent: system.FrameEvent{
+ Now: time.Now(),
+ Size: w.config.Size,
+ Insets: w.insets,
+ Metric: unit.Metric{
+ PxPerDp: ppdp,
+ PxPerSp: w.fontScale * ppdp,
+ },
+ },
+ Sync: sync,
+ })
+ a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive)
+ if err != nil {
+ panic(err)
+ }
+ if a11yActive {
+ if newR, oldR := w.callbacks.SemanticRoot(), w.semantic.rootID; newR != oldR {
+ // Remap focus and hover.
+ if oldR == w.semantic.hoverID {
+ w.semantic.hoverID = newR
+ }
+ if oldR == w.semantic.focusID {
+ w.semantic.focusID = newR
+ }
+ w.semantic.rootID = newR
+ callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(newR)))
+ }
+ w.semantic.diffs = w.callbacks.AppendSemanticDiffs(w.semantic.diffs[:0])
+ for _, id := range w.semantic.diffs {
+ callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(id)))
+ }
+ }
+}
+
+type keyMapper func(devId, keyCode C.int32_t) rune
+
+func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
+ if jvm == nil {
+ panic("nil JVM")
+ }
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+ var env *C.JNIEnv
+ if res := C.jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
+ if res != C.JNI_EDETACHED {
+ panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
+ }
+ if C.jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
+ panic(errors.New("runInJVM: AttachCurrentThread failed"))
+ }
+ defer C.jni_DetachCurrentThread(jvm)
+ }
+
+ f(env)
+}
+
+func convertKeyCode(code C.jint) (string, bool) {
+ var n string
+ switch code {
+ case C.AKEYCODE_DPAD_UP:
+ n = key.NameUpArrow
+ case C.AKEYCODE_DPAD_DOWN:
+ n = key.NameDownArrow
+ case C.AKEYCODE_DPAD_LEFT:
+ n = key.NameLeftArrow
+ case C.AKEYCODE_DPAD_RIGHT:
+ n = key.NameRightArrow
+ case C.AKEYCODE_FORWARD_DEL:
+ n = key.NameDeleteForward
+ case C.AKEYCODE_DEL:
+ n = key.NameDeleteBackward
+ case C.AKEYCODE_NUMPAD_ENTER:
+ n = key.NameEnter
+ case C.AKEYCODE_ENTER:
+ n = key.NameEnter
+ default:
+ return "", false
+ }
+ return n, true
+}
+
+//export Java_org_gioui_GioView_onKeyEvent
+func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) {
+ w := views[handle]
+ if n, ok := convertKeyCode(keyCode); ok {
+ w.callbacks.Event(key.Event{Name: n})
+ }
+ if r != 0 && r != '\n' { // Checking for "\n" to prevent duplication with key.NameEnter (gio#224).
+ w.callbacks.Event(key.EditEvent{Text: string(rune(r))})
+ }
+}
+
+//export Java_org_gioui_GioView_onTouchEvent
+func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) {
+ w := views[handle]
+ var typ pointer.Type
+ switch action {
+ case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
+ typ = pointer.Press
+ case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
+ typ = pointer.Release
+ case C.AMOTION_EVENT_ACTION_CANCEL:
+ typ = pointer.Cancel
+ case C.AMOTION_EVENT_ACTION_MOVE:
+ typ = pointer.Move
+ case C.AMOTION_EVENT_ACTION_SCROLL:
+ typ = pointer.Scroll
+ default:
+ return
+ }
+ var src pointer.Source
+ var btns pointer.Buttons
+ if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 {
+ btns |= pointer.ButtonPrimary
+ }
+ if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 {
+ btns |= pointer.ButtonSecondary
+ }
+ if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 {
+ btns |= pointer.ButtonTertiary
+ }
+ switch tool {
+ case C.AMOTION_EVENT_TOOL_TYPE_FINGER:
+ src = pointer.Touch
+ case C.AMOTION_EVENT_TOOL_TYPE_STYLUS:
+ src = pointer.Touch
+ case C.AMOTION_EVENT_TOOL_TYPE_MOUSE:
+ src = pointer.Mouse
+ case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
+ // For example, triggered via 'adb shell input tap'.
+ // Instead of discarding it, treat it as a touch event.
+ src = pointer.Touch
+ default:
+ return
+ }
+ w.callbacks.Event(pointer.Event{
+ Type: typ,
+ Source: src,
+ Buttons: btns,
+ PointerID: pointer.ID(pointerID),
+ Time: time.Duration(t) * time.Millisecond,
+ Position: f32.Point{X: float32(x), Y: float32(y)},
+ Scroll: f32.Pt(float32(scrollX), float32(scrollY)),
+ })
+}
+
+func (w *window) ShowTextInput(show bool) {
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ if show {
+ callVoidMethod(env, w.view, gioView.showTextInput)
+ } else {
+ callVoidMethod(env, w.view, gioView.hideTextInput)
+ }
+ })
+}
+
+func (w *window) SetInputHint(mode key.InputHint) {
+ // Constants defined at https://developer.android.com/reference/android/text/InputType.
+ const (
+ TYPE_NULL = 0
+ TYPE_CLASS_NUMBER = 2
+ TYPE_NUMBER_FLAG_DECIMAL = 8192
+ TYPE_NUMBER_FLAG_SIGNED = 4096
+ TYPE_TEXT_FLAG_NO_SUGGESTIONS = 524288
+ TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 144
+ )
+
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ var m jvalue
+ switch mode {
+ case key.HintNumeric:
+ m = TYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_DECIMAL | TYPE_NUMBER_FLAG_SIGNED
+ default:
+ // TYPE_NULL, since TYPE_CLASS_TEXT isn't currently supported.
+ m = TYPE_NULL
+ }
+
+ // The TYPE_TEXT_FLAG_NO_SUGGESTIONS and TYPE_TEXT_VARIATION_VISIBLE_PASSWORD are used to fix the
+ // Samsung keyboard compatibility, forcing to disable the suggests/auto-complete. gio#116.
+ m = m | TYPE_TEXT_FLAG_NO_SUGGESTIONS | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
+
+ callVoidMethod(env, w.view, gioView.setInputHint, m)
+ })
+}
+
+func javaBool(b bool) C.jboolean {
+ if b {
+ return C.JNI_TRUE
+ } else {
+ return C.JNI_FALSE
+ }
+}
+
+func javaString(env *C.JNIEnv, str string) C.jstring {
+ if str == "" {
+ return 0
+ }
+ utf16Chars := utf16.Encode([]rune(str))
+ return C.jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars)))
+}
+
+func varArgs(args []jvalue) *C.jvalue {
+ if len(args) == 0 {
+ return nil
+ }
+ return (*C.jvalue)(unsafe.Pointer(&args[0]))
+}
+
+func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error {
+ C.jni_CallStaticVoidMethodA(env, cls, method, varArgs(args))
+ return exception(env)
+}
+
+func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
+ res := C.jni_CallStaticObjectMethodA(env, cls, method, varArgs(args))
+ return res, exception(env)
+}
+
+func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error {
+ C.jni_CallVoidMethodA(env, obj, method, varArgs(args))
+ return exception(env)
+}
+
+func callBooleanMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (bool, error) {
+ res := C.jni_CallBooleanMethodA(env, obj, method, varArgs(args))
+ return res == C.JNI_TRUE, exception(env)
+}
+
+func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) {
+ res := C.jni_CallObjectMethodA(env, obj, method, varArgs(args))
+ return res, exception(env)
+}
+
+func newObject(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
+ res := C.jni_NewObjectA(env, cls, method, varArgs(args))
+ return res, exception(env)
+}
+
+// exception returns an error corresponding to the pending
+// exception, or nil if no exception is pending. The pending
+// exception is cleared.
+func exception(env *C.JNIEnv) error {
+ thr := C.jni_ExceptionOccurred(env)
+ if thr == 0 {
+ return nil
+ }
+ C.jni_ExceptionClear(env)
+ cls := getObjectClass(env, C.jobject(thr))
+ toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;")
+ msg, err := callObjectMethod(env, C.jobject(thr), toString)
+ if err != nil {
+ return err
+ }
+ return errors.New(goString(env, C.jstring(msg)))
+}
+
+func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass {
+ if obj == 0 {
+ panic("null object")
+ }
+ cls := C.jni_GetObjectClass(env, C.jobject(obj))
+ if err := exception(env); err != nil {
+ // GetObjectClass should never fail.
+ panic(err)
+ }
+ return cls
+}
+
+// goString converts the JVM jstring to a Go string.
+func goString(env *C.JNIEnv, str C.jstring) string {
+ if str == 0 {
+ return ""
+ }
+ strlen := C.jni_GetStringLength(env, C.jstring(str))
+ chars := C.jni_GetStringChars(env, C.jstring(str))
+ var utf16Chars []uint16
+ hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars))
+ hdr.Data = uintptr(unsafe.Pointer(chars))
+ hdr.Cap = int(strlen)
+ hdr.Len = int(strlen)
+ utf8 := utf16.Decode(utf16Chars)
+ return string(utf8)
+}
+
+func findClass(env *C.JNIEnv, name string) C.jclass {
+ cn := C.CString(name)
+ defer C.free(unsafe.Pointer(cn))
+ return C.jni_FindClass(env, cn)
+}
+
+func osMain() {
+}
+
+func newWindow(window *callbacks, options []Option) error {
+ mainWindow.in <- windowAndConfig{window, options}
+ return <-mainWindow.errs
+}
+
+func (w *window) WriteClipboard(s string) {
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ jstr := javaString(env, s)
+ callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard,
+ jvalue(android.appCtx), jvalue(jstr))
+ })
+}
+
+func (w *window) ReadClipboard() {
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard,
+ jvalue(android.appCtx))
+ if err != nil {
+ return
+ }
+ content := goString(env, C.jstring(c))
+ w.callbacks.Event(clipboard.Event{Text: content})
+ })
+}
+
+func (w *window) Configure(options []Option) {
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ prev := w.config
+ cnf := w.config
+ cnf.apply(unit.Metric{}, options)
+ if prev.Orientation != cnf.Orientation {
+ w.config.Orientation = cnf.Orientation
+ setOrientation(env, w.view, cnf.Orientation)
+ }
+ if prev.NavigationColor != cnf.NavigationColor {
+ w.config.NavigationColor = cnf.NavigationColor
+ setNavigationColor(env, w.view, cnf.NavigationColor)
+ }
+ if prev.StatusColor != cnf.StatusColor {
+ w.config.StatusColor = cnf.StatusColor
+ setStatusColor(env, w.view, cnf.StatusColor)
+ }
+ if prev.Mode != cnf.Mode {
+ switch cnf.Mode {
+ case Fullscreen:
+ callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_TRUE)
+ w.config.Mode = Fullscreen
+ case Windowed:
+ callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_FALSE)
+ w.config.Mode = Windowed
+ }
+ }
+ if w.config != prev {
+ w.callbacks.Event(ConfigEvent{Config: w.config})
+ }
+ })
+}
+
+func (w *window) Raise() {}
+
+func (w *window) SetCursor(name pointer.CursorName) {
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ setCursor(env, w.view, name)
+ })
+}
+
+func (w *window) Wakeup() {
+ runOnMain(func(env *C.JNIEnv) {
+ w.callbacks.Event(wakeupEvent{})
+ })
+}
+
+func setCursor(env *C.JNIEnv, view C.jobject, name pointer.CursorName) {
+ var curID int
+ switch name {
+ default:
+ fallthrough
+ case pointer.CursorDefault:
+ curID = 1000 // TYPE_ARROW
+ case pointer.CursorText:
+ curID = 1008 // TYPE_TEXT
+ case pointer.CursorPointer:
+ curID = 1002 // TYPE_HAND
+ case pointer.CursorCrossHair:
+ curID = 1007 // TYPE_CROSSHAIR
+ case pointer.CursorColResize:
+ curID = 1014 // TYPE_HORIZONTAL_DOUBLE_ARROW
+ case pointer.CursorRowResize:
+ curID = 1015 // TYPE_VERTICAL_DOUBLE_ARROW
+ case pointer.CursorNone:
+ curID = 0 // TYPE_NULL
+ }
+ callVoidMethod(env, view, gioView.setCursor, jvalue(curID))
+}
+
+func setOrientation(env *C.JNIEnv, view C.jobject, mode Orientation) {
+ var (
+ id int
+ idFallback int // Used only for SDK 17 or older.
+ )
+ // Constants defined at https://developer.android.com/reference/android/content/pm/ActivityInfo.
+ switch mode {
+ case AnyOrientation:
+ id, idFallback = 2, 2 // SCREEN_ORIENTATION_USER
+ case LandscapeOrientation:
+ id, idFallback = 11, 0 // SCREEN_ORIENTATION_USER_LANDSCAPE (or SCREEN_ORIENTATION_LANDSCAPE)
+ case PortraitOrientation:
+ id, idFallback = 12, 1 // SCREEN_ORIENTATION_USER_PORTRAIT (or SCREEN_ORIENTATION_PORTRAIT)
+ }
+ callVoidMethod(env, view, gioView.setOrientation, jvalue(id), jvalue(idFallback))
+}
+
+func setStatusColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) {
+ callVoidMethod(env, view, gioView.setStatusColor,
+ jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)),
+ jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)),
+ )
+}
+
+func setNavigationColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) {
+ callVoidMethod(env, view, gioView.setNavigationColor,
+ jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)),
+ jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)),
+ )
+}
+
+// Close the window. Not implemented for Android.
+func (w *window) Close() {}
+
+// Maximize maximizes the window. Not implemented for Android.
+func (w *window) Maximize() {}
+
+// Center the window. Not implemented for Android.
+func (w *window) Center() {}
+
+// runOnMain runs a function on the Java main thread.
+func runOnMain(f func(env *C.JNIEnv)) {
+ go func() {
+ mainFuncs <- f
+ runInJVM(javaVM(), func(env *C.JNIEnv) {
+ callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread)
+ })
+ }()
+}
+
+//export Java_org_gioui_Gio_scheduleMainFuncs
+func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
+ for {
+ select {
+ case f := <-mainFuncs:
+ f(env)
+ default:
+ return
+ }
+ }
+}
+
+func (_ ViewEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/app/internal/window/os_darwin.go b/vendor/gioui.org/app/os_darwin.go
index a3b77dc..c0554db 100644
--- a/vendor/gioui.org/app/internal/window/os_darwin.go
+++ b/vendor/gioui.org/app/os_darwin.go
@@ -1,18 +1,33 @@
// SPDX-License-Identifier: Unlicense OR MIT
-package window
+package app
/*
#include <Foundation/Foundation.h>
__attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void);
-__attribute__ ((visibility ("hidden"))) NSUInteger gio_nsstringLength(CFTypeRef str);
-__attribute__ ((visibility ("hidden"))) void gio_nsstringGetCharacters(CFTypeRef str, unichar *chars, NSUInteger loc, NSUInteger length);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void);
__attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl);
__attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl);
__attribute__ ((visibility ("hidden"))) int gio_stopDisplayLink(CFTypeRef dl);
__attribute__ ((visibility ("hidden"))) void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did);
+__attribute__ ((visibility ("hidden"))) void gio_hideCursor();
+__attribute__ ((visibility ("hidden"))) void gio_showCursor();
+__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID);
+
+static bool isMainThread() {
+ return [NSThread isMainThread];
+}
+
+static NSUInteger nsstringLength(CFTypeRef cstr) {
+ NSString *str = (__bridge NSString *)cstr;
+ return [str length];
+}
+
+static void nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc, NSUInteger length) {
+ NSString *str = (__bridge NSString *)cstr;
+ [str getCharacters:chars range:NSMakeRange(loc, length)];
+}
*/
import "C"
import (
@@ -22,6 +37,8 @@ import (
"time"
"unicode/utf16"
"unsafe"
+
+ "gioui.org/io/pointer"
)
// displayLink is the state for a display link (CVDisplayLinkRef on macOS,
@@ -50,6 +67,10 @@ var mainFuncs = make(chan func(), 1)
// runOnMain runs the function on the main thread.
func runOnMain(f func()) {
+ if C.isMainThread() {
+ f()
+ return
+ }
go func() {
mainFuncs <- f
C.gio_wakeupMainThread()
@@ -71,13 +92,16 @@ func gio_dispatchMainFuncs() {
// nsstringToString converts a NSString to a Go string, and
// releases the original string.
func nsstringToString(str C.CFTypeRef) string {
+ if str == 0 {
+ return ""
+ }
defer C.CFRelease(str)
- n := C.gio_nsstringLength(str)
+ n := C.nsstringLength(str)
if n == 0 {
return ""
}
chars := make([]uint16, n)
- C.gio_nsstringGetCharacters(str, (*C.unichar)(unsafe.Pointer(&chars[0])), 0, n)
+ C.nsstringGetCharacters(str, (*C.unichar)(unsafe.Pointer(&chars[0])), 0, n)
utf8 := utf16.Decode(chars)
return string(utf8)
}
@@ -169,3 +193,45 @@ func gio_onFrameCallback(dl C.CFTypeRef) {
}
}
}
+
+// windowSetCursor updates the cursor from the current one to a new one
+// and returns the new one.
+func windowSetCursor(from, to pointer.CursorName) pointer.CursorName {
+ if from == to {
+ return to
+ }
+ var curID int
+ switch to {
+ default:
+ to = pointer.CursorDefault
+ fallthrough
+ case pointer.CursorDefault:
+ curID = 1
+ case pointer.CursorText:
+ curID = 2
+ case pointer.CursorPointer:
+ curID = 3
+ case pointer.CursorCrossHair:
+ curID = 4
+ case pointer.CursorColResize:
+ curID = 5
+ case pointer.CursorRowResize:
+ curID = 6
+ case pointer.CursorGrab:
+ curID = 7
+ case pointer.CursorNone:
+ C.gio_hideCursor()
+ return to
+ }
+ if from == pointer.CursorNone {
+ C.gio_showCursor()
+ }
+ C.gio_setCursor(C.NSUInteger(curID))
+ return to
+}
+
+func (w *window) Wakeup() {
+ runOnMain(func() {
+ w.w.Event(wakeupEvent{})
+ })
+}
diff --git a/vendor/gioui.org/app/os_darwin.m b/vendor/gioui.org/app/os_darwin.m
new file mode 100644
index 0000000..772cc28
--- /dev/null
+++ b/vendor/gioui.org/app/os_darwin.m
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+@import Dispatch;
+@import Foundation;
+
+#include "_cgo_export.h"
+
+void gio_wakeupMainThread(void) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ gio_dispatchMainFuncs();
+ });
+}
diff --git a/vendor/gioui.org/app/internal/window/os_ios.go b/vendor/gioui.org/app/os_ios.go
index 1865454..f6f6dea 100644
--- a/vendor/gioui.org/app/internal/window/os_ios.go
+++ b/vendor/gioui.org/app/os_ios.go
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build darwin && ios
// +build darwin,ios
-package window
+package app
/*
#cgo CFLAGS: -DGLES_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c
@@ -17,14 +18,55 @@ struct drawParams {
CGFloat top, right, bottom, left;
};
-__attribute__ ((visibility ("hidden"))) void gio_showTextInput(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) void gio_hideTextInput(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef);
-__attribute__ ((visibility ("hidden"))) void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef);
-__attribute__ ((visibility ("hidden"))) void gio_removeLayer(CFTypeRef layerRef);
-__attribute__ ((visibility ("hidden"))) struct drawParams gio_viewDrawParams(CFTypeRef viewRef);
-__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
-__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
+static void writeClipboard(unichar *chars, NSUInteger length) {
+ @autoreleasepool {
+ NSString *s = [NSString string];
+ if (length > 0) {
+ s = [NSString stringWithCharacters:chars length:length];
+ }
+ UIPasteboard *p = UIPasteboard.generalPasteboard;
+ p.string = s;
+ }
+}
+
+static CFTypeRef readClipboard(void) {
+ @autoreleasepool {
+ UIPasteboard *p = UIPasteboard.generalPasteboard;
+ return (__bridge_retained CFTypeRef)p.string;
+ }
+}
+
+static void showTextInput(CFTypeRef viewRef) {
+ UIView *view = (__bridge UIView *)viewRef;
+ [view becomeFirstResponder];
+}
+
+static void hideTextInput(CFTypeRef viewRef) {
+ UIView *view = (__bridge UIView *)viewRef;
+ [view resignFirstResponder];
+}
+
+static struct drawParams viewDrawParams(CFTypeRef viewRef) {
+ UIView *v = (__bridge UIView *)viewRef;
+ struct drawParams params;
+ CGFloat scale = v.layer.contentsScale;
+ // Use 163 as the standard ppi on iOS.
+ params.dpi = 163*scale;
+ params.sdpi = params.dpi;
+ UIEdgeInsets insets = v.layoutMargins;
+ if (@available(iOS 11.0, tvOS 11.0, *)) {
+ UIFontMetrics *metrics = [UIFontMetrics defaultMetrics];
+ params.sdpi = [metrics scaledValueForValue:params.sdpi];
+ insets = v.safeAreaInsets;
+ }
+ params.width = v.bounds.size.width*scale;
+ params.height = v.bounds.size.height*scale;
+ params.top = insets.top*scale;
+ params.right = insets.right*scale;
+ params.bottom = insets.bottom*scale;
+ params.left = insets.left*scale;
+ return params;
+}
*/
import "C"
@@ -32,33 +74,36 @@ import (
"image"
"runtime"
"runtime/debug"
- "sync/atomic"
"time"
"unicode/utf16"
"unsafe"
"gioui.org/f32"
+ "gioui.org/io/clipboard"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/system"
"gioui.org/unit"
)
+type ViewEvent struct {
+ // ViewController is a CFTypeRef for the UIViewController backing a Window.
+ ViewController uintptr
+}
+
type window struct {
view C.CFTypeRef
- w Callbacks
+ w *callbacks
displayLink *displayLink
- layer C.CFTypeRef
- visible atomic.Value
+ visible bool
+ cursor pointer.CursorName
pointerMap []C.CFTypeRef
}
var mainWindow = newWindowRendezvous()
-var layerFactory func() uintptr
-
var views = make(map[C.CFTypeRef]*window)
func init() {
@@ -67,7 +112,7 @@ func init() {
}
//export onCreate
-func onCreate(view C.CFTypeRef) {
+func onCreate(view, controller C.CFTypeRef) {
w := &window{
view: view,
}
@@ -81,11 +126,9 @@ func onCreate(view C.CFTypeRef) {
wopts := <-mainWindow.out
w.w = wopts.window
w.w.SetDriver(w)
- w.visible.Store(false)
- w.layer = C.CFTypeRef(layerFactory())
- C.gio_addLayerToView(view, w.layer)
views[view] = w
w.w.Event(system.StageEvent{Stage: system.StagePaused})
+ w.w.Event(ViewEvent{ViewController: uintptr(controller)})
}
//export gio_onDraw
@@ -95,18 +138,17 @@ func gio_onDraw(view C.CFTypeRef) {
}
func (w *window) draw(sync bool) {
- params := C.gio_viewDrawParams(w.view)
+ params := C.viewDrawParams(w.view)
if params.width == 0 || params.height == 0 {
return
}
- wasVisible := w.isVisible()
- w.visible.Store(true)
- C.gio_updateView(w.view, w.layer)
+ wasVisible := w.visible
+ w.visible = true
if !wasVisible {
w.w.Event(system.StageEvent{Stage: system.StageRunning})
}
const inchPrDp = 1.0 / 163
- w.w.Event(FrameEvent{
+ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{
Now: time.Now(),
Size: image.Point{
@@ -131,7 +173,7 @@ func (w *window) draw(sync bool) {
//export onStop
func onStop(view C.CFTypeRef) {
w := views[view]
- w.visible.Store(false)
+ w.visible = false
w.w.Event(system.StageEvent{Stage: system.StagePaused})
}
@@ -139,11 +181,9 @@ func onStop(view C.CFTypeRef) {
func onDestroy(view C.CFTypeRef) {
w := views[view]
delete(views, view)
+ w.w.Event(ViewEvent{})
w.w.Event(system.DestroyEvent{})
w.displayLink.Close()
- C.gio_removeLayer(w.layer)
- C.CFRelease(w.layer)
- w.layer = 0
w.view = 0
}
@@ -220,23 +260,23 @@ func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.C
}
func (w *window) ReadClipboard() {
- runOnMain(func() {
- content := nsstringToString(C.gio_readClipboard())
- w.w.Event(system.ClipboardEvent{Text: content})
- })
+ content := nsstringToString(C.readClipboard())
+ w.w.Event(clipboard.Event{Text: content})
}
func (w *window) WriteClipboard(s string) {
u16 := utf16.Encode([]rune(s))
- runOnMain(func() {
- var chars *C.unichar
- if len(u16) > 0 {
- chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
- }
- C.gio_writeClipboard(chars, C.NSUInteger(len(u16)))
- })
+ var chars *C.unichar
+ if len(u16) > 0 {
+ chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
+ }
+ C.writeClipboard(chars, C.NSUInteger(len(u16)))
}
+func (w *window) Configure([]Option) {}
+
+func (w *window) Raise() {}
+
func (w *window) SetAnimating(anim bool) {
v := w.view
if v == 0 {
@@ -249,6 +289,10 @@ func (w *window) SetAnimating(anim bool) {
}
}
+func (w *window) SetCursor(name pointer.CursorName) {
+ w.cursor = windowSetCursor(w.cursor, name)
+}
+
func (w *window) onKeyCommand(name string) {
w.w.Event(key.Event{
Name: name,
@@ -275,42 +319,40 @@ func (w *window) lookupTouch(last bool, touch C.CFTypeRef) pointer.ID {
return pointer.ID(id)
}
-func (w *window) contextLayer() uintptr {
- return uintptr(w.layer)
-}
-
-func (w *window) isVisible() bool {
- return w.visible.Load().(bool)
+func (w *window) contextView() C.CFTypeRef {
+ return w.view
}
func (w *window) ShowTextInput(show bool) {
- v := w.view
- if v == 0 {
- return
+ if show {
+ C.showTextInput(w.view)
+ } else {
+ C.hideTextInput(w.view)
}
- C.CFRetain(v)
- runOnMain(func() {
- defer C.CFRelease(v)
- if show {
- C.gio_showTextInput(w.view)
- } else {
- C.gio_hideTextInput(w.view)
- }
- })
}
+func (w *window) SetInputHint(_ key.InputHint) {}
+
// Close the window. Not implemented for iOS.
func (w *window) Close() {}
-func NewWindow(win Callbacks, opts *Options) error {
- mainWindow.in <- windowAndOptions{win, opts}
+// Maximize the window. Not implemented for iOS.
+func (w *window) Maximize() {}
+
+// Center the window. Not implemented for iOS.
+func (w *window) Center() {}
+
+func newWindow(win *callbacks, options []Option) error {
+ mainWindow.in <- windowAndConfig{win, options}
return <-mainWindow.errs
}
-func Main() {
+func osMain() {
}
//export gio_runMain
func gio_runMain() {
runMain()
}
+
+func (_ ViewEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/app/internal/window/os_ios.m b/vendor/gioui.org/app/os_ios.m
index 66830e4..94f254b 100644
--- a/vendor/gioui.org/app/internal/window/os_ios.m
+++ b/vendor/gioui.org/app/os_ios.m
@@ -8,6 +8,8 @@
#include "_cgo_export.h"
#include "framework_ios.h"
+__attribute__ ((visibility ("hidden"))) Class gio_layerClass(void);
+
@interface GioView: UIView <UIKeyInput>
@end
@@ -28,7 +30,7 @@ CGFloat _keyboardHeight;
#endif
drawView.preservesSuperviewLayoutMargins = YES;
drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
- onCreate((__bridge CFTypeRef)drawView);
+ onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillChange:)
name:UIKeyboardWillShowNotification
@@ -125,6 +127,9 @@ NSArray<UIKeyCommand *> *_keyCommands;
gio_onFrameCallback((__bridge CFTypeRef)link);
}
++ (Class)layerClass {
+ return gio_layerClass();
+}
- (void)willMoveToWindow:(UIWindow *)newWindow {
if (self.window != nil) {
[[NSNotificationCenter defaultCenter] removeObserver:self
@@ -157,9 +162,6 @@ NSArray<UIKeyCommand *> *_keyCommands;
}
}
-- (void)dealloc {
-}
-
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
handleTouches(0, self, touches, event);
}
@@ -229,74 +231,6 @@ NSArray<UIKeyCommand *> *_keyCommands;
}
@end
-void gio_writeClipboard(unichar *chars, NSUInteger length) {
- @autoreleasepool {
- NSString *s = [NSString string];
- if (length > 0) {
- s = [NSString stringWithCharacters:chars length:length];
- }
- UIPasteboard *p = UIPasteboard.generalPasteboard;
- p.string = s;
- }
-}
-
-CFTypeRef gio_readClipboard(void) {
- @autoreleasepool {
- UIPasteboard *p = UIPasteboard.generalPasteboard;
- return (__bridge_retained CFTypeRef)p.string;
- }
-}
-
-void gio_showTextInput(CFTypeRef viewRef) {
- UIView *view = (__bridge UIView *)viewRef;
- [view becomeFirstResponder];
-}
-
-void gio_hideTextInput(CFTypeRef viewRef) {
- UIView *view = (__bridge UIView *)viewRef;
- [view resignFirstResponder];
-}
-
-void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef) {
- UIView *view = (__bridge UIView *)viewRef;
- CALayer *layer = (__bridge CALayer *)layerRef;
- [view.layer addSublayer:layer];
-}
-
-void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef) {
- UIView *view = (__bridge UIView *)viewRef;
- CAEAGLLayer *layer = (__bridge CAEAGLLayer *)layerRef;
- layer.contentsScale = view.contentScaleFactor;
- layer.bounds = view.bounds;
-}
-
-void gio_removeLayer(CFTypeRef layerRef) {
- CALayer *layer = (__bridge CALayer *)layerRef;
- [layer removeFromSuperlayer];
-}
-
-struct drawParams gio_viewDrawParams(CFTypeRef viewRef) {
- UIView *v = (__bridge UIView *)viewRef;
- struct drawParams params;
- CGFloat scale = v.layer.contentsScale;
- // Use 163 as the standard ppi on iOS.
- params.dpi = 163*scale;
- params.sdpi = params.dpi;
- UIEdgeInsets insets = v.layoutMargins;
- if (@available(iOS 11.0, tvOS 11.0, *)) {
- UIFontMetrics *metrics = [UIFontMetrics defaultMetrics];
- params.sdpi = [metrics scaledValueForValue:params.sdpi];
- insets = v.safeAreaInsets;
- }
- params.width = v.bounds.size.width*scale;
- params.height = v.bounds.size.height*scale;
- params.top = insets.top*scale;
- params.right = insets.right*scale;
- params.bottom = insets.bottom*scale;
- params.left = insets.left*scale;
- return params;
-}
-
CFTypeRef gio_createDisplayLink(void) {
CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)];
dl.paused = YES;
@@ -326,3 +260,15 @@ void gio_releaseDisplayLink(CFTypeRef dlref) {
void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
// Nothing to do on iOS.
}
+
+void gio_hideCursor() {
+ // Not supported.
+}
+
+void gio_showCursor() {
+ // Not supported.
+}
+
+void gio_setCursor(NSUInteger curID) {
+ // Not supported.
+}
diff --git a/vendor/gioui.org/app/internal/window/os_js.go b/vendor/gioui.org/app/os_js.go
index 32235df..5b1388c 100644
--- a/vendor/gioui.org/app/internal/window/os_js.go
+++ b/vendor/gioui.org/app/os_js.go
@@ -1,42 +1,62 @@
// SPDX-License-Identifier: Unlicense OR MIT
-package window
+package app
import (
+ "fmt"
"image"
+ "image/color"
"strings"
- "sync"
"syscall/js"
"time"
"unicode"
"unicode/utf8"
+ "gioui.org/internal/f32color"
+
"gioui.org/f32"
+ "gioui.org/io/clipboard"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/system"
"gioui.org/unit"
)
+type ViewEvent struct{}
+
type window struct {
window js.Value
+ document js.Value
+ head js.Value
clipboard js.Value
cnv js.Value
tarea js.Value
- w Callbacks
+ w *callbacks
redraw js.Func
clipboardCallback js.Func
requestAnimationFrame js.Value
+ browserHistory js.Value
+ visualViewport js.Value
+ screenOrientation js.Value
cleanfuncs []func()
touches []js.Value
composing bool
+ requestFocus bool
+
+ chanAnimation chan struct{}
+ chanRedraw chan struct{}
- mu sync.Mutex
+ config Config
+ inset f32.Point
scale float32
animating bool
+ // animRequested tracks whether a requestAnimationFrame callback
+ // is pending.
+ animRequested bool
+ wakeups chan struct{}
}
-func NewWindow(win Callbacks, opts *Options) error {
+func newWindow(win *callbacks, options []Option) error {
doc := js.Global().Get("document")
cont := getContainer(doc)
cnv := createCanvas(doc)
@@ -45,29 +65,55 @@ func NewWindow(win Callbacks, opts *Options) error {
cont.Call("appendChild", tarea)
w := &window{
cnv: cnv,
+ document: doc,
tarea: tarea,
window: js.Global().Get("window"),
+ head: doc.Get("head"),
clipboard: js.Global().Get("navigator").Get("clipboard"),
+ wakeups: make(chan struct{}, 1),
}
w.requestAnimationFrame = w.window.Get("requestAnimationFrame")
+ w.browserHistory = w.window.Get("history")
+ w.visualViewport = w.window.Get("visualViewport")
+ if w.visualViewport.IsUndefined() {
+ w.visualViewport = w.window
+ }
+ if screen := w.window.Get("screen"); screen.Truthy() {
+ w.screenOrientation = screen.Get("orientation")
+ }
+ w.chanAnimation = make(chan struct{}, 1)
+ w.chanRedraw = make(chan struct{}, 1)
w.redraw = w.funcOf(func(this js.Value, args []js.Value) interface{} {
- w.animCallback()
+ w.chanAnimation <- struct{}{}
return nil
})
w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} {
content := args[0].String()
- win.Event(system.ClipboardEvent{Text: content})
+ go win.Event(clipboard.Event{Text: content})
return nil
})
w.addEventListeners()
+ w.addHistory()
w.w = win
+
go func() {
+ defer w.cleanup()
w.w.SetDriver(w)
- w.focus()
+ w.Configure(options)
+ w.blur()
w.w.Event(system.StageEvent{Stage: system.StageRunning})
+ w.resize()
w.draw(true)
- select {}
- w.cleanup()
+ for {
+ select {
+ case <-w.wakeups:
+ w.w.Event(wakeupEvent{})
+ case <-w.chanAnimation:
+ w.animCallback()
+ case <-w.chanRedraw:
+ w.draw(true)
+ }
+ }
}()
return nil
}
@@ -116,8 +162,33 @@ func (w *window) cleanup() {
}
func (w *window) addEventListeners() {
- w.addEventListener(w.window, "resize", func(this js.Value, args []js.Value) interface{} {
- w.draw(true)
+ w.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} {
+ w.resize()
+ w.chanRedraw <- struct{}{}
+ return nil
+ })
+ w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} {
+ args[0].Call("preventDefault")
+ return nil
+ })
+ w.addEventListener(w.window, "popstate", func(this js.Value, args []js.Value) interface{} {
+ ev := &system.CommandEvent{Type: system.CommandBack}
+ w.w.Event(ev)
+ if ev.Cancel {
+ return w.browserHistory.Call("forward")
+ }
+
+ return w.browserHistory.Call("back")
+ })
+ w.addEventListener(w.document, "visibilitychange", func(this js.Value, args []js.Value) interface{} {
+ ev := system.StageEvent{}
+ switch w.document.Get("visibilityState").String() {
+ case "hidden", "prerender", "unloaded":
+ ev.Stage = system.StagePaused
+ default:
+ ev.Stage = system.StageRunning
+ }
+ w.w.Event(ev)
return nil
})
w.addEventListener(w.cnv, "mousemove", func(this js.Value, args []js.Value) interface{} {
@@ -126,6 +197,10 @@ func (w *window) addEventListeners() {
})
w.addEventListener(w.cnv, "mousedown", func(this js.Value, args []js.Value) interface{} {
w.pointerEvent(pointer.Press, 0, 0, args[0])
+ if w.requestFocus {
+ w.focus()
+ w.requestFocus = false
+ }
return nil
})
w.addEventListener(w.cnv, "mouseup", func(this js.Value, args []js.Value) interface{} {
@@ -149,6 +224,10 @@ func (w *window) addEventListeners() {
})
w.addEventListener(w.cnv, "touchstart", func(this js.Value, args []js.Value) interface{} {
w.touchEvent(pointer.Press, args[0])
+ if w.requestFocus {
+ w.focus() // iOS can only focus inside a Touch event.
+ w.requestFocus = false
+ }
return nil
})
w.addEventListener(w.cnv, "touchend", func(this js.Value, args []js.Value) interface{} {
@@ -177,10 +256,15 @@ func (w *window) addEventListeners() {
})
w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} {
w.w.Event(key.FocusEvent{Focus: false})
+ w.blur()
return nil
})
w.addEventListener(w.tarea, "keydown", func(this js.Value, args []js.Value) interface{} {
- w.keyEvent(args[0])
+ w.keyEvent(args[0], key.Press)
+ return nil
+ })
+ w.addEventListener(w.tarea, "keyup", func(this js.Value, args []js.Value) interface{} {
+ w.keyEvent(args[0], key.Release)
return nil
})
w.addEventListener(w.tarea, "compositionstart", func(this js.Value, args []js.Value) interface{} {
@@ -199,6 +283,18 @@ func (w *window) addEventListeners() {
w.flushInput()
return nil
})
+ w.addEventListener(w.tarea, "paste", func(this js.Value, args []js.Value) interface{} {
+ if w.clipboard.IsUndefined() {
+ return nil
+ }
+ // Prevents duplicated-paste, since "paste" is already handled through Clipboard API.
+ args[0].Call("preventDefault")
+ return nil
+ })
+}
+
+func (w *window) addHistory() {
+ w.browserHistory.Call("pushState", nil, nil, w.window.Get("location").Get("href"))
}
func (w *window) flushInput() {
@@ -209,18 +305,42 @@ func (w *window) flushInput() {
func (w *window) blur() {
w.tarea.Call("blur")
+ w.requestFocus = false
}
func (w *window) focus() {
w.tarea.Call("focus")
+ w.requestFocus = true
+}
+
+func (w *window) keyboard(hint key.InputHint) {
+ var m string
+ switch hint {
+ case key.HintAny:
+ m = "text"
+ case key.HintText:
+ m = "text"
+ case key.HintNumeric:
+ m = "decimal"
+ case key.HintEmail:
+ m = "email"
+ case key.HintURL:
+ m = "url"
+ case key.HintTelephone:
+ m = "tel"
+ default:
+ m = "text"
+ }
+ w.tarea.Set("inputMode", m)
}
-func (w *window) keyEvent(e js.Value) {
+func (w *window) keyEvent(e js.Value, ks key.State) {
k := e.Get("key").String()
if n, ok := translateKey(k); ok {
cmd := key.Event{
Name: n,
Modifiers: modifiersFor(e),
+ State: ks,
}
w.w.Event(cmd)
}
@@ -230,6 +350,10 @@ func (w *window) keyEvent(e js.Value) {
// KeyEvent.
func modifiersFor(e js.Value) key.Modifiers {
var mods key.Modifiers
+ if e.Get("getModifierState").IsUndefined() {
+ // Some browsers doesn't support getModifierState.
+ return mods
+ }
if e.Call("getModifierState", "Alt").Bool() {
mods |= key.ModAlt
}
@@ -248,9 +372,7 @@ func (w *window) touchEvent(typ pointer.Type, e js.Value) {
changedTouches := e.Get("changedTouches")
n := changedTouches.Length()
rect := w.cnv.Call("getBoundingClientRect")
- w.mu.Lock()
scale := w.scale
- w.mu.Unlock()
var mods key.Modifiers
if e.Get("shiftKey").Bool() {
mods |= key.ModShift
@@ -277,7 +399,7 @@ func (w *window) touchEvent(typ pointer.Type, e js.Value) {
Position: pos,
PointerID: pid,
Time: t,
- Modifiers: modifiersFor(e),
+ Modifiers: mods,
})
}
}
@@ -300,9 +422,7 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) {
rect := w.cnv.Call("getBoundingClientRect")
x -= rect.Get("left").Float()
y -= rect.Get("top").Float()
- w.mu.Lock()
scale := w.scale
- w.mu.Unlock()
pos := f32.Point{
X: float32(x) * scale,
Y: float32(y) * scale,
@@ -315,13 +435,13 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) {
jbtns := e.Get("buttons").Int()
var btns pointer.Buttons
if jbtns&1 != 0 {
- btns |= pointer.ButtonLeft
+ btns |= pointer.ButtonPrimary
}
if jbtns&2 != 0 {
- btns |= pointer.ButtonRight
+ btns |= pointer.ButtonSecondary
}
if jbtns&4 != 0 {
- btns |= pointer.ButtonMiddle
+ btns |= pointer.ButtonTertiary
}
w.w.Event(pointer.Event{
Type: typ,
@@ -343,7 +463,7 @@ func (w *window) addEventListener(this js.Value, event string, f func(this js.Va
}
// funcOf is like js.FuncOf but adds the js.Func to a list of
-// functions to be released up.
+// functions to be released during cleanup.
func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.Func {
jsf := js.FuncOf(f)
w.cleanfuncs = append(w.cleanfuncs, jsf.Release)
@@ -351,24 +471,22 @@ func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.F
}
func (w *window) animCallback() {
- w.mu.Lock()
anim := w.animating
+ w.animRequested = anim
if anim {
w.requestAnimationFrame.Invoke(w.redraw)
}
- w.mu.Unlock()
if anim {
w.draw(false)
}
}
func (w *window) SetAnimating(anim bool) {
- w.mu.Lock()
- defer w.mu.Unlock()
- if anim && !w.animating {
+ w.animating = anim
+ if anim && !w.animRequested {
+ w.animRequested = true
w.requestAnimationFrame.Invoke(w.redraw)
}
- w.animating = anim
}
func (w *window) ReadClipboard() {
@@ -391,6 +509,44 @@ func (w *window) WriteClipboard(s string) {
w.clipboard.Call("writeText", s)
}
+func (w *window) Configure(options []Option) {
+ prev := w.config
+ cnf := w.config
+ cnf.apply(unit.Metric{}, options)
+ if prev.Title != cnf.Title {
+ w.config.Title = cnf.Title
+ w.document.Set("title", cnf.Title)
+ }
+ if prev.Mode != cnf.Mode {
+ w.windowMode(cnf.Mode)
+ }
+ if prev.NavigationColor != cnf.NavigationColor {
+ w.config.NavigationColor = cnf.NavigationColor
+ w.navigationColor(cnf.NavigationColor)
+ }
+ if prev.Orientation != cnf.Orientation {
+ w.config.Orientation = cnf.Orientation
+ w.orientation(cnf.Orientation)
+ }
+ if w.config != prev {
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
+}
+
+func (w *window) Raise() {}
+
+func (w *window) SetCursor(name pointer.CursorName) {
+ style := w.cnv.Get("style")
+ style.Set("cursor", string(name))
+}
+
+func (w *window) Wakeup() {
+ select {
+ case w.wakeups <- struct{}{}:
+ default:
+ }
+}
+
func (w *window) ShowTextInput(show bool) {
// Run in a goroutine to avoid a deadlock if the
// focus change result in an event.
@@ -403,49 +559,119 @@ func (w *window) ShowTextInput(show bool) {
}()
}
+func (w *window) SetInputHint(mode key.InputHint) {
+ w.keyboard(mode)
+}
+
// Close the window. Not implemented for js.
func (w *window) Close() {}
+// Maximize the window. Not implemented for js.
+func (w *window) Maximize() {}
+
+// Center the window. Not implemented for js.
+func (w *window) Center() {}
+
+func (w *window) resize() {
+ w.scale = float32(w.window.Get("devicePixelRatio").Float())
+
+ rect := w.cnv.Call("getBoundingClientRect")
+ size := image.Point{
+ X: int(float32(rect.Get("width").Float()) * w.scale),
+ Y: int(float32(rect.Get("height").Float()) * w.scale),
+ }
+ if size != w.config.Size {
+ w.config.Size = size
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
+
+ if vx, vy := w.visualViewport.Get("width"), w.visualViewport.Get("height"); !vx.IsUndefined() && !vy.IsUndefined() {
+ w.inset.X = float32(w.config.Size.X) - float32(vx.Float())*w.scale
+ w.inset.Y = float32(w.config.Size.Y) - float32(vy.Float())*w.scale
+ }
+
+ if w.config.Size.X == 0 || w.config.Size.Y == 0 {
+ return
+ }
+
+ w.cnv.Set("width", w.config.Size.X)
+ w.cnv.Set("height", w.config.Size.Y)
+}
+
func (w *window) draw(sync bool) {
- width, height, scale, cfg := w.config()
- if cfg == (unit.Metric{}) || width == 0 || height == 0 {
+ size, insets, metric := w.getConfig()
+ if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 {
return
}
- w.mu.Lock()
- w.scale = float32(scale)
- w.mu.Unlock()
- w.w.Event(FrameEvent{
+ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{
- Now: time.Now(),
- Size: image.Point{
- X: width,
- Y: height,
- },
- Metric: cfg,
+ Now: time.Now(),
+ Size: size,
+ Insets: insets,
+ Metric: metric,
},
Sync: sync,
})
}
-func (w *window) config() (int, int, float32, unit.Metric) {
- rect := w.cnv.Call("getBoundingClientRect")
- width, height := rect.Get("width").Float(), rect.Get("height").Float()
- scale := w.window.Get("devicePixelRatio").Float()
- width *= scale
- height *= scale
- iw, ih := int(width+.5), int(height+.5)
- // Adjust internal size of canvas if necessary.
- if cw, ch := w.cnv.Get("width").Int(), w.cnv.Get("height").Int(); iw != cw || ih != ch {
- w.cnv.Set("width", iw)
- w.cnv.Set("height", ih)
+func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) {
+ return image.Pt(w.config.Size.X, w.config.Size.Y), system.Insets{
+ Bottom: unit.Px(w.inset.Y),
+ Right: unit.Px(w.inset.X),
+ }, unit.Metric{
+ PxPerDp: w.scale,
+ PxPerSp: w.scale,
+ }
+}
+
+func (w *window) windowMode(mode WindowMode) {
+ switch mode {
+ case Windowed:
+ if !w.document.Get("fullscreenElement").Truthy() {
+ return // Browser is already Windowed.
+ }
+ if !w.document.Get("exitFullscreen").Truthy() {
+ return // Browser doesn't support such feature.
+ }
+ w.document.Call("exitFullscreen")
+ w.config.Mode = Windowed
+ case Fullscreen:
+ elem := w.document.Get("documentElement")
+ if !elem.Get("requestFullscreen").Truthy() {
+ return // Browser doesn't support such feature.
+ }
+ elem.Call("requestFullscreen")
+ w.config.Mode = Fullscreen
+ }
+}
+
+func (w *window) orientation(mode Orientation) {
+ if j := w.screenOrientation; !j.Truthy() || !j.Get("unlock").Truthy() || !j.Get("lock").Truthy() {
+ return // Browser don't support Screen Orientation API.
}
- return iw, ih, float32(scale), unit.Metric{
- PxPerDp: float32(scale),
- PxPerSp: float32(scale),
+
+ switch mode {
+ case AnyOrientation:
+ w.screenOrientation.Call("unlock")
+ case LandscapeOrientation:
+ w.screenOrientation.Call("lock", "landscape").Call("then", w.redraw)
+ case PortraitOrientation:
+ w.screenOrientation.Call("lock", "portrait").Call("then", w.redraw)
}
}
-func Main() {
+func (w *window) navigationColor(c color.NRGBA) {
+ theme := w.head.Call("querySelector", `meta[name="theme-color"]`)
+ if !theme.Truthy() {
+ theme = w.document.Call("createElement", "meta")
+ theme.Set("name", "theme-color")
+ w.head.Call("appendChild", theme)
+ }
+ rgba := f32color.NRGBAToRGBA(c)
+ theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B}))
+}
+
+func osMain() {
select {}
}
@@ -479,7 +705,7 @@ func translateKey(k string) (string, bool) {
case "Tab":
n = key.NameTab
case " ":
- n = "Space"
+ n = key.NameSpace
case "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12":
n = k
default:
@@ -492,3 +718,5 @@ func translateKey(k string) (string, bool) {
}
return n, true
}
+
+func (_ ViewEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/app/os_macos.go b/vendor/gioui.org/app/os_macos.go
new file mode 100644
index 0000000..270db6a
--- /dev/null
+++ b/vendor/gioui.org/app/os_macos.go
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build darwin && !ios
+// +build darwin,!ios
+
+package app
+
+import (
+ "errors"
+ "image"
+ "runtime"
+ "time"
+ "unicode"
+ "unicode/utf16"
+ "unsafe"
+
+ "gioui.org/f32"
+ "gioui.org/io/clipboard"
+ "gioui.org/io/key"
+ "gioui.org/io/pointer"
+ "gioui.org/io/system"
+ "gioui.org/unit"
+
+ _ "gioui.org/internal/cocoainit"
+)
+
+/*
+#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c
+
+#include <AppKit/AppKit.h>
+
+#define MOUSE_MOVE 1
+#define MOUSE_UP 2
+#define MOUSE_DOWN 3
+#define MOUSE_SCROLL 4
+
+__attribute__ ((visibility ("hidden"))) void gio_main(void);
+__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(void);
+__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
+
+static void writeClipboard(unichar *chars, NSUInteger length) {
+ @autoreleasepool {
+ NSString *s = [NSString string];
+ if (length > 0) {
+ s = [NSString stringWithCharacters:chars length:length];
+ }
+ NSPasteboard *p = NSPasteboard.generalPasteboard;
+ [p declareTypes:@[NSPasteboardTypeString] owner:nil];
+ [p setString:s forType:NSPasteboardTypeString];
+ }
+}
+
+static CFTypeRef readClipboard(void) {
+ @autoreleasepool {
+ NSPasteboard *p = NSPasteboard.generalPasteboard;
+ NSString *content = [p stringForType:NSPasteboardTypeString];
+ return (__bridge_retained CFTypeRef)content;
+ }
+}
+
+static CGFloat viewHeight(CFTypeRef viewRef) {
+ NSView *view = (__bridge NSView *)viewRef;
+ return [view bounds].size.height;
+}
+
+static CGFloat viewWidth(CFTypeRef viewRef) {
+ NSView *view = (__bridge NSView *)viewRef;
+ return [view bounds].size.width;
+}
+
+static CGFloat getScreenBackingScale(void) {
+ return [NSScreen.mainScreen backingScaleFactor];
+}
+
+static CGFloat getViewBackingScale(CFTypeRef viewRef) {
+ NSView *view = (__bridge NSView *)viewRef;
+ return [view.window backingScaleFactor];
+}
+
+static void setNeedsDisplay(CFTypeRef viewRef) {
+ NSView *view = (__bridge NSView *)viewRef;
+ [view setNeedsDisplay:YES];
+}
+
+static NSPoint cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) {
+ NSWindow *window = (__bridge NSWindow *)windowRef;
+ return [window cascadeTopLeftFromPoint:topLeft];
+}
+
+static void makeKeyAndOrderFront(CFTypeRef windowRef) {
+ NSWindow *window = (__bridge NSWindow *)windowRef;
+ [window makeKeyAndOrderFront:nil];
+}
+
+static void toggleFullScreen(CFTypeRef windowRef) {
+ NSWindow *window = (__bridge NSWindow *)windowRef;
+ [window toggleFullScreen:nil];
+}
+
+static NSWindowStyleMask getWindowStyleMask(CFTypeRef windowRef) {
+ NSWindow *window = (__bridge NSWindow *)windowRef;
+ return [window styleMask];
+}
+
+static void closeWindow(CFTypeRef windowRef) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ [window performClose:nil];
+}
+
+static void setSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ NSSize size = NSMakeSize(width, height);
+ [window setContentSize:size];
+}
+
+static void setMinSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ window.contentMinSize = NSMakeSize(width, height);
+}
+
+static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ window.contentMaxSize = NSMakeSize(width, height);
+}
+
+static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ NSRect r = NSMakeRect(x, y, w, h);
+ [window setFrame:r display:YES];
+}
+
+static NSRect getScreenFrame(CFTypeRef windowRef) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ return [[window screen] frame];
+}
+
+static void setTitle(CFTypeRef windowRef, const char *title) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ window.title = [NSString stringWithUTF8String: title];
+}
+
+static CFTypeRef layerForView(CFTypeRef viewRef) {
+ NSView *view = (__bridge NSView *)viewRef;
+ return (__bridge CFTypeRef)view.layer;
+}
+
+static void raiseWindow(CFTypeRef windowRef) {
+ NSWindow* window = (__bridge NSWindow *)windowRef;
+ [window makeKeyAndOrderFront:nil];
+}
+
+*/
+import "C"
+
+func init() {
+ // Darwin requires that UI operations happen on the main thread only.
+ runtime.LockOSThread()
+}
+
+// ViewEvent notified the client of changes to the window AppKit handles.
+// The handles are retained until another ViewEvent is sent.
+type ViewEvent struct {
+ // View is a CFTypeRef for the NSView for the window.
+ View uintptr
+ // Layer is a CFTypeRef of the CALayer of View.
+ Layer uintptr
+}
+
+type window struct {
+ view C.CFTypeRef
+ window C.CFTypeRef
+ w *callbacks
+ stage system.Stage
+ displayLink *displayLink
+ cursor pointer.CursorName
+
+ scale float32
+ config Config
+}
+
+// viewMap is the mapping from Cocoa NSViews to Go windows.
+var viewMap = make(map[C.CFTypeRef]*window)
+
+// launched is closed when applicationDidFinishLaunching is called.
+var launched = make(chan struct{})
+
+// nextTopLeft is the offset to use for the next window's call to
+// cascadeTopLeftFromPoint.
+var nextTopLeft C.NSPoint
+
+// mustView is like lookupView, except that it panics
+// if the view isn't mapped.
+func mustView(view C.CFTypeRef) *window {
+ w, ok := lookupView(view)
+ if !ok {
+ panic("no window for view")
+ }
+ return w
+}
+
+func lookupView(view C.CFTypeRef) (*window, bool) {
+ w, exists := viewMap[view]
+ if !exists {
+ return nil, false
+ }
+ return w, true
+}
+
+func deleteView(view C.CFTypeRef) {
+ delete(viewMap, view)
+}
+
+func insertView(view C.CFTypeRef, w *window) {
+ viewMap[view] = w
+}
+
+func (w *window) contextView() C.CFTypeRef {
+ return w.view
+}
+
+func (w *window) ReadClipboard() {
+ content := nsstringToString(C.readClipboard())
+ w.w.Event(clipboard.Event{Text: content})
+}
+
+func (w *window) WriteClipboard(s string) {
+ u16 := utf16.Encode([]rune(s))
+ var chars *C.unichar
+ if len(u16) > 0 {
+ chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
+ }
+ C.writeClipboard(chars, C.NSUInteger(len(u16)))
+}
+
+func (w *window) updateWindowMode() {
+ style := int(C.getWindowStyleMask(w.window))
+ if style&C.NSWindowStyleMaskFullScreen > 0 {
+ w.config.Mode = Fullscreen
+ } else {
+ w.config.Mode = Windowed
+ }
+}
+
+func (w *window) Configure(options []Option) {
+ screenScale := float32(C.getScreenBackingScale())
+ cfg := configFor(screenScale)
+ prev := w.config
+ w.updateWindowMode()
+ cnf := w.config
+ cnf.apply(cfg, options)
+ cnf.Size = cnf.Size.Div(int(screenScale))
+ cnf.MinSize = cnf.MinSize.Div(int(screenScale))
+ cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
+
+ if cnf.Mode != Fullscreen && prev.Size != cnf.Size {
+ w.config.Size = cnf.Size
+ C.setSize(w.window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
+ }
+ if prev.MinSize != cnf.MinSize {
+ w.config.MinSize = cnf.MinSize
+ C.setMinSize(w.window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
+ }
+ if prev.MaxSize != cnf.MaxSize {
+ w.config.MaxSize = cnf.MaxSize
+ C.setMaxSize(w.window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
+ }
+
+ if prev.Title != cnf.Title {
+ w.config.Title = cnf.Title
+ title := C.CString(cnf.Title)
+ defer C.free(unsafe.Pointer(title))
+ C.setTitle(w.window, title)
+ }
+ if prev.Mode != cnf.Mode {
+ switch cnf.Mode {
+ case Windowed, Fullscreen:
+ w.config.Mode = cnf.Mode
+ C.toggleFullScreen(w.window)
+ }
+ }
+ if w.config != prev {
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
+}
+
+func (w *window) SetCursor(name pointer.CursorName) {
+ w.cursor = windowSetCursor(w.cursor, name)
+}
+
+func (w *window) ShowTextInput(show bool) {}
+
+func (w *window) SetInputHint(_ key.InputHint) {}
+
+func (w *window) SetAnimating(anim bool) {
+ if anim {
+ w.displayLink.Start()
+ } else {
+ w.displayLink.Stop()
+ }
+}
+
+func (w *window) Raise() {
+ C.raiseWindow(w.window)
+}
+
+func (w *window) runOnMain(f func()) {
+ runOnMain(func() {
+ // Make sure the view is still valid. The window might've been closed
+ // during the switch to the main thread.
+ if w.view != 0 {
+ f()
+ }
+ })
+}
+
+func (w *window) Close() {
+ C.closeWindow(w.window)
+}
+
+// Maximize the window.
+func (w *window) Maximize() {
+ r := C.getScreenFrame(w.window) // the screen size of the window
+ C.setScreenFrame(w.window, C.CGFloat(0), C.CGFloat(0), r.size.width, r.size.height)
+}
+
+// Center the window.
+func (w *window) Center() {
+ r := C.getScreenFrame(w.window) // the screen size of the window
+
+ screenScale := float32(C.getScreenBackingScale())
+ sz := w.config.Size.Div(int(screenScale))
+ x := (int(r.size.width) - sz.X) / 2
+ y := (int(r.size.height) - sz.Y) / 2
+
+ C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y))
+}
+
+func (w *window) setStage(stage system.Stage) {
+ if stage == w.stage {
+ return
+ }
+ w.stage = stage
+ w.w.Event(system.StageEvent{Stage: stage})
+}
+
+//export gio_onKeys
+func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger, keyDown C.bool) {
+ str := C.GoString(cstr)
+ kmods := convertMods(mods)
+ ks := key.Release
+ if keyDown {
+ ks = key.Press
+ }
+ w := mustView(view)
+ for _, k := range str {
+ if n, ok := convertKey(k); ok {
+ w.w.Event(key.Event{
+ Name: n,
+ Modifiers: kmods,
+ State: ks,
+ })
+ }
+ }
+}
+
+//export gio_onText
+func gio_onText(view C.CFTypeRef, cstr *C.char) {
+ str := C.GoString(cstr)
+ w := mustView(view)
+ w.w.Event(key.EditEvent{Text: str})
+}
+
+//export gio_onMouse
+func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) {
+ var typ pointer.Type
+ switch cdir {
+ case C.MOUSE_MOVE:
+ typ = pointer.Move
+ case C.MOUSE_UP:
+ typ = pointer.Release
+ case C.MOUSE_DOWN:
+ typ = pointer.Press
+ case C.MOUSE_SCROLL:
+ typ = pointer.Scroll
+ default:
+ panic("invalid direction")
+ }
+ var btns pointer.Buttons
+ if cbtns&(1<<0) != 0 {
+ btns |= pointer.ButtonPrimary
+ }
+ if cbtns&(1<<1) != 0 {
+ btns |= pointer.ButtonSecondary
+ }
+ if cbtns&(1<<2) != 0 {
+ btns |= pointer.ButtonTertiary
+ }
+ t := time.Duration(float64(ti)*float64(time.Second) + .5)
+ w := mustView(view)
+ xf, yf := float32(x)*w.scale, float32(y)*w.scale
+ dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale
+ w.w.Event(pointer.Event{
+ Type: typ,
+ Source: pointer.Mouse,
+ Time: t,
+ Buttons: btns,
+ Position: f32.Point{X: xf, Y: yf},
+ Scroll: f32.Point{X: dxf, Y: dyf},
+ Modifiers: convertMods(mods),
+ })
+}
+
+//export gio_onDraw
+func gio_onDraw(view C.CFTypeRef) {
+ w := mustView(view)
+ w.draw()
+}
+
+//export gio_onFocus
+func gio_onFocus(view C.CFTypeRef, focus C.int) {
+ w := mustView(view)
+ w.w.Event(key.FocusEvent{Focus: focus == 1})
+ w.SetCursor(w.cursor)
+}
+
+//export gio_onChangeScreen
+func gio_onChangeScreen(view C.CFTypeRef, did uint64) {
+ w := mustView(view)
+ w.displayLink.SetDisplayID(did)
+}
+
+func (w *window) draw() {
+ w.scale = float32(C.getViewBackingScale(w.view))
+ wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view))
+ sz := image.Point{
+ X: int(wf*w.scale + .5),
+ Y: int(hf*w.scale + .5),
+ }
+ if sz != w.config.Size {
+ w.config.Size = sz
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
+ if sz.X == 0 || sz.Y == 0 {
+ return
+ }
+ cfg := configFor(w.scale)
+ w.setStage(system.StageRunning)
+ w.w.Event(frameEvent{
+ FrameEvent: system.FrameEvent{
+ Now: time.Now(),
+ Size: w.config.Size,
+ Metric: cfg,
+ },
+ Sync: true,
+ })
+}
+
+func configFor(scale float32) unit.Metric {
+ return unit.Metric{
+ PxPerDp: scale,
+ PxPerSp: scale,
+ }
+}
+
+//export gio_onClose
+func gio_onClose(view C.CFTypeRef) {
+ w := mustView(view)
+ w.w.Event(ViewEvent{})
+ deleteView(view)
+ w.w.Event(system.DestroyEvent{})
+ w.displayLink.Close()
+ C.CFRelease(w.view)
+ C.CFRelease(w.window)
+ w.view = 0
+ w.window = 0
+ w.displayLink = nil
+}
+
+//export gio_onHide
+func gio_onHide(view C.CFTypeRef) {
+ w := mustView(view)
+ w.setStage(system.StagePaused)
+}
+
+//export gio_onShow
+func gio_onShow(view C.CFTypeRef) {
+ w := mustView(view)
+ w.setStage(system.StageRunning)
+}
+
+//export gio_onFullscreen
+func gio_onFullscreen(view C.CFTypeRef) {
+ w := mustView(view)
+ w.config.Mode = Fullscreen
+ w.w.Event(ConfigEvent{Config: w.config})
+}
+
+//export gio_onWindowed
+func gio_onWindowed(view C.CFTypeRef) {
+ w := mustView(view)
+ w.config.Mode = Windowed
+ w.w.Event(ConfigEvent{Config: w.config})
+}
+
+//export gio_onAppHide
+func gio_onAppHide() {
+ for _, w := range viewMap {
+ w.setStage(system.StagePaused)
+ }
+}
+
+//export gio_onAppShow
+func gio_onAppShow() {
+ for _, w := range viewMap {
+ w.setStage(system.StageRunning)
+ }
+}
+
+//export gio_onFinishLaunching
+func gio_onFinishLaunching() {
+ close(launched)
+}
+
+func newWindow(win *callbacks, options []Option) error {
+ <-launched
+ errch := make(chan error)
+ runOnMain(func() {
+ w, err := newOSWindow()
+ if err != nil {
+ errch <- err
+ return
+ }
+ errch <- nil
+ w.w = win
+ w.window = C.gio_createWindow(w.view, nil, 0, 0, 0, 0, 0, 0)
+ win.SetDriver(w)
+ w.Configure(options)
+ if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
+ // cascadeTopLeftFromPoint treats (0, 0) as a no-op,
+ // and just returns the offset we need for the first window.
+ nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft)
+ }
+ nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft)
+ C.makeKeyAndOrderFront(w.window)
+ layer := C.layerForView(w.view)
+ w.w.Event(ViewEvent{View: uintptr(w.view), Layer: uintptr(layer)})
+ })
+ return <-errch
+}
+
+func newOSWindow() (*window, error) {
+ view := C.gio_createView()
+ if view == 0 {
+ return nil, errors.New("CreateWindow: failed to create view")
+ }
+ scale := float32(C.getViewBackingScale(view))
+ w := &window{
+ view: view,
+ scale: scale,
+ }
+ dl, err := NewDisplayLink(func() {
+ w.runOnMain(func() {
+ C.setNeedsDisplay(w.view)
+ })
+ })
+ w.displayLink = dl
+ if err != nil {
+ C.CFRelease(view)
+ return nil, err
+ }
+ insertView(view, w)
+ return w, nil
+}
+
+func osMain() {
+ C.gio_main()
+}
+
+func convertKey(k rune) (string, bool) {
+ var n string
+ switch k {
+ case 0x1b:
+ n = key.NameEscape
+ case C.NSLeftArrowFunctionKey:
+ n = key.NameLeftArrow
+ case C.NSRightArrowFunctionKey:
+ n = key.NameRightArrow
+ case C.NSUpArrowFunctionKey:
+ n = key.NameUpArrow
+ case C.NSDownArrowFunctionKey:
+ n = key.NameDownArrow
+ case 0xd:
+ n = key.NameReturn
+ case 0x3:
+ n = key.NameEnter
+ case C.NSHomeFunctionKey:
+ n = key.NameHome
+ case C.NSEndFunctionKey:
+ n = key.NameEnd
+ case 0x7f:
+ n = key.NameDeleteBackward
+ case C.NSDeleteFunctionKey:
+ n = key.NameDeleteForward
+ case C.NSPageUpFunctionKey:
+ n = key.NamePageUp
+ case C.NSPageDownFunctionKey:
+ n = key.NamePageDown
+ case C.NSF1FunctionKey:
+ n = "F1"
+ case C.NSF2FunctionKey:
+ n = "F2"
+ case C.NSF3FunctionKey:
+ n = "F3"
+ case C.NSF4FunctionKey:
+ n = "F4"
+ case C.NSF5FunctionKey:
+ n = "F5"
+ case C.NSF6FunctionKey:
+ n = "F6"
+ case C.NSF7FunctionKey:
+ n = "F7"
+ case C.NSF8FunctionKey:
+ n = "F8"
+ case C.NSF9FunctionKey:
+ n = "F9"
+ case C.NSF10FunctionKey:
+ n = "F10"
+ case C.NSF11FunctionKey:
+ n = "F11"
+ case C.NSF12FunctionKey:
+ n = "F12"
+ case 0x09, 0x19:
+ n = key.NameTab
+ case 0x20:
+ n = key.NameSpace
+ default:
+ k = unicode.ToUpper(k)
+ if !unicode.IsPrint(k) {
+ return "", false
+ }
+ n = string(k)
+ }
+ return n, true
+}
+
+func convertMods(mods C.NSUInteger) key.Modifiers {
+ var kmods key.Modifiers
+ if mods&C.NSAlternateKeyMask != 0 {
+ kmods |= key.ModAlt
+ }
+ if mods&C.NSControlKeyMask != 0 {
+ kmods |= key.ModCtrl
+ }
+ if mods&C.NSCommandKeyMask != 0 {
+ kmods |= key.ModCommand
+ }
+ if mods&C.NSShiftKeyMask != 0 {
+ kmods |= key.ModShift
+ }
+ return kmods
+}
+
+func (_ ViewEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/app/internal/window/os_macos.m b/vendor/gioui.org/app/os_macos.m
index b8c0dee..f65099b 100644
--- a/vendor/gioui.org/app/internal/window/os_macos.m
+++ b/vendor/gioui.org/app/os_macos.m
@@ -6,6 +6,8 @@
#include "_cgo_export.h"
+__attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void);
+
@interface GioAppDelegate : NSObject<NSApplicationDelegate>
@end
@@ -21,6 +23,14 @@
NSWindow *window = (NSWindow *)[notification object];
gio_onShow((__bridge CFTypeRef)window.contentView);
}
+- (void)windowWillEnterFullScreen:(NSNotification *)notification {
+ NSWindow *window = (NSWindow *)[notification object];
+ gio_onFullscreen((__bridge CFTypeRef)window.contentView);
+}
+- (void)windowWillExitFullScreen:(NSNotification *)notification {
+ NSWindow *window = (NSWindow *)[notification object];
+ gio_onWindowed((__bridge CFTypeRef)window.contentView);
+}
- (void)windowDidChangeScreen:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object];
CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue];
@@ -29,11 +39,11 @@
}
- (void)windowDidBecomeKey:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object];
- gio_onFocus((__bridge CFTypeRef)window.contentView, YES);
+ gio_onFocus((__bridge CFTypeRef)window.contentView, 1);
}
- (void)windowDidResignKey:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object];
- gio_onFocus((__bridge CFTypeRef)window.contentView, NO);
+ gio_onFocus((__bridge CFTypeRef)window.contentView, 0);
}
- (void)windowWillClose:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object];
@@ -42,49 +52,92 @@
}
@end
-// Delegates are weakly referenced from their peers. Nothing
-// else holds a strong reference to our window delegate, so
-// keep a single global reference instead.
-static GioWindowDelegate *globalWindowDel;
-
-void gio_writeClipboard(unichar *chars, NSUInteger length) {
- @autoreleasepool {
- NSString *s = [NSString string];
- if (length > 0) {
- s = [NSString stringWithCharacters:chars length:length];
- }
- NSPasteboard *p = NSPasteboard.generalPasteboard;
- [p declareTypes:@[NSPasteboardTypeString] owner:nil];
- [p setString:s forType:NSPasteboardTypeString];
+static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) {
+ NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil];
+ if (!event.hasPreciseScrollingDeltas) {
+ // dx and dy are in rows and columns.
+ dx *= 10;
+ dy *= 10;
}
+ // Origin is in the lower left corner. Convert to upper left.
+ CGFloat height = view.bounds.size.height;
+ gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]);
}
-CFTypeRef gio_readClipboard(void) {
- @autoreleasepool {
- NSPasteboard *p = NSPasteboard.generalPasteboard;
- NSString *content = [p stringForType:NSPasteboardTypeString];
- return (__bridge_retained CFTypeRef)content;
- }
-}
+@interface GioView : NSView <CALayerDelegate>
+@end
-CGFloat gio_viewHeight(CFTypeRef viewRef) {
- NSView *view = (__bridge NSView *)viewRef;
- return [view bounds].size.height;
+@implementation GioView
+- (void)setFrameSize:(NSSize)newSize {
+ [super setFrameSize:newSize];
+ [self setNeedsDisplay:YES];
}
-
-CGFloat gio_viewWidth(CFTypeRef viewRef) {
- NSView *view = (__bridge NSView *)viewRef;
- return [view bounds].size.width;
+// drawRect is called when OpenGL is used, displayLayer otherwise.
+// Don't know why.
+- (void)drawRect:(NSRect)r {
+ gio_onDraw((__bridge CFTypeRef)self);
}
-
-CGFloat gio_getScreenBackingScale(void) {
- return [NSScreen.mainScreen backingScaleFactor];
+- (void)displayLayer:(CALayer *)layer {
+ layer.contentsScale = self.window.backingScaleFactor;
+ gio_onDraw((__bridge CFTypeRef)self);
}
-
-CGFloat gio_getViewBackingScale(CFTypeRef viewRef) {
- NSView *view = (__bridge NSView *)viewRef;
- return [view.window backingScaleFactor];
+- (CALayer *)makeBackingLayer {
+ CALayer *layer = gio_layerFactory();
+ layer.delegate = self;
+ return layer;
+}
+- (void)mouseDown:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_DOWN, 0, 0);
+}
+- (void)mouseUp:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_UP, 0, 0);
+}
+- (void)middleMouseDown:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_DOWN, 0, 0);
+}
+- (void)middletMouseUp:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_UP, 0, 0);
+}
+- (void)rightMouseDown:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_DOWN, 0, 0);
+}
+- (void)rightMouseUp:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_UP, 0, 0);
}
+- (void)mouseMoved:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_MOVE, 0, 0);
+}
+- (void)mouseDragged:(NSEvent *)event {
+ handleMouse(self, event, MOUSE_MOVE, 0, 0);
+}
+- (void)scrollWheel:(NSEvent *)event {
+ CGFloat dx = -event.scrollingDeltaX;
+ CGFloat dy = -event.scrollingDeltaY;
+ handleMouse(self, event, MOUSE_SCROLL, dx, dy);
+}
+- (void)keyDown:(NSEvent *)event {
+ NSString *keys = [event charactersIgnoringModifiers];
+ gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], true);
+ [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+}
+- (void)keyUp:(NSEvent *)event {
+ NSString *keys = [event charactersIgnoringModifiers];
+ gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], false);
+}
+- (void)insertText:(id)string {
+ const char *utf8 = [string UTF8String];
+ gio_onText((__bridge CFTypeRef)self, (char *)utf8);
+}
+- (void)doCommandBySelector:(SEL)sel {
+ // Don't pass commands up the responder chain.
+ // They will end up in a beep.
+}
+@end
+
+// Delegates are weakly referenced from their peers. Nothing
+// else holds a strong reference to our window delegate, so
+// keep a single global reference instead.
+static GioWindowDelegate *globalWindowDel;
static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
gio_onFrameCallback(dl);
@@ -114,14 +167,47 @@ void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
CVDisplayLinkSetCurrentCGDisplay((CVDisplayLinkRef)dl, (CGDirectDisplayID)did);
}
-NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) {
- NSWindow *window = (__bridge NSWindow *)windowRef;
- return [window cascadeTopLeftFromPoint:topLeft];
+void gio_hideCursor() {
+ @autoreleasepool {
+ [NSCursor hide];
+ }
+}
+
+void gio_showCursor() {
+ @autoreleasepool {
+ [NSCursor unhide];
+ }
}
-void gio_makeKeyAndOrderFront(CFTypeRef windowRef) {
- NSWindow *window = (__bridge NSWindow *)windowRef;
- [window makeKeyAndOrderFront:nil];
+void gio_setCursor(NSUInteger curID) {
+ @autoreleasepool {
+ switch (curID) {
+ case 1:
+ [NSCursor.arrowCursor set];
+ break;
+ case 2:
+ [NSCursor.IBeamCursor set];
+ break;
+ case 3:
+ [NSCursor.pointingHandCursor set];
+ break;
+ case 4:
+ [NSCursor.crosshairCursor set];
+ break;
+ case 5:
+ [NSCursor.resizeLeftRightCursor set];
+ break;
+ case 6:
+ [NSCursor.resizeUpDownCursor set];
+ break;
+ case 7:
+ [NSCursor.openHandCursor set];
+ break;
+ default:
+ [NSCursor.arrowCursor set];
+ break;
+ }
+ }
}
CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) {
@@ -143,7 +229,9 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width,
window.contentMaxSize = NSMakeSize(maxWidth, maxHeight);
}
[window setAcceptsMouseMovedEvents:YES];
- window.title = [NSString stringWithUTF8String: title];
+ if (title != nil) {
+ window.title = [NSString stringWithUTF8String: title];
+ }
NSView *view = (__bridge NSView *)viewRef;
[window setContentView:view];
[window makeFirstResponder:view];
@@ -153,14 +241,20 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width,
}
}
-void gio_close(CFTypeRef windowRef) {
- NSWindow* window = (__bridge NSWindow *)windowRef;
- [window performClose:nil];
+CFTypeRef gio_createView(void) {
+ @autoreleasepool {
+ NSRect frame = NSMakeRect(0, 0, 0, 0);
+ GioView* view = [[GioView alloc] initWithFrame:frame];
+ view.wantsLayer = YES;
+ view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
+ return CFBridgingRetain(view);
+ }
}
@implementation GioAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
- [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+ [NSApp activateIgnoringOtherApps:YES];
gio_onFinishLaunching();
}
- (void)applicationDidHide:(NSNotification *)aNotification {
@@ -176,7 +270,6 @@ void gio_main() {
[NSApplication sharedApplication];
GioAppDelegate *del = [[GioAppDelegate alloc] init];
[NSApp setDelegate:del];
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NSMenuItem *mainMenu = [NSMenuItem new];
diff --git a/vendor/gioui.org/app/os_unix.go b/vendor/gioui.org/app/os_unix.go
new file mode 100644
index 0000000..ee831b3
--- /dev/null
+++ b/vendor/gioui.org/app/os_unix.go
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build (linux && !android) || freebsd || openbsd
+// +build linux,!android freebsd openbsd
+
+package app
+
+import (
+ "errors"
+ "unsafe"
+)
+
+type ViewEvent struct {
+ // Display is a pointer to the X11 Display created by XOpenDisplay.
+ Display unsafe.Pointer
+ // Window is the X11 window ID as returned by XCreateWindow.
+ Window uintptr
+}
+
+func osMain() {
+ select {}
+}
+
+type windowDriver func(*callbacks, []Option) error
+
+// Instead of creating files with build tags for each combination of wayland +/- x11
+// let each driver initialize these variables with their own version of createWindow.
+var wlDriver, x11Driver windowDriver
+
+func newWindow(window *callbacks, options []Option) error {
+ var errFirst error
+ for _, d := range []windowDriver{x11Driver, wlDriver} {
+ if d == nil {
+ continue
+ }
+ err := d(window, options)
+ if err == nil {
+ return nil
+ }
+ if errFirst == nil {
+ errFirst = err
+ }
+ }
+ if errFirst != nil {
+ return errFirst
+ }
+ return errors.New("app: no window driver available")
+}
+
+func (_ ViewEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/app/internal/window/os_wayland.c b/vendor/gioui.org/app/os_wayland.c
index 5c1c075..b137e1f 100644
--- a/vendor/gioui.org/app/internal/window/os_wayland.c
+++ b/vendor/gioui.org/app/os_wayland.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Unlicense OR MIT
-// +build linux,!android,!nowayland freebsd
+//go:build ((linux && !android) || freebsd) && !nowayland
+// +build linux,!android freebsd
+// +build !nowayland
#include <wayland-client.h>
#include "wayland_xdg_shell.h"
diff --git a/vendor/gioui.org/app/internal/window/os_wayland.go b/vendor/gioui.org/app/os_wayland.go
index 4a456ce..ac7aa17 100644
--- a/vendor/gioui.org/app/internal/window/os_wayland.go
+++ b/vendor/gioui.org/app/os_wayland.go
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: Unlicense OR MIT
-// +build linux,!android,!nowayland freebsd
+//go:build ((linux && !android) || freebsd) && !nowayland
+// +build linux,!android freebsd
+// +build !nowayland
-package window
+package app
import (
"bytes"
@@ -19,14 +21,16 @@ import (
"time"
"unsafe"
+ syscall "golang.org/x/sys/unix"
+
"gioui.org/app/internal/xkb"
"gioui.org/f32"
"gioui.org/internal/fling"
+ "gioui.org/io/clipboard"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/system"
"gioui.org/unit"
- syscall "golang.org/x/sys/unix"
)
// Use wayland-scanner to generate glue code for the xdg-shell and xdg-decoration extensions.
@@ -39,9 +43,9 @@ import (
//go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.h
//go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.c
-//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_shell.c
-//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_decoration.c
-//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_text_input.c
+//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_shell.c
+//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_decoration.c
+//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_text_input.c
/*
#cgo linux pkg-config: wayland-client wayland-cursor
@@ -133,7 +137,7 @@ type repeatState struct {
delay time.Duration
key uint32
- win Callbacks
+ win *callbacks
stopC chan struct{}
start time.Duration
@@ -143,7 +147,7 @@ type repeatState struct {
}
type window struct {
- w Callbacks
+ w *callbacks
disp *wlDisplay
surf *C.struct_wl_surface
wmSurf *C.struct_xdg_surface
@@ -177,19 +181,17 @@ type window struct {
dead bool
lastFrameCallback *C.struct_wl_callback
- mu sync.Mutex
animating bool
needAck bool
// The most recent configure serial waiting to be ack'ed.
serial C.uint32_t
- width int
- height int
newScale bool
scale int
- // readClipboard tracks whether a ClipboardEvent is requested.
- readClipboard bool
- // writeClipboard is set whenever a clipboard write is requested.
- writeClipboard *string
+ // size is the unscaled window size (unlike config.Size which is scaled).
+ size image.Point
+ config Config
+
+ wakeups chan struct{}
}
type poller struct {
@@ -218,25 +220,34 @@ var callbackMap sync.Map
// order of preference.
var clipboardMimeTypes = []string{"text/plain;charset=utf8", "UTF8_STRING", "text/plain", "TEXT", "STRING"}
+var (
+ newWaylandEGLContext func(w *window) (context, error)
+ newWaylandVulkanContext func(w *window) (context, error)
+)
+
func init() {
wlDriver = newWLWindow
}
-func newWLWindow(window Callbacks, opts *Options) error {
+func newWLWindow(callbacks *callbacks, options []Option) error {
d, err := newWLDisplay()
if err != nil {
return err
}
- w, err := d.createNativeWindow(opts)
+ w, err := d.createNativeWindow(options)
if err != nil {
d.destroy()
return err
}
- w.w = window
+ w.w = callbacks
go func() {
defer d.destroy()
defer w.destroy()
+
w.w.SetDriver(w)
+ // Finish and commit setup from createNativeWindow.
+ w.Configure(options)
+ C.wl_surface_commit(w.surf)
if err := w.loop(); err != nil {
panic(err)
}
@@ -280,13 +291,16 @@ func (d *wlDisplay) readClipboard() (io.ReadCloser, error) {
if err != nil {
return nil, err
}
+ // wl_data_offer_receive performs and implicit dup(2) of the write end
+ // of the pipe. Close our version.
+ defer w.Close()
cmimeType := C.CString(s.mimeType)
defer C.free(unsafe.Pointer(cmimeType))
C.wl_data_offer_receive(s.clipboard, cmimeType, C.int(w.Fd()))
return r, nil
}
-func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) {
+func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) {
if d.compositor == nil {
return nil, errors.New("wayland: no compositor available")
}
@@ -313,6 +327,7 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) {
newScale: scale != 1,
ppdp: ppdp,
ppsp: ppdp,
+ wakeups: make(chan struct{}, 1),
}
w.surf = C.wl_compositor_create_surface(d.compositor)
if w.surf == nil {
@@ -351,20 +366,13 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) {
C.wl_surface_add_listener(w.surf, &C.gio_surface_listener, unsafe.Pointer(w.surf))
C.xdg_surface_add_listener(w.wmSurf, &C.gio_xdg_surface_listener, unsafe.Pointer(w.surf))
C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf))
- title := C.CString(opts.Title)
- C.xdg_toplevel_set_title(w.topLvl, title)
- C.free(unsafe.Pointer(title))
- _, _, cfg := w.config()
- w.width = cfg.Px(opts.Width)
- w.height = cfg.Px(opts.Height)
if d.decor != nil {
// Request server side decorations.
w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl)
C.zxdg_toplevel_decoration_v1_set_mode(w.decor, C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE)
}
w.updateOpaqueRegion()
- C.wl_surface_commit(w.surf)
return w, nil
}
@@ -471,10 +479,8 @@ func gio_onSeatName(data unsafe.Pointer, seat *C.struct_wl_seat, name *C.char) {
//export gio_onXdgSurfaceConfigure
func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) {
w := callbackLoad(data).(*window)
- w.mu.Lock()
w.serial = serial
w.needAck = true
- w.mu.Unlock()
w.setStage(system.StageRunning)
w.draw(true)
}
@@ -489,10 +495,7 @@ func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) {
func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel, width, height C.int32_t, states *C.struct_wl_array) {
w := callbackLoad(data).(*window)
if width != 0 && height != 0 {
- w.mu.Lock()
- defer w.mu.Unlock()
- w.width = int(width)
- w.height = int(height)
+ w.size = image.Pt(int(width), int(height))
w.updateOpaqueRegion()
}
}
@@ -584,10 +587,11 @@ func gio_onRegistryGlobal(data unsafe.Pointer, reg *C.struct_wl_registry, name C
break
}
d.seat = &wlSeat{
- disp: d,
- name: name,
- seat: s,
- offers: make(map[*C.struct_wl_data_offer][]string),
+ disp: d,
+ name: name,
+ seat: s,
+ offers: make(map[*C.struct_wl_data_offer][]string),
+ touchFoci: make(map[C.int32_t]*window),
}
callbackStore(unsafe.Pointer(s), d.seat)
C.wl_seat_add_listener(s, &C.gio_seat_listener, unsafe.Pointer(s))
@@ -763,16 +767,7 @@ func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, seria
s.serial = serial
w := callbackLoad(unsafe.Pointer(surf)).(*window)
s.pointerFocus = w
- // Get images[0].
- img := *w.cursor.cursor.images
- buf := C.wl_cursor_image_get_buffer(img)
- if buf == nil {
- return
- }
- C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y))
- C.wl_surface_attach(w.cursor.surf, buf, 0, 0)
- C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height))
- C.wl_surface_commit(w.cursor.surf)
+ w.setCursor(pointer, serial)
w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)}
}
@@ -804,11 +799,11 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
var btn pointer.Buttons
switch wbtn {
case BTN_LEFT:
- btn = pointer.ButtonLeft
+ btn = pointer.ButtonPrimary
case BTN_RIGHT:
- btn = pointer.ButtonRight
+ btn = pointer.ButtonSecondary
case BTN_MIDDLE:
- btn = pointer.ButtonMiddle
+ btn = pointer.ButtonTertiary
default:
return
}
@@ -867,15 +862,13 @@ func (w *window) flushFling() {
w.fling.xExtrapolation = fling.Extrapolation{}
w.fling.yExtrapolation = fling.Extrapolation{}
vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity)))
- _, _, c := w.config()
+ _, c := w.getConfig()
if !w.fling.anim.Start(c, time.Now(), vel) {
return
}
invDist := 1 / vel
w.fling.dir.X = estx.Velocity * invDist
w.fling.dir.Y = esty.Velocity * invDist
- // Wake up the window loop.
- w.disp.wakeup()
}
//export gio_onPointerAxisSource
@@ -903,17 +896,94 @@ func gio_onPointerAxisDiscrete(data unsafe.Pointer, p *C.struct_wl_pointer, axis
}
func (w *window) ReadClipboard() {
- w.mu.Lock()
- w.readClipboard = true
- w.mu.Unlock()
- w.disp.wakeup()
+ r, err := w.disp.readClipboard()
+ // Send empty responses on unavailable clipboards or errors.
+ if r == nil || err != nil {
+ w.w.Event(clipboard.Event{})
+ return
+ }
+ // Don't let slow clipboard transfers block event loop.
+ go func() {
+ defer r.Close()
+ data, _ := ioutil.ReadAll(r)
+ w.w.Event(clipboard.Event{Text: string(data)})
+ }()
}
func (w *window) WriteClipboard(s string) {
- w.mu.Lock()
- w.writeClipboard = &s
- w.mu.Unlock()
- w.disp.wakeup()
+ w.disp.writeClipboard([]byte(s))
+}
+
+func (w *window) Configure(options []Option) {
+ _, cfg := w.getConfig()
+ prev := w.config
+ cnf := w.config
+ cnf.apply(cfg, options)
+ if prev.Size != cnf.Size {
+ w.size = image.Pt(cnf.Size.X/w.scale, cnf.Size.Y/w.scale)
+ w.config.Size = cnf.Size
+ }
+ if prev.Title != cnf.Title {
+ w.config.Title = cnf.Title
+ title := C.CString(cnf.Title)
+ C.xdg_toplevel_set_title(w.topLvl, title)
+ C.free(unsafe.Pointer(title))
+ }
+ if w.config != prev {
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
+}
+
+func (w *window) Raise() {}
+
+func (w *window) SetCursor(name pointer.CursorName) {
+ ptr := w.disp.seat.pointer
+ if ptr == nil {
+ return
+ }
+ if name == pointer.CursorNone {
+ C.wl_pointer_set_cursor(ptr, w.serial, nil, 0, 0)
+ return
+ }
+ switch name {
+ default:
+ fallthrough
+ case pointer.CursorDefault:
+ name = "left_ptr"
+ case pointer.CursorText:
+ name = "xterm"
+ case pointer.CursorPointer:
+ name = "hand1"
+ case pointer.CursorCrossHair:
+ name = "crosshair"
+ case pointer.CursorRowResize:
+ name = "top_side"
+ case pointer.CursorColResize:
+ name = "left_side"
+ case pointer.CursorGrab:
+ name = "hand1"
+ }
+ cname := C.CString(string(name))
+ defer C.free(unsafe.Pointer(cname))
+ c := C.wl_cursor_theme_get_cursor(w.cursor.theme, cname)
+ if c == nil {
+ return
+ }
+ w.cursor.cursor = c
+ w.setCursor(ptr, w.serial)
+}
+
+func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) {
+ // Get images[0].
+ img := *w.cursor.cursor.images
+ buf := C.wl_cursor_image_get_buffer(img)
+ if buf == nil {
+ return
+ }
+ C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y))
+ C.wl_surface_attach(w.cursor.surf, buf, 0, 0)
+ C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height))
+ C.wl_surface_commit(w.cursor.surf)
}
func (w *window) resetFling() {
@@ -963,13 +1033,14 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri
t := time.Duration(timestamp) * time.Millisecond
s.disp.repeat.Stop(t)
w.resetFling()
- if state != C.WL_KEYBOARD_KEY_STATE_PRESSED {
- return
- }
kc := mapXKBKeycode(uint32(keyCode))
- for _, e := range w.disp.xkb.DispatchKey(kc) {
+ ks := mapXKBKeyState(uint32(state))
+ for _, e := range w.disp.xkb.DispatchKey(kc, ks) {
w.w.Event(e)
}
+ if state != C.WL_KEYBOARD_KEY_STATE_PRESSED {
+ return
+ }
if w.disp.xkb.IsRepeatKey(kc) {
w.disp.repeat.Start(w, kc, t)
}
@@ -980,6 +1051,15 @@ func mapXKBKeycode(keyCode uint32) uint32 {
return keyCode + 8
}
+func mapXKBKeyState(state uint32) key.State {
+ switch state {
+ case C.WL_KEYBOARD_KEY_STATE_RELEASED:
+ return key.Release
+ default:
+ return key.Press
+ }
+}
+
func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) {
if r.rate <= 0 {
return
@@ -1045,7 +1125,7 @@ func (r *repeatState) Repeat(d *wlDisplay) {
if r.last+delay > now {
break
}
- for _, e := range d.xkb.DispatchKey(r.key) {
+ for _, e := range d.xkb.DispatchKey(r.key, key.Press) {
r.win.Event(e)
}
r.last += delay
@@ -1068,43 +1148,21 @@ func (w *window) loop() error {
if err := w.disp.dispatch(&p); err != nil {
return err
}
+ select {
+ case <-w.wakeups:
+ w.w.Event(wakeupEvent{})
+ default:
+ }
if w.dead {
w.w.Event(system.DestroyEvent{})
break
}
- w.process()
+ // pass false to skip unnecessary drawing.
+ w.draw(false)
}
return nil
}
-func (w *window) process() {
- w.mu.Lock()
- readClipboard := w.readClipboard
- writeClipboard := w.writeClipboard
- w.readClipboard = false
- w.writeClipboard = nil
- w.mu.Unlock()
- if readClipboard {
- r, err := w.disp.readClipboard()
- // Send empty responses on unavailable clipboards or errors.
- if r == nil || err != nil {
- w.w.Event(system.ClipboardEvent{})
- return
- }
- // Don't let slow clipboard transfers block event loop.
- go func() {
- defer r.Close()
- data, _ := ioutil.ReadAll(r)
- w.w.Event(system.ClipboardEvent{Text: string(data)})
- }()
- }
- if writeClipboard != nil {
- w.disp.writeClipboard([]byte(*writeClipboard))
- }
- // pass false to skip unnecessary drawing.
- w.draw(false)
-}
-
func (d *wlDisplay) dispatch(p *poller) error {
dispfd := C.wl_display_get_fd(d.disp)
// Poll for events and notifications.
@@ -1147,11 +1205,16 @@ func (d *wlDisplay) dispatch(p *poller) error {
return nil
}
+func (w *window) Wakeup() {
+ select {
+ case w.wakeups <- struct{}{}:
+ default:
+ }
+ w.disp.wakeup()
+}
+
func (w *window) SetAnimating(anim bool) {
- w.mu.Lock()
w.animating = anim
- w.mu.Unlock()
- w.disp.wakeup()
}
// Wakeup wakes up the event loop through the notification pipe.
@@ -1322,7 +1385,7 @@ func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
func (w *window) updateOpaqueRegion() {
reg := C.wl_compositor_create_region(w.disp.compositor)
- C.wl_region_add(reg, 0, 0, C.int32_t(w.width), C.int32_t(w.height))
+ C.wl_region_add(reg, 0, 0, C.int32_t(w.size.X), C.int32_t(w.size.Y))
C.wl_surface_set_opaque_region(w.surf, reg)
C.wl_region_destroy(reg)
}
@@ -1340,12 +1403,10 @@ func (w *window) updateOutputs() {
}
}
}
- w.mu.Lock()
if found && scale != w.scale {
w.scale = scale
w.newScale = true
}
- w.mu.Unlock()
if !found {
w.setStage(system.StagePaused)
} else {
@@ -1354,9 +1415,9 @@ func (w *window) updateOutputs() {
}
}
-func (w *window) config() (int, int, unit.Metric) {
- width, height := w.width*w.scale, w.height*w.scale
- return width, height, unit.Metric{
+func (w *window) getConfig() (image.Point, unit.Metric) {
+ size := w.size.Mul(w.scale)
+ return size, unit.Metric{
PxPerDp: w.ppdp * float32(w.scale),
PxPerSp: w.ppsp * float32(w.scale),
}
@@ -1364,14 +1425,16 @@ func (w *window) config() (int, int, unit.Metric) {
func (w *window) draw(sync bool) {
w.flushScroll()
- w.mu.Lock()
anim := w.animating || w.fling.anim.Active()
dead := w.dead
- w.mu.Unlock()
if dead || (!anim && !sync) {
return
}
- width, height, cfg := w.config()
+ size, cfg := w.getConfig()
+ if size != w.config.Size {
+ w.config.Size = size
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
if cfg == (unit.Metric{}) {
return
}
@@ -1380,13 +1443,10 @@ func (w *window) draw(sync bool) {
// Use the surface as listener data for gio_onFrameDone.
C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf))
}
- w.w.Event(FrameEvent{
+ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{
- Now: time.Now(),
- Size: image.Point{
- X: width,
- Y: height,
- },
+ Now: time.Now(),
+ Size: w.config.Size,
Metric: cfg,
},
Sync: sync,
@@ -1398,7 +1458,7 @@ func (w *window) setStage(s system.Stage) {
return
}
w.stage = s
- w.w.Event(system.StageEvent{s})
+ w.w.Event(system.StageEvent{Stage: s})
}
func (w *window) display() *C.struct_wl_display {
@@ -1410,18 +1470,50 @@ func (w *window) surface() (*C.struct_wl_surface, int, int) {
C.xdg_surface_ack_configure(w.wmSurf, w.serial)
w.needAck = false
}
- width, height, scale := w.width, w.height, w.scale
if w.newScale {
- C.wl_surface_set_buffer_scale(w.surf, C.int32_t(scale))
+ C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale))
w.newScale = false
}
- return w.surf, width * scale, height * scale
+ sz, _ := w.getConfig()
+ return w.surf, sz.X, sz.Y
}
func (w *window) ShowTextInput(show bool) {}
-// Close the window. Not implemented for Wayland.
-func (w *window) Close() {}
+func (w *window) SetInputHint(_ key.InputHint) {}
+
+// Close the window.
+func (w *window) Close() {
+ w.dead = true
+}
+
+// Maximize the window. Not implemented for Wayland.
+func (w *window) Maximize() {}
+
+// Center the window. Not implemented for Wayland.
+func (w *window) Center() {}
+
+func (w *window) NewContext() (context, error) {
+ var firstErr error
+ if f := newWaylandVulkanContext; f != nil {
+ c, err := f(w)
+ if err == nil {
+ return c, nil
+ }
+ firstErr = err
+ }
+ if f := newWaylandEGLContext; f != nil {
+ c, err := f(w)
+ if err == nil {
+ return c, nil
+ }
+ firstErr = err
+ }
+ if firstErr != nil {
+ return nil, firstErr
+ }
+ return nil, errors.New("wayland: no available GPU backends")
+}
// detectUIScale reports the system UI scale, or 1.0 if it fails.
func detectUIScale() float32 {
diff --git a/vendor/gioui.org/app/internal/window/os_windows.go b/vendor/gioui.org/app/os_windows.go
index 247f10a..ecbffb4 100644
--- a/vendor/gioui.org/app/internal/window/os_windows.go
+++ b/vendor/gioui.org/app/os_windows.go
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
-package window
+package app
import (
"errors"
@@ -13,23 +13,23 @@ import (
"sync"
"time"
"unicode"
- "unicode/utf16"
"unsafe"
syscall "golang.org/x/sys/windows"
"gioui.org/app/internal/windows"
"gioui.org/unit"
+ gowindows "golang.org/x/sys/windows"
"gioui.org/f32"
+ "gioui.org/io/clipboard"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/system"
)
-type winConstraints struct {
- minWidth, minHeight int32
- maxWidth, maxHeight int32
+type ViewEvent struct {
+ HWND uintptr
}
type winDeltas struct {
@@ -40,69 +40,80 @@ type winDeltas struct {
type window struct {
hwnd syscall.Handle
hdc syscall.Handle
- w Callbacks
- width int
- height int
+ w *callbacks
stage system.Stage
- dead bool
pointerBtns pointer.Buttons
- mu sync.Mutex
+ // cursorIn tracks whether the cursor was inside the window according
+ // to the most recent WM_SETCURSOR.
+ cursorIn bool
+ cursor syscall.Handle
+
+ // placement saves the previous window position when in full screen mode.
+ placement *windows.WindowPlacement
+
animating bool
- minmax winConstraints
deltas winDeltas
- opts *Options
+ config Config
}
-const _WM_REDRAW = windows.WM_USER + 0
+const _WM_WAKEUP = windows.WM_USER + iota
type gpuAPI struct {
priority int
- initializer func(w *window) (Context, error)
+ initializer func(w *window) (context, error)
}
-// backends is the list of potential Context
-// implementations.
-var backends []gpuAPI
+// drivers is the list of potential Context implementations.
+var drivers []gpuAPI
// winMap maps win32 HWNDs to *windows.
var winMap sync.Map
+// iconID is the ID of the icon in the resource file.
+const iconID = 1
+
var resources struct {
once sync.Once
// handle is the module handle from GetModuleHandle.
handle syscall.Handle
// class is the Gio window class from RegisterClassEx.
class uint16
- // cursor is the arrow cursor resource
+ // cursor is the arrow cursor resource.
cursor syscall.Handle
}
-func Main() {
+func osMain() {
select {}
}
-func NewWindow(window Callbacks, opts *Options) error {
+func newWindow(window *callbacks, options []Option) error {
cerr := make(chan error)
go func() {
- // Call win32 API from a single OS thread.
+ // GetMessage and PeekMessage can filter on a window HWND, but
+ // then thread-specific messages such as WM_QUIT are ignored.
+ // Instead lock the thread so window messages arrive through
+ // unfiltered GetMessage calls.
runtime.LockOSThread()
- w, err := createNativeWindow(opts)
+ w, err := createNativeWindow()
if err != nil {
cerr <- err
return
}
- defer w.destroy()
cerr <- nil
winMap.Store(w.hwnd, w)
defer winMap.Delete(w.hwnd)
w.w = window
w.w.SetDriver(w)
- defer w.w.Event(system.DestroyEvent{})
+ w.w.Event(ViewEvent{HWND: uintptr(w.hwnd)})
+ w.Configure(options)
windows.ShowWindow(w.hwnd, windows.SW_SHOWDEFAULT)
windows.SetForegroundWindow(w.hwnd)
windows.SetFocus(w.hwnd)
+ // Since the window class for the cursor is null,
+ // set it here to show the cursor.
+ w.SetCursor(pointer.CursorDefault)
if err := w.loop(); err != nil {
panic(err)
}
@@ -118,17 +129,18 @@ func initResources() error {
return err
}
resources.handle = hInst
- curs, err := windows.LoadCursor(windows.IDC_ARROW)
+ c, err := windows.LoadCursor(windows.IDC_ARROW)
if err != nil {
return err
}
- resources.cursor = curs
+ resources.cursor = c
+ icon, _ := windows.LoadImage(hInst, iconID, windows.IMAGE_ICON, 0, 0, windows.LR_DEFAULTSIZE|windows.LR_SHARED)
wcls := windows.WndClassEx{
CbSize: uint32(unsafe.Sizeof(windows.WndClassEx{})),
Style: windows.CS_HREDRAW | windows.CS_VREDRAW | windows.CS_OWNDC,
LpfnWndProc: syscall.NewCallback(windowProc),
HInstance: hInst,
- HCursor: curs,
+ HIcon: icon,
LpszClassName: syscall.StringToUTF16Ptr("GioWindow"),
}
cls, err := windows.RegisterClassEx(&wcls)
@@ -139,16 +151,7 @@ func initResources() error {
return nil
}
-func getWindowConstraints(cfg unit.Metric, opts *Options, d winDeltas) winConstraints {
- var minmax winConstraints
- minmax.minWidth = int32(cfg.Px(opts.MinWidth))
- minmax.minHeight = int32(cfg.Px(opts.MinHeight))
- minmax.maxWidth = int32(cfg.Px(opts.MaxWidth))
- minmax.maxHeight = int32(cfg.Px(opts.MaxHeight))
- return minmax
-}
-
-func createNativeWindow(opts *Options) (*window, error) {
+func createNativeWindow() (*window, error) {
var resErr error
resources.once.Do(func() {
resErr = initResources()
@@ -156,28 +159,15 @@ func createNativeWindow(opts *Options) (*window, error) {
if resErr != nil {
return nil, resErr
}
- cfg := configForDC()
- wr := windows.Rect{
- Right: int32(cfg.Px(opts.Width)),
- Bottom: int32(cfg.Px(opts.Height)),
- }
dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW)
dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE)
- deltas := winDeltas{
- width: wr.Right,
- height: wr.Bottom,
- }
- windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle)
- deltas.width = wr.Right - wr.Left - deltas.width
- deltas.height = wr.Bottom - wr.Top - deltas.height
hwnd, err := windows.CreateWindowEx(dwExStyle,
resources.class,
- opts.Title,
+ "",
dwStyle|windows.WS_CLIPSIBLINGS|windows.WS_CLIPCHILDREN,
windows.CW_USEDEFAULT, windows.CW_USEDEFAULT,
- wr.Right-wr.Left,
- wr.Bottom-wr.Top,
+ windows.CW_USEDEFAULT, windows.CW_USEDEFAULT,
0,
0,
resources.handle,
@@ -186,10 +176,7 @@ func createNativeWindow(opts *Options) (*window, error) {
return nil, err
}
w := &window{
- hwnd: hwnd,
- minmax: getWindowConstraints(cfg, opts, deltas),
- deltas: deltas,
- opts: opts,
+ hwnd: hwnd,
}
w.hdc, err = windows.GetDC(hwnd)
if err != nil {
@@ -210,7 +197,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
case windows.WM_UNICHAR:
if wParam == windows.UNICODE_NOCHAR {
// Tell the system that we accept WM_UNICHAR messages.
- return 1
+ return windows.TRUE
}
fallthrough
case windows.WM_CHAR:
@@ -218,29 +205,44 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
w.w.Event(key.EditEvent{Text: string(r)})
}
// The message is processed.
- return 1
+ return windows.TRUE
case windows.WM_DPICHANGED:
// Let Windows know we're prepared for runtime DPI changes.
- return 1
+ return windows.TRUE
case windows.WM_ERASEBKGND:
// Avoid flickering between GPU content and background color.
- return 1
- case windows.WM_KEYDOWN, windows.WM_SYSKEYDOWN:
+ return windows.TRUE
+ case windows.WM_KEYDOWN, windows.WM_KEYUP, windows.WM_SYSKEYDOWN, windows.WM_SYSKEYUP:
if n, ok := convertKeyCode(wParam); ok {
- w.w.Event(key.Event{Name: n, Modifiers: getModifiers()})
+ e := key.Event{
+ Name: n,
+ Modifiers: getModifiers(),
+ State: key.Press,
+ }
+ if msg == windows.WM_KEYUP || msg == windows.WM_SYSKEYUP {
+ e.State = key.Release
+ }
+
+ w.w.Event(e)
+
+ if (wParam == windows.VK_F10) && (msg == windows.WM_SYSKEYDOWN || msg == windows.WM_SYSKEYUP) {
+ // Reserve F10 for ourselves, and don't let it open the system menu. Other Windows programs
+ // such as cmd.exe and graphical debuggers also reserve F10.
+ return 0
+ }
}
case windows.WM_LBUTTONDOWN:
- w.pointerButton(pointer.ButtonLeft, true, lParam, getModifiers())
+ w.pointerButton(pointer.ButtonPrimary, true, lParam, getModifiers())
case windows.WM_LBUTTONUP:
- w.pointerButton(pointer.ButtonLeft, false, lParam, getModifiers())
+ w.pointerButton(pointer.ButtonPrimary, false, lParam, getModifiers())
case windows.WM_RBUTTONDOWN:
- w.pointerButton(pointer.ButtonRight, true, lParam, getModifiers())
+ w.pointerButton(pointer.ButtonSecondary, true, lParam, getModifiers())
case windows.WM_RBUTTONUP:
- w.pointerButton(pointer.ButtonRight, false, lParam, getModifiers())
+ w.pointerButton(pointer.ButtonSecondary, false, lParam, getModifiers())
case windows.WM_MBUTTONDOWN:
- w.pointerButton(pointer.ButtonMiddle, true, lParam, getModifiers())
+ w.pointerButton(pointer.ButtonTertiary, true, lParam, getModifiers())
case windows.WM_MBUTTONUP:
- w.pointerButton(pointer.ButtonMiddle, false, lParam, getModifiers())
+ w.pointerButton(pointer.ButtonTertiary, false, lParam, getModifiers())
case windows.WM_CANCELMODE:
w.w.Event(pointer.Event{
Type: pointer.Cancel,
@@ -260,9 +262,19 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
Time: windows.GetMessageTime(),
})
case windows.WM_MOUSEWHEEL:
- w.scrollEvent(wParam, lParam)
+ w.scrollEvent(wParam, lParam, false)
+ case windows.WM_MOUSEHWHEEL:
+ w.scrollEvent(wParam, lParam, true)
case windows.WM_DESTROY:
- w.dead = true
+ w.w.Event(ViewEvent{})
+ w.w.Event(system.DestroyEvent{})
+ if w.hdc != 0 {
+ windows.ReleaseDC(w.hdc)
+ w.hdc = 0
+ }
+ // The system destroys the HWND for us.
+ w.hwnd = 0
+ windows.PostQuitMessage(0)
case windows.WM_PAINT:
w.draw(true)
case windows.WM_SIZE:
@@ -270,22 +282,51 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
case windows.SIZE_MINIMIZED:
w.setStage(system.StagePaused)
case windows.SIZE_MAXIMIZED, windows.SIZE_RESTORED:
+ var triggerEvent bool
+ // Check the window size change.
+ var r windows.Rect
+ windows.GetClientRect(w.hwnd, &r)
+ size := image.Point{
+ X: int(r.Right - r.Left),
+ Y: int(r.Bottom - r.Top),
+ }
+ if size != w.config.Size {
+ w.config.Size = size
+ triggerEvent = true
+ }
+ // Check the window mode.
+ mode := w.config.Mode
+ w.updateWindowMode()
+ if mode != w.config.Mode {
+ triggerEvent = true
+ }
+ if triggerEvent {
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
w.setStage(system.StageRunning)
}
case windows.WM_GETMINMAXINFO:
mm := (*windows.MinMaxInfo)(unsafe.Pointer(uintptr(lParam)))
- if w.minmax.minWidth > 0 || w.minmax.minHeight > 0 {
+ if p := w.config.MinSize; p.X > 0 || p.Y > 0 {
mm.PtMinTrackSize = windows.Point{
- w.minmax.minWidth+w.deltas.width,
- w.minmax.minHeight+w.deltas.height,
+ X: int32(p.X) + w.deltas.width,
+ Y: int32(p.Y) + w.deltas.height,
}
}
- if w.minmax.maxWidth > 0 || w.minmax.maxHeight > 0 {
+ if p := w.config.MaxSize; p.X > 0 || p.Y > 0 {
mm.PtMaxTrackSize = windows.Point{
- w.minmax.maxWidth+w.deltas.width,
- w.minmax.maxHeight+w.deltas.height,
+ X: int32(p.X) + w.deltas.width,
+ Y: int32(p.Y) + w.deltas.height,
}
}
+ case windows.WM_SETCURSOR:
+ w.cursorIn = (lParam & 0xffff) == windows.HTCLIENT
+ if w.cursorIn {
+ windows.SetCursor(w.cursor)
+ return windows.TRUE
+ }
+ case _WM_WAKEUP:
+ w.w.Event(wakeupEvent{})
}
return windows.DefWindowProc(hwnd, msg, wParam, lParam)
@@ -341,7 +382,7 @@ func coordsFromlParam(lParam uintptr) (int, int) {
return x, y
}
-func (w *window) scrollEvent(wParam, lParam uintptr) {
+func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool) {
x, y := coordsFromlParam(lParam)
// The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast
// to other mouse events.
@@ -349,12 +390,18 @@ func (w *window) scrollEvent(wParam, lParam uintptr) {
windows.ScreenToClient(w.hwnd, &np)
p := f32.Point{X: float32(np.X), Y: float32(np.Y)}
dist := float32(int16(wParam >> 16))
+ var sp f32.Point
+ if horizontal {
+ sp.X = dist
+ } else {
+ sp.Y = -dist
+ }
w.w.Event(pointer.Event{
Type: pointer.Scroll,
Source: pointer.Mouse,
Position: p,
Buttons: w.pointerBtns,
- Scroll: f32.Point{Y: -dist},
+ Scroll: sp,
Time: windows.GetMessageTime(),
})
}
@@ -362,18 +409,19 @@ func (w *window) scrollEvent(wParam, lParam uintptr) {
// Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/
func (w *window) loop() error {
msg := new(windows.Msg)
- for !w.dead {
- w.mu.Lock()
+loop:
+ for {
anim := w.animating
- w.mu.Unlock()
- if anim && !windows.PeekMessage(msg, w.hwnd, 0, 0, windows.PM_NOREMOVE) {
+ if anim && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) {
w.draw(false)
continue
}
- windows.GetMessage(msg, w.hwnd, 0, 0)
- if msg.Message == windows.WM_QUIT {
- windows.PostQuitMessage(msg.WParam)
- break
+ switch ret := windows.GetMessage(msg, 0, 0, 0); ret {
+ case -1:
+ return errors.New("GetMessage failed")
+ case 0:
+ // WM_QUIT received.
+ break loop
}
windows.TranslateMessage(msg)
windows.DispatchMessage(msg)
@@ -382,65 +430,44 @@ func (w *window) loop() error {
}
func (w *window) SetAnimating(anim bool) {
- w.mu.Lock()
w.animating = anim
- w.mu.Unlock()
- if anim {
- w.postRedraw()
- }
}
-func (w *window) postRedraw() {
- if err := windows.PostMessage(w.hwnd, _WM_REDRAW, 0, 0); err != nil {
+func (w *window) Wakeup() {
+ if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil {
panic(err)
}
}
func (w *window) setStage(s system.Stage) {
- w.stage = s
- w.w.Event(system.StageEvent{Stage: s})
+ if s != w.stage {
+ w.stage = s
+ w.w.Event(system.StageEvent{Stage: s})
+ }
}
func (w *window) draw(sync bool) {
- var r windows.Rect
- windows.GetClientRect(w.hwnd, &r)
- w.width = int(r.Right - r.Left)
- w.height = int(r.Bottom - r.Top)
- if w.width == 0 || w.height == 0 {
+ if w.config.Size.X == 0 || w.config.Size.Y == 0 {
return
}
- cfg := configForDC()
- w.minmax = getWindowConstraints(cfg, w.opts, w.deltas)
- w.w.Event(FrameEvent{
+ dpi := windows.GetWindowDPI(w.hwnd)
+ cfg := configForDPI(dpi)
+ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{
- Now: time.Now(),
- Size: image.Point{
- X: w.width,
- Y: w.height,
- },
+ Now: time.Now(),
+ Size: w.config.Size,
Metric: cfg,
},
Sync: sync,
})
}
-func (w *window) destroy() {
- if w.hdc != 0 {
- windows.ReleaseDC(w.hdc)
- w.hdc = 0
- }
- if w.hwnd != 0 {
- windows.DestroyWindow(w.hwnd)
- w.hwnd = 0
- }
-}
-
-func (w *window) NewContext() (Context, error) {
- sort.Slice(backends, func(i, j int) bool {
- return backends[i].priority < backends[j].priority
+func (w *window) NewContext() (context, error) {
+ sort.Slice(drivers, func(i, j int) bool {
+ return drivers[i].priority < drivers[j].priority
})
var errs []string
- for _, b := range backends {
+ for _, b := range drivers {
ctx, err := b.initializer(w)
if err == nil {
return ctx, nil
@@ -450,7 +477,7 @@ func (w *window) NewContext() (Context, error) {
if len(errs) > 0 {
return nil, fmt.Errorf("NewContext: failed to create a GPU device, tried: %s", strings.Join(errs, ", "))
}
- return nil, errors.New("NewContext: no available backends")
+ return nil, errors.New("NewContext: no available GPU drivers")
}
func (w *window) ReadClipboard() {
@@ -471,25 +498,142 @@ func (w *window) readClipboard() error {
return err
}
defer windows.GlobalUnlock(mem)
- // Look for terminating null character.
- n := 0
- for {
- ch := *(*uint16)(unsafe.Pointer(ptr + uintptr(n)*2))
- if ch == 0 {
- break
+ content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr)))
+ w.w.Event(clipboard.Event{Text: content})
+ return nil
+}
+
+func (w *window) updateWindowMode() {
+ p := windows.GetWindowPlacement(w.hwnd)
+ r := p.Rect()
+ mi := windows.GetMonitorInfo(w.hwnd)
+ if r == mi.Monitor {
+ w.config.Mode = Fullscreen
+ } else {
+ w.config.Mode = Windowed
+ }
+}
+
+func (w *window) Configure(options []Option) {
+ dpi := windows.GetSystemDPI()
+ cfg := configForDPI(dpi)
+ prev := w.config
+ w.updateWindowMode()
+ cnf := w.config
+ cnf.apply(cfg, options)
+
+ if cnf.Mode != Fullscreen && prev.Size != cnf.Size {
+ width := int32(cnf.Size.X)
+ height := int32(cnf.Size.Y)
+ w.config.Size = cnf.Size
+
+ // Include the window decorations.
+ wr := windows.Rect{
+ Right: width,
+ Bottom: height,
}
- n++
+ dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW)
+ dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE)
+ windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle)
+
+ dw, dh := width, height
+ width = wr.Right - wr.Left
+ height = wr.Bottom - wr.Top
+ w.deltas.width = width - dw
+ w.deltas.height = height - dh
+
+ windows.MoveWindow(w.hwnd, 0, 0, width, height, true)
+ }
+ if prev.MinSize != cnf.MinSize {
+ w.config.MinSize = cnf.MinSize
+ }
+ if prev.MaxSize != cnf.MaxSize {
+ w.config.MaxSize = cnf.MaxSize
+ }
+ if prev.Title != cnf.Title {
+ w.config.Title = cnf.Title
+ windows.SetWindowText(w.hwnd, cnf.Title)
+ }
+ if prev.Mode != cnf.Mode {
+ w.SetWindowMode(cnf.Mode)
+ }
+ if w.config != prev {
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
+}
+
+// Maximize the window. It will have no effect when in fullscreen mode.
+func (w *window) Maximize() {
+ if w.config.Mode == Fullscreen {
+ return
+ }
+ style := windows.GetWindowLong(w.hwnd)
+ windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW|windows.WS_MAXIMIZE)
+ mi := windows.GetMonitorInfo(w.hwnd)
+ windows.SetWindowPos(w.hwnd, 0,
+ mi.Monitor.Left, mi.Monitor.Top,
+ mi.Monitor.Right-mi.Monitor.Left,
+ mi.Monitor.Bottom-mi.Monitor.Top,
+ windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
+ )
+}
+
+// Center will place window at monitor center.
+func (w *window) Center() {
+ // Make sure that the window is sizeable
+ style := windows.GetWindowLong(w.hwnd) & (^uintptr(windows.WS_MAXIMIZE))
+ windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW)
+
+ // Find with/height including the window decorations.
+ wr := windows.Rect{
+ Right: int32(w.config.Size.X),
+ Bottom: int32(w.config.Size.Y),
+ }
+ dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW)
+ dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE)
+ windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle)
+ width := wr.Right - wr.Left
+ height := wr.Bottom - wr.Top
+
+ // Move to center of current monitor
+ mi := windows.GetMonitorInfo(w.hwnd).Monitor
+ x := mi.Left + (mi.Right-mi.Left-width)/2
+ y := mi.Top + (mi.Bottom-mi.Top-height)/2
+ windows.MoveWindow(w.hwnd, x, y, width, height, true)
+}
+
+func (w *window) SetWindowMode(mode WindowMode) {
+ // https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353
+ switch mode {
+ case Windowed:
+ if w.placement == nil {
+ return
+ }
+ w.config.Mode = Windowed
+ windows.SetWindowPlacement(w.hwnd, w.placement)
+ w.placement = nil
+ style := windows.GetWindowLong(w.hwnd)
+ windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW)
+ windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST,
+ 0, 0, 0, 0,
+ windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
+ )
+ case Fullscreen:
+ if w.placement != nil {
+ return
+ }
+ w.config.Mode = Fullscreen
+ w.placement = windows.GetWindowPlacement(w.hwnd)
+ style := windows.GetWindowLong(w.hwnd)
+ windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style&^windows.WS_OVERLAPPEDWINDOW)
+ mi := windows.GetMonitorInfo(w.hwnd)
+ windows.SetWindowPos(w.hwnd, 0,
+ mi.Monitor.Left, mi.Monitor.Top,
+ mi.Monitor.Right-mi.Monitor.Left,
+ mi.Monitor.Bottom-mi.Monitor.Top,
+ windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
+ )
}
- var u16 []uint16
- hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u16))
- hdr.Data = ptr
- hdr.Cap = n
- hdr.Len = n
- content := string(utf16.Decode(u16))
- go func() {
- w.w.Event(system.ClipboardEvent{Text: content})
- }()
- return nil
}
func (w *window) WriteClipboard(s string) {
@@ -497,9 +641,6 @@ func (w *window) WriteClipboard(s string) {
}
func (w *window) writeClipboard(s string) error {
- u16 := utf16.Encode([]rune(s))
- // Data must be null terminated.
- u16 = append(u16, 0)
if err := windows.OpenClipboard(w.hwnd); err != nil {
return err
}
@@ -507,6 +648,10 @@ func (w *window) writeClipboard(s string) error {
if err := windows.EmptyClipboard(); err != nil {
return err
}
+ u16, err := gowindows.UTF16FromString(s)
+ if err != nil {
+ return err
+ }
n := len(u16) * int(unsafe.Sizeof(u16[0]))
mem, err := windows.GlobalAlloc(n)
if err != nil {
@@ -531,20 +676,64 @@ func (w *window) writeClipboard(s string) error {
return nil
}
+func (w *window) SetCursor(name pointer.CursorName) {
+ c, err := loadCursor(name)
+ if err != nil {
+ c = resources.cursor
+ }
+ w.cursor = c
+ if w.cursorIn {
+ windows.SetCursor(w.cursor)
+ }
+}
+
+func loadCursor(name pointer.CursorName) (syscall.Handle, error) {
+ var curID uint16
+ switch name {
+ default:
+ fallthrough
+ case pointer.CursorDefault:
+ return resources.cursor, nil
+ case pointer.CursorText:
+ curID = windows.IDC_IBEAM
+ case pointer.CursorPointer:
+ curID = windows.IDC_HAND
+ case pointer.CursorCrossHair:
+ curID = windows.IDC_CROSS
+ case pointer.CursorColResize:
+ curID = windows.IDC_SIZEWE
+ case pointer.CursorRowResize:
+ curID = windows.IDC_SIZENS
+ case pointer.CursorGrab:
+ curID = windows.IDC_SIZEALL
+ case pointer.CursorNone:
+ return 0, nil
+ }
+ return windows.LoadCursor(curID)
+}
+
func (w *window) ShowTextInput(show bool) {}
+func (w *window) SetInputHint(_ key.InputHint) {}
+
func (w *window) HDC() syscall.Handle {
return w.hdc
}
func (w *window) HWND() (syscall.Handle, int, int) {
- return w.hwnd, w.width, w.height
+ return w.hwnd, w.config.Size.X, w.config.Size.Y
}
func (w *window) Close() {
windows.PostMessage(w.hwnd, windows.WM_CLOSE, 0, 0)
}
+func (w *window) Raise() {
+ windows.SetForegroundWindow(w.hwnd)
+ windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST, 0, 0, 0, 0,
+ windows.SWP_NOMOVE|windows.SWP_NOSIZE|windows.SWP_SHOWWINDOW)
+}
+
func convertKeyCode(code uintptr) (string, bool) {
if '0' <= code && code <= '9' || 'A' <= code && code <= 'Z' {
return string(rune(code)), true
@@ -602,7 +791,7 @@ func convertKeyCode(code uintptr) (string, bool) {
case windows.VK_TAB:
r = key.NameTab
case windows.VK_SPACE:
- r = "Space"
+ r = key.NameSpace
case windows.VK_OEM_1:
r = ";"
case windows.VK_OEM_PLUS:
@@ -631,8 +820,7 @@ func convertKeyCode(code uintptr) (string, bool) {
return r, true
}
-func configForDC() unit.Metric {
- dpi := windows.GetSystemDPI()
+func configForDPI(dpi int) unit.Metric {
const inchPrDp = 1.0 / 96.0
ppdp := float32(dpi) * inchPrDp
return unit.Metric{
@@ -640,3 +828,5 @@ func configForDC() unit.Metric {
PxPerSp: ppdp,
}
}
+
+func (_ ViewEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/app/internal/window/os_x11.go b/vendor/gioui.org/app/os_x11.go
index 69108c6..1c75247 100644
--- a/vendor/gioui.org/app/internal/window/os_x11.go
+++ b/vendor/gioui.org/app/os_x11.go
@@ -1,14 +1,16 @@
// SPDX-License-Identifier: Unlicense OR MIT
-// +build linux,!android,!nox11 freebsd openbsd
+//go:build ((linux && !android) || freebsd || openbsd) && !nox11
+// +build linux,!android freebsd openbsd
+// +build !nox11
-package window
+package app
/*
-#cgo openbsd CFLAGS: -I/usr/X11R6/include -I/usr/local/include
-#cgo openbsd LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib
-#cgo freebsd openbsd LDFLAGS: -lX11 -lxkbcommon -lxkbcommon-x11 -lX11-xcb
-#cgo linux pkg-config: x11 xkbcommon xkbcommon-x11 x11-xcb
+#cgo freebsd openbsd CFLAGS: -I/usr/X11R6/include -I/usr/local/include
+#cgo freebsd openbsd LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib
+#cgo freebsd openbsd LDFLAGS: -lX11 -lxkbcommon -lxkbcommon-x11 -lX11-xcb -lXcursor -lXfixes
+#cgo linux pkg-config: x11 xkbcommon xkbcommon-x11 x11-xcb xcursor xfixes
#include <stdlib.h>
#include <locale.h>
@@ -18,6 +20,8 @@ package window
#include <X11/Xresource.h>
#include <X11/XKBlib.h>
#include <X11/Xlib-xcb.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/Xcursor/Xcursor.h>
#include <xkbcommon/xkbcommon-x11.h>
*/
@@ -34,17 +38,24 @@ import (
"unsafe"
"gioui.org/f32"
+ "gioui.org/io/clipboard"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/system"
"gioui.org/unit"
- "gioui.org/app/internal/xkb"
syscall "golang.org/x/sys/unix"
+
+ "gioui.org/app/internal/xkb"
+)
+
+const (
+ _NET_WM_STATE_REMOVE = 0
+ _NET_WM_STATE_ADD = 1
)
type x11Window struct {
- w Callbacks
+ w *callbacks
x *C.Display
xkb *xkb.Context
xkbEventBase C.int
@@ -53,68 +64,213 @@ type x11Window struct {
atoms struct {
// "UTF8_STRING".
utf8string C.Atom
+ // "text/plain;charset=utf-8".
+ plaintext C.Atom
// "TARGETS"
targets C.Atom
// "CLIPBOARD".
clipboard C.Atom
+ // "PRIMARY".
+ primary C.Atom
// "CLIPBOARD_CONTENT", the clipboard destination property.
clipboardContent C.Atom
// "WM_DELETE_WINDOW"
evDelWindow C.Atom
// "ATOM"
atom C.Atom
+ // "GTK_TEXT_BUFFER_CONTENTS"
+ gtk_text_buffer_contents C.Atom
+ // "_NET_WM_NAME"
+ wmName C.Atom
+ // "_NET_WM_STATE"
+ wmState C.Atom
+ // "_NET_WM_STATE_FULLSCREEN"
+ wmStateFullscreen C.Atom
+ // "_NET_ACTIVE_WINDOW"
+ wmActiveWindow C.Atom
+ // _NET_WM_STATE_MAXIMIZED_HORZ
+ wmStateMaximizedHorz C.Atom
+ // _NET_WM_STATE_MAXIMIZED_VERT
+ wmStateMaximizedVert C.Atom
}
stage system.Stage
- cfg unit.Metric
- width int
- height int
+ metric unit.Metric
notify struct {
read, write int
}
dead bool
- mu sync.Mutex
animating bool
pointerBtns pointer.Buttons
clipboard struct {
- read bool
- write *string
content []byte
}
+ cursor pointer.CursorName
+ config Config
+
+ wakeups chan struct{}
+}
+
+var (
+ newX11EGLContext func(w *x11Window) (context, error)
+ newX11VulkanContext func(w *x11Window) (context, error)
+)
+
+func (w *x11Window) NewContext() (context, error) {
+ var firstErr error
+ if f := newX11VulkanContext; f != nil {
+ c, err := f(w)
+ if err == nil {
+ return c, nil
+ }
+ firstErr = err
+ }
+ if f := newX11EGLContext; f != nil {
+ c, err := f(w)
+ if err == nil {
+ return c, nil
+ }
+ firstErr = err
+ }
+ if firstErr != nil {
+ return nil, firstErr
+ }
+ return nil, errors.New("x11: no available GPU backends")
}
func (w *x11Window) SetAnimating(anim bool) {
- w.mu.Lock()
w.animating = anim
- w.mu.Unlock()
- if anim {
- w.wakeup()
- }
}
func (w *x11Window) ReadClipboard() {
- w.mu.Lock()
- w.clipboard.read = true
- w.mu.Unlock()
- w.wakeup()
+ C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent)
+ C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime)
}
func (w *x11Window) WriteClipboard(s string) {
- w.mu.Lock()
- w.clipboard.write = &s
- w.mu.Unlock()
- w.wakeup()
+ w.clipboard.content = []byte(s)
+ C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime)
+ C.XSetSelectionOwner(w.x, w.atoms.primary, w.xw, C.CurrentTime)
+}
+
+func (w *x11Window) Configure(options []Option) {
+ var shints C.XSizeHints
+ prev := w.config
+ cnf := w.config
+ cnf.apply(w.metric, options)
+ if prev.MinSize != cnf.MinSize {
+ w.config.MinSize = cnf.MinSize
+ shints.min_width = C.int(cnf.MinSize.X)
+ shints.min_height = C.int(cnf.MinSize.Y)
+ shints.flags = C.PMinSize
+ }
+ if prev.MaxSize != cnf.MaxSize {
+ w.config.MaxSize = cnf.MaxSize
+ shints.max_width = C.int(cnf.MaxSize.X)
+ shints.max_height = C.int(cnf.MaxSize.Y)
+ shints.flags = shints.flags | C.PMaxSize
+ }
+ if shints.flags != 0 {
+ C.XSetWMNormalHints(w.x, w.xw, &shints)
+ }
+
+ if cnf.Mode != Fullscreen && prev.Size != cnf.Size {
+ w.config.Size = cnf.Size
+ C.XResizeWindow(w.x, w.xw, C.uint(cnf.Size.X), C.uint(cnf.Size.Y))
+ }
+
+ if prev.Title != cnf.Title {
+ title := cnf.Title
+ ctitle := C.CString(title)
+ defer C.free(unsafe.Pointer(ctitle))
+ C.XStoreName(w.x, w.xw, ctitle)
+ // set _NET_WM_NAME as well for UTF-8 support in window title.
+ C.XSetTextProperty(w.x, w.xw,
+ &C.XTextProperty{
+ value: (*C.uchar)(unsafe.Pointer(ctitle)),
+ encoding: w.atoms.utf8string,
+ format: 8,
+ nitems: C.ulong(len(title)),
+ },
+ w.atoms.wmName)
+ }
+
+ if prev.Mode != cnf.Mode {
+ w.SetWindowMode(cnf.Mode)
+ }
+ if w.config != prev {
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
+}
+
+func (w *x11Window) Raise() {
+ var xev C.XEvent
+ ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev))
+ *ev = C.XClientMessageEvent{
+ _type: C.ClientMessage,
+ display: w.x,
+ window: w.xw,
+ message_type: w.atoms.wmActiveWindow,
+ format: 32,
+ }
+ C.XSendEvent(
+ w.x,
+ C.XDefaultRootWindow(w.x), // MUST be the root window
+ C.False,
+ C.SubstructureNotifyMask|C.SubstructureRedirectMask,
+ &xev,
+ )
+ C.XMapRaised(w.display(), w.xw)
+}
+
+func (w *x11Window) SetCursor(name pointer.CursorName) {
+ switch name {
+ case pointer.CursorNone:
+ w.cursor = name
+ C.XFixesHideCursor(w.x, w.xw)
+ return
+ case pointer.CursorGrab:
+ name = "hand1"
+ }
+ if w.cursor == pointer.CursorNone {
+ C.XFixesShowCursor(w.x, w.xw)
+ }
+ cname := C.CString(string(name))
+ defer C.free(unsafe.Pointer(cname))
+ c := C.XcursorLibraryLoadCursor(w.x, cname)
+ if c == 0 {
+ name = pointer.CursorDefault
+ }
+ w.cursor = name
+ // If c if null (i.e. name was not found),
+ // XDefineCursor will use the default cursor.
+ C.XDefineCursor(w.x, w.xw, c)
+}
+
+func (w *x11Window) SetWindowMode(mode WindowMode) {
+ var action C.long
+ switch mode {
+ case Windowed:
+ action = _NET_WM_STATE_REMOVE
+ case Fullscreen:
+ action = _NET_WM_STATE_ADD
+ default:
+ return
+ }
+ w.config.Mode = mode
+ // "A Client wishing to change the state of a window MUST send
+ // a _NET_WM_STATE client message to the root window."
+ w.sendWMStateEvent(action, w.atoms.wmStateFullscreen, 0)
}
func (w *x11Window) ShowTextInput(show bool) {}
+func (w *x11Window) SetInputHint(_ key.InputHint) {}
+
// Close the window.
func (w *x11Window) Close() {
- w.mu.Lock()
- defer w.mu.Unlock()
-
var xev C.XEvent
ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev))
*ev = C.XClientMessageEvent{
@@ -130,9 +286,62 @@ func (w *x11Window) Close() {
C.XSendEvent(w.x, w.xw, C.False, C.NoEventMask, &xev)
}
+// Maximize the window.
+func (w *x11Window) Maximize() {
+ w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert)
+}
+
+// Center the window.
+func (w *x11Window) Center() {
+ screen := C.XDefaultScreen(w.x)
+ width := C.XDisplayWidth(w.x, screen)
+ height := C.XDisplayHeight(w.x, screen)
+
+ var attrs C.XWindowAttributes
+ C.XGetWindowAttributes(w.x, w.xw, &attrs)
+ width -= attrs.border_width
+ height -= attrs.border_width
+
+ sz := w.config.Size
+ x := (int(width) - sz.X) / 2
+ y := (int(height) - sz.Y) / 2
+
+ C.XMoveResizeWindow(w.x, w.xw, C.int(x), C.int(y), C.uint(sz.X), C.uint(sz.Y))
+}
+
+// action is one of _NET_WM_STATE_REMOVE, _NET_WM_STATE_ADD.
+func (w *x11Window) sendWMStateEvent(action C.long, atom1, atom2 C.ulong) {
+ var xev C.XEvent
+ ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev))
+ *ev = C.XClientMessageEvent{
+ _type: C.ClientMessage,
+ display: w.x,
+ window: w.xw,
+ message_type: w.atoms.wmState,
+ format: 32,
+ }
+ data := (*[5]C.long)(unsafe.Pointer(&ev.data))
+ data[0] = C.long(action)
+ data[1] = C.long(atom1)
+ data[2] = C.long(atom2)
+ data[3] = 1 // application
+
+ C.XSendEvent(
+ w.x,
+ C.XDefaultRootWindow(w.x), // MUST be the root window
+ C.False,
+ C.SubstructureNotifyMask|C.SubstructureRedirectMask,
+ &xev,
+ )
+}
+
var x11OneByte = make([]byte, 1)
-func (w *x11Window) wakeup() {
+func (w *x11Window) Wakeup() {
+ select {
+ case w.wakeups <- struct{}{}:
+ default:
+ }
if _, err := syscall.Write(w.notify.write, x11OneByte); err != nil && err != syscall.EAGAIN {
panic(fmt.Errorf("failed to write to pipe: %v", err))
}
@@ -143,7 +352,7 @@ func (w *x11Window) display() *C.Display {
}
func (w *x11Window) window() (C.Window, int, int) {
- return w.xw, w.width, w.height
+ return w.xw, w.config.Size.X, w.config.Size.Y
}
func (w *x11Window) setStage(s system.Stage) {
@@ -151,7 +360,7 @@ func (w *x11Window) setStage(s system.Stage) {
return
}
w.stage = s
- w.w.Event(system.StageEvent{s})
+ w.w.Event(system.StageEvent{Stage: s})
}
func (w *x11Window) loop() {
@@ -174,9 +383,7 @@ loop:
// This fixes an issue on Xephyr where on startup XPending() > 0 but
// poll will still block. This also prevents no-op calls to poll.
if syn = h.handleEvents(); !syn {
- w.mu.Lock()
anim = w.animating
- w.mu.Unlock()
if !anim {
// Clear poll events.
*xEvents = 0
@@ -205,34 +412,22 @@ loop:
panic(fmt.Errorf("x11 loop: read from notify pipe failed: %w", err))
}
}
+ select {
+ case <-w.wakeups:
+ w.w.Event(wakeupEvent{})
+ default:
+ }
- if anim || syn {
- w.w.Event(FrameEvent{
+ if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 {
+ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{
- Now: time.Now(),
- Size: image.Point{
- X: w.width,
- Y: w.height,
- },
- Metric: w.cfg,
+ Now: time.Now(),
+ Size: w.config.Size,
+ Metric: w.metric,
},
Sync: syn,
})
}
- w.mu.Lock()
- readClipboard := w.clipboard.read
- writeClipboard := w.clipboard.write
- w.clipboard.read = false
- w.clipboard.write = nil
- w.mu.Unlock()
- if readClipboard {
- C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent)
- C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime)
- }
- if writeClipboard != nil {
- w.clipboard.content = []byte(*writeClipboard)
- C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime)
- }
}
w.w.Event(system.DestroyEvent{Err: nil})
}
@@ -301,12 +496,15 @@ func (h *x11EventHandler) handleEvents() bool {
h.w.xkb.UpdateMask(uint32(state.base_mods), uint32(state.latched_mods), uint32(state.locked_mods),
uint32(state.base_group), uint32(state.latched_group), uint32(state.locked_group))
}
- case C.KeyPress:
+ case C.KeyPress, C.KeyRelease:
+ ks := key.Press
+ if _type == C.KeyRelease {
+ ks = key.Release
+ }
kevt := (*C.XKeyPressedEvent)(unsafe.Pointer(xev))
- for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode)) {
+ for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode), ks) {
w.w.Event(e)
}
- case C.KeyRelease:
case C.ButtonPress, C.ButtonRelease:
bevt := (*C.XButtonEvent)(unsafe.Pointer(xev))
ev := pointer.Event{
@@ -326,11 +524,11 @@ func (h *x11EventHandler) handleEvents() bool {
const scrollScale = 10
switch bevt.button {
case C.Button1:
- btn = pointer.ButtonLeft
+ btn = pointer.ButtonPrimary
case C.Button2:
- btn = pointer.ButtonMiddle
+ btn = pointer.ButtonTertiary
case C.Button3:
- btn = pointer.ButtonRight
+ btn = pointer.ButtonSecondary
case C.Button4:
// scroll up
ev.Type = pointer.Scroll
@@ -339,6 +537,15 @@ func (h *x11EventHandler) handleEvents() bool {
// scroll down
ev.Type = pointer.Scroll
ev.Scroll.Y = +scrollScale
+ case 6:
+ // http://xahlee.info/linux/linux_x11_mouse_button_number.html
+ // scroll left
+ ev.Type = pointer.Scroll
+ ev.Scroll.X = -scrollScale * 2
+ case 7:
+ // scroll right
+ ev.Type = pointer.Scroll
+ ev.Scroll.X = +scrollScale * 2
default:
continue
}
@@ -372,8 +579,10 @@ func (h *x11EventHandler) handleEvents() bool {
w.w.Event(key.FocusEvent{Focus: false})
case C.ConfigureNotify: // window configuration change
cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev))
- w.width = int(cevt.width)
- w.height = int(cevt.height)
+ if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size {
+ w.config.Size = sz
+ w.w.Event(ConfigEvent{Config: w.config})
+ }
// redraw will be done by a later expose event
case C.SelectionNotify:
cevt := (*C.XSelectionEvent)(unsafe.Pointer(xev))
@@ -394,10 +603,10 @@ func (h *x11EventHandler) handleEvents() bool {
break
}
str := C.GoStringN((*C.char)(unsafe.Pointer(text.value)), C.int(text.nitems))
- w.w.Event(system.ClipboardEvent{Text: str})
+ w.w.Event(clipboard.Event{Text: str})
case C.SelectionRequest:
cevt := (*C.XSelectionRequestEvent)(unsafe.Pointer(xev))
- if cevt.selection != w.atoms.clipboard || cevt.property == C.None {
+ if (cevt.selection != w.atoms.clipboard && cevt.selection != w.atoms.primary) || cevt.property == C.None {
// Unsupported clipboard or obsolete requestor.
break
}
@@ -418,21 +627,27 @@ func (h *x11EventHandler) handleEvents() bool {
switch cevt.target {
case w.atoms.targets:
// The requestor wants the supported clipboard
- // formats. First write the formats...
- formats := []uint32{uint32(w.atoms.utf8string)}
+ // formats. First write the targets...
+ formats := [...]C.long{
+ C.long(w.atoms.targets),
+ C.long(w.atoms.utf8string),
+ C.long(w.atoms.plaintext),
+ // GTK clients need this.
+ C.long(w.atoms.gtk_text_buffer_contents),
+ }
C.XChangeProperty(w.x, cevt.requestor, cevt.property, w.atoms.atom,
32 /* bitwidth of formats */, C.PropModeReplace,
- (*C.uchar)(unsafe.Pointer(&formats[0])), C.int(len(formats)),
+ (*C.uchar)(unsafe.Pointer(&formats)), C.int(len(formats)),
)
// ...then notify the requestor.
notify()
- case w.atoms.utf8string:
+ case w.atoms.plaintext, w.atoms.utf8string, w.atoms.gtk_text_buffer_contents:
content := w.clipboard.content
var ptr *C.uchar
if len(content) > 0 {
ptr = (*C.uchar)(unsafe.Pointer(&content[0]))
}
- C.XChangeProperty(w.x, cevt.requestor, cevt.property, w.atoms.utf8string,
+ C.XChangeProperty(w.x, cevt.requestor, cevt.property, cevt.target,
8 /* bitwidth */, C.PropModeReplace,
ptr, C.int(len(content)),
)
@@ -458,7 +673,7 @@ func init() {
x11Driver = newX11Window
}
-func newX11Window(gioWin Callbacks, opts *Options) error {
+func newX11Window(gioWin *callbacks, options []Option) error {
var err error
pipe := make([]int, 2)
@@ -498,6 +713,10 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
ppsp := x11DetectUIScale(dpy)
cfg := unit.Metric{PxPerDp: ppsp, PxPerSp: ppsp}
+ // Only use cnf for getting the window size.
+ var cnf Config
+ cnf.apply(cfg, options)
+
swa := C.XSetWindowAttributes{
event_mask: C.ExposureMask | C.FocusChangeMask | // update
C.KeyPressMask | C.KeyReleaseMask | // keyboard
@@ -508,17 +727,17 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
override_redirect: C.False,
}
win := C.XCreateWindow(dpy, C.XDefaultRootWindow(dpy),
- 0, 0, C.uint(cfg.Px(opts.Width)), C.uint(cfg.Px(opts.Height)),
+ 0, 0, C.uint(cnf.Size.X), C.uint(cnf.Size.Y),
0, C.CopyFromParent, C.InputOutput, nil,
C.CWEventMask|C.CWBackPixmap|C.CWOverrideRedirect, &swa)
w := &x11Window{
w: gioWin, x: dpy, xw: win,
- width: cfg.Px(opts.Width),
- height: cfg.Px(opts.Height),
- cfg: cfg,
+ metric: cfg,
xkb: xkb,
xkbEventBase: xkbEventBase,
+ wakeups: make(chan struct{}, 1),
+ config: Config{Size: cnf.Size},
}
w.notify.read = pipe[0]
w.notify.write = pipe[1]
@@ -533,57 +752,40 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
hints.flags = C.InputHint
C.XSetWMHints(dpy, win, &hints)
- var shints C.XSizeHints
- if opts.MinWidth.V != 0 || opts.MinHeight.V != 0 {
- shints.min_width = C.int(cfg.Px(opts.MinWidth))
- shints.min_height = C.int(cfg.Px(opts.MinHeight))
- shints.flags = C.PMinSize
- }
- if opts.MaxWidth.V != 0 || opts.MaxHeight.V != 0 {
- shints.max_width = C.int(cfg.Px(opts.MaxWidth))
- shints.max_height = C.int(cfg.Px(opts.MaxHeight))
- shints.flags = shints.flags | C.PMaxSize
- }
- if shints.flags != 0 {
- C.XSetWMNormalHints(dpy, win, &shints)
- }
-
name := C.CString(filepath.Base(os.Args[0]))
defer C.free(unsafe.Pointer(name))
wmhints := C.XClassHint{name, name}
C.XSetClassHint(dpy, win, &wmhints)
w.atoms.utf8string = w.atom("UTF8_STRING", false)
+ w.atoms.plaintext = w.atom("text/plain;charset=utf-8", false)
+ w.atoms.gtk_text_buffer_contents = w.atom("GTK_TEXT_BUFFER_CONTENTS", false)
w.atoms.evDelWindow = w.atom("WM_DELETE_WINDOW", false)
w.atoms.clipboard = w.atom("CLIPBOARD", false)
+ w.atoms.primary = w.atom("PRIMARY", false)
w.atoms.clipboardContent = w.atom("CLIPBOARD_CONTENT", false)
w.atoms.atom = w.atom("ATOM", false)
w.atoms.targets = w.atom("TARGETS", false)
-
- // set the name
- ctitle := C.CString(opts.Title)
- defer C.free(unsafe.Pointer(ctitle))
- C.XStoreName(dpy, win, ctitle)
- // set _NET_WM_NAME as well for UTF-8 support in window title.
- C.XSetTextProperty(dpy, win,
- &C.XTextProperty{
- value: (*C.uchar)(unsafe.Pointer(ctitle)),
- encoding: w.atoms.utf8string,
- format: 8,
- nitems: C.ulong(len(opts.Title)),
- },
- w.atom("_NET_WM_NAME", false))
+ w.atoms.wmName = w.atom("_NET_WM_NAME", false)
+ w.atoms.wmState = w.atom("_NET_WM_STATE", false)
+ w.atoms.wmStateFullscreen = w.atom("_NET_WM_STATE_FULLSCREEN", false)
+ w.atoms.wmActiveWindow = w.atom("_NET_ACTIVE_WINDOW", false)
+ w.atoms.wmStateMaximizedHorz = w.atom("_NET_WM_STATE_MAXIMIZED_HORZ", false)
+ w.atoms.wmStateMaximizedVert = w.atom("_NET_WM_STATE_MAXIMIZED_VERT", false)
// extensions
C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1)
- // make the window visible on the screen
- C.XMapWindow(dpy, win)
-
go func() {
w.w.SetDriver(w)
+ w.Configure(options)
+
+ // make the window visible on the screen
+ C.XMapWindow(dpy, win)
+ w.w.Event(ViewEvent{Display: unsafe.Pointer(dpy), Window: uintptr(win)})
w.setStage(system.StageRunning)
w.loop()
+ w.w.Event(ViewEvent{})
w.destroy()
}()
return nil
diff --git a/vendor/gioui.org/app/internal/window/runmain.go b/vendor/gioui.org/app/runmain.go
index f9de5d0..a1c1e3d 100644
--- a/vendor/gioui.org/app/internal/window/runmain.go
+++ b/vendor/gioui.org/app/runmain.go
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build android || (darwin && ios)
// +build android darwin,ios
-package window
+package app
// Android only supports non-Java programs as c-shared libraries.
// Unfortunately, Go does not run a program's main function in
diff --git a/vendor/gioui.org/app/sigpipe_darwin.go b/vendor/gioui.org/app/sigpipe_darwin.go
deleted file mode 100644
index aca19b7..0000000
--- a/vendor/gioui.org/app/sigpipe_darwin.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-// +build !go1.14
-
-// Work around golang.org/issue/33384, fixed in CL 191785,
-// to be released in Go 1.14.
-
-package app
-
-import (
- "os"
- "os/signal"
- "syscall"
-)
-
-func init() {
- signal.Notify(make(chan os.Signal), syscall.SIGPIPE)
-}
diff --git a/vendor/gioui.org/app/vulkan.go b/vendor/gioui.org/app/vulkan.go
new file mode 100644
index 0000000..630c254
--- /dev/null
+++ b/vendor/gioui.org/app/vulkan.go
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build (linux || freebsd) && !novulkan
+// +build linux freebsd
+// +build !novulkan
+
+package app
+
+import (
+ "errors"
+ "unsafe"
+
+ "gioui.org/gpu"
+ "gioui.org/internal/vk"
+)
+
+type vkContext struct {
+ physDev vk.PhysicalDevice
+ inst vk.Instance
+ dev vk.Device
+ queueFam int
+ queue vk.Queue
+ acquireSem vk.Semaphore
+ presentSem vk.Semaphore
+
+ swchain vk.Swapchain
+ imgs []vk.Image
+ views []vk.ImageView
+ fbos []vk.Framebuffer
+ format vk.Format
+ presentIdx int
+}
+
+func newVulkanContext(inst vk.Instance, surf vk.Surface) (*vkContext, error) {
+ physDev, qFam, err := vk.ChoosePhysicalDevice(inst, surf)
+ if err != nil {
+ return nil, err
+ }
+ dev, err := vk.CreateDeviceAndQueue(physDev, qFam, "VK_KHR_swapchain")
+ if err != nil {
+ return nil, err
+ }
+ if err != nil {
+ vk.DestroyDevice(dev)
+ return nil, err
+ }
+ acquireSem, err := vk.CreateSemaphore(dev)
+ if err != nil {
+ vk.DestroyDevice(dev)
+ return nil, err
+ }
+ presentSem, err := vk.CreateSemaphore(dev)
+ if err != nil {
+ vk.DestroySemaphore(dev, acquireSem)
+ vk.DestroyDevice(dev)
+ return nil, err
+ }
+ c := &vkContext{
+ physDev: physDev,
+ inst: inst,
+ dev: dev,
+ queueFam: qFam,
+ queue: vk.GetDeviceQueue(dev, qFam, 0),
+ acquireSem: acquireSem,
+ presentSem: presentSem,
+ }
+ return c, nil
+}
+
+func (c *vkContext) RenderTarget() (gpu.RenderTarget, error) {
+ vk.DeviceWaitIdle(c.dev)
+
+ imgIdx, err := vk.AcquireNextImage(c.dev, c.swchain, c.acquireSem, 0)
+ if err := mapSurfaceErr(err); err != nil {
+ return nil, err
+ }
+ c.presentIdx = imgIdx
+ return gpu.VulkanRenderTarget{
+ WaitSem: uint64(c.acquireSem),
+ SignalSem: uint64(c.presentSem),
+ Framebuffer: uint64(c.fbos[imgIdx]),
+ Image: uint64(c.imgs[imgIdx]),
+ }, nil
+}
+
+func (c *vkContext) api() gpu.API {
+ return gpu.Vulkan{
+ PhysDevice: unsafe.Pointer(c.physDev),
+ Device: unsafe.Pointer(c.dev),
+ Format: int(c.format),
+ QueueFamily: c.queueFam,
+ QueueIndex: 0,
+ }
+}
+
+func mapErr(err error) error {
+ var vkErr vk.Error
+ if errors.As(err, &vkErr) && vkErr == vk.ERROR_DEVICE_LOST {
+ return gpu.ErrDeviceLost
+ }
+ return err
+}
+
+func mapSurfaceErr(err error) error {
+ var vkErr vk.Error
+ if !errors.As(err, &vkErr) {
+ return err
+ }
+ switch {
+ case vkErr == vk.SUBOPTIMAL_KHR:
+ // Android reports VK_SUBOPTIMAL_KHR when presenting to a rotated
+ // swapchain (preTransform != currentTransform). However, we don't
+ // support transforming the output ourselves, so we'll live with it.
+ return nil
+ case vkErr == vk.ERROR_OUT_OF_DATE_KHR:
+ return errOutOfDate
+ case vkErr == vk.ERROR_SURFACE_LOST_KHR:
+ // Treating a lost surface as a lost device isn't accurate, but
+ // probably not worth optimizing.
+ return gpu.ErrDeviceLost
+ }
+ return mapErr(err)
+}
+
+func (c *vkContext) release() {
+ vk.DeviceWaitIdle(c.dev)
+
+ c.destroySwapchain()
+ vk.DestroySemaphore(c.dev, c.acquireSem)
+ vk.DestroySemaphore(c.dev, c.presentSem)
+ vk.DestroyDevice(c.dev)
+ *c = vkContext{}
+}
+
+func (c *vkContext) present() error {
+ return mapSurfaceErr(vk.PresentQueue(c.queue, c.swchain, c.presentSem, c.presentIdx))
+}
+
+func (c *vkContext) destroyImageViews() {
+ for _, f := range c.fbos {
+ vk.DestroyFramebuffer(c.dev, f)
+ }
+ c.fbos = nil
+ for _, view := range c.views {
+ vk.DestroyImageView(c.dev, view)
+ }
+ c.views = nil
+}
+
+func (c *vkContext) destroySwapchain() {
+ vk.DeviceWaitIdle(c.dev)
+
+ c.destroyImageViews()
+ if c.swchain != 0 {
+ vk.DestroySwapchain(c.dev, c.swchain)
+ c.swchain = 0
+ }
+}
+
+func (c *vkContext) refresh(surf vk.Surface, width, height int) error {
+ vk.DeviceWaitIdle(c.dev)
+
+ c.destroyImageViews()
+ // Check whether size is valid. That's needed on X11, where ConfigureNotify
+ // is not always synchronized with the window extent.
+ caps, err := vk.GetPhysicalDeviceSurfaceCapabilities(c.physDev, surf)
+ if err != nil {
+ return err
+ }
+ minExt, maxExt := caps.MinExtent(), caps.MaxExtent()
+ if width < minExt.X || maxExt.X < width || height < minExt.Y || maxExt.Y < height {
+ return errOutOfDate
+ }
+ swchain, imgs, format, err := vk.CreateSwapchain(c.physDev, c.dev, surf, width, height, c.swchain)
+ if c.swchain != 0 {
+ vk.DestroySwapchain(c.dev, c.swchain)
+ c.swchain = 0
+ }
+ if err := mapSurfaceErr(err); err != nil {
+ return err
+ }
+ c.swchain = swchain
+ c.imgs = imgs
+ c.format = format
+ pass, err := vk.CreateRenderPass(
+ c.dev,
+ format,
+ vk.ATTACHMENT_LOAD_OP_CLEAR,
+ vk.IMAGE_LAYOUT_UNDEFINED,
+ vk.IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ nil,
+ )
+ if err := mapErr(err); err != nil {
+ return err
+ }
+ defer vk.DestroyRenderPass(c.dev, pass)
+ for _, img := range imgs {
+ view, err := vk.CreateImageView(c.dev, img, format)
+ if err := mapErr(err); err != nil {
+ return err
+ }
+ c.views = append(c.views, view)
+ fbo, err := vk.CreateFramebuffer(c.dev, pass, view, width, height)
+ if err := mapErr(err); err != nil {
+ return err
+ }
+ c.fbos = append(c.fbos, fbo)
+ }
+ return nil
+}
diff --git a/vendor/gioui.org/app/vulkan_android.go b/vendor/gioui.org/app/vulkan_android.go
new file mode 100644
index 0000000..3758d04
--- /dev/null
+++ b/vendor/gioui.org/app/vulkan_android.go
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build !novulkan
+// +build !novulkan
+
+package app
+
+import (
+ "unsafe"
+
+ "gioui.org/gpu"
+ "gioui.org/internal/vk"
+)
+
+type wlVkContext struct {
+ win *window
+ inst vk.Instance
+ surf vk.Surface
+ ctx *vkContext
+}
+
+func init() {
+ newAndroidVulkanContext = func(w *window) (context, error) {
+ inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_android_surface")
+ if err != nil {
+ return nil, err
+ }
+ window, _, _ := w.nativeWindow()
+ surf, err := vk.CreateAndroidSurface(inst, unsafe.Pointer(window))
+ if err != nil {
+ vk.DestroyInstance(inst)
+ return nil, err
+ }
+ ctx, err := newVulkanContext(inst, surf)
+ if err != nil {
+ vk.DestroySurface(inst, surf)
+ vk.DestroyInstance(inst)
+ return nil, err
+ }
+ c := &wlVkContext{
+ win: w,
+ inst: inst,
+ surf: surf,
+ ctx: ctx,
+ }
+ return c, nil
+ }
+}
+
+func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) {
+ return c.ctx.RenderTarget()
+}
+
+func (c *wlVkContext) API() gpu.API {
+ return c.ctx.api()
+}
+
+func (c *wlVkContext) Release() {
+ c.ctx.release()
+ if c.surf != 0 {
+ vk.DestroySurface(c.inst, c.surf)
+ }
+ vk.DestroyInstance(c.inst)
+ *c = wlVkContext{}
+}
+
+func (c *wlVkContext) Present() error {
+ return c.ctx.present()
+}
+
+func (c *wlVkContext) Lock() error {
+ return nil
+}
+
+func (c *wlVkContext) Unlock() {}
+
+func (c *wlVkContext) Refresh() error {
+ win, w, h := c.win.nativeWindow()
+ if c.surf != 0 {
+ c.ctx.destroySwapchain()
+ vk.DestroySurface(c.inst, c.surf)
+ c.surf = 0
+ }
+ surf, err := vk.CreateAndroidSurface(c.inst, unsafe.Pointer(win))
+ if err != nil {
+ return err
+ }
+ c.surf = surf
+ return c.ctx.refresh(c.surf, w, h)
+}
diff --git a/vendor/gioui.org/app/vulkan_wayland.go b/vendor/gioui.org/app/vulkan_wayland.go
new file mode 100644
index 0000000..7bf0662
--- /dev/null
+++ b/vendor/gioui.org/app/vulkan_wayland.go
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build ((linux && !android) || freebsd) && !nowayland && !novulkan
+// +build linux,!android freebsd
+// +build !nowayland
+// +build !novulkan
+
+package app
+
+import (
+ "unsafe"
+
+ "gioui.org/gpu"
+ "gioui.org/internal/vk"
+)
+
+type wlVkContext struct {
+ win *window
+ inst vk.Instance
+ surf vk.Surface
+ ctx *vkContext
+}
+
+func init() {
+ newWaylandVulkanContext = func(w *window) (context, error) {
+ inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_wayland_surface")
+ if err != nil {
+ return nil, err
+ }
+ disp := w.display()
+ wlSurf, _, _ := w.surface()
+ surf, err := vk.CreateWaylandSurface(inst, unsafe.Pointer(disp), unsafe.Pointer(wlSurf))
+ if err != nil {
+ vk.DestroyInstance(inst)
+ return nil, err
+ }
+ ctx, err := newVulkanContext(inst, surf)
+ if err != nil {
+ vk.DestroySurface(inst, surf)
+ vk.DestroyInstance(inst)
+ return nil, err
+ }
+ c := &wlVkContext{
+ win: w,
+ inst: inst,
+ surf: surf,
+ ctx: ctx,
+ }
+ return c, nil
+ }
+}
+
+func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) {
+ return c.ctx.RenderTarget()
+}
+
+func (c *wlVkContext) API() gpu.API {
+ return c.ctx.api()
+}
+
+func (c *wlVkContext) Release() {
+ c.ctx.release()
+ vk.DestroySurface(c.inst, c.surf)
+ vk.DestroyInstance(c.inst)
+ *c = wlVkContext{}
+}
+
+func (c *wlVkContext) Present() error {
+ return c.ctx.present()
+}
+
+func (c *wlVkContext) Lock() error {
+ return nil
+}
+
+func (c *wlVkContext) Unlock() {}
+
+func (c *wlVkContext) Refresh() error {
+ _, w, h := c.win.surface()
+ return c.ctx.refresh(c.surf, w, h)
+}
diff --git a/vendor/gioui.org/app/vulkan_x11.go b/vendor/gioui.org/app/vulkan_x11.go
new file mode 100644
index 0000000..cf97774
--- /dev/null
+++ b/vendor/gioui.org/app/vulkan_x11.go
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build ((linux && !android) || freebsd) && !nox11 && !novulkan
+// +build linux,!android freebsd
+// +build !nox11
+// +build !novulkan
+
+package app
+
+import (
+ "unsafe"
+
+ "gioui.org/gpu"
+ "gioui.org/internal/vk"
+)
+
+type x11VkContext struct {
+ win *x11Window
+ inst vk.Instance
+ surf vk.Surface
+ ctx *vkContext
+}
+
+func init() {
+ newX11VulkanContext = func(w *x11Window) (context, error) {
+ inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_xlib_surface")
+ if err != nil {
+ return nil, err
+ }
+ disp := w.display()
+ window, _, _ := w.window()
+ surf, err := vk.CreateXlibSurface(inst, unsafe.Pointer(disp), uintptr(window))
+ if err != nil {
+ vk.DestroyInstance(inst)
+ return nil, err
+ }
+ ctx, err := newVulkanContext(inst, surf)
+ if err != nil {
+ vk.DestroySurface(inst, surf)
+ vk.DestroyInstance(inst)
+ return nil, err
+ }
+ c := &x11VkContext{
+ win: w,
+ inst: inst,
+ surf: surf,
+ ctx: ctx,
+ }
+ return c, nil
+ }
+}
+
+func (c *x11VkContext) RenderTarget() (gpu.RenderTarget, error) {
+ return c.ctx.RenderTarget()
+}
+
+func (c *x11VkContext) API() gpu.API {
+ return c.ctx.api()
+}
+
+func (c *x11VkContext) Release() {
+ c.ctx.release()
+ vk.DestroySurface(c.inst, c.surf)
+ vk.DestroyInstance(c.inst)
+ *c = x11VkContext{}
+}
+
+func (c *x11VkContext) Present() error {
+ return c.ctx.present()
+}
+
+func (c *x11VkContext) Lock() error {
+ return nil
+}
+
+func (c *x11VkContext) Unlock() {}
+
+func (c *x11VkContext) Refresh() error {
+ _, w, h := c.win.window()
+ return c.ctx.refresh(c.surf, w, h)
+}
diff --git a/vendor/gioui.org/app/internal/window/wayland_text_input.c b/vendor/gioui.org/app/wayland_text_input.c
index a69af09..65de0bb 100644
--- a/vendor/gioui.org/app/internal/window/wayland_text_input.c
+++ b/vendor/gioui.org/app/wayland_text_input.c
@@ -1,6 +1,8 @@
-// +build linux,!android,!nowayland freebsd
+//go:build ((linux && !android) || freebsd) && !nowayland
+// +build linux,!android freebsd
+// +build !nowayland
-/* Generated by wayland-scanner 1.17.0 */
+/* Generated by wayland-scanner 1.19.0 */
/*
* Copyright © 2012, 2013 Intel Corporation
@@ -48,7 +50,7 @@ extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface zwp_text_input_v3_interface;
-static const struct wl_interface *types[] = {
+static const struct wl_interface *text_input_unstable_v3_types[] = {
NULL,
NULL,
NULL,
@@ -60,23 +62,23 @@ static const struct wl_interface *types[] = {
};
static const struct wl_message zwp_text_input_v3_requests[] = {
- { "destroy", "", types + 0 },
- { "enable", "", types + 0 },
- { "disable", "", types + 0 },
- { "set_surrounding_text", "sii", types + 0 },
- { "set_text_change_cause", "u", types + 0 },
- { "set_content_type", "uu", types + 0 },
- { "set_cursor_rectangle", "iiii", types + 0 },
- { "commit", "", types + 0 },
+ { "destroy", "", text_input_unstable_v3_types + 0 },
+ { "enable", "", text_input_unstable_v3_types + 0 },
+ { "disable", "", text_input_unstable_v3_types + 0 },
+ { "set_surrounding_text", "sii", text_input_unstable_v3_types + 0 },
+ { "set_text_change_cause", "u", text_input_unstable_v3_types + 0 },
+ { "set_content_type", "uu", text_input_unstable_v3_types + 0 },
+ { "set_cursor_rectangle", "iiii", text_input_unstable_v3_types + 0 },
+ { "commit", "", text_input_unstable_v3_types + 0 },
};
static const struct wl_message zwp_text_input_v3_events[] = {
- { "enter", "o", types + 4 },
- { "leave", "o", types + 5 },
- { "preedit_string", "?sii", types + 0 },
- { "commit_string", "?s", types + 0 },
- { "delete_surrounding_text", "uu", types + 0 },
- { "done", "u", types + 0 },
+ { "enter", "o", text_input_unstable_v3_types + 4 },
+ { "leave", "o", text_input_unstable_v3_types + 5 },
+ { "preedit_string", "?sii", text_input_unstable_v3_types + 0 },
+ { "commit_string", "?s", text_input_unstable_v3_types + 0 },
+ { "delete_surrounding_text", "uu", text_input_unstable_v3_types + 0 },
+ { "done", "u", text_input_unstable_v3_types + 0 },
};
WL_PRIVATE const struct wl_interface zwp_text_input_v3_interface = {
@@ -86,8 +88,8 @@ WL_PRIVATE const struct wl_interface zwp_text_input_v3_interface = {
};
static const struct wl_message zwp_text_input_manager_v3_requests[] = {
- { "destroy", "", types + 0 },
- { "get_text_input", "no", types + 6 },
+ { "destroy", "", text_input_unstable_v3_types + 0 },
+ { "get_text_input", "no", text_input_unstable_v3_types + 6 },
};
WL_PRIVATE const struct wl_interface zwp_text_input_manager_v3_interface = {
diff --git a/vendor/gioui.org/app/internal/window/wayland_text_input.h b/vendor/gioui.org/app/wayland_text_input.h
index 8a6f8dd..882da43 100644
--- a/vendor/gioui.org/app/internal/window/wayland_text_input.h
+++ b/vendor/gioui.org/app/wayland_text_input.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.17.0 */
+/* Generated by wayland-scanner 1.19.0 */
#ifndef TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H
#define TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H
@@ -71,6 +71,8 @@ struct wl_surface;
struct zwp_text_input_manager_v3;
struct zwp_text_input_v3;
+#ifndef ZWP_TEXT_INPUT_V3_INTERFACE
+#define ZWP_TEXT_INPUT_V3_INTERFACE
/**
* @page page_iface_zwp_text_input_v3 zwp_text_input_v3
* @section page_iface_zwp_text_input_v3_desc Description
@@ -135,6 +137,9 @@ struct zwp_text_input_v3;
* needs to be resent by the client.
*/
extern const struct wl_interface zwp_text_input_v3_interface;
+#endif
+#ifndef ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE
+#define ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE
/**
* @page page_iface_zwp_text_input_manager_v3 zwp_text_input_manager_v3
* @section page_iface_zwp_text_input_manager_v3_desc Description
@@ -149,6 +154,7 @@ extern const struct wl_interface zwp_text_input_v3_interface;
* A factory for text-input objects. This object is a global singleton.
*/
extern const struct wl_interface zwp_text_input_manager_v3_interface;
+#endif
#ifndef ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM
#define ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM
@@ -310,6 +316,9 @@ struct zwp_text_input_v3_listener {
* Notification that this seat's text-input focus is on a certain
* surface.
*
+ * If client has created multiple text input objects, compositor
+ * must send this event to all of them.
+ *
* When the seat has the keyboard capability the text-input focus
* follows the keyboard focus. This event sets the current surface
* for the text-input object.
@@ -325,7 +334,9 @@ struct zwp_text_input_v3_listener {
* previously set.
*
* The leave notification clears the current surface. It is sent
- * before the enter notification for the new focus.
+ * before the enter notification for the new focus. After leave
+ * event, compositor must ignore requests from any text input
+ * instances until next enter event.
*
* When the seat has the keyboard capability the text-input focus
* follows the keyboard focus.
@@ -559,6 +570,12 @@ zwp_text_input_v3_destroy(struct zwp_text_input_v3 *zwp_text_input_v3)
* zwp_text_input_v3.disable when there is no longer any input focus on
* the current surface.
*
+ * Clients must not enable more than one text input on the single seat
+ * and should disable the current text input before enabling the new one.
+ * At most one instance of text input may be in enabled state per instance,
+ * Requests to enable the another text input when some text input is active
+ * must be ignored by compositor.
+ *
* This request resets all state associated with previous enable, disable,
* set_surrounding_text, set_text_change_cause, set_content_type, and
* set_cursor_rectangle requests, as well as the state associated with
diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.c b/vendor/gioui.org/app/wayland_xdg_decoration.c
index fa13d7c..ee94c60 100644
--- a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.c
+++ b/vendor/gioui.org/app/wayland_xdg_decoration.c
@@ -1,6 +1,8 @@
-// +build linux,!android,!nowayland freebsd
+//go:build ((linux && !android) || freebsd) && !nowayland
+// +build linux,!android freebsd
+// +build !nowayland
-/* Generated by wayland-scanner 1.17.0 */
+/* Generated by wayland-scanner 1.19.0 */
/*
* Copyright © 2018 Simon Ser
@@ -42,15 +44,15 @@
extern const struct wl_interface xdg_toplevel_interface;
extern const struct wl_interface zxdg_toplevel_decoration_v1_interface;
-static const struct wl_interface *types[] = {
+static const struct wl_interface *xdg_decoration_unstable_v1_types[] = {
NULL,
&zxdg_toplevel_decoration_v1_interface,
&xdg_toplevel_interface,
};
static const struct wl_message zxdg_decoration_manager_v1_requests[] = {
- { "destroy", "", types + 0 },
- { "get_toplevel_decoration", "no", types + 1 },
+ { "destroy", "", xdg_decoration_unstable_v1_types + 0 },
+ { "get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1 },
};
WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = {
@@ -60,13 +62,13 @@ WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = {
};
static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = {
- { "destroy", "", types + 0 },
- { "set_mode", "u", types + 0 },
- { "unset_mode", "", types + 0 },
+ { "destroy", "", xdg_decoration_unstable_v1_types + 0 },
+ { "set_mode", "u", xdg_decoration_unstable_v1_types + 0 },
+ { "unset_mode", "", xdg_decoration_unstable_v1_types + 0 },
};
static const struct wl_message zxdg_toplevel_decoration_v1_events[] = {
- { "configure", "u", types + 0 },
+ { "configure", "u", xdg_decoration_unstable_v1_types + 0 },
};
WL_PRIVATE const struct wl_interface zxdg_toplevel_decoration_v1_interface = {
diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.h b/vendor/gioui.org/app/wayland_xdg_decoration.h
index 044ea2e..004d342 100644
--- a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.h
+++ b/vendor/gioui.org/app/wayland_xdg_decoration.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.17.0 */
+/* Generated by wayland-scanner 1.19.0 */
#ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H
#define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H
@@ -45,6 +45,8 @@ struct xdg_toplevel;
struct zxdg_decoration_manager_v1;
struct zxdg_toplevel_decoration_v1;
+#ifndef ZXDG_DECORATION_MANAGER_V1_INTERFACE
+#define ZXDG_DECORATION_MANAGER_V1_INTERFACE
/**
* @page page_iface_zxdg_decoration_manager_v1 zxdg_decoration_manager_v1
* @section page_iface_zxdg_decoration_manager_v1_desc Description
@@ -101,6 +103,9 @@ struct zxdg_toplevel_decoration_v1;
* interface version number is reset.
*/
extern const struct wl_interface zxdg_decoration_manager_v1_interface;
+#endif
+#ifndef ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE
+#define ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE
/**
* @page page_iface_zxdg_toplevel_decoration_v1 zxdg_toplevel_decoration_v1
* @section page_iface_zxdg_toplevel_decoration_v1_desc Description
@@ -125,6 +130,7 @@ extern const struct wl_interface zxdg_decoration_manager_v1_interface;
* xdg_toplevel.
*/
extern const struct wl_interface zxdg_toplevel_decoration_v1_interface;
+#endif
#define ZXDG_DECORATION_MANAGER_V1_DESTROY 0
#define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION 1
@@ -332,7 +338,7 @@ zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_top
* that the client prefers the provided decoration mode.
*
* After requesting a decoration mode, the compositor will respond by
- * emitting a xdg_surface.configure event. The client should then update
+ * emitting an xdg_surface.configure event. The client should then update
* its content, drawing it without decorations if the received mode is
* server-side decorations. The client must also acknowledge the configure
* when committing the new content (see xdg_surface.ack_configure).
@@ -341,7 +347,7 @@ zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_top
* different mode instead.
*
* Clients whose decoration mode depend on the xdg_toplevel state may send
- * a set_mode request in response to a xdg_surface.configure event and wait
+ * a set_mode request in response to an xdg_surface.configure event and wait
* for the next xdg_surface.configure event to prevent unwanted state.
* Such clients are responsible for preventing configure loops and must
* make sure not to send multiple successive set_mode requests with the
diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.c b/vendor/gioui.org/app/wayland_xdg_shell.c
index 1947847..54fe5cf 100644
--- a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.c
+++ b/vendor/gioui.org/app/wayland_xdg_shell.c
@@ -1,6 +1,8 @@
-// +build linux,!android,!nowayland freebsd
+//go:build ((linux && !android) || freebsd) && !nowayland
+// +build linux,!android freebsd
+// +build !nowayland
-/* Generated by wayland-scanner 1.17.0 */
+/* Generated by wayland-scanner 1.19.0 */
/*
* Copyright © 2008-2013 Kristian Høgsberg
@@ -52,7 +54,7 @@ extern const struct wl_interface xdg_positioner_interface;
extern const struct wl_interface xdg_surface_interface;
extern const struct wl_interface xdg_toplevel_interface;
-static const struct wl_interface *types[] = {
+static const struct wl_interface *xdg_shell_types[] = {
NULL,
NULL,
NULL,
@@ -77,100 +79,107 @@ static const struct wl_interface *types[] = {
&wl_output_interface,
&wl_seat_interface,
NULL,
+ &xdg_positioner_interface,
+ NULL,
};
static const struct wl_message xdg_wm_base_requests[] = {
- { "destroy", "", types + 0 },
- { "create_positioner", "n", types + 4 },
- { "get_xdg_surface", "no", types + 5 },
- { "pong", "u", types + 0 },
+ { "destroy", "", xdg_shell_types + 0 },
+ { "create_positioner", "n", xdg_shell_types + 4 },
+ { "get_xdg_surface", "no", xdg_shell_types + 5 },
+ { "pong", "u", xdg_shell_types + 0 },
};
static const struct wl_message xdg_wm_base_events[] = {
- { "ping", "u", types + 0 },
+ { "ping", "u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
- "xdg_wm_base", 2,
+ "xdg_wm_base", 3,
4, xdg_wm_base_requests,
1, xdg_wm_base_events,
};
static const struct wl_message xdg_positioner_requests[] = {
- { "destroy", "", types + 0 },
- { "set_size", "ii", types + 0 },
- { "set_anchor_rect", "iiii", types + 0 },
- { "set_anchor", "u", types + 0 },
- { "set_gravity", "u", types + 0 },
- { "set_constraint_adjustment", "u", types + 0 },
- { "set_offset", "ii", types + 0 },
+ { "destroy", "", xdg_shell_types + 0 },
+ { "set_size", "ii", xdg_shell_types + 0 },
+ { "set_anchor_rect", "iiii", xdg_shell_types + 0 },
+ { "set_anchor", "u", xdg_shell_types + 0 },
+ { "set_gravity", "u", xdg_shell_types + 0 },
+ { "set_constraint_adjustment", "u", xdg_shell_types + 0 },
+ { "set_offset", "ii", xdg_shell_types + 0 },
+ { "set_reactive", "3", xdg_shell_types + 0 },
+ { "set_parent_size", "3ii", xdg_shell_types + 0 },
+ { "set_parent_configure", "3u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
- "xdg_positioner", 2,
- 7, xdg_positioner_requests,
+ "xdg_positioner", 3,
+ 10, xdg_positioner_requests,
0, NULL,
};
static const struct wl_message xdg_surface_requests[] = {
- { "destroy", "", types + 0 },
- { "get_toplevel", "n", types + 7 },
- { "get_popup", "n?oo", types + 8 },
- { "set_window_geometry", "iiii", types + 0 },
- { "ack_configure", "u", types + 0 },
+ { "destroy", "", xdg_shell_types + 0 },
+ { "get_toplevel", "n", xdg_shell_types + 7 },
+ { "get_popup", "n?oo", xdg_shell_types + 8 },
+ { "set_window_geometry", "iiii", xdg_shell_types + 0 },
+ { "ack_configure", "u", xdg_shell_types + 0 },
};
static const struct wl_message xdg_surface_events[] = {
- { "configure", "u", types + 0 },
+ { "configure", "u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_surface_interface = {
- "xdg_surface", 2,
+ "xdg_surface", 3,
5, xdg_surface_requests,
1, xdg_surface_events,
};
static const struct wl_message xdg_toplevel_requests[] = {
- { "destroy", "", types + 0 },
- { "set_parent", "?o", types + 11 },
- { "set_title", "s", types + 0 },
- { "set_app_id", "s", types + 0 },
- { "show_window_menu", "ouii", types + 12 },
- { "move", "ou", types + 16 },
- { "resize", "ouu", types + 18 },
- { "set_max_size", "ii", types + 0 },
- { "set_min_size", "ii", types + 0 },
- { "set_maximized", "", types + 0 },
- { "unset_maximized", "", types + 0 },
- { "set_fullscreen", "?o", types + 21 },
- { "unset_fullscreen", "", types + 0 },
- { "set_minimized", "", types + 0 },
+ { "destroy", "", xdg_shell_types + 0 },
+ { "set_parent", "?o", xdg_shell_types + 11 },
+ { "set_title", "s", xdg_shell_types + 0 },
+ { "set_app_id", "s", xdg_shell_types + 0 },
+ { "show_window_menu", "ouii", xdg_shell_types + 12 },
+ { "move", "ou", xdg_shell_types + 16 },
+ { "resize", "ouu", xdg_shell_types + 18 },
+ { "set_max_size", "ii", xdg_shell_types + 0 },
+ { "set_min_size", "ii", xdg_shell_types + 0 },
+ { "set_maximized", "", xdg_shell_types + 0 },
+ { "unset_maximized", "", xdg_shell_types + 0 },
+ { "set_fullscreen", "?o", xdg_shell_types + 21 },
+ { "unset_fullscreen", "", xdg_shell_types + 0 },
+ { "set_minimized", "", xdg_shell_types + 0 },
};
static const struct wl_message xdg_toplevel_events[] = {
- { "configure", "iia", types + 0 },
- { "close", "", types + 0 },
+ { "configure", "iia", xdg_shell_types + 0 },
+ { "close", "", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
- "xdg_toplevel", 2,
+ "xdg_toplevel", 3,
14, xdg_toplevel_requests,
2, xdg_toplevel_events,
};
static const struct wl_message xdg_popup_requests[] = {
- { "destroy", "", types + 0 },
- { "grab", "ou", types + 22 },
+ { "destroy", "", xdg_shell_types + 0 },
+ { "grab", "ou", xdg_shell_types + 22 },
+ { "reposition", "3ou", xdg_shell_types + 24 },
};
static const struct wl_message xdg_popup_events[] = {
- { "configure", "iiii", types + 0 },
- { "popup_done", "", types + 0 },
+ { "configure", "iiii", xdg_shell_types + 0 },
+ { "popup_done", "", xdg_shell_types + 0 },
+ { "repositioned", "3u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_popup_interface = {
- "xdg_popup", 2,
- 2, xdg_popup_requests,
- 2, xdg_popup_events,
+ "xdg_popup", 3,
+ 3, xdg_popup_requests,
+ 3, xdg_popup_events,
};
diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.h b/vendor/gioui.org/app/wayland_xdg_shell.h
index 1f4bfb5..1db8fd9 100644
--- a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.h
+++ b/vendor/gioui.org/app/wayland_xdg_shell.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.17.0 */
+/* Generated by wayland-scanner 1.19.0 */
#ifndef XDG_SHELL_CLIENT_PROTOCOL_H
#define XDG_SHELL_CLIENT_PROTOCOL_H
@@ -58,6 +58,8 @@ struct xdg_surface;
struct xdg_toplevel;
struct xdg_wm_base;
+#ifndef XDG_WM_BASE_INTERFACE
+#define XDG_WM_BASE_INTERFACE
/**
* @page page_iface_xdg_wm_base xdg_wm_base
* @section page_iface_xdg_wm_base_desc Description
@@ -80,6 +82,9 @@ struct xdg_wm_base;
* creating transient windows such as popup menus.
*/
extern const struct wl_interface xdg_wm_base_interface;
+#endif
+#ifndef XDG_POSITIONER_INTERFACE
+#define XDG_POSITIONER_INTERFACE
/**
* @page page_iface_xdg_positioner xdg_positioner
* @section page_iface_xdg_positioner_desc Description
@@ -130,6 +135,9 @@ extern const struct wl_interface xdg_wm_base_interface;
* positioning a surface raises an error.
*/
extern const struct wl_interface xdg_positioner_interface;
+#endif
+#ifndef XDG_SURFACE_INTERFACE
+#define XDG_SURFACE_INTERFACE
/**
* @page page_iface_xdg_surface xdg_surface
* @section page_iface_xdg_surface_desc Description
@@ -159,6 +167,11 @@ extern const struct wl_interface xdg_positioner_interface;
* manipulate a buffer prior to the first xdg_surface.configure call must
* also be treated as errors.
*
+ * After creating a role-specific object and setting it up, the client must
+ * perform an initial commit without any buffer attached. The compositor
+ * will reply with an xdg_surface.configure event. The client must
+ * acknowledge it and is then allowed to attach a buffer to map the surface.
+ *
* Mapping an xdg_surface-based role surface is defined as making it
* possible for the surface to be shown by the compositor. Note that
* a mapped surface is not guaranteed to be visible once it is mapped.
@@ -204,6 +217,11 @@ extern const struct wl_interface xdg_positioner_interface;
* manipulate a buffer prior to the first xdg_surface.configure call must
* also be treated as errors.
*
+ * After creating a role-specific object and setting it up, the client must
+ * perform an initial commit without any buffer attached. The compositor
+ * will reply with an xdg_surface.configure event. The client must
+ * acknowledge it and is then allowed to attach a buffer to map the surface.
+ *
* Mapping an xdg_surface-based role surface is defined as making it
* possible for the surface to be shown by the compositor. Note that
* a mapped surface is not guaranteed to be visible once it is mapped.
@@ -220,6 +238,9 @@ extern const struct wl_interface xdg_positioner_interface;
* has not been destroyed.
*/
extern const struct wl_interface xdg_surface_interface;
+#endif
+#ifndef XDG_TOPLEVEL_INTERFACE
+#define XDG_TOPLEVEL_INTERFACE
/**
* @page page_iface_xdg_toplevel xdg_toplevel
* @section page_iface_xdg_toplevel_desc Description
@@ -234,7 +255,11 @@ extern const struct wl_interface xdg_surface_interface;
* by the compositor until it is explicitly mapped again.
* All active operations (e.g., move, resize) are canceled and all
* attributes (e.g. title, state, stacking, ...) are discarded for
- * an xdg_toplevel surface when it is unmapped.
+ * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ * the state it had right after xdg_surface.get_toplevel. The client
+ * can re-map the toplevel by perfoming a commit without any buffer
+ * attached, waiting for a configure event and handling it as usual (see
+ * xdg_surface description).
*
* Attaching a null buffer to a toplevel unmaps the surface.
* @section page_iface_xdg_toplevel_api API
@@ -253,11 +278,18 @@ extern const struct wl_interface xdg_surface_interface;
* by the compositor until it is explicitly mapped again.
* All active operations (e.g., move, resize) are canceled and all
* attributes (e.g. title, state, stacking, ...) are discarded for
- * an xdg_toplevel surface when it is unmapped.
+ * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ * the state it had right after xdg_surface.get_toplevel. The client
+ * can re-map the toplevel by perfoming a commit without any buffer
+ * attached, waiting for a configure event and handling it as usual (see
+ * xdg_surface description).
*
* Attaching a null buffer to a toplevel unmaps the surface.
*/
extern const struct wl_interface xdg_toplevel_interface;
+#endif
+#ifndef XDG_POPUP_INTERFACE
+#define XDG_POPUP_INTERFACE
/**
* @page page_iface_xdg_popup xdg_popup
* @section page_iface_xdg_popup_desc Description
@@ -284,12 +316,6 @@ extern const struct wl_interface xdg_toplevel_interface;
* The parent of an xdg_popup must be mapped (see the xdg_surface
* description) before the xdg_popup itself.
*
- * The x and y arguments passed when creating the popup object specify
- * where the top left of the popup should be placed, relative to the
- * local surface coordinates of the parent surface. See
- * xdg_surface.get_popup. An xdg_popup must intersect with or be at least
- * partially adjacent to its parent surface.
- *
* The client must call wl_surface.commit on the corresponding wl_surface
* for the xdg_popup state to take effect.
* @section page_iface_xdg_popup_api API
@@ -320,16 +346,11 @@ extern const struct wl_interface xdg_toplevel_interface;
* The parent of an xdg_popup must be mapped (see the xdg_surface
* description) before the xdg_popup itself.
*
- * The x and y arguments passed when creating the popup object specify
- * where the top left of the popup should be placed, relative to the
- * local surface coordinates of the parent surface. See
- * xdg_surface.get_popup. An xdg_popup must intersect with or be at least
- * partially adjacent to its parent surface.
- *
* The client must call wl_surface.commit on the corresponding wl_surface
* for the xdg_popup state to take effect.
*/
extern const struct wl_interface xdg_popup_interface;
+#endif
#ifndef XDG_WM_BASE_ERROR_ENUM
#define XDG_WM_BASE_ERROR_ENUM
@@ -587,6 +608,9 @@ enum xdg_positioner_constraint_adjustment {
#define XDG_POSITIONER_SET_GRAVITY 4
#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5
#define XDG_POSITIONER_SET_OFFSET 6
+#define XDG_POSITIONER_SET_REACTIVE 7
+#define XDG_POSITIONER_SET_PARENT_SIZE 8
+#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9
/**
@@ -617,6 +641,18 @@ enum xdg_positioner_constraint_adjustment {
* @ingroup iface_xdg_positioner
*/
#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3
+/**
+ * @ingroup iface_xdg_positioner
+ */
+#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3
/** @ingroup iface_xdg_positioner */
static inline void
@@ -769,6 +805,56 @@ xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int3
XDG_POSITIONER_SET_OFFSET, x, y);
}
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * When set reactive, the surface is reconstrained if the conditions used
+ * for constraining changed, e.g. the parent window moved.
+ *
+ * If the conditions changed and the popup was reconstrained, an
+ * xdg_popup.configure event is sent with updated geometry, followed by an
+ * xdg_surface.configure event.
+ */
+static inline void
+xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner)
+{
+ wl_proxy_marshal((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_REACTIVE);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Set the parent window geometry the compositor should use when
+ * positioning the popup. The compositor may use this information to
+ * determine the future state the popup should be constrained using. If
+ * this doesn't match the dimension of the parent the popup is eventually
+ * positioned against, the behavior is undefined.
+ *
+ * The arguments are given in the surface-local coordinate space.
+ */
+static inline void
+xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height)
+{
+ wl_proxy_marshal((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_PARENT_SIZE, parent_width, parent_height);
+}
+
+/**
+ * @ingroup iface_xdg_positioner
+ *
+ * Set the serial of an xdg_surface.configure event this positioner will be
+ * used in response to. The compositor may use this information together
+ * with set_parent_size to determine what future state the popup should be
+ * constrained using.
+ */
+static inline void
+xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial)
+{
+ wl_proxy_marshal((struct wl_proxy *) xdg_positioner,
+ XDG_POSITIONER_SET_PARENT_CONFIGURE, serial);
+}
+
#ifndef XDG_SURFACE_ERROR_ENUM
#define XDG_SURFACE_ERROR_ENUM
enum xdg_surface_error {
@@ -1691,6 +1777,12 @@ struct xdg_popup_listener {
* The x and y arguments represent the position the popup was
* placed at given the xdg_positioner rule, relative to the upper
* left corner of the window geometry of the parent surface.
+ *
+ * For version 2 or older, the configure event for an xdg_popup is
+ * only ever sent once for the initial configuration. Starting with
+ * version 3, it may be sent again if the popup is setup with an
+ * xdg_positioner with set_reactive requested, or in response to
+ * xdg_popup.reposition requests.
* @param x x position relative to parent surface window geometry
* @param y y position relative to parent surface window geometry
* @param width window geometry width
@@ -1711,6 +1803,32 @@ struct xdg_popup_listener {
*/
void (*popup_done)(void *data,
struct xdg_popup *xdg_popup);
+ /**
+ * signal the completion of a repositioned request
+ *
+ * The repositioned event is sent as part of a popup
+ * configuration sequence, together with xdg_popup.configure and
+ * lastly xdg_surface.configure to notify the completion of a
+ * reposition request.
+ *
+ * The repositioned event is to notify about the completion of a
+ * xdg_popup.reposition request. The token argument is the token
+ * passed in the xdg_popup.reposition request.
+ *
+ * Immediately after this event is emitted, xdg_popup.configure and
+ * xdg_surface.configure will be sent with the updated size and
+ * position, as well as a new configure serial.
+ *
+ * The client should optionally update the content of the popup,
+ * but must acknowledge the new popup configuration for the new
+ * position to take effect. See xdg_surface.ack_configure for
+ * details.
+ * @param token reposition request token
+ * @since 3
+ */
+ void (*repositioned)(void *data,
+ struct xdg_popup *xdg_popup,
+ uint32_t token);
};
/**
@@ -1726,6 +1844,7 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup,
#define XDG_POPUP_DESTROY 0
#define XDG_POPUP_GRAB 1
+#define XDG_POPUP_REPOSITION 2
/**
* @ingroup iface_xdg_popup
@@ -1735,6 +1854,10 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup,
* @ingroup iface_xdg_popup
*/
#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3
/**
* @ingroup iface_xdg_popup
@@ -1744,6 +1867,10 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup,
* @ingroup iface_xdg_popup
*/
#define XDG_POPUP_GRAB_SINCE_VERSION 1
+/**
+ * @ingroup iface_xdg_popup
+ */
+#define XDG_POPUP_REPOSITION_SINCE_VERSION 3
/** @ingroup iface_xdg_popup */
static inline void
@@ -1835,6 +1962,40 @@ xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t seria
XDG_POPUP_GRAB, seat, serial);
}
+/**
+ * @ingroup iface_xdg_popup
+ *
+ * Reposition an already-mapped popup. The popup will be placed given the
+ * details in the passed xdg_positioner object, and a
+ * xdg_popup.repositioned followed by xdg_popup.configure and
+ * xdg_surface.configure will be emitted in response. Any parameters set
+ * by the previous positioner will be discarded.
+ *
+ * The passed token will be sent in the corresponding
+ * xdg_popup.repositioned event. The new popup position will not take
+ * effect until the corresponding configure event is acknowledged by the
+ * client. See xdg_popup.repositioned for details. The token itself is
+ * opaque, and has no other special meaning.
+ *
+ * If multiple reposition requests are sent, the compositor may skip all
+ * but the last one.
+ *
+ * If the popup is repositioned in response to a configure event for its
+ * parent, the client should send an xdg_positioner.set_parent_configure
+ * and possibly an xdg_positioner.set_parent_size request to allow the
+ * compositor to properly constrain the popup.
+ *
+ * If the popup is repositioned together with a parent that is being
+ * resized, but not in response to a configure event, the client should
+ * send an xdg_positioner.set_parent_size request.
+ */
+static inline void
+xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token)
+{
+ wl_proxy_marshal((struct wl_proxy *) xdg_popup,
+ XDG_POPUP_REPOSITION, positioner, token);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/vendor/gioui.org/app/window.go b/vendor/gioui.org/app/window.go
index 2c9860c..ada92fb 100644
--- a/vendor/gioui.org/app/window.go
+++ b/vendor/gioui.org/app/window.go
@@ -6,10 +6,14 @@ import (
"errors"
"fmt"
"image"
+ "image/color"
+ "runtime"
"time"
- "gioui.org/app/internal/window"
+ "gioui.org/f32"
+ "gioui.org/gpu"
"gioui.org/io/event"
+ "gioui.org/io/pointer"
"gioui.org/io/profile"
"gioui.org/io/router"
"gioui.org/io/system"
@@ -19,24 +23,32 @@ import (
_ "gioui.org/app/internal/log"
)
-// WindowOption configures a Window.
-type Option func(opts *window.Options)
+// Option configures a window.
+type Option func(unit.Metric, *Config)
// Window represents an operating system window.
type Window struct {
- driver window.Driver
- loop *renderLoop
+ ctx context
+ gpu gpu.GPU
// driverFuncs is a channel of functions to run when
// the Window has a valid driver.
- driverFuncs chan func()
-
- out chan event.Event
- in chan event.Event
- ack chan struct{}
- invalidates chan struct{}
- frames chan *op.Ops
- frameAck chan struct{}
+ driverFuncs chan func(d driver)
+ // wakeups wakes up the native event loop to send a
+ // WakeupEvent that flushes driverFuncs.
+ wakeups chan struct{}
+ // wakeupFuncs is sent wakeup functions when the driver changes.
+ wakeupFuncs chan func()
+ // redraws is notified when a redraw is requested by the client.
+ redraws chan struct{}
+ // immediateRedraws is like redraw but doesn't need a wakeup.
+ immediateRedraws chan struct{}
+ // scheduledRedraws is sent the most recent delayed redraw time.
+ scheduledRedraws chan time.Time
+
+ out chan event.Event
+ frames chan *op.Ops
+ frameAck chan struct{}
// dead is closed when the window is destroyed.
dead chan struct{}
@@ -46,13 +58,33 @@ type Window struct {
nextFrame time.Time
delayedDraw *time.Timer
- queue queue
+ queue queue
+ cursor pointer.CursorName
callbacks callbacks
+
+ nocontext bool
+
+ // semantic data, lazily evaluated if requested by a backend to speed up
+ // the cases where semantic data is not needed.
+ semantic struct {
+ // uptodate tracks whether the fields below are up to date.
+ uptodate bool
+ root router.SemanticID
+ prevTree []router.SemanticNode
+ tree []router.SemanticNode
+ ids map[router.SemanticID]router.SemanticNode
+ }
+}
+
+type semanticResult struct {
+ found bool
+ node router.SemanticNode
}
type callbacks struct {
w *Window
+ d driver
}
// queue is an event.Queue implementation that distributes system events
@@ -61,12 +93,6 @@ type queue struct {
q router.Router
}
-// driverEvent is sent when a new native driver
-// is available for the Window.
-type driverEvent struct {
- driver window.Driver
-}
-
// Pre-allocate the ack event to avoid garbage.
var ackEvent event.Event
@@ -74,35 +100,37 @@ var ackEvent event.Event
// options. The options are hints; the platform is free to
// ignore or adjust them.
//
-// If the current program is running on iOS and Android,
+// If the current program is running on iOS or Android,
// NewWindow returns the window previously created by the
// platform.
//
// Calling NewWindow more than once is not supported on
// iOS, Android, WebAssembly.
func NewWindow(options ...Option) *Window {
- opts := &window.Options{
- Width: unit.Dp(800),
- Height: unit.Dp(600),
- Title: "Gio",
- }
-
- for _, o := range options {
- o(opts)
+ defaultOptions := []Option{
+ Size(unit.Dp(800), unit.Dp(600)),
+ Title("Gio"),
}
+ options = append(defaultOptions, options...)
+ var cnf Config
+ cnf.apply(unit.Metric{}, options)
w := &Window{
- in: make(chan event.Event),
- out: make(chan event.Event),
- ack: make(chan struct{}),
- invalidates: make(chan struct{}, 1),
- frames: make(chan *op.Ops),
- frameAck: make(chan struct{}),
- driverFuncs: make(chan func()),
- dead: make(chan struct{}),
+ out: make(chan event.Event),
+ immediateRedraws: make(chan struct{}, 0),
+ redraws: make(chan struct{}, 1),
+ scheduledRedraws: make(chan time.Time, 1),
+ frames: make(chan *op.Ops),
+ frameAck: make(chan struct{}),
+ driverFuncs: make(chan func(d driver), 1),
+ wakeups: make(chan struct{}, 1),
+ wakeupFuncs: make(chan func()),
+ dead: make(chan struct{}),
+ nocontext: cnf.CustomRenderer,
}
+ w.semantic.ids = make(map[router.SemanticID]router.SemanticNode)
w.callbacks.w = w
- go w.run(opts)
+ go w.run(options)
return w
}
@@ -111,141 +139,260 @@ func (w *Window) Events() <-chan event.Event {
return w.out
}
-// update updates the Window. Paint operations updates the
-// window contents, input operations declare input handlers,
-// and so on. The supplied operations list completely replaces
-// the window state from previous calls.
+// update updates the window contents, input operations declare input handlers,
+// and so on. The supplied operations list completely replaces the window state
+// from previous calls.
func (w *Window) update(frame *op.Ops) {
w.frames <- frame
<-w.frameAck
}
-func (w *Window) validateAndProcess(frameStart time.Time, size image.Point, sync bool, frame *op.Ops) error {
+func (w *Window) validateAndProcess(d driver, frameStart time.Time, size image.Point, sync bool, frame *op.Ops) error {
for {
- if w.loop != nil {
- if err := w.loop.Flush(); err != nil {
+ if w.gpu == nil && !w.nocontext {
+ var err error
+ if w.ctx == nil {
+ w.ctx, err = d.NewContext()
+ if err != nil {
+ return err
+ }
+ sync = true
+ }
+ }
+ if sync && w.ctx != nil {
+ if err := w.ctx.Refresh(); err != nil {
+ if errors.Is(err, errOutOfDate) {
+ // Surface couldn't be created for transient reasons. Skip
+ // this frame and wait for the next.
+ return nil
+ }
w.destroyGPU()
- if err == window.ErrDeviceLost {
+ if errors.Is(err, gpu.ErrDeviceLost) {
continue
}
return err
}
}
- if w.loop == nil {
- var ctx window.Context
- ctx, err := w.driver.NewContext()
- if err != nil {
+ if w.gpu == nil && !w.nocontext {
+ if err := w.ctx.Lock(); err != nil {
+ w.destroyGPU()
return err
}
- w.loop, err = newLoop(ctx)
+ gpu, err := gpu.New(w.ctx.API())
+ w.ctx.Unlock()
if err != nil {
- ctx.Release()
+ w.destroyGPU()
return err
}
+ w.gpu = gpu
}
- w.processFrame(frameStart, size, frame)
- if sync {
- if err := w.loop.Flush(); err != nil {
+ if w.gpu != nil {
+ if err := w.render(frame, size); err != nil {
+ if errors.Is(err, errOutOfDate) {
+ // GPU surface needs refreshing.
+ sync = true
+ continue
+ }
w.destroyGPU()
- if err == window.ErrDeviceLost {
+ if errors.Is(err, gpu.ErrDeviceLost) {
continue
}
return err
}
}
+ w.processFrame(d, frameStart, frame)
return nil
}
}
-func (w *Window) processFrame(frameStart time.Time, size image.Point, frame *op.Ops) {
- sync := w.loop.Draw(size, frame)
+func (w *Window) render(frame *op.Ops, viewport image.Point) error {
+ if err := w.ctx.Lock(); err != nil {
+ return err
+ }
+ defer w.ctx.Unlock()
+ if runtime.GOOS == "js" {
+ // Use transparent black when Gio is embedded, to allow mixing of Gio and
+ // foreign content below.
+ w.gpu.Clear(color.NRGBA{A: 0x00, R: 0x00, G: 0x00, B: 0x00})
+ } else {
+ w.gpu.Clear(color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff})
+ }
+ target, err := w.ctx.RenderTarget()
+ if err != nil {
+ return err
+ }
+ if err := w.gpu.Frame(frame, target, viewport); err != nil {
+ return err
+ }
+ return w.ctx.Present()
+}
+
+func (w *Window) processFrame(d driver, frameStart time.Time, frame *op.Ops) {
w.queue.q.Frame(frame)
+ for k := range w.semantic.ids {
+ delete(w.semantic.ids, k)
+ }
+ w.semantic.uptodate = false
switch w.queue.q.TextInputState() {
case router.TextInputOpen:
- w.driver.ShowTextInput(true)
+ d.ShowTextInput(true)
case router.TextInputClose:
- w.driver.ShowTextInput(false)
+ d.ShowTextInput(false)
+ }
+ if hint, ok := w.queue.q.TextInputHint(); ok {
+ d.SetInputHint(hint)
+ }
+ if txt, ok := w.queue.q.WriteClipboard(); ok {
+ w.WriteClipboard(txt)
+ }
+ if w.queue.q.ReadClipboard() {
+ w.ReadClipboard()
}
- if w.queue.q.Profiling() {
+ if w.queue.q.Profiling() && w.gpu != nil {
frameDur := time.Since(frameStart)
frameDur = frameDur.Truncate(100 * time.Microsecond)
q := 100 * time.Microsecond
- timings := fmt.Sprintf("tot:%7s %s", frameDur.Round(q), w.loop.Summary())
- w.queue.q.Add(profile.Event{Timings: timings})
+ timings := fmt.Sprintf("tot:%7s %s", frameDur.Round(q), w.gpu.Profile())
+ w.queue.q.Queue(profile.Event{Timings: timings})
}
if t, ok := w.queue.q.WakeupTime(); ok {
w.setNextFrame(t)
}
- w.updateAnimation()
- // Wait for the GPU goroutine to finish processing frame.
- <-sync
+ w.updateAnimation(d)
}
-// Invalidate the window such that a FrameEvent will be generated
-// immediately. If the window is inactive, the event is sent when the
-// window becomes active.
+// Invalidate the window such that a FrameEvent will be generated immediately.
+// If the window is inactive, the event is sent when the window becomes active.
+//
+// Note that Invalidate is intended for externally triggered updates, such as a
+// response from a network request. InvalidateOp is more efficient for animation
+// and similar internal updates.
+//
// Invalidate is safe for concurrent use.
func (w *Window) Invalidate() {
select {
- case w.invalidates <- struct{}{}:
+ case w.immediateRedraws <- struct{}{}:
+ return
default:
}
+ select {
+ case w.redraws <- struct{}{}:
+ w.wakeup()
+ default:
+ }
+}
+
+// Option applies the options to the window.
+func (w *Window) Option(opts ...Option) {
+ w.driverDefer(func(d driver) {
+ d.Configure(opts)
+ })
}
// ReadClipboard initiates a read of the clipboard in the form
-// of a system.ClipboardEvent. Multiple reads may be coalescedd
+// of a clipboard.Event. Multiple reads may be coalesced
// to a single event.
func (w *Window) ReadClipboard() {
- w.driverDo(func() {
- w.driver.ReadClipboard()
+ w.driverDefer(func(d driver) {
+ d.ReadClipboard()
})
}
// WriteClipboard writes a string to the clipboard.
func (w *Window) WriteClipboard(s string) {
- w.driverDo(func() {
- w.driver.WriteClipboard(s)
+ w.driverDefer(func(d driver) {
+ d.WriteClipboard(s)
+ })
+}
+
+// SetCursorName changes the current window cursor to name.
+func (w *Window) SetCursorName(name pointer.CursorName) {
+ w.driverDefer(func(d driver) {
+ d.SetCursor(name)
})
}
// Close the window. The window's event loop should exit when it receives
// system.DestroyEvent.
//
-// Currently, only macOS, Windows and X11 drivers implement this functionality,
+// Currently, only macOS, Windows, X11 and Wayland drivers implement this functionality,
// all others are stubbed.
func (w *Window) Close() {
- w.driverDo(func() {
- w.driver.Close()
+ w.driverDefer(func(d driver) {
+ d.Close()
})
}
-// driverDo calls f as soon as the window has a valid driver attached,
-// or does nothing if the window is destroyed while waiting.
-func (w *Window) driverDo(f func()) {
- go func() {
- select {
- case w.driverFuncs <- f:
- case <-w.dead:
- }
- }()
+// Maximize the window.
+// Note: only implemented on Windows, macOS and X11.
+func (w *Window) Maximize() {
+ w.driverDefer(func(d driver) {
+ d.Maximize()
+ })
}
-func (w *Window) updateAnimation() {
- animate := false
- if w.delayedDraw != nil {
- w.delayedDraw.Stop()
- w.delayedDraw = nil
+// Center the window.
+// Note: only implemented on Windows, macOS and X11.
+func (w *Window) Center() {
+ w.driverDefer(func(d driver) {
+ d.Center()
+ })
+}
+
+// Run f in the same thread as the native window event loop, and wait for f to
+// return or the window to close. Run is guaranteed not to deadlock if it is
+// invoked during the handling of a ViewEvent, system.FrameEvent,
+// system.StageEvent; call Run in a separate goroutine to avoid deadlock in all
+// other cases.
+//
+// Note that most programs should not call Run; configuring a Window with
+// CustomRenderer is a notable exception.
+func (w *Window) Run(f func()) {
+ done := make(chan struct{})
+ w.driverDefer(func(d driver) {
+ defer close(done)
+ f()
+ })
+ select {
+ case <-done:
+ case <-w.dead:
}
+}
+
+// driverDefer is like Run but can be run from any context. It doesn't wait
+// for f to return.
+func (w *Window) driverDefer(f func(d driver)) {
+ select {
+ case w.driverFuncs <- f:
+ w.wakeup()
+ case <-w.dead:
+ }
+}
+
+func (w *Window) updateAnimation(d driver) {
+ animate := false
if w.stage >= system.StageRunning && w.hasNextFrame {
if dt := time.Until(w.nextFrame); dt <= 0 {
animate = true
} else {
- w.delayedDraw = time.NewTimer(dt)
+ // Schedule redraw.
+ select {
+ case <-w.scheduledRedraws:
+ default:
+ }
+ w.scheduledRedraws <- w.nextFrame
}
}
if animate != w.animating {
w.animating = animate
- w.driver.SetAnimating(animate)
+ d.SetAnimating(animate)
+ }
+}
+
+func (w *Window) wakeup() {
+ select {
+ case w.wakeups <- struct{}{}:
+ default:
}
}
@@ -256,164 +403,289 @@ func (w *Window) setNextFrame(at time.Time) {
}
}
-func (c *callbacks) SetDriver(d window.Driver) {
- c.Event(driverEvent{d})
+func (c *callbacks) SetDriver(d driver) {
+ c.d = d
+ var wakeup func()
+ if d != nil {
+ wakeup = d.Wakeup
+ }
+ c.w.wakeupFuncs <- wakeup
}
func (c *callbacks) Event(e event.Event) {
- select {
- case c.w.in <- e:
- <-c.w.ack
- case <-c.w.dead:
+ if c.d == nil {
+ panic("event while no driver active")
}
+ c.w.processEvent(c.d, e)
+ c.w.updateState(c.d)
+}
+
+// SemanticRoot returns the ID of the semantic root.
+func (c *callbacks) SemanticRoot() router.SemanticID {
+ c.w.updateSemantics()
+ return c.w.semantic.root
}
-func (w *Window) waitAck() {
- // Send a dummy event; when it gets through we
- // know the application has processed the previous event.
- w.out <- ackEvent
+// LookupSemantic looks up a semantic node from an ID. The zero ID denotes the root.
+func (c *callbacks) LookupSemantic(semID router.SemanticID) (router.SemanticNode, bool) {
+ c.w.updateSemantics()
+ n, found := c.w.semantic.ids[semID]
+ return n, found
}
-// Prematurely destroy the window and wait for the native window
-// destroy event.
-func (w *Window) destroy(err error) {
- w.destroyGPU()
- // Ack the current event.
- w.ack <- struct{}{}
- w.out <- system.DestroyEvent{Err: err}
- close(w.dead)
- for e := range w.in {
- w.ack <- struct{}{}
- if _, ok := e.(system.DestroyEvent); ok {
+func (c *callbacks) AppendSemanticDiffs(diffs []router.SemanticID) []router.SemanticID {
+ c.w.updateSemantics()
+ if tree := c.w.semantic.prevTree; len(tree) > 0 {
+ c.w.collectSemanticDiffs(&diffs, c.w.semantic.prevTree[0])
+ }
+ return diffs
+}
+
+func (c *callbacks) SemanticAt(pos f32.Point) (router.SemanticID, bool) {
+ c.w.updateSemantics()
+ return c.w.queue.q.SemanticAt(pos)
+}
+
+func (w *Window) waitAck(d driver) {
+ for {
+ select {
+ case f := <-w.driverFuncs:
+ f(d)
+ case w.out <- ackEvent:
+ // A dummy event went through, so we know the application has processed the previous event.
return
+ case <-w.immediateRedraws:
+ // Invalidate was called during frame processing.
+ w.setNextFrame(time.Time{})
}
}
}
func (w *Window) destroyGPU() {
- if w.loop != nil {
- w.loop.Release()
- w.loop = nil
+ if w.gpu != nil {
+ w.ctx.Lock()
+ w.gpu.Release()
+ w.ctx.Unlock()
+ w.gpu = nil
+ }
+ if w.ctx != nil {
+ w.ctx.Release()
+ w.ctx = nil
}
}
// waitFrame waits for the client to either call FrameEvent.Frame
// or to continue event handling. It returns whether the client
// called Frame or not.
-func (w *Window) waitFrame() (*op.Ops, bool) {
+func (w *Window) waitFrame(d driver) (*op.Ops, bool) {
+ for {
+ select {
+ case f := <-w.driverFuncs:
+ f(d)
+ case frame := <-w.frames:
+ // The client called FrameEvent.Frame.
+ return frame, true
+ case w.out <- ackEvent:
+ // The client ignored FrameEvent and continued processing
+ // events.
+ return nil, false
+ case <-w.immediateRedraws:
+ // Invalidate was called during frame processing.
+ w.setNextFrame(time.Time{})
+ }
+ }
+}
+
+// updateSemantics refreshes the semantics tree, the id to node map and the ids of
+// updated nodes.
+func (w *Window) updateSemantics() {
+ if w.semantic.uptodate {
+ return
+ }
+ w.semantic.uptodate = true
+ w.semantic.prevTree, w.semantic.tree = w.semantic.tree, w.semantic.prevTree
+ w.semantic.tree = w.queue.q.AppendSemantics(w.semantic.tree[:0])
+ w.semantic.root = w.semantic.tree[0].ID
+ for _, n := range w.semantic.tree {
+ w.semantic.ids[n.ID] = n
+ }
+}
+
+// collectSemanticDiffs traverses the previous semantic tree, noting changed nodes.
+func (w *Window) collectSemanticDiffs(diffs *[]router.SemanticID, n router.SemanticNode) {
+ newNode, exists := w.semantic.ids[n.ID]
+ // Ignore deleted nodes, as their disappearance will be reported through an
+ // ancestor node.
+ if !exists {
+ return
+ }
+ diff := newNode.Desc != n.Desc || len(n.Children) != len(newNode.Children)
+ for i, ch := range n.Children {
+ if !diff {
+ newCh := newNode.Children[i]
+ diff = ch.ID != newCh.ID
+ }
+ w.collectSemanticDiffs(diffs, ch)
+ }
+ if diff {
+ *diffs = append(*diffs, n.ID)
+ }
+}
+
+func (w *Window) updateState(d driver) {
+ for {
+ select {
+ case f := <-w.driverFuncs:
+ f(d)
+ case <-w.redraws:
+ w.setNextFrame(time.Time{})
+ w.updateAnimation(d)
+ default:
+ return
+ }
+ }
+}
+
+func (w *Window) processEvent(d driver, e event.Event) {
select {
- case frame := <-w.frames:
- // The client called FrameEvent.Frame.
- return frame, true
- case w.out <- ackEvent:
- // The client ignored FrameEvent and continued processing
- // events.
- return nil, false
+ case <-w.dead:
+ return
+ default:
+ }
+ switch e2 := e.(type) {
+ case system.StageEvent:
+ if e2.Stage < system.StageRunning {
+ if w.gpu != nil {
+ w.ctx.Lock()
+ w.gpu.Release()
+ w.gpu = nil
+ w.ctx.Unlock()
+ }
+ }
+ w.stage = e2.Stage
+ w.updateAnimation(d)
+ w.out <- e
+ w.waitAck(d)
+ case frameEvent:
+ if e2.Size == (image.Point{}) {
+ panic(errors.New("internal error: zero-sized Draw"))
+ }
+ if w.stage < system.StageRunning {
+ // No drawing if not visible.
+ break
+ }
+ frameStart := time.Now()
+ w.hasNextFrame = false
+ e2.Frame = w.update
+ e2.Queue = &w.queue
+ w.out <- e2.FrameEvent
+ frame, gotFrame := w.waitFrame(d)
+ err := w.validateAndProcess(d, frameStart, e2.Size, e2.Sync, frame)
+ if gotFrame {
+ // We're done with frame, let the client continue.
+ w.frameAck <- struct{}{}
+ }
+ if err != nil {
+ w.destroyGPU()
+ w.out <- system.DestroyEvent{Err: err}
+ close(w.dead)
+ close(w.out)
+ break
+ }
+ w.updateCursor()
+ case *system.CommandEvent:
+ w.out <- e
+ w.waitAck(d)
+ case system.DestroyEvent:
+ w.destroyGPU()
+ w.out <- e2
+ close(w.dead)
+ close(w.out)
+ case ViewEvent:
+ w.out <- e2
+ w.waitAck(d)
+ case wakeupEvent:
+ case event.Event:
+ if w.queue.q.Queue(e2) {
+ w.setNextFrame(time.Time{})
+ w.updateAnimation(d)
+ }
+ w.updateCursor()
+ w.out <- e
}
}
-func (w *Window) run(opts *window.Options) {
- defer close(w.in)
- defer close(w.out)
- if err := window.NewWindow(&w.callbacks, opts); err != nil {
+func (w *Window) run(options []Option) {
+ if err := newWindow(&w.callbacks, options); err != nil {
w.out <- system.DestroyEvent{Err: err}
+ close(w.dead)
+ close(w.out)
return
}
+ var wakeup func()
+ var timer *time.Timer
for {
- var driverFuncs chan func()
- if w.driver != nil {
- driverFuncs = w.driverFuncs
- }
- var timer <-chan time.Time
- if w.delayedDraw != nil {
- timer = w.delayedDraw.C
+ var (
+ wakeups <-chan struct{}
+ timeC <-chan time.Time
+ )
+ if wakeup != nil {
+ wakeups = w.wakeups
+ if timer != nil {
+ timeC = timer.C
+ }
}
select {
- case <-timer:
- w.setNextFrame(time.Time{})
- w.updateAnimation()
- case <-w.invalidates:
- w.setNextFrame(time.Time{})
- w.updateAnimation()
- case f := <-driverFuncs:
- f()
- case e := <-w.in:
- switch e2 := e.(type) {
- case system.StageEvent:
- if w.loop != nil {
- if e2.Stage < system.StageRunning {
- w.destroyGPU()
- } else {
- w.loop.Refresh()
- }
- }
- w.stage = e2.Stage
- w.updateAnimation()
- w.out <- e
- w.waitAck()
- case window.FrameEvent:
- if e2.Size == (image.Point{}) {
- panic(errors.New("internal error: zero-sized Draw"))
- }
- if w.stage < system.StageRunning {
- // No drawing if not visible.
- break
- }
- frameStart := time.Now()
- w.hasNextFrame = false
- e2.Frame = w.update
- e2.Queue = &w.queue
- w.out <- e2.FrameEvent
- if w.loop != nil {
- if e2.Sync {
- w.loop.Refresh()
- }
- }
- frame, gotFrame := w.waitFrame()
- err := w.validateAndProcess(frameStart, e2.Size, e2.Sync, frame)
- if gotFrame {
- // We're done with frame, let the client continue.
- w.frameAck <- struct{}{}
- }
- if err != nil {
- w.destroyGPU()
- w.destroy(err)
- return
- }
- case *system.CommandEvent:
- w.out <- e
- w.waitAck()
- case driverEvent:
- w.driver = e2.driver
- case system.DestroyEvent:
- w.destroyGPU()
- w.out <- e2
- w.ack <- struct{}{}
- return
- case event.Event:
- if w.queue.q.Add(e2) {
- w.setNextFrame(time.Time{})
- w.updateAnimation()
- }
- w.out <- e
+ case t := <-w.scheduledRedraws:
+ if timer != nil {
+ timer.Stop()
}
- w.ack <- struct{}{}
+ timer = time.NewTimer(time.Until(t))
+ case <-w.dead:
+ return
+ case <-timeC:
+ select {
+ case w.redraws <- struct{}{}:
+ wakeup()
+ default:
+ }
+ case <-wakeups:
+ wakeup()
+ case wakeup = <-w.wakeupFuncs:
}
}
}
+func (w *Window) updateCursor() {
+ if c := w.queue.q.Cursor(); c != w.cursor {
+ w.cursor = c
+ w.SetCursorName(c)
+ }
+}
+
+// Raise requests that the platform bring this window to the top of all open windows.
+// Some platforms do not allow this except under certain circumstances, such as when
+// a window from the same application already has focus. If the platform does not
+// support it, this method will do nothing.
+func (w *Window) Raise() {
+ w.driverDefer(func(d driver) {
+ d.Raise()
+ })
+}
+
func (q *queue) Events(k event.Tag) []event.Event {
return q.q.Events(k)
}
// Title sets the title of the window.
func Title(t string) Option {
- return func(opts *window.Options) {
- opts.Title = t
+ return func(_ unit.Metric, cnf *Config) {
+ cnf.Title = t
}
}
-// Size sets the size of the window.
+// Size sets the size of the window. The option is ignored
+// in Fullscreen mode.
func Size(w, h unit.Value) Option {
if w.V <= 0 {
panic("width must be larger than or equal to 0")
@@ -421,9 +693,11 @@ func Size(w, h unit.Value) Option {
if h.V <= 0 {
panic("height must be larger than or equal to 0")
}
- return func(opts *window.Options) {
- opts.Width = w
- opts.Height = h
+ return func(m unit.Metric, cnf *Config) {
+ cnf.Size = image.Point{
+ X: m.Px(w),
+ Y: m.Px(h),
+ }
}
}
@@ -435,9 +709,11 @@ func MaxSize(w, h unit.Value) Option {
if h.V <= 0 {
panic("height must be larger than or equal to 0")
}
- return func(opts *window.Options) {
- opts.MaxWidth = w
- opts.MaxHeight = h
+ return func(m unit.Metric, cnf *Config) {
+ cnf.MaxSize = image.Point{
+ X: m.Px(w),
+ Y: m.Px(h),
+ }
}
}
@@ -449,10 +725,32 @@ func MinSize(w, h unit.Value) Option {
if h.V <= 0 {
panic("height must be larger than or equal to 0")
}
- return func(opts *window.Options) {
- opts.MinWidth = w
- opts.MinHeight = h
+ return func(m unit.Metric, cnf *Config) {
+ cnf.MinSize = image.Point{
+ X: m.Px(w),
+ Y: m.Px(h),
+ }
}
}
-func (driverEvent) ImplementsEvent() {}
+// StatusColor sets the color of the Android status bar.
+func StatusColor(color color.NRGBA) Option {
+ return func(_ unit.Metric, cnf *Config) {
+ cnf.StatusColor = color
+ }
+}
+
+// NavigationColor sets the color of the navigation bar on Android, or the address bar in browsers.
+func NavigationColor(color color.NRGBA) Option {
+ return func(_ unit.Metric, cnf *Config) {
+ cnf.NavigationColor = color
+ }
+}
+
+// CustomRenderer controls whether the window contents is
+// rendered by the client. If true, no GPU context is created.
+func CustomRenderer(custom bool) Option {
+ return func(_ unit.Metric, cnf *Config) {
+ cnf.CustomRenderer = custom
+ }
+}
diff --git a/vendor/gioui.org/cpu/LICENSE b/vendor/gioui.org/cpu/LICENSE
new file mode 100644
index 0000000..81f4733
--- /dev/null
+++ b/vendor/gioui.org/cpu/LICENSE
@@ -0,0 +1,63 @@
+This project is provided under the terms of the UNLICENSE or
+the MIT license denoted by the following SPDX identifier:
+
+SPDX-License-Identifier: Unlicense OR MIT
+
+You may use the project under the terms of either license.
+
+Both licenses are reproduced below.
+
+----
+The MIT License (MIT)
+
+Copyright (c) 2019 The Gio authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+---
+
+
+
+---
+The UNLICENSE
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org/>
+---
diff --git a/vendor/gioui.org/cpu/README.md b/vendor/gioui.org/cpu/README.md
new file mode 100644
index 0000000..4244fe0
--- /dev/null
+++ b/vendor/gioui.org/cpu/README.md
@@ -0,0 +1,25 @@
+# Compile and run compute programs on CPU
+
+This projects contains the compiler for turning Vulkan SPIR-V compute shaders
+into binaries for arm64, arm or amd64, using
+[SwiftShader](https://github.com/eliasnaur/swiftshader) with a few
+modifications. A runtime implemented in C and Go is available for running the
+resulting binaries.
+
+The primary use is to support a CPU-based rendering fallback for
+[Gio](https://gioui.org). In particular, the `gioui.org/shader/piet` package
+contains arm, arm64, amd64 binaries for
+[piet-gpu](https://github.com/linebender/piet-gpu).
+
+# Compiling and running shaders
+
+The `init.sh` script clones the modifed SwiftShader projects and builds it for
+64-bit and 32-bit. SwiftShader is not designed to cross-compile which is why a
+32-bit build is needed to compile shaders for arm.
+
+The `example/run.sh` script demonstrates compiling and running a simple compute
+program.
+
+## Issues and contributions
+
+See the [Gio contribution guide](https://gioui.org/doc/contribute).
diff --git a/vendor/gioui.org/cpu/abi.h b/vendor/gioui.org/cpu/abi.h
new file mode 100644
index 0000000..365d936
--- /dev/null
+++ b/vendor/gioui.org/cpu/abi.h
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#define ALIGN(bytes, type) type __attribute__((aligned(bytes)))
+
+typedef ALIGN(8, uint8_t) byte8[8];
+typedef ALIGN(8, uint16_t) word4[4];
+typedef ALIGN(4, uint32_t) dword;
+typedef ALIGN(16, uint32_t) dword4[4];
+typedef ALIGN(8, uint64_t) qword;
+typedef ALIGN(16, uint64_t) qword2[2];
+typedef ALIGN(16, unsigned int) uint4[4];
+typedef ALIGN(8, uint32_t) dword2[2];
+typedef ALIGN(8, unsigned short) ushort4[4];
+typedef ALIGN(16, float) float4[4];
+typedef ALIGN(16, int) int4[4];
+
+typedef unsigned short half;
+
+typedef unsigned char bool;
+
+enum {
+ MAX_BOUND_DESCRIPTOR_SETS = 4,
+ MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC = 8,
+ MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC = 4,
+ MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC =
+ MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC +
+ MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC,
+ MAX_PUSH_CONSTANT_SIZE = 128,
+
+ MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT = 256,
+
+ REQUIRED_MEMORY_ALIGNMENT = 16,
+
+ SIMD_WIDTH = 4,
+};
+
+struct image_descriptor {
+ ALIGN(16, void *ptr);
+ int width;
+ int height;
+ int depth;
+ int row_pitch_bytes;
+ int slice_pitch_bytes;
+ int sample_pitch_bytes;
+ int sample_count;
+ int size_in_bytes;
+
+ void *stencil_ptr;
+ int stencil_row_pitch_bytes;
+ int stencil_slice_pitch_bytes;
+ int stencil_sample_pitch_bytes;
+
+ // TODO: unused?
+ void *memoryOwner;
+};
+
+struct buffer_descriptor {
+ ALIGN(16, void *ptr);
+ int size_in_bytes;
+ int robustness_size;
+};
+
+struct program_data {
+ uint8_t *descriptor_sets[MAX_BOUND_DESCRIPTOR_SETS];
+ uint32_t descriptor_dynamic_offsets[MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC];
+ uint4 num_workgroups;
+ uint4 workgroup_size;
+ uint32_t invocations_per_subgroup;
+ uint32_t subgroups_per_workgroup;
+ uint32_t invocations_per_workgroup;
+ unsigned char push_constants[MAX_PUSH_CONSTANT_SIZE];
+ // Unused.
+ void *constants;
+};
+
+typedef int32_t yield_result;
+
+typedef void * coroutine;
+
+typedef coroutine (*routine_begin)(struct program_data *data,
+ int32_t workgroupX,
+ int32_t workgroupY,
+ int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount);
+
+typedef bool (*routine_await)(coroutine r, yield_result *res);
+
+typedef void (*routine_destroy)(coroutine r);
+
diff --git a/vendor/gioui.org/cpu/driver.go b/vendor/gioui.org/cpu/driver.go
new file mode 100644
index 0000000..d156e2b
--- /dev/null
+++ b/vendor/gioui.org/cpu/driver.go
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package cpu
+
+/*
+#cgo CFLAGS: -std=c11 -D_POSIX_C_SOURCE=200112L
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+*/
+import "C"
+import (
+ "unsafe"
+)
+
+type (
+ BufferDescriptor = C.struct_buffer_descriptor
+ ImageDescriptor = C.struct_image_descriptor
+ SamplerDescriptor = C.struct_sampler_descriptor
+
+ DispatchContext = C.struct_dispatch_context
+ ThreadContext = C.struct_thread_context
+ ProgramInfo = C.struct_program_info
+)
+
+const Supported = true
+
+func NewBuffer(size int) BufferDescriptor {
+ return C.alloc_buffer(C.size_t(size))
+}
+
+func (d *BufferDescriptor) Data() []byte {
+ return (*(*[1 << 30]byte)(d.ptr))[:d.size_in_bytes:d.size_in_bytes]
+}
+
+func (d *BufferDescriptor) Free() {
+ if d.ptr != nil {
+ C.free(d.ptr)
+ }
+ *d = BufferDescriptor{}
+}
+
+func NewImageRGBA(width, height int) ImageDescriptor {
+ return C.alloc_image_rgba(C.int(width), C.int(height))
+}
+
+func (d *ImageDescriptor) Data() []byte {
+ return (*(*[1 << 30]byte)(d.ptr))[:d.size_in_bytes:d.size_in_bytes]
+}
+
+func (d *ImageDescriptor) Free() {
+ if d.ptr != nil {
+ C.free(d.ptr)
+ }
+ *d = ImageDescriptor{}
+}
+
+func NewDispatchContext() *DispatchContext {
+ return C.alloc_dispatch_context()
+}
+
+func (c *DispatchContext) Free() {
+ C.free_dispatch_context(c)
+}
+
+func (c *DispatchContext) Prepare(numThreads int, prog *ProgramInfo, descSet unsafe.Pointer, x, y, z int) {
+ C.prepare_dispatch(c, C.int(numThreads), prog, (*C.uint8_t)(descSet), C.int(x), C.int(y), C.int(z))
+}
+
+func (c *DispatchContext) Dispatch(threadIdx int, ctx *ThreadContext) {
+ C.dispatch_thread(c, C.int(threadIdx), ctx)
+}
+
+func NewThreadContext() *ThreadContext {
+ return C.alloc_thread_context()
+}
+
+func (c *ThreadContext) Free() {
+ C.free_thread_context(c)
+}
diff --git a/vendor/gioui.org/cpu/driver_nosupport.go b/vendor/gioui.org/cpu/driver_nosupport.go
new file mode 100644
index 0000000..3a118f2
--- /dev/null
+++ b/vendor/gioui.org/cpu/driver_nosupport.go
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package cpu
+
+import "unsafe"
+
+type (
+ BufferDescriptor struct{}
+ ImageDescriptor struct{}
+ SamplerDescriptor struct{}
+
+ DispatchContext struct{}
+ ThreadContext struct{}
+ ProgramInfo struct{}
+)
+
+const Supported = false
+
+func NewBuffer(size int) BufferDescriptor {
+ panic("unsupported")
+}
+
+func (d *BufferDescriptor) Data() []byte {
+ panic("unsupported")
+}
+
+func (d *BufferDescriptor) Free() {
+}
+
+func NewImageRGBA(width, height int) ImageDescriptor {
+ panic("unsupported")
+}
+
+func (d *ImageDescriptor) Data() []byte {
+ panic("unsupported")
+}
+
+func (d *ImageDescriptor) Free() {
+}
+
+func NewDispatchContext() *DispatchContext {
+ panic("unsupported")
+}
+
+func (c *DispatchContext) Free() {
+}
+
+func (c *DispatchContext) Prepare(numThreads int, prog *ProgramInfo, descSet unsafe.Pointer, x, y, z int) {
+ panic("unsupported")
+}
+
+func (c *DispatchContext) Dispatch(threadIdx int, ctx *ThreadContext) {
+ panic("unsupported")
+}
+
+func NewThreadContext() *ThreadContext {
+ panic("unsupported")
+}
+
+func (c *ThreadContext) Free() {
+}
diff --git a/vendor/gioui.org/cpu/embed.go b/vendor/gioui.org/cpu/embed.go
new file mode 100644
index 0000000..9d3b944
--- /dev/null
+++ b/vendor/gioui.org/cpu/embed.go
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package cpu
+
+import _ "embed"
+
+//go:embed abi.h
+var ABIH []byte
+
+//go:embed runtime.h
+var RuntimeH []byte
diff --git a/vendor/gioui.org/cpu/go.mod b/vendor/gioui.org/cpu/go.mod
new file mode 100644
index 0000000..46709a0
--- /dev/null
+++ b/vendor/gioui.org/cpu/go.mod
@@ -0,0 +1,3 @@
+module gioui.org/cpu
+
+go 1.17
diff --git a/vendor/gioui.org/cpu/go.sum b/vendor/gioui.org/cpu/go.sum
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/gioui.org/cpu/go.sum
diff --git a/vendor/gioui.org/cpu/init.sh b/vendor/gioui.org/cpu/init.sh
new file mode 100644
index 0000000..f0f0a9c
--- /dev/null
+++ b/vendor/gioui.org/cpu/init.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: Unlicense OR MIT
+
+set -e
+
+cd ~/.cache
+git clone https://github.com/eliasnaur/swiftshader
+cd swiftshader
+
+# 32-bit build
+cp -a build build.32bit
+cd build.32bit
+CXX=clang++ CC=clang CFLAGS=-m32 CXXFLAGS=-m32 cmake -DREACTOR_EMIT_ASM_FILE=true -DSWIFTSHADER_BUILD_PVR=false -DSWIFTSHADER_BUILD_TESTS=false -DSWIFTSHADER_BUILD_GLESv2=false -DSWIFTSHADER_BUILD_EGL=false -DSWIFTSHADER_BUILD_ANGLE=false ..
+cmake --build . --parallel 4
+cd ..
+
+# 64-bit build
+cp -a build build.64bit
+cd build.64bit
+CXX=clang++ CC=clang cmake -DREACTOR_EMIT_ASM_FILE=true -DSWIFTSHADER_BUILD_PVR=false -DSWIFTSHADER_BUILD_TESTS=false -DSWIFTSHADER_BUILD_GLESv2=false -DSWIFTSHADER_BUILD_EGL=false -DSWIFTSHADER_BUILD_ANGLE=false ..
+cmake --build . --parallel 4
+cd ..
diff --git a/vendor/gioui.org/cpu/runtime.c b/vendor/gioui.org/cpu/runtime.c
new file mode 100644
index 0000000..f7f6108
--- /dev/null
+++ b/vendor/gioui.org/cpu/runtime.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "abi.h"
+#include "runtime.h"
+#include "_cgo_export.h"
+
+// coroutines is a FIFO queue of coroutines implemented as a circular
+// buffer.
+struct coroutines {
+ coroutine *routines;
+ // start and end indexes into routines.
+ unsigned int start;
+ unsigned int end;
+ // cap is the capacity of routines.
+ unsigned int cap;
+};
+
+struct dispatch_context {
+ // descriptor_set is the aligned storage for the descriptor set.
+ void *descriptor_set;
+ int desc_set_size;
+
+ int nthreads;
+ bool has_cbarriers;
+ size_t memory_size;
+ // Program entrypoints.
+ routine_begin begin;
+ routine_await await;
+ routine_destroy destroy;
+
+ struct program_data data;
+};
+
+struct thread_context {
+ struct coroutines routines;
+ size_t memory_size;
+ uint8_t *memory;
+};
+
+static void *malloc_align(size_t alignment, size_t size) {
+ void *ptr;
+ int ret = posix_memalign(&ptr, alignment, size);
+ assert(ret == 0);
+ return ptr;
+}
+
+static void coroutines_dump(struct coroutines *routines) {
+ fprintf(stderr, "s: %d e: %d c: %d [", routines->start, routines->end, routines->cap);
+ unsigned int i = routines->start;
+ while (i != routines->end) {
+ fprintf(stderr, "%p,", routines->routines[routines->start]);
+ i = (i + 1)%routines->cap;
+ }
+ fprintf(stderr, "]\n");
+}
+
+static void coroutines_push(struct coroutines *routines, coroutine r) {
+ unsigned int next = routines->end + 1;
+ if (next >= routines->cap) {
+ next = 0;
+ }
+ if (next == routines->start) {
+ unsigned int newcap = routines->cap*2;
+ if (newcap < 10) {
+ newcap = 10;
+ }
+ routines->routines = realloc(routines->routines, newcap*sizeof(coroutine));
+ // Move elements wrapped around the old cap to the newly allocated space.
+ if (routines->end < routines->start) {
+ unsigned int nelems = routines->end;
+ unsigned int max = newcap - routines->cap;
+ // We doubled the space above, so we can assume enough room.
+ assert(nelems <= max);
+ memmove(&routines->routines[routines->cap], &routines->routines[0], nelems*sizeof(coroutine));
+ routines->end += routines->cap;
+ }
+ routines->cap = newcap;
+ next = (routines->end + 1)%routines->cap;
+ }
+ routines->routines[routines->end] = r;
+ routines->end = next;
+}
+
+static bool coroutines_pop(struct coroutines *routines, coroutine *r) {
+ if (routines->start == routines->end) {
+ return 0;
+ }
+ *r = routines->routines[routines->start];
+ routines->start = (routines->start + 1)%routines->cap;
+ return 1;
+}
+
+static void coroutines_free(struct coroutines *routines) {
+ if (routines->routines != NULL) {
+ free(routines->routines);
+ }
+ struct coroutines clr = { 0 }; *routines = clr;
+}
+
+struct dispatch_context *alloc_dispatch_context(void) {
+ struct dispatch_context *c = malloc(sizeof(*c));
+ assert(c != NULL);
+ struct dispatch_context clr = { 0 }; *c = clr;
+ return c;
+}
+
+void free_dispatch_context(struct dispatch_context *c) {
+ if (c->descriptor_set != NULL) {
+ free(c->descriptor_set);
+ c->descriptor_set = NULL;
+ }
+}
+
+struct thread_context *alloc_thread_context(void) {
+ struct thread_context *c = malloc(sizeof(*c));
+ assert(c != NULL);
+ struct thread_context clr = { 0 }; *c = clr;
+ return c;
+}
+
+void free_thread_context(struct thread_context *c) {
+ if (c->memory != NULL) {
+ free(c->memory);
+ }
+ coroutines_free(&c->routines);
+ struct thread_context clr = { 0 }; *c = clr;
+}
+
+struct buffer_descriptor alloc_buffer(size_t size) {
+ void *buf = malloc_align(MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT, size);
+ struct buffer_descriptor desc = {
+ .ptr = buf,
+ .size_in_bytes = size,
+ .robustness_size = size,
+ };
+ return desc;
+}
+
+struct image_descriptor alloc_image_rgba(int width, int height) {
+ size_t size = width*height*4;
+ size = (size + 16 - 1)&~(16 - 1);
+ void *storage = malloc_align(REQUIRED_MEMORY_ALIGNMENT, size);
+ struct image_descriptor desc = { 0 };
+ desc.ptr = storage;
+ desc.width = width;
+ desc.height = height;
+ desc.depth = 1;
+ desc.row_pitch_bytes = width*4;
+ desc.slice_pitch_bytes = size;
+ desc.sample_pitch_bytes = size;
+ desc.sample_count = 1;
+ desc.size_in_bytes = size;
+ return desc;
+}
+
+void prepare_dispatch(struct dispatch_context *ctx, int nthreads, struct program_info *info, uint8_t *desc_set, int ngroupx, int ngroupy, int ngroupz) {
+ if (ctx->desc_set_size < info->desc_set_size) {
+ if (ctx->descriptor_set != NULL) {
+ free(ctx->descriptor_set);
+ }
+ ctx->descriptor_set = malloc_align(16, info->desc_set_size);
+ ctx->desc_set_size = info->desc_set_size;
+ }
+ memcpy(ctx->descriptor_set, desc_set, info->desc_set_size);
+
+ int invocations_per_subgroup = SIMD_WIDTH;
+ int invocations_per_workgroup = info->workgroup_size_x * info->workgroup_size_y * info->workgroup_size_z;
+ int subgroups_per_workgroup = (invocations_per_workgroup + invocations_per_subgroup - 1) / invocations_per_subgroup;
+
+ ctx->has_cbarriers = info->has_cbarriers;
+ ctx->begin = info->begin;
+ ctx->await = info->await;
+ ctx->destroy = info->destroy;
+ ctx->nthreads = nthreads;
+ ctx->memory_size = info->min_memory_size;
+
+ ctx->data.workgroup_size[0] = info->workgroup_size_x;
+ ctx->data.workgroup_size[1] = info->workgroup_size_y;
+ ctx->data.workgroup_size[2] = info->workgroup_size_z;
+ ctx->data.num_workgroups[0] = ngroupx;
+ ctx->data.num_workgroups[1] = ngroupy;
+ ctx->data.num_workgroups[2] = ngroupz;
+ ctx->data.invocations_per_subgroup = invocations_per_subgroup;
+ ctx->data.invocations_per_workgroup = invocations_per_workgroup;
+ ctx->data.subgroups_per_workgroup = subgroups_per_workgroup;
+ ctx->data.descriptor_sets[0] = ctx->descriptor_set;
+}
+
+void dispatch_thread(struct dispatch_context *ctx, int thread_idx, struct thread_context *thread) {
+ if (thread->memory_size < ctx->memory_size) {
+ if (thread->memory != NULL) {
+ free(thread->memory);
+ }
+ // SwiftShader doesn't seem to align shared memory. However, better safe
+ // than subtle errors. Note that the program info generator pads
+ // memory_size to ensure space for alignment.
+ thread->memory = malloc_align(16, ctx->memory_size);
+ thread->memory_size = ctx->memory_size;
+ }
+ uint8_t *memory = thread->memory;
+
+ struct program_data *data = &ctx->data;
+
+ int sx = data->num_workgroups[0];
+ int sy = data->num_workgroups[1];
+ int sz = data->num_workgroups[2];
+ int ngroups = sx * sy * sz;
+
+ for (int i = thread_idx; i < ngroups; i += ctx->nthreads) {
+ int group_id = i;
+ int z = group_id / (sx * sy);
+ group_id -= z * sx * sy;
+ int y = group_id / sx;
+ group_id -= y * sx;
+ int x = group_id;
+ if (ctx->has_cbarriers) {
+ for (int subgroup = 0; subgroup < data->subgroups_per_workgroup; subgroup++) {
+ coroutine r = ctx->begin(data, x, y, z, memory, subgroup, 1);
+ coroutines_push(&thread->routines, r);
+ }
+ } else {
+ coroutine r = ctx->begin(data, x, y, z, memory, 0, data->subgroups_per_workgroup);
+ coroutines_push(&thread->routines, r);
+ }
+ coroutine r;
+ while (coroutines_pop(&thread->routines, &r)) {
+ yield_result res;
+ if (ctx->await(r, &res)) {
+ coroutines_push(&thread->routines, r);
+ } else {
+ ctx->destroy(r);
+ }
+ }
+ }
+}
diff --git a/vendor/gioui.org/cpu/runtime.h b/vendor/gioui.org/cpu/runtime.h
new file mode 100644
index 0000000..cfae912
--- /dev/null
+++ b/vendor/gioui.org/cpu/runtime.h
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#define ATTR_HIDDEN __attribute__ ((visibility ("hidden")))
+
+// program_info contains constant parameters for a program.
+struct program_info {
+ // MinMemorySize is the minimum size of memory passed to dispatch.
+ size_t min_memory_size;
+ // has_cbarriers is 1 when the program contains control barriers.
+ bool has_cbarriers;
+ // desc_set_size is the size of the first descriptor set for the program.
+ size_t desc_set_size;
+ int workgroup_size_x;
+ int workgroup_size_y;
+ int workgroup_size_z;
+ // Program entrypoints.
+ routine_begin begin;
+ routine_await await;
+ routine_destroy destroy;
+};
+
+// dispatch_context contains the information a program dispatch.
+struct dispatch_context;
+
+// thread_context contains the working memory of a batch. It may be
+// reused, but not concurrently.
+struct thread_context;
+
+extern struct buffer_descriptor alloc_buffer(size_t size) ATTR_HIDDEN;
+extern struct image_descriptor alloc_image_rgba(int width, int height) ATTR_HIDDEN;
+
+extern struct dispatch_context *alloc_dispatch_context(void) ATTR_HIDDEN;
+
+extern void free_dispatch_context(struct dispatch_context *c) ATTR_HIDDEN;
+
+extern struct thread_context *alloc_thread_context(void) ATTR_HIDDEN;
+
+extern void free_thread_context(struct thread_context *c) ATTR_HIDDEN;
+
+// prepare_dispatch initializes ctx to run a dispatch of a program distributed
+// among nthreads threads.
+extern void prepare_dispatch(struct dispatch_context *ctx, int nthreads, struct program_info *info, uint8_t *desc_set, int ngroupx, int ngroupy, int ngroupz) ATTR_HIDDEN;
+
+// dispatch_batch executes a dispatch batch.
+extern void dispatch_thread(struct dispatch_context *ctx, int thread_idx, struct thread_context *thread) ATTR_HIDDEN;
diff --git a/vendor/gioui.org/f32/affine.go b/vendor/gioui.org/f32/affine.go
index 44e914b..667f7e9 100644
--- a/vendor/gioui.org/f32/affine.go
+++ b/vendor/gioui.org/f32/affine.go
@@ -3,6 +3,7 @@
package f32
import (
+ "fmt"
"math"
)
@@ -11,7 +12,7 @@ import (
type Affine2D struct {
// in order to make the zero value of Affine2D represent the identity
// transform we store it with the identity matrix subtracted, that is
- // if the actual transformaiton matrix is:
+ // if the actual transformation matrix is:
// [sx, hx, ox]
// [hy, sy, oy]
// [ 0, 0, 1]
@@ -24,9 +25,9 @@ type Affine2D struct {
// in row major order. The rows are: [sx, hx, ox], [hy, sy, oy], [0, 0, 1].
func NewAffine2D(sx, hx, ox, hy, sy, oy float32) Affine2D {
return Affine2D{
- a: sx, b: hx, c: ox,
- d: hy, e: sy, f: oy,
- }.encode()
+ a: sx - 1, b: hx, c: ox,
+ d: hy, e: sy - 1, f: oy,
+ }
}
// Offset the transformation.
@@ -69,14 +70,13 @@ func (a Affine2D) Shear(origin Point, radiansX, radiansY float32) Affine2D {
// Mul returns A*B.
func (A Affine2D) Mul(B Affine2D) (r Affine2D) {
- A, B = A.decode(), B.decode()
- r.a = A.a*B.a + A.b*B.d
- r.b = A.a*B.b + A.b*B.e
- r.c = A.a*B.c + A.b*B.f + A.c
- r.d = A.d*B.a + A.e*B.d
- r.e = A.d*B.b + A.e*B.e
- r.f = A.d*B.c + A.e*B.f + A.f
- return r.encode()
+ r.a = (A.a+1)*(B.a+1) + A.b*B.d - 1
+ r.b = (A.a+1)*B.b + A.b*(B.e+1)
+ r.c = (A.a+1)*B.c + A.b*B.f + A.c
+ r.d = A.d*(B.a+1) + (A.e+1)*B.d
+ r.e = A.d*B.b + (A.e+1)*(B.e+1) - 1
+ r.f = A.d*B.c + (A.e+1)*B.f + A.f
+ return r
}
// Invert the transformation. Note that if the matrix is close to singular
@@ -85,70 +85,59 @@ func (a Affine2D) Invert() Affine2D {
if a.a == 0 && a.b == 0 && a.d == 0 && a.e == 0 {
return Affine2D{a: 0, b: 0, c: -a.c, d: 0, e: 0, f: -a.f}
}
- a = a.decode()
+ a.a += 1
+ a.e += 1
det := a.a*a.e - a.b*a.d
a.a, a.e = a.e/det, a.a/det
a.b, a.d = -a.b/det, -a.d/det
temp := a.c
a.c = -a.a*a.c - a.b*a.f
a.f = -a.d*temp - a.e*a.f
- return a.encode()
+ a.a -= 1
+ a.e -= 1
+ return a
}
// Transform p by returning a*p.
func (a Affine2D) Transform(p Point) Point {
- a = a.decode()
return Point{
- X: p.X*a.a + p.Y*a.b + a.c,
- Y: p.X*a.d + p.Y*a.e + a.f,
+ X: p.X*(a.a+1) + p.Y*a.b + a.c,
+ Y: p.X*a.d + p.Y*(a.e+1) + a.f,
}
}
// Elems returns the matrix elements of the transform in row-major order. The
// rows are: [sx, hx, ox], [hy, sy, oy], [0, 0, 1].
func (a Affine2D) Elems() (sx, hx, ox, hy, sy, oy float32) {
- a = a.decode()
- return a.a, a.b, a.c, a.d, a.e, a.f
-}
-
-func (a Affine2D) encode() Affine2D {
- // since we store with identity matrix subtracted
- a.a -= 1
- a.e -= 1
- return a
-}
-
-func (a Affine2D) decode() Affine2D {
- // since we store with identity matrix subtracted
- a.a += 1
- a.e += 1
- return a
+ return a.a + 1, a.b, a.c, a.d, a.e + 1, a.f
}
func (a Affine2D) scale(factor Point) Affine2D {
- a = a.decode()
return Affine2D{
- a.a * factor.X, a.b * factor.X, a.c * factor.X,
- a.d * factor.Y, a.e * factor.Y, a.f * factor.Y,
- }.encode()
+ (a.a+1)*factor.X - 1, a.b * factor.X, a.c * factor.X,
+ a.d * factor.Y, (a.e+1)*factor.Y - 1, a.f * factor.Y,
+ }
}
func (a Affine2D) rotate(radians float32) Affine2D {
sin, cos := math.Sincos(float64(radians))
s, c := float32(sin), float32(cos)
- a = a.decode()
return Affine2D{
- a.a*c - a.d*s, a.b*c - a.e*s, a.c*c - a.f*s,
- a.a*s + a.d*c, a.b*s + a.e*c, a.c*s + a.f*c,
- }.encode()
+ (a.a+1)*c - a.d*s - 1, a.b*c - (a.e+1)*s, a.c*c - a.f*s,
+ (a.a+1)*s + a.d*c, a.b*s + (a.e+1)*c - 1, a.c*s + a.f*c,
+ }
}
func (a Affine2D) shear(radiansX, radiansY float32) Affine2D {
tx := float32(math.Tan(float64(radiansX)))
ty := float32(math.Tan(float64(radiansY)))
- a = a.decode()
return Affine2D{
- a.a + a.d*tx, a.b + a.e*tx, a.c + a.f*tx,
- a.a*ty + a.d, a.b*ty + a.e, a.f*ty + a.f,
- }.encode()
+ (a.a + 1) + a.d*tx - 1, a.b + (a.e+1)*tx, a.c + a.f*tx,
+ (a.a+1)*ty + a.d, a.b*ty + (a.e + 1) - 1, a.f*ty + a.f,
+ }
+}
+
+func (a Affine2D) String() string {
+ sx, hx, ox, hy, sy, oy := a.Elems()
+ return fmt.Sprintf("[[%f %f %f] [%f %f %f]]", sx, hx, ox, hy, sy, oy)
}
diff --git a/vendor/gioui.org/f32/f32.go b/vendor/gioui.org/f32/f32.go
index 798e3bf..54882cb 100644
--- a/vendor/gioui.org/f32/f32.go
+++ b/vendor/gioui.org/f32/f32.go
@@ -66,6 +66,17 @@ func (p Point) Mul(s float32) Point {
return Point{X: p.X * s, Y: p.Y * s}
}
+// Div returns the vector p/s.
+func (p Point) Div(s float32) Point {
+ return Point{X: p.X / s, Y: p.Y / s}
+}
+
+// In reports whether p is in r.
+func (p Point) In(r Rectangle) bool {
+ return r.Min.X <= p.X && p.X < r.Max.X &&
+ r.Min.Y <= p.Y && p.Y < r.Max.Y
+}
+
// Size returns r's width and height.
func (r Rectangle) Size() Point {
return Point{X: r.Dx(), Y: r.Dy()}
@@ -95,11 +106,20 @@ func (r Rectangle) Intersect(s Rectangle) Rectangle {
if r.Max.Y > s.Max.Y {
r.Max.Y = s.Max.Y
}
+ if r.Empty() {
+ return Rectangle{}
+ }
return r
}
// Union returns the union of r and s.
func (r Rectangle) Union(s Rectangle) Rectangle {
+ if r.Empty() {
+ return s
+ }
+ if s.Empty() {
+ return r
+ }
if r.Min.X > s.Min.X {
r.Min.X = s.Min.X
}
diff --git a/vendor/gioui.org/font/opentype/opentype.go b/vendor/gioui.org/font/opentype/opentype.go
index 27afea4..1f42330 100644
--- a/vendor/gioui.org/font/opentype/opentype.go
+++ b/vendor/gioui.org/font/opentype/opentype.go
@@ -5,29 +5,32 @@
package opentype
import (
+ "bytes"
"io"
-
"unicode"
"unicode/utf8"
+ "golang.org/x/image/font"
+ "golang.org/x/image/font/sfnt"
+ "golang.org/x/image/math/fixed"
+
"gioui.org/f32"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/text"
- "golang.org/x/image/font"
- "golang.org/x/image/font/sfnt"
- "golang.org/x/image/math/fixed"
)
-// Font implements text.Face.
+// Font implements text.Face. Its methods are safe to use
+// concurrently.
type Font struct {
font *sfnt.Font
- buf sfnt.Buffer
}
-// Collection is a collection of one or more fonts.
+// Collection is a collection of one or more fonts. When used as a text.Face,
+// each rune will be assigned a glyph from the first font in the collection
+// that supports it.
type Collection struct {
- coll *sfnt.Collection
+ fonts []*opentype
}
type opentype struct {
@@ -35,6 +38,13 @@ type opentype struct {
Hinting font.Hinting
}
+// a glyph represents a rune and its advance according to a Font.
+// TODO: remove this type and work on io.Readers directly.
+type glyph struct {
+ Rune rune
+ Advance fixed.Int26_6
+}
+
// NewFont parses an SFNT font, such as TTF or OTF data, from a []byte
// data source.
func Parse(src []byte) (*Font, error) {
@@ -55,7 +65,7 @@ func ParseCollection(src []byte) (*Collection, error) {
if err != nil {
return nil, err
}
- return &Collection{c}, nil
+ return newCollectionFrom(c)
}
// ParseCollectionReaderAt parses an SFNT collection, such as TTC or OTC data,
@@ -68,21 +78,35 @@ func ParseCollectionReaderAt(src io.ReaderAt) (*Collection, error) {
if err != nil {
return nil, err
}
- return &Collection{c}, nil
+ return newCollectionFrom(c)
+}
+
+func newCollectionFrom(coll *sfnt.Collection) (*Collection, error) {
+ fonts := make([]*opentype, coll.NumFonts())
+ for i := range fonts {
+ fnt, err := coll.Font(i)
+ if err != nil {
+ return nil, err
+ }
+ fonts[i] = &opentype{
+ Font: fnt,
+ Hinting: font.HintingFull,
+ }
+ }
+ return &Collection{fonts: fonts}, nil
}
// NumFonts returns the number of fonts in the collection.
func (c *Collection) NumFonts() int {
- return c.coll.NumFonts()
+ return len(c.fonts)
}
// Font returns the i'th font in the collection.
func (c *Collection) Font(i int) (*Font, error) {
- fnt, err := c.coll.Font(i)
- if err != nil {
- return nil, err
+ if i < 0 || len(c.fonts) <= i {
+ return nil, sfnt.ErrNotFound
}
- return &Font{font: fnt}, nil
+ return &Font{font: c.fonts[i].Font}, nil
}
func (f *Font) Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]text.Line, error) {
@@ -90,31 +114,69 @@ func (f *Font) Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]text.L
if err != nil {
return nil, err
}
- return layoutText(&f.buf, ppem, maxWidth, &opentype{Font: f.font, Hinting: font.HintingFull}, glyphs)
+ fonts := []*opentype{{Font: f.font, Hinting: font.HintingFull}}
+ var buf sfnt.Buffer
+ return layoutText(&buf, ppem, maxWidth, fonts, glyphs)
}
-func (f *Font) Shape(ppem fixed.Int26_6, str []text.Glyph) op.CallOp {
- return textPath(&f.buf, ppem, &opentype{Font: f.font, Hinting: font.HintingFull}, str)
+func (f *Font) Shape(ppem fixed.Int26_6, str text.Layout) clip.PathSpec {
+ var buf sfnt.Buffer
+ return textPath(&buf, ppem, []*opentype{{Font: f.font, Hinting: font.HintingFull}}, str)
}
func (f *Font) Metrics(ppem fixed.Int26_6) font.Metrics {
o := &opentype{Font: f.font, Hinting: font.HintingFull}
- return o.Metrics(&f.buf, ppem)
+ var buf sfnt.Buffer
+ return o.Metrics(&buf, ppem)
+}
+
+func (c *Collection) Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]text.Line, error) {
+ glyphs, err := readGlyphs(txt)
+ if err != nil {
+ return nil, err
+ }
+ var buf sfnt.Buffer
+ return layoutText(&buf, ppem, maxWidth, c.fonts, glyphs)
+}
+
+func (c *Collection) Shape(ppem fixed.Int26_6, str text.Layout) clip.PathSpec {
+ var buf sfnt.Buffer
+ return textPath(&buf, ppem, c.fonts, str)
+}
+
+func fontForGlyph(buf *sfnt.Buffer, fonts []*opentype, r rune) *opentype {
+ if len(fonts) < 1 {
+ return nil
+ }
+ for _, f := range fonts {
+ if f.HasGlyph(buf, r) {
+ return f
+ }
+ }
+ return fonts[0] // Use replacement character from the first font if necessary
}
-func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype, glyphs []text.Glyph) ([]text.Line, error) {
- m := f.Metrics(sbuf, ppem)
- lineTmpl := text.Line{
- Ascent: m.Ascent,
+func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, fonts []*opentype, glyphs []glyph) ([]text.Line, error) {
+ var lines []text.Line
+ var nextLine text.Line
+ updateBounds := func(f *opentype) {
+ m := f.Metrics(sbuf, ppem)
+ if m.Ascent > nextLine.Ascent {
+ nextLine.Ascent = m.Ascent
+ }
// m.Height is equal to m.Ascent + m.Descent + linegap.
// Compute the descent including the linegap.
- Descent: m.Height - m.Ascent,
- Bounds: f.Bounds(sbuf, ppem),
+ descent := m.Height - m.Ascent
+ if descent > nextLine.Descent {
+ nextLine.Descent = descent
+ }
+ b := f.Bounds(sbuf, ppem)
+ nextLine.Bounds = nextLine.Bounds.Union(b)
}
- var lines []text.Line
maxDotX := fixed.I(maxWidth)
type state struct {
r rune
+ f *opentype
adv fixed.Int26_6
x fixed.Int26_6
idx int
@@ -123,26 +185,33 @@ func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype
}
var prev, word state
endLine := func() {
- line := lineTmpl
- line.Layout = glyphs[:prev.idx:prev.idx]
- line.Len = prev.len
- line.Width = prev.x + prev.adv
- line.Bounds.Max.X += prev.x
- lines = append(lines, line)
+ if prev.f == nil && len(fonts) > 0 {
+ prev.f = fonts[0]
+ }
+ updateBounds(prev.f)
+ nextLine.Layout = toLayout(glyphs[:prev.idx:prev.idx])
+ nextLine.Width = prev.x + prev.adv
+ nextLine.Bounds.Max.X += prev.x
+ lines = append(lines, nextLine)
glyphs = glyphs[prev.idx:]
+ nextLine = text.Line{}
prev = state{}
word = state{}
}
for prev.idx < len(glyphs) {
g := &glyphs[prev.idx]
- a, valid := f.GlyphAdvance(sbuf, ppem, g.Rune)
next := state{
- r: g.Rune,
- idx: prev.idx + 1,
- len: prev.len + utf8.RuneLen(g.Rune),
- x: prev.x + prev.adv,
- adv: a,
- valid: valid,
+ r: g.Rune,
+ f: fontForGlyph(sbuf, fonts, g.Rune),
+ idx: prev.idx + 1,
+ len: prev.len + utf8.RuneLen(g.Rune),
+ x: prev.x + prev.adv,
+ }
+ if next.f != nil {
+ if next.f != prev.f {
+ updateBounds(next.f)
+ }
+ next.adv, next.valid = next.f.GlyphAdvance(sbuf, ppem, g.Rune)
}
if g.Rune == '\n' {
// The newline is zero width; use the previous
@@ -153,8 +222,8 @@ func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype
continue
}
var k fixed.Int26_6
- if prev.valid {
- k = f.Kern(sbuf, ppem, prev.r, next.r)
+ if prev.valid && next.f != nil {
+ k = next.f.Kern(sbuf, ppem, prev.r, next.r)
}
// Break the line if we're out of space.
if prev.idx > 0 && next.x+next.adv+k > maxDotX {
@@ -181,16 +250,30 @@ func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype
return lines, nil
}
-func textPath(buf *sfnt.Buffer, ppem fixed.Int26_6, f *opentype, str []text.Glyph) op.CallOp {
+// toLayout converts a slice of glyphs to a text.Layout.
+func toLayout(glyphs []glyph) text.Layout {
+ var buf bytes.Buffer
+ advs := make([]fixed.Int26_6, len(glyphs))
+ for i, g := range glyphs {
+ buf.WriteRune(g.Rune)
+ advs[i] = glyphs[i].Advance
+ }
+ return text.Layout{Text: buf.String(), Advances: advs}
+}
+
+func textPath(buf *sfnt.Buffer, ppem fixed.Int26_6, fonts []*opentype, str text.Layout) clip.PathSpec {
var lastPos f32.Point
var builder clip.Path
- ops := new(op.Ops)
- m := op.Record(ops)
var x fixed.Int26_6
- builder.Begin(ops)
- for _, g := range str {
- if !unicode.IsSpace(g.Rune) {
- segs, ok := f.LoadGlyph(buf, ppem, g.Rune)
+ builder.Begin(new(op.Ops))
+ rune := 0
+ for _, r := range str.Text {
+ if !unicode.IsSpace(r) {
+ f := fontForGlyph(buf, fonts, r)
+ if f == nil {
+ continue
+ }
+ segs, ok := f.LoadGlyph(buf, ppem, r)
if !ok {
continue
}
@@ -236,14 +319,14 @@ func textPath(buf *sfnt.Buffer, ppem fixed.Int26_6, f *opentype, str []text.Glyp
}
lastPos = lastPos.Add(lastArg)
}
- x += g.Advance
+ x += str.Advances[rune]
+ rune++
}
- builder.End().Add(ops)
- return m.Stop()
+ return builder.End()
}
-func readGlyphs(r io.Reader) ([]text.Glyph, error) {
- var glyphs []text.Glyph
+func readGlyphs(r io.Reader) ([]glyph, error) {
+ var glyphs []glyph
buf := make([]byte, 0, 1024)
for {
n, err := r.Read(buf[len(buf):cap(buf)])
@@ -257,7 +340,7 @@ func readGlyphs(r io.Reader) ([]text.Glyph, error) {
for i < lim {
c, s := utf8.DecodeRune(buf[i:])
i += s
- glyphs = append(glyphs, text.Glyph{Rune: c})
+ glyphs = append(glyphs, glyph{Rune: c})
}
n = copy(buf, buf[i:])
buf = buf[:n]
@@ -271,6 +354,11 @@ func readGlyphs(r io.Reader) ([]text.Glyph, error) {
return glyphs, nil
}
+func (f *opentype) HasGlyph(buf *sfnt.Buffer, r rune) bool {
+ g, err := f.Font.GlyphIndex(buf, r)
+ return g != 0 && err == nil
+}
+
func (f *opentype) GlyphAdvance(buf *sfnt.Buffer, ppem fixed.Int26_6, r rune) (advance fixed.Int26_6, ok bool) {
g, err := f.Font.GlyphIndex(buf, r)
if err != nil {
diff --git a/vendor/gioui.org/gesture/gesture.go b/vendor/gioui.org/gesture/gesture.go
index 36ecd80..2d63963 100644
--- a/vendor/gioui.org/gesture/gesture.go
+++ b/vendor/gioui.org/gesture/gesture.go
@@ -10,6 +10,7 @@ and scrolling.
package gesture
import (
+ "image"
"math"
"runtime"
"time"
@@ -26,11 +27,49 @@ import (
// The duration is somewhat arbitrary.
const doubleClickDuration = 200 * time.Millisecond
+// Hover detects the hover gesture for a pointer area.
+type Hover struct {
+ // entered tracks whether the pointer is inside the gesture.
+ entered bool
+ // pid is the pointer.ID.
+ pid pointer.ID
+}
+
+// Add the gesture to detect hovering over the current pointer area.
+func (h *Hover) Add(ops *op.Ops) {
+ pointer.InputOp{
+ Tag: h,
+ Types: pointer.Enter | pointer.Leave,
+ }.Add(ops)
+}
+
+// Hovered returns whether a pointer is inside the area.
+func (h *Hover) Hovered(q event.Queue) bool {
+ for _, ev := range q.Events(h) {
+ e, ok := ev.(pointer.Event)
+ if !ok {
+ continue
+ }
+ switch e.Type {
+ case pointer.Leave:
+ if h.entered && h.pid == e.PointerID {
+ h.entered = false
+ }
+ case pointer.Enter:
+ if !h.entered {
+ h.pid = e.PointerID
+ }
+ if h.pid == e.PointerID {
+ h.entered = true
+ }
+ }
+ }
+ return h.entered
+}
+
// Click detects click gestures in the form
// of ClickEvents.
type Click struct {
- // state tracks the gesture state.
- state ClickState
// clickedAt is the timestamp at which
// the last click occurred.
clickedAt time.Duration
@@ -45,8 +84,6 @@ type Click struct {
pid pointer.ID
}
-type ClickState uint8
-
// ClickEvent represent a click action, either a
// TypePress for the beginning of a click or a
// TypeClick for a completed click.
@@ -65,6 +102,7 @@ type ClickType uint8
// Drag detects drag gestures in the form of pointer.Drag events.
type Drag struct {
dragging bool
+ pressed bool
pid pointer.ID
start f32.Point
grab bool
@@ -92,6 +130,7 @@ type Axis uint8
const (
Horizontal Axis = iota
Vertical
+ Both
)
const (
@@ -109,7 +148,7 @@ const (
const (
// StateIdle is the default scroll state.
StateIdle ScrollState = iota
- // StateDrag is reported during drag gestures.
+ // StateDragging is reported during drag gestures.
StateDragging
// StateFlinging is reported when a fling is
// in progress.
@@ -120,14 +159,23 @@ var touchSlop = unit.Dp(3)
// Add the handler to the operation list to receive click events.
func (c *Click) Add(ops *op.Ops) {
- op := pointer.InputOp{
+ pointer.InputOp{
Tag: c,
Types: pointer.Press | pointer.Release | pointer.Enter | pointer.Leave,
- }
- op.Add(ops)
+ }.Add(ops)
+}
+
+// Hovered returns whether a pointer is inside the area.
+func (c *Click) Hovered() bool {
+ return c.entered
}
-// Events returns the next click event, if any.
+// Pressed returns whether a pointer is pressing.
+func (c *Click) Pressed() bool {
+ return c.pressed
+}
+
+// Events returns the next click events, if any.
func (c *Click) Events(q event.Queue) []ClickEvent {
var events []ClickEvent
for _, evt := range q.Events(c) {
@@ -163,7 +211,7 @@ func (c *Click) Events(q event.Queue) []ClickEvent {
if c.pressed {
break
}
- if e.Source == pointer.Mouse && e.Buttons != pointer.ButtonLeft {
+ if e.Source == pointer.Mouse && e.Buttons != pointer.ButtonPrimary {
break
}
if !c.entered {
@@ -193,12 +241,17 @@ func (c *Click) Events(q event.Queue) []ClickEvent {
return events
}
+func (ClickEvent) ImplementsEvent() {}
+
// Add the handler to the operation list to receive scroll events.
-func (s *Scroll) Add(ops *op.Ops) {
+// The bounds variable refers to the scrolling boundaries
+// as defined in io/pointer.InputOp.
+func (s *Scroll) Add(ops *op.Ops, bounds image.Rectangle) {
oph := pointer.InputOp{
- Tag: s,
- Grab: s.grab,
- Types: pointer.Press | pointer.Drag | pointer.Release | pointer.Scroll,
+ Tag: s,
+ Grab: s.grab,
+ Types: pointer.Press | pointer.Drag | pointer.Release | pointer.Scroll,
+ ScrollBounds: bounds,
}
oph.Add(ops)
if s.flinger.Active() {
@@ -254,9 +307,6 @@ func (s *Scroll) Scroll(cfg unit.Metric, q event.Queue, t time.Time, axis Axis)
s.dragging = false
s.grab = false
case pointer.Scroll:
- if e.Priority < pointer.Foremost {
- continue
- }
switch s.axis {
case Horizontal:
s.scroll += e.Scroll.X
@@ -311,12 +361,11 @@ func (s *Scroll) State() ScrollState {
// Add the handler to the operation list to receive drag events.
func (d *Drag) Add(ops *op.Ops) {
- op := pointer.InputOp{
+ pointer.InputOp{
Tag: d,
Grab: d.grab,
Types: pointer.Press | pointer.Drag | pointer.Release,
- }
- op.Add(ops)
+ }.Add(ops)
}
// Events returns the next drag events, if any.
@@ -330,9 +379,10 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event
switch e.Type {
case pointer.Press:
- if !(e.Buttons == pointer.ButtonLeft || e.Source == pointer.Touch) {
+ if !(e.Buttons == pointer.ButtonPrimary || e.Source == pointer.Touch) {
continue
}
+ d.pressed = true
if d.dragging {
continue
}
@@ -348,6 +398,8 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event
e.Position.Y = d.start.Y
case Vertical:
e.Position.X = d.start.X
+ case Both:
+ // Do nothing
}
if e.Priority < pointer.Grabbed {
diff := e.Position.Sub(d.start)
@@ -357,6 +409,7 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event
}
}
case pointer.Release, pointer.Cancel:
+ d.pressed = false
if !d.dragging || e.PointerID != d.pid {
continue
}
@@ -370,6 +423,12 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event
return events
}
+// Dragging reports whether it is currently in use.
+func (d *Drag) Dragging() bool { return d.dragging }
+
+// Pressed returns whether a pointer is pressing.
+func (d *Drag) Pressed() bool { return d.pressed }
+
func (a Axis) String() string {
switch a {
case Horizontal:
diff --git a/vendor/gioui.org/gpu/api.go b/vendor/gioui.org/gpu/api.go
new file mode 100644
index 0000000..d347e5a
--- /dev/null
+++ b/vendor/gioui.org/gpu/api.go
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gpu
+
+import "gioui.org/gpu/internal/driver"
+
+// An API carries the necessary GPU API specific resources to create a Device.
+// There is an API type for each supported GPU API such as OpenGL and Direct3D.
+type API = driver.API
+
+// A RenderTarget denotes the destination framebuffer for a frame.
+type RenderTarget = driver.RenderTarget
+
+// OpenGLRenderTarget is a render target suitable for the OpenGL backend.
+type OpenGLRenderTarget = driver.OpenGLRenderTarget
+
+// Direct3D11RenderTarget is a render target suitable for the Direct3D 11 backend.
+type Direct3D11RenderTarget = driver.Direct3D11RenderTarget
+
+// MetalRenderTarget is a render target suitable for the Metal backend.
+type MetalRenderTarget = driver.MetalRenderTarget
+
+// VulkanRenderTarget is a render target suitable for the Vulkan backend.
+type VulkanRenderTarget = driver.VulkanRenderTarget
+
+// OpenGL denotes the OpenGL or OpenGL ES API.
+type OpenGL = driver.OpenGL
+
+// Direct3D11 denotes the Direct3D API.
+type Direct3D11 = driver.Direct3D11
+
+// Metal denotes the Apple Metal API.
+type Metal = driver.Metal
+
+// Vulkan denotes the Vulkan API.
+type Vulkan = driver.Vulkan
+
+// ErrDeviceLost is returned from GPU operations when the underlying GPU device
+// is lost and should be recreated.
+var ErrDeviceLost = driver.ErrDeviceLost
diff --git a/vendor/gioui.org/gpu/backend/backend.go b/vendor/gioui.org/gpu/backend/backend.go
deleted file mode 100644
index 871eea4..0000000
--- a/vendor/gioui.org/gpu/backend/backend.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package backend
-
-import (
- "image"
- "time"
-)
-
-// Device represents the abstraction of underlying GPU
-// APIs such as OpenGL, Direct3D useful for rendering Gio
-// operations.
-type Device interface {
- BeginFrame()
- EndFrame()
- Caps() Caps
- NewTimer() Timer
- // IsContinuousTime reports whether all timer measurements
- // are valid at the point of call.
- IsTimeContinuous() bool
- NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error)
- CurrentFramebuffer() Framebuffer
- NewFramebuffer(tex Texture, depthBits int) (Framebuffer, error)
- NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
- NewBuffer(typ BufferBinding, size int) (Buffer, error)
- NewProgram(vertexShader, fragmentShader ShaderSources) (Program, error)
- NewInputLayout(vertexShader ShaderSources, layout []InputDesc) (InputLayout, error)
-
- DepthFunc(f DepthFunc)
- ClearDepth(d float32)
- Clear(r, g, b, a float32)
- Viewport(x, y, width, height int)
- DrawArrays(mode DrawMode, off, count int)
- DrawElements(mode DrawMode, off, count int)
- SetBlend(enable bool)
- SetDepthTest(enable bool)
- DepthMask(mask bool)
- BlendFunc(sfactor, dfactor BlendFactor)
-
- BindInputLayout(i InputLayout)
- BindProgram(p Program)
- BindFramebuffer(f Framebuffer)
- BindTexture(unit int, t Texture)
- BindVertexBuffer(b Buffer, stride, offset int)
- BindIndexBuffer(b Buffer)
-}
-
-type ShaderSources struct {
- GLSL100ES string
- GLSL300ES string
- GLSL130 string
- GLSL150 string
- HLSL []byte
- Uniforms UniformsReflection
- Inputs []InputLocation
- Textures []TextureBinding
-}
-
-type UniformsReflection struct {
- Blocks []UniformBlock
- Locations []UniformLocation
- Size int
-}
-
-type TextureBinding struct {
- Name string
- Binding int
-}
-
-type UniformBlock struct {
- Name string
- Binding int
-}
-
-type UniformLocation struct {
- Name string
- Type DataType
- Size int
- Offset int
-}
-
-type InputLocation struct {
- // For GLSL.
- Name string
- Location int
- // For HLSL.
- Semantic string
- SemanticIndex int
-
- Type DataType
- Size int
-}
-
-// InputDesc describes a vertex attribute as laid out in a Buffer.
-type InputDesc struct {
- Type DataType
- Size int
-
- Offset int
-}
-
-// InputLayout is the backend specific representation of the mapping
-// between Buffers and shader attributes.
-type InputLayout interface {
- Release()
-}
-
-type BlendFactor uint8
-
-type DrawMode uint8
-
-type TextureFilter uint8
-type TextureFormat uint8
-
-type BufferBinding uint8
-
-type DataType uint8
-
-type DepthFunc uint8
-
-type Features uint
-
-type Caps struct {
- Features Features
- MaxTextureSize int
-}
-
-type Program interface {
- Release()
- SetVertexUniforms(buf Buffer)
- SetFragmentUniforms(buf Buffer)
-}
-
-type Buffer interface {
- Release()
- Upload(data []byte)
-}
-
-type Framebuffer interface {
- Invalidate()
- Release()
- ReadPixels(src image.Rectangle, pixels []byte) error
-}
-
-type Timer interface {
- Begin()
- End()
- Duration() (time.Duration, bool)
- Release()
-}
-
-type Texture interface {
- Upload(img *image.RGBA)
- Release()
-}
-
-const (
- DepthFuncGreater DepthFunc = iota
- DepthFuncGreaterEqual
-)
-
-const (
- DataTypeFloat DataType = iota
- DataTypeInt
- DataTypeShort
-)
-
-const (
- BufferBindingIndices BufferBinding = 1 << iota
- BufferBindingVertices
- BufferBindingUniforms
- BufferBindingTexture
- BufferBindingFramebuffer
-)
-
-const (
- TextureFormatSRGB TextureFormat = iota
- TextureFormatFloat
-)
-
-const (
- FilterNearest TextureFilter = iota
- FilterLinear
-)
-
-const (
- FeatureTimers Features = iota
-)
-
-const (
- DrawModeTriangleStrip DrawMode = iota
- DrawModeTriangles
-)
-
-const (
- BlendFactorOne BlendFactor = iota
- BlendFactorOneMinusSrcAlpha
- BlendFactorZero
- BlendFactorDstColor
-)
-
-func (f Features) Has(feats Features) bool {
- return f&feats == feats
-}
diff --git a/vendor/gioui.org/gpu/caches.go b/vendor/gioui.org/gpu/caches.go
index cefba83..c6a71bc 100644
--- a/vendor/gioui.org/gpu/caches.go
+++ b/vendor/gioui.org/gpu/caches.go
@@ -6,7 +6,6 @@ import (
"fmt"
"gioui.org/f32"
- "gioui.org/internal/ops"
)
type resourceCache struct {
@@ -19,17 +18,18 @@ type resourceCache struct {
// since benchmarking showed them as a bottleneck.
type opCache struct {
// store the index + 1 in cache this key is stored in
- index map[ops.Key]int
+ index map[opKey]int
// list of indexes in cache that are free and can be used
freelist []int
cache []opCacheValue
}
type opCacheValue struct {
- data pathData
+ data pathData
+
bounds f32.Rectangle
// the fields below are handled by opCache
- key ops.Key
+ key opKey
keep bool
}
@@ -70,7 +70,8 @@ func (r *resourceCache) frame() {
}
func (r *resourceCache) release() {
- for _, v := range r.newRes {
+ r.frame()
+ for _, v := range r.res {
v.release()
}
r.newRes = nil
@@ -79,13 +80,13 @@ func (r *resourceCache) release() {
func newOpCache() *opCache {
return &opCache{
- index: make(map[ops.Key]int),
+ index: make(map[opKey]int),
freelist: make([]int, 0),
cache: make([]opCacheValue, 0),
}
}
-func (r *opCache) get(key ops.Key) (o opCacheValue, exist bool) {
+func (r *opCache) get(key opKey) (o opCacheValue, exist bool) {
v := r.index[key]
if v == 0 {
return
@@ -94,7 +95,7 @@ func (r *opCache) get(key ops.Key) (o opCacheValue, exist bool) {
return r.cache[v-1], true
}
-func (r *opCache) put(key ops.Key, val opCacheValue) {
+func (r *opCache) put(key opKey, val opCacheValue) {
v := r.index[key]
val.keep = true
val.key = key
diff --git a/vendor/gioui.org/gpu/clip.go b/vendor/gioui.org/gpu/clip.go
index 51022a0..8ae25b5 100644
--- a/vendor/gioui.org/gpu/clip.go
+++ b/vendor/gioui.org/gpu/clip.go
@@ -2,11 +2,10 @@ package gpu
import (
"gioui.org/f32"
- "gioui.org/internal/ops"
+ "gioui.org/internal/stroke"
)
type quadSplitter struct {
- verts []byte
bounds f32.Rectangle
contour uint32
d *drawOps
@@ -48,7 +47,7 @@ func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) {
encodeQuadTo(data, qs.contour, from, ctrl, to)
}
-func (qs *quadSplitter) splitAndEncode(quad ops.Quad) {
+func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) {
cbnd := f32.Rectangle{
Min: quad.From,
Max: quad.To,
@@ -94,5 +93,22 @@ func (qs *quadSplitter) splitAndEncode(quad ops.Quad) {
}
}
- qs.bounds = qs.bounds.Union(cbnd)
+ qs.bounds = unionRect(qs.bounds, cbnd)
+}
+
+// Union is like f32.Rectangle.Union but ignores empty rectangles.
+func unionRect(r, s f32.Rectangle) f32.Rectangle {
+ if r.Min.X > s.Min.X {
+ r.Min.X = s.Min.X
+ }
+ if r.Min.Y > s.Min.Y {
+ r.Min.Y = s.Min.Y
+ }
+ if r.Max.X < s.Max.X {
+ r.Max.X = s.Max.X
+ }
+ if r.Max.Y < s.Max.Y {
+ r.Max.Y = s.Max.Y
+ }
+ return r
}
diff --git a/vendor/gioui.org/gpu/compute.go b/vendor/gioui.org/gpu/compute.go
new file mode 100644
index 0000000..625658c
--- /dev/null
+++ b/vendor/gioui.org/gpu/compute.go
@@ -0,0 +1,2219 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gpu
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "hash/maphash"
+ "image"
+ "image/color"
+ "image/draw"
+ "image/png"
+ "io/ioutil"
+ "math"
+ "math/bits"
+ "runtime"
+ "sort"
+ "time"
+ "unsafe"
+
+ "gioui.org/cpu"
+ "gioui.org/f32"
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/byteslice"
+ "gioui.org/internal/f32color"
+ "gioui.org/internal/ops"
+ "gioui.org/internal/scene"
+ "gioui.org/layout"
+ "gioui.org/op"
+ "gioui.org/shader"
+ "gioui.org/shader/gio"
+ "gioui.org/shader/piet"
+)
+
+type compute struct {
+ ctx driver.Device
+
+ collector collector
+ enc encoder
+ texOps []textureOp
+ viewport image.Point
+ maxTextureDim int
+ srgb bool
+ atlases []*textureAtlas
+ frameCount uint
+ moves []atlasMove
+
+ programs struct {
+ elements computeProgram
+ tileAlloc computeProgram
+ pathCoarse computeProgram
+ backdrop computeProgram
+ binning computeProgram
+ coarse computeProgram
+ kernel4 computeProgram
+ }
+ buffers struct {
+ config sizedBuffer
+ scene sizedBuffer
+ state sizedBuffer
+ memory sizedBuffer
+ }
+ output struct {
+ blitPipeline driver.Pipeline
+
+ buffer sizedBuffer
+
+ uniforms *copyUniforms
+ uniBuf driver.Buffer
+
+ layerVertices []layerVertex
+ descriptors *piet.Kernel4DescriptorSetLayout
+
+ nullMaterials driver.Texture
+ }
+ // imgAllocs maps imageOpData.handles to allocs.
+ imgAllocs map[interface{}]*atlasAlloc
+ // materials contains the pre-processed materials (transformed images for
+ // now, gradients etc. later) packed in a texture atlas. The atlas is used
+ // as source in kernel4.
+ materials struct {
+ // allocs maps texture ops the their atlases and FillImage offsets.
+ allocs map[textureKey]materialAlloc
+
+ pipeline driver.Pipeline
+ buffer sizedBuffer
+ quads []materialVertex
+ uniforms struct {
+ u *materialUniforms
+ buf driver.Buffer
+ }
+ }
+ timers struct {
+ profile string
+ t *timers
+ compact *timer
+ render *timer
+ blit *timer
+ }
+
+ // CPU fallback fields.
+ useCPU bool
+ dispatcher *dispatcher
+
+ // The following fields hold scratch space to avoid garbage.
+ zeroSlice []byte
+ memHeader *memoryHeader
+ conf *config
+}
+
+type materialAlloc struct {
+ alloc *atlasAlloc
+ offset image.Point
+}
+
+type layer struct {
+ rect image.Rectangle
+ alloc *atlasAlloc
+ ops []paintOp
+ materials *textureAtlas
+}
+
+type allocQuery struct {
+ atlas *textureAtlas
+ size image.Point
+ empty bool
+ format driver.TextureFormat
+ bindings driver.BufferBinding
+ nocompact bool
+}
+
+type atlasAlloc struct {
+ atlas *textureAtlas
+ rect image.Rectangle
+ cpu bool
+ dead bool
+ frameCount uint
+}
+
+type atlasMove struct {
+ src *textureAtlas
+ dstPos image.Point
+ srcRect image.Rectangle
+ cpu bool
+}
+
+type textureAtlas struct {
+ image driver.Texture
+ format driver.TextureFormat
+ bindings driver.BufferBinding
+ hasCPU bool
+ cpuImage cpu.ImageDescriptor
+ size image.Point
+ allocs []*atlasAlloc
+ packer packer
+ realized bool
+ lastFrame uint
+ compact bool
+}
+
+type copyUniforms struct {
+ scale [2]float32
+ pos [2]float32
+ uvScale [2]float32
+ _ [8]byte // Pad to 16 bytes.
+}
+
+type materialUniforms struct {
+ scale [2]float32
+ pos [2]float32
+ emulatesRGB float32
+ _ [12]byte // Pad to 16 bytes
+}
+
+type collector struct {
+ hasher maphash.Hash
+ profile bool
+ reader ops.Reader
+ states []f32.Affine2D
+ clear bool
+ clearColor f32color.RGBA
+ clipStates []clipState
+ order []hashIndex
+ transStack []transEntry
+ prevFrame opsCollector
+ frame opsCollector
+}
+
+type transEntry struct {
+ t f32.Affine2D
+ relTrans f32.Affine2D
+}
+
+type hashIndex struct {
+ index int
+ hash uint64
+}
+
+type opsCollector struct {
+ paths []byte
+ clipCmds []clipCmd
+ ops []paintOp
+ layers []layer
+}
+
+type paintOp struct {
+ clipStack []clipCmd
+ offset image.Point
+ state paintKey
+ intersect f32.Rectangle
+ hash uint64
+ layer int
+ texOpIdx int
+}
+
+// clipCmd describes a clipping command ready to be used for the compute
+// pipeline.
+type clipCmd struct {
+ // union of the bounds of the operations that are clipped.
+ union f32.Rectangle
+ state clipKey
+ path []byte
+ pathKey ops.Key
+ absBounds f32.Rectangle
+}
+
+type encoderState struct {
+ relTrans f32.Affine2D
+ clip *clipState
+
+ paintKey
+}
+
+// clipKey completely describes a clip operation (along with its path) and is appropriate
+// for hashing and equality checks.
+type clipKey struct {
+ bounds f32.Rectangle
+ strokeWidth float32
+ relTrans f32.Affine2D
+ pathHash uint64
+}
+
+// paintKey completely defines a paint operation. It is suitable for hashing and
+// equality checks.
+type paintKey struct {
+ t f32.Affine2D
+ matType materialType
+ // Current paint.ImageOp
+ image imageOpData
+ // Current paint.ColorOp, if any.
+ color color.NRGBA
+
+ // Current paint.LinearGradientOp.
+ stop1 f32.Point
+ stop2 f32.Point
+ color1 color.NRGBA
+ color2 color.NRGBA
+}
+
+type clipState struct {
+ absBounds f32.Rectangle
+ parent *clipState
+ path []byte
+ pathKey ops.Key
+ intersect f32.Rectangle
+ push bool
+
+ clipKey
+}
+
+type layerVertex struct {
+ posX, posY float32
+ u, v float32
+}
+
+// materialVertex describes a vertex of a quad used to render a transformed
+// material.
+type materialVertex struct {
+ posX, posY float32
+ u, v float32
+}
+
+// textureKey identifies textureOp.
+type textureKey struct {
+ handle interface{}
+ transform f32.Affine2D
+ bounds image.Rectangle
+}
+
+// textureOp represents an paintOp that requires texture space.
+type textureOp struct {
+ img imageOpData
+ key textureKey
+ // offset is the integer offset separated from key.transform to increase cache hit rate.
+ off image.Point
+ // matAlloc is the atlas placement for material.
+ matAlloc materialAlloc
+ // imgAlloc is the atlas placement for the source image
+ imgAlloc *atlasAlloc
+}
+
+type encoder struct {
+ scene []scene.Command
+ npath int
+ npathseg int
+ ntrans int
+}
+
+type encodeState struct {
+ trans f32.Affine2D
+ clip f32.Rectangle
+}
+
+// sizedBuffer holds a GPU buffer, or its equivalent CPU memory.
+type sizedBuffer struct {
+ size int
+ buffer driver.Buffer
+ // cpuBuf is initialized when useCPU is true.
+ cpuBuf cpu.BufferDescriptor
+}
+
+// computeProgram holds a compute program, or its equivalent CPU implementation.
+type computeProgram struct {
+ prog driver.Program
+
+ // CPU fields.
+ progInfo *cpu.ProgramInfo
+ descriptors unsafe.Pointer
+ buffers []*cpu.BufferDescriptor
+}
+
+// config matches Config in setup.h
+type config struct {
+ n_elements uint32 // paths
+ n_pathseg uint32
+ width_in_tiles uint32
+ height_in_tiles uint32
+ tile_alloc memAlloc
+ bin_alloc memAlloc
+ ptcl_alloc memAlloc
+ pathseg_alloc memAlloc
+ anno_alloc memAlloc
+ trans_alloc memAlloc
+}
+
+// memAlloc matches Alloc in mem.h
+type memAlloc struct {
+ offset uint32
+ //size uint32
+}
+
+// memoryHeader matches the header of Memory in mem.h.
+type memoryHeader struct {
+ mem_offset uint32
+ mem_error uint32
+}
+
+// rect is a oriented rectangle.
+type rectangle [4]f32.Point
+
+const (
+ layersBindings = driver.BufferBindingShaderStorageWrite | driver.BufferBindingTexture
+ materialsBindings = driver.BufferBindingFramebuffer | driver.BufferBindingShaderStorageRead
+ // Materials and layers can share texture storage if their bindings match.
+ combinedBindings = layersBindings | materialsBindings
+)
+
+// GPU structure sizes and constants.
+const (
+ tileWidthPx = 32
+ tileHeightPx = 32
+ ptclInitialAlloc = 1024
+ kernel4OutputUnit = 2
+ kernel4AtlasUnit = 3
+
+ pathSize = 12
+ binSize = 8
+ pathsegSize = 52
+ annoSize = 32
+ transSize = 24
+ stateSize = 60
+ stateStride = 4 + 2*stateSize
+)
+
+// mem.h constants.
+const (
+ memNoError = 0 // NO_ERROR
+ memMallocFailed = 1 // ERR_MALLOC_FAILED
+)
+
+func newCompute(ctx driver.Device) (*compute, error) {
+ caps := ctx.Caps()
+ maxDim := caps.MaxTextureSize
+ // Large atlas textures cause artifacts due to precision loss in
+ // shaders.
+ if cap := 8192; maxDim > cap {
+ maxDim = cap
+ }
+ // The compute programs can only span 128x64 tiles. Limit to 64 for now, and leave the
+ // complexity of a rectangular limit for later.
+ if computeCap := 4096; maxDim > computeCap {
+ maxDim = computeCap
+ }
+ g := &compute{
+ ctx: ctx,
+ maxTextureDim: maxDim,
+ srgb: caps.Features.Has(driver.FeatureSRGB),
+ conf: new(config),
+ memHeader: new(memoryHeader),
+ }
+ null, err := ctx.NewTexture(driver.TextureFormatRGBA8, 1, 1, driver.FilterNearest, driver.FilterNearest, driver.BufferBindingShaderStorageRead)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.output.nullMaterials = null
+ shaders := []struct {
+ prog *computeProgram
+ src shader.Sources
+ info *cpu.ProgramInfo
+ }{
+ {&g.programs.elements, piet.Shader_elements_comp, piet.ElementsProgramInfo},
+ {&g.programs.tileAlloc, piet.Shader_tile_alloc_comp, piet.Tile_allocProgramInfo},
+ {&g.programs.pathCoarse, piet.Shader_path_coarse_comp, piet.Path_coarseProgramInfo},
+ {&g.programs.backdrop, piet.Shader_backdrop_comp, piet.BackdropProgramInfo},
+ {&g.programs.binning, piet.Shader_binning_comp, piet.BinningProgramInfo},
+ {&g.programs.coarse, piet.Shader_coarse_comp, piet.CoarseProgramInfo},
+ {&g.programs.kernel4, piet.Shader_kernel4_comp, piet.Kernel4ProgramInfo},
+ }
+ if !caps.Features.Has(driver.FeatureCompute) {
+ if !cpu.Supported {
+ return nil, errors.New("gpu: missing support for compute programs")
+ }
+ g.useCPU = true
+ }
+ if g.useCPU {
+ g.dispatcher = newDispatcher(runtime.NumCPU())
+ }
+
+ copyVert, copyFrag, err := newShaders(ctx, gio.Shader_copy_vert, gio.Shader_copy_frag)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ defer copyVert.Release()
+ defer copyFrag.Release()
+ pipe, err := ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: copyVert,
+ FragmentShader: copyFrag,
+ VertexLayout: driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: int(unsafe.Sizeof(g.output.layerVertices[0])),
+ },
+ PixelFormat: driver.TextureFormatOutput,
+ BlendDesc: driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorOne,
+ DstFactor: driver.BlendFactorOneMinusSrcAlpha,
+ },
+ Topology: driver.TopologyTriangles,
+ })
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.output.blitPipeline = pipe
+ g.output.uniforms = new(copyUniforms)
+
+ buf, err := ctx.NewBuffer(driver.BufferBindingUniforms, int(unsafe.Sizeof(*g.output.uniforms)))
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.output.uniBuf = buf
+
+ materialVert, materialFrag, err := newShaders(ctx, gio.Shader_material_vert, gio.Shader_material_frag)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ defer materialVert.Release()
+ defer materialFrag.Release()
+ pipe, err = ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: materialVert,
+ FragmentShader: materialFrag,
+ VertexLayout: driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: int(unsafe.Sizeof(g.materials.quads[0])),
+ },
+ PixelFormat: driver.TextureFormatRGBA8,
+ Topology: driver.TopologyTriangles,
+ })
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.materials.pipeline = pipe
+ g.materials.uniforms.u = new(materialUniforms)
+
+ buf, err = ctx.NewBuffer(driver.BufferBindingUniforms, int(unsafe.Sizeof(*g.materials.uniforms.u)))
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.materials.uniforms.buf = buf
+
+ for _, shader := range shaders {
+ if !g.useCPU {
+ p, err := ctx.NewComputeProgram(shader.src)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ shader.prog.prog = p
+ } else {
+ shader.prog.progInfo = shader.info
+ }
+ }
+ if g.useCPU {
+ {
+ desc := new(piet.ElementsDescriptorSetLayout)
+ g.programs.elements.descriptors = unsafe.Pointer(desc)
+ g.programs.elements.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1(), desc.Binding2(), desc.Binding3()}
+ }
+ {
+ desc := new(piet.Tile_allocDescriptorSetLayout)
+ g.programs.tileAlloc.descriptors = unsafe.Pointer(desc)
+ g.programs.tileAlloc.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.Path_coarseDescriptorSetLayout)
+ g.programs.pathCoarse.descriptors = unsafe.Pointer(desc)
+ g.programs.pathCoarse.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.BackdropDescriptorSetLayout)
+ g.programs.backdrop.descriptors = unsafe.Pointer(desc)
+ g.programs.backdrop.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.BinningDescriptorSetLayout)
+ g.programs.binning.descriptors = unsafe.Pointer(desc)
+ g.programs.binning.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.CoarseDescriptorSetLayout)
+ g.programs.coarse.descriptors = unsafe.Pointer(desc)
+ g.programs.coarse.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.Kernel4DescriptorSetLayout)
+ g.programs.kernel4.descriptors = unsafe.Pointer(desc)
+ g.programs.kernel4.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ g.output.descriptors = desc
+ }
+ }
+ return g, nil
+}
+
+func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) {
+ vert, err = ctx.NewVertexShader(vsrc)
+ if err != nil {
+ return
+ }
+ frag, err = ctx.NewFragmentShader(fsrc)
+ if err != nil {
+ vert.Release()
+ }
+ return
+}
+
+func (g *compute) Frame(frameOps *op.Ops, target RenderTarget, viewport image.Point) error {
+ g.frameCount++
+ g.collect(viewport, frameOps)
+ return g.frame(target)
+}
+
+func (g *compute) collect(viewport image.Point, ops *op.Ops) {
+ g.viewport = viewport
+ g.collector.reset()
+
+ g.texOps = g.texOps[:0]
+ g.collector.collect(ops, viewport, &g.texOps)
+}
+
+func (g *compute) Clear(col color.NRGBA) {
+ g.collector.clear = true
+ g.collector.clearColor = f32color.LinearFromSRGB(col)
+}
+
+func (g *compute) frame(target RenderTarget) error {
+ viewport := g.viewport
+ defFBO := g.ctx.BeginFrame(target, g.collector.clear, viewport)
+ defer g.ctx.EndFrame()
+
+ t := &g.timers
+ if g.collector.profile && t.t == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) {
+ t.t = newTimers(g.ctx)
+ t.compact = t.t.newTimer()
+ t.render = t.t.newTimer()
+ t.blit = t.t.newTimer()
+ }
+
+ if err := g.uploadImages(); err != nil {
+ return err
+ }
+ if err := g.renderMaterials(); err != nil {
+ return err
+ }
+ g.layer(viewport, g.texOps)
+ t.render.begin()
+ if err := g.renderLayers(viewport); err != nil {
+ return err
+ }
+ t.render.end()
+ d := driver.LoadDesc{
+ ClearColor: g.collector.clearColor,
+ }
+ if g.collector.clear {
+ g.collector.clear = false
+ d.Action = driver.LoadActionClear
+ }
+ t.blit.begin()
+ g.blitLayers(d, defFBO, viewport)
+ t.blit.end()
+ t.compact.begin()
+ if err := g.compactAllocs(); err != nil {
+ return err
+ }
+ t.compact.end()
+ if g.collector.profile && t.t.ready() {
+ com, ren, blit := t.compact.Elapsed, t.render.Elapsed, t.blit.Elapsed
+ ft := com + ren + blit
+ q := 100 * time.Microsecond
+ ft = ft.Round(q)
+ com, ren, blit = com.Round(q), ren.Round(q), blit.Round(q)
+ t.profile = fmt.Sprintf("ft:%7s com: %7s ren:%7s blit:%7s", ft, com, ren, blit)
+ }
+ return nil
+}
+
+func (g *compute) dumpAtlases() {
+ for i, a := range g.atlases {
+ dump := image.NewRGBA(image.Rectangle{Max: a.size})
+ err := driver.DownloadImage(g.ctx, a.image, dump)
+ if err != nil {
+ panic(err)
+ }
+ nrgba := image.NewNRGBA(dump.Bounds())
+ draw.Draw(nrgba, image.Rectangle{}, dump, image.Point{}, draw.Src)
+ var buf bytes.Buffer
+ if err := png.Encode(&buf, nrgba); err != nil {
+ panic(err)
+ }
+ if err := ioutil.WriteFile(fmt.Sprintf("dump-%d.png", i), buf.Bytes(), 0600); err != nil {
+ panic(err)
+ }
+ }
+}
+
+func (g *compute) Profile() string {
+ return g.timers.profile
+}
+
+func (g *compute) compactAllocs() error {
+ const (
+ maxAllocAge = 3
+ maxAtlasAge = 10
+ )
+ atlases := g.atlases
+ for _, a := range atlases {
+ if len(a.allocs) > 0 && g.frameCount-a.lastFrame > maxAtlasAge {
+ a.compact = true
+ }
+ }
+ for len(atlases) > 0 {
+ var (
+ dstAtlas *textureAtlas
+ format driver.TextureFormat
+ bindings driver.BufferBinding
+ )
+ g.moves = g.moves[:0]
+ addedLayers := false
+ useCPU := false
+ fill:
+ for len(atlases) > 0 {
+ srcAtlas := atlases[0]
+ allocs := srcAtlas.allocs
+ if !srcAtlas.compact {
+ atlases = atlases[1:]
+ continue
+ }
+ if addedLayers && (format != srcAtlas.format || srcAtlas.bindings&bindings != srcAtlas.bindings) {
+ break
+ }
+ format = srcAtlas.format
+ bindings = srcAtlas.bindings
+ for len(srcAtlas.allocs) > 0 {
+ a := srcAtlas.allocs[0]
+ n := len(srcAtlas.allocs)
+ if g.frameCount-a.frameCount > maxAllocAge {
+ a.dead = true
+ srcAtlas.allocs[0] = srcAtlas.allocs[n-1]
+ srcAtlas.allocs = srcAtlas.allocs[:n-1]
+ continue
+ }
+ size := a.rect.Size()
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: dstAtlas,
+ size: size,
+ format: format,
+ bindings: bindings,
+ nocompact: true,
+ })
+ if !fits {
+ break fill
+ }
+ dstAtlas = alloc.atlas
+ allocs = append(allocs, a)
+ addedLayers = true
+ useCPU = useCPU || a.cpu
+ dstAtlas.allocs = append(dstAtlas.allocs, a)
+ pos := alloc.rect.Min
+ g.moves = append(g.moves, atlasMove{
+ src: srcAtlas, dstPos: pos, srcRect: a.rect, cpu: a.cpu,
+ })
+ a.atlas = dstAtlas
+ a.rect = image.Rectangle{Min: pos, Max: pos.Add(a.rect.Size())}
+ srcAtlas.allocs[0] = srcAtlas.allocs[n-1]
+ srcAtlas.allocs = srcAtlas.allocs[:n-1]
+ }
+ srcAtlas.compact = false
+ srcAtlas.realized = false
+ srcAtlas.packer.clear()
+ srcAtlas.packer.newPage()
+ srcAtlas.packer.maxDims = image.Pt(g.maxTextureDim, g.maxTextureDim)
+ atlases = atlases[1:]
+ }
+ if !addedLayers {
+ break
+ }
+ outputSize := dstAtlas.packer.sizes[0]
+ if err := g.realizeAtlas(dstAtlas, useCPU, outputSize); err != nil {
+ return err
+ }
+ for _, move := range g.moves {
+ if !move.cpu {
+ g.ctx.CopyTexture(dstAtlas.image, move.dstPos, move.src.image, move.srcRect)
+ } else {
+ src := move.src.cpuImage.Data()
+ dst := dstAtlas.cpuImage.Data()
+ sstride := move.src.size.X * 4
+ dstride := dstAtlas.size.X * 4
+ copyImage(dst, dstride, move.dstPos, src, sstride, move.srcRect)
+ }
+ }
+ }
+ for i := len(g.atlases) - 1; i >= 0; i-- {
+ a := g.atlases[i]
+ if len(a.allocs) == 0 && g.frameCount-a.lastFrame > maxAtlasAge {
+ a.Release()
+ n := len(g.atlases)
+ g.atlases[i] = g.atlases[n-1]
+ g.atlases = g.atlases[:n-1]
+ }
+ }
+ return nil
+}
+
+func copyImage(dst []byte, dstStride int, dstPos image.Point, src []byte, srcStride int, srcRect image.Rectangle) {
+ sz := srcRect.Size()
+ soff := srcRect.Min.Y*srcStride + srcRect.Min.X*4
+ doff := dstPos.Y*dstStride + dstPos.X*4
+ rowLen := sz.X * 4
+ for y := 0; y < sz.Y; y++ {
+ srow := src[soff : soff+rowLen]
+ drow := dst[doff : doff+rowLen]
+ copy(drow, srow)
+ soff += srcStride
+ doff += dstStride
+ }
+}
+
+func (g *compute) renderLayers(viewport image.Point) error {
+ layers := g.collector.frame.layers
+ for len(layers) > 0 {
+ var materials, dst *textureAtlas
+ addedLayers := false
+ g.enc.reset()
+ for len(layers) > 0 {
+ l := &layers[0]
+ if l.alloc != nil {
+ layers = layers[1:]
+ continue
+ }
+ if materials != nil {
+ if l.materials != nil && materials != l.materials {
+ // Only one materials texture per compute pass.
+ break
+ }
+ } else {
+ materials = l.materials
+ }
+ size := l.rect.Size()
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: dst,
+ empty: true,
+ format: driver.TextureFormatRGBA8,
+ bindings: combinedBindings,
+ // Pad to avoid overlap.
+ size: size.Add(image.Pt(1, 1)),
+ })
+ if !fits {
+ // Only one output atlas per compute pass.
+ break
+ }
+ dst = alloc.atlas
+ dst.compact = true
+ addedLayers = true
+ l.alloc = &alloc
+ dst.allocs = append(dst.allocs, l.alloc)
+ encodeLayer(*l, alloc.rect.Min, viewport, &g.enc, g.texOps)
+ layers = layers[1:]
+ }
+ if !addedLayers {
+ break
+ }
+ outputSize := dst.packer.sizes[0]
+ tileDims := image.Point{
+ X: (outputSize.X + tileWidthPx - 1) / tileWidthPx,
+ Y: (outputSize.Y + tileHeightPx - 1) / tileHeightPx,
+ }
+ w, h := tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx
+ if err := g.realizeAtlas(dst, g.useCPU, image.Pt(w, h)); err != nil {
+ return err
+ }
+ if err := g.render(materials, dst.image, dst.cpuImage, tileDims, dst.size.X*4); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *compute) blitLayers(d driver.LoadDesc, fbo driver.Texture, viewport image.Point) {
+ layers := g.collector.frame.layers
+ g.output.layerVertices = g.output.layerVertices[:0]
+ for _, l := range layers {
+ placef := layout.FPt(l.alloc.rect.Min)
+ sizef := layout.FPt(l.rect.Size())
+ r := layout.FRect(l.rect)
+ quad := [4]layerVertex{
+ {posX: float32(r.Min.X), posY: float32(r.Min.Y), u: placef.X, v: placef.Y},
+ {posX: float32(r.Max.X), posY: float32(r.Min.Y), u: placef.X + sizef.X, v: placef.Y},
+ {posX: float32(r.Max.X), posY: float32(r.Max.Y), u: placef.X + sizef.X, v: placef.Y + sizef.Y},
+ {posX: float32(r.Min.X), posY: float32(r.Max.Y), u: placef.X, v: placef.Y + sizef.Y},
+ }
+ g.output.layerVertices = append(g.output.layerVertices, quad[0], quad[1], quad[3], quad[3], quad[2], quad[1])
+ g.ctx.PrepareTexture(l.alloc.atlas.image)
+ }
+ if len(g.output.layerVertices) > 0 {
+ vertexData := byteslice.Slice(g.output.layerVertices)
+ g.output.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, len(vertexData))
+ g.output.buffer.buffer.Upload(vertexData)
+ }
+ g.ctx.BeginRenderPass(fbo, d)
+ defer g.ctx.EndRenderPass()
+ if len(layers) == 0 {
+ return
+ }
+ g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
+ g.ctx.BindPipeline(g.output.blitPipeline)
+ g.ctx.BindVertexBuffer(g.output.buffer.buffer, 0)
+ start := 0
+ for len(layers) > 0 {
+ count := 0
+ atlas := layers[0].alloc.atlas
+ for len(layers) > 0 {
+ l := layers[0]
+ if l.alloc.atlas != atlas {
+ break
+ }
+ layers = layers[1:]
+ const verticesPerQuad = 6
+ count += verticesPerQuad
+ }
+
+ // Transform positions to clip space: [-1, -1] - [1, 1], and texture
+ // coordinates to texture space: [0, 0] - [1, 1].
+ clip := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(2/float32(viewport.X), 2/float32(viewport.Y))).Offset(f32.Pt(-1, -1))
+ sx, _, ox, _, sy, oy := clip.Elems()
+ g.output.uniforms.scale = [2]float32{sx, sy}
+ g.output.uniforms.pos = [2]float32{ox, oy}
+ g.output.uniforms.uvScale = [2]float32{1 / float32(atlas.size.X), 1 / float32(atlas.size.Y)}
+ g.output.uniBuf.Upload(byteslice.Struct(g.output.uniforms))
+ g.ctx.BindUniforms(g.output.uniBuf)
+ g.ctx.BindTexture(0, atlas.image)
+ g.ctx.DrawArrays(start, count)
+ start += count
+ }
+}
+
+func (g *compute) renderMaterials() error {
+ m := &g.materials
+ for k, place := range m.allocs {
+ if place.alloc.dead {
+ delete(m.allocs, k)
+ }
+ }
+ texOps := g.texOps
+ for len(texOps) > 0 {
+ m.quads = m.quads[:0]
+ var (
+ atlas *textureAtlas
+ imgAtlas *textureAtlas
+ )
+ // A material is clipped to avoid drawing outside its atlas bounds.
+ // However, imprecision in the clipping may cause a single pixel
+ // overflow.
+ var padding = image.Pt(1, 1)
+ var allocStart int
+ for len(texOps) > 0 {
+ op := &texOps[0]
+ if a, exists := m.allocs[op.key]; exists {
+ g.touchAlloc(a.alloc)
+ op.matAlloc = a
+ texOps = texOps[1:]
+ continue
+ }
+
+ if imgAtlas != nil && op.imgAlloc.atlas != imgAtlas {
+ // Only one image atlas per render pass.
+ break
+ }
+ imgAtlas = op.imgAlloc.atlas
+ quad := g.materialQuad(imgAtlas.size, op.key.transform, op.img, op.imgAlloc.rect.Min)
+ boundsf := quadBounds(quad)
+ bounds := boundRectF(boundsf)
+ bounds = bounds.Intersect(op.key.bounds)
+
+ size := bounds.Size()
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: atlas,
+ size: size.Add(padding),
+ format: driver.TextureFormatRGBA8,
+ bindings: combinedBindings,
+ })
+ if !fits {
+ break
+ }
+ if atlas == nil {
+ allocStart = len(alloc.atlas.allocs)
+ }
+ atlas = alloc.atlas
+ alloc.cpu = g.useCPU
+ offsetf := layout.FPt(bounds.Min.Mul(-1))
+ scale := f32.Pt(float32(size.X), float32(size.Y))
+ for i := range quad {
+ // Position quad to match place.
+ quad[i].posX += offsetf.X
+ quad[i].posY += offsetf.Y
+ // Scale to match viewport [0, 1].
+ quad[i].posX /= scale.X
+ quad[i].posY /= scale.Y
+ }
+ // Draw quad as two triangles.
+ m.quads = append(m.quads, quad[0], quad[1], quad[3], quad[3], quad[1], quad[2])
+ if m.allocs == nil {
+ m.allocs = make(map[textureKey]materialAlloc)
+ }
+ atlasAlloc := materialAlloc{
+ alloc: &alloc,
+ offset: bounds.Min.Mul(-1),
+ }
+ atlas.allocs = append(atlas.allocs, atlasAlloc.alloc)
+ m.allocs[op.key] = atlasAlloc
+ op.matAlloc = atlasAlloc
+ texOps = texOps[1:]
+ }
+ if len(m.quads) == 0 {
+ break
+ }
+ realized := atlas.realized
+ if err := g.realizeAtlas(atlas, g.useCPU, atlas.packer.sizes[0]); err != nil {
+ return err
+ }
+ // Transform to clip space: [-1, -1] - [1, 1].
+ *m.uniforms.u = materialUniforms{
+ scale: [2]float32{2, 2},
+ pos: [2]float32{-1, -1},
+ }
+ if !g.srgb {
+ m.uniforms.u.emulatesRGB = 1.0
+ }
+ m.uniforms.buf.Upload(byteslice.Struct(m.uniforms.u))
+ vertexData := byteslice.Slice(m.quads)
+ n := pow2Ceil(len(vertexData))
+ m.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, n)
+ m.buffer.buffer.Upload(vertexData)
+ var d driver.LoadDesc
+ if !realized {
+ d.Action = driver.LoadActionClear
+ }
+ g.ctx.PrepareTexture(imgAtlas.image)
+ g.ctx.BeginRenderPass(atlas.image, d)
+ g.ctx.BindTexture(0, imgAtlas.image)
+ g.ctx.BindPipeline(m.pipeline)
+ g.ctx.BindUniforms(m.uniforms.buf)
+ g.ctx.BindVertexBuffer(m.buffer.buffer, 0)
+ newAllocs := atlas.allocs[allocStart:]
+ for i, a := range newAllocs {
+ sz := a.rect.Size().Sub(padding)
+ g.ctx.Viewport(a.rect.Min.X, a.rect.Min.Y, sz.X, sz.Y)
+ g.ctx.DrawArrays(i*6, 6)
+ }
+ g.ctx.EndRenderPass()
+ if !g.useCPU {
+ continue
+ }
+ src := atlas.image
+ data := atlas.cpuImage.Data()
+ for _, a := range newAllocs {
+ stride := atlas.size.X * 4
+ col := a.rect.Min.X * 4
+ row := stride * a.rect.Min.Y
+ off := col + row
+ src.ReadPixels(a.rect, data[off:], stride)
+ }
+ }
+ return nil
+}
+
+func (g *compute) uploadImages() error {
+ for k, a := range g.imgAllocs {
+ if a.dead {
+ delete(g.imgAllocs, k)
+ }
+ }
+ type upload struct {
+ pos image.Point
+ img *image.RGBA
+ }
+ var uploads []upload
+ format := driver.TextureFormatSRGBA
+ if !g.srgb {
+ format = driver.TextureFormatRGBA8
+ }
+ // padding is the number of pixels added to the right and below
+ // images, to avoid atlas filtering artifacts.
+ const padding = 1
+ texOps := g.texOps
+ for len(texOps) > 0 {
+ uploads = uploads[:0]
+ var atlas *textureAtlas
+ for len(texOps) > 0 {
+ op := &texOps[0]
+ if a, exists := g.imgAllocs[op.img.handle]; exists {
+ g.touchAlloc(a)
+ op.imgAlloc = a
+ texOps = texOps[1:]
+ continue
+ }
+ size := op.img.src.Bounds().Size().Add(image.Pt(padding, padding))
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: atlas,
+ size: size,
+ format: format,
+ bindings: driver.BufferBindingTexture | driver.BufferBindingFramebuffer,
+ })
+ if !fits {
+ break
+ }
+ atlas = alloc.atlas
+ if g.imgAllocs == nil {
+ g.imgAllocs = make(map[interface{}]*atlasAlloc)
+ }
+ op.imgAlloc = &alloc
+ atlas.allocs = append(atlas.allocs, op.imgAlloc)
+ g.imgAllocs[op.img.handle] = op.imgAlloc
+ uploads = append(uploads, upload{pos: alloc.rect.Min, img: op.img.src})
+ texOps = texOps[1:]
+ }
+ if len(uploads) == 0 {
+ break
+ }
+ if err := g.realizeAtlas(atlas, false, atlas.packer.sizes[0]); err != nil {
+ return err
+ }
+ for _, u := range uploads {
+ size := u.img.Bounds().Size()
+ driver.UploadImage(atlas.image, u.pos, u.img)
+ rightPadding := image.Pt(padding, size.Y)
+ atlas.image.Upload(image.Pt(u.pos.X+size.X, u.pos.Y), rightPadding, g.zeros(rightPadding.X*rightPadding.Y*4), 0)
+ bottomPadding := image.Pt(size.X, padding)
+ atlas.image.Upload(image.Pt(u.pos.X, u.pos.Y+size.Y), bottomPadding, g.zeros(bottomPadding.X*bottomPadding.Y*4), 0)
+ }
+ }
+ return nil
+}
+
+func pow2Ceil(v int) int {
+ exp := bits.Len(uint(v))
+ if bits.OnesCount(uint(v)) == 1 {
+ exp--
+ }
+ return 1 << exp
+}
+
+// materialQuad constructs a quad that represents the transformed image. It returns the quad
+// and its bounds.
+func (g *compute) materialQuad(imgAtlasSize image.Point, M f32.Affine2D, img imageOpData, uvPos image.Point) [4]materialVertex {
+ imgSize := layout.FPt(img.src.Bounds().Size())
+ sx, hx, ox, hy, sy, oy := M.Elems()
+ transOff := f32.Pt(ox, oy)
+ // The 4 corners of the image rectangle transformed by M, excluding its offset, are:
+ //
+ // q0: M * (0, 0) q3: M * (w, 0)
+ // q1: M * (0, h) q2: M * (w, h)
+ //
+ // Note that q0 = M*0 = 0, q2 = q1 + q3.
+ q0 := f32.Pt(0, 0)
+ q1 := f32.Pt(hx*imgSize.Y, sy*imgSize.Y)
+ q3 := f32.Pt(sx*imgSize.X, hy*imgSize.X)
+ q2 := q1.Add(q3)
+ q0 = q0.Add(transOff)
+ q1 = q1.Add(transOff)
+ q2 = q2.Add(transOff)
+ q3 = q3.Add(transOff)
+
+ uvPosf := layout.FPt(uvPos)
+ atlasScale := f32.Pt(1/float32(imgAtlasSize.X), 1/float32(imgAtlasSize.Y))
+ uvBounds := f32.Rectangle{
+ Min: uvPosf,
+ Max: uvPosf.Add(imgSize),
+ }
+ uvBounds.Min.X *= atlasScale.X
+ uvBounds.Min.Y *= atlasScale.Y
+ uvBounds.Max.X *= atlasScale.X
+ uvBounds.Max.Y *= atlasScale.Y
+ quad := [4]materialVertex{
+ {posX: q0.X, posY: q0.Y, u: uvBounds.Min.X, v: uvBounds.Min.Y},
+ {posX: q1.X, posY: q1.Y, u: uvBounds.Min.X, v: uvBounds.Max.Y},
+ {posX: q2.X, posY: q2.Y, u: uvBounds.Max.X, v: uvBounds.Max.Y},
+ {posX: q3.X, posY: q3.Y, u: uvBounds.Max.X, v: uvBounds.Min.Y},
+ }
+ return quad
+}
+
+func quadBounds(q [4]materialVertex) f32.Rectangle {
+ q0 := f32.Pt(q[0].posX, q[0].posY)
+ q1 := f32.Pt(q[1].posX, q[1].posY)
+ q2 := f32.Pt(q[2].posX, q[2].posY)
+ q3 := f32.Pt(q[3].posX, q[3].posY)
+ return f32.Rectangle{
+ Min: min(min(q0, q1), min(q2, q3)),
+ Max: max(max(q0, q1), max(q2, q3)),
+ }
+}
+
+func max(p1, p2 f32.Point) f32.Point {
+ p := p1
+ if p2.X > p.X {
+ p.X = p2.X
+ }
+ if p2.Y > p.Y {
+ p.Y = p2.Y
+ }
+ return p
+}
+
+func min(p1, p2 f32.Point) f32.Point {
+ p := p1
+ if p2.X < p.X {
+ p.X = p2.X
+ }
+ if p2.Y < p.Y {
+ p.Y = p2.Y
+ }
+ return p
+}
+
+func (enc *encoder) encodePath(verts []byte, fillMode int) {
+ for ; len(verts) >= scene.CommandSize+4; verts = verts[scene.CommandSize+4:] {
+ cmd := ops.DecodeCommand(verts[4:])
+ if cmd.Op() == scene.OpGap {
+ if fillMode != scene.FillModeNonzero {
+ // Skip gaps in strokes.
+ continue
+ }
+ // Replace them by a straight line in outlines.
+ cmd = scene.Line(scene.DecodeGap(cmd))
+ }
+ enc.scene = append(enc.scene, cmd)
+ enc.npathseg++
+ }
+}
+
+func (g *compute) render(images *textureAtlas, dst driver.Texture, cpuDst cpu.ImageDescriptor, tileDims image.Point, stride int) error {
+ const (
+ // wgSize is the largest and most common workgroup size.
+ wgSize = 128
+ // PARTITION_SIZE from elements.comp
+ partitionSize = 32 * 4
+ )
+ widthInBins := (tileDims.X + 15) / 16
+ heightInBins := (tileDims.Y + 7) / 8
+ if widthInBins*heightInBins > wgSize {
+ return fmt.Errorf("gpu: output too large (%dx%d)", tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx)
+ }
+
+ enc := &g.enc
+ // Pad scene with zeroes to avoid reading garbage in elements.comp.
+ scenePadding := partitionSize - len(enc.scene)%partitionSize
+ enc.scene = append(enc.scene, make([]scene.Command, scenePadding)...)
+
+ scene := byteslice.Slice(enc.scene)
+ if s := len(scene); s > g.buffers.scene.size {
+ paddedCap := s * 11 / 10
+ if err := g.buffers.scene.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead, paddedCap); err != nil {
+ return err
+ }
+ }
+ g.buffers.scene.upload(scene)
+
+ // alloc is the number of allocated bytes for static buffers.
+ var alloc uint32
+ round := func(v, quantum int) int {
+ return (v + quantum - 1) &^ (quantum - 1)
+ }
+ malloc := func(size int) memAlloc {
+ size = round(size, 4)
+ offset := alloc
+ alloc += uint32(size)
+ return memAlloc{offset /*, uint32(size)*/}
+ }
+
+ *g.conf = config{
+ n_elements: uint32(enc.npath),
+ n_pathseg: uint32(enc.npathseg),
+ width_in_tiles: uint32(tileDims.X),
+ height_in_tiles: uint32(tileDims.Y),
+ tile_alloc: malloc(enc.npath * pathSize),
+ bin_alloc: malloc(round(enc.npath, wgSize) * binSize),
+ ptcl_alloc: malloc(tileDims.X * tileDims.Y * ptclInitialAlloc),
+ pathseg_alloc: malloc(enc.npathseg * pathsegSize),
+ anno_alloc: malloc(enc.npath * annoSize),
+ trans_alloc: malloc(enc.ntrans * transSize),
+ }
+
+ numPartitions := (enc.numElements() + 127) / 128
+ // clearSize is the atomic partition counter plus flag and 2 states per partition.
+ clearSize := 4 + numPartitions*stateStride
+ if clearSize > g.buffers.state.size {
+ paddedCap := clearSize * 11 / 10
+ if err := g.buffers.state.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, paddedCap); err != nil {
+ return err
+ }
+ }
+
+ confData := byteslice.Struct(g.conf)
+ g.buffers.config.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead, len(confData))
+ g.buffers.config.upload(confData)
+
+ minSize := int(unsafe.Sizeof(memoryHeader{})) + int(alloc)
+ if minSize > g.buffers.memory.size {
+ // Add space for dynamic GPU allocations.
+ const sizeBump = 4 * 1024 * 1024
+ minSize += sizeBump
+ if err := g.buffers.memory.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, minSize); err != nil {
+ return err
+ }
+ }
+
+ for {
+ *g.memHeader = memoryHeader{
+ mem_offset: alloc,
+ }
+ g.buffers.memory.upload(byteslice.Struct(g.memHeader))
+ g.buffers.state.upload(g.zeros(clearSize))
+
+ if !g.useCPU {
+ g.ctx.BeginCompute()
+ g.ctx.BindImageTexture(kernel4OutputUnit, dst)
+ img := g.output.nullMaterials
+ if images != nil {
+ img = images.image
+ }
+ g.ctx.BindImageTexture(kernel4AtlasUnit, img)
+ } else {
+ *g.output.descriptors.Binding2() = cpuDst
+ if images != nil {
+ *g.output.descriptors.Binding3() = images.cpuImage
+ }
+ }
+
+ g.bindBuffers()
+ g.memoryBarrier()
+ g.dispatch(g.programs.elements, numPartitions, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.tileAlloc, (enc.npath+wgSize-1)/wgSize, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.pathCoarse, (enc.npathseg+31)/32, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.backdrop, (enc.npath+wgSize-1)/wgSize, 1, 1)
+ // No barrier needed between backdrop and binning.
+ g.dispatch(g.programs.binning, (enc.npath+wgSize-1)/wgSize, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.coarse, widthInBins, heightInBins, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.kernel4, tileDims.X, tileDims.Y, 1)
+ g.memoryBarrier()
+ if !g.useCPU {
+ g.ctx.EndCompute()
+ } else {
+ g.dispatcher.Sync()
+ }
+
+ if err := g.buffers.memory.download(byteslice.Struct(g.memHeader)); err != nil {
+ if err == driver.ErrContentLost {
+ continue
+ }
+ return err
+ }
+ switch errCode := g.memHeader.mem_error; errCode {
+ case memNoError:
+ if g.useCPU {
+ w, h := tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx
+ dst.Upload(image.Pt(0, 0), image.Pt(w, h), cpuDst.Data(), stride)
+ }
+ return nil
+ case memMallocFailed:
+ // Resize memory and try again.
+ sz := g.buffers.memory.size * 15 / 10
+ if err := g.buffers.memory.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, sz); err != nil {
+ return err
+ }
+ continue
+ default:
+ return fmt.Errorf("compute: shader program failed with error %d", errCode)
+ }
+ }
+}
+
+func (g *compute) memoryBarrier() {
+ if g.useCPU {
+ g.dispatcher.Barrier()
+ }
+}
+
+func (g *compute) dispatch(p computeProgram, x, y, z int) {
+ if !g.useCPU {
+ g.ctx.BindProgram(p.prog)
+ g.ctx.DispatchCompute(x, y, z)
+ } else {
+ g.dispatcher.Dispatch(p.progInfo, p.descriptors, x, y, z)
+ }
+}
+
+// zeros returns a byte slice with size bytes of zeros.
+func (g *compute) zeros(size int) []byte {
+ if cap(g.zeroSlice) < size {
+ g.zeroSlice = append(g.zeroSlice, make([]byte, size)...)
+ }
+ return g.zeroSlice[:size]
+}
+
+func (g *compute) touchAlloc(a *atlasAlloc) {
+ if a.dead {
+ panic("re-use of dead allocation")
+ }
+ a.frameCount = g.frameCount
+ a.atlas.lastFrame = a.frameCount
+}
+
+func (g *compute) atlasAlloc(q allocQuery) (atlasAlloc, bool) {
+ var (
+ place placement
+ fits bool
+ atlas = q.atlas
+ )
+ if atlas != nil {
+ place, fits = atlas.packer.tryAdd(q.size)
+ if !fits {
+ atlas.compact = true
+ }
+ }
+ if atlas == nil {
+ // Look for matching atlas to re-use.
+ for _, a := range g.atlases {
+ if q.empty && len(a.allocs) > 0 {
+ continue
+ }
+ if q.nocompact && a.compact {
+ continue
+ }
+ if a.format != q.format || a.bindings&q.bindings != q.bindings {
+ continue
+ }
+ place, fits = a.packer.tryAdd(q.size)
+ if !fits {
+ a.compact = true
+ continue
+ }
+ atlas = a
+ break
+ }
+ }
+ if atlas == nil {
+ atlas = &textureAtlas{
+ format: q.format,
+ bindings: q.bindings,
+ }
+ atlas.packer.maxDims = image.Pt(g.maxTextureDim, g.maxTextureDim)
+ atlas.packer.newPage()
+ g.atlases = append(g.atlases, atlas)
+ place, fits = atlas.packer.tryAdd(q.size)
+ if !fits {
+ panic(fmt.Errorf("compute: atlas allocation too large (%v)", q.size))
+ }
+ }
+ if !fits {
+ return atlasAlloc{}, false
+ }
+ atlas.lastFrame = g.frameCount
+ return atlasAlloc{
+ frameCount: g.frameCount,
+ atlas: atlas,
+ rect: image.Rectangle{Min: place.Pos, Max: place.Pos.Add(q.size)},
+ }, true
+}
+
+func (g *compute) realizeAtlas(atlas *textureAtlas, useCPU bool, size image.Point) error {
+ defer func() {
+ atlas.packer.maxDims = atlas.size
+ atlas.realized = true
+ atlas.ensureCPUImage(useCPU)
+ }()
+ if atlas.size.X >= size.X && atlas.size.Y >= size.Y {
+ return nil
+ }
+ if atlas.realized {
+ panic("resizing a realized atlas")
+ }
+ if err := atlas.resize(g.ctx, size); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (a *textureAtlas) resize(ctx driver.Device, size image.Point) error {
+ a.Release()
+
+ img, err := ctx.NewTexture(a.format, size.X, size.Y,
+ driver.FilterNearest,
+ driver.FilterNearest,
+ a.bindings)
+ if err != nil {
+ return err
+ }
+ a.image = img
+ a.size = size
+ return nil
+}
+
+func (a *textureAtlas) ensureCPUImage(useCPU bool) {
+ if !useCPU || a.hasCPU {
+ return
+ }
+ a.hasCPU = true
+ a.cpuImage = cpu.NewImageRGBA(a.size.X, a.size.Y)
+}
+
+func (g *compute) Release() {
+ if g.useCPU {
+ g.dispatcher.Stop()
+ }
+ type resource interface {
+ Release()
+ }
+ res := []resource{
+ g.output.nullMaterials,
+ &g.programs.elements,
+ &g.programs.tileAlloc,
+ &g.programs.pathCoarse,
+ &g.programs.backdrop,
+ &g.programs.binning,
+ &g.programs.coarse,
+ &g.programs.kernel4,
+ g.output.blitPipeline,
+ &g.output.buffer,
+ g.output.uniBuf,
+ &g.buffers.scene,
+ &g.buffers.state,
+ &g.buffers.memory,
+ &g.buffers.config,
+ g.materials.pipeline,
+ &g.materials.buffer,
+ g.materials.uniforms.buf,
+ g.timers.t,
+ }
+ for _, r := range res {
+ if r != nil {
+ r.Release()
+ }
+ }
+ for _, a := range g.atlases {
+ a.Release()
+ }
+ g.ctx.Release()
+ *g = compute{}
+}
+
+func (a *textureAtlas) Release() {
+ if a.image != nil {
+ a.image.Release()
+ a.image = nil
+ }
+ a.cpuImage.Free()
+ a.hasCPU = false
+}
+
+func (g *compute) bindBuffers() {
+ g.bindStorageBuffers(g.programs.elements, g.buffers.memory, g.buffers.config, g.buffers.scene, g.buffers.state)
+ g.bindStorageBuffers(g.programs.tileAlloc, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.pathCoarse, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.backdrop, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.binning, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.coarse, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.kernel4, g.buffers.memory, g.buffers.config)
+}
+
+func (p *computeProgram) Release() {
+ if p.prog != nil {
+ p.prog.Release()
+ }
+ *p = computeProgram{}
+}
+
+func (b *sizedBuffer) Release() {
+ if b.buffer != nil {
+ b.buffer.Release()
+ }
+ b.cpuBuf.Free()
+ *b = sizedBuffer{}
+}
+
+func (b *sizedBuffer) ensureCapacity(useCPU bool, ctx driver.Device, binding driver.BufferBinding, size int) error {
+ if b.size >= size {
+ return nil
+ }
+ if b.buffer != nil {
+ b.Release()
+ }
+ b.cpuBuf.Free()
+ if !useCPU {
+ buf, err := ctx.NewBuffer(binding, size)
+ if err != nil {
+ return err
+ }
+ b.buffer = buf
+ } else {
+ b.cpuBuf = cpu.NewBuffer(size)
+ }
+ b.size = size
+ return nil
+}
+
+func (b *sizedBuffer) download(data []byte) error {
+ if b.buffer != nil {
+ return b.buffer.Download(data)
+ } else {
+ copy(data, b.cpuBuf.Data())
+ return nil
+ }
+}
+
+func (b *sizedBuffer) upload(data []byte) {
+ if b.buffer != nil {
+ b.buffer.Upload(data)
+ } else {
+ copy(b.cpuBuf.Data(), data)
+ }
+}
+
+func (g *compute) bindStorageBuffers(prog computeProgram, buffers ...sizedBuffer) {
+ for i, buf := range buffers {
+ if !g.useCPU {
+ g.ctx.BindStorageBuffer(i, buf.buffer)
+ } else {
+ *prog.buffers[i] = buf.cpuBuf
+ }
+ }
+}
+
+var bo = binary.LittleEndian
+
+func (e *encoder) reset() {
+ e.scene = e.scene[:0]
+ e.npath = 0
+ e.npathseg = 0
+ e.ntrans = 0
+}
+
+func (e *encoder) numElements() int {
+ return len(e.scene)
+}
+
+func (e *encoder) append(e2 encoder) {
+ e.scene = append(e.scene, e2.scene...)
+ e.npath += e2.npath
+ e.npathseg += e2.npathseg
+ e.ntrans += e2.ntrans
+}
+
+func (e *encoder) transform(m f32.Affine2D) {
+ e.scene = append(e.scene, scene.Transform(m))
+ e.ntrans++
+}
+
+func (e *encoder) lineWidth(width float32) {
+ e.scene = append(e.scene, scene.SetLineWidth(width))
+}
+
+func (e *encoder) fillMode(mode scene.FillMode) {
+ e.scene = append(e.scene, scene.SetFillMode(mode))
+}
+
+func (e *encoder) beginClip(bbox f32.Rectangle) {
+ e.scene = append(e.scene, scene.BeginClip(bbox))
+ e.npath++
+}
+
+func (e *encoder) endClip(bbox f32.Rectangle) {
+ e.scene = append(e.scene, scene.EndClip(bbox))
+ e.npath++
+}
+
+func (e *encoder) rect(r f32.Rectangle) {
+ // Rectangle corners, clock-wise.
+ c0, c1, c2, c3 := r.Min, f32.Pt(r.Min.X, r.Max.Y), r.Max, f32.Pt(r.Max.X, r.Min.Y)
+ e.line(c0, c1)
+ e.line(c1, c2)
+ e.line(c2, c3)
+ e.line(c3, c0)
+}
+
+func (e *encoder) fillColor(col color.RGBA) {
+ e.scene = append(e.scene, scene.FillColor(col))
+ e.npath++
+}
+
+func (e *encoder) fillImage(index int, offset image.Point) {
+ e.scene = append(e.scene, scene.FillImage(index, offset))
+ e.npath++
+}
+
+func (e *encoder) line(start, end f32.Point) {
+ e.scene = append(e.scene, scene.Line(start, end))
+ e.npathseg++
+}
+
+func (e *encoder) quad(start, ctrl, end f32.Point) {
+ e.scene = append(e.scene, scene.Quad(start, ctrl, end))
+ e.npathseg++
+}
+
+func (c *collector) reset() {
+ c.prevFrame, c.frame = c.frame, c.prevFrame
+ c.profile = false
+ c.clipStates = c.clipStates[:0]
+ c.transStack = c.transStack[:0]
+ c.frame.reset()
+}
+
+func (c *opsCollector) reset() {
+ c.paths = c.paths[:0]
+ c.clipCmds = c.clipCmds[:0]
+ c.ops = c.ops[:0]
+ c.layers = c.layers[:0]
+}
+
+func (c *collector) addClip(state *encoderState, viewport, bounds f32.Rectangle, path []byte, key ops.Key, hash uint64, strokeWidth float32, push bool) {
+ // Rectangle clip regions.
+ if len(path) == 0 && !push {
+ // If the rectangular clip region contains a previous path it can be discarded.
+ p := state.clip
+ t := state.relTrans.Invert()
+ for p != nil {
+ // rect is the parent bounds transformed relative to the rectangle.
+ rect := transformBounds(t, p.bounds)
+ if rect.In(bounds) {
+ return
+ }
+ t = p.relTrans.Invert().Mul(t)
+ p = p.parent
+ }
+ }
+
+ absBounds := transformBounds(state.t, bounds).Bounds()
+ intersect := absBounds
+ if state.clip != nil {
+ intersect = state.clip.intersect.Intersect(intersect)
+ }
+ c.clipStates = append(c.clipStates, clipState{
+ parent: state.clip,
+ absBounds: absBounds,
+ path: path,
+ pathKey: key,
+ intersect: intersect,
+ clipKey: clipKey{
+ bounds: bounds,
+ relTrans: state.relTrans,
+ strokeWidth: strokeWidth,
+ pathHash: hash,
+ },
+ })
+ state.clip = &c.clipStates[len(c.clipStates)-1]
+ state.relTrans = f32.Affine2D{}
+}
+
+func (c *collector) collect(root *op.Ops, viewport image.Point, texOps *[]textureOp) {
+ fview := f32.Rectangle{Max: layout.FPt(viewport)}
+ var intOps *ops.Ops
+ if root != nil {
+ intOps = &root.Internal
+ }
+ c.reader.Reset(intOps)
+ var state encoderState
+ reset := func() {
+ state = encoderState{
+ paintKey: paintKey{
+ color: color.NRGBA{A: 0xff},
+ },
+ }
+ }
+ reset()
+ r := &c.reader
+ var (
+ pathData struct {
+ data []byte
+ key ops.Key
+ hash uint64
+ }
+ strWidth float32
+ )
+ c.addClip(&state, fview, fview, nil, ops.Key{}, 0, 0, false)
+ for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
+ switch ops.OpType(encOp.Data[0]) {
+ case ops.TypeProfile:
+ c.profile = true
+ case ops.TypeTransform:
+ dop, push := ops.DecodeTransform(encOp.Data)
+ if push {
+ c.transStack = append(c.transStack, transEntry{t: state.t, relTrans: state.relTrans})
+ }
+ state.t = state.t.Mul(dop)
+ state.relTrans = state.relTrans.Mul(dop)
+ case ops.TypePopTransform:
+ n := len(c.transStack)
+ st := c.transStack[n-1]
+ c.transStack = c.transStack[:n-1]
+ state.t = st.t
+ state.relTrans = st.relTrans
+ case ops.TypeStroke:
+ strWidth = decodeStrokeOp(encOp.Data)
+ case ops.TypePath:
+ hash := bo.Uint64(encOp.Data[1:])
+ encOp, ok = r.Decode()
+ if !ok {
+ panic("unexpected end of path operation")
+ }
+ pathData.data = encOp.Data[ops.TypeAuxLen:]
+ pathData.key = encOp.Key
+ pathData.hash = hash
+ case ops.TypeClip:
+ var op ops.ClipOp
+ op.Decode(encOp.Data)
+ bounds := layout.FRect(op.Bounds)
+ c.addClip(&state, fview, bounds, pathData.data, pathData.key, pathData.hash, strWidth, true)
+ pathData.data = nil
+ strWidth = 0
+ case ops.TypePopClip:
+ state.relTrans = state.clip.relTrans.Mul(state.relTrans)
+ state.clip = state.clip.parent
+ case ops.TypeColor:
+ state.matType = materialColor
+ state.color = decodeColorOp(encOp.Data)
+ case ops.TypeLinearGradient:
+ state.matType = materialLinearGradient
+ op := decodeLinearGradientOp(encOp.Data)
+ state.stop1 = op.stop1
+ state.stop2 = op.stop2
+ state.color1 = op.color1
+ state.color2 = op.color2
+ case ops.TypeImage:
+ state.matType = materialTexture
+ state.image = decodeImageOp(encOp.Data, encOp.Refs)
+ case ops.TypePaint:
+ paintState := state
+ if paintState.matType == materialTexture {
+ // Clip to the bounds of the image, to hide other images in the atlas.
+ sz := state.image.src.Rect.Size()
+ bounds := f32.Rectangle{Max: layout.FPt(sz)}
+ c.addClip(&paintState, fview, bounds, nil, ops.Key{}, 0, 0, false)
+ }
+ intersect := paintState.clip.intersect
+ if intersect.Empty() {
+ break
+ }
+
+ // If the paint is a uniform opaque color that takes up the whole
+ // screen, it covers all previous paints and we can discard all
+ // rendering commands recorded so far.
+ if paintState.clip == nil && paintState.matType == materialColor && paintState.color.A == 255 {
+ c.clearColor = f32color.LinearFromSRGB(paintState.color).Opaque()
+ c.clear = true
+ c.frame.reset()
+ break
+ }
+
+ // Flatten clip stack.
+ p := paintState.clip
+ startIdx := len(c.frame.clipCmds)
+ for p != nil {
+ idx := len(c.frame.paths)
+ c.frame.paths = append(c.frame.paths, make([]byte, len(p.path))...)
+ path := c.frame.paths[idx:]
+ copy(path, p.path)
+ c.frame.clipCmds = append(c.frame.clipCmds, clipCmd{
+ state: p.clipKey,
+ path: path,
+ pathKey: p.pathKey,
+ absBounds: p.absBounds,
+ })
+ p = p.parent
+ }
+ clipStack := c.frame.clipCmds[startIdx:]
+ c.frame.ops = append(c.frame.ops, paintOp{
+ clipStack: clipStack,
+ state: paintState.paintKey,
+ intersect: intersect,
+ })
+ case ops.TypeSave:
+ id := ops.DecodeSave(encOp.Data)
+ c.save(id, state.t)
+ case ops.TypeLoad:
+ reset()
+ id := ops.DecodeLoad(encOp.Data)
+ state.t = c.states[id]
+ state.relTrans = state.t
+ }
+ }
+ for i := range c.frame.ops {
+ op := &c.frame.ops[i]
+ // For each clip, cull rectangular clip regions that contain its
+ // (transformed) bounds. addClip already handled the converse case.
+ // TODO: do better than O(n²) to efficiently deal with deep stacks.
+ for j := 0; j < len(op.clipStack)-1; j++ {
+ cl := op.clipStack[j]
+ p := cl.state
+ r := transformBounds(p.relTrans, p.bounds)
+ for k := j + 1; k < len(op.clipStack); k++ {
+ cl2 := op.clipStack[k]
+ p2 := cl2.state
+ if len(cl2.path) == 0 && r.In(cl2.state.bounds) {
+ op.clipStack = append(op.clipStack[:k], op.clipStack[k+1:]...)
+ k--
+ op.clipStack[k].state.relTrans = p2.relTrans.Mul(op.clipStack[k].state.relTrans)
+ }
+ r = transformRect(p2.relTrans, r)
+ }
+ }
+ // Separate the integer offset from the first transform. Two ops that differ
+ // only in integer offsets may share backing storage.
+ if len(op.clipStack) > 0 {
+ c := &op.clipStack[len(op.clipStack)-1]
+ t := c.state.relTrans
+ t, off := separateTransform(t)
+ c.state.relTrans = t
+ op.offset = off
+ op.state.t = op.state.t.Offset(layout.FPt(off.Mul(-1)))
+ }
+ op.hash = c.hashOp(*op)
+ op.texOpIdx = -1
+ switch op.state.matType {
+ case materialTexture:
+ op.texOpIdx = len(*texOps)
+ // Separate integer offset from transformation. TextureOps that have identical transforms
+ // except for their integer offsets can share a transformed image.
+ t := op.state.t.Offset(layout.FPt(op.offset))
+ t, off := separateTransform(t)
+ bounds := boundRectF(op.intersect).Sub(off)
+ *texOps = append(*texOps, textureOp{
+ img: op.state.image,
+ off: off,
+ key: textureKey{
+ bounds: bounds,
+ transform: t,
+ handle: op.state.image.handle,
+ },
+ })
+ }
+ }
+}
+
+func (c *collector) hashOp(op paintOp) uint64 {
+ c.hasher.Reset()
+ for _, cl := range op.clipStack {
+ k := cl.state
+ keyBytes := (*[unsafe.Sizeof(k)]byte)(unsafe.Pointer(unsafe.Pointer(&k)))
+ c.hasher.Write(keyBytes[:])
+ }
+ k := op.state
+ keyBytes := (*[unsafe.Sizeof(k)]byte)(unsafe.Pointer(unsafe.Pointer(&k)))
+ c.hasher.Write(keyBytes[:])
+ return c.hasher.Sum64()
+}
+
+func (g *compute) layer(viewport image.Point, texOps []textureOp) {
+ // Sort ops from previous frames by hash.
+ c := &g.collector
+ prevOps := c.prevFrame.ops
+ c.order = c.order[:0]
+ for i, op := range prevOps {
+ c.order = append(c.order, hashIndex{
+ index: i,
+ hash: op.hash,
+ })
+ }
+ sort.Slice(c.order, func(i, j int) bool {
+ return c.order[i].hash < c.order[j].hash
+ })
+ // Split layers with different materials atlas; the compute stage has only
+ // one materials slot.
+ splitLayer := func(ops []paintOp, prevLayerIdx int) {
+ for len(ops) > 0 {
+ var materials *textureAtlas
+ idx := 0
+ for idx < len(ops) {
+ if i := ops[idx].texOpIdx; i != -1 {
+ omats := texOps[i].matAlloc.alloc.atlas
+ if materials != nil && omats != nil && omats != materials {
+ break
+ }
+ materials = omats
+ }
+ idx++
+ }
+ l := layer{ops: ops[:idx], materials: materials}
+ if prevLayerIdx != -1 {
+ prev := c.prevFrame.layers[prevLayerIdx]
+ if !prev.alloc.dead && len(prev.ops) == len(l.ops) {
+ l.alloc = prev.alloc
+ l.materials = prev.materials
+ g.touchAlloc(l.alloc)
+ }
+ }
+ for i, op := range l.ops {
+ l.rect = l.rect.Union(boundRectF(op.intersect))
+ l.ops[i].layer = len(c.frame.layers)
+ }
+ c.frame.layers = append(c.frame.layers, l)
+ ops = ops[idx:]
+ }
+ }
+ ops := c.frame.ops
+ idx := 0
+ for idx < len(ops) {
+ op := ops[idx]
+ // Search for longest matching op sequence.
+ // start is the earliest index of a match.
+ start := searchOp(c.order, op.hash)
+ layerOps, prevLayerIdx := longestLayer(prevOps, c.order[start:], ops[idx:])
+ if len(layerOps) == 0 {
+ idx++
+ continue
+ }
+ if unmatched := ops[:idx]; len(unmatched) > 0 {
+ // Flush layer of unmatched ops.
+ splitLayer(unmatched, -1)
+ ops = ops[idx:]
+ idx = 0
+ }
+ splitLayer(layerOps, prevLayerIdx)
+ ops = ops[len(layerOps):]
+ }
+ if len(ops) > 0 {
+ splitLayer(ops, -1)
+ }
+}
+
+func longestLayer(prev []paintOp, order []hashIndex, ops []paintOp) ([]paintOp, int) {
+ longest := 0
+ longestIdx := -1
+outer:
+ for len(order) > 0 {
+ first := order[0]
+ order = order[1:]
+ match := prev[first.index:]
+ // Potential match found. Now find longest matching sequence.
+ end := 0
+ layer := match[0].layer
+ off := match[0].offset.Sub(ops[0].offset)
+ for end < len(match) && end < len(ops) {
+ m := match[end]
+ o := ops[end]
+ // End layers on previous match.
+ if m.layer != layer {
+ break
+ }
+ // End layer when the next op doesn't match.
+ if m.hash != o.hash {
+ if end == 0 {
+ // Hashes are sorted so if the first op doesn't match, no
+ // more matches are possible.
+ break outer
+ }
+ break
+ }
+ if !opEqual(off, m, o) {
+ break
+ }
+ end++
+ }
+ if end > longest {
+ longest = end
+ longestIdx = layer
+
+ }
+ }
+ return ops[:longest], longestIdx
+}
+
+func searchOp(order []hashIndex, hash uint64) int {
+ lo, hi := 0, len(order)
+ for lo < hi {
+ mid := (lo + hi) / 2
+ if order[mid].hash < hash {
+ lo = mid + 1
+ } else {
+ hi = mid
+ }
+ }
+ return lo
+}
+
+func opEqual(off image.Point, o1 paintOp, o2 paintOp) bool {
+ if len(o1.clipStack) != len(o2.clipStack) {
+ return false
+ }
+ if o1.state != o2.state {
+ return false
+ }
+ if o1.offset.Sub(o2.offset) != off {
+ return false
+ }
+ for i, cl1 := range o1.clipStack {
+ cl2 := o2.clipStack[i]
+ if len(cl1.path) != len(cl2.path) {
+ return false
+ }
+ if cl1.state != cl2.state {
+ return false
+ }
+ if cl1.pathKey != cl2.pathKey && !bytes.Equal(cl1.path, cl2.path) {
+ return false
+ }
+ }
+ return true
+}
+
+func encodeLayer(l layer, pos image.Point, viewport image.Point, enc *encoder, texOps []textureOp) {
+ off := pos.Sub(l.rect.Min)
+ offf := layout.FPt(off)
+
+ enc.transform(f32.Affine2D{}.Offset(offf))
+ for _, op := range l.ops {
+ encodeOp(viewport, off, enc, texOps, op)
+ }
+ enc.transform(f32.Affine2D{}.Offset(offf.Mul(-1)))
+}
+
+func encodeOp(viewport image.Point, absOff image.Point, enc *encoder, texOps []textureOp, op paintOp) {
+ // Fill in clip bounds, which the shaders expect to be the union
+ // of all affected bounds.
+ var union f32.Rectangle
+ for i, cl := range op.clipStack {
+ union = union.Union(cl.absBounds)
+ op.clipStack[i].union = union
+ }
+
+ absOfff := layout.FPt(absOff)
+ fillMode := scene.FillModeNonzero
+ opOff := layout.FPt(op.offset)
+ inv := f32.Affine2D{}.Offset(opOff)
+ enc.transform(inv)
+ for i := len(op.clipStack) - 1; i >= 0; i-- {
+ cl := op.clipStack[i]
+ if w := cl.state.strokeWidth; w > 0 {
+ enc.fillMode(scene.FillModeStroke)
+ enc.lineWidth(w)
+ fillMode = scene.FillModeStroke
+ } else if fillMode != scene.FillModeNonzero {
+ enc.fillMode(scene.FillModeNonzero)
+ fillMode = scene.FillModeNonzero
+ }
+ enc.transform(cl.state.relTrans)
+ inv = inv.Mul(cl.state.relTrans)
+ if len(cl.path) == 0 {
+ enc.rect(cl.state.bounds)
+ } else {
+ enc.encodePath(cl.path, fillMode)
+ }
+ if i != 0 {
+ enc.beginClip(cl.union.Add(absOfff))
+ }
+ }
+ if len(op.clipStack) == 0 {
+ // No clipping; fill the entire view.
+ enc.rect(f32.Rectangle{Max: layout.FPt(viewport)})
+ }
+
+ switch op.state.matType {
+ case materialTexture:
+ texOp := texOps[op.texOpIdx]
+ off := texOp.matAlloc.alloc.rect.Min.Add(texOp.matAlloc.offset).Sub(texOp.off).Sub(absOff)
+ enc.fillImage(0, off)
+ case materialColor:
+ enc.fillColor(f32color.NRGBAToRGBA(op.state.color))
+ case materialLinearGradient:
+ // TODO: implement.
+ enc.fillColor(f32color.NRGBAToRGBA(op.state.color1))
+ default:
+ panic("not implemented")
+ }
+ enc.transform(inv.Invert())
+ // Pop the clip stack, except the first entry used for fill.
+ for i := 1; i < len(op.clipStack); i++ {
+ cl := op.clipStack[i]
+ enc.endClip(cl.union.Add(absOfff))
+ }
+ if fillMode != scene.FillModeNonzero {
+ enc.fillMode(scene.FillModeNonzero)
+ }
+}
+
+func (c *collector) save(id int, state f32.Affine2D) {
+ if extra := id - len(c.states) + 1; extra > 0 {
+ c.states = append(c.states, make([]f32.Affine2D, extra)...)
+ }
+ c.states[id] = state
+}
+
+func transformBounds(t f32.Affine2D, bounds f32.Rectangle) rectangle {
+ return rectangle{
+ t.Transform(bounds.Min), t.Transform(f32.Pt(bounds.Max.X, bounds.Min.Y)),
+ t.Transform(bounds.Max), t.Transform(f32.Pt(bounds.Min.X, bounds.Max.Y)),
+ }
+}
+
+func separateTransform(t f32.Affine2D) (f32.Affine2D, image.Point) {
+ sx, hx, ox, hy, sy, oy := t.Elems()
+ intx, fracx := math.Modf(float64(ox))
+ inty, fracy := math.Modf(float64(oy))
+ t = f32.NewAffine2D(sx, hx, float32(fracx), hy, sy, float32(fracy))
+ return t, image.Pt(int(intx), int(inty))
+}
+
+func transformRect(t f32.Affine2D, r rectangle) rectangle {
+ var tr rectangle
+ for i, c := range r {
+ tr[i] = t.Transform(c)
+ }
+ return tr
+}
+
+func (r rectangle) In(b f32.Rectangle) bool {
+ for _, c := range r {
+ inside := b.Min.X <= c.X && c.X <= b.Max.X &&
+ b.Min.Y <= c.Y && c.Y <= b.Max.Y
+ if !inside {
+ return false
+ }
+ }
+ return true
+}
+
+func (r rectangle) Contains(b f32.Rectangle) bool {
+ return true
+}
+
+func (r rectangle) Bounds() f32.Rectangle {
+ bounds := f32.Rectangle{
+ Min: f32.Pt(math.MaxFloat32, math.MaxFloat32),
+ Max: f32.Pt(-math.MaxFloat32, -math.MaxFloat32),
+ }
+ for _, c := range r {
+ if c.X < bounds.Min.X {
+ bounds.Min.X = c.X
+ }
+ if c.Y < bounds.Min.Y {
+ bounds.Min.Y = c.Y
+ }
+ if c.X > bounds.Max.X {
+ bounds.Max.X = c.X
+ }
+ if c.Y > bounds.Max.Y {
+ bounds.Max.Y = c.Y
+ }
+ }
+ return bounds
+}
diff --git a/vendor/gioui.org/gpu/cpu.go b/vendor/gioui.org/gpu/cpu.go
new file mode 100644
index 0000000..f2f84ad
--- /dev/null
+++ b/vendor/gioui.org/gpu/cpu.go
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gpu
+
+import (
+ "unsafe"
+
+ "gioui.org/cpu"
+)
+
+// This file contains code specific to running compute shaders on the CPU.
+
+// dispatcher dispatches CPU compute programs across multiple goroutines.
+type dispatcher struct {
+ // done is notified when a worker completes its work slice.
+ done chan struct{}
+ // work receives work slice indices. It is closed when the dispatcher is released.
+ work chan work
+ // dispatch receives compute jobs, which is then split among workers.
+ dispatch chan dispatch
+ // sync receives notification when a Sync completes.
+ sync chan struct{}
+}
+
+type work struct {
+ ctx *cpu.DispatchContext
+ index int
+}
+
+type dispatch struct {
+ _type jobType
+ program *cpu.ProgramInfo
+ descSet unsafe.Pointer
+ x, y, z int
+}
+
+type jobType uint8
+
+const (
+ jobDispatch jobType = iota
+ jobBarrier
+ jobSync
+)
+
+func newDispatcher(workers int) *dispatcher {
+ d := &dispatcher{
+ work: make(chan work, workers),
+ done: make(chan struct{}, workers),
+ // Leave some room to avoid blocking calls to Dispatch.
+ dispatch: make(chan dispatch, 20),
+ sync: make(chan struct{}),
+ }
+ for i := 0; i < workers; i++ {
+ go d.worker()
+ }
+ go d.dispatcher()
+ return d
+}
+
+func (d *dispatcher) dispatcher() {
+ defer close(d.work)
+ var free []*cpu.DispatchContext
+ defer func() {
+ for _, ctx := range free {
+ ctx.Free()
+ }
+ }()
+ var used []*cpu.DispatchContext
+ for job := range d.dispatch {
+ switch job._type {
+ case jobDispatch:
+ if len(free) == 0 {
+ free = append(free, cpu.NewDispatchContext())
+ }
+ ctx := free[len(free)-1]
+ free = free[:len(free)-1]
+ used = append(used, ctx)
+ ctx.Prepare(cap(d.work), job.program, job.descSet, job.x, job.y, job.z)
+ for i := 0; i < cap(d.work); i++ {
+ d.work <- work{
+ ctx: ctx,
+ index: i,
+ }
+ }
+ case jobBarrier:
+ // Wait for all outstanding dispatches to complete.
+ for i := 0; i < len(used)*cap(d.work); i++ {
+ <-d.done
+ }
+ free = append(free, used...)
+ used = used[:0]
+ case jobSync:
+ d.sync <- struct{}{}
+ }
+ }
+}
+
+func (d *dispatcher) worker() {
+ thread := cpu.NewThreadContext()
+ defer thread.Free()
+ for w := range d.work {
+ w.ctx.Dispatch(w.index, thread)
+ d.done <- struct{}{}
+ }
+}
+
+func (d *dispatcher) Barrier() {
+ d.dispatch <- dispatch{_type: jobBarrier}
+}
+
+func (d *dispatcher) Sync() {
+ d.dispatch <- dispatch{_type: jobSync}
+ <-d.sync
+}
+
+func (d *dispatcher) Dispatch(program *cpu.ProgramInfo, descSet unsafe.Pointer, x, y, z int) {
+ d.dispatch <- dispatch{
+ _type: jobDispatch,
+ program: program,
+ descSet: descSet,
+ x: x,
+ y: y,
+ z: z,
+ }
+}
+
+func (d *dispatcher) Stop() {
+ close(d.dispatch)
+}
diff --git a/vendor/gioui.org/gpu/gen.go b/vendor/gioui.org/gpu/gen.go
deleted file mode 100644
index 709d0a7..0000000
--- a/vendor/gioui.org/gpu/gen.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gpu
-
-//go:generate go run ../internal/cmd/convertshaders -package gpu
diff --git a/vendor/gioui.org/gpu/gl/backend.go b/vendor/gioui.org/gpu/gl/backend.go
deleted file mode 100644
index 02037c3..0000000
--- a/vendor/gioui.org/gpu/gl/backend.go
+++ /dev/null
@@ -1,835 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gl
-
-import (
- "errors"
- "fmt"
- "image"
- "strings"
- "time"
- "unsafe"
-
- "gioui.org/gpu/backend"
-)
-
-// Backend implements backend.Device.
-type Backend struct {
- funcs Functions
-
- state glstate
-
- glver [2]int
- gles bool
- ubo bool
- feats backend.Caps
- // floatTriple holds the settings for floating point
- // textures.
- floatTriple textureTriple
- // Single channel alpha textures.
- alphaTriple textureTriple
- srgbaTriple textureTriple
-}
-
-// State tracking.
-type glstate struct {
- // nattr is the current number of enabled vertex arrays.
- nattr int
- prog *gpuProgram
- texUnits [2]*gpuTexture
- layout *gpuInputLayout
- buffer bufferBinding
-}
-
-type bufferBinding struct {
- buf *gpuBuffer
- offset int
- stride int
-}
-
-type gpuTimer struct {
- funcs Functions
- obj Query
-}
-
-type gpuTexture struct {
- backend *Backend
- obj Texture
- triple textureTriple
- width int
- height int
-}
-
-type gpuFramebuffer struct {
- backend *Backend
- obj Framebuffer
- hasDepth bool
- depthBuf Renderbuffer
- foreign bool
-}
-
-type gpuBuffer struct {
- backend *Backend
- hasBuffer bool
- obj Buffer
- typ backend.BufferBinding
- size int
- immutable bool
- version int
- // For emulation of uniform buffers.
- data []byte
-}
-
-type gpuProgram struct {
- backend *Backend
- obj Program
- nattr int
- vertUniforms uniformsTracker
- fragUniforms uniformsTracker
-}
-
-type uniformsTracker struct {
- locs []uniformLocation
- size int
- buf *gpuBuffer
- version int
-}
-
-type uniformLocation struct {
- uniform Uniform
- offset int
- typ backend.DataType
- size int
-}
-
-type gpuInputLayout struct {
- inputs []backend.InputLocation
- layout []backend.InputDesc
-}
-
-// textureTriple holds the type settings for
-// a TexImage2D call.
-type textureTriple struct {
- internalFormat int
- format Enum
- typ Enum
-}
-
-func NewBackend(f Functions) (*Backend, error) {
- exts := strings.Split(f.GetString(EXTENSIONS), " ")
- glVer := f.GetString(VERSION)
- ver, gles, err := ParseGLVersion(glVer)
- if err != nil {
- return nil, err
- }
- floatTriple, err := floatTripleFor(f, ver, exts)
- if err != nil {
- return nil, err
- }
- srgbaTriple, err := srgbaTripleFor(ver, exts)
- if err != nil {
- return nil, err
- }
- ubo := ver[0] >= 3 && gles
- b := &Backend{
- glver: ver,
- gles: gles,
- ubo: ubo,
- funcs: f,
- floatTriple: floatTriple,
- alphaTriple: alphaTripleFor(ver),
- srgbaTriple: srgbaTriple,
- }
- if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") {
- b.feats.Features |= backend.FeatureTimers
- }
- b.feats.MaxTextureSize = f.GetInteger(MAX_TEXTURE_SIZE)
- return b, nil
-}
-
-func (b *Backend) BeginFrame() {
- // Assume GL state is reset between frames.
- b.state = glstate{}
-}
-
-func (b *Backend) EndFrame() {
- b.funcs.ActiveTexture(TEXTURE0)
-}
-
-func (b *Backend) Caps() backend.Caps {
- return b.feats
-}
-
-func (b *Backend) NewTimer() backend.Timer {
- return &gpuTimer{
- funcs: b.funcs,
- obj: b.funcs.CreateQuery(),
- }
-}
-
-func (b *Backend) IsTimeContinuous() bool {
- return b.funcs.GetInteger(GPU_DISJOINT_EXT) == FALSE
-}
-
-func (b *Backend) NewFramebuffer(tex backend.Texture, depthBits int) (backend.Framebuffer, error) {
- glErr(b.funcs)
- gltex := tex.(*gpuTexture)
- fb := b.funcs.CreateFramebuffer()
- fbo := &gpuFramebuffer{backend: b, obj: fb}
- b.BindFramebuffer(fbo)
- if err := glErr(b.funcs); err != nil {
- fbo.Release()
- return nil, err
- }
- b.funcs.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, gltex.obj, 0)
- if depthBits > 0 {
- size := Enum(DEPTH_COMPONENT16)
- switch {
- case depthBits > 24:
- size = DEPTH_COMPONENT32F
- case depthBits > 16:
- size = DEPTH_COMPONENT24
- }
- depthBuf := b.funcs.CreateRenderbuffer()
- b.funcs.BindRenderbuffer(RENDERBUFFER, depthBuf)
- b.funcs.RenderbufferStorage(RENDERBUFFER, size, gltex.width, gltex.height)
- b.funcs.FramebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, depthBuf)
- fbo.depthBuf = depthBuf
- fbo.hasDepth = true
- if err := glErr(b.funcs); err != nil {
- fbo.Release()
- return nil, err
- }
- }
- if st := b.funcs.CheckFramebufferStatus(FRAMEBUFFER); st != FRAMEBUFFER_COMPLETE {
- fbo.Release()
- return nil, fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError())
- }
- return fbo, nil
-}
-
-func (b *Backend) CurrentFramebuffer() backend.Framebuffer {
- fboID := Framebuffer(b.funcs.GetBinding(FRAMEBUFFER_BINDING))
- return &gpuFramebuffer{backend: b, obj: fboID, foreign: true}
-}
-
-func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, minFilter, magFilter backend.TextureFilter, binding backend.BufferBinding) (backend.Texture, error) {
- glErr(b.funcs)
- tex := &gpuTexture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height}
- switch format {
- case backend.TextureFormatFloat:
- tex.triple = b.floatTriple
- case backend.TextureFormatSRGB:
- tex.triple = b.srgbaTriple
- default:
- return nil, errors.New("unsupported texture format")
- }
- b.BindTexture(0, tex)
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, toTexFilter(magFilter))
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, toTexFilter(minFilter))
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
- b.funcs.TexImage2D(TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ, nil)
- if err := glErr(b.funcs); err != nil {
- tex.Release()
- return nil, err
- }
- return tex, nil
-}
-
-func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer, error) {
- glErr(b.funcs)
- buf := &gpuBuffer{backend: b, typ: typ, size: size}
- if typ&backend.BufferBindingUniforms != 0 {
- if typ != backend.BufferBindingUniforms {
- return nil, errors.New("uniforms buffers cannot be bound as anything else")
- }
- if !b.ubo {
- // GLES 2 doesn't support uniform buffers.
- buf.data = make([]byte, size)
- }
- }
- if typ&^backend.BufferBindingUniforms != 0 || b.ubo {
- buf.hasBuffer = true
- buf.obj = b.funcs.CreateBuffer()
- if err := glErr(b.funcs); err != nil {
- buf.Release()
- return nil, err
- }
- }
- return buf, nil
-}
-
-func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (backend.Buffer, error) {
- glErr(b.funcs)
- obj := b.funcs.CreateBuffer()
- buf := &gpuBuffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true}
- buf.Upload(data)
- buf.immutable = true
- if err := glErr(b.funcs); err != nil {
- buf.Release()
- return nil, err
- }
- return buf, nil
-}
-
-func glErr(f Functions) error {
- if st := f.GetError(); st != NO_ERROR {
- return fmt.Errorf("glGetError: %#x", st)
- }
- return nil
-}
-
-func (b *Backend) bindTexture(unit int, t *gpuTexture) {
- if b.state.texUnits[unit] != t {
- b.funcs.ActiveTexture(TEXTURE0 + Enum(unit))
- b.funcs.BindTexture(TEXTURE_2D, t.obj)
- b.state.texUnits[unit] = t
- }
-}
-
-func (b *Backend) useProgram(p *gpuProgram) {
- if b.state.prog != p {
- p.backend.funcs.UseProgram(p.obj)
- b.state.prog = p
- }
-}
-
-func (b *Backend) enableVertexArrays(n int) {
- // Enable needed arrays.
- for i := b.state.nattr; i < n; i++ {
- b.funcs.EnableVertexAttribArray(Attrib(i))
- }
- // Disable extra arrays.
- for i := n; i < b.state.nattr; i++ {
- b.funcs.DisableVertexAttribArray(Attrib(i))
- }
- b.state.nattr = n
-}
-
-func (b *Backend) SetDepthTest(enable bool) {
- if enable {
- b.funcs.Enable(DEPTH_TEST)
- } else {
- b.funcs.Disable(DEPTH_TEST)
- }
-}
-
-func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) {
- b.funcs.BlendFunc(toGLBlendFactor(sfactor), toGLBlendFactor(dfactor))
-}
-
-func toGLBlendFactor(f backend.BlendFactor) Enum {
- switch f {
- case backend.BlendFactorOne:
- return ONE
- case backend.BlendFactorOneMinusSrcAlpha:
- return ONE_MINUS_SRC_ALPHA
- case backend.BlendFactorZero:
- return ZERO
- case backend.BlendFactorDstColor:
- return DST_COLOR
- default:
- panic("unsupported blend factor")
- }
-}
-
-func (b *Backend) DepthMask(mask bool) {
- b.funcs.DepthMask(mask)
-}
-
-func (b *Backend) SetBlend(enable bool) {
- if enable {
- b.funcs.Enable(BLEND)
- } else {
- b.funcs.Disable(BLEND)
- }
-}
-
-func (b *Backend) DrawElements(mode backend.DrawMode, off, count int) {
- b.prepareDraw()
- // off is in 16-bit indices, but DrawElements take a byte offset.
- byteOff := off * 2
- b.funcs.DrawElements(toGLDrawMode(mode), count, UNSIGNED_SHORT, byteOff)
-}
-
-func (b *Backend) DrawArrays(mode backend.DrawMode, off, count int) {
- b.prepareDraw()
- b.funcs.DrawArrays(toGLDrawMode(mode), off, count)
-}
-
-func (b *Backend) prepareDraw() {
- nattr := b.state.prog.nattr
- b.enableVertexArrays(nattr)
- if nattr > 0 {
- b.setupVertexArrays()
- }
- if p := b.state.prog; p != nil {
- p.updateUniforms()
- }
-}
-
-func toGLDrawMode(mode backend.DrawMode) Enum {
- switch mode {
- case backend.DrawModeTriangleStrip:
- return TRIANGLE_STRIP
- case backend.DrawModeTriangles:
- return TRIANGLES
- default:
- panic("unsupported draw mode")
- }
-}
-
-func (b *Backend) Viewport(x, y, width, height int) {
- b.funcs.Viewport(x, y, width, height)
-}
-
-func (b *Backend) Clear(colR, colG, colB, colA float32) {
- b.funcs.ClearColor(colR, colG, colB, colA)
- b.funcs.Clear(COLOR_BUFFER_BIT)
-}
-
-func (b *Backend) ClearDepth(d float32) {
- b.funcs.ClearDepthf(d)
- b.funcs.Clear(DEPTH_BUFFER_BIT)
-}
-
-func (b *Backend) DepthFunc(f backend.DepthFunc) {
- var glfunc Enum
- switch f {
- case backend.DepthFuncGreater:
- glfunc = GREATER
- case backend.DepthFuncGreaterEqual:
- glfunc = GEQUAL
- default:
- panic("unsupported depth func")
- }
- b.funcs.DepthFunc(glfunc)
-}
-
-func (b *Backend) NewInputLayout(vs backend.ShaderSources, layout []backend.InputDesc) (backend.InputLayout, error) {
- if len(vs.Inputs) != len(layout) {
- return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vs.Inputs))
- }
- for i, inp := range vs.Inputs {
- if exp, got := inp.Size, layout[i].Size; exp != got {
- return nil, fmt.Errorf("NewInputLayout: data size mismatch for %q: got %d expected %d", inp.Name, got, exp)
- }
- }
- return &gpuInputLayout{
- inputs: vs.Inputs,
- layout: layout,
- }, nil
-}
-
-func (b *Backend) NewProgram(vertShader, fragShader backend.ShaderSources) (backend.Program, error) {
- attr := make([]string, len(vertShader.Inputs))
- for _, inp := range vertShader.Inputs {
- attr[inp.Location] = inp.Name
- }
- vsrc, fsrc := vertShader.GLSL100ES, fragShader.GLSL100ES
- if b.glver[0] >= 3 {
- // OpenGL (ES) 3.0.
- switch {
- case b.gles:
- vsrc, fsrc = vertShader.GLSL300ES, fragShader.GLSL300ES
- case b.glver[0] >= 4 || b.glver[1] >= 2:
- // OpenGL 3.2 Core only accepts glsl 1.50 or newer.
- vsrc, fsrc = vertShader.GLSL150, fragShader.GLSL150
- default:
- vsrc, fsrc = vertShader.GLSL130, fragShader.GLSL130
- }
- }
- p, err := CreateProgram(b.funcs, vsrc, fsrc, attr)
- if err != nil {
- return nil, err
- }
- gpuProg := &gpuProgram{
- backend: b,
- obj: p,
- nattr: len(attr),
- }
- b.BindProgram(gpuProg)
- // Bind texture uniforms.
- for _, tex := range vertShader.Textures {
- u := b.funcs.GetUniformLocation(p, tex.Name)
- if u.valid() {
- b.funcs.Uniform1i(u, tex.Binding)
- }
- }
- for _, tex := range fragShader.Textures {
- u := b.funcs.GetUniformLocation(p, tex.Name)
- if u.valid() {
- b.funcs.Uniform1i(u, tex.Binding)
- }
- }
- if b.ubo {
- for _, block := range vertShader.Uniforms.Blocks {
- blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
- if blockIdx != INVALID_INDEX {
- b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding))
- }
- }
- // To match Direct3D 11 with separate vertex and fragment
- // shader uniform buffers, offset all fragment blocks to be
- // located after the vertex blocks.
- off := len(vertShader.Uniforms.Blocks)
- for _, block := range fragShader.Uniforms.Blocks {
- blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
- if blockIdx != INVALID_INDEX {
- b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding+off))
- }
- }
- } else {
- gpuProg.vertUniforms.setup(b.funcs, p, vertShader.Uniforms.Size, vertShader.Uniforms.Locations)
- gpuProg.fragUniforms.setup(b.funcs, p, fragShader.Uniforms.Size, fragShader.Uniforms.Locations)
- }
- return gpuProg, nil
-}
-
-func lookupUniform(funcs Functions, p Program, loc backend.UniformLocation) uniformLocation {
- u := GetUniformLocation(funcs, p, loc.Name)
- return uniformLocation{uniform: u, offset: loc.Offset, typ: loc.Type, size: loc.Size}
-}
-
-func (p *gpuProgram) SetVertexUniforms(buffer backend.Buffer) {
- p.vertUniforms.setBuffer(buffer)
-}
-
-func (p *gpuProgram) SetFragmentUniforms(buffer backend.Buffer) {
- p.fragUniforms.setBuffer(buffer)
-}
-
-func (p *gpuProgram) updateUniforms() {
- f := p.backend.funcs
- if p.backend.ubo {
- if b := p.vertUniforms.buf; b != nil {
- f.BindBufferBase(UNIFORM_BUFFER, 0, b.obj)
- }
- if b := p.fragUniforms.buf; b != nil {
- f.BindBufferBase(UNIFORM_BUFFER, 1, b.obj)
- }
- } else {
- p.vertUniforms.update(f)
- p.fragUniforms.update(f)
- }
-}
-
-func (b *Backend) BindProgram(prog backend.Program) {
- p := prog.(*gpuProgram)
- b.useProgram(p)
-}
-
-func (p *gpuProgram) Release() {
- p.backend.funcs.DeleteProgram(p.obj)
-}
-
-func (u *uniformsTracker) setup(funcs Functions, p Program, uniformSize int, uniforms []backend.UniformLocation) {
- u.locs = make([]uniformLocation, len(uniforms))
- for i, uniform := range uniforms {
- u.locs[i] = lookupUniform(funcs, p, uniform)
- }
- u.size = uniformSize
-}
-
-func (u *uniformsTracker) setBuffer(buffer backend.Buffer) {
- buf := buffer.(*gpuBuffer)
- if buf.typ&backend.BufferBindingUniforms == 0 {
- panic("not a uniform buffer")
- }
- if buf.size < u.size {
- panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, u.size))
- }
- u.buf = buf
- // Force update.
- u.version = buf.version - 1
-}
-
-func (p *uniformsTracker) update(funcs Functions) {
- b := p.buf
- if b == nil || b.version == p.version {
- return
- }
- p.version = b.version
- data := b.data
- for _, u := range p.locs {
- data := data[u.offset:]
- switch {
- case u.typ == backend.DataTypeFloat && u.size == 1:
- data := data[:4]
- v := *(*[1]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform1f(u.uniform, v[0])
- case u.typ == backend.DataTypeFloat && u.size == 2:
- data := data[:8]
- v := *(*[2]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform2f(u.uniform, v[0], v[1])
- case u.typ == backend.DataTypeFloat && u.size == 3:
- data := data[:12]
- v := *(*[3]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform3f(u.uniform, v[0], v[1], v[2])
- case u.typ == backend.DataTypeFloat && u.size == 4:
- data := data[:16]
- v := *(*[4]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3])
- default:
- panic("unsupported uniform data type or size")
- }
- }
-}
-
-func (b *gpuBuffer) Upload(data []byte) {
- if b.immutable {
- panic("immutable buffer")
- }
- if len(data) > b.size {
- panic("buffer size overflow")
- }
- b.version++
- copy(b.data, data)
- if b.hasBuffer {
- firstBinding := firstBufferType(b.typ)
- b.backend.funcs.BindBuffer(firstBinding, b.obj)
- b.backend.funcs.BufferData(firstBinding, data, STATIC_DRAW)
- }
-}
-
-func (b *gpuBuffer) Release() {
- if b.hasBuffer {
- b.backend.funcs.DeleteBuffer(b.obj)
- b.hasBuffer = false
- }
-}
-
-func (b *Backend) BindVertexBuffer(buf backend.Buffer, stride, offset int) {
- gbuf := buf.(*gpuBuffer)
- if gbuf.typ&backend.BufferBindingVertices == 0 {
- panic("not a vertex buffer")
- }
- b.state.buffer = bufferBinding{buf: gbuf, stride: stride, offset: offset}
-}
-
-func (b *Backend) setupVertexArrays() {
- layout := b.state.layout
- if layout == nil {
- return
- }
- buf := b.state.buffer
- b.funcs.BindBuffer(ARRAY_BUFFER, buf.buf.obj)
- for i, inp := range layout.inputs {
- l := layout.layout[i]
- var gltyp Enum
- switch l.Type {
- case backend.DataTypeFloat:
- gltyp = FLOAT
- case backend.DataTypeShort:
- gltyp = SHORT
- default:
- panic("unsupported data type")
- }
- b.funcs.VertexAttribPointer(Attrib(inp.Location), l.Size, gltyp, false, buf.stride, buf.offset+l.Offset)
- }
-}
-
-func (b *Backend) BindIndexBuffer(buf backend.Buffer) {
- gbuf := buf.(*gpuBuffer)
- if gbuf.typ&backend.BufferBindingIndices == 0 {
- panic("not an index buffer")
- }
- b.funcs.BindBuffer(ELEMENT_ARRAY_BUFFER, gbuf.obj)
-}
-
-func (f *gpuFramebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
- glErr(f.backend.funcs)
- f.backend.BindFramebuffer(f)
- if len(pixels) < src.Dx()*src.Dy() {
- return errors.New("unexpected RGBA size")
- }
- f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), RGBA, UNSIGNED_BYTE, pixels)
- // OpenGL origin is in the lower-left corner. Flip the image to
- // match.
- flipImageY(src.Dx()*4, src.Dy(), pixels)
- return glErr(f.backend.funcs)
-}
-
-func flipImageY(stride int, height int, pixels []byte) {
- // Flip image in y-direction. OpenGL's origin is in the lower
- // left corner.
- row := make([]uint8, stride)
- for y := 0; y < height/2; y++ {
- y1 := height - y - 1
- dest := y1 * stride
- src := y * stride
- copy(row, pixels[dest:])
- copy(pixels[dest:], pixels[src:src+len(row)])
- copy(pixels[src:], row)
- }
-}
-
-func (b *Backend) BindFramebuffer(fbo backend.Framebuffer) {
- b.funcs.BindFramebuffer(FRAMEBUFFER, fbo.(*gpuFramebuffer).obj)
-}
-
-func (f *gpuFramebuffer) Invalidate() {
- f.backend.BindFramebuffer(f)
- f.backend.funcs.InvalidateFramebuffer(FRAMEBUFFER, COLOR_ATTACHMENT0)
-}
-
-func (f *gpuFramebuffer) Release() {
- if f.foreign {
- panic("cannot release framebuffer created by CurrentFramebuffer")
- }
- f.backend.funcs.DeleteFramebuffer(f.obj)
- if f.hasDepth {
- f.backend.funcs.DeleteRenderbuffer(f.depthBuf)
- }
-}
-
-func toTexFilter(f backend.TextureFilter) int {
- switch f {
- case backend.FilterNearest:
- return NEAREST
- case backend.FilterLinear:
- return LINEAR
- default:
- panic("unsupported texture filter")
- }
-}
-
-func (b *Backend) BindTexture(unit int, t backend.Texture) {
- b.bindTexture(unit, t.(*gpuTexture))
-}
-
-func (t *gpuTexture) Release() {
- t.backend.funcs.DeleteTexture(t.obj)
-}
-
-func (t *gpuTexture) Upload(img *image.RGBA) {
- t.backend.BindTexture(0, t)
- var pixels []byte
- b := img.Bounds()
- w, h := b.Dx(), b.Dy()
- if img.Stride != w*4 {
- panic("unsupported stride")
- }
- start := (b.Min.X + b.Min.Y*w) * 4
- end := (b.Max.X + (b.Max.Y-1)*w) * 4
- pixels = img.Pix[start:end]
- t.backend.funcs.TexImage2D(TEXTURE_2D, 0, t.triple.internalFormat, w, h, t.triple.format, t.triple.typ, pixels)
-}
-
-func (t *gpuTimer) Begin() {
- t.funcs.BeginQuery(TIME_ELAPSED_EXT, t.obj)
-}
-
-func (t *gpuTimer) End() {
- t.funcs.EndQuery(TIME_ELAPSED_EXT)
-}
-
-func (t *gpuTimer) ready() bool {
- return t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT_AVAILABLE) == TRUE
-}
-
-func (t *gpuTimer) Release() {
- t.funcs.DeleteQuery(t.obj)
-}
-
-func (t *gpuTimer) Duration() (time.Duration, bool) {
- if !t.ready() {
- return 0, false
- }
- nanos := t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT)
- return time.Duration(nanos), true
-}
-
-func (b *Backend) BindInputLayout(l backend.InputLayout) {
- b.state.layout = l.(*gpuInputLayout)
-}
-
-func (l *gpuInputLayout) Release() {}
-
-// floatTripleFor determines the best texture triple for floating point FBOs.
-func floatTripleFor(f Functions, ver [2]int, exts []string) (textureTriple, error) {
- var triples []textureTriple
- if ver[0] >= 3 {
- triples = append(triples, textureTriple{R16F, Enum(RED), Enum(HALF_FLOAT)})
- }
- // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to
- // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not
- // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're
- // wrong.
- if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") {
- // Try single channel.
- triples = append(triples, textureTriple{LUMINANCE, Enum(LUMINANCE), Enum(HALF_FLOAT_OES)})
- // Fallback to 4 channels.
- triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(HALF_FLOAT_OES)})
- }
- if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") {
- triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(FLOAT)})
- }
- tex := f.CreateTexture()
- defer f.DeleteTexture(tex)
- f.BindTexture(TEXTURE_2D, tex)
- f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
- f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
- f.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST)
- f.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST)
- fbo := f.CreateFramebuffer()
- defer f.DeleteFramebuffer(fbo)
- defFBO := Framebuffer(f.GetBinding(FRAMEBUFFER_BINDING))
- f.BindFramebuffer(FRAMEBUFFER, fbo)
- defer f.BindFramebuffer(FRAMEBUFFER, defFBO)
- var attempts []string
- for _, tt := range triples {
- const size = 256
- f.TexImage2D(TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil)
- f.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, tex, 0)
- st := f.CheckFramebufferStatus(FRAMEBUFFER)
- if st == FRAMEBUFFER_COMPLETE {
- return tt, nil
- }
- attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st))
- }
- return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts)
-}
-
-func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
- switch {
- case ver[0] >= 3:
- return textureTriple{SRGB8_ALPHA8, Enum(RGBA), Enum(UNSIGNED_BYTE)}, nil
- case hasExtension(exts, "GL_EXT_sRGB"):
- return textureTriple{SRGB_ALPHA_EXT, Enum(SRGB_ALPHA_EXT), Enum(UNSIGNED_BYTE)}, nil
- default:
- return textureTriple{}, errors.New("no sRGB texture formats found")
- }
-}
-
-func alphaTripleFor(ver [2]int) textureTriple {
- intf, f := R8, Enum(RED)
- if ver[0] < 3 {
- // R8, RED not supported on OpenGL ES 2.0.
- intf, f = LUMINANCE, Enum(LUMINANCE)
- }
- return textureTriple{intf, f, UNSIGNED_BYTE}
-}
-
-func hasExtension(exts []string, ext string) bool {
- for _, e := range exts {
- if ext == e {
- return true
- }
- }
- return false
-}
-
-func firstBufferType(typ backend.BufferBinding) Enum {
- switch {
- case typ&backend.BufferBindingIndices != 0:
- return ELEMENT_ARRAY_BUFFER
- case typ&backend.BufferBindingVertices != 0:
- return ARRAY_BUFFER
- case typ&backend.BufferBindingUniforms != 0:
- return UNIFORM_BUFFER
- default:
- panic("unsupported buffer type")
- }
-}
diff --git a/vendor/gioui.org/gpu/gl/types.go b/vendor/gioui.org/gpu/gl/types.go
deleted file mode 100644
index f986bba..0000000
--- a/vendor/gioui.org/gpu/gl/types.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// +build !js
-
-package gl
-
-type (
- Buffer struct{ V uint }
- Framebuffer struct{ V uint }
- Program struct{ V uint }
- Renderbuffer struct{ V uint }
- Shader struct{ V uint }
- Texture struct{ V uint }
- Query struct{ V uint }
- Uniform struct{ V int }
- Object struct{ V uint }
-)
-
-func (u Uniform) valid() bool {
- return u.V != -1
-}
-
-func (p Program) valid() bool {
- return p.V != 0
-}
-
-func (s Shader) valid() bool {
- return s.V != 0
-}
diff --git a/vendor/gioui.org/gpu/gl/types_js.go b/vendor/gioui.org/gpu/gl/types_js.go
deleted file mode 100644
index 16e3a71..0000000
--- a/vendor/gioui.org/gpu/gl/types_js.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gl
-
-import "syscall/js"
-
-type (
- Buffer js.Value
- Framebuffer js.Value
- Program js.Value
- Renderbuffer js.Value
- Shader js.Value
- Texture js.Value
- Query js.Value
- Uniform js.Value
- Object js.Value
-)
-
-func (p Program) valid() bool {
- return !js.Value(p).IsUndefined() && !js.Value(p).IsNull()
-}
-
-func (s Shader) valid() bool {
- return !js.Value(s).IsUndefined() && !js.Value(s).IsNull()
-}
-
-func (u Uniform) valid() bool {
- return !js.Value(u).IsUndefined() && !js.Value(u).IsNull()
-}
diff --git a/vendor/gioui.org/gpu/gpu.go b/vendor/gioui.org/gpu/gpu.go
index 321b2a8..0bd15c6 100644
--- a/vendor/gioui.org/gpu/gpu.go
+++ b/vendor/gioui.org/gpu/gpu.go
@@ -13,36 +13,57 @@ import (
"image"
"image/color"
"math"
+ "os"
"reflect"
"time"
"unsafe"
"gioui.org/f32"
- "gioui.org/gpu/backend"
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/byteslice"
"gioui.org/internal/f32color"
- "gioui.org/internal/opconst"
"gioui.org/internal/ops"
- gunsafe "gioui.org/internal/unsafe"
+ "gioui.org/internal/scene"
+ "gioui.org/internal/stroke"
"gioui.org/layout"
"gioui.org/op"
- "gioui.org/op/paint"
+ "gioui.org/shader"
+ "gioui.org/shader/gio"
+
+ // Register backends.
+ _ "gioui.org/gpu/internal/d3d11"
+ _ "gioui.org/gpu/internal/metal"
+ _ "gioui.org/gpu/internal/opengl"
+ _ "gioui.org/gpu/internal/vulkan"
)
-type GPU struct {
+type GPU interface {
+ // Release non-Go resources. The GPU is no longer valid after Release.
+ Release()
+ // Clear sets the clear color for the next Frame.
+ Clear(color color.NRGBA)
+ // Frame draws the graphics operations from op into a viewport of target.
+ Frame(frame *op.Ops, target RenderTarget, viewport image.Point) error
+ // Profile returns the last available profiling information. Profiling
+ // information is requested when Frame sees an io/profile.Op, and the result
+ // is available through Profile at some later time.
+ Profile() string
+}
+
+type gpu struct {
cache *resourceCache
- defFBO backend.Framebuffer
- profile string
- timers *timers
- frameStart time.Time
- zopsTimer, stencilTimer, coverTimer, cleanupTimer *timer
- drawOps drawOps
- ctx backend.Device
- renderer *renderer
+ profile string
+ timers *timers
+ frameStart time.Time
+ stencilTimer, coverTimer, cleanupTimer *timer
+ drawOps drawOps
+ ctx driver.Device
+ renderer *renderer
}
type renderer struct {
- ctx backend.Device
+ ctx driver.Device
blitter *blitter
pather *pather
packer packer
@@ -50,17 +71,15 @@ type renderer struct {
}
type drawOps struct {
- profile bool
- reader ops.Reader
- cache *resourceCache
- vertCache []byte
- viewport image.Point
- clearColor f32color.RGBA
- imageOps []imageOp
- // zimageOps are the rectangle clipped opaque images
- // that can use fast front-to-back rendering with z-test
- // and no blending.
- zimageOps []imageOp
+ profile bool
+ reader ops.Reader
+ states []f32.Affine2D
+ transStack []f32.Affine2D
+ vertCache []byte
+ viewport image.Point
+ clear bool
+ clearColor f32color.RGBA
+ imageOps []imageOp
pathOps []*pathOp
pathOpCache []pathOp
qs quadSplitter
@@ -68,26 +87,35 @@ type drawOps struct {
}
type drawState struct {
- clip f32.Rectangle
t f32.Affine2D
cpath *pathOp
- rect bool
- z int
matType materialType
// Current paint.ImageOp
image imageOpData
// Current paint.ColorOp, if any.
- color color.RGBA
+ color color.NRGBA
+
+ // Current paint.LinearGradientOp.
+ stop1 f32.Point
+ stop2 f32.Point
+ color1 color.NRGBA
+ color2 color.NRGBA
}
type pathOp struct {
off f32.Point
+ // rect tracks whether the clip stack can be represented by a
+ // pixel-aligned rectangle.
+ rect bool
// clip is the union of all
// later clip rectangles.
- clip image.Rectangle
- bounds f32.Rectangle
- pathKey ops.Key
+ clip image.Rectangle
+ bounds f32.Rectangle
+ // intersect is the intersection of bounds and all
+ // previous clip bounds.
+ intersect f32.Rectangle
+ pathKey opKey
path bool
pathVerts []byte
parent *pathOp
@@ -95,87 +123,70 @@ type pathOp struct {
}
type imageOp struct {
- z float32
path *pathOp
- off f32.Point
clip image.Rectangle
material material
clipType clipType
place placement
}
+func decodeStrokeOp(data []byte) float32 {
+ _ = data[4]
+ bo := binary.LittleEndian
+ return math.Float32frombits(bo.Uint32(data[1:]))
+}
+
+type quadsOp struct {
+ key opKey
+ aux []byte
+}
+
+type opKey struct {
+ outline bool
+ strokeWidth float32
+ sx, hx, sy, hy float32
+ ops.Key
+}
+
type material struct {
material materialType
opaque bool
// For materialTypeColor.
color f32color.RGBA
+ // For materialTypeLinearGradient.
+ color1 f32color.RGBA
+ color2 f32color.RGBA
// For materialTypeTexture.
- texture *texture
+ data imageOpData
uvTrans f32.Affine2D
}
-// clipOp is the shadow of clip.Op.
-type clipOp struct {
- bounds f32.Rectangle
-}
-
// imageOpData is the shadow of paint.ImageOp.
type imageOpData struct {
- rect image.Rectangle
src *image.RGBA
handle interface{}
}
-func (op *clipOp) decode(data []byte) {
- if opconst.OpType(data[0]) != opconst.TypeClip {
- panic("invalid op")
- }
- bo := binary.LittleEndian
- r := f32.Rectangle{
- Min: f32.Point{
- X: math.Float32frombits(bo.Uint32(data[1:])),
- Y: math.Float32frombits(bo.Uint32(data[5:])),
- },
- Max: f32.Point{
- X: math.Float32frombits(bo.Uint32(data[9:])),
- Y: math.Float32frombits(bo.Uint32(data[13:])),
- },
- }
- *op = clipOp{
- bounds: r,
- }
+type linearGradientOpData struct {
+ stop1 f32.Point
+ color1 color.NRGBA
+ stop2 f32.Point
+ color2 color.NRGBA
}
func decodeImageOp(data []byte, refs []interface{}) imageOpData {
- if opconst.OpType(data[0]) != opconst.TypeImage {
- panic("invalid op")
- }
handle := refs[1]
if handle == nil {
return imageOpData{}
}
- bo := binary.LittleEndian
return imageOpData{
- rect: image.Rectangle{
- Min: image.Point{
- X: int(bo.Uint32(data[1:])),
- Y: int(bo.Uint32(data[5:])),
- },
- Max: image.Point{
- X: int(bo.Uint32(data[9:])),
- Y: int(bo.Uint32(data[13:])),
- },
- },
src: refs[0].(*image.RGBA),
handle: handle,
}
}
-func decodeColorOp(data []byte) color.RGBA {
- if opconst.OpType(data[0]) != opconst.TypeColor {
- panic("invalid op")
- }
- return color.RGBA{
+func decodeColorOp(data []byte) color.NRGBA {
+ return color.NRGBA{
R: data[1],
G: data[2],
B: data[3],
@@ -183,23 +194,29 @@ func decodeColorOp(data []byte) color.RGBA {
}
}
-func decodePaintOp(data []byte) paint.PaintOp {
+func decodeLinearGradientOp(data []byte) linearGradientOpData {
bo := binary.LittleEndian
- if opconst.OpType(data[0]) != opconst.TypePaint {
- panic("invalid op")
- }
- r := f32.Rectangle{
- Min: f32.Point{
+ return linearGradientOpData{
+ stop1: f32.Point{
X: math.Float32frombits(bo.Uint32(data[1:])),
Y: math.Float32frombits(bo.Uint32(data[5:])),
},
- Max: f32.Point{
+ stop2: f32.Point{
X: math.Float32frombits(bo.Uint32(data[9:])),
Y: math.Float32frombits(bo.Uint32(data[13:])),
},
- }
- return paint.PaintOp{
- Rect: r,
+ color1: color.NRGBA{
+ R: data[17+0],
+ G: data[17+1],
+ B: data[17+2],
+ A: data[17+3],
+ },
+ color2: color.NRGBA{
+ R: data[21+0],
+ G: data[21+1],
+ B: data[21+2],
+ A: data[21+3],
+ },
}
}
@@ -211,58 +228,60 @@ type resource interface {
type texture struct {
src *image.RGBA
- tex backend.Texture
+ tex driver.Texture
}
type blitter struct {
- ctx backend.Device
- viewport image.Point
- prog [2]*program
- layout backend.InputLayout
- colUniforms *blitColUniforms
- texUniforms *blitTexUniforms
- quadVerts backend.Buffer
+ ctx driver.Device
+ viewport image.Point
+ pipelines [3]*pipeline
+ colUniforms *blitColUniforms
+ texUniforms *blitTexUniforms
+ linearGradientUniforms *blitLinearGradientUniforms
+ quadVerts driver.Buffer
}
type blitColUniforms struct {
- vert struct {
- blitUniforms
- _ [12]byte // Padding to a multiple of 16.
- }
- frag struct {
- colorUniforms
- }
+ blitUniforms
+ _ [128 - unsafe.Sizeof(blitUniforms{}) - unsafe.Sizeof(colorUniforms{})]byte // Padding to 128 bytes.
+ colorUniforms
}
type blitTexUniforms struct {
- vert struct {
- blitUniforms
- _ [12]byte // Padding to a multiple of 16.
- }
+ blitUniforms
+}
+
+type blitLinearGradientUniforms struct {
+ blitUniforms
+ _ [128 - unsafe.Sizeof(blitUniforms{}) - unsafe.Sizeof(gradientUniforms{})]byte // Padding to 128 bytes.
+ gradientUniforms
}
type uniformBuffer struct {
- buf backend.Buffer
+ buf driver.Buffer
ptr []byte
}
-type program struct {
- prog backend.Program
- vertUniforms *uniformBuffer
- fragUniforms *uniformBuffer
+type pipeline struct {
+ pipeline driver.Pipeline
+ uniforms *uniformBuffer
}
type blitUniforms struct {
transform [4]float32
uvTransformR1 [4]float32
uvTransformR2 [4]float32
- z float32
}
type colorUniforms struct {
color f32color.RGBA
}
+type gradientUniforms struct {
+ color1 f32color.RGBA
+ color2 f32color.RGBA
+}
+
type materialType uint8
const (
@@ -273,14 +292,37 @@ const (
const (
materialColor materialType = iota
+ materialLinearGradient
materialTexture
)
-func New(ctx backend.Device) (*GPU, error) {
- defFBO := ctx.CurrentFramebuffer()
- g := &GPU{
- defFBO: defFBO,
- cache: newResourceCache(),
+// New creates a GPU for the given API.
+func New(api API) (GPU, error) {
+ d, err := driver.NewDevice(api)
+ if err != nil {
+ return nil, err
+ }
+ return NewWithDevice(d)
+}
+
+// NewWithDevice creates a GPU with a pre-existing device.
+//
+// Note: for internal use only.
+func NewWithDevice(d driver.Device) (GPU, error) {
+ d.BeginFrame(nil, false, image.Point{})
+ defer d.EndFrame()
+ forceCompute := os.Getenv("GIORENDERER") == "forcecompute"
+ feats := d.Caps().Features
+ switch {
+ case !forceCompute && feats.Has(driver.FeatureFloatRenderTargets) && feats.Has(driver.FeatureSRGB):
+ return newGPU(d)
+ }
+ return newCompute(d)
+}
+
+func newGPU(ctx driver.Device) (*gpu, error) {
+ g := &gpu{
+ cache: newResourceCache(),
}
g.drawOps.pathCache = newOpCache()
if err := g.init(ctx); err != nil {
@@ -289,111 +331,116 @@ func New(ctx backend.Device) (*GPU, error) {
return g, nil
}
-func (g *GPU) init(ctx backend.Device) error {
+func (g *gpu) init(ctx driver.Device) error {
g.ctx = ctx
g.renderer = newRenderer(ctx)
return nil
}
-func (g *GPU) Release() {
+func (g *gpu) Clear(col color.NRGBA) {
+ g.drawOps.clear = true
+ g.drawOps.clearColor = f32color.LinearFromSRGB(col)
+}
+
+func (g *gpu) Release() {
g.renderer.release()
g.drawOps.pathCache.release()
g.cache.release()
if g.timers != nil {
- g.timers.release()
+ g.timers.Release()
}
+ g.ctx.Release()
+}
+
+func (g *gpu) Frame(frameOps *op.Ops, target RenderTarget, viewport image.Point) error {
+ g.collect(viewport, frameOps)
+ return g.frame(target)
}
-func (g *GPU) Collect(viewport image.Point, frameOps *op.Ops) {
+func (g *gpu) collect(viewport image.Point, frameOps *op.Ops) {
g.renderer.blitter.viewport = viewport
g.renderer.pather.viewport = viewport
- g.drawOps.reset(g.cache, viewport)
- g.drawOps.collect(g.cache, frameOps, viewport)
+ g.drawOps.reset(viewport)
+ g.drawOps.collect(frameOps, viewport)
g.frameStart = time.Now()
- if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(backend.FeatureTimers) {
+ if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) {
g.timers = newTimers(g.ctx)
- g.zopsTimer = g.timers.newTimer()
g.stencilTimer = g.timers.newTimer()
g.coverTimer = g.timers.newTimer()
g.cleanupTimer = g.timers.newTimer()
}
- for _, p := range g.drawOps.pathOps {
- if v, exists := g.drawOps.pathCache.get(p.pathKey); !exists || v.data.data == nil {
- data := buildPath(g.ctx, p.pathVerts)
- g.drawOps.pathCache.put(p.pathKey, opCacheValue{
- data: data,
- bounds: p.bounds,
- })
- }
- p.pathVerts = nil
- }
}
-func (g *GPU) BeginFrame() {
- g.ctx.BeginFrame()
- defer g.ctx.EndFrame()
+func (g *gpu) frame(target RenderTarget) error {
viewport := g.renderer.blitter.viewport
+ defFBO := g.ctx.BeginFrame(target, g.drawOps.clear, viewport)
+ defer g.ctx.EndFrame()
+ g.drawOps.buildPaths(g.ctx)
for _, img := range g.drawOps.imageOps {
expandPathOp(img.path, img.clip)
}
- if g.drawOps.profile {
- g.zopsTimer.begin()
- }
- g.ctx.BindFramebuffer(g.defFBO)
- g.ctx.DepthFunc(backend.DepthFuncGreater)
- g.ctx.ClearDepth(0.0)
- g.ctx.Clear(g.drawOps.clearColor.Float32())
- g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
- g.renderer.drawZOps(g.drawOps.zimageOps)
- g.zopsTimer.end()
g.stencilTimer.begin()
- g.ctx.SetBlend(true)
g.renderer.packStencils(&g.drawOps.pathOps)
g.renderer.stencilClips(g.drawOps.pathCache, g.drawOps.pathOps)
g.renderer.packIntersections(g.drawOps.imageOps)
+ g.renderer.prepareIntersections(g.drawOps.imageOps)
g.renderer.intersect(g.drawOps.imageOps)
g.stencilTimer.end()
g.coverTimer.begin()
- g.ctx.BindFramebuffer(g.defFBO)
+ g.renderer.uploadImages(g.cache, g.drawOps.imageOps)
+ g.renderer.prepareDrawOps(g.cache, g.drawOps.imageOps)
+ d := driver.LoadDesc{
+ ClearColor: g.drawOps.clearColor,
+ }
+ if g.drawOps.clear {
+ g.drawOps.clear = false
+ d.Action = driver.LoadActionClear
+ }
+ g.ctx.BeginRenderPass(defFBO, d)
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
- g.renderer.drawOps(g.drawOps.imageOps)
- g.ctx.SetBlend(false)
- g.renderer.pather.stenciler.invalidateFBO()
+ g.renderer.drawOps(g.cache, g.drawOps.imageOps)
g.coverTimer.end()
- g.ctx.BindFramebuffer(g.defFBO)
-}
-
-func (g *GPU) EndFrame() {
+ g.ctx.EndRenderPass()
g.cleanupTimer.begin()
g.cache.frame()
g.drawOps.pathCache.frame()
g.cleanupTimer.end()
if g.drawOps.profile && g.timers.ready() {
- zt, st, covt, cleant := g.zopsTimer.Elapsed, g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
- ft := zt + st + covt + cleant
+ st, covt, cleant := g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
+ ft := st + covt + cleant
q := 100 * time.Microsecond
- zt, st, covt = zt.Round(q), st.Round(q), covt.Round(q)
+ st, covt = st.Round(q), covt.Round(q)
frameDur := time.Since(g.frameStart).Round(q)
ft = ft.Round(q)
- g.profile = fmt.Sprintf("draw:%7s gpu:%7s zt:%7s st:%7s cov:%7s", frameDur, ft, zt, st, covt)
+ g.profile = fmt.Sprintf("draw:%7s gpu:%7s st:%7s cov:%7s", frameDur, ft, st, covt)
}
+ return nil
}
-func (g *GPU) Profile() string {
+func (g *gpu) Profile() string {
return g.profile
}
-func (r *renderer) texHandle(t *texture) backend.Texture {
- if t.tex != nil {
- return t.tex
+func (r *renderer) texHandle(cache *resourceCache, data imageOpData) driver.Texture {
+ var tex *texture
+ t, exists := cache.get(data.handle)
+ if !exists {
+ t = &texture{
+ src: data.src,
+ }
+ cache.put(data.handle, t)
}
- tex, err := r.ctx.NewTexture(backend.TextureFormatSRGB, t.src.Bounds().Dx(), t.src.Bounds().Dy(), backend.FilterLinear, backend.FilterLinear, backend.BufferBindingTexture)
+ tex = t.(*texture)
+ if tex.tex != nil {
+ return tex.tex
+ }
+ handle, err := r.ctx.NewTexture(driver.TextureFormatSRGBA, data.src.Bounds().Dx(), data.src.Bounds().Dy(), driver.FilterLinear, driver.FilterLinear, driver.BufferBindingTexture)
if err != nil {
panic(err)
}
- tex.Upload(t.src)
- t.tex = tex
- return t.tex
+ driver.UploadImage(handle, image.Pt(0, 0), data.src)
+ tex.tex = handle
+ return tex.tex
}
func (t *texture) release() {
@@ -402,14 +449,22 @@ func (t *texture) release() {
}
}
-func newRenderer(ctx backend.Device) *renderer {
+func newRenderer(ctx driver.Device) *renderer {
r := &renderer{
ctx: ctx,
blitter: newBlitter(ctx),
pather: newPather(ctx),
}
- r.packer.maxDim = ctx.Caps().MaxTextureSize
- r.intersections.maxDim = r.packer.maxDim
+
+ maxDim := ctx.Caps().MaxTextureSize
+ // Large atlas textures cause artifacts due to precision loss in
+ // shaders.
+ if cap := 8192; maxDim > cap {
+ maxDim = cap
+ }
+
+ r.packer.maxDims = image.Pt(maxDim, maxDim)
+ r.intersections.maxDims = image.Pt(maxDim, maxDim)
return r
}
@@ -418,13 +473,13 @@ func (r *renderer) release() {
r.blitter.release()
}
-func newBlitter(ctx backend.Device) *blitter {
- quadVerts, err := ctx.NewImmutableBuffer(backend.BufferBindingVertices,
- gunsafe.BytesView([]float32{
- -1, +1, 0, 0,
- +1, +1, 1, 0,
- -1, -1, 0, 1,
- +1, -1, 1, 1,
+func newBlitter(ctx driver.Device) *blitter {
+ quadVerts, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices,
+ byteslice.Slice([]float32{
+ -1, -1, 0, 0,
+ +1, -1, 1, 0,
+ -1, +1, 0, 1,
+ +1, +1, 1, 1,
}),
)
if err != nil {
@@ -436,65 +491,125 @@ func newBlitter(ctx backend.Device) *blitter {
}
b.colUniforms = new(blitColUniforms)
b.texUniforms = new(blitTexUniforms)
- prog, layout, err := createColorPrograms(ctx, shader_blit_vert, shader_blit_frag,
- [2]interface{}{&b.colUniforms.vert, &b.texUniforms.vert}, [2]interface{}{&b.colUniforms.frag, nil})
+ b.linearGradientUniforms = new(blitLinearGradientUniforms)
+ pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag,
+ [3]interface{}{b.colUniforms, b.linearGradientUniforms, b.texUniforms},
+ )
if err != nil {
panic(err)
}
- b.prog = prog
- b.layout = layout
+ b.pipelines = pipelines
return b
}
func (b *blitter) release() {
b.quadVerts.Release()
- for _, p := range b.prog {
+ for _, p := range b.pipelines {
p.Release()
}
- b.layout.Release()
}
-func createColorPrograms(b backend.Device, vsSrc backend.ShaderSources, fsSrc [2]backend.ShaderSources, vertUniforms, fragUniforms [2]interface{}) ([2]*program, backend.InputLayout, error) {
- var progs [2]*program
- prog, err := b.NewProgram(vsSrc, fsSrc[materialTexture])
- if err != nil {
- return progs, nil, err
+func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, uniforms [3]interface{}) ([3]*pipeline, error) {
+ var pipelines [3]*pipeline
+ blend := driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorOne,
+ DstFactor: driver.BlendFactorOneMinusSrcAlpha,
}
- var vertBuffer *uniformBuffer
- if u := vertUniforms[materialTexture]; u != nil {
- vertBuffer = newUniformBuffer(b, u)
- prog.SetVertexUniforms(vertBuffer.buf)
- }
- var fragBuffer *uniformBuffer
- if u := fragUniforms[materialTexture]; u != nil {
- fragBuffer = newUniformBuffer(b, u)
- prog.SetFragmentUniforms(fragBuffer.buf)
+ layout := driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: 4 * 4,
}
- progs[materialTexture] = newProgram(prog, vertBuffer, fragBuffer)
- prog, err = b.NewProgram(vsSrc, fsSrc[materialColor])
+ vsh, err := b.NewVertexShader(vsSrc)
if err != nil {
- progs[materialTexture].Release()
- return progs, nil, err
- }
- if u := vertUniforms[materialColor]; u != nil {
- vertBuffer = newUniformBuffer(b, u)
- prog.SetVertexUniforms(vertBuffer.buf)
- }
- if u := fragUniforms[materialColor]; u != nil {
- fragBuffer = newUniformBuffer(b, u)
- prog.SetFragmentUniforms(fragBuffer.buf)
- }
- progs[materialColor] = newProgram(prog, vertBuffer, fragBuffer)
- layout, err := b.NewInputLayout(vsSrc, []backend.InputDesc{
- {Type: backend.DataTypeFloat, Size: 2, Offset: 0},
- {Type: backend.DataTypeFloat, Size: 2, Offset: 4 * 2},
- })
+ return pipelines, err
+ }
+ defer vsh.Release()
+ {
+ fsh, err := b.NewFragmentShader(fsSrc[materialTexture])
+ if err != nil {
+ return pipelines, err
+ }
+ defer fsh.Release()
+ pipe, err := b.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ BlendDesc: blend,
+ VertexLayout: layout,
+ PixelFormat: driver.TextureFormatOutput,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ if err != nil {
+ return pipelines, err
+ }
+ var vertBuffer *uniformBuffer
+ if u := uniforms[materialTexture]; u != nil {
+ vertBuffer = newUniformBuffer(b, u)
+ }
+ pipelines[materialTexture] = &pipeline{pipe, vertBuffer}
+ }
+ {
+ var vertBuffer *uniformBuffer
+ fsh, err := b.NewFragmentShader(fsSrc[materialColor])
+ if err != nil {
+ pipelines[materialTexture].Release()
+ return pipelines, err
+ }
+ defer fsh.Release()
+ pipe, err := b.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ BlendDesc: blend,
+ VertexLayout: layout,
+ PixelFormat: driver.TextureFormatOutput,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ if err != nil {
+ pipelines[materialTexture].Release()
+ return pipelines, err
+ }
+ if u := uniforms[materialColor]; u != nil {
+ vertBuffer = newUniformBuffer(b, u)
+ }
+ pipelines[materialColor] = &pipeline{pipe, vertBuffer}
+ }
+ {
+ var vertBuffer *uniformBuffer
+ fsh, err := b.NewFragmentShader(fsSrc[materialLinearGradient])
+ if err != nil {
+ pipelines[materialTexture].Release()
+ pipelines[materialColor].Release()
+ return pipelines, err
+ }
+ defer fsh.Release()
+ pipe, err := b.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ BlendDesc: blend,
+ VertexLayout: layout,
+ PixelFormat: driver.TextureFormatOutput,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ if err != nil {
+ pipelines[materialTexture].Release()
+ pipelines[materialColor].Release()
+ return pipelines, err
+ }
+ if u := uniforms[materialLinearGradient]; u != nil {
+ vertBuffer = newUniformBuffer(b, u)
+ }
+ pipelines[materialLinearGradient] = &pipeline{pipe, vertBuffer}
+ }
if err != nil {
- progs[materialTexture].Release()
- progs[materialColor].Release()
- return progs, nil, err
+ for _, p := range pipelines {
+ p.Release()
+ }
+ return pipelines, err
}
- return progs, layout, nil
+ return pipelines, nil
}
func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
@@ -505,14 +620,31 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
r.pather.begin(r.packer.sizes)
for _, p := range ops {
if fbo != p.place.Idx {
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
fbo = p.place.Idx
f := r.pather.stenciler.cover(fbo)
- r.ctx.BindFramebuffer(f.fbo)
- r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
+ r.ctx.BeginRenderPass(f.tex, driver.LoadDesc{Action: driver.LoadActionClear})
+ r.ctx.BindPipeline(r.pather.stenciler.pipeline.pipeline.pipeline)
+ r.ctx.BindIndexBuffer(r.pather.stenciler.indexBuf)
}
v, _ := pathCache.get(p.pathKey)
r.pather.stencilPath(p.clip, p.off, p.place.Pos, v.data)
}
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
+}
+
+func (r *renderer) prepareIntersections(ops []imageOp) {
+ for _, img := range ops {
+ if img.clipType != clipTypeIntersection {
+ continue
+ }
+ fbo := r.pather.stenciler.cover(img.path.place.Idx)
+ r.ctx.PrepareTexture(fbo.tex)
+ }
}
func (r *renderer) intersect(ops []imageOp) {
@@ -521,21 +653,28 @@ func (r *renderer) intersect(ops []imageOp) {
}
fbo := -1
r.pather.stenciler.beginIntersect(r.intersections.sizes)
- r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
- r.ctx.BindInputLayout(r.pather.stenciler.iprog.layout)
for _, img := range ops {
if img.clipType != clipTypeIntersection {
continue
}
if fbo != img.place.Idx {
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
fbo = img.place.Idx
f := r.pather.stenciler.intersections.fbos[fbo]
- r.ctx.BindFramebuffer(f.fbo)
- r.ctx.Clear(1.0, 0.0, 0.0, 0.0)
+ d := driver.LoadDesc{Action: driver.LoadActionClear}
+ d.ClearColor.R = 1.0
+ r.ctx.BeginRenderPass(f.tex, d)
+ r.ctx.BindPipeline(r.pather.stenciler.ipipeline.pipeline.pipeline)
+ r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
}
r.ctx.Viewport(img.place.Pos.X, img.place.Pos.Y, img.clip.Dx(), img.clip.Dy())
r.intersectPath(img.path, img.clip)
}
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
}
func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
@@ -556,12 +695,12 @@ func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
}
fbo := r.pather.stenciler.cover(p.place.Idx)
r.ctx.BindTexture(0, fbo.tex)
- coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
- subScale, subOff := texSpaceTransform(toRectF(sub), p.clip.Size())
- r.pather.stenciler.iprog.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
- r.pather.stenciler.iprog.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y}
- r.pather.stenciler.iprog.prog.UploadUniforms()
- r.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4)
+ coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size)
+ subScale, subOff := texSpaceTransform(layout.FRect(sub), p.clip.Size())
+ r.pather.stenciler.ipipeline.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
+ r.pather.stenciler.ipipeline.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y}
+ r.pather.stenciler.ipipeline.pipeline.UploadUniforms(r.ctx)
+ r.ctx.DrawArrays(0, 4)
}
func (r *renderer) packIntersections(ops []imageOp) {
@@ -611,7 +750,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) {
if !ok {
// The clip area is at most the entire screen. Hopefully no
// screen is larger than GL_MAX_TEXTURE_SIZE.
- panic(fmt.Errorf("clip area %v is larger than maximum texture size %dx%d", p.clip, r.packer.maxDim, r.packer.maxDim))
+ panic(fmt.Errorf("clip area %v is larger than maximum texture size %v", p.clip, r.packer.maxDims))
}
p.place = place
i++
@@ -619,8 +758,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) {
*pops = ops
}
-// intersects intersects clip and b where b is offset by off.
-// ceilRect returns a bounding image.Rectangle for a f32.Rectangle.
+// boundRectF returns a bounding image.Rectangle for a f32.Rectangle.
func boundRectF(r f32.Rectangle) image.Rectangle {
return image.Rectangle{
Min: image.Point{
@@ -634,19 +772,6 @@ func boundRectF(r f32.Rectangle) image.Rectangle {
}
}
-func toRectF(r image.Rectangle) f32.Rectangle {
- return f32.Rectangle{
- Min: f32.Point{
- X: float32(r.Min.X),
- Y: float32(r.Min.Y),
- },
- Max: f32.Point{
- X: float32(r.Max.X),
- Y: float32(r.Max.Y),
- },
- }
-}
-
func ceil(v float32) int {
return int(math.Ceil(float64(v)))
}
@@ -655,30 +780,39 @@ func floor(v float32) int {
return int(math.Floor(float64(v)))
}
-func (d *drawOps) reset(cache *resourceCache, viewport image.Point) {
+func (d *drawOps) reset(viewport image.Point) {
d.profile = false
- d.clearColor = f32color.RGBA{R: 1.0, G: 1.0, B: 1.0, A: 1.0}
- d.cache = cache
d.viewport = viewport
d.imageOps = d.imageOps[:0]
- d.zimageOps = d.zimageOps[:0]
d.pathOps = d.pathOps[:0]
d.pathOpCache = d.pathOpCache[:0]
d.vertCache = d.vertCache[:0]
+ d.transStack = d.transStack[:0]
}
-func (d *drawOps) collect(cache *resourceCache, root *op.Ops, viewport image.Point) {
- d.reset(cache, viewport)
- clip := f32.Rectangle{
+func (d *drawOps) collect(root *op.Ops, viewport image.Point) {
+ viewf := f32.Rectangle{
Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)},
}
- d.reader.Reset(root)
- state := drawState{
- clip: clip,
- rect: true,
- color: color.RGBA{A: 0xff},
+ var ops *ops.Ops
+ if root != nil {
+ ops = &root.Internal
+ }
+ d.reader.Reset(ops)
+ d.collectOps(&d.reader, viewf)
+}
+
+func (d *drawOps) buildPaths(ctx driver.Device) {
+ for _, p := range d.pathOps {
+ if v, exists := d.pathCache.get(p.pathKey); !exists || v.data.data == nil {
+ data := buildPath(ctx, p.pathVerts)
+ d.pathCache.put(p.pathKey, opCacheValue{
+ data: data,
+ bounds: p.bounds,
+ })
+ }
+ p.pathVerts = nil
}
- d.collectOps(&d.reader, state)
}
func (d *drawOps) newPathOp() *pathOp {
@@ -686,24 +820,30 @@ func (d *drawOps) newPathOp() *pathOp {
return &d.pathOpCache[len(d.pathOpCache)-1]
}
-func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key, bounds f32.Rectangle, off f32.Point) {
+func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds f32.Rectangle, off f32.Point, push bool) {
npath := d.newPathOp()
*npath = pathOp{
- parent: state.cpath,
- bounds: bounds,
- off: off,
+ parent: state.cpath,
+ bounds: bounds,
+ off: off,
+ intersect: bounds.Add(off),
+ rect: true,
+ }
+ if npath.parent != nil {
+ npath.rect = npath.parent.rect
+ npath.intersect = npath.parent.intersect.Intersect(npath.intersect)
}
- state.cpath = npath
if len(aux) > 0 {
- state.rect = false
- state.cpath.pathKey = auxKey
- state.cpath.path = true
- state.cpath.pathVerts = aux
- d.pathOps = append(d.pathOps, state.cpath)
+ npath.rect = false
+ npath.pathKey = auxKey
+ npath.path = true
+ npath.pathVerts = aux
+ d.pathOps = append(d.pathOps, npath)
}
+ state.cpath = npath
}
-// split a transform into two parts, one which is pur offset and the
+// split a transform into two parts, one which is pure offset and the
// other representing the scaling, shearing and rotation part
func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) {
sx, hx, ox, hy, sy, oy := t.Elems()
@@ -712,121 +852,169 @@ func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) {
return
}
-func (d *drawOps) collectOps(r *ops.Reader, state drawState) int {
- var aux []byte
- var auxKey ops.Key
+func (d *drawOps) save(id int, state f32.Affine2D) {
+ if extra := id - len(d.states) + 1; extra > 0 {
+ d.states = append(d.states, make([]f32.Affine2D, extra)...)
+ }
+ d.states[id] = state
+}
+
+func (k opKey) SetTransform(t f32.Affine2D) opKey {
+ sx, hx, _, hy, sy, _ := t.Elems()
+ k.sx = sx
+ k.hx = hx
+ k.hy = hy
+ k.sy = sy
+ return k
+}
+
+func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) {
+ var (
+ quads quadsOp
+ state drawState
+ )
+ reset := func() {
+ state = drawState{
+ color: color.NRGBA{A: 0xff},
+ }
+ }
+ reset()
loop:
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
- switch opconst.OpType(encOp.Data[0]) {
- case opconst.TypeProfile:
+ switch ops.OpType(encOp.Data[0]) {
+ case ops.TypeProfile:
d.profile = true
- case opconst.TypeTransform:
- dop := ops.DecodeTransform(encOp.Data)
+ case ops.TypeTransform:
+ dop, push := ops.DecodeTransform(encOp.Data)
+ if push {
+ d.transStack = append(d.transStack, state.t)
+ }
state.t = state.t.Mul(dop)
- case opconst.TypeAux:
- aux = encOp.Data[opconst.TypeAuxLen:]
- auxKey = encOp.Key
- case opconst.TypeClip:
- var op clipOp
- op.decode(encOp.Data)
- bounds := op.bounds
+ case ops.TypePopTransform:
+ n := len(d.transStack)
+ state.t = d.transStack[n-1]
+ d.transStack = d.transStack[:n-1]
+
+ case ops.TypeStroke:
+ quads.key.strokeWidth = decodeStrokeOp(encOp.Data)
+
+ case ops.TypePath:
+ encOp, ok = r.Decode()
+ if !ok {
+ break loop
+ }
+ quads.aux = encOp.Data[ops.TypeAuxLen:]
+ quads.key.Key = encOp.Key
+
+ case ops.TypeClip:
+ var op ops.ClipOp
+ op.Decode(encOp.Data)
+ quads.key.outline = op.Outline
+ bounds := layout.FRect(op.Bounds)
trans, off := splitTransform(state.t)
- if len(aux) > 0 {
+ if len(quads.aux) > 0 {
// There is a clipping path, build the gpu data and update the
// cache key such that it will be equal only if the transform is the
// same also. Use cached data if we have it.
- auxKey = auxKey.SetTransform(trans)
- if v, ok := d.pathCache.get(auxKey); ok {
+ quads.key = quads.key.SetTransform(trans)
+ if v, ok := d.pathCache.get(quads.key); ok {
// Since the GPU data exists in the cache aux will not be used.
// Why is this not used for the offset shapes?
- op.bounds = v.bounds
+ bounds = v.bounds
} else {
- aux, op.bounds = d.buildVerts(aux, trans)
+ var pathData []byte
+ pathData, bounds = d.buildVerts(
+ quads.aux, trans, quads.key.outline, quads.key.strokeWidth,
+ )
+ quads.aux = pathData
// add it to the cache, without GPU data, so the transform can be
// reused.
- d.pathCache.put(auxKey, opCacheValue{bounds: op.bounds})
+ d.pathCache.put(quads.key, opCacheValue{bounds: bounds})
}
} else {
- aux, op.bounds, _ = d.boundsForTransformedRect(bounds, trans)
- auxKey = encOp.Key
- auxKey.SetTransform(trans)
+ quads.aux, bounds, _ = d.boundsForTransformedRect(bounds, trans)
+ quads.key = opKey{Key: encOp.Key}
}
- state.clip = state.clip.Intersect(op.bounds.Add(off))
- d.addClipPath(&state, aux, auxKey, op.bounds, off)
- aux = nil
- auxKey = ops.Key{}
- case opconst.TypeColor:
+ d.addClipPath(&state, quads.aux, quads.key, bounds, off, true)
+ quads = quadsOp{}
+ case ops.TypePopClip:
+ state.cpath = state.cpath.parent
+
+ case ops.TypeColor:
state.matType = materialColor
state.color = decodeColorOp(encOp.Data)
- case opconst.TypeImage:
+ case ops.TypeLinearGradient:
+ state.matType = materialLinearGradient
+ op := decodeLinearGradientOp(encOp.Data)
+ state.stop1 = op.stop1
+ state.stop2 = op.stop2
+ state.color1 = op.color1
+ state.color2 = op.color2
+ case ops.TypeImage:
state.matType = materialTexture
state.image = decodeImageOp(encOp.Data, encOp.Refs)
- case opconst.TypePaint:
- op := decodePaintOp(encOp.Data)
+ case ops.TypePaint:
// Transform (if needed) the painting rectangle and if so generate a clip path,
// for those cases also compute a partialTrans that maps texture coordinates between
// the new bounding rectangle and the transformed original paint rectangle.
- trans, off := splitTransform(state.t)
- clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, trans)
- clip := state.clip.Intersect(bnd.Add(off))
- if clip.Empty() {
+ t, off := splitTransform(state.t)
+ // Fill the clip area, unless the material is a (bounded) image.
+ // TODO: Find a tighter bound.
+ inf := float32(1e6)
+ dst := f32.Rect(-inf, -inf, inf, inf)
+ if state.matType == materialTexture {
+ sz := state.image.src.Rect.Size()
+ dst = f32.Rectangle{Max: layout.FPt(sz)}
+ }
+ clipData, bnd, partialTrans := d.boundsForTransformedRect(dst, t)
+ cl := viewport.Intersect(bnd.Add(off))
+ if state.cpath != nil {
+ cl = state.cpath.intersect.Intersect(cl)
+ }
+ if cl.Empty() {
continue
}
- wasrect := state.rect
if clipData != nil {
// The paint operation is sheared or rotated, add a clip path representing
// this transformed rectangle.
- encOp.Key.SetTransform(trans)
- d.addClipPath(&state, clipData, encOp.Key, bnd, off)
+ k := opKey{Key: encOp.Key}
+ k.SetTransform(t) // TODO: This call has no effect.
+ d.addClipPath(&state, clipData, k, bnd, off, false)
}
- bounds := boundRectF(clip)
- mat := state.materialFor(d.cache, bnd, off, partialTrans, bounds)
+ bounds := boundRectF(cl)
+ mat := state.materialFor(bnd, off, partialTrans, bounds)
- if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && state.rect && mat.opaque && mat.material == materialColor {
+ rect := state.cpath == nil || state.cpath.rect
+ if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && rect && mat.opaque && (mat.material == materialColor) {
// The image is a uniform opaque color and takes up the whole screen.
// Scrap images up to and including this image and set clear color.
- d.zimageOps = d.zimageOps[:0]
d.imageOps = d.imageOps[:0]
- state.z = 0
d.clearColor = mat.color.Opaque()
+ d.clear = true
continue
}
- state.z++
- if state.z != int(uint16(state.z)) {
- // TODO(eliasnaur) gioui.org/issue/127.
- panic("more than 65k paint objects not supported")
- }
- // Assume 16-bit depth buffer.
- const zdepth = 1 << 16
- // Convert z to window-space, assuming depth range [0;1].
- zf := float32(state.z)*2/zdepth - 1.0
img := imageOp{
- z: zf,
path: state.cpath,
- off: off,
clip: bounds,
material: mat,
}
- if state.rect && img.material.opaque {
- d.zimageOps = append(d.zimageOps, img)
- } else {
- d.imageOps = append(d.imageOps, img)
- }
+ d.imageOps = append(d.imageOps, img)
if clipData != nil {
// we added a clip path that should not remain
state.cpath = state.cpath.parent
- state.rect = wasrect
}
- case opconst.TypePush:
- state.z = d.collectOps(r, state)
- case opconst.TypePop:
- break loop
+ case ops.TypeSave:
+ id := ops.DecodeSave(encOp.Data)
+ d.save(id, state.t)
+ case ops.TypeLoad:
+ reset()
+ id := ops.DecodeLoad(encOp.Data)
+ state.t = d.states[id]
}
}
- return state.z
}
func expandPathOp(p *pathOp, clip image.Rectangle) {
@@ -840,78 +1028,83 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
}
}
-func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, trans f32.Affine2D, clip image.Rectangle) material {
+func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32.Affine2D, clip image.Rectangle) material {
var m material
switch d.matType {
case materialColor:
m.material = materialColor
- m.color = f32color.RGBAFromSRGB(d.color)
+ m.color = f32color.LinearFromSRGB(d.color)
m.opaque = m.color.A == 1.0
+ case materialLinearGradient:
+ m.material = materialLinearGradient
+
+ m.color1 = f32color.LinearFromSRGB(d.color1)
+ m.color2 = f32color.LinearFromSRGB(d.color2)
+ m.opaque = m.color1.A == 1.0 && m.color2.A == 1.0
+
+ m.uvTrans = partTrans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2))
case materialTexture:
m.material = materialTexture
dr := boundRectF(rect.Add(off))
sz := d.image.src.Bounds().Size()
- sr := layout.FRect(d.image.rect)
- if dx := float32(dr.Dx()); dx != 0 {
- // Don't clip 1 px width sources.
- if sdx := sr.Dx(); sdx > 1 {
- sr.Min.X += (float32(clip.Min.X-dr.Min.X)*sdx + dx/2) / dx
- sr.Max.X -= (float32(dr.Max.X-clip.Max.X)*sdx + dx/2) / dx
- }
- }
- if dy := float32(dr.Dy()); dy != 0 {
- // Don't clip 1 px height sources.
- if sdy := sr.Dy(); sdy > 1 {
- sr.Min.Y += (float32(clip.Min.Y-dr.Min.Y)*sdy + dy/2) / dy
- sr.Max.Y -= (float32(dr.Max.Y-clip.Max.Y)*sdy + dy/2) / dy
- }
- }
- tex, exists := cache.get(d.image.handle)
- if !exists {
- t := &texture{
- src: d.image.src,
- }
- cache.put(d.image.handle, t)
- tex = t
+ sr := f32.Rectangle{
+ Max: f32.Point{
+ X: float32(sz.X),
+ Y: float32(sz.Y),
+ },
}
- m.texture = tex.(*texture)
+ dx := float32(dr.Dx())
+ sdx := sr.Dx()
+ sr.Min.X += float32(clip.Min.X-dr.Min.X) * sdx / dx
+ sr.Max.X -= float32(dr.Max.X-clip.Max.X) * sdx / dx
+ dy := float32(dr.Dy())
+ sdy := sr.Dy()
+ sr.Min.Y += float32(clip.Min.Y-dr.Min.Y) * sdy / dy
+ sr.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy
uvScale, uvOffset := texSpaceTransform(sr, sz)
- m.uvTrans = trans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset))
+ m.uvTrans = partTrans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset))
+ m.data = d.image
}
return m
}
-func (r *renderer) drawZOps(ops []imageOp) {
- r.ctx.SetDepthTest(true)
- r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
- r.ctx.BindInputLayout(r.blitter.layout)
- // Render front to back.
- for i := len(ops) - 1; i >= 0; i-- {
- img := ops[i]
+func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) {
+ for _, img := range ops {
+ m := img.material
+ if m.material == materialTexture {
+ r.texHandle(cache, m.data)
+ }
+ }
+}
+
+func (r *renderer) prepareDrawOps(cache *resourceCache, ops []imageOp) {
+ for _, img := range ops {
m := img.material
switch m.material {
case materialTexture:
- r.ctx.BindTexture(0, r.texHandle(m.texture))
+ r.ctx.PrepareTexture(r.texHandle(cache, m.data))
}
- drc := img.clip
- scale, off := clipSpaceTransform(drc, r.blitter.viewport)
- r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
+
+ var fbo stencilFBO
+ switch img.clipType {
+ case clipTypeNone:
+ continue
+ case clipTypePath:
+ fbo = r.pather.stenciler.cover(img.place.Idx)
+ case clipTypeIntersection:
+ fbo = r.pather.stenciler.intersections.fbos[img.place.Idx]
+ }
+ r.ctx.PrepareTexture(fbo.tex)
}
- r.ctx.SetDepthTest(false)
}
-func (r *renderer) drawOps(ops []imageOp) {
- r.ctx.SetDepthTest(true)
- r.ctx.DepthMask(false)
- r.ctx.BlendFunc(backend.BlendFactorOne, backend.BlendFactorOneMinusSrcAlpha)
- r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
- r.ctx.BindInputLayout(r.pather.coverer.layout)
- var coverTex backend.Texture
+func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
+ var coverTex driver.Texture
for _, img := range ops {
m := img.material
switch m.material {
case materialTexture:
- r.ctx.BindTexture(0, r.texHandle(m.texture))
+ r.ctx.BindTexture(0, r.texHandle(cache, m.data))
}
drc := img.clip
@@ -919,7 +1112,10 @@ func (r *renderer) drawOps(ops []imageOp) {
var fbo stencilFBO
switch img.clipType {
case clipTypeNone:
- r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
+ p := r.blitter.pipelines[m.material]
+ r.ctx.BindPipeline(p.pipeline)
+ r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
+ r.blitter.blit(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans)
continue
case clipTypePath:
fbo = r.pather.stenciler.cover(img.place.Idx)
@@ -934,42 +1130,50 @@ func (r *renderer) drawOps(ops []imageOp) {
Min: img.place.Pos,
Max: img.place.Pos.Add(drc.Size()),
}
- coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
- r.pather.cover(img.z, m.material, m.color, scale, off, m.uvTrans, coverScale, coverOff)
+ coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size)
+ p := r.pather.coverer.pipelines[m.material]
+ r.ctx.BindPipeline(p.pipeline)
+ r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
+ r.pather.cover(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans, coverScale, coverOff)
}
- r.ctx.DepthMask(true)
- r.ctx.SetDepthTest(false)
}
-func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
- p := b.prog[mat]
- b.ctx.BindProgram(p.prog)
+func (b *blitter) blit(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
+ p := b.pipelines[mat]
+ b.ctx.BindPipeline(p.pipeline)
var uniforms *blitUniforms
switch mat {
case materialColor:
- b.colUniforms.frag.color = col
- uniforms = &b.colUniforms.vert.blitUniforms
+ b.colUniforms.color = col
+ uniforms = &b.colUniforms.blitUniforms
case materialTexture:
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
- b.texUniforms.vert.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
- b.texUniforms.vert.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
- uniforms = &b.texUniforms.vert.blitUniforms
+ b.texUniforms.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ b.texUniforms.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &b.texUniforms.blitUniforms
+ case materialLinearGradient:
+ b.linearGradientUniforms.color1 = col1
+ b.linearGradientUniforms.color2 = col2
+
+ t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
+ b.linearGradientUniforms.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ b.linearGradientUniforms.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &b.linearGradientUniforms.blitUniforms
}
- uniforms.z = z
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
- p.UploadUniforms()
- b.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4)
+ p.UploadUniforms(b.ctx)
+ b.ctx.DrawArrays(0, 4)
}
// newUniformBuffer creates a new GPU uniform buffer backed by the
// structure uniformBlock points to.
-func newUniformBuffer(b backend.Device, uniformBlock interface{}) *uniformBuffer {
+func newUniformBuffer(b driver.Device, uniformBlock interface{}) *uniformBuffer {
ref := reflect.ValueOf(uniformBlock)
// Determine the size of the uniforms structure, *uniforms.
size := ref.Elem().Type().Size()
// Map the uniforms structure as a byte slice.
ptr := (*[1 << 30]byte)(unsafe.Pointer(ref.Pointer()))[:size:size]
- ubuf, err := b.NewBuffer(backend.BufferBindingUniforms, len(ptr))
+ ubuf, err := b.NewBuffer(driver.BufferBindingUniforms, len(ptr))
if err != nil {
panic(err)
}
@@ -985,36 +1189,19 @@ func (u *uniformBuffer) Release() {
u.buf = nil
}
-func newProgram(prog backend.Program, vertUniforms, fragUniforms *uniformBuffer) *program {
- if vertUniforms != nil {
- prog.SetVertexUniforms(vertUniforms.buf)
- }
- if fragUniforms != nil {
- prog.SetFragmentUniforms(fragUniforms.buf)
- }
- return &program{prog: prog, vertUniforms: vertUniforms, fragUniforms: fragUniforms}
-}
-
-func (p *program) UploadUniforms() {
- if p.vertUniforms != nil {
- p.vertUniforms.Upload()
- }
- if p.fragUniforms != nil {
- p.fragUniforms.Upload()
+func (p *pipeline) UploadUniforms(ctx driver.Device) {
+ if p.uniforms != nil {
+ p.uniforms.Upload()
+ ctx.BindUniforms(p.uniforms.buf)
}
}
-func (p *program) Release() {
- p.prog.Release()
- p.prog = nil
- if p.vertUniforms != nil {
- p.vertUniforms.Release()
- p.vertUniforms = nil
- }
- if p.fragUniforms != nil {
- p.fragUniforms.Release()
- p.fragUniforms = nil
+func (p *pipeline) Release() {
+ p.pipeline.Release()
+ if p.uniforms != nil {
+ p.uniforms.Release()
}
+ *p = pipeline{}
}
// texSpaceTransform return the scale and offset that transforms the given subimage
@@ -1026,26 +1213,42 @@ func texSpaceTransform(r f32.Rectangle, bounds image.Point) (f32.Point, f32.Poin
return scale, offset
}
+// gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)].
+func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f32.Point) f32.Affine2D {
+ d := stop2.Sub(stop1)
+ l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y)))
+ a := float32(math.Atan2(float64(-d.Y), float64(d.X)))
+
+ // TODO: optimize
+ zp := f32.Point{}
+ return f32.Affine2D{}.
+ Scale(zp, layout.FPt(clip.Size())). // scale to pixel space
+ Offset(zp.Sub(off).Add(layout.FPt(clip.Min))). // offset to clip space
+ Offset(zp.Sub(stop1)). // offset to first stop point
+ Rotate(zp, a). // rotate to align gradient
+ Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size
+}
+
// clipSpaceTransform returns the scale and offset that transforms the given
-// rectangle from a viewport into OpenGL clip space.
+// rectangle from a viewport into GPU driver device coordinates.
func clipSpaceTransform(r image.Rectangle, viewport image.Point) (f32.Point, f32.Point) {
- // First, transform UI coordinates to OpenGL coordinates:
+ // First, transform UI coordinates to device coordinates:
//
- // [(-1, +1) (+1, +1)]
// [(-1, -1) (+1, -1)]
+ // [(-1, +1) (+1, +1)]
//
x, y := float32(r.Min.X), float32(r.Min.Y)
w, h := float32(r.Dx()), float32(r.Dy())
vx, vy := 2/float32(viewport.X), 2/float32(viewport.Y)
x = x*vx - 1
- y = 1 - y*vy
+ y = y*vy - 1
w *= vx
h *= vy
// Then, compute the transformation from the fullscreen quad to
// the rectangle at (x, y) and dimensions (w, h).
scale := f32.Point{X: w * .5, Y: h * .5}
- offset := f32.Point{X: x + w*.5, Y: y - h*.5}
+ offset := f32.Point{X: x + w*.5, Y: y + h*.5}
return scale, offset
}
@@ -1097,29 +1300,73 @@ func (d *drawOps) writeVertCache(n int) []byte {
}
// transform, split paths as needed, calculate maxY, bounds and create GPU vertices.
-func (d *drawOps) buildVerts(aux []byte, tr f32.Affine2D) (verts []byte, bounds f32.Rectangle) {
+func (d *drawOps) buildVerts(pathData []byte, tr f32.Affine2D, outline bool, strWidth float32) (verts []byte, bounds f32.Rectangle) {
inf := float32(math.Inf(+1))
d.qs.bounds = f32.Rectangle{
Min: f32.Point{X: inf, Y: inf},
Max: f32.Point{X: -inf, Y: -inf},
}
d.qs.d = d
- bo := binary.LittleEndian
startLength := len(d.vertCache)
- for qi := 0; len(aux) >= (ops.QuadSize + 4); qi++ {
- d.qs.contour = bo.Uint32(aux)
- quad := ops.DecodeQuad(aux[4:])
- quad = quad.Transform(tr)
- d.qs.splitAndEncode(quad)
+ switch {
+ case strWidth > 0:
+ // Stroke path.
+ ss := stroke.StrokeStyle{
+ Width: strWidth,
+ }
+ quads := stroke.StrokePathCommands(ss, pathData)
+ for _, quad := range quads {
+ d.qs.contour = quad.Contour
+ quad.Quad = quad.Quad.Transform(tr)
+
+ d.qs.splitAndEncode(quad.Quad)
+ }
- aux = aux[ops.QuadSize+4:]
+ case outline:
+ decodeToOutlineQuads(&d.qs, tr, pathData)
}
fillMaxY(d.vertCache[startLength:])
return d.vertCache[startLength:], d.qs.bounds
}
+// decodeOutlineQuads decodes scene commands, splits them into quadratic béziers
+// as needed and feeds them to the supplied splitter.
+func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) {
+ for len(pathData) >= scene.CommandSize+4 {
+ qs.contour = bo.Uint32(pathData)
+ cmd := ops.DecodeCommand(pathData[4:])
+ switch cmd.Op() {
+ case scene.OpLine:
+ var q stroke.QuadSegment
+ q.From, q.To = scene.DecodeLine(cmd)
+ q.Ctrl = q.From.Add(q.To).Mul(.5)
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ case scene.OpGap:
+ var q stroke.QuadSegment
+ q.From, q.To = scene.DecodeGap(cmd)
+ q.Ctrl = q.From.Add(q.To).Mul(.5)
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ case scene.OpQuad:
+ var q stroke.QuadSegment
+ q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd)
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ case scene.OpCubic:
+ for _, q := range stroke.SplitCubic(scene.DecodeCubic(cmd)) {
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ }
+ default:
+ panic("unsupported scene command")
+ }
+ pathData = pathData[scene.CommandSize+4:]
+ }
+}
+
// create GPU vertices for transformed r, find the bounds and establish texture transform.
func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.Rectangle, ptr f32.Affine2D) {
if isPureOffset(tr) {
diff --git a/vendor/gioui.org/gpu/internal/d3d11/d3d11.go b/vendor/gioui.org/gpu/internal/d3d11/d3d11.go
new file mode 100644
index 0000000..3ddf7c3
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/d3d11/d3d11.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// This file exists so this package builds on non-Windows platforms.
+
+package d3d11
diff --git a/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go
new file mode 100644
index 0000000..08698c3
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package d3d11
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "math"
+ "reflect"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/d3d11"
+ "gioui.org/shader"
+)
+
+type Backend struct {
+ dev *d3d11.Device
+ ctx *d3d11.DeviceContext
+
+ // Temporary storage to avoid garbage.
+ clearColor [4]float32
+ viewport d3d11.VIEWPORT
+
+ pipeline *Pipeline
+ vert struct {
+ buffer *Buffer
+ offset int
+ }
+
+ program *Program
+
+ caps driver.Caps
+
+ floatFormat uint32
+}
+
+type Pipeline struct {
+ vert *d3d11.VertexShader
+ frag *d3d11.PixelShader
+ layout *d3d11.InputLayout
+ blend *d3d11.BlendState
+ stride int
+ topology driver.Topology
+}
+
+type Texture struct {
+ backend *Backend
+ format uint32
+ bindings driver.BufferBinding
+ tex *d3d11.Texture2D
+ sampler *d3d11.SamplerState
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ renderTarget *d3d11.RenderTargetView
+
+ width int
+ height int
+ foreign bool
+}
+
+type VertexShader struct {
+ backend *Backend
+ shader *d3d11.VertexShader
+ src shader.Sources
+}
+
+type FragmentShader struct {
+ backend *Backend
+ shader *d3d11.PixelShader
+}
+
+type Program struct {
+ backend *Backend
+ shader *d3d11.ComputeShader
+}
+
+type Buffer struct {
+ backend *Backend
+ bind uint32
+ buf *d3d11.Buffer
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ size int
+ immutable bool
+}
+
+func init() {
+ driver.NewDirect3D11Device = newDirect3D11Device
+}
+
+func detectFloatFormat(dev *d3d11.Device) (uint32, bool) {
+ formats := []uint32{
+ d3d11.DXGI_FORMAT_R16_FLOAT,
+ d3d11.DXGI_FORMAT_R32_FLOAT,
+ d3d11.DXGI_FORMAT_R16G16_FLOAT,
+ d3d11.DXGI_FORMAT_R32G32_FLOAT,
+ // These last two are really wasteful, but c'est la vie.
+ d3d11.DXGI_FORMAT_R16G16B16A16_FLOAT,
+ d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT,
+ }
+ for _, format := range formats {
+ need := uint32(d3d11.FORMAT_SUPPORT_TEXTURE2D | d3d11.FORMAT_SUPPORT_RENDER_TARGET)
+ if support, _ := dev.CheckFormatSupport(format); support&need == need {
+ return format, true
+ }
+ }
+ return 0, false
+}
+
+func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
+ dev := (*d3d11.Device)(api.Device)
+ b := &Backend{
+ dev: dev,
+ ctx: dev.GetImmediateContext(),
+ caps: driver.Caps{
+ MaxTextureSize: 2048, // 9.1 maximum
+ Features: driver.FeatureSRGB,
+ },
+ }
+ featLvl := dev.GetFeatureLevel()
+ switch {
+ case featLvl < d3d11.FEATURE_LEVEL_9_1:
+ d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
+ return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
+ case featLvl >= d3d11.FEATURE_LEVEL_11_0:
+ b.caps.MaxTextureSize = 16384
+ b.caps.Features |= driver.FeatureCompute
+ case featLvl >= d3d11.FEATURE_LEVEL_9_3:
+ b.caps.MaxTextureSize = 4096
+ }
+ if fmt, ok := detectFloatFormat(dev); ok {
+ b.floatFormat = fmt
+ b.caps.Features |= driver.FeatureFloatRenderTargets
+ }
+ // Disable backface culling to match OpenGL.
+ state, err := dev.CreateRasterizerState(&d3d11.RASTERIZER_DESC{
+ CullMode: d3d11.CULL_NONE,
+ FillMode: d3d11.FILL_SOLID,
+ })
+ if err != nil {
+ return nil, err
+ }
+ defer d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
+ b.ctx.RSSetState(state)
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ var (
+ renderTarget *d3d11.RenderTargetView
+ )
+ if target != nil {
+ switch t := target.(type) {
+ case driver.Direct3D11RenderTarget:
+ renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget)
+ case *Texture:
+ renderTarget = t.renderTarget
+ default:
+ panic(fmt.Errorf("d3d11: invalid render target type: %T", target))
+ }
+ }
+ b.ctx.OMSetRenderTargets(renderTarget, nil)
+ return &Texture{backend: b, renderTarget: renderTarget, foreign: true}
+}
+
+func (b *Backend) CopyTexture(dstTex driver.Texture, dstOrigin image.Point, srcTex driver.Texture, srcRect image.Rectangle) {
+ dst := (*d3d11.Resource)(unsafe.Pointer(dstTex.(*Texture).tex))
+ src := (*d3d11.Resource)(srcTex.(*Texture).tex)
+ b.ctx.CopySubresourceRegion(
+ dst,
+ 0, // Destination subresource.
+ uint32(dstOrigin.X), uint32(dstOrigin.Y), 0, // Destination coordinates (x, y, z).
+ src,
+ 0, // Source subresource.
+ &d3d11.BOX{
+ Left: uint32(srcRect.Min.X),
+ Top: uint32(srcRect.Min.Y),
+ Right: uint32(srcRect.Max.X),
+ Bottom: uint32(srcRect.Max.Y),
+ Front: 0,
+ Back: 1,
+ },
+ )
+}
+
+func (b *Backend) EndFrame() {
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return b.caps
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ panic("timers not supported")
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ panic("timers not supported")
+}
+
+func (b *Backend) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
+ *b = Backend{}
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
+ var d3dfmt uint32
+ switch format {
+ case driver.TextureFormatFloat:
+ d3dfmt = b.floatFormat
+ case driver.TextureFormatSRGBA:
+ d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
+ case driver.TextureFormatRGBA8:
+ d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM
+ default:
+ return nil, fmt.Errorf("unsupported texture format %d", format)
+ }
+ tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
+ Width: uint32(width),
+ Height: uint32(height),
+ MipLevels: 1,
+ ArraySize: 1,
+ Format: d3dfmt,
+ SampleDesc: d3d11.DXGI_SAMPLE_DESC{
+ Count: 1,
+ Quality: 0,
+ },
+ BindFlags: convBufferBinding(bindings),
+ })
+ if err != nil {
+ return nil, err
+ }
+ var (
+ sampler *d3d11.SamplerState
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ fbo *d3d11.RenderTargetView
+ )
+ if bindings&driver.BufferBindingTexture != 0 {
+ var filter uint32
+ switch {
+ case minFilter == driver.FilterNearest && magFilter == driver.FilterNearest:
+ filter = d3d11.FILTER_MIN_MAG_MIP_POINT
+ case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear:
+ filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT
+ default:
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
+ }
+ var err error
+ sampler, err = b.dev.CreateSamplerState(&d3d11.SAMPLER_DESC{
+ Filter: filter,
+ AddressU: d3d11.TEXTURE_ADDRESS_CLAMP,
+ AddressV: d3d11.TEXTURE_ADDRESS_CLAMP,
+ AddressW: d3d11.TEXTURE_ADDRESS_CLAMP,
+ MaxAnisotropy: 1,
+ MinLOD: -math.MaxFloat32,
+ MaxLOD: math.MaxFloat32,
+ })
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ resView, err = b.dev.CreateShaderResourceView(
+ (*d3d11.Resource)(unsafe.Pointer(tex)),
+ unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_TEX2D{
+ SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
+ Format: d3dfmt,
+ ViewDimension: d3d11.SRV_DIMENSION_TEXTURE2D,
+ },
+ Texture2D: d3d11.TEX2D_SRV{
+ MostDetailedMip: 0,
+ MipLevels: ^uint32(0),
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ return nil, err
+ }
+ }
+ if bindings&driver.BufferBindingShaderStorageWrite != 0 {
+ uaView, err = b.dev.CreateUnorderedAccessView(
+ (*d3d11.Resource)(unsafe.Pointer(tex)),
+ unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_TEX2D{
+ UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
+ Format: d3dfmt,
+ ViewDimension: d3d11.UAV_DIMENSION_TEXTURE2D,
+ },
+ Texture2D: d3d11.TEX2D_UAV{
+ MipSlice: 0,
+ },
+ }),
+ )
+ if err != nil {
+ if sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ }
+ if resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ }
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ resource := (*d3d11.Resource)(unsafe.Pointer(tex))
+ fbo, err = b.dev.CreateRenderTargetView(resource)
+ if err != nil {
+ if uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(uaView), uaView.Vtbl.Release)
+ }
+ if sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ }
+ if resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ }
+ return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height}, nil
+}
+
+func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) {
+ if len(vertexShader.Inputs) != len(layout) {
+ return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
+ }
+ descs := make([]d3d11.INPUT_ELEMENT_DESC, len(layout))
+ for i, l := range layout {
+ inp := vertexShader.Inputs[i]
+ cname, err := windows.BytePtrFromString(inp.Semantic)
+ if err != nil {
+ return nil, err
+ }
+ var format uint32
+ switch l.Type {
+ case shader.DataTypeFloat:
+ switch l.Size {
+ case 1:
+ format = d3d11.DXGI_FORMAT_R32_FLOAT
+ case 2:
+ format = d3d11.DXGI_FORMAT_R32G32_FLOAT
+ case 3:
+ format = d3d11.DXGI_FORMAT_R32G32B32_FLOAT
+ case 4:
+ format = d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT
+ default:
+ panic("unsupported data size")
+ }
+ case shader.DataTypeShort:
+ switch l.Size {
+ case 1:
+ format = d3d11.DXGI_FORMAT_R16_SINT
+ case 2:
+ format = d3d11.DXGI_FORMAT_R16G16_SINT
+ default:
+ panic("unsupported data size")
+ }
+ default:
+ panic("unsupported data type")
+ }
+ descs[i] = d3d11.INPUT_ELEMENT_DESC{
+ SemanticName: cname,
+ SemanticIndex: uint32(inp.SemanticIndex),
+ Format: format,
+ AlignedByteOffset: uint32(l.Offset),
+ }
+ }
+ return b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC))
+}
+
+func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
+ return b.newBuffer(typ, size, nil, false)
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ return b.newBuffer(typ, len(data), data, true)
+}
+
+func (b *Backend) newBuffer(typ driver.BufferBinding, size int, data []byte, immutable bool) (*Buffer, error) {
+ if typ&driver.BufferBindingUniforms != 0 {
+ if typ != driver.BufferBindingUniforms {
+ return nil, errors.New("uniform buffers cannot have other bindings")
+ }
+ if size%16 != 0 {
+ return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size)
+ }
+ }
+ bind := convBufferBinding(typ)
+ var usage, miscFlags, cpuFlags uint32
+ if immutable {
+ usage = d3d11.USAGE_IMMUTABLE
+ }
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ cpuFlags = d3d11.CPU_ACCESS_READ
+ }
+ if typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
+ miscFlags |= d3d11.RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS
+ }
+ buf, err := b.dev.CreateBuffer(&d3d11.BUFFER_DESC{
+ ByteWidth: uint32(size),
+ Usage: usage,
+ BindFlags: bind,
+ CPUAccessFlags: cpuFlags,
+ MiscFlags: miscFlags,
+ }, data)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ )
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ uaView, err = b.dev.CreateUnorderedAccessView(
+ (*d3d11.Resource)(unsafe.Pointer(buf)),
+ unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_BUFFER{
+ UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
+ Format: d3d11.DXGI_FORMAT_R32_TYPELESS,
+ ViewDimension: d3d11.UAV_DIMENSION_BUFFER,
+ },
+ Buffer: d3d11.BUFFER_UAV{
+ FirstElement: 0,
+ NumElements: uint32(size / 4),
+ Flags: d3d11.BUFFER_UAV_FLAG_RAW,
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
+ return nil, err
+ }
+ } else if typ&driver.BufferBindingShaderStorageRead != 0 {
+ resView, err = b.dev.CreateShaderResourceView(
+ (*d3d11.Resource)(unsafe.Pointer(buf)),
+ unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_BUFFEREX{
+ SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
+ Format: d3d11.DXGI_FORMAT_R32_TYPELESS,
+ ViewDimension: d3d11.SRV_DIMENSION_BUFFEREX,
+ },
+ Buffer: d3d11.BUFFEREX_SRV{
+ FirstElement: 0,
+ NumElements: uint32(size / 4),
+ Flags: d3d11.BUFFEREX_SRV_FLAG_RAW,
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
+ return nil, err
+ }
+ }
+ return &Buffer{backend: b, buf: buf, bind: bind, size: size, resView: resView, uaView: uaView, immutable: immutable}, nil
+}
+
+func (b *Backend) NewComputeProgram(shader shader.Sources) (driver.Program, error) {
+ cs, err := b.dev.CreateComputeShader([]byte(shader.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &Program{backend: b, shader: cs}, nil
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ vsh := desc.VertexShader.(*VertexShader)
+ fsh := desc.FragmentShader.(*FragmentShader)
+ blend, err := b.newBlendState(desc.BlendDesc)
+ if err != nil {
+ return nil, err
+ }
+ var layout *d3d11.InputLayout
+ if l := desc.VertexLayout; l.Stride > 0 {
+ var err error
+ layout, err = b.newInputLayout(vsh.src, l.Inputs)
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(blend), blend.Vtbl.AddRef)
+ return nil, err
+ }
+ }
+
+ // Retain shaders.
+ vshRef := vsh.shader
+ fshRef := fsh.shader
+ d3d11.IUnknownAddRef(unsafe.Pointer(vshRef), vshRef.Vtbl.AddRef)
+ d3d11.IUnknownAddRef(unsafe.Pointer(fshRef), fshRef.Vtbl.AddRef)
+
+ return &Pipeline{
+ vert: vshRef,
+ frag: fshRef,
+ layout: layout,
+ stride: desc.VertexLayout.Stride,
+ blend: blend,
+ topology: desc.Topology,
+ }, nil
+}
+
+func (b *Backend) newBlendState(desc driver.BlendDesc) (*d3d11.BlendState, error) {
+ var d3ddesc d3d11.BLEND_DESC
+ t0 := &d3ddesc.RenderTarget[0]
+ t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL
+ t0.BlendOp = d3d11.BLEND_OP_ADD
+ t0.BlendOpAlpha = d3d11.BLEND_OP_ADD
+ if desc.Enable {
+ t0.BlendEnable = 1
+ }
+ scol, salpha := toBlendFactor(desc.SrcFactor)
+ dcol, dalpha := toBlendFactor(desc.DstFactor)
+ t0.SrcBlend = scol
+ t0.SrcBlendAlpha = salpha
+ t0.DestBlend = dcol
+ t0.DestBlendAlpha = dalpha
+ return b.dev.CreateBlendState(&d3ddesc)
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ vs, err := b.dev.CreateVertexShader([]byte(src.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &VertexShader{b, vs, src}, nil
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ fs, err := b.dev.CreatePixelShader([]byte(src.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &FragmentShader{b, fs}, nil
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ b.viewport = d3d11.VIEWPORT{
+ TopLeftX: float32(x),
+ TopLeftY: float32(y),
+ Width: float32(width),
+ Height: float32(height),
+ MinDepth: 0.0,
+ MaxDepth: 1.0,
+ }
+ b.ctx.RSSetViewports(&b.viewport)
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ b.prepareDraw()
+ b.ctx.Draw(uint32(count), uint32(off))
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ b.prepareDraw()
+ b.ctx.DrawIndexed(uint32(count), uint32(off), 0)
+}
+
+func (b *Backend) prepareDraw() {
+ p := b.pipeline
+ if p == nil {
+ return
+ }
+ b.ctx.VSSetShader(p.vert)
+ b.ctx.PSSetShader(p.frag)
+ b.ctx.IASetInputLayout(p.layout)
+ b.ctx.OMSetBlendState(p.blend, nil, 0xffffffff)
+ if b.vert.buffer != nil {
+ b.ctx.IASetVertexBuffers(b.vert.buffer.buf, uint32(p.stride), uint32(b.vert.offset))
+ }
+ var topology uint32
+ switch p.topology {
+ case driver.TopologyTriangles:
+ topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLELIST
+ case driver.TopologyTriangleStrip:
+ topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
+ default:
+ panic("unsupported draw mode")
+ }
+ b.ctx.IASetPrimitiveTopology(topology)
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ if t.uaView != nil {
+ b.ctx.CSSetUnorderedAccessViews(uint32(unit), t.uaView)
+ } else {
+ b.ctx.CSSetShaderResources(uint32(unit), t.resView)
+ }
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ b.ctx.CSSetShader(b.program.shader)
+ b.ctx.Dispatch(uint32(x), uint32(y), uint32(z))
+}
+
+func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if stride == 0 {
+ stride = size.X * 4
+ }
+ dst := &d3d11.BOX{
+ Left: uint32(offset.X),
+ Top: uint32(offset.Y),
+ Right: uint32(offset.X + size.X),
+ Bottom: uint32(offset.Y + size.Y),
+ Front: 0,
+ Back: 1,
+ }
+ res := (*d3d11.Resource)(unsafe.Pointer(t.tex))
+ t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
+}
+
+func (t *Texture) Release() {
+ if t.foreign {
+ panic("texture not created by NewTexture")
+ }
+ if t.renderTarget != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.renderTarget), t.renderTarget.Vtbl.Release)
+ }
+ if t.sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release)
+ }
+ if t.resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.resView), t.resView.Vtbl.Release)
+ }
+ if t.uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.uaView), t.uaView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(t.tex), t.tex.Vtbl.Release)
+ *t = Texture{}
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {}
+
+func (b *Backend) BindTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ b.ctx.PSSetSamplers(uint32(unit), t.sampler)
+ b.ctx.PSSetShaderResources(uint32(unit), t.resView)
+}
+
+func (b *Backend) BindPipeline(pipe driver.Pipeline) {
+ b.pipeline = pipe.(*Pipeline)
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ b.program = prog.(*Program)
+}
+
+func (s *VertexShader) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = VertexShader{}
+}
+
+func (s *FragmentShader) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = FragmentShader{}
+}
+
+func (s *Program) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = Program{}
+}
+
+func (p *Pipeline) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(p.vert), p.vert.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(p.frag), p.frag.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(p.blend), p.blend.Vtbl.Release)
+ if l := p.layout; l != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(l), l.Vtbl.Release)
+ }
+ *p = Pipeline{}
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ if buf.resView != nil {
+ b.ctx.CSSetShaderResources(uint32(binding), buf.resView)
+ } else {
+ b.ctx.CSSetUnorderedAccessViews(uint32(binding), buf.uaView)
+ }
+}
+
+func (b *Backend) BindUniforms(buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ b.ctx.VSSetConstantBuffers(buf.buf)
+ b.ctx.PSSetConstantBuffers(buf.buf)
+}
+
+func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
+ b.vert.buffer = buf.(*Buffer)
+ b.vert.offset = offset
+}
+
+func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
+ b.ctx.IASetIndexBuffer(buf.(*Buffer).buf, d3d11.DXGI_FORMAT_R16_UINT, 0)
+}
+
+func (b *Buffer) Download(dst []byte) error {
+ res := (*d3d11.Resource)(unsafe.Pointer(b.buf))
+ resMap, err := b.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
+ if err != nil {
+ return fmt.Errorf("d3d11: %v", err)
+ }
+ defer b.backend.ctx.Unmap(res, 0)
+ data := sliceOf(resMap.PData, len(dst))
+ copy(dst, data)
+ return nil
+}
+
+func (b *Buffer) Upload(data []byte) {
+ var dst *d3d11.BOX
+ if len(data) < b.size {
+ dst = &d3d11.BOX{
+ Left: 0,
+ Right: uint32(len(data)),
+ Top: 0,
+ Bottom: 1,
+ Front: 0,
+ Back: 1,
+ }
+ }
+ b.backend.ctx.UpdateSubresource((*d3d11.Resource)(unsafe.Pointer(b.buf)), dst, 0, 0, data)
+}
+
+func (b *Buffer) Release() {
+ if b.resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.resView), b.resView.Vtbl.Release)
+ }
+ if b.uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.uaView), b.uaView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(b.buf), b.buf.Vtbl.Release)
+ *b = Buffer{}
+}
+
+func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ w, h := src.Dx(), src.Dy()
+ tex, err := t.backend.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
+ Width: uint32(w),
+ Height: uint32(h),
+ MipLevels: 1,
+ ArraySize: 1,
+ Format: t.format,
+ SampleDesc: d3d11.DXGI_SAMPLE_DESC{
+ Count: 1,
+ Quality: 0,
+ },
+ Usage: d3d11.USAGE_STAGING,
+ CPUAccessFlags: d3d11.CPU_ACCESS_READ,
+ })
+ if err != nil {
+ return fmt.Errorf("ReadPixels: %v", err)
+ }
+ defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ res := (*d3d11.Resource)(unsafe.Pointer(tex))
+ t.backend.ctx.CopySubresourceRegion(
+ res,
+ 0, // Destination subresource.
+ 0, 0, 0, // Destination coordinates (x, y, z).
+ (*d3d11.Resource)(t.tex),
+ 0, // Source subresource.
+ &d3d11.BOX{
+ Left: uint32(src.Min.X),
+ Top: uint32(src.Min.Y),
+ Right: uint32(src.Max.X),
+ Bottom: uint32(src.Max.Y),
+ Front: 0,
+ Back: 1,
+ },
+ )
+ resMap, err := t.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
+ if err != nil {
+ return fmt.Errorf("ReadPixels: %v", err)
+ }
+ defer t.backend.ctx.Unmap(res, 0)
+ srcPitch := stride
+ dstPitch := int(resMap.RowPitch)
+ mapSize := dstPitch * h
+ data := sliceOf(resMap.PData, mapSize)
+ width := w * 4
+ for r := 0; r < h; r++ {
+ pixels := pixels[r*srcPitch:]
+ copy(pixels[:width], data[r*dstPitch:])
+ }
+ return nil
+}
+
+func (b *Backend) BeginCompute() {
+}
+
+func (b *Backend) EndCompute() {
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
+ t := tex.(*Texture)
+ b.ctx.OMSetRenderTargets(t.renderTarget, nil)
+ if d.Action == driver.LoadActionClear {
+ c := d.ClearColor
+ b.clearColor = [4]float32{c.R, c.G, c.B, c.A}
+ b.ctx.ClearRenderTargetView(t.renderTarget, &b.clearColor)
+ }
+}
+
+func (b *Backend) EndRenderPass() {
+}
+
+func (f *Texture) ImplementsRenderTarget() {}
+
+func convBufferBinding(typ driver.BufferBinding) uint32 {
+ var bindings uint32
+ if typ&driver.BufferBindingVertices != 0 {
+ bindings |= d3d11.BIND_VERTEX_BUFFER
+ }
+ if typ&driver.BufferBindingIndices != 0 {
+ bindings |= d3d11.BIND_INDEX_BUFFER
+ }
+ if typ&driver.BufferBindingUniforms != 0 {
+ bindings |= d3d11.BIND_CONSTANT_BUFFER
+ }
+ if typ&driver.BufferBindingTexture != 0 {
+ bindings |= d3d11.BIND_SHADER_RESOURCE
+ }
+ if typ&driver.BufferBindingFramebuffer != 0 {
+ bindings |= d3d11.BIND_RENDER_TARGET
+ }
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ bindings |= d3d11.BIND_UNORDERED_ACCESS
+ } else if typ&driver.BufferBindingShaderStorageRead != 0 {
+ bindings |= d3d11.BIND_SHADER_RESOURCE
+ }
+ return bindings
+}
+
+func toBlendFactor(f driver.BlendFactor) (uint32, uint32) {
+ switch f {
+ case driver.BlendFactorOne:
+ return d3d11.BLEND_ONE, d3d11.BLEND_ONE
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return d3d11.BLEND_INV_SRC_ALPHA, d3d11.BLEND_INV_SRC_ALPHA
+ case driver.BlendFactorZero:
+ return d3d11.BLEND_ZERO, d3d11.BLEND_ZERO
+ case driver.BlendFactorDstColor:
+ return d3d11.BLEND_DEST_COLOR, d3d11.BLEND_DEST_ALPHA
+ default:
+ panic("unsupported blend source factor")
+ }
+}
+
+// sliceOf returns a slice from a (native) pointer.
+func sliceOf(ptr uintptr, cap int) []byte {
+ var data []byte
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ h.Data = ptr
+ h.Cap = cap
+ h.Len = cap
+ return data
+}
diff --git a/vendor/gioui.org/gpu/internal/driver/api.go b/vendor/gioui.org/gpu/internal/driver/api.go
new file mode 100644
index 0000000..9a762a6
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/driver/api.go
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package driver
+
+import (
+ "fmt"
+ "unsafe"
+
+ "gioui.org/internal/gl"
+)
+
+// See gpu/api.go for documentation for the API types.
+
+type API interface {
+ implementsAPI()
+}
+
+type RenderTarget interface {
+ ImplementsRenderTarget()
+}
+
+type OpenGLRenderTarget gl.Framebuffer
+
+type Direct3D11RenderTarget struct {
+ // RenderTarget is a *ID3D11RenderTargetView.
+ RenderTarget unsafe.Pointer
+}
+
+type MetalRenderTarget struct {
+ // Texture is a MTLTexture.
+ Texture uintptr
+}
+
+type VulkanRenderTarget struct {
+ // WaitSem is a VkSemaphore that must signaled before accessing Framebuffer.
+ WaitSem uint64
+ // SignalSem is a VkSemaphore that signal access to Framebuffer is complete.
+ SignalSem uint64
+ // Image is the VkImage to render into.
+ Image uint64
+ // Framebuffer is a VkFramebuffer for Image.
+ Framebuffer uint64
+}
+
+type OpenGL struct {
+ // ES forces the use of ANGLE OpenGL ES libraries on macOS. It is
+ // ignored on all other platforms.
+ ES bool
+ // Context contains the WebGL context for WebAssembly platforms. It is
+ // empty for all other platforms; an OpenGL context is assumed current when
+ // calling NewDevice.
+ Context gl.Context
+ // Shared instructs users of the context to restore the GL state after
+ // use.
+ Shared bool
+}
+
+type Direct3D11 struct {
+ // Device contains a *ID3D11Device.
+ Device unsafe.Pointer
+}
+
+type Metal struct {
+ // Device is an MTLDevice.
+ Device uintptr
+ // Queue is a MTLCommandQueue.
+ Queue uintptr
+ // PixelFormat is the MTLPixelFormat of the default framebuffer.
+ PixelFormat int
+}
+
+type Vulkan struct {
+ // PhysDevice is a VkPhysicalDevice.
+ PhysDevice unsafe.Pointer
+ // Device is a VkDevice.
+ Device unsafe.Pointer
+ // QueueFamily is the queue familily index of the queue.
+ QueueFamily int
+ // QueueIndex is the logical queue index of the queue.
+ QueueIndex int
+ // Format is a VkFormat that matches render targets.
+ Format int
+}
+
+// API specific device constructors.
+var (
+ NewOpenGLDevice func(api OpenGL) (Device, error)
+ NewDirect3D11Device func(api Direct3D11) (Device, error)
+ NewMetalDevice func(api Metal) (Device, error)
+ NewVulkanDevice func(api Vulkan) (Device, error)
+)
+
+// NewDevice creates a new Device given the api.
+//
+// Note that the device does not assume ownership of the resources contained in
+// api; the caller must ensure the resources are valid until the device is
+// released.
+func NewDevice(api API) (Device, error) {
+ switch api := api.(type) {
+ case OpenGL:
+ if NewOpenGLDevice != nil {
+ return NewOpenGLDevice(api)
+ }
+ case Direct3D11:
+ if NewDirect3D11Device != nil {
+ return NewDirect3D11Device(api)
+ }
+ case Metal:
+ if NewMetalDevice != nil {
+ return NewMetalDevice(api)
+ }
+ case Vulkan:
+ if NewVulkanDevice != nil {
+ return NewVulkanDevice(api)
+ }
+ }
+ return nil, fmt.Errorf("driver: no driver available for the API %T", api)
+}
+
+func (OpenGL) implementsAPI() {}
+func (Direct3D11) implementsAPI() {}
+func (Metal) implementsAPI() {}
+func (Vulkan) implementsAPI() {}
+func (OpenGLRenderTarget) ImplementsRenderTarget() {}
+func (Direct3D11RenderTarget) ImplementsRenderTarget() {}
+func (MetalRenderTarget) ImplementsRenderTarget() {}
+func (VulkanRenderTarget) ImplementsRenderTarget() {}
diff --git a/vendor/gioui.org/gpu/internal/driver/driver.go b/vendor/gioui.org/gpu/internal/driver/driver.go
new file mode 100644
index 0000000..58cb89b
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/driver/driver.go
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package driver
+
+import (
+ "errors"
+ "image"
+ "time"
+
+ "gioui.org/internal/f32color"
+ "gioui.org/shader"
+)
+
+// Device represents the abstraction of underlying GPU
+// APIs such as OpenGL, Direct3D useful for rendering Gio
+// operations.
+type Device interface {
+ BeginFrame(target RenderTarget, clear bool, viewport image.Point) Texture
+ EndFrame()
+ Caps() Caps
+ NewTimer() Timer
+ // IsContinuousTime reports whether all timer measurements
+ // are valid at the point of call.
+ IsTimeContinuous() bool
+ NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error)
+ NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
+ NewBuffer(typ BufferBinding, size int) (Buffer, error)
+ NewComputeProgram(shader shader.Sources) (Program, error)
+ NewVertexShader(src shader.Sources) (VertexShader, error)
+ NewFragmentShader(src shader.Sources) (FragmentShader, error)
+ NewPipeline(desc PipelineDesc) (Pipeline, error)
+
+ Viewport(x, y, width, height int)
+ DrawArrays(off, count int)
+ DrawElements(off, count int)
+
+ BeginRenderPass(t Texture, desc LoadDesc)
+ EndRenderPass()
+ PrepareTexture(t Texture)
+ BindProgram(p Program)
+ BindPipeline(p Pipeline)
+ BindTexture(unit int, t Texture)
+ BindVertexBuffer(b Buffer, offset int)
+ BindIndexBuffer(b Buffer)
+ BindImageTexture(unit int, texture Texture)
+ BindUniforms(buf Buffer)
+ BindStorageBuffer(binding int, buf Buffer)
+
+ BeginCompute()
+ EndCompute()
+ CopyTexture(dst Texture, dstOrigin image.Point, src Texture, srcRect image.Rectangle)
+ DispatchCompute(x, y, z int)
+
+ Release()
+}
+
+var ErrDeviceLost = errors.New("GPU device lost")
+
+type LoadDesc struct {
+ Action LoadAction
+ ClearColor f32color.RGBA
+}
+
+type Pipeline interface {
+ Release()
+}
+
+type PipelineDesc struct {
+ VertexShader VertexShader
+ FragmentShader FragmentShader
+ VertexLayout VertexLayout
+ BlendDesc BlendDesc
+ PixelFormat TextureFormat
+ Topology Topology
+}
+
+type VertexLayout struct {
+ Inputs []InputDesc
+ Stride int
+}
+
+// InputDesc describes a vertex attribute as laid out in a Buffer.
+type InputDesc struct {
+ Type shader.DataType
+ Size int
+
+ Offset int
+}
+
+type BlendDesc struct {
+ Enable bool
+ SrcFactor, DstFactor BlendFactor
+}
+
+type BlendFactor uint8
+
+type Topology uint8
+
+type TextureFilter uint8
+type TextureFormat uint8
+
+type BufferBinding uint8
+
+type LoadAction uint8
+
+type Features uint
+
+type Caps struct {
+ // BottomLeftOrigin is true if the driver has the origin in the lower left
+ // corner. The OpenGL driver returns true.
+ BottomLeftOrigin bool
+ Features Features
+ MaxTextureSize int
+}
+
+type VertexShader interface {
+ Release()
+}
+
+type FragmentShader interface {
+ Release()
+}
+
+type Program interface {
+ Release()
+}
+
+type Buffer interface {
+ Release()
+ Upload(data []byte)
+ Download(data []byte) error
+}
+
+type Timer interface {
+ Begin()
+ End()
+ Duration() (time.Duration, bool)
+ Release()
+}
+
+type Texture interface {
+ RenderTarget
+ Upload(offset, size image.Point, pixels []byte, stride int)
+ ReadPixels(src image.Rectangle, pixels []byte, stride int) error
+ Release()
+}
+
+const (
+ BufferBindingIndices BufferBinding = 1 << iota
+ BufferBindingVertices
+ BufferBindingUniforms
+ BufferBindingTexture
+ BufferBindingFramebuffer
+ BufferBindingShaderStorageRead
+ BufferBindingShaderStorageWrite
+)
+
+const (
+ TextureFormatSRGBA TextureFormat = iota
+ TextureFormatFloat
+ TextureFormatRGBA8
+ // TextureFormatOutput denotes the format used by the output framebuffer.
+ TextureFormatOutput
+)
+
+const (
+ FilterNearest TextureFilter = iota
+ FilterLinear
+)
+
+const (
+ FeatureTimers Features = 1 << iota
+ FeatureFloatRenderTargets
+ FeatureCompute
+ FeatureSRGB
+)
+
+const (
+ TopologyTriangleStrip Topology = iota
+ TopologyTriangles
+)
+
+const (
+ BlendFactorOne BlendFactor = iota
+ BlendFactorOneMinusSrcAlpha
+ BlendFactorZero
+ BlendFactorDstColor
+)
+
+const (
+ LoadActionKeep LoadAction = iota
+ LoadActionClear
+ LoadActionInvalidate
+)
+
+var ErrContentLost = errors.New("buffer content lost")
+
+func (f Features) Has(feats Features) bool {
+ return f&feats == feats
+}
+
+func DownloadImage(d Device, t Texture, img *image.RGBA) error {
+ r := img.Bounds()
+ if err := t.ReadPixels(r, img.Pix, img.Stride); err != nil {
+ return err
+ }
+ if d.Caps().BottomLeftOrigin {
+ // OpenGL origin is in the lower-left corner. Flip the image to
+ // match.
+ flipImageY(r.Dx()*4, r.Dy(), img.Pix)
+ }
+ return nil
+}
+
+func flipImageY(stride, height int, pixels []byte) {
+ // Flip image in y-direction. OpenGL's origin is in the lower
+ // left corner.
+ row := make([]uint8, stride)
+ for y := 0; y < height/2; y++ {
+ y1 := height - y - 1
+ dest := y1 * stride
+ src := y * stride
+ copy(row, pixels[dest:])
+ copy(pixels[dest:], pixels[src:src+len(row)])
+ copy(pixels[src:], row)
+ }
+}
+
+func UploadImage(t Texture, offset image.Point, img *image.RGBA) {
+ var pixels []byte
+ size := img.Bounds().Size()
+ min := img.Rect.Min
+ start := img.PixOffset(min.X, min.Y)
+ end := img.PixOffset(min.X+size.X, min.Y+size.Y-1)
+ pixels = img.Pix[start:end]
+ t.Upload(offset, size, pixels, img.Stride)
+}
diff --git a/vendor/gioui.org/gpu/internal/metal/metal.go b/vendor/gioui.org/gpu/internal/metal/metal.go
new file mode 100644
index 0000000..b9739af
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/metal/metal.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// This file exists so this package builds on non-Darwin platforms.
+
+package metal
diff --git a/vendor/gioui.org/gpu/internal/metal/metal_darwin.go b/vendor/gioui.org/gpu/internal/metal/metal_darwin.go
new file mode 100644
index 0000000..c180731
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/metal/metal_darwin.go
@@ -0,0 +1,1141 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package metal
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "unsafe"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/shader"
+)
+
+/*
+#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc
+#cgo LDFLAGS: -framework CoreGraphics
+
+@import Metal;
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Metal/Metal.h>
+
+typedef struct {
+ void *addr;
+ NSUInteger size;
+} slice;
+
+static CFTypeRef queueNewBuffer(CFTypeRef queueRef) {
+ @autoreleasepool {
+ id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
+ return CFBridgingRetain([queue commandBuffer]);
+ }
+}
+
+static void cmdBufferCommit(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ [cmdBuf commit];
+ }
+}
+
+static void cmdBufferWaitUntilCompleted(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ [cmdBuf waitUntilCompleted];
+ }
+}
+
+static CFTypeRef cmdBufferRenderEncoder(CFTypeRef cmdBufRef, CFTypeRef textureRef, MTLLoadAction act, float r, float g, float b, float a) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor new];
+ desc.colorAttachments[0].texture = (__bridge id<MTLTexture>)textureRef;
+ desc.colorAttachments[0].loadAction = act;
+ desc.colorAttachments[0].clearColor = MTLClearColorMake(r, g, b, a);
+ return CFBridgingRetain([cmdBuf renderCommandEncoderWithDescriptor:desc]);
+ }
+}
+
+static CFTypeRef cmdBufferComputeEncoder(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ return CFBridgingRetain([cmdBuf computeCommandEncoder]);
+ }
+}
+
+static CFTypeRef cmdBufferBlitEncoder(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ return CFBridgingRetain([cmdBuf blitCommandEncoder]);
+ }
+}
+
+static void renderEncEnd(CFTypeRef renderEncRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc endEncoding];
+ }
+}
+
+static void renderEncViewport(CFTypeRef renderEncRef, MTLViewport viewport) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc setViewport:viewport];
+ }
+}
+
+static void renderEncSetFragmentTexture(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef texRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
+ [enc setFragmentTexture:tex atIndex:index];
+ }
+}
+
+static void renderEncSetFragmentSamplerState(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef samplerRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLSamplerState> sampler = (__bridge id<MTLSamplerState>)samplerRef;
+ [enc setFragmentSamplerState:sampler atIndex:index];
+ }
+}
+
+static void renderEncSetVertexBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc setVertexBuffer:buf offset:offset atIndex:idx];
+ }
+}
+
+static void renderEncSetFragmentBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc setFragmentBuffer:buf offset:offset atIndex:idx];
+ }
+}
+
+static void renderEncSetFragmentBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc setFragmentBytes:bytes length:length atIndex:idx];
+ }
+}
+
+static void renderEncSetVertexBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc setVertexBytes:bytes length:length atIndex:idx];
+ }
+}
+
+static void renderEncSetRenderPipelineState(CFTypeRef renderEncRef, CFTypeRef pipeRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLRenderPipelineState> pipe = (__bridge id<MTLRenderPipelineState>)pipeRef;
+ [enc setRenderPipelineState:pipe];
+ }
+}
+
+static void renderEncDrawPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, NSUInteger start, NSUInteger count) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc drawPrimitives:type vertexStart:start vertexCount:count];
+ }
+}
+
+static void renderEncDrawIndexedPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, CFTypeRef bufRef, NSUInteger offset, NSUInteger count) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc drawIndexedPrimitives:type indexCount:count indexType:MTLIndexTypeUInt16 indexBuffer:buf indexBufferOffset:offset];
+ }
+}
+
+static void computeEncSetPipeline(CFTypeRef computeEncRef, CFTypeRef pipeRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ id<MTLComputePipelineState> pipe = (__bridge id<MTLComputePipelineState>)pipeRef;
+ [enc setComputePipelineState:pipe];
+ }
+}
+
+static void computeEncSetTexture(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef texRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
+ [enc setTexture:tex atIndex:index];
+ }
+}
+
+static void computeEncEnd(CFTypeRef computeEncRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ [enc endEncoding];
+ }
+}
+
+static void computeEncSetBuffer(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef bufRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc setBuffer:buf offset:0 atIndex:index];
+ }
+}
+
+static void computeEncDispatch(CFTypeRef computeEncRef, MTLSize threadgroupsPerGrid, MTLSize threadsPerThreadgroup) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ [enc dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup];
+ }
+}
+
+static void computeEncSetBytes(CFTypeRef computeEncRef, const void *bytes, NSUInteger length, NSUInteger index) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ [enc setBytes:bytes length:length atIndex:index];
+ }
+}
+
+static void blitEncEnd(CFTypeRef blitEncRef) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ [enc endEncoding];
+ }
+}
+
+static void blitEncCopyFromTexture(CFTypeRef blitEncRef, CFTypeRef srcRef, MTLOrigin srcOrig, MTLSize srcSize, CFTypeRef dstRef, MTLOrigin dstOrig) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLTexture> src = (__bridge id<MTLTexture>)srcRef;
+ id<MTLTexture> dst = (__bridge id<MTLTexture>)dstRef;
+ [enc copyFromTexture:src
+ sourceSlice:0
+ sourceLevel:0
+ sourceOrigin:srcOrig
+ sourceSize:srcSize
+ toTexture:dst
+ destinationSlice:0
+ destinationLevel:0
+ destinationOrigin:dstOrig];
+ }
+}
+
+static void blitEncCopyBufferToTexture(CFTypeRef blitEncRef, CFTypeRef bufRef, CFTypeRef texRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLBuffer> src = (__bridge id<MTLBuffer>)bufRef;
+ id<MTLTexture> dst = (__bridge id<MTLTexture>)texRef;
+ [enc copyFromBuffer:src
+ sourceOffset:offset
+ sourceBytesPerRow:stride
+ sourceBytesPerImage:length
+ sourceSize:dims
+ toTexture:dst
+ destinationSlice:0
+ destinationLevel:0
+ destinationOrigin:orig];
+ }
+}
+
+static void blitEncCopyTextureToBuffer(CFTypeRef blitEncRef, CFTypeRef texRef, CFTypeRef bufRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLTexture> src = (__bridge id<MTLTexture>)texRef;
+ id<MTLBuffer> dst = (__bridge id<MTLBuffer>)bufRef;
+ [enc copyFromTexture:src
+ sourceSlice:0
+ sourceLevel:0
+ sourceOrigin:orig
+ sourceSize:dims
+ toBuffer:dst
+ destinationOffset:offset
+ destinationBytesPerRow:stride
+ destinationBytesPerImage:length];
+ }
+}
+
+static void blitEncCopyBufferToBuffer(CFTypeRef blitEncRef, CFTypeRef srcRef, CFTypeRef dstRef, NSUInteger srcOff, NSUInteger dstOff, NSUInteger size) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLBuffer> src = (__bridge id<MTLBuffer>)srcRef;
+ id<MTLBuffer> dst = (__bridge id<MTLBuffer>)dstRef;
+ [enc copyFromBuffer:src
+ sourceOffset:srcOff
+ toBuffer:dst
+ destinationOffset:dstOff
+ size:size];
+ }
+}
+
+static CFTypeRef newTexture(CFTypeRef devRef, NSUInteger width, NSUInteger height, MTLPixelFormat format, MTLTextureUsage usage) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ MTLTextureDescriptor *mtlDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
+ width: width
+ height: height
+ mipmapped: NO];
+ mtlDesc.usage = usage;
+ mtlDesc.storageMode = MTLStorageModePrivate;
+ return CFBridgingRetain([dev newTextureWithDescriptor:mtlDesc]);
+ }
+}
+
+static CFTypeRef newSampler(CFTypeRef devRef, MTLSamplerMinMagFilter minFilter, MTLSamplerMinMagFilter magFilter) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new];
+ desc.minFilter = minFilter;
+ desc.magFilter = magFilter;
+ return CFBridgingRetain([dev newSamplerStateWithDescriptor:desc]);
+ }
+}
+
+static CFTypeRef newBuffer(CFTypeRef devRef, NSUInteger size, MTLResourceOptions opts) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ id<MTLBuffer> buf = [dev newBufferWithLength:size
+ options:opts];
+ return CFBridgingRetain(buf);
+ }
+}
+
+static slice bufferContents(CFTypeRef bufRef) {
+ @autoreleasepool {
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ slice s = {.addr = [buf contents], .size = [buf length]};
+ return s;
+ }
+}
+
+static CFTypeRef newLibrary(CFTypeRef devRef, char *name, void *mtllib, size_t size) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ dispatch_data_t data = dispatch_data_create(mtllib, size, DISPATCH_TARGET_QUEUE_DEFAULT, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+ id<MTLLibrary> lib = [dev newLibraryWithData:data error:nil];
+ lib.label = [NSString stringWithUTF8String:name];
+ return CFBridgingRetain(lib);
+ }
+}
+
+static CFTypeRef libraryNewFunction(CFTypeRef libRef, char *funcName) {
+ @autoreleasepool {
+ id<MTLLibrary> lib = (__bridge id<MTLLibrary>)libRef;
+ NSString *name = [NSString stringWithUTF8String:funcName];
+ return CFBridgingRetain([lib newFunctionWithName:name]);
+ }
+}
+
+static CFTypeRef newComputePipeline(CFTypeRef devRef, CFTypeRef funcRef) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ id<MTLFunction> func = (__bridge id<MTLFunction>)funcRef;
+ return CFBridgingRetain([dev newComputePipelineStateWithFunction:func error:nil]);
+ }
+}
+
+static CFTypeRef newRenderPipeline(CFTypeRef devRef, CFTypeRef vertFunc, CFTypeRef fragFunc, MTLPixelFormat pixelFormat, NSUInteger bufIdx, NSUInteger nverts, MTLVertexFormat *fmts, NSUInteger *offsets, NSUInteger stride, int blend, MTLBlendFactor srcFactor, MTLBlendFactor dstFactor, NSUInteger nvertBufs, NSUInteger nfragBufs) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ id<MTLFunction> vfunc = (__bridge id<MTLFunction>)vertFunc;
+ id<MTLFunction> ffunc = (__bridge id<MTLFunction>)fragFunc;
+ MTLVertexDescriptor *vdesc = [MTLVertexDescriptor vertexDescriptor];
+ vdesc.layouts[bufIdx].stride = stride;
+ for (NSUInteger i = 0; i < nverts; i++) {
+ vdesc.attributes[i].format = fmts[i];
+ vdesc.attributes[i].offset = offsets[i];
+ vdesc.attributes[i].bufferIndex = bufIdx;
+ }
+ MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new];
+ desc.vertexFunction = vfunc;
+ desc.fragmentFunction = ffunc;
+ desc.vertexDescriptor = vdesc;
+ for (NSUInteger i = 0; i < nvertBufs; i++) {
+ if (@available(iOS 11.0, *)) {
+ desc.vertexBuffers[i].mutability = MTLMutabilityImmutable;
+ }
+ }
+ for (NSUInteger i = 0; i < nfragBufs; i++) {
+ if (@available(iOS 11.0, *)) {
+ desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable;
+ }
+ }
+ desc.colorAttachments[0].pixelFormat = pixelFormat;
+ desc.colorAttachments[0].blendingEnabled = blend ? YES : NO;
+ desc.colorAttachments[0].sourceAlphaBlendFactor = srcFactor;
+ desc.colorAttachments[0].sourceRGBBlendFactor = srcFactor;
+ desc.colorAttachments[0].destinationAlphaBlendFactor = dstFactor;
+ desc.colorAttachments[0].destinationRGBBlendFactor = dstFactor;
+ return CFBridgingRetain([dev newRenderPipelineStateWithDescriptor:desc
+ error:nil]);
+ }
+}
+*/
+import "C"
+
+type Backend struct {
+ dev C.CFTypeRef
+ queue C.CFTypeRef
+ pixelFmt C.MTLPixelFormat
+
+ cmdBuffer C.CFTypeRef
+ lastCmdBuffer C.CFTypeRef
+ renderEnc C.CFTypeRef
+ computeEnc C.CFTypeRef
+ blitEnc C.CFTypeRef
+
+ prog *Program
+ topology C.MTLPrimitiveType
+
+ stagingBuf C.CFTypeRef
+ stagingOff int
+
+ indexBuf *Buffer
+
+ // bufSizes is scratch space for filling out the spvBufferSizeConstants
+ // that spirv-cross generates for emulating buffer.length expressions in
+ // shaders.
+ bufSizes []uint32
+}
+
+type Texture struct {
+ backend *Backend
+ texture C.CFTypeRef
+ sampler C.CFTypeRef
+ width int
+ height int
+ foreign bool
+}
+
+type Shader struct {
+ function C.CFTypeRef
+ inputs []shader.InputLocation
+}
+
+type Program struct {
+ pipeline C.CFTypeRef
+ groupSize [3]int
+}
+
+type Pipeline struct {
+ pipeline C.CFTypeRef
+ topology C.MTLPrimitiveType
+}
+
+type Buffer struct {
+ backend *Backend
+ size int
+ buffer C.CFTypeRef
+
+ // store is the buffer contents For buffers not allocated on the GPU.
+ store []byte
+}
+
+const (
+ uniformBufferIndex = 0
+ attributeBufferIndex = 1
+
+ spvBufferSizeConstantsBinding = 25
+)
+
+const (
+ texUnits = 4
+ bufferUnits = 4
+)
+
+func init() {
+ driver.NewMetalDevice = newMetalDevice
+}
+
+func newMetalDevice(api driver.Metal) (driver.Device, error) {
+ dev := C.CFTypeRef(api.Device)
+ C.CFRetain(dev)
+ queue := C.CFTypeRef(api.Queue)
+ C.CFRetain(queue)
+ b := &Backend{
+ dev: dev,
+ queue: queue,
+ pixelFmt: C.MTLPixelFormat(api.PixelFormat),
+ bufSizes: make([]uint32, bufferUnits),
+ }
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ if b.lastCmdBuffer != 0 {
+ C.cmdBufferWaitUntilCompleted(b.lastCmdBuffer)
+ b.stagingOff = 0
+ }
+ if target == nil {
+ return nil
+ }
+ switch t := target.(type) {
+ case driver.MetalRenderTarget:
+ texture := C.CFTypeRef(t.Texture)
+ return &Texture{texture: texture, foreign: true}
+ case *Texture:
+ return t
+ default:
+ panic(fmt.Sprintf("metal: unsupported render target type: %T", t))
+ }
+}
+
+func (b *Backend) startBlit() C.CFTypeRef {
+ if b.blitEnc != 0 {
+ return b.blitEnc
+ }
+ b.endEncoder()
+ b.ensureCmdBuffer()
+ b.blitEnc = C.cmdBufferBlitEncoder(b.cmdBuffer)
+ if b.blitEnc == 0 {
+ panic("metal: [MTLCommandBuffer blitCommandEncoder:] failed")
+ }
+ return b.blitEnc
+}
+
+func (b *Backend) CopyTexture(dst driver.Texture, dorig image.Point, src driver.Texture, srect image.Rectangle) {
+ enc := b.startBlit()
+ dstTex := dst.(*Texture).texture
+ srcTex := src.(*Texture).texture
+ ssz := srect.Size()
+ C.blitEncCopyFromTexture(
+ enc,
+ srcTex,
+ C.MTLOrigin{
+ x: C.NSUInteger(srect.Min.X),
+ y: C.NSUInteger(srect.Min.Y),
+ },
+ C.MTLSize{
+ width: C.NSUInteger(ssz.X),
+ height: C.NSUInteger(ssz.Y),
+ depth: 1,
+ },
+ dstTex,
+ C.MTLOrigin{
+ x: C.NSUInteger(dorig.X),
+ y: C.NSUInteger(dorig.Y),
+ },
+ )
+}
+
+func (b *Backend) EndFrame() {
+ b.endCmdBuffer(false)
+}
+
+func (b *Backend) endCmdBuffer(wait bool) {
+ b.endEncoder()
+ if b.cmdBuffer == 0 {
+ return
+ }
+ C.cmdBufferCommit(b.cmdBuffer)
+ if wait {
+ C.cmdBufferWaitUntilCompleted(b.cmdBuffer)
+ }
+ if b.lastCmdBuffer != 0 {
+ C.CFRelease(b.lastCmdBuffer)
+ }
+ b.lastCmdBuffer = b.cmdBuffer
+ b.cmdBuffer = 0
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return driver.Caps{
+ MaxTextureSize: 8192,
+ Features: driver.FeatureSRGB | driver.FeatureCompute | driver.FeatureFloatRenderTargets,
+ }
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ panic("timers not supported")
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ panic("timers not supported")
+}
+
+func (b *Backend) Release() {
+ if b.cmdBuffer != 0 {
+ C.CFRelease(b.cmdBuffer)
+ }
+ if b.lastCmdBuffer != 0 {
+ C.CFRelease(b.lastCmdBuffer)
+ }
+ if b.stagingBuf != 0 {
+ C.CFRelease(b.stagingBuf)
+ }
+ C.CFRelease(b.queue)
+ C.CFRelease(b.dev)
+ *b = Backend{}
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
+ mformat := pixelFormatFor(format)
+ var usage C.MTLTextureUsage
+ if bindings&(driver.BufferBindingTexture|driver.BufferBindingShaderStorageRead) != 0 {
+ usage |= C.MTLTextureUsageShaderRead
+ }
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ usage |= C.MTLTextureUsageRenderTarget
+ }
+ if bindings&driver.BufferBindingShaderStorageWrite != 0 {
+ usage |= C.MTLTextureUsageShaderWrite
+ }
+ tex := C.newTexture(b.dev, C.NSUInteger(width), C.NSUInteger(height), mformat, usage)
+ if tex == 0 {
+ return nil, errors.New("metal: [MTLDevice newTextureWithDescriptor:] failed")
+ }
+ min := samplerFilterFor(minFilter)
+ max := samplerFilterFor(magFilter)
+ s := C.newSampler(b.dev, min, max)
+ if s == 0 {
+ C.CFRelease(tex)
+ return nil, errors.New("metal: [MTLDevice newSamplerStateWithDescriptor:] failed")
+ }
+ return &Texture{backend: b, texture: tex, sampler: s, width: width, height: height}, nil
+}
+
+func samplerFilterFor(f driver.TextureFilter) C.MTLSamplerMinMagFilter {
+ switch f {
+ case driver.FilterNearest:
+ return C.MTLSamplerMinMagFilterNearest
+ case driver.FilterLinear:
+ return C.MTLSamplerMinMagFilterLinear
+ default:
+ panic("invalid texture filter")
+ }
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ vsh, fsh := desc.VertexShader.(*Shader), desc.FragmentShader.(*Shader)
+ layout := desc.VertexLayout.Inputs
+ if got, exp := len(layout), len(vsh.inputs); got != exp {
+ return nil, fmt.Errorf("metal: number of input descriptors (%d) doesn't match number of inputs (%d)", got, exp)
+ }
+ formats := make([]C.MTLVertexFormat, len(layout))
+ offsets := make([]C.NSUInteger, len(layout))
+ for i, inp := range layout {
+ index := vsh.inputs[i].Location
+ formats[index] = vertFormatFor(vsh.inputs[i])
+ offsets[index] = C.NSUInteger(inp.Offset)
+ }
+ var (
+ fmtPtr *C.MTLVertexFormat
+ offPtr *C.NSUInteger
+ )
+ if len(layout) > 0 {
+ fmtPtr = &formats[0]
+ offPtr = &offsets[0]
+ }
+ srcFactor := blendFactorFor(desc.BlendDesc.SrcFactor)
+ dstFactor := blendFactorFor(desc.BlendDesc.DstFactor)
+ blend := C.int(0)
+ if desc.BlendDesc.Enable {
+ blend = 1
+ }
+ pf := b.pixelFmt
+ if f := desc.PixelFormat; f != driver.TextureFormatOutput {
+ pf = pixelFormatFor(f)
+ }
+ pipe := C.newRenderPipeline(
+ b.dev,
+ vsh.function,
+ fsh.function,
+ pf,
+ attributeBufferIndex,
+ C.NSUInteger(len(layout)), fmtPtr, offPtr,
+ C.NSUInteger(desc.VertexLayout.Stride),
+ blend, srcFactor, dstFactor,
+ 2, // Number of vertex buffers.
+ 1, // Number of fragment buffers.
+ )
+ if pipe == 0 {
+ return nil, errors.New("metal: pipeline construction failed")
+ }
+ return &Pipeline{pipeline: pipe, topology: primitiveFor(desc.Topology)}, nil
+}
+
+func dataTypeSize(d shader.DataType) int {
+ switch d {
+ case shader.DataTypeFloat:
+ return 4
+ default:
+ panic("unsupported data type")
+ }
+}
+
+func blendFactorFor(f driver.BlendFactor) C.MTLBlendFactor {
+ switch f {
+ case driver.BlendFactorZero:
+ return C.MTLBlendFactorZero
+ case driver.BlendFactorOne:
+ return C.MTLBlendFactorOne
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return C.MTLBlendFactorOneMinusSourceAlpha
+ case driver.BlendFactorDstColor:
+ return C.MTLBlendFactorDestinationColor
+ default:
+ panic("unsupported blend factor")
+ }
+}
+
+func vertFormatFor(f shader.InputLocation) C.MTLVertexFormat {
+ t := f.Type
+ s := f.Size
+ switch {
+ case t == shader.DataTypeFloat && s == 1:
+ return C.MTLVertexFormatFloat
+ case t == shader.DataTypeFloat && s == 2:
+ return C.MTLVertexFormatFloat2
+ case t == shader.DataTypeFloat && s == 3:
+ return C.MTLVertexFormatFloat3
+ case t == shader.DataTypeFloat && s == 4:
+ return C.MTLVertexFormatFloat4
+ default:
+ panic("unsupported data type")
+ }
+}
+
+func pixelFormatFor(f driver.TextureFormat) C.MTLPixelFormat {
+ switch f {
+ case driver.TextureFormatFloat:
+ return C.MTLPixelFormatR16Float
+ case driver.TextureFormatRGBA8:
+ return C.MTLPixelFormatRGBA8Unorm
+ case driver.TextureFormatSRGBA:
+ return C.MTLPixelFormatRGBA8Unorm_sRGB
+ default:
+ panic("unsupported pixel format")
+ }
+}
+
+func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
+ // Transfer buffer contents in command encoders on every use for
+ // smaller buffers. The advantage is that buffer re-use during a frame
+ // won't occur a GPU wait.
+ // We can't do this for buffers written to by the GPU and read by the client,
+ // and Metal doesn't require a buffer for indexed draws.
+ if size <= 4096 && typ&(driver.BufferBindingShaderStorageWrite|driver.BufferBindingIndices) == 0 {
+ return &Buffer{size: size, store: make([]byte, size)}, nil
+ }
+ buf := C.newBuffer(b.dev, C.NSUInteger(size), C.MTLResourceStorageModePrivate)
+ return &Buffer{backend: b, size: size, buffer: buf}, nil
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ buf, err := b.NewBuffer(typ, len(data))
+ if err != nil {
+ return nil, err
+ }
+ buf.Upload(data)
+ return buf, nil
+}
+
+func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
+ sh, err := b.newShader(src)
+ if err != nil {
+ return nil, err
+ }
+ defer sh.Release()
+ pipe := C.newComputePipeline(b.dev, sh.function)
+ if pipe == 0 {
+ return nil, fmt.Errorf("metal: compute program %q load failed", src.Name)
+ }
+ return &Program{pipeline: pipe, groupSize: src.WorkgroupSize}, nil
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ return b.newShader(src)
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ return b.newShader(src)
+}
+
+func (b *Backend) newShader(src shader.Sources) (*Shader, error) {
+ vsrc := []byte(src.MetalLib)
+ cname := C.CString(src.Name)
+ defer C.free(unsafe.Pointer(cname))
+ vlib := C.newLibrary(b.dev, cname, unsafe.Pointer(&vsrc[0]), C.size_t(len(vsrc)))
+ if vlib == 0 {
+ return nil, fmt.Errorf("metal: vertex shader %q load failed", src.Name)
+ }
+ defer C.CFRelease(vlib)
+ funcName := C.CString("main0")
+ defer C.free(unsafe.Pointer(funcName))
+ f := C.libraryNewFunction(vlib, funcName)
+ if f == 0 {
+ return nil, fmt.Errorf("metal: main function not found in %q", src.Name)
+ }
+ return &Shader{function: f, inputs: src.Inputs}, nil
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncViewport(enc, C.MTLViewport{
+ originX: C.double(x),
+ originY: C.double(y),
+ width: C.double(width),
+ height: C.double(height),
+ znear: 0.0,
+ zfar: 1.0,
+ })
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncDrawPrimitives(enc, b.topology, C.NSUInteger(off), C.NSUInteger(count))
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncDrawIndexedPrimitives(enc, b.topology, b.indexBuf.buffer, C.NSUInteger(off), C.NSUInteger(count))
+}
+
+func primitiveFor(mode driver.Topology) C.MTLPrimitiveType {
+ switch mode {
+ case driver.TopologyTriangles:
+ return C.MTLPrimitiveTypeTriangle
+ case driver.TopologyTriangleStrip:
+ return C.MTLPrimitiveTypeTriangleStrip
+ default:
+ panic("metal: unknown draw mode")
+ }
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ b.BindTexture(unit, tex)
+}
+
+func (b *Backend) BeginCompute() {
+ b.endEncoder()
+ b.ensureCmdBuffer()
+ for i := range b.bufSizes {
+ b.bufSizes[i] = 0
+ }
+ b.computeEnc = C.cmdBufferComputeEncoder(b.cmdBuffer)
+ if b.computeEnc == 0 {
+ panic("metal: [MTLCommandBuffer computeCommandEncoder:] failed")
+ }
+}
+
+func (b *Backend) EndCompute() {
+ if b.computeEnc == 0 {
+ panic("no active compute pass")
+ }
+ C.computeEncEnd(b.computeEnc)
+ C.CFRelease(b.computeEnc)
+ b.computeEnc = 0
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ enc := b.computeEnc
+ if enc == 0 {
+ panic("no active compute pass")
+ }
+ C.computeEncSetBytes(enc, unsafe.Pointer(&b.bufSizes[0]), C.NSUInteger(len(b.bufSizes)*4), spvBufferSizeConstantsBinding)
+ threadgroupsPerGrid := C.MTLSize{
+ width: C.NSUInteger(x), height: C.NSUInteger(y), depth: C.NSUInteger(z),
+ }
+ sz := b.prog.groupSize
+ threadsPerThreadgroup := C.MTLSize{
+ width: C.NSUInteger(sz[0]), height: C.NSUInteger(sz[1]), depth: C.NSUInteger(sz[2]),
+ }
+ C.computeEncDispatch(enc, threadgroupsPerGrid, threadsPerThreadgroup)
+}
+
+func (b *Backend) stagingBuffer(size int) (C.CFTypeRef, int) {
+ if b.stagingBuf == 0 || b.stagingOff+size > len(bufferStore(b.stagingBuf)) {
+ if b.stagingBuf != 0 {
+ C.CFRelease(b.stagingBuf)
+ }
+ cap := 2 * (b.stagingOff + size)
+ b.stagingBuf = C.newBuffer(b.dev, C.NSUInteger(cap), C.MTLResourceStorageModeShared|C.MTLResourceCPUCacheModeWriteCombined)
+ if b.stagingBuf == 0 {
+ panic(fmt.Errorf("metal: failed to allocate %d bytes of staging buffer", cap))
+ }
+ b.stagingOff = 0
+ }
+ off := b.stagingOff
+ b.stagingOff += size
+ return b.stagingBuf, off
+}
+
+func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if len(pixels) == 0 {
+ return
+ }
+ if stride == 0 {
+ stride = size.X * 4
+ }
+ dstStride := size.X * 4
+ n := size.Y * dstStride
+ buf, off := t.backend.stagingBuffer(n)
+ store := bufferSlice(buf, off, n)
+ var srcOff, dstOff int
+ for y := 0; y < size.Y; y++ {
+ srcRow := pixels[srcOff : srcOff+dstStride]
+ dstRow := store[dstOff : dstOff+dstStride]
+ copy(dstRow, srcRow)
+ dstOff += dstStride
+ srcOff += stride
+ }
+ enc := t.backend.startBlit()
+ orig := C.MTLOrigin{
+ x: C.NSUInteger(offset.X),
+ y: C.NSUInteger(offset.Y),
+ }
+ msize := C.MTLSize{
+ width: C.NSUInteger(size.X),
+ height: C.NSUInteger(size.Y),
+ depth: 1,
+ }
+ C.blitEncCopyBufferToTexture(enc, buf, t.texture, C.NSUInteger(off), C.NSUInteger(dstStride), C.NSUInteger(len(store)), msize, orig)
+}
+
+func (t *Texture) Release() {
+ if t.foreign {
+ panic("metal: release of external texture")
+ }
+ C.CFRelease(t.texture)
+ C.CFRelease(t.sampler)
+ *t = Texture{}
+}
+
+func (p *Pipeline) Release() {
+ C.CFRelease(p.pipeline)
+ *p = Pipeline{}
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {}
+
+func (b *Backend) BindTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ if enc := b.renderEnc; enc != 0 {
+ C.renderEncSetFragmentTexture(enc, C.NSUInteger(unit), t.texture)
+ C.renderEncSetFragmentSamplerState(enc, C.NSUInteger(unit), t.sampler)
+ } else if enc := b.computeEnc; enc != 0 {
+ C.computeEncSetTexture(enc, C.NSUInteger(unit), t.texture)
+ } else {
+ panic("no active render nor compute pass")
+ }
+}
+
+func (b *Backend) ensureCmdBuffer() {
+ if b.cmdBuffer != 0 {
+ return
+ }
+ b.cmdBuffer = C.queueNewBuffer(b.queue)
+ if b.cmdBuffer == 0 {
+ panic("metal: [MTLCommandQueue cmdBuffer] failed")
+ }
+}
+
+func (b *Backend) BindPipeline(pipe driver.Pipeline) {
+ p := pipe.(*Pipeline)
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncSetRenderPipelineState(enc, p.pipeline)
+ b.topology = p.topology
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ enc := b.computeEnc
+ if enc == 0 {
+ panic("no active compute pass")
+ }
+ p := prog.(*Program)
+ C.computeEncSetPipeline(enc, p.pipeline)
+ b.prog = p
+}
+
+func (s *Shader) Release() {
+ C.CFRelease(s.function)
+ *s = Shader{}
+}
+
+func (p *Program) Release() {
+ C.CFRelease(p.pipeline)
+ *p = Program{}
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ b.bufSizes[binding] = uint32(buf.size)
+ enc := b.computeEnc
+ if enc == 0 {
+ panic("no active compute pass")
+ }
+ if buf.buffer != 0 {
+ C.computeEncSetBuffer(enc, C.NSUInteger(binding), buf.buffer)
+ } else if buf.size > 0 {
+ C.computeEncSetBytes(enc, unsafe.Pointer(&buf.store[0]), C.NSUInteger(buf.size), C.NSUInteger(binding))
+ }
+}
+
+func (b *Backend) BindUniforms(buf driver.Buffer) {
+ bf := buf.(*Buffer)
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ if bf.buffer != 0 {
+ C.renderEncSetVertexBuffer(enc, bf.buffer, uniformBufferIndex, 0)
+ C.renderEncSetFragmentBuffer(enc, bf.buffer, uniformBufferIndex, 0)
+ } else if bf.size > 0 {
+ C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
+ C.renderEncSetFragmentBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
+ }
+}
+
+func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
+ bf := buf.(*Buffer)
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ if bf.buffer != 0 {
+ C.renderEncSetVertexBuffer(enc, bf.buffer, attributeBufferIndex, C.NSUInteger(offset))
+ } else if n := bf.size - offset; n > 0 {
+ C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[offset]), C.NSUInteger(n), attributeBufferIndex)
+ }
+}
+
+func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
+ b.indexBuf = buf.(*Buffer)
+}
+
+func (b *Buffer) Download(data []byte) error {
+ if len(data) > b.size {
+ panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size))
+ }
+ buf, off := b.backend.stagingBuffer(len(data))
+ enc := b.backend.startBlit()
+ C.blitEncCopyBufferToBuffer(enc, b.buffer, buf, 0, C.NSUInteger(off), C.NSUInteger(len(data)))
+ b.backend.endCmdBuffer(true)
+ store := bufferSlice(buf, off, len(data))
+ copy(data, store)
+ return nil
+}
+
+func (b *Buffer) Upload(data []byte) {
+ if len(data) > b.size {
+ panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size))
+ }
+ if b.buffer == 0 {
+ copy(b.store, data)
+ return
+ }
+ buf, off := b.backend.stagingBuffer(len(data))
+ store := bufferSlice(buf, off, len(data))
+ copy(store, data)
+ enc := b.backend.startBlit()
+ C.blitEncCopyBufferToBuffer(enc, buf, b.buffer, C.NSUInteger(off), 0, C.NSUInteger(len(store)))
+}
+
+func bufferStore(buf C.CFTypeRef) []byte {
+ contents := C.bufferContents(buf)
+ return (*(*[1 << 30]byte)(contents.addr))[:contents.size:contents.size]
+}
+
+func bufferSlice(buf C.CFTypeRef, off, len int) []byte {
+ store := bufferStore(buf)
+ return store[off : off+len]
+}
+
+func (b *Buffer) Release() {
+ if b.buffer != 0 {
+ C.CFRelease(b.buffer)
+ }
+ *b = Buffer{}
+}
+
+func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ if len(pixels) == 0 {
+ return nil
+ }
+ sz := src.Size()
+ orig := C.MTLOrigin{
+ x: C.NSUInteger(src.Min.X),
+ y: C.NSUInteger(src.Min.Y),
+ }
+ msize := C.MTLSize{
+ width: C.NSUInteger(sz.X),
+ height: C.NSUInteger(sz.Y),
+ depth: 1,
+ }
+ stageStride := sz.X * 4
+ n := sz.Y * stageStride
+ buf, off := t.backend.stagingBuffer(n)
+ enc := t.backend.startBlit()
+ C.blitEncCopyTextureToBuffer(enc, t.texture, buf, C.NSUInteger(off), C.NSUInteger(stageStride), C.NSUInteger(n), msize, orig)
+ t.backend.endCmdBuffer(true)
+ store := bufferSlice(buf, off, n)
+ var srcOff, dstOff int
+ for y := 0; y < sz.Y; y++ {
+ dstRow := pixels[srcOff : srcOff+stageStride]
+ srcRow := store[dstOff : dstOff+stageStride]
+ copy(dstRow, srcRow)
+ dstOff += stageStride
+ srcOff += stride
+ }
+ return nil
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
+ b.endEncoder()
+ b.ensureCmdBuffer()
+ f := tex.(*Texture)
+ col := d.ClearColor
+ var act C.MTLLoadAction
+ switch d.Action {
+ case driver.LoadActionKeep:
+ act = C.MTLLoadActionLoad
+ case driver.LoadActionClear:
+ act = C.MTLLoadActionClear
+ case driver.LoadActionInvalidate:
+ act = C.MTLLoadActionDontCare
+ }
+ b.renderEnc = C.cmdBufferRenderEncoder(b.cmdBuffer, f.texture, act, C.float(col.R), C.float(col.G), C.float(col.B), C.float(col.A))
+ if b.renderEnc == 0 {
+ panic("metal: [MTLCommandBuffer renderCommandEncoderWithDescriptor:] failed")
+ }
+}
+
+func (b *Backend) EndRenderPass() {
+ if b.renderEnc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncEnd(b.renderEnc)
+ C.CFRelease(b.renderEnc)
+ b.renderEnc = 0
+}
+
+func (b *Backend) endEncoder() {
+ if b.renderEnc != 0 {
+ panic("active render pass")
+ }
+ if b.computeEnc != 0 {
+ panic("active compute pass")
+ }
+ if b.blitEnc != 0 {
+ C.blitEncEnd(b.blitEnc)
+ C.CFRelease(b.blitEnc)
+ b.blitEnc = 0
+ }
+}
+
+func (f *Texture) ImplementsRenderTarget() {}
diff --git a/vendor/gioui.org/gpu/internal/opengl/opengl.go b/vendor/gioui.org/gpu/internal/opengl/opengl.go
new file mode 100644
index 0000000..ef89197
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/opengl/opengl.go
@@ -0,0 +1,1357 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package opengl
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "strings"
+ "time"
+ "unsafe"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/gl"
+ "gioui.org/shader"
+)
+
+// Backend implements driver.Device.
+type Backend struct {
+ funcs *gl.Functions
+
+ clear bool
+ glstate glState
+ state state
+ savedState glState
+ sharedCtx bool
+
+ glver [2]int
+ gles bool
+ feats driver.Caps
+ // floatTriple holds the settings for floating point
+ // textures.
+ floatTriple textureTriple
+ // Single channel alpha textures.
+ alphaTriple textureTriple
+ srgbaTriple textureTriple
+ storage [storageBindings]*buffer
+
+ outputFBO gl.Framebuffer
+ sRGBFBO *SRGBFBO
+
+ // vertArray is bound during a frame. We don't need it, but
+ // core desktop OpenGL profile 3.3 requires some array bound.
+ vertArray gl.VertexArray
+}
+
+// State tracking.
+type glState struct {
+ drawFBO gl.Framebuffer
+ readFBO gl.Framebuffer
+ renderBuf gl.Renderbuffer
+ vertAttribs [5]struct {
+ obj gl.Buffer
+ enabled bool
+ size int
+ typ gl.Enum
+ normalized bool
+ stride int
+ offset uintptr
+ }
+ prog gl.Program
+ texUnits struct {
+ active gl.Enum
+ binds [2]gl.Texture
+ }
+ arrayBuf gl.Buffer
+ elemBuf gl.Buffer
+ uniBuf gl.Buffer
+ uniBufs [2]gl.Buffer
+ storeBuf gl.Buffer
+ storeBufs [4]gl.Buffer
+ vertArray gl.VertexArray
+ srgb bool
+ blend struct {
+ enable bool
+ srcRGB, dstRGB gl.Enum
+ srcA, dstA gl.Enum
+ }
+ clearColor [4]float32
+ viewport [4]int
+ unpack_row_length int
+ pack_row_length int
+}
+
+type state struct {
+ pipeline *pipeline
+ buffer bufferBinding
+}
+
+type bufferBinding struct {
+ obj gl.Buffer
+ offset int
+}
+
+type timer struct {
+ funcs *gl.Functions
+ obj gl.Query
+}
+
+type texture struct {
+ backend *Backend
+ obj gl.Texture
+ fbo gl.Framebuffer
+ hasFBO bool
+ triple textureTriple
+ width int
+ height int
+ bindings driver.BufferBinding
+ foreign bool
+}
+
+type pipeline struct {
+ prog *program
+ inputs []shader.InputLocation
+ layout driver.VertexLayout
+ blend driver.BlendDesc
+ topology driver.Topology
+}
+
+type buffer struct {
+ backend *Backend
+ hasBuffer bool
+ obj gl.Buffer
+ typ driver.BufferBinding
+ size int
+ immutable bool
+ // For emulation of uniform buffers.
+ data []byte
+}
+
+type glshader struct {
+ backend *Backend
+ obj gl.Shader
+ src shader.Sources
+}
+
+type program struct {
+ backend *Backend
+ obj gl.Program
+ vertUniforms uniforms
+ fragUniforms uniforms
+}
+
+type uniforms struct {
+ locs []uniformLocation
+ size int
+}
+
+type uniformLocation struct {
+ uniform gl.Uniform
+ offset int
+ typ shader.DataType
+ size int
+}
+
+type inputLayout struct {
+ inputs []shader.InputLocation
+ layout []driver.InputDesc
+}
+
+// textureTriple holds the type settings for
+// a TexImage2D call.
+type textureTriple struct {
+ internalFormat gl.Enum
+ format gl.Enum
+ typ gl.Enum
+}
+
+const (
+ storageBindings = 32
+)
+
+func init() {
+ driver.NewOpenGLDevice = newOpenGLDevice
+}
+
+// Supporting compute programs is theoretically possible with OpenGL ES 3.1. In
+// practice, there are too many driver issues, especially on Android (e.g.
+// Google Pixel, Samsung J2 are both broken i different ways). Disable support
+// and rely on Vulkan for devices that support it, and the CPU fallback for
+// devices that don't.
+const brokenGLES31 = true
+
+func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) {
+ f, err := gl.NewFunctions(api.Context, api.ES)
+ if err != nil {
+ return nil, err
+ }
+ exts := strings.Split(f.GetString(gl.EXTENSIONS), " ")
+ glVer := f.GetString(gl.VERSION)
+ ver, gles, err := gl.ParseGLVersion(glVer)
+ if err != nil {
+ return nil, err
+ }
+ floatTriple, ffboErr := floatTripleFor(f, ver, exts)
+ srgbaTriple, srgbErr := srgbaTripleFor(ver, exts)
+ gles31 := gles && (ver[0] > 3 || (ver[0] == 3 && ver[1] >= 1))
+ b := &Backend{
+ glver: ver,
+ gles: gles,
+ funcs: f,
+ floatTriple: floatTriple,
+ alphaTriple: alphaTripleFor(ver),
+ srgbaTriple: srgbaTriple,
+ sharedCtx: api.Shared,
+ }
+ b.feats.BottomLeftOrigin = true
+ if srgbErr == nil {
+ b.feats.Features |= driver.FeatureSRGB
+ }
+ if ffboErr == nil {
+ b.feats.Features |= driver.FeatureFloatRenderTargets
+ }
+ if gles31 && !brokenGLES31 {
+ b.feats.Features |= driver.FeatureCompute
+ }
+ if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") {
+ b.feats.Features |= driver.FeatureTimers
+ }
+ b.feats.MaxTextureSize = f.GetInteger(gl.MAX_TEXTURE_SIZE)
+ if !b.sharedCtx {
+ // We have exclusive access to the context, so query the GL state once
+ // instead of at each frame.
+ b.glstate = b.queryState()
+ }
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ b.clear = clear
+ if b.sharedCtx {
+ b.glstate = b.queryState()
+ b.savedState = b.glstate
+ }
+ b.state = state{}
+ var renderFBO gl.Framebuffer
+ if target != nil {
+ switch t := target.(type) {
+ case driver.OpenGLRenderTarget:
+ renderFBO = gl.Framebuffer(t)
+ case *texture:
+ renderFBO = t.ensureFBO()
+ default:
+ panic(fmt.Errorf("opengl: invalid render target type: %T", target))
+ }
+ }
+ b.outputFBO = renderFBO
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO)
+ if b.gles {
+ // If the output framebuffer is not in the sRGB colorspace already, emulate it.
+ var fbEncoding int
+ if !renderFBO.Valid() {
+ fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
+ } else {
+ fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
+ }
+ if fbEncoding == gl.LINEAR && viewport != (image.Point{}) {
+ if b.sRGBFBO == nil {
+ sfbo, err := NewSRGBFBO(b.funcs, &b.glstate)
+ if err != nil {
+ panic(err)
+ }
+ b.sRGBFBO = sfbo
+ }
+ if err := b.sRGBFBO.Refresh(viewport); err != nil {
+ panic(err)
+ }
+ renderFBO = b.sRGBFBO.Framebuffer()
+ } else if b.sRGBFBO != nil {
+ b.sRGBFBO.Release()
+ b.sRGBFBO = nil
+ }
+ } else {
+ b.glstate.set(b.funcs, gl.FRAMEBUFFER_SRGB, true)
+ if !b.vertArray.Valid() {
+ b.vertArray = b.funcs.CreateVertexArray()
+ }
+ b.glstate.bindVertexArray(b.funcs, b.vertArray)
+ }
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO)
+ if b.sRGBFBO != nil && !clear {
+ b.clearOutput(0, 0, 0, 0)
+ }
+ return &texture{backend: b, fbo: renderFBO, hasFBO: true, foreign: true}
+}
+
+func (b *Backend) EndFrame() {
+ if b.sRGBFBO != nil {
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, b.outputFBO)
+ if b.clear {
+ b.SetBlend(false)
+ } else {
+ b.BlendFunc(driver.BlendFactorOne, driver.BlendFactorOneMinusSrcAlpha)
+ b.SetBlend(true)
+ }
+ b.sRGBFBO.Blit()
+ }
+ if b.sharedCtx {
+ b.restoreState(b.savedState)
+ }
+}
+
+func (b *Backend) queryState() glState {
+ s := glState{
+ prog: gl.Program(b.funcs.GetBinding(gl.CURRENT_PROGRAM)),
+ arrayBuf: gl.Buffer(b.funcs.GetBinding(gl.ARRAY_BUFFER_BINDING)),
+ elemBuf: gl.Buffer(b.funcs.GetBinding(gl.ELEMENT_ARRAY_BUFFER_BINDING)),
+ drawFBO: gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)),
+ clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE),
+ viewport: b.funcs.GetInteger4(gl.VIEWPORT),
+ unpack_row_length: b.funcs.GetInteger(gl.UNPACK_ROW_LENGTH),
+ pack_row_length: b.funcs.GetInteger(gl.PACK_ROW_LENGTH),
+ }
+ s.blend.enable = b.funcs.IsEnabled(gl.BLEND)
+ s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB))
+ s.blend.dstRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_RGB))
+ s.blend.srcA = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_ALPHA))
+ s.blend.dstA = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_ALPHA))
+ s.texUnits.active = gl.Enum(b.funcs.GetInteger(gl.ACTIVE_TEXTURE))
+ if !b.gles {
+ s.srgb = b.funcs.IsEnabled(gl.FRAMEBUFFER_SRGB)
+ }
+ if !b.gles || b.glver[0] >= 3 {
+ s.vertArray = gl.VertexArray(b.funcs.GetBinding(gl.VERTEX_ARRAY_BINDING))
+ s.readFBO = gl.Framebuffer(b.funcs.GetBinding(gl.READ_FRAMEBUFFER_BINDING))
+ s.uniBuf = gl.Buffer(b.funcs.GetBinding(gl.UNIFORM_BUFFER_BINDING))
+ for i := range s.uniBufs {
+ s.uniBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.UNIFORM_BUFFER_BINDING, i))
+ }
+ }
+ if b.gles && (b.glver[0] > 3 || (b.glver[0] == 3 && b.glver[1] >= 1)) {
+ s.storeBuf = gl.Buffer(b.funcs.GetBinding(gl.SHADER_STORAGE_BUFFER_BINDING))
+ for i := range s.storeBufs {
+ s.storeBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.SHADER_STORAGE_BUFFER_BINDING, i))
+ }
+ }
+ for i := range s.texUnits.binds {
+ s.activeTexture(b.funcs, gl.TEXTURE0+gl.Enum(i))
+ s.texUnits.binds[i] = gl.Texture(b.funcs.GetBinding(gl.TEXTURE_BINDING_2D))
+ }
+ for i := range s.vertAttribs {
+ a := &s.vertAttribs[i]
+ a.enabled = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED) != gl.FALSE
+ a.obj = gl.Buffer(b.funcs.GetVertexAttribBinding(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED))
+ a.size = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_SIZE)
+ a.typ = gl.Enum(b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_TYPE))
+ a.normalized = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) != gl.FALSE
+ a.stride = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_STRIDE)
+ a.offset = b.funcs.GetVertexAttribPointer(i, gl.VERTEX_ATTRIB_ARRAY_POINTER)
+ }
+ return s
+}
+
+func (b *Backend) restoreState(dst glState) {
+ src := b.glstate
+ f := b.funcs
+ for i, unit := range dst.texUnits.binds {
+ src.bindTexture(f, i, unit)
+ }
+ src.activeTexture(f, dst.texUnits.active)
+ src.bindFramebuffer(f, gl.FRAMEBUFFER, dst.drawFBO)
+ src.bindFramebuffer(f, gl.READ_FRAMEBUFFER, dst.readFBO)
+ src.set(f, gl.BLEND, dst.blend.enable)
+ bf := dst.blend
+ src.setBlendFuncSeparate(f, bf.srcRGB, bf.dstRGB, bf.srcA, bf.dstA)
+ src.set(f, gl.FRAMEBUFFER_SRGB, dst.srgb)
+ src.bindVertexArray(f, dst.vertArray)
+ src.useProgram(f, dst.prog)
+ src.bindBuffer(f, gl.ELEMENT_ARRAY_BUFFER, dst.elemBuf)
+ for i, b := range dst.uniBufs {
+ src.bindBufferBase(f, gl.UNIFORM_BUFFER, i, b)
+ }
+ src.bindBuffer(f, gl.UNIFORM_BUFFER, dst.uniBuf)
+ for i, b := range dst.storeBufs {
+ src.bindBufferBase(f, gl.SHADER_STORAGE_BUFFER, i, b)
+ }
+ src.bindBuffer(f, gl.SHADER_STORAGE_BUFFER, dst.storeBuf)
+ col := dst.clearColor
+ src.setClearColor(f, col[0], col[1], col[2], col[3])
+ for i, attr := range dst.vertAttribs {
+ src.setVertexAttribArray(f, i, attr.enabled)
+ src.vertexAttribPointer(f, attr.obj, i, attr.size, attr.typ, attr.normalized, attr.stride, int(attr.offset))
+ }
+ src.bindBuffer(f, gl.ARRAY_BUFFER, dst.arrayBuf)
+ v := dst.viewport
+ src.setViewport(f, v[0], v[1], v[2], v[3])
+ src.pixelStorei(f, gl.UNPACK_ROW_LENGTH, dst.unpack_row_length)
+ src.pixelStorei(f, gl.PACK_ROW_LENGTH, dst.pack_row_length)
+}
+
+func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) {
+ a := &s.vertAttribs[idx]
+ if enabled != a.enabled {
+ if enabled {
+ f.EnableVertexAttribArray(gl.Attrib(idx))
+ } else {
+ f.DisableVertexAttribArray(gl.Attrib(idx))
+ }
+ a.enabled = enabled
+ }
+}
+
+func (s *glState) vertexAttribPointer(f *gl.Functions, buf gl.Buffer, idx, size int, typ gl.Enum, normalized bool, stride, offset int) {
+ s.bindBuffer(f, gl.ARRAY_BUFFER, buf)
+ a := &s.vertAttribs[idx]
+ a.obj = buf
+ a.size = size
+ a.typ = typ
+ a.normalized = normalized
+ a.stride = stride
+ a.offset = uintptr(offset)
+ f.VertexAttribPointer(gl.Attrib(idx), a.size, a.typ, a.normalized, a.stride, int(a.offset))
+}
+
+func (s *glState) activeTexture(f *gl.Functions, unit gl.Enum) {
+ if unit != s.texUnits.active {
+ f.ActiveTexture(unit)
+ s.texUnits.active = unit
+ }
+}
+
+func (s *glState) bindRenderbuffer(f *gl.Functions, target gl.Enum, r gl.Renderbuffer) {
+ if !r.Equal(s.renderBuf) {
+ f.BindRenderbuffer(gl.RENDERBUFFER, r)
+ s.renderBuf = r
+ }
+}
+
+func (s *glState) bindTexture(f *gl.Functions, unit int, t gl.Texture) {
+ s.activeTexture(f, gl.TEXTURE0+gl.Enum(unit))
+ if !t.Equal(s.texUnits.binds[unit]) {
+ f.BindTexture(gl.TEXTURE_2D, t)
+ s.texUnits.binds[unit] = t
+ }
+}
+
+func (s *glState) bindVertexArray(f *gl.Functions, a gl.VertexArray) {
+ if !a.Equal(s.vertArray) {
+ f.BindVertexArray(a)
+ s.vertArray = a
+ }
+}
+
+func (s *glState) deleteRenderbuffer(f *gl.Functions, r gl.Renderbuffer) {
+ f.DeleteRenderbuffer(r)
+ if r.Equal(s.renderBuf) {
+ s.renderBuf = gl.Renderbuffer{}
+ }
+}
+
+func (s *glState) deleteFramebuffer(f *gl.Functions, fbo gl.Framebuffer) {
+ f.DeleteFramebuffer(fbo)
+ if fbo.Equal(s.drawFBO) {
+ s.drawFBO = gl.Framebuffer{}
+ }
+ if fbo.Equal(s.readFBO) {
+ s.readFBO = gl.Framebuffer{}
+ }
+}
+
+func (s *glState) deleteBuffer(f *gl.Functions, b gl.Buffer) {
+ f.DeleteBuffer(b)
+ if b.Equal(s.arrayBuf) {
+ s.arrayBuf = gl.Buffer{}
+ }
+ if b.Equal(s.elemBuf) {
+ s.elemBuf = gl.Buffer{}
+ }
+ if b.Equal(s.uniBuf) {
+ s.uniBuf = gl.Buffer{}
+ }
+ if b.Equal(s.storeBuf) {
+ s.uniBuf = gl.Buffer{}
+ }
+ for i, b2 := range s.storeBufs {
+ if b.Equal(b2) {
+ s.storeBufs[i] = gl.Buffer{}
+ }
+ }
+ for i, b2 := range s.uniBufs {
+ if b.Equal(b2) {
+ s.uniBufs[i] = gl.Buffer{}
+ }
+ }
+}
+
+func (s *glState) deleteProgram(f *gl.Functions, p gl.Program) {
+ f.DeleteProgram(p)
+ if p.Equal(s.prog) {
+ s.prog = gl.Program{}
+ }
+}
+
+func (s *glState) deleteVertexArray(f *gl.Functions, a gl.VertexArray) {
+ f.DeleteVertexArray(a)
+ if a.Equal(s.vertArray) {
+ s.vertArray = gl.VertexArray{}
+ }
+}
+
+func (s *glState) deleteTexture(f *gl.Functions, t gl.Texture) {
+ f.DeleteTexture(t)
+ binds := &s.texUnits.binds
+ for i, obj := range binds {
+ if t.Equal(obj) {
+ binds[i] = gl.Texture{}
+ }
+ }
+}
+
+func (s *glState) useProgram(f *gl.Functions, p gl.Program) {
+ if !p.Equal(s.prog) {
+ f.UseProgram(p)
+ s.prog = p
+ }
+}
+
+func (s *glState) bindFramebuffer(f *gl.Functions, target gl.Enum, fbo gl.Framebuffer) {
+ switch target {
+ case gl.FRAMEBUFFER:
+ if fbo.Equal(s.drawFBO) && fbo.Equal(s.readFBO) {
+ return
+ }
+ s.drawFBO = fbo
+ s.readFBO = fbo
+ case gl.READ_FRAMEBUFFER:
+ if fbo.Equal(s.readFBO) {
+ return
+ }
+ s.readFBO = fbo
+ case gl.DRAW_FRAMEBUFFER:
+ if fbo.Equal(s.drawFBO) {
+ return
+ }
+ s.drawFBO = fbo
+ default:
+ panic("unknown target")
+ }
+ f.BindFramebuffer(target, fbo)
+}
+
+func (s *glState) bindBufferBase(f *gl.Functions, target gl.Enum, idx int, buf gl.Buffer) {
+ switch target {
+ case gl.UNIFORM_BUFFER:
+ if buf.Equal(s.uniBuf) && buf.Equal(s.uniBufs[idx]) {
+ return
+ }
+ s.uniBuf = buf
+ s.uniBufs[idx] = buf
+ case gl.SHADER_STORAGE_BUFFER:
+ if buf.Equal(s.storeBuf) && buf.Equal(s.storeBufs[idx]) {
+ return
+ }
+ s.storeBuf = buf
+ s.storeBufs[idx] = buf
+ default:
+ panic("unknown buffer target")
+ }
+ f.BindBufferBase(target, idx, buf)
+}
+
+func (s *glState) bindBuffer(f *gl.Functions, target gl.Enum, buf gl.Buffer) {
+ switch target {
+ case gl.ARRAY_BUFFER:
+ if buf.Equal(s.arrayBuf) {
+ return
+ }
+ s.arrayBuf = buf
+ case gl.ELEMENT_ARRAY_BUFFER:
+ if buf.Equal(s.elemBuf) {
+ return
+ }
+ s.elemBuf = buf
+ case gl.UNIFORM_BUFFER:
+ if buf.Equal(s.uniBuf) {
+ return
+ }
+ s.uniBuf = buf
+ case gl.SHADER_STORAGE_BUFFER:
+ if buf.Equal(s.storeBuf) {
+ return
+ }
+ s.storeBuf = buf
+ default:
+ panic("unknown buffer target")
+ }
+ f.BindBuffer(target, buf)
+}
+
+func (s *glState) pixelStorei(f *gl.Functions, pname gl.Enum, val int) {
+ switch pname {
+ case gl.UNPACK_ROW_LENGTH:
+ if val == s.unpack_row_length {
+ return
+ }
+ s.unpack_row_length = val
+ case gl.PACK_ROW_LENGTH:
+ if val == s.pack_row_length {
+ return
+ }
+ s.pack_row_length = val
+ default:
+ panic("unsupported PixelStorei pname")
+ }
+ f.PixelStorei(pname, val)
+}
+
+func (s *glState) setClearColor(f *gl.Functions, r, g, b, a float32) {
+ col := [4]float32{r, g, b, a}
+ if col != s.clearColor {
+ f.ClearColor(r, g, b, a)
+ s.clearColor = col
+ }
+}
+
+func (s *glState) setViewport(f *gl.Functions, x, y, width, height int) {
+ view := [4]int{x, y, width, height}
+ if view != s.viewport {
+ f.Viewport(x, y, width, height)
+ s.viewport = view
+ }
+}
+
+func (s *glState) setBlendFuncSeparate(f *gl.Functions, srcRGB, dstRGB, srcA, dstA gl.Enum) {
+ if srcRGB != s.blend.srcRGB || dstRGB != s.blend.dstRGB || srcA != s.blend.srcA || dstA != s.blend.dstA {
+ s.blend.srcRGB = srcRGB
+ s.blend.dstRGB = dstRGB
+ s.blend.srcA = srcA
+ s.blend.dstA = dstA
+ f.BlendFuncSeparate(srcA, dstA, srcA, dstA)
+ }
+}
+
+func (s *glState) set(f *gl.Functions, target gl.Enum, enable bool) {
+ switch target {
+ case gl.FRAMEBUFFER_SRGB:
+ if s.srgb == enable {
+ return
+ }
+ s.srgb = enable
+ case gl.BLEND:
+ if enable == s.blend.enable {
+ return
+ }
+ s.blend.enable = enable
+ default:
+ panic("unknown enable")
+ }
+ if enable {
+ f.Enable(target)
+ } else {
+ f.Disable(target)
+ }
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return b.feats
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ return &timer{
+ funcs: b.funcs,
+ obj: b.funcs.CreateQuery(),
+ }
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ return b.funcs.GetInteger(gl.GPU_DISJOINT_EXT) == gl.FALSE
+}
+
+func (t *texture) ensureFBO() gl.Framebuffer {
+ if t.hasFBO {
+ return t.fbo
+ }
+ b := t.backend
+ oldFBO := b.glstate.drawFBO
+ defer func() {
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, oldFBO)
+ }()
+ glErr(b.funcs)
+ fb := b.funcs.CreateFramebuffer()
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fb)
+ if err := glErr(b.funcs); err != nil {
+ b.funcs.DeleteFramebuffer(fb)
+ panic(err)
+ }
+ b.funcs.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.obj, 0)
+ if st := b.funcs.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
+ b.funcs.DeleteFramebuffer(fb)
+ panic(fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError()))
+ }
+ t.fbo = fb
+ t.hasFBO = true
+ return fb
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, binding driver.BufferBinding) (driver.Texture, error) {
+ glErr(b.funcs)
+ tex := &texture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height, bindings: binding}
+ switch format {
+ case driver.TextureFormatFloat:
+ tex.triple = b.floatTriple
+ case driver.TextureFormatSRGBA:
+ tex.triple = b.srgbaTriple
+ case driver.TextureFormatRGBA8:
+ tex.triple = textureTriple{gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE}
+ default:
+ return nil, errors.New("unsupported texture format")
+ }
+ b.BindTexture(0, tex)
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, toTexFilter(magFilter))
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, toTexFilter(minFilter))
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
+ if b.gles && b.glver[0] >= 3 {
+ // Immutable textures are required for BindImageTexture, and can't hurt otherwise.
+ b.funcs.TexStorage2D(gl.TEXTURE_2D, 1, tex.triple.internalFormat, width, height)
+ } else {
+ b.funcs.TexImage2D(gl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ)
+ }
+ if err := glErr(b.funcs); err != nil {
+ tex.Release()
+ return nil, err
+ }
+ return tex, nil
+}
+
+func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
+ glErr(b.funcs)
+ buf := &buffer{backend: b, typ: typ, size: size}
+ if typ&driver.BufferBindingUniforms != 0 {
+ if typ != driver.BufferBindingUniforms {
+ return nil, errors.New("uniforms buffers cannot be bound as anything else")
+ }
+ buf.data = make([]byte, size)
+ }
+ if typ&^driver.BufferBindingUniforms != 0 {
+ buf.hasBuffer = true
+ buf.obj = b.funcs.CreateBuffer()
+ if err := glErr(b.funcs); err != nil {
+ buf.Release()
+ return nil, err
+ }
+ firstBinding := firstBufferType(typ)
+ b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj)
+ b.funcs.BufferData(firstBinding, size, gl.DYNAMIC_DRAW, nil)
+ }
+ return buf, nil
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ glErr(b.funcs)
+ obj := b.funcs.CreateBuffer()
+ buf := &buffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true}
+ firstBinding := firstBufferType(typ)
+ b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj)
+ b.funcs.BufferData(firstBinding, len(data), gl.STATIC_DRAW, data)
+ buf.immutable = true
+ if err := glErr(b.funcs); err != nil {
+ buf.Release()
+ return nil, err
+ }
+ return buf, nil
+}
+
+func glErr(f *gl.Functions) error {
+ if st := f.GetError(); st != gl.NO_ERROR {
+ return fmt.Errorf("glGetError: %#x", st)
+ }
+ return nil
+}
+
+func (b *Backend) Release() {
+ if b.sRGBFBO != nil {
+ b.sRGBFBO.Release()
+ }
+ if b.vertArray.Valid() {
+ b.glstate.deleteVertexArray(b.funcs, b.vertArray)
+ }
+ *b = Backend{}
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ for binding, buf := range b.storage {
+ if buf != nil {
+ b.glstate.bindBufferBase(b.funcs, gl.SHADER_STORAGE_BUFFER, binding, buf.obj)
+ }
+ }
+ b.funcs.DispatchCompute(x, y, z)
+ b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS)
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ t := tex.(*texture)
+ var acc gl.Enum
+ switch t.bindings & (driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite) {
+ case driver.BufferBindingShaderStorageRead:
+ acc = gl.READ_ONLY
+ case driver.BufferBindingShaderStorageWrite:
+ acc = gl.WRITE_ONLY
+ case driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite:
+ acc = gl.READ_WRITE
+ default:
+ panic("unsupported access bits")
+ }
+ b.funcs.BindImageTexture(unit, t.obj, 0, false, 0, acc, t.triple.internalFormat)
+}
+
+func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) {
+ src, dst := toGLBlendFactor(sfactor), toGLBlendFactor(dfactor)
+ b.glstate.setBlendFuncSeparate(b.funcs, src, dst, src, dst)
+}
+
+func toGLBlendFactor(f driver.BlendFactor) gl.Enum {
+ switch f {
+ case driver.BlendFactorOne:
+ return gl.ONE
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return gl.ONE_MINUS_SRC_ALPHA
+ case driver.BlendFactorZero:
+ return gl.ZERO
+ case driver.BlendFactorDstColor:
+ return gl.DST_COLOR
+ default:
+ panic("unsupported blend factor")
+ }
+}
+
+func (b *Backend) SetBlend(enable bool) {
+ b.glstate.set(b.funcs, gl.BLEND, enable)
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ b.prepareDraw()
+ // off is in 16-bit indices, but DrawElements take a byte offset.
+ byteOff := off * 2
+ b.funcs.DrawElements(toGLDrawMode(b.state.pipeline.topology), count, gl.UNSIGNED_SHORT, byteOff)
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ b.prepareDraw()
+ b.funcs.DrawArrays(toGLDrawMode(b.state.pipeline.topology), off, count)
+}
+
+func (b *Backend) prepareDraw() {
+ p := b.state.pipeline
+ if p == nil {
+ return
+ }
+ b.setupVertexArrays()
+}
+
+func toGLDrawMode(mode driver.Topology) gl.Enum {
+ switch mode {
+ case driver.TopologyTriangleStrip:
+ return gl.TRIANGLE_STRIP
+ case driver.TopologyTriangles:
+ return gl.TRIANGLES
+ default:
+ panic("unsupported draw mode")
+ }
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ b.glstate.setViewport(b.funcs, x, y, width, height)
+}
+
+func (b *Backend) clearOutput(colR, colG, colB, colA float32) {
+ b.glstate.setClearColor(b.funcs, colR, colG, colB, colA)
+ b.funcs.Clear(gl.COLOR_BUFFER_BIT)
+}
+
+func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
+ // We don't support ES 3.1 compute, see brokenGLES31 above.
+ const GLES31Source = ""
+ p, err := gl.CreateComputeProgram(b.funcs, GLES31Source)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %v", src.Name, err)
+ }
+ return &program{
+ backend: b,
+ obj: p,
+ }, nil
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ glslSrc := b.glslFor(src)
+ sh, err := gl.CreateShader(b.funcs, gl.VERTEX_SHADER, glslSrc)
+ return &glshader{backend: b, obj: sh, src: src}, err
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ glslSrc := b.glslFor(src)
+ sh, err := gl.CreateShader(b.funcs, gl.FRAGMENT_SHADER, glslSrc)
+ return &glshader{backend: b, obj: sh, src: src}, err
+}
+
+func (b *Backend) glslFor(src shader.Sources) string {
+ if b.gles {
+ return src.GLSL100ES
+ } else {
+ return src.GLSL150
+ }
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ p, err := b.newProgram(desc)
+ if err != nil {
+ return nil, err
+ }
+ layout := desc.VertexLayout
+ vsrc := desc.VertexShader.(*glshader).src
+ if len(vsrc.Inputs) != len(layout.Inputs) {
+ return nil, fmt.Errorf("opengl: got %d inputs, expected %d", len(layout.Inputs), len(vsrc.Inputs))
+ }
+ for i, inp := range vsrc.Inputs {
+ if exp, got := inp.Size, layout.Inputs[i].Size; exp != got {
+ return nil, fmt.Errorf("opengl: data size mismatch for %q: got %d expected %d", inp.Name, got, exp)
+ }
+ }
+ return &pipeline{
+ prog: p,
+ inputs: vsrc.Inputs,
+ layout: layout,
+ blend: desc.BlendDesc,
+ topology: desc.Topology,
+ }, nil
+}
+
+func (b *Backend) newProgram(desc driver.PipelineDesc) (*program, error) {
+ p := b.funcs.CreateProgram()
+ if !p.Valid() {
+ return nil, errors.New("opengl: glCreateProgram failed")
+ }
+ vsh, fsh := desc.VertexShader.(*glshader), desc.FragmentShader.(*glshader)
+ b.funcs.AttachShader(p, vsh.obj)
+ b.funcs.AttachShader(p, fsh.obj)
+ for _, inp := range vsh.src.Inputs {
+ b.funcs.BindAttribLocation(p, gl.Attrib(inp.Location), inp.Name)
+ }
+ b.funcs.LinkProgram(p)
+ if b.funcs.GetProgrami(p, gl.LINK_STATUS) == 0 {
+ log := b.funcs.GetProgramInfoLog(p)
+ b.funcs.DeleteProgram(p)
+ return nil, fmt.Errorf("opengl: program link failed: %s", strings.TrimSpace(log))
+ }
+ prog := &program{
+ backend: b,
+ obj: p,
+ }
+ b.glstate.useProgram(b.funcs, p)
+ // Bind texture uniforms.
+ for _, tex := range vsh.src.Textures {
+ u := b.funcs.GetUniformLocation(p, tex.Name)
+ if u.Valid() {
+ b.funcs.Uniform1i(u, tex.Binding)
+ }
+ }
+ for _, tex := range fsh.src.Textures {
+ u := b.funcs.GetUniformLocation(p, tex.Name)
+ if u.Valid() {
+ b.funcs.Uniform1i(u, tex.Binding)
+ }
+ }
+ prog.vertUniforms.setup(b.funcs, p, vsh.src.Uniforms.Size, vsh.src.Uniforms.Locations)
+ prog.fragUniforms.setup(b.funcs, p, fsh.src.Uniforms.Size, fsh.src.Uniforms.Locations)
+ return prog, nil
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buf driver.Buffer) {
+ bf := buf.(*buffer)
+ if bf.typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) == 0 {
+ panic("not a shader storage buffer")
+ }
+ b.storage[binding] = bf
+}
+
+func (b *Backend) BindUniforms(buf driver.Buffer) {
+ bf := buf.(*buffer)
+ if bf.typ&driver.BufferBindingUniforms == 0 {
+ panic("not a uniform buffer")
+ }
+ b.state.pipeline.prog.vertUniforms.update(b.funcs, bf)
+ b.state.pipeline.prog.fragUniforms.update(b.funcs, bf)
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ p := prog.(*program)
+ b.glstate.useProgram(b.funcs, p.obj)
+}
+
+func (s *glshader) Release() {
+ s.backend.funcs.DeleteShader(s.obj)
+}
+
+func (p *program) Release() {
+ p.backend.glstate.deleteProgram(p.backend.funcs, p.obj)
+}
+
+func (u *uniforms) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []shader.UniformLocation) {
+ u.locs = make([]uniformLocation, len(uniforms))
+ for i, uniform := range uniforms {
+ loc := funcs.GetUniformLocation(p, uniform.Name)
+ u.locs[i] = uniformLocation{uniform: loc, offset: uniform.Offset, typ: uniform.Type, size: uniform.Size}
+ }
+ u.size = uniformSize
+}
+
+func (p *uniforms) update(funcs *gl.Functions, buf *buffer) {
+ if buf.size < p.size {
+ panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, p.size))
+ }
+ data := buf.data
+ for _, u := range p.locs {
+ if !u.uniform.Valid() {
+ continue
+ }
+ data := data[u.offset:]
+ switch {
+ case u.typ == shader.DataTypeFloat && u.size == 1:
+ data := data[:4]
+ v := *(*[1]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform1f(u.uniform, v[0])
+ case u.typ == shader.DataTypeFloat && u.size == 2:
+ data := data[:8]
+ v := *(*[2]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform2f(u.uniform, v[0], v[1])
+ case u.typ == shader.DataTypeFloat && u.size == 3:
+ data := data[:12]
+ v := *(*[3]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform3f(u.uniform, v[0], v[1], v[2])
+ case u.typ == shader.DataTypeFloat && u.size == 4:
+ data := data[:16]
+ v := *(*[4]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3])
+ default:
+ panic("unsupported uniform data type or size")
+ }
+ }
+}
+
+func (b *buffer) Upload(data []byte) {
+ if b.immutable {
+ panic("immutable buffer")
+ }
+ if len(data) > b.size {
+ panic("buffer size overflow")
+ }
+ copy(b.data, data)
+ if b.hasBuffer {
+ firstBinding := firstBufferType(b.typ)
+ b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj)
+ if len(data) == b.size {
+ // the iOS GL implementation doesn't recognize when BufferSubData
+ // clears the entire buffer. Tell it and avoid GPU stalls.
+ // See also https://github.com/godotengine/godot/issues/23956.
+ b.backend.funcs.BufferData(firstBinding, b.size, gl.DYNAMIC_DRAW, data)
+ } else {
+ b.backend.funcs.BufferSubData(firstBinding, 0, data)
+ }
+ }
+}
+
+func (b *buffer) Download(data []byte) error {
+ if len(data) > b.size {
+ panic("buffer size overflow")
+ }
+ if !b.hasBuffer {
+ copy(data, b.data)
+ return nil
+ }
+ firstBinding := firstBufferType(b.typ)
+ b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj)
+ bufferMap := b.backend.funcs.MapBufferRange(firstBinding, 0, len(data), gl.MAP_READ_BIT)
+ if bufferMap == nil {
+ return fmt.Errorf("MapBufferRange: error %#x", b.backend.funcs.GetError())
+ }
+ copy(data, bufferMap)
+ if !b.backend.funcs.UnmapBuffer(firstBinding) {
+ return driver.ErrContentLost
+ }
+ return nil
+}
+
+func (b *buffer) Release() {
+ if b.hasBuffer {
+ b.backend.glstate.deleteBuffer(b.backend.funcs, b.obj)
+ b.hasBuffer = false
+ }
+}
+
+func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
+ gbuf := buf.(*buffer)
+ if gbuf.typ&driver.BufferBindingVertices == 0 {
+ panic("not a vertex buffer")
+ }
+ b.state.buffer = bufferBinding{obj: gbuf.obj, offset: offset}
+}
+
+func (b *Backend) setupVertexArrays() {
+ p := b.state.pipeline
+ inputs := p.inputs
+ if len(inputs) == 0 {
+ return
+ }
+ layout := p.layout
+ const max = len(b.glstate.vertAttribs)
+ var enabled [max]bool
+ buf := b.state.buffer
+ for i, inp := range inputs {
+ l := layout.Inputs[i]
+ var gltyp gl.Enum
+ switch l.Type {
+ case shader.DataTypeFloat:
+ gltyp = gl.FLOAT
+ case shader.DataTypeShort:
+ gltyp = gl.SHORT
+ default:
+ panic("unsupported data type")
+ }
+ enabled[inp.Location] = true
+ b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset)
+ }
+ for i := 0; i < max; i++ {
+ b.glstate.setVertexAttribArray(b.funcs, i, enabled[i])
+ }
+}
+
+func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
+ gbuf := buf.(*buffer)
+ if gbuf.typ&driver.BufferBindingIndices == 0 {
+ panic("not an index buffer")
+ }
+ b.glstate.bindBuffer(b.funcs, gl.ELEMENT_ARRAY_BUFFER, gbuf.obj)
+}
+
+func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src driver.Texture, srcRect image.Rectangle) {
+ const unit = 0
+ oldTex := b.glstate.texUnits.binds[unit]
+ defer func() {
+ b.glstate.bindTexture(b.funcs, unit, oldTex)
+ }()
+ b.glstate.bindTexture(b.funcs, unit, dst.(*texture).obj)
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, src.(*texture).ensureFBO())
+ sz := srcRect.Size()
+ b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y)
+}
+
+func (t *texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ glErr(t.backend.funcs)
+ t.backend.glstate.bindFramebuffer(t.backend.funcs, gl.FRAMEBUFFER, t.ensureFBO())
+ if len(pixels) < src.Dx()*src.Dy()*4 {
+ return errors.New("unexpected RGBA size")
+ }
+ w, h := src.Dx(), src.Dy()
+ // WebGL 1 doesn't support PACK_ROW_LENGTH != 0. Avoid it if possible.
+ rowLen := 0
+ if n := stride / 4; n != w {
+ rowLen = n
+ }
+ t.backend.glstate.pixelStorei(t.backend.funcs, gl.PACK_ROW_LENGTH, rowLen)
+ t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
+ return glErr(t.backend.funcs)
+}
+
+func (b *Backend) BindPipeline(pl driver.Pipeline) {
+ p := pl.(*pipeline)
+ b.state.pipeline = p
+ b.glstate.useProgram(b.funcs, p.prog.obj)
+ b.SetBlend(p.blend.Enable)
+ b.BlendFunc(p.blend.SrcFactor, p.blend.DstFactor)
+}
+
+func (b *Backend) BeginCompute() {
+ b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS)
+}
+
+func (b *Backend) EndCompute() {
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, desc driver.LoadDesc) {
+ fbo := tex.(*texture).ensureFBO()
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo)
+ switch desc.Action {
+ case driver.LoadActionClear:
+ c := desc.ClearColor
+ b.clearOutput(c.R, c.G, c.B, c.A)
+ case driver.LoadActionInvalidate:
+ b.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
+ }
+}
+
+func (b *Backend) EndRenderPass() {
+}
+
+func (f *texture) ImplementsRenderTarget() {}
+
+func (p *pipeline) Release() {
+ p.prog.Release()
+ *p = pipeline{}
+}
+
+func toTexFilter(f driver.TextureFilter) int {
+ switch f {
+ case driver.FilterNearest:
+ return gl.NEAREST
+ case driver.FilterLinear:
+ return gl.LINEAR
+ default:
+ panic("unsupported texture filter")
+ }
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {}
+
+func (b *Backend) BindTexture(unit int, t driver.Texture) {
+ b.glstate.bindTexture(b.funcs, unit, t.(*texture).obj)
+}
+
+func (t *texture) Release() {
+ if t.foreign {
+ panic("texture not created by NewTexture")
+ }
+ if t.hasFBO {
+ t.backend.glstate.deleteFramebuffer(t.backend.funcs, t.fbo)
+ }
+ t.backend.glstate.deleteTexture(t.backend.funcs, t.obj)
+}
+
+func (t *texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if min := size.X * size.Y * 4; min > len(pixels) {
+ panic(fmt.Errorf("size %d larger than data %d", min, len(pixels)))
+ }
+ t.backend.BindTexture(0, t)
+ // WebGL 1 doesn't support UNPACK_ROW_LENGTH != 0. Avoid it if possible.
+ rowLen := 0
+ if n := stride / 4; n != size.X {
+ rowLen = n
+ }
+ t.backend.glstate.pixelStorei(t.backend.funcs, gl.UNPACK_ROW_LENGTH, rowLen)
+ t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels)
+}
+
+func (t *timer) Begin() {
+ t.funcs.BeginQuery(gl.TIME_ELAPSED_EXT, t.obj)
+}
+
+func (t *timer) End() {
+ t.funcs.EndQuery(gl.TIME_ELAPSED_EXT)
+}
+
+func (t *timer) ready() bool {
+ return t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT_AVAILABLE) == gl.TRUE
+}
+
+func (t *timer) Release() {
+ t.funcs.DeleteQuery(t.obj)
+}
+
+func (t *timer) Duration() (time.Duration, bool) {
+ if !t.ready() {
+ return 0, false
+ }
+ nanos := t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT)
+ return time.Duration(nanos), true
+}
+
+// floatTripleFor determines the best texture triple for floating point FBOs.
+func floatTripleFor(f *gl.Functions, ver [2]int, exts []string) (textureTriple, error) {
+ var triples []textureTriple
+ if ver[0] >= 3 {
+ triples = append(triples, textureTriple{gl.R16F, gl.Enum(gl.RED), gl.Enum(gl.HALF_FLOAT)})
+ }
+ // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to
+ // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not
+ // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're
+ // wrong.
+ if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") {
+ // Try single channel.
+ triples = append(triples, textureTriple{gl.LUMINANCE, gl.Enum(gl.LUMINANCE), gl.Enum(gl.HALF_FLOAT_OES)})
+ // Fallback to 4 channels.
+ triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)})
+ }
+ if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") {
+ triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)})
+ }
+ tex := f.CreateTexture()
+ defer f.DeleteTexture(tex)
+ defTex := gl.Texture(f.GetBinding(gl.TEXTURE_BINDING_2D))
+ defer f.BindTexture(gl.TEXTURE_2D, defTex)
+ f.BindTexture(gl.TEXTURE_2D, tex)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
+ fbo := f.CreateFramebuffer()
+ defer f.DeleteFramebuffer(fbo)
+ defFBO := gl.Framebuffer(f.GetBinding(gl.FRAMEBUFFER_BINDING))
+ f.BindFramebuffer(gl.FRAMEBUFFER, fbo)
+ defer f.BindFramebuffer(gl.FRAMEBUFFER, defFBO)
+ var attempts []string
+ for _, tt := range triples {
+ const size = 256
+ f.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ)
+ f.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)
+ st := f.CheckFramebufferStatus(gl.FRAMEBUFFER)
+ if st == gl.FRAMEBUFFER_COMPLETE {
+ return tt, nil
+ }
+ attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st))
+ }
+ return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts)
+}
+
+func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
+ switch {
+ case ver[0] >= 3:
+ return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil
+ case hasExtension(exts, "GL_EXT_sRGB"):
+ return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil
+ default:
+ return textureTriple{}, errors.New("no sRGB texture formats found")
+ }
+}
+
+func alphaTripleFor(ver [2]int) textureTriple {
+ intf, f := gl.Enum(gl.R8), gl.Enum(gl.RED)
+ if ver[0] < 3 {
+ // R8, RED not supported on OpenGL ES 2.0.
+ intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE)
+ }
+ return textureTriple{intf, f, gl.UNSIGNED_BYTE}
+}
+
+func hasExtension(exts []string, ext string) bool {
+ for _, e := range exts {
+ if ext == e {
+ return true
+ }
+ }
+ return false
+}
+
+func firstBufferType(typ driver.BufferBinding) gl.Enum {
+ switch {
+ case typ&driver.BufferBindingIndices != 0:
+ return gl.ELEMENT_ARRAY_BUFFER
+ case typ&driver.BufferBindingVertices != 0:
+ return gl.ARRAY_BUFFER
+ case typ&driver.BufferBindingUniforms != 0:
+ return gl.UNIFORM_BUFFER
+ case typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0:
+ return gl.SHADER_STORAGE_BUFFER
+ default:
+ panic("unsupported buffer type")
+ }
+}
diff --git a/vendor/gioui.org/gpu/internal/opengl/srgb.go b/vendor/gioui.org/gpu/internal/opengl/srgb.go
new file mode 100644
index 0000000..4871d94
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/opengl/srgb.go
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package opengl
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "runtime"
+ "strings"
+
+ "gioui.org/internal/byteslice"
+ "gioui.org/internal/gl"
+)
+
+// SRGBFBO implements an intermediate sRGB FBO
+// for gamma-correct rendering on platforms without
+// sRGB enabled native framebuffers.
+type SRGBFBO struct {
+ c *gl.Functions
+ state *glState
+ viewport image.Point
+ fbo gl.Framebuffer
+ tex gl.Texture
+ blitted bool
+ quad gl.Buffer
+ prog gl.Program
+ format textureTriple
+}
+
+func NewSRGBFBO(f *gl.Functions, state *glState) (*SRGBFBO, error) {
+ glVer := f.GetString(gl.VERSION)
+ ver, _, err := gl.ParseGLVersion(glVer)
+ if err != nil {
+ return nil, err
+ }
+ exts := strings.Split(f.GetString(gl.EXTENSIONS), " ")
+ srgbTriple, err := srgbaTripleFor(ver, exts)
+ if err != nil {
+ // Fall back to the linear RGB colorspace, at the cost of color precision loss.
+ srgbTriple = textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}
+ }
+ s := &SRGBFBO{
+ c: f,
+ state: state,
+ format: srgbTriple,
+ fbo: f.CreateFramebuffer(),
+ tex: f.CreateTexture(),
+ }
+ state.bindTexture(f, 0, s.tex)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
+ return s, nil
+}
+
+func (s *SRGBFBO) Blit() {
+ if !s.blitted {
+ prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"})
+ if err != nil {
+ panic(err)
+ }
+ s.prog = prog
+ s.state.useProgram(s.c, prog)
+ s.c.Uniform1i(s.c.GetUniformLocation(prog, "tex"), 0)
+ s.quad = s.c.CreateBuffer()
+ s.state.bindBuffer(s.c, gl.ARRAY_BUFFER, s.quad)
+ coords := byteslice.Slice([]float32{
+ -1, +1, 0, 1,
+ +1, +1, 1, 1,
+ -1, -1, 0, 0,
+ +1, -1, 1, 0,
+ })
+ s.c.BufferData(gl.ARRAY_BUFFER, len(coords), gl.STATIC_DRAW, coords)
+ s.blitted = true
+ }
+ s.state.useProgram(s.c, s.prog)
+ s.state.bindTexture(s.c, 0, s.tex)
+ s.state.vertexAttribPointer(s.c, s.quad, 0 /* pos */, 2, gl.FLOAT, false, 4*4, 0)
+ s.state.vertexAttribPointer(s.c, s.quad, 1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2)
+ s.state.setVertexAttribArray(s.c, 0, true)
+ s.state.setVertexAttribArray(s.c, 1, true)
+ s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
+ s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.fbo)
+ s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
+}
+
+func (s *SRGBFBO) Framebuffer() gl.Framebuffer {
+ return s.fbo
+}
+
+func (s *SRGBFBO) Refresh(viewport image.Point) error {
+ if viewport.X == 0 || viewport.Y == 0 {
+ return errors.New("srgb: zero-sized framebuffer")
+ }
+ if s.viewport == viewport {
+ return nil
+ }
+ s.viewport = viewport
+ s.state.bindTexture(s.c, 0, s.tex)
+ s.c.TexImage2D(gl.TEXTURE_2D, 0, s.format.internalFormat, viewport.X, viewport.Y, s.format.format, s.format.typ)
+ s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.fbo)
+ s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.tex, 0)
+ if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
+ return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError())
+ }
+
+ if runtime.GOOS == "js" {
+ // With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8
+ // texture result in twice gamma corrected colors. Using a plain RGBA
+ // texture seems to work.
+ s.state.setClearColor(s.c, .5, .5, .5, 1.0)
+ s.c.Clear(gl.COLOR_BUFFER_BIT)
+ var pixel [4]byte
+ s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:])
+ if pixel[0] == 128 { // Correct sRGB color value is ~188
+ s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewport.X, viewport.Y, gl.RGBA, gl.UNSIGNED_BYTE)
+ if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
+ return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError())
+ }
+ }
+ }
+
+ return nil
+}
+
+func (s *SRGBFBO) Release() {
+ s.state.deleteFramebuffer(s.c, s.fbo)
+ s.state.deleteTexture(s.c, s.tex)
+ if s.blitted {
+ s.state.deleteBuffer(s.c, s.quad)
+ s.state.deleteProgram(s.c, s.prog)
+ }
+ s.c = nil
+}
+
+const (
+ blitVSrc = `
+#version 100
+
+precision highp float;
+
+attribute vec2 pos;
+attribute vec2 uv;
+
+varying vec2 vUV;
+
+void main() {
+ gl_Position = vec4(pos, 0, 1);
+ vUV = uv;
+}
+`
+ blitFSrc = `
+#version 100
+
+precision mediump float;
+
+uniform sampler2D tex;
+varying vec2 vUV;
+
+vec3 gamma(vec3 rgb) {
+ vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055);
+ vec3 lin = rgb * vec3(12.92);
+ bvec3 cut = lessThan(rgb, vec3(0.0031308));
+ return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b);
+}
+
+void main() {
+ vec4 col = texture2D(tex, vUV);
+ vec3 rgb = col.rgb;
+ rgb = gamma(rgb);
+ gl_FragColor = vec4(rgb, col.a);
+}
+`
+)
diff --git a/vendor/gioui.org/gpu/internal/vulkan/vulkan.go b/vendor/gioui.org/gpu/internal/vulkan/vulkan.go
new file mode 100644
index 0000000..7d3791e
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/vulkan/vulkan.go
@@ -0,0 +1,1121 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build (linux || freebsd) && !novulkan
+// +build linux freebsd
+// +build !novulkan
+
+package vulkan
+
+import (
+ "errors"
+ "fmt"
+ "image"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/vk"
+ "gioui.org/shader"
+)
+
+type Backend struct {
+ physDev vk.PhysicalDevice
+ dev vk.Device
+ queue vk.Queue
+ cmdPool struct {
+ current vk.CommandBuffer
+ pool vk.CommandPool
+ used int
+ buffers []vk.CommandBuffer
+ }
+ outFormat vk.Format
+ staging struct {
+ buf *Buffer
+ mem []byte
+ size int
+ cap int
+ }
+ defers []func(d vk.Device)
+ frameSig vk.Semaphore
+ waitSems []vk.Semaphore
+ waitStages []vk.PipelineStageFlags
+ sigSems []vk.Semaphore
+ fence vk.Fence
+
+ allPipes []*Pipeline
+
+ pipe *Pipeline
+
+ passes map[passKey]vk.RenderPass
+
+ // bindings and offset are temporary storage for BindVertexBuffer.
+ bindings []vk.Buffer
+ offsets []vk.DeviceSize
+
+ desc struct {
+ dirty bool
+ texBinds [texUnits]*Texture
+ bufBinds [storageUnits]*Buffer
+ }
+
+ caps driver.Features
+}
+
+type passKey struct {
+ fmt vk.Format
+ loadAct vk.AttachmentLoadOp
+ initLayout vk.ImageLayout
+ finalLayout vk.ImageLayout
+}
+
+type Texture struct {
+ backend *Backend
+ img vk.Image
+ mem vk.DeviceMemory
+ view vk.ImageView
+ sampler vk.Sampler
+ fbo vk.Framebuffer
+ format vk.Format
+ layout vk.ImageLayout
+ passLayout vk.ImageLayout
+ width int
+ height int
+ acquire vk.Semaphore
+ foreign bool
+
+ scope struct {
+ stage vk.PipelineStageFlags
+ access vk.AccessFlags
+ }
+}
+
+type Shader struct {
+ dev vk.Device
+ module vk.ShaderModule
+ pushRange vk.PushConstantRange
+ src shader.Sources
+}
+
+type Pipeline struct {
+ backend *Backend
+ pipe vk.Pipeline
+ pushRanges []vk.PushConstantRange
+ ninputs int
+ desc *descPool
+}
+
+type descPool struct {
+ layout vk.PipelineLayout
+ descLayout vk.DescriptorSetLayout
+ pool vk.DescriptorPool
+ size int
+ cap int
+ texBinds []int
+ imgBinds []int
+ bufBinds []int
+}
+
+type Buffer struct {
+ backend *Backend
+ buf vk.Buffer
+ store []byte
+ mem vk.DeviceMemory
+ usage vk.BufferUsageFlags
+
+ scope struct {
+ stage vk.PipelineStageFlags
+ access vk.AccessFlags
+ }
+}
+
+const (
+ texUnits = 4
+ storageUnits = 4
+)
+
+func init() {
+ driver.NewVulkanDevice = newVulkanDevice
+}
+
+func newVulkanDevice(api driver.Vulkan) (driver.Device, error) {
+ b := &Backend{
+ physDev: vk.PhysicalDevice(api.PhysDevice),
+ dev: vk.Device(api.Device),
+ outFormat: vk.Format(api.Format),
+ caps: driver.FeatureCompute,
+ passes: make(map[passKey]vk.RenderPass),
+ }
+ b.queue = vk.GetDeviceQueue(b.dev, api.QueueFamily, api.QueueIndex)
+ cmdPool, err := vk.CreateCommandPool(b.dev, api.QueueFamily)
+ if err != nil {
+ return nil, err
+ }
+ b.cmdPool.pool = cmdPool
+ props := vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R16_SFLOAT)
+ reqs := vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT
+ if props&reqs == reqs {
+ b.caps |= driver.FeatureFloatRenderTargets
+ }
+ reqs = vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT
+ props = vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R8G8B8A8_SRGB)
+ if props&reqs == reqs {
+ b.caps |= driver.FeatureSRGB
+ }
+ fence, err := vk.CreateFence(b.dev)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ b.fence = fence
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ vk.QueueWaitIdle(b.queue)
+ b.staging.size = 0
+ b.cmdPool.used = 0
+ b.runDefers()
+ b.resetPipes()
+
+ if target == nil {
+ return nil
+ }
+ switch t := target.(type) {
+ case driver.VulkanRenderTarget:
+ layout := vk.IMAGE_LAYOUT_UNDEFINED
+ if !clear {
+ layout = vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ }
+ b.frameSig = vk.Semaphore(t.SignalSem)
+ tex := &Texture{
+ img: vk.Image(t.Image),
+ fbo: vk.Framebuffer(t.Framebuffer),
+ width: viewport.X,
+ height: viewport.Y,
+ layout: layout,
+ passLayout: vk.IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ format: b.outFormat,
+ acquire: vk.Semaphore(t.WaitSem),
+ foreign: true,
+ }
+ return tex
+ case *Texture:
+ return t
+ default:
+ panic(fmt.Sprintf("vulkan: unsupported render target type: %T", t))
+ }
+}
+
+func (b *Backend) deferFunc(f func(d vk.Device)) {
+ b.defers = append(b.defers, f)
+}
+
+func (b *Backend) runDefers() {
+ for _, f := range b.defers {
+ f(b.dev)
+ }
+ b.defers = b.defers[:0]
+}
+
+func (b *Backend) resetPipes() {
+ for i := len(b.allPipes) - 1; i >= 0; i-- {
+ p := b.allPipes[i]
+ if p.pipe == 0 {
+ // Released pipeline.
+ b.allPipes = append(b.allPipes[:i], b.allPipes[:i+1]...)
+ continue
+ }
+ if p.desc.size > 0 {
+ vk.ResetDescriptorPool(b.dev, p.desc.pool)
+ p.desc.size = 0
+ }
+ }
+}
+
+func (b *Backend) EndFrame() {
+ if b.frameSig != 0 {
+ b.sigSems = append(b.sigSems, b.frameSig)
+ b.frameSig = 0
+ }
+ b.submitCmdBuf(false)
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return driver.Caps{
+ MaxTextureSize: 4096,
+ Features: b.caps,
+ }
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ panic("timers not supported")
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ panic("timers not supported")
+}
+
+func (b *Backend) Release() {
+ vk.DeviceWaitIdle(b.dev)
+ if buf := b.staging.buf; buf != nil {
+ vk.UnmapMemory(b.dev, b.staging.buf.mem)
+ buf.Release()
+ }
+ b.runDefers()
+ for _, rp := range b.passes {
+ vk.DestroyRenderPass(b.dev, rp)
+ }
+ vk.DestroyFence(b.dev, b.fence)
+ vk.FreeCommandBuffers(b.dev, b.cmdPool.pool, b.cmdPool.buffers...)
+ vk.DestroyCommandPool(b.dev, b.cmdPool.pool)
+ *b = Backend{}
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
+ vkfmt := formatFor(format)
+ usage := vk.IMAGE_USAGE_TRANSFER_DST_BIT | vk.IMAGE_USAGE_TRANSFER_SRC_BIT
+ passLayout := vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ if bindings&driver.BufferBindingTexture != 0 {
+ usage |= vk.IMAGE_USAGE_SAMPLED_BIT
+ passLayout = vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ }
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ usage |= vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT
+ }
+ if bindings&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
+ usage |= vk.IMAGE_USAGE_STORAGE_BIT
+ }
+ filterFor := func(f driver.TextureFilter) vk.Filter {
+ switch minFilter {
+ case driver.FilterLinear:
+ return vk.FILTER_LINEAR
+ case driver.FilterNearest:
+ return vk.FILTER_NEAREST
+ }
+ panic("unknown filter")
+ }
+ sampler, err := vk.CreateSampler(b.dev, filterFor(minFilter), filterFor(magFilter))
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ img, mem, err := vk.CreateImage(b.physDev, b.dev, vkfmt, width, height, usage)
+ if err != nil {
+ vk.DestroySampler(b.dev, sampler)
+ return nil, mapErr(err)
+ }
+ view, err := vk.CreateImageView(b.dev, img, vkfmt)
+ if err != nil {
+ vk.DestroySampler(b.dev, sampler)
+ vk.DestroyImage(b.dev, img)
+ vk.FreeMemory(b.dev, mem)
+ return nil, mapErr(err)
+ }
+ t := &Texture{backend: b, img: img, mem: mem, view: view, sampler: sampler, layout: vk.IMAGE_LAYOUT_UNDEFINED, passLayout: passLayout, width: width, height: height, format: vkfmt}
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ pass, err := vk.CreateRenderPass(b.dev, vkfmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE,
+ vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ defer vk.DestroyRenderPass(b.dev, pass)
+ fbo, err := vk.CreateFramebuffer(b.dev, pass, view, width, height)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ t.fbo = fbo
+ }
+ return t, nil
+}
+
+func (b *Backend) NewBuffer(bindings driver.BufferBinding, size int) (driver.Buffer, error) {
+ if bindings&driver.BufferBindingUniforms != 0 {
+ // Implement uniform buffers as inline push constants.
+ return &Buffer{store: make([]byte, size)}, nil
+ }
+ usage := vk.BUFFER_USAGE_TRANSFER_DST_BIT | vk.BUFFER_USAGE_TRANSFER_SRC_BIT
+ if bindings&driver.BufferBindingIndices != 0 {
+ usage |= vk.BUFFER_USAGE_INDEX_BUFFER_BIT
+ }
+ if bindings&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
+ usage |= vk.BUFFER_USAGE_STORAGE_BUFFER_BIT
+ }
+ if bindings&driver.BufferBindingVertices != 0 {
+ usage |= vk.BUFFER_USAGE_VERTEX_BUFFER_BIT
+ }
+ buf, err := b.newBuffer(size, usage, vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ return buf, mapErr(err)
+}
+
+func (b *Backend) newBuffer(size int, usage vk.BufferUsageFlags, props vk.MemoryPropertyFlags) (*Buffer, error) {
+ buf, mem, err := vk.CreateBuffer(b.physDev, b.dev, size, usage, props)
+ return &Buffer{backend: b, buf: buf, mem: mem, usage: usage}, err
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ buf, err := b.NewBuffer(typ, len(data))
+ if err != nil {
+ return nil, err
+ }
+ buf.Upload(data)
+ return buf, nil
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ sh, err := b.newShader(src, vk.SHADER_STAGE_VERTEX_BIT)
+ return sh, mapErr(err)
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ sh, err := b.newShader(src, vk.SHADER_STAGE_FRAGMENT_BIT)
+ return sh, mapErr(err)
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ vs := desc.VertexShader.(*Shader)
+ fs := desc.FragmentShader.(*Shader)
+ var ranges []vk.PushConstantRange
+ if r := vs.pushRange; r != (vk.PushConstantRange{}) {
+ ranges = append(ranges, r)
+ }
+ if r := fs.pushRange; r != (vk.PushConstantRange{}) {
+ ranges = append(ranges, r)
+ }
+ descPool, err := createPipelineLayout(b.dev, fs.src, ranges)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ blend := desc.BlendDesc
+ factorFor := func(f driver.BlendFactor) vk.BlendFactor {
+ switch f {
+ case driver.BlendFactorZero:
+ return vk.BLEND_FACTOR_ZERO
+ case driver.BlendFactorOne:
+ return vk.BLEND_FACTOR_ONE
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
+ case driver.BlendFactorDstColor:
+ return vk.BLEND_FACTOR_DST_COLOR
+ default:
+ panic("unknown blend factor")
+ }
+ }
+ var top vk.PrimitiveTopology
+ switch desc.Topology {
+ case driver.TopologyTriangles:
+ top = vk.PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
+ case driver.TopologyTriangleStrip:
+ top = vk.PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
+ default:
+ panic("unknown topology")
+ }
+ var binds []vk.VertexInputBindingDescription
+ var attrs []vk.VertexInputAttributeDescription
+ inputs := desc.VertexLayout.Inputs
+ for i, inp := range inputs {
+ binds = append(binds, vk.VertexInputBindingDescription{
+ Binding: i,
+ Stride: desc.VertexLayout.Stride,
+ })
+ attrs = append(attrs, vk.VertexInputAttributeDescription{
+ Binding: i,
+ Location: vs.src.Inputs[i].Location,
+ Format: vertFormatFor(vs.src.Inputs[i]),
+ Offset: inp.Offset,
+ })
+ }
+ fmt := b.outFormat
+ if f := desc.PixelFormat; f != driver.TextureFormatOutput {
+ fmt = formatFor(f)
+ }
+ pass, err := vk.CreateRenderPass(b.dev, fmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE,
+ vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ defer vk.DestroyRenderPass(b.dev, pass)
+ pipe, err := vk.CreateGraphicsPipeline(b.dev, pass, vs.module, fs.module, blend.Enable, factorFor(blend.SrcFactor), factorFor(blend.DstFactor), top, binds, attrs, descPool.layout)
+ if err != nil {
+ descPool.release(b.dev)
+ return nil, mapErr(err)
+ }
+ p := &Pipeline{backend: b, pipe: pipe, desc: descPool, pushRanges: ranges, ninputs: len(inputs)}
+ b.allPipes = append(b.allPipes, p)
+ return p, nil
+}
+
+func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
+ sh, err := b.newShader(src, vk.SHADER_STAGE_COMPUTE_BIT)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ defer sh.Release()
+ descPool, err := createPipelineLayout(b.dev, src, nil)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ pipe, err := vk.CreateComputePipeline(b.dev, sh.module, descPool.layout)
+ if err != nil {
+ descPool.release(b.dev)
+ return nil, mapErr(err)
+ }
+ return &Pipeline{backend: b, pipe: pipe, desc: descPool}, nil
+}
+
+func vertFormatFor(f shader.InputLocation) vk.Format {
+ t := f.Type
+ s := f.Size
+ switch {
+ case t == shader.DataTypeFloat && s == 1:
+ return vk.FORMAT_R32_SFLOAT
+ case t == shader.DataTypeFloat && s == 2:
+ return vk.FORMAT_R32G32_SFLOAT
+ case t == shader.DataTypeFloat && s == 3:
+ return vk.FORMAT_R32G32B32_SFLOAT
+ case t == shader.DataTypeFloat && s == 4:
+ return vk.FORMAT_R32G32B32A32_SFLOAT
+ default:
+ panic("unsupported data type")
+ }
+}
+
+func createPipelineLayout(d vk.Device, src shader.Sources, ranges []vk.PushConstantRange) (*descPool, error) {
+ var (
+ descLayouts []vk.DescriptorSetLayout
+ descLayout vk.DescriptorSetLayout
+ )
+ texBinds := make([]int, len(src.Textures))
+ imgBinds := make([]int, len(src.Images))
+ bufBinds := make([]int, len(src.StorageBuffers))
+ var descBinds []vk.DescriptorSetLayoutBinding
+ for i, t := range src.Textures {
+ descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{
+ Binding: t.Binding,
+ StageFlags: vk.SHADER_STAGE_FRAGMENT_BIT,
+ DescriptorType: vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ })
+ texBinds[i] = t.Binding
+ }
+ for i, img := range src.Images {
+ descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{
+ Binding: img.Binding,
+ StageFlags: vk.SHADER_STAGE_COMPUTE_BIT,
+ DescriptorType: vk.DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ })
+ imgBinds[i] = img.Binding
+ }
+ for i, buf := range src.StorageBuffers {
+ descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{
+ Binding: buf.Binding,
+ StageFlags: vk.SHADER_STAGE_COMPUTE_BIT,
+ DescriptorType: vk.DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ })
+ bufBinds[i] = buf.Binding
+ }
+ if len(descBinds) > 0 {
+ var err error
+ descLayout, err = vk.CreateDescriptorSetLayout(d, descBinds)
+ if err != nil {
+ return nil, err
+ }
+ descLayouts = append(descLayouts, descLayout)
+ }
+ layout, err := vk.CreatePipelineLayout(d, ranges, descLayouts)
+ if err != nil {
+ if descLayout != 0 {
+ vk.DestroyDescriptorSetLayout(d, descLayout)
+ }
+ return nil, err
+ }
+ descPool := &descPool{
+ texBinds: texBinds,
+ bufBinds: bufBinds,
+ imgBinds: imgBinds,
+ layout: layout,
+ descLayout: descLayout,
+ }
+ return descPool, nil
+}
+
+func (b *Backend) newShader(src shader.Sources, stage vk.ShaderStageFlags) (*Shader, error) {
+ mod, err := vk.CreateShaderModule(b.dev, src.SPIRV)
+ if err != nil {
+ return nil, err
+ }
+
+ sh := &Shader{dev: b.dev, module: mod, src: src}
+ if locs := src.Uniforms.Locations; len(locs) > 0 {
+ pushOffset := 0x7fffffff
+ for _, l := range locs {
+ if l.Offset < pushOffset {
+ pushOffset = l.Offset
+ }
+ }
+ sh.pushRange = vk.BuildPushConstantRange(stage, pushOffset, src.Uniforms.Size)
+ }
+ return sh, nil
+}
+
+func (b *Backend) CopyTexture(dstTex driver.Texture, dorig image.Point, srcFBO driver.Texture, srect image.Rectangle) {
+ dst := dstTex.(*Texture)
+ src := srcFBO.(*Texture)
+ cmdBuf := b.ensureCmdBuf()
+ op := vk.BuildImageCopy(srect.Min.X, srect.Min.Y, dorig.X, dorig.Y, srect.Dx(), srect.Dy())
+ src.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_READ_BIT,
+ )
+ dst.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_WRITE_BIT,
+ )
+ vk.CmdCopyImage(cmdBuf, src.img, src.layout, dst.img, dst.layout, []vk.ImageCopy{op})
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ cmdBuf := b.currentCmdBuf()
+ vp := vk.BuildViewport(float32(x), float32(y), float32(width), float32(height))
+ vk.CmdSetViewport(cmdBuf, 0, vp)
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ cmdBuf := b.currentCmdBuf()
+ if b.desc.dirty {
+ b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_GRAPHICS, b.desc.texBinds, b.desc.bufBinds)
+ b.desc.dirty = false
+ }
+ vk.CmdDraw(cmdBuf, count, 1, off, 0)
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ cmdBuf := b.currentCmdBuf()
+ if b.desc.dirty {
+ b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_GRAPHICS, b.desc.texBinds, b.desc.bufBinds)
+ b.desc.dirty = false
+ }
+ vk.CmdDrawIndexed(cmdBuf, count, 1, off, 0, 0)
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ b.desc.texBinds[unit] = t
+ b.desc.dirty = true
+ t.imageBarrier(b.currentCmdBuf(),
+ vk.IMAGE_LAYOUT_GENERAL,
+ vk.PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ vk.ACCESS_SHADER_READ_BIT|vk.ACCESS_SHADER_WRITE_BIT,
+ )
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ cmdBuf := b.currentCmdBuf()
+ if b.desc.dirty {
+ b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_COMPUTE, b.desc.texBinds, b.desc.bufBinds)
+ b.desc.dirty = false
+ }
+ vk.CmdDispatch(cmdBuf, x, y, z)
+}
+
+func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if stride == 0 {
+ stride = size.X * 4
+ }
+ cmdBuf := t.backend.ensureCmdBuf()
+ dstStride := size.X * 4
+ n := size.Y * dstStride
+ stage, mem, off := t.backend.stagingBuffer(n)
+ var srcOff, dstOff int
+ for y := 0; y < size.Y; y++ {
+ srcRow := pixels[srcOff : srcOff+dstStride]
+ dstRow := mem[dstOff : dstOff+dstStride]
+ copy(dstRow, srcRow)
+ dstOff += dstStride
+ srcOff += stride
+ }
+ op := vk.BuildBufferImageCopy(off, dstStride/4, offset.X, offset.Y, size.X, size.Y)
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_WRITE_BIT,
+ )
+ vk.CmdCopyBufferToImage(cmdBuf, stage.buf, t.img, t.layout, op)
+}
+
+func (t *Texture) Release() {
+ if t.foreign {
+ panic("external textures cannot be released")
+ }
+ freet := *t
+ t.backend.deferFunc(func(d vk.Device) {
+ if freet.fbo != 0 {
+ vk.DestroyFramebuffer(d, freet.fbo)
+ }
+ vk.DestroySampler(d, freet.sampler)
+ vk.DestroyImageView(d, freet.view)
+ vk.DestroyImage(d, freet.img)
+ vk.FreeMemory(d, freet.mem)
+ })
+ *t = Texture{}
+}
+
+func (p *Pipeline) Release() {
+ freep := *p
+ p.backend.deferFunc(func(d vk.Device) {
+ freep.desc.release(d)
+ vk.DestroyPipeline(d, freep.pipe)
+ })
+ *p = Pipeline{}
+}
+
+func (p *descPool) release(d vk.Device) {
+ if p := p.pool; p != 0 {
+ vk.DestroyDescriptorPool(d, p)
+ }
+ if l := p.descLayout; l != 0 {
+ vk.DestroyDescriptorSetLayout(d, l)
+ }
+ vk.DestroyPipelineLayout(d, p.layout)
+}
+
+func (p *descPool) bindDescriptorSet(b *Backend, cmdBuf vk.CommandBuffer, bindPoint vk.PipelineBindPoint, texBinds [texUnits]*Texture, bufBinds [storageUnits]*Buffer) {
+ realloced := false
+ destroyPool := func() {
+ if pool := p.pool; pool != 0 {
+ b.deferFunc(func(d vk.Device) {
+ vk.DestroyDescriptorPool(d, pool)
+ })
+ }
+ p.pool = 0
+ p.cap = 0
+ }
+ for {
+ if p.size == p.cap {
+ if realloced {
+ panic("vulkan: vkAllocateDescriptorSet failed on a newly allocated descriptor pool")
+ }
+ destroyPool()
+ realloced = true
+ newCap := p.cap * 2
+ const initialPoolSize = 100
+ if newCap < initialPoolSize {
+ newCap = initialPoolSize
+ }
+ var poolSizes []vk.DescriptorPoolSize
+ if n := len(p.texBinds); n > 0 {
+ poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, newCap*n))
+ }
+ if n := len(p.imgBinds); n > 0 {
+ poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_STORAGE_IMAGE, newCap*n))
+ }
+ if n := len(p.bufBinds); n > 0 {
+ poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_STORAGE_BUFFER, newCap*n))
+ }
+ pool, err := vk.CreateDescriptorPool(b.dev, newCap, poolSizes)
+ if err != nil {
+ panic(fmt.Errorf("vulkan: failed to allocate descriptor pool with %d descriptors", newCap))
+ }
+ p.pool = pool
+ p.cap = newCap
+ p.size = 0
+ }
+ l := p.descLayout
+ if l == 0 {
+ panic("vulkan: descriptor set is dirty, but pipeline has empty layout")
+ }
+ descSet, err := vk.AllocateDescriptorSet(b.dev, p.pool, l)
+ if err != nil {
+ destroyPool()
+ continue
+ }
+ p.size++
+ for _, bind := range p.texBinds {
+ tex := texBinds[bind]
+ write := vk.BuildWriteDescriptorSetImage(descSet, bind, vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, tex.sampler, tex.view, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+ vk.UpdateDescriptorSet(b.dev, write)
+ }
+ for _, bind := range p.imgBinds {
+ tex := texBinds[bind]
+ write := vk.BuildWriteDescriptorSetImage(descSet, bind, vk.DESCRIPTOR_TYPE_STORAGE_IMAGE, 0, tex.view, vk.IMAGE_LAYOUT_GENERAL)
+ vk.UpdateDescriptorSet(b.dev, write)
+ }
+ for _, bind := range p.bufBinds {
+ buf := bufBinds[bind]
+ write := vk.BuildWriteDescriptorSetBuffer(descSet, bind, vk.DESCRIPTOR_TYPE_STORAGE_BUFFER, buf.buf)
+ vk.UpdateDescriptorSet(b.dev, write)
+ }
+ vk.CmdBindDescriptorSets(cmdBuf, bindPoint, p.layout, 0, []vk.DescriptorSet{descSet})
+ break
+ }
+}
+
+func (t *Texture) imageBarrier(cmdBuf vk.CommandBuffer, layout vk.ImageLayout, stage vk.PipelineStageFlags, access vk.AccessFlags) {
+ srcStage := t.scope.stage
+ if srcStage == 0 && t.layout == layout {
+ t.scope.stage = stage
+ t.scope.access = access
+ return
+ }
+ if srcStage == 0 {
+ srcStage = vk.PIPELINE_STAGE_TOP_OF_PIPE_BIT
+ }
+ b := vk.BuildImageMemoryBarrier(
+ t.img,
+ t.scope.access, access,
+ t.layout, layout,
+ )
+ vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, nil, []vk.ImageMemoryBarrier{b})
+ t.layout = layout
+ t.scope.stage = stage
+ t.scope.access = access
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {
+ t := tex.(*Texture)
+ cmdBuf := b.ensureCmdBuf()
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+ vk.ACCESS_SHADER_READ_BIT,
+ )
+}
+
+func (b *Backend) BindTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ b.desc.texBinds[unit] = t
+ b.desc.dirty = true
+}
+
+func (b *Backend) BindPipeline(pipe driver.Pipeline) {
+ b.bindPipeline(pipe.(*Pipeline), vk.PIPELINE_BIND_POINT_GRAPHICS)
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ b.bindPipeline(prog.(*Pipeline), vk.PIPELINE_BIND_POINT_COMPUTE)
+}
+
+func (b *Backend) bindPipeline(p *Pipeline, point vk.PipelineBindPoint) {
+ b.pipe = p
+ b.desc.dirty = p.desc.descLayout != 0
+ cmdBuf := b.currentCmdBuf()
+ vk.CmdBindPipeline(cmdBuf, point, p.pipe)
+}
+
+func (s *Shader) Release() {
+ vk.DestroyShaderModule(s.dev, s.module)
+ *s = Shader{}
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ b.desc.bufBinds[binding] = buf
+ b.desc.dirty = true
+ buf.barrier(b.currentCmdBuf(),
+ vk.PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ vk.ACCESS_SHADER_READ_BIT|vk.ACCESS_SHADER_WRITE_BIT,
+ )
+}
+
+func (b *Backend) BindUniforms(buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ cmdBuf := b.currentCmdBuf()
+ for _, s := range b.pipe.pushRanges {
+ off := s.Offset()
+ vk.CmdPushConstants(cmdBuf, b.pipe.desc.layout, s.StageFlags(), off, buf.store[off:off+s.Size()])
+ }
+}
+
+func (b *Backend) BindVertexBuffer(buffer driver.Buffer, offset int) {
+ buf := buffer.(*Buffer)
+ cmdBuf := b.currentCmdBuf()
+ b.bindings = b.bindings[:0]
+ b.offsets = b.offsets[:0]
+ for i := 0; i < b.pipe.ninputs; i++ {
+ b.bindings = append(b.bindings, buf.buf)
+ b.offsets = append(b.offsets, vk.DeviceSize(offset))
+ }
+ vk.CmdBindVertexBuffers(cmdBuf, 0, b.bindings, b.offsets)
+}
+
+func (b *Backend) BindIndexBuffer(buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ cmdBuf := b.currentCmdBuf()
+ vk.CmdBindIndexBuffer(cmdBuf, buf.buf, 0, vk.INDEX_TYPE_UINT16)
+}
+
+func (b *Buffer) Download(data []byte) error {
+ if b.buf == 0 {
+ copy(data, b.store)
+ return nil
+ }
+ stage, mem, off := b.backend.stagingBuffer(len(data))
+ cmdBuf := b.backend.ensureCmdBuf()
+ b.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_READ_BIT,
+ )
+ vk.CmdCopyBuffer(cmdBuf, b.buf, stage.buf, 0, off, len(data))
+ stage.scope.stage = vk.PIPELINE_STAGE_TRANSFER_BIT
+ stage.scope.access = vk.ACCESS_TRANSFER_WRITE_BIT
+ stage.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_HOST_BIT,
+ vk.ACCESS_HOST_READ_BIT,
+ )
+ b.backend.submitCmdBuf(true)
+ copy(data, mem)
+ return nil
+}
+
+func (b *Buffer) Upload(data []byte) {
+ if b.buf == 0 {
+ copy(b.store, data)
+ return
+ }
+ stage, mem, off := b.backend.stagingBuffer(len(data))
+ copy(mem, data)
+ cmdBuf := b.backend.ensureCmdBuf()
+ b.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_WRITE_BIT,
+ )
+ vk.CmdCopyBuffer(cmdBuf, stage.buf, b.buf, off, 0, len(data))
+ var access vk.AccessFlags
+ if b.usage&vk.BUFFER_USAGE_INDEX_BUFFER_BIT != 0 {
+ access |= vk.ACCESS_INDEX_READ_BIT
+ }
+ if b.usage&vk.BUFFER_USAGE_VERTEX_BUFFER_BIT != 0 {
+ access |= vk.ACCESS_VERTEX_ATTRIBUTE_READ_BIT
+ }
+ if access != 0 {
+ b.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_VERTEX_INPUT_BIT,
+ access,
+ )
+ }
+}
+
+func (b *Buffer) barrier(cmdBuf vk.CommandBuffer, stage vk.PipelineStageFlags, access vk.AccessFlags) {
+ srcStage := b.scope.stage
+ if srcStage == 0 {
+ b.scope.stage = stage
+ b.scope.access = access
+ return
+ }
+ barrier := vk.BuildBufferMemoryBarrier(
+ b.buf,
+ b.scope.access, access,
+ )
+ vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, []vk.BufferMemoryBarrier{barrier}, nil)
+ b.scope.stage = stage
+ b.scope.access = access
+}
+
+func (b *Buffer) Release() {
+ freeb := *b
+ if freeb.buf != 0 {
+ b.backend.deferFunc(func(d vk.Device) {
+ vk.DestroyBuffer(d, freeb.buf)
+ vk.FreeMemory(d, freeb.mem)
+ })
+ }
+ *b = Buffer{}
+}
+
+func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ if len(pixels) == 0 {
+ return nil
+ }
+ sz := src.Size()
+ stageStride := sz.X * 4
+ n := sz.Y * stageStride
+ stage, mem, off := t.backend.stagingBuffer(n)
+ cmdBuf := t.backend.ensureCmdBuf()
+ region := vk.BuildBufferImageCopy(off, stageStride/4, src.Min.X, src.Min.Y, sz.X, sz.Y)
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_READ_BIT,
+ )
+ vk.CmdCopyImageToBuffer(cmdBuf, t.img, t.layout, stage.buf, []vk.BufferImageCopy{region})
+ stage.scope.stage = vk.PIPELINE_STAGE_TRANSFER_BIT
+ stage.scope.access = vk.ACCESS_TRANSFER_WRITE_BIT
+ stage.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_HOST_BIT,
+ vk.ACCESS_HOST_READ_BIT,
+ )
+ t.backend.submitCmdBuf(true)
+ var srcOff, dstOff int
+ for y := 0; y < sz.Y; y++ {
+ dstRow := pixels[srcOff : srcOff+stageStride]
+ srcRow := mem[dstOff : dstOff+stageStride]
+ copy(dstRow, srcRow)
+ dstOff += stageStride
+ srcOff += stride
+ }
+ return nil
+}
+
+func (b *Backend) currentCmdBuf() vk.CommandBuffer {
+ cur := b.cmdPool.current
+ if cur == nil {
+ panic("vulkan: invalid operation outside a render or compute pass")
+ }
+ return cur
+}
+
+func (b *Backend) ensureCmdBuf() vk.CommandBuffer {
+ if b.cmdPool.current != nil {
+ return b.cmdPool.current
+ }
+ if b.cmdPool.used < len(b.cmdPool.buffers) {
+ buf := b.cmdPool.buffers[b.cmdPool.used]
+ b.cmdPool.current = buf
+ } else {
+ buf, err := vk.AllocateCommandBuffer(b.dev, b.cmdPool.pool)
+ if err != nil {
+ panic(err)
+ }
+ b.cmdPool.buffers = append(b.cmdPool.buffers, buf)
+ b.cmdPool.current = buf
+ }
+ b.cmdPool.used++
+ buf := b.cmdPool.current
+ if err := vk.BeginCommandBuffer(buf); err != nil {
+ panic(err)
+ }
+ return buf
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
+ t := tex.(*Texture)
+ var vkop vk.AttachmentLoadOp
+ switch d.Action {
+ case driver.LoadActionClear:
+ vkop = vk.ATTACHMENT_LOAD_OP_CLEAR
+ case driver.LoadActionInvalidate:
+ vkop = vk.ATTACHMENT_LOAD_OP_DONT_CARE
+ case driver.LoadActionKeep:
+ vkop = vk.ATTACHMENT_LOAD_OP_LOAD
+ }
+ cmdBuf := b.ensureCmdBuf()
+ if sem := t.acquire; sem != 0 {
+ // The render pass targets a framebuffer that has an associated acquire semaphore.
+ // Wait for it by forming an execution barrier.
+ b.waitSems = append(b.waitSems, sem)
+ b.waitStages = append(b.waitStages, vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
+ // But only for the first pass in a frame.
+ t.acquire = 0
+ }
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ vk.ACCESS_COLOR_ATTACHMENT_READ_BIT|vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ )
+ pass := b.lookupPass(t.format, vkop, t.layout, t.passLayout)
+ col := d.ClearColor
+ vk.CmdBeginRenderPass(cmdBuf, pass, t.fbo, t.width, t.height, [4]float32{col.R, col.G, col.B, col.A})
+ t.layout = t.passLayout
+ // If the render pass describes an automatic image layout transition to its final layout, there
+ // is an implicit image barrier with destination PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT. Make
+ // sure any subsequent barrier includes the transition.
+ // See also https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VkSubpassDependency.
+ t.scope.stage |= vk.PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
+}
+
+func (b *Backend) EndRenderPass() {
+ vk.CmdEndRenderPass(b.cmdPool.current)
+}
+
+func (b *Backend) BeginCompute() {
+ b.ensureCmdBuf()
+}
+
+func (b *Backend) EndCompute() {
+}
+
+func (b *Backend) lookupPass(fmt vk.Format, loadAct vk.AttachmentLoadOp, initLayout, finalLayout vk.ImageLayout) vk.RenderPass {
+ key := passKey{fmt: fmt, loadAct: loadAct, initLayout: initLayout, finalLayout: finalLayout}
+ if pass, ok := b.passes[key]; ok {
+ return pass
+ }
+ pass, err := vk.CreateRenderPass(b.dev, fmt, loadAct, initLayout, finalLayout, nil)
+ if err != nil {
+ panic(err)
+ }
+ b.passes[key] = pass
+ return pass
+}
+
+func (b *Backend) submitCmdBuf(sync bool) {
+ buf := b.cmdPool.current
+ if buf == nil {
+ return
+ }
+ b.cmdPool.current = nil
+ if err := vk.EndCommandBuffer(buf); err != nil {
+ panic(err)
+ }
+ var fence vk.Fence
+ if sync {
+ fence = b.fence
+ }
+ if err := vk.QueueSubmit(b.queue, buf, b.waitSems, b.waitStages, b.sigSems, fence); err != nil {
+ panic(err)
+ }
+ b.waitSems = b.waitSems[:0]
+ b.sigSems = b.sigSems[:0]
+ b.waitStages = b.waitStages[:0]
+ if sync {
+ vk.WaitForFences(b.dev, b.fence)
+ vk.ResetFences(b.dev, b.fence)
+ }
+}
+
+func (b *Backend) stagingBuffer(size int) (*Buffer, []byte, int) {
+ if b.staging.size+size > b.staging.cap {
+ if b.staging.buf != nil {
+ vk.UnmapMemory(b.dev, b.staging.buf.mem)
+ b.staging.buf.Release()
+ b.staging.cap = 0
+ }
+ cap := 2 * (b.staging.size + size)
+ buf, err := b.newBuffer(cap, vk.BUFFER_USAGE_TRANSFER_SRC_BIT|vk.BUFFER_USAGE_TRANSFER_DST_BIT,
+ vk.MEMORY_PROPERTY_HOST_VISIBLE_BIT|vk.MEMORY_PROPERTY_HOST_COHERENT_BIT)
+ if err != nil {
+ panic(err)
+ }
+ mem, err := vk.MapMemory(b.dev, buf.mem, 0, cap)
+ if err != nil {
+ buf.Release()
+ panic(err)
+ }
+ b.staging.buf = buf
+ b.staging.mem = mem
+ b.staging.size = 0
+ b.staging.cap = cap
+ }
+ off := b.staging.size
+ b.staging.size += size
+ mem := b.staging.mem[off : off+size]
+ return b.staging.buf, mem, off
+}
+
+func formatFor(format driver.TextureFormat) vk.Format {
+ switch format {
+ case driver.TextureFormatRGBA8:
+ return vk.FORMAT_R8G8B8A8_UNORM
+ case driver.TextureFormatSRGBA:
+ return vk.FORMAT_R8G8B8A8_SRGB
+ case driver.TextureFormatFloat:
+ return vk.FORMAT_R16_SFLOAT
+ default:
+ panic("unsupported texture format")
+ }
+}
+
+func mapErr(err error) error {
+ var vkErr vk.Error
+ if errors.As(err, &vkErr) && vkErr == vk.ERROR_DEVICE_LOST {
+ return driver.ErrDeviceLost
+ }
+ return err
+}
+
+func (f *Texture) ImplementsRenderTarget() {}
diff --git a/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go b/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go
new file mode 100644
index 0000000..4364a43
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package vulkan
+
+// Empty file to avoid the build error for platforms without Vulkan support.
diff --git a/vendor/gioui.org/gpu/pack.go b/vendor/gioui.org/gpu/pack.go
index c4dbaad..3f8c925 100644
--- a/vendor/gioui.org/gpu/pack.go
+++ b/vendor/gioui.org/gpu/pack.go
@@ -9,8 +9,8 @@ import (
// packer packs a set of many smaller rectangles into
// much fewer larger atlases.
type packer struct {
- maxDim int
- spaces []image.Rectangle
+ maxDims image.Point
+ spaces []image.Rectangle
sizes []image.Point
pos image.Point
@@ -41,45 +41,65 @@ func (p *packer) newPage() {
p.sizes = append(p.sizes, image.Point{})
p.spaces = p.spaces[:0]
p.spaces = append(p.spaces, image.Rectangle{
- Max: image.Point{X: p.maxDim, Y: p.maxDim},
+ Max: image.Point{X: 1e6, Y: 1e6},
})
}
func (p *packer) tryAdd(s image.Point) (placement, bool) {
- // Go backwards to prioritize smaller spaces first.
- for i := len(p.spaces) - 1; i >= 0; i-- {
- space := p.spaces[i]
+ var (
+ bestIdx = -1
+ bestSpace image.Rectangle
+ bestSize = p.maxDims
+ )
+ // Go backwards to prioritize smaller spaces.
+ for i, space := range p.spaces {
rightSpace := space.Dx() - s.X
bottomSpace := space.Dy() - s.Y
- if rightSpace >= 0 && bottomSpace >= 0 {
- // Remove space.
- p.spaces[i] = p.spaces[len(p.spaces)-1]
- p.spaces = p.spaces[:len(p.spaces)-1]
- // Put s in the top left corner and add the (at most)
- // two smaller spaces.
- pos := space.Min
- if bottomSpace > 0 {
- p.spaces = append(p.spaces, image.Rectangle{
- Min: image.Point{X: pos.X, Y: pos.Y + s.Y},
- Max: image.Point{X: space.Max.X, Y: space.Max.Y},
- })
- }
- if rightSpace > 0 {
- p.spaces = append(p.spaces, image.Rectangle{
- Min: image.Point{X: pos.X + s.X, Y: pos.Y},
- Max: image.Point{X: space.Max.X, Y: pos.Y + s.Y},
- })
- }
- idx := len(p.sizes) - 1
- size := &p.sizes[idx]
- if x := pos.X + s.X; x > size.X {
- size.X = x
+ if rightSpace < 0 || bottomSpace < 0 {
+ continue
+ }
+ idx := len(p.sizes) - 1
+ size := p.sizes[idx]
+ if x := space.Min.X + s.X; x > size.X {
+ if x > p.maxDims.X {
+ continue
}
- if y := pos.Y + s.Y; y > size.Y {
- size.Y = y
+ size.X = x
+ }
+ if y := space.Min.Y + s.Y; y > size.Y {
+ if y > p.maxDims.Y {
+ continue
}
- return placement{Idx: idx, Pos: pos}, true
+ size.Y = y
}
+ if size.X*size.Y < bestSize.X*bestSize.Y {
+ bestIdx = i
+ bestSpace = space
+ bestSize = size
+ }
+ }
+ if bestIdx == -1 {
+ return placement{}, false
+ }
+ // Remove space.
+ p.spaces[bestIdx] = p.spaces[len(p.spaces)-1]
+ p.spaces = p.spaces[:len(p.spaces)-1]
+ // Put s in the top left corner and add the (at most)
+ // two smaller spaces.
+ pos := bestSpace.Min
+ if rem := bestSpace.Dy() - s.Y; rem > 0 {
+ p.spaces = append(p.spaces, image.Rectangle{
+ Min: image.Point{X: pos.X, Y: pos.Y + s.Y},
+ Max: image.Point{X: bestSpace.Max.X, Y: bestSpace.Max.Y},
+ })
+ }
+ if rem := bestSpace.Dx() - s.X; rem > 0 {
+ p.spaces = append(p.spaces, image.Rectangle{
+ Min: image.Point{X: pos.X + s.X, Y: pos.Y},
+ Max: image.Point{X: bestSpace.Max.X, Y: pos.Y + s.Y},
+ })
}
- return placement{}, false
+ idx := len(p.sizes) - 1
+ p.sizes[idx] = bestSize
+ return placement{Idx: idx, Pos: pos}, true
}
diff --git a/vendor/gioui.org/gpu/path.go b/vendor/gioui.org/gpu/path.go
index 2dd5875..5fd73cf 100644
--- a/vendor/gioui.org/gpu/path.go
+++ b/vendor/gioui.org/gpu/path.go
@@ -12,13 +12,15 @@ import (
"unsafe"
"gioui.org/f32"
- "gioui.org/gpu/backend"
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/byteslice"
"gioui.org/internal/f32color"
- gunsafe "gioui.org/internal/unsafe"
+ "gioui.org/shader"
+ "gioui.org/shader/gio"
)
type pather struct {
- ctx backend.Device
+ ctx driver.Device
viewport image.Point
@@ -27,28 +29,28 @@ type pather struct {
}
type coverer struct {
- ctx backend.Device
- prog [2]*program
- texUniforms *coverTexUniforms
- colUniforms *coverColUniforms
- layout backend.InputLayout
+ ctx driver.Device
+ pipelines [3]*pipeline
+ texUniforms *coverTexUniforms
+ colUniforms *coverColUniforms
+ linearGradientUniforms *coverLinearGradientUniforms
}
type coverTexUniforms struct {
- vert struct {
- coverUniforms
- _ [12]byte // Padding to multiple of 16.
- }
+ coverUniforms
+ _ [12]byte // Padding to multiple of 16.
}
type coverColUniforms struct {
- vert struct {
- coverUniforms
- _ [12]byte // Padding to multiple of 16.
- }
- frag struct {
- colorUniforms
- }
+ coverUniforms
+ _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(colorUniforms{})]byte // Padding to 128 bytes.
+ colorUniforms
+}
+
+type coverLinearGradientUniforms struct {
+ coverUniforms
+ _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(gradientUniforms{})]byte // Padding to 128.
+ gradientUniforms
}
type coverUniforms struct {
@@ -60,28 +62,24 @@ type coverUniforms struct {
}
type stenciler struct {
- ctx backend.Device
- prog struct {
- prog *program
+ ctx driver.Device
+ pipeline struct {
+ pipeline *pipeline
uniforms *stencilUniforms
- layout backend.InputLayout
}
- iprog struct {
- prog *program
+ ipipeline struct {
+ pipeline *pipeline
uniforms *intersectUniforms
- layout backend.InputLayout
}
fbos fboSet
intersections fboSet
- indexBuf backend.Buffer
+ indexBuf driver.Buffer
}
type stencilUniforms struct {
- vert struct {
- transform [4]float32
- pathOffset [2]float32
- _ [8]byte // Padding to multiple of 16.
- }
+ transform [4]float32
+ pathOffset [2]float32
+ _ [8]byte // Padding to multiple of 16.
}
type intersectUniforms struct {
@@ -97,13 +95,12 @@ type fboSet struct {
type stencilFBO struct {
size image.Point
- fbo backend.Framebuffer
- tex backend.Texture
+ tex driver.Texture
}
type pathData struct {
ncurves int
- data backend.Buffer
+ data driver.Buffer
}
// vertex data suitable for passing to vertex programs.
@@ -132,10 +129,10 @@ const (
// Number of path quads per draw batch.
pathBatchSize = 10000
// Size of a vertex as sent to gpu
- vertStride = 7*4 + 2*2
+ vertStride = 8 * 4
)
-func newPather(ctx backend.Device) *pather {
+func newPather(ctx driver.Device) *pather {
return &pather{
ctx: ctx,
stenciler: newStenciler(ctx),
@@ -143,25 +140,24 @@ func newPather(ctx backend.Device) *pather {
}
}
-func newCoverer(ctx backend.Device) *coverer {
+func newCoverer(ctx driver.Device) *coverer {
c := &coverer{
ctx: ctx,
}
c.colUniforms = new(coverColUniforms)
c.texUniforms = new(coverTexUniforms)
- prog, layout, err := createColorPrograms(ctx, shader_cover_vert, shader_cover_frag,
- [2]interface{}{&c.colUniforms.vert, &c.texUniforms.vert},
- [2]interface{}{&c.colUniforms.frag, nil},
+ c.linearGradientUniforms = new(coverLinearGradientUniforms)
+ pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag,
+ [3]interface{}{c.colUniforms, c.linearGradientUniforms, c.texUniforms},
)
if err != nil {
panic(err)
}
- c.prog = prog
- c.layout = layout
+ c.pipelines = pipelines
return c
}
-func newStenciler(ctx backend.Device) *stenciler {
+func newStenciler(ctx driver.Device) *stenciler {
// Allocate a suitably large index buffer for drawing paths.
indices := make([]uint16, pathBatchSize*6)
for i := 0; i < pathBatchSize; i++ {
@@ -173,51 +169,80 @@ func newStenciler(ctx backend.Device) *stenciler {
indices[i*6+4] = i*4 + 1
indices[i*6+5] = i*4 + 3
}
- indexBuf, err := ctx.NewImmutableBuffer(backend.BufferBindingIndices, gunsafe.BytesView(indices))
+ indexBuf, err := ctx.NewImmutableBuffer(driver.BufferBindingIndices, byteslice.Slice(indices))
if err != nil {
panic(err)
}
- progLayout, err := ctx.NewInputLayout(shader_stencil_vert, []backend.InputDesc{
- {Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))},
- {Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))},
- {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))},
- {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))},
- {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))},
- })
- if err != nil {
- panic(err)
+ progLayout := driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))},
+ {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))},
+ },
+ Stride: vertStride,
}
- iprogLayout, err := ctx.NewInputLayout(shader_intersect_vert, []backend.InputDesc{
- {Type: backend.DataTypeFloat, Size: 2, Offset: 0},
- {Type: backend.DataTypeFloat, Size: 2, Offset: 4 * 2},
- })
- if err != nil {
- panic(err)
+ iprogLayout := driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: 4 * 4,
}
st := &stenciler{
ctx: ctx,
indexBuf: indexBuf,
}
- prog, err := ctx.NewProgram(shader_stencil_vert, shader_stencil_frag)
+ vsh, fsh, err := newShaders(ctx, gio.Shader_stencil_vert, gio.Shader_stencil_frag)
if err != nil {
panic(err)
}
- st.prog.uniforms = new(stencilUniforms)
- vertUniforms := newUniformBuffer(ctx, &st.prog.uniforms.vert)
- st.prog.prog = newProgram(prog, vertUniforms, nil)
- st.prog.layout = progLayout
- iprog, err := ctx.NewProgram(shader_intersect_vert, shader_intersect_frag)
+ defer vsh.Release()
+ defer fsh.Release()
+ st.pipeline.uniforms = new(stencilUniforms)
+ vertUniforms := newUniformBuffer(ctx, st.pipeline.uniforms)
+ pipe, err := st.ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ VertexLayout: progLayout,
+ BlendDesc: driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorOne,
+ DstFactor: driver.BlendFactorOne,
+ },
+ PixelFormat: driver.TextureFormatFloat,
+ Topology: driver.TopologyTriangles,
+ })
+ st.pipeline.pipeline = &pipeline{pipe, vertUniforms}
+ if err != nil {
+ panic(err)
+ }
+ vsh, fsh, err = newShaders(ctx, gio.Shader_intersect_vert, gio.Shader_intersect_frag)
if err != nil {
panic(err)
}
- st.iprog.uniforms = new(intersectUniforms)
- vertUniforms = newUniformBuffer(ctx, &st.iprog.uniforms.vert)
- st.iprog.prog = newProgram(iprog, vertUniforms, nil)
- st.iprog.layout = iprogLayout
+ defer vsh.Release()
+ defer fsh.Release()
+ st.ipipeline.uniforms = new(intersectUniforms)
+ vertUniforms = newUniformBuffer(ctx, &st.ipipeline.uniforms.vert)
+ ipipe, err := st.ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ VertexLayout: iprogLayout,
+ BlendDesc: driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorDstColor,
+ DstFactor: driver.BlendFactorZero,
+ },
+ PixelFormat: driver.TextureFormatFloat,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ st.ipipeline.pipeline = &pipeline{ipipe, vertUniforms}
return st
}
-func (s *fboSet) resize(ctx backend.Device, sizes []image.Point) {
+func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) {
// Add fbos.
for i := len(s.fbos); i < len(sizes); i++ {
s.fbos = append(s.fbos, stencilFBO{})
@@ -231,38 +256,25 @@ func (s *fboSet) resize(ctx backend.Device, sizes []image.Point) {
waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y)
resize = resize || waste > 1.2
if resize {
- if f.fbo != nil {
- f.fbo.Release()
+ if f.tex != nil {
f.tex.Release()
}
- tex, err := ctx.NewTexture(backend.TextureFormatFloat, sz.X, sz.Y, backend.FilterNearest, backend.FilterNearest,
- backend.BufferBindingTexture|backend.BufferBindingFramebuffer)
- if err != nil {
- panic(err)
- }
- fbo, err := ctx.NewFramebuffer(tex, 0)
+ tex, err := ctx.NewTexture(driver.TextureFormatFloat, sz.X, sz.Y, driver.FilterNearest, driver.FilterNearest,
+ driver.BufferBindingTexture|driver.BufferBindingFramebuffer)
if err != nil {
panic(err)
}
f.size = sz
f.tex = tex
- f.fbo = fbo
}
}
// Delete extra fbos.
s.delete(ctx, len(sizes))
}
-func (s *fboSet) invalidate(ctx backend.Device) {
- for _, f := range s.fbos {
- f.fbo.Invalidate()
- }
-}
-
-func (s *fboSet) delete(ctx backend.Device, idx int) {
+func (s *fboSet) delete(ctx driver.Device, idx int) {
for i := idx; i < len(s.fbos); i++ {
f := s.fbos[i]
- f.fbo.Release()
f.tex.Release()
}
s.fbos = s.fbos[:idx]
@@ -270,10 +282,9 @@ func (s *fboSet) delete(ctx backend.Device, idx int) {
func (s *stenciler) release() {
s.fbos.delete(s.ctx, 0)
- s.prog.layout.Release()
- s.prog.prog.Release()
- s.iprog.layout.Release()
- s.iprog.prog.Release()
+ s.intersections.delete(s.ctx, 0)
+ s.pipeline.pipeline.Release()
+ s.ipipeline.pipeline.Release()
s.indexBuf.Release()
}
@@ -283,14 +294,13 @@ func (p *pather) release() {
}
func (c *coverer) release() {
- for _, p := range c.prog {
+ for _, p := range c.pipelines {
p.Release()
}
- c.layout.Release()
}
-func buildPath(ctx backend.Device, p []byte) pathData {
- buf, err := ctx.NewImmutableBuffer(backend.BufferBindingVertices, p)
+func buildPath(ctx driver.Device, p []byte) pathData {
+ buf, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices, p)
if err != nil {
panic(err)
}
@@ -313,17 +323,10 @@ func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.
}
func (s *stenciler) beginIntersect(sizes []image.Point) {
- s.ctx.BlendFunc(backend.BlendFactorDstColor, backend.BlendFactorZero)
// 8 bit coverage is enough, but OpenGL ES only supports single channel
// floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if
// no floating point support is available.
s.intersections.resize(s.ctx, sizes)
- s.ctx.BindProgram(s.iprog.prog.prog)
-}
-
-func (s *stenciler) invalidateFBO() {
- s.intersections.invalidate(s.ctx)
- s.fbos.invalidate(s.ctx)
}
func (s *stenciler) cover(idx int) stencilFBO {
@@ -331,11 +334,7 @@ func (s *stenciler) cover(idx int) stencilFBO {
}
func (s *stenciler) begin(sizes []image.Point) {
- s.ctx.BlendFunc(backend.BlendFactorOne, backend.BlendFactorOne)
s.fbos.resize(s.ctx, sizes)
- s.ctx.BindProgram(s.prog.prog.prog)
- s.ctx.BindInputLayout(s.prog.layout)
- s.ctx.BindIndexBuffer(s.indexBuf)
}
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
@@ -344,9 +343,9 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
- s.prog.uniforms.vert.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
- s.prog.uniforms.vert.pathOffset = [2]float32{offset.X, offset.Y}
- s.prog.prog.UploadUniforms()
+ s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
+ s.pipeline.uniforms.pathOffset = [2]float32{offset.X, offset.Y}
+ s.pipeline.pipeline.UploadUniforms(s.ctx)
// Draw in batches that fit in uint16 indices.
start := 0
nquads := data.ncurves / 4
@@ -356,35 +355,40 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
batch = max
}
off := vertStride * start * 4
- s.ctx.BindVertexBuffer(data.data, vertStride, off)
- s.ctx.DrawElements(backend.DrawModeTriangles, 0, batch*6)
+ s.ctx.BindVertexBuffer(data.data, off)
+ s.ctx.DrawElements(0, batch*6)
start += batch
}
}
-func (p *pather) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
- p.coverer.cover(z, mat, col, scale, off, uvTrans, coverScale, coverOff)
+func (p *pather) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
+ p.coverer.cover(mat, col, col1, col2, scale, off, uvTrans, coverScale, coverOff)
}
-func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
- p := c.prog[mat]
- c.ctx.BindProgram(p.prog)
+func (c *coverer) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
var uniforms *coverUniforms
switch mat {
case materialColor:
- c.colUniforms.frag.color = col
- uniforms = &c.colUniforms.vert.coverUniforms
+ c.colUniforms.color = col
+ uniforms = &c.colUniforms.coverUniforms
+ case materialLinearGradient:
+ c.linearGradientUniforms.color1 = col1
+ c.linearGradientUniforms.color2 = col2
+
+ t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
+ c.linearGradientUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ c.linearGradientUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &c.linearGradientUniforms.coverUniforms
case materialTexture:
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
- c.texUniforms.vert.uvTransformR1 = [4]float32{t1, t2, t3, 0}
- c.texUniforms.vert.uvTransformR2 = [4]float32{t4, t5, t6, 0}
- uniforms = &c.texUniforms.vert.coverUniforms
+ c.texUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ c.texUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &c.texUniforms.coverUniforms
}
- uniforms.z = z
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
- p.UploadUniforms()
- c.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4)
+ c.pipelines[mat].UploadUniforms(c.ctx)
+ c.ctx.DrawArrays(0, 4)
}
func init() {
diff --git a/vendor/gioui.org/gpu/shaders.go b/vendor/gioui.org/gpu/shaders.go
deleted file mode 100644
index d8e8435..0000000
--- a/vendor/gioui.org/gpu/shaders.go
+++ /dev/null
@@ -1,657 +0,0 @@
-// Code generated by build.go. DO NOT EDIT.
-
-package gpu
-
-import "gioui.org/gpu/backend"
-
-var (
- shader_blit_frag = [...]backend.ShaderSources{
- {
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Color", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_12._color", Type: 0x0, Size: 4, Offset: 0}},
- Size: 16,
- },
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = _12._color;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nlayout(std140) uniform Color\n{\n vec4 _color;\n} _12;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n",
- GLSL130: "#version 130\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n",
- GLSL150: "#version 150\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n",
- /*
- cbuffer Color : register(b0)
- {
- float4 _12_color : packoffset(c0);
- };
-
-
- static float4 fragColor;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vUV : TEXCOORD0;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = _12_color;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xc6, 0x4a, 0x23, 0x81, 0x87, 0xab, 0xe3, 0xca, 0x12, 0x9, 0x7e, 0x2f, 0x5e, 0x2, 0x62, 0x14, 0x1, 0x0, 0x0, 0x0, 0x70, 0x2, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x84, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x48, 0x1, 0x0, 0x0, 0x8, 0x2, 0x0, 0x0, 0x3c, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x44, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x14, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x40, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x6, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xb8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x32, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- {
- Textures: []backend.TextureBinding{{Name: "tex", Binding: 0}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n",
- GLSL130: "#version 130\n\nuniform sampler2D tex;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n",
- GLSL150: "#version 150\n\nuniform sampler2D tex;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n",
- /*
- Texture2D<float4> tex : register(t0);
- SamplerState _tex_sampler : register(s0);
-
- static float4 fragColor;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vUV : TEXCOORD0;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = tex.Sample(_tex_sampler, vUV);
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xb7, 0x3f, 0x1d, 0xb1, 0x80, 0xcd, 0x80, 0xa3, 0x57, 0x9, 0xfb, 0x5a, 0x9f, 0x56, 0xd6, 0xda, 0x1, 0x0, 0x0, 0x0, 0x94, 0x2, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xa4, 0x0, 0x0, 0x0, 0x10, 0x1, 0x0, 0x0, 0x8c, 0x1, 0x0, 0x0, 0x2c, 0x2, 0x0, 0x0, 0x60, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x64, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x3c, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x3, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x64, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x6d, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x69, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x74, 0x65, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x74, 0x65, 0x78, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- }
- shader_blit_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_52.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_52.uvTransformR1", Type: 0x0, Size: 4, Offset: 16}, {Name: "_52.uvTransformR2", Type: 0x0, Size: 4, Offset: 32}, {Name: "_52.z", Type: 0x0, Size: 1, Offset: 48}},
- Size: 52,
- },
- GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n} _52;\n\nlayout(location = 0) in vec2 pos;\nout vec2 vUV;\nlayout(location = 1) in vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- /*
- struct m3x2
- {
- float3 r0;
- float3 r1;
- };
-
- static const m3x2 _116 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) };
- static const m3x2 _118 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) };
-
- cbuffer Block : register(b0)
- {
- float4 _69_transform : packoffset(c0);
- float4 _69_uvTransformR1 : packoffset(c1);
- float4 _69_uvTransformR2 : packoffset(c2);
- float _69_z : packoffset(c3);
- };
-
-
- static float4 gl_Position;
- static float2 pos;
- static float2 vUV;
- static float2 uv;
-
- struct SPIRV_Cross_Input
- {
- float2 pos : POSITION;
- float2 uv : NORMAL;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vUV : TEXCOORD0;
- float4 gl_Position : SV_Position;
- };
-
- float4 toClipSpace(float4 pos_1)
- {
- return float4(pos_1.xy, (pos_1.z + pos_1.w) * 0.5f, pos_1.w);
- }
-
- float3 transform3x2(m3x2 t, float3 v)
- {
- return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v));
- }
-
- void vert_main()
- {
- float2 p = (pos * _69_transform.xy) + _69_transform.zw;
- float4 param = float4(p, _69_z, 1.0f);
- gl_Position = toClipSpace(param);
- m3x2 _103 = { _69_uvTransformR1.xyz, _69_uvTransformR2.xyz };
- m3x2 param_1 = _103;
- float3 param_2 = float3(uv, 1.0f);
- vUV = transform3x2(param_1, param_2).xy;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- pos = stage_input.pos;
- uv = stage_input.uv;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vUV = vUV;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x57, 0x0, 0x34, 0xa, 0xf6, 0x3b, 0x1b, 0xc4, 0x28, 0xa1, 0x26, 0x75, 0x95, 0x95, 0xc4, 0x27, 0x1, 0x0, 0x0, 0x0, 0xc0, 0x4, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x24, 0x1, 0x0, 0x0, 0x54, 0x2, 0x0, 0x0, 0xd0, 0x2, 0x0, 0x0, 0x18, 0x4, 0x0, 0x0, 0x68, 0x4, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xe4, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x5, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x7, 0x80, 0x1, 0x0, 0xc4, 0x90, 0x5, 0x0, 0xd0, 0xa0, 0x5, 0x0, 0xc5, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0xe0, 0x2, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0xe0, 0x3, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x90, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7, 0x80, 0x5, 0x0, 0xe4, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0xc, 0xc0, 0x4, 0x0, 0x0, 0xa0, 0x0, 0x0, 0x64, 0x80, 0x0, 0x0, 0x24, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x28, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x4a, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x10, 0x0, 0x0, 0x8, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x22, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xa, 0x42, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x82, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x40, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x18, 0x1, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xee, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x31, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x32, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x7a, 0x0, 0xab, 0xab, 0x0, 0x0, 0x3, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
- shader_cover_frag = [...]backend.ShaderSources{
- {
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Color", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_12._color", Type: 0x0, Size: 4, Offset: 0}},
- Size: 16,
- },
- Textures: []backend.TextureBinding{{Name: "cover", Binding: 1}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vCoverUV;\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = _12._color;\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nlayout(std140) uniform Color\n{\n vec4 _color;\n} _12;\n\nuniform mediump sampler2D cover;\n\nlayout(location = 0) out vec4 fragColor;\nin highp vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL130: "#version 130\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL150: "#version 150\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- /*
- cbuffer Color : register(b0)
- {
- float4 _12_color : packoffset(c0);
- };
-
- Texture2D<float4> cover : register(t1);
- SamplerState _cover_sampler : register(s1);
-
- static float4 fragColor;
- static float2 vCoverUV;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vCoverUV : TEXCOORD0;
- float2 vUV : TEXCOORD1;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = _12_color;
- float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x);
- fragColor *= cover_1;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vCoverUV = stage_input.vCoverUV;
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x94, 0xe9, 0xc3, 0x12, 0xd8, 0xfb, 0xb, 0x3b, 0xe, 0xda, 0x43, 0x25, 0xcb, 0x53, 0x1c, 0x9d, 0x1, 0x0, 0x0, 0x0, 0x84, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x74, 0x1, 0x0, 0x0, 0xf0, 0x1, 0x0, 0x0, 0x4, 0x3, 0x0, 0x0, 0x50, 0x3, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x8c, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x58, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x28, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x34, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x34, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xa0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x9, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xc, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8b, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x91, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0x91, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x32, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x44, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- {
- Textures: []backend.TextureBinding{{Name: "tex", Binding: 0}, {Name: "cover", Binding: 1}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\nuniform mediump sampler2D cover;\n\nvarying vec2 vUV;\nvarying highp vec2 vCoverUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\nuniform mediump sampler2D cover;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\nin highp vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL130: "#version 130\n\nuniform sampler2D tex;\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vUV;\nin vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL150: "#version 150\n\nuniform sampler2D tex;\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vUV;\nin vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- /*
- Texture2D<float4> tex : register(t0);
- SamplerState _tex_sampler : register(s0);
- Texture2D<float4> cover : register(t1);
- SamplerState _cover_sampler : register(s1);
-
- static float4 fragColor;
- static float2 vUV;
- static float2 vCoverUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vCoverUV : TEXCOORD0;
- float2 vUV : TEXCOORD1;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = tex.Sample(_tex_sampler, vUV);
- float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x);
- fragColor *= cover_1;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- vCoverUV = stage_input.vCoverUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xcb, 0xa0, 0xb0, 0x5b, 0x6e, 0x48, 0xa6, 0x5a, 0x1c, 0x34, 0x50, 0xfe, 0xd4, 0x6f, 0x72, 0xb5, 0x1, 0x0, 0x0, 0x0, 0xbc, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xec, 0x0, 0x0, 0x0, 0xcc, 0x1, 0x0, 0x0, 0x48, 0x2, 0x0, 0x0, 0x3c, 0x3, 0x0, 0x0, 0x88, 0x3, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xac, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x2, 0x0, 0x24, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x1, 0x8, 0xf, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x1b, 0xb0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x8, 0xe4, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x1, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x1, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xd8, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0xc2, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x1a, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x8, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xec, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0xc2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa9, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xb8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x74, 0x65, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x74, 0x65, 0x78, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x44, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- }
- shader_cover_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_53.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_53.uvCoverTransform", Type: 0x0, Size: 4, Offset: 16}, {Name: "_53.uvTransformR1", Type: 0x0, Size: 4, Offset: 32}, {Name: "_53.uvTransformR2", Type: 0x0, Size: 4, Offset: 48}, {Name: "_53.z", Type: 0x0, Size: 1, Offset: 64}},
- Size: 68,
- },
- GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\nvarying vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n} _53;\n\nlayout(location = 0) in vec2 pos;\nout vec2 vUV;\nlayout(location = 1) in vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- /*
- struct m3x2
- {
- float3 r0;
- float3 r1;
- };
-
- static const m3x2 _115 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) };
- static const m3x2 _141 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) };
-
- cbuffer Block : register(b0)
- {
- float4 _70_transform : packoffset(c0);
- float4 _70_uvCoverTransform : packoffset(c1);
- float4 _70_uvTransformR1 : packoffset(c2);
- float4 _70_uvTransformR2 : packoffset(c3);
- float _70_z : packoffset(c4);
- };
-
-
- static float4 gl_Position;
- static float2 pos;
- static float2 vUV;
- static float2 uv;
- static float2 vCoverUV;
-
- struct SPIRV_Cross_Input
- {
- float2 pos : POSITION;
- float2 uv : NORMAL;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vCoverUV : TEXCOORD0;
- float2 vUV : TEXCOORD1;
- float4 gl_Position : SV_Position;
- };
-
- float4 toClipSpace(float4 pos_1)
- {
- return float4(pos_1.xy, (pos_1.z + pos_1.w) * 0.5f, pos_1.w);
- }
-
- float3 transform3x2(m3x2 t, float3 v)
- {
- return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v));
- }
-
- void vert_main()
- {
- float4 param = float4((pos * _70_transform.xy) + _70_transform.zw, _70_z, 1.0f);
- gl_Position = toClipSpace(param);
- m3x2 _101 = { _70_uvTransformR1.xyz, _70_uvTransformR2.xyz };
- m3x2 param_1 = _101;
- float3 param_2 = float3(uv, 1.0f);
- vUV = transform3x2(param_1, param_2).xy;
- m3x2 param_3 = _115;
- float3 param_4 = float3(uv, 1.0f);
- float3 uv3 = transform3x2(param_3, param_4);
- vCoverUV = ((uv3 * float3(_70_uvCoverTransform.xy, 1.0f)) + float3(_70_uvCoverTransform.zw, 0.0f)).xy;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- pos = stage_input.pos;
- uv = stage_input.uv;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vUV = vUV;
- stage_output.vCoverUV = vCoverUV;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xb3, 0x71, 0x8c, 0x9f, 0x3e, 0x4f, 0x8b, 0x50, 0xb7, 0xa2, 0x4b, 0xfb, 0xad, 0x7d, 0xc8, 0x71, 0x1, 0x0, 0x0, 0x0, 0xcc, 0x5, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x78, 0x1, 0x0, 0x0, 0x1c, 0x3, 0x0, 0x0, 0x98, 0x3, 0x0, 0x0, 0xc, 0x5, 0x0, 0x0, 0x5c, 0x5, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x38, 0x1, 0x0, 0x0, 0x38, 0x1, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x4, 0x1, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x6, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x3f, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x7, 0x80, 0x1, 0x0, 0xc4, 0x90, 0x6, 0x0, 0xd0, 0xa0, 0x6, 0x0, 0xc5, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x8, 0xe0, 0x3, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0xe0, 0x4, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x1, 0x0, 0xe1, 0x90, 0x6, 0x0, 0xe4, 0xa0, 0x6, 0x0, 0xe1, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x6, 0x0, 0xe2, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0xe4, 0x80, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0xee, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x90, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xb, 0x80, 0x6, 0x0, 0xe4, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0xc, 0xc0, 0x5, 0x0, 0x0, 0xa0, 0x0, 0x0, 0x74, 0x80, 0x0, 0x0, 0x34, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x9c, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x67, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xc2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0xf, 0x0, 0x0, 0xa, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x5, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x42, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x82, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xa, 0x42, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x82, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x6c, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x44, 0x1, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf4, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x1, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x34, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x31, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x32, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x7a, 0x0, 0xab, 0x0, 0x0, 0x3, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x68, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x3, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
- shader_intersect_frag = backend.ShaderSources{
- Textures: []backend.TextureBinding{{Name: "cover", Binding: 0}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vUV;\n\nvoid main()\n{\n float cover_1 = abs(texture2D(cover, vUV).x);\n gl_FragData[0].x = cover_1;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D cover;\n\nin highp vec2 vUV;\nlayout(location = 0) out vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n",
- GLSL130: "#version 130\n\nuniform sampler2D cover;\n\nin vec2 vUV;\nout vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n",
- GLSL150: "#version 150\n\nuniform sampler2D cover;\n\nin vec2 vUV;\nout vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n",
- /*
- Texture2D<float4> cover : register(t0);
- SamplerState _cover_sampler : register(s0);
-
- static float2 vUV;
- static float4 fragColor;
-
- struct SPIRV_Cross_Input
- {
- float2 vUV : TEXCOORD0;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- float cover_1 = abs(cover.Sample(_cover_sampler, vUV).x);
- fragColor.x = cover_1;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xe0, 0xe4, 0x3, 0x8c, 0xac, 0x56, 0x46, 0x82, 0x6c, 0xe7, 0x7c, 0xc3, 0x54, 0xa6, 0x27, 0xef, 0x1, 0x0, 0x0, 0x0, 0x8, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0xfc, 0x1, 0x0, 0x0, 0xa0, 0x2, 0x0, 0x0, 0xd4, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x94, 0x0, 0x0, 0x0, 0x94, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x6c, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x51, 0x0, 0x0, 0x5, 0x0, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x3, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xe, 0x80, 0x0, 0x0, 0x0, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xa4, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x6, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xe2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x71, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6b, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- }
- shader_intersect_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_78.uvTransform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_78.subUVTransform", Type: 0x0, Size: 4, Offset: 16}},
- Size: 32,
- },
- GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nattribute vec2 pos;\nattribute vec2 uv;\nvarying vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n} _78;\n\nlayout(location = 0) in vec2 pos;\nlayout(location = 1) in vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nin vec2 pos;\nin vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nin vec2 pos;\nin vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- /*
- struct m3x2
- {
- float3 r0;
- float3 r1;
- };
-
- static const m3x2 _38 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) };
- static const m3x2 _63 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) };
-
- cbuffer Block : register(b0)
- {
- float4 _78_uvTransform : packoffset(c0);
- float4 _78_subUVTransform : packoffset(c1);
- };
-
-
- static float4 gl_Position;
- static float2 pos;
- static float2 uv;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 pos : POSITION;
- float2 uv : NORMAL;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vUV : TEXCOORD0;
- float4 gl_Position : SV_Position;
- };
-
- float3 transform3x2(m3x2 t, float3 v)
- {
- return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v));
- }
-
- void vert_main()
- {
- m3x2 param = _38;
- float3 param_1 = float3(pos, 1.0f);
- float3 p = transform3x2(param, param_1);
- gl_Position = float4(p, 1.0f);
- m3x2 param_2 = _63;
- float3 param_3 = float3(uv, 1.0f);
- float3 uv3 = transform3x2(param_2, param_3);
- vUV = (uv3.xy * _78_subUVTransform.xy) + _78_subUVTransform.zw;
- m3x2 param_4 = _63;
- float3 param_5 = float3(vUV, 1.0f);
- vUV = transform3x2(param_4, param_5).xy;
- vUV = (vUV * _78_uvTransform.xy) + _78_uvTransform.zw;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- pos = stage_input.pos;
- uv = stage_input.uv;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vUV = vUV;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x27, 0x8, 0x9f, 0xbf, 0x30, 0xa, 0x5b, 0x38, 0xe, 0x78, 0x0, 0x22, 0xdb, 0x3b, 0x30, 0x54, 0x1, 0x0, 0x0, 0x0, 0xd8, 0x4, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x4c, 0x1, 0x0, 0x0, 0xc4, 0x2, 0x0, 0x0, 0x40, 0x3, 0x0, 0x0, 0x30, 0x4, 0x0, 0x0, 0x80, 0x4, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xc, 0x1, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xd8, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x3, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x1, 0x0, 0x55, 0x90, 0x3, 0x0, 0xe4, 0xa0, 0x3, 0x0, 0xe1, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x3, 0x0, 0xe2, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0xee, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x4, 0x80, 0x3, 0x0, 0x0, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x8, 0x80, 0x3, 0x0, 0xc9, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0xec, 0x80, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x90, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc, 0xc0, 0x3, 0x0, 0x0, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x70, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x52, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x14, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0xa, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xa, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0xf, 0x0, 0x0, 0xa, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x5, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xc2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xe8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x38, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x38, 0x5f, 0x73, 0x75, 0x62, 0x55, 0x56, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
- shader_stencil_frag = backend.ShaderSources{
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nvarying vec2 vTo;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n gl_FragData[0].x = area;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nlayout(location = 0) out vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n",
- GLSL130: "#version 130\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nout vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n",
- GLSL150: "#version 150\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nout vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n",
- /*
- static float2 vTo;
- static float2 vFrom;
- static float2 vCtrl;
- static float4 fragCover;
-
- struct SPIRV_Cross_Input
- {
- float2 vFrom : TEXCOORD0;
- float2 vCtrl : TEXCOORD1;
- float2 vTo : TEXCOORD2;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragCover : SV_Target0;
- };
-
- void frag_main()
- {
- float dx = vTo.x - vFrom.x;
- bool increasing = vTo.x >= vFrom.x;
- bool2 _35 = increasing.xx;
- float2 left = float2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);
- bool2 _41 = increasing.xx;
- float2 right = float2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);
- float2 extent = clamp(float2(vFrom.x, vTo.x), (-0.5f).xx, 0.5f.xx);
- float midx = lerp(extent.x, extent.y, 0.5f);
- float x0 = midx - left.x;
- float2 p1 = vCtrl - left;
- float2 v = right - vCtrl;
- float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));
- float y = lerp(lerp(left.y, vCtrl.y, t), lerp(vCtrl.y, right.y, t), t);
- float2 d_half = lerp(p1, v, t.xx);
- float dy = d_half.y / d_half.x;
- float width = extent.y - extent.x;
- dy = abs(dy * width);
- float4 sides = float4((dy * 0.5f) + y, (dy * (-0.5f)) + y, (0.5f - y) / dy, ((-0.5f) - y) / dy);
- sides = clamp(sides + 0.5f.xxxx, 0.0f.xxxx, 1.0f.xxxx);
- float area = 0.5f * ((((sides.z - (sides.z * sides.y)) + 1.0f) - sides.x) + (sides.x * sides.w));
- area *= width;
- if (width == 0.0f)
- {
- area = 0.0f;
- }
- fragCover.x = area;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vTo = stage_input.vTo;
- vFrom = stage_input.vFrom;
- vCtrl = stage_input.vCtrl;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragCover = fragCover;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x94, 0x21, 0xb9, 0x13, 0x4c, 0xba, 0xd, 0x11, 0x8f, 0xc7, 0xce, 0xe, 0x41, 0x73, 0xec, 0xe1, 0x1, 0x0, 0x0, 0x0, 0x5c, 0xa, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x9c, 0x3, 0x0, 0x0, 0xfc, 0x8, 0x0, 0x0, 0x78, 0x9, 0x0, 0x0, 0xc4, 0x9, 0x0, 0x0, 0x28, 0xa, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x5c, 0x3, 0x0, 0x0, 0x5c, 0x3, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x38, 0x3, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x2, 0xff, 0xff, 0x51, 0x0, 0x0, 0x5, 0x0, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x3, 0xb0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0xa0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x81, 0x1, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x1, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0xa, 0x0, 0x0, 0x3, 0x2, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x81, 0xb, 0x0, 0x0, 0x3, 0x3, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x1, 0x0, 0x0, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0x80, 0x1, 0x0, 0x0, 0x81, 0x1, 0x0, 0x0, 0xb0, 0x58, 0x0, 0x0, 0x4, 0x3, 0x0, 0x2, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0xb0, 0x1, 0x0, 0x55, 0x80, 0x58, 0x0, 0x0, 0x4, 0x2, 0x0, 0x2, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xc, 0x80, 0x3, 0x0, 0x1b, 0x80, 0x0, 0x0, 0xe4, 0xb1, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x2, 0x0, 0xe4, 0x81, 0x0, 0x0, 0x1b, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0xff, 0x80, 0x1, 0x0, 0x0, 0x81, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x7, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x0, 0x80, 0x6, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0x80, 0x2, 0x0, 0x55, 0x80, 0x12, 0x0, 0x0, 0x4, 0x2, 0x0, 0x3, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x1b, 0x80, 0x1, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x0, 0x0, 0xaa, 0xb0, 0x12, 0x0, 0x0, 0x4, 0x2, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x2, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x55, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0x80, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x1, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x2, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0xa0, 0x2, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xc, 0x80, 0x2, 0x0, 0xaa, 0x81, 0x0, 0x0, 0x1b, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x8, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xff, 0x80, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x1f, 0x80, 0x1, 0x0, 0xe4, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0x81, 0x1, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0x81, 0x0, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0xff, 0x80, 0x0, 0x0, 0x55, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x58, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x81, 0x0, 0x0, 0xff, 0xa0, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xe, 0x80, 0x0, 0x0, 0xff, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x58, 0x5, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x56, 0x1, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0xc2, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x42, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x72, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa6, 0x1b, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x9, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0x1e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xb2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xe, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4b, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0xc2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0xd, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xe, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xd, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa6, 0xa, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x4, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xa, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0xa, 0xf2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x32, 0x0, 0x0, 0xa, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x37, 0x0, 0x0, 0x9, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xe2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- }
- shader_stencil_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "corner", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "maxy", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "from", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "ctrl", Location: 3, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}, {Name: "to", Location: 4, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_16.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_16.pathOffset", Type: 0x0, Size: 2, Offset: 16}},
- Size: 24,
- },
- GLSL100ES: "\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nattribute vec2 from;\nattribute vec2 ctrl;\nattribute vec2 to;\nattribute float maxy;\nattribute float corner;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\nvarying vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- GLSL300ES: "#version 300 es\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec2 pathOffset;\n} _16;\n\nlayout(location = 2) in vec2 from;\nlayout(location = 3) in vec2 ctrl;\nlayout(location = 4) in vec2 to;\nlayout(location = 1) in float maxy;\nlayout(location = 0) in float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- GLSL130: "#version 130\n\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nin vec2 from;\nin vec2 ctrl;\nin vec2 to;\nin float maxy;\nin float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- GLSL150: "#version 150\n\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nin vec2 from;\nin vec2 ctrl;\nin vec2 to;\nin float maxy;\nin float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- /*
- cbuffer Block : register(b0)
- {
- float4 _16_transform : packoffset(c0);
- float2 _16_pathOffset : packoffset(c1);
- };
-
-
- static float4 gl_Position;
- static float2 from;
- static float2 ctrl;
- static float2 to;
- static float maxy;
- static float corner;
- static float2 vFrom;
- static float2 vCtrl;
- static float2 vTo;
-
- struct SPIRV_Cross_Input
- {
- float corner : POSITION;
- float maxy : NORMAL;
- float2 from : TEXCOORD0;
- float2 ctrl : TEXCOORD1;
- float2 to : TEXCOORD2;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vFrom : TEXCOORD0;
- float2 vCtrl : TEXCOORD1;
- float2 vTo : TEXCOORD2;
- float4 gl_Position : SV_Position;
- };
-
- void vert_main()
- {
- float2 from_1 = from + _16_pathOffset;
- float2 ctrl_1 = ctrl + _16_pathOffset;
- float2 to_1 = to + _16_pathOffset;
- float maxy_1 = maxy + _16_pathOffset.y;
- float c = corner;
- float2 pos;
- if (c >= 0.375f)
- {
- c -= 0.5f;
- pos.y = maxy_1 + 1.0f;
- }
- else
- {
- pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0f;
- }
- if (c >= 0.125f)
- {
- pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0f;
- }
- else
- {
- pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0f;
- }
- vFrom = from_1 - pos;
- vCtrl = ctrl_1 - pos;
- vTo = to_1 - pos;
- pos = (pos * _16_transform.xy) + _16_transform.zw;
- gl_Position = float4(pos, 1.0f, 1.0f);
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- from = stage_input.from;
- ctrl = stage_input.ctrl;
- to = stage_input.to;
- maxy = stage_input.maxy;
- corner = stage_input.corner;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vFrom = vFrom;
- stage_output.vCtrl = vCtrl;
- stage_output.vTo = vTo;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x99, 0xea, 0x97, 0xb5, 0xa8, 0xd5, 0x84, 0x5e, 0x4b, 0x12, 0x14, 0x56, 0xdd, 0xee, 0xfc, 0x44, 0x1, 0x0, 0x0, 0x0, 0x18, 0x8, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x4c, 0x2, 0x0, 0x0, 0x74, 0x5, 0x0, 0x0, 0xf0, 0x5, 0x0, 0x0, 0xec, 0x6, 0x0, 0x0, 0x90, 0x7, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xc, 0x2, 0x0, 0x0, 0xc, 0x2, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xd8, 0x1, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x3, 0x0, 0xf, 0xa0, 0x0, 0x0, 0xc0, 0x3e, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0xbf, 0x51, 0x0, 0x0, 0x5, 0x4, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x2, 0x80, 0x2, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x3, 0x80, 0x3, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x4, 0x80, 0x4, 0x0, 0xf, 0x90, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x2, 0x0, 0x55, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0x80, 0x0, 0x0, 0x0, 0x80, 0x3, 0x0, 0x55, 0xa0, 0xd, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x90, 0x3, 0x0, 0x0, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x0, 0x90, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x90, 0x3, 0x0, 0xff, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x2, 0x0, 0x3, 0x80, 0x2, 0x0, 0xe4, 0x90, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x2, 0x0, 0xc, 0x80, 0x3, 0x0, 0x14, 0x90, 0x2, 0x0, 0x14, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x3, 0x0, 0x3, 0x80, 0x2, 0x0, 0xee, 0x80, 0x2, 0x0, 0xe1, 0x80, 0x2, 0x0, 0x0, 0x3, 0x3, 0x0, 0xc, 0x80, 0x4, 0x0, 0x44, 0x90, 0x2, 0x0, 0x44, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x3, 0x0, 0x3, 0x80, 0x3, 0x0, 0xeb, 0x80, 0x3, 0x0, 0xe4, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x3, 0x0, 0xe4, 0x80, 0x3, 0x0, 0xaa, 0xa0, 0x12, 0x0, 0x0, 0x4, 0x4, 0x0, 0x6, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x1, 0x0, 0xc8, 0x80, 0xd, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x4, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0xa0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x2, 0x0, 0xff, 0x80, 0x2, 0x0, 0x0, 0x80, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x3, 0x0, 0xaa, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x3, 0x0, 0x55, 0xa0, 0x12, 0x0, 0x0, 0x4, 0x4, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0xe0, 0x2, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x28, 0x81, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0xe0, 0x3, 0x0, 0xee, 0x80, 0x4, 0x0, 0xe8, 0x81, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x4, 0x0, 0xe8, 0x80, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc, 0xc0, 0x3, 0x0, 0x55, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x20, 0x3, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x12, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x12, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xc2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x1d, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x3e, 0x0, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x14, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6, 0x84, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0xb6, 0xf, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x16, 0x5, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6, 0x14, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6, 0x84, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0xb6, 0xf, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x62, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x6, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa6, 0x8, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x34, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x37, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x86, 0x8, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x86, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xa, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x86, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xc2, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xf4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x36, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x36, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x0, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x9c, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x89, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x80, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x3, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x71, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
-)
diff --git a/vendor/gioui.org/gpu/timer.go b/vendor/gioui.org/gpu/timer.go
index a1da948..c1e3227 100644
--- a/vendor/gioui.org/gpu/timer.go
+++ b/vendor/gioui.org/gpu/timer.go
@@ -5,18 +5,18 @@ package gpu
import (
"time"
- "gioui.org/gpu/backend"
+ "gioui.org/gpu/internal/driver"
)
type timers struct {
- backend backend.Device
+ backend driver.Device
timers []*timer
}
type timer struct {
Elapsed time.Duration
- backend backend.Device
- timer backend.Timer
+ backend driver.Device
+ timer driver.Timer
state timerState
}
@@ -28,7 +28,7 @@ const (
timerWaiting
)
-func newTimers(b backend.Device) *timers {
+func newTimers(b driver.Device) *timers {
return &timers{
backend: b,
}
@@ -83,7 +83,7 @@ func (t *timers) ready() bool {
return t.backend.IsTimeContinuous()
}
-func (t *timers) release() {
+func (t *timers) Release() {
if t == nil {
return
}
diff --git a/vendor/gioui.org/internal/byteslice/byteslice.go b/vendor/gioui.org/internal/byteslice/byteslice.go
new file mode 100644
index 0000000..26ebdb2
--- /dev/null
+++ b/vendor/gioui.org/internal/byteslice/byteslice.go
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// Package byteslice provides byte slice views of other Go values such as
+// slices and structs.
+package byteslice
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+// Struct returns a byte slice view of a struct.
+func Struct(s interface{}) []byte {
+ v := reflect.ValueOf(s).Elem()
+ sz := int(v.Type().Size())
+ var res []byte
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+ h.Data = uintptr(unsafe.Pointer(v.UnsafeAddr()))
+ h.Cap = sz
+ h.Len = sz
+ return res
+}
+
+// Uint32 returns a byte slice view of a uint32 slice.
+func Uint32(s []uint32) []byte {
+ n := len(s)
+ if n == 0 {
+ return nil
+ }
+ blen := n * int(unsafe.Sizeof(s[0]))
+ return (*[1 << 30]byte)(unsafe.Pointer(&s[0]))[:blen:blen]
+}
+
+// Slice returns a byte slice view of a slice.
+func Slice(s interface{}) []byte {
+ v := reflect.ValueOf(s)
+ first := v.Index(0)
+ sz := int(first.Type().Size())
+ var res []byte
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+ h.Data = first.UnsafeAddr()
+ h.Cap = v.Cap() * sz
+ h.Len = v.Len() * sz
+ return res
+}
diff --git a/vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go b/vendor/gioui.org/internal/cocoainit/cocoa_darwin.go
index 2a34e57..2a34e57 100644
--- a/vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go
+++ b/vendor/gioui.org/internal/cocoainit/cocoa_darwin.go
diff --git a/vendor/gioui.org/app/internal/d3d11/d3d11_windows.go b/vendor/gioui.org/internal/d3d11/d3d11_windows.go
index 3dd2138..450fde1 100644
--- a/vendor/gioui.org/app/internal/d3d11/d3d11_windows.go
+++ b/vendor/gioui.org/internal/d3d11/d3d11_windows.go
@@ -13,9 +13,9 @@ import (
"golang.org/x/sys/windows"
)
-type _DXGI_SWAP_CHAIN_DESC struct {
- BufferDesc _DXGI_MODE_DESC
- SampleDesc _DXGI_SAMPLE_DESC
+type DXGI_SWAP_CHAIN_DESC struct {
+ BufferDesc DXGI_MODE_DESC
+ SampleDesc DXGI_SAMPLE_DESC
BufferUsage uint32
BufferCount uint32
OutputWindow windows.Handle
@@ -24,39 +24,39 @@ type _DXGI_SWAP_CHAIN_DESC struct {
Flags uint32
}
-type _DXGI_SAMPLE_DESC struct {
+type DXGI_SAMPLE_DESC struct {
Count uint32
Quality uint32
}
-type _DXGI_MODE_DESC struct {
+type DXGI_MODE_DESC struct {
Width uint32
Height uint32
- RefreshRate _DXGI_RATIONAL
+ RefreshRate DXGI_RATIONAL
Format uint32
ScanlineOrdering uint32
Scaling uint32
}
-type _DXGI_RATIONAL struct {
+type DXGI_RATIONAL struct {
Numerator uint32
Denominator uint32
}
-type _D3D11_TEXTURE2D_DESC struct {
+type TEXTURE2D_DESC struct {
Width uint32
Height uint32
MipLevels uint32
ArraySize uint32
Format uint32
- SampleDesc _DXGI_SAMPLE_DESC
+ SampleDesc DXGI_SAMPLE_DESC
Usage uint32
BindFlags uint32
CPUAccessFlags uint32
MiscFlags uint32
}
-type _D3D11_SAMPLER_DESC struct {
+type SAMPLER_DESC struct {
Filter uint32
AddressU uint32
AddressV uint32
@@ -69,22 +69,58 @@ type _D3D11_SAMPLER_DESC struct {
MaxLOD float32
}
-type _D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D struct {
- _D3D11_SHADER_RESOURCE_VIEW_DESC
- Texture2D _D3D11_TEX2D_SRV
+type SHADER_RESOURCE_VIEW_DESC_TEX2D struct {
+ SHADER_RESOURCE_VIEW_DESC
+ Texture2D TEX2D_SRV
}
-type _D3D11_SHADER_RESOURCE_VIEW_DESC struct {
+type SHADER_RESOURCE_VIEW_DESC_BUFFEREX struct {
+ SHADER_RESOURCE_VIEW_DESC
+ Buffer BUFFEREX_SRV
+}
+
+type UNORDERED_ACCESS_VIEW_DESC_TEX2D struct {
+ UNORDERED_ACCESS_VIEW_DESC
+ Texture2D TEX2D_UAV
+}
+
+type UNORDERED_ACCESS_VIEW_DESC_BUFFER struct {
+ UNORDERED_ACCESS_VIEW_DESC
+ Buffer BUFFER_UAV
+}
+
+type SHADER_RESOURCE_VIEW_DESC struct {
Format uint32
ViewDimension uint32
}
-type _D3D11_TEX2D_SRV struct {
+type UNORDERED_ACCESS_VIEW_DESC struct {
+ Format uint32
+ ViewDimension uint32
+}
+
+type TEX2D_SRV struct {
MostDetailedMip uint32
MipLevels uint32
}
-type _D3D11_INPUT_ELEMENT_DESC struct {
+type BUFFEREX_SRV struct {
+ FirstElement uint32
+ NumElements uint32
+ Flags uint32
+}
+
+type TEX2D_UAV struct {
+ MipSlice uint32
+}
+
+type BUFFER_UAV struct {
+ FirstElement uint32
+ NumElements uint32
+ Flags uint32
+}
+
+type INPUT_ELEMENT_DESC struct {
SemanticName *byte
SemanticIndex uint32
Format uint32
@@ -94,8 +130,8 @@ type _D3D11_INPUT_ELEMENT_DESC struct {
InstanceDataStepRate uint32
}
-type _IDXGISwapChain struct {
- vtbl *struct {
+type IDXGISwapChain struct {
+ Vtbl *struct {
_IUnknownVTbl
SetPrivateData uintptr
SetPrivateDataInterface uintptr
@@ -115,8 +151,23 @@ type _IDXGISwapChain struct {
}
}
-type _ID3D11Device struct {
- vtbl *struct {
+type Debug struct {
+ Vtbl *struct {
+ _IUnknownVTbl
+ SetFeatureMask uintptr
+ GetFeatureMask uintptr
+ SetPresentPerRenderOpDelay uintptr
+ GetPresentPerRenderOpDelay uintptr
+ SetSwapChain uintptr
+ GetSwapChain uintptr
+ ValidateContext uintptr
+ ReportLiveDeviceObjects uintptr
+ ValidateContextForDispatch uintptr
+ }
+}
+
+type Device struct {
+ Vtbl *struct {
_IUnknownVTbl
CreateBuffer uintptr
CreateTexture1D uintptr
@@ -161,8 +212,8 @@ type _ID3D11Device struct {
}
}
-type _ID3D11DeviceContext struct {
- vtbl *struct {
+type DeviceContext struct {
+ Vtbl *struct {
_IUnknownVTbl
GetDevice uintptr
GetPrivateData uintptr
@@ -279,122 +330,134 @@ type _ID3D11DeviceContext struct {
}
}
-type _ID3D11RenderTargetView struct {
- vtbl *struct {
+type RenderTargetView struct {
+ Vtbl *struct {
+ _IUnknownVTbl
+ }
+}
+
+type Resource struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11Resource struct {
- vtbl *struct {
+type Texture2D struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11Texture2D struct {
- vtbl *struct {
+type Buffer struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11Buffer struct {
- vtbl *struct {
+type SamplerState struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11SamplerState struct {
- vtbl *struct {
+type PixelShader struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11PixelShader struct {
- vtbl *struct {
+type ShaderResourceView struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11ShaderResourceView struct {
- vtbl *struct {
+type UnorderedAccessView struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11DepthStencilView struct {
- vtbl *struct {
+type DepthStencilView struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11BlendState struct {
- vtbl *struct {
+type BlendState struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11DepthStencilState struct {
- vtbl *struct {
+type DepthStencilState struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11VertexShader struct {
- vtbl *struct {
+type VertexShader struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11RasterizerState struct {
- vtbl *struct {
+type ComputeShader struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
-type _ID3D11InputLayout struct {
- vtbl *struct {
+type RasterizerState struct {
+ Vtbl *struct {
+ _IUnknownVTbl
+ }
+}
+
+type InputLayout struct {
+ Vtbl *struct {
_IUnknownVTbl
GetBufferPointer uintptr
GetBufferSize uintptr
}
}
-type _D3D11_DEPTH_STENCIL_DESC struct {
+type DEPTH_STENCIL_DESC struct {
DepthEnable uint32
DepthWriteMask uint32
DepthFunc uint32
StencilEnable uint32
StencilReadMask uint8
StencilWriteMask uint8
- FrontFace _D3D11_DEPTH_STENCILOP_DESC
- BackFace _D3D11_DEPTH_STENCILOP_DESC
+ FrontFace DEPTH_STENCILOP_DESC
+ BackFace DEPTH_STENCILOP_DESC
}
-type _D3D11_DEPTH_STENCILOP_DESC struct {
+type DEPTH_STENCILOP_DESC struct {
StencilFailOp uint32
StencilDepthFailOp uint32
StencilPassOp uint32
StencilFunc uint32
}
-type _D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D struct {
+type DEPTH_STENCIL_VIEW_DESC_TEX2D struct {
Format uint32
ViewDimension uint32
Flags uint32
- Texture2D _D3D11_TEX2D_DSV
+ Texture2D TEX2D_DSV
}
-type _D3D11_TEX2D_DSV struct {
+type TEX2D_DSV struct {
MipSlice uint32
}
-type _D3D11_BLEND_DESC struct {
+type BLEND_DESC struct {
AlphaToCoverageEnable uint32
IndependentBlendEnable uint32
- RenderTarget [8]_D3D11_RENDER_TARGET_BLEND_DESC
+ RenderTarget [8]RENDER_TARGET_BLEND_DESC
}
-type _D3D11_RENDER_TARGET_BLEND_DESC struct {
+type RENDER_TARGET_BLEND_DESC struct {
BlendEnable uint32
SrcBlend uint32
DestBlend uint32
@@ -405,8 +468,8 @@ type _D3D11_RENDER_TARGET_BLEND_DESC struct {
RenderTargetWriteMask uint8
}
-type _IDXGIObject struct {
- vtbl *struct {
+type IDXGIObject struct {
+ Vtbl *struct {
_IUnknownVTbl
SetPrivateData uintptr
SetPrivateDataInterface uintptr
@@ -415,8 +478,8 @@ type _IDXGIObject struct {
}
}
-type _IDXGIAdapter struct {
- vtbl *struct {
+type IDXGIAdapter struct {
+ Vtbl *struct {
_IUnknownVTbl
SetPrivateData uintptr
SetPrivateDataInterface uintptr
@@ -429,8 +492,8 @@ type _IDXGIAdapter struct {
}
}
-type _IDXGIFactory struct {
- vtbl *struct {
+type IDXGIFactory struct {
+ Vtbl *struct {
_IUnknownVTbl
SetPrivateData uintptr
SetPrivateDataInterface uintptr
@@ -444,8 +507,15 @@ type _IDXGIFactory struct {
}
}
-type _IDXGIDevice struct {
- vtbl *struct {
+type IDXGIDebug struct {
+ Vtbl *struct {
+ _IUnknownVTbl
+ ReportLiveObjects uintptr
+ }
+}
+
+type IDXGIDevice struct {
+ Vtbl *struct {
_IUnknownVTbl
SetPrivateData uintptr
SetPrivateDataInterface uintptr
@@ -459,8 +529,8 @@ type _IDXGIDevice struct {
}
}
-type _IUnknown struct {
- vtbl *struct {
+type IUnknown struct {
+ Vtbl *struct {
_IUnknownVTbl
}
}
@@ -471,7 +541,7 @@ type _IUnknownVTbl struct {
Release uintptr
}
-type _D3D11_BUFFER_DESC struct {
+type BUFFER_DESC struct {
ByteWidth uint32
Usage uint32
BindFlags uint32
@@ -480,7 +550,7 @@ type _D3D11_BUFFER_DESC struct {
StructureByteStride uint32
}
-type _GUID struct {
+type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
@@ -494,7 +564,7 @@ type _GUID struct {
Data4_7 uint8
}
-type _D3D11_VIEWPORT struct {
+type VIEWPORT struct {
TopLeftX float32
TopLeftY float32
Width float32
@@ -503,21 +573,21 @@ type _D3D11_VIEWPORT struct {
MaxDepth float32
}
-type _D3D11_SUBRESOURCE_DATA struct {
+type SUBRESOURCE_DATA struct {
pSysMem *byte
}
-type _D3D11_BOX struct {
- left uint32
- top uint32
- front uint32
- right uint32
- bottom uint32
- back uint32
+type BOX struct {
+ Left uint32
+ Top uint32
+ Front uint32
+ Right uint32
+ Bottom uint32
+ Back uint32
}
-type _D3D11_MAPPED_SUBRESOURCE struct {
- pData uintptr
+type MAPPED_SUBRESOURCE struct {
+ PData uintptr
RowPitch uint32
DepthPitch uint32
}
@@ -527,7 +597,7 @@ type ErrorCode struct {
Code uint32
}
-type _D3D11_RASTERIZER_DESC struct {
+type RASTERIZER_DESC struct {
FillMode uint32
CullMode uint32
FrontCounterClockwise uint32
@@ -541,118 +611,146 @@ type _D3D11_RASTERIZER_DESC struct {
}
var (
- _IID_ID3D11Texture2D = _GUID{0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c}
- _IID_IDXGIDevice = _GUID{0x54ec77fa, 0x1377, 0x44e6, 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c}
- _IID_IDXGIFactory = _GUID{0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69}
+ IID_Texture2D = GUID{0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c}
+ IID_IDXGIDebug = GUID{0x119E7452, 0xDE9E, 0x40fe, 0x88, 0x06, 0x88, 0xF9, 0x0C, 0x12, 0xB4, 0x41}
+ IID_IDXGIDevice = GUID{0x54ec77fa, 0x1377, 0x44e6, 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c}
+ IID_IDXGIFactory = GUID{0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69}
+ IID_ID3D11Debug = GUID{0x79cf2233, 0x7536, 0x4948, 0x9d, 0x36, 0x1e, 0x46, 0x92, 0xdc, 0x57, 0x60}
+
+ DXGI_DEBUG_ALL = GUID{0xe48ae283, 0xda80, 0x490b, 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8}
)
var (
d3d11 = windows.NewLazySystemDLL("d3d11.dll")
- __D3D11CreateDevice = d3d11.NewProc("D3D11CreateDevice")
- __D3D11CreateDeviceAndSwapChain = d3d11.NewProc("D3D11CreateDeviceAndSwapChain")
+ _D3D11CreateDevice = d3d11.NewProc("D3D11CreateDevice")
+ _D3D11CreateDeviceAndSwapChain = d3d11.NewProc("D3D11CreateDeviceAndSwapChain")
+
+ dxgi = windows.NewLazySystemDLL("dxgi.dll")
+
+ _DXGIGetDebugInterface1 = dxgi.NewProc("DXGIGetDebugInterface1")
)
const (
- _D3D11_SDK_VERSION = 7
- _D3D_DRIVER_TYPE_HARDWARE = 1
+ SDK_VERSION = 7
+ DRIVER_TYPE_HARDWARE = 1
- _DXGI_FORMAT_UNKNOWN = 0
- _DXGI_FORMAT_R16_FLOAT = 54
- _DXGI_FORMAT_R32_FLOAT = 41
- _DXGI_FORMAT_R32G32_FLOAT = 16
- _DXGI_FORMAT_R32G32B32_FLOAT = 6
- _DXGI_FORMAT_R32G32B32A32_FLOAT = 2
- _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29
- _DXGI_FORMAT_R16_SINT = 59
- _DXGI_FORMAT_R16G16_SINT = 38
- _DXGI_FORMAT_R16_UINT = 57
- _DXGI_FORMAT_D24_UNORM_S8_UINT = 45
- _DXGI_FORMAT_R16G16_FLOAT = 34
- _DXGI_FORMAT_R16G16B16A16_FLOAT = 10
+ DXGI_FORMAT_UNKNOWN = 0
+ DXGI_FORMAT_R16_FLOAT = 54
+ DXGI_FORMAT_R32_FLOAT = 41
+ DXGI_FORMAT_R32_TYPELESS = 39
+ DXGI_FORMAT_R32G32_FLOAT = 16
+ DXGI_FORMAT_R32G32B32_FLOAT = 6
+ DXGI_FORMAT_R32G32B32A32_FLOAT = 2
+ DXGI_FORMAT_R8G8B8A8_UNORM = 28
+ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29
+ DXGI_FORMAT_R16_SINT = 59
+ DXGI_FORMAT_R16G16_SINT = 38
+ DXGI_FORMAT_R16_UINT = 57
+ DXGI_FORMAT_D24_UNORM_S8_UINT = 45
+ DXGI_FORMAT_R16G16_FLOAT = 34
+ DXGI_FORMAT_R16G16B16A16_FLOAT = 10
- _D3D11_FORMAT_SUPPORT_TEXTURE2D = 0x20
- _D3D11_FORMAT_SUPPORT_RENDER_TARGET = 0x4000
+ DXGI_DEBUG_RLO_SUMMARY = 0x1
+ DXGI_DEBUG_RLO_DETAIL = 0x2
+ DXGI_DEBUG_RLO_IGNORE_INTERNAL = 0x4
- _DXGI_USAGE_RENDER_TARGET_OUTPUT = 1 << (1 + 4)
+ FORMAT_SUPPORT_TEXTURE2D = 0x20
+ FORMAT_SUPPORT_RENDER_TARGET = 0x4000
- _D3D11_CPU_ACCESS_READ = 0x20000
+ DXGI_USAGE_RENDER_TARGET_OUTPUT = 1 << (1 + 4)
- _D3D11_MAP_READ = 1
+ CPU_ACCESS_READ = 0x20000
- _DXGI_SWAP_EFFECT_DISCARD = 0
+ MAP_READ = 1
- _D3D_FEATURE_LEVEL_9_1 = 0x9100
- _D3D_FEATURE_LEVEL_9_3 = 0x9300
- _D3D_FEATURE_LEVEL_11_0 = 0xb000
+ DXGI_SWAP_EFFECT_DISCARD = 0
- _D3D11_USAGE_IMMUTABLE = 1
- _D3D11_USAGE_STAGING = 3
+ FEATURE_LEVEL_9_1 = 0x9100
+ FEATURE_LEVEL_9_3 = 0x9300
+ FEATURE_LEVEL_11_0 = 0xb000
- _D3D11_BIND_VERTEX_BUFFER = 0x1
- _D3D11_BIND_INDEX_BUFFER = 0x2
- _D3D11_BIND_CONSTANT_BUFFER = 0x4
- _D3D11_BIND_SHADER_RESOURCE = 0x8
- _D3D11_BIND_RENDER_TARGET = 0x20
- _D3D11_BIND_DEPTH_STENCIL = 0x40
+ USAGE_IMMUTABLE = 1
+ USAGE_STAGING = 3
- _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4
- _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5
+ BIND_VERTEX_BUFFER = 0x1
+ BIND_INDEX_BUFFER = 0x2
+ BIND_CONSTANT_BUFFER = 0x4
+ BIND_SHADER_RESOURCE = 0x8
+ BIND_RENDER_TARGET = 0x20
+ BIND_DEPTH_STENCIL = 0x40
+ BIND_UNORDERED_ACCESS = 0x80
- _D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14
- _D3D11_FILTER_MIN_MAG_MIP_POINT = 0
+ PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4
+ PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5
- _D3D11_TEXTURE_ADDRESS_MIRROR = 2
- _D3D11_TEXTURE_ADDRESS_CLAMP = 3
- _D3D11_TEXTURE_ADDRESS_WRAP = 1
+ FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14
+ FILTER_MIN_MAG_MIP_POINT = 0
- _D3D11_SRV_DIMENSION_TEXTURE2D = 4
+ TEXTURE_ADDRESS_MIRROR = 2
+ TEXTURE_ADDRESS_CLAMP = 3
+ TEXTURE_ADDRESS_WRAP = 1
- _D3D11_CREATE_DEVICE_DEBUG = 0x2
+ SRV_DIMENSION_BUFFER = 1
+ UAV_DIMENSION_BUFFER = 1
+ SRV_DIMENSION_BUFFEREX = 11
+ SRV_DIMENSION_TEXTURE2D = 4
+ UAV_DIMENSION_TEXTURE2D = 4
- _D3D11_FILL_SOLID = 3
+ BUFFER_UAV_FLAG_RAW = 0x1
+ BUFFEREX_SRV_FLAG_RAW = 0x1
- _D3D11_CULL_NONE = 1
+ RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS = 0x20
- _D3D11_CLEAR_DEPTH = 0x1
- _D3D11_CLEAR_STENCIL = 0x2
+ CREATE_DEVICE_DEBUG = 0x2
- _D3D11_DSV_DIMENSION_TEXTURE2D = 3
+ FILL_SOLID = 3
- _D3D11_DEPTH_WRITE_MASK_ALL = 1
+ CULL_NONE = 1
- _D3D11_COMPARISON_GREATER = 5
- _D3D11_COMPARISON_GREATER_EQUAL = 7
+ CLEAR_DEPTH = 0x1
+ CLEAR_STENCIL = 0x2
- _D3D11_BLEND_OP_ADD = 1
- _D3D11_BLEND_ONE = 2
- _D3D11_BLEND_INV_SRC_ALPHA = 6
- _D3D11_BLEND_ZERO = 1
- _D3D11_BLEND_DEST_COLOR = 9
- _D3D11_BLEND_DEST_ALPHA = 7
+ DSV_DIMENSION_TEXTURE2D = 3
- _D3D11_COLOR_WRITE_ENABLE_ALL = 1 | 2 | 4 | 8
+ DEPTH_WRITE_MASK_ALL = 1
+
+ COMPARISON_GREATER = 5
+ COMPARISON_GREATER_EQUAL = 7
+
+ BLEND_OP_ADD = 1
+ BLEND_ONE = 2
+ BLEND_INV_SRC_ALPHA = 6
+ BLEND_ZERO = 1
+ BLEND_DEST_COLOR = 9
+ BLEND_DEST_ALPHA = 7
+
+ COLOR_WRITE_ENABLE_ALL = 1 | 2 | 4 | 8
DXGI_STATUS_OCCLUDED = 0x087A0001
DXGI_ERROR_DEVICE_RESET = 0x887A0007
DXGI_ERROR_DEVICE_REMOVED = 0x887A0005
D3DDDIERR_DEVICEREMOVED = 1<<31 | 0x876<<16 | 2160
+
+ RLDO_SUMMARY = 1
+ RLDO_DETAIL = 2
+ RLDO_IGNORE_INTERNAL = 4
)
-func _D3D11CreateDevice(driverType uint32, flags uint32) (*_ID3D11Device, *_ID3D11DeviceContext, uint32, error) {
+func CreateDevice(driverType uint32, flags uint32) (*Device, *DeviceContext, uint32, error) {
var (
- dev *_ID3D11Device
- ctx *_ID3D11DeviceContext
+ dev *Device
+ ctx *DeviceContext
featLvl uint32
)
- r, _, _ := __D3D11CreateDevice.Call(
+ r, _, _ := _D3D11CreateDevice.Call(
0, // pAdapter
uintptr(driverType), // driverType
0, // Software
uintptr(flags), // Flags
0, // pFeatureLevels
0, // FeatureLevels
- _D3D11_SDK_VERSION, // SDKVersion
+ SDK_VERSION, // SDKVersion
uintptr(unsafe.Pointer(&dev)), // ppDevice
uintptr(unsafe.Pointer(&featLvl)), // pFeatureLevel
uintptr(unsafe.Pointer(&ctx)), // ppImmediateContext
@@ -663,21 +761,21 @@ func _D3D11CreateDevice(driverType uint32, flags uint32) (*_ID3D11Device, *_ID3D
return dev, ctx, featLvl, nil
}
-func _D3D11CreateDeviceAndSwapChain(driverType uint32, flags uint32, swapDesc *_DXGI_SWAP_CHAIN_DESC) (*_ID3D11Device, *_ID3D11DeviceContext, *_IDXGISwapChain, uint32, error) {
+func CreateDeviceAndSwapChain(driverType uint32, flags uint32, swapDesc *DXGI_SWAP_CHAIN_DESC) (*Device, *DeviceContext, *IDXGISwapChain, uint32, error) {
var (
- dev *_ID3D11Device
- ctx *_ID3D11DeviceContext
- swchain *_IDXGISwapChain
+ dev *Device
+ ctx *DeviceContext
+ swchain *IDXGISwapChain
featLvl uint32
)
- r, _, _ := __D3D11CreateDeviceAndSwapChain.Call(
+ r, _, _ := _D3D11CreateDeviceAndSwapChain.Call(
0, // pAdapter
uintptr(driverType), // driverType
0, // Software
uintptr(flags), // Flags
0, // pFeatureLevels
0, // FeatureLevels
- _D3D11_SDK_VERSION, // SDKVersion
+ SDK_VERSION, // SDKVersion
uintptr(unsafe.Pointer(swapDesc)), // pSwapChainDesc
uintptr(unsafe.Pointer(&swchain)), // ppSwapChain
uintptr(unsafe.Pointer(&dev)), // ppDevice
@@ -690,31 +788,67 @@ func _D3D11CreateDeviceAndSwapChain(driverType uint32, flags uint32, swapDesc *_
return dev, ctx, swchain, featLvl, nil
}
-func (d *_ID3D11Device) CheckFormatSupport(format uint32) (uint32, error) {
+func DXGIGetDebugInterface1() (*IDXGIDebug, error) {
+ var dbg *IDXGIDebug
+ r, _, _ := _DXGIGetDebugInterface1.Call(
+ 0, // Flags
+ uintptr(unsafe.Pointer(&IID_IDXGIDebug)),
+ uintptr(unsafe.Pointer(&dbg)),
+ )
+ if r != 0 {
+ return nil, ErrorCode{Name: "DXGIGetDebugInterface1", Code: uint32(r)}
+ }
+ return dbg, nil
+}
+
+func ReportLiveObjects() error {
+ dxgi, err := DXGIGetDebugInterface1()
+ if err != nil {
+ return err
+ }
+ defer IUnknownRelease(unsafe.Pointer(dxgi), dxgi.Vtbl.Release)
+ dxgi.ReportLiveObjects(&DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_DETAIL|DXGI_DEBUG_RLO_IGNORE_INTERNAL)
+ return nil
+}
+
+func (d *IDXGIDebug) ReportLiveObjects(guid *GUID, flags uint32) {
+ syscall.Syscall6(
+ d.Vtbl.ReportLiveObjects,
+ 3,
+ uintptr(unsafe.Pointer(d)),
+ uintptr(unsafe.Pointer(guid)),
+ uintptr(flags),
+ 0,
+ 0,
+ 0,
+ )
+}
+
+func (d *Device) CheckFormatSupport(format uint32) (uint32, error) {
var support uint32
r, _, _ := syscall.Syscall(
- d.vtbl.CheckFormatSupport,
+ d.Vtbl.CheckFormatSupport,
3,
uintptr(unsafe.Pointer(d)),
uintptr(format),
uintptr(unsafe.Pointer(&support)),
)
if r != 0 {
- return 0, ErrorCode{Name: "ID3D11DeviceCheckFormatSupport", Code: uint32(r)}
+ return 0, ErrorCode{Name: "DeviceCheckFormatSupport", Code: uint32(r)}
}
return support, nil
}
-func (d *_ID3D11Device) CreateBuffer(desc *_D3D11_BUFFER_DESC, data []byte) (*_ID3D11Buffer, error) {
- var dataDesc *_D3D11_SUBRESOURCE_DATA
+func (d *Device) CreateBuffer(desc *BUFFER_DESC, data []byte) (*Buffer, error) {
+ var dataDesc *SUBRESOURCE_DATA
if len(data) > 0 {
- dataDesc = &_D3D11_SUBRESOURCE_DATA{
+ dataDesc = &SUBRESOURCE_DATA{
pSysMem: &data[0],
}
}
- var buf *_ID3D11Buffer
+ var buf *Buffer
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateBuffer,
+ d.Vtbl.CreateBuffer,
4,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(desc)),
@@ -723,15 +857,15 @@ func (d *_ID3D11Device) CreateBuffer(desc *_D3D11_BUFFER_DESC, data []byte) (*_I
0, 0,
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateBuffer", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateBuffer", Code: uint32(r)}
}
return buf, nil
}
-func (d *_ID3D11Device) CreateDepthStencilViewTEX2D(res *_ID3D11Resource, desc *_D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D) (*_ID3D11DepthStencilView, error) {
- var view *_ID3D11DepthStencilView
+func (d *Device) CreateDepthStencilViewTEX2D(res *Resource, desc *DEPTH_STENCIL_VIEW_DESC_TEX2D) (*DepthStencilView, error) {
+ var view *DepthStencilView
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateDepthStencilView,
+ d.Vtbl.CreateDepthStencilView,
4,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(res)),
@@ -740,15 +874,33 @@ func (d *_ID3D11Device) CreateDepthStencilViewTEX2D(res *_ID3D11Resource, desc *
0, 0,
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateDepthStencilView", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateDepthStencilView", Code: uint32(r)}
}
return view, nil
}
-func (d *_ID3D11Device) CreatePixelShader(bytecode []byte) (*_ID3D11PixelShader, error) {
- var shader *_ID3D11PixelShader
+func (d *Device) CreatePixelShader(bytecode []byte) (*PixelShader, error) {
+ var shader *PixelShader
+ r, _, _ := syscall.Syscall6(
+ d.Vtbl.CreatePixelShader,
+ 5,
+ uintptr(unsafe.Pointer(d)),
+ uintptr(unsafe.Pointer(&bytecode[0])),
+ uintptr(len(bytecode)),
+ 0, // pClassLinkage
+ uintptr(unsafe.Pointer(&shader)),
+ 0,
+ )
+ if r != 0 {
+ return nil, ErrorCode{Name: "DeviceCreatePixelShader", Code: uint32(r)}
+ }
+ return shader, nil
+}
+
+func (d *Device) CreateVertexShader(bytecode []byte) (*VertexShader, error) {
+ var shader *VertexShader
r, _, _ := syscall.Syscall6(
- d.vtbl.CreatePixelShader,
+ d.Vtbl.CreateVertexShader,
5,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(&bytecode[0])),
@@ -758,15 +910,15 @@ func (d *_ID3D11Device) CreatePixelShader(bytecode []byte) (*_ID3D11PixelShader,
0,
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreatePixelShader", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateVertexShader", Code: uint32(r)}
}
return shader, nil
}
-func (d *_ID3D11Device) CreateVertexShader(bytecode []byte) (*_ID3D11VertexShader, error) {
- var shader *_ID3D11VertexShader
+func (d *Device) CreateComputeShader(bytecode []byte) (*ComputeShader, error) {
+ var shader *ComputeShader
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateVertexShader,
+ d.Vtbl.CreateComputeShader,
5,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(&bytecode[0])),
@@ -776,51 +928,68 @@ func (d *_ID3D11Device) CreateVertexShader(bytecode []byte) (*_ID3D11VertexShade
0,
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateVertexShader", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateComputeShader", Code: uint32(r)}
}
return shader, nil
}
-func (d *_ID3D11Device) CreateShaderResourceViewTEX2D(res *_ID3D11Resource, desc *_D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D) (*_ID3D11ShaderResourceView, error) {
- var resView *_ID3D11ShaderResourceView
+func (d *Device) CreateShaderResourceView(res *Resource, desc unsafe.Pointer) (*ShaderResourceView, error) {
+ var resView *ShaderResourceView
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateShaderResourceView,
+ d.Vtbl.CreateShaderResourceView,
4,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(res)),
- uintptr(unsafe.Pointer(desc)),
+ uintptr(desc),
uintptr(unsafe.Pointer(&resView)),
0, 0,
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateShaderResourceView", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateShaderResourceView", Code: uint32(r)}
}
return resView, nil
}
-func (d *_ID3D11Device) CreateRasterizerState(desc *_D3D11_RASTERIZER_DESC) (*_ID3D11RasterizerState, error) {
- var state *_ID3D11RasterizerState
+func (d *Device) CreateUnorderedAccessView(res *Resource, desc unsafe.Pointer) (*UnorderedAccessView, error) {
+ var uaView *UnorderedAccessView
+ r, _, _ := syscall.Syscall6(
+ d.Vtbl.CreateUnorderedAccessView,
+ 4,
+ uintptr(unsafe.Pointer(d)),
+ uintptr(unsafe.Pointer(res)),
+ uintptr(desc),
+ uintptr(unsafe.Pointer(&uaView)),
+ 0, 0,
+ )
+ if r != 0 {
+ return nil, ErrorCode{Name: "DeviceCreateUnorderedAccessView", Code: uint32(r)}
+ }
+ return uaView, nil
+}
+
+func (d *Device) CreateRasterizerState(desc *RASTERIZER_DESC) (*RasterizerState, error) {
+ var state *RasterizerState
r, _, _ := syscall.Syscall(
- d.vtbl.CreateRasterizerState,
+ d.Vtbl.CreateRasterizerState,
3,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(desc)),
uintptr(unsafe.Pointer(&state)),
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateRasterizerState", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateRasterizerState", Code: uint32(r)}
}
return state, nil
}
-func (d *_ID3D11Device) CreateInputLayout(descs []_D3D11_INPUT_ELEMENT_DESC, bytecode []byte) (*_ID3D11InputLayout, error) {
- var pdesc *_D3D11_INPUT_ELEMENT_DESC
+func (d *Device) CreateInputLayout(descs []INPUT_ELEMENT_DESC, bytecode []byte) (*InputLayout, error) {
+ var pdesc *INPUT_ELEMENT_DESC
if len(descs) > 0 {
pdesc = &descs[0]
}
- var layout *_ID3D11InputLayout
+ var layout *InputLayout
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateInputLayout,
+ d.Vtbl.CreateInputLayout,
6,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(pdesc)),
@@ -830,30 +999,30 @@ func (d *_ID3D11Device) CreateInputLayout(descs []_D3D11_INPUT_ELEMENT_DESC, byt
uintptr(unsafe.Pointer(&layout)),
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateInputLayout", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateInputLayout", Code: uint32(r)}
}
return layout, nil
}
-func (d *_ID3D11Device) CreateSamplerState(desc *_D3D11_SAMPLER_DESC) (*_ID3D11SamplerState, error) {
- var sampler *_ID3D11SamplerState
+func (d *Device) CreateSamplerState(desc *SAMPLER_DESC) (*SamplerState, error) {
+ var sampler *SamplerState
r, _, _ := syscall.Syscall(
- d.vtbl.CreateSamplerState,
+ d.Vtbl.CreateSamplerState,
3,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(desc)),
uintptr(unsafe.Pointer(&sampler)),
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateSamplerState", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateSamplerState", Code: uint32(r)}
}
return sampler, nil
}
-func (d *_ID3D11Device) CreateTexture2D(desc *_D3D11_TEXTURE2D_DESC) (*_ID3D11Texture2D, error) {
- var tex *_ID3D11Texture2D
+func (d *Device) CreateTexture2D(desc *TEXTURE2D_DESC) (*Texture2D, error) {
+ var tex *Texture2D
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateTexture2D,
+ d.Vtbl.CreateTexture2D,
4,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(desc)),
@@ -862,15 +1031,15 @@ func (d *_ID3D11Device) CreateTexture2D(desc *_D3D11_TEXTURE2D_DESC) (*_ID3D11Te
0, 0,
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11CreateTexture2D", Code: uint32(r)}
+ return nil, ErrorCode{Name: "CreateTexture2D", Code: uint32(r)}
}
return tex, nil
}
-func (d *_ID3D11Device) CreateRenderTargetView(res *_ID3D11Resource) (*_ID3D11RenderTargetView, error) {
- var target *_ID3D11RenderTargetView
+func (d *Device) CreateRenderTargetView(res *Resource) (*RenderTargetView, error) {
+ var target *RenderTargetView
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateRenderTargetView,
+ d.Vtbl.CreateRenderTargetView,
4,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(res)),
@@ -879,59 +1048,102 @@ func (d *_ID3D11Device) CreateRenderTargetView(res *_ID3D11Resource) (*_ID3D11Re
0, 0,
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateRenderTargetView", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateRenderTargetView", Code: uint32(r)}
}
return target, nil
}
-func (d *_ID3D11Device) CreateBlendState(desc *_D3D11_BLEND_DESC) (*_ID3D11BlendState, error) {
- var state *_ID3D11BlendState
+func (d *Device) CreateBlendState(desc *BLEND_DESC) (*BlendState, error) {
+ var state *BlendState
r, _, _ := syscall.Syscall(
- d.vtbl.CreateBlendState,
+ d.Vtbl.CreateBlendState,
3,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(desc)),
uintptr(unsafe.Pointer(&state)),
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateBlendState", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateBlendState", Code: uint32(r)}
}
return state, nil
}
-func (d *_ID3D11Device) CreateDepthStencilState(desc *_D3D11_DEPTH_STENCIL_DESC) (*_ID3D11DepthStencilState, error) {
- var state *_ID3D11DepthStencilState
+func (d *Device) CreateDepthStencilState(desc *DEPTH_STENCIL_DESC) (*DepthStencilState, error) {
+ var state *DepthStencilState
r, _, _ := syscall.Syscall(
- d.vtbl.CreateDepthStencilState,
+ d.Vtbl.CreateDepthStencilState,
3,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(desc)),
uintptr(unsafe.Pointer(&state)),
)
if r != 0 {
- return nil, ErrorCode{Name: "ID3D11DeviceCreateDepthStencilState", Code: uint32(r)}
+ return nil, ErrorCode{Name: "DeviceCreateDepthStencilState", Code: uint32(r)}
}
return state, nil
}
-func (s *_IDXGISwapChain) GetDesc() (_DXGI_SWAP_CHAIN_DESC, error) {
- var desc _DXGI_SWAP_CHAIN_DESC
+func (d *Device) GetFeatureLevel() int {
+ lvl, _, _ := syscall.Syscall(
+ d.Vtbl.GetFeatureLevel,
+ 1,
+ uintptr(unsafe.Pointer(d)),
+ 0, 0,
+ )
+ return int(lvl)
+}
+
+func (d *Device) GetImmediateContext() *DeviceContext {
+ var ctx *DeviceContext
+ syscall.Syscall(
+ d.Vtbl.GetImmediateContext,
+ 2,
+ uintptr(unsafe.Pointer(d)),
+ uintptr(unsafe.Pointer(&ctx)),
+ 0,
+ )
+ return ctx
+}
+
+func (d *Device) ReportLiveDeviceObjects() error {
+ intf, err := IUnknownQueryInterface(unsafe.Pointer(d), d.Vtbl.QueryInterface, &IID_ID3D11Debug)
+ if err != nil {
+ return fmt.Errorf("ReportLiveObjects: failed to query ID3D11Debug interface: %v", err)
+ }
+ defer IUnknownRelease(unsafe.Pointer(intf), intf.Vtbl.Release)
+ dbg := (*Debug)(unsafe.Pointer(intf))
+ dbg.ReportLiveDeviceObjects(RLDO_DETAIL | RLDO_IGNORE_INTERNAL)
+ return nil
+}
+
+func (d *Debug) ReportLiveDeviceObjects(flags uint32) {
+ syscall.Syscall(
+ d.Vtbl.ReportLiveDeviceObjects,
+ 2,
+ uintptr(unsafe.Pointer(d)),
+ uintptr(flags),
+ 0,
+ )
+}
+
+func (s *IDXGISwapChain) GetDesc() (DXGI_SWAP_CHAIN_DESC, error) {
+ var desc DXGI_SWAP_CHAIN_DESC
r, _, _ := syscall.Syscall(
- s.vtbl.GetDesc,
+ s.Vtbl.GetDesc,
2,
uintptr(unsafe.Pointer(s)),
uintptr(unsafe.Pointer(&desc)),
0,
)
if r != 0 {
- return _DXGI_SWAP_CHAIN_DESC{}, ErrorCode{Name: "IDXGISwapChainGetDesc", Code: uint32(r)}
+ return DXGI_SWAP_CHAIN_DESC{}, ErrorCode{Name: "IDXGISwapChainGetDesc", Code: uint32(r)}
}
return desc, nil
}
-func (s *_IDXGISwapChain) ResizeBuffers(buffers, width, height, newFormat, flags uint32) error {
+func (s *IDXGISwapChain) ResizeBuffers(buffers, width, height, newFormat, flags uint32) error {
r, _, _ := syscall.Syscall6(
- s.vtbl.ResizeBuffers,
+ s.Vtbl.ResizeBuffers,
6,
uintptr(unsafe.Pointer(s)),
uintptr(buffers),
@@ -946,9 +1158,9 @@ func (s *_IDXGISwapChain) ResizeBuffers(buffers, width, height, newFormat, flags
return nil
}
-func (s *_IDXGISwapChain) Present(SyncInterval int, Flags uint32) error {
+func (s *IDXGISwapChain) Present(SyncInterval int, Flags uint32) error {
r, _, _ := syscall.Syscall(
- s.vtbl.Present,
+ s.Vtbl.Present,
3,
uintptr(unsafe.Pointer(s)),
uintptr(SyncInterval),
@@ -960,10 +1172,10 @@ func (s *_IDXGISwapChain) Present(SyncInterval int, Flags uint32) error {
return nil
}
-func (s *_IDXGISwapChain) GetBuffer(index int, riid *_GUID) (*_IUnknown, error) {
- var buf *_IUnknown
+func (s *IDXGISwapChain) GetBuffer(index int, riid *GUID) (*IUnknown, error) {
+ var buf *IUnknown
r, _, _ := syscall.Syscall6(
- s.vtbl.GetBuffer,
+ s.Vtbl.GetBuffer,
4,
uintptr(unsafe.Pointer(s)),
uintptr(index),
@@ -978,9 +1190,9 @@ func (s *_IDXGISwapChain) GetBuffer(index int, riid *_GUID) (*_IUnknown, error)
return buf, nil
}
-func (c *_ID3D11DeviceContext) Unmap(resource *_ID3D11Resource, subResource uint32) {
+func (c *DeviceContext) Unmap(resource *Resource, subResource uint32) {
syscall.Syscall(
- c.vtbl.Unmap,
+ c.Vtbl.Unmap,
3,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(resource)),
@@ -988,10 +1200,10 @@ func (c *_ID3D11DeviceContext) Unmap(resource *_ID3D11Resource, subResource uint
)
}
-func (c *_ID3D11DeviceContext) Map(resource *_ID3D11Resource, subResource, mapType, mapFlags uint32) (_D3D11_MAPPED_SUBRESOURCE, error) {
- var resMap _D3D11_MAPPED_SUBRESOURCE
+func (c *DeviceContext) Map(resource *Resource, subResource, mapType, mapFlags uint32) (MAPPED_SUBRESOURCE, error) {
+ var resMap MAPPED_SUBRESOURCE
r, _, _ := syscall.Syscall6(
- c.vtbl.Map,
+ c.Vtbl.Map,
6,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(resource)),
@@ -1001,14 +1213,14 @@ func (c *_ID3D11DeviceContext) Map(resource *_ID3D11Resource, subResource, mapTy
uintptr(unsafe.Pointer(&resMap)),
)
if r != 0 {
- return resMap, ErrorCode{Name: "ID3D11DeviceContextMap", Code: uint32(r)}
+ return resMap, ErrorCode{Name: "DeviceContextMap", Code: uint32(r)}
}
return resMap, nil
}
-func (c *_ID3D11DeviceContext) CopySubresourceRegion(dst *_ID3D11Resource, dstSubresource, dstX, dstY, dstZ uint32, src *_ID3D11Resource, srcSubresource uint32, srcBox *_D3D11_BOX) {
+func (c *DeviceContext) CopySubresourceRegion(dst *Resource, dstSubresource, dstX, dstY, dstZ uint32, src *Resource, srcSubresource uint32, srcBox *BOX) {
syscall.Syscall9(
- c.vtbl.CopySubresourceRegion,
+ c.Vtbl.CopySubresourceRegion,
9,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(dst)),
@@ -1022,9 +1234,9 @@ func (c *_ID3D11DeviceContext) CopySubresourceRegion(dst *_ID3D11Resource, dstSu
)
}
-func (c *_ID3D11DeviceContext) ClearDepthStencilView(target *_ID3D11DepthStencilView, flags uint32, depth float32, stencil uint8) {
+func (c *DeviceContext) ClearDepthStencilView(target *DepthStencilView, flags uint32, depth float32, stencil uint8) {
syscall.Syscall6(
- c.vtbl.ClearDepthStencilView,
+ c.Vtbl.ClearDepthStencilView,
5,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(target)),
@@ -1035,9 +1247,9 @@ func (c *_ID3D11DeviceContext) ClearDepthStencilView(target *_ID3D11DepthStencil
)
}
-func (c *_ID3D11DeviceContext) ClearRenderTargetView(target *_ID3D11RenderTargetView, color *[4]float32) {
+func (c *DeviceContext) ClearRenderTargetView(target *RenderTargetView, color *[4]float32) {
syscall.Syscall(
- c.vtbl.ClearRenderTargetView,
+ c.Vtbl.ClearRenderTargetView,
3,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(target)),
@@ -1045,9 +1257,45 @@ func (c *_ID3D11DeviceContext) ClearRenderTargetView(target *_ID3D11RenderTarget
)
}
-func (c *_ID3D11DeviceContext) RSSetViewports(viewport *_D3D11_VIEWPORT) {
+func (c *DeviceContext) CSSetShaderResources(startSlot uint32, s *ShaderResourceView) {
+ syscall.Syscall6(
+ c.Vtbl.CSSetShaderResources,
+ 4,
+ uintptr(unsafe.Pointer(c)),
+ uintptr(startSlot),
+ 1, // NumViews
+ uintptr(unsafe.Pointer(&s)),
+ 0, 0,
+ )
+}
+
+func (c *DeviceContext) CSSetUnorderedAccessViews(startSlot uint32, v *UnorderedAccessView) {
+ syscall.Syscall6(
+ c.Vtbl.CSSetUnorderedAccessViews,
+ 4,
+ uintptr(unsafe.Pointer(c)),
+ uintptr(startSlot),
+ 1, // NumViews
+ uintptr(unsafe.Pointer(&v)),
+ 0, 0,
+ )
+}
+
+func (c *DeviceContext) CSSetShader(s *ComputeShader) {
+ syscall.Syscall6(
+ c.Vtbl.CSSetShader,
+ 4,
+ uintptr(unsafe.Pointer(c)),
+ uintptr(unsafe.Pointer(s)),
+ 0, // ppClassInstances
+ 0, // NumClassInstances
+ 0, 0,
+ )
+}
+
+func (c *DeviceContext) RSSetViewports(viewport *VIEWPORT) {
syscall.Syscall(
- c.vtbl.RSSetViewports,
+ c.Vtbl.RSSetViewports,
3,
uintptr(unsafe.Pointer(c)),
1, // NumViewports
@@ -1055,9 +1303,9 @@ func (c *_ID3D11DeviceContext) RSSetViewports(viewport *_D3D11_VIEWPORT) {
)
}
-func (c *_ID3D11DeviceContext) VSSetShader(s *_ID3D11VertexShader) {
+func (c *DeviceContext) VSSetShader(s *VertexShader) {
syscall.Syscall6(
- c.vtbl.VSSetShader,
+ c.Vtbl.VSSetShader,
4,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(s)),
@@ -1067,9 +1315,9 @@ func (c *_ID3D11DeviceContext) VSSetShader(s *_ID3D11VertexShader) {
)
}
-func (c *_ID3D11DeviceContext) VSSetConstantBuffers(b *_ID3D11Buffer) {
+func (c *DeviceContext) VSSetConstantBuffers(b *Buffer) {
syscall.Syscall6(
- c.vtbl.VSSetConstantBuffers,
+ c.Vtbl.VSSetConstantBuffers,
4,
uintptr(unsafe.Pointer(c)),
0, // StartSlot
@@ -1079,9 +1327,9 @@ func (c *_ID3D11DeviceContext) VSSetConstantBuffers(b *_ID3D11Buffer) {
)
}
-func (c *_ID3D11DeviceContext) PSSetConstantBuffers(b *_ID3D11Buffer) {
+func (c *DeviceContext) PSSetConstantBuffers(b *Buffer) {
syscall.Syscall6(
- c.vtbl.PSSetConstantBuffers,
+ c.Vtbl.PSSetConstantBuffers,
4,
uintptr(unsafe.Pointer(c)),
0, // StartSlot
@@ -1091,9 +1339,9 @@ func (c *_ID3D11DeviceContext) PSSetConstantBuffers(b *_ID3D11Buffer) {
)
}
-func (c *_ID3D11DeviceContext) PSSetShaderResources(startSlot uint32, s *_ID3D11ShaderResourceView) {
+func (c *DeviceContext) PSSetShaderResources(startSlot uint32, s *ShaderResourceView) {
syscall.Syscall6(
- c.vtbl.PSSetShaderResources,
+ c.Vtbl.PSSetShaderResources,
4,
uintptr(unsafe.Pointer(c)),
uintptr(startSlot),
@@ -1103,9 +1351,9 @@ func (c *_ID3D11DeviceContext) PSSetShaderResources(startSlot uint32, s *_ID3D11
)
}
-func (c *_ID3D11DeviceContext) PSSetSamplers(startSlot uint32, s *_ID3D11SamplerState) {
+func (c *DeviceContext) PSSetSamplers(startSlot uint32, s *SamplerState) {
syscall.Syscall6(
- c.vtbl.PSSetSamplers,
+ c.Vtbl.PSSetSamplers,
4,
uintptr(unsafe.Pointer(c)),
uintptr(startSlot),
@@ -1115,9 +1363,9 @@ func (c *_ID3D11DeviceContext) PSSetSamplers(startSlot uint32, s *_ID3D11Sampler
)
}
-func (c *_ID3D11DeviceContext) PSSetShader(s *_ID3D11PixelShader) {
+func (c *DeviceContext) PSSetShader(s *PixelShader) {
syscall.Syscall6(
- c.vtbl.PSSetShader,
+ c.Vtbl.PSSetShader,
4,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(s)),
@@ -1127,14 +1375,14 @@ func (c *_ID3D11DeviceContext) PSSetShader(s *_ID3D11PixelShader) {
)
}
-func (c *_ID3D11DeviceContext) UpdateSubresource(res *_ID3D11Resource, rowPitch, depthPitch uint32, data []byte) {
+func (c *DeviceContext) UpdateSubresource(res *Resource, dstBox *BOX, rowPitch, depthPitch uint32, data []byte) {
syscall.Syscall9(
- c.vtbl.UpdateSubresource,
+ c.Vtbl.UpdateSubresource,
7,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(res)),
0, // DstSubresource
- 0, // pDstBox
+ uintptr(unsafe.Pointer(dstBox)),
uintptr(unsafe.Pointer(&data[0])),
uintptr(rowPitch),
uintptr(depthPitch),
@@ -1142,9 +1390,9 @@ func (c *_ID3D11DeviceContext) UpdateSubresource(res *_ID3D11Resource, rowPitch,
)
}
-func (c *_ID3D11DeviceContext) RSSetState(state *_ID3D11RasterizerState) {
+func (c *DeviceContext) RSSetState(state *RasterizerState) {
syscall.Syscall(
- c.vtbl.RSSetState,
+ c.Vtbl.RSSetState,
2,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(state)),
@@ -1152,9 +1400,9 @@ func (c *_ID3D11DeviceContext) RSSetState(state *_ID3D11RasterizerState) {
)
}
-func (c *_ID3D11DeviceContext) IASetInputLayout(layout *_ID3D11InputLayout) {
+func (c *DeviceContext) IASetInputLayout(layout *InputLayout) {
syscall.Syscall(
- c.vtbl.IASetInputLayout,
+ c.Vtbl.IASetInputLayout,
2,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(layout)),
@@ -1162,9 +1410,9 @@ func (c *_ID3D11DeviceContext) IASetInputLayout(layout *_ID3D11InputLayout) {
)
}
-func (c *_ID3D11DeviceContext) IASetIndexBuffer(buf *_ID3D11Buffer, format, offset uint32) {
+func (c *DeviceContext) IASetIndexBuffer(buf *Buffer, format, offset uint32) {
syscall.Syscall6(
- c.vtbl.IASetIndexBuffer,
+ c.Vtbl.IASetIndexBuffer,
4,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(buf)),
@@ -1174,9 +1422,9 @@ func (c *_ID3D11DeviceContext) IASetIndexBuffer(buf *_ID3D11Buffer, format, offs
)
}
-func (c *_ID3D11DeviceContext) IASetVertexBuffers(buf *_ID3D11Buffer, stride, offset uint32) {
+func (c *DeviceContext) IASetVertexBuffers(buf *Buffer, stride, offset uint32) {
syscall.Syscall6(
- c.vtbl.IASetVertexBuffers,
+ c.Vtbl.IASetVertexBuffers,
6,
uintptr(unsafe.Pointer(c)),
0, // StartSlot
@@ -1187,9 +1435,9 @@ func (c *_ID3D11DeviceContext) IASetVertexBuffers(buf *_ID3D11Buffer, stride, of
)
}
-func (c *_ID3D11DeviceContext) IASetPrimitiveTopology(mode uint32) {
+func (c *DeviceContext) IASetPrimitiveTopology(mode uint32) {
syscall.Syscall(
- c.vtbl.IASetPrimitiveTopology,
+ c.Vtbl.IASetPrimitiveTopology,
2,
uintptr(unsafe.Pointer(c)),
uintptr(mode),
@@ -1197,23 +1445,26 @@ func (c *_ID3D11DeviceContext) IASetPrimitiveTopology(mode uint32) {
)
}
-func (c *_ID3D11DeviceContext) OMGetRenderTargets() *_ID3D11RenderTargetView {
- var target *_ID3D11RenderTargetView
+func (c *DeviceContext) OMGetRenderTargets() (*RenderTargetView, *DepthStencilView) {
+ var (
+ target *RenderTargetView
+ depthStencilView *DepthStencilView
+ )
syscall.Syscall6(
- c.vtbl.OMGetRenderTargets,
+ c.Vtbl.OMGetRenderTargets,
4,
uintptr(unsafe.Pointer(c)),
1, // NumViews
uintptr(unsafe.Pointer(&target)),
- 0, // pDepthStencilView
+ uintptr(unsafe.Pointer(&depthStencilView)),
0, 0,
)
- return target
+ return target, depthStencilView
}
-func (c *_ID3D11DeviceContext) OMSetRenderTargets(target *_ID3D11RenderTargetView, depthStencil *_ID3D11DepthStencilView) {
+func (c *DeviceContext) OMSetRenderTargets(target *RenderTargetView, depthStencil *DepthStencilView) {
syscall.Syscall6(
- c.vtbl.OMSetRenderTargets,
+ c.Vtbl.OMSetRenderTargets,
4,
uintptr(unsafe.Pointer(c)),
1, // NumViews
@@ -1223,9 +1474,9 @@ func (c *_ID3D11DeviceContext) OMSetRenderTargets(target *_ID3D11RenderTargetVie
)
}
-func (c *_ID3D11DeviceContext) Draw(count, start uint32) {
+func (c *DeviceContext) Draw(count, start uint32) {
syscall.Syscall(
- c.vtbl.Draw,
+ c.Vtbl.Draw,
3,
uintptr(unsafe.Pointer(c)),
uintptr(count),
@@ -1233,9 +1484,9 @@ func (c *_ID3D11DeviceContext) Draw(count, start uint32) {
)
}
-func (c *_ID3D11DeviceContext) DrawIndexed(count, start uint32, base int32) {
+func (c *DeviceContext) DrawIndexed(count, start uint32, base int32) {
syscall.Syscall6(
- c.vtbl.DrawIndexed,
+ c.Vtbl.DrawIndexed,
4,
uintptr(unsafe.Pointer(c)),
uintptr(count),
@@ -1245,9 +1496,21 @@ func (c *_ID3D11DeviceContext) DrawIndexed(count, start uint32, base int32) {
)
}
-func (c *_ID3D11DeviceContext) OMSetBlendState(state *_ID3D11BlendState, factor *f32color.RGBA, sampleMask uint32) {
+func (c *DeviceContext) Dispatch(x, y, z uint32) {
+ syscall.Syscall6(
+ c.Vtbl.Dispatch,
+ 4,
+ uintptr(unsafe.Pointer(c)),
+ uintptr(x),
+ uintptr(y),
+ uintptr(z),
+ 0, 0,
+ )
+}
+
+func (c *DeviceContext) OMSetBlendState(state *BlendState, factor *f32color.RGBA, sampleMask uint32) {
syscall.Syscall6(
- c.vtbl.OMSetBlendState,
+ c.Vtbl.OMSetBlendState,
4,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(state)),
@@ -1257,9 +1520,9 @@ func (c *_ID3D11DeviceContext) OMSetBlendState(state *_ID3D11BlendState, factor
)
}
-func (c *_ID3D11DeviceContext) OMSetDepthStencilState(state *_ID3D11DepthStencilState, stencilRef uint32) {
+func (c *DeviceContext) OMSetDepthStencilState(state *DepthStencilState, stencilRef uint32) {
syscall.Syscall(
- c.vtbl.OMSetDepthStencilState,
+ c.Vtbl.OMSetDepthStencilState,
3,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(state)),
@@ -1267,10 +1530,10 @@ func (c *_ID3D11DeviceContext) OMSetDepthStencilState(state *_ID3D11DepthStencil
)
}
-func (d *_IDXGIObject) GetParent(guid *_GUID) (*_IDXGIObject, error) {
- var parent *_IDXGIObject
+func (d *IDXGIObject) GetParent(guid *GUID) (*IDXGIObject, error) {
+ var parent *IDXGIObject
r, _, _ := syscall.Syscall(
- d.vtbl.GetParent,
+ d.Vtbl.GetParent,
3,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(guid)),
@@ -1282,10 +1545,10 @@ func (d *_IDXGIObject) GetParent(guid *_GUID) (*_IDXGIObject, error) {
return parent, nil
}
-func (d *_IDXGIFactory) CreateSwapChain(device *_IUnknown, desc *_DXGI_SWAP_CHAIN_DESC) (*_IDXGISwapChain, error) {
- var swchain *_IDXGISwapChain
+func (d *IDXGIFactory) CreateSwapChain(device *IUnknown, desc *DXGI_SWAP_CHAIN_DESC) (*IDXGISwapChain, error) {
+ var swchain *IDXGISwapChain
r, _, _ := syscall.Syscall6(
- d.vtbl.CreateSwapChain,
+ d.Vtbl.CreateSwapChain,
4,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(device)),
@@ -1299,10 +1562,10 @@ func (d *_IDXGIFactory) CreateSwapChain(device *_IUnknown, desc *_DXGI_SWAP_CHAI
return swchain, nil
}
-func (d *_IDXGIDevice) GetAdapter() (*_IDXGIAdapter, error) {
- var adapter *_IDXGIAdapter
+func (d *IDXGIDevice) GetAdapter() (*IDXGIAdapter, error) {
+ var adapter *IDXGIAdapter
r, _, _ := syscall.Syscall(
- d.vtbl.GetAdapter,
+ d.Vtbl.GetAdapter,
2,
uintptr(unsafe.Pointer(d)),
uintptr(unsafe.Pointer(&adapter)),
@@ -1314,8 +1577,8 @@ func (d *_IDXGIDevice) GetAdapter() (*_IDXGIAdapter, error) {
return adapter, nil
}
-func _IUnknownQueryInterface(obj unsafe.Pointer, queryInterfaceMethod uintptr, guid *_GUID) (*_IUnknown, error) {
- var ref *_IUnknown
+func IUnknownQueryInterface(obj unsafe.Pointer, queryInterfaceMethod uintptr, guid *GUID) (*IUnknown, error) {
+ var ref *IUnknown
r, _, _ := syscall.Syscall(
queryInterfaceMethod,
3,
@@ -1329,7 +1592,17 @@ func _IUnknownQueryInterface(obj unsafe.Pointer, queryInterfaceMethod uintptr, g
return ref, nil
}
-func _IUnknownRelease(obj unsafe.Pointer, releaseMethod uintptr) {
+func IUnknownAddRef(obj unsafe.Pointer, addRefMethod uintptr) {
+ syscall.Syscall(
+ addRefMethod,
+ 1,
+ uintptr(obj),
+ 0,
+ 0,
+ )
+}
+
+func IUnknownRelease(obj unsafe.Pointer, releaseMethod uintptr) {
syscall.Syscall(
releaseMethod,
1,
@@ -1342,3 +1615,68 @@ func _IUnknownRelease(obj unsafe.Pointer, releaseMethod uintptr) {
func (e ErrorCode) Error() string {
return fmt.Sprintf("%s: %#x", e.Name, e.Code)
}
+
+func CreateSwapChain(dev *Device, hwnd windows.Handle) (*IDXGISwapChain, error) {
+ dxgiDev, err := IUnknownQueryInterface(unsafe.Pointer(dev), dev.Vtbl.QueryInterface, &IID_IDXGIDevice)
+ if err != nil {
+ return nil, fmt.Errorf("NewContext: %v", err)
+ }
+ adapter, err := (*IDXGIDevice)(unsafe.Pointer(dxgiDev)).GetAdapter()
+ IUnknownRelease(unsafe.Pointer(dxgiDev), dxgiDev.Vtbl.Release)
+ if err != nil {
+ return nil, fmt.Errorf("NewContext: %v", err)
+ }
+ dxgiFactory, err := (*IDXGIObject)(unsafe.Pointer(adapter)).GetParent(&IID_IDXGIFactory)
+ IUnknownRelease(unsafe.Pointer(adapter), adapter.Vtbl.Release)
+ if err != nil {
+ return nil, fmt.Errorf("NewContext: %v", err)
+ }
+ swchain, err := (*IDXGIFactory)(unsafe.Pointer(dxgiFactory)).CreateSwapChain(
+ (*IUnknown)(unsafe.Pointer(dev)),
+ &DXGI_SWAP_CHAIN_DESC{
+ BufferDesc: DXGI_MODE_DESC{
+ Format: DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
+ },
+ SampleDesc: DXGI_SAMPLE_DESC{
+ Count: 1,
+ },
+ BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
+ BufferCount: 1,
+ OutputWindow: hwnd,
+ Windowed: 1,
+ SwapEffect: DXGI_SWAP_EFFECT_DISCARD,
+ },
+ )
+ IUnknownRelease(unsafe.Pointer(dxgiFactory), dxgiFactory.Vtbl.Release)
+ if err != nil {
+ return nil, fmt.Errorf("NewContext: %v", err)
+ }
+ return swchain, nil
+}
+
+func CreateDepthView(d *Device, width, height, depthBits int) (*DepthStencilView, error) {
+ depthTex, err := d.CreateTexture2D(&TEXTURE2D_DESC{
+ Width: uint32(width),
+ Height: uint32(height),
+ MipLevels: 1,
+ ArraySize: 1,
+ Format: DXGI_FORMAT_D24_UNORM_S8_UINT,
+ SampleDesc: DXGI_SAMPLE_DESC{
+ Count: 1,
+ Quality: 0,
+ },
+ BindFlags: BIND_DEPTH_STENCIL,
+ })
+ if err != nil {
+ return nil, err
+ }
+ depthView, err := d.CreateDepthStencilViewTEX2D(
+ (*Resource)(unsafe.Pointer(depthTex)),
+ &DEPTH_STENCIL_VIEW_DESC_TEX2D{
+ Format: DXGI_FORMAT_D24_UNORM_S8_UINT,
+ ViewDimension: DSV_DIMENSION_TEXTURE2D,
+ },
+ )
+ IUnknownRelease(unsafe.Pointer(depthTex), depthTex.Vtbl.Release)
+ return depthView, err
+}
diff --git a/vendor/gioui.org/app/internal/egl/egl.go b/vendor/gioui.org/internal/egl/egl.go
index f1d2595..7d7b551 100644
--- a/vendor/gioui.org/app/internal/egl/egl.go
+++ b/vendor/gioui.org/internal/egl/egl.go
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build linux || windows || freebsd || openbsd
// +build linux windows freebsd openbsd
package egl
@@ -10,21 +11,14 @@ import (
"runtime"
"strings"
- "gioui.org/app/internal/glimpl"
- "gioui.org/app/internal/srgb"
- "gioui.org/gpu/backend"
- "gioui.org/gpu/gl"
+ "gioui.org/gpu"
)
type Context struct {
- c *glimpl.Functions
disp _EGLDisplay
eglCtx *eglContext
eglSurf _EGLSurface
width, height int
- refreshFBO bool
- // For sRGB emulation.
- srgbFBO *srgb.FBO
}
type eglContext struct {
@@ -63,30 +57,18 @@ const (
)
func (c *Context) Release() {
- if c.srgbFBO != nil {
- c.srgbFBO.Release()
- c.srgbFBO = nil
- }
c.ReleaseSurface()
if c.eglCtx != nil {
eglDestroyContext(c.disp, c.eglCtx.ctx)
- eglTerminate(c.disp)
- eglReleaseThread()
c.eglCtx = nil
}
c.disp = nilEGLDisplay
}
func (c *Context) Present() error {
- if c.srgbFBO != nil {
- c.srgbFBO.Blit()
- }
if !eglSwapBuffers(c.disp, c.eglSurf) {
return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
}
- if c.srgbFBO != nil {
- c.srgbFBO.AfterPresent()
- }
return nil
}
@@ -111,17 +93,16 @@ func NewContext(disp NativeDisplayType) (*Context, error) {
c := &Context{
disp: eglDisp,
eglCtx: eglCtx,
- c: new(glimpl.Functions),
}
return c, nil
}
-func (c *Context) Functions() *glimpl.Functions {
- return c.c
+func (c *Context) RenderTarget() (gpu.RenderTarget, error) {
+ return gpu.OpenGLRenderTarget{}, nil
}
-func (c *Context) Backend() (backend.Device, error) {
- return gl.NewBackend(c.c)
+func (c *Context) API() gpu.API {
+ return gpu.OpenGL{}
}
func (c *Context) ReleaseSurface() {
@@ -129,7 +110,7 @@ func (c *Context) ReleaseSurface() {
return
}
// Make sure any in-flight GL commands are complete.
- c.c.Finish()
+ eglWaitClient()
c.ReleaseCurrent()
eglDestroySurface(c.disp, c.eglSurf)
c.eglSurf = nilEGLSurface
@@ -144,7 +125,6 @@ func (c *Context) CreateSurface(win NativeWindowType, width, height int) error {
c.eglSurf = eglSurf
c.width = width
c.height = height
- c.refreshFBO = true
return err
}
@@ -155,26 +135,15 @@ func (c *Context) ReleaseCurrent() {
}
func (c *Context) MakeCurrent() error {
+ // OpenGL contexts are implicit and thread-local. Lock the OS thread.
+ runtime.LockOSThread()
+
if c.eglSurf == nilEGLSurface && !c.eglCtx.surfaceless {
return errors.New("no surface created yet EGL_KHR_surfaceless_context is not supported")
}
if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) {
return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError())
}
- if c.eglCtx.srgb || c.eglSurf == nilEGLSurface {
- return nil
- }
- if c.srgbFBO == nil {
- var err error
- c.srgbFBO, err = srgb.New(c.c)
- if err != nil {
- return err
- }
- }
- if c.refreshFBO {
- c.refreshFBO = false
- return c.srgbFBO.Refresh(c.width, c.height)
- }
return nil
}
@@ -216,11 +185,9 @@ func createContext(disp _EGLDisplay) (*eglContext, error) {
// Some Mesa drivers crash if an sRGB framebuffer is requested without alpha.
// https://bugs.freedesktop.org/show_bug.cgi?id=107782.
//
- // Also, some Android devices (Samsung S9) needs alpha for sRGB to work.
- attribs = append(attribs, _EGL_ALPHA_SIZE, 1)
+ // Also, some Android devices (Samsung S9) need alpha for sRGB to work.
+ attribs = append(attribs, _EGL_ALPHA_SIZE, 8)
}
- // Only request a depth buffer if we're going to render directly to the framebuffer.
- attribs = append(attribs, _EGL_DEPTH_SIZE, 16)
}
attribs = append(attribs, _EGL_NONE)
eglCfg, ret := eglChooseConfig(disp, attribs)
@@ -228,11 +195,18 @@ func createContext(disp _EGLDisplay) (*eglContext, error) {
return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError())
}
if eglCfg == nilEGLConfig {
- return nil, errors.New("eglChooseConfig returned 0 configs")
+ supportsNoCfg := hasExtension(exts, "EGL_KHR_no_config_context")
+ if !supportsNoCfg {
+ return nil, errors.New("eglChooseConfig returned no configs")
+ }
}
- visID, ret := eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID)
- if !ret {
- return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed")
+ var visID _EGLint
+ if eglCfg != nilEGLConfig {
+ var ok bool
+ visID, ok = eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID)
+ if !ok {
+ return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed")
+ }
}
ctxAttribs := []_EGLint{
_EGL_CONTEXT_CLIENT_VERSION, 3,
@@ -267,7 +241,7 @@ func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (
surfAttribs = append(surfAttribs, _EGL_NONE)
eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
if eglSurf == nilEGLSurface && eglCtx.srgb {
- // Try again without sRGB
+ // Try again without sRGB.
eglCtx.srgb = false
surfAttribs = []_EGLint{_EGL_NONE}
eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
diff --git a/vendor/gioui.org/app/internal/egl/egl_unix.go b/vendor/gioui.org/internal/egl/egl_unix.go
index 0c6734f..bd3efa5 100644
--- a/vendor/gioui.org/app/internal/egl/egl_unix.go
+++ b/vendor/gioui.org/internal/egl/egl_unix.go
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build linux || freebsd || openbsd
// +build linux freebsd openbsd
package egl
@@ -11,7 +12,7 @@ package egl
#cgo freebsd LDFLAGS: -L/usr/local/lib
#cgo openbsd CFLAGS: -I/usr/X11R6/include
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib
-#cgo CFLAGS: -DMESA_EGL_NO_X11_HEADERS
+#cgo CFLAGS: -DEGL_NO_X11
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -102,3 +103,7 @@ func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win NativeWindowT
eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0])
return eglSurf
}
+
+func eglWaitClient() bool {
+ return C.eglWaitClient() == C.EGL_TRUE
+}
diff --git a/vendor/gioui.org/app/internal/egl/egl_windows.go b/vendor/gioui.org/internal/egl/egl_windows.go
index 5d9a4fe..4433dd7 100644
--- a/vendor/gioui.org/app/internal/egl/egl_windows.go
+++ b/vendor/gioui.org/internal/egl/egl_windows.go
@@ -10,8 +10,7 @@ import (
syscall "golang.org/x/sys/windows"
- "gioui.org/app/internal/glimpl"
- gunsafe "gioui.org/internal/unsafe"
+ "gioui.org/internal/gl"
)
type (
@@ -41,6 +40,7 @@ var (
_eglSwapBuffers = libEGL.NewProc("eglSwapBuffers")
_eglTerminate = libEGL.NewProc("eglTerminate")
_eglQueryString = libEGL.NewProc("eglQueryString")
+ _eglWaitClient = libEGL.NewProc("eglWaitClient")
)
var loadOnce sync.Once
@@ -57,7 +57,7 @@ func loadDLLs() error {
if err := loadDLL(libEGL, "libEGL.dll"); err != nil {
return err
}
- if err := loadDLL(glimpl.LibGLESv2, "libGLESv2.dll"); err != nil {
+ if err := loadDLL(gl.LibGLESv2, "libGLESv2.dll"); err != nil {
return err
}
// d3dcompiler_47.dll is needed internally for shader compilation to function.
@@ -154,7 +154,12 @@ func eglTerminate(disp _EGLDisplay) bool {
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name))
- return gunsafe.GoString(gunsafe.SliceOf(r))
+ return syscall.BytePtrToString((*byte)(unsafe.Pointer(r)))
+}
+
+func eglWaitClient() bool {
+ r, _, _ := _eglWaitClient.Call()
+ return r != 0
}
// issue34474KeepAlive calls runtime.KeepAlive as a
diff --git a/vendor/gioui.org/internal/f32color/rgba.go b/vendor/gioui.org/internal/f32color/rgba.go
index efaa1fb..eecf018 100644
--- a/vendor/gioui.org/internal/f32color/rgba.go
+++ b/vendor/gioui.org/internal/f32color/rgba.go
@@ -7,7 +7,7 @@ import (
"math"
)
-// RGBA is a 32 bit floating point linear space color.
+// RGBA is a 32 bit floating point linear premultiplied color space.
type RGBA struct {
R, G, B, A float32
}
@@ -23,30 +23,90 @@ func (col RGBA) Float32() (r, g, b, a float32) {
}
// SRGBA converts from linear to sRGB color space.
-func (col RGBA) SRGB() color.RGBA {
- return color.RGBA{
- R: uint8(linearTosRGB(col.R)*255 + .5),
- G: uint8(linearTosRGB(col.G)*255 + .5),
- B: uint8(linearTosRGB(col.B)*255 + .5),
+func (col RGBA) SRGB() color.NRGBA {
+ if col.A == 0 {
+ return color.NRGBA{}
+ }
+ return color.NRGBA{
+ R: uint8(linearTosRGB(col.R/col.A)*255 + .5),
+ G: uint8(linearTosRGB(col.G/col.A)*255 + .5),
+ B: uint8(linearTosRGB(col.B/col.A)*255 + .5),
A: uint8(col.A*255 + .5),
}
}
+// Luminance calculates the relative luminance of a linear RGBA color.
+// Normalized to 0 for black and 1 for white.
+//
+// See https://www.w3.org/TR/WCAG20/#relativeluminancedef for more details
+func (col RGBA) Luminance() float32 {
+ return 0.2126*col.R + 0.7152*col.G + 0.0722*col.B
+}
+
// Opaque returns the color without alpha component.
func (col RGBA) Opaque() RGBA {
col.A = 1.0
return col
}
-// RGBAFromSRGB converts from SRGBA to RGBA.
-func RGBAFromSRGB(col color.RGBA) RGBA {
- r, g, b, a := col.RGBA()
+// LinearFromSRGB converts from col in the sRGB colorspace to RGBA.
+func LinearFromSRGB(col color.NRGBA) RGBA {
+ af := float32(col.A) / 0xFF
return RGBA{
- R: sRGBToLinear(float32(r) / 0xffff),
- G: sRGBToLinear(float32(g) / 0xffff),
- B: sRGBToLinear(float32(b) / 0xffff),
- A: float32(a) / 0xFFFF,
+ R: sRGBToLinear(float32(col.R)/0xff) * af,
+ G: sRGBToLinear(float32(col.G)/0xff) * af,
+ B: sRGBToLinear(float32(col.B)/0xff) * af,
+ A: af,
+ }
+}
+
+// NRGBAToRGBA converts from non-premultiplied sRGB color to premultiplied sRGB color.
+//
+// Each component in the result is `sRGBToLinear(c * alpha)`, where `c`
+// is the linear color.
+func NRGBAToRGBA(col color.NRGBA) color.RGBA {
+ if col.A == 0xFF {
+ return color.RGBA(col)
+ }
+ c := LinearFromSRGB(col)
+ return color.RGBA{
+ R: uint8(linearTosRGB(c.R)*255 + .5),
+ G: uint8(linearTosRGB(c.G)*255 + .5),
+ B: uint8(linearTosRGB(c.B)*255 + .5),
+ A: col.A,
+ }
+}
+
+// NRGBAToLinearRGBA converts from non-premultiplied sRGB color to premultiplied linear RGBA color.
+//
+// Each component in the result is `c * alpha`, where `c` is the linear color.
+func NRGBAToLinearRGBA(col color.NRGBA) color.RGBA {
+ if col.A == 0xFF {
+ return color.RGBA(col)
+ }
+ c := LinearFromSRGB(col)
+ return color.RGBA{
+ R: uint8(c.R*255 + .5),
+ G: uint8(c.G*255 + .5),
+ B: uint8(c.B*255 + .5),
+ A: col.A,
+ }
+}
+
+// RGBAToNRGBA converts from premultiplied sRGB color to non-premultiplied sRGB color.
+func RGBAToNRGBA(col color.RGBA) color.NRGBA {
+ if col.A == 0xFF {
+ return color.NRGBA(col)
+ }
+
+ linear := RGBA{
+ R: sRGBToLinear(float32(col.R) / 0xff),
+ G: sRGBToLinear(float32(col.G) / 0xff),
+ B: sRGBToLinear(float32(col.B) / 0xff),
+ A: float32(col.A) / 0xff,
}
+
+ return linear.SRGB()
}
// linearTosRGB transforms color value from linear to sRGB.
@@ -73,3 +133,45 @@ func sRGBToLinear(c float32) float32 {
return float32(math.Pow(float64((c+0.055)/1.055), 2.4))
}
}
+
+// MulAlpha applies the alpha to the color.
+func MulAlpha(c color.NRGBA, alpha uint8) color.NRGBA {
+ c.A = uint8(uint32(c.A) * uint32(alpha) / 0xFF)
+ return c
+}
+
+// Disabled blends color towards the luminance and multiplies alpha.
+// Blending towards luminance will desaturate the color.
+// Multiplying alpha blends the color together more with the background.
+func Disabled(c color.NRGBA) (d color.NRGBA) {
+ const r = 80 // blend ratio
+ lum := approxLuminance(c)
+ return color.NRGBA{
+ R: byte((int(c.R)*r + int(lum)*(256-r)) / 256),
+ G: byte((int(c.G)*r + int(lum)*(256-r)) / 256),
+ B: byte((int(c.B)*r + int(lum)*(256-r)) / 256),
+ A: byte(int(c.A) * (128 + 32) / 256),
+ }
+}
+
+// Hovered blends color towards a brighter color.
+func Hovered(c color.NRGBA) (d color.NRGBA) {
+ const r = 0x20 // lighten ratio
+ return color.NRGBA{
+ R: byte(255 - int(255-c.R)*(255-r)/256),
+ G: byte(255 - int(255-c.G)*(255-r)/256),
+ B: byte(255 - int(255-c.B)*(255-r)/256),
+ A: c.A,
+ }
+}
+
+// approxLuminance is a fast approximate version of RGBA.Luminance.
+func approxLuminance(c color.NRGBA) byte {
+ const (
+ r = 13933 // 0.2126 * 256 * 256
+ g = 46871 // 0.7152 * 256 * 256
+ b = 4732 // 0.0722 * 256 * 256
+ t = r + g + b
+ )
+ return byte((r*int(c.R) + g*int(c.G) + b*int(c.B)) / t)
+}
diff --git a/vendor/gioui.org/gpu/gl/gl.go b/vendor/gioui.org/internal/gl/gl.go
index 350f60f..a9e378a 100644
--- a/vendor/gioui.org/gpu/gl/gl.go
+++ b/vendor/gioui.org/internal/gl/gl.go
@@ -8,20 +8,38 @@ type (
)
const (
+ ACTIVE_TEXTURE = 0x84E0
+ ALL_BARRIER_BITS = 0xffffffff
ARRAY_BUFFER = 0x8892
+ ARRAY_BUFFER_BINDING = 0x8894
+ BACK = 0x0405
BLEND = 0xbe2
+ BLEND_DST_RGB = 0x80C8
+ BLEND_SRC_RGB = 0x80C9
+ BLEND_DST_ALPHA = 0x80CA
+ BLEND_SRC_ALPHA = 0x80CB
CLAMP_TO_EDGE = 0x812f
COLOR_ATTACHMENT0 = 0x8ce0
COLOR_BUFFER_BIT = 0x4000
+ COLOR_CLEAR_VALUE = 0x0C22
COMPILE_STATUS = 0x8b81
- DEPTH_BUFFER_BIT = 0x100
+ COMPUTE_SHADER = 0x91B9
+ CURRENT_PROGRAM = 0x8B8D
DEPTH_ATTACHMENT = 0x8d00
+ DEPTH_BUFFER_BIT = 0x100
+ DEPTH_CLEAR_VALUE = 0x0B73
DEPTH_COMPONENT16 = 0x81a5
DEPTH_COMPONENT24 = 0x81A6
DEPTH_COMPONENT32F = 0x8CAC
+ DEPTH_FUNC = 0x0B74
DEPTH_TEST = 0xb71
+ DEPTH_WRITEMASK = 0x0B72
+ DRAW_FRAMEBUFFER = 0x8CA9
DST_COLOR = 0x306
+ DYNAMIC_DRAW = 0x88E8
+ DYNAMIC_READ = 0x88E9
ELEMENT_ARRAY_BUFFER = 0x8893
+ ELEMENT_ARRAY_BUFFER_BINDING = 0x8895
EXTENSIONS = 0x1f03
FALSE = 0
FLOAT = 0x1406
@@ -30,6 +48,7 @@ const (
FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210
FRAMEBUFFER_BINDING = 0x8ca6
FRAMEBUFFER_COMPLETE = 0x8cd5
+ FRAMEBUFFER_SRGB = 0x8db9
HALF_FLOAT = 0x140b
HALF_FLOAT_OES = 0x8d61
INFO_LOG_LENGTH = 0x8B84
@@ -39,17 +58,23 @@ const (
LINEAR = 0x2601
LINK_STATUS = 0x8b82
LUMINANCE = 0x1909
+ MAP_READ_BIT = 0x0001
MAX_TEXTURE_SIZE = 0xd33
NEAREST = 0x2600
NO_ERROR = 0x0
NUM_EXTENSIONS = 0x821D
ONE = 0x1
ONE_MINUS_SRC_ALPHA = 0x303
+ PACK_ROW_LENGTH = 0x0D02
+ PROGRAM_BINARY_LENGTH = 0x8741
QUERY_RESULT = 0x8866
QUERY_RESULT_AVAILABLE = 0x8867
R16F = 0x822d
R8 = 0x8229
READ_FRAMEBUFFER = 0x8ca8
+ READ_FRAMEBUFFER_BINDING = 0x8CAA
+ READ_ONLY = 0x88B8
+ READ_WRITE = 0x88BA
RED = 0x1903
RENDERER = 0x1F01
RENDERBUFFER = 0x8d41
@@ -59,13 +84,17 @@ const (
RGB = 0x1907
RGBA = 0x1908
RGBA8 = 0x8058
+ SHADER_STORAGE_BUFFER = 0x90D2
+ SHADER_STORAGE_BUFFER_BINDING = 0x90D3
SHORT = 0x1402
SRGB = 0x8c40
SRGB_ALPHA_EXT = 0x8c42
SRGB8 = 0x8c41
SRGB8_ALPHA8 = 0x8c43
STATIC_DRAW = 0x88e4
+ STENCIL_BUFFER_BIT = 0x00000400
TEXTURE_2D = 0xde1
+ TEXTURE_BINDING_2D = 0x8069
TEXTURE_MAG_FILTER = 0x2800
TEXTURE_MIN_FILTER = 0x2801
TEXTURE_WRAP_S = 0x2802
@@ -76,86 +105,26 @@ const (
TRIANGLES = 0x4
TRUE = 1
UNIFORM_BUFFER = 0x8A11
+ UNIFORM_BUFFER_BINDING = 0x8A28
UNPACK_ALIGNMENT = 0xcf5
+ UNPACK_ROW_LENGTH = 0x0CF2
UNSIGNED_BYTE = 0x1401
UNSIGNED_SHORT = 0x1403
+ VIEWPORT = 0x0BA2
VERSION = 0x1f02
+ VERTEX_ARRAY_BINDING = 0x85B5
VERTEX_SHADER = 0x8b31
+ VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F
+ VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622
+ VERTEX_ATTRIB_ARRAY_POINTER = 0x8645
+ VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A
+ VERTEX_ATTRIB_ARRAY_SIZE = 0x8623
+ VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624
+ VERTEX_ATTRIB_ARRAY_TYPE = 0x8625
+ WRITE_ONLY = 0x88B9
ZERO = 0x0
// EXT_disjoint_timer_query
TIME_ELAPSED_EXT = 0x88BF
GPU_DISJOINT_EXT = 0x8FBB
)
-
-type Functions interface {
- ActiveTexture(texture Enum)
- AttachShader(p Program, s Shader)
- BeginQuery(target Enum, query Query)
- BindAttribLocation(p Program, a Attrib, name string)
- BindBuffer(target Enum, b Buffer)
- BindBufferBase(target Enum, index int, buffer Buffer)
- BindFramebuffer(target Enum, fb Framebuffer)
- BindRenderbuffer(target Enum, fb Renderbuffer)
- BindTexture(target Enum, t Texture)
- BlendEquation(mode Enum)
- BlendFunc(sfactor, dfactor Enum)
- BufferData(target Enum, src []byte, usage Enum)
- CheckFramebufferStatus(target Enum) Enum
- Clear(mask Enum)
- ClearColor(red, green, blue, alpha float32)
- ClearDepthf(d float32)
- CompileShader(s Shader)
- CreateBuffer() Buffer
- CreateFramebuffer() Framebuffer
- CreateProgram() Program
- CreateQuery() Query
- CreateRenderbuffer() Renderbuffer
- CreateShader(ty Enum) Shader
- CreateTexture() Texture
- DeleteBuffer(v Buffer)
- DeleteFramebuffer(v Framebuffer)
- DeleteProgram(p Program)
- DeleteQuery(query Query)
- DeleteRenderbuffer(r Renderbuffer)
- DeleteShader(s Shader)
- DeleteTexture(v Texture)
- DepthFunc(f Enum)
- DepthMask(mask bool)
- DisableVertexAttribArray(a Attrib)
- Disable(cap Enum)
- DrawArrays(mode Enum, first, count int)
- DrawElements(mode Enum, count int, ty Enum, offset int)
- Enable(cap Enum)
- EnableVertexAttribArray(a Attrib)
- EndQuery(target Enum)
- FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int)
- FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer)
- GetBinding(pname Enum) Object
- GetError() Enum
- GetInteger(pname Enum) int
- GetProgrami(p Program, pname Enum) int
- GetProgramInfoLog(p Program) string
- GetQueryObjectuiv(query Query, pname Enum) uint
- GetShaderi(s Shader, pname Enum) int
- GetShaderInfoLog(s Shader) string
- GetString(pname Enum) string
- GetUniformBlockIndex(p Program, name string) uint
- GetUniformLocation(p Program, name string) Uniform
- InvalidateFramebuffer(target, attachment Enum)
- LinkProgram(p Program)
- ReadPixels(x, y, width, height int, format, ty Enum, data []byte)
- RenderbufferStorage(target, internalformat Enum, width, height int)
- ShaderSource(s Shader, src string)
- TexImage2D(target Enum, level int, internalFormat int, width, height int, format, ty Enum, data []byte)
- TexParameteri(target, pname Enum, param int)
- UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint)
- Uniform1f(dst Uniform, v float32)
- Uniform1i(dst Uniform, v int)
- Uniform2f(dst Uniform, v0, v1 float32)
- Uniform3f(dst Uniform, v0, v1, v2 float32)
- Uniform4f(dst Uniform, v0, v1, v2, v3 float32)
- UseProgram(p Program)
- VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int)
- Viewport(x, y, width, height int)
-}
diff --git a/vendor/gioui.org/internal/gl/gl_js.go b/vendor/gioui.org/internal/gl/gl_js.go
new file mode 100644
index 0000000..2c7976e
--- /dev/null
+++ b/vendor/gioui.org/internal/gl/gl_js.go
@@ -0,0 +1,458 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gl
+
+import (
+ "errors"
+ "strings"
+ "syscall/js"
+)
+
+type Functions struct {
+ Ctx js.Value
+ EXT_disjoint_timer_query js.Value
+ EXT_disjoint_timer_query_webgl2 js.Value
+
+ // Cached reference to the Uint8Array JS type.
+ uint8Array js.Value
+
+ // Cached JS arrays.
+ arrayBuf js.Value
+ int32Buf js.Value
+
+ isWebGL2 bool
+}
+
+type Context js.Value
+
+func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
+ f := &Functions{
+ Ctx: js.Value(ctx),
+ uint8Array: js.Global().Get("Uint8Array"),
+ }
+ if err := f.Init(); err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+func (f *Functions) Init() error {
+ webgl2Class := js.Global().Get("WebGL2RenderingContext")
+ f.isWebGL2 = !webgl2Class.IsUndefined() && f.Ctx.InstanceOf(webgl2Class)
+ if !f.isWebGL2 {
+ f.EXT_disjoint_timer_query = f.getExtension("EXT_disjoint_timer_query")
+ if f.getExtension("OES_texture_half_float").IsNull() && f.getExtension("OES_texture_float").IsNull() {
+ return errors.New("gl: no support for neither OES_texture_half_float nor OES_texture_float")
+ }
+ if f.getExtension("EXT_sRGB").IsNull() {
+ return errors.New("gl: EXT_sRGB not supported")
+ }
+ } else {
+ // WebGL2 extensions.
+ f.EXT_disjoint_timer_query_webgl2 = f.getExtension("EXT_disjoint_timer_query_webgl2")
+ if f.getExtension("EXT_color_buffer_half_float").IsNull() && f.getExtension("EXT_color_buffer_float").IsNull() {
+ return errors.New("gl: no support for neither EXT_color_buffer_half_float nor EXT_color_buffer_float")
+ }
+ }
+ return nil
+}
+
+func (f *Functions) getExtension(name string) js.Value {
+ return f.Ctx.Call("getExtension", name)
+}
+
+func (f *Functions) ActiveTexture(t Enum) {
+ f.Ctx.Call("activeTexture", int(t))
+}
+func (f *Functions) AttachShader(p Program, s Shader) {
+ f.Ctx.Call("attachShader", js.Value(p), js.Value(s))
+}
+func (f *Functions) BeginQuery(target Enum, query Query) {
+ if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
+ f.Ctx.Call("beginQuery", int(target), js.Value(query))
+ } else {
+ f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query))
+ }
+}
+func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) {
+ f.Ctx.Call("bindAttribLocation", js.Value(p), int(a), name)
+}
+func (f *Functions) BindBuffer(target Enum, b Buffer) {
+ f.Ctx.Call("bindBuffer", int(target), js.Value(b))
+}
+func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) {
+ f.Ctx.Call("bindBufferBase", int(target), index, js.Value(b))
+}
+func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
+ f.Ctx.Call("bindFramebuffer", int(target), js.Value(fb))
+}
+func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
+ f.Ctx.Call("bindRenderbuffer", int(target), js.Value(rb))
+}
+func (f *Functions) BindTexture(target Enum, t Texture) {
+ f.Ctx.Call("bindTexture", int(target), js.Value(t))
+}
+func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
+ panic("not implemented")
+}
+func (f *Functions) BindVertexArray(a VertexArray) {
+ panic("not supported")
+}
+func (f *Functions) BlendEquation(mode Enum) {
+ f.Ctx.Call("blendEquation", int(mode))
+}
+func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
+ f.Ctx.Call("blendFunc", int(srcRGB), int(dstRGB), int(srcA), int(dstA))
+}
+func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
+ if data == nil {
+ f.Ctx.Call("bufferData", int(target), size, int(usage))
+ } else {
+ if len(data) != size {
+ panic("size mismatch")
+ }
+ f.Ctx.Call("bufferData", int(target), f.byteArrayOf(data), int(usage))
+ }
+}
+func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
+ f.Ctx.Call("bufferSubData", int(target), offset, f.byteArrayOf(src))
+}
+func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
+ return Enum(f.Ctx.Call("checkFramebufferStatus", int(target)).Int())
+}
+func (f *Functions) Clear(mask Enum) {
+ f.Ctx.Call("clear", int(mask))
+}
+func (f *Functions) ClearColor(red, green, blue, alpha float32) {
+ f.Ctx.Call("clearColor", red, green, blue, alpha)
+}
+func (f *Functions) ClearDepthf(d float32) {
+ f.Ctx.Call("clearDepth", d)
+}
+func (f *Functions) CompileShader(s Shader) {
+ f.Ctx.Call("compileShader", js.Value(s))
+}
+func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
+ f.Ctx.Call("copyTexSubImage2D", int(target), level, xoffset, yoffset, x, y, width, height)
+}
+func (f *Functions) CreateBuffer() Buffer {
+ return Buffer(f.Ctx.Call("createBuffer"))
+}
+func (f *Functions) CreateFramebuffer() Framebuffer {
+ return Framebuffer(f.Ctx.Call("createFramebuffer"))
+}
+func (f *Functions) CreateProgram() Program {
+ return Program(f.Ctx.Call("createProgram"))
+}
+func (f *Functions) CreateQuery() Query {
+ return Query(f.Ctx.Call("createQuery"))
+}
+func (f *Functions) CreateRenderbuffer() Renderbuffer {
+ return Renderbuffer(f.Ctx.Call("createRenderbuffer"))
+}
+func (f *Functions) CreateShader(ty Enum) Shader {
+ return Shader(f.Ctx.Call("createShader", int(ty)))
+}
+func (f *Functions) CreateTexture() Texture {
+ return Texture(f.Ctx.Call("createTexture"))
+}
+func (f *Functions) CreateVertexArray() VertexArray {
+ panic("not supported")
+}
+func (f *Functions) DeleteBuffer(v Buffer) {
+ f.Ctx.Call("deleteBuffer", js.Value(v))
+}
+func (f *Functions) DeleteFramebuffer(v Framebuffer) {
+ f.Ctx.Call("deleteFramebuffer", js.Value(v))
+}
+func (f *Functions) DeleteProgram(p Program) {
+ f.Ctx.Call("deleteProgram", js.Value(p))
+}
+func (f *Functions) DeleteQuery(query Query) {
+ if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
+ f.Ctx.Call("deleteQuery", js.Value(query))
+ } else {
+ f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query))
+ }
+}
+func (f *Functions) DeleteShader(s Shader) {
+ f.Ctx.Call("deleteShader", js.Value(s))
+}
+func (f *Functions) DeleteRenderbuffer(v Renderbuffer) {
+ f.Ctx.Call("deleteRenderbuffer", js.Value(v))
+}
+func (f *Functions) DeleteTexture(v Texture) {
+ f.Ctx.Call("deleteTexture", js.Value(v))
+}
+func (f *Functions) DeleteVertexArray(a VertexArray) {
+ panic("not implemented")
+}
+func (f *Functions) DepthFunc(fn Enum) {
+ f.Ctx.Call("depthFunc", int(fn))
+}
+func (f *Functions) DepthMask(mask bool) {
+ f.Ctx.Call("depthMask", mask)
+}
+func (f *Functions) DisableVertexAttribArray(a Attrib) {
+ f.Ctx.Call("disableVertexAttribArray", int(a))
+}
+func (f *Functions) Disable(cap Enum) {
+ f.Ctx.Call("disable", int(cap))
+}
+func (f *Functions) DrawArrays(mode Enum, first, count int) {
+ f.Ctx.Call("drawArrays", int(mode), first, count)
+}
+func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
+ f.Ctx.Call("drawElements", int(mode), count, int(ty), offset)
+}
+func (f *Functions) DispatchCompute(x, y, z int) {
+ panic("not implemented")
+}
+func (f *Functions) Enable(cap Enum) {
+ f.Ctx.Call("enable", int(cap))
+}
+func (f *Functions) EnableVertexAttribArray(a Attrib) {
+ f.Ctx.Call("enableVertexAttribArray", int(a))
+}
+func (f *Functions) EndQuery(target Enum) {
+ if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
+ f.Ctx.Call("endQuery", int(target))
+ } else {
+ f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target))
+ }
+}
+func (f *Functions) Finish() {
+ f.Ctx.Call("finish")
+}
+func (f *Functions) Flush() {
+ f.Ctx.Call("flush")
+}
+func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
+ f.Ctx.Call("framebufferRenderbuffer", int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer))
+}
+func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
+ f.Ctx.Call("framebufferTexture2D", int(target), int(attachment), int(texTarget), js.Value(t), level)
+}
+func (f *Functions) GetError() Enum {
+ // Avoid slow getError calls. See gio#179.
+ return 0
+}
+func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int {
+ return paramVal(f.Ctx.Call("getRenderbufferParameteri", int(pname)))
+}
+func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
+ if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING {
+ // FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2
+ return LINEAR
+ }
+ return paramVal(f.Ctx.Call("getFramebufferAttachmentParameter", int(target), int(attachment), int(pname)))
+}
+func (f *Functions) GetBinding(pname Enum) Object {
+ obj := f.Ctx.Call("getParameter", int(pname))
+ if !obj.Truthy() {
+ return Object{}
+ }
+ return Object(obj)
+}
+func (f *Functions) GetBindingi(pname Enum, idx int) Object {
+ obj := f.Ctx.Call("getIndexedParameter", int(pname), idx)
+ if !obj.Truthy() {
+ return Object{}
+ }
+ return Object(obj)
+}
+func (f *Functions) GetInteger(pname Enum) int {
+ if !f.isWebGL2 {
+ switch pname {
+ case PACK_ROW_LENGTH, UNPACK_ROW_LENGTH:
+ return 0 // PACK_ROW_LENGTH and UNPACK_ROW_LENGTH is only available on WebGL 2
+ }
+ }
+ return paramVal(f.Ctx.Call("getParameter", int(pname)))
+}
+func (f *Functions) GetFloat(pname Enum) float32 {
+ return float32(f.Ctx.Call("getParameter", int(pname)).Float())
+}
+func (f *Functions) GetInteger4(pname Enum) [4]int {
+ arr := f.Ctx.Call("getParameter", int(pname))
+ var res [4]int
+ for i := range res {
+ res[i] = arr.Index(i).Int()
+ }
+ return res
+}
+func (f *Functions) GetFloat4(pname Enum) [4]float32 {
+ arr := f.Ctx.Call("getParameter", int(pname))
+ var res [4]float32
+ for i := range res {
+ res[i] = float32(arr.Index(i).Float())
+ }
+ return res
+}
+func (f *Functions) GetProgrami(p Program, pname Enum) int {
+ return paramVal(f.Ctx.Call("getProgramParameter", js.Value(p), int(pname)))
+}
+func (f *Functions) GetProgramInfoLog(p Program) string {
+ return f.Ctx.Call("getProgramInfoLog", js.Value(p)).String()
+}
+func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
+ if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
+ return uint(paramVal(f.Ctx.Call("getQueryParameter", js.Value(query), int(pname))))
+ } else {
+ return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname))))
+ }
+}
+func (f *Functions) GetShaderi(s Shader, pname Enum) int {
+ return paramVal(f.Ctx.Call("getShaderParameter", js.Value(s), int(pname)))
+}
+func (f *Functions) GetShaderInfoLog(s Shader) string {
+ return f.Ctx.Call("getShaderInfoLog", js.Value(s)).String()
+}
+func (f *Functions) GetString(pname Enum) string {
+ switch pname {
+ case EXTENSIONS:
+ extsjs := f.Ctx.Call("getSupportedExtensions")
+ var exts []string
+ for i := 0; i < extsjs.Length(); i++ {
+ exts = append(exts, "GL_"+extsjs.Index(i).String())
+ }
+ return strings.Join(exts, " ")
+ default:
+ return f.Ctx.Call("getParameter", int(pname)).String()
+ }
+}
+func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
+ return uint(paramVal(f.Ctx.Call("getUniformBlockIndex", js.Value(p), name)))
+}
+func (f *Functions) GetUniformLocation(p Program, name string) Uniform {
+ return Uniform(f.Ctx.Call("getUniformLocation", js.Value(p), name))
+}
+func (f *Functions) GetVertexAttrib(index int, pname Enum) int {
+ return paramVal(f.Ctx.Call("getVertexAttrib", index, int(pname)))
+}
+func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
+ obj := f.Ctx.Call("getVertexAttrib", index, int(pname))
+ if !obj.Truthy() {
+ return Object{}
+ }
+ return Object(obj)
+}
+func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
+ return uintptr(f.Ctx.Call("getVertexAttribOffset", index, int(pname)).Int())
+}
+func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
+ fn := f.Ctx.Get("invalidateFramebuffer")
+ if !fn.IsUndefined() {
+ if f.int32Buf.IsUndefined() {
+ f.int32Buf = js.Global().Get("Int32Array").New(1)
+ }
+ f.int32Buf.SetIndex(0, int32(attachment))
+ f.Ctx.Call("invalidateFramebuffer", int(target), f.int32Buf)
+ }
+}
+func (f *Functions) IsEnabled(cap Enum) bool {
+ return f.Ctx.Call("isEnabled", int(cap)).Truthy()
+}
+func (f *Functions) LinkProgram(p Program) {
+ f.Ctx.Call("linkProgram", js.Value(p))
+}
+func (f *Functions) PixelStorei(pname Enum, param int) {
+ f.Ctx.Call("pixelStorei", int(pname), param)
+}
+func (f *Functions) MemoryBarrier(barriers Enum) {
+ panic("not implemented")
+}
+func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
+ panic("not implemented")
+}
+func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
+ f.Ctx.Call("renderbufferStorage", int(target), int(internalformat), width, height)
+}
+func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
+ ba := f.byteArrayOf(data)
+ f.Ctx.Call("readPixels", x, y, width, height, int(format), int(ty), ba)
+ js.CopyBytesToGo(data, ba)
+}
+func (f *Functions) Scissor(x, y, width, height int32) {
+ f.Ctx.Call("scissor", x, y, width, height)
+}
+func (f *Functions) ShaderSource(s Shader, src string) {
+ f.Ctx.Call("shaderSource", js.Value(s), src)
+}
+func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) {
+ f.Ctx.Call("texImage2D", int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), nil)
+}
+func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
+ f.Ctx.Call("texStorage2D", int(target), levels, int(internalFormat), width, height)
+}
+func (f *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
+ f.Ctx.Call("texSubImage2D", int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data))
+}
+func (f *Functions) TexParameteri(target, pname Enum, param int) {
+ f.Ctx.Call("texParameteri", int(target), int(pname), int(param))
+}
+func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
+ f.Ctx.Call("uniformBlockBinding", js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding))
+}
+func (f *Functions) Uniform1f(dst Uniform, v float32) {
+ f.Ctx.Call("uniform1f", js.Value(dst), v)
+}
+func (f *Functions) Uniform1i(dst Uniform, v int) {
+ f.Ctx.Call("uniform1i", js.Value(dst), v)
+}
+func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
+ f.Ctx.Call("uniform2f", js.Value(dst), v0, v1)
+}
+func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
+ f.Ctx.Call("uniform3f", js.Value(dst), v0, v1, v2)
+}
+func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
+ f.Ctx.Call("uniform4f", js.Value(dst), v0, v1, v2, v3)
+}
+func (f *Functions) UseProgram(p Program) {
+ f.Ctx.Call("useProgram", js.Value(p))
+}
+func (f *Functions) UnmapBuffer(target Enum) bool {
+ panic("not implemented")
+}
+func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
+ f.Ctx.Call("vertexAttribPointer", int(dst), size, int(ty), normalized, stride, offset)
+}
+func (f *Functions) Viewport(x, y, width, height int) {
+ f.Ctx.Call("viewport", x, y, width, height)
+}
+
+func (f *Functions) byteArrayOf(data []byte) js.Value {
+ if len(data) == 0 {
+ return js.Null()
+ }
+ f.resizeByteBuffer(len(data))
+ ba := f.uint8Array.New(f.arrayBuf, int(0), int(len(data)))
+ js.CopyBytesToJS(ba, data)
+ return ba
+}
+
+func (f *Functions) resizeByteBuffer(n int) {
+ if n == 0 {
+ return
+ }
+ if !f.arrayBuf.IsUndefined() && f.arrayBuf.Length() >= n {
+ return
+ }
+ f.arrayBuf = js.Global().Get("ArrayBuffer").New(n)
+}
+
+func paramVal(v js.Value) int {
+ switch v.Type() {
+ case js.TypeBoolean:
+ if b := v.Bool(); b {
+ return 1
+ } else {
+ return 0
+ }
+ case js.TypeNumber:
+ return v.Int()
+ default:
+ panic("unknown parameter type")
+ }
+}
diff --git a/vendor/gioui.org/internal/gl/gl_unix.go b/vendor/gioui.org/internal/gl/gl_unix.go
new file mode 100644
index 0000000..f223ced
--- /dev/null
+++ b/vendor/gioui.org/internal/gl/gl_unix.go
@@ -0,0 +1,1222 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build darwin || linux || freebsd || openbsd
+// +build darwin linux freebsd openbsd
+
+package gl
+
+import (
+ "fmt"
+ "runtime"
+ "strings"
+ "unsafe"
+)
+
+/*
+#cgo CFLAGS: -Werror
+#cgo linux freebsd LDFLAGS: -ldl
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#define __USE_GNU
+#include <dlfcn.h>
+
+typedef unsigned int GLenum;
+typedef unsigned int GLuint;
+typedef char GLchar;
+typedef float GLfloat;
+typedef ssize_t GLsizeiptr;
+typedef intptr_t GLintptr;
+typedef unsigned int GLbitfield;
+typedef int GLint;
+typedef unsigned char GLboolean;
+typedef int GLsizei;
+typedef uint8_t GLubyte;
+
+typedef struct {
+ void (*glActiveTexture)(GLenum texture);
+ void (*glAttachShader)(GLuint program, GLuint shader);
+ void (*glBindAttribLocation)(GLuint program, GLuint index, const GLchar *name);
+ void (*glBindBuffer)(GLenum target, GLuint buffer);
+ void (*glBindFramebuffer)(GLenum target, GLuint framebuffer);
+ void (*glBindRenderbuffer)(GLenum target, GLuint renderbuffer);
+ void (*glBindTexture)(GLenum target, GLuint texture);
+ void (*glBlendEquation)(GLenum mode);
+ void (*glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcA, GLenum dstA);
+ void (*glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
+ void (*glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
+ GLenum (*glCheckFramebufferStatus)(GLenum target);
+ void (*glClear)(GLbitfield mask);
+ void (*glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+ void (*glClearDepthf)(GLfloat d);
+ void (*glCompileShader)(GLuint shader);
+ void (*glCopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+ GLuint (*glCreateProgram)(void);
+ GLuint (*glCreateShader)(GLenum type);
+ void (*glDeleteBuffers)(GLsizei n, const GLuint *buffers);
+ void (*glDeleteFramebuffers)(GLsizei n, const GLuint *framebuffers);
+ void (*glDeleteProgram)(GLuint program);
+ void (*glDeleteRenderbuffers)(GLsizei n, const GLuint *renderbuffers);
+ void (*glDeleteShader)(GLuint shader);
+ void (*glDeleteTextures)(GLsizei n, const GLuint *textures);
+ void (*glDepthFunc)(GLenum func);
+ void (*glDepthMask)(GLboolean flag);
+ void (*glDisable)(GLenum cap);
+ void (*glDisableVertexAttribArray)(GLuint index);
+ void (*glDrawArrays)(GLenum mode, GLint first, GLsizei count);
+ void (*glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void *indices);
+ void (*glEnable)(GLenum cap);
+ void (*glEnableVertexAttribArray)(GLuint index);
+ void (*glFinish)(void);
+ void (*glFlush)(void);
+ void (*glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+ void (*glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+ void (*glGenBuffers)(GLsizei n, GLuint *buffers);
+ void (*glGenFramebuffers)(GLsizei n, GLuint *framebuffers);
+ void (*glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers);
+ void (*glGenTextures)(GLsizei n, GLuint *textures);
+ GLenum (*glGetError)(void);
+ void (*glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint *params);
+ void (*glGetFloatv)(GLenum pname, GLfloat *data);
+ void (*glGetIntegerv)(GLenum pname, GLint *data);
+ void (*glGetIntegeri_v)(GLenum pname, GLuint idx, GLint *data);
+ void (*glGetProgramiv)(GLuint program, GLenum pname, GLint *params);
+ void (*glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+ void (*glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params);
+ void (*glGetShaderiv)(GLuint shader, GLenum pname, GLint *params);
+ void (*glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+ const GLubyte *(*glGetString)(GLenum name);
+ GLint (*glGetUniformLocation)(GLuint program, const GLchar *name);
+ void (*glGetVertexAttribiv)(GLuint index, GLenum pname, GLint *params);
+ void (*glGetVertexAttribPointerv)(GLuint index, GLenum pname, void **params);
+ GLboolean (*glIsEnabled)(GLenum cap);
+ void (*glLinkProgram)(GLuint program);
+ void (*glPixelStorei)(GLenum pname, GLint param);
+ void (*glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
+ void (*glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+ void (*glScissor)(GLint x, GLint y, GLsizei width, GLsizei height);
+ void (*glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
+ void (*glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
+ void (*glTexParameteri)(GLenum target, GLenum pname, GLint param);
+ void (*glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
+ void (*glUniform1f)(GLint location, GLfloat v0);
+ void (*glUniform1i)(GLint location, GLint v0);
+ void (*glUniform2f)(GLint location, GLfloat v0, GLfloat v1);
+ void (*glUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+ void (*glUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+ void (*glUseProgram)(GLuint program);
+ void (*glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
+ void (*glViewport)(GLint x, GLint y, GLsizei width, GLsizei height);
+
+ void (*glBindVertexArray)(GLuint array);
+ void (*glBindBufferBase)(GLenum target, GLuint index, GLuint buffer);
+ GLuint (*glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName);
+ void (*glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
+ void (*glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments);
+ void (*glBeginQuery)(GLenum target, GLuint id);
+ void (*glDeleteQueries)(GLsizei n, const GLuint *ids);
+ void (*glDeleteVertexArrays)(GLsizei n, const GLuint *ids);
+ void (*glEndQuery)(GLenum target);
+ void (*glGenQueries)(GLsizei n, GLuint *ids);
+ void (*glGenVertexArrays)(GLsizei n, GLuint *ids);
+ void (*glGetProgramBinary)(GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary);
+ void (*glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params);
+ const GLubyte* (*glGetStringi)(GLenum name, GLuint index);
+ void (*glDispatchCompute)(GLuint x, GLuint y, GLuint z);
+ void (*glMemoryBarrier)(GLbitfield barriers);
+ void* (*glMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+ GLboolean (*glUnmapBuffer)(GLenum target);
+ void (*glBindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
+ void (*glTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
+ void (*glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
+} glFunctions;
+
+static void glActiveTexture(glFunctions *f, GLenum texture) {
+ f->glActiveTexture(texture);
+}
+
+static void glAttachShader(glFunctions *f, GLuint program, GLuint shader) {
+ f->glAttachShader(program, shader);
+}
+
+static void glBindAttribLocation(glFunctions *f, GLuint program, GLuint index, const GLchar *name) {
+ f->glBindAttribLocation(program, index, name);
+}
+
+static void glBindBuffer(glFunctions *f, GLenum target, GLuint buffer) {
+ f->glBindBuffer(target, buffer);
+}
+
+static void glBindFramebuffer(glFunctions *f, GLenum target, GLuint framebuffer) {
+ f->glBindFramebuffer(target, framebuffer);
+}
+
+static void glBindRenderbuffer(glFunctions *f, GLenum target, GLuint renderbuffer) {
+ f->glBindRenderbuffer(target, renderbuffer);
+}
+
+static void glBindTexture(glFunctions *f, GLenum target, GLuint texture) {
+ f->glBindTexture(target, texture);
+}
+
+static void glBindVertexArray(glFunctions *f, GLuint array) {
+ f->glBindVertexArray(array);
+}
+
+static void glBlendEquation(glFunctions *f, GLenum mode) {
+ f->glBlendEquation(mode);
+}
+
+static void glBlendFuncSeparate(glFunctions *f, GLenum srcRGB, GLenum dstRGB, GLenum srcA, GLenum dstA) {
+ f->glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA);
+}
+
+static void glBufferData(glFunctions *f, GLenum target, GLsizeiptr size, const void *data, GLenum usage) {
+ f->glBufferData(target, size, data, usage);
+}
+
+static void glBufferSubData(glFunctions *f, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) {
+ f->glBufferSubData(target, offset, size, data);
+}
+
+static GLenum glCheckFramebufferStatus(glFunctions *f, GLenum target) {
+ return f->glCheckFramebufferStatus(target);
+}
+
+static void glClear(glFunctions *f, GLbitfield mask) {
+ f->glClear(mask);
+}
+
+static void glClearColor(glFunctions *f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
+ f->glClearColor(red, green, blue, alpha);
+}
+
+static void glClearDepthf(glFunctions *f, GLfloat d) {
+ f->glClearDepthf(d);
+}
+
+static void glCompileShader(glFunctions *f, GLuint shader) {
+ f->glCompileShader(shader);
+}
+
+static void glCopyTexSubImage2D(glFunctions *f, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ f->glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+}
+
+static GLuint glCreateProgram(glFunctions *f) {
+ return f->glCreateProgram();
+}
+
+static GLuint glCreateShader(glFunctions *f, GLenum type) {
+ return f->glCreateShader(type);
+}
+
+static void glDeleteBuffers(glFunctions *f, GLsizei n, const GLuint *buffers) {
+ f->glDeleteBuffers(n, buffers);
+}
+
+static void glDeleteFramebuffers(glFunctions *f, GLsizei n, const GLuint *framebuffers) {
+ f->glDeleteFramebuffers(n, framebuffers);
+}
+
+static void glDeleteProgram(glFunctions *f, GLuint program) {
+ f->glDeleteProgram(program);
+}
+
+static void glDeleteRenderbuffers(glFunctions *f, GLsizei n, const GLuint *renderbuffers) {
+ f->glDeleteRenderbuffers(n, renderbuffers);
+}
+
+static void glDeleteShader(glFunctions *f, GLuint shader) {
+ f->glDeleteShader(shader);
+}
+
+static void glDeleteTextures(glFunctions *f, GLsizei n, const GLuint *textures) {
+ f->glDeleteTextures(n, textures);
+}
+
+static void glDepthFunc(glFunctions *f, GLenum func) {
+ f->glDepthFunc(func);
+}
+
+static void glDepthMask(glFunctions *f, GLboolean flag) {
+ f->glDepthMask(flag);
+}
+
+static void glDisable(glFunctions *f, GLenum cap) {
+ f->glDisable(cap);
+}
+
+static void glDisableVertexAttribArray(glFunctions *f, GLuint index) {
+ f->glDisableVertexAttribArray(index);
+}
+
+static void glDrawArrays(glFunctions *f, GLenum mode, GLint first, GLsizei count) {
+ f->glDrawArrays(mode, first, count);
+}
+
+// offset is defined as an uintptr_t to omit Cgo pointer checks.
+static void glDrawElements(glFunctions *f, GLenum mode, GLsizei count, GLenum type, const uintptr_t offset) {
+ f->glDrawElements(mode, count, type, (const void *)offset);
+}
+
+static void glEnable(glFunctions *f, GLenum cap) {
+ f->glEnable(cap);
+}
+
+static void glEnableVertexAttribArray(glFunctions *f, GLuint index) {
+ f->glEnableVertexAttribArray(index);
+}
+
+static void glFinish(glFunctions *f) {
+ f->glFinish();
+}
+
+static void glFlush(glFunctions *f) {
+ f->glFlush();
+}
+
+static void glFramebufferRenderbuffer(glFunctions *f, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
+ f->glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
+}
+
+static void glFramebufferTexture2D(glFunctions *f, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
+ f->glFramebufferTexture2D(target, attachment, textarget, texture, level);
+}
+
+static void glGenBuffers(glFunctions *f, GLsizei n, GLuint *buffers) {
+ f->glGenBuffers(n, buffers);
+}
+
+static void glGenFramebuffers(glFunctions *f, GLsizei n, GLuint *framebuffers) {
+ f->glGenFramebuffers(n, framebuffers);
+}
+
+static void glGenRenderbuffers(glFunctions *f, GLsizei n, GLuint *renderbuffers) {
+ f->glGenRenderbuffers(n, renderbuffers);
+}
+
+static void glGenTextures(glFunctions *f, GLsizei n, GLuint *textures) {
+ f->glGenTextures(n, textures);
+}
+
+static GLenum glGetError(glFunctions *f) {
+ return f->glGetError();
+}
+
+static void glGetFramebufferAttachmentParameteriv(glFunctions *f, GLenum target, GLenum attachment, GLenum pname, GLint *params) {
+ f->glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
+}
+
+static void glGetIntegerv(glFunctions *f, GLenum pname, GLint *data) {
+ f->glGetIntegerv(pname, data);
+}
+
+static void glGetFloatv(glFunctions *f, GLenum pname, GLfloat *data) {
+ f->glGetFloatv(pname, data);
+}
+
+static void glGetIntegeri_v(glFunctions *f, GLenum pname, GLuint idx, GLint *data) {
+ f->glGetIntegeri_v(pname, idx, data);
+}
+
+static void glGetProgramiv(glFunctions *f, GLuint program, GLenum pname, GLint *params) {
+ f->glGetProgramiv(program, pname, params);
+}
+
+static void glGetProgramInfoLog(glFunctions *f, GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ f->glGetProgramInfoLog(program, bufSize, length, infoLog);
+}
+
+static void glGetRenderbufferParameteriv(glFunctions *f, GLenum target, GLenum pname, GLint *params) {
+ f->glGetRenderbufferParameteriv(target, pname, params);
+}
+
+static void glGetShaderiv(glFunctions *f, GLuint shader, GLenum pname, GLint *params) {
+ f->glGetShaderiv(shader, pname, params);
+}
+
+static void glGetShaderInfoLog(glFunctions *f, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ f->glGetShaderInfoLog(shader, bufSize, length, infoLog);
+}
+
+static const GLubyte *glGetString(glFunctions *f, GLenum name) {
+ return f->glGetString(name);
+}
+
+static GLint glGetUniformLocation(glFunctions *f, GLuint program, const GLchar *name) {
+ return f->glGetUniformLocation(program, name);
+}
+
+static void glGetVertexAttribiv(glFunctions *f, GLuint index, GLenum pname, GLint *data) {
+ f->glGetVertexAttribiv(index, pname, data);
+}
+
+// Return uintptr_t to avoid Cgo pointer check.
+static uintptr_t glGetVertexAttribPointerv(glFunctions *f, GLuint index, GLenum pname) {
+ void *ptrs;
+ f->glGetVertexAttribPointerv(index, pname, &ptrs);
+ return (uintptr_t)ptrs;
+}
+
+static GLboolean glIsEnabled(glFunctions *f, GLenum cap) {
+ return f->glIsEnabled(cap);
+}
+
+static void glLinkProgram(glFunctions *f, GLuint program) {
+ f->glLinkProgram(program);
+}
+
+static void glPixelStorei(glFunctions *f, GLenum pname, GLint param) {
+ f->glPixelStorei(pname, param);
+}
+
+static void glReadPixels(glFunctions *f, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) {
+ f->glReadPixels(x, y, width, height, format, type, pixels);
+}
+
+static void glRenderbufferStorage(glFunctions *f, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
+ f->glRenderbufferStorage(target, internalformat, width, height);
+}
+
+static void glScissor(glFunctions *f, GLint x, GLint y, GLsizei width, GLsizei height) {
+ f->glScissor(x, y, width, height);
+}
+
+static void glShaderSource(glFunctions *f, GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {
+ f->glShaderSource(shader, count, string, length);
+}
+
+static void glTexImage2D(glFunctions *f, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
+ f->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+}
+
+static void glTexParameteri(glFunctions *f, GLenum target, GLenum pname, GLint param) {
+ f->glTexParameteri(target, pname, param);
+}
+
+static void glTexSubImage2D(glFunctions *f, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
+ f->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+}
+
+static void glUniform1f(glFunctions *f, GLint location, GLfloat v0) {
+ f->glUniform1f(location, v0);
+}
+
+static void glUniform1i(glFunctions *f, GLint location, GLint v0) {
+ f->glUniform1i(location, v0);
+}
+
+static void glUniform2f(glFunctions *f, GLint location, GLfloat v0, GLfloat v1) {
+ f->glUniform2f(location, v0, v1);
+}
+
+static void glUniform3f(glFunctions *f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
+ f->glUniform3f(location, v0, v1, v2);
+}
+
+static void glUniform4f(glFunctions *f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
+ f->glUniform4f(location, v0, v1, v2, v3);
+}
+
+static void glUseProgram(glFunctions *f, GLuint program) {
+ f->glUseProgram(program);
+}
+
+// offset is defined as an uintptr_t to omit Cgo pointer checks.
+static void glVertexAttribPointer(glFunctions *f, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uintptr_t offset) {
+ f->glVertexAttribPointer(index, size, type, normalized, stride, (const void *)offset);
+}
+
+static void glViewport(glFunctions *f, GLint x, GLint y, GLsizei width, GLsizei height) {
+ f->glViewport(x, y, width, height);
+}
+
+static void glBindBufferBase(glFunctions *f, GLenum target, GLuint index, GLuint buffer) {
+ f->glBindBufferBase(target, index, buffer);
+}
+
+static void glUniformBlockBinding(glFunctions *f, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) {
+ f->glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
+}
+
+static GLuint glGetUniformBlockIndex(glFunctions *f, GLuint program, const GLchar *uniformBlockName) {
+ return f->glGetUniformBlockIndex(program, uniformBlockName);
+}
+
+static void glInvalidateFramebuffer(glFunctions *f, GLenum target, GLenum attachment) {
+ // Framebuffer invalidation is just a hint and can safely be ignored.
+ if (f->glInvalidateFramebuffer != NULL) {
+ f->glInvalidateFramebuffer(target, 1, &attachment);
+ }
+}
+
+static void glBeginQuery(glFunctions *f, GLenum target, GLenum attachment) {
+ f->glBeginQuery(target, attachment);
+}
+
+static void glDeleteQueries(glFunctions *f, GLsizei n, const GLuint *ids) {
+ f->glDeleteQueries(n, ids);
+}
+
+static void glDeleteVertexArrays(glFunctions *f, GLsizei n, const GLuint *ids) {
+ f->glDeleteVertexArrays(n, ids);
+}
+
+static void glEndQuery(glFunctions *f, GLenum target) {
+ f->glEndQuery(target);
+}
+
+static const GLubyte* glGetStringi(glFunctions *f, GLenum name, GLuint index) {
+ return f->glGetStringi(name, index);
+}
+
+static void glGenQueries(glFunctions *f, GLsizei n, GLuint *ids) {
+ f->glGenQueries(n, ids);
+}
+
+static void glGenVertexArrays(glFunctions *f, GLsizei n, GLuint *ids) {
+ f->glGenVertexArrays(n, ids);
+}
+
+static void glGetProgramBinary(glFunctions *f, GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary) {
+ f->glGetProgramBinary(program, bufsize, length, binaryFormat, binary);
+}
+
+static void glGetQueryObjectuiv(glFunctions *f, GLuint id, GLenum pname, GLuint *params) {
+ f->glGetQueryObjectuiv(id, pname, params);
+}
+
+static void glMemoryBarrier(glFunctions *f, GLbitfield barriers) {
+ f->glMemoryBarrier(barriers);
+}
+
+static void glDispatchCompute(glFunctions *f, GLuint x, GLuint y, GLuint z) {
+ f->glDispatchCompute(x, y, z);
+}
+
+static void *glMapBufferRange(glFunctions *f, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
+ return f->glMapBufferRange(target, offset, length, access);
+}
+
+static GLboolean glUnmapBuffer(glFunctions *f, GLenum target) {
+ return f->glUnmapBuffer(target);
+}
+
+static void glBindImageTexture(glFunctions *f, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) {
+ f->glBindImageTexture(unit, texture, level, layered, layer, access, format);
+}
+
+static void glTexStorage2D(glFunctions *f, GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) {
+ f->glTexStorage2D(target, levels, internalFormat, width, height);
+}
+
+static void glBlitFramebuffer(glFunctions *f, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
+ f->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+*/
+import "C"
+
+type Context interface{}
+
+type Functions struct {
+ // Query caches.
+ uints [100]C.GLuint
+ ints [100]C.GLint
+ floats [100]C.GLfloat
+
+ f C.glFunctions
+}
+
+func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
+ if ctx != nil {
+ panic("non-nil context")
+ }
+ f := new(Functions)
+ err := f.load(forceES)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+func dlsym(handle unsafe.Pointer, s string) unsafe.Pointer {
+ cs := C.CString(s)
+ defer C.free(unsafe.Pointer(cs))
+ return C.dlsym(handle, cs)
+}
+
+func dlopen(lib string) unsafe.Pointer {
+ clib := C.CString(lib)
+ defer C.free(unsafe.Pointer(clib))
+ return C.dlopen(clib, C.RTLD_NOW|C.RTLD_LOCAL)
+}
+
+func (f *Functions) load(forceES bool) error {
+ var (
+ loadErr error
+ libNames []string
+ handles []unsafe.Pointer
+ )
+ switch {
+ case runtime.GOOS == "darwin" && !forceES:
+ libNames = []string{"/System/Library/Frameworks/OpenGL.framework/OpenGL"}
+ case runtime.GOOS == "darwin" && forceES:
+ libNames = []string{"libGLESv2.dylib"}
+ case runtime.GOOS == "ios":
+ libNames = []string{"/System/Library/Frameworks/OpenGLES.framework/OpenGLES"}
+ case runtime.GOOS == "android":
+ libNames = []string{"libGLESv2.so", "libGLESv3.so"}
+ default:
+ libNames = []string{"libGLESv2.so.2"}
+ }
+ for _, lib := range libNames {
+ if h := dlopen(lib); h != nil {
+ handles = append(handles, h)
+ }
+ }
+ if len(handles) == 0 {
+ return fmt.Errorf("gl: no OpenGL implementation could be loaded (tried %q)", libNames)
+ }
+ load := func(s string) *[0]byte {
+ for _, h := range handles {
+ if f := dlsym(h, s); f != nil {
+ return (*[0]byte)(f)
+ }
+ }
+ return nil
+ }
+ must := func(s string) *[0]byte {
+ ptr := load(s)
+ if ptr == nil {
+ loadErr = fmt.Errorf("gl: failed to load symbol %q", s)
+ }
+ return ptr
+ }
+ // GL ES 2.0 functions.
+ f.f.glActiveTexture = must("glActiveTexture")
+ f.f.glAttachShader = must("glAttachShader")
+ f.f.glBindAttribLocation = must("glBindAttribLocation")
+ f.f.glBindBuffer = must("glBindBuffer")
+ f.f.glBindFramebuffer = must("glBindFramebuffer")
+ f.f.glBindRenderbuffer = must("glBindRenderbuffer")
+ f.f.glBindTexture = must("glBindTexture")
+ f.f.glBlendEquation = must("glBlendEquation")
+ f.f.glBlendFuncSeparate = must("glBlendFuncSeparate")
+ f.f.glBufferData = must("glBufferData")
+ f.f.glBufferSubData = must("glBufferSubData")
+ f.f.glCheckFramebufferStatus = must("glCheckFramebufferStatus")
+ f.f.glClear = must("glClear")
+ f.f.glClearColor = must("glClearColor")
+ f.f.glClearDepthf = must("glClearDepthf")
+ f.f.glCompileShader = must("glCompileShader")
+ f.f.glCopyTexSubImage2D = must("glCopyTexSubImage2D")
+ f.f.glCreateProgram = must("glCreateProgram")
+ f.f.glCreateShader = must("glCreateShader")
+ f.f.glDeleteBuffers = must("glDeleteBuffers")
+ f.f.glDeleteFramebuffers = must("glDeleteFramebuffers")
+ f.f.glDeleteProgram = must("glDeleteProgram")
+ f.f.glDeleteRenderbuffers = must("glDeleteRenderbuffers")
+ f.f.glDeleteShader = must("glDeleteShader")
+ f.f.glDeleteTextures = must("glDeleteTextures")
+ f.f.glDepthFunc = must("glDepthFunc")
+ f.f.glDepthMask = must("glDepthMask")
+ f.f.glDisable = must("glDisable")
+ f.f.glDisableVertexAttribArray = must("glDisableVertexAttribArray")
+ f.f.glDrawArrays = must("glDrawArrays")
+ f.f.glDrawElements = must("glDrawElements")
+ f.f.glEnable = must("glEnable")
+ f.f.glEnableVertexAttribArray = must("glEnableVertexAttribArray")
+ f.f.glFinish = must("glFinish")
+ f.f.glFlush = must("glFlush")
+ f.f.glFramebufferRenderbuffer = must("glFramebufferRenderbuffer")
+ f.f.glFramebufferTexture2D = must("glFramebufferTexture2D")
+ f.f.glGenBuffers = must("glGenBuffers")
+ f.f.glGenFramebuffers = must("glGenFramebuffers")
+ f.f.glGenRenderbuffers = must("glGenRenderbuffers")
+ f.f.glGenTextures = must("glGenTextures")
+ f.f.glGetError = must("glGetError")
+ f.f.glGetFramebufferAttachmentParameteriv = must("glGetFramebufferAttachmentParameteriv")
+ f.f.glGetIntegerv = must("glGetIntegerv")
+ f.f.glGetFloatv = must("glGetFloatv")
+ f.f.glGetProgramiv = must("glGetProgramiv")
+ f.f.glGetProgramInfoLog = must("glGetProgramInfoLog")
+ f.f.glGetRenderbufferParameteriv = must("glGetRenderbufferParameteriv")
+ f.f.glGetShaderiv = must("glGetShaderiv")
+ f.f.glGetShaderInfoLog = must("glGetShaderInfoLog")
+ f.f.glGetString = must("glGetString")
+ f.f.glGetUniformLocation = must("glGetUniformLocation")
+ f.f.glGetVertexAttribiv = must("glGetVertexAttribiv")
+ f.f.glGetVertexAttribPointerv = must("glGetVertexAttribPointerv")
+ f.f.glIsEnabled = must("glIsEnabled")
+ f.f.glLinkProgram = must("glLinkProgram")
+ f.f.glPixelStorei = must("glPixelStorei")
+ f.f.glReadPixels = must("glReadPixels")
+ f.f.glRenderbufferStorage = must("glRenderbufferStorage")
+ f.f.glScissor = must("glScissor")
+ f.f.glShaderSource = must("glShaderSource")
+ f.f.glTexImage2D = must("glTexImage2D")
+ f.f.glTexParameteri = must("glTexParameteri")
+ f.f.glTexSubImage2D = must("glTexSubImage2D")
+ f.f.glUniform1f = must("glUniform1f")
+ f.f.glUniform1i = must("glUniform1i")
+ f.f.glUniform2f = must("glUniform2f")
+ f.f.glUniform3f = must("glUniform3f")
+ f.f.glUniform4f = must("glUniform4f")
+ f.f.glUseProgram = must("glUseProgram")
+ f.f.glVertexAttribPointer = must("glVertexAttribPointer")
+ f.f.glViewport = must("glViewport")
+
+ // Extensions and GL ES 3 functions.
+ f.f.glBindBufferBase = load("glBindBufferBase")
+ f.f.glBindVertexArray = load("glBindVertexArray")
+ f.f.glGetIntegeri_v = load("glGetIntegeri_v")
+ f.f.glGetUniformBlockIndex = load("glGetUniformBlockIndex")
+ f.f.glUniformBlockBinding = load("glUniformBlockBinding")
+ f.f.glInvalidateFramebuffer = load("glInvalidateFramebuffer")
+ f.f.glGetStringi = load("glGetStringi")
+ // Fall back to EXT_invalidate_framebuffer if available.
+ if f.f.glInvalidateFramebuffer == nil {
+ f.f.glInvalidateFramebuffer = load("glDiscardFramebufferEXT")
+ }
+
+ f.f.glBeginQuery = load("glBeginQuery")
+ if f.f.glBeginQuery == nil {
+ f.f.glBeginQuery = load("glBeginQueryEXT")
+ }
+ f.f.glDeleteQueries = load("glDeleteQueries")
+ if f.f.glDeleteQueries == nil {
+ f.f.glDeleteQueries = load("glDeleteQueriesEXT")
+ }
+ f.f.glEndQuery = load("glEndQuery")
+ if f.f.glEndQuery == nil {
+ f.f.glEndQuery = load("glEndQueryEXT")
+ }
+ f.f.glGenQueries = load("glGenQueries")
+ if f.f.glGenQueries == nil {
+ f.f.glGenQueries = load("glGenQueriesEXT")
+ }
+ f.f.glGetQueryObjectuiv = load("glGetQueryObjectuiv")
+ if f.f.glGetQueryObjectuiv == nil {
+ f.f.glGetQueryObjectuiv = load("glGetQueryObjectuivEXT")
+ }
+
+ f.f.glDeleteVertexArrays = load("glDeleteVertexArrays")
+ f.f.glGenVertexArrays = load("glGenVertexArrays")
+ f.f.glMemoryBarrier = load("glMemoryBarrier")
+ f.f.glDispatchCompute = load("glDispatchCompute")
+ f.f.glMapBufferRange = load("glMapBufferRange")
+ f.f.glUnmapBuffer = load("glUnmapBuffer")
+ f.f.glBindImageTexture = load("glBindImageTexture")
+ f.f.glTexStorage2D = load("glTexStorage2D")
+ f.f.glBlitFramebuffer = load("glBlitFramebuffer")
+ f.f.glGetProgramBinary = load("glGetProgramBinary")
+
+ return loadErr
+}
+
+func (f *Functions) ActiveTexture(texture Enum) {
+ C.glActiveTexture(&f.f, C.GLenum(texture))
+}
+
+func (f *Functions) AttachShader(p Program, s Shader) {
+ C.glAttachShader(&f.f, C.GLuint(p.V), C.GLuint(s.V))
+}
+
+func (f *Functions) BeginQuery(target Enum, query Query) {
+ C.glBeginQuery(&f.f, C.GLenum(target), C.GLenum(query.V))
+}
+
+func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+ C.glBindAttribLocation(&f.f, C.GLuint(p.V), C.GLuint(a), cname)
+}
+
+func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) {
+ C.glBindBufferBase(&f.f, C.GLenum(target), C.GLuint(index), C.GLuint(b.V))
+}
+
+func (f *Functions) BindBuffer(target Enum, b Buffer) {
+ C.glBindBuffer(&f.f, C.GLenum(target), C.GLuint(b.V))
+}
+
+func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
+ C.glBindFramebuffer(&f.f, C.GLenum(target), C.GLuint(fb.V))
+}
+
+func (f *Functions) BindRenderbuffer(target Enum, fb Renderbuffer) {
+ C.glBindRenderbuffer(&f.f, C.GLenum(target), C.GLuint(fb.V))
+}
+
+func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
+ l := C.GLboolean(FALSE)
+ if layered {
+ l = TRUE
+ }
+ C.glBindImageTexture(&f.f, C.GLuint(unit), C.GLuint(t.V), C.GLint(level), l, C.GLint(layer), C.GLenum(access), C.GLenum(format))
+}
+
+func (f *Functions) BindTexture(target Enum, t Texture) {
+ C.glBindTexture(&f.f, C.GLenum(target), C.GLuint(t.V))
+}
+
+func (f *Functions) BindVertexArray(a VertexArray) {
+ C.glBindVertexArray(&f.f, C.GLuint(a.V))
+}
+
+func (f *Functions) BlendEquation(mode Enum) {
+ C.glBlendEquation(&f.f, C.GLenum(mode))
+}
+
+func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
+ C.glBlendFuncSeparate(&f.f, C.GLenum(srcRGB), C.GLenum(dstRGB), C.GLenum(srcA), C.GLenum(dstA))
+}
+
+func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) {
+ C.glBlitFramebuffer(&f.f,
+ C.GLint(sx0), C.GLint(sy0), C.GLint(sx1), C.GLint(sy1),
+ C.GLint(dx0), C.GLint(dy0), C.GLint(dx1), C.GLint(dy1),
+ C.GLenum(mask), C.GLenum(filter),
+ )
+}
+
+func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
+ var p unsafe.Pointer
+ if len(data) > 0 {
+ p = unsafe.Pointer(&data[0])
+ }
+ C.glBufferData(&f.f, C.GLenum(target), C.GLsizeiptr(size), p, C.GLenum(usage))
+}
+
+func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
+ var p unsafe.Pointer
+ if len(src) > 0 {
+ p = unsafe.Pointer(&src[0])
+ }
+ C.glBufferSubData(&f.f, C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(len(src)), p)
+}
+
+func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
+ return Enum(C.glCheckFramebufferStatus(&f.f, C.GLenum(target)))
+}
+
+func (f *Functions) Clear(mask Enum) {
+ C.glClear(&f.f, C.GLbitfield(mask))
+}
+
+func (f *Functions) ClearColor(red float32, green float32, blue float32, alpha float32) {
+ C.glClearColor(&f.f, C.GLfloat(red), C.GLfloat(green), C.GLfloat(blue), C.GLfloat(alpha))
+}
+
+func (f *Functions) ClearDepthf(d float32) {
+ C.glClearDepthf(&f.f, C.GLfloat(d))
+}
+
+func (f *Functions) CompileShader(s Shader) {
+ C.glCompileShader(&f.f, C.GLuint(s.V))
+}
+
+func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
+ C.glCopyTexSubImage2D(&f.f, C.GLenum(target), C.GLint(level), C.GLint(xoffset), C.GLint(yoffset), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
+}
+
+func (f *Functions) CreateBuffer() Buffer {
+ C.glGenBuffers(&f.f, 1, &f.uints[0])
+ return Buffer{uint(f.uints[0])}
+}
+
+func (f *Functions) CreateFramebuffer() Framebuffer {
+ C.glGenFramebuffers(&f.f, 1, &f.uints[0])
+ return Framebuffer{uint(f.uints[0])}
+}
+
+func (f *Functions) CreateProgram() Program {
+ return Program{uint(C.glCreateProgram(&f.f))}
+}
+
+func (f *Functions) CreateQuery() Query {
+ C.glGenQueries(&f.f, 1, &f.uints[0])
+ return Query{uint(f.uints[0])}
+}
+
+func (f *Functions) CreateRenderbuffer() Renderbuffer {
+ C.glGenRenderbuffers(&f.f, 1, &f.uints[0])
+ return Renderbuffer{uint(f.uints[0])}
+}
+
+func (f *Functions) CreateShader(ty Enum) Shader {
+ return Shader{uint(C.glCreateShader(&f.f, C.GLenum(ty)))}
+}
+
+func (f *Functions) CreateTexture() Texture {
+ C.glGenTextures(&f.f, 1, &f.uints[0])
+ return Texture{uint(f.uints[0])}
+}
+
+func (f *Functions) CreateVertexArray() VertexArray {
+ C.glGenVertexArrays(&f.f, 1, &f.uints[0])
+ return VertexArray{uint(f.uints[0])}
+}
+
+func (f *Functions) DeleteBuffer(v Buffer) {
+ f.uints[0] = C.GLuint(v.V)
+ C.glDeleteBuffers(&f.f, 1, &f.uints[0])
+}
+
+func (f *Functions) DeleteFramebuffer(v Framebuffer) {
+ f.uints[0] = C.GLuint(v.V)
+ C.glDeleteFramebuffers(&f.f, 1, &f.uints[0])
+}
+
+func (f *Functions) DeleteProgram(p Program) {
+ C.glDeleteProgram(&f.f, C.GLuint(p.V))
+}
+
+func (f *Functions) DeleteQuery(query Query) {
+ f.uints[0] = C.GLuint(query.V)
+ C.glDeleteQueries(&f.f, 1, &f.uints[0])
+}
+
+func (f *Functions) DeleteVertexArray(array VertexArray) {
+ f.uints[0] = C.GLuint(array.V)
+ C.glDeleteVertexArrays(&f.f, 1, &f.uints[0])
+}
+
+func (f *Functions) DeleteRenderbuffer(v Renderbuffer) {
+ f.uints[0] = C.GLuint(v.V)
+ C.glDeleteRenderbuffers(&f.f, 1, &f.uints[0])
+}
+
+func (f *Functions) DeleteShader(s Shader) {
+ C.glDeleteShader(&f.f, C.GLuint(s.V))
+}
+
+func (f *Functions) DeleteTexture(v Texture) {
+ f.uints[0] = C.GLuint(v.V)
+ C.glDeleteTextures(&f.f, 1, &f.uints[0])
+}
+
+func (f *Functions) DepthFunc(v Enum) {
+ C.glDepthFunc(&f.f, C.GLenum(v))
+}
+
+func (f *Functions) DepthMask(mask bool) {
+ m := C.GLboolean(FALSE)
+ if mask {
+ m = C.GLboolean(TRUE)
+ }
+ C.glDepthMask(&f.f, m)
+}
+
+func (f *Functions) DisableVertexAttribArray(a Attrib) {
+ C.glDisableVertexAttribArray(&f.f, C.GLuint(a))
+}
+
+func (f *Functions) Disable(cap Enum) {
+ C.glDisable(&f.f, C.GLenum(cap))
+}
+
+func (f *Functions) DrawArrays(mode Enum, first int, count int) {
+ C.glDrawArrays(&f.f, C.GLenum(mode), C.GLint(first), C.GLsizei(count))
+}
+
+func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
+ C.glDrawElements(&f.f, C.GLenum(mode), C.GLsizei(count), C.GLenum(ty), C.uintptr_t(offset))
+}
+
+func (f *Functions) DispatchCompute(x, y, z int) {
+ C.glDispatchCompute(&f.f, C.GLuint(x), C.GLuint(y), C.GLuint(z))
+}
+
+func (f *Functions) Enable(cap Enum) {
+ C.glEnable(&f.f, C.GLenum(cap))
+}
+
+func (f *Functions) EndQuery(target Enum) {
+ C.glEndQuery(&f.f, C.GLenum(target))
+}
+
+func (f *Functions) EnableVertexAttribArray(a Attrib) {
+ C.glEnableVertexAttribArray(&f.f, C.GLuint(a))
+}
+
+func (f *Functions) Finish() {
+ C.glFinish(&f.f)
+}
+
+func (f *Functions) Flush() {
+ C.glFlush(&f.f)
+}
+
+func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
+ C.glFramebufferRenderbuffer(&f.f, C.GLenum(target), C.GLenum(attachment), C.GLenum(renderbuffertarget), C.GLuint(renderbuffer.V))
+}
+
+func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
+ C.glFramebufferTexture2D(&f.f, C.GLenum(target), C.GLenum(attachment), C.GLenum(texTarget), C.GLuint(t.V), C.GLint(level))
+}
+
+func (c *Functions) GetBinding(pname Enum) Object {
+ return Object{uint(c.GetInteger(pname))}
+}
+
+func (c *Functions) GetBindingi(pname Enum, idx int) Object {
+ return Object{uint(c.GetIntegeri(pname, idx))}
+}
+
+func (f *Functions) GetError() Enum {
+ return Enum(C.glGetError(&f.f))
+}
+
+func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int {
+ C.glGetRenderbufferParameteriv(&f.f, C.GLenum(target), C.GLenum(pname), &f.ints[0])
+ return int(f.ints[0])
+}
+
+func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
+ C.glGetFramebufferAttachmentParameteriv(&f.f, C.GLenum(target), C.GLenum(attachment), C.GLenum(pname), &f.ints[0])
+ return int(f.ints[0])
+}
+
+func (f *Functions) GetFloat4(pname Enum) [4]float32 {
+ C.glGetFloatv(&f.f, C.GLenum(pname), &f.floats[0])
+ var r [4]float32
+ for i := range r {
+ r[i] = float32(f.floats[i])
+ }
+ return r
+}
+
+func (f *Functions) GetFloat(pname Enum) float32 {
+ C.glGetFloatv(&f.f, C.GLenum(pname), &f.floats[0])
+ return float32(f.floats[0])
+}
+
+func (f *Functions) GetInteger4(pname Enum) [4]int {
+ C.glGetIntegerv(&f.f, C.GLenum(pname), &f.ints[0])
+ var r [4]int
+ for i := range r {
+ r[i] = int(f.ints[i])
+ }
+ return r
+}
+
+func (f *Functions) GetInteger(pname Enum) int {
+ C.glGetIntegerv(&f.f, C.GLenum(pname), &f.ints[0])
+ return int(f.ints[0])
+}
+
+func (f *Functions) GetIntegeri(pname Enum, idx int) int {
+ C.glGetIntegeri_v(&f.f, C.GLenum(pname), C.GLuint(idx), &f.ints[0])
+ return int(f.ints[0])
+}
+
+func (f *Functions) GetProgrami(p Program, pname Enum) int {
+ C.glGetProgramiv(&f.f, C.GLuint(p.V), C.GLenum(pname), &f.ints[0])
+ return int(f.ints[0])
+}
+
+func (f *Functions) GetProgramBinary(p Program) []byte {
+ sz := f.GetProgrami(p, PROGRAM_BINARY_LENGTH)
+ if sz == 0 {
+ return nil
+ }
+ buf := make([]byte, sz)
+ var format C.GLenum
+ C.glGetProgramBinary(&f.f, C.GLuint(p.V), C.GLsizei(sz), nil, &format, unsafe.Pointer(&buf[0]))
+ return buf
+}
+
+func (f *Functions) GetProgramInfoLog(p Program) string {
+ n := f.GetProgrami(p, INFO_LOG_LENGTH)
+ buf := make([]byte, n)
+ C.glGetProgramInfoLog(&f.f, C.GLuint(p.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0])))
+ return string(buf)
+}
+
+func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
+ C.glGetQueryObjectuiv(&f.f, C.GLuint(query.V), C.GLenum(pname), &f.uints[0])
+ return uint(f.uints[0])
+}
+
+func (f *Functions) GetShaderi(s Shader, pname Enum) int {
+ C.glGetShaderiv(&f.f, C.GLuint(s.V), C.GLenum(pname), &f.ints[0])
+ return int(f.ints[0])
+}
+
+func (f *Functions) GetShaderInfoLog(s Shader) string {
+ n := f.GetShaderi(s, INFO_LOG_LENGTH)
+ buf := make([]byte, n)
+ C.glGetShaderInfoLog(&f.f, C.GLuint(s.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0])))
+ return string(buf)
+}
+
+func (f *Functions) getStringi(pname Enum, index int) string {
+ str := C.glGetStringi(&f.f, C.GLenum(pname), C.GLuint(index))
+ if str == nil {
+ return ""
+ }
+ return C.GoString((*C.char)(unsafe.Pointer(str)))
+}
+
+func (f *Functions) GetString(pname Enum) string {
+ switch {
+ case runtime.GOOS == "darwin" && pname == EXTENSIONS:
+ // macOS OpenGL 3 core profile doesn't support glGetString(GL_EXTENSIONS).
+ // Use glGetStringi(GL_EXTENSIONS, <index>).
+ var exts []string
+ nexts := f.GetInteger(NUM_EXTENSIONS)
+ for i := 0; i < nexts; i++ {
+ ext := f.getStringi(EXTENSIONS, i)
+ exts = append(exts, ext)
+ }
+ return strings.Join(exts, " ")
+ default:
+ str := C.glGetString(&f.f, C.GLenum(pname))
+ return C.GoString((*C.char)(unsafe.Pointer(str)))
+ }
+}
+
+func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+ return uint(C.glGetUniformBlockIndex(&f.f, C.GLuint(p.V), cname))
+}
+
+func (f *Functions) GetUniformLocation(p Program, name string) Uniform {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+ return Uniform{int(C.glGetUniformLocation(&f.f, C.GLuint(p.V), cname))}
+}
+
+func (f *Functions) GetVertexAttrib(index int, pname Enum) int {
+ C.glGetVertexAttribiv(&f.f, C.GLuint(index), C.GLenum(pname), &f.ints[0])
+ return int(f.ints[0])
+}
+
+func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
+ return Object{uint(f.GetVertexAttrib(index, pname))}
+}
+
+func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
+ ptr := C.glGetVertexAttribPointerv(&f.f, C.GLuint(index), C.GLenum(pname))
+ return uintptr(ptr)
+}
+
+func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
+ C.glInvalidateFramebuffer(&f.f, C.GLenum(target), C.GLenum(attachment))
+}
+
+func (f *Functions) IsEnabled(cap Enum) bool {
+ return C.glIsEnabled(&f.f, C.GLenum(cap)) == TRUE
+}
+
+func (f *Functions) LinkProgram(p Program) {
+ C.glLinkProgram(&f.f, C.GLuint(p.V))
+}
+
+func (f *Functions) PixelStorei(pname Enum, param int) {
+ C.glPixelStorei(&f.f, C.GLenum(pname), C.GLint(param))
+}
+
+func (f *Functions) MemoryBarrier(barriers Enum) {
+ C.glMemoryBarrier(&f.f, C.GLbitfield(barriers))
+}
+
+func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
+ p := C.glMapBufferRange(&f.f, C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(length), C.GLbitfield(access))
+ if p == nil {
+ return nil
+ }
+ return (*[1 << 30]byte)(p)[:length:length]
+}
+
+func (f *Functions) Scissor(x, y, width, height int32) {
+ C.glScissor(&f.f, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
+}
+
+func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
+ var p unsafe.Pointer
+ if len(data) > 0 {
+ p = unsafe.Pointer(&data[0])
+ }
+ C.glReadPixels(&f.f, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p)
+}
+
+func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
+ C.glRenderbufferStorage(&f.f, C.GLenum(target), C.GLenum(internalformat), C.GLsizei(width), C.GLsizei(height))
+}
+
+func (f *Functions) ShaderSource(s Shader, src string) {
+ csrc := C.CString(src)
+ defer C.free(unsafe.Pointer(csrc))
+ strlen := C.GLint(len(src))
+ C.glShaderSource(&f.f, C.GLuint(s.V), 1, &csrc, &strlen)
+}
+
+func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) {
+ C.glTexImage2D(&f.f, C.GLenum(target), C.GLint(level), C.GLint(internalFormat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(ty), nil)
+}
+
+func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
+ C.glTexStorage2D(&f.f, C.GLenum(target), C.GLsizei(levels), C.GLenum(internalFormat), C.GLsizei(width), C.GLsizei(height))
+}
+
+func (f *Functions) TexSubImage2D(target Enum, level int, x int, y int, width int, height int, format Enum, ty Enum, data []byte) {
+ var p unsafe.Pointer
+ if len(data) > 0 {
+ p = unsafe.Pointer(&data[0])
+ }
+ C.glTexSubImage2D(&f.f, C.GLenum(target), C.GLint(level), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p)
+}
+
+func (f *Functions) TexParameteri(target, pname Enum, param int) {
+ C.glTexParameteri(&f.f, C.GLenum(target), C.GLenum(pname), C.GLint(param))
+}
+
+func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
+ C.glUniformBlockBinding(&f.f, C.GLuint(p.V), C.GLuint(uniformBlockIndex), C.GLuint(uniformBlockBinding))
+}
+
+func (f *Functions) Uniform1f(dst Uniform, v float32) {
+ C.glUniform1f(&f.f, C.GLint(dst.V), C.GLfloat(v))
+}
+
+func (f *Functions) Uniform1i(dst Uniform, v int) {
+ C.glUniform1i(&f.f, C.GLint(dst.V), C.GLint(v))
+}
+
+func (f *Functions) Uniform2f(dst Uniform, v0 float32, v1 float32) {
+ C.glUniform2f(&f.f, C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1))
+}
+
+func (f *Functions) Uniform3f(dst Uniform, v0 float32, v1 float32, v2 float32) {
+ C.glUniform3f(&f.f, C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2))
+}
+
+func (f *Functions) Uniform4f(dst Uniform, v0 float32, v1 float32, v2 float32, v3 float32) {
+ C.glUniform4f(&f.f, C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3))
+}
+
+func (f *Functions) UseProgram(p Program) {
+ C.glUseProgram(&f.f, C.GLuint(p.V))
+}
+
+func (f *Functions) UnmapBuffer(target Enum) bool {
+ r := C.glUnmapBuffer(&f.f, C.GLenum(target))
+ return r == TRUE
+}
+
+func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride int, offset int) {
+ var n C.GLboolean = FALSE
+ if normalized {
+ n = TRUE
+ }
+ C.glVertexAttribPointer(&f.f, C.GLuint(dst), C.GLint(size), C.GLenum(ty), n, C.GLsizei(stride), C.uintptr_t(offset))
+}
+
+func (f *Functions) Viewport(x int, y int, width int, height int) {
+ C.glViewport(&f.f, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
+}
diff --git a/vendor/gioui.org/internal/gl/gl_windows.go b/vendor/gioui.org/internal/gl/gl_windows.go
new file mode 100644
index 0000000..99b1694
--- /dev/null
+++ b/vendor/gioui.org/internal/gl/gl_windows.go
@@ -0,0 +1,502 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gl
+
+import (
+ "math"
+ "runtime"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var (
+ LibGLESv2 = windows.NewLazyDLL("libGLESv2.dll")
+ _glActiveTexture = LibGLESv2.NewProc("glActiveTexture")
+ _glAttachShader = LibGLESv2.NewProc("glAttachShader")
+ _glBeginQuery = LibGLESv2.NewProc("glBeginQuery")
+ _glBindAttribLocation = LibGLESv2.NewProc("glBindAttribLocation")
+ _glBindBuffer = LibGLESv2.NewProc("glBindBuffer")
+ _glBindBufferBase = LibGLESv2.NewProc("glBindBufferBase")
+ _glBindFramebuffer = LibGLESv2.NewProc("glBindFramebuffer")
+ _glBindRenderbuffer = LibGLESv2.NewProc("glBindRenderbuffer")
+ _glBindTexture = LibGLESv2.NewProc("glBindTexture")
+ _glBindVertexArray = LibGLESv2.NewProc("glBindVertexArray")
+ _glBlendEquation = LibGLESv2.NewProc("glBlendEquation")
+ _glBlendFuncSeparate = LibGLESv2.NewProc("glBlendFuncSeparate")
+ _glBufferData = LibGLESv2.NewProc("glBufferData")
+ _glBufferSubData = LibGLESv2.NewProc("glBufferSubData")
+ _glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus")
+ _glClear = LibGLESv2.NewProc("glClear")
+ _glClearColor = LibGLESv2.NewProc("glClearColor")
+ _glClearDepthf = LibGLESv2.NewProc("glClearDepthf")
+ _glDeleteQueries = LibGLESv2.NewProc("glDeleteQueries")
+ _glDeleteVertexArrays = LibGLESv2.NewProc("glDeleteVertexArrays")
+ _glCompileShader = LibGLESv2.NewProc("glCompileShader")
+ _glCopyTexSubImage2D = LibGLESv2.NewProc("glCopyTexSubImage2D")
+ _glGenBuffers = LibGLESv2.NewProc("glGenBuffers")
+ _glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers")
+ _glGenVertexArrays = LibGLESv2.NewProc("glGenVertexArrays")
+ _glGetUniformBlockIndex = LibGLESv2.NewProc("glGetUniformBlockIndex")
+ _glCreateProgram = LibGLESv2.NewProc("glCreateProgram")
+ _glGenRenderbuffers = LibGLESv2.NewProc("glGenRenderbuffers")
+ _glCreateShader = LibGLESv2.NewProc("glCreateShader")
+ _glGenTextures = LibGLESv2.NewProc("glGenTextures")
+ _glDeleteBuffers = LibGLESv2.NewProc("glDeleteBuffers")
+ _glDeleteFramebuffers = LibGLESv2.NewProc("glDeleteFramebuffers")
+ _glDeleteProgram = LibGLESv2.NewProc("glDeleteProgram")
+ _glDeleteShader = LibGLESv2.NewProc("glDeleteShader")
+ _glDeleteRenderbuffers = LibGLESv2.NewProc("glDeleteRenderbuffers")
+ _glDeleteTextures = LibGLESv2.NewProc("glDeleteTextures")
+ _glDepthFunc = LibGLESv2.NewProc("glDepthFunc")
+ _glDepthMask = LibGLESv2.NewProc("glDepthMask")
+ _glDisableVertexAttribArray = LibGLESv2.NewProc("glDisableVertexAttribArray")
+ _glDisable = LibGLESv2.NewProc("glDisable")
+ _glDrawArrays = LibGLESv2.NewProc("glDrawArrays")
+ _glDrawElements = LibGLESv2.NewProc("glDrawElements")
+ _glEnable = LibGLESv2.NewProc("glEnable")
+ _glEnableVertexAttribArray = LibGLESv2.NewProc("glEnableVertexAttribArray")
+ _glEndQuery = LibGLESv2.NewProc("glEndQuery")
+ _glFinish = LibGLESv2.NewProc("glFinish")
+ _glFlush = LibGLESv2.NewProc("glFlush")
+ _glFramebufferRenderbuffer = LibGLESv2.NewProc("glFramebufferRenderbuffer")
+ _glFramebufferTexture2D = LibGLESv2.NewProc("glFramebufferTexture2D")
+ _glGenQueries = LibGLESv2.NewProc("glGenQueries")
+ _glGetError = LibGLESv2.NewProc("glGetError")
+ _glGetRenderbufferParameteriv = LibGLESv2.NewProc("glGetRenderbufferParameteriv")
+ _glGetFloatv = LibGLESv2.NewProc("glGetFloatv")
+ _glGetFramebufferAttachmentParameteriv = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteriv")
+ _glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv")
+ _glGetIntegeri_v = LibGLESv2.NewProc("glGetIntegeri_v")
+ _glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv")
+ _glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog")
+ _glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv")
+ _glGetShaderiv = LibGLESv2.NewProc("glGetShaderiv")
+ _glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog")
+ _glGetString = LibGLESv2.NewProc("glGetString")
+ _glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation")
+ _glGetVertexAttribiv = LibGLESv2.NewProc("glGetVertexAttribiv")
+ _glGetVertexAttribPointerv = LibGLESv2.NewProc("glGetVertexAttribPointerv")
+ _glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer")
+ _glIsEnabled = LibGLESv2.NewProc("glIsEnabled")
+ _glLinkProgram = LibGLESv2.NewProc("glLinkProgram")
+ _glPixelStorei = LibGLESv2.NewProc("glPixelStorei")
+ _glReadPixels = LibGLESv2.NewProc("glReadPixels")
+ _glRenderbufferStorage = LibGLESv2.NewProc("glRenderbufferStorage")
+ _glScissor = LibGLESv2.NewProc("glScissor")
+ _glShaderSource = LibGLESv2.NewProc("glShaderSource")
+ _glTexImage2D = LibGLESv2.NewProc("glTexImage2D")
+ _glTexStorage2D = LibGLESv2.NewProc("glTexStorage2D")
+ _glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D")
+ _glTexParameteri = LibGLESv2.NewProc("glTexParameteri")
+ _glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding")
+ _glUniform1f = LibGLESv2.NewProc("glUniform1f")
+ _glUniform1i = LibGLESv2.NewProc("glUniform1i")
+ _glUniform2f = LibGLESv2.NewProc("glUniform2f")
+ _glUniform3f = LibGLESv2.NewProc("glUniform3f")
+ _glUniform4f = LibGLESv2.NewProc("glUniform4f")
+ _glUseProgram = LibGLESv2.NewProc("glUseProgram")
+ _glVertexAttribPointer = LibGLESv2.NewProc("glVertexAttribPointer")
+ _glViewport = LibGLESv2.NewProc("glViewport")
+)
+
+type Functions struct {
+ // Query caches.
+ int32s [100]int32
+ float32s [100]float32
+ uintptrs [100]uintptr
+}
+
+type Context interface{}
+
+func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
+ if ctx != nil {
+ panic("non-nil context")
+ }
+ return new(Functions), nil
+}
+
+func (c *Functions) ActiveTexture(t Enum) {
+ syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0)
+}
+func (c *Functions) AttachShader(p Program, s Shader) {
+ syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0)
+}
+func (f *Functions) BeginQuery(target Enum, query Query) {
+ syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0)
+}
+func (c *Functions) BindAttribLocation(p Program, a Attrib, name string) {
+ cname := cString(name)
+ c0 := &cname[0]
+ syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0)))
+ issue34474KeepAlive(c)
+}
+func (c *Functions) BindBuffer(target Enum, b Buffer) {
+ syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0)
+}
+func (c *Functions) BindBufferBase(target Enum, index int, b Buffer) {
+ syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V))
+}
+func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
+ syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0)
+}
+func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
+ syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0)
+}
+func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
+ panic("not implemented")
+}
+func (c *Functions) BindTexture(target Enum, t Texture) {
+ syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0)
+}
+func (c *Functions) BindVertexArray(a VertexArray) {
+ syscall.Syscall(_glBindVertexArray.Addr(), 1, uintptr(a.V), 0, 0)
+}
+func (c *Functions) BlendEquation(mode Enum) {
+ syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0)
+}
+func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
+ syscall.Syscall6(_glBlendFuncSeparate.Addr(), 4, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcA), uintptr(dstA), 0, 0)
+}
+func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
+ var p unsafe.Pointer
+ if len(data) > 0 {
+ p = unsafe.Pointer(&data[0])
+ }
+ syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(size), uintptr(p), uintptr(usage), 0, 0)
+}
+func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
+ if n := len(src); n > 0 {
+ s0 := &src[0]
+ syscall.Syscall6(_glBufferSubData.Addr(), 4, uintptr(target), uintptr(offset), uintptr(n), uintptr(unsafe.Pointer(s0)), 0, 0)
+ issue34474KeepAlive(s0)
+ }
+}
+func (c *Functions) CheckFramebufferStatus(target Enum) Enum {
+ s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0)
+ return Enum(s)
+}
+func (c *Functions) Clear(mask Enum) {
+ syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0)
+}
+func (c *Functions) ClearColor(red, green, blue, alpha float32) {
+ syscall.Syscall6(_glClearColor.Addr(), 4, uintptr(math.Float32bits(red)), uintptr(math.Float32bits(green)), uintptr(math.Float32bits(blue)), uintptr(math.Float32bits(alpha)), 0, 0)
+}
+func (c *Functions) ClearDepthf(d float32) {
+ syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0)
+}
+func (c *Functions) CompileShader(s Shader) {
+ syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0)
+}
+func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
+ syscall.Syscall9(_glCopyTexSubImage2D.Addr(), 8, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0)
+}
+func (c *Functions) CreateBuffer() Buffer {
+ var buf uintptr
+ syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
+ return Buffer{uint(buf)}
+}
+func (c *Functions) CreateFramebuffer() Framebuffer {
+ var fb uintptr
+ syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0)
+ return Framebuffer{uint(fb)}
+}
+func (c *Functions) CreateProgram() Program {
+ p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0)
+ return Program{uint(p)}
+}
+func (f *Functions) CreateQuery() Query {
+ var q uintptr
+ syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0)
+ return Query{uint(q)}
+}
+func (c *Functions) CreateRenderbuffer() Renderbuffer {
+ var rb uintptr
+ syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0)
+ return Renderbuffer{uint(rb)}
+}
+func (c *Functions) CreateShader(ty Enum) Shader {
+ s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0)
+ return Shader{uint(s)}
+}
+func (c *Functions) CreateTexture() Texture {
+ var t uintptr
+ syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
+ return Texture{uint(t)}
+}
+func (c *Functions) CreateVertexArray() VertexArray {
+ var t uintptr
+ syscall.Syscall(_glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
+ return VertexArray{uint(t)}
+}
+func (c *Functions) DeleteBuffer(v Buffer) {
+ syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0)
+}
+func (c *Functions) DeleteFramebuffer(v Framebuffer) {
+ syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
+}
+func (c *Functions) DeleteProgram(p Program) {
+ syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0)
+}
+func (f *Functions) DeleteQuery(query Query) {
+ syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0)
+}
+func (c *Functions) DeleteShader(s Shader) {
+ syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0)
+}
+func (c *Functions) DeleteRenderbuffer(v Renderbuffer) {
+ syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
+}
+func (c *Functions) DeleteTexture(v Texture) {
+ syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
+}
+func (f *Functions) DeleteVertexArray(array VertexArray) {
+ syscall.Syscall(_glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&array.V)), 0)
+}
+func (c *Functions) DepthFunc(f Enum) {
+ syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0)
+}
+func (c *Functions) DepthMask(mask bool) {
+ var m uintptr
+ if mask {
+ m = 1
+ }
+ syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0)
+}
+func (c *Functions) DisableVertexAttribArray(a Attrib) {
+ syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
+}
+func (c *Functions) Disable(cap Enum) {
+ syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0)
+}
+func (c *Functions) DrawArrays(mode Enum, first, count int) {
+ syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count))
+}
+func (c *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
+ syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0)
+}
+func (f *Functions) DispatchCompute(x, y, z int) {
+ panic("not implemented")
+}
+func (c *Functions) Enable(cap Enum) {
+ syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0)
+}
+func (c *Functions) EnableVertexAttribArray(a Attrib) {
+ syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
+}
+func (f *Functions) EndQuery(target Enum) {
+ syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0)
+}
+func (c *Functions) Finish() {
+ syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0)
+}
+func (c *Functions) Flush() {
+ syscall.Syscall(_glFlush.Addr(), 0, 0, 0, 0)
+}
+func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
+ syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0)
+}
+func (c *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
+ syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0)
+}
+func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
+ cname := cString(name)
+ c0 := &cname[0]
+ u, _, _ := syscall.Syscall(_glGetUniformBlockIndex.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0)
+ issue34474KeepAlive(c0)
+ return uint(u)
+}
+func (c *Functions) GetBinding(pname Enum) Object {
+ return Object{uint(c.GetInteger(pname))}
+}
+func (c *Functions) GetBindingi(pname Enum, idx int) Object {
+ return Object{uint(c.GetIntegeri(pname, idx))}
+}
+func (c *Functions) GetError() Enum {
+ e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0)
+ return Enum(e)
+}
+func (c *Functions) GetRenderbufferParameteri(target, pname Enum) int {
+ syscall.Syscall(_glGetRenderbufferParameteriv.Addr(), 3, uintptr(target), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
+ return int(c.int32s[0])
+}
+func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
+ syscall.Syscall6(_glGetFramebufferAttachmentParameteriv.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0, 0)
+ return int(c.int32s[0])
+}
+func (c *Functions) GetInteger4(pname Enum) [4]int {
+ syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
+ var r [4]int
+ for i := range r {
+ r[i] = int(c.int32s[i])
+ }
+ return r
+}
+func (c *Functions) GetInteger(pname Enum) int {
+ syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
+ return int(c.int32s[0])
+}
+func (c *Functions) GetIntegeri(pname Enum, idx int) int {
+ syscall.Syscall(_glGetIntegeri_v.Addr(), 3, uintptr(pname), uintptr(idx), uintptr(unsafe.Pointer(&c.int32s[0])))
+ return int(c.int32s[0])
+}
+func (c *Functions) GetFloat(pname Enum) float32 {
+ syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
+ return c.float32s[0]
+}
+func (c *Functions) GetFloat4(pname Enum) [4]float32 {
+ syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
+ var r [4]float32
+ copy(r[:], c.float32s[:])
+ return r
+}
+func (c *Functions) GetProgrami(p Program, pname Enum) int {
+ syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
+ return int(c.int32s[0])
+}
+func (c *Functions) GetProgramInfoLog(p Program) string {
+ n := c.GetProgrami(p, INFO_LOG_LENGTH)
+ buf := make([]byte, n)
+ syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
+ return string(buf)
+}
+func (c *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
+ syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
+ return uint(c.int32s[0])
+}
+func (c *Functions) GetShaderi(s Shader, pname Enum) int {
+ syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
+ return int(c.int32s[0])
+}
+func (c *Functions) GetShaderInfoLog(s Shader) string {
+ n := c.GetShaderi(s, INFO_LOG_LENGTH)
+ buf := make([]byte, n)
+ syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
+ return string(buf)
+}
+func (c *Functions) GetString(pname Enum) string {
+ s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0)
+ return windows.BytePtrToString((*byte)(unsafe.Pointer(s)))
+}
+func (c *Functions) GetUniformLocation(p Program, name string) Uniform {
+ cname := cString(name)
+ c0 := &cname[0]
+ u, _, _ := syscall.Syscall(_glGetUniformLocation.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0)
+ issue34474KeepAlive(c0)
+ return Uniform{int(u)}
+}
+func (c *Functions) GetVertexAttrib(index int, pname Enum) int {
+ syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
+ return int(c.int32s[0])
+}
+
+func (c *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
+ return Object{uint(c.GetVertexAttrib(index, pname))}
+}
+
+func (c *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
+ syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0])))
+ return c.uintptrs[0]
+}
+func (c *Functions) InvalidateFramebuffer(target, attachment Enum) {
+ addr := _glInvalidateFramebuffer.Addr()
+ if addr == 0 {
+ // InvalidateFramebuffer is just a hint. Skip it if not supported.
+ return
+ }
+ syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment)))
+}
+func (f *Functions) IsEnabled(cap Enum) bool {
+ u, _, _ := syscall.Syscall(_glIsEnabled.Addr(), 1, uintptr(cap), 0, 0)
+ return u == TRUE
+}
+func (c *Functions) LinkProgram(p Program) {
+ syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0)
+}
+func (c *Functions) PixelStorei(pname Enum, param int) {
+ syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0)
+}
+func (f *Functions) MemoryBarrier(barriers Enum) {
+ panic("not implemented")
+}
+func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
+ panic("not implemented")
+}
+func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
+ d0 := &data[0]
+ syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0)
+ issue34474KeepAlive(d0)
+}
+func (c *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
+ syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0)
+}
+func (c *Functions) Scissor(x, y, width, height int32) {
+ syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
+}
+func (c *Functions) ShaderSource(s Shader, src string) {
+ var n uintptr = uintptr(len(src))
+ psrc := &src
+ syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0)
+ issue34474KeepAlive(psrc)
+}
+func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) {
+ syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0)
+}
+func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
+ syscall.Syscall6(_glTexStorage2D.Addr(), 5, uintptr(target), uintptr(levels), uintptr(internalFormat), uintptr(width), uintptr(height), 0)
+}
+func (c *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
+ d0 := &data[0]
+ syscall.Syscall9(_glTexSubImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)))
+ issue34474KeepAlive(d0)
+}
+func (c *Functions) TexParameteri(target, pname Enum, param int) {
+ syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param))
+}
+func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
+ syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding))
+}
+func (c *Functions) Uniform1f(dst Uniform, v float32) {
+ syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0)
+}
+func (c *Functions) Uniform1i(dst Uniform, v int) {
+ syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0)
+}
+func (c *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
+ syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)))
+}
+func (c *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
+ syscall.Syscall6(_glUniform3f.Addr(), 4, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), 0, 0)
+}
+func (c *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
+ syscall.Syscall6(_glUniform4f.Addr(), 5, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), uintptr(math.Float32bits(v3)), 0)
+}
+func (c *Functions) UseProgram(p Program) {
+ syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0)
+}
+func (f *Functions) UnmapBuffer(target Enum) bool {
+ panic("not implemented")
+}
+func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
+ var norm uintptr
+ if normalized {
+ norm = 1
+ }
+ syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset))
+}
+func (c *Functions) Viewport(x, y, width, height int) {
+ syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
+}
+
+func cString(s string) []byte {
+ b := make([]byte, len(s)+1)
+ copy(b, s)
+ return b
+}
+
+// issue34474KeepAlive calls runtime.KeepAlive as a
+// workaround for golang.org/issue/34474.
+func issue34474KeepAlive(v interface{}) {
+ runtime.KeepAlive(v)
+}
diff --git a/vendor/gioui.org/internal/gl/types.go b/vendor/gioui.org/internal/gl/types.go
new file mode 100644
index 0000000..dd24963
--- /dev/null
+++ b/vendor/gioui.org/internal/gl/types.go
@@ -0,0 +1,77 @@
+//go:build !js
+// +build !js
+
+package gl
+
+type (
+ Object struct{ V uint }
+ Buffer Object
+ Framebuffer Object
+ Program Object
+ Renderbuffer Object
+ Shader Object
+ Texture Object
+ Query Object
+ Uniform struct{ V int }
+ VertexArray Object
+)
+
+func (o Object) valid() bool {
+ return o.V != 0
+}
+
+func (o Object) equal(o2 Object) bool {
+ return o == o2
+}
+
+func (u Framebuffer) Valid() bool {
+ return Object(u).valid()
+}
+
+func (u Uniform) Valid() bool {
+ return u.V != -1
+}
+
+func (p Program) Valid() bool {
+ return Object(p).valid()
+}
+
+func (s Shader) Valid() bool {
+ return Object(s).valid()
+}
+
+func (a VertexArray) Valid() bool {
+ return Object(a).valid()
+}
+
+func (f Framebuffer) Equal(f2 Framebuffer) bool {
+ return Object(f).equal(Object(f2))
+}
+
+func (p Program) Equal(p2 Program) bool {
+ return Object(p).equal(Object(p2))
+}
+
+func (s Shader) Equal(s2 Shader) bool {
+ return Object(s).equal(Object(s2))
+}
+
+func (u Uniform) Equal(u2 Uniform) bool {
+ return u == u2
+}
+
+func (a VertexArray) Equal(a2 VertexArray) bool {
+ return Object(a).equal(Object(a2))
+}
+
+func (r Renderbuffer) Equal(r2 Renderbuffer) bool {
+ return Object(r).equal(Object(r2))
+}
+
+func (t Texture) Equal(t2 Texture) bool {
+ return Object(t).equal(Object(t2))
+}
+
+func (b Buffer) Equal(b2 Buffer) bool {
+ return Object(b).equal(Object(b2))
+}
diff --git a/vendor/gioui.org/internal/gl/types_js.go b/vendor/gioui.org/internal/gl/types_js.go
new file mode 100644
index 0000000..8d91a6b
--- /dev/null
+++ b/vendor/gioui.org/internal/gl/types_js.go
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gl
+
+import "syscall/js"
+
+type (
+ Object js.Value
+ Buffer Object
+ Framebuffer Object
+ Program Object
+ Renderbuffer Object
+ Shader Object
+ Texture Object
+ Query Object
+ Uniform Object
+ VertexArray Object
+)
+
+func (o Object) valid() bool {
+ return js.Value(o).Truthy()
+}
+
+func (o Object) equal(o2 Object) bool {
+ return js.Value(o).Equal(js.Value(o2))
+}
+
+func (b Buffer) Valid() bool {
+ return Object(b).valid()
+}
+
+func (f Framebuffer) Valid() bool {
+ return Object(f).valid()
+}
+
+func (p Program) Valid() bool {
+ return Object(p).valid()
+}
+
+func (r Renderbuffer) Valid() bool {
+ return Object(r).valid()
+}
+
+func (s Shader) Valid() bool {
+ return Object(s).valid()
+}
+
+func (t Texture) Valid() bool {
+ return Object(t).valid()
+}
+
+func (u Uniform) Valid() bool {
+ return Object(u).valid()
+}
+
+func (a VertexArray) Valid() bool {
+ return Object(a).valid()
+}
+
+func (f Framebuffer) Equal(f2 Framebuffer) bool {
+ return Object(f).equal(Object(f2))
+}
+
+func (p Program) Equal(p2 Program) bool {
+ return Object(p).equal(Object(p2))
+}
+
+func (s Shader) Equal(s2 Shader) bool {
+ return Object(s).equal(Object(s2))
+}
+
+func (u Uniform) Equal(u2 Uniform) bool {
+ return Object(u).equal(Object(u2))
+}
+
+func (a VertexArray) Equal(a2 VertexArray) bool {
+ return Object(a).equal(Object(a2))
+}
+
+func (r Renderbuffer) Equal(r2 Renderbuffer) bool {
+ return Object(r).equal(Object(r2))
+}
+
+func (t Texture) Equal(t2 Texture) bool {
+ return Object(t).equal(Object(t2))
+}
+
+func (b Buffer) Equal(b2 Buffer) bool {
+ return Object(b).equal(Object(b2))
+}
diff --git a/vendor/gioui.org/gpu/gl/util.go b/vendor/gioui.org/internal/gl/util.go
index 65b2155..c696b69 100644
--- a/vendor/gioui.org/gpu/gl/util.go
+++ b/vendor/gioui.org/internal/gl/util.go
@@ -8,19 +8,19 @@ import (
"strings"
)
-func CreateProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Program, error) {
- vs, err := createShader(ctx, VERTEX_SHADER, vsSrc)
+func CreateProgram(ctx *Functions, vsSrc, fsSrc string, attribs []string) (Program, error) {
+ vs, err := CreateShader(ctx, VERTEX_SHADER, vsSrc)
if err != nil {
return Program{}, err
}
defer ctx.DeleteShader(vs)
- fs, err := createShader(ctx, FRAGMENT_SHADER, fsSrc)
+ fs, err := CreateShader(ctx, FRAGMENT_SHADER, fsSrc)
if err != nil {
return Program{}, err
}
defer ctx.DeleteShader(fs)
prog := ctx.CreateProgram()
- if !prog.valid() {
+ if !prog.Valid() {
return Program{}, errors.New("glCreateProgram failed")
}
ctx.AttachShader(prog, vs)
@@ -37,17 +37,29 @@ func CreateProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Progra
return prog, nil
}
-func GetUniformLocation(ctx Functions, prog Program, name string) Uniform {
- loc := ctx.GetUniformLocation(prog, name)
- if !loc.valid() {
- panic(fmt.Errorf("uniform %s not found", name))
+func CreateComputeProgram(ctx *Functions, src string) (Program, error) {
+ cs, err := CreateShader(ctx, COMPUTE_SHADER, src)
+ if err != nil {
+ return Program{}, err
+ }
+ defer ctx.DeleteShader(cs)
+ prog := ctx.CreateProgram()
+ if !prog.Valid() {
+ return Program{}, errors.New("glCreateProgram failed")
+ }
+ ctx.AttachShader(prog, cs)
+ ctx.LinkProgram(prog)
+ if ctx.GetProgrami(prog, LINK_STATUS) == 0 {
+ log := ctx.GetProgramInfoLog(prog)
+ ctx.DeleteProgram(prog)
+ return Program{}, fmt.Errorf("program link failed: %s", strings.TrimSpace(log))
}
- return loc
+ return prog, nil
}
-func createShader(ctx Functions, typ Enum, src string) (Shader, error) {
+func CreateShader(ctx *Functions, typ Enum, src string) (Shader, error) {
sh := ctx.CreateShader(typ)
- if !sh.valid() {
+ if !sh.Valid() {
return Shader{}, errors.New("glCreateShader failed")
}
ctx.ShaderSource(sh, src)
diff --git a/vendor/gioui.org/internal/opconst/ops.go b/vendor/gioui.org/internal/opconst/ops.go
deleted file mode 100644
index 62365b8..0000000
--- a/vendor/gioui.org/internal/opconst/ops.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package opconst
-
-type OpType byte
-
-// Start at a high number for easier debugging.
-const firstOpIndex = 200
-
-const (
- TypeMacro OpType = iota + firstOpIndex
- TypeCall
- TypeTransform
- TypeLayer
- TypeInvalidate
- TypeImage
- TypePaint
- TypeColor
- TypeArea
- TypePointerInput
- TypePass
- TypeKeyInput
- TypeHideInput
- TypePush
- TypePop
- TypeAux
- TypeClip
- TypeProfile
-)
-
-const (
- TypeMacroLen = 1 + 4 + 4
- TypeCallLen = 1 + 4 + 4
- TypeTransformLen = 1 + 4*6
- TypeLayerLen = 1
- TypeRedrawLen = 1 + 8
- TypeImageLen = 1 + 4*4
- TypePaintLen = 1 + 4*4
- TypeColorLen = 1 + 4
- TypeAreaLen = 1 + 1 + 4*4
- TypePointerInputLen = 1 + 1 + 1
- TypePassLen = 1 + 1
- TypeKeyInputLen = 1 + 1
- TypeHideInputLen = 1
- TypePushLen = 1
- TypePopLen = 1
- TypeAuxLen = 1
- TypeClipLen = 1 + 4*4
- TypeProfileLen = 1
-)
-
-func (t OpType) Size() int {
- return [...]int{
- TypeMacroLen,
- TypeCallLen,
- TypeTransformLen,
- TypeLayerLen,
- TypeRedrawLen,
- TypeImageLen,
- TypePaintLen,
- TypeColorLen,
- TypeAreaLen,
- TypePointerInputLen,
- TypePassLen,
- TypeKeyInputLen,
- TypeHideInputLen,
- TypePushLen,
- TypePopLen,
- TypeAuxLen,
- TypeClipLen,
- TypeProfileLen,
- }[t-firstOpIndex]
-}
-
-func (t OpType) NumRefs() int {
- switch t {
- case TypeKeyInput, TypePointerInput, TypeProfile, TypeCall:
- return 1
- case TypeImage:
- return 2
- default:
- return 0
- }
-}
diff --git a/vendor/gioui.org/internal/ops/ops.go b/vendor/gioui.org/internal/ops/ops.go
index c56b9d6..fece6d6 100644
--- a/vendor/gioui.org/internal/ops/ops.go
+++ b/vendor/gioui.org/internal/ops/ops.go
@@ -4,57 +4,321 @@ package ops
import (
"encoding/binary"
+ "image"
"math"
"gioui.org/f32"
- "gioui.org/internal/opconst"
+ "gioui.org/internal/byteslice"
+ "gioui.org/internal/scene"
)
-const QuadSize = 4 * 2 * 3
+type Ops struct {
+ // version is incremented at each Reset.
+ version int
+ // data contains the serialized operations.
+ data []byte
+ // refs hold external references for operations.
+ refs []interface{}
+ // nextStateID is the id allocated for the next
+ // StateOp.
+ nextStateID int
-type Quad struct {
- From, Ctrl, To f32.Point
+ macroStack stack
+ stacks [5]stack
}
-func (q Quad) Transform(t f32.Affine2D) Quad {
- q.From = t.Transform(q.From)
- q.Ctrl = t.Transform(q.Ctrl)
- q.To = t.Transform(q.To)
- return q
+type OpType byte
+
+type Shape byte
+
+// Start at a high number for easier debugging.
+const firstOpIndex = 200
+
+const (
+ TypeMacro OpType = iota + firstOpIndex
+ TypeCall
+ TypeDefer
+ TypePushTransform
+ TypeTransform
+ TypePopTransform
+ TypeInvalidate
+ TypeImage
+ TypePaint
+ TypeColor
+ TypeLinearGradient
+ TypePass
+ TypePopPass
+ TypePointerInput
+ TypeClipboardRead
+ TypeClipboardWrite
+ TypeSource
+ TypeTarget
+ TypeOffer
+ TypeKeyInput
+ TypeKeyFocus
+ TypeKeySoftKeyboard
+ TypeSave
+ TypeLoad
+ TypeAux
+ TypeClip
+ TypePopClip
+ TypeProfile
+ TypeCursor
+ TypePath
+ TypeStroke
+ TypeSemanticLabel
+ TypeSemanticDesc
+ TypeSemanticClass
+ TypeSemanticSelected
+ TypeSemanticDisabled
+)
+
+type StackID struct {
+ id int
+ prev int
+}
+
+// StateOp represents a saved operation snapshop to be restored
+// later.
+type StateOp struct {
+ id int
+ macroID int
+ ops *Ops
+}
+
+// stack tracks the integer identities of stack operations to ensure correct
+// pairing of their push and pop methods.
+type stack struct {
+ currentID int
+ nextID int
+}
+
+type StackKind uint8
+
+// ClipOp is the shadow of clip.Op.
+type ClipOp struct {
+ Bounds image.Rectangle
+ Outline bool
+ Shape Shape
}
-func EncodeQuad(d []byte, q Quad) {
+const (
+ ClipStack StackKind = iota
+ TransStack
+ PassStack
+ MetaStack
+)
+
+const (
+ Path Shape = iota
+ Ellipse
+ Rect
+)
+
+const (
+ TypeMacroLen = 1 + 4 + 4
+ TypeCallLen = 1 + 4 + 4
+ TypeDeferLen = 1
+ TypePushTransformLen = 1 + 4*6
+ TypeTransformLen = 1 + 1 + 4*6
+ TypePopTransformLen = 1
+ TypeRedrawLen = 1 + 8
+ TypeImageLen = 1
+ TypePaintLen = 1
+ TypeColorLen = 1 + 4
+ TypeLinearGradientLen = 1 + 8*2 + 4*2
+ TypePassLen = 1
+ TypePopPassLen = 1
+ TypePointerInputLen = 1 + 1 + 1*2 + 2*4 + 2*4
+ TypeClipboardReadLen = 1
+ TypeClipboardWriteLen = 1
+ TypeSourceLen = 1
+ TypeTargetLen = 1
+ TypeOfferLen = 1
+ TypeKeyInputLen = 1 + 1
+ TypeKeyFocusLen = 1 + 1
+ TypeKeySoftKeyboardLen = 1 + 1
+ TypeSaveLen = 1 + 4
+ TypeLoadLen = 1 + 4
+ TypeAuxLen = 1
+ TypeClipLen = 1 + 4*4 + 1 + 1
+ TypePopClipLen = 1
+ TypeProfileLen = 1
+ TypeCursorLen = 1 + 1
+ TypePathLen = 8 + 1
+ TypeStrokeLen = 1 + 4
+ TypeSemanticLabelLen = 1
+ TypeSemanticDescLen = 1
+ TypeSemanticClassLen = 2
+ TypeSemanticSelectedLen = 2
+ TypeSemanticDisabledLen = 2
+)
+
+func (op *ClipOp) Decode(data []byte) {
+ if OpType(data[0]) != TypeClip {
+ panic("invalid op")
+ }
bo := binary.LittleEndian
- bo.PutUint32(d[0:], math.Float32bits(q.From.X))
- bo.PutUint32(d[4:], math.Float32bits(q.From.Y))
- bo.PutUint32(d[8:], math.Float32bits(q.Ctrl.X))
- bo.PutUint32(d[12:], math.Float32bits(q.Ctrl.Y))
- bo.PutUint32(d[16:], math.Float32bits(q.To.X))
- bo.PutUint32(d[20:], math.Float32bits(q.To.Y))
+ r := image.Rectangle{
+ Min: image.Point{
+ X: int(int32(bo.Uint32(data[1:]))),
+ Y: int(int32(bo.Uint32(data[5:]))),
+ },
+ Max: image.Point{
+ X: int(int32(bo.Uint32(data[9:]))),
+ Y: int(int32(bo.Uint32(data[13:]))),
+ },
+ }
+ *op = ClipOp{
+ Bounds: r,
+ Outline: data[17] == 1,
+ Shape: Shape(data[18]),
+ }
}
-func DecodeQuad(d []byte) (q Quad) {
+func Reset(o *Ops) {
+ o.macroStack = stack{}
+ for i := range o.stacks {
+ o.stacks[i] = stack{}
+ }
+ // Leave references to the GC.
+ for i := range o.refs {
+ o.refs[i] = nil
+ }
+ o.data = o.data[:0]
+ o.refs = o.refs[:0]
+ o.nextStateID = 0
+ o.version++
+}
+
+func Write(o *Ops, n int) []byte {
+ o.data = append(o.data, make([]byte, n)...)
+ return o.data[len(o.data)-n:]
+}
+
+func PushMacro(o *Ops) StackID {
+ return o.macroStack.push()
+}
+
+func PopMacro(o *Ops, id StackID) {
+ o.macroStack.pop(id)
+}
+
+func FillMacro(o *Ops, startPC PC) {
+ pc := PCFor(o)
+ // Fill out the macro definition reserved in Record.
+ data := o.data[startPC.data:]
+ data = data[:TypeMacroLen]
+ data[0] = byte(TypeMacro)
bo := binary.LittleEndian
- q.From.X = math.Float32frombits(bo.Uint32(d[0:]))
- q.From.Y = math.Float32frombits(bo.Uint32(d[4:]))
- q.Ctrl.X = math.Float32frombits(bo.Uint32(d[8:]))
- q.Ctrl.Y = math.Float32frombits(bo.Uint32(d[12:]))
- q.To.X = math.Float32frombits(bo.Uint32(d[16:]))
- q.To.Y = math.Float32frombits(bo.Uint32(d[20:]))
- return
+ bo.PutUint32(data[1:], uint32(pc.data))
+ bo.PutUint32(data[5:], uint32(pc.refs))
}
-func DecodeTransform(d []byte) (t f32.Affine2D) {
- if opconst.OpType(d[0]) != opconst.TypeTransform {
- panic("invalid op")
+func AddCall(o *Ops, callOps *Ops, pc PC) {
+ data := Write1(o, TypeCallLen, callOps)
+ data[0] = byte(TypeCall)
+ bo := binary.LittleEndian
+ bo.PutUint32(data[1:], uint32(pc.data))
+ bo.PutUint32(data[5:], uint32(pc.refs))
+}
+
+func PushOp(o *Ops, kind StackKind) (StackID, int) {
+ return o.stacks[kind].push(), o.macroStack.currentID
+}
+
+func PopOp(o *Ops, kind StackKind, sid StackID, macroID int) {
+ if o.macroStack.currentID != macroID {
+ panic("stack push and pop must not cross macro boundary")
}
- if len(d) < 1+6*4 {
- panic("too short buffer")
+ o.stacks[kind].pop(sid)
+}
+
+func Write1(o *Ops, n int, ref1 interface{}) []byte {
+ o.data = append(o.data, make([]byte, n)...)
+ o.refs = append(o.refs, ref1)
+ return o.data[len(o.data)-n:]
+}
+
+func Write2(o *Ops, n int, ref1, ref2 interface{}) []byte {
+ o.data = append(o.data, make([]byte, n)...)
+ o.refs = append(o.refs, ref1, ref2)
+ return o.data[len(o.data)-n:]
+}
+
+func Write3(o *Ops, n int, ref1, ref2, ref3 interface{}) []byte {
+ o.data = append(o.data, make([]byte, n)...)
+ o.refs = append(o.refs, ref1, ref2, ref3)
+ return o.data[len(o.data)-n:]
+}
+
+func PCFor(o *Ops) PC {
+ return PC{data: len(o.data), refs: len(o.refs)}
+}
+
+func (s *stack) push() StackID {
+ s.nextID++
+ sid := StackID{
+ id: s.nextID,
+ prev: s.currentID,
}
- return decodeAffine2D(d[1:])
+ s.currentID = s.nextID
+ return sid
+}
+
+func (s *stack) check(sid StackID) {
+ if s.currentID != sid.id {
+ panic("unbalanced operation")
+ }
+}
+
+func (s *stack) pop(sid StackID) {
+ s.check(sid)
+ s.currentID = sid.prev
+}
+
+// Save the effective transformation.
+func Save(o *Ops) StateOp {
+ o.nextStateID++
+ s := StateOp{
+ ops: o,
+ id: o.nextStateID,
+ macroID: o.macroStack.currentID,
+ }
+ bo := binary.LittleEndian
+ data := Write(o, TypeSaveLen)
+ data[0] = byte(TypeSave)
+ bo.PutUint32(data[1:], uint32(s.id))
+ return s
+}
+
+// Load a previously saved operations state given
+// its ID.
+func (s StateOp) Load() {
+ bo := binary.LittleEndian
+ data := Write(s.ops, TypeLoadLen)
+ data[0] = byte(TypeLoad)
+ bo.PutUint32(data[1:], uint32(s.id))
+}
+
+func DecodeCommand(d []byte) scene.Command {
+ var cmd scene.Command
+ copy(byteslice.Uint32(cmd[:]), d)
+ return cmd
+}
+
+func EncodeCommand(out []byte, cmd scene.Command) {
+ copy(out, byteslice.Uint32(cmd[:]))
}
-func decodeAffine2D(data []byte) f32.Affine2D {
+func DecodeTransform(data []byte) (t f32.Affine2D, push bool) {
+ if OpType(data[0]) != TypeTransform {
+ panic("invalid op")
+ }
+ push = data[1] != 0
+ data = data[2:]
+ data = data[:4*6]
+
bo := binary.LittleEndian
a := math.Float32frombits(bo.Uint32(data))
b := math.Float32frombits(bo.Uint32(data[4*1:]))
@@ -62,5 +326,148 @@ func decodeAffine2D(data []byte) f32.Affine2D {
d := math.Float32frombits(bo.Uint32(data[4*3:]))
e := math.Float32frombits(bo.Uint32(data[4*4:]))
f := math.Float32frombits(bo.Uint32(data[4*5:]))
- return f32.NewAffine2D(a, b, c, d, e, f)
+ return f32.NewAffine2D(a, b, c, d, e, f), push
+}
+
+// DecodeSave decodes the state id of a save op.
+func DecodeSave(data []byte) int {
+ if OpType(data[0]) != TypeSave {
+ panic("invalid op")
+ }
+ bo := binary.LittleEndian
+ return int(bo.Uint32(data[1:]))
+}
+
+// DecodeLoad decodes the state id of a load op.
+func DecodeLoad(data []byte) int {
+ if OpType(data[0]) != TypeLoad {
+ panic("invalid op")
+ }
+ bo := binary.LittleEndian
+ return int(bo.Uint32(data[1:]))
+}
+
+func (t OpType) Size() int {
+ return [...]int{
+ TypeMacroLen,
+ TypeCallLen,
+ TypeDeferLen,
+ TypePushTransformLen,
+ TypeTransformLen,
+ TypePopTransformLen,
+ TypeRedrawLen,
+ TypeImageLen,
+ TypePaintLen,
+ TypeColorLen,
+ TypeLinearGradientLen,
+ TypePassLen,
+ TypePopPassLen,
+ TypePointerInputLen,
+ TypeClipboardReadLen,
+ TypeClipboardWriteLen,
+ TypeSourceLen,
+ TypeTargetLen,
+ TypeOfferLen,
+ TypeKeyInputLen,
+ TypeKeyFocusLen,
+ TypeKeySoftKeyboardLen,
+ TypeSaveLen,
+ TypeLoadLen,
+ TypeAuxLen,
+ TypeClipLen,
+ TypePopClipLen,
+ TypeProfileLen,
+ TypeCursorLen,
+ TypePathLen,
+ TypeStrokeLen,
+ TypeSemanticLabelLen,
+ TypeSemanticDescLen,
+ TypeSemanticClassLen,
+ TypeSemanticSelectedLen,
+ TypeSemanticDisabledLen,
+ }[t-firstOpIndex]
+}
+
+func (t OpType) NumRefs() int {
+ switch t {
+ case TypeKeyInput, TypeKeyFocus, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite, TypeCursor, TypeSemanticLabel, TypeSemanticDesc:
+ return 1
+ case TypeImage, TypeSource, TypeTarget:
+ return 2
+ case TypeOffer:
+ return 3
+ default:
+ return 0
+ }
+}
+
+func (t OpType) String() string {
+ switch t {
+ case TypeMacro:
+ return "Macro"
+ case TypeCall:
+ return "Call"
+ case TypeDefer:
+ return "Defer"
+ case TypePushTransform:
+ return "PushTransform"
+ case TypeTransform:
+ return "Transform"
+ case TypePopTransform:
+ return "PopTransform"
+ case TypeInvalidate:
+ return "Invalidate"
+ case TypeImage:
+ return "Image"
+ case TypePaint:
+ return "Paint"
+ case TypeColor:
+ return "Color"
+ case TypeLinearGradient:
+ return "LinearGradient"
+ case TypePass:
+ return "Pass"
+ case TypePopPass:
+ return "PopPass"
+ case TypePointerInput:
+ return "PointerInput"
+ case TypeClipboardRead:
+ return "ClipboardRead"
+ case TypeClipboardWrite:
+ return "ClipboardWrite"
+ case TypeSource:
+ return "Source"
+ case TypeTarget:
+ return "Target"
+ case TypeOffer:
+ return "Offer"
+ case TypeKeyInput:
+ return "KeyInput"
+ case TypeKeyFocus:
+ return "KeyFocus"
+ case TypeKeySoftKeyboard:
+ return "KeySoftKeyboard"
+ case TypeSave:
+ return "Save"
+ case TypeLoad:
+ return "Load"
+ case TypeAux:
+ return "Aux"
+ case TypeClip:
+ return "Clip"
+ case TypePopClip:
+ return "PopClip"
+ case TypeProfile:
+ return "Profile"
+ case TypeCursor:
+ return "Cursor"
+ case TypePath:
+ return "Path"
+ case TypeStroke:
+ return "Stroke"
+ case TypeSemanticLabel:
+ return "SemanticDescription"
+ default:
+ panic("unknown OpType")
+ }
}
diff --git a/vendor/gioui.org/internal/ops/reader.go b/vendor/gioui.org/internal/ops/reader.go
index 5d713db..99b8cb6 100644
--- a/vendor/gioui.org/internal/ops/reader.go
+++ b/vendor/gioui.org/internal/ops/reader.go
@@ -4,17 +4,15 @@ package ops
import (
"encoding/binary"
-
- "gioui.org/f32"
- "gioui.org/internal/opconst"
- "gioui.org/op"
)
// Reader parses an ops list.
type Reader struct {
- pc pc
- stack []macro
- ops *op.Ops
+ pc PC
+ stack []macro
+ ops *Ops
+ deferOps Ops
+ deferDone bool
}
// EncodedOp represents an encoded op returned by
@@ -27,53 +25,52 @@ type EncodedOp struct {
// Key is a unique key for a given op.
type Key struct {
- ops *op.Ops
- pc int
- version int
- sx, hx, sy, hy float32
+ ops *Ops
+ pc int
+ version int
}
// Shadow of op.MacroOp.
type macroOp struct {
- ops *op.Ops
- pc pc
+ ops *Ops
+ pc PC
}
-type pc struct {
+// PC is an instruction counter for an operation list.
+type PC struct {
data int
refs int
}
type macro struct {
- ops *op.Ops
- retPC pc
- endPC pc
+ ops *Ops
+ retPC PC
+ endPC PC
}
type opMacroDef struct {
- endpc pc
+ endpc PC
}
-// Reset start reading from the op list.
-func (r *Reader) Reset(ops *op.Ops) {
- r.stack = r.stack[:0]
- r.pc = pc{}
- r.ops = ops
+// Reset start reading from the beginning of ops.
+func (r *Reader) Reset(ops *Ops) {
+ r.ResetAt(ops, PC{})
}
-func (k Key) SetTransform(t f32.Affine2D) Key {
- sx, hx, _, hy, sy, _ := t.Elems()
- k.sx = sx
- k.hx = hx
- k.hy = hy
- k.sy = sy
- return k
+// ResetAt is like Reset, except it starts reading from pc.
+func (r *Reader) ResetAt(ops *Ops, pc PC) {
+ r.stack = r.stack[:0]
+ Reset(&r.deferOps)
+ r.deferDone = false
+ r.pc = pc
+ r.ops = ops
}
func (r *Reader) Decode() (EncodedOp, bool) {
if r.ops == nil {
return EncodedOp{}, false
}
+ deferring := false
for {
if len(r.stack) > 0 {
b := r.stack[len(r.stack)-1]
@@ -84,35 +81,59 @@ func (r *Reader) Decode() (EncodedOp, bool) {
continue
}
}
- data := r.ops.Data()
+ data := r.ops.data
data = data[r.pc.data:]
+ refs := r.ops.refs
if len(data) == 0 {
- return EncodedOp{}, false
+ if r.deferDone {
+ return EncodedOp{}, false
+ }
+ r.deferDone = true
+ // Execute deferred macros.
+ r.ops = &r.deferOps
+ r.pc = PC{}
+ continue
}
- key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.Version()}
- t := opconst.OpType(data[0])
+ key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.version}
+ t := OpType(data[0])
n := t.Size()
nrefs := t.NumRefs()
data = data[:n]
- refs := r.ops.Refs()
refs = refs[r.pc.refs:]
refs = refs[:nrefs]
switch t {
- case opconst.TypeAux:
+ case TypeDefer:
+ deferring = true
+ r.pc.data += n
+ r.pc.refs += nrefs
+ continue
+ case TypeAux:
// An Aux operations is always wrapped in a macro, and
// its length is the remaining space.
block := r.stack[len(r.stack)-1]
- n += block.endPC.data - r.pc.data - opconst.TypeAuxLen
+ n += block.endPC.data - r.pc.data - TypeAuxLen
data = data[:n]
- case opconst.TypeCall:
+ case TypeCall:
+ if deferring {
+ deferring = false
+ // Copy macro for deferred execution.
+ if t.NumRefs() != 1 {
+ panic("internal error: unexpected number of macro refs")
+ }
+ deferData := Write1(&r.deferOps, t.Size(), refs[0])
+ copy(deferData, data)
+ r.pc.data += n
+ r.pc.refs += nrefs
+ continue
+ }
var op macroOp
op.decode(data, refs)
- macroData := op.ops.Data()[op.pc.data:]
- if opconst.OpType(macroData[0]) != opconst.TypeMacro {
+ macroData := op.ops.data[op.pc.data:]
+ if OpType(macroData[0]) != TypeMacro {
panic("invalid macro reference")
}
var opDef opMacroDef
- opDef.decode(macroData[:opconst.TypeMacro.Size()])
+ opDef.decode(macroData[:TypeMacro.Size()])
retPC := r.pc
retPC.data += n
retPC.refs += nrefs
@@ -123,10 +144,10 @@ func (r *Reader) Decode() (EncodedOp, bool) {
})
r.ops = op.ops
r.pc = op.pc
- r.pc.data += opconst.TypeMacro.Size()
- r.pc.refs += opconst.TypeMacro.NumRefs()
+ r.pc.data += TypeMacro.Size()
+ r.pc.refs += TypeMacro.NumRefs()
continue
- case opconst.TypeMacro:
+ case TypeMacro:
var op opMacroDef
op.decode(data)
r.pc = op.endpc
@@ -139,14 +160,15 @@ func (r *Reader) Decode() (EncodedOp, bool) {
}
func (op *opMacroDef) decode(data []byte) {
- if opconst.OpType(data[0]) != opconst.TypeMacro {
+ if OpType(data[0]) != TypeMacro {
panic("invalid op")
}
bo := binary.LittleEndian
+ data = data[:9]
dataIdx := int(int32(bo.Uint32(data[1:])))
refsIdx := int(int32(bo.Uint32(data[5:])))
*op = opMacroDef{
- endpc: pc{
+ endpc: PC{
data: dataIdx,
refs: refsIdx,
},
@@ -154,15 +176,16 @@ func (op *opMacroDef) decode(data []byte) {
}
func (m *macroOp) decode(data []byte, refs []interface{}) {
- if opconst.OpType(data[0]) != opconst.TypeCall {
+ if OpType(data[0]) != TypeCall {
panic("invalid op")
}
+ data = data[:9]
bo := binary.LittleEndian
dataIdx := int(int32(bo.Uint32(data[1:])))
refsIdx := int(int32(bo.Uint32(data[5:])))
*m = macroOp{
- ops: refs[0].(*op.Ops),
- pc: pc{
+ ops: refs[0].(*Ops),
+ pc: PC{
data: dataIdx,
refs: refsIdx,
},
diff --git a/vendor/gioui.org/internal/scene/scene.go b/vendor/gioui.org/internal/scene/scene.go
new file mode 100644
index 0000000..0358858
--- /dev/null
+++ b/vendor/gioui.org/internal/scene/scene.go
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// Package scene encodes and decodes graphics commands in the format used by the
+// compute renderer.
+package scene
+
+import (
+ "fmt"
+ "image"
+ "image/color"
+ "math"
+ "unsafe"
+
+ "gioui.org/f32"
+)
+
+type Op uint32
+
+type Command [sceneElemSize / 4]uint32
+
+// GPU commands from piet/scene.h in package gioui.org/shaders.
+const (
+ OpNop Op = iota
+ OpLine
+ OpQuad
+ OpCubic
+ OpFillColor
+ OpLineWidth
+ OpTransform
+ OpBeginClip
+ OpEndClip
+ OpFillImage
+ OpSetFillMode
+ OpGap
+)
+
+// FillModes, from setup.h.
+type FillMode uint32
+
+const (
+ FillModeNonzero = 0
+ FillModeStroke = 1
+)
+
+const CommandSize = int(unsafe.Sizeof(Command{}))
+
+const sceneElemSize = 36
+
+func (c Command) Op() Op {
+ return Op(c[0])
+}
+
+func (c Command) String() string {
+ switch Op(c[0]) {
+ case OpNop:
+ return "nop"
+ case OpLine:
+ from, to := DecodeLine(c)
+ return fmt.Sprintf("line(%v, %v)", from, to)
+ case OpGap:
+ from, to := DecodeLine(c)
+ return fmt.Sprintf("gap(%v, %v)", from, to)
+ case OpQuad:
+ from, ctrl, to := DecodeQuad(c)
+ return fmt.Sprintf("quad(%v, %v, %v)", from, ctrl, to)
+ case OpCubic:
+ from, ctrl0, ctrl1, to := DecodeCubic(c)
+ return fmt.Sprintf("cubic(%v, %v, %v, %v)", from, ctrl0, ctrl1, to)
+ case OpFillColor:
+ return fmt.Sprintf("fillcolor %#.8x", c[1])
+ case OpLineWidth:
+ return "linewidth"
+ case OpTransform:
+ t := f32.NewAffine2D(
+ math.Float32frombits(c[1]),
+ math.Float32frombits(c[3]),
+ math.Float32frombits(c[5]),
+ math.Float32frombits(c[2]),
+ math.Float32frombits(c[4]),
+ math.Float32frombits(c[6]),
+ )
+ return fmt.Sprintf("transform (%v)", t)
+ case OpBeginClip:
+ bounds := f32.Rectangle{
+ Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
+ Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
+ }
+ return fmt.Sprintf("beginclip (%v)", bounds)
+ case OpEndClip:
+ bounds := f32.Rectangle{
+ Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
+ Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
+ }
+ return fmt.Sprintf("endclip (%v)", bounds)
+ case OpFillImage:
+ return "fillimage"
+ case OpSetFillMode:
+ return "setfillmode"
+ default:
+ panic("unreachable")
+ }
+}
+
+func Line(start, end f32.Point) Command {
+ return Command{
+ 0: uint32(OpLine),
+ 1: math.Float32bits(start.X),
+ 2: math.Float32bits(start.Y),
+ 3: math.Float32bits(end.X),
+ 4: math.Float32bits(end.Y),
+ }
+}
+
+func Gap(start, end f32.Point) Command {
+ return Command{
+ 0: uint32(OpGap),
+ 1: math.Float32bits(start.X),
+ 2: math.Float32bits(start.Y),
+ 3: math.Float32bits(end.X),
+ 4: math.Float32bits(end.Y),
+ }
+}
+
+func Cubic(start, ctrl0, ctrl1, end f32.Point) Command {
+ return Command{
+ 0: uint32(OpCubic),
+ 1: math.Float32bits(start.X),
+ 2: math.Float32bits(start.Y),
+ 3: math.Float32bits(ctrl0.X),
+ 4: math.Float32bits(ctrl0.Y),
+ 5: math.Float32bits(ctrl1.X),
+ 6: math.Float32bits(ctrl1.Y),
+ 7: math.Float32bits(end.X),
+ 8: math.Float32bits(end.Y),
+ }
+}
+
+func Quad(start, ctrl, end f32.Point) Command {
+ return Command{
+ 0: uint32(OpQuad),
+ 1: math.Float32bits(start.X),
+ 2: math.Float32bits(start.Y),
+ 3: math.Float32bits(ctrl.X),
+ 4: math.Float32bits(ctrl.Y),
+ 5: math.Float32bits(end.X),
+ 6: math.Float32bits(end.Y),
+ }
+}
+
+func Transform(m f32.Affine2D) Command {
+ sx, hx, ox, hy, sy, oy := m.Elems()
+ return Command{
+ 0: uint32(OpTransform),
+ 1: math.Float32bits(sx),
+ 2: math.Float32bits(hy),
+ 3: math.Float32bits(hx),
+ 4: math.Float32bits(sy),
+ 5: math.Float32bits(ox),
+ 6: math.Float32bits(oy),
+ }
+}
+
+func SetLineWidth(width float32) Command {
+ return Command{
+ 0: uint32(OpLineWidth),
+ 1: math.Float32bits(width),
+ }
+}
+
+func BeginClip(bbox f32.Rectangle) Command {
+ return Command{
+ 0: uint32(OpBeginClip),
+ 1: math.Float32bits(bbox.Min.X),
+ 2: math.Float32bits(bbox.Min.Y),
+ 3: math.Float32bits(bbox.Max.X),
+ 4: math.Float32bits(bbox.Max.Y),
+ }
+}
+
+func EndClip(bbox f32.Rectangle) Command {
+ return Command{
+ 0: uint32(OpEndClip),
+ 1: math.Float32bits(bbox.Min.X),
+ 2: math.Float32bits(bbox.Min.Y),
+ 3: math.Float32bits(bbox.Max.X),
+ 4: math.Float32bits(bbox.Max.Y),
+ }
+}
+
+func FillColor(col color.RGBA) Command {
+ return Command{
+ 0: uint32(OpFillColor),
+ 1: uint32(col.R)<<24 | uint32(col.G)<<16 | uint32(col.B)<<8 | uint32(col.A),
+ }
+}
+
+func FillImage(index int, offset image.Point) Command {
+ x := int16(offset.X)
+ y := int16(offset.Y)
+ return Command{
+ 0: uint32(OpFillImage),
+ 1: uint32(index),
+ 2: uint32(uint16(x)) | uint32(uint16(y))<<16,
+ }
+}
+
+func SetFillMode(mode FillMode) Command {
+ return Command{
+ 0: uint32(OpSetFillMode),
+ 1: uint32(mode),
+ }
+}
+
+func DecodeLine(cmd Command) (from, to f32.Point) {
+ if cmd[0] != uint32(OpLine) {
+ panic("invalid command")
+ }
+ from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
+ to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
+ return
+}
+
+func DecodeGap(cmd Command) (from, to f32.Point) {
+ if cmd[0] != uint32(OpGap) {
+ panic("invalid command")
+ }
+ from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
+ to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
+ return
+}
+
+func DecodeQuad(cmd Command) (from, ctrl, to f32.Point) {
+ if cmd[0] != uint32(OpQuad) {
+ panic("invalid command")
+ }
+ from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
+ ctrl = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
+ to = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
+ return
+}
+
+func DecodeCubic(cmd Command) (from, ctrl0, ctrl1, to f32.Point) {
+ if cmd[0] != uint32(OpCubic) {
+ panic("invalid command")
+ }
+ from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
+ ctrl0 = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
+ ctrl1 = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
+ to = f32.Pt(math.Float32frombits(cmd[7]), math.Float32frombits(cmd[8]))
+ return
+}
diff --git a/vendor/gioui.org/internal/stroke/stroke.go b/vendor/gioui.org/internal/stroke/stroke.go
new file mode 100644
index 0000000..47db6ca
--- /dev/null
+++ b/vendor/gioui.org/internal/stroke/stroke.go
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// Most of the algorithms to compute strokes and their offsets have been
+// extracted, adapted from (and used as a reference implementation):
+// - github.com/tdewolff/canvas (Licensed under MIT)
+//
+// These algorithms have been implemented from:
+// Fast, precise flattening of cubic Bézier path and offset curves
+// Thomas F. Hain, et al.
+//
+// An electronic version is available at:
+// https://seant23.files.wordpress.com/2010/11/fastpreciseflatteningofbeziercurve.pdf
+//
+// Possible improvements (in term of speed and/or accuracy) on these
+// algorithms are:
+//
+// - Polar Stroking: New Theory and Methods for Stroking Paths,
+// M. Kilgard
+// https://arxiv.org/pdf/2007.00308.pdf
+//
+// - https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html
+// R. Levien
+
+// Package stroke implements conversion of strokes to filled outlines. It is used as a
+// fallback for stroke configurations not natively supported by the renderer.
+package stroke
+
+import (
+ "encoding/binary"
+ "math"
+
+ "gioui.org/f32"
+ "gioui.org/internal/ops"
+ "gioui.org/internal/scene"
+)
+
+// The following are copies of types from op/clip to avoid a circular import of
+// that package.
+// TODO: when the old renderer is gone, this package can be merged with
+// op/clip, eliminating the duplicate types.
+type StrokeStyle struct {
+ Width float32
+}
+
+// strokeTolerance is used to reconcile rounding errors arising
+// when splitting quads into smaller and smaller segments to approximate
+// them into straight lines, and when joining back segments.
+//
+// The magic value of 0.01 was found by striking a compromise between
+// aesthetic looking (curves did look like curves, even after linearization)
+// and speed.
+const strokeTolerance = 0.01
+
+type QuadSegment struct {
+ From, Ctrl, To f32.Point
+}
+
+type StrokeQuad struct {
+ Contour uint32
+ Quad QuadSegment
+}
+
+type strokeState struct {
+ p0, p1 f32.Point // p0 is the start point, p1 the end point.
+ n0, n1 f32.Point // n0 is the normal vector at the start point, n1 at the end point.
+ r0, r1 float32 // r0 is the curvature at the start point, r1 at the end point.
+ ctl f32.Point // ctl is the control point of the quadratic Bézier segment.
+}
+
+type StrokeQuads []StrokeQuad
+
+func (qs *StrokeQuads) setContour(n uint32) {
+ for i := range *qs {
+ (*qs)[i].Contour = n
+ }
+}
+
+func (qs *StrokeQuads) pen() f32.Point {
+ return (*qs)[len(*qs)-1].Quad.To
+}
+
+func (qs *StrokeQuads) lineTo(pt f32.Point) {
+ end := qs.pen()
+ *qs = append(*qs, StrokeQuad{
+ Quad: QuadSegment{
+ From: end,
+ Ctrl: end.Add(pt).Mul(0.5),
+ To: pt,
+ },
+ })
+}
+
+func (qs *StrokeQuads) arc(f1, f2 f32.Point, angle float32) {
+ const segments = 16
+ pen := qs.pen()
+ m := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle, segments)
+ for i := 0; i < segments; i++ {
+ p0 := qs.pen()
+ p1 := m.Transform(p0)
+ p2 := m.Transform(p1)
+ ctl := p1.Mul(2).Sub(p0.Add(p2).Mul(.5))
+ *qs = append(*qs, StrokeQuad{
+ Quad: QuadSegment{
+ From: p0, Ctrl: ctl, To: p2,
+ },
+ })
+ }
+}
+
+// split splits a slice of quads into slices of quads grouped
+// by contours (ie: splitted at move-to boundaries).
+func (qs StrokeQuads) split() []StrokeQuads {
+ if len(qs) == 0 {
+ return nil
+ }
+
+ var (
+ c uint32
+ o []StrokeQuads
+ i = len(o)
+ )
+ for _, q := range qs {
+ if q.Contour != c {
+ c = q.Contour
+ i = len(o)
+ o = append(o, StrokeQuads{})
+ }
+ o[i] = append(o[i], q)
+ }
+
+ return o
+}
+
+func (qs StrokeQuads) stroke(stroke StrokeStyle) StrokeQuads {
+ var (
+ o StrokeQuads
+ hw = 0.5 * stroke.Width
+ )
+
+ for _, ps := range qs.split() {
+ rhs, lhs := ps.offset(hw, stroke)
+ switch lhs {
+ case nil:
+ o = o.append(rhs)
+ default:
+ // Closed path.
+ // Inner path should go opposite direction to cancel outer path.
+ switch {
+ case ps.ccw():
+ lhs = lhs.reverse()
+ o = o.append(rhs)
+ o = o.append(lhs)
+ default:
+ rhs = rhs.reverse()
+ o = o.append(lhs)
+ o = o.append(rhs)
+ }
+ }
+ }
+
+ return o
+}
+
+// offset returns the right-hand and left-hand sides of the path, offset by
+// the half-width hw.
+// The stroke handles how segments are joined and ends are capped.
+func (qs StrokeQuads) offset(hw float32, stroke StrokeStyle) (rhs, lhs StrokeQuads) {
+ var (
+ states []strokeState
+ beg = qs[0].Quad.From
+ end = qs[len(qs)-1].Quad.To
+ closed = beg == end
+ )
+ for i := range qs {
+ q := qs[i].Quad
+
+ var (
+ n0 = strokePathNorm(q.From, q.Ctrl, q.To, 0, hw)
+ n1 = strokePathNorm(q.From, q.Ctrl, q.To, 1, hw)
+ r0 = strokePathCurv(q.From, q.Ctrl, q.To, 0)
+ r1 = strokePathCurv(q.From, q.Ctrl, q.To, 1)
+ )
+ states = append(states, strokeState{
+ p0: q.From,
+ p1: q.To,
+ n0: n0,
+ n1: n1,
+ r0: r0,
+ r1: r1,
+ ctl: q.Ctrl,
+ })
+ }
+
+ for i, state := range states {
+ rhs = rhs.append(strokeQuadBezier(state, +hw, strokeTolerance))
+ lhs = lhs.append(strokeQuadBezier(state, -hw, strokeTolerance))
+
+ // join the current and next segments
+ if hasNext := i+1 < len(states); hasNext || closed {
+ var next strokeState
+ switch {
+ case hasNext:
+ next = states[i+1]
+ case closed:
+ next = states[0]
+ }
+ if state.n1 != next.n0 {
+ strokePathJoin(stroke, &rhs, &lhs, hw, state.p1, state.n1, next.n0, state.r1, next.r0)
+ }
+ }
+ }
+
+ if closed {
+ rhs.close()
+ lhs.close()
+ return rhs, lhs
+ }
+
+ qbeg := &states[0]
+ qend := &states[len(states)-1]
+
+ // Default to counter-clockwise direction.
+ lhs = lhs.reverse()
+ strokePathCap(stroke, &rhs, hw, qend.p1, qend.n1)
+
+ rhs = rhs.append(lhs)
+ strokePathCap(stroke, &rhs, hw, qbeg.p0, qbeg.n0.Mul(-1))
+
+ rhs.close()
+
+ return rhs, nil
+}
+
+func (qs *StrokeQuads) close() {
+ p0 := (*qs)[len(*qs)-1].Quad.To
+ p1 := (*qs)[0].Quad.From
+
+ if p1 == p0 {
+ return
+ }
+
+ *qs = append(*qs, StrokeQuad{
+ Quad: QuadSegment{
+ From: p0,
+ Ctrl: p0.Add(p1).Mul(0.5),
+ To: p1,
+ },
+ })
+}
+
+// ccw returns whether the path is counter-clockwise.
+func (qs StrokeQuads) ccw() bool {
+ // Use the Shoelace formula:
+ // https://en.wikipedia.org/wiki/Shoelace_formula
+ var area float32
+ for _, ps := range qs.split() {
+ for i := 1; i < len(ps); i++ {
+ pi := ps[i].Quad.To
+ pj := ps[i-1].Quad.To
+ area += (pi.X - pj.X) * (pi.Y + pj.Y)
+ }
+ }
+ return area <= 0.0
+}
+
+func (qs StrokeQuads) reverse() StrokeQuads {
+ if len(qs) == 0 {
+ return nil
+ }
+
+ ps := make(StrokeQuads, 0, len(qs))
+ for i := range qs {
+ q := qs[len(qs)-1-i]
+ q.Quad.To, q.Quad.From = q.Quad.From, q.Quad.To
+ ps = append(ps, q)
+ }
+
+ return ps
+}
+
+func (qs StrokeQuads) append(ps StrokeQuads) StrokeQuads {
+ switch {
+ case len(ps) == 0:
+ return qs
+ case len(qs) == 0:
+ return ps
+ }
+
+ // Consolidate quads and smooth out rounding errors.
+ // We need to also check for the strokeTolerance to correctly handle
+ // join/cap points or on-purpose disjoint quads.
+ p0 := qs[len(qs)-1].Quad.To
+ p1 := ps[0].Quad.From
+ if p0 != p1 && lenPt(p0.Sub(p1)) < strokeTolerance {
+ qs = append(qs, StrokeQuad{
+ Quad: QuadSegment{
+ From: p0,
+ Ctrl: p0.Add(p1).Mul(0.5),
+ To: p1,
+ },
+ })
+ }
+ return append(qs, ps...)
+}
+
+func (q QuadSegment) Transform(t f32.Affine2D) QuadSegment {
+ q.From = t.Transform(q.From)
+ q.Ctrl = t.Transform(q.Ctrl)
+ q.To = t.Transform(q.To)
+ return q
+}
+
+// strokePathNorm returns the normal vector at t.
+func strokePathNorm(p0, p1, p2 f32.Point, t, d float32) f32.Point {
+ switch t {
+ case 0:
+ n := p1.Sub(p0)
+ if n.X == 0 && n.Y == 0 {
+ return f32.Point{}
+ }
+ n = rot90CW(n)
+ return normPt(n, d)
+ case 1:
+ n := p2.Sub(p1)
+ if n.X == 0 && n.Y == 0 {
+ return f32.Point{}
+ }
+ n = rot90CW(n)
+ return normPt(n, d)
+ }
+ panic("impossible")
+}
+
+func rot90CW(p f32.Point) f32.Point { return f32.Pt(+p.Y, -p.X) }
+func rot90CCW(p f32.Point) f32.Point { return f32.Pt(-p.Y, +p.X) }
+
+// cosPt returns the cosine of the opening angle between p and q.
+func cosPt(p, q f32.Point) float32 {
+ np := math.Hypot(float64(p.X), float64(p.Y))
+ nq := math.Hypot(float64(q.X), float64(q.Y))
+ return dotPt(p, q) / float32(np*nq)
+}
+
+func normPt(p f32.Point, l float32) f32.Point {
+ d := math.Hypot(float64(p.X), float64(p.Y))
+ l64 := float64(l)
+ if math.Abs(d-l64) < 1e-10 {
+ return f32.Point{}
+ }
+ n := float32(l64 / d)
+ return f32.Point{X: p.X * n, Y: p.Y * n}
+}
+
+func lenPt(p f32.Point) float32 {
+ return float32(math.Hypot(float64(p.X), float64(p.Y)))
+}
+
+func dotPt(p, q f32.Point) float32 {
+ return p.X*q.X + p.Y*q.Y
+}
+
+func perpDot(p, q f32.Point) float32 {
+ return p.X*q.Y - p.Y*q.X
+}
+
+// strokePathCurv returns the curvature at t, along the quadratic Bézier
+// curve defined by the triplet (beg, ctl, end).
+func strokePathCurv(beg, ctl, end f32.Point, t float32) float32 {
+ var (
+ d1p = quadBezierD1(beg, ctl, end, t)
+ d2p = quadBezierD2(beg, ctl, end, t)
+
+ // Negative when bending right, ie: the curve is CW at this point.
+ a = float64(perpDot(d1p, d2p))
+ )
+
+ // We check early that the segment isn't too line-like and
+ // save a costly call to math.Pow that will be discarded by dividing
+ // with a too small 'a'.
+ if math.Abs(a) < 1e-10 {
+ return float32(math.NaN())
+ }
+ return float32(math.Pow(float64(d1p.X*d1p.X+d1p.Y*d1p.Y), 1.5) / a)
+}
+
+// quadBezierSample returns the point on the Bézier curve at t.
+// B(t) = (1-t)^2 P0 + 2(1-t)t P1 + t^2 P2
+func quadBezierSample(p0, p1, p2 f32.Point, t float32) f32.Point {
+ t1 := 1 - t
+ c0 := t1 * t1
+ c1 := 2 * t1 * t
+ c2 := t * t
+
+ o := p0.Mul(c0)
+ o = o.Add(p1.Mul(c1))
+ o = o.Add(p2.Mul(c2))
+ return o
+}
+
+// quadBezierD1 returns the first derivative of the Bézier curve with respect to t.
+// B'(t) = 2(1-t)(P1 - P0) + 2t(P2 - P1)
+func quadBezierD1(p0, p1, p2 f32.Point, t float32) f32.Point {
+ p10 := p1.Sub(p0).Mul(2 * (1 - t))
+ p21 := p2.Sub(p1).Mul(2 * t)
+
+ return p10.Add(p21)
+}
+
+// quadBezierD2 returns the second derivative of the Bézier curve with respect to t:
+// B''(t) = 2(P2 - 2P1 + P0)
+func quadBezierD2(p0, p1, p2 f32.Point, t float32) f32.Point {
+ p := p2.Sub(p1.Mul(2)).Add(p0)
+ return p.Mul(2)
+}
+
+func strokeQuadBezier(state strokeState, d, flatness float32) StrokeQuads {
+ // Gio strokes are only quadratic Bézier curves, w/o any inflection point.
+ // So we just have to flatten them.
+ var qs StrokeQuads
+ return flattenQuadBezier(qs, state.p0, state.ctl, state.p1, d, flatness)
+}
+
+// flattenQuadBezier splits a Bézier quadratic curve into linear sub-segments,
+// themselves also encoded as Bézier (degenerate, flat) quadratic curves.
+func flattenQuadBezier(qs StrokeQuads, p0, p1, p2 f32.Point, d, flatness float32) StrokeQuads {
+ var (
+ t float32
+ flat64 = float64(flatness)
+ )
+ for t < 1 {
+ s2 := float64((p2.X-p0.X)*(p1.Y-p0.Y) - (p2.Y-p0.Y)*(p1.X-p0.X))
+ den := math.Hypot(float64(p1.X-p0.X), float64(p1.Y-p0.Y))
+ if s2*den == 0.0 {
+ break
+ }
+
+ s2 /= den
+ t = 2.0 * float32(math.Sqrt(flat64/3.0/math.Abs(s2)))
+ if t >= 1.0 {
+ break
+ }
+ var q0, q1, q2 f32.Point
+ q0, q1, q2, p0, p1, p2 = quadBezierSplit(p0, p1, p2, t)
+ qs.addLine(q0, q1, q2, 0, d)
+ }
+ qs.addLine(p0, p1, p2, 1, d)
+ return qs
+}
+
+func (qs *StrokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) {
+
+ switch i := len(*qs); i {
+ case 0:
+ p0 = p0.Add(strokePathNorm(p0, ctrl, p1, 0, d))
+ default:
+ // Address possible rounding errors and use previous point.
+ p0 = (*qs)[i-1].Quad.To
+ }
+
+ p1 = p1.Add(strokePathNorm(p0, ctrl, p1, 1, d))
+
+ *qs = append(*qs,
+ StrokeQuad{
+ Quad: QuadSegment{
+ From: p0,
+ Ctrl: p0.Add(p1).Mul(0.5),
+ To: p1,
+ },
+ },
+ )
+}
+
+// quadInterp returns the interpolated point at t.
+func quadInterp(p, q f32.Point, t float32) f32.Point {
+ return f32.Pt(
+ (1-t)*p.X+t*q.X,
+ (1-t)*p.Y+t*q.Y,
+ )
+}
+
+// quadBezierSplit returns the pair of triplets (from,ctrl,to) Bézier curve,
+// split before (resp. after) the provided parametric t value.
+func quadBezierSplit(p0, p1, p2 f32.Point, t float32) (f32.Point, f32.Point, f32.Point, f32.Point, f32.Point, f32.Point) {
+
+ var (
+ b0 = p0
+ b1 = quadInterp(p0, p1, t)
+ b2 = quadBezierSample(p0, p1, p2, t)
+
+ a0 = b2
+ a1 = quadInterp(p1, p2, t)
+ a2 = p2
+ )
+
+ return b0, b1, b2, a0, a1, a2
+}
+
+// strokePathJoin joins the two paths rhs and lhs, according to the provided
+// stroke operation.
+func strokePathJoin(stroke StrokeStyle, rhs, lhs *StrokeQuads, hw float32, pivot, n0, n1 f32.Point, r0, r1 float32) {
+ strokePathRoundJoin(rhs, lhs, hw, pivot, n0, n1, r0, r1)
+}
+
+func strokePathRoundJoin(rhs, lhs *StrokeQuads, hw float32, pivot, n0, n1 f32.Point, r0, r1 float32) {
+ rp := pivot.Add(n1)
+ lp := pivot.Sub(n1)
+ cw := dotPt(rot90CW(n0), n1) >= 0.0
+ switch {
+ case cw:
+ // Path bends to the right, ie. CW (or 180 degree turn).
+ c := pivot.Sub(lhs.pen())
+ angle := -math.Acos(float64(cosPt(n0, n1)))
+ lhs.arc(c, c, float32(angle))
+ lhs.lineTo(lp) // Add a line to accommodate for rounding errors.
+ rhs.lineTo(rp)
+ default:
+ // Path bends to the left, ie. CCW.
+ angle := math.Acos(float64(cosPt(n0, n1)))
+ c := pivot.Sub(rhs.pen())
+ rhs.arc(c, c, float32(angle))
+ rhs.lineTo(rp) // Add a line to accommodate for rounding errors.
+ lhs.lineTo(lp)
+ }
+}
+
+// strokePathCap caps the provided path qs, according to the provided stroke operation.
+func strokePathCap(stroke StrokeStyle, qs *StrokeQuads, hw float32, pivot, n0 f32.Point) {
+ strokePathRoundCap(qs, hw, pivot, n0)
+}
+
+// strokePathRoundCap caps the start or end of a path with a round cap.
+func strokePathRoundCap(qs *StrokeQuads, hw float32, pivot, n0 f32.Point) {
+ c := pivot.Sub(qs.pen())
+ qs.arc(c, c, math.Pi)
+}
+
+// ArcTransform computes a transformation that can be used for generating quadratic bézier
+// curve approximations for an arc.
+//
+// The math is extracted from the following paper:
+// "Drawing an elliptical arc using polylines, quadratic or
+// cubic Bezier curves", L. Maisonobe
+// An electronic version may be found at:
+// http://spaceroots.org/documents/ellipse/elliptical-arc.pdf
+func ArcTransform(p, f1, f2 f32.Point, angle float32, segments int) f32.Affine2D {
+ var rx, ry, alpha float64
+ if f1 == f2 {
+ // degenerate case of a circle.
+ rx = dist(f1, p)
+ ry = rx
+ } else {
+ // semi-major axis: 2a = |PF1| + |PF2|
+ a := 0.5 * (dist(f1, p) + dist(f2, p))
+ // semi-minor axis: c^2 = a^2 - b^2 (c: focal distance)
+ c := dist(f1, f2) * 0.5
+ b := math.Sqrt(a*a - c*c)
+ switch {
+ case a > b:
+ rx = a
+ ry = b
+ default:
+ rx = b
+ ry = a
+ }
+ if f1.X == f2.X {
+ // special case of a "vertical" ellipse.
+ alpha = math.Pi / 2
+ if f1.Y < f2.Y {
+ alpha = -alpha
+ }
+ } else {
+ x := float64(f1.X-f2.X) * 0.5
+ if x < 0 {
+ x = -x
+ }
+ alpha = math.Acos(x / c)
+ }
+ }
+
+ var (
+ θ = angle / float32(segments)
+ ref f32.Affine2D // transform from absolute frame to ellipse-based one
+ rot f32.Affine2D // rotation matrix for each segment
+ inv f32.Affine2D // transform from ellipse-based frame to absolute one
+ )
+ center := f32.Point{
+ X: 0.5 * (f1.X + f2.X),
+ Y: 0.5 * (f1.Y + f2.Y),
+ }
+ ref = ref.Offset(f32.Point{}.Sub(center))
+ ref = ref.Rotate(f32.Point{}, float32(-alpha))
+ ref = ref.Scale(f32.Point{}, f32.Point{
+ X: float32(1 / rx),
+ Y: float32(1 / ry),
+ })
+ inv = ref.Invert()
+ rot = rot.Rotate(f32.Point{}, 0.5*θ)
+
+ // Instead of invoking math.Sincos for every segment, compute a rotation
+ // matrix once and apply for each segment.
+ // Before applying the rotation matrix rot, transform the coordinates
+ // to a frame centered to the ellipse (and warped into a unit circle), then rotate.
+ // Finally, transform back into the original frame.
+ return inv.Mul(rot).Mul(ref)
+}
+
+func dist(p1, p2 f32.Point) float64 {
+ var (
+ x1 = float64(p1.X)
+ y1 = float64(p1.Y)
+ x2 = float64(p2.X)
+ y2 = float64(p2.Y)
+ dx = x2 - x1
+ dy = y2 - y1
+ )
+ return math.Hypot(dx, dy)
+}
+
+func StrokePathCommands(style StrokeStyle, scene []byte) StrokeQuads {
+ quads := decodeToStrokeQuads(scene)
+ return quads.stroke(style)
+}
+
+// decodeToStrokeQuads decodes scene commands to quads ready to stroke.
+func decodeToStrokeQuads(pathData []byte) StrokeQuads {
+ quads := make(StrokeQuads, 0, 2*len(pathData)/(scene.CommandSize+4))
+ for len(pathData) >= scene.CommandSize+4 {
+ contour := binary.LittleEndian.Uint32(pathData)
+ cmd := ops.DecodeCommand(pathData[4:])
+ switch cmd.Op() {
+ case scene.OpLine:
+ var q QuadSegment
+ q.From, q.To = scene.DecodeLine(cmd)
+ q.Ctrl = q.From.Add(q.To).Mul(.5)
+ quad := StrokeQuad{
+ Contour: contour,
+ Quad: q,
+ }
+ quads = append(quads, quad)
+ case scene.OpGap:
+ // Ignore gaps for strokes.
+ case scene.OpQuad:
+ var q QuadSegment
+ q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd)
+ quad := StrokeQuad{
+ Contour: contour,
+ Quad: q,
+ }
+ quads = append(quads, quad)
+ case scene.OpCubic:
+ for _, q := range SplitCubic(scene.DecodeCubic(cmd)) {
+ quad := StrokeQuad{
+ Contour: contour,
+ Quad: q,
+ }
+ quads = append(quads, quad)
+ }
+ default:
+ panic("unsupported scene command")
+ }
+ pathData = pathData[scene.CommandSize+4:]
+ }
+ return quads
+}
+
+func SplitCubic(from, ctrl0, ctrl1, to f32.Point) []QuadSegment {
+ quads := make([]QuadSegment, 0, 10)
+ // Set the maximum distance proportionally to the longest side
+ // of the bounding rectangle.
+ hull := f32.Rectangle{
+ Min: from,
+ Max: ctrl0,
+ }.Canon().Union(f32.Rectangle{
+ Min: ctrl1,
+ Max: to,
+ }.Canon())
+ l := hull.Dx()
+ if h := hull.Dy(); h > l {
+ l = h
+ }
+ approxCubeTo(&quads, 0, l*0.001, from, ctrl0, ctrl1, to)
+ return quads
+}
+
+// approxCubeTo approximates a cubic Bézier by a series of quadratic
+// curves.
+func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0, ctrl1, to f32.Point) int {
+ // The idea is from
+ // https://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
+ // where a quadratic approximates a cubic by eliminating its t³ term
+ // from its polynomial expression anchored at the starting point:
+ //
+ // P(t) = pen + 3t(ctrl0 - pen) + 3t²(ctrl1 - 2ctrl0 + pen) + t³(to - 3ctrl1 + 3ctrl0 - pen)
+ //
+ // The control point for the new quadratic Q1 that shares starting point, pen, with P is
+ //
+ // C1 = (3ctrl0 - pen)/2
+ //
+ // The reverse cubic anchored at the end point has the polynomial
+ //
+ // P'(t) = to + 3t(ctrl1 - to) + 3t²(ctrl0 - 2ctrl1 + to) + t³(pen - 3ctrl0 + 3ctrl1 - to)
+ //
+ // The corresponding quadratic Q2 that shares the end point, to, with P has control
+ // point
+ //
+ // C2 = (3ctrl1 - to)/2
+ //
+ // The combined quadratic Bézier, Q, shares both start and end points with its cubic
+ // and use the midpoint between the two curves Q1 and Q2 as control point:
+ //
+ // C = (3ctrl0 - pen + 3ctrl1 - to)/4
+ c := ctrl0.Mul(3).Sub(from).Add(ctrl1.Mul(3)).Sub(to).Mul(1.0 / 4.0)
+ const maxSplits = 32
+ if splits >= maxSplits {
+ *quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to})
+ return splits
+ }
+ // The maximum distance between the cubic P and its approximation Q given t
+ // can be shown to be
+ //
+ // d = sqrt(3)/36*|to - 3ctrl1 + 3ctrl0 - pen|
+ //
+ // To save a square root, compare d² with the squared tolerance.
+ v := to.Sub(ctrl1.Mul(3)).Add(ctrl0.Mul(3)).Sub(from)
+ d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36)
+ if d2 <= maxDist*maxDist {
+ *quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to})
+ return splits
+ }
+ // De Casteljau split the curve and approximate the halves.
+ t := float32(0.5)
+ c0 := from.Add(ctrl0.Sub(from).Mul(t))
+ c1 := ctrl0.Add(ctrl1.Sub(ctrl0).Mul(t))
+ c2 := ctrl1.Add(to.Sub(ctrl1).Mul(t))
+ c01 := c0.Add(c1.Sub(c0).Mul(t))
+ c12 := c1.Add(c2.Sub(c1).Mul(t))
+ c0112 := c01.Add(c12.Sub(c01).Mul(t))
+ splits++
+ splits = approxCubeTo(quads, splits, maxDist, from, c0, c01, c0112)
+ splits = approxCubeTo(quads, splits, maxDist, c0112, c12, c2, to)
+ return splits
+}
diff --git a/vendor/gioui.org/internal/unsafe/unsafe.go b/vendor/gioui.org/internal/unsafe/unsafe.go
deleted file mode 100644
index 5353144..0000000
--- a/vendor/gioui.org/internal/unsafe/unsafe.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package unsafe
-
-import (
- "reflect"
- "unsafe"
-)
-
-// BytesView returns a byte slice view of a slice.
-func BytesView(s interface{}) []byte {
- v := reflect.ValueOf(s)
- first := v.Index(0)
- sz := int(first.Type().Size())
- return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
- Data: uintptr(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(first.UnsafeAddr())))),
- Len: v.Len() * sz,
- Cap: v.Cap() * sz,
- }))
-}
-
-// SliceOf returns a slice from a (native) pointer.
-func SliceOf(s uintptr) []byte {
- if s == 0 {
- return nil
- }
- sh := reflect.SliceHeader{
- Data: s,
- Len: 1 << 30,
- Cap: 1 << 30,
- }
- return *(*[]byte)(unsafe.Pointer(&sh))
-}
-
-// GoString convert a NUL-terminated C string
-// to a Go string.
-func GoString(s []byte) string {
- i := 0
- for {
- if s[i] == 0 {
- break
- }
- i++
- }
- return string(s[:i])
-}
diff --git a/vendor/gioui.org/internal/vk/vulkan.go b/vendor/gioui.org/internal/vk/vulkan.go
new file mode 100644
index 0000000..f210d40
--- /dev/null
+++ b/vendor/gioui.org/internal/vk/vulkan.go
@@ -0,0 +1,2081 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build linux || freebsd
+// +build linux freebsd
+
+package vk
+
+/*
+#cgo linux freebsd LDFLAGS: -ldl
+#cgo freebsd CFLAGS: -I/usr/local/include
+#cgo CFLAGS: -Werror -Werror=return-type
+
+#define VK_NO_PROTOTYPES 1
+#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
+#include <vulkan/vulkan.h>
+#define __USE_GNU
+#include <dlfcn.h>
+#include <stdlib.h>
+
+static VkResult vkCreateInstance(PFN_vkCreateInstance f, VkInstanceCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) {
+ return f(&pCreateInfo, pAllocator, pInstance);
+}
+
+static void vkDestroyInstance(PFN_vkDestroyInstance f, VkInstance instance, const VkAllocationCallbacks *pAllocator) {
+ f(instance, pAllocator);
+}
+
+static VkResult vkEnumeratePhysicalDevices(PFN_vkEnumeratePhysicalDevices f, VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) {
+ return f(instance, pPhysicalDeviceCount, pPhysicalDevices);
+}
+
+static void vkGetPhysicalDeviceQueueFamilyProperties(PFN_vkGetPhysicalDeviceQueueFamilyProperties f, VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties) {
+ f(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+static void vkGetPhysicalDeviceFormatProperties(PFN_vkGetPhysicalDeviceFormatProperties f, VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties *pFormatProperties) {
+ f(physicalDevice, format, pFormatProperties);
+}
+
+static VkResult vkCreateDevice(PFN_vkCreateDevice f, VkPhysicalDevice physicalDevice, VkDeviceCreateInfo pCreateInfo, VkDeviceQueueCreateInfo qinf, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {
+ pCreateInfo.pQueueCreateInfos = &qinf;
+ return f(physicalDevice, &pCreateInfo, pAllocator, pDevice);
+}
+
+static void vkDestroyDevice(PFN_vkDestroyDevice f, VkDevice device, const VkAllocationCallbacks *pAllocator) {
+ f(device, pAllocator);
+}
+
+static void vkGetDeviceQueue(PFN_vkGetDeviceQueue f, VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue *pQueue) {
+ f(device, queueFamilyIndex, queueIndex, pQueue);
+}
+
+static VkResult vkCreateImageView(PFN_vkCreateImageView f, VkDevice device, const VkImageViewCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImageView *pView) {
+ return f(device, pCreateInfo, pAllocator, pView);
+}
+
+static void vkDestroyImageView(PFN_vkDestroyImageView f, VkDevice device, VkImageView imageView, const VkAllocationCallbacks *pAllocator) {
+ f(device, imageView, pAllocator);
+}
+
+static VkResult vkCreateFramebuffer(PFN_vkCreateFramebuffer f, VkDevice device, VkFramebufferCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer) {
+ return f(device, &pCreateInfo, pAllocator, pFramebuffer);
+}
+
+static void vkDestroyFramebuffer(PFN_vkDestroyFramebuffer f, VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks *pAllocator) {
+ f(device, framebuffer, pAllocator);
+}
+
+static VkResult vkDeviceWaitIdle(PFN_vkDeviceWaitIdle f, VkDevice device) {
+ return f(device);
+}
+
+static VkResult vkQueueWaitIdle(PFN_vkQueueWaitIdle f, VkQueue queue) {
+ return f(queue);
+}
+
+static VkResult vkCreateSemaphore(PFN_vkCreateSemaphore f, VkDevice device, const VkSemaphoreCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSemaphore *pSemaphore) {
+ return f(device, pCreateInfo, pAllocator, pSemaphore);
+}
+
+static void vkDestroySemaphore(PFN_vkDestroySemaphore f, VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks *pAllocator) {
+ f(device, semaphore, pAllocator);
+}
+
+static VkResult vkCreateRenderPass(PFN_vkCreateRenderPass f, VkDevice device, VkRenderPassCreateInfo pCreateInfo, VkSubpassDescription subpassInf, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) {
+ pCreateInfo.pSubpasses = &subpassInf;
+ return f(device, &pCreateInfo, pAllocator, pRenderPass);
+}
+
+static void vkDestroyRenderPass(PFN_vkDestroyRenderPass f, VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks *pAllocator) {
+ f(device, renderPass, pAllocator);
+}
+
+static VkResult vkCreateCommandPool(PFN_vkCreateCommandPool f, VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool) {
+ return f(device, pCreateInfo, pAllocator, pCommandPool);
+}
+
+static void vkDestroyCommandPool(PFN_vkDestroyCommandPool f, VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks *pAllocator) {
+ f(device, commandPool, pAllocator);
+}
+
+static VkResult vkAllocateCommandBuffers(PFN_vkAllocateCommandBuffers f, VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo, VkCommandBuffer *pCommandBuffers) {
+ return f(device, pAllocateInfo, pCommandBuffers);
+}
+
+static void vkFreeCommandBuffers(PFN_vkFreeCommandBuffers f, VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers) {
+ f(device, commandPool, commandBufferCount, pCommandBuffers);
+}
+
+static VkResult vkBeginCommandBuffer(PFN_vkBeginCommandBuffer f, VkCommandBuffer commandBuffer, VkCommandBufferBeginInfo pBeginInfo) {
+ return f(commandBuffer, &pBeginInfo);
+}
+
+static VkResult vkEndCommandBuffer(PFN_vkEndCommandBuffer f, VkCommandBuffer commandBuffer) {
+ return f(commandBuffer);
+}
+
+static VkResult vkQueueSubmit(PFN_vkQueueSubmit f, VkQueue queue, VkSubmitInfo pSubmits, VkFence fence) {
+ return f(queue, 1, &pSubmits, fence);
+}
+
+static void vkCmdBeginRenderPass(PFN_vkCmdBeginRenderPass f, VkCommandBuffer commandBuffer, VkRenderPassBeginInfo pRenderPassBegin, VkSubpassContents contents) {
+ f(commandBuffer, &pRenderPassBegin, contents);
+}
+
+static void vkCmdEndRenderPass(PFN_vkCmdEndRenderPass f, VkCommandBuffer commandBuffer) {
+ f(commandBuffer);
+}
+
+static void vkCmdCopyBuffer(PFN_vkCmdCopyBuffer f, VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy *pRegions) {
+ f(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions);
+}
+
+static void vkCmdCopyBufferToImage(PFN_vkCmdCopyBufferToImage f, VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy *pRegions) {
+ f(commandBuffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions);
+}
+
+static void vkCmdPipelineBarrier(PFN_vkCmdPipelineBarrier f, VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers) {
+ f(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers);
+}
+
+static void vkCmdPushConstants(PFN_vkCmdPushConstants f, VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void *pValues) {
+ f(commandBuffer, layout, stageFlags, offset, size, pValues);
+}
+
+static void vkCmdBindPipeline(PFN_vkCmdBindPipeline f, VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) {
+ f(commandBuffer, pipelineBindPoint, pipeline);
+}
+
+static void vkCmdBindVertexBuffers(PFN_vkCmdBindVertexBuffers f, VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer *pBuffers, const VkDeviceSize *pOffsets) {
+ f(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets);
+}
+
+static void vkCmdSetViewport(PFN_vkCmdSetViewport f, VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport *pViewports) {
+ f(commandBuffer, firstViewport, viewportCount, pViewports);
+}
+
+static void vkCmdBindIndexBuffer(PFN_vkCmdBindIndexBuffer f, VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType) {
+ f(commandBuffer, buffer, offset, indexType);
+}
+
+static void vkCmdDraw(PFN_vkCmdDraw f, VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) {
+ f(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance);
+}
+
+static void vkCmdDrawIndexed(PFN_vkCmdDrawIndexed f, VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) {
+ f(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
+}
+
+static void vkCmdBindDescriptorSets(PFN_vkCmdBindDescriptorSets f, VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t *pDynamicOffsets) {
+ f(commandBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets);
+}
+
+static void vkCmdCopyImageToBuffer(PFN_vkCmdCopyImageToBuffer f, VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy *pRegions) {
+ f(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions);
+}
+
+static void vkCmdDispatch(PFN_vkCmdDispatch f, VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
+ f(commandBuffer, groupCountX, groupCountY, groupCountZ);
+}
+
+static VkResult vkCreateImage(PFN_vkCreateImage f, VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage) {
+ return f(device, pCreateInfo, pAllocator, pImage);
+}
+
+static void vkDestroyImage(PFN_vkDestroyImage f, VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator) {
+ f(device, image, pAllocator);
+}
+
+static void vkGetImageMemoryRequirements(PFN_vkGetImageMemoryRequirements f, VkDevice device, VkImage image, VkMemoryRequirements *pMemoryRequirements) {
+ f(device, image, pMemoryRequirements);
+}
+
+static VkResult vkAllocateMemory(PFN_vkAllocateMemory f, VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory) {
+ return f(device, pAllocateInfo, pAllocator, pMemory);
+}
+
+static VkResult vkBindImageMemory(PFN_vkBindImageMemory f, VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
+ return f(device, image, memory, memoryOffset);
+}
+
+static void vkFreeMemory(PFN_vkFreeMemory f, VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks *pAllocator) {
+ f(device, memory, pAllocator);
+}
+
+static void vkGetPhysicalDeviceMemoryProperties(PFN_vkGetPhysicalDeviceMemoryProperties f, VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties) {
+ f(physicalDevice, pMemoryProperties);
+}
+
+static VkResult vkCreateSampler(PFN_vkCreateSampler f,VkDevice device, const VkSamplerCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSampler *pSampler) {
+ return f(device, pCreateInfo, pAllocator, pSampler);
+}
+
+static void vkDestroySampler(PFN_vkDestroySampler f, VkDevice device, VkSampler sampler, const VkAllocationCallbacks *pAllocator) {
+ f(device, sampler, pAllocator);
+}
+
+static VkResult vkCreateBuffer(PFN_vkCreateBuffer f, VkDevice device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) {
+ return f(device, pCreateInfo, pAllocator, pBuffer);
+}
+
+static void vkDestroyBuffer(PFN_vkDestroyBuffer f, VkDevice device, VkBuffer buffer, const VkAllocationCallbacks *pAllocator) {
+ f(device, buffer, pAllocator);
+}
+
+static void vkGetBufferMemoryRequirements(PFN_vkGetBufferMemoryRequirements f, VkDevice device, VkBuffer buffer, VkMemoryRequirements *pMemoryRequirements) {
+ f(device, buffer, pMemoryRequirements);
+}
+
+static VkResult vkBindBufferMemory(PFN_vkBindBufferMemory f, VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
+ return f(device, buffer, memory, memoryOffset);
+}
+
+static VkResult vkCreateShaderModule(PFN_vkCreateShaderModule f, VkDevice device, VkShaderModuleCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkShaderModule *pShaderModule) {
+ return f(device, &pCreateInfo, pAllocator, pShaderModule);
+}
+
+static void vkDestroyShaderModule(PFN_vkDestroyShaderModule f, VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks *pAllocator) {
+ f(device, shaderModule, pAllocator);
+}
+
+static VkResult vkCreateGraphicsPipelines(PFN_vkCreateGraphicsPipelines f, VkDevice device, VkPipelineCache pipelineCache, VkGraphicsPipelineCreateInfo pCreateInfo, VkPipelineDynamicStateCreateInfo dynInf, VkPipelineColorBlendStateCreateInfo blendInf, VkPipelineVertexInputStateCreateInfo vertexInf, VkPipelineViewportStateCreateInfo viewportInf, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines) {
+ pCreateInfo.pDynamicState = &dynInf;
+ pCreateInfo.pViewportState = &viewportInf;
+ pCreateInfo.pColorBlendState = &blendInf;
+ pCreateInfo.pVertexInputState = &vertexInf;
+ return f(device, pipelineCache, 1, &pCreateInfo, pAllocator, pPipelines);
+}
+
+static void vkDestroyPipeline(PFN_vkDestroyPipeline f, VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks *pAllocator) {
+ f(device, pipeline, pAllocator);
+}
+
+static VkResult vkCreatePipelineLayout(PFN_vkCreatePipelineLayout f, VkDevice device, VkPipelineLayoutCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkPipelineLayout *pPipelineLayout) {
+ return f(device, &pCreateInfo, pAllocator, pPipelineLayout);
+}
+
+static void vkDestroyPipelineLayout(PFN_vkDestroyPipelineLayout f, VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks *pAllocator) {
+ f(device, pipelineLayout, pAllocator);
+}
+
+static VkResult vkCreateDescriptorSetLayout(PFN_vkCreateDescriptorSetLayout f, VkDevice device, VkDescriptorSetLayoutCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorSetLayout *pSetLayout) {
+ return f(device, &pCreateInfo, pAllocator, pSetLayout);
+}
+
+static void vkDestroyDescriptorSetLayout(PFN_vkDestroyDescriptorSetLayout f, VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks *pAllocator) {
+ f(device, descriptorSetLayout, pAllocator);
+}
+
+static VkResult vkMapMemory(PFN_vkMapMemory f, VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **ppData) {
+ return f(device, memory, offset, size, flags, ppData);
+}
+
+static void vkUnmapMemory(PFN_vkUnmapMemory f, VkDevice device, VkDeviceMemory memory) {
+ f(device, memory);
+}
+
+static VkResult vkResetCommandBuffer(PFN_vkResetCommandBuffer f, VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags) {
+ return f(commandBuffer, flags);
+}
+
+static VkResult vkCreateDescriptorPool(PFN_vkCreateDescriptorPool f, VkDevice device, VkDescriptorPoolCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorPool *pDescriptorPool) {
+ return f(device, &pCreateInfo, pAllocator, pDescriptorPool);
+}
+
+static void vkDestroyDescriptorPool(PFN_vkDestroyDescriptorPool f, VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks *pAllocator) {
+ f(device, descriptorPool, pAllocator);
+}
+
+static VkResult vkAllocateDescriptorSets(PFN_vkAllocateDescriptorSets f, VkDevice device, VkDescriptorSetAllocateInfo pAllocateInfo, VkDescriptorSet *pDescriptorSets) {
+ return f(device, &pAllocateInfo, pDescriptorSets);
+}
+
+static VkResult vkFreeDescriptorSets(PFN_vkFreeDescriptorSets f, VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets) {
+ return f(device, descriptorPool, descriptorSetCount, pDescriptorSets);
+}
+
+static void vkUpdateDescriptorSets(PFN_vkUpdateDescriptorSets f, VkDevice device, VkWriteDescriptorSet pDescriptorWrite, uint32_t descriptorCopyCount, const VkCopyDescriptorSet *pDescriptorCopies) {
+ f(device, 1, &pDescriptorWrite, descriptorCopyCount, pDescriptorCopies);
+}
+
+static VkResult vkResetDescriptorPool(PFN_vkResetDescriptorPool f, VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags) {
+ return f(device, descriptorPool, flags);
+}
+
+static void vkCmdCopyImage(PFN_vkCmdCopyImage f, VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions) {
+ f(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
+}
+
+static VkResult vkCreateComputePipelines(PFN_vkCreateComputePipelines f, VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines) {
+ return f(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);
+}
+
+static VkResult vkCreateFence(PFN_vkCreateFence f, VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence) {
+ return f(device, pCreateInfo, pAllocator, pFence);
+}
+
+static void vkDestroyFence(PFN_vkDestroyFence f, VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator) {
+ f(device, fence, pAllocator);
+}
+
+static VkResult vkWaitForFences(PFN_vkWaitForFences f, VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout) {
+ return f(device, fenceCount, pFences, waitAll, timeout);
+}
+
+static VkResult vkResetFences(PFN_vkResetFences f, VkDevice device, uint32_t fenceCount, const VkFence *pFences) {
+ return f(device, fenceCount, pFences);
+}
+
+static void vkGetPhysicalDeviceProperties(PFN_vkGetPhysicalDeviceProperties f, VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties *pProperties) {
+ f(physicalDevice, pProperties);
+}
+
+static VkResult vkGetPhysicalDeviceSurfaceSupportKHR(PFN_vkGetPhysicalDeviceSurfaceSupportKHR f, VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32 *pSupported) {
+ return f(physicalDevice, queueFamilyIndex, surface, pSupported);
+}
+
+static void vkDestroySurfaceKHR(PFN_vkDestroySurfaceKHR f, VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks *pAllocator) {
+ f(instance, surface, pAllocator);
+}
+
+static VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(PFN_vkGetPhysicalDeviceSurfaceFormatsKHR f, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount, VkSurfaceFormatKHR *pSurfaceFormats) {
+ return f(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats);
+}
+
+static VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(PFN_vkGetPhysicalDeviceSurfacePresentModesKHR f, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes) {
+ return f(physicalDevice, surface, pPresentModeCount, pPresentModes);
+}
+
+static VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR f, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) {
+ return f(physicalDevice, surface, pSurfaceCapabilities);
+}
+
+static VkResult vkCreateSwapchainKHR(PFN_vkCreateSwapchainKHR f, VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) {
+ return f(device, pCreateInfo, pAllocator, pSwapchain);
+}
+
+static void vkDestroySwapchainKHR(PFN_vkDestroySwapchainKHR f, VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks *pAllocator) {
+ f(device, swapchain, pAllocator);
+}
+
+static VkResult vkGetSwapchainImagesKHR(PFN_vkGetSwapchainImagesKHR f, VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages) {
+ return f(device, swapchain, pSwapchainImageCount, pSwapchainImages);
+}
+
+// indexAndResult holds both an integer and a result returned by value, to
+// avoid Go heap allocation of the integer with Vulkan's return style.
+struct intAndResult {
+ uint32_t uint;
+ VkResult res;
+};
+
+static struct intAndResult vkAcquireNextImageKHR(PFN_vkAcquireNextImageKHR f, VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence) {
+ struct intAndResult res;
+ res.res = f(device, swapchain, timeout, semaphore, fence, &res.uint);
+ return res;
+}
+
+static VkResult vkQueuePresentKHR(PFN_vkQueuePresentKHR f, VkQueue queue, const VkPresentInfoKHR pPresentInfo) {
+ return f(queue, &pPresentInfo);
+}
+*/
+import "C"
+import (
+ "errors"
+ "fmt"
+ "image"
+ "math"
+ "reflect"
+ "runtime"
+ "sync"
+ "unsafe"
+)
+
+type (
+ AttachmentLoadOp = C.VkAttachmentLoadOp
+ AccessFlags = C.VkAccessFlags
+ BlendFactor = C.VkBlendFactor
+ Buffer = C.VkBuffer
+ BufferImageCopy = C.VkBufferImageCopy
+ BufferMemoryBarrier = C.VkBufferMemoryBarrier
+ BufferUsageFlags = C.VkBufferUsageFlags
+ CommandPool = C.VkCommandPool
+ CommandBuffer = C.VkCommandBuffer
+ DependencyFlags = C.VkDependencyFlags
+ DescriptorPool = C.VkDescriptorPool
+ DescriptorPoolSize = C.VkDescriptorPoolSize
+ DescriptorSet = C.VkDescriptorSet
+ DescriptorSetLayout = C.VkDescriptorSetLayout
+ DescriptorType = C.VkDescriptorType
+ Device = C.VkDevice
+ DeviceMemory = C.VkDeviceMemory
+ DeviceSize = C.VkDeviceSize
+ Fence = C.VkFence
+ Queue = C.VkQueue
+ IndexType = C.VkIndexType
+ Image = C.VkImage
+ ImageCopy = C.VkImageCopy
+ ImageLayout = C.VkImageLayout
+ ImageMemoryBarrier = C.VkImageMemoryBarrier
+ ImageUsageFlags = C.VkImageUsageFlags
+ ImageView = C.VkImageView
+ Instance = C.VkInstance
+ Filter = C.VkFilter
+ Format = C.VkFormat
+ FormatFeatureFlags = C.VkFormatFeatureFlags
+ Framebuffer = C.VkFramebuffer
+ MemoryBarrier = C.VkMemoryBarrier
+ MemoryPropertyFlags = C.VkMemoryPropertyFlags
+ Pipeline = C.VkPipeline
+ PipelineBindPoint = C.VkPipelineBindPoint
+ PipelineLayout = C.VkPipelineLayout
+ PipelineStageFlags = C.VkPipelineStageFlags
+ PhysicalDevice = C.VkPhysicalDevice
+ PrimitiveTopology = C.VkPrimitiveTopology
+ PushConstantRange = C.VkPushConstantRange
+ QueueFamilyProperties = C.VkQueueFamilyProperties
+ QueueFlags = C.VkQueueFlags
+ RenderPass = C.VkRenderPass
+ Sampler = C.VkSampler
+ SamplerMipmapMode = C.VkSamplerMipmapMode
+ Semaphore = C.VkSemaphore
+ ShaderModule = C.VkShaderModule
+ ShaderStageFlags = C.VkShaderStageFlags
+ SubpassDependency = C.VkSubpassDependency
+ Viewport = C.VkViewport
+ WriteDescriptorSet = C.VkWriteDescriptorSet
+
+ Surface = C.VkSurfaceKHR
+ SurfaceCapabilities = C.VkSurfaceCapabilitiesKHR
+
+ Swapchain = C.VkSwapchainKHR
+)
+
+type VertexInputBindingDescription struct {
+ Binding int
+ Stride int
+}
+
+type VertexInputAttributeDescription struct {
+ Location int
+ Binding int
+ Format Format
+ Offset int
+}
+
+type DescriptorSetLayoutBinding struct {
+ Binding int
+ DescriptorType DescriptorType
+ StageFlags ShaderStageFlags
+}
+
+type Error C.VkResult
+
+const (
+ FORMAT_R8G8B8A8_UNORM Format = C.VK_FORMAT_R8G8B8A8_UNORM
+ FORMAT_B8G8R8A8_SRGB Format = C.VK_FORMAT_B8G8R8A8_SRGB
+ FORMAT_R8G8B8A8_SRGB Format = C.VK_FORMAT_R8G8B8A8_SRGB
+ FORMAT_R16_SFLOAT Format = C.VK_FORMAT_R16_SFLOAT
+ FORMAT_R32_SFLOAT Format = C.VK_FORMAT_R32_SFLOAT
+ FORMAT_R32G32_SFLOAT Format = C.VK_FORMAT_R32G32_SFLOAT
+ FORMAT_R32G32B32_SFLOAT Format = C.VK_FORMAT_R32G32B32_SFLOAT
+ FORMAT_R32G32B32A32_SFLOAT Format = C.VK_FORMAT_R32G32B32A32_SFLOAT
+
+ FORMAT_FEATURE_COLOR_ATTACHMENT_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
+ FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT
+ FORMAT_FEATURE_SAMPLED_IMAGE_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
+
+ IMAGE_USAGE_SAMPLED_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_SAMPLED_BIT
+ IMAGE_USAGE_COLOR_ATTACHMENT_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
+ IMAGE_USAGE_STORAGE_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_STORAGE_BIT
+ IMAGE_USAGE_TRANSFER_DST_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_TRANSFER_DST_BIT
+ IMAGE_USAGE_TRANSFER_SRC_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_TRANSFER_SRC_BIT
+
+ FILTER_NEAREST Filter = C.VK_FILTER_NEAREST
+ FILTER_LINEAR Filter = C.VK_FILTER_LINEAR
+
+ ATTACHMENT_LOAD_OP_CLEAR AttachmentLoadOp = C.VK_ATTACHMENT_LOAD_OP_CLEAR
+ ATTACHMENT_LOAD_OP_DONT_CARE AttachmentLoadOp = C.VK_ATTACHMENT_LOAD_OP_DONT_CARE
+ ATTACHMENT_LOAD_OP_LOAD AttachmentLoadOp = C.VK_ATTACHMENT_LOAD_OP_LOAD
+
+ IMAGE_LAYOUT_UNDEFINED ImageLayout = C.VK_IMAGE_LAYOUT_UNDEFINED
+ IMAGE_LAYOUT_PRESENT_SRC_KHR ImageLayout = C.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
+ IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
+ IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
+ IMAGE_LAYOUT_GENERAL ImageLayout = C.VK_IMAGE_LAYOUT_GENERAL
+
+ BUFFER_USAGE_TRANSFER_DST_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_TRANSFER_DST_BIT
+ BUFFER_USAGE_TRANSFER_SRC_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_TRANSFER_SRC_BIT
+ BUFFER_USAGE_UNIFORM_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
+ BUFFER_USAGE_STORAGE_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
+ BUFFER_USAGE_INDEX_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_INDEX_BUFFER_BIT
+ BUFFER_USAGE_VERTEX_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
+
+ ERROR_OUT_OF_DATE_KHR = Error(C.VK_ERROR_OUT_OF_DATE_KHR)
+ ERROR_SURFACE_LOST_KHR = Error(C.VK_ERROR_SURFACE_LOST_KHR)
+ ERROR_DEVICE_LOST = Error(C.VK_ERROR_DEVICE_LOST)
+ SUBOPTIMAL_KHR = Error(C.VK_SUBOPTIMAL_KHR)
+
+ BLEND_FACTOR_ZERO BlendFactor = C.VK_BLEND_FACTOR_ZERO
+ BLEND_FACTOR_ONE BlendFactor = C.VK_BLEND_FACTOR_ONE
+ BLEND_FACTOR_ONE_MINUS_SRC_ALPHA BlendFactor = C.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
+ BLEND_FACTOR_DST_COLOR BlendFactor = C.VK_BLEND_FACTOR_DST_COLOR
+
+ PRIMITIVE_TOPOLOGY_TRIANGLE_LIST PrimitiveTopology = C.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
+ PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP PrimitiveTopology = C.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
+
+ SHADER_STAGE_VERTEX_BIT ShaderStageFlags = C.VK_SHADER_STAGE_VERTEX_BIT
+ SHADER_STAGE_FRAGMENT_BIT ShaderStageFlags = C.VK_SHADER_STAGE_FRAGMENT_BIT
+ SHADER_STAGE_COMPUTE_BIT ShaderStageFlags = C.VK_SHADER_STAGE_COMPUTE_BIT
+
+ DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER DescriptorType = C.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
+ DESCRIPTOR_TYPE_UNIFORM_BUFFER DescriptorType = C.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
+ DESCRIPTOR_TYPE_STORAGE_BUFFER DescriptorType = C.VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
+ DESCRIPTOR_TYPE_STORAGE_IMAGE DescriptorType = C.VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
+
+ MEMORY_PROPERTY_DEVICE_LOCAL_BIT MemoryPropertyFlags = C.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
+ MEMORY_PROPERTY_HOST_VISIBLE_BIT MemoryPropertyFlags = C.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
+ MEMORY_PROPERTY_HOST_COHERENT_BIT MemoryPropertyFlags = C.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
+
+ DEPENDENCY_BY_REGION_BIT DependencyFlags = C.VK_DEPENDENCY_BY_REGION_BIT
+
+ PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
+ PIPELINE_STAGE_TRANSFER_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_TRANSFER_BIT
+ PIPELINE_STAGE_FRAGMENT_SHADER_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
+ PIPELINE_STAGE_COMPUTE_SHADER_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
+ PIPELINE_STAGE_TOP_OF_PIPE_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
+ PIPELINE_STAGE_HOST_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_HOST_BIT
+ PIPELINE_STAGE_VERTEX_INPUT_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_VERTEX_INPUT_BIT
+ PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
+
+ ACCESS_MEMORY_READ_BIT AccessFlags = C.VK_ACCESS_MEMORY_READ_BIT
+ ACCESS_MEMORY_WRITE_BIT AccessFlags = C.VK_ACCESS_MEMORY_WRITE_BIT
+ ACCESS_TRANSFER_READ_BIT AccessFlags = C.VK_ACCESS_TRANSFER_READ_BIT
+ ACCESS_TRANSFER_WRITE_BIT AccessFlags = C.VK_ACCESS_TRANSFER_WRITE_BIT
+ ACCESS_SHADER_READ_BIT AccessFlags = C.VK_ACCESS_SHADER_READ_BIT
+ ACCESS_SHADER_WRITE_BIT AccessFlags = C.VK_ACCESS_SHADER_WRITE_BIT
+ ACCESS_COLOR_ATTACHMENT_READ_BIT AccessFlags = C.VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
+ ACCESS_COLOR_ATTACHMENT_WRITE_BIT AccessFlags = C.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
+ ACCESS_HOST_READ_BIT AccessFlags = C.VK_ACCESS_HOST_READ_BIT
+ ACCESS_HOST_WRITE_BIT AccessFlags = C.VK_ACCESS_HOST_WRITE_BIT
+ ACCESS_VERTEX_ATTRIBUTE_READ_BIT AccessFlags = C.VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT
+ ACCESS_INDEX_READ_BIT AccessFlags = C.VK_ACCESS_INDEX_READ_BIT
+
+ PIPELINE_BIND_POINT_COMPUTE PipelineBindPoint = C.VK_PIPELINE_BIND_POINT_COMPUTE
+ PIPELINE_BIND_POINT_GRAPHICS PipelineBindPoint = C.VK_PIPELINE_BIND_POINT_GRAPHICS
+
+ INDEX_TYPE_UINT16 IndexType = C.VK_INDEX_TYPE_UINT16
+ INDEX_TYPE_UINT32 IndexType = C.VK_INDEX_TYPE_UINT32
+
+ QUEUE_GRAPHICS_BIT QueueFlags = C.VK_QUEUE_GRAPHICS_BIT
+ QUEUE_COMPUTE_BIT QueueFlags = C.VK_QUEUE_COMPUTE_BIT
+)
+
+var (
+ once sync.Once
+ loadErr error
+
+ loadFuncs []func(dlopen func(name string) *[0]byte)
+)
+
+var funcs struct {
+ vkCreateInstance C.PFN_vkCreateInstance
+ vkDestroyInstance C.PFN_vkDestroyInstance
+ vkEnumeratePhysicalDevices C.PFN_vkEnumeratePhysicalDevices
+ vkGetPhysicalDeviceQueueFamilyProperties C.PFN_vkGetPhysicalDeviceQueueFamilyProperties
+ vkGetPhysicalDeviceFormatProperties C.PFN_vkGetPhysicalDeviceFormatProperties
+ vkCreateDevice C.PFN_vkCreateDevice
+ vkDestroyDevice C.PFN_vkDestroyDevice
+ vkGetDeviceQueue C.PFN_vkGetDeviceQueue
+ vkCreateImageView C.PFN_vkCreateImageView
+ vkDestroyImageView C.PFN_vkDestroyImageView
+ vkCreateFramebuffer C.PFN_vkCreateFramebuffer
+ vkDestroyFramebuffer C.PFN_vkDestroyFramebuffer
+ vkDeviceWaitIdle C.PFN_vkDeviceWaitIdle
+ vkQueueWaitIdle C.PFN_vkQueueWaitIdle
+ vkCreateSemaphore C.PFN_vkCreateSemaphore
+ vkDestroySemaphore C.PFN_vkDestroySemaphore
+ vkCreateRenderPass C.PFN_vkCreateRenderPass
+ vkDestroyRenderPass C.PFN_vkDestroyRenderPass
+ vkCreateCommandPool C.PFN_vkCreateCommandPool
+ vkDestroyCommandPool C.PFN_vkDestroyCommandPool
+ vkAllocateCommandBuffers C.PFN_vkAllocateCommandBuffers
+ vkFreeCommandBuffers C.PFN_vkFreeCommandBuffers
+ vkBeginCommandBuffer C.PFN_vkBeginCommandBuffer
+ vkEndCommandBuffer C.PFN_vkEndCommandBuffer
+ vkQueueSubmit C.PFN_vkQueueSubmit
+ vkCmdBeginRenderPass C.PFN_vkCmdBeginRenderPass
+ vkCmdEndRenderPass C.PFN_vkCmdEndRenderPass
+ vkCmdCopyBuffer C.PFN_vkCmdCopyBuffer
+ vkCmdCopyBufferToImage C.PFN_vkCmdCopyBufferToImage
+ vkCmdPipelineBarrier C.PFN_vkCmdPipelineBarrier
+ vkCmdPushConstants C.PFN_vkCmdPushConstants
+ vkCmdBindPipeline C.PFN_vkCmdBindPipeline
+ vkCmdBindVertexBuffers C.PFN_vkCmdBindVertexBuffers
+ vkCmdSetViewport C.PFN_vkCmdSetViewport
+ vkCmdBindIndexBuffer C.PFN_vkCmdBindIndexBuffer
+ vkCmdDraw C.PFN_vkCmdDraw
+ vkCmdDrawIndexed C.PFN_vkCmdDrawIndexed
+ vkCmdBindDescriptorSets C.PFN_vkCmdBindDescriptorSets
+ vkCmdCopyImageToBuffer C.PFN_vkCmdCopyImageToBuffer
+ vkCmdDispatch C.PFN_vkCmdDispatch
+ vkCreateImage C.PFN_vkCreateImage
+ vkDestroyImage C.PFN_vkDestroyImage
+ vkGetImageMemoryRequirements C.PFN_vkGetImageMemoryRequirements
+ vkAllocateMemory C.PFN_vkAllocateMemory
+ vkBindImageMemory C.PFN_vkBindImageMemory
+ vkFreeMemory C.PFN_vkFreeMemory
+ vkGetPhysicalDeviceMemoryProperties C.PFN_vkGetPhysicalDeviceMemoryProperties
+ vkCreateSampler C.PFN_vkCreateSampler
+ vkDestroySampler C.PFN_vkDestroySampler
+ vkCreateBuffer C.PFN_vkCreateBuffer
+ vkDestroyBuffer C.PFN_vkDestroyBuffer
+ vkGetBufferMemoryRequirements C.PFN_vkGetBufferMemoryRequirements
+ vkBindBufferMemory C.PFN_vkBindBufferMemory
+ vkCreateShaderModule C.PFN_vkCreateShaderModule
+ vkDestroyShaderModule C.PFN_vkDestroyShaderModule
+ vkCreateGraphicsPipelines C.PFN_vkCreateGraphicsPipelines
+ vkDestroyPipeline C.PFN_vkDestroyPipeline
+ vkCreatePipelineLayout C.PFN_vkCreatePipelineLayout
+ vkDestroyPipelineLayout C.PFN_vkDestroyPipelineLayout
+ vkCreateDescriptorSetLayout C.PFN_vkCreateDescriptorSetLayout
+ vkDestroyDescriptorSetLayout C.PFN_vkDestroyDescriptorSetLayout
+ vkMapMemory C.PFN_vkMapMemory
+ vkUnmapMemory C.PFN_vkUnmapMemory
+ vkResetCommandBuffer C.PFN_vkResetCommandBuffer
+ vkCreateDescriptorPool C.PFN_vkCreateDescriptorPool
+ vkDestroyDescriptorPool C.PFN_vkDestroyDescriptorPool
+ vkAllocateDescriptorSets C.PFN_vkAllocateDescriptorSets
+ vkFreeDescriptorSets C.PFN_vkFreeDescriptorSets
+ vkUpdateDescriptorSets C.PFN_vkUpdateDescriptorSets
+ vkResetDescriptorPool C.PFN_vkResetDescriptorPool
+ vkCmdCopyImage C.PFN_vkCmdCopyImage
+ vkCreateComputePipelines C.PFN_vkCreateComputePipelines
+ vkCreateFence C.PFN_vkCreateFence
+ vkDestroyFence C.PFN_vkDestroyFence
+ vkWaitForFences C.PFN_vkWaitForFences
+ vkResetFences C.PFN_vkResetFences
+ vkGetPhysicalDeviceProperties C.PFN_vkGetPhysicalDeviceProperties
+
+ vkGetPhysicalDeviceSurfaceSupportKHR C.PFN_vkGetPhysicalDeviceSurfaceSupportKHR
+ vkDestroySurfaceKHR C.PFN_vkDestroySurfaceKHR
+ vkGetPhysicalDeviceSurfaceFormatsKHR C.PFN_vkGetPhysicalDeviceSurfaceFormatsKHR
+ vkGetPhysicalDeviceSurfacePresentModesKHR C.PFN_vkGetPhysicalDeviceSurfacePresentModesKHR
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR C.PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
+
+ vkCreateSwapchainKHR C.PFN_vkCreateSwapchainKHR
+ vkDestroySwapchainKHR C.PFN_vkDestroySwapchainKHR
+ vkGetSwapchainImagesKHR C.PFN_vkGetSwapchainImagesKHR
+ vkAcquireNextImageKHR C.PFN_vkAcquireNextImageKHR
+ vkQueuePresentKHR C.PFN_vkQueuePresentKHR
+}
+
+var (
+ nilSurface C.VkSurfaceKHR
+ nilSwapchain C.VkSwapchainKHR
+ nilSemaphore C.VkSemaphore
+ nilImageView C.VkImageView
+ nilRenderPass C.VkRenderPass
+ nilFramebuffer C.VkFramebuffer
+ nilCommandPool C.VkCommandPool
+ nilImage C.VkImage
+ nilDeviceMemory C.VkDeviceMemory
+ nilSampler C.VkSampler
+ nilBuffer C.VkBuffer
+ nilShaderModule C.VkShaderModule
+ nilPipeline C.VkPipeline
+ nilPipelineCache C.VkPipelineCache
+ nilPipelineLayout C.VkPipelineLayout
+ nilDescriptorSetLayout C.VkDescriptorSetLayout
+ nilDescriptorPool C.VkDescriptorPool
+ nilDescriptorSet C.VkDescriptorSet
+ nilFence C.VkFence
+)
+
+func vkInit() error {
+ once.Do(func() {
+ var libName string
+ switch {
+ case runtime.GOOS == "android":
+ libName = "libvulkan.so"
+ default:
+ libName = "libvulkan.so.1"
+ }
+ lib := dlopen(libName)
+ if lib == nil {
+ loadErr = fmt.Errorf("vulkan: %s", C.GoString(C.dlerror()))
+ return
+ }
+ dlopen := func(name string) *[0]byte {
+ return (*[0]byte)(dlsym(lib, name))
+ }
+ must := func(name string) *[0]byte {
+ ptr := dlopen(name)
+ if ptr != nil {
+ return ptr
+ }
+ if loadErr == nil {
+ loadErr = fmt.Errorf("vulkan: function %q not found: %s", name, C.GoString(C.dlerror()))
+ }
+ return nil
+ }
+ funcs.vkCreateInstance = must("vkCreateInstance")
+ funcs.vkDestroyInstance = must("vkDestroyInstance")
+ funcs.vkEnumeratePhysicalDevices = must("vkEnumeratePhysicalDevices")
+ funcs.vkGetPhysicalDeviceQueueFamilyProperties = must("vkGetPhysicalDeviceQueueFamilyProperties")
+ funcs.vkGetPhysicalDeviceFormatProperties = must("vkGetPhysicalDeviceFormatProperties")
+ funcs.vkCreateDevice = must("vkCreateDevice")
+ funcs.vkDestroyDevice = must("vkDestroyDevice")
+ funcs.vkGetDeviceQueue = must("vkGetDeviceQueue")
+ funcs.vkCreateImageView = must("vkCreateImageView")
+ funcs.vkDestroyImageView = must("vkDestroyImageView")
+ funcs.vkCreateFramebuffer = must("vkCreateFramebuffer")
+ funcs.vkDestroyFramebuffer = must("vkDestroyFramebuffer")
+ funcs.vkDeviceWaitIdle = must("vkDeviceWaitIdle")
+ funcs.vkQueueWaitIdle = must("vkQueueWaitIdle")
+ funcs.vkCreateSemaphore = must("vkCreateSemaphore")
+ funcs.vkDestroySemaphore = must("vkDestroySemaphore")
+ funcs.vkCreateRenderPass = must("vkCreateRenderPass")
+ funcs.vkDestroyRenderPass = must("vkDestroyRenderPass")
+ funcs.vkCreateCommandPool = must("vkCreateCommandPool")
+ funcs.vkDestroyCommandPool = must("vkDestroyCommandPool")
+ funcs.vkAllocateCommandBuffers = must("vkAllocateCommandBuffers")
+ funcs.vkFreeCommandBuffers = must("vkFreeCommandBuffers")
+ funcs.vkBeginCommandBuffer = must("vkBeginCommandBuffer")
+ funcs.vkEndCommandBuffer = must("vkEndCommandBuffer")
+ funcs.vkQueueSubmit = must("vkQueueSubmit")
+ funcs.vkCmdBeginRenderPass = must("vkCmdBeginRenderPass")
+ funcs.vkCmdEndRenderPass = must("vkCmdEndRenderPass")
+ funcs.vkCmdCopyBuffer = must("vkCmdCopyBuffer")
+ funcs.vkCmdCopyBufferToImage = must("vkCmdCopyBufferToImage")
+ funcs.vkCmdPipelineBarrier = must("vkCmdPipelineBarrier")
+ funcs.vkCmdPushConstants = must("vkCmdPushConstants")
+ funcs.vkCmdBindPipeline = must("vkCmdBindPipeline")
+ funcs.vkCmdBindVertexBuffers = must("vkCmdBindVertexBuffers")
+ funcs.vkCmdSetViewport = must("vkCmdSetViewport")
+ funcs.vkCmdBindIndexBuffer = must("vkCmdBindIndexBuffer")
+ funcs.vkCmdDraw = must("vkCmdDraw")
+ funcs.vkCmdDrawIndexed = must("vkCmdDrawIndexed")
+ funcs.vkCmdBindDescriptorSets = must("vkCmdBindDescriptorSets")
+ funcs.vkCmdCopyImageToBuffer = must("vkCmdCopyImageToBuffer")
+ funcs.vkCmdDispatch = must("vkCmdDispatch")
+ funcs.vkCreateImage = must("vkCreateImage")
+ funcs.vkDestroyImage = must("vkDestroyImage")
+ funcs.vkGetImageMemoryRequirements = must("vkGetImageMemoryRequirements")
+ funcs.vkAllocateMemory = must("vkAllocateMemory")
+ funcs.vkBindImageMemory = must("vkBindImageMemory")
+ funcs.vkFreeMemory = must("vkFreeMemory")
+ funcs.vkGetPhysicalDeviceMemoryProperties = must("vkGetPhysicalDeviceMemoryProperties")
+ funcs.vkCreateSampler = must("vkCreateSampler")
+ funcs.vkDestroySampler = must("vkDestroySampler")
+ funcs.vkCreateBuffer = must("vkCreateBuffer")
+ funcs.vkDestroyBuffer = must("vkDestroyBuffer")
+ funcs.vkGetBufferMemoryRequirements = must("vkGetBufferMemoryRequirements")
+ funcs.vkBindBufferMemory = must("vkBindBufferMemory")
+ funcs.vkCreateShaderModule = must("vkCreateShaderModule")
+ funcs.vkDestroyShaderModule = must("vkDestroyShaderModule")
+ funcs.vkCreateGraphicsPipelines = must("vkCreateGraphicsPipelines")
+ funcs.vkDestroyPipeline = must("vkDestroyPipeline")
+ funcs.vkCreatePipelineLayout = must("vkCreatePipelineLayout")
+ funcs.vkDestroyPipelineLayout = must("vkDestroyPipelineLayout")
+ funcs.vkCreateDescriptorSetLayout = must("vkCreateDescriptorSetLayout")
+ funcs.vkDestroyDescriptorSetLayout = must("vkDestroyDescriptorSetLayout")
+ funcs.vkMapMemory = must("vkMapMemory")
+ funcs.vkUnmapMemory = must("vkUnmapMemory")
+ funcs.vkResetCommandBuffer = must("vkResetCommandBuffer")
+ funcs.vkCreateDescriptorPool = must("vkCreateDescriptorPool")
+ funcs.vkDestroyDescriptorPool = must("vkDestroyDescriptorPool")
+ funcs.vkAllocateDescriptorSets = must("vkAllocateDescriptorSets")
+ funcs.vkFreeDescriptorSets = must("vkFreeDescriptorSets")
+ funcs.vkUpdateDescriptorSets = must("vkUpdateDescriptorSets")
+ funcs.vkResetDescriptorPool = must("vkResetDescriptorPool")
+ funcs.vkCmdCopyImage = must("vkCmdCopyImage")
+ funcs.vkCreateComputePipelines = must("vkCreateComputePipelines")
+ funcs.vkCreateFence = must("vkCreateFence")
+ funcs.vkDestroyFence = must("vkDestroyFence")
+ funcs.vkWaitForFences = must("vkWaitForFences")
+ funcs.vkResetFences = must("vkResetFences")
+ funcs.vkGetPhysicalDeviceProperties = must("vkGetPhysicalDeviceProperties")
+
+ funcs.vkGetPhysicalDeviceSurfaceSupportKHR = dlopen("vkGetPhysicalDeviceSurfaceSupportKHR")
+ funcs.vkDestroySurfaceKHR = dlopen("vkDestroySurfaceKHR")
+ funcs.vkGetPhysicalDeviceSurfaceFormatsKHR = dlopen("vkGetPhysicalDeviceSurfaceFormatsKHR")
+ funcs.vkGetPhysicalDeviceSurfacePresentModesKHR = dlopen("vkGetPhysicalDeviceSurfacePresentModesKHR")
+ funcs.vkGetPhysicalDeviceSurfaceCapabilitiesKHR = dlopen("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")
+
+ funcs.vkCreateSwapchainKHR = dlopen("vkCreateSwapchainKHR")
+ funcs.vkDestroySwapchainKHR = dlopen("vkDestroySwapchainKHR")
+ funcs.vkGetSwapchainImagesKHR = dlopen("vkGetSwapchainImagesKHR")
+ funcs.vkAcquireNextImageKHR = dlopen("vkAcquireNextImageKHR")
+ funcs.vkQueuePresentKHR = dlopen("vkQueuePresentKHR")
+
+ for _, f := range loadFuncs {
+ f(dlopen)
+ }
+ })
+ return loadErr
+}
+
+func CreateInstance(exts ...string) (Instance, error) {
+ if err := vkInit(); err != nil {
+ return nil, err
+ }
+ inf := C.VkInstanceCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ }
+ if len(exts) > 0 {
+ cexts := mallocCStringArr(exts)
+ defer freeCStringArr(cexts)
+ inf.enabledExtensionCount = C.uint32_t(len(exts))
+ inf.ppEnabledExtensionNames = &cexts[0]
+ }
+ var inst Instance
+ if err := vkErr(C.vkCreateInstance(funcs.vkCreateInstance, inf, nil, &inst)); err != nil {
+ return nil, fmt.Errorf("vulkan: vkCreateInstance: %w", err)
+ }
+ return inst, nil
+}
+
+func mallocCStringArr(s []string) []*C.char {
+ carr := make([]*C.char, len(s))
+ for i, ext := range s {
+ carr[i] = C.CString(ext)
+ }
+ return carr
+}
+
+func freeCStringArr(s []*C.char) {
+ for i := range s {
+ C.free(unsafe.Pointer(s[i]))
+ s[i] = nil
+ }
+}
+
+func DestroyInstance(inst Instance) {
+ C.vkDestroyInstance(funcs.vkDestroyInstance, inst, nil)
+}
+
+func GetPhysicalDeviceQueueFamilyProperties(pd PhysicalDevice) []QueueFamilyProperties {
+ var count C.uint32_t
+ C.vkGetPhysicalDeviceQueueFamilyProperties(funcs.vkGetPhysicalDeviceQueueFamilyProperties, pd, &count, nil)
+ if count == 0 {
+ return nil
+ }
+ queues := make([]C.VkQueueFamilyProperties, count)
+ C.vkGetPhysicalDeviceQueueFamilyProperties(funcs.vkGetPhysicalDeviceQueueFamilyProperties, pd, &count, &queues[0])
+ return queues
+}
+
+func EnumeratePhysicalDevices(inst Instance) ([]PhysicalDevice, error) {
+ var count C.uint32_t
+ if err := vkErr(C.vkEnumeratePhysicalDevices(funcs.vkEnumeratePhysicalDevices, inst, &count, nil)); err != nil {
+ return nil, fmt.Errorf("vulkan: vkEnumeratePhysicalDevices: %w", err)
+ }
+ if count == 0 {
+ return nil, nil
+ }
+ devs := make([]C.VkPhysicalDevice, count)
+ if err := vkErr(C.vkEnumeratePhysicalDevices(funcs.vkEnumeratePhysicalDevices, inst, &count, &devs[0])); err != nil {
+ return nil, fmt.Errorf("vulkan: vkEnumeratePhysicalDevices: %w", err)
+ }
+ return devs, nil
+}
+
+func ChoosePhysicalDevice(inst Instance, surf Surface) (PhysicalDevice, int, error) {
+ devs, err := EnumeratePhysicalDevices(inst)
+ if err != nil {
+ return nil, 0, err
+ }
+ for _, pd := range devs {
+ var props C.VkPhysicalDeviceProperties
+ C.vkGetPhysicalDeviceProperties(funcs.vkGetPhysicalDeviceProperties, pd, &props)
+ // The lavapipe software implementation doesn't work well rendering to a surface.
+ // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/5473.
+ if surf != 0 && props.deviceType == C.VK_PHYSICAL_DEVICE_TYPE_CPU {
+ continue
+ }
+ const caps = C.VK_QUEUE_GRAPHICS_BIT | C.VK_QUEUE_COMPUTE_BIT
+ queueIdx, ok, err := chooseQueue(pd, surf, caps)
+ if err != nil {
+ return nil, 0, err
+ }
+ if !ok {
+ continue
+ }
+ if surf != nilSurface {
+ _, fmtFound, err := chooseFormat(pd, surf)
+ if err != nil {
+ return nil, 0, err
+ }
+ _, modFound, err := choosePresentMode(pd, surf)
+ if err != nil {
+ return nil, 0, err
+ }
+ if !fmtFound || !modFound {
+ continue
+ }
+ }
+ return pd, queueIdx, nil
+ }
+ return nil, 0, errors.New("vulkan: no suitable device found")
+}
+
+func CreateDeviceAndQueue(pd C.VkPhysicalDevice, queueIdx int, exts ...string) (Device, error) {
+ priority := C.float(1.0)
+ qinf := C.VkDeviceQueueCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ queueCount: 1,
+ queueFamilyIndex: C.uint32_t(queueIdx),
+ pQueuePriorities: &priority,
+ }
+ inf := C.VkDeviceCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ queueCreateInfoCount: 1,
+ enabledExtensionCount: C.uint32_t(len(exts)),
+ }
+ if len(exts) > 0 {
+ cexts := mallocCStringArr(exts)
+ defer freeCStringArr(cexts)
+ inf.ppEnabledExtensionNames = &cexts[0]
+ }
+ var dev Device
+ if err := vkErr(C.vkCreateDevice(funcs.vkCreateDevice, pd, inf, qinf, nil, &dev)); err != nil {
+ return nil, fmt.Errorf("vulkan: vkCreateDevice: %w", err)
+ }
+ return dev, nil
+}
+
+func GetDeviceQueue(d Device, queueFamily, queueIndex int) Queue {
+ var queue Queue
+ C.vkGetDeviceQueue(funcs.vkGetDeviceQueue, d, C.uint32_t(queueFamily), C.uint32_t(queueIndex), &queue)
+ return queue
+}
+
+func GetPhysicalDeviceSurfaceCapabilities(pd PhysicalDevice, surf Surface) (SurfaceCapabilities, error) {
+ var caps C.VkSurfaceCapabilitiesKHR
+ err := vkErr(C.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(funcs.vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pd, surf, &caps))
+ if err != nil {
+ return SurfaceCapabilities{}, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceCapabilitiesKHR: %w", err)
+ }
+ return caps, nil
+}
+
+func CreateSwapchain(pd PhysicalDevice, d Device, surf Surface, width, height int, old Swapchain) (Swapchain, []Image, Format, error) {
+ caps, err := GetPhysicalDeviceSurfaceCapabilities(pd, surf)
+ if err != nil {
+ return nilSwapchain, nil, 0, err
+ }
+ mode, modeOK, err := choosePresentMode(pd, surf)
+ if err != nil {
+ return nilSwapchain, nil, 0, err
+ }
+ format, fmtOK, err := chooseFormat(pd, surf)
+ if err != nil {
+ return nilSwapchain, nil, 0, err
+ }
+ if !modeOK || !fmtOK {
+ // This shouldn't happen because CreateDeviceAndQueue found at least
+ // one valid format and present mode.
+ return nilSwapchain, nil, 0, errors.New("vulkan: no valid format and present mode found")
+ }
+ // Find supported alpha composite mode. It doesn't matter which one, because rendering is
+ // always opaque.
+ alphaComp := C.VkCompositeAlphaFlagBitsKHR(1)
+ for caps.supportedCompositeAlpha&C.VkCompositeAlphaFlagsKHR(alphaComp) == 0 {
+ alphaComp <<= 1
+ }
+ trans := C.VkSurfaceTransformFlagBitsKHR(C.VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
+ if caps.supportedTransforms&C.VkSurfaceTransformFlagsKHR(trans) == 0 {
+ return nilSwapchain, nil, 0, errors.New("vulkan: VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR not supported")
+ }
+ inf := C.VkSwapchainCreateInfoKHR{
+ sType: C.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ surface: surf,
+ minImageCount: caps.minImageCount,
+ imageFormat: format.format,
+ imageColorSpace: format.colorSpace,
+ imageExtent: C.VkExtent2D{width: C.uint32_t(width), height: C.uint32_t(height)},
+ imageArrayLayers: 1,
+ imageUsage: C.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ imageSharingMode: C.VK_SHARING_MODE_EXCLUSIVE,
+ preTransform: trans,
+ presentMode: mode,
+ compositeAlpha: C.VkCompositeAlphaFlagBitsKHR(alphaComp),
+ clipped: C.VK_TRUE,
+ oldSwapchain: old,
+ }
+ var swchain Swapchain
+ if err := vkErr(C.vkCreateSwapchainKHR(funcs.vkCreateSwapchainKHR, d, &inf, nil, &swchain)); err != nil {
+ return nilSwapchain, nil, 0, fmt.Errorf("vulkan: vkCreateSwapchainKHR: %w", err)
+ }
+ var count C.uint32_t
+ if err := vkErr(C.vkGetSwapchainImagesKHR(funcs.vkGetSwapchainImagesKHR, d, swchain, &count, nil)); err != nil {
+ DestroySwapchain(d, swchain)
+ return nilSwapchain, nil, 0, fmt.Errorf("vulkan: vkGetSwapchainImagesKHR: %w", err)
+ }
+ if count == 0 {
+ DestroySwapchain(d, swchain)
+ return nilSwapchain, nil, 0, errors.New("vulkan: vkGetSwapchainImagesKHR returned no images")
+ }
+ imgs := make([]Image, count)
+ if err := vkErr(C.vkGetSwapchainImagesKHR(funcs.vkGetSwapchainImagesKHR, d, swchain, &count, &imgs[0])); err != nil {
+ DestroySwapchain(d, swchain)
+ return nilSwapchain, nil, 0, fmt.Errorf("vulkan: vkGetSwapchainImagesKHR: %w", err)
+ }
+ return swchain, imgs, format.format, nil
+}
+
+func DestroySwapchain(d Device, swchain Swapchain) {
+ C.vkDestroySwapchainKHR(funcs.vkDestroySwapchainKHR, d, swchain, nil)
+}
+
+func AcquireNextImage(d Device, swchain Swapchain, sem Semaphore, fence Fence) (int, error) {
+ res := C.vkAcquireNextImageKHR(funcs.vkAcquireNextImageKHR, d, swchain, math.MaxUint64, sem, fence)
+ if err := vkErr(res.res); err != nil {
+ return 0, fmt.Errorf("vulkan: vkAcquireNextImageKHR: %w", err)
+ }
+ return int(res.uint), nil
+}
+
+func PresentQueue(q Queue, swchain Swapchain, sem Semaphore, imgIdx int) error {
+ cidx := C.uint32_t(imgIdx)
+ inf := C.VkPresentInfoKHR{
+ sType: C.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+ swapchainCount: 1,
+ pSwapchains: &swchain,
+ pImageIndices: &cidx,
+ }
+ if sem != nilSemaphore {
+ inf.waitSemaphoreCount = 1
+ inf.pWaitSemaphores = &sem
+ }
+ if err := vkErr(C.vkQueuePresentKHR(funcs.vkQueuePresentKHR, q, inf)); err != nil {
+ return fmt.Errorf("vulkan: vkQueuePresentKHR: %w", err)
+ }
+ return nil
+}
+
+func CreateImageView(d Device, img Image, format Format) (ImageView, error) {
+ inf := C.VkImageViewCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ image: img,
+ viewType: C.VK_IMAGE_VIEW_TYPE_2D,
+ format: format,
+ subresourceRange: C.VkImageSubresourceRange{
+ aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
+ levelCount: C.VK_REMAINING_MIP_LEVELS,
+ layerCount: C.VK_REMAINING_ARRAY_LAYERS,
+ },
+ }
+ var view C.VkImageView
+ if err := vkErr(C.vkCreateImageView(funcs.vkCreateImageView, d, &inf, nil, &view)); err != nil {
+ return nilImageView, fmt.Errorf("vulkan: vkCreateImageView: %w", err)
+ }
+ return view, nil
+}
+
+func DestroyImageView(d Device, view ImageView) {
+ C.vkDestroyImageView(funcs.vkDestroyImageView, d, view, nil)
+}
+
+func CreateRenderPass(d Device, format Format, loadOp AttachmentLoadOp, initialLayout, finalLayout ImageLayout, passDeps []SubpassDependency) (RenderPass, error) {
+ att := C.VkAttachmentDescription{
+ format: format,
+ samples: C.VK_SAMPLE_COUNT_1_BIT,
+ loadOp: loadOp,
+ storeOp: C.VK_ATTACHMENT_STORE_OP_STORE,
+ stencilLoadOp: C.VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ stencilStoreOp: C.VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ initialLayout: initialLayout,
+ finalLayout: finalLayout,
+ }
+
+ ref := C.VkAttachmentReference{
+ attachment: 0,
+ layout: C.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ }
+
+ sub := C.VkSubpassDescription{
+ pipelineBindPoint: C.VK_PIPELINE_BIND_POINT_GRAPHICS,
+ colorAttachmentCount: 1,
+ pColorAttachments: &ref,
+ }
+
+ inf := C.VkRenderPassCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ attachmentCount: 1,
+ pAttachments: &att,
+ subpassCount: 1,
+ }
+ if n := len(passDeps); n > 0 {
+ inf.dependencyCount = C.uint32_t(n)
+ inf.pDependencies = &passDeps[0]
+ }
+
+ var pass RenderPass
+ if err := vkErr(C.vkCreateRenderPass(funcs.vkCreateRenderPass, d, inf, sub, nil, &pass)); err != nil {
+ return nilRenderPass, fmt.Errorf("vulkan: vkCreateRenderPass: %w", err)
+ }
+ return pass, nil
+}
+
+func DestroyRenderPass(d Device, r RenderPass) {
+ C.vkDestroyRenderPass(funcs.vkDestroyRenderPass, d, r, nil)
+}
+
+func CreateFramebuffer(d Device, rp RenderPass, view ImageView, width, height int) (Framebuffer, error) {
+ inf := C.VkFramebufferCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ renderPass: rp,
+ attachmentCount: 1,
+ pAttachments: &view,
+ width: C.uint32_t(width),
+ height: C.uint32_t(height),
+ layers: 1,
+ }
+ var fbo Framebuffer
+ if err := vkErr(C.vkCreateFramebuffer(funcs.vkCreateFramebuffer, d, inf, nil, &fbo)); err != nil {
+ return nilFramebuffer, fmt.Errorf("vulkan: vkCreateFramebuffer: %w", err)
+ }
+ return fbo, nil
+
+}
+
+func DestroyFramebuffer(d Device, f Framebuffer) {
+ C.vkDestroyFramebuffer(funcs.vkDestroyFramebuffer, d, f, nil)
+}
+
+func DeviceWaitIdle(d Device) error {
+ if err := vkErr(C.vkDeviceWaitIdle(funcs.vkDeviceWaitIdle, d)); err != nil {
+ return fmt.Errorf("vulkan: vkDeviceWaitIdle: %w", err)
+ }
+ return nil
+}
+
+func QueueWaitIdle(q Queue) error {
+ if err := vkErr(C.vkQueueWaitIdle(funcs.vkQueueWaitIdle, q)); err != nil {
+ return fmt.Errorf("vulkan: vkQueueWaitIdle: %w", err)
+ }
+ return nil
+}
+
+func CreateSemaphore(d Device) (Semaphore, error) {
+ inf := C.VkSemaphoreCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ }
+ var sem Semaphore
+ err := vkErr(C.vkCreateSemaphore(funcs.vkCreateSemaphore, d, &inf, nil, &sem))
+ if err != nil {
+ return nilSemaphore, fmt.Errorf("vulkan: vkCreateSemaphore: %w", err)
+ }
+ return sem, err
+}
+
+func DestroySemaphore(d Device, sem Semaphore) {
+ C.vkDestroySemaphore(funcs.vkDestroySemaphore, d, sem, nil)
+}
+
+func DestroyDevice(dev Device) {
+ C.vkDestroyDevice(funcs.vkDestroyDevice, dev, nil)
+}
+
+func DestroySurface(inst Instance, s Surface) {
+ C.vkDestroySurfaceKHR(funcs.vkDestroySurfaceKHR, inst, s, nil)
+}
+
+func CreateCommandPool(d Device, queueIndex int) (CommandPool, error) {
+ inf := C.VkCommandPoolCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ queueFamilyIndex: C.uint32_t(queueIndex),
+ flags: C.VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | C.VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+ }
+
+ var pool CommandPool
+ if err := vkErr(C.vkCreateCommandPool(funcs.vkCreateCommandPool, d, &inf, nil, &pool)); err != nil {
+ return nilCommandPool, fmt.Errorf("vulkan: vkCreateCommandPool: %w", err)
+ }
+ return pool, nil
+}
+
+func DestroyCommandPool(d Device, pool CommandPool) {
+ C.vkDestroyCommandPool(funcs.vkDestroyCommandPool, d, pool, nil)
+}
+
+func AllocateCommandBuffer(d Device, pool CommandPool) (CommandBuffer, error) {
+ inf := C.VkCommandBufferAllocateInfo{
+ sType: C.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ commandPool: pool,
+ level: C.VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ commandBufferCount: 1,
+ }
+
+ var buf CommandBuffer
+ if err := vkErr(C.vkAllocateCommandBuffers(funcs.vkAllocateCommandBuffers, d, &inf, &buf)); err != nil {
+ return nil, fmt.Errorf("vulkan: vkAllocateCommandBuffers: %w", err)
+ }
+ return buf, nil
+}
+
+func FreeCommandBuffers(d Device, pool CommandPool, bufs ...CommandBuffer) {
+ if len(bufs) == 0 {
+ return
+ }
+ C.vkFreeCommandBuffers(funcs.vkFreeCommandBuffers, d, pool, C.uint32_t(len(bufs)), &bufs[0])
+}
+
+func BeginCommandBuffer(buf CommandBuffer) error {
+ inf := C.VkCommandBufferBeginInfo{
+ sType: C.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ flags: C.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ }
+ if err := vkErr(C.vkBeginCommandBuffer(funcs.vkBeginCommandBuffer, buf, inf)); err != nil {
+ return fmt.Errorf("vulkan: vkBeginCommandBuffer: %w", err)
+ }
+ return nil
+}
+
+func EndCommandBuffer(buf CommandBuffer) error {
+ if err := vkErr(C.vkEndCommandBuffer(funcs.vkEndCommandBuffer, buf)); err != nil {
+ return fmt.Errorf("vulkan: vkEndCommandBuffer: %w", err)
+ }
+ return nil
+}
+
+func QueueSubmit(q Queue, buf CommandBuffer, waitSems []Semaphore, waitStages []PipelineStageFlags, sigSems []Semaphore, fence Fence) error {
+ inf := C.VkSubmitInfo{
+ sType: C.VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ commandBufferCount: 1,
+ pCommandBuffers: &buf,
+ }
+ if len(waitSems) > 0 {
+ if len(waitSems) != len(waitStages) {
+ panic("len(waitSems) != len(waitStages)")
+ }
+ inf.waitSemaphoreCount = C.uint32_t(len(waitSems))
+ inf.pWaitSemaphores = &waitSems[0]
+ inf.pWaitDstStageMask = &waitStages[0]
+ }
+ if len(sigSems) > 0 {
+ inf.signalSemaphoreCount = C.uint32_t(len(sigSems))
+ inf.pSignalSemaphores = &sigSems[0]
+ }
+ if err := vkErr(C.vkQueueSubmit(funcs.vkQueueSubmit, q, inf, fence)); err != nil {
+ return fmt.Errorf("vulkan: vkQueueSubmit: %w", err)
+ }
+ return nil
+}
+
+func CmdBeginRenderPass(buf CommandBuffer, rp RenderPass, fbo Framebuffer, width, height int, clearCol [4]float32) {
+ cclearCol := [4]C.float{C.float(clearCol[0]), C.float(clearCol[1]), C.float(clearCol[2]), C.float(clearCol[3])}
+ inf := C.VkRenderPassBeginInfo{
+ sType: C.VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ renderPass: rp,
+ framebuffer: fbo,
+ renderArea: C.VkRect2D{extent: C.VkExtent2D{width: C.uint32_t(width), height: C.uint32_t(height)}},
+ clearValueCount: 1,
+ pClearValues: (*C.VkClearValue)(unsafe.Pointer(&cclearCol)),
+ }
+ C.vkCmdBeginRenderPass(funcs.vkCmdBeginRenderPass, buf, inf, C.VK_SUBPASS_CONTENTS_INLINE)
+}
+
+func CmdEndRenderPass(buf CommandBuffer) {
+ C.vkCmdEndRenderPass(funcs.vkCmdEndRenderPass, buf)
+}
+
+func CmdCopyBuffer(cmdBuf CommandBuffer, src, dst Buffer, srcOff, dstOff, size int) {
+ C.vkCmdCopyBuffer(funcs.vkCmdCopyBuffer, cmdBuf, src, dst, 1, &C.VkBufferCopy{
+ srcOffset: C.VkDeviceSize(srcOff),
+ dstOffset: C.VkDeviceSize(dstOff),
+ size: C.VkDeviceSize(size),
+ })
+}
+
+func CmdCopyBufferToImage(cmdBuf CommandBuffer, src Buffer, dst Image, layout ImageLayout, copy BufferImageCopy) {
+ C.vkCmdCopyBufferToImage(funcs.vkCmdCopyBufferToImage, cmdBuf, src, dst, layout, 1, &copy)
+}
+
+func CmdPipelineBarrier(cmdBuf CommandBuffer, srcStage, dstStage PipelineStageFlags, flags DependencyFlags, memBarriers []MemoryBarrier, bufBarriers []BufferMemoryBarrier, imgBarriers []ImageMemoryBarrier) {
+ var memPtr *MemoryBarrier
+ if len(memBarriers) > 0 {
+ memPtr = &memBarriers[0]
+ }
+ var bufPtr *BufferMemoryBarrier
+ if len(bufBarriers) > 0 {
+ bufPtr = &bufBarriers[0]
+ }
+ var imgPtr *ImageMemoryBarrier
+ if len(imgBarriers) > 0 {
+ imgPtr = &imgBarriers[0]
+ }
+ C.vkCmdPipelineBarrier(funcs.vkCmdPipelineBarrier, cmdBuf, srcStage, dstStage, flags,
+ C.uint32_t(len(memBarriers)), memPtr,
+ C.uint32_t(len(bufBarriers)), bufPtr,
+ C.uint32_t(len(imgBarriers)), imgPtr)
+}
+
+func CmdPushConstants(cmdBuf CommandBuffer, layout PipelineLayout, stages ShaderStageFlags, offset int, data []byte) {
+ if len(data) == 0 {
+ return
+ }
+ C.vkCmdPushConstants(funcs.vkCmdPushConstants, cmdBuf, layout, stages, C.uint32_t(offset), C.uint32_t(len(data)), unsafe.Pointer(&data[0]))
+}
+
+func CmdBindPipeline(cmdBuf CommandBuffer, bindPoint PipelineBindPoint, pipe Pipeline) {
+ C.vkCmdBindPipeline(funcs.vkCmdBindPipeline, cmdBuf, bindPoint, pipe)
+}
+
+func CmdBindVertexBuffers(cmdBuf CommandBuffer, first int, buffers []Buffer, sizes []DeviceSize) {
+ if len(buffers) == 0 {
+ return
+ }
+ C.vkCmdBindVertexBuffers(funcs.vkCmdBindVertexBuffers, cmdBuf, C.uint32_t(first), C.uint32_t(len(buffers)), &buffers[0], &sizes[0])
+}
+
+func CmdSetViewport(cmdBuf CommandBuffer, first int, viewports ...Viewport) {
+ if len(viewports) == 0 {
+ return
+ }
+ C.vkCmdSetViewport(funcs.vkCmdSetViewport, cmdBuf, C.uint32_t(first), C.uint32_t(len(viewports)), &viewports[0])
+}
+
+func CmdBindIndexBuffer(cmdBuf CommandBuffer, buffer Buffer, offset int, typ IndexType) {
+ C.vkCmdBindIndexBuffer(funcs.vkCmdBindIndexBuffer, cmdBuf, buffer, C.VkDeviceSize(offset), typ)
+}
+
+func CmdDraw(cmdBuf CommandBuffer, vertCount, instCount, firstVert, firstInst int) {
+ C.vkCmdDraw(funcs.vkCmdDraw, cmdBuf, C.uint32_t(vertCount), C.uint32_t(instCount), C.uint32_t(firstVert), C.uint32_t(firstInst))
+}
+
+func CmdDrawIndexed(cmdBuf CommandBuffer, idxCount, instCount, firstIdx, vertOff, firstInst int) {
+ C.vkCmdDrawIndexed(funcs.vkCmdDrawIndexed, cmdBuf, C.uint32_t(idxCount), C.uint32_t(instCount), C.uint32_t(firstIdx), C.int32_t(vertOff), C.uint32_t(firstInst))
+}
+
+func GetPhysicalDeviceFormatProperties(physDev PhysicalDevice, format Format) FormatFeatureFlags {
+ var props C.VkFormatProperties
+ C.vkGetPhysicalDeviceFormatProperties(funcs.vkGetPhysicalDeviceFormatProperties, physDev, format, &props)
+ return FormatFeatureFlags(props.optimalTilingFeatures)
+}
+
+func CmdBindDescriptorSets(cmdBuf CommandBuffer, point PipelineBindPoint, layout PipelineLayout, firstSet int, sets []DescriptorSet) {
+ C.vkCmdBindDescriptorSets(funcs.vkCmdBindDescriptorSets, cmdBuf, point, layout, C.uint32_t(firstSet), C.uint32_t(len(sets)), &sets[0], 0, nil)
+}
+
+func CmdCopyImage(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Image, dstLayout ImageLayout, regions []ImageCopy) {
+ if len(regions) == 0 {
+ return
+ }
+ C.vkCmdCopyImage(funcs.vkCmdCopyImage, cmdBuf, src, srcLayout, dst, dstLayout, C.uint32_t(len(regions)), &regions[0])
+}
+
+func CmdCopyImageToBuffer(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Buffer, regions []BufferImageCopy) {
+ if len(regions) == 0 {
+ return
+ }
+ C.vkCmdCopyImageToBuffer(funcs.vkCmdCopyImageToBuffer, cmdBuf, src, srcLayout, dst, C.uint32_t(len(regions)), &regions[0])
+}
+
+func CmdDispatch(cmdBuf CommandBuffer, x, y, z int) {
+ C.vkCmdDispatch(funcs.vkCmdDispatch, cmdBuf, C.uint32_t(x), C.uint32_t(y), C.uint32_t(z))
+}
+
+func CreateImage(pd PhysicalDevice, d Device, format Format, width, height int, usage ImageUsageFlags) (Image, DeviceMemory, error) {
+ inf := C.VkImageCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ imageType: C.VK_IMAGE_TYPE_2D,
+ format: format,
+ extent: C.VkExtent3D{
+ width: C.uint32_t(width),
+ height: C.uint32_t(height),
+ depth: 1,
+ },
+ mipLevels: 1,
+ arrayLayers: 1,
+ samples: C.VK_SAMPLE_COUNT_1_BIT,
+ tiling: C.VK_IMAGE_TILING_OPTIMAL,
+ usage: usage,
+ initialLayout: C.VK_IMAGE_LAYOUT_UNDEFINED,
+ }
+ var img C.VkImage
+ if err := vkErr(C.vkCreateImage(funcs.vkCreateImage, d, &inf, nil, &img)); err != nil {
+ return nilImage, nilDeviceMemory, fmt.Errorf("vulkan: vkCreateImage: %w", err)
+ }
+ var memReqs C.VkMemoryRequirements
+ C.vkGetImageMemoryRequirements(funcs.vkGetImageMemoryRequirements, d, img, &memReqs)
+
+ memIdx, found := findMemoryTypeIndex(pd, memReqs.memoryTypeBits, C.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ if !found {
+ DestroyImage(d, img)
+ return nilImage, nilDeviceMemory, errors.New("vulkan: no memory type suitable for images found")
+ }
+
+ memInf := C.VkMemoryAllocateInfo{
+ sType: C.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ allocationSize: memReqs.size,
+ memoryTypeIndex: C.uint32_t(memIdx),
+ }
+ var imgMem C.VkDeviceMemory
+ if err := vkErr(C.vkAllocateMemory(funcs.vkAllocateMemory, d, &memInf, nil, &imgMem)); err != nil {
+ DestroyImage(d, img)
+ return nilImage, nilDeviceMemory, fmt.Errorf("vulkan: vkAllocateMemory: %w", err)
+ }
+
+ if err := vkErr(C.vkBindImageMemory(funcs.vkBindImageMemory, d, img, imgMem, 0)); err != nil {
+ FreeMemory(d, imgMem)
+ DestroyImage(d, img)
+ return nilImage, nilDeviceMemory, fmt.Errorf("vulkan: vkBindImageMemory: %w", err)
+ }
+ return img, imgMem, nil
+}
+
+func DestroyImage(d Device, img Image) {
+ C.vkDestroyImage(funcs.vkDestroyImage, d, img, nil)
+}
+
+func FreeMemory(d Device, mem DeviceMemory) {
+ C.vkFreeMemory(funcs.vkFreeMemory, d, mem, nil)
+}
+
+func CreateSampler(d Device, minFilter, magFilter Filter) (Sampler, error) {
+ inf := C.VkSamplerCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ minFilter: minFilter,
+ magFilter: magFilter,
+ addressModeU: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+ addressModeV: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+ }
+ var s C.VkSampler
+ if err := vkErr(C.vkCreateSampler(funcs.vkCreateSampler, d, &inf, nil, &s)); err != nil {
+ return nilSampler, fmt.Errorf("vulkan: vkCreateSampler: %w", err)
+ }
+ return s, nil
+}
+
+func DestroySampler(d Device, sampler Sampler) {
+ C.vkDestroySampler(funcs.vkDestroySampler, d, sampler, nil)
+}
+
+func CreateBuffer(pd PhysicalDevice, d Device, size int, usage BufferUsageFlags, props MemoryPropertyFlags) (Buffer, DeviceMemory, error) {
+ inf := C.VkBufferCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ size: C.VkDeviceSize(size),
+ usage: usage,
+ }
+ var buf C.VkBuffer
+ if err := vkErr(C.vkCreateBuffer(funcs.vkCreateBuffer, d, &inf, nil, &buf)); err != nil {
+ return nilBuffer, nilDeviceMemory, fmt.Errorf("vulkan: vkCreateBuffer: %w", err)
+ }
+
+ var memReqs C.VkMemoryRequirements
+ C.vkGetBufferMemoryRequirements(funcs.vkGetBufferMemoryRequirements, d, buf, &memReqs)
+
+ memIdx, found := findMemoryTypeIndex(pd, memReqs.memoryTypeBits, props)
+ if !found {
+ DestroyBuffer(d, buf)
+ return nilBuffer, nilDeviceMemory, errors.New("vulkan: no memory suitable for buffers found")
+ }
+ memInf := C.VkMemoryAllocateInfo{
+ sType: C.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ allocationSize: memReqs.size,
+ memoryTypeIndex: C.uint32_t(memIdx),
+ }
+
+ var mem C.VkDeviceMemory
+ if err := vkErr(C.vkAllocateMemory(funcs.vkAllocateMemory, d, &memInf, nil, &mem)); err != nil {
+ DestroyBuffer(d, buf)
+ return nilBuffer, nilDeviceMemory, fmt.Errorf("vulkan: vkAllocateMemory: %w", err)
+ }
+
+ if err := vkErr(C.vkBindBufferMemory(funcs.vkBindBufferMemory, d, buf, mem, 0)); err != nil {
+ FreeMemory(d, mem)
+ DestroyBuffer(d, buf)
+ return nilBuffer, nilDeviceMemory, fmt.Errorf("vulkan: vkBindBufferMemory: %w", err)
+ }
+ return buf, mem, nil
+}
+
+func DestroyBuffer(d Device, buf Buffer) {
+ C.vkDestroyBuffer(funcs.vkDestroyBuffer, d, buf, nil)
+}
+
+func CreateShaderModule(d Device, spirv string) (ShaderModule, error) {
+ ptr := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&spirv)).Data)
+ inf := C.VkShaderModuleCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+ codeSize: C.size_t(len(spirv)),
+ pCode: (*C.uint32_t)(ptr),
+ }
+
+ var mod C.VkShaderModule
+ if err := vkErr(C.vkCreateShaderModule(funcs.vkCreateShaderModule, d, inf, nil, &mod)); err != nil {
+ return nilShaderModule, fmt.Errorf("vulkan: vkCreateShaderModule: %w", err)
+ }
+ return mod, nil
+}
+
+func DestroyShaderModule(d Device, mod ShaderModule) {
+ C.vkDestroyShaderModule(funcs.vkDestroyShaderModule, d, mod, nil)
+}
+
+func CreateGraphicsPipeline(d Device, pass RenderPass, vmod, fmod ShaderModule, blend bool, srcFactor, dstFactor BlendFactor, topology PrimitiveTopology, bindings []VertexInputBindingDescription, attrs []VertexInputAttributeDescription, layout PipelineLayout) (Pipeline, error) {
+ main := C.CString("main")
+ defer C.free(unsafe.Pointer(main))
+ stages := []C.VkPipelineShaderStageCreateInfo{
+ {
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ stage: C.VK_SHADER_STAGE_VERTEX_BIT,
+ module: vmod,
+ pName: main,
+ },
+ {
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ stage: C.VK_SHADER_STAGE_FRAGMENT_BIT,
+ module: fmod,
+ pName: main,
+ },
+ }
+ dynStates := []C.VkDynamicState{C.VK_DYNAMIC_STATE_VIEWPORT}
+ dynInf := C.VkPipelineDynamicStateCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ dynamicStateCount: C.uint32_t(len(dynStates)),
+ pDynamicStates: &dynStates[0],
+ }
+ const maxDim = 0x7fffffff
+ scissors := []C.VkRect2D{{extent: C.VkExtent2D{width: maxDim, height: maxDim}}}
+ viewportInf := C.VkPipelineViewportStateCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ viewportCount: 1,
+ scissorCount: C.uint32_t(len(scissors)),
+ pScissors: &scissors[0],
+ }
+ enable := C.VkBool32(0)
+ if blend {
+ enable = 1
+ }
+ attBlendInf := C.VkPipelineColorBlendAttachmentState{
+ blendEnable: enable,
+ srcColorBlendFactor: srcFactor,
+ srcAlphaBlendFactor: srcFactor,
+ dstColorBlendFactor: dstFactor,
+ dstAlphaBlendFactor: dstFactor,
+ colorBlendOp: C.VK_BLEND_OP_ADD,
+ alphaBlendOp: C.VK_BLEND_OP_ADD,
+ colorWriteMask: C.VK_COLOR_COMPONENT_R_BIT | C.VK_COLOR_COMPONENT_G_BIT | C.VK_COLOR_COMPONENT_B_BIT | C.VK_COLOR_COMPONENT_A_BIT,
+ }
+ blendInf := C.VkPipelineColorBlendStateCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ attachmentCount: 1,
+ pAttachments: &attBlendInf,
+ }
+ var vkBinds []C.VkVertexInputBindingDescription
+ var vkAttrs []C.VkVertexInputAttributeDescription
+ for _, b := range bindings {
+ vkBinds = append(vkBinds, C.VkVertexInputBindingDescription{
+ binding: C.uint32_t(b.Binding),
+ stride: C.uint32_t(b.Stride),
+ })
+ }
+ for _, a := range attrs {
+ vkAttrs = append(vkAttrs, C.VkVertexInputAttributeDescription{
+ location: C.uint32_t(a.Location),
+ binding: C.uint32_t(a.Binding),
+ format: a.Format,
+ offset: C.uint32_t(a.Offset),
+ })
+ }
+ vertexInf := C.VkPipelineVertexInputStateCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ }
+ if n := len(vkBinds); n > 0 {
+ vertexInf.vertexBindingDescriptionCount = C.uint32_t(n)
+ vertexInf.pVertexBindingDescriptions = &vkBinds[0]
+ }
+ if n := len(vkAttrs); n > 0 {
+ vertexInf.vertexAttributeDescriptionCount = C.uint32_t(n)
+ vertexInf.pVertexAttributeDescriptions = &vkAttrs[0]
+ }
+ inf := C.VkGraphicsPipelineCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ stageCount: C.uint32_t(len(stages)),
+ pStages: &stages[0],
+ renderPass: pass,
+ layout: layout,
+ pRasterizationState: &C.VkPipelineRasterizationStateCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ lineWidth: 1.0,
+ },
+ pMultisampleState: &C.VkPipelineMultisampleStateCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ rasterizationSamples: C.VK_SAMPLE_COUNT_1_BIT,
+ },
+ pInputAssemblyState: &C.VkPipelineInputAssemblyStateCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ topology: topology,
+ },
+ }
+
+ var pipe C.VkPipeline
+ if err := vkErr(C.vkCreateGraphicsPipelines(funcs.vkCreateGraphicsPipelines, d, nilPipelineCache, inf, dynInf, blendInf, vertexInf, viewportInf, nil, &pipe)); err != nil {
+ return nilPipeline, fmt.Errorf("vulkan: vkCreateGraphicsPipelines: %w", err)
+ }
+ return pipe, nil
+}
+
+func DestroyPipeline(d Device, p Pipeline) {
+ C.vkDestroyPipeline(funcs.vkDestroyPipeline, d, p, nil)
+}
+
+func CreatePipelineLayout(d Device, pushRanges []PushConstantRange, sets []DescriptorSetLayout) (PipelineLayout, error) {
+ inf := C.VkPipelineLayoutCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ }
+ if n := len(sets); n > 0 {
+ inf.setLayoutCount = C.uint32_t(n)
+ inf.pSetLayouts = &sets[0]
+ }
+ if n := len(pushRanges); n > 0 {
+ inf.pushConstantRangeCount = C.uint32_t(n)
+ inf.pPushConstantRanges = &pushRanges[0]
+ }
+ var l C.VkPipelineLayout
+ if err := vkErr(C.vkCreatePipelineLayout(funcs.vkCreatePipelineLayout, d, inf, nil, &l)); err != nil {
+ return nilPipelineLayout, fmt.Errorf("vulkan: vkCreatePipelineLayout: %w", err)
+ }
+ return l, nil
+}
+
+func DestroyPipelineLayout(d Device, l PipelineLayout) {
+ C.vkDestroyPipelineLayout(funcs.vkDestroyPipelineLayout, d, l, nil)
+}
+
+func CreateDescriptorSetLayout(d Device, bindings []DescriptorSetLayoutBinding) (DescriptorSetLayout, error) {
+ var vkbinds []C.VkDescriptorSetLayoutBinding
+ for _, b := range bindings {
+ vkbinds = append(vkbinds, C.VkDescriptorSetLayoutBinding{
+ binding: C.uint32_t(b.Binding),
+ descriptorType: b.DescriptorType,
+ descriptorCount: 1,
+ stageFlags: b.StageFlags,
+ })
+ }
+ inf := C.VkDescriptorSetLayoutCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ }
+ if n := len(vkbinds); n > 0 {
+ inf.bindingCount = C.uint32_t(len(vkbinds))
+ inf.pBindings = &vkbinds[0]
+ }
+ var l C.VkDescriptorSetLayout
+ if err := vkErr(C.vkCreateDescriptorSetLayout(funcs.vkCreateDescriptorSetLayout, d, inf, nil, &l)); err != nil {
+ return nilDescriptorSetLayout, fmt.Errorf("vulkan: vkCreateDescriptorSetLayout: %w", err)
+ }
+ return l, nil
+}
+
+func DestroyDescriptorSetLayout(d Device, l DescriptorSetLayout) {
+ C.vkDestroyDescriptorSetLayout(funcs.vkDestroyDescriptorSetLayout, d, l, nil)
+}
+
+func MapMemory(d Device, mem DeviceMemory, offset, size int) ([]byte, error) {
+ var ptr unsafe.Pointer
+ if err := vkErr(C.vkMapMemory(funcs.vkMapMemory, d, mem, C.VkDeviceSize(offset), C.VkDeviceSize(size), 0, &ptr)); err != nil {
+ return nil, fmt.Errorf("vulkan: vkMapMemory: %w", err)
+ }
+ return ((*[1 << 30]byte)(ptr))[:size:size], nil
+}
+
+func UnmapMemory(d Device, mem DeviceMemory) {
+ C.vkUnmapMemory(funcs.vkUnmapMemory, d, mem)
+}
+
+func ResetCommandBuffer(buf CommandBuffer) error {
+ if err := vkErr(C.vkResetCommandBuffer(funcs.vkResetCommandBuffer, buf, 0)); err != nil {
+ return fmt.Errorf("vulkan: vkResetCommandBuffer. %w", err)
+ }
+ return nil
+}
+
+func CreateDescriptorPool(d Device, maxSets int, sizes []DescriptorPoolSize) (DescriptorPool, error) {
+ inf := C.VkDescriptorPoolCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ maxSets: C.uint32_t(maxSets),
+ poolSizeCount: C.uint32_t(len(sizes)),
+ pPoolSizes: &sizes[0],
+ }
+ var pool C.VkDescriptorPool
+ if err := vkErr(C.vkCreateDescriptorPool(funcs.vkCreateDescriptorPool, d, inf, nil, &pool)); err != nil {
+ return nilDescriptorPool, fmt.Errorf("vulkan: vkCreateDescriptorPool: %w", err)
+ }
+ return pool, nil
+}
+
+func DestroyDescriptorPool(d Device, pool DescriptorPool) {
+ C.vkDestroyDescriptorPool(funcs.vkDestroyDescriptorPool, d, pool, nil)
+}
+
+func ResetDescriptorPool(d Device, pool DescriptorPool) error {
+ if err := vkErr(C.vkResetDescriptorPool(funcs.vkResetDescriptorPool, d, pool, 0)); err != nil {
+ return fmt.Errorf("vulkan: vkResetDescriptorPool: %w", err)
+ }
+ return nil
+}
+
+func UpdateDescriptorSet(d Device, write WriteDescriptorSet) {
+ C.vkUpdateDescriptorSets(funcs.vkUpdateDescriptorSets, d, write, 0, nil)
+}
+
+func AllocateDescriptorSet(d Device, pool DescriptorPool, layout DescriptorSetLayout) (DescriptorSet, error) {
+ inf := C.VkDescriptorSetAllocateInfo{
+ sType: C.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ descriptorPool: pool,
+ descriptorSetCount: 1,
+ pSetLayouts: &layout,
+ }
+ var set C.VkDescriptorSet
+ if err := vkErr(C.vkAllocateDescriptorSets(funcs.vkAllocateDescriptorSets, d, inf, &set)); err != nil {
+ return nilDescriptorSet, fmt.Errorf("vulkan: vkAllocateDescriptorSets: %w", err)
+ }
+ return set, nil
+}
+
+func CreateComputePipeline(d Device, mod ShaderModule, layout PipelineLayout) (Pipeline, error) {
+ main := C.CString("main")
+ defer C.free(unsafe.Pointer(main))
+ inf := C.VkComputePipelineCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
+ stage: C.VkPipelineShaderStageCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ stage: C.VK_SHADER_STAGE_COMPUTE_BIT,
+ module: mod,
+ pName: main,
+ },
+ layout: layout,
+ }
+ var pipe C.VkPipeline
+ if err := vkErr(C.vkCreateComputePipelines(funcs.vkCreateComputePipelines, d, nilPipelineCache, 1, &inf, nil, &pipe)); err != nil {
+ return nilPipeline, fmt.Errorf("vulkan: vkCreateComputePipelines: %w", err)
+ }
+ return pipe, nil
+}
+
+func CreateFence(d Device) (Fence, error) {
+ inf := C.VkFenceCreateInfo{
+ sType: C.VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ }
+ var f C.VkFence
+ if err := vkErr(C.vkCreateFence(funcs.vkCreateFence, d, &inf, nil, &f)); err != nil {
+ return nilFence, fmt.Errorf("vulkan: vkCreateFence: %w", err)
+ }
+ return f, nil
+}
+
+func DestroyFence(d Device, f Fence) {
+ C.vkDestroyFence(funcs.vkDestroyFence, d, f, nil)
+}
+
+func WaitForFences(d Device, fences ...Fence) error {
+ if len(fences) == 0 {
+ return nil
+ }
+ err := vkErr(C.vkWaitForFences(funcs.vkWaitForFences, d, C.uint32_t(len(fences)), &fences[0], C.VK_TRUE, 0xffffffffffffffff))
+ if err != nil {
+ return fmt.Errorf("vulkan: vkWaitForFences: %w", err)
+ }
+ return nil
+}
+
+func ResetFences(d Device, fences ...Fence) error {
+ if len(fences) == 0 {
+ return nil
+ }
+ err := vkErr(C.vkResetFences(funcs.vkResetFences, d, C.uint32_t(len(fences)), &fences[0]))
+ if err != nil {
+ return fmt.Errorf("vulkan: vkResetFences: %w", err)
+ }
+ return nil
+}
+
+func BuildSubpassDependency(srcStage, dstStage PipelineStageFlags, srcMask, dstMask AccessFlags, flags DependencyFlags) SubpassDependency {
+ return C.VkSubpassDependency{
+ srcSubpass: C.VK_SUBPASS_EXTERNAL,
+ srcStageMask: srcStage,
+ srcAccessMask: srcMask,
+ dstSubpass: 0,
+ dstStageMask: dstStage,
+ dstAccessMask: dstMask,
+ dependencyFlags: flags,
+ }
+}
+
+func BuildPushConstantRange(stages ShaderStageFlags, offset, size int) PushConstantRange {
+ return C.VkPushConstantRange{
+ stageFlags: stages,
+ offset: C.uint32_t(offset),
+ size: C.uint32_t(size),
+ }
+}
+
+func BuildDescriptorPoolSize(typ DescriptorType, count int) DescriptorPoolSize {
+ return C.VkDescriptorPoolSize{
+ _type: typ,
+ descriptorCount: C.uint32_t(count),
+ }
+}
+
+func BuildWriteDescriptorSetImage(set DescriptorSet, binding int, typ DescriptorType, sampler Sampler, view ImageView, layout ImageLayout) WriteDescriptorSet {
+ return C.VkWriteDescriptorSet{
+ sType: C.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ dstSet: set,
+ dstBinding: C.uint32_t(binding),
+ descriptorCount: 1,
+ descriptorType: typ,
+ pImageInfo: &C.VkDescriptorImageInfo{
+ sampler: sampler,
+ imageView: view,
+ imageLayout: layout,
+ },
+ }
+}
+
+func BuildWriteDescriptorSetBuffer(set DescriptorSet, binding int, typ DescriptorType, buf Buffer) WriteDescriptorSet {
+ return C.VkWriteDescriptorSet{
+ sType: C.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ dstSet: set,
+ dstBinding: C.uint32_t(binding),
+ descriptorCount: 1,
+ descriptorType: typ,
+ pBufferInfo: &C.VkDescriptorBufferInfo{
+ buffer: buf,
+ _range: C.VK_WHOLE_SIZE,
+ },
+ }
+}
+
+func (r PushConstantRange) StageFlags() ShaderStageFlags {
+ return r.stageFlags
+}
+
+func (r PushConstantRange) Offset() int {
+ return int(r.offset)
+}
+
+func (r PushConstantRange) Size() int {
+ return int(r.size)
+}
+
+func (p QueueFamilyProperties) Flags() QueueFlags {
+ return p.queueFlags
+}
+
+func (c SurfaceCapabilities) MinExtent() image.Point {
+ return image.Pt(int(c.minImageExtent.width), int(c.minImageExtent.height))
+}
+
+func (c SurfaceCapabilities) MaxExtent() image.Point {
+ return image.Pt(int(c.maxImageExtent.width), int(c.maxImageExtent.height))
+}
+
+func BuildViewport(x, y, width, height float32) Viewport {
+ return C.VkViewport{
+ x: C.float(x),
+ y: C.float(y),
+ width: C.float(width),
+ height: C.float(height),
+ maxDepth: 1.0,
+ }
+}
+
+func BuildImageMemoryBarrier(img Image, srcMask, dstMask AccessFlags, oldLayout, newLayout ImageLayout) ImageMemoryBarrier {
+ return C.VkImageMemoryBarrier{
+ sType: C.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ srcAccessMask: srcMask,
+ dstAccessMask: dstMask,
+ oldLayout: oldLayout,
+ newLayout: newLayout,
+ image: img,
+ subresourceRange: C.VkImageSubresourceRange{
+ aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
+ levelCount: C.VK_REMAINING_MIP_LEVELS,
+ layerCount: C.VK_REMAINING_ARRAY_LAYERS,
+ },
+ }
+}
+
+func BuildBufferMemoryBarrier(buf Buffer, srcMask, dstMask AccessFlags) BufferMemoryBarrier {
+ return C.VkBufferMemoryBarrier{
+ sType: C.VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ srcAccessMask: srcMask,
+ dstAccessMask: dstMask,
+ buffer: buf,
+ size: C.VK_WHOLE_SIZE,
+ }
+}
+
+func BuildMemoryBarrier(srcMask, dstMask AccessFlags) MemoryBarrier {
+ return C.VkMemoryBarrier{
+ sType: C.VK_STRUCTURE_TYPE_MEMORY_BARRIER,
+ srcAccessMask: srcMask,
+ dstAccessMask: dstMask,
+ }
+}
+
+func BuildBufferImageCopy(bufOff, bufStride, x, y, width, height int) BufferImageCopy {
+ return C.VkBufferImageCopy{
+ bufferOffset: C.VkDeviceSize(bufOff),
+ bufferRowLength: C.uint32_t(bufStride),
+ imageSubresource: C.VkImageSubresourceLayers{
+ aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
+ layerCount: 1,
+ },
+ imageOffset: C.VkOffset3D{
+ x: C.int32_t(x), y: C.int32_t(y), z: 0,
+ },
+ imageExtent: C.VkExtent3D{
+ width: C.uint32_t(width), height: C.uint32_t(height), depth: 1,
+ },
+ }
+}
+
+func BuildImageCopy(srcX, srcY, dstX, dstY, width, height int) ImageCopy {
+ return C.VkImageCopy{
+ srcSubresource: C.VkImageSubresourceLayers{
+ aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
+ layerCount: 1,
+ },
+ srcOffset: C.VkOffset3D{
+ x: C.int32_t(srcX),
+ y: C.int32_t(srcY),
+ },
+ dstSubresource: C.VkImageSubresourceLayers{
+ aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
+ layerCount: 1,
+ },
+ dstOffset: C.VkOffset3D{
+ x: C.int32_t(dstX),
+ y: C.int32_t(dstY),
+ },
+ extent: C.VkExtent3D{
+ width: C.uint32_t(width),
+ height: C.uint32_t(height),
+ depth: 1,
+ },
+ }
+}
+
+func findMemoryTypeIndex(pd C.VkPhysicalDevice, constraints C.uint32_t, wantProps C.VkMemoryPropertyFlags) (int, bool) {
+ var memProps C.VkPhysicalDeviceMemoryProperties
+ C.vkGetPhysicalDeviceMemoryProperties(funcs.vkGetPhysicalDeviceMemoryProperties, pd, &memProps)
+
+ for i := 0; i < int(memProps.memoryTypeCount); i++ {
+ if (constraints & (1 << i)) == 0 {
+ continue
+ }
+ if (memProps.memoryTypes[i].propertyFlags & wantProps) != wantProps {
+ continue
+ }
+ return i, true
+ }
+
+ return 0, false
+}
+
+func choosePresentMode(pd C.VkPhysicalDevice, surf Surface) (C.VkPresentModeKHR, bool, error) {
+ var count C.uint32_t
+ err := vkErr(C.vkGetPhysicalDeviceSurfacePresentModesKHR(funcs.vkGetPhysicalDeviceSurfacePresentModesKHR, pd, surf, &count, nil))
+ if err != nil {
+ return 0, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfacePresentModesKHR: %w", err)
+ }
+ if count == 0 {
+ return 0, false, nil
+ }
+ modes := make([]C.VkPresentModeKHR, count)
+ err = vkErr(C.vkGetPhysicalDeviceSurfacePresentModesKHR(funcs.vkGetPhysicalDeviceSurfacePresentModesKHR, pd, surf, &count, &modes[0]))
+ if err != nil {
+ return 0, false, fmt.Errorf("vulkan: kGetPhysicalDeviceSurfacePresentModesKHR: %w", err)
+ }
+ for _, m := range modes {
+ if m == C.VK_PRESENT_MODE_MAILBOX_KHR || m == C.VK_PRESENT_MODE_FIFO_KHR {
+ return m, true, nil
+ }
+ }
+ return 0, false, nil
+}
+
+func chooseFormat(pd C.VkPhysicalDevice, surf Surface) (C.VkSurfaceFormatKHR, bool, error) {
+ var count C.uint32_t
+ err := vkErr(C.vkGetPhysicalDeviceSurfaceFormatsKHR(funcs.vkGetPhysicalDeviceSurfaceFormatsKHR, pd, surf, &count, nil))
+ if err != nil {
+ return C.VkSurfaceFormatKHR{}, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceFormatsKHR: %w", err)
+ }
+ if count == 0 {
+ return C.VkSurfaceFormatKHR{}, false, nil
+ }
+ formats := make([]C.VkSurfaceFormatKHR, count)
+ err = vkErr(C.vkGetPhysicalDeviceSurfaceFormatsKHR(funcs.vkGetPhysicalDeviceSurfaceFormatsKHR, pd, surf, &count, &formats[0]))
+ if err != nil {
+ return C.VkSurfaceFormatKHR{}, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceFormatsKHR: %w", err)
+ }
+ // Query for format with sRGB support.
+ // TODO: Support devices without sRGB.
+ for _, f := range formats {
+ if f.colorSpace != C.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR {
+ continue
+ }
+ switch f.format {
+ case C.VK_FORMAT_B8G8R8A8_SRGB, C.VK_FORMAT_R8G8B8A8_SRGB:
+ return f, true, nil
+ }
+ }
+ return C.VkSurfaceFormatKHR{}, false, nil
+}
+
+func chooseQueue(pd C.VkPhysicalDevice, surf Surface, flags C.VkQueueFlags) (int, bool, error) {
+ queues := GetPhysicalDeviceQueueFamilyProperties(pd)
+ for i, q := range queues {
+ // Check for presentation and feature support.
+ if q.queueFlags&flags != flags {
+ continue
+ }
+ if surf != nilSurface {
+ // Check for presentation support. It is possible that a device has no
+ // queue with both rendering and presentation support, but not in reality.
+ // See https://github.com/KhronosGroup/Vulkan-Docs/issues/1234.
+ var support C.VkBool32
+ if err := vkErr(C.vkGetPhysicalDeviceSurfaceSupportKHR(funcs.vkGetPhysicalDeviceSurfaceSupportKHR, pd, C.uint32_t(i), surf, &support)); err != nil {
+ return 0, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceSupportKHR: %w", err)
+ }
+ if support != C.VK_TRUE {
+ continue
+ }
+ }
+ return i, true, nil
+ }
+ return 0, false, nil
+}
+
+func dlsym(handle unsafe.Pointer, s string) unsafe.Pointer {
+ cs := C.CString(s)
+ defer C.free(unsafe.Pointer(cs))
+ return C.dlsym(handle, cs)
+}
+
+func dlopen(lib string) unsafe.Pointer {
+ clib := C.CString(lib)
+ defer C.free(unsafe.Pointer(clib))
+ return C.dlopen(clib, C.RTLD_NOW|C.RTLD_LOCAL)
+}
+
+func vkErr(res C.VkResult) error {
+ switch res {
+ case C.VK_SUCCESS:
+ return nil
+ default:
+ return Error(res)
+ }
+}
+
+func (e Error) Error() string {
+ return fmt.Sprintf("error %d", e)
+}
diff --git a/vendor/gioui.org/internal/vk/vulkan_android.go b/vendor/gioui.org/internal/vk/vulkan_android.go
new file mode 100644
index 0000000..143146e
--- /dev/null
+++ b/vendor/gioui.org/internal/vk/vulkan_android.go
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build !nowayland
+// +build !nowayland
+
+package vk
+
+/*
+#define VK_USE_PLATFORM_ANDROID_KHR
+#define VK_NO_PROTOTYPES 1
+#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
+#include <android/native_window.h>
+#include <vulkan/vulkan.h>
+
+static VkResult vkCreateAndroidSurfaceKHR(PFN_vkCreateAndroidSurfaceKHR f, VkInstance instance, const VkAndroidSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
+ return f(instance, pCreateInfo, pAllocator, pSurface);
+}
+*/
+import "C"
+import (
+ "fmt"
+ "unsafe"
+)
+
+var wlFuncs struct {
+ vkCreateAndroidSurfaceKHR C.PFN_vkCreateAndroidSurfaceKHR
+}
+
+func init() {
+ loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) {
+ wlFuncs.vkCreateAndroidSurfaceKHR = dlopen("vkCreateAndroidSurfaceKHR")
+ })
+}
+
+func CreateAndroidSurface(inst Instance, window unsafe.Pointer) (Surface, error) {
+ inf := C.VkAndroidSurfaceCreateInfoKHR{
+ sType: C.VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
+ window: (*C.ANativeWindow)(window),
+ }
+ var surf Surface
+ if err := vkErr(C.vkCreateAndroidSurfaceKHR(wlFuncs.vkCreateAndroidSurfaceKHR, inst, &inf, nil, &surf)); err != nil {
+ return 0, fmt.Errorf("vulkan: vkCreateAndroidSurfaceKHR: %w", err)
+ }
+ return surf, nil
+}
diff --git a/vendor/gioui.org/internal/vk/vulkan_wayland.go b/vendor/gioui.org/internal/vk/vulkan_wayland.go
new file mode 100644
index 0000000..cb057bc
--- /dev/null
+++ b/vendor/gioui.org/internal/vk/vulkan_wayland.go
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build ((linux && !android) || freebsd) && !nowayland
+// +build linux,!android freebsd
+// +build !nowayland
+
+package vk
+
+/*
+#define VK_USE_PLATFORM_WAYLAND_KHR
+#define VK_NO_PROTOTYPES 1
+#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
+#include <vulkan/vulkan.h>
+
+static VkResult vkCreateWaylandSurfaceKHR(PFN_vkCreateWaylandSurfaceKHR f, VkInstance instance, const VkWaylandSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
+ return f(instance, pCreateInfo, pAllocator, pSurface);
+}
+*/
+import "C"
+import (
+ "fmt"
+ "unsafe"
+)
+
+var wlFuncs struct {
+ vkCreateWaylandSurfaceKHR C.PFN_vkCreateWaylandSurfaceKHR
+}
+
+func init() {
+ loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) {
+ wlFuncs.vkCreateWaylandSurfaceKHR = dlopen("vkCreateWaylandSurfaceKHR")
+ })
+}
+
+func CreateWaylandSurface(inst Instance, disp unsafe.Pointer, wlSurf unsafe.Pointer) (Surface, error) {
+ inf := C.VkWaylandSurfaceCreateInfoKHR{
+ sType: C.VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
+ display: (*C.struct_wl_display)(disp),
+ surface: (*C.struct_wl_surface)(wlSurf),
+ }
+ var surf Surface
+ if err := vkErr(C.vkCreateWaylandSurfaceKHR(wlFuncs.vkCreateWaylandSurfaceKHR, inst, &inf, nil, &surf)); err != nil {
+ return 0, fmt.Errorf("vulkan: vkCreateWaylandSurfaceKHR: %w", err)
+ }
+ return surf, nil
+}
diff --git a/vendor/gioui.org/internal/vk/vulkan_x11.go b/vendor/gioui.org/internal/vk/vulkan_x11.go
new file mode 100644
index 0000000..780a5d5
--- /dev/null
+++ b/vendor/gioui.org/internal/vk/vulkan_x11.go
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build ((linux && !android) || freebsd) && !nox11
+// +build linux,!android freebsd
+// +build !nox11
+
+package vk
+
+/*
+#define VK_USE_PLATFORM_XLIB_KHR
+#define VK_NO_PROTOTYPES 1
+#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
+#include <vulkan/vulkan.h>
+
+static VkResult vkCreateXlibSurfaceKHR(PFN_vkCreateXlibSurfaceKHR f, VkInstance instance, const VkXlibSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
+ return f(instance, pCreateInfo, pAllocator, pSurface);
+}
+*/
+import "C"
+import (
+ "fmt"
+ "unsafe"
+)
+
+var x11Funcs struct {
+ vkCreateXlibSurfaceKHR C.PFN_vkCreateXlibSurfaceKHR
+}
+
+func init() {
+ loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) {
+ x11Funcs.vkCreateXlibSurfaceKHR = dlopen("vkCreateXlibSurfaceKHR")
+ })
+}
+
+func CreateXlibSurface(inst Instance, dpy unsafe.Pointer, window uintptr) (Surface, error) {
+ inf := C.VkXlibSurfaceCreateInfoKHR{
+ sType: C.VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
+ dpy: (*C.Display)(dpy),
+ window: (C.Window)(window),
+ }
+ var surf Surface
+ if err := vkErr(C.vkCreateXlibSurfaceKHR(x11Funcs.vkCreateXlibSurfaceKHR, inst, &inf, nil, &surf)); err != nil {
+ return 0, fmt.Errorf("vulkan: vkCreateXlibSurfaceKHR: %w", err)
+ }
+ return surf, nil
+}
diff --git a/vendor/gioui.org/io/clipboard/clipboard.go b/vendor/gioui.org/io/clipboard/clipboard.go
new file mode 100644
index 0000000..ae4a435
--- /dev/null
+++ b/vendor/gioui.org/io/clipboard/clipboard.go
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package clipboard
+
+import (
+ "gioui.org/internal/ops"
+ "gioui.org/io/event"
+ "gioui.org/op"
+)
+
+// Event is generated when the clipboard content is requested.
+type Event struct {
+ Text string
+}
+
+// ReadOp requests the text of the clipboard, delivered to
+// the current handler through an Event.
+type ReadOp struct {
+ Tag event.Tag
+}
+
+// WriteOp copies Text to the clipboard.
+type WriteOp struct {
+ Text string
+}
+
+func (h ReadOp) Add(o *op.Ops) {
+ data := ops.Write1(&o.Internal, ops.TypeClipboardReadLen, h.Tag)
+ data[0] = byte(ops.TypeClipboardRead)
+}
+
+func (h WriteOp) Add(o *op.Ops) {
+ data := ops.Write1(&o.Internal, ops.TypeClipboardWriteLen, &h.Text)
+ data[0] = byte(ops.TypeClipboardWrite)
+}
+
+func (Event) ImplementsEvent() {}
diff --git a/vendor/gioui.org/io/key/key.go b/vendor/gioui.org/io/key/key.go
index 5729ae4..b6af2a4 100644
--- a/vendor/gioui.org/io/key/key.go
+++ b/vendor/gioui.org/io/key/key.go
@@ -10,25 +10,35 @@ events.
package key
import (
+ "fmt"
"strings"
- "gioui.org/internal/opconst"
+ "gioui.org/internal/ops"
"gioui.org/io/event"
"gioui.org/op"
)
// InputOp declares a handler ready for key events.
// Key events are in general only delivered to the
-// focused key handler. Set the Focus flag to request
-// the focus.
+// focused key handler.
type InputOp struct {
- Tag event.Tag
- Focus bool
+ Tag event.Tag
+ Hint InputHint
}
-// HideInputOp request that any on screen text input
-// be hidden.
-type HideInputOp struct{}
+// SoftKeyboardOp shows or hide the on-screen keyboard, if available.
+// It replaces any previous SoftKeyboardOp.
+type SoftKeyboardOp struct {
+ Show bool
+}
+
+// FocusOp sets or clears the keyboard focus. It replaces any previous
+// FocusOp in the same frame.
+type FocusOp struct {
+ // Tag is the new focus. The focus is cleared if Tag is nil, or if Tag
+ // has no InputOp in the same frame.
+ Tag event.Tag
+}
// A FocusEvent is generated when a handler gains or loses
// focus.
@@ -46,6 +56,8 @@ type Event struct {
Name string
// Modifiers is the set of active modifiers when the key was pressed.
Modifiers Modifiers
+ // State is the state of the key when the event was fired.
+ State State
}
// An EditEvent is generated when text is input.
@@ -53,6 +65,38 @@ type EditEvent struct {
Text string
}
+// InputHint changes the on-screen-keyboard type. That hints the
+// type of data that might be entered by the user.
+type InputHint uint8
+
+const (
+ // HintAny hints that any input is expected.
+ HintAny InputHint = iota
+ // HintText hints that text input is expected. It may activate auto-correction and suggestions.
+ HintText
+ // HintNumeric hints that numeric input is expected. It may activate shortcuts for 0-9, "." and ",".
+ HintNumeric
+ // HintEmail hints that email input is expected. It may activate shortcuts for common email characters, such as "@" and ".com".
+ HintEmail
+ // HintURL hints that URL input is expected. It may activate shortcuts for common URL fragments such as "/" and ".com".
+ HintURL
+ // HintTelephone hints that telephone number input is expected. It may activate shortcuts for 0-9, "#" and "*".
+ HintTelephone
+)
+
+// State is the state of a key during an event.
+type State uint8
+
+const (
+ // Press is the state of a pressed key.
+ Press State = iota
+ // Release is the state of a key that has been released.
+ //
+ // Note: release events are only implemented on the following platforms:
+ // macOS, Linux, Windows, WebAssembly.
+ Release
+)
+
// Modifiers
type Modifiers uint32
@@ -88,6 +132,7 @@ const (
NamePageUp = "⇞"
NamePageDown = "⇟"
NameTab = "⇥"
+ NameSpace = "Space"
)
// Contain reports whether m contains all modifiers
@@ -97,16 +142,25 @@ func (m Modifiers) Contain(m2 Modifiers) bool {
}
func (h InputOp) Add(o *op.Ops) {
- data := o.Write(opconst.TypeKeyInputLen, h.Tag)
- data[0] = byte(opconst.TypeKeyInput)
- if h.Focus {
+ if h.Tag == nil {
+ panic("Tag must be non-nil")
+ }
+ data := ops.Write1(&o.Internal, ops.TypeKeyInputLen, h.Tag)
+ data[0] = byte(ops.TypeKeyInput)
+ data[1] = byte(h.Hint)
+}
+
+func (h SoftKeyboardOp) Add(o *op.Ops) {
+ data := ops.Write(&o.Internal, ops.TypeKeySoftKeyboardLen)
+ data[0] = byte(ops.TypeKeySoftKeyboard)
+ if h.Show {
data[1] = 1
}
}
-func (h HideInputOp) Add(o *op.Ops) {
- data := o.Write(opconst.TypeHideInputLen)
- data[0] = byte(opconst.TypeHideInput)
+func (h FocusOp) Add(o *op.Ops) {
+ data := ops.Write1(&o.Internal, ops.TypeKeyFocusLen, h.Tag)
+ data[0] = byte(ops.TypeKeyFocus)
}
func (EditEvent) ImplementsEvent() {}
@@ -114,25 +168,36 @@ func (Event) ImplementsEvent() {}
func (FocusEvent) ImplementsEvent() {}
func (e Event) String() string {
- return "{" + string(e.Name) + " " + e.Modifiers.String() + "}"
+ return fmt.Sprintf("%v %v %v}", e.Name, e.Modifiers, e.State)
}
func (m Modifiers) String() string {
var strs []string
if m.Contain(ModCtrl) {
- strs = append(strs, "ModCtrl")
+ strs = append(strs, "Ctrl")
}
if m.Contain(ModCommand) {
- strs = append(strs, "ModCommand")
+ strs = append(strs, "Command")
}
if m.Contain(ModShift) {
- strs = append(strs, "ModShift")
+ strs = append(strs, "Shift")
}
if m.Contain(ModAlt) {
- strs = append(strs, "ModAlt")
+ strs = append(strs, "Alt")
}
if m.Contain(ModSuper) {
- strs = append(strs, "ModSuper")
+ strs = append(strs, "Super")
}
return strings.Join(strs, "|")
}
+
+func (s State) String() string {
+ switch s {
+ case Press:
+ return "Press"
+ case Release:
+ return "Release"
+ default:
+ panic("invalid State")
+ }
+}
diff --git a/vendor/gioui.org/io/key/mod.go b/vendor/gioui.org/io/key/mod.go
index c5db56c..4b23d32 100644
--- a/vendor/gioui.org/io/key/mod.go
+++ b/vendor/gioui.org/io/key/mod.go
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT
+//go:build !darwin
// +build !darwin
package key
diff --git a/vendor/gioui.org/io/pointer/doc.go b/vendor/gioui.org/io/pointer/doc.go
index b99d97d..2f521c2 100644
--- a/vendor/gioui.org/io/pointer/doc.go
+++ b/vendor/gioui.org/io/pointer/doc.go
@@ -25,63 +25,67 @@ Leave, or Scroll):
Cancel events are always delivered.
-Areas
+Hit areas
-The area operations are used for specifying the area where
-subsequent InputOp are active.
+Clip operations from package op/clip are used for specifying
+hit areas where subsequent InputOps are active.
-For example, to set up a rectangular hit area:
+For example, to set up a handler with a rectangular hit area:
r := image.Rectangle{...}
- pointer.Rect(r).Add(ops)
+ area := clip.Rect(r).Push(ops)
pointer.InputOp{Tag: h}.Add(ops)
+ area.Pop()
-Note that areas compound: the effective area of multiple area
-operations is the intersection of the areas.
+Note that hit areas behave similar to painting: the effective area of a stack
+of multiple area operations is the intersection of the areas.
+
+BUG: Clip operations other than clip.Rect and clip.Ellipse are approximated
+with their bounding boxes.
Matching events
-StackOp operations and input handlers form an implicit tree.
-Each stack operation is a node, and each input handler is associated
-with the most recent node.
+Areas form an implicit tree, with input handlers as leaves. The children of
+an area is every area and handler added between its Push and corresponding Pop.
For example:
ops := new(op.Ops)
- var stack op.StackOp
var h1, h2 *Handler
- stack := op.Push(ops)
+ area := clip.Rect(...).Push(ops)
pointer.InputOp{Tag: h1}.Add(Ops)
- stack.Pop()
+ area.Pop()
- stack = op.Push(ops)
+ area := clip.Rect(...).Push(ops)
pointer.InputOp{Tag: h2}.Add(ops)
- stack.Pop()
+ area.Pop()
+
+implies a tree of two inner nodes, each with one pointer handler attached.
-implies a tree of two inner nodes, each with one pointer handler.
+The matching proceeds as follows.
-When determining which handlers match an Event, only handlers whose
-areas contain the event position are considered. The matching
-proceeds as follows.
+First, the foremost area that contains the event is found. Only areas whose
+parent areas all contain the event is considered.
-First, the foremost matching handler is included. If the handler
-has pass-through enabled, this step is repeated.
+Then, every handler attached to the area is matched with the event.
-Then, all matching handlers from the current node and all parent
-nodes are included.
+If all attached handlers are marked pass-through or if no handlers are
+attached, the matching repeats with the next foremost (sibling) area. Otherwise
+the matching repeats with the parent area.
-In the example above, all events will go to h2 only even though both
-handlers have the same area (the entire screen).
+In the example above, all events will go to h2 because it and h1 are siblings
+and none are pass-through.
Pass-through
-The PassOp operations controls the pass-through setting. A handler's
-pass-through setting is recorded along with the InputOp.
+The PassOp operations controls the pass-through setting. All handlers added
+inside one or more PassOp scopes are marked pass-through.
-Pass-through handlers are useful for overlay widgets such as a hidden
-side drawer. When the user touches the side, both the (transparent)
-drawer handle and the interface below should receive pointer events.
+Pass-through is useful for overlay widgets. Consider a hidden side drawer: when
+the user touches the side, both the (transparent) drawer handle and the
+interface below should receive pointer events. This effect is achieved by
+marking the drawer handle pass-through.
Disambiguation
diff --git a/vendor/gioui.org/io/pointer/pointer.go b/vendor/gioui.org/io/pointer/pointer.go
index 3344d6b..97987f7 100644
--- a/vendor/gioui.org/io/pointer/pointer.go
+++ b/vendor/gioui.org/io/pointer/pointer.go
@@ -4,12 +4,13 @@ package pointer
import (
"encoding/binary"
+ "fmt"
"image"
"strings"
"time"
"gioui.org/f32"
- "gioui.org/internal/opconst"
+ "gioui.org/internal/ops"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/op"
@@ -41,12 +42,21 @@ type Event struct {
Modifiers key.Modifiers
}
-// AreaOp updates the hit area to the intersection of the current
-// hit area and the area. The area is transformed before applying
-// it.
-type AreaOp struct {
- kind areaKind
- rect image.Rectangle
+// PassOp sets the pass-through mode. InputOps added while the pass-through
+// mode is set don't block events to siblings.
+type PassOp struct {
+}
+
+// PassStack represents a PassOp on the pass stack.
+type PassStack struct {
+ ops *ops.Ops
+ id ops.StackID
+ macroID int
+}
+
+// CursorNameOp sets the cursor for the current area.
+type CursorNameOp struct {
+ Name CursorName
}
// InputOp declares an input handler ready for pointer
@@ -58,17 +68,18 @@ type InputOp struct {
Grab bool
// Types is a bitwise-or of event types to receive.
Types Type
-}
-
-// PassOp sets the pass-through mode.
-type PassOp struct {
- Pass bool
+ // ScrollBounds describe the maximum scrollable distances in both
+ // axes. Specifically, any Event e delivered to Tag will satisfy
+ //
+ // ScrollBounds.Min.X <= e.Scroll.X <= ScrollBounds.Max.X (horizontal axis)
+ // ScrollBounds.Min.Y <= e.Scroll.Y <= ScrollBounds.Max.Y (vertical axis)
+ ScrollBounds image.Rectangle
}
type ID uint16
// Type of an Event.
-type Type uint8
+type Type uint
// Priority of an Event.
type Priority uint8
@@ -79,8 +90,27 @@ type Source uint8
// Buttons is a set of mouse buttons
type Buttons uint8
-// Must match app/internal/input.areaKind
-type areaKind uint8
+// CursorName is the name of a cursor.
+type CursorName string
+
+const (
+ // CursorDefault is the default cursor.
+ CursorDefault CursorName = ""
+ // CursorText is the cursor for text.
+ CursorText CursorName = "text"
+ // CursorPointer is the cursor for a link.
+ CursorPointer CursorName = "pointer"
+ // CursorCrossHair is the cursor for precise location.
+ CursorCrossHair CursorName = "crosshair"
+ // CursorColResize is the cursor for vertical resize.
+ CursorColResize CursorName = "col-resize"
+ // CursorRowResize is the cursor for horizontal resize.
+ CursorRowResize CursorName = "row-resize"
+ // CursorGrab is the cursor for moving object in any direction.
+ CursorGrab CursorName = "grab"
+ // CursorNone hides the cursor. To show it again, use any other cursor.
+ CursorNone CursorName = "none"
+)
const (
// A Cancel event is generated when the current gesture is
@@ -121,61 +151,90 @@ const (
)
const (
- ButtonLeft Buttons = 1 << iota
- ButtonRight
- ButtonMiddle
-)
-
-const (
- areaRect areaKind = iota
- areaEllipse
+ // ButtonPrimary is the primary button, usually the left button for a
+ // right-handed user.
+ ButtonPrimary Buttons = 1 << iota
+ // ButtonSecondary is the secondary button, usually the right button for a
+ // right-handed user.
+ ButtonSecondary
+ // ButtonTertiary is the tertiary button, usually the middle button.
+ ButtonTertiary
)
-// Rect constructs a rectangular hit area.
-func Rect(size image.Rectangle) AreaOp {
- return AreaOp{
- kind: areaRect,
- rect: size,
+// frect converts a rectangle to a f32.Rectangle.
+func frect(r image.Rectangle) f32.Rectangle {
+ return f32.Rectangle{
+ Min: fpt(r.Min), Max: fpt(r.Max),
}
}
-// Ellipse constructs an ellipsoid hit area.
-func Ellipse(size image.Rectangle) AreaOp {
- return AreaOp{
- kind: areaEllipse,
- rect: size,
+// fpt converts an point to a f32.Point.
+func fpt(p image.Point) f32.Point {
+ return f32.Point{
+ X: float32(p.X), Y: float32(p.Y),
}
}
-func (op AreaOp) Add(o *op.Ops) {
- data := o.Write(opconst.TypeAreaLen)
- data[0] = byte(opconst.TypeArea)
- data[1] = byte(op.kind)
- bo := binary.LittleEndian
- bo.PutUint32(data[2:], uint32(op.rect.Min.X))
- bo.PutUint32(data[6:], uint32(op.rect.Min.Y))
- bo.PutUint32(data[10:], uint32(op.rect.Max.X))
- bo.PutUint32(data[14:], uint32(op.rect.Max.Y))
+// Push the current pass mode to the pass stack and set the pass mode.
+func (p PassOp) Push(o *op.Ops) PassStack {
+ id, mid := ops.PushOp(&o.Internal, ops.PassStack)
+ data := ops.Write(&o.Internal, ops.TypePassLen)
+ data[0] = byte(ops.TypePass)
+ return PassStack{ops: &o.Internal, id: id, macroID: mid}
}
-func (h InputOp) Add(o *op.Ops) {
- data := o.Write(opconst.TypePointerInputLen, h.Tag)
- data[0] = byte(opconst.TypePointerInput)
- if h.Grab {
- data[1] = 1
- }
- data[2] = byte(h.Types)
+func (p PassStack) Pop() {
+ ops.PopOp(p.ops, ops.PassStack, p.id, p.macroID)
+ data := ops.Write(p.ops, ops.TypePopPassLen)
+ data[0] = byte(ops.TypePopPass)
+}
+
+func (op CursorNameOp) Add(o *op.Ops) {
+ data := ops.Write1(&o.Internal, ops.TypeCursorLen, op.Name)
+ data[0] = byte(ops.TypeCursor)
}
-func (op PassOp) Add(o *op.Ops) {
- data := o.Write(opconst.TypePassLen)
- data[0] = byte(opconst.TypePass)
- if op.Pass {
+// Add panics if the scroll range does not contain zero.
+func (op InputOp) Add(o *op.Ops) {
+ if op.Tag == nil {
+ panic("Tag must be non-nil")
+ }
+ if b := op.ScrollBounds; b.Min.X > 0 || b.Max.X < 0 || b.Min.Y > 0 || b.Max.Y < 0 {
+ panic(fmt.Errorf("invalid scroll range value %v", b))
+ }
+ if op.Types>>16 > 0 {
+ panic(fmt.Errorf("value in Types overflows uint16"))
+ }
+ data := ops.Write1(&o.Internal, ops.TypePointerInputLen, op.Tag)
+ data[0] = byte(ops.TypePointerInput)
+ if op.Grab {
data[1] = 1
}
+ bo := binary.LittleEndian
+ bo.PutUint16(data[2:], uint16(op.Types))
+ bo.PutUint32(data[4:], uint32(op.ScrollBounds.Min.X))
+ bo.PutUint32(data[8:], uint32(op.ScrollBounds.Min.Y))
+ bo.PutUint32(data[12:], uint32(op.ScrollBounds.Max.X))
+ bo.PutUint32(data[16:], uint32(op.ScrollBounds.Max.Y))
}
func (t Type) String() string {
+ if t == Cancel {
+ return "Cancel"
+ }
+ var buf strings.Builder
+ for tt := Type(1); tt > 0; tt <<= 1 {
+ if t&tt > 0 {
+ if buf.Len() > 0 {
+ buf.WriteByte('|')
+ }
+ buf.WriteString((t & tt).string())
+ }
+ }
+ return buf.String()
+}
+
+func (t Type) string() string {
switch t {
case Press:
return "Press"
@@ -230,16 +289,23 @@ func (b Buttons) Contain(buttons Buttons) bool {
func (b Buttons) String() string {
var strs []string
- if b.Contain(ButtonLeft) {
- strs = append(strs, "ButtonLeft")
+ if b.Contain(ButtonPrimary) {
+ strs = append(strs, "ButtonPrimary")
}
- if b.Contain(ButtonRight) {
- strs = append(strs, "ButtonRight")
+ if b.Contain(ButtonSecondary) {
+ strs = append(strs, "ButtonSecondary")
}
- if b.Contain(ButtonMiddle) {
- strs = append(strs, "ButtonMiddle")
+ if b.Contain(ButtonTertiary) {
+ strs = append(strs, "ButtonTertiary")
}
return strings.Join(strs, "|")
}
+func (c CursorName) String() string {
+ if c == CursorDefault {
+ return "default"
+ }
+ return string(c)
+}
+
func (Event) ImplementsEvent() {}
diff --git a/vendor/gioui.org/io/profile/profile.go b/vendor/gioui.org/io/profile/profile.go
index 24bf52b..b9a4476 100644
--- a/vendor/gioui.org/io/profile/profile.go
+++ b/vendor/gioui.org/io/profile/profile.go
@@ -5,7 +5,7 @@
package profile
import (
- "gioui.org/internal/opconst"
+ "gioui.org/internal/ops"
"gioui.org/io/event"
"gioui.org/op"
)
@@ -24,8 +24,8 @@ type Event struct {
}
func (p Op) Add(o *op.Ops) {
- data := o.Write(opconst.TypeProfileLen, p.Tag)
- data[0] = byte(opconst.TypeProfile)
+ data := ops.Write1(&o.Internal, ops.TypeProfileLen, p.Tag)
+ data[0] = byte(ops.TypeProfile)
}
func (p Event) ImplementsEvent() {}
diff --git a/vendor/gioui.org/io/router/clipboard.go b/vendor/gioui.org/io/router/clipboard.go
new file mode 100644
index 0000000..5f1623c
--- /dev/null
+++ b/vendor/gioui.org/io/router/clipboard.go
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package router
+
+import (
+ "gioui.org/io/event"
+)
+
+type clipboardQueue struct {
+ receivers map[event.Tag]struct{}
+ // request avoid read clipboard every frame while waiting.
+ requested bool
+ text *string
+}
+
+// WriteClipboard returns the most recent text to be copied
+// to the clipboard, if any.
+func (q *clipboardQueue) WriteClipboard() (string, bool) {
+ if q.text == nil {
+ return "", false
+ }
+ text := *q.text
+ q.text = nil
+ return text, true
+}
+
+// ReadClipboard reports if any new handler is waiting
+// to read the clipboard.
+func (q *clipboardQueue) ReadClipboard() bool {
+ if len(q.receivers) == 0 || q.requested {
+ return false
+ }
+ q.requested = true
+ return true
+}
+
+func (q *clipboardQueue) Push(e event.Event, events *handlerEvents) {
+ for r := range q.receivers {
+ events.Add(r, e)
+ delete(q.receivers, r)
+ }
+}
+
+func (q *clipboardQueue) ProcessWriteClipboard(refs []interface{}) {
+ q.text = refs[0].(*string)
+}
+
+func (q *clipboardQueue) ProcessReadClipboard(refs []interface{}) {
+ if q.receivers == nil {
+ q.receivers = make(map[event.Tag]struct{})
+ }
+ tag := refs[0].(event.Tag)
+ if _, ok := q.receivers[tag]; !ok {
+ q.receivers[tag] = struct{}{}
+ q.requested = false
+ }
+}
diff --git a/vendor/gioui.org/io/router/key.go b/vendor/gioui.org/io/router/key.go
index a64544b..9fef7dc 100644
--- a/vendor/gioui.org/io/router/key.go
+++ b/vendor/gioui.org/io/router/key.go
@@ -3,11 +3,8 @@
package router
import (
- "gioui.org/internal/opconst"
- "gioui.org/internal/ops"
"gioui.org/io/event"
"gioui.org/io/key"
- "gioui.org/op"
)
type TextInputState uint8
@@ -15,22 +12,25 @@ type TextInputState uint8
type keyQueue struct {
focus event.Tag
handlers map[event.Tag]*keyHandler
- reader ops.Reader
state TextInputState
+ hint key.InputHint
}
type keyHandler struct {
- active bool
+ // visible will be true if the InputOp is present
+ // in the current frame.
+ visible bool
+ new bool
+ hint key.InputHint
}
-type listenerPriority uint8
-
-const (
- priNone listenerPriority = iota
- priDefault
- priCurrentFocus
- priNewFocus
-)
+// keyCollector tracks state required to update a keyQueue
+// from key ops.
+type keyCollector struct {
+ q *keyQueue
+ focus event.Tag
+ changed bool
+}
const (
TextInputKeep TextInputState = iota
@@ -44,43 +44,60 @@ func (q *keyQueue) InputState() TextInputState {
return q.state
}
-func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) {
+// InputHint returns the input mode from the most recent key.InputOp.
+func (q *keyQueue) InputHint() (key.InputHint, bool) {
+ if q.focus == nil {
+ return q.hint, false
+ }
+ focused, ok := q.handlers[q.focus]
+ if !ok {
+ return q.hint, false
+ }
+ old := q.hint
+ q.hint = focused.hint
+ return q.hint, old != q.hint
+}
+
+func (q *keyQueue) Reset() {
if q.handlers == nil {
q.handlers = make(map[event.Tag]*keyHandler)
}
for _, h := range q.handlers {
- h.active = false
+ h.visible, h.new = false, false
}
- q.reader.Reset(root)
- focus, pri, hide := q.resolveFocus(events)
+ q.state = TextInputKeep
+}
+
+func (q *keyQueue) Frame(events *handlerEvents, collector keyCollector) {
for k, h := range q.handlers {
- if !h.active {
+ if !h.visible {
delete(q.handlers, k)
if q.focus == k {
+ // Remove the focus from the handler that is no longer visible.
q.focus = nil
- hide = true
+ q.state = TextInputClose
}
+ } else if h.new && k != collector.focus {
+ // Reset the handler on (each) first appearance, but don't trigger redraw.
+ events.AddNoRedraw(k, key.FocusEvent{Focus: false})
+ }
+ }
+ if collector.changed && collector.focus != nil {
+ if _, exists := q.handlers[collector.focus]; !exists {
+ collector.focus = nil
}
}
- if focus != q.focus {
+ if collector.changed && collector.focus != q.focus {
if q.focus != nil {
events.Add(q.focus, key.FocusEvent{Focus: false})
}
- q.focus = focus
+ q.focus = collector.focus
if q.focus != nil {
events.Add(q.focus, key.FocusEvent{Focus: true})
} else {
- hide = true
+ q.state = TextInputClose
}
}
- switch {
- case pri == priNewFocus:
- q.state = TextInputOpen
- case hide:
- q.state = TextInputClose
- default:
- q.state = TextInputKeep
- }
}
func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
@@ -89,62 +106,38 @@ func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
}
}
-func (q *keyQueue) resolveFocus(events *handlerEvents) (event.Tag, listenerPriority, bool) {
- var k event.Tag
- var pri listenerPriority
- var hide bool
-loop:
- for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
- switch opconst.OpType(encOp.Data[0]) {
- case opconst.TypeKeyInput:
- op := decodeKeyInputOp(encOp.Data, encOp.Refs)
- var newPri listenerPriority
- switch {
- case op.Focus:
- newPri = priNewFocus
- case op.Tag == q.focus:
- newPri = priCurrentFocus
- default:
- newPri = priDefault
- }
- // Switch focus if higher priority or if focus requested.
- if newPri.replaces(pri) {
- k, pri = op.Tag, newPri
- }
- h, ok := q.handlers[op.Tag]
- if !ok {
- h = new(keyHandler)
- q.handlers[op.Tag] = h
- // Reset the handler on (each) first appearance.
- events.Add(op.Tag, key.FocusEvent{Focus: false})
- }
- h.active = true
- case opconst.TypeHideInput:
- hide = true
- case opconst.TypePush:
- newK, newPri, h := q.resolveFocus(events)
- hide = hide || h
- if newPri.replaces(pri) {
- k, pri = newK, newPri
- }
- case opconst.TypePop:
- break loop
- }
- }
- return k, pri, hide
+func (k *keyCollector) focusOp(tag event.Tag) {
+ k.focus = tag
+ k.changed = true
}
-func (p listenerPriority) replaces(p2 listenerPriority) bool {
- // Favor earliest default focus or latest requested focus.
- return p > p2 || p == p2 && p == priNewFocus
+func (k *keyCollector) softKeyboard(show bool) {
+ if show {
+ k.q.state = TextInputOpen
+ } else {
+ k.q.state = TextInputClose
+ }
}
-func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp {
- if opconst.OpType(d[0]) != opconst.TypeKeyInput {
- panic("invalid op")
+func (k *keyCollector) inputOp(op key.InputOp) {
+ h, ok := k.q.handlers[op.Tag]
+ if !ok {
+ h = &keyHandler{new: true}
+ k.q.handlers[op.Tag] = h
}
- return key.InputOp{
- Tag: refs[0].(event.Tag),
- Focus: d[1] != 0,
+ h.visible = true
+ h.hint = op.Hint
+}
+
+func (t TextInputState) String() string {
+ switch t {
+ case TextInputKeep:
+ return "Keep"
+ case TextInputClose:
+ return "Close"
+ case TextInputOpen:
+ return "Open"
+ default:
+ panic("unexpected value")
}
}
diff --git a/vendor/gioui.org/io/router/pointer.go b/vendor/gioui.org/io/router/pointer.go
index 0d0977f..cb30f63 100644
--- a/vendor/gioui.org/io/router/pointer.go
+++ b/vendor/gioui.org/io/router/pointer.go
@@ -3,44 +3,65 @@
package router
import (
- "encoding/binary"
"image"
+ "io"
"gioui.org/f32"
- "gioui.org/internal/opconst"
"gioui.org/internal/ops"
"gioui.org/io/event"
"gioui.org/io/pointer"
- "gioui.org/op"
+ "gioui.org/io/semantic"
+ "gioui.org/io/transfer"
)
type pointerQueue struct {
- hitTree []hitNode
- areas []areaNode
- handlers map[event.Tag]*pointerHandler
- pointers []pointerInfo
- reader ops.Reader
+ hitTree []hitNode
+ areas []areaNode
+ cursors []cursorNode
+ cursor pointer.CursorName
+ handlers map[event.Tag]*pointerHandler
+ pointers []pointerInfo
+ transfers []io.ReadCloser // pending data transfers
scratch []event.Tag
+
+ semantic struct {
+ idsAssigned bool
+ lastID SemanticID
+ // contentIDs maps semantic content to a list of semantic IDs
+ // previously assigned. It is used to maintain stable IDs across
+ // frames.
+ contentIDs map[semanticContent][]semanticID
+ }
}
type hitNode struct {
next int
area int
- // Pass tracks the most recent PassOp mode.
- pass bool
// For handler nodes.
- tag event.Tag
+ tag event.Tag
+ pass bool
+}
+
+type cursorNode struct {
+ name pointer.CursorName
+ area int
}
type pointerInfo struct {
id pointer.ID
pressed bool
handlers []event.Tag
+ // last tracks the last pointer event received,
+ // used while processing frame events.
+ last pointer.Event
// entered tracks the tags that contain the pointer.
entered []event.Tag
+
+ dataSource event.Tag // dragging source tag
+ dataTarget event.Tag // dragging target tag
}
type pointerHandler struct {
@@ -48,80 +69,381 @@ type pointerHandler struct {
active bool
wantsGrab bool
types pointer.Type
+ // min and max horizontal/vertical scroll
+ scrollRange image.Rectangle
+
+ sourceMimes []string
+ targetMimes []string
+ offeredMime string
+ data io.ReadCloser
}
type areaOp struct {
kind areaKind
- rect image.Rectangle
+ rect f32.Rectangle
}
type areaNode struct {
trans f32.Affine2D
- next int
area areaOp
+
+ // Tree indices, with -1 being the sentinel.
+ parent int
+ firstChild int
+ lastChild int
+ sibling int
+
+ semantic struct {
+ valid bool
+ id SemanticID
+ content semanticContent
+ }
}
type areaKind uint8
+// collectState represents the state for pointerCollector.
+type collectState struct {
+ t f32.Affine2D
+ // nodePlusOne is the current node index, plus one to
+ // make the zero value collectState the initial state.
+ nodePlusOne int
+ pass int
+}
+
+// pointerCollector tracks the state needed to update an pointerQueue
+// from pointer ops.
+type pointerCollector struct {
+ q *pointerQueue
+ state collectState
+ nodeStack []int
+}
+
+type semanticContent struct {
+ tag event.Tag
+ label string
+ desc string
+ class semantic.ClassOp
+ gestures SemanticGestures
+ selected bool
+ disabled bool
+}
+
+type semanticID struct {
+ id SemanticID
+ used bool
+}
+
const (
areaRect areaKind = iota
areaEllipse
)
-func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t f32.Affine2D, area, node int, pass bool) {
- for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
- switch opconst.OpType(encOp.Data[0]) {
- case opconst.TypePush:
- q.collectHandlers(r, events, t, area, node, pass)
- case opconst.TypePop:
- return
- case opconst.TypePass:
- op := decodePassOp(encOp.Data)
- pass = op.Pass
- case opconst.TypeArea:
- var op areaOp
- op.Decode(encOp.Data)
- q.areas = append(q.areas, areaNode{trans: t, next: area, area: op})
- area = len(q.areas) - 1
- q.hitTree = append(q.hitTree, hitNode{
- next: node,
- area: area,
- pass: pass,
- })
- node = len(q.hitTree) - 1
- case opconst.TypeTransform:
- dop := ops.DecodeTransform(encOp.Data)
- t = t.Mul(dop)
- case opconst.TypePointerInput:
- op := decodePointerInputOp(encOp.Data, encOp.Refs)
- q.hitTree = append(q.hitTree, hitNode{
- next: node,
- area: area,
- pass: pass,
- tag: op.Tag,
- })
- node = len(q.hitTree) - 1
- h, ok := q.handlers[op.Tag]
- if !ok {
- h = new(pointerHandler)
- q.handlers[op.Tag] = h
- events.Add(op.Tag, pointer.Event{Type: pointer.Cancel})
- }
- h.active = true
- h.area = area
- h.wantsGrab = h.wantsGrab || op.Grab
- h.types = h.types | op.Types
+func (c *pointerCollector) resetState() {
+ c.state = collectState{}
+}
+
+func (c *pointerCollector) setTrans(t f32.Affine2D) {
+ c.state.t = t
+}
+
+func (c *pointerCollector) clip(op ops.ClipOp) {
+ kind := areaRect
+ if op.Shape == ops.Ellipse {
+ kind = areaEllipse
+ }
+ c.pushArea(kind, frect(op.Bounds))
+}
+
+func (c *pointerCollector) pushArea(kind areaKind, bounds f32.Rectangle) {
+ parentID := c.currentArea()
+ areaID := len(c.q.areas)
+ areaOp := areaOp{kind: kind, rect: bounds}
+ if parentID != -1 {
+ parent := &c.q.areas[parentID]
+ if parent.firstChild == -1 {
+ parent.firstChild = areaID
+ }
+ if siblingID := parent.lastChild; siblingID != -1 {
+ c.q.areas[siblingID].sibling = areaID
+ }
+ parent.lastChild = areaID
+ }
+ an := areaNode{
+ trans: c.state.t,
+ area: areaOp,
+ parent: parentID,
+ sibling: -1,
+ firstChild: -1,
+ lastChild: -1,
+ }
+
+ c.q.areas = append(c.q.areas, an)
+ c.nodeStack = append(c.nodeStack, c.state.nodePlusOne-1)
+ c.addHitNode(hitNode{
+ area: areaID,
+ pass: true,
+ })
+}
+
+// frect converts a rectangle to a f32.Rectangle.
+func frect(r image.Rectangle) f32.Rectangle {
+ return f32.Rectangle{
+ Min: fpt(r.Min), Max: fpt(r.Max),
+ }
+}
+
+// fpt converts a point to a f32.Point.
+func fpt(p image.Point) f32.Point {
+ return f32.Point{
+ X: float32(p.X), Y: float32(p.Y),
+ }
+}
+
+func (c *pointerCollector) popArea() {
+ n := len(c.nodeStack)
+ c.state.nodePlusOne = c.nodeStack[n-1] + 1
+ c.nodeStack = c.nodeStack[:n-1]
+}
+
+func (c *pointerCollector) pass() {
+ c.state.pass++
+}
+
+func (c *pointerCollector) popPass() {
+ c.state.pass--
+}
+
+func (c *pointerCollector) currentArea() int {
+ if i := c.state.nodePlusOne - 1; i != -1 {
+ n := c.q.hitTree[i]
+ return n.area
+ }
+ return -1
+}
+
+func (c *pointerCollector) addHitNode(n hitNode) {
+ n.next = c.state.nodePlusOne - 1
+ c.q.hitTree = append(c.q.hitTree, n)
+ c.state.nodePlusOne = len(c.q.hitTree) - 1 + 1
+}
+
+// newHandler returns the current handler or a new one for tag.
+func (c *pointerCollector) newHandler(tag event.Tag, events *handlerEvents) *pointerHandler {
+ areaID := c.currentArea()
+ c.addHitNode(hitNode{
+ area: areaID,
+ tag: tag,
+ pass: c.state.pass > 0,
+ })
+ h, ok := c.q.handlers[tag]
+ if !ok {
+ h = new(pointerHandler)
+ c.q.handlers[tag] = h
+ // Cancel handlers on (each) first appearance, but don't
+ // trigger redraw.
+ events.AddNoRedraw(tag, pointer.Event{Type: pointer.Cancel})
+ }
+ h.active = true
+ h.area = areaID
+ return h
+}
+
+func (c *pointerCollector) inputOp(op pointer.InputOp, events *handlerEvents) {
+ areaID := c.currentArea()
+ area := &c.q.areas[areaID]
+ area.semantic.content.tag = op.Tag
+ if op.Types&(pointer.Press|pointer.Release) != 0 {
+ area.semantic.content.gestures |= ClickGesture
+ }
+ area.semantic.valid = area.semantic.content.gestures != 0
+ h := c.newHandler(op.Tag, events)
+ h.wantsGrab = h.wantsGrab || op.Grab
+ h.types = h.types | op.Types
+ h.scrollRange = op.ScrollBounds
+}
+
+func (c *pointerCollector) semanticLabel(lbl string) {
+ areaID := c.currentArea()
+ area := &c.q.areas[areaID]
+ area.semantic.valid = true
+ area.semantic.content.label = lbl
+}
+
+func (c *pointerCollector) semanticDesc(desc string) {
+ areaID := c.currentArea()
+ area := &c.q.areas[areaID]
+ area.semantic.valid = true
+ area.semantic.content.desc = desc
+}
+
+func (c *pointerCollector) semanticClass(class semantic.ClassOp) {
+ areaID := c.currentArea()
+ area := &c.q.areas[areaID]
+ area.semantic.valid = true
+ area.semantic.content.class = class
+}
+
+func (c *pointerCollector) semanticSelected(selected bool) {
+ areaID := c.currentArea()
+ area := &c.q.areas[areaID]
+ area.semantic.valid = true
+ area.semantic.content.selected = selected
+}
+
+func (c *pointerCollector) semanticDisabled(disabled bool) {
+ areaID := c.currentArea()
+ area := &c.q.areas[areaID]
+ area.semantic.valid = true
+ area.semantic.content.disabled = disabled
+}
+
+func (c *pointerCollector) cursor(name pointer.CursorName) {
+ c.q.cursors = append(c.q.cursors, cursorNode{
+ name: name,
+ area: len(c.q.areas) - 1,
+ })
+}
+
+func (c *pointerCollector) sourceOp(op transfer.SourceOp, events *handlerEvents) {
+ h := c.newHandler(op.Tag, events)
+ h.sourceMimes = append(h.sourceMimes, op.Type)
+}
+
+func (c *pointerCollector) targetOp(op transfer.TargetOp, events *handlerEvents) {
+ h := c.newHandler(op.Tag, events)
+ h.targetMimes = append(h.targetMimes, op.Type)
+}
+
+func (c *pointerCollector) offerOp(op transfer.OfferOp, events *handlerEvents) {
+ h := c.newHandler(op.Tag, events)
+ h.offeredMime = op.Type
+ h.data = op.Data
+}
+
+func (c *pointerCollector) reset() {
+ c.q.reset()
+ c.resetState()
+ c.nodeStack = c.nodeStack[:0]
+ c.ensureRoot()
+}
+
+// Ensure implicit root area for semantic descriptions to hang onto.
+func (c *pointerCollector) ensureRoot() {
+ if len(c.q.areas) > 0 {
+ return
+ }
+ c.pushArea(areaRect, f32.Rect(-1e6, -1e6, 1e6, 1e6))
+ // Make it semantic to ensure a single semantic root.
+ c.q.areas[0].semantic.valid = true
+}
+
+func (q *pointerQueue) assignSemIDs() {
+ if q.semantic.idsAssigned {
+ return
+ }
+ q.semantic.idsAssigned = true
+ for i, a := range q.areas {
+ if a.semantic.valid {
+ q.areas[i].semantic.id = q.semanticIDFor(a.semantic.content)
}
}
}
-func (q *pointerQueue) opHit(handlers *[]event.Tag, pos f32.Point) {
+func (q *pointerQueue) AppendSemantics(nodes []SemanticNode) []SemanticNode {
+ q.assignSemIDs()
+ nodes = q.appendSemanticChildren(nodes, 0)
+ nodes = q.appendSemanticArea(nodes, 0, 0)
+ return nodes
+}
+
+func (q *pointerQueue) appendSemanticArea(nodes []SemanticNode, parentID SemanticID, nodeIdx int) []SemanticNode {
+ areaIdx := nodes[nodeIdx].areaIdx
+ a := q.areas[areaIdx]
+ childStart := len(nodes)
+ nodes = q.appendSemanticChildren(nodes, a.firstChild)
+ childEnd := len(nodes)
+ for i := childStart; i < childEnd; i++ {
+ nodes = q.appendSemanticArea(nodes, a.semantic.id, i)
+ }
+ n := &nodes[nodeIdx]
+ n.ParentID = parentID
+ n.Children = nodes[childStart:childEnd]
+ return nodes
+}
+
+func (q *pointerQueue) appendSemanticChildren(nodes []SemanticNode, areaIdx int) []SemanticNode {
+ if areaIdx == -1 {
+ return nodes
+ }
+ a := q.areas[areaIdx]
+ if semID := a.semantic.id; semID != 0 {
+ cnt := a.semantic.content
+ nodes = append(nodes, SemanticNode{
+ ID: semID,
+ Desc: SemanticDesc{
+ Bounds: f32.Rectangle{
+ Min: a.trans.Transform(a.area.rect.Min),
+ Max: a.trans.Transform(a.area.rect.Max),
+ },
+ Label: cnt.label,
+ Description: cnt.desc,
+ Class: cnt.class,
+ Gestures: cnt.gestures,
+ Selected: cnt.selected,
+ Disabled: cnt.disabled,
+ },
+ areaIdx: areaIdx,
+ })
+ } else {
+ nodes = q.appendSemanticChildren(nodes, a.firstChild)
+ }
+ return q.appendSemanticChildren(nodes, a.sibling)
+}
+
+func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID {
+ ids := q.semantic.contentIDs[content]
+ for i, id := range ids {
+ if !id.used {
+ ids[i].used = true
+ return id.id
+ }
+ }
+ // No prior assigned ID; allocate a new one.
+ q.semantic.lastID++
+ id := semanticID{id: q.semantic.lastID, used: true}
+ if q.semantic.contentIDs == nil {
+ q.semantic.contentIDs = make(map[semanticContent][]semanticID)
+ }
+ q.semantic.contentIDs[content] = append(q.semantic.contentIDs[content], id)
+ return id.id
+}
+
+func (q *pointerQueue) SemanticAt(pos f32.Point) (SemanticID, bool) {
+ q.assignSemIDs()
+ for i := len(q.hitTree) - 1; i >= 0; i-- {
+ n := &q.hitTree[i]
+ hit := q.hit(n.area, pos)
+ if !hit {
+ continue
+ }
+ area := q.areas[n.area]
+ if area.semantic.id != 0 {
+ return area.semantic.id, true
+ }
+ }
+ return 0, false
+}
+
+func (q *pointerQueue) opHit(pos f32.Point) []event.Tag {
// Track whether we're passing through hits.
pass := true
+ hits := q.scratch[:0]
idx := len(q.hitTree) - 1
for idx >= 0 {
n := &q.hitTree[idx]
- if !q.hit(n.area, pos) {
+ hit := q.hit(n.area, pos)
+ if !hit {
idx--
continue
}
@@ -133,10 +455,12 @@ func (q *pointerQueue) opHit(handlers *[]event.Tag, pos f32.Point) {
}
if n.tag != nil {
if _, exists := q.handlers[n.tag]; exists {
- *handlers = append(*handlers, n.tag)
+ hits = addHandler(hits, n.tag)
}
}
}
+ q.scratch = hits[:0]
+ return hits
}
func (q *pointerQueue) invTransform(areaIdx int, p f32.Point) f32.Point {
@@ -153,32 +477,53 @@ func (q *pointerQueue) hit(areaIdx int, p f32.Point) bool {
if !a.area.Hit(p) {
return false
}
- areaIdx = a.next
+ areaIdx = a.parent
}
return true
}
-func (q *pointerQueue) init() {
+func (q *pointerQueue) reset() {
if q.handlers == nil {
q.handlers = make(map[event.Tag]*pointerHandler)
}
-}
-
-func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) {
- q.init()
for _, h := range q.handlers {
// Reset handler.
h.active = false
h.wantsGrab = false
h.types = 0
+ h.sourceMimes = h.sourceMimes[:0]
+ h.targetMimes = h.targetMimes[:0]
}
q.hitTree = q.hitTree[:0]
q.areas = q.areas[:0]
- q.reader.Reset(root)
- q.collectHandlers(&q.reader, events, f32.Affine2D{}, -1, -1, false)
+ q.cursors = q.cursors[:0]
+ q.semantic.idsAssigned = false
+ for k, ids := range q.semantic.contentIDs {
+ for i := len(ids) - 1; i >= 0; i-- {
+ if !ids[i].used {
+ ids = append(ids[:i], ids[i+1:]...)
+ } else {
+ ids[i].used = false
+ }
+ }
+ if len(ids) > 0 {
+ q.semantic.contentIDs[k] = ids
+ } else {
+ delete(q.semantic.contentIDs, k)
+ }
+ }
+ for _, rc := range q.transfers {
+ if rc != nil {
+ rc.Close()
+ }
+ }
+ q.transfers = nil
+}
+
+func (q *pointerQueue) Frame(events *handlerEvents) {
for k, h := range q.handlers {
if !h.active {
- q.dropHandlers(events, k)
+ q.dropHandler(nil, k)
delete(q.handlers, k)
}
if h.wantsGrab {
@@ -189,86 +534,93 @@ func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) {
for i, k2 := range p.handlers {
if k2 == k {
// Drop other handlers that lost their grab.
- q.dropHandlers(events, p.handlers[i+1:]...)
- q.dropHandlers(events, p.handlers[:i]...)
+ dropped := q.scratch[:0]
+ dropped = append(dropped, p.handlers[:i]...)
+ dropped = append(dropped, p.handlers[i+1:]...)
+ for _, tag := range dropped {
+ q.dropHandler(events, tag)
+ }
break
}
}
}
}
}
+ for i := range q.pointers {
+ p := &q.pointers[i]
+ q.deliverEnterLeaveEvents(p, events, p.last)
+ q.deliverTransferDataEvent(p, events)
+ }
}
-func (q *pointerQueue) dropHandlers(events *handlerEvents, tags ...event.Tag) {
- for _, k := range tags {
- events.Add(k, pointer.Event{Type: pointer.Cancel})
- for i := range q.pointers {
- p := &q.pointers[i]
- for i := len(p.handlers) - 1; i >= 0; i-- {
- if p.handlers[i] == k {
- p.handlers = append(p.handlers[:i], p.handlers[i+1:]...)
- }
+func (q *pointerQueue) dropHandler(events *handlerEvents, tag event.Tag) {
+ if events != nil {
+ events.Add(tag, pointer.Event{Type: pointer.Cancel})
+ }
+ for i := range q.pointers {
+ p := &q.pointers[i]
+ for i := len(p.handlers) - 1; i >= 0; i-- {
+ if p.handlers[i] == tag {
+ p.handlers = append(p.handlers[:i], p.handlers[i+1:]...)
}
- for i := len(p.entered) - 1; i >= 0; i-- {
- if p.entered[i] == k {
- p.entered = append(p.entered[:i], p.entered[i+1:]...)
- }
+ }
+ for i := len(p.entered) - 1; i >= 0; i-- {
+ if p.entered[i] == tag {
+ p.entered = append(p.entered[:i], p.entered[i+1:]...)
}
}
}
}
+// pointerOf returns the pointerInfo index corresponding to the pointer in e.
+func (q *pointerQueue) pointerOf(e pointer.Event) int {
+ for i, p := range q.pointers {
+ if p.id == e.PointerID {
+ return i
+ }
+ }
+ q.pointers = append(q.pointers, pointerInfo{id: e.PointerID})
+ return len(q.pointers) - 1
+}
+
func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) {
- q.init()
if e.Type == pointer.Cancel {
q.pointers = q.pointers[:0]
for k := range q.handlers {
- q.dropHandlers(events, k)
+ q.dropHandler(events, k)
}
return
}
- pidx := -1
- for i, p := range q.pointers {
- if p.id == e.PointerID {
- pidx = i
- break
- }
- }
- if pidx == -1 {
- q.pointers = append(q.pointers, pointerInfo{id: e.PointerID})
- pidx = len(q.pointers) - 1
- }
+ pidx := q.pointerOf(e)
p := &q.pointers[pidx]
+ p.last = e
- if e.Type == pointer.Move && p.pressed {
- e.Type = pointer.Drag
- }
-
- if e.Type == pointer.Release {
+ switch e.Type {
+ case pointer.Press:
+ q.deliverEnterLeaveEvents(p, events, e)
+ p.pressed = true
q.deliverEvent(p, events, e)
- p.pressed = false
- }
- q.scratch = q.scratch[:0]
- q.opHit(&q.scratch, e.Position)
- if p.pressed {
- // Filter out non-participating handlers.
- for i := len(q.scratch) - 1; i >= 0; i-- {
- if _, found := searchTag(p.handlers, q.scratch[i]); !found {
- q.scratch = append(q.scratch[:i], q.scratch[i+1:]...)
- }
+ case pointer.Move:
+ if p.pressed {
+ e.Type = pointer.Drag
}
- }
- q.deliverEnterLeaveEvents(p, q.scratch, events, e)
-
- if !p.pressed {
- p.handlers = append(p.handlers[:0], q.scratch...)
- }
- if e.Type == pointer.Press {
- p.pressed = true
- }
- if e.Type != pointer.Release {
+ q.deliverEnterLeaveEvents(p, events, e)
q.deliverEvent(p, events, e)
+ if p.pressed {
+ q.deliverDragEvent(p, events)
+ }
+ case pointer.Release:
+ q.deliverEvent(p, events, e)
+ p.pressed = false
+ q.deliverEnterLeaveEvents(p, events, e)
+ q.deliverDropEvent(p, events)
+ case pointer.Scroll:
+ q.deliverEnterLeaveEvents(p, events, e)
+ q.deliverScrollEvent(p, events, e)
+ default:
+ panic("unsupported pointer event type")
}
+
if !p.pressed && len(p.entered) == 0 {
// No longer need to track pointer.
q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...)
@@ -277,28 +629,77 @@ func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) {
func (q *pointerQueue) deliverEvent(p *pointerInfo, events *handlerEvents, e pointer.Event) {
foremost := true
+ if p.pressed && len(p.handlers) == 1 {
+ e.Priority = pointer.Grabbed
+ foremost = false
+ }
for _, k := range p.handlers {
h := q.handlers[k]
+ if e.Type&h.types == 0 {
+ continue
+ }
e := e
- if p.pressed && len(p.handlers) == 1 {
- e.Priority = pointer.Grabbed
- } else if foremost {
+ if foremost {
+ foremost = false
e.Priority = pointer.Foremost
}
-
e.Position = q.invTransform(h.area, e.Position)
+ events.Add(k, e)
+ }
+}
- if e.Type&h.types == e.Type {
+func (q *pointerQueue) deliverScrollEvent(p *pointerInfo, events *handlerEvents, e pointer.Event) {
+ foremost := true
+ if p.pressed && len(p.handlers) == 1 {
+ e.Priority = pointer.Grabbed
+ foremost = false
+ }
+ var sx, sy = e.Scroll.X, e.Scroll.Y
+ for _, k := range p.handlers {
+ if sx == 0 && sy == 0 {
+ return
+ }
+ h := q.handlers[k]
+ // Distribute the scroll to the handler based on its ScrollRange.
+ sx, e.Scroll.X = setScrollEvent(sx, h.scrollRange.Min.X, h.scrollRange.Max.X)
+ sy, e.Scroll.Y = setScrollEvent(sy, h.scrollRange.Min.Y, h.scrollRange.Max.Y)
+ e := e
+ if foremost {
foremost = false
- events.Add(k, e)
+ e.Priority = pointer.Foremost
}
+ e.Position = q.invTransform(h.area, e.Position)
+ events.Add(k, e)
}
}
-func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag, events *handlerEvents, e pointer.Event) {
+func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, events *handlerEvents, e pointer.Event) {
+ var hits []event.Tag
if e.Source != pointer.Mouse && !p.pressed && e.Type != pointer.Press {
// Consider non-mouse pointers leaving when they're released.
- hits = nil
+ } else {
+ hits = q.opHit(e.Position)
+ if p.pressed {
+ // Filter out non-participating handlers,
+ // except potential transfer targets when a transfer has been initiated.
+ var hitsHaveTarget bool
+ if p.dataSource != nil {
+ transferSource := q.handlers[p.dataSource]
+ for _, hit := range hits {
+ if _, ok := firstMimeMatch(transferSource, q.handlers[hit]); ok {
+ hitsHaveTarget = true
+ break
+ }
+ }
+ }
+ for i := len(hits) - 1; i >= 0; i-- {
+ if _, found := searchTag(p.handlers, hits[i]); !found && !hitsHaveTarget {
+ hits = append(hits[:i], hits[i+1:]...)
+ }
+ }
+ } else {
+ p.handlers = append(p.handlers[:0], hits...)
+ }
}
// Deliver Leave events.
for _, k := range p.entered {
@@ -307,28 +708,116 @@ func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag,
}
h := q.handlers[k]
e.Type = pointer.Leave
- e.Position = q.invTransform(h.area, e.Position)
- if e.Type&h.types == e.Type {
+ if e.Type&h.types != 0 {
+ e.Position = q.invTransform(h.area, e.Position)
events.Add(k, e)
}
}
- // Deliver Enter events.
+ // Deliver Enter events and update cursor.
+ q.cursor = pointer.CursorDefault
for _, k := range hits {
+ h := q.handlers[k]
+ for i := len(q.cursors) - 1; i >= 0; i-- {
+ if c := q.cursors[i]; c.area == h.area {
+ q.cursor = c.name
+ break
+ }
+ }
if _, found := searchTag(p.entered, k); found {
continue
}
- h := q.handlers[k]
e.Type = pointer.Enter
- e.Position = q.invTransform(h.area, e.Position)
- if e.Type&h.types == e.Type {
+ if e.Type&h.types != 0 {
+ e.Position = q.invTransform(h.area, e.Position)
events.Add(k, e)
}
}
p.entered = append(p.entered[:0], hits...)
}
+func (q *pointerQueue) deliverDragEvent(p *pointerInfo, events *handlerEvents) {
+ if p.dataSource != nil {
+ return
+ }
+ // Identify the data source.
+ for _, k := range p.entered {
+ src := q.handlers[k]
+ if len(src.sourceMimes) == 0 {
+ continue
+ }
+ // One data source handler per pointer.
+ p.dataSource = k
+ // Notify all potential targets.
+ for k, tgt := range q.handlers {
+ if _, ok := firstMimeMatch(src, tgt); ok {
+ events.Add(k, transfer.InitiateEvent{})
+ }
+ }
+ break
+ }
+}
+
+func (q *pointerQueue) deliverDropEvent(p *pointerInfo, events *handlerEvents) {
+ if p.dataSource == nil {
+ return
+ }
+ // Request data from the source.
+ src := q.handlers[p.dataSource]
+ for _, k := range p.entered {
+ h := q.handlers[k]
+ if m, ok := firstMimeMatch(src, h); ok {
+ p.dataTarget = k
+ events.Add(p.dataSource, transfer.RequestEvent{Type: m})
+ return
+ }
+ }
+ // No valid target found, abort.
+ q.deliverTransferCancelEvent(p, events)
+}
+
+func (q *pointerQueue) deliverTransferDataEvent(p *pointerInfo, events *handlerEvents) {
+ if p.dataSource == nil {
+ return
+ }
+ src := q.handlers[p.dataSource]
+ if src.data == nil {
+ // Data not received yet.
+ return
+ }
+ if p.dataTarget == nil {
+ q.deliverTransferCancelEvent(p, events)
+ return
+ }
+ // Send the offered data to the target.
+ transferIdx := len(q.transfers)
+ events.Add(p.dataTarget, transfer.DataEvent{
+ Type: src.offeredMime,
+ Open: func() io.ReadCloser {
+ q.transfers[transferIdx] = nil
+ return src.data
+ },
+ })
+ q.transfers = append(q.transfers, src.data)
+ p.dataTarget = nil
+}
+
+func (q *pointerQueue) deliverTransferCancelEvent(p *pointerInfo, events *handlerEvents) {
+ events.Add(p.dataSource, transfer.CancelEvent{})
+ // Cancel all potential targets.
+ src := q.handlers[p.dataSource]
+ for k, h := range q.handlers {
+ if _, ok := firstMimeMatch(src, h); ok {
+ events.Add(k, transfer.CancelEvent{})
+ }
+ }
+ src.offeredMime = ""
+ src.data = nil
+ p.dataSource = nil
+ p.dataTarget = nil
+}
+
func searchTag(tags []event.Tag, tag event.Tag) (int, bool) {
for i, t := range tags {
if t == tag {
@@ -338,41 +827,38 @@ func searchTag(tags []event.Tag, tag event.Tag) (int, bool) {
return 0, false
}
-func (op *areaOp) Decode(d []byte) {
- if opconst.OpType(d[0]) != opconst.TypeArea {
- panic("invalid op")
- }
- bo := binary.LittleEndian
- rect := image.Rectangle{
- Min: image.Point{
- X: int(int32(bo.Uint32(d[2:]))),
- Y: int(int32(bo.Uint32(d[6:]))),
- },
- Max: image.Point{
- X: int(int32(bo.Uint32(d[10:]))),
- Y: int(int32(bo.Uint32(d[14:]))),
- },
+// addHandler adds tag to the slice if not present.
+func addHandler(tags []event.Tag, tag event.Tag) []event.Tag {
+ for _, t := range tags {
+ if t == tag {
+ return tags
+ }
}
- *op = areaOp{
- kind: areaKind(d[1]),
- rect: rect,
+ return append(tags, tag)
+}
+
+// firstMimeMatch returns the first type match between src and tgt.
+func firstMimeMatch(src, tgt *pointerHandler) (first string, matched bool) {
+ for _, m1 := range tgt.targetMimes {
+ for _, m2 := range src.sourceMimes {
+ if m1 == m2 {
+ return m1, true
+ }
+ }
}
+ return "", false
}
func (op *areaOp) Hit(pos f32.Point) bool {
- min := f32.Point{
- X: float32(op.rect.Min.X),
- Y: float32(op.rect.Min.Y),
- }
- pos = pos.Sub(min)
+ pos = pos.Sub(op.rect.Min)
size := op.rect.Size()
switch op.kind {
case areaRect:
- return 0 <= pos.X && pos.X < float32(size.X) &&
- 0 <= pos.Y && pos.Y < float32(size.Y)
+ return 0 <= pos.X && pos.X < size.X &&
+ 0 <= pos.Y && pos.Y < size.Y
case areaEllipse:
- rx := float32(size.X) / 2
- ry := float32(size.Y) / 2
+ rx := size.X / 2
+ ry := size.Y / 2
xh := pos.X - rx
yk := pos.Y - ry
// The ellipse function works in all cases because
@@ -383,22 +869,12 @@ func (op *areaOp) Hit(pos f32.Point) bool {
}
}
-func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp {
- if opconst.OpType(d[0]) != opconst.TypePointerInput {
- panic("invalid op")
- }
- return pointer.InputOp{
- Tag: refs[0].(event.Tag),
- Grab: d[1] != 0,
- Types: pointer.Type(d[2]),
- }
-}
-
-func decodePassOp(d []byte) pointer.PassOp {
- if opconst.OpType(d[0]) != opconst.TypePass {
- panic("invalid op")
+func setScrollEvent(scroll float32, min, max int) (left, scrolled float32) {
+ if v := float32(max); scroll > v {
+ return scroll - v, v
}
- return pointer.PassOp{
- Pass: d[1] != 0,
+ if v := float32(min); scroll < v {
+ return scroll - v, v
}
+ return 0, scroll
}
diff --git a/vendor/gioui.org/io/router/router.go b/vendor/gioui.org/io/router/router.go
index d24d214..a02f0f6 100644
--- a/vendor/gioui.org/io/router/router.go
+++ b/vendor/gioui.org/io/router/router.go
@@ -12,22 +12,37 @@ package router
import (
"encoding/binary"
+ "image"
+ "io"
+ "strings"
"time"
- "gioui.org/internal/opconst"
+ "gioui.org/f32"
"gioui.org/internal/ops"
+ "gioui.org/io/clipboard"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/profile"
+ "gioui.org/io/semantic"
+ "gioui.org/io/transfer"
"gioui.org/op"
)
// Router is a Queue implementation that routes events
// to handlers declared in operation lists.
type Router struct {
- pqueue pointerQueue
- kqueue keyQueue
+ savedTrans []f32.Affine2D
+ transStack []f32.Affine2D
+ pointer struct {
+ queue pointerQueue
+ collector pointerCollector
+ }
+ key struct {
+ queue keyQueue
+ collector keyCollector
+ }
+ cqueue clipboardQueue
handlers handlerEvents
@@ -38,11 +53,44 @@ type Router struct {
wakeupTime time.Time
// ProfileOp summary.
- profiling bool
profHandlers map[event.Tag]struct{}
profile profile.Event
}
+// SemanticNode represents a node in the tree describing the components
+// contained in a frame.
+type SemanticNode struct {
+ ID SemanticID
+ ParentID SemanticID
+ Children []SemanticNode
+ Desc SemanticDesc
+
+ areaIdx int
+}
+
+// SemanticDesc provides a semantic description of a UI component.
+type SemanticDesc struct {
+ Class semantic.ClassOp
+ Description string
+ Label string
+ Selected bool
+ Disabled bool
+ Gestures SemanticGestures
+ Bounds f32.Rectangle
+}
+
+// SemanticGestures is a bit-set of supported gestures.
+type SemanticGestures int
+
+const (
+ ClickGesture SemanticGestures = 1 << iota
+)
+
+// SemanticID uniquely identifies a SemanticDescription.
+//
+// By convention, the zero value denotes the non-existent ID.
+type SemanticID uint64
+
type handlerEvents struct {
handlers map[event.Tag][]event.Event
hadEvents bool
@@ -61,33 +109,39 @@ func (q *Router) Events(k event.Tag) []event.Event {
// Frame replaces the declared handlers from the supplied
// operation list. The text input state, wakeup time and whether
// there are active profile handlers is also saved.
-func (q *Router) Frame(ops *op.Ops) {
+func (q *Router) Frame(frame *op.Ops) {
q.handlers.Clear()
q.wakeup = false
- q.profiling = false
for k := range q.profHandlers {
delete(q.profHandlers, k)
}
+ var ops *ops.Ops
+ if frame != nil {
+ ops = &frame.Internal
+ }
q.reader.Reset(ops)
q.collect()
- q.pqueue.Frame(ops, &q.handlers)
- q.kqueue.Frame(ops, &q.handlers)
+ q.pointer.queue.Frame(&q.handlers)
+ q.key.queue.Frame(&q.handlers, q.key.collector)
if q.handlers.HadEvents() {
q.wakeup = true
q.wakeupTime = time.Time{}
}
}
-func (q *Router) Add(events ...event.Event) bool {
+// Queue an event and report whether at least one handler had an event queued.
+func (q *Router) Queue(events ...event.Event) bool {
for _, e := range events {
switch e := e.(type) {
case profile.Event:
q.profile = e
case pointer.Event:
- q.pqueue.Push(e, &q.handlers)
+ q.pointer.queue.Push(e, &q.handlers)
case key.EditEvent, key.Event, key.FocusEvent:
- q.kqueue.Push(e, &q.handlers)
+ q.key.queue.Push(e, &q.handlers)
+ case clipboard.Event:
+ q.cqueue.Push(e, &q.handlers)
}
}
return q.handlers.HadEvents()
@@ -96,25 +150,189 @@ func (q *Router) Add(events ...event.Event) bool {
// TextInputState returns the input state from the most recent
// call to Frame.
func (q *Router) TextInputState() TextInputState {
- return q.kqueue.InputState()
+ return q.key.queue.InputState()
+}
+
+// TextInputHint returns the input mode from the most recent key.InputOp.
+func (q *Router) TextInputHint() (key.InputHint, bool) {
+ return q.key.queue.InputHint()
+}
+
+// WriteClipboard returns the most recent text to be copied
+// to the clipboard, if any.
+func (q *Router) WriteClipboard() (string, bool) {
+ return q.cqueue.WriteClipboard()
+}
+
+// ReadClipboard reports if any new handler is waiting
+// to read the clipboard.
+func (q *Router) ReadClipboard() bool {
+ return q.cqueue.ReadClipboard()
+}
+
+// Cursor returns the last cursor set.
+func (q *Router) Cursor() pointer.CursorName {
+ return q.pointer.queue.cursor
+}
+
+// SemanticAt returns the first semantic description under pos, if any.
+func (q *Router) SemanticAt(pos f32.Point) (SemanticID, bool) {
+ return q.pointer.queue.SemanticAt(pos)
+}
+
+// AppendSemantics appends the semantic tree to nodes, and returns the result.
+// The root node is the first added.
+func (q *Router) AppendSemantics(nodes []SemanticNode) []SemanticNode {
+ q.pointer.collector.q = &q.pointer.queue
+ q.pointer.collector.ensureRoot()
+ return q.pointer.queue.AppendSemantics(nodes)
}
func (q *Router) collect() {
+ q.transStack = q.transStack[:0]
+ pc := &q.pointer.collector
+ pc.q = &q.pointer.queue
+ pc.reset()
+ kc := &q.key.collector
+ *kc = keyCollector{q: &q.key.queue}
+ q.key.queue.Reset()
+ var t f32.Affine2D
for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
- switch opconst.OpType(encOp.Data[0]) {
- case opconst.TypeInvalidate:
+ switch ops.OpType(encOp.Data[0]) {
+ case ops.TypeInvalidate:
op := decodeInvalidateOp(encOp.Data)
if !q.wakeup || op.At.Before(q.wakeupTime) {
q.wakeup = true
q.wakeupTime = op.At
}
- case opconst.TypeProfile:
+ case ops.TypeProfile:
op := decodeProfileOp(encOp.Data, encOp.Refs)
if q.profHandlers == nil {
q.profHandlers = make(map[event.Tag]struct{})
}
- q.profiling = true
q.profHandlers[op.Tag] = struct{}{}
+ case ops.TypeClipboardRead:
+ q.cqueue.ProcessReadClipboard(encOp.Refs)
+ case ops.TypeClipboardWrite:
+ q.cqueue.ProcessWriteClipboard(encOp.Refs)
+ case ops.TypeSave:
+ id := ops.DecodeSave(encOp.Data)
+ if extra := id - len(q.savedTrans) + 1; extra > 0 {
+ q.savedTrans = append(q.savedTrans, make([]f32.Affine2D, extra)...)
+ }
+ q.savedTrans[id] = t
+ case ops.TypeLoad:
+ id := ops.DecodeLoad(encOp.Data)
+ t = q.savedTrans[id]
+ pc.resetState()
+ pc.setTrans(t)
+
+ case ops.TypeClip:
+ var op ops.ClipOp
+ op.Decode(encOp.Data)
+ pc.clip(op)
+ case ops.TypePopClip:
+ pc.popArea()
+ case ops.TypeTransform:
+ t2, push := ops.DecodeTransform(encOp.Data)
+ if push {
+ q.transStack = append(q.transStack, t)
+ }
+ t = t.Mul(t2)
+ pc.setTrans(t)
+ case ops.TypePopTransform:
+ n := len(q.transStack)
+ t = q.transStack[n-1]
+ q.transStack = q.transStack[:n-1]
+ pc.setTrans(t)
+
+ // Pointer ops.
+ case ops.TypePass:
+ pc.pass()
+ case ops.TypePopPass:
+ pc.popPass()
+ case ops.TypePointerInput:
+ bo := binary.LittleEndian
+ op := pointer.InputOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Grab: encOp.Data[1] != 0,
+ Types: pointer.Type(bo.Uint16(encOp.Data[2:])),
+ ScrollBounds: image.Rectangle{
+ Min: image.Point{
+ X: int(int32(bo.Uint32(encOp.Data[4:]))),
+ Y: int(int32(bo.Uint32(encOp.Data[8:]))),
+ },
+ Max: image.Point{
+ X: int(int32(bo.Uint32(encOp.Data[12:]))),
+ Y: int(int32(bo.Uint32(encOp.Data[16:]))),
+ },
+ },
+ }
+ pc.inputOp(op, &q.handlers)
+ case ops.TypeCursor:
+ name := encOp.Refs[0].(pointer.CursorName)
+ pc.cursor(name)
+ case ops.TypeSource:
+ op := transfer.SourceOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Type: encOp.Refs[1].(string),
+ }
+ pc.sourceOp(op, &q.handlers)
+ case ops.TypeTarget:
+ op := transfer.TargetOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Type: encOp.Refs[1].(string),
+ }
+ pc.targetOp(op, &q.handlers)
+ case ops.TypeOffer:
+ op := transfer.OfferOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Type: encOp.Refs[1].(string),
+ Data: encOp.Refs[2].(io.ReadCloser),
+ }
+ pc.offerOp(op, &q.handlers)
+
+ // Key ops.
+ case ops.TypeKeyFocus:
+ tag, _ := encOp.Refs[0].(event.Tag)
+ op := key.FocusOp{
+ Tag: tag,
+ }
+ kc.focusOp(op.Tag)
+ case ops.TypeKeySoftKeyboard:
+ op := key.SoftKeyboardOp{
+ Show: encOp.Data[1] != 0,
+ }
+ kc.softKeyboard(op.Show)
+ case ops.TypeKeyInput:
+ op := key.InputOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Hint: key.InputHint(encOp.Data[1]),
+ }
+ kc.inputOp(op)
+
+ // Semantic ops.
+ case ops.TypeSemanticLabel:
+ lbl := encOp.Refs[0].(*string)
+ pc.semanticLabel(*lbl)
+ case ops.TypeSemanticDesc:
+ desc := encOp.Refs[0].(*string)
+ pc.semanticDesc(*desc)
+ case ops.TypeSemanticClass:
+ class := semantic.ClassOp(encOp.Data[1])
+ pc.semanticClass(class)
+ case ops.TypeSemanticSelected:
+ if encOp.Data[1] != 0 {
+ pc.semanticSelected(true)
+ } else {
+ pc.semanticSelected(false)
+ }
+ case ops.TypeSemanticDisabled:
+ if encOp.Data[1] != 0 {
+ pc.semanticDisabled(true)
+ } else {
+ pc.semanticDisabled(false)
+ }
}
}
}
@@ -122,7 +340,7 @@ func (q *Router) collect() {
// Profiling reports whether there was profile handlers in the
// most recent Frame call.
func (q *Router) Profiling() bool {
- return q.profiling
+ return len(q.profHandlers) > 0
}
// WakeupTime returns the most recent time for doing another frame,
@@ -137,9 +355,13 @@ func (h *handlerEvents) init() {
}
}
-func (h *handlerEvents) Add(k event.Tag, e event.Event) {
+func (h *handlerEvents) AddNoRedraw(k event.Tag, e event.Event) {
h.init()
h.handlers[k] = append(h.handlers[k], e)
+}
+
+func (h *handlerEvents) Add(k event.Tag, e event.Event) {
+ h.AddNoRedraw(k, e)
h.hadEvents = true
}
@@ -174,7 +396,7 @@ func (h *handlerEvents) Clear() {
}
func decodeProfileOp(d []byte, refs []interface{}) profile.Op {
- if opconst.OpType(d[0]) != opconst.TypeProfile {
+ if ops.OpType(d[0]) != ops.TypeProfile {
panic("invalid op")
}
return profile.Op{
@@ -184,7 +406,7 @@ func decodeProfileOp(d []byte, refs []interface{}) profile.Op {
func decodeInvalidateOp(d []byte) op.InvalidateOp {
bo := binary.LittleEndian
- if opconst.OpType(d[0]) != opconst.TypeInvalidate {
+ if ops.OpType(d[0]) != ops.TypeInvalidate {
panic("invalid op")
}
var o op.InvalidateOp
@@ -193,3 +415,11 @@ func decodeInvalidateOp(d []byte) op.InvalidateOp {
}
return o
}
+
+func (s SemanticGestures) String() string {
+ var gestures []string
+ if s&ClickGesture != 0 {
+ gestures = append(gestures, "Click")
+ }
+ return strings.Join(gestures, ",")
+}
diff --git a/vendor/gioui.org/io/semantic/semantic.go b/vendor/gioui.org/io/semantic/semantic.go
new file mode 100644
index 0000000..7499fc8
--- /dev/null
+++ b/vendor/gioui.org/io/semantic/semantic.go
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// Package semantic provides operations for semantic descriptions of a user
+// interface, to facilitate presentation and interaction in external software
+// such as screen readers.
+//
+// Semantic descriptions are organized in a tree, with clip operations as
+// nodes. Operations in this package are associated with the current semantic
+// node, that is the most recent pushed clip operation.
+package semantic
+
+import (
+ "gioui.org/internal/ops"
+ "gioui.org/op"
+)
+
+// LabelOp provides the content of a textual component.
+type LabelOp string
+
+// DescriptionOp describes a component.
+type DescriptionOp string
+
+// ClassOp provides the component class.
+type ClassOp int
+
+const (
+ Unknown ClassOp = iota
+ Button
+ CheckBox
+ Editor
+ RadioButton
+ Switch
+)
+
+// SelectedOp describes the selected state for components that have
+// boolean state.
+type SelectedOp bool
+
+// DisabledOp describes the disabled state.
+type DisabledOp bool
+
+func (l LabelOp) Add(o *op.Ops) {
+ s := string(l)
+ data := ops.Write1(&o.Internal, ops.TypeSemanticLabelLen, &s)
+ data[0] = byte(ops.TypeSemanticLabel)
+}
+
+func (d DescriptionOp) Add(o *op.Ops) {
+ s := string(d)
+ data := ops.Write1(&o.Internal, ops.TypeSemanticDescLen, &s)
+ data[0] = byte(ops.TypeSemanticDesc)
+}
+
+func (c ClassOp) Add(o *op.Ops) {
+ data := ops.Write(&o.Internal, ops.TypeSemanticClassLen)
+ data[0] = byte(ops.TypeSemanticClass)
+ data[1] = byte(c)
+}
+
+func (s SelectedOp) Add(o *op.Ops) {
+ data := ops.Write(&o.Internal, ops.TypeSemanticSelectedLen)
+ data[0] = byte(ops.TypeSemanticSelected)
+ if s {
+ data[1] = 1
+ }
+}
+
+func (d DisabledOp) Add(o *op.Ops) {
+ data := ops.Write(&o.Internal, ops.TypeSemanticDisabledLen)
+ data[0] = byte(ops.TypeSemanticDisabled)
+ if d {
+ data[1] = 1
+ }
+}
+
+func (c ClassOp) String() string {
+ switch c {
+ case Unknown:
+ return "Unknown"
+ case Button:
+ return "Button"
+ case CheckBox:
+ return "CheckBox"
+ case Editor:
+ return "Editor"
+ case RadioButton:
+ return "RadioButton"
+ case Switch:
+ return "Switch"
+ default:
+ panic("invalid ClassOp")
+ }
+}
diff --git a/vendor/gioui.org/io/system/system.go b/vendor/gioui.org/io/system/system.go
index 8abac6d..e145825 100644
--- a/vendor/gioui.org/io/system/system.go
+++ b/vendor/gioui.org/io/system/system.go
@@ -26,27 +26,8 @@ type FrameEvent struct {
Size image.Point
// Insets is the insets to apply.
Insets Insets
- // Frame is the callback to supply the list of
- // operations to complete the FrameEvent.
- //
- // Note that the operation list and the operations themselves
- // may not be mutated until another FrameEvent is received from
- // the same event source.
- // That means that calls to frame.Reset and changes to referenced
- // data such as ImageOp backing images should happen between
- // receiving a FrameEvent and calling Frame.
- //
- // Example:
- //
- // var w *app.Window
- // var frame *op.Ops
- // for e := range w.Events() {
- // if e, ok := e.(system.FrameEvent); ok {
- // // Call frame.Reset and manipulate images for ImageOps
- // // here.
- // e.Frame(frame)
- // }
- // }
+ // Frame completes the FrameEvent by drawing the graphical operations
+ // from ops into the window.
Frame func(frame *op.Ops)
// Queue supplies the events for event handlers.
Queue event.Queue
@@ -60,12 +41,6 @@ type DestroyEvent struct {
Err error
}
-// ClipboardEvent is sent once for each request for the
-// clipboard content.
-type ClipboardEvent struct {
- Text string
-}
-
// Insets is the space taken up by
// system decoration such as translucent
// system bars and software keyboards.
@@ -79,10 +54,11 @@ type StageEvent struct {
Stage Stage
}
-// CommandEvent is a system event.
+// CommandEvent is a system event. Unlike most other events, CommandEvent is
+// delivered as a pointer to allow Cancel to suppress it.
type CommandEvent struct {
Type CommandType
- // Suppress the default action of the command.
+ // Cancel suppress the default action of the command.
Cancel bool
}
@@ -117,8 +93,7 @@ func (l Stage) String() string {
}
}
-func (FrameEvent) ImplementsEvent() {}
-func (StageEvent) ImplementsEvent() {}
-func (*CommandEvent) ImplementsEvent() {}
-func (DestroyEvent) ImplementsEvent() {}
-func (ClipboardEvent) ImplementsEvent() {}
+func (FrameEvent) ImplementsEvent() {}
+func (StageEvent) ImplementsEvent() {}
+func (*CommandEvent) ImplementsEvent() {}
+func (DestroyEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/io/transfer/transfer.go b/vendor/gioui.org/io/transfer/transfer.go
new file mode 100644
index 0000000..85ab68c
--- /dev/null
+++ b/vendor/gioui.org/io/transfer/transfer.go
@@ -0,0 +1,109 @@
+// Package transfer contains operations and events for brokering data transfers.
+//
+// The transfer protocol is as follows:
+//
+// - Data sources are registered with SourceOps, data targets with TargetOps.
+// - A data source receives a RequestEvent when a transfer is initiated.
+// It must respond with an OfferOp.
+// - The target receives a DataEvent when transferring to it. It must close
+// the event data after use.
+//
+// When a user initiates a pointer-guided drag and drop transfer, the
+// source as well as all potential targets receive an InitiateEvent.
+// Potential targets are targets with at least one MIME type in common
+// with the source. When a drag gesture completes, a CancelEvent is sent
+// to the source and all potential targets.
+//
+// Note that the RequestEvent is sent to the source upon drop.
+package transfer
+
+import (
+ "io"
+
+ "gioui.org/internal/ops"
+ "gioui.org/io/event"
+ "gioui.org/op"
+)
+
+// SourceOp registers a tag as a data source for a MIME type.
+// Use multiple SourceOps if a tag supports multiple types.
+type SourceOp struct {
+ Tag event.Tag
+ // Type is the MIME type supported by this source.
+ Type string
+}
+
+// TargetOp registers a tag as a data target.
+// Use multiple TargetOps if a tag supports multiple types.
+type TargetOp struct {
+ Tag event.Tag
+ // Type is the MIME type accepted by this target.
+ Type string
+}
+
+// OfferOp is used by data sources as a response to a RequestEvent.
+type OfferOp struct {
+ Tag event.Tag
+ // Type is the MIME type of Data.
+ // It must be the Type from the corresponding RequestEvent.
+ Type string
+ // Data contains the offered data. It is closed when the
+ // transfer is complete or cancelled.
+ // Data must be kept valid until closed, and it may be used from
+ // a goroutine separate from the one processing the frame..
+ Data io.ReadCloser
+}
+
+func (op SourceOp) Add(o *op.Ops) {
+ data := ops.Write2(&o.Internal, ops.TypeSourceLen, op.Tag, op.Type)
+ data[0] = byte(ops.TypeSource)
+}
+
+func (op TargetOp) Add(o *op.Ops) {
+ data := ops.Write2(&o.Internal, ops.TypeTargetLen, op.Tag, op.Type)
+ data[0] = byte(ops.TypeTarget)
+}
+
+// Add the offer to the list of operations.
+// It panics if the Data field is not set.
+func (op OfferOp) Add(o *op.Ops) {
+ if op.Data == nil {
+ panic("invalid nil data in OfferOp")
+ }
+ data := ops.Write3(&o.Internal, ops.TypeOfferLen, op.Tag, op.Type, op.Data)
+ data[0] = byte(ops.TypeOffer)
+}
+
+// RequestEvent requests data from a data source. The source must
+// respond with an OfferOp.
+type RequestEvent struct {
+ // Type is the first matched type between the source and the target.
+ Type string
+}
+
+func (RequestEvent) ImplementsEvent() {}
+
+// InitiateEvent is sent to a data source when a drag-and-drop
+// transfer gesture is initiated.
+//
+// Potential data targets also receive the event.
+type InitiateEvent struct{}
+
+func (InitiateEvent) ImplementsEvent() {}
+
+// CancelEvent is sent to data sources and targets to cancel the
+// effects of an InitiateEvent.
+type CancelEvent struct{}
+
+func (CancelEvent) ImplementsEvent() {}
+
+// DataEvent is sent to the target receiving the transfer.
+type DataEvent struct {
+ // Type is the MIME type of Data.
+ Type string
+ // Open returns the transfer data. It is only valid to call Open in the frame
+ // the DataEvent is received. The caller must close the return value after use.
+ Open func() io.ReadCloser
+}
+
+func (DataEvent) ImplementsEvent() {}
diff --git a/vendor/gioui.org/layout/context.go b/vendor/gioui.org/layout/context.go
index f47233b..5d31496 100644
--- a/vendor/gioui.org/layout/context.go
+++ b/vendor/gioui.org/layout/context.go
@@ -5,6 +5,7 @@ package layout
import (
"time"
+ "gioui.org/f32"
"gioui.org/io/event"
"gioui.org/io/system"
"gioui.org/op"
@@ -39,15 +40,30 @@ type Context struct {
// Constraints: Exact(e.Size),
// }
//
-// NewContext calls ops.Reset.
+// NewContext calls ops.Reset and adjusts ops for e.Insets.
func NewContext(ops *op.Ops, e system.FrameEvent) Context {
ops.Reset()
+
+ size := e.Size
+
+ if e.Insets != (system.Insets{}) {
+ left := e.Metric.Px(e.Insets.Left)
+ top := e.Metric.Px(e.Insets.Top)
+ op.Offset(f32.Point{
+ X: float32(left),
+ Y: float32(top),
+ }).Add(ops)
+
+ size.X -= left + e.Metric.Px(e.Insets.Right)
+ size.Y -= top + e.Metric.Px(e.Insets.Bottom)
+ }
+
return Context{
Ops: ops,
Now: e.Now,
Queue: e.Queue,
Metric: e.Metric,
- Constraints: Exact(e.Size),
+ Constraints: Exact(size),
}
}
diff --git a/vendor/gioui.org/layout/doc.go b/vendor/gioui.org/layout/doc.go
index d318765..3824084 100644
--- a/vendor/gioui.org/layout/doc.go
+++ b/vendor/gioui.org/layout/doc.go
@@ -37,7 +37,7 @@ This example both aligns and insets a child:
inset := layout.Inset{...}
inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
- align := layout.Align(...)
+ align := layout.Alignment(...)
return align.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return widget.Layout(gtx, ...)
})
diff --git a/vendor/gioui.org/layout/flex.go b/vendor/gioui.org/layout/flex.go
index fe85b64..7d66f8f 100644
--- a/vendor/gioui.org/layout/flex.go
+++ b/vendor/gioui.org/layout/flex.go
@@ -65,9 +65,10 @@ func Rigid(widget Widget) FlexChild {
}
}
-// Flexed returns a Flex child forced to take up w fraction of the
-// of the space left over from Rigid children. The fraction is weight
-// divided by the weight sum of all Flexed children.
+// Flexed returns a Flex child forced to take up weight fraction of the
+// space left over from Rigid children. The fraction is weight
+// divided by either the weight sum of all Flexed children or the Flex
+// WeightSum if non zero.
func Flexed(weight float32, widget Widget) FlexChild {
return FlexChild{
flex: true,
@@ -82,10 +83,11 @@ func Flexed(weight float32, widget Widget) FlexChild {
func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
size := 0
cs := gtx.Constraints
- mainMin, mainMax := axisMainConstraint(f.Axis, cs)
- crossMin, crossMax := axisCrossConstraint(f.Axis, cs)
+ mainMin, mainMax := f.Axis.mainConstraint(cs)
+ crossMin, crossMax := f.Axis.crossConstraint(cs)
remaining := mainMax
var totalWeight float32
+ cgtx := gtx
// Lay out Rigid children.
for i, child := range children {
if child.flex {
@@ -93,11 +95,10 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
continue
}
macro := op.Record(gtx.Ops)
- gtx := gtx
- gtx.Constraints = axisConstraints(f.Axis, 0, remaining, crossMin, crossMax)
- dims := child.widget(gtx)
+ cgtx.Constraints = f.Axis.constraints(0, remaining, crossMin, crossMax)
+ dims := child.widget(cgtx)
c := macro.Stop()
- sz := axisMain(f.Axis, dims.Size)
+ sz := f.Axis.Convert(dims.Size).X
size += sz
remaining -= sz
if remaining < 0 {
@@ -129,11 +130,10 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
}
}
macro := op.Record(gtx.Ops)
- gtx := gtx
- gtx.Constraints = axisConstraints(f.Axis, flexSize, flexSize, crossMin, crossMax)
- dims := child.widget(gtx)
+ cgtx.Constraints = f.Axis.constraints(flexSize, flexSize, crossMin, crossMax)
+ dims := child.widget(cgtx)
c := macro.Stop()
- sz := axisMain(f.Axis, dims.Size)
+ sz := f.Axis.Convert(dims.Size).X
size += sz
remaining -= sz
if remaining < 0 {
@@ -145,7 +145,7 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
var maxCross int
var maxBaseline int
for _, child := range children {
- if c := axisCross(f.Axis, child.dims.Size); c > maxCross {
+ if c := f.Axis.Convert(child.dims.Size).Y; c > maxCross {
maxCross = c
}
if b := child.dims.Size.Y - child.dims.Baseline; b > maxBaseline {
@@ -165,7 +165,9 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
case SpaceEvenly:
mainSize += space / (1 + len(children))
case SpaceAround:
- mainSize += space / (len(children) * 2)
+ if len(children) > 0 {
+ mainSize += space / (len(children) * 2)
+ }
}
for i, child := range children {
dims := child.dims
@@ -173,27 +175,31 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
var cross int
switch f.Alignment {
case End:
- cross = maxCross - axisCross(f.Axis, dims.Size)
+ cross = maxCross - f.Axis.Convert(dims.Size).Y
case Middle:
- cross = (maxCross - axisCross(f.Axis, dims.Size)) / 2
+ cross = (maxCross - f.Axis.Convert(dims.Size).Y) / 2
case Baseline:
if f.Axis == Horizontal {
cross = maxBaseline - b
}
}
- stack := op.Push(gtx.Ops)
- op.Offset(FPt(axisPoint(f.Axis, mainSize, cross))).Add(gtx.Ops)
+ pt := f.Axis.Convert(image.Pt(mainSize, cross))
+ trans := op.Offset(FPt(pt)).Push(gtx.Ops)
child.call.Add(gtx.Ops)
- stack.Pop()
- mainSize += axisMain(f.Axis, dims.Size)
+ trans.Pop()
+ mainSize += f.Axis.Convert(dims.Size).X
if i < len(children)-1 {
switch f.Spacing {
case SpaceEvenly:
mainSize += space / (1 + len(children))
case SpaceAround:
- mainSize += space / len(children)
+ if len(children) > 0 {
+ mainSize += space / len(children)
+ }
case SpaceBetween:
- mainSize += space / (len(children) - 1)
+ if len(children) > 1 {
+ mainSize += space / (len(children) - 1)
+ }
}
}
}
@@ -205,60 +211,14 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
case SpaceEvenly:
mainSize += space / (1 + len(children))
case SpaceAround:
- mainSize += space / (len(children) * 2)
+ if len(children) > 0 {
+ mainSize += space / (len(children) * 2)
+ }
}
- sz := axisPoint(f.Axis, mainSize, maxCross)
+ sz := f.Axis.Convert(image.Pt(mainSize, maxCross))
return Dimensions{Size: sz, Baseline: sz.Y - maxBaseline}
}
-func axisPoint(a Axis, main, cross int) image.Point {
- if a == Horizontal {
- return image.Point{main, cross}
- } else {
- return image.Point{cross, main}
- }
-}
-
-func axisMain(a Axis, sz image.Point) int {
- if a == Horizontal {
- return sz.X
- } else {
- return sz.Y
- }
-}
-
-func axisCross(a Axis, sz image.Point) int {
- if a == Horizontal {
- return sz.Y
- } else {
- return sz.X
- }
-}
-
-func axisMainConstraint(a Axis, cs Constraints) (int, int) {
- if a == Horizontal {
- return cs.Min.X, cs.Max.X
- } else {
- return cs.Min.Y, cs.Max.Y
- }
-}
-
-func axisCrossConstraint(a Axis, cs Constraints) (int, int) {
- if a == Horizontal {
- return cs.Min.Y, cs.Max.Y
- } else {
- return cs.Min.X, cs.Max.X
- }
-}
-
-func axisConstraints(a Axis, mainMin, mainMax, crossMin, crossMax int) Constraints {
- if a == Horizontal {
- return Constraints{Min: image.Pt(mainMin, crossMin), Max: image.Pt(mainMax, crossMax)}
- } else {
- return Constraints{Min: image.Pt(crossMin, mainMin), Max: image.Pt(crossMax, mainMax)}
- }
-}
-
func (s Spacing) String() string {
switch s {
case SpaceEnd:
diff --git a/vendor/gioui.org/layout/layout.go b/vendor/gioui.org/layout/layout.go
index 112c45d..2318c22 100644
--- a/vendor/gioui.org/layout/layout.go
+++ b/vendor/gioui.org/layout/layout.go
@@ -23,6 +23,10 @@ type Constraints struct {
}
// Dimensions are the resolved size and baseline for a widget.
+//
+// Baseline is the distance from the bottom of a widget to the baseline of
+// any text it contains (or 0). The purpose is to be able to align text
+// that span multiple widgets.
type Dimensions struct {
Size image.Point
Baseline int
@@ -105,7 +109,9 @@ func (c Constraints) Constrain(size image.Point) image.Point {
return size
}
-// Inset adds space around a widget.
+// Inset adds space around a widget by decreasing its maximum
+// constraints. The minimum constraints will be adjusted to ensure
+// they do not exceed the maximum.
type Inset struct {
Top, Right, Bottom, Left unit.Value
}
@@ -135,11 +141,10 @@ func (in Inset) Layout(gtx Context, w Widget) Dimensions {
if mcs.Min.Y > mcs.Max.Y {
mcs.Min.Y = mcs.Max.Y
}
- stack := op.Push(gtx.Ops)
- op.Offset(FPt(image.Point{X: left, Y: top})).Add(gtx.Ops)
gtx.Constraints = mcs
+ trans := op.Offset(FPt(image.Point{X: left, Y: top})).Push(gtx.Ops)
dims := w(gtx)
- stack.Pop()
+ trans.Pop()
return Dimensions{
Size: dims.Size.Add(image.Point{X: right + left, Y: top + bottom}),
Baseline: dims.Baseline + bottom,
@@ -153,39 +158,70 @@ func UniformInset(v unit.Value) Inset {
}
// Layout a widget according to the direction.
-func (a Direction) Layout(gtx Context, w Widget) Dimensions {
+// The widget is called with the context constraints minimum cleared.
+func (d Direction) Layout(gtx Context, w Widget) Dimensions {
macro := op.Record(gtx.Ops)
- cs := gtx.Constraints
- gtx.Constraints.Min = image.Point{}
+ csn := gtx.Constraints.Min
+ switch d {
+ case N, S:
+ gtx.Constraints.Min.Y = 0
+ case E, W:
+ gtx.Constraints.Min.X = 0
+ default:
+ gtx.Constraints.Min = image.Point{}
+ }
dims := w(gtx)
call := macro.Stop()
sz := dims.Size
- if sz.X < cs.Min.X {
- sz.X = cs.Min.X
+ if sz.X < csn.X {
+ sz.X = csn.X
+ }
+ if sz.Y < csn.Y {
+ sz.Y = csn.Y
}
- if sz.Y < cs.Min.Y {
- sz.Y = cs.Min.Y
+
+ p := d.Position(dims.Size, sz)
+ defer op.Offset(FPt(p)).Push(gtx.Ops).Pop()
+ call.Add(gtx.Ops)
+
+ return Dimensions{
+ Size: sz,
+ Baseline: dims.Baseline + sz.Y - dims.Size.Y - p.Y,
}
+}
+
+// Position calculates widget position according to the direction.
+func (d Direction) Position(widget, bounds image.Point) image.Point {
var p image.Point
- switch Direction(a) {
+
+ switch d {
case N, S, Center:
- p.X = (sz.X - dims.Size.X) / 2
+ p.X = (bounds.X - widget.X) / 2
case NE, SE, E:
- p.X = sz.X - dims.Size.X
+ p.X = bounds.X - widget.X
}
- switch Direction(a) {
+
+ switch d {
case W, Center, E:
- p.Y = (sz.Y - dims.Size.Y) / 2
+ p.Y = (bounds.Y - widget.Y) / 2
case SW, S, SE:
- p.Y = sz.Y - dims.Size.Y
+ p.Y = bounds.Y - widget.Y
}
- stack := op.Push(gtx.Ops)
- op.Offset(FPt(p)).Add(gtx.Ops)
- call.Add(gtx.Ops)
- stack.Pop()
+
+ return p
+}
+
+// Spacer adds space between widgets.
+type Spacer struct {
+ Width, Height unit.Value
+}
+
+func (s Spacer) Layout(gtx Context) Dimensions {
return Dimensions{
- Size: sz,
- Baseline: dims.Baseline + sz.Y - dims.Size.Y - p.Y,
+ Size: image.Point{
+ X: gtx.Px(s.Width),
+ Y: gtx.Px(s.Height),
+ },
}
}
@@ -204,6 +240,50 @@ func (a Alignment) String() string {
}
}
+// Convert a point in (x, y) coordinates to (main, cross) coordinates,
+// or vice versa. Specifically, Convert((x, y)) returns (x, y) unchanged
+// for the horizontal axis, or (y, x) for the vertical axis.
+func (a Axis) Convert(pt image.Point) image.Point {
+ if a == Horizontal {
+ return pt
+ }
+ return image.Pt(pt.Y, pt.X)
+}
+
+// FConvert a point in (x, y) coordinates to (main, cross) coordinates,
+// or vice versa. Specifically, FConvert((x, y)) returns (x, y) unchanged
+// for the horizontal axis, or (y, x) for the vertical axis.
+func (a Axis) FConvert(pt f32.Point) f32.Point {
+ if a == Horizontal {
+ return pt
+ }
+ return f32.Pt(pt.Y, pt.X)
+}
+
+// mainConstraint returns the min and max main constraints for axis a.
+func (a Axis) mainConstraint(cs Constraints) (int, int) {
+ if a == Horizontal {
+ return cs.Min.X, cs.Max.X
+ }
+ return cs.Min.Y, cs.Max.Y
+}
+
+// crossConstraint returns the min and max cross constraints for axis a.
+func (a Axis) crossConstraint(cs Constraints) (int, int) {
+ if a == Horizontal {
+ return cs.Min.Y, cs.Max.Y
+ }
+ return cs.Min.X, cs.Max.X
+}
+
+// constraints returns the constraints for axis a.
+func (a Axis) constraints(mainMin, mainMax, crossMin, crossMax int) Constraints {
+ if a == Horizontal {
+ return Constraints{Min: image.Pt(mainMin, crossMin), Max: image.Pt(mainMax, crossMax)}
+ }
+ return Constraints{Min: image.Pt(crossMin, mainMin), Max: image.Pt(crossMax, mainMax)}
+}
+
func (a Axis) String() string {
switch a {
case Horizontal:
diff --git a/vendor/gioui.org/layout/list.go b/vendor/gioui.org/layout/list.go
index 68b63f7..aa78f1b 100644
--- a/vendor/gioui.org/layout/list.go
+++ b/vendor/gioui.org/layout/list.go
@@ -6,7 +6,6 @@ import (
"image"
"gioui.org/gesture"
- "gioui.org/io/pointer"
"gioui.org/op"
"gioui.org/op/clip"
)
@@ -29,15 +28,13 @@ type List struct {
// Alignment is the cross axis alignment of list elements.
Alignment Alignment
- ctx Context
- macro op.MacroOp
- child op.MacroOp
+ cs Constraints
scroll gesture.Scroll
scrollDelta int
// Position is updated during Layout. To save the list scroll position,
// just save Position after Layout finishes. To scroll the list
- // programatically, update Position (e.g. restore it from a saved value)
+ // programmatically, update Position (e.g. restore it from a saved value)
// before calling Layout.
Position Position
@@ -71,6 +68,13 @@ type Position struct {
// Offset is the distance in pixels from the top edge to the child at index
// First.
Offset int
+ // OffsetLast is the signed distance in pixels from the bottom edge to the
+ // bottom edge of the child at index First+Count.
+ OffsetLast int
+ // Count is the number of visible children.
+ Count int
+ // Length is the estimated total size of all children, measured in pixels.
+ Length int
}
const (
@@ -86,29 +90,41 @@ func (l *List) init(gtx Context, len int) {
if l.more() {
panic("unfinished child")
}
- l.ctx = gtx
+ l.cs = gtx.Constraints
l.maxSize = 0
l.children = l.children[:0]
l.len = len
- l.update()
+ l.update(gtx)
if l.scrollToEnd() || l.Position.First > len {
l.Position.Offset = 0
l.Position.First = len
}
- l.macro = op.Record(gtx.Ops)
- l.next()
}
// Layout the List.
func (l *List) Layout(gtx Context, len int, w ListElement) Dimensions {
- for l.init(gtx, len); l.more(); l.next() {
- crossMin, crossMax := axisCrossConstraint(l.Axis, l.ctx.Constraints)
- cs := axisConstraints(l.Axis, 0, inf, crossMin, crossMax)
- i := l.index()
- gtx.Constraints = cs
- l.end(w(gtx, i))
+ l.init(gtx, len)
+ crossMin, crossMax := l.Axis.crossConstraint(gtx.Constraints)
+ gtx.Constraints = l.Axis.constraints(0, inf, crossMin, crossMax)
+ macro := op.Record(gtx.Ops)
+ laidOutTotalLength := 0
+ numLaidOut := 0
+
+ for l.next(); l.more(); l.next() {
+ child := op.Record(gtx.Ops)
+ dims := w(gtx, l.index())
+ call := child.Stop()
+ l.end(dims, call)
+ laidOutTotalLength += l.Axis.Convert(dims.Size).X
+ numLaidOut++
+ }
+
+ if numLaidOut > 0 {
+ l.Position.Length = laidOutTotalLength * len / numLaidOut
+ } else {
+ l.Position.Length = 0
}
- return l.layout()
+ return l.layout(gtx.Ops, macro)
}
func (l *List) scrollToEnd() bool {
@@ -120,8 +136,8 @@ func (l *List) Dragging() bool {
return l.scroll.State() == gesture.StateDragging
}
-func (l *List) update() {
- d := l.scroll.Scroll(l.ctx.Metric, l.ctx, l.ctx.Now, gesture.Axis(l.Axis))
+func (l *List) update(gtx Context) {
+ d := l.scroll.Scroll(gtx.Metric, gtx, gtx.Now, gesture.Axis(l.Axis))
l.scrollDelta = d
l.Position.Offset += d
}
@@ -136,9 +152,6 @@ func (l *List) next() {
l.Position.Offset += l.scrollDelta
l.dir = l.nextDir()
}
- if l.more() {
- l.child = op.Record(l.ctx.Ops)
- }
}
// index is current child's position in the underlying list.
@@ -159,7 +172,7 @@ func (l *List) more() bool {
}
func (l *List) nextDir() iterationDir {
- _, vsize := axisMainConstraint(l.Axis, l.ctx.Constraints)
+ _, vsize := l.Axis.mainConstraint(l.cs)
last := l.Position.First + len(l.children)
// Clamp offset.
if l.maxSize-l.Position.Offset < vsize && last == l.len {
@@ -180,16 +193,17 @@ func (l *List) nextDir() iterationDir {
}
// End the current child by specifying its dimensions.
-func (l *List) end(dims Dimensions) {
- call := l.child.Stop()
+func (l *List) end(dims Dimensions, call op.CallOp) {
child := scrollChild{dims.Size, call}
- mainSize := axisMain(l.Axis, child.size)
+ mainSize := l.Axis.Convert(child.size).X
l.maxSize += mainSize
switch l.dir {
case iterateForward:
l.children = append(l.children, child)
case iterateBackward:
- l.children = append([]scrollChild{child}, l.children...)
+ l.children = append(l.children, scrollChild{})
+ copy(l.children[1:], l.children)
+ l.children[0] = child
l.Position.First--
l.Position.Offset += mainSize
default:
@@ -199,17 +213,18 @@ func (l *List) end(dims Dimensions) {
}
// Layout the List and return its dimensions.
-func (l *List) layout() Dimensions {
+func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
if l.more() {
panic("unfinished child")
}
- mainMin, mainMax := axisMainConstraint(l.Axis, l.ctx.Constraints)
+ mainMin, mainMax := l.Axis.mainConstraint(l.cs)
children := l.children
// Skip invisible children
for len(children) > 0 {
sz := children[0].size
- mainSize := axisMain(l.Axis, sz)
- if l.Position.Offset <= mainSize {
+ mainSize := l.Axis.Convert(sz).X
+ if l.Position.Offset < mainSize {
+ // First child is partially visible.
break
}
l.Position.First++
@@ -219,32 +234,33 @@ func (l *List) layout() Dimensions {
size := -l.Position.Offset
var maxCross int
for i, child := range children {
- sz := child.size
- if c := axisCross(l.Axis, sz); c > maxCross {
+ sz := l.Axis.Convert(child.size)
+ if c := sz.Y; c > maxCross {
maxCross = c
}
- size += axisMain(l.Axis, sz)
+ size += sz.X
if size >= mainMax {
children = children[:i+1]
break
}
}
- ops := l.ctx.Ops
+ l.Position.Count = len(children)
+ l.Position.OffsetLast = mainMax - size
pos := -l.Position.Offset
// ScrollToEnd lists are end aligned.
- if space := mainMax - size; l.ScrollToEnd && space > 0 {
+ if space := l.Position.OffsetLast; l.ScrollToEnd && space > 0 {
pos += space
}
for _, child := range children {
- sz := child.size
+ sz := l.Axis.Convert(child.size)
var cross int
switch l.Alignment {
case End:
- cross = maxCross - axisCross(l.Axis, sz)
+ cross = maxCross - sz.Y
case Middle:
- cross = (maxCross - axisCross(l.Axis, sz)) / 2
+ cross = (maxCross - sz.Y) / 2
}
- childSize := axisMain(l.Axis, sz)
+ childSize := sz.X
max := childSize + pos
if max > mainMax {
max = mainMax
@@ -254,14 +270,15 @@ func (l *List) layout() Dimensions {
min = 0
}
r := image.Rectangle{
- Min: axisPoint(l.Axis, min, -inf),
- Max: axisPoint(l.Axis, max, inf),
+ Min: l.Axis.Convert(image.Pt(min, -inf)),
+ Max: l.Axis.Convert(image.Pt(max, inf)),
}
- stack := op.Push(ops)
- clip.Rect{Rect: FRect(r)}.Op(ops).Add(ops)
- op.Offset(FPt(axisPoint(l.Axis, pos, cross))).Add(ops)
+ cl := clip.Rect(r).Push(ops)
+ pt := l.Axis.Convert(image.Pt(pos, cross))
+ trans := op.Offset(FPt(pt)).Push(ops)
child.call.Add(ops)
- stack.Pop()
+ trans.Pop()
+ cl.Pop()
pos += childSize
}
atStart := l.Position.First == 0 && l.Position.Offset <= 0
@@ -276,11 +293,33 @@ func (l *List) layout() Dimensions {
if pos > mainMax {
pos = mainMax
}
- dims := axisPoint(l.Axis, pos, maxCross)
- call := l.macro.Stop()
- defer op.Push(l.ctx.Ops).Pop()
- pointer.Rect(image.Rectangle{Max: dims}).Add(ops)
- l.scroll.Add(ops)
+ if crossMin, crossMax := l.Axis.crossConstraint(l.cs); maxCross < crossMin {
+ maxCross = crossMin
+ } else if maxCross > crossMax {
+ maxCross = crossMax
+ }
+ dims := l.Axis.Convert(image.Pt(pos, maxCross))
+ call := macro.Stop()
+ defer clip.Rect(image.Rectangle{Max: dims}).Push(ops).Pop()
+
+ var min, max int
+ if o := l.Position.Offset; o > 0 {
+ // Use the size of the invisible part as scroll boundary.
+ min = -o
+ } else if l.Position.First > 0 {
+ min = -inf
+ }
+ if o := l.Position.OffsetLast; o < 0 {
+ max = -o
+ } else if l.Position.First+l.Position.Count < l.len {
+ max = inf
+ }
+ scrollRange := image.Rectangle{
+ Min: l.Axis.Convert(image.Pt(min, 0)),
+ Max: l.Axis.Convert(image.Pt(max, 0)),
+ }
+ l.scroll.Add(ops, scrollRange)
+
call.Add(ops)
return Dimensions{Size: dims}
}
diff --git a/vendor/gioui.org/layout/stack.go b/vendor/gioui.org/layout/stack.go
index 0de43df..fb8b8ac 100644
--- a/vendor/gioui.org/layout/stack.go
+++ b/vendor/gioui.org/layout/stack.go
@@ -50,14 +50,14 @@ func Expanded(w Widget) StackChild {
func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
var maxSZ image.Point
// First lay out Stacked children.
+ cgtx := gtx
+ cgtx.Constraints.Min = image.Point{}
for i, w := range children {
if w.expanded {
continue
}
macro := op.Record(gtx.Ops)
- gtx := gtx
- gtx.Constraints.Min = image.Pt(0, 0)
- dims := w.widget(gtx)
+ dims := w.widget(cgtx)
call := macro.Stop()
if w := dims.Size.X; w > maxSZ.X {
maxSZ.X = w
@@ -74,11 +74,8 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
continue
}
macro := op.Record(gtx.Ops)
- gtx := gtx
- gtx.Constraints = Constraints{
- Min: maxSZ, Max: gtx.Constraints.Max,
- }
- dims := w.widget(gtx)
+ cgtx.Constraints.Min = maxSZ
+ dims := w.widget(cgtx)
call := macro.Stop()
if w := dims.Size.X; w > maxSZ.X {
maxSZ.X = w
@@ -107,10 +104,9 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
case SW, S, SE:
p.Y = maxSZ.Y - sz.Y
}
- stack := op.Push(gtx.Ops)
- op.Offset(FPt(p)).Add(gtx.Ops)
+ trans := op.Offset(FPt(p)).Push(gtx.Ops)
ch.call.Add(gtx.Ops)
- stack.Pop()
+ trans.Pop()
if baseline == 0 {
if b := ch.dims.Baseline; b != 0 {
baseline = b + maxSZ.Y - sz.Y - p.Y
diff --git a/vendor/gioui.org/op/clip/clip.go b/vendor/gioui.org/op/clip/clip.go
index 59554b5..5b4dad0 100644
--- a/vendor/gioui.org/op/clip/clip.go
+++ b/vendor/gioui.org/op/clip/clip.go
@@ -4,85 +4,240 @@ package clip
import (
"encoding/binary"
+ "hash/maphash"
"image"
"math"
"gioui.org/f32"
- "gioui.org/internal/opconst"
"gioui.org/internal/ops"
+ "gioui.org/internal/scene"
+ "gioui.org/internal/stroke"
"gioui.org/op"
)
+// Op represents a clip area. Op intersects the current clip area with
+// itself.
+type Op struct {
+ path PathSpec
+
+ outline bool
+ width float32
+}
+
+// Stack represents an Op pushed on the clip stack.
+type Stack struct {
+ ops *ops.Ops
+ id ops.StackID
+ macroID int
+}
+
+var pathSeed maphash.Seed
+
+func init() {
+ pathSeed = maphash.MakeSeed()
+}
+
+// Push saves the current clip state on the stack and updates the current
+// state to the intersection of the current p.
+func (p Op) Push(o *op.Ops) Stack {
+ id, macroID := ops.PushOp(&o.Internal, ops.ClipStack)
+ p.add(o)
+ return Stack{ops: &o.Internal, id: id, macroID: macroID}
+}
+
+func (p Op) add(o *op.Ops) {
+ path := p.path
+
+ bo := binary.LittleEndian
+ if path.hasSegments {
+ data := ops.Write(&o.Internal, ops.TypePathLen)
+ data[0] = byte(ops.TypePath)
+ bo.PutUint64(data[1:], path.hash)
+ path.spec.Add(o)
+ }
+
+ bounds := path.bounds
+ if p.width > 0 {
+ // Expand bounds to cover stroke.
+ half := int(p.width*.5 + .5)
+ bounds.Min.X -= half
+ bounds.Min.Y -= half
+ bounds.Max.X += half
+ bounds.Max.Y += half
+ data := ops.Write(&o.Internal, ops.TypeStrokeLen)
+ data[0] = byte(ops.TypeStroke)
+ bo := binary.LittleEndian
+ bo.PutUint32(data[1:], math.Float32bits(p.width))
+ }
+
+ data := ops.Write(&o.Internal, ops.TypeClipLen)
+ data[0] = byte(ops.TypeClip)
+ bo.PutUint32(data[1:], uint32(bounds.Min.X))
+ bo.PutUint32(data[5:], uint32(bounds.Min.Y))
+ bo.PutUint32(data[9:], uint32(bounds.Max.X))
+ bo.PutUint32(data[13:], uint32(bounds.Max.Y))
+ if p.outline {
+ data[17] = byte(1)
+ }
+ data[18] = byte(path.shape)
+}
+
+func (s Stack) Pop() {
+ ops.PopOp(s.ops, ops.ClipStack, s.id, s.macroID)
+ data := ops.Write(s.ops, ops.TypePopClipLen)
+ data[0] = byte(ops.TypePopClip)
+}
+
+type PathSpec struct {
+ spec op.CallOp
+ // hasSegments tracks whether there are any segments in the path.
+ hasSegments bool
+ bounds image.Rectangle
+ shape ops.Shape
+ hash uint64
+}
+
// Path constructs a Op clip path described by lines and
// Bézier curves, where drawing outside the Path is discarded.
-// The inside-ness of a pixel is determines by the even-odd rule,
+// The inside-ness of a pixel is determines by the non-zero winding rule,
// similar to the SVG rule of the same name.
//
// Path generates no garbage and can be used for dynamic paths; path
// data is stored directly in the Ops list supplied to Begin.
type Path struct {
- ops *op.Ops
- contour int
- pen f32.Point
- macro op.MacroOp
- start f32.Point
+ ops *ops.Ops
+ contour int
+ pen f32.Point
+ macro op.MacroOp
+ start f32.Point
+ hasSegments bool
+ bounds f32.Rectangle
+ hash maphash.Hash
}
-// Op sets the current clip to the intersection of
-// the existing clip with this clip.
-//
-// If you need to reset the clip to its previous values after
-// applying a Op, use op.StackOp.
-type Op struct {
- call op.CallOp
- bounds f32.Rectangle
+// Pos returns the current pen position.
+func (p *Path) Pos() f32.Point { return p.pen }
+
+// Begin the path, storing the path data and final Op into ops.
+func (p *Path) Begin(o *op.Ops) {
+ *p = Path{
+ ops: &o.Internal,
+ macro: op.Record(o),
+ contour: 1,
+ }
+ p.hash.SetSeed(pathSeed)
+ data := ops.Write(p.ops, ops.TypeAuxLen)
+ data[0] = byte(ops.TypeAux)
}
-func (p Op) Add(o *op.Ops) {
- p.call.Add(o)
- data := o.Write(opconst.TypeClipLen)
- data[0] = byte(opconst.TypeClip)
- bo := binary.LittleEndian
- bo.PutUint32(data[1:], math.Float32bits(p.bounds.Min.X))
- bo.PutUint32(data[5:], math.Float32bits(p.bounds.Min.Y))
- bo.PutUint32(data[9:], math.Float32bits(p.bounds.Max.X))
- bo.PutUint32(data[13:], math.Float32bits(p.bounds.Max.Y))
+// End returns a PathSpec ready to use in clipping operations.
+func (p *Path) End() PathSpec {
+ p.gap()
+ c := p.macro.Stop()
+ return PathSpec{
+ spec: c,
+ hasSegments: p.hasSegments,
+ bounds: boundRectF(p.bounds),
+ hash: p.hash.Sum64(),
+ }
}
-// Begin the path, storing the path data and final Op into ops.
-func (p *Path) Begin(ops *op.Ops) {
- p.ops = ops
- p.macro = op.Record(ops)
- // Write the TypeAux opcode
- data := ops.Write(opconst.TypeAuxLen)
- data[0] = byte(opconst.TypeAux)
+// Move moves the pen by the amount specified by delta.
+func (p *Path) Move(delta f32.Point) {
+ to := delta.Add(p.pen)
+ p.MoveTo(to)
}
-// MoveTo moves the pen to the given position.
-func (p *Path) Move(to f32.Point) {
- to = to.Add(p.pen)
+// MoveTo moves the pen to the specified absolute coordinate.
+func (p *Path) MoveTo(to f32.Point) {
+ if p.pen == to {
+ return
+ }
+ p.gap()
p.end()
p.pen = to
p.start = to
}
-// end completes the current contour.
-func (p *Path) end() {
+func (p *Path) gap() {
if p.pen != p.start {
- p.lineTo(p.start)
+ // A closed contour starts and ends in the same point.
+ // This move creates a gap in the contour, register it.
+ data := ops.Write(p.ops, scene.CommandSize+4)
+ bo := binary.LittleEndian
+ bo.PutUint32(data[0:], uint32(p.contour))
+ p.cmd(data[4:], scene.Gap(p.pen, p.start))
}
+}
+
+// end completes the current contour.
+func (p *Path) end() {
p.contour++
}
// Line moves the pen by the amount specified by delta, recording a line.
func (p *Path) Line(delta f32.Point) {
to := delta.Add(p.pen)
- p.lineTo(to)
+ p.LineTo(to)
}
-func (p *Path) lineTo(to f32.Point) {
- // Model lines as degenerate quadratic Béziers.
- p.quadTo(to.Add(p.pen).Mul(.5), to)
+// LineTo moves the pen to the absolute point specified, recording a line.
+func (p *Path) LineTo(to f32.Point) {
+ data := ops.Write(p.ops, scene.CommandSize+4)
+ bo := binary.LittleEndian
+ bo.PutUint32(data[0:], uint32(p.contour))
+ p.cmd(data[4:], scene.Line(p.pen, to))
+ p.pen = to
+ p.expand(to)
+}
+
+func (p *Path) cmd(data []byte, c scene.Command) {
+ ops.EncodeCommand(data, c)
+ p.hash.Write(data)
+}
+
+func (p *Path) expand(pt f32.Point) {
+ if !p.hasSegments {
+ p.hasSegments = true
+ p.bounds = f32.Rectangle{Min: pt, Max: pt}
+ } else {
+ b := p.bounds
+ if pt.X < b.Min.X {
+ b.Min.X = pt.X
+ }
+ if pt.Y < b.Min.Y {
+ b.Min.Y = pt.Y
+ }
+ if pt.X > b.Max.X {
+ b.Max.X = pt.X
+ }
+ if pt.Y > b.Max.Y {
+ b.Max.Y = pt.Y
+ }
+ p.bounds = b
+ }
+}
+
+// boundRectF returns a bounding image.Rectangle for a f32.Rectangle.
+func boundRectF(r f32.Rectangle) image.Rectangle {
+ return image.Rectangle{
+ Min: image.Point{
+ X: int(floor(r.Min.X)),
+ Y: int(floor(r.Min.Y)),
+ },
+ Max: image.Point{
+ X: int(ceil(r.Max.X)),
+ Y: int(ceil(r.Max.Y)),
+ },
+ }
+}
+
+func ceil(v float32) int {
+ return int(math.Ceil(float64(v)))
+}
+
+func floor(v float32) int {
+ return int(math.Floor(float64(v)))
}
// Quad records a quadratic Bézier from the pen to end
@@ -90,174 +245,101 @@ func (p *Path) lineTo(to f32.Point) {
func (p *Path) Quad(ctrl, to f32.Point) {
ctrl = ctrl.Add(p.pen)
to = to.Add(p.pen)
- p.quadTo(ctrl, to)
+ p.QuadTo(ctrl, to)
}
-func (p *Path) quadTo(ctrl, to f32.Point) {
- data := p.ops.Write(ops.QuadSize + 4)
+// QuadTo records a quadratic Bézier from the pen to end
+// with the control point ctrl, with absolute coordinates.
+func (p *Path) QuadTo(ctrl, to f32.Point) {
+ data := ops.Write(p.ops, scene.CommandSize+4)
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
- ops.EncodeQuad(data[4:], ops.Quad{
- From: p.pen,
- Ctrl: ctrl,
- To: to,
- })
+ p.cmd(data[4:], scene.Quad(p.pen, ctrl, to))
p.pen = to
+ p.expand(ctrl)
+ p.expand(to)
+}
+
+// ArcTo adds an elliptical arc to the path. The implied ellipse is defined
+// by its focus points f1 and f2.
+// The arc starts in the current point and ends angle radians along the ellipse boundary.
+// The sign of angle determines the direction; positive being counter-clockwise,
+// negative clockwise.
+func (p *Path) ArcTo(f1, f2 f32.Point, angle float32) {
+ const segments = 16
+ m := stroke.ArcTransform(p.pen, f1, f2, angle, segments)
+
+ for i := 0; i < segments; i++ {
+ p0 := p.pen
+ p1 := m.Transform(p0)
+ p2 := m.Transform(p1)
+ ctl := p1.Mul(2).Sub(p0.Add(p2).Mul(.5))
+ p.QuadTo(ctl, p2)
+ }
+}
+
+// Arc is like ArcTo where f1 and f2 are relative to the current position.
+func (p *Path) Arc(f1, f2 f32.Point, angle float32) {
+ f1 = f1.Add(p.pen)
+ f2 = f2.Add(p.pen)
+ p.ArcTo(f1, f2, angle)
}
// Cube records a cubic Bézier from the pen through
// two control points ending in to.
func (p *Path) Cube(ctrl0, ctrl1, to f32.Point) {
- ctrl0 = ctrl0.Add(p.pen)
- ctrl1 = ctrl1.Add(p.pen)
- to = to.Add(p.pen)
- // Set the maximum distance proportionally to the longest side
- // of the bounding rectangle.
- hull := f32.Rectangle{
- Min: p.pen,
- Max: ctrl0,
- }.Canon().Add(ctrl1).Add(to)
- l := hull.Dx()
- if h := hull.Dy(); h > l {
- l = h
- }
- p.approxCubeTo(0, l*0.001, ctrl0, ctrl1, to)
-}
-
-// approxCube approximates a cubic Bézier by a series of quadratic
-// curves.
-func (p *Path) approxCubeTo(splits int, maxDist float32, ctrl0, ctrl1, to f32.Point) int {
- // The idea is from
- // https://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
- // where a quadratic approximates a cubic by eliminating its t³ term
- // from its polynomial expression anchored at the starting point:
- //
- // P(t) = pen + 3t(ctrl0 - pen) + 3t²(ctrl1 - 2ctrl0 + pen) + t³(to - 3ctrl1 + 3ctrl0 - pen)
- //
- // The control point for the new quadratic Q1 that shares starting point, pen, with P is
- //
- // C1 = (3ctrl0 - pen)/2
- //
- // The reverse cubic anchored at the end point has the polynomial
- //
- // P'(t) = to + 3t(ctrl1 - to) + 3t²(ctrl0 - 2ctrl1 + to) + t³(pen - 3ctrl0 + 3ctrl1 - to)
- //
- // The corresponding quadratic Q2 that shares the end point, to, with P has control
- // point
- //
- // C2 = (3ctrl1 - to)/2
- //
- // The combined quadratic Bézier, Q, shares both start and end points with its cubic
- // and use the midpoint between the two curves Q1 and Q2 as control point:
- //
- // C = (3ctrl0 - pen + 3ctrl1 - to)/4
- c := ctrl0.Mul(3).Sub(p.pen).Add(ctrl1.Mul(3)).Sub(to).Mul(1.0 / 4.0)
- const maxSplits = 32
- if splits >= maxSplits {
- p.quadTo(c, to)
- return splits
+ p.CubeTo(p.pen.Add(ctrl0), p.pen.Add(ctrl1), p.pen.Add(to))
+}
+
+// CubeTo records a cubic Bézier from the pen through
+// two control points ending in to, with absolute coordinates.
+func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) {
+ if ctrl0 == p.pen && ctrl1 == p.pen && to == p.pen {
+ return
}
- // The maximum distance between the cubic P and its approximation Q given t
- // can be shown to be
- //
- // d = sqrt(3)/36*|to - 3ctrl1 + 3ctrl0 - pen|
- //
- // To save a square root, compare d² with the squared tolerance.
- v := to.Sub(ctrl1.Mul(3)).Add(ctrl0.Mul(3)).Sub(p.pen)
- d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36)
- if d2 <= maxDist*maxDist {
- p.quadTo(c, to)
- return splits
+ data := ops.Write(p.ops, scene.CommandSize+4)
+ bo := binary.LittleEndian
+ bo.PutUint32(data[0:], uint32(p.contour))
+ p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))
+ p.pen = to
+ p.expand(ctrl0)
+ p.expand(ctrl1)
+ p.expand(to)
+}
+
+// Close closes the path contour.
+func (p *Path) Close() {
+ if p.pen != p.start {
+ p.LineTo(p.start)
}
- // De Casteljau split the curve and approximate the halves.
- t := float32(0.5)
- c0 := p.pen.Add(ctrl0.Sub(p.pen).Mul(t))
- c1 := ctrl0.Add(ctrl1.Sub(ctrl0).Mul(t))
- c2 := ctrl1.Add(to.Sub(ctrl1).Mul(t))
- c01 := c0.Add(c1.Sub(c0).Mul(t))
- c12 := c1.Add(c2.Sub(c1).Mul(t))
- c0112 := c01.Add(c12.Sub(c01).Mul(t))
- splits++
- splits = p.approxCubeTo(splits, maxDist, c0, c01, c0112)
- splits = p.approxCubeTo(splits, maxDist, c12, c2, to)
- return splits
-}
-
-// End the path and return a clip operation that represents it.
-func (p *Path) End() Op {
p.end()
- c := p.macro.Stop()
+}
+
+// Stroke represents a stroked path.
+type Stroke struct {
+ Path PathSpec
+ // Width of the stroked path.
+ Width float32
+}
+
+// Op returns a clip operation representing the stroke.
+func (s Stroke) Op() Op {
return Op{
- call: c,
+ path: s.Path,
+ width: s.Width,
}
}
-// Rect represents the clip area of a rectangle with rounded
-// corners.The origin is in the upper left
-// corner.
-// Specify a square with corner radii equal to half the square size to
-// construct a circular clip area.
-type Rect struct {
- Rect f32.Rectangle
- // The corner radii.
- SE, SW, NW, NE float32
-}
-
-// Op returns the Op for the rectangle.
-func (rr Rect) Op(ops *op.Ops) Op {
- r := rr.Rect
- // Optimize for the common pixel aligned rectangle with no
- // corner rounding.
- if rr.SE == 0 && rr.SW == 0 && rr.NW == 0 && rr.NE == 0 {
- ri := image.Rectangle{
- Min: image.Point{X: int(r.Min.X), Y: int(r.Min.Y)},
- Max: image.Point{X: int(r.Max.X), Y: int(r.Max.Y)},
- }
- // Optimize pixel-aligned rectangles to just its bounds.
- if r == fRect(ri) {
- return Op{bounds: r}
- }
- }
- return roundRect(ops, r, rr.SE, rr.SW, rr.NW, rr.NE)
-}
-
-// Add is a shorthand for Op(ops).Add(ops).
-func (rr Rect) Add(ops *op.Ops) {
- rr.Op(ops).Add(ops)
-}
-
-// roundRect returns the clip area of a rectangle with rounded
-// corners defined by their radii.
-func roundRect(ops *op.Ops, r f32.Rectangle, se, sw, nw, ne float32) Op {
- size := r.Size()
- // https://pomax.github.io/bezierinfo/#circles_cubic.
- w, h := float32(size.X), float32(size.Y)
- const c = 0.55228475 // 4*(sqrt(2)-1)/3
- var p Path
- p.Begin(ops)
- p.Move(r.Min)
-
- p.Move(f32.Point{X: w, Y: h - se})
- p.Cube(f32.Point{X: 0, Y: se * c}, f32.Point{X: -se + se*c, Y: se}, f32.Point{X: -se, Y: se}) // SE
- p.Line(f32.Point{X: sw - w + se, Y: 0})
- p.Cube(f32.Point{X: -sw * c, Y: 0}, f32.Point{X: -sw, Y: -sw + sw*c}, f32.Point{X: -sw, Y: -sw}) // SW
- p.Line(f32.Point{X: 0, Y: nw - h + sw})
- p.Cube(f32.Point{X: 0, Y: -nw * c}, f32.Point{X: nw - nw*c, Y: -nw}, f32.Point{X: nw, Y: -nw}) // NW
- p.Line(f32.Point{X: w - ne - nw, Y: 0})
- p.Cube(f32.Point{X: ne * c, Y: 0}, f32.Point{X: ne, Y: ne - ne*c}, f32.Point{X: ne, Y: ne}) // NE
- return p.End()
-}
-
-// fRect converts a rectangle to a f32.Rectangle.
-func fRect(r image.Rectangle) f32.Rectangle {
- return f32.Rectangle{
- Min: fPt(r.Min), Max: fPt(r.Max),
- }
+// Outline represents the area inside of a path, according to the
+// non-zero winding rule.
+type Outline struct {
+ Path PathSpec
}
-// fPt converts an point to a f32.Point.
-func fPt(p image.Point) f32.Point {
- return f32.Point{
- X: float32(p.X), Y: float32(p.Y),
+// Op returns a clip operation representing the outline.
+func (o Outline) Op() Op {
+ return Op{
+ path: o.Path,
+ outline: true,
}
}
diff --git a/vendor/gioui.org/op/clip/doc.go b/vendor/gioui.org/op/clip/doc.go
index 6ba5546..894cffd 100644
--- a/vendor/gioui.org/op/clip/doc.go
+++ b/vendor/gioui.org/op/clip/doc.go
@@ -1,16 +1,14 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
-Package clip provides operations for clipping paint operations.
-Drawing outside the current clip area is ignored.
+Package clip provides operations for defining areas that applies to operations
+such as paints and pointer handlers.
-The current clip is initially the infinite set. An Op sets the clip
-to the intersection of the current clip and the clip area it
-represents. If you need to reset the current clip to its value
-before applying an Op, use op.StackOp.
+The current clip is initially the infinite set. Pushing an Op sets the clip
+to the intersection of the current clip and pushed clip area. Popping the
+area restores the clip to its state before pushing.
-General clipping areas are constructed with Path. Simpler special
-cases such as rectangular clip areas also exist as convenient
-constructors.
+General clipping areas are constructed with Path. Common cases such as
+rectangular clip areas also exist as convenient constructors.
*/
package clip
diff --git a/vendor/gioui.org/op/clip/shapes.go b/vendor/gioui.org/op/clip/shapes.go
new file mode 100644
index 0000000..30609e5
--- /dev/null
+++ b/vendor/gioui.org/op/clip/shapes.go
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package clip
+
+import (
+ "image"
+ "math"
+
+ "gioui.org/f32"
+ "gioui.org/internal/ops"
+ "gioui.org/op"
+)
+
+// Rect represents the clip area of a pixel-aligned rectangle.
+type Rect image.Rectangle
+
+// Op returns the op for the rectangle.
+func (r Rect) Op() Op {
+ return Op{
+ outline: true,
+ path: r.Path(),
+ }
+}
+
+// Push the clip operation on the clip stack.
+func (r Rect) Push(ops *op.Ops) Stack {
+ return r.Op().Push(ops)
+}
+
+// Path returns the PathSpec for the rectangle.
+func (r Rect) Path() PathSpec {
+ return PathSpec{
+ shape: ops.Rect,
+ bounds: image.Rectangle(r),
+ }
+}
+
+// UniformRRect returns an RRect with all corner radii set to the
+// provided radius.
+func UniformRRect(rect f32.Rectangle, radius float32) RRect {
+ return RRect{
+ Rect: rect,
+ SE: radius,
+ SW: radius,
+ NE: radius,
+ NW: radius,
+ }
+}
+
+// RRect represents the clip area of a rectangle with rounded
+// corners.
+//
+// Specify a square with corner radii equal to half the square size to
+// construct a circular clip area.
+type RRect struct {
+ Rect f32.Rectangle
+ // The corner radii.
+ SE, SW, NW, NE float32
+}
+
+// Op returns the op for the rounded rectangle.
+func (rr RRect) Op(ops *op.Ops) Op {
+ if rr.SE == 0 && rr.SW == 0 && rr.NW == 0 && rr.NE == 0 {
+ r := image.Rectangle{
+ Min: image.Point{X: int(rr.Rect.Min.X), Y: int(rr.Rect.Min.Y)},
+ Max: image.Point{X: int(rr.Rect.Max.X), Y: int(rr.Rect.Max.Y)},
+ }
+ // Only use Rect if rr is pixel-aligned, as Rect is guaranteed to be.
+ if fPt(r.Min) == rr.Rect.Min && fPt(r.Max) == rr.Rect.Max {
+ return Rect(r).Op()
+ }
+ }
+ return Outline{Path: rr.Path(ops)}.Op()
+}
+
+// Push the rectangle clip on the clip stack.
+func (rr RRect) Push(ops *op.Ops) Stack {
+ return rr.Op(ops).Push(ops)
+}
+
+// Path returns the PathSpec for the rounded rectangle.
+func (rr RRect) Path(ops *op.Ops) PathSpec {
+ var p Path
+ p.Begin(ops)
+
+ // https://pomax.github.io/bezierinfo/#circles_cubic.
+ const q = 4 * (math.Sqrt2 - 1) / 3
+ const iq = 1 - q
+
+ se, sw, nw, ne := rr.SE, rr.SW, rr.NW, rr.NE
+ w, n, e, s := rr.Rect.Min.X, rr.Rect.Min.Y, rr.Rect.Max.X, rr.Rect.Max.Y
+
+ p.MoveTo(f32.Point{X: w + nw, Y: n})
+ p.LineTo(f32.Point{X: e - ne, Y: n}) // N
+ p.CubeTo( // NE
+ f32.Point{X: e - ne*iq, Y: n},
+ f32.Point{X: e, Y: n + ne*iq},
+ f32.Point{X: e, Y: n + ne})
+ p.LineTo(f32.Point{X: e, Y: s - se}) // E
+ p.CubeTo( // SE
+ f32.Point{X: e, Y: s - se*iq},
+ f32.Point{X: e - se*iq, Y: s},
+ f32.Point{X: e - se, Y: s})
+ p.LineTo(f32.Point{X: w + sw, Y: s}) // S
+ p.CubeTo( // SW
+ f32.Point{X: w + sw*iq, Y: s},
+ f32.Point{X: w, Y: s - sw*iq},
+ f32.Point{X: w, Y: s - sw})
+ p.LineTo(f32.Point{X: w, Y: n + nw}) // W
+ p.CubeTo( // NW
+ f32.Point{X: w, Y: n + nw*iq},
+ f32.Point{X: w + nw*iq, Y: n},
+ f32.Point{X: w + nw, Y: n})
+
+ return p.End()
+}
+
+// Ellipse represents the largest axis-aligned ellipse that
+// is contained in its bounds.
+type Ellipse f32.Rectangle
+
+// Op returns the op for the filled ellipse.
+func (e Ellipse) Op(ops *op.Ops) Op {
+ return Outline{Path: e.Path(ops)}.Op()
+}
+
+// Push the filled ellipse clip op on the clip stack.
+func (e Ellipse) Push(ops *op.Ops) Stack {
+ return e.Op(ops).Push(ops)
+}
+
+// Path constructs a path for the ellipse.
+func (e Ellipse) Path(o *op.Ops) PathSpec {
+ bounds := f32.Rectangle(e)
+ if bounds.Dx() == 0 || bounds.Dy() == 0 {
+ return PathSpec{shape: ops.Rect}
+ }
+
+ var p Path
+ p.Begin(o)
+
+ center := bounds.Max.Add(bounds.Min).Mul(.5)
+ diam := bounds.Dx()
+ r := diam * .5
+ // We'll model the ellipse as a circle scaled in the Y
+ // direction.
+ scale := bounds.Dy() / diam
+
+ // https://pomax.github.io/bezierinfo/#circles_cubic.
+ const q = 4 * (math.Sqrt2 - 1) / 3
+
+ curve := r * q
+ top := f32.Point{X: center.X, Y: center.Y - r*scale}
+
+ p.MoveTo(top)
+ p.CubeTo(
+ f32.Point{X: center.X + curve, Y: center.Y - r*scale},
+ f32.Point{X: center.X + r, Y: center.Y - curve*scale},
+ f32.Point{X: center.X + r, Y: center.Y},
+ )
+ p.CubeTo(
+ f32.Point{X: center.X + r, Y: center.Y + curve*scale},
+ f32.Point{X: center.X + curve, Y: center.Y + r*scale},
+ f32.Point{X: center.X, Y: center.Y + r*scale},
+ )
+ p.CubeTo(
+ f32.Point{X: center.X - curve, Y: center.Y + r*scale},
+ f32.Point{X: center.X - r, Y: center.Y + curve*scale},
+ f32.Point{X: center.X - r, Y: center.Y},
+ )
+ p.CubeTo(
+ f32.Point{X: center.X - r, Y: center.Y - curve*scale},
+ f32.Point{X: center.X - curve, Y: center.Y - r*scale},
+ top,
+ )
+ ellipse := p.End()
+ ellipse.shape = ops.Ellipse
+ return ellipse
+}
+
+func fPt(p image.Point) f32.Point {
+ return f32.Point{
+ X: float32(p.X), Y: float32(p.Y),
+ }
+}
diff --git a/vendor/gioui.org/op/op.go b/vendor/gioui.org/op/op.go
index 174d318..f0b2620 100644
--- a/vendor/gioui.org/op/op.go
+++ b/vendor/gioui.org/op/op.go
@@ -30,25 +30,25 @@ Drawing a colored square:
State
-An Ops list can be viewed as a very simple virtual machine: it has an implicit
-mutable state stack and execution flow can be controlled with macros.
+An Ops list can be viewed as a very simple virtual machine: it has state such
+as transformation and color and execution flow can be controlled with macros.
-The StackOp saves the current state to the state stack and restores it later:
+Some state, such as the current color, is modified directly by operations with
+Add methods. Other state, such as transformation and clip shape, are
+represented by stacks.
+
+This example sets the simple color state and pushes an offset to the
+transformation stack.
ops := new(op.Ops)
- // Save the current state, in particular the transform.
- stack := op.Push(ops)
- // Apply a transform to subsequent operations.
- op.Offset(...).Add(ops)
+ // Set the color.
+ paint.ColorOp{...}.Add(ops)
+ // Apply an offset to subsequent operations.
+ stack := op.Offset(...).Push(ops)
...
- // Restore the previous transform.
+ // Undo the offset transformation.
stack.Pop()
-You can also use this one-line to save the current state and restore it at the
-end of a function :
-
- defer op.Push(ops).Pop()
-
The MacroOp records a list of operations to be executed later:
ops := new(op.Ops)
@@ -71,44 +71,29 @@ import (
"time"
"gioui.org/f32"
- "gioui.org/internal/opconst"
+ "gioui.org/internal/ops"
)
// Ops holds a list of operations. Operations are stored in
// serialized form to avoid garbage during construction of
// the ops list.
type Ops struct {
- // version is incremented at each Reset.
- version int
- // data contains the serialized operations.
- data []byte
- // External references for operations.
- refs []interface{}
-
- stackStack stack
- macroStack stack
-}
-
-// StackOp saves and restores the operation state
-// in a stack-like manner.
-type StackOp struct {
- id stackID
- macroID int
- ops *Ops
+ // Internal is for internal use, despite being exported.
+ Internal ops.Ops
}
// MacroOp records a list of operations for later use.
type MacroOp struct {
- ops *Ops
- id stackID
- pc pc
+ ops *ops.Ops
+ id ops.StackID
+ pc ops.PC
}
// CallOp invokes the operations recorded by Record.
type CallOp struct {
// Ops is the list of operations to invoke.
- ops *Ops
- pc pc
+ ops *ops.Ops
+ pc ops.PC
}
// InvalidateOp requests a redraw at the given time. Use
@@ -117,100 +102,58 @@ type InvalidateOp struct {
At time.Time
}
-// TransformOp applies a transform to the current transform. The zero value
-// for TransformOp represents the identity transform.
+// TransformOp represents a transformation that can be pushed on the
+// transformation stack.
type TransformOp struct {
t f32.Affine2D
}
-// stack tracks the integer identities of StackOp and MacroOp
-// operations to ensure correct pairing of Push/Pop and Record/End.
-type stack struct {
- currentID int
- nextID int
-}
-
-type stackID struct {
- id int
- prev int
-}
-
-type pc struct {
- data int
- refs int
-}
-
-// Push (save) the current operations state.
-func Push(o *Ops) StackOp {
- s := StackOp{
- ops: o,
- id: o.stackStack.push(),
- macroID: o.macroStack.currentID,
- }
- data := o.Write(opconst.TypePushLen)
- data[0] = byte(opconst.TypePush)
- return s
+// TransformStack represents a TransformOp pushed on the transformation stack.
+type TransformStack struct {
+ id ops.StackID
+ macroID int
+ ops *ops.Ops
}
-// Pop (restore) a previously Pushed operations state.
-func (s StackOp) Pop() {
- if s.ops.macroStack.currentID != s.macroID {
- panic("pop in a different macro than push")
+// Defer executes c after all other operations have completed, including
+// previously deferred operations.
+// Defer saves the transformation stack and pushes it prior to executing
+// c. All other operation state is reset.
+//
+// Note that deferred operations are executed in first-in-first-out order,
+// unlike the Go facility of the same name.
+func Defer(o *Ops, c CallOp) {
+ if c.ops == nil {
+ return
}
- s.ops.stackStack.pop(s.id)
- data := s.ops.Write(opconst.TypePopLen)
- data[0] = byte(opconst.TypePop)
+ state := ops.Save(&o.Internal)
+ // Wrap c in a macro that loads the saved state before execution.
+ m := Record(o)
+ state.Load()
+ c.Add(o)
+ c = m.Stop()
+ // A Defer is recorded as a TypeDefer followed by the
+ // wrapped macro.
+ data := ops.Write(&o.Internal, ops.TypeDeferLen)
+ data[0] = byte(ops.TypeDefer)
+ c.Add(o)
}
// Reset the Ops, preparing it for re-use. Reset invalidates
// any recorded macros.
func (o *Ops) Reset() {
- o.stackStack = stack{}
- o.macroStack = stack{}
- // Leave references to the GC.
- for i := range o.refs {
- o.refs[i] = nil
- }
- o.data = o.data[:0]
- o.refs = o.refs[:0]
- o.version++
-}
-
-// Data is for internal use only.
-func (o *Ops) Data() []byte {
- return o.data
-}
-
-// Refs is for internal use only.
-func (o *Ops) Refs() []interface{} {
- return o.refs
-}
-
-// Version is for internal use only.
-func (o *Ops) Version() int {
- return o.version
-}
-
-// Write is for internal use only.
-func (o *Ops) Write(n int, refs ...interface{}) []byte {
- o.data = append(o.data, make([]byte, n)...)
- o.refs = append(o.refs, refs...)
- return o.data[len(o.data)-n:]
-}
-
-func (o *Ops) pc() pc {
- return pc{data: len(o.data), refs: len(o.refs)}
+ ops.Reset(&o.Internal)
}
// Record a macro of operations.
func Record(o *Ops) MacroOp {
m := MacroOp{
- ops: o,
- id: o.macroStack.push(),
- pc: o.pc(),
+ ops: &o.Internal,
+ id: ops.PushMacro(&o.Internal),
+ pc: ops.PCFor(&o.Internal),
}
// Reserve room for a macro definition. Updated in Stop.
- m.ops.Write(opconst.TypeMacroLen)
+ ops.Write(m.ops, ops.TypeMacroLen)
m.fill()
return m
}
@@ -218,7 +161,7 @@ func Record(o *Ops) MacroOp {
// Stop ends a previously started recording and returns an
// operation for replaying it.
func (m MacroOp) Stop() CallOp {
- m.ops.macroStack.pop(m.id)
+ ops.PopMacro(m.ops, m.id)
m.fill()
return CallOp{
ops: m.ops,
@@ -227,14 +170,7 @@ func (m MacroOp) Stop() CallOp {
}
func (m MacroOp) fill() {
- pc := m.ops.pc()
- // Fill out the macro definition reserved in Record.
- data := m.ops.data[m.pc.data:]
- data = data[:opconst.TypeMacroLen]
- data[0] = byte(opconst.TypeMacro)
- bo := binary.LittleEndian
- bo.PutUint32(data[1:], uint32(pc.data))
- bo.PutUint32(data[5:], uint32(pc.refs))
+ ops.FillMacro(m.ops, m.pc)
}
// Add the recorded list of operations. Add
@@ -244,16 +180,12 @@ func (c CallOp) Add(o *Ops) {
if c.ops == nil {
return
}
- data := o.Write(opconst.TypeCallLen, c.ops)
- data[0] = byte(opconst.TypeCall)
- bo := binary.LittleEndian
- bo.PutUint32(data[1:], uint32(c.pc.data))
- bo.PutUint32(data[5:], uint32(c.pc.refs))
+ ops.AddCall(&o.Internal, c.ops, c.pc)
}
func (r InvalidateOp) Add(o *Ops) {
- data := o.Write(opconst.TypeRedrawLen)
- data[0] = byte(opconst.TypeInvalidate)
+ data := ops.Write(&o.Internal, ops.TypeRedrawLen)
+ data[0] = byte(ops.TypeInvalidate)
bo := binary.LittleEndian
// UnixNano cannot represent the zero time.
if t := r.At; !t.IsZero() {
@@ -274,36 +206,38 @@ func Affine(a f32.Affine2D) TransformOp {
return TransformOp{t: a}
}
-func (t TransformOp) Add(o *Ops) {
- data := o.Write(opconst.TypeTransformLen)
- data[0] = byte(opconst.TypeTransform)
- bo := binary.LittleEndian
- a, b, c, d, e, f := t.t.Elems()
- bo.PutUint32(data[1:], math.Float32bits(a))
- bo.PutUint32(data[1+4*1:], math.Float32bits(b))
- bo.PutUint32(data[1+4*2:], math.Float32bits(c))
- bo.PutUint32(data[1+4*3:], math.Float32bits(d))
- bo.PutUint32(data[1+4*4:], math.Float32bits(e))
- bo.PutUint32(data[1+4*5:], math.Float32bits(f))
+// Push the current transformation to the stack and then multiply the
+// current transformation with t.
+func (t TransformOp) Push(o *Ops) TransformStack {
+ id, macroID := ops.PushOp(&o.Internal, ops.TransStack)
+ t.add(o, true)
+ return TransformStack{ops: &o.Internal, id: id, macroID: macroID}
}
-func (s *stack) push() stackID {
- s.nextID++
- sid := stackID{
- id: s.nextID,
- prev: s.currentID,
- }
- s.currentID = s.nextID
- return sid
+// Add is like Push except it doesn't push the current transformation to the
+// stack.
+func (t TransformOp) Add(o *Ops) {
+ t.add(o, false)
}
-func (s *stack) check(sid stackID) {
- if s.currentID != sid.id {
- panic("unbalanced operation")
+func (t TransformOp) add(o *Ops, push bool) {
+ data := ops.Write(&o.Internal, ops.TypeTransformLen)
+ data[0] = byte(ops.TypeTransform)
+ if push {
+ data[1] = 1
}
-}
-
-func (s *stack) pop(sid stackID) {
- s.check(sid)
- s.currentID = sid.prev
+ bo := binary.LittleEndian
+ a, b, c, d, e, f := t.t.Elems()
+ bo.PutUint32(data[2:], math.Float32bits(a))
+ bo.PutUint32(data[2+4*1:], math.Float32bits(b))
+ bo.PutUint32(data[2+4*2:], math.Float32bits(c))
+ bo.PutUint32(data[2+4*3:], math.Float32bits(d))
+ bo.PutUint32(data[2+4*4:], math.Float32bits(e))
+ bo.PutUint32(data[2+4*5:], math.Float32bits(f))
+}
+
+func (t TransformStack) Pop() {
+ ops.PopOp(t.ops, ops.TransStack, t.id, t.macroID)
+ data := ops.Write(t.ops, ops.TypePopTransformLen)
+ data[0] = byte(ops.TypePopTransform)
}
diff --git a/vendor/gioui.org/op/paint/doc.go b/vendor/gioui.org/op/paint/doc.go
index b7b569a..bbec006 100644
--- a/vendor/gioui.org/op/paint/doc.go
+++ b/vendor/gioui.org/op/paint/doc.go
@@ -3,10 +3,13 @@
/*
Package paint provides drawing operations for 2D graphics.
-The PaintOp operation draws the current brush into a rectangular
-area, taking the current clip path and transformation into account.
+The PaintOp operation fills the current clip with the current brush, taking the
+current transformation into account. Drawing outside the current clip area is
+ignored.
The current brush is set by either a ColorOp for a constant color, or
-ImageOp for an image.
+ImageOp for an image, or LinearGradientOp for gradients.
+
+All color.NRGBA values are in the sRGB color space.
*/
package paint
diff --git a/vendor/gioui.org/op/paint/paint.go b/vendor/gioui.org/op/paint/paint.go
index b4a9a56..1c99297 100644
--- a/vendor/gioui.org/op/paint/paint.go
+++ b/vendor/gioui.org/op/paint/paint.go
@@ -10,20 +10,15 @@ import (
"math"
"gioui.org/f32"
- "gioui.org/internal/opconst"
+ "gioui.org/internal/ops"
"gioui.org/op"
+ "gioui.org/op/clip"
)
// ImageOp sets the brush to an image.
-//
-// Note: the ImageOp may keep a reference to the backing image.
-// See NewImageOp for details.
type ImageOp struct {
- // Rect is the section if the backing image to use.
- Rect image.Rectangle
-
uniform bool
- color color.RGBA
+ color color.NRGBA
src *image.RGBA
// handle is a key to uniquely identify this ImageOp
@@ -33,20 +28,23 @@ type ImageOp struct {
// ColorOp sets the brush to a constant color.
type ColorOp struct {
- Color color.RGBA
+ Color color.NRGBA
}
-// PaintOp fills an area with the current brush, respecting the
-// current clip path and transformation.
+// LinearGradientOp sets the brush to a gradient starting at stop1 with color1 and
+// ending at stop2 with color2.
+type LinearGradientOp struct {
+ Stop1 f32.Point
+ Color1 color.NRGBA
+ Stop2 f32.Point
+ Color2 color.NRGBA
+}
+
+// PaintOp fills the current clip area with the current brush.
type PaintOp struct {
- // Rect is the destination area to paint. If necessary, the brush is
- // scaled to cover the rectangle area.
- Rect f32.Rectangle
}
-// NewImageOp creates an ImageOp backed by src. See
-// gioui.org/io/system.FrameEvent for a description of when data
-// referenced by operations is safe to re-use.
+// NewImageOp creates an ImageOp backed by src.
//
// NewImageOp assumes the backing image is immutable, and may cache a
// copy of its contents in a GPU-friendly way. Create new ImageOps to
@@ -55,19 +53,15 @@ type PaintOp struct {
func NewImageOp(src image.Image) ImageOp {
switch src := src.(type) {
case *image.Uniform:
- col := color.RGBAModel.Convert(src.C).(color.RGBA)
+ col := color.NRGBAModel.Convert(src.C).(color.NRGBA)
return ImageOp{
uniform: true,
color: col,
}
case *image.RGBA:
- bounds := src.Bounds()
- if bounds.Min == (image.Point{}) && src.Stride == bounds.Dx()*4 {
- return ImageOp{
- Rect: src.Bounds(),
- src: src,
- handle: new(int),
- }
+ return ImageOp{
+ src: src,
+ handle: new(int),
}
}
@@ -78,7 +72,6 @@ func NewImageOp(src image.Image) ImageOp {
})
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
return ImageOp{
- Rect: dst.Bounds(),
src: dst,
handle: new(int),
}
@@ -97,31 +90,58 @@ func (i ImageOp) Add(o *op.Ops) {
Color: i.color,
}.Add(o)
return
+ } else if i.src == nil || i.src.Bounds().Empty() {
+ return
}
- data := o.Write(opconst.TypeImageLen, i.src, i.handle)
- data[0] = byte(opconst.TypeImage)
- bo := binary.LittleEndian
- bo.PutUint32(data[1:], uint32(i.Rect.Min.X))
- bo.PutUint32(data[5:], uint32(i.Rect.Min.Y))
- bo.PutUint32(data[9:], uint32(i.Rect.Max.X))
- bo.PutUint32(data[13:], uint32(i.Rect.Max.Y))
+ data := ops.Write2(&o.Internal, ops.TypeImageLen, i.src, i.handle)
+ data[0] = byte(ops.TypeImage)
}
func (c ColorOp) Add(o *op.Ops) {
- data := o.Write(opconst.TypeColorLen)
- data[0] = byte(opconst.TypeColor)
+ data := ops.Write(&o.Internal, ops.TypeColorLen)
+ data[0] = byte(ops.TypeColor)
data[1] = c.Color.R
data[2] = c.Color.G
data[3] = c.Color.B
data[4] = c.Color.A
}
-func (d PaintOp) Add(o *op.Ops) {
- data := o.Write(opconst.TypePaintLen)
- data[0] = byte(opconst.TypePaint)
+func (c LinearGradientOp) Add(o *op.Ops) {
+ data := ops.Write(&o.Internal, ops.TypeLinearGradientLen)
+ data[0] = byte(ops.TypeLinearGradient)
+
bo := binary.LittleEndian
- bo.PutUint32(data[1:], math.Float32bits(d.Rect.Min.X))
- bo.PutUint32(data[5:], math.Float32bits(d.Rect.Min.Y))
- bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X))
- bo.PutUint32(data[13:], math.Float32bits(d.Rect.Max.Y))
+ bo.PutUint32(data[1:], math.Float32bits(c.Stop1.X))
+ bo.PutUint32(data[5:], math.Float32bits(c.Stop1.Y))
+ bo.PutUint32(data[9:], math.Float32bits(c.Stop2.X))
+ bo.PutUint32(data[13:], math.Float32bits(c.Stop2.Y))
+
+ data[17+0] = c.Color1.R
+ data[17+1] = c.Color1.G
+ data[17+2] = c.Color1.B
+ data[17+3] = c.Color1.A
+ data[21+0] = c.Color2.R
+ data[21+1] = c.Color2.G
+ data[21+2] = c.Color2.B
+ data[21+3] = c.Color2.A
+}
+
+func (d PaintOp) Add(o *op.Ops) {
+ data := ops.Write(&o.Internal, ops.TypePaintLen)
+ data[0] = byte(ops.TypePaint)
+}
+
+// FillShape fills the clip shape with a color.
+func FillShape(ops *op.Ops, c color.NRGBA, shape clip.Op) {
+ defer shape.Push(ops).Pop()
+ Fill(ops, c)
+}
+
+// Fill paints an infinitely large plane with the provided color. It
+// is intended to be used with a clip.Op already in place to limit
+// the painted area. Use FillShape unless you need to paint several
+// times within the same clip.Op.
+func Fill(ops *op.Ops, c color.NRGBA) {
+ ColorOp{Color: c}.Add(ops)
+ PaintOp{}.Add(ops)
}
diff --git a/vendor/gioui.org/shader/LICENSE b/vendor/gioui.org/shader/LICENSE
new file mode 100644
index 0000000..81f4733
--- /dev/null
+++ b/vendor/gioui.org/shader/LICENSE
@@ -0,0 +1,63 @@
+This project is provided under the terms of the UNLICENSE or
+the MIT license denoted by the following SPDX identifier:
+
+SPDX-License-Identifier: Unlicense OR MIT
+
+You may use the project under the terms of either license.
+
+Both licenses are reproduced below.
+
+----
+The MIT License (MIT)
+
+Copyright (c) 2019 The Gio authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+---
+
+
+
+---
+The UNLICENSE
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org/>
+---
diff --git a/vendor/gioui.org/shader/README.md b/vendor/gioui.org/shader/README.md
new file mode 100644
index 0000000..4ea4867
--- /dev/null
+++ b/vendor/gioui.org/shader/README.md
@@ -0,0 +1,18 @@
+# GPU programs for the Gio project
+
+This repository contains the source code for the [Gio](https://gioui.org)
+project. It also contains the generators and dereived versions for use with the
+GPU APIs supported by Gio.
+
+# Generating CPU fallbacks
+
+The `piet/gencpu.sh` script updates the piet-gpu binaries:
+
+```
+$ cd piet
+$ ./gencpu.sh
+```
+
+## Issues and contributions
+
+See the [Gio contribution guide](https://gioui.org/doc/contribute).
diff --git a/vendor/gioui.org/shader/gio/blit.frag b/vendor/gioui.org/shader/gio/blit.frag
new file mode 100644
index 0000000..a88f4dc
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/blit.frag
@@ -0,0 +1,15 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+precision mediump float;
+
+layout(location=0) in highp vec2 vUV;
+
+{{.Header}}
+
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = {{.FetchColorExpr}};
+}
diff --git a/vendor/gioui.org/shader/gio/blit.vert b/vendor/gioui.org/shader/gio/blit.vert
new file mode 100644
index 0000000..83d041f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/blit.vert
@@ -0,0 +1,27 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#extension GL_GOOGLE_include_directive : enable
+
+precision highp float;
+
+#include "common.h"
+
+layout(push_constant) uniform Block {
+ vec4 transform;
+ vec4 uvTransformR1;
+ vec4 uvTransformR2;
+} _block;
+
+layout(location = 0) in vec2 pos;
+
+layout(location = 1) in vec2 uv;
+
+layout(location = 0) out vec2 vUV;
+
+void main() {
+ vec2 p = pos*_block.transform.xy + _block.transform.zw;
+ gl_Position = vec4(transform3x2(windowTransform, vec3(p, 0)), 1);
+ vUV = transform3x2(m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz), vec3(uv,1)).xy;
+}
diff --git a/vendor/gioui.org/shader/gio/common.h b/vendor/gioui.org/shader/gio/common.h
new file mode 100644
index 0000000..9b6dc59
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/common.h
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+struct m3x2 {
+ vec3 r0;
+ vec3 r1;
+};
+
+// fboTransform is the transformation that cancels the implied transformation
+// between the clip space and the framebuffer. Only two rows are returned. The
+// last is implied to be [0, 0, 1].
+const m3x2 fboTransform = m3x2(
+#if defined(LANG_HLSL) || defined(LANG_MSL) || defined(LANG_MSLIOS)
+ vec3(1.0, 0.0, 0.0),
+ vec3(0.0, -1.0, 0.0)
+#else
+ vec3(1.0, 0.0, 0.0),
+ vec3(0.0, 1.0, 0.0)
+#endif
+);
+
+// windowTransform is the transformation that cancels the implied transformation
+// between framebuffer space and window system coordinates.
+const m3x2 windowTransform = m3x2(
+#if defined(LANG_VULKAN)
+ vec3(1.0, 0.0, 0.0),
+ vec3(0.0, 1.0, 0.0)
+#else
+ vec3(1.0, 0.0, 0.0),
+ vec3(0.0, -1.0, 0.0)
+#endif
+);
+
+vec3 transform3x2(m3x2 t, vec3 v) {
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
diff --git a/vendor/gioui.org/shader/gio/copy.frag b/vendor/gioui.org/shader/gio/copy.frag
new file mode 100644
index 0000000..048d9e7
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/copy.frag
@@ -0,0 +1,24 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+precision mediump float;
+
+layout(binding = 0) uniform sampler2D tex;
+
+layout(location = 0) in highp vec2 vUV;
+
+layout(location = 0) out vec4 fragColor;
+
+vec3 sRGBtoRGB(vec3 rgb) {
+ bvec3 cutoff = greaterThanEqual(rgb, vec3(0.04045));
+ vec3 below = rgb/vec3(12.92);
+ vec3 above = pow((rgb + vec3(0.055))/vec3(1.055), vec3(2.4));
+ return mix(below, above, cutoff);
+}
+
+void main() {
+ vec4 texel = texture(tex, vUV);
+ texel.rgb = sRGBtoRGB(texel.rgb);
+ fragColor = texel;
+}
diff --git a/vendor/gioui.org/shader/gio/copy.vert b/vendor/gioui.org/shader/gio/copy.vert
new file mode 100644
index 0000000..c079b96
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/copy.vert
@@ -0,0 +1,26 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#extension GL_GOOGLE_include_directive : enable
+
+precision highp float;
+
+#include "common.h"
+
+layout(push_constant) uniform Block {
+ vec2 scale;
+ vec2 pos;
+ vec2 uvScale;
+} _block;
+
+layout(location = 0) in vec2 pos;
+layout(location = 1) in vec2 uv;
+
+layout(location = 0) out vec2 vUV;
+
+void main() {
+ vUV = vec2(uv*_block.uvScale);
+ vec2 p = vec2(pos*_block.scale + _block.pos);
+ gl_Position = vec4(transform3x2(windowTransform, vec3(p, 0)), 1);
+}
diff --git a/vendor/gioui.org/shader/gio/cover.frag b/vendor/gioui.org/shader/gio/cover.frag
new file mode 100644
index 0000000..34c5e4c
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/cover.frag
@@ -0,0 +1,20 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+precision mediump float;
+
+{{.Header}}
+
+layout(location = 0) in highp vec2 vCoverUV;
+layout(location = 1) in highp vec2 vUV;
+
+layout(binding = 1) uniform sampler2D cover;
+
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = {{.FetchColorExpr}};
+ float c = min(abs(texture(cover, vCoverUV).r), 1.0);
+ fragColor *= c;
+}
diff --git a/vendor/gioui.org/shader/gio/cover.vert b/vendor/gioui.org/shader/gio/cover.vert
new file mode 100644
index 0000000..02fae50
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/cover.vert
@@ -0,0 +1,31 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#extension GL_GOOGLE_include_directive : enable
+
+precision highp float;
+
+#include "common.h"
+
+layout(push_constant) uniform Block {
+ vec4 transform;
+ vec4 uvCoverTransform;
+ vec4 uvTransformR1;
+ vec4 uvTransformR2;
+} _block;
+
+layout(location = 0) in vec2 pos;
+
+layout(location = 0) out vec2 vCoverUV;
+
+layout(location = 1) in vec2 uv;
+layout(location = 1) out vec2 vUV;
+
+void main() {
+ vec2 p = vec2(pos*_block.transform.xy + _block.transform.zw);
+ gl_Position = vec4(transform3x2(windowTransform, vec3(p, 0)), 1);
+ vUV = transform3x2(m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz), vec3(uv,1)).xy;
+ vec3 uv3 = vec3(uv, 1.0);
+ vCoverUV = (uv3*vec3(_block.uvCoverTransform.xy, 1.0)+vec3(_block.uvCoverTransform.zw, 0.0)).xy;
+}
diff --git a/vendor/gioui.org/shader/gio/gen.go b/vendor/gioui.org/shader/gio/gen.go
new file mode 100644
index 0000000..f26056a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/gen.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gio
+
+//go:generate go run ../cmd/convertshaders -package gio -dir .
diff --git a/vendor/gioui.org/shader/gio/input.vert b/vendor/gioui.org/shader/gio/input.vert
new file mode 100644
index 0000000..3d0cd50
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/input.vert
@@ -0,0 +1,15 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#extension GL_GOOGLE_include_directive : enable
+
+precision highp float;
+
+#include "common.h"
+
+layout(location=0) in vec4 position;
+
+void main() {
+ gl_Position = vec4(transform3x2(windowTransform, position.xyz), position.w);
+}
diff --git a/vendor/gioui.org/shader/gio/intersect.frag b/vendor/gioui.org/shader/gio/intersect.frag
new file mode 100644
index 0000000..21a126f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/intersect.frag
@@ -0,0 +1,15 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+precision mediump float;
+
+layout(location = 0) in highp vec2 vUV;
+
+layout(binding = 0) uniform sampler2D cover;
+
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor.r = abs(texture(cover, vUV).r);
+}
diff --git a/vendor/gioui.org/shader/gio/intersect.vert b/vendor/gioui.org/shader/gio/intersect.vert
new file mode 100644
index 0000000..e7ee2fe
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/intersect.vert
@@ -0,0 +1,26 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#extension GL_GOOGLE_include_directive : enable
+
+precision highp float;
+
+#include "common.h"
+
+layout(location = 0) in vec2 pos;
+layout(location = 1) in vec2 uv;
+
+layout(push_constant) uniform Block {
+ vec4 uvTransform;
+ vec4 subUVTransform;
+} _block;
+
+layout(location = 0) out vec2 vUV;
+
+void main() {
+ vec3 p = transform3x2(fboTransform, vec3(pos, 1.0));
+ gl_Position = vec4(p, 1);
+ vUV = uv.xy*_block.subUVTransform.xy + _block.subUVTransform.zw;
+ vUV = vUV*_block.uvTransform.xy + _block.uvTransform.zw;
+}
diff --git a/vendor/gioui.org/shader/gio/material.frag b/vendor/gioui.org/shader/gio/material.frag
new file mode 100644
index 0000000..489461e
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/material.frag
@@ -0,0 +1,32 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+precision mediump float;
+
+layout(binding = 0) uniform sampler2D tex;
+
+layout(location = 0) in highp vec2 vUV;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(push_constant) uniform Color {
+ // If emulateSRGB is set (!= 0), the input texels are sRGB encoded. We save the
+ // conversion step below, at the cost of texture filtering in sRGB space.
+ layout(offset=16) float emulateSRGB;
+} _color;
+
+vec3 RGBtosRGB(vec3 rgb) {
+ bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308));
+ vec3 below = vec3(12.92)*rgb;
+ vec3 above = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055);
+ return mix(below, above, cutoff);
+}
+
+void main() {
+ vec4 texel = texture(tex, vUV);
+ if (_color.emulateSRGB == 0.0) {
+ texel.rgb = RGBtosRGB(texel.rgb);
+ }
+ fragColor = texel;
+}
diff --git a/vendor/gioui.org/shader/gio/material.vert b/vendor/gioui.org/shader/gio/material.vert
new file mode 100644
index 0000000..22c41a0
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/material.vert
@@ -0,0 +1,25 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#extension GL_GOOGLE_include_directive : enable
+
+precision highp float;
+
+#include "common.h"
+
+layout(push_constant) uniform Block {
+ vec2 scale;
+ vec2 pos;
+} _block;
+
+layout(location = 0) in vec2 pos;
+layout(location = 1) in vec2 uv;
+
+layout(location = 0) out vec2 vUV;
+
+void main() {
+ vUV = uv;
+ vec2 p = vec2(pos*_block.scale + _block.pos);
+ gl_Position = vec4(transform3x2(fboTransform, vec3(p, 0)), 1);
+}
diff --git a/vendor/gioui.org/shader/gio/shaders.go b/vendor/gioui.org/shader/gio/shaders.go
new file mode 100644
index 0000000..1166c1c
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/shaders.go
@@ -0,0 +1,796 @@
+// Code generated by build.go. DO NOT EDIT.
+
+package gio
+
+import (
+ _ "embed"
+ "runtime"
+
+ "gioui.org/shader"
+)
+
+var (
+ Shader_blit_frag = [...]shader.Sources{
+ {
+ Name: "blit.frag",
+ Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_color.color", Type: 0x0, Size: 4, Offset: 112}},
+ Size: 16,
+ },
+ },
+ {
+ Name: "blit.frag",
+ Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_gradient.color1", Type: 0x0, Size: 4, Offset: 96}, {Name: "_gradient.color2", Type: 0x0, Size: 4, Offset: 112}},
+ Size: 32,
+ },
+ },
+ {
+ Name: "blit.frag",
+ Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}},
+ Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}},
+ },
+ }
+ //go:embed zblit.frag.0.spirv
+ zblit_frag_0_spirv string
+ //go:embed zblit.frag.0.glsl100es
+ zblit_frag_0_glsl100es string
+ //go:embed zblit.frag.0.glsl150
+ zblit_frag_0_glsl150 string
+ //go:embed zblit.frag.0.dxbc
+ zblit_frag_0_dxbc string
+ //go:embed zblit.frag.0.metallibmacos
+ zblit_frag_0_metallibmacos string
+ //go:embed zblit.frag.0.metallibios
+ zblit_frag_0_metallibios string
+ //go:embed zblit.frag.0.metallibiossimulator
+ zblit_frag_0_metallibiossimulator string
+ //go:embed zblit.frag.1.spirv
+ zblit_frag_1_spirv string
+ //go:embed zblit.frag.1.glsl100es
+ zblit_frag_1_glsl100es string
+ //go:embed zblit.frag.1.glsl150
+ zblit_frag_1_glsl150 string
+ //go:embed zblit.frag.1.dxbc
+ zblit_frag_1_dxbc string
+ //go:embed zblit.frag.1.metallibmacos
+ zblit_frag_1_metallibmacos string
+ //go:embed zblit.frag.1.metallibios
+ zblit_frag_1_metallibios string
+ //go:embed zblit.frag.1.metallibiossimulator
+ zblit_frag_1_metallibiossimulator string
+ //go:embed zblit.frag.2.spirv
+ zblit_frag_2_spirv string
+ //go:embed zblit.frag.2.glsl100es
+ zblit_frag_2_glsl100es string
+ //go:embed zblit.frag.2.glsl150
+ zblit_frag_2_glsl150 string
+ //go:embed zblit.frag.2.dxbc
+ zblit_frag_2_dxbc string
+ //go:embed zblit.frag.2.metallibmacos
+ zblit_frag_2_metallibmacos string
+ //go:embed zblit.frag.2.metallibios
+ zblit_frag_2_metallibios string
+ //go:embed zblit.frag.2.metallibiossimulator
+ zblit_frag_2_metallibiossimulator string
+ Shader_blit_vert = shader.Sources{
+ Name: "blit.vert",
+ Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_block.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.uvTransformR1", Type: 0x0, Size: 4, Offset: 16}, {Name: "_block.uvTransformR2", Type: 0x0, Size: 4, Offset: 32}},
+ Size: 48,
+ },
+ }
+ //go:embed zblit.vert.0.spirv
+ zblit_vert_0_spirv string
+ //go:embed zblit.vert.0.glsl100es
+ zblit_vert_0_glsl100es string
+ //go:embed zblit.vert.0.glsl150
+ zblit_vert_0_glsl150 string
+ //go:embed zblit.vert.0.dxbc
+ zblit_vert_0_dxbc string
+ //go:embed zblit.vert.0.metallibmacos
+ zblit_vert_0_metallibmacos string
+ //go:embed zblit.vert.0.metallibios
+ zblit_vert_0_metallibios string
+ //go:embed zblit.vert.0.metallibiossimulator
+ zblit_vert_0_metallibiossimulator string
+ Shader_copy_frag = shader.Sources{
+ Name: "copy.frag",
+ Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}},
+ Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}},
+ }
+ //go:embed zcopy.frag.0.spirv
+ zcopy_frag_0_spirv string
+ //go:embed zcopy.frag.0.glsl100es
+ zcopy_frag_0_glsl100es string
+ //go:embed zcopy.frag.0.glsl150
+ zcopy_frag_0_glsl150 string
+ //go:embed zcopy.frag.0.dxbc
+ zcopy_frag_0_dxbc string
+ //go:embed zcopy.frag.0.metallibmacos
+ zcopy_frag_0_metallibmacos string
+ //go:embed zcopy.frag.0.metallibios
+ zcopy_frag_0_metallibios string
+ //go:embed zcopy.frag.0.metallibiossimulator
+ zcopy_frag_0_metallibiossimulator string
+ Shader_copy_vert = shader.Sources{
+ Name: "copy.vert",
+ Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_block.scale", Type: 0x0, Size: 2, Offset: 0}, {Name: "_block.pos", Type: 0x0, Size: 2, Offset: 8}, {Name: "_block.uvScale", Type: 0x0, Size: 2, Offset: 16}},
+ Size: 24,
+ },
+ }
+ //go:embed zcopy.vert.0.spirv
+ zcopy_vert_0_spirv string
+ //go:embed zcopy.vert.0.glsl100es
+ zcopy_vert_0_glsl100es string
+ //go:embed zcopy.vert.0.glsl150
+ zcopy_vert_0_glsl150 string
+ //go:embed zcopy.vert.0.dxbc
+ zcopy_vert_0_dxbc string
+ //go:embed zcopy.vert.0.metallibmacos
+ zcopy_vert_0_metallibmacos string
+ //go:embed zcopy.vert.0.metallibios
+ zcopy_vert_0_metallibios string
+ //go:embed zcopy.vert.0.metallibiossimulator
+ zcopy_vert_0_metallibiossimulator string
+ Shader_cover_frag = [...]shader.Sources{
+ {
+ Name: "cover.frag",
+ Inputs: []shader.InputLocation{{Name: "vCoverUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vUV", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_color.color", Type: 0x0, Size: 4, Offset: 112}},
+ Size: 16,
+ },
+ Textures: []shader.TextureBinding{{Name: "cover", Binding: 1}},
+ },
+ {
+ Name: "cover.frag",
+ Inputs: []shader.InputLocation{{Name: "vCoverUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vUV", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_gradient.color1", Type: 0x0, Size: 4, Offset: 96}, {Name: "_gradient.color2", Type: 0x0, Size: 4, Offset: 112}},
+ Size: 32,
+ },
+ Textures: []shader.TextureBinding{{Name: "cover", Binding: 1}},
+ },
+ {
+ Name: "cover.frag",
+ Inputs: []shader.InputLocation{{Name: "vCoverUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vUV", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}, {Name: "cover", Binding: 1}},
+ },
+ }
+ //go:embed zcover.frag.0.spirv
+ zcover_frag_0_spirv string
+ //go:embed zcover.frag.0.glsl100es
+ zcover_frag_0_glsl100es string
+ //go:embed zcover.frag.0.glsl150
+ zcover_frag_0_glsl150 string
+ //go:embed zcover.frag.0.dxbc
+ zcover_frag_0_dxbc string
+ //go:embed zcover.frag.0.metallibmacos
+ zcover_frag_0_metallibmacos string
+ //go:embed zcover.frag.0.metallibios
+ zcover_frag_0_metallibios string
+ //go:embed zcover.frag.0.metallibiossimulator
+ zcover_frag_0_metallibiossimulator string
+ //go:embed zcover.frag.1.spirv
+ zcover_frag_1_spirv string
+ //go:embed zcover.frag.1.glsl100es
+ zcover_frag_1_glsl100es string
+ //go:embed zcover.frag.1.glsl150
+ zcover_frag_1_glsl150 string
+ //go:embed zcover.frag.1.dxbc
+ zcover_frag_1_dxbc string
+ //go:embed zcover.frag.1.metallibmacos
+ zcover_frag_1_metallibmacos string
+ //go:embed zcover.frag.1.metallibios
+ zcover_frag_1_metallibios string
+ //go:embed zcover.frag.1.metallibiossimulator
+ zcover_frag_1_metallibiossimulator string
+ //go:embed zcover.frag.2.spirv
+ zcover_frag_2_spirv string
+ //go:embed zcover.frag.2.glsl100es
+ zcover_frag_2_glsl100es string
+ //go:embed zcover.frag.2.glsl150
+ zcover_frag_2_glsl150 string
+ //go:embed zcover.frag.2.dxbc
+ zcover_frag_2_dxbc string
+ //go:embed zcover.frag.2.metallibmacos
+ zcover_frag_2_metallibmacos string
+ //go:embed zcover.frag.2.metallibios
+ zcover_frag_2_metallibios string
+ //go:embed zcover.frag.2.metallibiossimulator
+ zcover_frag_2_metallibiossimulator string
+ Shader_cover_vert = shader.Sources{
+ Name: "cover.vert",
+ Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_block.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.uvCoverTransform", Type: 0x0, Size: 4, Offset: 16}, {Name: "_block.uvTransformR1", Type: 0x0, Size: 4, Offset: 32}, {Name: "_block.uvTransformR2", Type: 0x0, Size: 4, Offset: 48}},
+ Size: 64,
+ },
+ }
+ //go:embed zcover.vert.0.spirv
+ zcover_vert_0_spirv string
+ //go:embed zcover.vert.0.glsl100es
+ zcover_vert_0_glsl100es string
+ //go:embed zcover.vert.0.glsl150
+ zcover_vert_0_glsl150 string
+ //go:embed zcover.vert.0.dxbc
+ zcover_vert_0_dxbc string
+ //go:embed zcover.vert.0.metallibmacos
+ zcover_vert_0_metallibmacos string
+ //go:embed zcover.vert.0.metallibios
+ zcover_vert_0_metallibios string
+ //go:embed zcover.vert.0.metallibiossimulator
+ zcover_vert_0_metallibiossimulator string
+ Shader_input_vert = shader.Sources{
+ Name: "input.vert",
+ Inputs: []shader.InputLocation{{Name: "position", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 4}},
+ }
+ //go:embed zinput.vert.0.spirv
+ zinput_vert_0_spirv string
+ //go:embed zinput.vert.0.glsl100es
+ zinput_vert_0_glsl100es string
+ //go:embed zinput.vert.0.glsl150
+ zinput_vert_0_glsl150 string
+ //go:embed zinput.vert.0.dxbc
+ zinput_vert_0_dxbc string
+ //go:embed zinput.vert.0.metallibmacos
+ zinput_vert_0_metallibmacos string
+ //go:embed zinput.vert.0.metallibios
+ zinput_vert_0_metallibios string
+ //go:embed zinput.vert.0.metallibiossimulator
+ zinput_vert_0_metallibiossimulator string
+ Shader_intersect_frag = shader.Sources{
+ Name: "intersect.frag",
+ Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}},
+ Textures: []shader.TextureBinding{{Name: "cover", Binding: 0}},
+ }
+ //go:embed zintersect.frag.0.spirv
+ zintersect_frag_0_spirv string
+ //go:embed zintersect.frag.0.glsl100es
+ zintersect_frag_0_glsl100es string
+ //go:embed zintersect.frag.0.glsl150
+ zintersect_frag_0_glsl150 string
+ //go:embed zintersect.frag.0.dxbc
+ zintersect_frag_0_dxbc string
+ //go:embed zintersect.frag.0.metallibmacos
+ zintersect_frag_0_metallibmacos string
+ //go:embed zintersect.frag.0.metallibios
+ zintersect_frag_0_metallibios string
+ //go:embed zintersect.frag.0.metallibiossimulator
+ zintersect_frag_0_metallibiossimulator string
+ Shader_intersect_vert = shader.Sources{
+ Name: "intersect.vert",
+ Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_block.uvTransform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.subUVTransform", Type: 0x0, Size: 4, Offset: 16}},
+ Size: 32,
+ },
+ }
+ //go:embed zintersect.vert.0.spirv
+ zintersect_vert_0_spirv string
+ //go:embed zintersect.vert.0.glsl100es
+ zintersect_vert_0_glsl100es string
+ //go:embed zintersect.vert.0.glsl150
+ zintersect_vert_0_glsl150 string
+ //go:embed zintersect.vert.0.dxbc
+ zintersect_vert_0_dxbc string
+ //go:embed zintersect.vert.0.metallibmacos
+ zintersect_vert_0_metallibmacos string
+ //go:embed zintersect.vert.0.metallibios
+ zintersect_vert_0_metallibios string
+ //go:embed zintersect.vert.0.metallibiossimulator
+ zintersect_vert_0_metallibiossimulator string
+ Shader_material_frag = shader.Sources{
+ Name: "material.frag",
+ Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_color.emulateSRGB", Type: 0x0, Size: 1, Offset: 16}},
+ Size: 4,
+ },
+ Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}},
+ }
+ //go:embed zmaterial.frag.0.spirv
+ zmaterial_frag_0_spirv string
+ //go:embed zmaterial.frag.0.glsl100es
+ zmaterial_frag_0_glsl100es string
+ //go:embed zmaterial.frag.0.glsl150
+ zmaterial_frag_0_glsl150 string
+ //go:embed zmaterial.frag.0.dxbc
+ zmaterial_frag_0_dxbc string
+ //go:embed zmaterial.frag.0.metallibmacos
+ zmaterial_frag_0_metallibmacos string
+ //go:embed zmaterial.frag.0.metallibios
+ zmaterial_frag_0_metallibios string
+ //go:embed zmaterial.frag.0.metallibiossimulator
+ zmaterial_frag_0_metallibiossimulator string
+ Shader_material_vert = shader.Sources{
+ Name: "material.vert",
+ Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_block.scale", Type: 0x0, Size: 2, Offset: 0}, {Name: "_block.pos", Type: 0x0, Size: 2, Offset: 8}},
+ Size: 16,
+ },
+ }
+ //go:embed zmaterial.vert.0.spirv
+ zmaterial_vert_0_spirv string
+ //go:embed zmaterial.vert.0.glsl100es
+ zmaterial_vert_0_glsl100es string
+ //go:embed zmaterial.vert.0.glsl150
+ zmaterial_vert_0_glsl150 string
+ //go:embed zmaterial.vert.0.dxbc
+ zmaterial_vert_0_dxbc string
+ //go:embed zmaterial.vert.0.metallibmacos
+ zmaterial_vert_0_metallibmacos string
+ //go:embed zmaterial.vert.0.metallibios
+ zmaterial_vert_0_metallibios string
+ //go:embed zmaterial.vert.0.metallibiossimulator
+ zmaterial_vert_0_metallibiossimulator string
+ Shader_simple_frag = shader.Sources{
+ Name: "simple.frag",
+ }
+ //go:embed zsimple.frag.0.spirv
+ zsimple_frag_0_spirv string
+ //go:embed zsimple.frag.0.glsl100es
+ zsimple_frag_0_glsl100es string
+ //go:embed zsimple.frag.0.glsl150
+ zsimple_frag_0_glsl150 string
+ //go:embed zsimple.frag.0.dxbc
+ zsimple_frag_0_dxbc string
+ //go:embed zsimple.frag.0.metallibmacos
+ zsimple_frag_0_metallibmacos string
+ //go:embed zsimple.frag.0.metallibios
+ zsimple_frag_0_metallibios string
+ //go:embed zsimple.frag.0.metallibiossimulator
+ zsimple_frag_0_metallibiossimulator string
+ Shader_stencil_frag = shader.Sources{
+ Name: "stencil.frag",
+ Inputs: []shader.InputLocation{{Name: "vFrom", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vCtrl", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}, {Name: "vTo", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0x0, Size: 2}},
+ }
+ //go:embed zstencil.frag.0.spirv
+ zstencil_frag_0_spirv string
+ //go:embed zstencil.frag.0.glsl100es
+ zstencil_frag_0_glsl100es string
+ //go:embed zstencil.frag.0.glsl150
+ zstencil_frag_0_glsl150 string
+ //go:embed zstencil.frag.0.dxbc
+ zstencil_frag_0_dxbc string
+ //go:embed zstencil.frag.0.metallibmacos
+ zstencil_frag_0_metallibmacos string
+ //go:embed zstencil.frag.0.metallibios
+ zstencil_frag_0_metallibios string
+ //go:embed zstencil.frag.0.metallibiossimulator
+ zstencil_frag_0_metallibiossimulator string
+ Shader_stencil_vert = shader.Sources{
+ Name: "stencil.vert",
+ Inputs: []shader.InputLocation{{Name: "corner", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "maxy", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 1}, {Name: "from", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0x0, Size: 2}, {Name: "ctrl", Location: 3, Semantic: "TEXCOORD", SemanticIndex: 3, Type: 0x0, Size: 2}, {Name: "to", Location: 4, Semantic: "TEXCOORD", SemanticIndex: 4, Type: 0x0, Size: 2}},
+ Uniforms: shader.UniformsReflection{
+ Locations: []shader.UniformLocation{{Name: "_block.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.pathOffset", Type: 0x0, Size: 2, Offset: 16}},
+ Size: 24,
+ },
+ }
+ //go:embed zstencil.vert.0.spirv
+ zstencil_vert_0_spirv string
+ //go:embed zstencil.vert.0.glsl100es
+ zstencil_vert_0_glsl100es string
+ //go:embed zstencil.vert.0.glsl150
+ zstencil_vert_0_glsl150 string
+ //go:embed zstencil.vert.0.dxbc
+ zstencil_vert_0_dxbc string
+ //go:embed zstencil.vert.0.metallibmacos
+ zstencil_vert_0_metallibmacos string
+ //go:embed zstencil.vert.0.metallibios
+ zstencil_vert_0_metallibios string
+ //go:embed zstencil.vert.0.metallibiossimulator
+ zstencil_vert_0_metallibiossimulator string
+)
+
+func init() {
+ const (
+ opengles = runtime.GOOS == "linux" || runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "windows" || runtime.GOOS == "js" || runtime.GOOS == "android" || runtime.GOOS == "darwin" || runtime.GOOS == "ios"
+ opengl = runtime.GOOS == "darwin"
+ d3d11 = runtime.GOOS == "windows"
+ vulkan = runtime.GOOS == "linux" || runtime.GOOS == "android"
+ )
+ if vulkan {
+ Shader_blit_frag[0].SPIRV = zblit_frag_0_spirv
+ }
+ if opengles {
+ Shader_blit_frag[0].GLSL100ES = zblit_frag_0_glsl100es
+ }
+ if opengl {
+ Shader_blit_frag[0].GLSL150 = zblit_frag_0_glsl150
+ }
+ if d3d11 {
+ Shader_blit_frag[0].DXBC = zblit_frag_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_blit_frag[0].MetalLib = zblit_frag_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_blit_frag[0].MetalLib = zblit_frag_0_metallibiossimulator
+ } else {
+ Shader_blit_frag[0].MetalLib = zblit_frag_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_blit_frag[1].SPIRV = zblit_frag_1_spirv
+ }
+ if opengles {
+ Shader_blit_frag[1].GLSL100ES = zblit_frag_1_glsl100es
+ }
+ if opengl {
+ Shader_blit_frag[1].GLSL150 = zblit_frag_1_glsl150
+ }
+ if d3d11 {
+ Shader_blit_frag[1].DXBC = zblit_frag_1_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_blit_frag[1].MetalLib = zblit_frag_1_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_blit_frag[1].MetalLib = zblit_frag_1_metallibiossimulator
+ } else {
+ Shader_blit_frag[1].MetalLib = zblit_frag_1_metallibios
+ }
+ }
+ if vulkan {
+ Shader_blit_frag[2].SPIRV = zblit_frag_2_spirv
+ }
+ if opengles {
+ Shader_blit_frag[2].GLSL100ES = zblit_frag_2_glsl100es
+ }
+ if opengl {
+ Shader_blit_frag[2].GLSL150 = zblit_frag_2_glsl150
+ }
+ if d3d11 {
+ Shader_blit_frag[2].DXBC = zblit_frag_2_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_blit_frag[2].MetalLib = zblit_frag_2_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_blit_frag[2].MetalLib = zblit_frag_2_metallibiossimulator
+ } else {
+ Shader_blit_frag[2].MetalLib = zblit_frag_2_metallibios
+ }
+ }
+ if vulkan {
+ Shader_blit_vert.SPIRV = zblit_vert_0_spirv
+ }
+ if opengles {
+ Shader_blit_vert.GLSL100ES = zblit_vert_0_glsl100es
+ }
+ if opengl {
+ Shader_blit_vert.GLSL150 = zblit_vert_0_glsl150
+ }
+ if d3d11 {
+ Shader_blit_vert.DXBC = zblit_vert_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_blit_vert.MetalLib = zblit_vert_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_blit_vert.MetalLib = zblit_vert_0_metallibiossimulator
+ } else {
+ Shader_blit_vert.MetalLib = zblit_vert_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_copy_frag.SPIRV = zcopy_frag_0_spirv
+ }
+ if opengles {
+ Shader_copy_frag.GLSL100ES = zcopy_frag_0_glsl100es
+ }
+ if opengl {
+ Shader_copy_frag.GLSL150 = zcopy_frag_0_glsl150
+ }
+ if d3d11 {
+ Shader_copy_frag.DXBC = zcopy_frag_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_copy_frag.MetalLib = zcopy_frag_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_copy_frag.MetalLib = zcopy_frag_0_metallibiossimulator
+ } else {
+ Shader_copy_frag.MetalLib = zcopy_frag_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_copy_vert.SPIRV = zcopy_vert_0_spirv
+ }
+ if opengles {
+ Shader_copy_vert.GLSL100ES = zcopy_vert_0_glsl100es
+ }
+ if opengl {
+ Shader_copy_vert.GLSL150 = zcopy_vert_0_glsl150
+ }
+ if d3d11 {
+ Shader_copy_vert.DXBC = zcopy_vert_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_copy_vert.MetalLib = zcopy_vert_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_copy_vert.MetalLib = zcopy_vert_0_metallibiossimulator
+ } else {
+ Shader_copy_vert.MetalLib = zcopy_vert_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_cover_frag[0].SPIRV = zcover_frag_0_spirv
+ }
+ if opengles {
+ Shader_cover_frag[0].GLSL100ES = zcover_frag_0_glsl100es
+ }
+ if opengl {
+ Shader_cover_frag[0].GLSL150 = zcover_frag_0_glsl150
+ }
+ if d3d11 {
+ Shader_cover_frag[0].DXBC = zcover_frag_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_cover_frag[0].MetalLib = zcover_frag_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_cover_frag[0].MetalLib = zcover_frag_0_metallibiossimulator
+ } else {
+ Shader_cover_frag[0].MetalLib = zcover_frag_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_cover_frag[1].SPIRV = zcover_frag_1_spirv
+ }
+ if opengles {
+ Shader_cover_frag[1].GLSL100ES = zcover_frag_1_glsl100es
+ }
+ if opengl {
+ Shader_cover_frag[1].GLSL150 = zcover_frag_1_glsl150
+ }
+ if d3d11 {
+ Shader_cover_frag[1].DXBC = zcover_frag_1_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_cover_frag[1].MetalLib = zcover_frag_1_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_cover_frag[1].MetalLib = zcover_frag_1_metallibiossimulator
+ } else {
+ Shader_cover_frag[1].MetalLib = zcover_frag_1_metallibios
+ }
+ }
+ if vulkan {
+ Shader_cover_frag[2].SPIRV = zcover_frag_2_spirv
+ }
+ if opengles {
+ Shader_cover_frag[2].GLSL100ES = zcover_frag_2_glsl100es
+ }
+ if opengl {
+ Shader_cover_frag[2].GLSL150 = zcover_frag_2_glsl150
+ }
+ if d3d11 {
+ Shader_cover_frag[2].DXBC = zcover_frag_2_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_cover_frag[2].MetalLib = zcover_frag_2_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_cover_frag[2].MetalLib = zcover_frag_2_metallibiossimulator
+ } else {
+ Shader_cover_frag[2].MetalLib = zcover_frag_2_metallibios
+ }
+ }
+ if vulkan {
+ Shader_cover_vert.SPIRV = zcover_vert_0_spirv
+ }
+ if opengles {
+ Shader_cover_vert.GLSL100ES = zcover_vert_0_glsl100es
+ }
+ if opengl {
+ Shader_cover_vert.GLSL150 = zcover_vert_0_glsl150
+ }
+ if d3d11 {
+ Shader_cover_vert.DXBC = zcover_vert_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_cover_vert.MetalLib = zcover_vert_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_cover_vert.MetalLib = zcover_vert_0_metallibiossimulator
+ } else {
+ Shader_cover_vert.MetalLib = zcover_vert_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_input_vert.SPIRV = zinput_vert_0_spirv
+ }
+ if opengles {
+ Shader_input_vert.GLSL100ES = zinput_vert_0_glsl100es
+ }
+ if opengl {
+ Shader_input_vert.GLSL150 = zinput_vert_0_glsl150
+ }
+ if d3d11 {
+ Shader_input_vert.DXBC = zinput_vert_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_input_vert.MetalLib = zinput_vert_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_input_vert.MetalLib = zinput_vert_0_metallibiossimulator
+ } else {
+ Shader_input_vert.MetalLib = zinput_vert_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_intersect_frag.SPIRV = zintersect_frag_0_spirv
+ }
+ if opengles {
+ Shader_intersect_frag.GLSL100ES = zintersect_frag_0_glsl100es
+ }
+ if opengl {
+ Shader_intersect_frag.GLSL150 = zintersect_frag_0_glsl150
+ }
+ if d3d11 {
+ Shader_intersect_frag.DXBC = zintersect_frag_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_intersect_frag.MetalLib = zintersect_frag_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_intersect_frag.MetalLib = zintersect_frag_0_metallibiossimulator
+ } else {
+ Shader_intersect_frag.MetalLib = zintersect_frag_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_intersect_vert.SPIRV = zintersect_vert_0_spirv
+ }
+ if opengles {
+ Shader_intersect_vert.GLSL100ES = zintersect_vert_0_glsl100es
+ }
+ if opengl {
+ Shader_intersect_vert.GLSL150 = zintersect_vert_0_glsl150
+ }
+ if d3d11 {
+ Shader_intersect_vert.DXBC = zintersect_vert_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_intersect_vert.MetalLib = zintersect_vert_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_intersect_vert.MetalLib = zintersect_vert_0_metallibiossimulator
+ } else {
+ Shader_intersect_vert.MetalLib = zintersect_vert_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_material_frag.SPIRV = zmaterial_frag_0_spirv
+ }
+ if opengles {
+ Shader_material_frag.GLSL100ES = zmaterial_frag_0_glsl100es
+ }
+ if opengl {
+ Shader_material_frag.GLSL150 = zmaterial_frag_0_glsl150
+ }
+ if d3d11 {
+ Shader_material_frag.DXBC = zmaterial_frag_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_material_frag.MetalLib = zmaterial_frag_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_material_frag.MetalLib = zmaterial_frag_0_metallibiossimulator
+ } else {
+ Shader_material_frag.MetalLib = zmaterial_frag_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_material_vert.SPIRV = zmaterial_vert_0_spirv
+ }
+ if opengles {
+ Shader_material_vert.GLSL100ES = zmaterial_vert_0_glsl100es
+ }
+ if opengl {
+ Shader_material_vert.GLSL150 = zmaterial_vert_0_glsl150
+ }
+ if d3d11 {
+ Shader_material_vert.DXBC = zmaterial_vert_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_material_vert.MetalLib = zmaterial_vert_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_material_vert.MetalLib = zmaterial_vert_0_metallibiossimulator
+ } else {
+ Shader_material_vert.MetalLib = zmaterial_vert_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_simple_frag.SPIRV = zsimple_frag_0_spirv
+ }
+ if opengles {
+ Shader_simple_frag.GLSL100ES = zsimple_frag_0_glsl100es
+ }
+ if opengl {
+ Shader_simple_frag.GLSL150 = zsimple_frag_0_glsl150
+ }
+ if d3d11 {
+ Shader_simple_frag.DXBC = zsimple_frag_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_simple_frag.MetalLib = zsimple_frag_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_simple_frag.MetalLib = zsimple_frag_0_metallibiossimulator
+ } else {
+ Shader_simple_frag.MetalLib = zsimple_frag_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_stencil_frag.SPIRV = zstencil_frag_0_spirv
+ }
+ if opengles {
+ Shader_stencil_frag.GLSL100ES = zstencil_frag_0_glsl100es
+ }
+ if opengl {
+ Shader_stencil_frag.GLSL150 = zstencil_frag_0_glsl150
+ }
+ if d3d11 {
+ Shader_stencil_frag.DXBC = zstencil_frag_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_stencil_frag.MetalLib = zstencil_frag_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_stencil_frag.MetalLib = zstencil_frag_0_metallibiossimulator
+ } else {
+ Shader_stencil_frag.MetalLib = zstencil_frag_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_stencil_vert.SPIRV = zstencil_vert_0_spirv
+ }
+ if opengles {
+ Shader_stencil_vert.GLSL100ES = zstencil_vert_0_glsl100es
+ }
+ if opengl {
+ Shader_stencil_vert.GLSL150 = zstencil_vert_0_glsl150
+ }
+ if d3d11 {
+ Shader_stencil_vert.DXBC = zstencil_vert_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_stencil_vert.MetalLib = zstencil_vert_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_stencil_vert.MetalLib = zstencil_vert_0_metallibiossimulator
+ } else {
+ Shader_stencil_vert.MetalLib = zstencil_vert_0_metallibios
+ }
+ }
+}
diff --git a/vendor/gioui.org/shader/gio/simple.frag b/vendor/gioui.org/shader/gio/simple.frag
new file mode 100644
index 0000000..4614f33
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/simple.frag
@@ -0,0 +1,11 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+precision mediump float;
+
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = vec4(.25, .55, .75, 1.0);
+}
diff --git a/vendor/gioui.org/shader/gio/stencil.frag b/vendor/gioui.org/shader/gio/stencil.frag
new file mode 100644
index 0000000..956dae8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/stencil.frag
@@ -0,0 +1,81 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+precision mediump float;
+
+layout(location=0) in highp vec2 vFrom;
+layout(location=1) in highp vec2 vCtrl;
+layout(location=2) in highp vec2 vTo;
+
+layout(location = 0) out vec4 fragCover;
+
+void main() {
+ float dx = vTo.x - vFrom.x;
+ // Sort from and to in increasing order so the root below
+ // is always the positive square root, if any.
+ // We need the direction of the curve below, so this can't be
+ // done from the vertex shader.
+ bool increasing = vTo.x >= vFrom.x;
+ vec2 left = increasing ? vFrom : vTo;
+ vec2 right = increasing ? vTo : vFrom;
+
+ // The signed horizontal extent of the fragment.
+ vec2 extent = clamp(vec2(vFrom.x, vTo.x), -0.5, 0.5);
+ // Find the t where the curve crosses the middle of the
+ // extent, x₀.
+ // Given the Bézier curve with x coordinates P₀, P₁, P₂
+ // where P₀ is at the origin, its x coordinate in t
+ // is given by:
+ //
+ // x(t) = 2(1-t)tP₁ + t²P₂
+ //
+ // Rearranging:
+ //
+ // x(t) = (P₂ - 2P₁)t² + 2P₁t
+ //
+ // Setting x(t) = x₀ and using Muller's quadratic formula ("Citardauq")
+ // for robustnesss,
+ //
+ // t = 2x₀/(2P₁±√(4P₁²+4(P₂-2P₁)x₀))
+ //
+ // which simplifies to
+ //
+ // t = x₀/(P₁±√(P₁²+(P₂-2P₁)x₀))
+ //
+ // Setting v = P₂-P₁,
+ //
+ // t = x₀/(P₁±√(P₁²+(v-P₁)x₀))
+ //
+ // t lie in [0; 1]; P₂ ≥ P₁ and P₁ ≥ 0 since we split curves where
+ // the control point lies before the start point or after the end point.
+ // It can then be shown that only the positive square root is valid.
+ float midx = mix(extent.x, extent.y, 0.5);
+ float x0 = midx - left.x;
+ vec2 p1 = vCtrl - left;
+ vec2 v = right - vCtrl;
+ float t = x0/(p1.x+sqrt(p1.x*p1.x+(v.x-p1.x)*x0));
+ // Find y(t) on the curve.
+ float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);
+ // And the slope.
+ vec2 d_half = mix(p1, v, t);
+ float dy = d_half.y/d_half.x;
+ // Together, y and dy form a line approximation.
+
+ // Compute the fragment area above the line.
+ // The area is symmetric around dy = 0. Scale slope with extent width.
+ float width = extent.y - extent.x;
+ dy = abs(dy*width);
+
+ vec4 sides = vec4(dy*+0.5 + y, dy*-0.5 + y, (+0.5-y)/dy, (-0.5-y)/dy);
+ sides = clamp(sides+0.5, 0.0, 1.0);
+
+ float area = 0.5*(sides.z - sides.z*sides.y + 1.0 - sides.x+sides.x*sides.w);
+ area *= width;
+
+ // Work around issue #13.
+ if (width == 0.0)
+ area = 0.0;
+
+ fragCover.r = area;
+}
diff --git a/vendor/gioui.org/shader/gio/stencil.vert b/vendor/gioui.org/shader/gio/stencil.vert
new file mode 100644
index 0000000..fd6f28b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/stencil.vert
@@ -0,0 +1,57 @@
+#version 310 es
+
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#extension GL_GOOGLE_include_directive : enable
+
+precision highp float;
+
+#include "common.h"
+
+layout(push_constant) uniform Block {
+ vec4 transform;
+ vec2 pathOffset;
+} _block;
+
+layout(location=0) in float corner;
+layout(location=1) in float maxy;
+layout(location=2) in vec2 from;
+layout(location=3) in vec2 ctrl;
+layout(location=4) in vec2 to;
+
+layout(location=0) out vec2 vFrom;
+layout(location=1) out vec2 vCtrl;
+layout(location=2) out vec2 vTo;
+
+void main() {
+ // Add a one pixel overlap so curve quads cover their
+ // entire curves. Could use conservative rasterization
+ // if available.
+ vec2 from = from + _block.pathOffset;
+ vec2 ctrl = ctrl + _block.pathOffset;
+ vec2 to = to + _block.pathOffset;
+ float maxy = maxy + _block.pathOffset.y;
+ vec2 pos;
+ float c = corner;
+ if (c >= 0.375) {
+ // North.
+ c -= 0.5;
+ pos.y = maxy + 1.0;
+ } else {
+ // South.
+ pos.y = min(min(from.y, ctrl.y), to.y) - 1.0;
+ }
+ if (c >= 0.125) {
+ // East.
+ pos.x = max(max(from.x, ctrl.x), to.x)+1.0;
+ } else {
+ // West.
+ pos.x = min(min(from.x, ctrl.x), to.x)-1.0;
+ }
+ vFrom = from-pos;
+ vCtrl = ctrl-pos;
+ vTo = to-pos;
+ pos = pos*_block.transform.xy + _block.transform.zw;
+ gl_Position = vec4(transform3x2(fboTransform, vec3(pos, 0)), 1);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.dxbc b/vendor/gioui.org/shader/gio/zblit.frag.0.dxbc
new file mode 100644
index 0000000..45e8355
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl100es
new file mode 100644
index 0000000..d7ca41a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl100es
@@ -0,0 +1,18 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+struct Color
+{
+ vec4 color;
+};
+
+uniform Color _color;
+
+varying highp vec2 vUV;
+
+void main()
+{
+ gl_FragData[0] = _color.color;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl150
new file mode 100644
index 0000000..9559c85
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl150
@@ -0,0 +1,17 @@
+#version 150
+
+struct Color
+{
+ vec4 color;
+};
+
+uniform Color _color;
+
+out vec4 fragColor;
+in vec2 vUV;
+
+void main()
+{
+ fragColor = _color.color;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.metallibios b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibios
new file mode 100644
index 0000000..63e606f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibiossimulator
new file mode 100644
index 0000000..d8d2589
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibmacos
new file mode 100644
index 0000000..85946aa
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.spirv b/vendor/gioui.org/shader/gio/zblit.frag.0.spirv
new file mode 100644
index 0000000..c2a88d2
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.dxbc b/vendor/gioui.org/shader/gio/zblit.frag.1.dxbc
new file mode 100644
index 0000000..ddb8dad
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.1.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.glsl100es b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl100es
new file mode 100644
index 0000000..ccc6d2a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl100es
@@ -0,0 +1,19 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+struct Gradient
+{
+ vec4 color1;
+ vec4 color2;
+};
+
+uniform Gradient _gradient;
+
+varying highp vec2 vUV;
+
+void main()
+{
+ gl_FragData[0] = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0)));
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.glsl150 b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl150
new file mode 100644
index 0000000..a55f29f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl150
@@ -0,0 +1,18 @@
+#version 150
+
+struct Gradient
+{
+ vec4 color1;
+ vec4 color2;
+};
+
+uniform Gradient _gradient;
+
+out vec4 fragColor;
+in vec2 vUV;
+
+void main()
+{
+ fragColor = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0)));
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.metallibios b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibios
new file mode 100644
index 0000000..680c495
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibiossimulator
new file mode 100644
index 0000000..ef5ac86
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.metallibmacos b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibmacos
new file mode 100644
index 0000000..5fbd2c2
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.spirv b/vendor/gioui.org/shader/gio/zblit.frag.1.spirv
new file mode 100644
index 0000000..88128fc
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.1.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.dxbc b/vendor/gioui.org/shader/gio/zblit.frag.2.dxbc
new file mode 100644
index 0000000..fd95db8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.2.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.glsl100es b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl100es
new file mode 100644
index 0000000..5c2d832
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl100es
@@ -0,0 +1,13 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+uniform mediump sampler2D tex;
+
+varying highp vec2 vUV;
+
+void main()
+{
+ gl_FragData[0] = texture2D(tex, vUV);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.glsl150 b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl150
new file mode 100644
index 0000000..f5df869
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl150
@@ -0,0 +1,12 @@
+#version 150
+
+uniform sampler2D tex;
+
+out vec4 fragColor;
+in vec2 vUV;
+
+void main()
+{
+ fragColor = texture(tex, vUV);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.metallibios b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibios
new file mode 100644
index 0000000..c4eb922
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibiossimulator
new file mode 100644
index 0000000..81f7aa3
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.metallibmacos b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibmacos
new file mode 100644
index 0000000..70b3da8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.spirv b/vendor/gioui.org/shader/gio/zblit.frag.2.spirv
new file mode 100644
index 0000000..efa380b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.frag.2.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.dxbc b/vendor/gioui.org/shader/gio/zblit.vert.0.dxbc
new file mode 100644
index 0000000..5ad9601
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.vert.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl100es
new file mode 100644
index 0000000..61dee04
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl100es
@@ -0,0 +1,37 @@
+#version 100
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 transform;
+ vec4 uvTransformR1;
+ vec4 uvTransformR2;
+};
+
+uniform Block _block;
+
+attribute vec2 pos;
+varying vec2 vUV;
+attribute vec2 uv;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vec2 p = (pos * _block.transform.xy) + _block.transform.zw;
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+ m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz);
+ vec3 param_3 = vec3(uv, 1.0);
+ vUV = transform3x2(param_2, param_3).xy;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl150
new file mode 100644
index 0000000..eba9f1a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl150
@@ -0,0 +1,37 @@
+#version 150
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 transform;
+ vec4 uvTransformR1;
+ vec4 uvTransformR2;
+};
+
+uniform Block _block;
+
+in vec2 pos;
+out vec2 vUV;
+in vec2 uv;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vec2 p = (pos * _block.transform.xy) + _block.transform.zw;
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+ m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz);
+ vec3 param_3 = vec3(uv, 1.0);
+ vUV = transform3x2(param_2, param_3).xy;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.metallibios b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibios
new file mode 100644
index 0000000..2450ec9
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibiossimulator
new file mode 100644
index 0000000..6b4960b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibmacos
new file mode 100644
index 0000000..d902b36
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.spirv b/vendor/gioui.org/shader/gio/zblit.vert.0.spirv
new file mode 100644
index 0000000..4f762bb
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zblit.vert.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.dxbc b/vendor/gioui.org/shader/gio/zcopy.frag.0.dxbc
new file mode 100644
index 0000000..a521803
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl100es
new file mode 100644
index 0000000..b2dd95a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl100es
@@ -0,0 +1,27 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+uniform mediump sampler2D tex;
+
+varying highp vec2 vUV;
+
+vec3 sRGBtoRGB(vec3 rgb)
+{
+ bvec3 cutoff = greaterThanEqual(rgb, vec3(0.040449999272823333740234375));
+ vec3 below = rgb / vec3(12.9200000762939453125);
+ vec3 above = pow((rgb + vec3(0.054999999701976776123046875)) / vec3(1.05499994754791259765625), vec3(2.400000095367431640625));
+ return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z);
+}
+
+void main()
+{
+ vec4 texel = texture2D(tex, vUV);
+ vec3 param = texel.xyz;
+ vec3 _59 = sRGBtoRGB(param);
+ texel.x = _59.x;
+ texel.y = _59.y;
+ texel.z = _59.z;
+ gl_FragData[0] = texel;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl150
new file mode 100644
index 0000000..799add1
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl150
@@ -0,0 +1,26 @@
+#version 150
+
+uniform sampler2D tex;
+
+in vec2 vUV;
+out vec4 fragColor;
+
+vec3 sRGBtoRGB(vec3 rgb)
+{
+ bvec3 cutoff = greaterThanEqual(rgb, vec3(0.040449999272823333740234375));
+ vec3 below = rgb / vec3(12.9200000762939453125);
+ vec3 above = pow((rgb + vec3(0.054999999701976776123046875)) / vec3(1.05499994754791259765625), vec3(2.400000095367431640625));
+ return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z);
+}
+
+void main()
+{
+ vec4 texel = texture(tex, vUV);
+ vec3 param = texel.xyz;
+ vec3 _59 = sRGBtoRGB(param);
+ texel.x = _59.x;
+ texel.y = _59.y;
+ texel.z = _59.z;
+ fragColor = texel;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibios b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibios
new file mode 100644
index 0000000..f143069
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiossimulator
new file mode 100644
index 0000000..8fe4c74
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibmacos
new file mode 100644
index 0000000..2e43803
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.spirv b/vendor/gioui.org/shader/gio/zcopy.frag.0.spirv
new file mode 100644
index 0000000..3299552
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.dxbc b/vendor/gioui.org/shader/gio/zcopy.vert.0.dxbc
new file mode 100644
index 0000000..acce7ab
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl100es
new file mode 100644
index 0000000..ff0be47
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl100es
@@ -0,0 +1,35 @@
+#version 100
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec2 scale;
+ vec2 pos;
+ vec2 uvScale;
+};
+
+uniform Block _block;
+
+varying vec2 vUV;
+attribute vec2 uv;
+attribute vec2 pos;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vUV = vec2(uv * _block.uvScale);
+ vec2 p = vec2((pos * _block.scale) + _block.pos);
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl150
new file mode 100644
index 0000000..036cd39
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl150
@@ -0,0 +1,35 @@
+#version 150
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec2 scale;
+ vec2 pos;
+ vec2 uvScale;
+};
+
+uniform Block _block;
+
+out vec2 vUV;
+in vec2 uv;
+in vec2 pos;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vUV = vec2(uv * _block.uvScale);
+ vec2 p = vec2((pos * _block.scale) + _block.pos);
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibios b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibios
new file mode 100644
index 0000000..25f5e56
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiossimulator
new file mode 100644
index 0000000..f696caf
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibmacos
new file mode 100644
index 0000000..d4a5777
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.spirv b/vendor/gioui.org/shader/gio/zcopy.vert.0.spirv
new file mode 100644
index 0000000..7251754
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.dxbc b/vendor/gioui.org/shader/gio/zcover.frag.0.dxbc
new file mode 100644
index 0000000..8b77ae4
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl100es
new file mode 100644
index 0000000..2399901
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl100es
@@ -0,0 +1,23 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+struct Color
+{
+ vec4 color;
+};
+
+uniform Color _color;
+
+uniform mediump sampler2D cover;
+
+varying highp vec2 vCoverUV;
+varying highp vec2 vUV;
+
+void main()
+{
+ gl_FragData[0] = _color.color;
+ float c = min(abs(texture2D(cover, vCoverUV).x), 1.0);
+ gl_FragData[0] *= c;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl150
new file mode 100644
index 0000000..ce1beaa
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl150
@@ -0,0 +1,22 @@
+#version 150
+
+struct Color
+{
+ vec4 color;
+};
+
+uniform Color _color;
+
+uniform sampler2D cover;
+
+out vec4 fragColor;
+in vec2 vCoverUV;
+in vec2 vUV;
+
+void main()
+{
+ fragColor = _color.color;
+ float c = min(abs(texture(cover, vCoverUV).x), 1.0);
+ fragColor *= c;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.metallibios b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibios
new file mode 100644
index 0000000..22256c8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibiossimulator
new file mode 100644
index 0000000..c8040ad
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibmacos
new file mode 100644
index 0000000..fe25160
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.spirv b/vendor/gioui.org/shader/gio/zcover.frag.0.spirv
new file mode 100644
index 0000000..2061ff1
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.dxbc b/vendor/gioui.org/shader/gio/zcover.frag.1.dxbc
new file mode 100644
index 0000000..c9a44cf
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.1.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.glsl100es b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl100es
new file mode 100644
index 0000000..01cc88e
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl100es
@@ -0,0 +1,24 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+struct Gradient
+{
+ vec4 color1;
+ vec4 color2;
+};
+
+uniform Gradient _gradient;
+
+uniform mediump sampler2D cover;
+
+varying highp vec2 vUV;
+varying highp vec2 vCoverUV;
+
+void main()
+{
+ gl_FragData[0] = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0)));
+ float c = min(abs(texture2D(cover, vCoverUV).x), 1.0);
+ gl_FragData[0] *= c;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.glsl150 b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl150
new file mode 100644
index 0000000..3f832c3
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl150
@@ -0,0 +1,23 @@
+#version 150
+
+struct Gradient
+{
+ vec4 color1;
+ vec4 color2;
+};
+
+uniform Gradient _gradient;
+
+uniform sampler2D cover;
+
+out vec4 fragColor;
+in vec2 vUV;
+in vec2 vCoverUV;
+
+void main()
+{
+ fragColor = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0)));
+ float c = min(abs(texture(cover, vCoverUV).x), 1.0);
+ fragColor *= c;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.metallibios b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibios
new file mode 100644
index 0000000..228996a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibiossimulator
new file mode 100644
index 0000000..1c99553
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.metallibmacos b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibmacos
new file mode 100644
index 0000000..889e249
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.spirv b/vendor/gioui.org/shader/gio/zcover.frag.1.spirv
new file mode 100644
index 0000000..4c48d20
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.1.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.dxbc b/vendor/gioui.org/shader/gio/zcover.frag.2.dxbc
new file mode 100644
index 0000000..d8c6a80
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.2.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.glsl100es b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl100es
new file mode 100644
index 0000000..7cfde4b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl100es
@@ -0,0 +1,17 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+uniform mediump sampler2D tex;
+uniform mediump sampler2D cover;
+
+varying highp vec2 vUV;
+varying highp vec2 vCoverUV;
+
+void main()
+{
+ gl_FragData[0] = texture2D(tex, vUV);
+ float c = min(abs(texture2D(cover, vCoverUV).x), 1.0);
+ gl_FragData[0] *= c;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.glsl150 b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl150
new file mode 100644
index 0000000..939baee
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl150
@@ -0,0 +1,16 @@
+#version 150
+
+uniform sampler2D tex;
+uniform sampler2D cover;
+
+out vec4 fragColor;
+in vec2 vUV;
+in vec2 vCoverUV;
+
+void main()
+{
+ fragColor = texture(tex, vUV);
+ float c = min(abs(texture(cover, vCoverUV).x), 1.0);
+ fragColor *= c;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.metallibios b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibios
new file mode 100644
index 0000000..6eba400
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibiossimulator
new file mode 100644
index 0000000..09ddb1f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.metallibmacos b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibmacos
new file mode 100644
index 0000000..3ba9105
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.spirv b/vendor/gioui.org/shader/gio/zcover.frag.2.spirv
new file mode 100644
index 0000000..cd618ce
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.frag.2.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.dxbc b/vendor/gioui.org/shader/gio/zcover.vert.0.dxbc
new file mode 100644
index 0000000..3b6cf6b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.vert.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl100es
new file mode 100644
index 0000000..423ca60
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl100es
@@ -0,0 +1,41 @@
+#version 100
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 transform;
+ vec4 uvCoverTransform;
+ vec4 uvTransformR1;
+ vec4 uvTransformR2;
+};
+
+uniform Block _block;
+
+attribute vec2 pos;
+varying vec2 vUV;
+attribute vec2 uv;
+varying vec2 vCoverUV;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vec2 p = vec2((pos * _block.transform.xy) + _block.transform.zw);
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+ m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz);
+ vec3 param_3 = vec3(uv, 1.0);
+ vUV = transform3x2(param_2, param_3).xy;
+ vec3 uv3 = vec3(uv, 1.0);
+ vCoverUV = ((uv3 * vec3(_block.uvCoverTransform.xy, 1.0)) + vec3(_block.uvCoverTransform.zw, 0.0)).xy;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl150
new file mode 100644
index 0000000..a5005d6
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl150
@@ -0,0 +1,41 @@
+#version 150
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 transform;
+ vec4 uvCoverTransform;
+ vec4 uvTransformR1;
+ vec4 uvTransformR2;
+};
+
+uniform Block _block;
+
+in vec2 pos;
+out vec2 vUV;
+in vec2 uv;
+out vec2 vCoverUV;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vec2 p = vec2((pos * _block.transform.xy) + _block.transform.zw);
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+ m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz);
+ vec3 param_3 = vec3(uv, 1.0);
+ vUV = transform3x2(param_2, param_3).xy;
+ vec3 uv3 = vec3(uv, 1.0);
+ vCoverUV = ((uv3 * vec3(_block.uvCoverTransform.xy, 1.0)) + vec3(_block.uvCoverTransform.zw, 0.0)).xy;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.metallibios b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibios
new file mode 100644
index 0000000..335704b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibiossimulator
new file mode 100644
index 0000000..96470cd
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibmacos
new file mode 100644
index 0000000..629e82b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.spirv b/vendor/gioui.org/shader/gio/zcover.vert.0.spirv
new file mode 100644
index 0000000..f9af8ab
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zcover.vert.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.dxbc b/vendor/gioui.org/shader/gio/zinput.vert.0.dxbc
new file mode 100644
index 0000000..096bd01
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zinput.vert.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl100es
new file mode 100644
index 0000000..d1d4c8d
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl100es
@@ -0,0 +1,22 @@
+#version 100
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+attribute vec4 position;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = position.xyz;
+ gl_Position = vec4(transform3x2(param, param_1), position.w);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl150
new file mode 100644
index 0000000..ab28308
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl150
@@ -0,0 +1,22 @@
+#version 150
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+in vec4 position;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));
+ vec3 param_1 = position.xyz;
+ gl_Position = vec4(transform3x2(param, param_1), position.w);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.metallibios b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibios
new file mode 100644
index 0000000..6fdc427
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibiossimulator
new file mode 100644
index 0000000..da28890
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibmacos
new file mode 100644
index 0000000..064c501
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.spirv b/vendor/gioui.org/shader/gio/zinput.vert.0.spirv
new file mode 100644
index 0000000..eb999e5
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zinput.vert.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.dxbc b/vendor/gioui.org/shader/gio/zintersect.frag.0.dxbc
new file mode 100644
index 0000000..f2081ee
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl100es
new file mode 100644
index 0000000..90fe2f9
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl100es
@@ -0,0 +1,13 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+uniform mediump sampler2D cover;
+
+varying highp vec2 vUV;
+
+void main()
+{
+ gl_FragData[0].x = abs(texture2D(cover, vUV).x);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl150
new file mode 100644
index 0000000..f574aae
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl150
@@ -0,0 +1,12 @@
+#version 150
+
+uniform sampler2D cover;
+
+out vec4 fragColor;
+in vec2 vUV;
+
+void main()
+{
+ fragColor.x = abs(texture(cover, vUV).x);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibios b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibios
new file mode 100644
index 0000000..f48da4e
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiossimulator
new file mode 100644
index 0000000..b1a40c4
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibmacos
new file mode 100644
index 0000000..f16d190
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.spirv b/vendor/gioui.org/shader/gio/zintersect.frag.0.spirv
new file mode 100644
index 0000000..6c28644
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.dxbc b/vendor/gioui.org/shader/gio/zintersect.vert.0.dxbc
new file mode 100644
index 0000000..03005ea
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl100es
new file mode 100644
index 0000000..4441763
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl100es
@@ -0,0 +1,35 @@
+#version 100
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 uvTransform;
+ vec4 subUVTransform;
+};
+
+uniform Block _block;
+
+attribute vec2 pos;
+varying vec2 vUV;
+attribute vec2 uv;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
+ vec3 param_1 = vec3(pos, 1.0);
+ vec3 p = transform3x2(param, param_1);
+ gl_Position = vec4(p, 1.0);
+ vUV = (uv * _block.subUVTransform.xy) + _block.subUVTransform.zw;
+ vUV = (vUV * _block.uvTransform.xy) + _block.uvTransform.zw;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl150
new file mode 100644
index 0000000..656b4c9
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl150
@@ -0,0 +1,35 @@
+#version 150
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 uvTransform;
+ vec4 subUVTransform;
+};
+
+uniform Block _block;
+
+in vec2 pos;
+out vec2 vUV;
+in vec2 uv;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
+ vec3 param_1 = vec3(pos, 1.0);
+ vec3 p = transform3x2(param, param_1);
+ gl_Position = vec4(p, 1.0);
+ vUV = (uv * _block.subUVTransform.xy) + _block.subUVTransform.zw;
+ vUV = (vUV * _block.uvTransform.xy) + _block.uvTransform.zw;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibios b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibios
new file mode 100644
index 0000000..975fbb8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiossimulator
new file mode 100644
index 0000000..682bad1
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibmacos
new file mode 100644
index 0000000..a43236d
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.spirv b/vendor/gioui.org/shader/gio/zintersect.vert.0.spirv
new file mode 100644
index 0000000..13e7335
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.dxbc b/vendor/gioui.org/shader/gio/zmaterial.frag.0.dxbc
new file mode 100644
index 0000000..e250378
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl100es
new file mode 100644
index 0000000..f2f2a30
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl100es
@@ -0,0 +1,37 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+struct Color
+{
+ float emulateSRGB;
+};
+
+uniform Color _color;
+
+uniform mediump sampler2D tex;
+
+varying highp vec2 vUV;
+
+vec3 RGBtosRGB(vec3 rgb)
+{
+ bvec3 cutoff = greaterThanEqual(rgb, vec3(0.003130800090730190277099609375));
+ vec3 below = vec3(12.9200000762939453125) * rgb;
+ vec3 above = (vec3(1.05499994754791259765625) * pow(rgb, vec3(0.416660010814666748046875))) - vec3(0.054999999701976776123046875);
+ return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z);
+}
+
+void main()
+{
+ vec4 texel = texture2D(tex, vUV);
+ if (_color.emulateSRGB == 0.0)
+ {
+ vec3 param = texel.xyz;
+ vec3 _71 = RGBtosRGB(param);
+ texel.x = _71.x;
+ texel.y = _71.y;
+ texel.z = _71.z;
+ }
+ gl_FragData[0] = texel;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl150
new file mode 100644
index 0000000..6b8bed3
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl150
@@ -0,0 +1,36 @@
+#version 150
+
+struct Color
+{
+ float emulateSRGB;
+};
+
+uniform Color _color;
+
+uniform sampler2D tex;
+
+in vec2 vUV;
+out vec4 fragColor;
+
+vec3 RGBtosRGB(vec3 rgb)
+{
+ bvec3 cutoff = greaterThanEqual(rgb, vec3(0.003130800090730190277099609375));
+ vec3 below = vec3(12.9200000762939453125) * rgb;
+ vec3 above = (vec3(1.05499994754791259765625) * pow(rgb, vec3(0.416660010814666748046875))) - vec3(0.054999999701976776123046875);
+ return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z);
+}
+
+void main()
+{
+ vec4 texel = texture(tex, vUV);
+ if (_color.emulateSRGB == 0.0)
+ {
+ vec3 param = texel.xyz;
+ vec3 _71 = RGBtosRGB(param);
+ texel.x = _71.x;
+ texel.y = _71.y;
+ texel.z = _71.z;
+ }
+ fragColor = texel;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibios b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibios
new file mode 100644
index 0000000..8679f2a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiossimulator
new file mode 100644
index 0000000..6306d9a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibmacos
new file mode 100644
index 0000000..911948e
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.spirv b/vendor/gioui.org/shader/gio/zmaterial.frag.0.spirv
new file mode 100644
index 0000000..475cfb8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.dxbc b/vendor/gioui.org/shader/gio/zmaterial.vert.0.dxbc
new file mode 100644
index 0000000..a5056cc
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl100es
new file mode 100644
index 0000000..39a7b50
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl100es
@@ -0,0 +1,34 @@
+#version 100
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec2 scale;
+ vec2 pos;
+};
+
+uniform Block _block;
+
+varying vec2 vUV;
+attribute vec2 uv;
+attribute vec2 pos;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vUV = uv;
+ vec2 p = vec2((pos * _block.scale) + _block.pos);
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl150
new file mode 100644
index 0000000..be02b44
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl150
@@ -0,0 +1,34 @@
+#version 150
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec2 scale;
+ vec2 pos;
+};
+
+uniform Block _block;
+
+out vec2 vUV;
+in vec2 uv;
+in vec2 pos;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vUV = uv;
+ vec2 p = vec2((pos * _block.scale) + _block.pos);
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
+ vec3 param_1 = vec3(p, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibios b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibios
new file mode 100644
index 0000000..19cf6d8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiossimulator
new file mode 100644
index 0000000..c710ca9
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibmacos
new file mode 100644
index 0000000..cfbdfbc
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.spirv b/vendor/gioui.org/shader/gio/zmaterial.vert.0.spirv
new file mode 100644
index 0000000..18e731f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.dxbc b/vendor/gioui.org/shader/gio/zsimple.frag.0.dxbc
new file mode 100644
index 0000000..f4f8894
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl100es
new file mode 100644
index 0000000..0f86af5
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl100es
@@ -0,0 +1,9 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+void main()
+{
+ gl_FragData[0] = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl150
new file mode 100644
index 0000000..db4c060
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl150
@@ -0,0 +1,9 @@
+#version 150
+
+out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibios b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibios
new file mode 100644
index 0000000..05e6329
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiossimulator
new file mode 100644
index 0000000..2c87af9
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibmacos
new file mode 100644
index 0000000..407d7ea
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.spirv b/vendor/gioui.org/shader/gio/zsimple.frag.0.spirv
new file mode 100644
index 0000000..2b2aba3
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.dxbc b/vendor/gioui.org/shader/gio/zstencil.frag.0.dxbc
new file mode 100644
index 0000000..ded34dc
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl100es
new file mode 100644
index 0000000..15530e3
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl100es
@@ -0,0 +1,38 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+varying highp vec2 vTo;
+varying highp vec2 vFrom;
+varying highp vec2 vCtrl;
+
+void main()
+{
+ float dx = vTo.x - vFrom.x;
+ bool increasing = vTo.x >= vFrom.x;
+ bvec2 _35 = bvec2(increasing);
+ vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);
+ bvec2 _41 = bvec2(increasing);
+ vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);
+ vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));
+ float midx = mix(extent.x, extent.y, 0.5);
+ float x0 = midx - left.x;
+ vec2 p1 = vCtrl - left;
+ vec2 v = right - vCtrl;
+ float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));
+ float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);
+ vec2 d_half = mix(p1, v, vec2(t));
+ float dy = d_half.y / d_half.x;
+ float width = extent.y - extent.x;
+ dy = abs(dy * width);
+ vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);
+ sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));
+ float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));
+ area *= width;
+ if (width == 0.0)
+ {
+ area = 0.0;
+ }
+ gl_FragData[0].x = area;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl150
new file mode 100644
index 0000000..8e2abd8
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl150
@@ -0,0 +1,37 @@
+#version 150
+
+in vec2 vTo;
+in vec2 vFrom;
+in vec2 vCtrl;
+out vec4 fragCover;
+
+void main()
+{
+ float dx = vTo.x - vFrom.x;
+ bool increasing = vTo.x >= vFrom.x;
+ bvec2 _35 = bvec2(increasing);
+ vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);
+ bvec2 _41 = bvec2(increasing);
+ vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);
+ vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));
+ float midx = mix(extent.x, extent.y, 0.5);
+ float x0 = midx - left.x;
+ vec2 p1 = vCtrl - left;
+ vec2 v = right - vCtrl;
+ float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));
+ float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);
+ vec2 d_half = mix(p1, v, vec2(t));
+ float dy = d_half.y / d_half.x;
+ float width = extent.y - extent.x;
+ dy = abs(dy * width);
+ vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);
+ sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));
+ float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));
+ area *= width;
+ if (width == 0.0)
+ {
+ area = 0.0;
+ }
+ fragCover.x = area;
+}
+
diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibios b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibios
new file mode 100644
index 0000000..52f668f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiossimulator
new file mode 100644
index 0000000..3fbfa8f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibmacos
new file mode 100644
index 0000000..358a02a
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.spirv b/vendor/gioui.org/shader/gio/zstencil.frag.0.spirv
new file mode 100644
index 0000000..9a22d9e
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.dxbc b/vendor/gioui.org/shader/gio/zstencil.vert.0.dxbc
new file mode 100644
index 0000000..505672f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl100es
new file mode 100644
index 0000000..c8e1261
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl100es
@@ -0,0 +1,64 @@
+#version 100
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 transform;
+ vec2 pathOffset;
+};
+
+uniform Block _block;
+
+attribute vec2 from;
+attribute vec2 ctrl;
+attribute vec2 to;
+attribute float maxy;
+attribute float corner;
+varying vec2 vFrom;
+varying vec2 vCtrl;
+varying vec2 vTo;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vec2 from_1 = from + _block.pathOffset;
+ vec2 ctrl_1 = ctrl + _block.pathOffset;
+ vec2 to_1 = to + _block.pathOffset;
+ float maxy_1 = maxy + _block.pathOffset.y;
+ float c = corner;
+ vec2 pos;
+ if (c >= 0.375)
+ {
+ c -= 0.5;
+ pos.y = maxy_1 + 1.0;
+ }
+ else
+ {
+ pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;
+ }
+ if (c >= 0.125)
+ {
+ pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;
+ }
+ else
+ {
+ pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;
+ }
+ vFrom = from_1 - pos;
+ vCtrl = ctrl_1 - pos;
+ vTo = to_1 - pos;
+ pos = (pos * _block.transform.xy) + _block.transform.zw;
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
+ vec3 param_1 = vec3(pos, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl150
new file mode 100644
index 0000000..9ae6c9b
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl150
@@ -0,0 +1,64 @@
+#version 150
+
+struct m3x2
+{
+ vec3 r0;
+ vec3 r1;
+};
+
+struct Block
+{
+ vec4 transform;
+ vec2 pathOffset;
+};
+
+uniform Block _block;
+
+in vec2 from;
+in vec2 ctrl;
+in vec2 to;
+in float maxy;
+in float corner;
+out vec2 vFrom;
+out vec2 vCtrl;
+out vec2 vTo;
+
+vec3 transform3x2(m3x2 t, vec3 v)
+{
+ return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));
+}
+
+void main()
+{
+ vec2 from_1 = from + _block.pathOffset;
+ vec2 ctrl_1 = ctrl + _block.pathOffset;
+ vec2 to_1 = to + _block.pathOffset;
+ float maxy_1 = maxy + _block.pathOffset.y;
+ float c = corner;
+ vec2 pos;
+ if (c >= 0.375)
+ {
+ c -= 0.5;
+ pos.y = maxy_1 + 1.0;
+ }
+ else
+ {
+ pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;
+ }
+ if (c >= 0.125)
+ {
+ pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;
+ }
+ else
+ {
+ pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;
+ }
+ vFrom = from_1 - pos;
+ vCtrl = ctrl_1 - pos;
+ vTo = to_1 - pos;
+ pos = (pos * _block.transform.xy) + _block.transform.zw;
+ m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
+ vec3 param_1 = vec3(pos, 0.0);
+ gl_Position = vec4(transform3x2(param, param_1), 1.0);
+}
+
diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibios b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibios
new file mode 100644
index 0000000..75ac068
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiossimulator
new file mode 100644
index 0000000..9ba5f58
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibmacos
new file mode 100644
index 0000000..4de454d
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.spirv b/vendor/gioui.org/shader/gio/zstencil.vert.0.spirv
new file mode 100644
index 0000000..b45803f
--- /dev/null
+++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/go.mod b/vendor/gioui.org/shader/go.mod
new file mode 100644
index 0000000..4c9167c
--- /dev/null
+++ b/vendor/gioui.org/shader/go.mod
@@ -0,0 +1,5 @@
+module gioui.org/shader
+
+go 1.16
+
+require gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334
diff --git a/vendor/gioui.org/shader/go.sum b/vendor/gioui.org/shader/go.sum
new file mode 100644
index 0000000..ec5a47a
--- /dev/null
+++ b/vendor/gioui.org/shader/go.sum
@@ -0,0 +1,2 @@
+gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334 h1:1xK224B5DnjlPKCfVDTl7+olrzgAXn4ym6dum3l34rs=
+gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
diff --git a/vendor/gioui.org/shader/piet/abi.h b/vendor/gioui.org/shader/piet/abi.h
new file mode 100644
index 0000000..365d936
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/abi.h
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#define ALIGN(bytes, type) type __attribute__((aligned(bytes)))
+
+typedef ALIGN(8, uint8_t) byte8[8];
+typedef ALIGN(8, uint16_t) word4[4];
+typedef ALIGN(4, uint32_t) dword;
+typedef ALIGN(16, uint32_t) dword4[4];
+typedef ALIGN(8, uint64_t) qword;
+typedef ALIGN(16, uint64_t) qword2[2];
+typedef ALIGN(16, unsigned int) uint4[4];
+typedef ALIGN(8, uint32_t) dword2[2];
+typedef ALIGN(8, unsigned short) ushort4[4];
+typedef ALIGN(16, float) float4[4];
+typedef ALIGN(16, int) int4[4];
+
+typedef unsigned short half;
+
+typedef unsigned char bool;
+
+enum {
+ MAX_BOUND_DESCRIPTOR_SETS = 4,
+ MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC = 8,
+ MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC = 4,
+ MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC =
+ MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC +
+ MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC,
+ MAX_PUSH_CONSTANT_SIZE = 128,
+
+ MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT = 256,
+
+ REQUIRED_MEMORY_ALIGNMENT = 16,
+
+ SIMD_WIDTH = 4,
+};
+
+struct image_descriptor {
+ ALIGN(16, void *ptr);
+ int width;
+ int height;
+ int depth;
+ int row_pitch_bytes;
+ int slice_pitch_bytes;
+ int sample_pitch_bytes;
+ int sample_count;
+ int size_in_bytes;
+
+ void *stencil_ptr;
+ int stencil_row_pitch_bytes;
+ int stencil_slice_pitch_bytes;
+ int stencil_sample_pitch_bytes;
+
+ // TODO: unused?
+ void *memoryOwner;
+};
+
+struct buffer_descriptor {
+ ALIGN(16, void *ptr);
+ int size_in_bytes;
+ int robustness_size;
+};
+
+struct program_data {
+ uint8_t *descriptor_sets[MAX_BOUND_DESCRIPTOR_SETS];
+ uint32_t descriptor_dynamic_offsets[MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC];
+ uint4 num_workgroups;
+ uint4 workgroup_size;
+ uint32_t invocations_per_subgroup;
+ uint32_t subgroups_per_workgroup;
+ uint32_t invocations_per_workgroup;
+ unsigned char push_constants[MAX_PUSH_CONSTANT_SIZE];
+ // Unused.
+ void *constants;
+};
+
+typedef int32_t yield_result;
+
+typedef void * coroutine;
+
+typedef coroutine (*routine_begin)(struct program_data *data,
+ int32_t workgroupX,
+ int32_t workgroupY,
+ int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount);
+
+typedef bool (*routine_await)(coroutine r, yield_result *res);
+
+typedef void (*routine_destroy)(coroutine r);
+
diff --git a/vendor/gioui.org/shader/piet/annotated.h b/vendor/gioui.org/shader/piet/annotated.h
new file mode 100644
index 0000000..6b18155
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/annotated.h
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Code auto-generated by piet-gpu-derive
+
+struct AnnoImageRef {
+ uint offset;
+};
+
+struct AnnoColorRef {
+ uint offset;
+};
+
+struct AnnoBeginClipRef {
+ uint offset;
+};
+
+struct AnnoEndClipRef {
+ uint offset;
+};
+
+struct AnnotatedRef {
+ uint offset;
+};
+
+struct AnnoImage {
+ vec4 bbox;
+ float linewidth;
+ uint index;
+ ivec2 offset;
+};
+
+#define AnnoImage_size 28
+
+AnnoImageRef AnnoImage_index(AnnoImageRef ref, uint index) {
+ return AnnoImageRef(ref.offset + index * AnnoImage_size);
+}
+
+struct AnnoColor {
+ vec4 bbox;
+ float linewidth;
+ uint rgba_color;
+};
+
+#define AnnoColor_size 24
+
+AnnoColorRef AnnoColor_index(AnnoColorRef ref, uint index) {
+ return AnnoColorRef(ref.offset + index * AnnoColor_size);
+}
+
+struct AnnoBeginClip {
+ vec4 bbox;
+ float linewidth;
+};
+
+#define AnnoBeginClip_size 20
+
+AnnoBeginClipRef AnnoBeginClip_index(AnnoBeginClipRef ref, uint index) {
+ return AnnoBeginClipRef(ref.offset + index * AnnoBeginClip_size);
+}
+
+struct AnnoEndClip {
+ vec4 bbox;
+};
+
+#define AnnoEndClip_size 16
+
+AnnoEndClipRef AnnoEndClip_index(AnnoEndClipRef ref, uint index) {
+ return AnnoEndClipRef(ref.offset + index * AnnoEndClip_size);
+}
+
+#define Annotated_Nop 0
+#define Annotated_Color 1
+#define Annotated_Image 2
+#define Annotated_BeginClip 3
+#define Annotated_EndClip 4
+#define Annotated_size 32
+
+AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) {
+ return AnnotatedRef(ref.offset + index * Annotated_size);
+}
+
+struct AnnotatedTag {
+ uint tag;
+ uint flags;
+};
+
+AnnoImage AnnoImage_read(Alloc a, AnnoImageRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ uint raw3 = read_mem(a, ix + 3);
+ uint raw4 = read_mem(a, ix + 4);
+ uint raw5 = read_mem(a, ix + 5);
+ uint raw6 = read_mem(a, ix + 6);
+ AnnoImage s;
+ s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.linewidth = uintBitsToFloat(raw4);
+ s.index = raw5;
+ s.offset = ivec2(int(raw6 << 16) >> 16, int(raw6) >> 16);
+ return s;
+}
+
+void AnnoImage_write(Alloc a, AnnoImageRef ref, AnnoImage s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.bbox.x));
+ write_mem(a, ix + 1, floatBitsToUint(s.bbox.y));
+ write_mem(a, ix + 2, floatBitsToUint(s.bbox.z));
+ write_mem(a, ix + 3, floatBitsToUint(s.bbox.w));
+ write_mem(a, ix + 4, floatBitsToUint(s.linewidth));
+ write_mem(a, ix + 5, s.index);
+ write_mem(a, ix + 6, (uint(s.offset.x) & 0xffff) | (uint(s.offset.y) << 16));
+}
+
+AnnoColor AnnoColor_read(Alloc a, AnnoColorRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ uint raw3 = read_mem(a, ix + 3);
+ uint raw4 = read_mem(a, ix + 4);
+ uint raw5 = read_mem(a, ix + 5);
+ AnnoColor s;
+ s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.linewidth = uintBitsToFloat(raw4);
+ s.rgba_color = raw5;
+ return s;
+}
+
+void AnnoColor_write(Alloc a, AnnoColorRef ref, AnnoColor s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.bbox.x));
+ write_mem(a, ix + 1, floatBitsToUint(s.bbox.y));
+ write_mem(a, ix + 2, floatBitsToUint(s.bbox.z));
+ write_mem(a, ix + 3, floatBitsToUint(s.bbox.w));
+ write_mem(a, ix + 4, floatBitsToUint(s.linewidth));
+ write_mem(a, ix + 5, s.rgba_color);
+}
+
+AnnoBeginClip AnnoBeginClip_read(Alloc a, AnnoBeginClipRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ uint raw3 = read_mem(a, ix + 3);
+ uint raw4 = read_mem(a, ix + 4);
+ AnnoBeginClip s;
+ s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.linewidth = uintBitsToFloat(raw4);
+ return s;
+}
+
+void AnnoBeginClip_write(Alloc a, AnnoBeginClipRef ref, AnnoBeginClip s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.bbox.x));
+ write_mem(a, ix + 1, floatBitsToUint(s.bbox.y));
+ write_mem(a, ix + 2, floatBitsToUint(s.bbox.z));
+ write_mem(a, ix + 3, floatBitsToUint(s.bbox.w));
+ write_mem(a, ix + 4, floatBitsToUint(s.linewidth));
+}
+
+AnnoEndClip AnnoEndClip_read(Alloc a, AnnoEndClipRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ uint raw3 = read_mem(a, ix + 3);
+ AnnoEndClip s;
+ s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ return s;
+}
+
+void AnnoEndClip_write(Alloc a, AnnoEndClipRef ref, AnnoEndClip s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.bbox.x));
+ write_mem(a, ix + 1, floatBitsToUint(s.bbox.y));
+ write_mem(a, ix + 2, floatBitsToUint(s.bbox.z));
+ write_mem(a, ix + 3, floatBitsToUint(s.bbox.w));
+}
+
+AnnotatedTag Annotated_tag(Alloc a, AnnotatedRef ref) {
+ uint tag_and_flags = read_mem(a, ref.offset >> 2);
+ return AnnotatedTag(tag_and_flags & 0xffff, tag_and_flags >> 16);
+}
+
+AnnoColor Annotated_Color_read(Alloc a, AnnotatedRef ref) {
+ return AnnoColor_read(a, AnnoColorRef(ref.offset + 4));
+}
+
+AnnoImage Annotated_Image_read(Alloc a, AnnotatedRef ref) {
+ return AnnoImage_read(a, AnnoImageRef(ref.offset + 4));
+}
+
+AnnoBeginClip Annotated_BeginClip_read(Alloc a, AnnotatedRef ref) {
+ return AnnoBeginClip_read(a, AnnoBeginClipRef(ref.offset + 4));
+}
+
+AnnoEndClip Annotated_EndClip_read(Alloc a, AnnotatedRef ref) {
+ return AnnoEndClip_read(a, AnnoEndClipRef(ref.offset + 4));
+}
+
+void Annotated_Nop_write(Alloc a, AnnotatedRef ref) {
+ write_mem(a, ref.offset >> 2, Annotated_Nop);
+}
+
+void Annotated_Color_write(Alloc a, AnnotatedRef ref, uint flags, AnnoColor s) {
+ write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_Color);
+ AnnoColor_write(a, AnnoColorRef(ref.offset + 4), s);
+}
+
+void Annotated_Image_write(Alloc a, AnnotatedRef ref, uint flags, AnnoImage s) {
+ write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_Image);
+ AnnoImage_write(a, AnnoImageRef(ref.offset + 4), s);
+}
+
+void Annotated_BeginClip_write(Alloc a, AnnotatedRef ref, uint flags, AnnoBeginClip s) {
+ write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_BeginClip);
+ AnnoBeginClip_write(a, AnnoBeginClipRef(ref.offset + 4), s);
+}
+
+void Annotated_EndClip_write(Alloc a, AnnotatedRef ref, AnnoEndClip s) {
+ write_mem(a, ref.offset >> 2, Annotated_EndClip);
+ AnnoEndClip_write(a, AnnoEndClipRef(ref.offset + 4), s);
+}
+
diff --git a/vendor/gioui.org/shader/piet/backdrop.comp b/vendor/gioui.org/shader/piet/backdrop.comp
new file mode 100644
index 0000000..12ae5b1
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop.comp
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Propagation of tile backdrop for filling.
+//
+// Each thread reads one path element and calculates the number of spanned tiles
+// based on the bounding box.
+// In a further compaction step, the workgroup loops over the corresponding tile rows per element in parallel.
+// For each row the per tile backdrop will be read, as calculated in the previous coarse path segment kernel,
+// and propagated from the left to the right (prefix summed).
+//
+// Output state:
+// - Each path element has an array of tiles covering the whole path based on boundig box
+// - Each tile per path element contains the 'backdrop' and a list of subdivided path segments
+
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+#include "mem.h"
+#include "setup.h"
+
+#define LG_BACKDROP_WG (7 + LG_WG_FACTOR)
+#define BACKDROP_WG (1 << LG_BACKDROP_WG)
+
+layout(local_size_x = BACKDROP_WG, local_size_y = 1) in;
+
+layout(set = 0, binding = 1) readonly buffer ConfigBuf {
+ Config conf;
+};
+
+#include "annotated.h"
+#include "tile.h"
+
+shared uint sh_row_count[BACKDROP_WG];
+shared Alloc sh_row_alloc[BACKDROP_WG];
+shared uint sh_row_width[BACKDROP_WG];
+
+void main() {
+ uint th_ix = gl_LocalInvocationID.x;
+ uint element_ix = gl_GlobalInvocationID.x;
+ AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size);
+
+ // Work assignment: 1 thread : 1 path element
+ uint row_count = 0;
+ bool mem_ok = mem_error == NO_ERROR;
+ if (element_ix < conf.n_elements) {
+ AnnotatedTag tag = Annotated_tag(conf.anno_alloc, ref);
+ switch (tag.tag) {
+ case Annotated_Image:
+ case Annotated_BeginClip:
+ case Annotated_Color:
+ if (fill_mode_from_flags(tag.flags) != MODE_NONZERO) {
+ break;
+ }
+ // Fall through.
+ PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size);
+ Path path = Path_read(conf.tile_alloc, path_ref);
+ sh_row_width[th_ix] = path.bbox.z - path.bbox.x;
+ row_count = path.bbox.w - path.bbox.y;
+ // Paths that don't cross tile top edges don't have backdrops.
+ // Don't apply the optimization to paths that may cross the y = 0
+ // top edge, but clipped to 1 row.
+ if (row_count == 1 && path.bbox.y > 0) {
+ // Note: this can probably be expanded to width = 2 as
+ // long as it doesn't cross the left edge.
+ row_count = 0;
+ }
+ Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size, mem_ok);
+ sh_row_alloc[th_ix] = path_alloc;
+ }
+ }
+
+ sh_row_count[th_ix] = row_count;
+ // Prefix sum of sh_row_count
+ for (uint i = 0; i < LG_BACKDROP_WG; i++) {
+ barrier();
+ if (th_ix >= (1 << i)) {
+ row_count += sh_row_count[th_ix - (1 << i)];
+ }
+ barrier();
+ sh_row_count[th_ix] = row_count;
+ }
+ barrier();
+ // Work assignment: 1 thread : 1 path element row
+ uint total_rows = sh_row_count[BACKDROP_WG - 1];
+ for (uint row = th_ix; row < total_rows; row += BACKDROP_WG) {
+ // Binary search to find element
+ uint el_ix = 0;
+ for (uint i = 0; i < LG_BACKDROP_WG; i++) {
+ uint probe = el_ix + ((BACKDROP_WG / 2) >> i);
+ if (row >= sh_row_count[probe - 1]) {
+ el_ix = probe;
+ }
+ }
+ uint width = sh_row_width[el_ix];
+ if (width > 0 && mem_ok) {
+ // Process one row sequentially
+ // Read backdrop value per tile and prefix sum it
+ Alloc tiles_alloc = sh_row_alloc[el_ix];
+ uint seq_ix = row - (el_ix > 0 ? sh_row_count[el_ix - 1] : 0);
+ uint tile_el_ix = (tiles_alloc.offset >> 2) + 1 + seq_ix * 2 * width;
+ uint sum = read_mem(tiles_alloc, tile_el_ix);
+ for (uint x = 1; x < width; x++) {
+ tile_el_ix += 2;
+ sum += read_mem(tiles_alloc, tile_el_ix);
+ write_mem(tiles_alloc, tile_el_ix, sum);
+ }
+ }
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/backdrop_abi.c b/vendor/gioui.org/shader/piet/backdrop_abi.c
new file mode 100644
index 0000000..48a4a30
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop_abi.c
@@ -0,0 +1,23 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stddef.h>
+#include "abi.h"
+#include "runtime.h"
+#include "backdrop_abi.h"
+
+const struct program_info backdrop_program_info = {
+ .has_cbarriers = 1,
+ .min_memory_size = 100000,
+ .desc_set_size = sizeof(struct backdrop_descriptor_set_layout),
+ .workgroup_size_x = 128,
+ .workgroup_size_y = 1,
+ .workgroup_size_z = 1,
+ .begin = backdrop_coroutine_begin,
+ .await = backdrop_coroutine_await,
+ .destroy = backdrop_coroutine_destroy,
+};
diff --git a/vendor/gioui.org/shader/piet/backdrop_abi.go b/vendor/gioui.org/shader/piet/backdrop_abi.go
new file mode 100644
index 0000000..a40a37d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop_abi.go
@@ -0,0 +1,35 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package piet
+
+import "gioui.org/cpu"
+import "unsafe"
+
+/*
+#cgo LDFLAGS: -lm
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+#include "backdrop_abi.h"
+*/
+import "C"
+
+var BackdropProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.backdrop_program_info))
+
+type BackdropDescriptorSetLayout = C.struct_backdrop_descriptor_set_layout
+
+const BackdropHash = "6862eaf623d89da635e9d5bc981c77aae4e39aa44047ab47c62d243cf5fe7e73"
+
+func (l *BackdropDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0))
+}
+
+func (l *BackdropDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1))
+}
diff --git a/vendor/gioui.org/shader/piet/backdrop_abi.h b/vendor/gioui.org/shader/piet/backdrop_abi.h
new file mode 100644
index 0000000..f5c0303
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop_abi.h
@@ -0,0 +1,17 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+struct backdrop_descriptor_set_layout {
+ struct buffer_descriptor binding0;
+ struct buffer_descriptor binding1;
+};
+
+extern coroutine backdrop_coroutine_begin(struct program_data *data,
+ int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount) ATTR_HIDDEN;
+
+extern bool backdrop_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN;
+extern void backdrop_coroutine_destroy(coroutine r) ATTR_HIDDEN;
+
+extern const struct program_info backdrop_program_info ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/backdrop_abi_nosupport.go b/vendor/gioui.org/shader/piet/backdrop_abi_nosupport.go
new file mode 100644
index 0000000..c60119b
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop_abi_nosupport.go
@@ -0,0 +1,22 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package piet
+
+import "gioui.org/cpu"
+
+var BackdropProgramInfo *cpu.ProgramInfo
+
+type BackdropDescriptorSetLayout struct{}
+
+const BackdropHash = ""
+
+func (l *BackdropDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *BackdropDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
diff --git a/vendor/gioui.org/shader/piet/backdrop_linux_amd64.syso b/vendor/gioui.org/shader/piet/backdrop_linux_amd64.syso
new file mode 100644
index 0000000..2a71e8a
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop_linux_amd64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/backdrop_linux_arm.syso b/vendor/gioui.org/shader/piet/backdrop_linux_arm.syso
new file mode 100644
index 0000000..c86dada
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop_linux_arm.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/backdrop_linux_arm64.syso b/vendor/gioui.org/shader/piet/backdrop_linux_arm64.syso
new file mode 100644
index 0000000..4608a7e
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/backdrop_linux_arm64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/binning.comp b/vendor/gioui.org/shader/piet/binning.comp
new file mode 100644
index 0000000..acda83c
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning.comp
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// The binning stage of the pipeline.
+//
+// Each workgroup processes N_TILE paths.
+// Each thread processes one path and calculates a N_TILE_X x N_TILE_Y coverage mask
+// based on the path bounding box to bin the paths.
+
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+#include "mem.h"
+#include "setup.h"
+
+layout(local_size_x = N_TILE, local_size_y = 1) in;
+
+layout(set = 0, binding = 1) readonly buffer ConfigBuf {
+ Config conf;
+};
+
+#include "annotated.h"
+#include "bins.h"
+
+// scale factors useful for converting coordinates to bins
+#define SX (1.0 / float(N_TILE_X * TILE_WIDTH_PX))
+#define SY (1.0 / float(N_TILE_Y * TILE_HEIGHT_PX))
+
+// Constant not available in GLSL. Also consider uintBitsToFloat(0x7f800000)
+#define INFINITY (1.0 / 0.0)
+
+// Note: cudaraster has N_TILE + 1 to cut down on bank conflicts.
+// Bitmaps are sliced (256bit into 8 (N_SLICE) 32bit submaps)
+shared uint bitmaps[N_SLICE][N_TILE];
+shared uint count[N_SLICE][N_TILE];
+shared Alloc sh_chunk_alloc[N_TILE];
+// Really a bool, but some Metal devices don't accept shared bools.
+shared uint sh_alloc_failed;
+
+void main() {
+ uint my_n_elements = conf.n_elements;
+ uint my_partition = gl_WorkGroupID.x;
+
+ for (uint i = 0; i < N_SLICE; i++) {
+ bitmaps[i][gl_LocalInvocationID.x] = 0;
+ }
+ if (gl_LocalInvocationID.x == 0) {
+ sh_alloc_failed = 0;
+ }
+ barrier();
+
+ // Read inputs and determine coverage of bins
+ uint element_ix = my_partition * N_TILE + gl_LocalInvocationID.x;
+ AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size);
+ uint tag = Annotated_Nop;
+ if (element_ix < my_n_elements) {
+ tag = Annotated_tag(conf.anno_alloc, ref).tag;
+ }
+ int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
+ switch (tag) {
+ case Annotated_Color:
+ case Annotated_Image:
+ case Annotated_BeginClip:
+ case Annotated_EndClip:
+ // Note: we take advantage of the fact that these drawing elements
+ // have the bbox at the same place in their layout.
+ AnnoEndClip clip = Annotated_EndClip_read(conf.anno_alloc, ref);
+ x0 = int(floor(clip.bbox.x * SX));
+ y0 = int(floor(clip.bbox.y * SY));
+ x1 = int(ceil(clip.bbox.z * SX));
+ y1 = int(ceil(clip.bbox.w * SY));
+ break;
+ }
+
+ // At this point, we run an iterator over the coverage area,
+ // trying to keep divergence low.
+ // Right now, it's just a bbox, but we'll get finer with
+ // segments.
+ uint width_in_bins = (conf.width_in_tiles + N_TILE_X - 1)/N_TILE_X;
+ uint height_in_bins = (conf.height_in_tiles + N_TILE_Y - 1)/N_TILE_Y;
+ x0 = clamp(x0, 0, int(width_in_bins));
+ x1 = clamp(x1, x0, int(width_in_bins));
+ y0 = clamp(y0, 0, int(height_in_bins));
+ y1 = clamp(y1, y0, int(height_in_bins));
+ if (x0 == x1) y1 = y0;
+ int x = x0, y = y0;
+ uint my_slice = gl_LocalInvocationID.x / 32;
+ uint my_mask = 1 << (gl_LocalInvocationID.x & 31);
+ while (y < y1) {
+ atomicOr(bitmaps[my_slice][y * width_in_bins + x], my_mask);
+ x++;
+ if (x == x1) {
+ x = x0;
+ y++;
+ }
+ }
+
+ barrier();
+ // Allocate output segments.
+ uint element_count = 0;
+ for (uint i = 0; i < N_SLICE; i++) {
+ element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]);
+ count[i][gl_LocalInvocationID.x] = element_count;
+ }
+ // element_count is number of elements covering bin for this invocation.
+ Alloc chunk_alloc = new_alloc(0, 0, true);
+ if (element_count != 0) {
+ // TODO: aggregate atomic adds (subgroup is probably fastest)
+ MallocResult chunk = malloc(element_count * BinInstance_size);
+ chunk_alloc = chunk.alloc;
+ sh_chunk_alloc[gl_LocalInvocationID.x] = chunk_alloc;
+ if (chunk.failed) {
+ sh_alloc_failed = 1;
+ }
+ }
+ // Note: it might be more efficient for reading to do this in the
+ // other order (each bin is a contiguous sequence of partitions)
+ uint out_ix = (conf.bin_alloc.offset >> 2) + (my_partition * N_TILE + gl_LocalInvocationID.x) * 2;
+ write_mem(conf.bin_alloc, out_ix, element_count);
+ write_mem(conf.bin_alloc, out_ix + 1, chunk_alloc.offset);
+
+ barrier();
+ if (sh_alloc_failed != 0 || mem_error != NO_ERROR) {
+ return;
+ }
+
+ // Use similar strategy as Laine & Karras paper; loop over bbox of bins
+ // touched by this element
+ x = x0;
+ y = y0;
+ while (y < y1) {
+ uint bin_ix = y * width_in_bins + x;
+ uint out_mask = bitmaps[my_slice][bin_ix];
+ if ((out_mask & my_mask) != 0) {
+ uint idx = bitCount(out_mask & (my_mask - 1));
+ if (my_slice > 0) {
+ idx += count[my_slice - 1][bin_ix];
+ }
+ Alloc out_alloc = sh_chunk_alloc[bin_ix];
+ uint out_offset = out_alloc.offset + idx * BinInstance_size;
+ BinInstance_write(out_alloc, BinInstanceRef(out_offset), BinInstance(element_ix));
+ }
+ x++;
+ if (x == x1) {
+ x = x0;
+ y++;
+ }
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/binning_abi.c b/vendor/gioui.org/shader/piet/binning_abi.c
new file mode 100644
index 0000000..b28be33
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning_abi.c
@@ -0,0 +1,23 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stddef.h>
+#include "abi.h"
+#include "runtime.h"
+#include "binning_abi.h"
+
+const struct program_info binning_program_info = {
+ .has_cbarriers = 1,
+ .min_memory_size = 100000,
+ .desc_set_size = sizeof(struct binning_descriptor_set_layout),
+ .workgroup_size_x = 128,
+ .workgroup_size_y = 1,
+ .workgroup_size_z = 1,
+ .begin = binning_coroutine_begin,
+ .await = binning_coroutine_await,
+ .destroy = binning_coroutine_destroy,
+};
diff --git a/vendor/gioui.org/shader/piet/binning_abi.go b/vendor/gioui.org/shader/piet/binning_abi.go
new file mode 100644
index 0000000..7ee361f
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning_abi.go
@@ -0,0 +1,35 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package piet
+
+import "gioui.org/cpu"
+import "unsafe"
+
+/*
+#cgo LDFLAGS: -lm
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+#include "binning_abi.h"
+*/
+import "C"
+
+var BinningProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.binning_program_info))
+
+type BinningDescriptorSetLayout = C.struct_binning_descriptor_set_layout
+
+const BinningHash = "84177b6dfb90309a6c054ab9fea42293bd49033c221651c756eb40188f4d6ce8"
+
+func (l *BinningDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0))
+}
+
+func (l *BinningDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1))
+}
diff --git a/vendor/gioui.org/shader/piet/binning_abi.h b/vendor/gioui.org/shader/piet/binning_abi.h
new file mode 100644
index 0000000..0152f34
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning_abi.h
@@ -0,0 +1,17 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+struct binning_descriptor_set_layout {
+ struct buffer_descriptor binding0;
+ struct buffer_descriptor binding1;
+};
+
+extern coroutine binning_coroutine_begin(struct program_data *data,
+ int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount) ATTR_HIDDEN;
+
+extern bool binning_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN;
+extern void binning_coroutine_destroy(coroutine r) ATTR_HIDDEN;
+
+extern const struct program_info binning_program_info ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/binning_abi_nosupport.go b/vendor/gioui.org/shader/piet/binning_abi_nosupport.go
new file mode 100644
index 0000000..81b9b56
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning_abi_nosupport.go
@@ -0,0 +1,22 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package piet
+
+import "gioui.org/cpu"
+
+var BinningProgramInfo *cpu.ProgramInfo
+
+type BinningDescriptorSetLayout struct{}
+
+const BinningHash = ""
+
+func (l *BinningDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *BinningDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
diff --git a/vendor/gioui.org/shader/piet/binning_linux_amd64.syso b/vendor/gioui.org/shader/piet/binning_linux_amd64.syso
new file mode 100644
index 0000000..329d95d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning_linux_amd64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/binning_linux_arm.syso b/vendor/gioui.org/shader/piet/binning_linux_arm.syso
new file mode 100644
index 0000000..a8f21a7
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning_linux_arm.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/binning_linux_arm64.syso b/vendor/gioui.org/shader/piet/binning_linux_arm64.syso
new file mode 100644
index 0000000..02938c5
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/binning_linux_arm64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/bins.h b/vendor/gioui.org/shader/piet/bins.h
new file mode 100644
index 0000000..853adab
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/bins.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Code auto-generated by piet-gpu-derive
+
+struct BinInstanceRef {
+ uint offset;
+};
+
+struct BinInstance {
+ uint element_ix;
+};
+
+#define BinInstance_size 4
+
+BinInstanceRef BinInstance_index(BinInstanceRef ref, uint index) {
+ return BinInstanceRef(ref.offset + index * BinInstance_size);
+}
+
+BinInstance BinInstance_read(Alloc a, BinInstanceRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ BinInstance s;
+ s.element_ix = raw0;
+ return s;
+}
+
+void BinInstance_write(Alloc a, BinInstanceRef ref, BinInstance s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.element_ix);
+}
+
diff --git a/vendor/gioui.org/shader/piet/coarse.comp b/vendor/gioui.org/shader/piet/coarse.comp
new file mode 100644
index 0000000..731da97
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse.comp
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// The coarse rasterizer stage of the pipeline.
+//
+// As input we have the ordered partitions of paths from the binning phase and
+// the annotated tile list of segments and backdrop per path.
+//
+// Each workgroup operating on one bin by stream compacting
+// the elements corresponding to the bin.
+//
+// As output we have an ordered command stream per tile. Every tile from a path (backdrop + segment list) will be encoded.
+
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+#include "mem.h"
+#include "setup.h"
+
+layout(local_size_x = N_TILE, local_size_y = 1) in;
+
+layout(set = 0, binding = 1) readonly buffer ConfigBuf {
+ Config conf;
+};
+
+#include "annotated.h"
+#include "bins.h"
+#include "tile.h"
+#include "ptcl.h"
+
+#define LG_N_PART_READ (7 + LG_WG_FACTOR)
+#define N_PART_READ (1 << LG_N_PART_READ)
+
+shared uint sh_elements[N_TILE];
+
+// Number of elements in the partition; prefix sum.
+shared uint sh_part_count[N_PART_READ];
+shared Alloc sh_part_elements[N_PART_READ];
+
+shared uint sh_bitmaps[N_SLICE][N_TILE];
+
+shared uint sh_tile_count[N_TILE];
+// The width of the tile rect for the element, intersected with this bin
+shared uint sh_tile_width[N_TILE];
+shared uint sh_tile_x0[N_TILE];
+shared uint sh_tile_y0[N_TILE];
+
+// These are set up so base + tile_y * stride + tile_x points to a Tile.
+shared uint sh_tile_base[N_TILE];
+shared uint sh_tile_stride[N_TILE];
+
+#ifdef MEM_DEBUG
+// Store allocs only when MEM_DEBUG to save shared memory traffic.
+shared Alloc sh_tile_alloc[N_TILE];
+
+void write_tile_alloc(uint el_ix, Alloc a) {
+ sh_tile_alloc[el_ix] = a;
+}
+
+Alloc read_tile_alloc(uint el_ix, bool mem_ok) {
+ return sh_tile_alloc[el_ix];
+}
+#else
+void write_tile_alloc(uint el_ix, Alloc a) {
+ // No-op
+}
+
+Alloc read_tile_alloc(uint el_ix, bool mem_ok) {
+ // All memory.
+ return new_alloc(0, memory.length()*4, mem_ok);
+}
+#endif
+
+// The maximum number of commands per annotated element.
+#define ANNO_COMMANDS 2
+
+// Perhaps cmd_alloc should be a global? This is a style question.
+bool alloc_cmd(inout Alloc cmd_alloc, inout CmdRef cmd_ref, inout uint cmd_limit) {
+ if (cmd_ref.offset < cmd_limit) {
+ return true;
+ }
+ MallocResult new_cmd = malloc(PTCL_INITIAL_ALLOC);
+ if (new_cmd.failed) {
+ return false;
+ }
+ CmdJump jump = CmdJump(new_cmd.alloc.offset);
+ Cmd_Jump_write(cmd_alloc, cmd_ref, jump);
+ cmd_alloc = new_cmd.alloc;
+ cmd_ref = CmdRef(cmd_alloc.offset);
+ // Reserve space for the maximum number of commands and a potential jump.
+ cmd_limit = cmd_alloc.offset + PTCL_INITIAL_ALLOC - (ANNO_COMMANDS + 1) * Cmd_size;
+ return true;
+}
+
+void write_fill(Alloc alloc, inout CmdRef cmd_ref, uint flags, Tile tile, float linewidth) {
+ if (fill_mode_from_flags(flags) == MODE_NONZERO) {
+ if (tile.tile.offset != 0) {
+ CmdFill cmd_fill = CmdFill(tile.tile.offset, tile.backdrop);
+ Cmd_Fill_write(alloc, cmd_ref, cmd_fill);
+ cmd_ref.offset += 4 + CmdFill_size;
+ } else {
+ Cmd_Solid_write(alloc, cmd_ref);
+ cmd_ref.offset += 4;
+ }
+ } else {
+ CmdStroke cmd_stroke = CmdStroke(tile.tile.offset, 0.5 * linewidth);
+ Cmd_Stroke_write(alloc, cmd_ref, cmd_stroke);
+ cmd_ref.offset += 4 + CmdStroke_size;
+ }
+}
+
+void main() {
+ // Could use either linear or 2d layouts for both dispatch and
+ // invocations within the workgroup. We'll use variables to abstract.
+ uint width_in_bins = (conf.width_in_tiles + N_TILE_X - 1)/N_TILE_X;
+ uint bin_ix = width_in_bins * gl_WorkGroupID.y + gl_WorkGroupID.x;
+ uint partition_ix = 0;
+ uint n_partitions = (conf.n_elements + N_TILE - 1) / N_TILE;
+ uint th_ix = gl_LocalInvocationID.x;
+
+ // Coordinates of top left of bin, in tiles.
+ uint bin_tile_x = N_TILE_X * gl_WorkGroupID.x;
+ uint bin_tile_y = N_TILE_Y * gl_WorkGroupID.y;
+
+ // Per-tile state
+ uint tile_x = gl_LocalInvocationID.x % N_TILE_X;
+ uint tile_y = gl_LocalInvocationID.x / N_TILE_X;
+ uint this_tile_ix = (bin_tile_y + tile_y) * conf.width_in_tiles + bin_tile_x + tile_x;
+ Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, this_tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC);
+ CmdRef cmd_ref = CmdRef(cmd_alloc.offset);
+ // Reserve space for the maximum number of commands and a potential jump.
+ uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - (ANNO_COMMANDS + 1) * Cmd_size;
+ // The nesting depth of the clip stack
+ uint clip_depth = 0;
+ // State for the "clip zero" optimization. If it's nonzero, then we are
+ // currently in a clip for which the entire tile has an alpha of zero, and
+ // the value is the depth after the "begin clip" of that element.
+ uint clip_zero_depth = 0;
+ // State for the "clip one" optimization. If bit `i` is set, then that means
+ // that the clip pushed at depth `i` has an alpha of all one.
+ uint clip_one_mask = 0;
+
+ // I'm sure we can figure out how to do this with at least one fewer register...
+ // Items up to rd_ix have been read from sh_elements
+ uint rd_ix = 0;
+ // Items up to wr_ix have been written into sh_elements
+ uint wr_ix = 0;
+ // Items between part_start_ix and ready_ix are ready to be transferred from sh_part_elements
+ uint part_start_ix = 0;
+ uint ready_ix = 0;
+
+ // Leave room for the fine rasterizer scratch allocation.
+ Alloc scratch_alloc = slice_mem(cmd_alloc, 0, Alloc_size);
+ cmd_ref.offset += Alloc_size;
+
+ uint num_begin_slots = 0;
+ uint begin_slot = 0;
+ bool mem_ok = mem_error == NO_ERROR;
+ while (true) {
+ for (uint i = 0; i < N_SLICE; i++) {
+ sh_bitmaps[i][th_ix] = 0;
+ }
+
+ // parallel read of input partitions
+ do {
+ if (ready_ix == wr_ix && partition_ix < n_partitions) {
+ part_start_ix = ready_ix;
+ uint count = 0;
+ if (th_ix < N_PART_READ && partition_ix + th_ix < n_partitions) {
+ uint in_ix = (conf.bin_alloc.offset >> 2) + ((partition_ix + th_ix) * N_TILE + bin_ix) * 2;
+ count = read_mem(conf.bin_alloc, in_ix);
+ uint offset = read_mem(conf.bin_alloc, in_ix + 1);
+ sh_part_elements[th_ix] = new_alloc(offset, count*BinInstance_size, mem_ok);
+ }
+ // prefix sum of counts
+ for (uint i = 0; i < LG_N_PART_READ; i++) {
+ if (th_ix < N_PART_READ) {
+ sh_part_count[th_ix] = count;
+ }
+ barrier();
+ if (th_ix < N_PART_READ) {
+ if (th_ix >= (1 << i)) {
+ count += sh_part_count[th_ix - (1 << i)];
+ }
+ }
+ barrier();
+ }
+ if (th_ix < N_PART_READ) {
+ sh_part_count[th_ix] = part_start_ix + count;
+ }
+ barrier();
+ ready_ix = sh_part_count[N_PART_READ - 1];
+ partition_ix += N_PART_READ;
+ }
+ // use binary search to find element to read
+ uint ix = rd_ix + th_ix;
+ if (ix >= wr_ix && ix < ready_ix && mem_ok) {
+ uint part_ix = 0;
+ for (uint i = 0; i < LG_N_PART_READ; i++) {
+ uint probe = part_ix + ((N_PART_READ / 2) >> i);
+ if (ix >= sh_part_count[probe - 1]) {
+ part_ix = probe;
+ }
+ }
+ ix -= part_ix > 0 ? sh_part_count[part_ix - 1] : part_start_ix;
+ Alloc bin_alloc = sh_part_elements[part_ix];
+ BinInstanceRef inst_ref = BinInstanceRef(bin_alloc.offset);
+ BinInstance inst = BinInstance_read(bin_alloc, BinInstance_index(inst_ref, ix));
+ sh_elements[th_ix] = inst.element_ix;
+ }
+ barrier();
+
+ wr_ix = min(rd_ix + N_TILE, ready_ix);
+ } while (wr_ix - rd_ix < N_TILE && (wr_ix < ready_ix || partition_ix < n_partitions));
+
+ // We've done the merge and filled the buffer.
+
+ // Read one element, compute coverage.
+ uint tag = Annotated_Nop;
+ uint element_ix;
+ AnnotatedRef ref;
+ if (th_ix + rd_ix < wr_ix) {
+ element_ix = sh_elements[th_ix];
+ ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size);
+ tag = Annotated_tag(conf.anno_alloc, ref).tag;
+ }
+
+ // Bounding box of element in pixel coordinates.
+ uint tile_count;
+ switch (tag) {
+ case Annotated_Color:
+ case Annotated_Image:
+ case Annotated_BeginClip:
+ case Annotated_EndClip:
+ // We have one "path" for each element, even if the element isn't
+ // actually a path (currently EndClip, but images etc in the future).
+ uint path_ix = element_ix;
+ Path path = Path_read(conf.tile_alloc, PathRef(conf.tile_alloc.offset + path_ix * Path_size));
+ uint stride = path.bbox.z - path.bbox.x;
+ sh_tile_stride[th_ix] = stride;
+ int dx = int(path.bbox.x) - int(bin_tile_x);
+ int dy = int(path.bbox.y) - int(bin_tile_y);
+ int x0 = clamp(dx, 0, N_TILE_X);
+ int y0 = clamp(dy, 0, N_TILE_Y);
+ int x1 = clamp(int(path.bbox.z) - int(bin_tile_x), 0, N_TILE_X);
+ int y1 = clamp(int(path.bbox.w) - int(bin_tile_y), 0, N_TILE_Y);
+ sh_tile_width[th_ix] = uint(x1 - x0);
+ sh_tile_x0[th_ix] = x0;
+ sh_tile_y0[th_ix] = y0;
+ tile_count = uint(x1 - x0) * uint(y1 - y0);
+ // base relative to bin
+ uint base = path.tiles.offset - uint(dy * stride + dx) * Tile_size;
+ sh_tile_base[th_ix] = base;
+ Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size, mem_ok);
+ write_tile_alloc(th_ix, path_alloc);
+ break;
+ default:
+ tile_count = 0;
+ break;
+ }
+
+ // Prefix sum of sh_tile_count
+ sh_tile_count[th_ix] = tile_count;
+ for (uint i = 0; i < LG_N_TILE; i++) {
+ barrier();
+ if (th_ix >= (1 << i)) {
+ tile_count += sh_tile_count[th_ix - (1 << i)];
+ }
+ barrier();
+ sh_tile_count[th_ix] = tile_count;
+ }
+ barrier();
+ uint total_tile_count = sh_tile_count[N_TILE - 1];
+ for (uint ix = th_ix; ix < total_tile_count; ix += N_TILE) {
+ // Binary search to find element
+ uint el_ix = 0;
+ for (uint i = 0; i < LG_N_TILE; i++) {
+ uint probe = el_ix + ((N_TILE / 2) >> i);
+ if (ix >= sh_tile_count[probe - 1]) {
+ el_ix = probe;
+ }
+ }
+ AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + sh_elements[el_ix] * Annotated_size);
+ uint tag = Annotated_tag(conf.anno_alloc, ref).tag;
+ uint seq_ix = ix - (el_ix > 0 ? sh_tile_count[el_ix - 1] : 0);
+ uint width = sh_tile_width[el_ix];
+ uint x = sh_tile_x0[el_ix] + seq_ix % width;
+ uint y = sh_tile_y0[el_ix] + seq_ix / width;
+ bool include_tile = false;
+ if (tag == Annotated_BeginClip || tag == Annotated_EndClip) {
+ include_tile = true;
+ } else if (mem_ok) {
+ Tile tile = Tile_read(read_tile_alloc(el_ix, mem_ok), TileRef(sh_tile_base[el_ix] + (sh_tile_stride[el_ix] * y + x) * Tile_size));
+ // Include the path in the tile if
+ // - the tile contains at least a segment (tile offset non-zero)
+ // - the tile is completely covered (backdrop non-zero)
+ include_tile = tile.tile.offset != 0 || tile.backdrop != 0;
+ }
+ if (include_tile) {
+ uint el_slice = el_ix / 32;
+ uint el_mask = 1 << (el_ix & 31);
+ atomicOr(sh_bitmaps[el_slice][y * N_TILE_X + x], el_mask);
+ }
+ }
+
+ barrier();
+
+ // Output non-segment elements for this tile. The thread does a sequential walk
+ // through the non-segment elements.
+ uint slice_ix = 0;
+ uint bitmap = sh_bitmaps[0][th_ix];
+ while (mem_ok) {
+ if (bitmap == 0) {
+ slice_ix++;
+ if (slice_ix == N_SLICE) {
+ break;
+ }
+ bitmap = sh_bitmaps[slice_ix][th_ix];
+ if (bitmap == 0) {
+ continue;
+ }
+ }
+ uint element_ref_ix = slice_ix * 32 + findLSB(bitmap);
+ uint element_ix = sh_elements[element_ref_ix];
+
+ // Clear LSB
+ bitmap &= bitmap - 1;
+
+ // At this point, we read the element again from global memory.
+ // If that turns out to be expensive, maybe we can pack it into
+ // shared memory (or perhaps just the tag).
+ ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size);
+ AnnotatedTag tag = Annotated_tag(conf.anno_alloc, ref);
+
+ if (clip_zero_depth == 0) {
+ switch (tag.tag) {
+ case Annotated_Color:
+ Tile tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix]
+ + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size));
+ AnnoColor fill = Annotated_Color_read(conf.anno_alloc, ref);
+ if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
+ break;
+ }
+ write_fill(cmd_alloc, cmd_ref, tag.flags, tile, fill.linewidth);
+ Cmd_Color_write(cmd_alloc, cmd_ref, CmdColor(fill.rgba_color));
+ cmd_ref.offset += 4 + CmdColor_size;
+ break;
+ case Annotated_Image:
+ tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix]
+ + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size));
+ AnnoImage fill_img = Annotated_Image_read(conf.anno_alloc, ref);
+ if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
+ break;
+ }
+ write_fill(cmd_alloc, cmd_ref, tag.flags, tile, fill_img.linewidth);
+ Cmd_Image_write(cmd_alloc, cmd_ref, CmdImage(fill_img.index, fill_img.offset));
+ cmd_ref.offset += 4 + CmdImage_size;
+ break;
+ case Annotated_BeginClip:
+ tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix]
+ + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size));
+ if (tile.tile.offset == 0 && tile.backdrop == 0) {
+ clip_zero_depth = clip_depth + 1;
+ } else if (tile.tile.offset == 0 && clip_depth < 32) {
+ clip_one_mask |= (1 << clip_depth);
+ } else {
+ AnnoBeginClip begin_clip = Annotated_BeginClip_read(conf.anno_alloc, ref);
+ if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
+ break;
+ }
+ write_fill(cmd_alloc, cmd_ref, tag.flags, tile, begin_clip.linewidth);
+ Cmd_BeginClip_write(cmd_alloc, cmd_ref);
+ cmd_ref.offset += 4;
+ if (clip_depth < 32) {
+ clip_one_mask &= ~(1 << clip_depth);
+ }
+ begin_slot++;
+ num_begin_slots = max(num_begin_slots, begin_slot);
+ }
+ clip_depth++;
+ break;
+ case Annotated_EndClip:
+ clip_depth--;
+ if (clip_depth >= 32 || (clip_one_mask & (1 << clip_depth)) == 0) {
+ if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
+ break;
+ }
+ Cmd_Solid_write(cmd_alloc, cmd_ref);
+ cmd_ref.offset += 4;
+ begin_slot--;
+ Cmd_EndClip_write(cmd_alloc, cmd_ref);
+ cmd_ref.offset += 4;
+ }
+ break;
+ }
+ } else {
+ // In "clip zero" state, suppress all drawing
+ switch (tag.tag) {
+ case Annotated_BeginClip:
+ clip_depth++;
+ break;
+ case Annotated_EndClip:
+ if (clip_depth == clip_zero_depth) {
+ clip_zero_depth = 0;
+ }
+ clip_depth--;
+ break;
+ }
+ }
+ }
+ barrier();
+
+ rd_ix += N_TILE;
+ if (rd_ix >= ready_ix && partition_ix >= n_partitions) break;
+ }
+ if (bin_tile_x + tile_x < conf.width_in_tiles && bin_tile_y + tile_y < conf.height_in_tiles) {
+ Cmd_End_write(cmd_alloc, cmd_ref);
+ if (num_begin_slots > 0) {
+ // Write scratch allocation: one state per BeginClip per rasterizer chunk.
+ uint scratch_size = num_begin_slots * TILE_WIDTH_PX * TILE_HEIGHT_PX * CLIP_STATE_SIZE * 4;
+ MallocResult scratch = malloc(scratch_size);
+ // Ignore scratch.failed; we don't use the allocation and kernel4
+ // checks for memory overflow before using it.
+ alloc_write(scratch_alloc, scratch_alloc.offset, scratch.alloc);
+ }
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/coarse_abi.c b/vendor/gioui.org/shader/piet/coarse_abi.c
new file mode 100644
index 0000000..dbecf9f
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse_abi.c
@@ -0,0 +1,23 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stddef.h>
+#include "abi.h"
+#include "runtime.h"
+#include "coarse_abi.h"
+
+const struct program_info coarse_program_info = {
+ .has_cbarriers = 1,
+ .min_memory_size = 100000,
+ .desc_set_size = sizeof(struct coarse_descriptor_set_layout),
+ .workgroup_size_x = 128,
+ .workgroup_size_y = 1,
+ .workgroup_size_z = 1,
+ .begin = coarse_coroutine_begin,
+ .await = coarse_coroutine_await,
+ .destroy = coarse_coroutine_destroy,
+};
diff --git a/vendor/gioui.org/shader/piet/coarse_abi.go b/vendor/gioui.org/shader/piet/coarse_abi.go
new file mode 100644
index 0000000..dfd977d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse_abi.go
@@ -0,0 +1,35 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package piet
+
+import "gioui.org/cpu"
+import "unsafe"
+
+/*
+#cgo LDFLAGS: -lm
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+#include "coarse_abi.h"
+*/
+import "C"
+
+var CoarseProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.coarse_program_info))
+
+type CoarseDescriptorSetLayout = C.struct_coarse_descriptor_set_layout
+
+const CoarseHash = "e7ef250c08701490aed979a889cca73943b988bdb5e4ca4b02735aebcf5e5505"
+
+func (l *CoarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0))
+}
+
+func (l *CoarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1))
+}
diff --git a/vendor/gioui.org/shader/piet/coarse_abi.h b/vendor/gioui.org/shader/piet/coarse_abi.h
new file mode 100644
index 0000000..24d874d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse_abi.h
@@ -0,0 +1,17 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+struct coarse_descriptor_set_layout {
+ struct buffer_descriptor binding0;
+ struct buffer_descriptor binding1;
+};
+
+extern coroutine coarse_coroutine_begin(struct program_data *data,
+ int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount) ATTR_HIDDEN;
+
+extern bool coarse_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN;
+extern void coarse_coroutine_destroy(coroutine r) ATTR_HIDDEN;
+
+extern const struct program_info coarse_program_info ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/coarse_abi_nosupport.go b/vendor/gioui.org/shader/piet/coarse_abi_nosupport.go
new file mode 100644
index 0000000..00e2ad8
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse_abi_nosupport.go
@@ -0,0 +1,22 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package piet
+
+import "gioui.org/cpu"
+
+var CoarseProgramInfo *cpu.ProgramInfo
+
+type CoarseDescriptorSetLayout struct{}
+
+const CoarseHash = ""
+
+func (l *CoarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *CoarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
diff --git a/vendor/gioui.org/shader/piet/coarse_linux_amd64.syso b/vendor/gioui.org/shader/piet/coarse_linux_amd64.syso
new file mode 100644
index 0000000..1ac8407
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse_linux_amd64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/coarse_linux_arm.syso b/vendor/gioui.org/shader/piet/coarse_linux_arm.syso
new file mode 100644
index 0000000..3f5ee88
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse_linux_arm.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/coarse_linux_arm64.syso b/vendor/gioui.org/shader/piet/coarse_linux_arm64.syso
new file mode 100644
index 0000000..da3b563
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/coarse_linux_arm64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/elements.comp b/vendor/gioui.org/shader/piet/elements.comp
new file mode 100644
index 0000000..17ef1ee
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements.comp
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// The element processing stage, first in the pipeline.
+//
+// This stage is primarily about applying transforms and computing bounding
+// boxes. It is organized as a scan over the input elements, producing
+// annotated output elements.
+
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+#include "mem.h"
+#include "setup.h"
+
+#define N_ROWS 4
+#define WG_SIZE 32
+#define LG_WG_SIZE 5
+#define PARTITION_SIZE (WG_SIZE * N_ROWS)
+
+layout(local_size_x = WG_SIZE, local_size_y = 1) in;
+
+layout(set = 0, binding = 1) readonly buffer ConfigBuf {
+ Config conf;
+};
+
+layout(set = 0, binding = 2) readonly buffer SceneBuf {
+ uint[] scene;
+};
+
+// It would be better to use the Vulkan memory model than
+// "volatile" but shooting for compatibility here rather
+// than doing things right.
+layout(set = 0, binding = 3) volatile buffer StateBuf {
+ uint part_counter;
+ uint[] state;
+};
+
+#include "scene.h"
+#include "state.h"
+#include "annotated.h"
+#include "pathseg.h"
+#include "tile.h"
+
+#define StateBuf_stride (4 + 2 * State_size)
+
+StateRef state_aggregate_ref(uint partition_ix) {
+ return StateRef(4 + partition_ix * StateBuf_stride);
+}
+
+StateRef state_prefix_ref(uint partition_ix) {
+ return StateRef(4 + partition_ix * StateBuf_stride + State_size);
+}
+
+uint state_flag_index(uint partition_ix) {
+ return partition_ix * (StateBuf_stride / 4);
+}
+
+// These correspond to X, A, P respectively in the prefix sum paper.
+#define FLAG_NOT_READY 0
+#define FLAG_AGGREGATE_READY 1
+#define FLAG_PREFIX_READY 2
+
+#define FLAG_SET_LINEWIDTH 1
+#define FLAG_SET_BBOX 2
+#define FLAG_RESET_BBOX 4
+#define FLAG_SET_FILL_MODE 8
+// Fill modes take up the next bit. Non-zero fill is 0, stroke is 1.
+#define LG_FILL_MODE 4
+#define FILL_MODE_BITS 1
+#define FILL_MODE_MASK (FILL_MODE_BITS << LG_FILL_MODE)
+
+// This is almost like a monoid (the interaction between transformation and
+// bounding boxes is approximate)
+State combine_state(State a, State b) {
+ State c;
+ c.bbox.x = min(a.mat.x * b.bbox.x, a.mat.x * b.bbox.z) + min(a.mat.z * b.bbox.y, a.mat.z * b.bbox.w) + a.translate.x;
+ c.bbox.y = min(a.mat.y * b.bbox.x, a.mat.y * b.bbox.z) + min(a.mat.w * b.bbox.y, a.mat.w * b.bbox.w) + a.translate.y;
+ c.bbox.z = max(a.mat.x * b.bbox.x, a.mat.x * b.bbox.z) + max(a.mat.z * b.bbox.y, a.mat.z * b.bbox.w) + a.translate.x;
+ c.bbox.w = max(a.mat.y * b.bbox.x, a.mat.y * b.bbox.z) + max(a.mat.w * b.bbox.y, a.mat.w * b.bbox.w) + a.translate.y;
+ if ((a.flags & FLAG_RESET_BBOX) == 0 && b.bbox.z <= b.bbox.x && b.bbox.w <= b.bbox.y) {
+ c.bbox = a.bbox;
+ } else if ((a.flags & FLAG_RESET_BBOX) == 0 && (b.flags & FLAG_SET_BBOX) == 0 &&
+ (a.bbox.z > a.bbox.x || a.bbox.w > a.bbox.y))
+ {
+ c.bbox.xy = min(a.bbox.xy, c.bbox.xy);
+ c.bbox.zw = max(a.bbox.zw, c.bbox.zw);
+ }
+ // It would be more concise to cast to matrix types; ah well.
+ c.mat.x = a.mat.x * b.mat.x + a.mat.z * b.mat.y;
+ c.mat.y = a.mat.y * b.mat.x + a.mat.w * b.mat.y;
+ c.mat.z = a.mat.x * b.mat.z + a.mat.z * b.mat.w;
+ c.mat.w = a.mat.y * b.mat.z + a.mat.w * b.mat.w;
+ c.translate.x = a.mat.x * b.translate.x + a.mat.z * b.translate.y + a.translate.x;
+ c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y;
+ c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth;
+ c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX | FLAG_SET_FILL_MODE)) | b.flags;
+ c.flags |= (a.flags & FLAG_RESET_BBOX) >> 1;
+ uint fill_mode = (b.flags & FLAG_SET_FILL_MODE) == 0 ? a.flags : b.flags;
+ fill_mode &= FILL_MODE_MASK;
+ c.flags = (c.flags & ~FILL_MODE_MASK) | fill_mode;
+ c.path_count = a.path_count + b.path_count;
+ c.pathseg_count = a.pathseg_count + b.pathseg_count;
+ c.trans_count = a.trans_count + b.trans_count;
+ return c;
+}
+
+State map_element(ElementRef ref) {
+ // TODO: it would *probably* be more efficient to make the memory read patterns less
+ // divergent, though it would be more wasted memory.
+ uint tag = Element_tag(ref).tag;
+ State c;
+ c.bbox = vec4(0.0, 0.0, 0.0, 0.0);
+ c.mat = vec4(1.0, 0.0, 0.0, 1.0);
+ c.translate = vec2(0.0, 0.0);
+ c.linewidth = 1.0; // TODO should be 0.0
+ c.flags = 0;
+ c.path_count = 0;
+ c.pathseg_count = 0;
+ c.trans_count = 0;
+ switch (tag) {
+ case Element_Line:
+ LineSeg line = Element_Line_read(ref);
+ c.bbox.xy = min(line.p0, line.p1);
+ c.bbox.zw = max(line.p0, line.p1);
+ c.pathseg_count = 1;
+ break;
+ case Element_Quad:
+ QuadSeg quad = Element_Quad_read(ref);
+ c.bbox.xy = min(min(quad.p0, quad.p1), quad.p2);
+ c.bbox.zw = max(max(quad.p0, quad.p1), quad.p2);
+ c.pathseg_count = 1;
+ break;
+ case Element_Cubic:
+ CubicSeg cubic = Element_Cubic_read(ref);
+ c.bbox.xy = min(min(cubic.p0, cubic.p1), min(cubic.p2, cubic.p3));
+ c.bbox.zw = max(max(cubic.p0, cubic.p1), max(cubic.p2, cubic.p3));
+ c.pathseg_count = 1;
+ break;
+ case Element_FillColor:
+ case Element_FillImage:
+ case Element_BeginClip:
+ c.flags = FLAG_RESET_BBOX;
+ c.path_count = 1;
+ break;
+ case Element_EndClip:
+ c.path_count = 1;
+ break;
+ case Element_SetLineWidth:
+ SetLineWidth lw = Element_SetLineWidth_read(ref);
+ c.linewidth = lw.width;
+ c.flags = FLAG_SET_LINEWIDTH;
+ break;
+ case Element_Transform:
+ Transform t = Element_Transform_read(ref);
+ c.mat = t.mat;
+ c.translate = t.translate;
+ c.trans_count = 1;
+ break;
+ case Element_SetFillMode:
+ SetFillMode fm = Element_SetFillMode_read(ref);
+ c.flags = FLAG_SET_FILL_MODE | (fm.fill_mode << LG_FILL_MODE);
+ break;
+ }
+ return c;
+}
+
+// Get the bounding box of a circle transformed by the matrix into an ellipse.
+vec2 get_linewidth(State st) {
+ // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm
+ return 0.5 * st.linewidth * vec2(length(st.mat.xz), length(st.mat.yw));
+}
+
+shared State sh_state[WG_SIZE];
+
+shared uint sh_part_ix;
+shared State sh_prefix;
+
+void main() {
+ State th_state[N_ROWS];
+ // Determine partition to process by atomic counter (described in Section
+ // 4.4 of prefix sum paper).
+ if (gl_LocalInvocationID.x == 0) {
+ sh_part_ix = atomicAdd(part_counter, 1);
+ }
+ barrier();
+ uint part_ix = sh_part_ix;
+
+ uint ix = part_ix * PARTITION_SIZE + gl_LocalInvocationID.x * N_ROWS;
+ ElementRef ref = ElementRef(ix * Element_size);
+
+ th_state[0] = map_element(ref);
+ for (uint i = 1; i < N_ROWS; i++) {
+ // discussion question: would it be faster to load using more coherent patterns
+ // into thread memory? This is kinda strided.
+ th_state[i] = combine_state(th_state[i - 1], map_element(Element_index(ref, i)));
+ }
+ State agg = th_state[N_ROWS - 1];
+ sh_state[gl_LocalInvocationID.x] = agg;
+ for (uint i = 0; i < LG_WG_SIZE; i++) {
+ barrier();
+ if (gl_LocalInvocationID.x >= (1 << i)) {
+ State other = sh_state[gl_LocalInvocationID.x - (1 << i)];
+ agg = combine_state(other, agg);
+ }
+ barrier();
+ sh_state[gl_LocalInvocationID.x] = agg;
+ }
+
+ State exclusive;
+ exclusive.bbox = vec4(0.0, 0.0, 0.0, 0.0);
+ exclusive.mat = vec4(1.0, 0.0, 0.0, 1.0);
+ exclusive.translate = vec2(0.0, 0.0);
+ exclusive.linewidth = 1.0; //TODO should be 0.0
+ exclusive.flags = 0;
+ exclusive.path_count = 0;
+ exclusive.pathseg_count = 0;
+ exclusive.trans_count = 0;
+
+ // Publish aggregate for this partition
+ if (gl_LocalInvocationID.x == WG_SIZE - 1) {
+ // Note: with memory model, we'd want to generate the atomic store version of this.
+ State_write(state_aggregate_ref(part_ix), agg);
+ }
+ memoryBarrierBuffer();
+ if (gl_LocalInvocationID.x == WG_SIZE - 1) {
+ uint flag = FLAG_AGGREGATE_READY;
+ if (part_ix == 0) {
+ State_write(state_prefix_ref(part_ix), agg);
+ flag = FLAG_PREFIX_READY;
+ }
+ state[state_flag_index(part_ix)] = flag;
+ if (part_ix != 0) {
+ // step 4 of paper: decoupled lookback
+ uint look_back_ix = part_ix - 1;
+
+ State their_agg;
+ uint their_ix = 0;
+ while (true) {
+ flag = state[state_flag_index(look_back_ix)];
+ if (flag == FLAG_PREFIX_READY) {
+ State their_prefix = State_read(state_prefix_ref(look_back_ix));
+ exclusive = combine_state(their_prefix, exclusive);
+ break;
+ } else if (flag == FLAG_AGGREGATE_READY) {
+ their_agg = State_read(state_aggregate_ref(look_back_ix));
+ exclusive = combine_state(their_agg, exclusive);
+ look_back_ix--;
+ their_ix = 0;
+ continue;
+ }
+ // else spin
+
+ // Unfortunately there's no guarantee of forward progress of other
+ // workgroups, so compute a bit of the aggregate before trying again.
+ // In the worst case, spinning stops when the aggregate is complete.
+ ElementRef ref = ElementRef((look_back_ix * PARTITION_SIZE + their_ix) * Element_size);
+ State s = map_element(ref);
+ if (their_ix == 0) {
+ their_agg = s;
+ } else {
+ their_agg = combine_state(their_agg, s);
+ }
+ their_ix++;
+ if (their_ix == PARTITION_SIZE) {
+ exclusive = combine_state(their_agg, exclusive);
+ if (look_back_ix == 0) {
+ break;
+ }
+ look_back_ix--;
+ their_ix = 0;
+ }
+ }
+
+ // step 5 of paper: compute inclusive prefix
+ State inclusive_prefix = combine_state(exclusive, agg);
+ sh_prefix = exclusive;
+ State_write(state_prefix_ref(part_ix), inclusive_prefix);
+ }
+ }
+ memoryBarrierBuffer();
+ if (gl_LocalInvocationID.x == WG_SIZE - 1 && part_ix != 0) {
+ state[state_flag_index(part_ix)] = FLAG_PREFIX_READY;
+ }
+ barrier();
+ if (part_ix != 0) {
+ exclusive = sh_prefix;
+ }
+
+ State row = exclusive;
+ if (gl_LocalInvocationID.x > 0) {
+ State other = sh_state[gl_LocalInvocationID.x - 1];
+ row = combine_state(row, other);
+ }
+ for (uint i = 0; i < N_ROWS; i++) {
+ State st = combine_state(row, th_state[i]);
+
+ // Here we read again from the original scene. There may be
+ // gains to be had from stashing in shared memory or possibly
+ // registers (though register pressure is an issue).
+ ElementRef this_ref = Element_index(ref, i);
+ ElementTag tag = Element_tag(this_ref);
+ uint fill_mode = fill_mode_from_flags(st.flags >> LG_FILL_MODE);
+ bool is_stroke = fill_mode == MODE_STROKE;
+ switch (tag.tag) {
+ case Element_Line:
+ LineSeg line = Element_Line_read(this_ref);
+ PathCubic path_cubic;
+ path_cubic.p0 = line.p0;
+ path_cubic.p1 = mix(line.p0, line.p1, 1.0 / 3.0);
+ path_cubic.p2 = mix(line.p1, line.p0, 1.0 / 3.0);
+ path_cubic.p3 = line.p1;
+ path_cubic.path_ix = st.path_count;
+ path_cubic.trans_ix = st.trans_count;
+ if (is_stroke) {
+ path_cubic.stroke = get_linewidth(st);
+ } else {
+ path_cubic.stroke = vec2(0.0);
+ }
+ PathSegRef path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size);
+ PathSeg_Cubic_write(conf.pathseg_alloc, path_out_ref, fill_mode, path_cubic);
+ break;
+ case Element_Quad:
+ QuadSeg quad = Element_Quad_read(this_ref);
+ path_cubic.p0 = quad.p0;
+ path_cubic.p1 = mix(quad.p1, quad.p0, 1.0 / 3.0);
+ path_cubic.p2 = mix(quad.p1, quad.p2, 1.0 / 3.0);
+ path_cubic.p3 = quad.p2;
+ path_cubic.path_ix = st.path_count;
+ path_cubic.trans_ix = st.trans_count;
+ if (is_stroke) {
+ path_cubic.stroke = get_linewidth(st);
+ } else {
+ path_cubic.stroke = vec2(0.0);
+ }
+ path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size);
+ PathSeg_Cubic_write(conf.pathseg_alloc, path_out_ref, fill_mode, path_cubic);
+ break;
+ case Element_Cubic:
+ CubicSeg cubic = Element_Cubic_read(this_ref);
+ path_cubic.p0 = cubic.p0;
+ path_cubic.p1 = cubic.p1;
+ path_cubic.p2 = cubic.p2;
+ path_cubic.p3 = cubic.p3;
+ path_cubic.path_ix = st.path_count;
+ path_cubic.trans_ix = st.trans_count;
+ if (is_stroke) {
+ path_cubic.stroke = get_linewidth(st);
+ } else {
+ path_cubic.stroke = vec2(0.0);
+ }
+ path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size);
+ PathSeg_Cubic_write(conf.pathseg_alloc, path_out_ref, fill_mode, path_cubic);
+ break;
+ case Element_FillColor:
+ FillColor fill = Element_FillColor_read(this_ref);
+ AnnoColor anno_fill;
+ anno_fill.rgba_color = fill.rgba_color;
+ if (is_stroke) {
+ vec2 lw = get_linewidth(st);
+ anno_fill.bbox = st.bbox + vec4(-lw, lw);
+ anno_fill.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z));
+ } else {
+ anno_fill.bbox = st.bbox;
+ anno_fill.linewidth = 0.0;
+ }
+ AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
+ Annotated_Color_write(conf.anno_alloc, out_ref, fill_mode, anno_fill);
+ break;
+ case Element_FillImage:
+ FillImage fill_img = Element_FillImage_read(this_ref);
+ AnnoImage anno_img;
+ anno_img.index = fill_img.index;
+ anno_img.offset = fill_img.offset;
+ if (is_stroke) {
+ vec2 lw = get_linewidth(st);
+ anno_img.bbox = st.bbox + vec4(-lw, lw);
+ anno_img.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z));
+ } else {
+ anno_img.bbox = st.bbox;
+ anno_img.linewidth = 0.0;
+ }
+ out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
+ Annotated_Image_write(conf.anno_alloc, out_ref, fill_mode, anno_img);
+ break;
+ case Element_BeginClip:
+ Clip begin_clip = Element_BeginClip_read(this_ref);
+ AnnoBeginClip anno_begin_clip;
+ // This is the absolute bbox, it's been transformed during encoding.
+ anno_begin_clip.bbox = begin_clip.bbox;
+ if (is_stroke) {
+ vec2 lw = get_linewidth(st);
+ anno_begin_clip.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z));
+ } else {
+ anno_fill.linewidth = 0.0;
+ }
+ out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
+ Annotated_BeginClip_write(conf.anno_alloc, out_ref, fill_mode, anno_begin_clip);
+ break;
+ case Element_EndClip:
+ Clip end_clip = Element_EndClip_read(this_ref);
+ // This bbox is expected to be the same as the begin one.
+ AnnoEndClip anno_end_clip = AnnoEndClip(end_clip.bbox);
+ out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
+ Annotated_EndClip_write(conf.anno_alloc, out_ref, anno_end_clip);
+ break;
+ case Element_Transform:
+ TransformSeg transform = TransformSeg(st.mat, st.translate);
+ TransformSegRef trans_ref = TransformSegRef(conf.trans_alloc.offset + (st.trans_count - 1) * TransformSeg_size);
+ TransformSeg_write(conf.trans_alloc, trans_ref, transform);
+ break;
+ }
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/elements_abi.c b/vendor/gioui.org/shader/piet/elements_abi.c
new file mode 100644
index 0000000..bacf32d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements_abi.c
@@ -0,0 +1,23 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stddef.h>
+#include "abi.h"
+#include "runtime.h"
+#include "elements_abi.h"
+
+const struct program_info elements_program_info = {
+ .has_cbarriers = 1,
+ .min_memory_size = 100000,
+ .desc_set_size = sizeof(struct elements_descriptor_set_layout),
+ .workgroup_size_x = 32,
+ .workgroup_size_y = 1,
+ .workgroup_size_z = 1,
+ .begin = elements_coroutine_begin,
+ .await = elements_coroutine_await,
+ .destroy = elements_coroutine_destroy,
+};
diff --git a/vendor/gioui.org/shader/piet/elements_abi.go b/vendor/gioui.org/shader/piet/elements_abi.go
new file mode 100644
index 0000000..a85eeca
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements_abi.go
@@ -0,0 +1,43 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package piet
+
+import "gioui.org/cpu"
+import "unsafe"
+
+/*
+#cgo LDFLAGS: -lm
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+#include "elements_abi.h"
+*/
+import "C"
+
+var ElementsProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.elements_program_info))
+
+type ElementsDescriptorSetLayout = C.struct_elements_descriptor_set_layout
+
+const ElementsHash = "0f18de15866045b36217068789c9c8715a63e0f9f120c53ea2d4d76f53e443c3"
+
+func (l *ElementsDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0))
+}
+
+func (l *ElementsDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1))
+}
+
+func (l *ElementsDescriptorSetLayout) Binding2() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding2))
+}
+
+func (l *ElementsDescriptorSetLayout) Binding3() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding3))
+}
diff --git a/vendor/gioui.org/shader/piet/elements_abi.h b/vendor/gioui.org/shader/piet/elements_abi.h
new file mode 100644
index 0000000..c455224
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements_abi.h
@@ -0,0 +1,19 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+struct elements_descriptor_set_layout {
+ struct buffer_descriptor binding0;
+ struct buffer_descriptor binding1;
+ struct buffer_descriptor binding2;
+ struct buffer_descriptor binding3;
+};
+
+extern coroutine elements_coroutine_begin(struct program_data *data,
+ int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount) ATTR_HIDDEN;
+
+extern bool elements_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN;
+extern void elements_coroutine_destroy(coroutine r) ATTR_HIDDEN;
+
+extern const struct program_info elements_program_info ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/elements_abi_nosupport.go b/vendor/gioui.org/shader/piet/elements_abi_nosupport.go
new file mode 100644
index 0000000..8b30234
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements_abi_nosupport.go
@@ -0,0 +1,30 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package piet
+
+import "gioui.org/cpu"
+
+var ElementsProgramInfo *cpu.ProgramInfo
+
+type ElementsDescriptorSetLayout struct{}
+
+const ElementsHash = ""
+
+func (l *ElementsDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *ElementsDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *ElementsDescriptorSetLayout) Binding2() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *ElementsDescriptorSetLayout) Binding3() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
diff --git a/vendor/gioui.org/shader/piet/elements_linux_amd64.syso b/vendor/gioui.org/shader/piet/elements_linux_amd64.syso
new file mode 100644
index 0000000..bba953b
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements_linux_amd64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/elements_linux_arm.syso b/vendor/gioui.org/shader/piet/elements_linux_arm.syso
new file mode 100644
index 0000000..1325ac8
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements_linux_arm.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/elements_linux_arm64.syso b/vendor/gioui.org/shader/piet/elements_linux_arm64.syso
new file mode 100644
index 0000000..6855eb6
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/elements_linux_arm64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/gen.go b/vendor/gioui.org/shader/piet/gen.go
new file mode 100644
index 0000000..4b80e92
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/gen.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package piet
+
+//go:generate go run ../cmd/convertshaders -package piet -dir .
diff --git a/vendor/gioui.org/shader/piet/gencpu.sh b/vendor/gioui.org/shader/piet/gencpu.sh
new file mode 100644
index 0000000..d6f6f7d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/gencpu.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: Unlicense OR MIT
+
+set -e
+
+OBJCOPY_ARM64=$ANDROID_SDK_ROOT/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/aarch64-linux-android/bin/objcopy
+OBJCOPY_ARM=$ANDROID_SDK_ROOT/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/objcopy
+SWIFTSHADER=$HOME/.cache/swiftshader
+
+export CGO_ENABLED=1
+export GOARCH=386
+export VK_ICD_FILENAMES=$SWIFTSHADER/build.32bit/Linux/vk_swiftshader_icd.json
+
+export SWIFTSHADER_TRIPLE=armv7a-none-eabi
+go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer,2:image,3:image" kernel4.comp
+go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" coarse.comp
+go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" binning.comp
+go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" backdrop.comp
+go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" path_coarse.comp
+go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" tile_alloc.comp
+go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer,2:buffer,3:buffer" elements.comp
+
+export GOARCH=amd64
+export VK_ICD_FILENAMES=$SWIFTSHADER/build.64bit/Linux/vk_swiftshader_icd.json
+export SWIFTSHADER_TRIPLE=x86_64-unknown-none-gnu
+
+go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer,2:image,3:image" kernel4.comp
+go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" coarse.comp
+go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" binning.comp
+go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" backdrop.comp
+go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" path_coarse.comp
+go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" tile_alloc.comp
+go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer,2:buffer,3:buffer" elements.comp
+
+export SWIFTSHADER_TRIPLE=aarch64-unknown-linux-gnu
+
+go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer,2:image,3:image" kernel4.comp
+go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" coarse.comp
+go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" binning.comp
+go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" backdrop.comp
+go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" path_coarse.comp
+go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" tile_alloc.comp
+go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer,2:buffer,3:buffer" elements.comp
diff --git a/vendor/gioui.org/shader/piet/kernel4.comp b/vendor/gioui.org/shader/piet/kernel4.comp
new file mode 100644
index 0000000..3f8ef65
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4.comp
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// This is "kernel 4" in a 4-kernel pipeline. It renders the commands
+// in the per-tile command list to an image.
+
+// Right now, this kernel stores the image in a buffer, but a better
+// plan is to use a texture. This is because of limited support.
+
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+#ifdef ENABLE_IMAGE_INDICES
+#extension GL_EXT_nonuniform_qualifier : enable
+#endif
+
+#include "mem.h"
+#include "setup.h"
+
+#define CHUNK_X 2
+#define CHUNK_Y 4
+#define CHUNK CHUNK_X * CHUNK_Y
+#define CHUNK_DX (TILE_WIDTH_PX / CHUNK_X)
+#define CHUNK_DY (TILE_HEIGHT_PX / CHUNK_Y)
+layout(local_size_x = CHUNK_DX, local_size_y = CHUNK_DY) in;
+
+layout(set = 0, binding = 1) restrict readonly buffer ConfigBuf {
+ Config conf;
+};
+
+layout(rgba8, set = 0, binding = 2) uniform restrict writeonly image2D image;
+
+#ifdef ENABLE_IMAGE_INDICES
+layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images[];
+#else
+layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images;
+#endif
+
+#include "ptcl.h"
+#include "tile.h"
+
+mediump vec3 tosRGB(mediump vec3 rgb) {
+ bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308));
+ mediump vec3 below = vec3(12.92)*rgb;
+ mediump vec3 above = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055);
+ return mix(below, above, cutoff);
+}
+
+mediump vec3 fromsRGB(mediump vec3 srgb) {
+ // Formula from EXT_sRGB.
+ bvec3 cutoff = greaterThanEqual(srgb, vec3(0.04045));
+ mediump vec3 below = srgb/vec3(12.92);
+ mediump vec3 above = pow((srgb + vec3(0.055))/vec3(1.055), vec3(2.4));
+ return mix(below, above, cutoff);
+}
+
+// unpacksRGB unpacks a color in the sRGB color space to a vec4 in the linear color
+// space.
+mediump vec4 unpacksRGB(uint srgba) {
+ mediump vec4 color = unpackUnorm4x8(srgba).wzyx;
+ return vec4(fromsRGB(color.rgb), color.a);
+}
+
+// packsRGB packs a color in the linear color space into its 8-bit sRGB equivalent.
+uint packsRGB(mediump vec4 rgba) {
+ rgba = vec4(tosRGB(rgba.rgb), rgba.a);
+ return packUnorm4x8(rgba.wzyx);
+}
+
+uvec2 chunk_offset(uint i) {
+ return uvec2(i % CHUNK_X * CHUNK_DX, i / CHUNK_X * CHUNK_DY);
+}
+
+mediump vec4[CHUNK] fillImage(uvec2 xy, CmdImage cmd_img) {
+ mediump vec4 rgba[CHUNK];
+ for (uint i = 0; i < CHUNK; i++) {
+ ivec2 uv = ivec2(xy + chunk_offset(i)) + cmd_img.offset;
+ mediump vec4 fg_rgba;
+#ifdef ENABLE_IMAGE_INDICES
+ fg_rgba = imageLoad(images[cmd_img.index], uv);
+#else
+ fg_rgba = imageLoad(images, uv);
+#endif
+ fg_rgba.rgb = fromsRGB(fg_rgba.rgb);
+ rgba[i] = fg_rgba;
+ }
+ return rgba;
+}
+
+void main() {
+ uint tile_ix = gl_WorkGroupID.y * conf.width_in_tiles + gl_WorkGroupID.x;
+ Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC);
+ CmdRef cmd_ref = CmdRef(cmd_alloc.offset);
+
+ // Read scrach space allocation, written first in the command list.
+ Alloc scratch_alloc = alloc_read(cmd_alloc, cmd_ref.offset);
+ cmd_ref.offset += Alloc_size;
+
+ uvec2 xy_uint = uvec2(gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_WorkGroupID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y);
+ vec2 xy = vec2(xy_uint);
+ mediump vec4 rgba[CHUNK];
+ for (uint i = 0; i < CHUNK; i++) {
+ rgba[i] = vec4(0.0);
+ // TODO: remove this debug image support when the actual image method is plumbed.
+#ifdef DEBUG_IMAGES
+#ifdef ENABLE_IMAGE_INDICES
+ if (xy_uint.x < 1024 && xy_uint.y < 1024) {
+ rgba[i] = imageLoad(images[gl_WorkGroupID.x / 64], ivec2(xy_uint + chunk_offset(i))/4);
+ }
+#else
+ if (xy_uint.x < 1024 && xy_uint.y < 1024) {
+ rgb[i] = imageLoad(images[0], ivec2(xy_uint + chunk_offset(i))/4).rgb;
+ }
+#endif
+#endif
+ }
+
+ mediump float area[CHUNK];
+ uint clip_depth = 0;
+ bool mem_ok = mem_error == NO_ERROR;
+ while (mem_ok) {
+ uint tag = Cmd_tag(cmd_alloc, cmd_ref).tag;
+ if (tag == Cmd_End) {
+ break;
+ }
+ switch (tag) {
+ case Cmd_Stroke:
+ // Calculate distance field from all the line segments in this tile.
+ CmdStroke stroke = Cmd_Stroke_read(cmd_alloc, cmd_ref);
+ mediump float df[CHUNK];
+ for (uint k = 0; k < CHUNK; k++) df[k] = 1e9;
+ TileSegRef tile_seg_ref = TileSegRef(stroke.tile_ref);
+ do {
+ TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size, mem_ok), tile_seg_ref);
+ vec2 line_vec = seg.vector;
+ for (uint k = 0; k < CHUNK; k++) {
+ vec2 dpos = xy + vec2(0.5, 0.5) - seg.origin;
+ dpos += vec2(chunk_offset(k));
+ float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0);
+ df[k] = min(df[k], length(line_vec * t - dpos));
+ }
+ tile_seg_ref = seg.next;
+ } while (tile_seg_ref.offset != 0);
+ for (uint k = 0; k < CHUNK; k++) {
+ area[k] = clamp(stroke.half_width + 0.5 - df[k], 0.0, 1.0);
+ }
+ cmd_ref.offset += 4 + CmdStroke_size;
+ break;
+ case Cmd_Fill:
+ CmdFill fill = Cmd_Fill_read(cmd_alloc, cmd_ref);
+ for (uint k = 0; k < CHUNK; k++) area[k] = float(fill.backdrop);
+ tile_seg_ref = TileSegRef(fill.tile_ref);
+ // Calculate coverage based on backdrop + coverage of each line segment
+ do {
+ TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size, mem_ok), tile_seg_ref);
+ for (uint k = 0; k < CHUNK; k++) {
+ vec2 my_xy = xy + vec2(chunk_offset(k));
+ vec2 start = seg.origin - my_xy;
+ vec2 end = start + seg.vector;
+ vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0);
+ if (window.x != window.y) {
+ vec2 t = (window - start.y) / seg.vector.y;
+ vec2 xs = vec2(mix(start.x, end.x, t.x), mix(start.x, end.x, t.y));
+ float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6;
+ float xmax = max(xs.x, xs.y);
+ float b = min(xmax, 1.0);
+ float c = max(b, 0.0);
+ float d = max(xmin, 0.0);
+ float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin);
+ area[k] += a * (window.x - window.y);
+ }
+ area[k] += sign(seg.vector.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0);
+ }
+ tile_seg_ref = seg.next;
+ } while (tile_seg_ref.offset != 0);
+ for (uint k = 0; k < CHUNK; k++) {
+ area[k] = min(abs(area[k]), 1.0);
+ }
+ cmd_ref.offset += 4 + CmdFill_size;
+ break;
+ case Cmd_Solid:
+ for (uint k = 0; k < CHUNK; k++) {
+ area[k] = 1.0;
+ }
+ cmd_ref.offset += 4;
+ break;
+ case Cmd_Alpha:
+ CmdAlpha alpha = Cmd_Alpha_read(cmd_alloc, cmd_ref);
+ for (uint k = 0; k < CHUNK; k++) {
+ area[k] = alpha.alpha;
+ }
+ cmd_ref.offset += 4 + CmdAlpha_size;
+ break;
+ case Cmd_Color:
+ CmdColor color = Cmd_Color_read(cmd_alloc, cmd_ref);
+ mediump vec4 fg = unpacksRGB(color.rgba_color);
+ for (uint k = 0; k < CHUNK; k++) {
+ mediump vec4 fg_k = fg * area[k];
+ rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k;
+ }
+ cmd_ref.offset += 4 + CmdColor_size;
+ break;
+ case Cmd_Image:
+ CmdImage fill_img = Cmd_Image_read(cmd_alloc, cmd_ref);
+ mediump vec4 img[CHUNK] = fillImage(xy_uint, fill_img);
+ for (uint k = 0; k < CHUNK; k++) {
+ mediump vec4 fg_k = img[k] * area[k];
+ rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k;
+ }
+ cmd_ref.offset += 4 + CmdImage_size;
+ break;
+ case Cmd_BeginClip:
+ uint base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX +
+ gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y);
+ for (uint k = 0; k < CHUNK; k++) {
+ uvec2 offset = chunk_offset(k);
+ uint srgb = packsRGB(vec4(rgba[k]));
+ mediump float alpha = clamp(abs(area[k]), 0.0, 1.0);
+ write_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), srgb);
+ write_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), floatBitsToUint(alpha));
+ rgba[k] = vec4(0.0);
+ }
+ clip_depth++;
+ cmd_ref.offset += 4;
+ break;
+ case Cmd_EndClip:
+ clip_depth--;
+ base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX +
+ gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y);
+ for (uint k = 0; k < CHUNK; k++) {
+ uvec2 offset = chunk_offset(k);
+ uint srgb = read_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX));
+ uint alpha = read_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX));
+ mediump vec4 bg = unpacksRGB(srgb);
+ mediump vec4 fg = rgba[k] * area[k] * uintBitsToFloat(alpha);
+ rgba[k] = bg * (1.0 - fg.a) + fg;
+ }
+ cmd_ref.offset += 4;
+ break;
+ case Cmd_Jump:
+ cmd_ref = CmdRef(Cmd_Jump_read(cmd_alloc, cmd_ref).new_ref);
+ cmd_alloc.offset = cmd_ref.offset;
+ break;
+ }
+ }
+
+ for (uint i = 0; i < CHUNK; i++) {
+ imageStore(image, ivec2(xy_uint + chunk_offset(i)), vec4(tosRGB(rgba[i].rgb), rgba[i].a));
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/kernel4_abi.c b/vendor/gioui.org/shader/piet/kernel4_abi.c
new file mode 100644
index 0000000..81c4ac4
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4_abi.c
@@ -0,0 +1,23 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stddef.h>
+#include "abi.h"
+#include "runtime.h"
+#include "kernel4_abi.h"
+
+const struct program_info kernel4_program_info = {
+ .has_cbarriers = 0,
+ .min_memory_size = 100000,
+ .desc_set_size = sizeof(struct kernel4_descriptor_set_layout),
+ .workgroup_size_x = 16,
+ .workgroup_size_y = 8,
+ .workgroup_size_z = 1,
+ .begin = kernel4_coroutine_begin,
+ .await = kernel4_coroutine_await,
+ .destroy = kernel4_coroutine_destroy,
+};
diff --git a/vendor/gioui.org/shader/piet/kernel4_abi.go b/vendor/gioui.org/shader/piet/kernel4_abi.go
new file mode 100644
index 0000000..9804647
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4_abi.go
@@ -0,0 +1,43 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package piet
+
+import "gioui.org/cpu"
+import "unsafe"
+
+/*
+#cgo LDFLAGS: -lm
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+#include "kernel4_abi.h"
+*/
+import "C"
+
+var Kernel4ProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.kernel4_program_info))
+
+type Kernel4DescriptorSetLayout = C.struct_kernel4_descriptor_set_layout
+
+const Kernel4Hash = "88ae29cf53c1819fad9680e85faaee30fcc934d1a978a717695c966ef051bf1d"
+
+func (l *Kernel4DescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0))
+}
+
+func (l *Kernel4DescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1))
+}
+
+func (l *Kernel4DescriptorSetLayout) Binding2() *cpu.ImageDescriptor {
+ return (*cpu.ImageDescriptor)(unsafe.Pointer(&l.binding2))
+}
+
+func (l *Kernel4DescriptorSetLayout) Binding3() *cpu.ImageDescriptor {
+ return (*cpu.ImageDescriptor)(unsafe.Pointer(&l.binding3))
+}
diff --git a/vendor/gioui.org/shader/piet/kernel4_abi.h b/vendor/gioui.org/shader/piet/kernel4_abi.h
new file mode 100644
index 0000000..0d3b4c9
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4_abi.h
@@ -0,0 +1,19 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+struct kernel4_descriptor_set_layout {
+ struct buffer_descriptor binding0;
+ struct buffer_descriptor binding1;
+ struct image_descriptor binding2;
+ struct image_descriptor binding3;
+};
+
+extern coroutine kernel4_coroutine_begin(struct program_data *data,
+ int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount) ATTR_HIDDEN;
+
+extern bool kernel4_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN;
+extern void kernel4_coroutine_destroy(coroutine r) ATTR_HIDDEN;
+
+extern const struct program_info kernel4_program_info ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/kernel4_abi_nosupport.go b/vendor/gioui.org/shader/piet/kernel4_abi_nosupport.go
new file mode 100644
index 0000000..6cff47f
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4_abi_nosupport.go
@@ -0,0 +1,30 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package piet
+
+import "gioui.org/cpu"
+
+var Kernel4ProgramInfo *cpu.ProgramInfo
+
+type Kernel4DescriptorSetLayout struct{}
+
+const Kernel4Hash = ""
+
+func (l *Kernel4DescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *Kernel4DescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *Kernel4DescriptorSetLayout) Binding2() *cpu.ImageDescriptor {
+ panic("unsupported")
+}
+
+func (l *Kernel4DescriptorSetLayout) Binding3() *cpu.ImageDescriptor {
+ panic("unsupported")
+}
diff --git a/vendor/gioui.org/shader/piet/kernel4_linux_amd64.syso b/vendor/gioui.org/shader/piet/kernel4_linux_amd64.syso
new file mode 100644
index 0000000..bdf0e46
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4_linux_amd64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/kernel4_linux_arm.syso b/vendor/gioui.org/shader/piet/kernel4_linux_arm.syso
new file mode 100644
index 0000000..6fab185
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4_linux_arm.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/kernel4_linux_arm64.syso b/vendor/gioui.org/shader/piet/kernel4_linux_arm64.syso
new file mode 100644
index 0000000..b7994d7
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/kernel4_linux_arm64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/mem.h b/vendor/gioui.org/shader/piet/mem.h
new file mode 100644
index 0000000..9e81f04
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/mem.h
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+layout(set = 0, binding = 0) buffer Memory {
+ // offset into memory of the next allocation, initialized by the user.
+ uint mem_offset;
+ // mem_error tracks the status of memory accesses, initialized to NO_ERROR
+ // by the user. ERR_MALLOC_FAILED is reported for insufficient memory.
+ // If MEM_DEBUG is defined the following errors are reported:
+ // - ERR_OUT_OF_BOUNDS is reported for out of bounds writes.
+ // - ERR_UNALIGNED_ACCESS for memory access not aligned to 32-bit words.
+ uint mem_error;
+ uint[] memory;
+};
+
+// Uncomment this line to add the size field to Alloc and enable memory checks.
+// Note that the Config struct in setup.h grows size fields as well.
+//#define MEM_DEBUG
+
+#define NO_ERROR 0
+#define ERR_MALLOC_FAILED 1
+#define ERR_OUT_OF_BOUNDS 2
+#define ERR_UNALIGNED_ACCESS 3
+
+#ifdef MEM_DEBUG
+#define Alloc_size 16
+#else
+#define Alloc_size 8
+#endif
+
+// Alloc represents a memory allocation.
+struct Alloc {
+ // offset in bytes into memory.
+ uint offset;
+#ifdef MEM_DEBUG
+ // size in bytes of the allocation.
+ uint size;
+#endif
+};
+
+struct MallocResult {
+ Alloc alloc;
+ // failed is true if the allocation overflowed memory.
+ bool failed;
+};
+
+// new_alloc synthesizes an Alloc from an offset and size.
+Alloc new_alloc(uint offset, uint size, bool mem_ok) {
+ Alloc a;
+ a.offset = offset;
+#ifdef MEM_DEBUG
+ if (mem_ok) {
+ a.size = size;
+ } else {
+ a.size = 0;
+ }
+#endif
+ return a;
+}
+
+// malloc allocates size bytes of memory.
+MallocResult malloc(uint size) {
+ MallocResult r;
+ uint offset = atomicAdd(mem_offset, size);
+ r.failed = offset + size > memory.length() * 4;
+ r.alloc = new_alloc(offset, size, !r.failed);
+ if (r.failed) {
+ atomicMax(mem_error, ERR_MALLOC_FAILED);
+ return r;
+ }
+#ifdef MEM_DEBUG
+ if ((size & 3) != 0) {
+ r.failed = true;
+ atomicMax(mem_error, ERR_UNALIGNED_ACCESS);
+ return r;
+ }
+#endif
+ return r;
+}
+
+// touch_mem checks whether access to the memory word at offset is valid.
+// If MEM_DEBUG is defined, touch_mem returns false if offset is out of bounds.
+// Offset is in words.
+bool touch_mem(Alloc alloc, uint offset) {
+#ifdef MEM_DEBUG
+ if (offset < alloc.offset/4 || offset >= (alloc.offset + alloc.size)/4) {
+ atomicMax(mem_error, ERR_OUT_OF_BOUNDS);
+ return false;
+ }
+#endif
+ return true;
+}
+
+// write_mem writes val to memory at offset.
+// Offset is in words.
+void write_mem(Alloc alloc, uint offset, uint val) {
+ if (!touch_mem(alloc, offset)) {
+ return;
+ }
+ memory[offset] = val;
+}
+
+// read_mem reads the value from memory at offset.
+// Offset is in words.
+uint read_mem(Alloc alloc, uint offset) {
+ if (!touch_mem(alloc, offset)) {
+ return 0;
+ }
+ uint v = memory[offset];
+ return v;
+}
+
+// slice_mem returns a sub-allocation inside another. Offset and size are in
+// bytes, relative to a.offset.
+Alloc slice_mem(Alloc a, uint offset, uint size) {
+#ifdef MEM_DEBUG
+ if ((offset & 3) != 0 || (size & 3) != 0) {
+ atomicMax(mem_error, ERR_UNALIGNED_ACCESS);
+ return Alloc(0, 0);
+ }
+ if (offset + size > a.size) {
+ // slice_mem is sometimes used for slices outside bounds,
+ // but never written.
+ return Alloc(0, 0);
+ }
+ return Alloc(a.offset + offset, size);
+#else
+ return Alloc(a.offset + offset);
+#endif
+}
+
+// alloc_write writes alloc to memory at offset bytes.
+void alloc_write(Alloc a, uint offset, Alloc alloc) {
+ write_mem(a, offset >> 2, alloc.offset);
+#ifdef MEM_DEBUG
+ write_mem(a, (offset >> 2) + 1, alloc.size);
+#endif
+}
+
+// alloc_read reads an Alloc from memory at offset bytes.
+Alloc alloc_read(Alloc a, uint offset) {
+ Alloc alloc;
+ alloc.offset = read_mem(a, offset >> 2);
+#ifdef MEM_DEBUG
+ alloc.size = read_mem(a, (offset >> 2) + 1);
+#endif
+ return alloc;
+}
diff --git a/vendor/gioui.org/shader/piet/path_coarse.comp b/vendor/gioui.org/shader/piet/path_coarse.comp
new file mode 100644
index 0000000..ea525f5
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse.comp
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Coarse rasterization of path segments.
+
+// Allocation and initialization of tiles for paths.
+
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+#include "mem.h"
+#include "setup.h"
+
+#define LG_COARSE_WG 5
+#define COARSE_WG (1 << LG_COARSE_WG)
+
+layout(local_size_x = COARSE_WG, local_size_y = 1) in;
+
+layout(set = 0, binding = 1) readonly buffer ConfigBuf {
+ Config conf;
+};
+
+#include "pathseg.h"
+#include "tile.h"
+
+// scale factors useful for converting coordinates to tiles
+#define SX (1.0 / float(TILE_WIDTH_PX))
+#define SY (1.0 / float(TILE_HEIGHT_PX))
+
+#define ACCURACY 0.25
+#define Q_ACCURACY (ACCURACY * 0.1)
+#define REM_ACCURACY (ACCURACY - Q_ACCURACY)
+#define MAX_HYPOT2 (432.0 * Q_ACCURACY * Q_ACCURACY)
+#define MAX_QUADS 16
+
+vec2 eval_quad(vec2 p0, vec2 p1, vec2 p2, float t) {
+ float mt = 1.0 - t;
+ return p0 * (mt * mt) + (p1 * (mt * 2.0) + p2 * t) * t;
+}
+
+vec2 eval_cubic(vec2 p0, vec2 p1, vec2 p2, vec2 p3, float t) {
+ float mt = 1.0 - t;
+ return p0 * (mt * mt * mt) + (p1 * (mt * mt * 3.0) + (p2 * (mt * 3.0) + p3 * t) * t) * t;
+}
+
+struct SubdivResult {
+ float val;
+ float a0;
+ float a2;
+};
+
+/// An approximation to $\int (1 + 4x^2) ^ -0.25 dx$
+///
+/// This is used for flattening curves.
+#define D 0.67
+float approx_parabola_integral(float x) {
+ return x * inversesqrt(sqrt(1.0 - D + (D * D * D * D + 0.25 * x * x)));
+}
+
+/// An approximation to the inverse parabola integral.
+#define B 0.39
+float approx_parabola_inv_integral(float x) {
+ return x * sqrt(1.0 - B + (B * B + 0.25 * x * x));
+}
+
+SubdivResult estimate_subdiv(vec2 p0, vec2 p1, vec2 p2, float sqrt_tol) {
+ vec2 d01 = p1 - p0;
+ vec2 d12 = p2 - p1;
+ vec2 dd = d01 - d12;
+ float cross = (p2.x - p0.x) * dd.y - (p2.y - p0.y) * dd.x;
+ float x0 = (d01.x * dd.x + d01.y * dd.y) / cross;
+ float x2 = (d12.x * dd.x + d12.y * dd.y) / cross;
+ float scale = abs(cross / (length(dd) * (x2 - x0)));
+
+ float a0 = approx_parabola_integral(x0);
+ float a2 = approx_parabola_integral(x2);
+ float val = 0.0;
+ if (scale < 1e9) {
+ float da = abs(a2 - a0);
+ float sqrt_scale = sqrt(scale);
+ if (sign(x0) == sign(x2)) {
+ val = da * sqrt_scale;
+ } else {
+ float xmin = sqrt_tol / sqrt_scale;
+ val = sqrt_tol * da / approx_parabola_integral(xmin);
+ }
+ }
+ return SubdivResult(val, a0, a2);
+}
+
+void main() {
+ uint element_ix = gl_GlobalInvocationID.x;
+ PathSegRef ref = PathSegRef(conf.pathseg_alloc.offset + element_ix * PathSeg_size);
+
+ PathSegTag tag = PathSegTag(PathSeg_Nop, 0);
+ if (element_ix < conf.n_pathseg) {
+ tag = PathSeg_tag(conf.pathseg_alloc, ref);
+ }
+ bool mem_ok = mem_error == NO_ERROR;
+ switch (tag.tag) {
+ case PathSeg_Cubic:
+ PathCubic cubic = PathSeg_Cubic_read(conf.pathseg_alloc, ref);
+
+ uint trans_ix = cubic.trans_ix;
+ if (trans_ix > 0) {
+ TransformSegRef trans_ref = TransformSegRef(conf.trans_alloc.offset + (trans_ix - 1) * TransformSeg_size);
+ TransformSeg trans = TransformSeg_read(conf.trans_alloc, trans_ref);
+ cubic.p0 = trans.mat.xy * cubic.p0.x + trans.mat.zw * cubic.p0.y + trans.translate;
+ cubic.p1 = trans.mat.xy * cubic.p1.x + trans.mat.zw * cubic.p1.y + trans.translate;
+ cubic.p2 = trans.mat.xy * cubic.p2.x + trans.mat.zw * cubic.p2.y + trans.translate;
+ cubic.p3 = trans.mat.xy * cubic.p3.x + trans.mat.zw * cubic.p3.y + trans.translate;
+ }
+
+ vec2 err_v = 3.0 * (cubic.p2 - cubic.p1) + cubic.p0 - cubic.p3;
+ float err = err_v.x * err_v.x + err_v.y * err_v.y;
+ // The number of quadratics.
+ uint n_quads = max(uint(ceil(pow(err * (1.0 / MAX_HYPOT2), 1.0 / 6.0))), 1);
+ n_quads = min(n_quads, MAX_QUADS);
+ SubdivResult keep_params[MAX_QUADS];
+ // Iterate over quadratics and tote up the estimated number of segments.
+ float val = 0.0;
+ vec2 qp0 = cubic.p0;
+ float step = 1.0 / float(n_quads);
+ for (uint i = 0; i < n_quads; i++) {
+ float t = float(i + 1) * step;
+ vec2 qp2 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t);
+ vec2 qp1 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t - 0.5 * step);
+ qp1 = 2.0 * qp1 - 0.5 * (qp0 + qp2);
+ SubdivResult params = estimate_subdiv(qp0, qp1, qp2, sqrt(REM_ACCURACY));
+ keep_params[i] = params;
+ val += params.val;
+
+ qp0 = qp2;
+ }
+ uint n = max(uint(ceil(val * 0.5 / sqrt(REM_ACCURACY))), 1);
+
+ bool is_stroke = fill_mode_from_flags(tag.flags) == MODE_STROKE;
+ uint path_ix = cubic.path_ix;
+ Path path = Path_read(conf.tile_alloc, PathRef(conf.tile_alloc.offset + path_ix * Path_size));
+ Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size, mem_ok);
+ ivec4 bbox = ivec4(path.bbox);
+ vec2 p0 = cubic.p0;
+ qp0 = cubic.p0;
+ float v_step = val / float(n);
+ int n_out = 1;
+ float val_sum = 0.0;
+ for (uint i = 0; i < n_quads; i++) {
+ float t = float(i + 1) * step;
+ vec2 qp2 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t);
+ vec2 qp1 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t - 0.5 * step);
+ qp1 = 2.0 * qp1 - 0.5 * (qp0 + qp2);
+ SubdivResult params = keep_params[i];
+ float u0 = approx_parabola_inv_integral(params.a0);
+ float u2 = approx_parabola_inv_integral(params.a2);
+ float uscale = 1.0 / (u2 - u0);
+ float target = float(n_out) * v_step;
+ while (n_out == n || target < val_sum + params.val) {
+ vec2 p1;
+ if (n_out == n) {
+ p1 = cubic.p3;
+ } else {
+ float u = (target - val_sum) / params.val;
+ float a = mix(params.a0, params.a2, u);
+ float au = approx_parabola_inv_integral(a);
+ float t = (au - u0) * uscale;
+ p1 = eval_quad(qp0, qp1, qp2, t);
+ }
+
+ // Output line segment
+
+ // Bounding box of element in pixel coordinates.
+ float xmin = min(p0.x, p1.x) - cubic.stroke.x;
+ float xmax = max(p0.x, p1.x) + cubic.stroke.x;
+ float ymin = min(p0.y, p1.y) - cubic.stroke.y;
+ float ymax = max(p0.y, p1.y) + cubic.stroke.y;
+ float dx = p1.x - p0.x;
+ float dy = p1.y - p0.y;
+ // Set up for per-scanline coverage formula, below.
+ float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy;
+ float c = (cubic.stroke.x + abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + cubic.stroke.y)) * SX;
+ float b = invslope; // Note: assumes square tiles, otherwise scale.
+ float a = (p0.x - (p0.y - 0.5 * float(TILE_HEIGHT_PX)) * b) * SX;
+
+ int x0 = int(floor(xmin * SX));
+ int x1 = int(floor(xmax * SX) + 1);
+ int y0 = int(floor(ymin * SY));
+ int y1 = int(floor(ymax * SY) + 1);
+
+ x0 = clamp(x0, bbox.x, bbox.z);
+ y0 = clamp(y0, bbox.y, bbox.w);
+ x1 = clamp(x1, bbox.x, bbox.z);
+ y1 = clamp(y1, bbox.y, bbox.w);
+ float xc = a + b * float(y0);
+ int stride = bbox.z - bbox.x;
+ int base = (y0 - bbox.y) * stride - bbox.x;
+ // TODO: can be tighter, use c to bound width
+ uint n_tile_alloc = uint((x1 - x0) * (y1 - y0));
+ // Consider using subgroups to aggregate atomic add.
+ MallocResult tile_alloc = malloc(n_tile_alloc * TileSeg_size);
+ if (tile_alloc.failed || !mem_ok) {
+ return;
+ }
+ uint tile_offset = tile_alloc.alloc.offset;
+
+ TileSeg tile_seg;
+
+ int xray = int(floor(p0.x*SX));
+ int last_xray = int(floor(p1.x*SX));
+ if (p0.y > p1.y) {
+ int tmp = xray;
+ xray = last_xray;
+ last_xray = tmp;
+ }
+ for (int y = y0; y < y1; y++) {
+ float tile_y0 = float(y * TILE_HEIGHT_PX);
+ int xbackdrop = max(xray + 1, bbox.x);
+ if (!is_stroke && min(p0.y, p1.y) < tile_y0 && xbackdrop < bbox.z) {
+ int backdrop = p1.y < p0.y ? 1 : -1;
+ TileRef tile_ref = Tile_index(path.tiles, uint(base + xbackdrop));
+ uint tile_el = tile_ref.offset >> 2;
+ if (touch_mem(path_alloc, tile_el + 1)) {
+ atomicAdd(memory[tile_el + 1], backdrop);
+ }
+ }
+
+ // next_xray is the xray for the next scanline; the line segment intersects
+ // all tiles between xray and next_xray.
+ int next_xray = last_xray;
+ if (y < y1 - 1) {
+ float tile_y1 = float((y + 1) * TILE_HEIGHT_PX);
+ float x_edge = mix(p0.x, p1.x, (tile_y1 - p0.y) / dy);
+ next_xray = int(floor(x_edge*SX));
+ }
+
+ int min_xray = min(xray, next_xray);
+ int max_xray = max(xray, next_xray);
+ int xx0 = min(int(floor(xc - c)), min_xray);
+ int xx1 = max(int(ceil(xc + c)), max_xray + 1);
+ xx0 = clamp(xx0, x0, x1);
+ xx1 = clamp(xx1, x0, x1);
+
+ for (int x = xx0; x < xx1; x++) {
+ float tile_x0 = float(x * TILE_WIDTH_PX);
+ TileRef tile_ref = Tile_index(TileRef(path.tiles.offset), uint(base + x));
+ uint tile_el = tile_ref.offset >> 2;
+ uint old = 0;
+ if (touch_mem(path_alloc, tile_el)) {
+ old = atomicExchange(memory[tile_el], tile_offset);
+ }
+ tile_seg.origin = p0;
+ tile_seg.vector = p1 - p0;
+ float y_edge = 0.0;
+ if (!is_stroke) {
+ y_edge = mix(p0.y, p1.y, (tile_x0 - p0.x) / dx);
+ if (min(p0.x, p1.x) < tile_x0) {
+ vec2 p = vec2(tile_x0, y_edge);
+ if (p0.x > p1.x) {
+ tile_seg.vector = p - p0;
+ } else {
+ tile_seg.origin = p;
+ tile_seg.vector = p1 - p;
+ }
+ // kernel4 uses sign(vector.x) for the sign of the intersection backdrop.
+ // Nudge zeroes towards the intended sign.
+ if (tile_seg.vector.x == 0) {
+ tile_seg.vector.x = sign(p1.x - p0.x)*1e-9;
+ }
+ }
+ if (x <= min_xray || max_xray < x) {
+ // Reject inconsistent intersections.
+ y_edge = 1e9;
+ }
+ }
+ tile_seg.y_edge = y_edge;
+ tile_seg.next.offset = old;
+ TileSeg_write(tile_alloc.alloc, TileSegRef(tile_offset), tile_seg);
+ tile_offset += TileSeg_size;
+ }
+ xc += b;
+ base += stride;
+ xray = next_xray;
+ }
+
+ n_out += 1;
+ target += v_step;
+ p0 = p1;
+ }
+ val_sum += params.val;
+
+ qp0 = qp2;
+ }
+
+ break;
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi.c b/vendor/gioui.org/shader/piet/path_coarse_abi.c
new file mode 100644
index 0000000..575d260
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse_abi.c
@@ -0,0 +1,23 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stddef.h>
+#include "abi.h"
+#include "runtime.h"
+#include "path_coarse_abi.h"
+
+const struct program_info path_coarse_program_info = {
+ .has_cbarriers = 0,
+ .min_memory_size = 100000,
+ .desc_set_size = sizeof(struct path_coarse_descriptor_set_layout),
+ .workgroup_size_x = 32,
+ .workgroup_size_y = 1,
+ .workgroup_size_z = 1,
+ .begin = path_coarse_coroutine_begin,
+ .await = path_coarse_coroutine_await,
+ .destroy = path_coarse_coroutine_destroy,
+};
diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi.go b/vendor/gioui.org/shader/piet/path_coarse_abi.go
new file mode 100644
index 0000000..34d8ac3
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse_abi.go
@@ -0,0 +1,35 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package piet
+
+import "gioui.org/cpu"
+import "unsafe"
+
+/*
+#cgo LDFLAGS: -lm
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+#include "path_coarse_abi.h"
+*/
+import "C"
+
+var Path_coarseProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.path_coarse_program_info))
+
+type Path_coarseDescriptorSetLayout = C.struct_path_coarse_descriptor_set_layout
+
+const Path_coarseHash = "ed67e14c880cf92bdd7a9d520610e8c8b139907ff8b55df20464d353a7f58e79"
+
+func (l *Path_coarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0))
+}
+
+func (l *Path_coarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1))
+}
diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi.h b/vendor/gioui.org/shader/piet/path_coarse_abi.h
new file mode 100644
index 0000000..9e43e8e
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse_abi.h
@@ -0,0 +1,17 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+struct path_coarse_descriptor_set_layout {
+ struct buffer_descriptor binding0;
+ struct buffer_descriptor binding1;
+};
+
+extern coroutine path_coarse_coroutine_begin(struct program_data *data,
+ int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount) ATTR_HIDDEN;
+
+extern bool path_coarse_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN;
+extern void path_coarse_coroutine_destroy(coroutine r) ATTR_HIDDEN;
+
+extern const struct program_info path_coarse_program_info ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi_nosupport.go b/vendor/gioui.org/shader/piet/path_coarse_abi_nosupport.go
new file mode 100644
index 0000000..f67a16a
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse_abi_nosupport.go
@@ -0,0 +1,22 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package piet
+
+import "gioui.org/cpu"
+
+var Path_coarseProgramInfo *cpu.ProgramInfo
+
+type Path_coarseDescriptorSetLayout struct{}
+
+const Path_coarseHash = ""
+
+func (l *Path_coarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *Path_coarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
diff --git a/vendor/gioui.org/shader/piet/path_coarse_linux_amd64.syso b/vendor/gioui.org/shader/piet/path_coarse_linux_amd64.syso
new file mode 100644
index 0000000..a9f91d5
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse_linux_amd64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/path_coarse_linux_arm.syso b/vendor/gioui.org/shader/piet/path_coarse_linux_arm.syso
new file mode 100644
index 0000000..2fcd6a0
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse_linux_arm.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/path_coarse_linux_arm64.syso b/vendor/gioui.org/shader/piet/path_coarse_linux_arm64.syso
new file mode 100644
index 0000000..95f731a
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/path_coarse_linux_arm64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/pathseg.h b/vendor/gioui.org/shader/piet/pathseg.h
new file mode 100644
index 0000000..749771e
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/pathseg.h
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Code auto-generated by piet-gpu-derive
+
+struct PathCubicRef {
+ uint offset;
+};
+
+struct PathSegRef {
+ uint offset;
+};
+
+struct PathCubic {
+ vec2 p0;
+ vec2 p1;
+ vec2 p2;
+ vec2 p3;
+ uint path_ix;
+ uint trans_ix;
+ vec2 stroke;
+};
+
+#define PathCubic_size 48
+
+PathCubicRef PathCubic_index(PathCubicRef ref, uint index) {
+ return PathCubicRef(ref.offset + index * PathCubic_size);
+}
+
+#define PathSeg_Nop 0
+#define PathSeg_Cubic 1
+#define PathSeg_size 52
+
+PathSegRef PathSeg_index(PathSegRef ref, uint index) {
+ return PathSegRef(ref.offset + index * PathSeg_size);
+}
+
+struct PathSegTag {
+ uint tag;
+ uint flags;
+};
+
+PathCubic PathCubic_read(Alloc a, PathCubicRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ uint raw3 = read_mem(a, ix + 3);
+ uint raw4 = read_mem(a, ix + 4);
+ uint raw5 = read_mem(a, ix + 5);
+ uint raw6 = read_mem(a, ix + 6);
+ uint raw7 = read_mem(a, ix + 7);
+ uint raw8 = read_mem(a, ix + 8);
+ uint raw9 = read_mem(a, ix + 9);
+ uint raw10 = read_mem(a, ix + 10);
+ uint raw11 = read_mem(a, ix + 11);
+ PathCubic s;
+ s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1));
+ s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5));
+ s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7));
+ s.path_ix = raw8;
+ s.trans_ix = raw9;
+ s.stroke = vec2(uintBitsToFloat(raw10), uintBitsToFloat(raw11));
+ return s;
+}
+
+void PathCubic_write(Alloc a, PathCubicRef ref, PathCubic s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.p0.x));
+ write_mem(a, ix + 1, floatBitsToUint(s.p0.y));
+ write_mem(a, ix + 2, floatBitsToUint(s.p1.x));
+ write_mem(a, ix + 3, floatBitsToUint(s.p1.y));
+ write_mem(a, ix + 4, floatBitsToUint(s.p2.x));
+ write_mem(a, ix + 5, floatBitsToUint(s.p2.y));
+ write_mem(a, ix + 6, floatBitsToUint(s.p3.x));
+ write_mem(a, ix + 7, floatBitsToUint(s.p3.y));
+ write_mem(a, ix + 8, s.path_ix);
+ write_mem(a, ix + 9, s.trans_ix);
+ write_mem(a, ix + 10, floatBitsToUint(s.stroke.x));
+ write_mem(a, ix + 11, floatBitsToUint(s.stroke.y));
+}
+
+PathSegTag PathSeg_tag(Alloc a, PathSegRef ref) {
+ uint tag_and_flags = read_mem(a, ref.offset >> 2);
+ return PathSegTag(tag_and_flags & 0xffff, tag_and_flags >> 16);
+}
+
+PathCubic PathSeg_Cubic_read(Alloc a, PathSegRef ref) {
+ return PathCubic_read(a, PathCubicRef(ref.offset + 4));
+}
+
+void PathSeg_Nop_write(Alloc a, PathSegRef ref) {
+ write_mem(a, ref.offset >> 2, PathSeg_Nop);
+}
+
+void PathSeg_Cubic_write(Alloc a, PathSegRef ref, uint flags, PathCubic s) {
+ write_mem(a, ref.offset >> 2, (flags << 16) | PathSeg_Cubic);
+ PathCubic_write(a, PathCubicRef(ref.offset + 4), s);
+}
+
diff --git a/vendor/gioui.org/shader/piet/ptcl.h b/vendor/gioui.org/shader/piet/ptcl.h
new file mode 100644
index 0000000..6267fc5
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/ptcl.h
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Code auto-generated by piet-gpu-derive
+
+struct CmdStrokeRef {
+ uint offset;
+};
+
+struct CmdFillRef {
+ uint offset;
+};
+
+struct CmdColorRef {
+ uint offset;
+};
+
+struct CmdImageRef {
+ uint offset;
+};
+
+struct CmdAlphaRef {
+ uint offset;
+};
+
+struct CmdJumpRef {
+ uint offset;
+};
+
+struct CmdRef {
+ uint offset;
+};
+
+struct CmdStroke {
+ uint tile_ref;
+ float half_width;
+};
+
+#define CmdStroke_size 8
+
+CmdStrokeRef CmdStroke_index(CmdStrokeRef ref, uint index) {
+ return CmdStrokeRef(ref.offset + index * CmdStroke_size);
+}
+
+struct CmdFill {
+ uint tile_ref;
+ int backdrop;
+};
+
+#define CmdFill_size 8
+
+CmdFillRef CmdFill_index(CmdFillRef ref, uint index) {
+ return CmdFillRef(ref.offset + index * CmdFill_size);
+}
+
+struct CmdColor {
+ uint rgba_color;
+};
+
+#define CmdColor_size 4
+
+CmdColorRef CmdColor_index(CmdColorRef ref, uint index) {
+ return CmdColorRef(ref.offset + index * CmdColor_size);
+}
+
+struct CmdImage {
+ uint index;
+ ivec2 offset;
+};
+
+#define CmdImage_size 8
+
+CmdImageRef CmdImage_index(CmdImageRef ref, uint index) {
+ return CmdImageRef(ref.offset + index * CmdImage_size);
+}
+
+struct CmdAlpha {
+ float alpha;
+};
+
+#define CmdAlpha_size 4
+
+CmdAlphaRef CmdAlpha_index(CmdAlphaRef ref, uint index) {
+ return CmdAlphaRef(ref.offset + index * CmdAlpha_size);
+}
+
+struct CmdJump {
+ uint new_ref;
+};
+
+#define CmdJump_size 4
+
+CmdJumpRef CmdJump_index(CmdJumpRef ref, uint index) {
+ return CmdJumpRef(ref.offset + index * CmdJump_size);
+}
+
+#define Cmd_End 0
+#define Cmd_Fill 1
+#define Cmd_Stroke 2
+#define Cmd_Solid 3
+#define Cmd_Alpha 4
+#define Cmd_Color 5
+#define Cmd_Image 6
+#define Cmd_BeginClip 7
+#define Cmd_EndClip 8
+#define Cmd_Jump 9
+#define Cmd_size 12
+
+CmdRef Cmd_index(CmdRef ref, uint index) {
+ return CmdRef(ref.offset + index * Cmd_size);
+}
+
+struct CmdTag {
+ uint tag;
+ uint flags;
+};
+
+CmdStroke CmdStroke_read(Alloc a, CmdStrokeRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ CmdStroke s;
+ s.tile_ref = raw0;
+ s.half_width = uintBitsToFloat(raw1);
+ return s;
+}
+
+void CmdStroke_write(Alloc a, CmdStrokeRef ref, CmdStroke s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.tile_ref);
+ write_mem(a, ix + 1, floatBitsToUint(s.half_width));
+}
+
+CmdFill CmdFill_read(Alloc a, CmdFillRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ CmdFill s;
+ s.tile_ref = raw0;
+ s.backdrop = int(raw1);
+ return s;
+}
+
+void CmdFill_write(Alloc a, CmdFillRef ref, CmdFill s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.tile_ref);
+ write_mem(a, ix + 1, uint(s.backdrop));
+}
+
+CmdColor CmdColor_read(Alloc a, CmdColorRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ CmdColor s;
+ s.rgba_color = raw0;
+ return s;
+}
+
+void CmdColor_write(Alloc a, CmdColorRef ref, CmdColor s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.rgba_color);
+}
+
+CmdImage CmdImage_read(Alloc a, CmdImageRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ CmdImage s;
+ s.index = raw0;
+ s.offset = ivec2(int(raw1 << 16) >> 16, int(raw1) >> 16);
+ return s;
+}
+
+void CmdImage_write(Alloc a, CmdImageRef ref, CmdImage s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.index);
+ write_mem(a, ix + 1, (uint(s.offset.x) & 0xffff) | (uint(s.offset.y) << 16));
+}
+
+CmdAlpha CmdAlpha_read(Alloc a, CmdAlphaRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ CmdAlpha s;
+ s.alpha = uintBitsToFloat(raw0);
+ return s;
+}
+
+void CmdAlpha_write(Alloc a, CmdAlphaRef ref, CmdAlpha s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.alpha));
+}
+
+CmdJump CmdJump_read(Alloc a, CmdJumpRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ CmdJump s;
+ s.new_ref = raw0;
+ return s;
+}
+
+void CmdJump_write(Alloc a, CmdJumpRef ref, CmdJump s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.new_ref);
+}
+
+CmdTag Cmd_tag(Alloc a, CmdRef ref) {
+ uint tag_and_flags = read_mem(a, ref.offset >> 2);
+ return CmdTag(tag_and_flags & 0xffff, tag_and_flags >> 16);
+}
+
+CmdFill Cmd_Fill_read(Alloc a, CmdRef ref) {
+ return CmdFill_read(a, CmdFillRef(ref.offset + 4));
+}
+
+CmdStroke Cmd_Stroke_read(Alloc a, CmdRef ref) {
+ return CmdStroke_read(a, CmdStrokeRef(ref.offset + 4));
+}
+
+CmdAlpha Cmd_Alpha_read(Alloc a, CmdRef ref) {
+ return CmdAlpha_read(a, CmdAlphaRef(ref.offset + 4));
+}
+
+CmdColor Cmd_Color_read(Alloc a, CmdRef ref) {
+ return CmdColor_read(a, CmdColorRef(ref.offset + 4));
+}
+
+CmdImage Cmd_Image_read(Alloc a, CmdRef ref) {
+ return CmdImage_read(a, CmdImageRef(ref.offset + 4));
+}
+
+CmdJump Cmd_Jump_read(Alloc a, CmdRef ref) {
+ return CmdJump_read(a, CmdJumpRef(ref.offset + 4));
+}
+
+void Cmd_End_write(Alloc a, CmdRef ref) {
+ write_mem(a, ref.offset >> 2, Cmd_End);
+}
+
+void Cmd_Fill_write(Alloc a, CmdRef ref, CmdFill s) {
+ write_mem(a, ref.offset >> 2, Cmd_Fill);
+ CmdFill_write(a, CmdFillRef(ref.offset + 4), s);
+}
+
+void Cmd_Stroke_write(Alloc a, CmdRef ref, CmdStroke s) {
+ write_mem(a, ref.offset >> 2, Cmd_Stroke);
+ CmdStroke_write(a, CmdStrokeRef(ref.offset + 4), s);
+}
+
+void Cmd_Solid_write(Alloc a, CmdRef ref) {
+ write_mem(a, ref.offset >> 2, Cmd_Solid);
+}
+
+void Cmd_Alpha_write(Alloc a, CmdRef ref, CmdAlpha s) {
+ write_mem(a, ref.offset >> 2, Cmd_Alpha);
+ CmdAlpha_write(a, CmdAlphaRef(ref.offset + 4), s);
+}
+
+void Cmd_Color_write(Alloc a, CmdRef ref, CmdColor s) {
+ write_mem(a, ref.offset >> 2, Cmd_Color);
+ CmdColor_write(a, CmdColorRef(ref.offset + 4), s);
+}
+
+void Cmd_Image_write(Alloc a, CmdRef ref, CmdImage s) {
+ write_mem(a, ref.offset >> 2, Cmd_Image);
+ CmdImage_write(a, CmdImageRef(ref.offset + 4), s);
+}
+
+void Cmd_BeginClip_write(Alloc a, CmdRef ref) {
+ write_mem(a, ref.offset >> 2, Cmd_BeginClip);
+}
+
+void Cmd_EndClip_write(Alloc a, CmdRef ref) {
+ write_mem(a, ref.offset >> 2, Cmd_EndClip);
+}
+
+void Cmd_Jump_write(Alloc a, CmdRef ref, CmdJump s) {
+ write_mem(a, ref.offset >> 2, Cmd_Jump);
+ CmdJump_write(a, CmdJumpRef(ref.offset + 4), s);
+}
+
diff --git a/vendor/gioui.org/shader/piet/runtime.h b/vendor/gioui.org/shader/piet/runtime.h
new file mode 100644
index 0000000..cfae912
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/runtime.h
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+#define ATTR_HIDDEN __attribute__ ((visibility ("hidden")))
+
+// program_info contains constant parameters for a program.
+struct program_info {
+ // MinMemorySize is the minimum size of memory passed to dispatch.
+ size_t min_memory_size;
+ // has_cbarriers is 1 when the program contains control barriers.
+ bool has_cbarriers;
+ // desc_set_size is the size of the first descriptor set for the program.
+ size_t desc_set_size;
+ int workgroup_size_x;
+ int workgroup_size_y;
+ int workgroup_size_z;
+ // Program entrypoints.
+ routine_begin begin;
+ routine_await await;
+ routine_destroy destroy;
+};
+
+// dispatch_context contains the information a program dispatch.
+struct dispatch_context;
+
+// thread_context contains the working memory of a batch. It may be
+// reused, but not concurrently.
+struct thread_context;
+
+extern struct buffer_descriptor alloc_buffer(size_t size) ATTR_HIDDEN;
+extern struct image_descriptor alloc_image_rgba(int width, int height) ATTR_HIDDEN;
+
+extern struct dispatch_context *alloc_dispatch_context(void) ATTR_HIDDEN;
+
+extern void free_dispatch_context(struct dispatch_context *c) ATTR_HIDDEN;
+
+extern struct thread_context *alloc_thread_context(void) ATTR_HIDDEN;
+
+extern void free_thread_context(struct thread_context *c) ATTR_HIDDEN;
+
+// prepare_dispatch initializes ctx to run a dispatch of a program distributed
+// among nthreads threads.
+extern void prepare_dispatch(struct dispatch_context *ctx, int nthreads, struct program_info *info, uint8_t *desc_set, int ngroupx, int ngroupy, int ngroupz) ATTR_HIDDEN;
+
+// dispatch_batch executes a dispatch batch.
+extern void dispatch_thread(struct dispatch_context *ctx, int thread_idx, struct thread_context *thread) ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/scene.h b/vendor/gioui.org/shader/piet/scene.h
new file mode 100644
index 0000000..38c2549
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/scene.h
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Code auto-generated by piet-gpu-derive
+
+struct LineSegRef {
+ uint offset;
+};
+
+struct QuadSegRef {
+ uint offset;
+};
+
+struct CubicSegRef {
+ uint offset;
+};
+
+struct FillColorRef {
+ uint offset;
+};
+
+struct FillImageRef {
+ uint offset;
+};
+
+struct SetLineWidthRef {
+ uint offset;
+};
+
+struct TransformRef {
+ uint offset;
+};
+
+struct ClipRef {
+ uint offset;
+};
+
+struct SetFillModeRef {
+ uint offset;
+};
+
+struct ElementRef {
+ uint offset;
+};
+
+struct LineSeg {
+ vec2 p0;
+ vec2 p1;
+};
+
+#define LineSeg_size 16
+
+LineSegRef LineSeg_index(LineSegRef ref, uint index) {
+ return LineSegRef(ref.offset + index * LineSeg_size);
+}
+
+struct QuadSeg {
+ vec2 p0;
+ vec2 p1;
+ vec2 p2;
+};
+
+#define QuadSeg_size 24
+
+QuadSegRef QuadSeg_index(QuadSegRef ref, uint index) {
+ return QuadSegRef(ref.offset + index * QuadSeg_size);
+}
+
+struct CubicSeg {
+ vec2 p0;
+ vec2 p1;
+ vec2 p2;
+ vec2 p3;
+};
+
+#define CubicSeg_size 32
+
+CubicSegRef CubicSeg_index(CubicSegRef ref, uint index) {
+ return CubicSegRef(ref.offset + index * CubicSeg_size);
+}
+
+struct FillColor {
+ uint rgba_color;
+};
+
+#define FillColor_size 4
+
+FillColorRef FillColor_index(FillColorRef ref, uint index) {
+ return FillColorRef(ref.offset + index * FillColor_size);
+}
+
+struct FillImage {
+ uint index;
+ ivec2 offset;
+};
+
+#define FillImage_size 8
+
+FillImageRef FillImage_index(FillImageRef ref, uint index) {
+ return FillImageRef(ref.offset + index * FillImage_size);
+}
+
+struct SetLineWidth {
+ float width;
+};
+
+#define SetLineWidth_size 4
+
+SetLineWidthRef SetLineWidth_index(SetLineWidthRef ref, uint index) {
+ return SetLineWidthRef(ref.offset + index * SetLineWidth_size);
+}
+
+struct Transform {
+ vec4 mat;
+ vec2 translate;
+};
+
+#define Transform_size 24
+
+TransformRef Transform_index(TransformRef ref, uint index) {
+ return TransformRef(ref.offset + index * Transform_size);
+}
+
+struct Clip {
+ vec4 bbox;
+};
+
+#define Clip_size 16
+
+ClipRef Clip_index(ClipRef ref, uint index) {
+ return ClipRef(ref.offset + index * Clip_size);
+}
+
+struct SetFillMode {
+ uint fill_mode;
+};
+
+#define SetFillMode_size 4
+
+SetFillModeRef SetFillMode_index(SetFillModeRef ref, uint index) {
+ return SetFillModeRef(ref.offset + index * SetFillMode_size);
+}
+
+#define Element_Nop 0
+#define Element_Line 1
+#define Element_Quad 2
+#define Element_Cubic 3
+#define Element_FillColor 4
+#define Element_SetLineWidth 5
+#define Element_Transform 6
+#define Element_BeginClip 7
+#define Element_EndClip 8
+#define Element_FillImage 9
+#define Element_SetFillMode 10
+#define Element_size 36
+
+ElementRef Element_index(ElementRef ref, uint index) {
+ return ElementRef(ref.offset + index * Element_size);
+}
+
+struct ElementTag {
+ uint tag;
+ uint flags;
+};
+
+LineSeg LineSeg_read(LineSegRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ uint raw1 = scene[ix + 1];
+ uint raw2 = scene[ix + 2];
+ uint raw3 = scene[ix + 3];
+ LineSeg s;
+ s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1));
+ s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ return s;
+}
+
+QuadSeg QuadSeg_read(QuadSegRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ uint raw1 = scene[ix + 1];
+ uint raw2 = scene[ix + 2];
+ uint raw3 = scene[ix + 3];
+ uint raw4 = scene[ix + 4];
+ uint raw5 = scene[ix + 5];
+ QuadSeg s;
+ s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1));
+ s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5));
+ return s;
+}
+
+CubicSeg CubicSeg_read(CubicSegRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ uint raw1 = scene[ix + 1];
+ uint raw2 = scene[ix + 2];
+ uint raw3 = scene[ix + 3];
+ uint raw4 = scene[ix + 4];
+ uint raw5 = scene[ix + 5];
+ uint raw6 = scene[ix + 6];
+ uint raw7 = scene[ix + 7];
+ CubicSeg s;
+ s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1));
+ s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5));
+ s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7));
+ return s;
+}
+
+FillColor FillColor_read(FillColorRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ FillColor s;
+ s.rgba_color = raw0;
+ return s;
+}
+
+FillImage FillImage_read(FillImageRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ uint raw1 = scene[ix + 1];
+ FillImage s;
+ s.index = raw0;
+ s.offset = ivec2(int(raw1 << 16) >> 16, int(raw1) >> 16);
+ return s;
+}
+
+SetLineWidth SetLineWidth_read(SetLineWidthRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ SetLineWidth s;
+ s.width = uintBitsToFloat(raw0);
+ return s;
+}
+
+Transform Transform_read(TransformRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ uint raw1 = scene[ix + 1];
+ uint raw2 = scene[ix + 2];
+ uint raw3 = scene[ix + 3];
+ uint raw4 = scene[ix + 4];
+ uint raw5 = scene[ix + 5];
+ Transform s;
+ s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5));
+ return s;
+}
+
+Clip Clip_read(ClipRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ uint raw1 = scene[ix + 1];
+ uint raw2 = scene[ix + 2];
+ uint raw3 = scene[ix + 3];
+ Clip s;
+ s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ return s;
+}
+
+SetFillMode SetFillMode_read(SetFillModeRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = scene[ix + 0];
+ SetFillMode s;
+ s.fill_mode = raw0;
+ return s;
+}
+
+ElementTag Element_tag(ElementRef ref) {
+ uint tag_and_flags = scene[ref.offset >> 2];
+ return ElementTag(tag_and_flags & 0xffff, tag_and_flags >> 16);
+}
+
+LineSeg Element_Line_read(ElementRef ref) {
+ return LineSeg_read(LineSegRef(ref.offset + 4));
+}
+
+QuadSeg Element_Quad_read(ElementRef ref) {
+ return QuadSeg_read(QuadSegRef(ref.offset + 4));
+}
+
+CubicSeg Element_Cubic_read(ElementRef ref) {
+ return CubicSeg_read(CubicSegRef(ref.offset + 4));
+}
+
+FillColor Element_FillColor_read(ElementRef ref) {
+ return FillColor_read(FillColorRef(ref.offset + 4));
+}
+
+SetLineWidth Element_SetLineWidth_read(ElementRef ref) {
+ return SetLineWidth_read(SetLineWidthRef(ref.offset + 4));
+}
+
+Transform Element_Transform_read(ElementRef ref) {
+ return Transform_read(TransformRef(ref.offset + 4));
+}
+
+Clip Element_BeginClip_read(ElementRef ref) {
+ return Clip_read(ClipRef(ref.offset + 4));
+}
+
+Clip Element_EndClip_read(ElementRef ref) {
+ return Clip_read(ClipRef(ref.offset + 4));
+}
+
+FillImage Element_FillImage_read(ElementRef ref) {
+ return FillImage_read(FillImageRef(ref.offset + 4));
+}
+
+SetFillMode Element_SetFillMode_read(ElementRef ref) {
+ return SetFillMode_read(SetFillModeRef(ref.offset + 4));
+}
+
diff --git a/vendor/gioui.org/shader/piet/setup.h b/vendor/gioui.org/shader/piet/setup.h
new file mode 100644
index 0000000..83b6d1d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/setup.h
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Various constants for the sizes of groups and tiles.
+
+// Much of this will be made dynamic in various ways, but for now it's easiest
+// to hardcode and keep all in one place.
+
+// A LG_WG_FACTOR of n scales workgroup sizes by 2^n. Use 0 for a
+// maximum workgroup size of 128, or 1 for a maximum size of 256.
+#define LG_WG_FACTOR 0
+#define WG_FACTOR (1<<LG_WG_FACTOR)
+
+#define TILE_WIDTH_PX 32
+#define TILE_HEIGHT_PX 32
+
+#define PTCL_INITIAL_ALLOC 1024
+
+// These should probably be renamed and/or reworked. In the binning
+// kernel, they represent the number of bins. Also, the workgroup size
+// of that kernel is equal to the number of bins, but should probably
+// be more flexible (it's 512 in the K&L paper).
+#define N_TILE_X 16
+#define N_TILE_Y (8 * WG_FACTOR)
+#define N_TILE (N_TILE_X * N_TILE_Y)
+#define LG_N_TILE (7 + LG_WG_FACTOR)
+#define N_SLICE (N_TILE / 32)
+
+struct Config {
+ uint n_elements; // paths
+ uint n_pathseg;
+ uint width_in_tiles;
+ uint height_in_tiles;
+ Alloc tile_alloc;
+ Alloc bin_alloc;
+ Alloc ptcl_alloc;
+ Alloc pathseg_alloc;
+ Alloc anno_alloc;
+ Alloc trans_alloc;
+};
+
+// Fill modes.
+#define MODE_NONZERO 0
+#define MODE_STROKE 1
+
+// Size of kernel4 clip state, in words.
+#define CLIP_STATE_SIZE 2
+
+// fill_mode_from_flags extracts the fill mode from tag flags.
+uint fill_mode_from_flags(uint flags) {
+ return flags & 0x1;
+}
diff --git a/vendor/gioui.org/shader/piet/shaders.go b/vendor/gioui.org/shader/piet/shaders.go
new file mode 100644
index 0000000..b4b6996
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/shaders.go
@@ -0,0 +1,268 @@
+// Code generated by build.go. DO NOT EDIT.
+
+package piet
+
+import (
+ _ "embed"
+ "runtime"
+
+ "gioui.org/shader"
+)
+
+var (
+ Shader_backdrop_comp = shader.Sources{
+ Name: "backdrop.comp",
+ StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}},
+ WorkgroupSize: [3]int{128, 1, 1},
+ }
+ //go:embed zbackdrop.comp.0.spirv
+ zbackdrop_comp_0_spirv string
+ //go:embed zbackdrop.comp.0.dxbc
+ zbackdrop_comp_0_dxbc string
+ //go:embed zbackdrop.comp.0.metallibmacos
+ zbackdrop_comp_0_metallibmacos string
+ //go:embed zbackdrop.comp.0.metallibios
+ zbackdrop_comp_0_metallibios string
+ //go:embed zbackdrop.comp.0.metallibiossimulator
+ zbackdrop_comp_0_metallibiossimulator string
+ Shader_binning_comp = shader.Sources{
+ Name: "binning.comp",
+ StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}},
+ WorkgroupSize: [3]int{128, 1, 1},
+ }
+ //go:embed zbinning.comp.0.spirv
+ zbinning_comp_0_spirv string
+ //go:embed zbinning.comp.0.dxbc
+ zbinning_comp_0_dxbc string
+ //go:embed zbinning.comp.0.metallibmacos
+ zbinning_comp_0_metallibmacos string
+ //go:embed zbinning.comp.0.metallibios
+ zbinning_comp_0_metallibios string
+ //go:embed zbinning.comp.0.metallibiossimulator
+ zbinning_comp_0_metallibiossimulator string
+ Shader_coarse_comp = shader.Sources{
+ Name: "coarse.comp",
+ StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}},
+ WorkgroupSize: [3]int{128, 1, 1},
+ }
+ //go:embed zcoarse.comp.0.spirv
+ zcoarse_comp_0_spirv string
+ //go:embed zcoarse.comp.0.dxbc
+ zcoarse_comp_0_dxbc string
+ //go:embed zcoarse.comp.0.metallibmacos
+ zcoarse_comp_0_metallibmacos string
+ //go:embed zcoarse.comp.0.metallibios
+ zcoarse_comp_0_metallibios string
+ //go:embed zcoarse.comp.0.metallibiossimulator
+ zcoarse_comp_0_metallibiossimulator string
+ Shader_elements_comp = shader.Sources{
+ Name: "elements.comp",
+ StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "SceneBuf", Binding: 2}, {Name: "StateBuf", Binding: 3}, {Name: "ConfigBuf", Binding: 1}},
+ WorkgroupSize: [3]int{32, 1, 1},
+ }
+ //go:embed zelements.comp.0.spirv
+ zelements_comp_0_spirv string
+ //go:embed zelements.comp.0.dxbc
+ zelements_comp_0_dxbc string
+ //go:embed zelements.comp.0.metallibmacos
+ zelements_comp_0_metallibmacos string
+ //go:embed zelements.comp.0.metallibios
+ zelements_comp_0_metallibios string
+ //go:embed zelements.comp.0.metallibiossimulator
+ zelements_comp_0_metallibiossimulator string
+ Shader_kernel4_comp = shader.Sources{
+ Name: "kernel4.comp",
+ Images: []shader.ImageBinding{{Name: "images", Binding: 3}, {Name: "image", Binding: 2}},
+ StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}},
+ WorkgroupSize: [3]int{16, 8, 1},
+ }
+ //go:embed zkernel4.comp.0.spirv
+ zkernel4_comp_0_spirv string
+ //go:embed zkernel4.comp.0.dxbc
+ zkernel4_comp_0_dxbc string
+ //go:embed zkernel4.comp.0.metallibmacos
+ zkernel4_comp_0_metallibmacos string
+ //go:embed zkernel4.comp.0.metallibios
+ zkernel4_comp_0_metallibios string
+ //go:embed zkernel4.comp.0.metallibiossimulator
+ zkernel4_comp_0_metallibiossimulator string
+ Shader_path_coarse_comp = shader.Sources{
+ Name: "path_coarse.comp",
+ StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}},
+ WorkgroupSize: [3]int{32, 1, 1},
+ }
+ //go:embed zpath_coarse.comp.0.spirv
+ zpath_coarse_comp_0_spirv string
+ //go:embed zpath_coarse.comp.0.dxbc
+ zpath_coarse_comp_0_dxbc string
+ //go:embed zpath_coarse.comp.0.metallibmacos
+ zpath_coarse_comp_0_metallibmacos string
+ //go:embed zpath_coarse.comp.0.metallibios
+ zpath_coarse_comp_0_metallibios string
+ //go:embed zpath_coarse.comp.0.metallibiossimulator
+ zpath_coarse_comp_0_metallibiossimulator string
+ Shader_tile_alloc_comp = shader.Sources{
+ Name: "tile_alloc.comp",
+ StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}},
+ WorkgroupSize: [3]int{128, 1, 1},
+ }
+ //go:embed ztile_alloc.comp.0.spirv
+ ztile_alloc_comp_0_spirv string
+ //go:embed ztile_alloc.comp.0.dxbc
+ ztile_alloc_comp_0_dxbc string
+ //go:embed ztile_alloc.comp.0.metallibmacos
+ ztile_alloc_comp_0_metallibmacos string
+ //go:embed ztile_alloc.comp.0.metallibios
+ ztile_alloc_comp_0_metallibios string
+ //go:embed ztile_alloc.comp.0.metallibiossimulator
+ ztile_alloc_comp_0_metallibiossimulator string
+)
+
+func init() {
+ const (
+ opengles = runtime.GOOS == "linux" || runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "windows" || runtime.GOOS == "js" || runtime.GOOS == "android" || runtime.GOOS == "darwin" || runtime.GOOS == "ios"
+ opengl = runtime.GOOS == "darwin"
+ d3d11 = runtime.GOOS == "windows"
+ vulkan = runtime.GOOS == "linux" || runtime.GOOS == "android"
+ )
+ if vulkan {
+ Shader_backdrop_comp.SPIRV = zbackdrop_comp_0_spirv
+ }
+ if opengles {
+ }
+ if opengl {
+ }
+ if d3d11 {
+ Shader_backdrop_comp.DXBC = zbackdrop_comp_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_backdrop_comp.MetalLib = zbackdrop_comp_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_backdrop_comp.MetalLib = zbackdrop_comp_0_metallibiossimulator
+ } else {
+ Shader_backdrop_comp.MetalLib = zbackdrop_comp_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_binning_comp.SPIRV = zbinning_comp_0_spirv
+ }
+ if opengles {
+ }
+ if opengl {
+ }
+ if d3d11 {
+ Shader_binning_comp.DXBC = zbinning_comp_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_binning_comp.MetalLib = zbinning_comp_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_binning_comp.MetalLib = zbinning_comp_0_metallibiossimulator
+ } else {
+ Shader_binning_comp.MetalLib = zbinning_comp_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_coarse_comp.SPIRV = zcoarse_comp_0_spirv
+ }
+ if opengles {
+ }
+ if opengl {
+ }
+ if d3d11 {
+ Shader_coarse_comp.DXBC = zcoarse_comp_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_coarse_comp.MetalLib = zcoarse_comp_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_coarse_comp.MetalLib = zcoarse_comp_0_metallibiossimulator
+ } else {
+ Shader_coarse_comp.MetalLib = zcoarse_comp_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_elements_comp.SPIRV = zelements_comp_0_spirv
+ }
+ if opengles {
+ }
+ if opengl {
+ }
+ if d3d11 {
+ Shader_elements_comp.DXBC = zelements_comp_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_elements_comp.MetalLib = zelements_comp_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_elements_comp.MetalLib = zelements_comp_0_metallibiossimulator
+ } else {
+ Shader_elements_comp.MetalLib = zelements_comp_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_kernel4_comp.SPIRV = zkernel4_comp_0_spirv
+ }
+ if opengles {
+ }
+ if opengl {
+ }
+ if d3d11 {
+ Shader_kernel4_comp.DXBC = zkernel4_comp_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_kernel4_comp.MetalLib = zkernel4_comp_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_kernel4_comp.MetalLib = zkernel4_comp_0_metallibiossimulator
+ } else {
+ Shader_kernel4_comp.MetalLib = zkernel4_comp_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_path_coarse_comp.SPIRV = zpath_coarse_comp_0_spirv
+ }
+ if opengles {
+ }
+ if opengl {
+ }
+ if d3d11 {
+ Shader_path_coarse_comp.DXBC = zpath_coarse_comp_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_path_coarse_comp.MetalLib = zpath_coarse_comp_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_path_coarse_comp.MetalLib = zpath_coarse_comp_0_metallibiossimulator
+ } else {
+ Shader_path_coarse_comp.MetalLib = zpath_coarse_comp_0_metallibios
+ }
+ }
+ if vulkan {
+ Shader_tile_alloc_comp.SPIRV = ztile_alloc_comp_0_spirv
+ }
+ if opengles {
+ }
+ if opengl {
+ }
+ if d3d11 {
+ Shader_tile_alloc_comp.DXBC = ztile_alloc_comp_0_dxbc
+ }
+ if runtime.GOOS == "darwin" {
+ Shader_tile_alloc_comp.MetalLib = ztile_alloc_comp_0_metallibmacos
+ }
+ if runtime.GOOS == "ios" {
+ if runtime.GOARCH == "amd64" {
+ Shader_tile_alloc_comp.MetalLib = ztile_alloc_comp_0_metallibiossimulator
+ } else {
+ Shader_tile_alloc_comp.MetalLib = ztile_alloc_comp_0_metallibios
+ }
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/state.h b/vendor/gioui.org/shader/piet/state.h
new file mode 100644
index 0000000..d2df804
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/state.h
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Code auto-generated by piet-gpu-derive
+
+struct StateRef {
+ uint offset;
+};
+
+struct State {
+ vec4 mat;
+ vec2 translate;
+ vec4 bbox;
+ float linewidth;
+ uint flags;
+ uint path_count;
+ uint pathseg_count;
+ uint trans_count;
+};
+
+#define State_size 60
+
+StateRef State_index(StateRef ref, uint index) {
+ return StateRef(ref.offset + index * State_size);
+}
+
+State State_read(StateRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = state[ix + 0];
+ uint raw1 = state[ix + 1];
+ uint raw2 = state[ix + 2];
+ uint raw3 = state[ix + 3];
+ uint raw4 = state[ix + 4];
+ uint raw5 = state[ix + 5];
+ uint raw6 = state[ix + 6];
+ uint raw7 = state[ix + 7];
+ uint raw8 = state[ix + 8];
+ uint raw9 = state[ix + 9];
+ uint raw10 = state[ix + 10];
+ uint raw11 = state[ix + 11];
+ uint raw12 = state[ix + 12];
+ uint raw13 = state[ix + 13];
+ uint raw14 = state[ix + 14];
+ State s;
+ s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5));
+ s.bbox = vec4(uintBitsToFloat(raw6), uintBitsToFloat(raw7), uintBitsToFloat(raw8), uintBitsToFloat(raw9));
+ s.linewidth = uintBitsToFloat(raw10);
+ s.flags = raw11;
+ s.path_count = raw12;
+ s.pathseg_count = raw13;
+ s.trans_count = raw14;
+ return s;
+}
+
+void State_write(StateRef ref, State s) {
+ uint ix = ref.offset >> 2;
+ state[ix + 0] = floatBitsToUint(s.mat.x);
+ state[ix + 1] = floatBitsToUint(s.mat.y);
+ state[ix + 2] = floatBitsToUint(s.mat.z);
+ state[ix + 3] = floatBitsToUint(s.mat.w);
+ state[ix + 4] = floatBitsToUint(s.translate.x);
+ state[ix + 5] = floatBitsToUint(s.translate.y);
+ state[ix + 6] = floatBitsToUint(s.bbox.x);
+ state[ix + 7] = floatBitsToUint(s.bbox.y);
+ state[ix + 8] = floatBitsToUint(s.bbox.z);
+ state[ix + 9] = floatBitsToUint(s.bbox.w);
+ state[ix + 10] = floatBitsToUint(s.linewidth);
+ state[ix + 11] = s.flags;
+ state[ix + 12] = s.path_count;
+ state[ix + 13] = s.pathseg_count;
+ state[ix + 14] = s.trans_count;
+}
+
diff --git a/vendor/gioui.org/shader/piet/support.c b/vendor/gioui.org/shader/piet/support.c
new file mode 100644
index 0000000..1b7dd42
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/support.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "abi.h"
+#include "runtime.h"
+
+static void *malloc_align(size_t alignment, size_t size) {
+ void *ptr;
+ int ret = posix_memalign(&ptr, alignment, size);
+ assert(ret == 0);
+ return ptr;
+}
+
+ATTR_HIDDEN void *coroutine_alloc_frame(size_t size) {
+ void *ptr = malloc_align(16, size);
+ return ptr;
+}
+
+ATTR_HIDDEN void coroutine_free_frame(void *ptr) {
+ free(ptr);
+}
diff --git a/vendor/gioui.org/shader/piet/tile.h b/vendor/gioui.org/shader/piet/tile.h
new file mode 100644
index 0000000..e11329c
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile.h
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Code auto-generated by piet-gpu-derive
+
+struct PathRef {
+ uint offset;
+};
+
+struct TileRef {
+ uint offset;
+};
+
+struct TileSegRef {
+ uint offset;
+};
+
+struct TransformSegRef {
+ uint offset;
+};
+
+struct Path {
+ uvec4 bbox;
+ TileRef tiles;
+};
+
+#define Path_size 12
+
+PathRef Path_index(PathRef ref, uint index) {
+ return PathRef(ref.offset + index * Path_size);
+}
+
+struct Tile {
+ TileSegRef tile;
+ int backdrop;
+};
+
+#define Tile_size 8
+
+TileRef Tile_index(TileRef ref, uint index) {
+ return TileRef(ref.offset + index * Tile_size);
+}
+
+struct TileSeg {
+ vec2 origin;
+ vec2 vector;
+ float y_edge;
+ TileSegRef next;
+};
+
+#define TileSeg_size 24
+
+TileSegRef TileSeg_index(TileSegRef ref, uint index) {
+ return TileSegRef(ref.offset + index * TileSeg_size);
+}
+
+struct TransformSeg {
+ vec4 mat;
+ vec2 translate;
+};
+
+#define TransformSeg_size 24
+
+TransformSegRef TransformSeg_index(TransformSegRef ref, uint index) {
+ return TransformSegRef(ref.offset + index * TransformSeg_size);
+}
+
+Path Path_read(Alloc a, PathRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ Path s;
+ s.bbox = uvec4(raw0 & 0xffff, raw0 >> 16, raw1 & 0xffff, raw1 >> 16);
+ s.tiles = TileRef(raw2);
+ return s;
+}
+
+void Path_write(Alloc a, PathRef ref, Path s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.bbox.x | (s.bbox.y << 16));
+ write_mem(a, ix + 1, s.bbox.z | (s.bbox.w << 16));
+ write_mem(a, ix + 2, s.tiles.offset);
+}
+
+Tile Tile_read(Alloc a, TileRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ Tile s;
+ s.tile = TileSegRef(raw0);
+ s.backdrop = int(raw1);
+ return s;
+}
+
+void Tile_write(Alloc a, TileRef ref, Tile s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, s.tile.offset);
+ write_mem(a, ix + 1, uint(s.backdrop));
+}
+
+TileSeg TileSeg_read(Alloc a, TileSegRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ uint raw3 = read_mem(a, ix + 3);
+ uint raw4 = read_mem(a, ix + 4);
+ uint raw5 = read_mem(a, ix + 5);
+ TileSeg s;
+ s.origin = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1));
+ s.vector = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.y_edge = uintBitsToFloat(raw4);
+ s.next = TileSegRef(raw5);
+ return s;
+}
+
+void TileSeg_write(Alloc a, TileSegRef ref, TileSeg s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.origin.x));
+ write_mem(a, ix + 1, floatBitsToUint(s.origin.y));
+ write_mem(a, ix + 2, floatBitsToUint(s.vector.x));
+ write_mem(a, ix + 3, floatBitsToUint(s.vector.y));
+ write_mem(a, ix + 4, floatBitsToUint(s.y_edge));
+ write_mem(a, ix + 5, s.next.offset);
+}
+
+TransformSeg TransformSeg_read(Alloc a, TransformSegRef ref) {
+ uint ix = ref.offset >> 2;
+ uint raw0 = read_mem(a, ix + 0);
+ uint raw1 = read_mem(a, ix + 1);
+ uint raw2 = read_mem(a, ix + 2);
+ uint raw3 = read_mem(a, ix + 3);
+ uint raw4 = read_mem(a, ix + 4);
+ uint raw5 = read_mem(a, ix + 5);
+ TransformSeg s;
+ s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
+ s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5));
+ return s;
+}
+
+void TransformSeg_write(Alloc a, TransformSegRef ref, TransformSeg s) {
+ uint ix = ref.offset >> 2;
+ write_mem(a, ix + 0, floatBitsToUint(s.mat.x));
+ write_mem(a, ix + 1, floatBitsToUint(s.mat.y));
+ write_mem(a, ix + 2, floatBitsToUint(s.mat.z));
+ write_mem(a, ix + 3, floatBitsToUint(s.mat.w));
+ write_mem(a, ix + 4, floatBitsToUint(s.translate.x));
+ write_mem(a, ix + 5, floatBitsToUint(s.translate.y));
+}
+
diff --git a/vendor/gioui.org/shader/piet/tile_alloc.comp b/vendor/gioui.org/shader/piet/tile_alloc.comp
new file mode 100644
index 0000000..0b6eca4
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc.comp
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
+
+// Allocation and initialization of tiles for paths.
+
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+#include "mem.h"
+#include "setup.h"
+
+#define LG_TILE_ALLOC_WG (7 + LG_WG_FACTOR)
+#define TILE_ALLOC_WG (1 << LG_TILE_ALLOC_WG)
+
+layout(local_size_x = TILE_ALLOC_WG, local_size_y = 1) in;
+
+layout(set = 0, binding = 1) readonly buffer ConfigBuf {
+ Config conf;
+};
+
+#include "annotated.h"
+#include "tile.h"
+
+// scale factors useful for converting coordinates to tiles
+#define SX (1.0 / float(TILE_WIDTH_PX))
+#define SY (1.0 / float(TILE_HEIGHT_PX))
+
+shared uint sh_tile_count[TILE_ALLOC_WG];
+shared Alloc sh_tile_alloc;
+// Really a bool, but some Metal devices don't accept shared bools.
+shared uint sh_tile_alloc_failed;
+
+void main() {
+ uint th_ix = gl_LocalInvocationID.x;
+ uint element_ix = gl_GlobalInvocationID.x;
+ PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size);
+ AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size);
+
+ uint tag = Annotated_Nop;
+ if (element_ix < conf.n_elements) {
+ tag = Annotated_tag(conf.anno_alloc, ref).tag;
+ }
+ int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
+ switch (tag) {
+ case Annotated_Color:
+ case Annotated_Image:
+ case Annotated_BeginClip:
+ case Annotated_EndClip:
+ // Note: we take advantage of the fact that fills, strokes, and
+ // clips have compatible layout.
+ AnnoEndClip clip = Annotated_EndClip_read(conf.anno_alloc, ref);
+ x0 = int(floor(clip.bbox.x * SX));
+ y0 = int(floor(clip.bbox.y * SY));
+ x1 = int(ceil(clip.bbox.z * SX));
+ y1 = int(ceil(clip.bbox.w * SY));
+ break;
+ }
+ x0 = clamp(x0, 0, int(conf.width_in_tiles));
+ y0 = clamp(y0, 0, int(conf.height_in_tiles));
+ x1 = clamp(x1, 0, int(conf.width_in_tiles));
+ y1 = clamp(y1, 0, int(conf.height_in_tiles));
+
+ Path path;
+ path.bbox = uvec4(x0, y0, x1, y1);
+ uint tile_count = (x1 - x0) * (y1 - y0);
+ if (tag == Annotated_EndClip) {
+ // Don't actually allocate tiles for an end clip, but we do want
+ // the path structure (especially bbox) allocated for it.
+ tile_count = 0;
+ }
+
+ sh_tile_count[th_ix] = tile_count;
+ uint total_tile_count = tile_count;
+ // Prefix sum of sh_tile_count
+ for (uint i = 0; i < LG_TILE_ALLOC_WG; i++) {
+ barrier();
+ if (th_ix >= (1 << i)) {
+ total_tile_count += sh_tile_count[th_ix - (1 << i)];
+ }
+ barrier();
+ sh_tile_count[th_ix] = total_tile_count;
+ }
+ if (th_ix == TILE_ALLOC_WG - 1) {
+ MallocResult res = malloc(total_tile_count * Tile_size);
+ sh_tile_alloc = res.alloc;
+ sh_tile_alloc_failed = res.failed ? 1 : 0;
+ }
+ barrier();
+ if (sh_tile_alloc_failed != 0 || mem_error != NO_ERROR) {
+ return;
+ }
+ Alloc alloc_start = sh_tile_alloc;
+
+ if (element_ix < conf.n_elements) {
+ uint tile_subix = th_ix > 0 ? sh_tile_count[th_ix - 1] : 0;
+ Alloc tiles_alloc = slice_mem(alloc_start, Tile_size * tile_subix, Tile_size * tile_count);
+ path.tiles = TileRef(tiles_alloc.offset);
+ Path_write(conf.tile_alloc, path_ref, path);
+ }
+
+ // Zero out allocated tiles efficiently
+ uint total_count = sh_tile_count[TILE_ALLOC_WG - 1] * (Tile_size / 4);
+ uint start_ix = alloc_start.offset >> 2;
+ for (uint i = th_ix; i < total_count; i += TILE_ALLOC_WG) {
+ // Note: this interleaving is faster than using Tile_write
+ // by a significant amount.
+ write_mem(alloc_start, start_ix + i, 0);
+ }
+}
diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi.c b/vendor/gioui.org/shader/piet/tile_alloc_abi.c
new file mode 100644
index 0000000..0cffe2c
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc_abi.c
@@ -0,0 +1,23 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+#include <stdint.h>
+#include <stddef.h>
+#include "abi.h"
+#include "runtime.h"
+#include "tile_alloc_abi.h"
+
+const struct program_info tile_alloc_program_info = {
+ .has_cbarriers = 1,
+ .min_memory_size = 100000,
+ .desc_set_size = sizeof(struct tile_alloc_descriptor_set_layout),
+ .workgroup_size_x = 128,
+ .workgroup_size_y = 1,
+ .workgroup_size_z = 1,
+ .begin = tile_alloc_coroutine_begin,
+ .await = tile_alloc_coroutine_await,
+ .destroy = tile_alloc_coroutine_destroy,
+};
diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi.go b/vendor/gioui.org/shader/piet/tile_alloc_abi.go
new file mode 100644
index 0000000..8d0414e
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc_abi.go
@@ -0,0 +1,35 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build linux && (arm64 || arm || amd64)
+// +build linux
+// +build arm64 arm amd64
+
+package piet
+
+import "gioui.org/cpu"
+import "unsafe"
+
+/*
+#cgo LDFLAGS: -lm
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "abi.h"
+#include "runtime.h"
+#include "tile_alloc_abi.h"
+*/
+import "C"
+
+var Tile_allocProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.tile_alloc_program_info))
+
+type Tile_allocDescriptorSetLayout = C.struct_tile_alloc_descriptor_set_layout
+
+const Tile_allocHash = "364b3cf559d02a86c751292bedc571d5ceef2df899de39ad483b4176294e9857"
+
+func (l *Tile_allocDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0))
+}
+
+func (l *Tile_allocDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1))
+}
diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi.h b/vendor/gioui.org/shader/piet/tile_alloc_abi.h
new file mode 100644
index 0000000..aac2706
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc_abi.h
@@ -0,0 +1,17 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+struct tile_alloc_descriptor_set_layout {
+ struct buffer_descriptor binding0;
+ struct buffer_descriptor binding1;
+};
+
+extern coroutine tile_alloc_coroutine_begin(struct program_data *data,
+ int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ,
+ void *workgroupMemory,
+ int32_t firstSubgroup,
+ int32_t subgroupCount) ATTR_HIDDEN;
+
+extern bool tile_alloc_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN;
+extern void tile_alloc_coroutine_destroy(coroutine r) ATTR_HIDDEN;
+
+extern const struct program_info tile_alloc_program_info ATTR_HIDDEN;
diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi_nosupport.go b/vendor/gioui.org/shader/piet/tile_alloc_abi_nosupport.go
new file mode 100644
index 0000000..fb3a023
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc_abi_nosupport.go
@@ -0,0 +1,22 @@
+// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT.
+
+//go:build !(linux && (arm64 || arm || amd64))
+// +build !linux !arm64,!arm,!amd64
+
+package piet
+
+import "gioui.org/cpu"
+
+var Tile_allocProgramInfo *cpu.ProgramInfo
+
+type Tile_allocDescriptorSetLayout struct{}
+
+const Tile_allocHash = ""
+
+func (l *Tile_allocDescriptorSetLayout) Binding0() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
+
+func (l *Tile_allocDescriptorSetLayout) Binding1() *cpu.BufferDescriptor {
+ panic("unsupported")
+}
diff --git a/vendor/gioui.org/shader/piet/tile_alloc_linux_amd64.syso b/vendor/gioui.org/shader/piet/tile_alloc_linux_amd64.syso
new file mode 100644
index 0000000..0f37c87
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc_linux_amd64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/tile_alloc_linux_arm.syso b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm.syso
new file mode 100644
index 0000000..ab62099
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/tile_alloc_linux_arm64.syso b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm64.syso
new file mode 100644
index 0000000..0c968b0
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm64.syso
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.dxbc b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.dxbc
new file mode 100644
index 0000000..a255faf
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibios b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibios
new file mode 100644
index 0000000..349eb6e
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiossimulator
new file mode 100644
index 0000000..2d9f890
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibmacos
new file mode 100644
index 0000000..a72cd7b
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.spirv b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.spirv
new file mode 100644
index 0000000..e3ffcfb
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.dxbc b/vendor/gioui.org/shader/piet/zbinning.comp.0.dxbc
new file mode 100644
index 0000000..ba04622
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibios b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibios
new file mode 100644
index 0000000..862042e
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiossimulator
new file mode 100644
index 0000000..c1e8019
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibmacos
new file mode 100644
index 0000000..27508e1
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.spirv b/vendor/gioui.org/shader/piet/zbinning.comp.0.spirv
new file mode 100644
index 0000000..7cfcb0c
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.dxbc b/vendor/gioui.org/shader/piet/zcoarse.comp.0.dxbc
new file mode 100644
index 0000000..94a3259
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibios b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibios
new file mode 100644
index 0000000..29c8dbf
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiossimulator
new file mode 100644
index 0000000..12b533d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibmacos
new file mode 100644
index 0000000..c9a781b
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.spirv b/vendor/gioui.org/shader/piet/zcoarse.comp.0.spirv
new file mode 100644
index 0000000..23e652a
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.dxbc b/vendor/gioui.org/shader/piet/zelements.comp.0.dxbc
new file mode 100644
index 0000000..e4916b7
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zelements.comp.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.metallibios b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibios
new file mode 100644
index 0000000..1d5097f
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibiossimulator
new file mode 100644
index 0000000..367e234
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibmacos
new file mode 100644
index 0000000..b405013
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.spirv b/vendor/gioui.org/shader/piet/zelements.comp.0.spirv
new file mode 100644
index 0000000..84057ac
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zelements.comp.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.dxbc b/vendor/gioui.org/shader/piet/zkernel4.comp.0.dxbc
new file mode 100644
index 0000000..f459a5d
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibios b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibios
new file mode 100644
index 0000000..a979d51
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiossimulator
new file mode 100644
index 0000000..1dbb051
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibmacos
new file mode 100644
index 0000000..99a823b
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.spirv b/vendor/gioui.org/shader/piet/zkernel4.comp.0.spirv
new file mode 100644
index 0000000..d558185
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.dxbc b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.dxbc
new file mode 100644
index 0000000..70eaf00
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibios b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibios
new file mode 100644
index 0000000..e6c2985
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiossimulator
new file mode 100644
index 0000000..edde25c
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibmacos
new file mode 100644
index 0000000..d1c5653
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.spirv b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.spirv
new file mode 100644
index 0000000..078ea61
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.dxbc b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.dxbc
new file mode 100644
index 0000000..309a77a
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.dxbc
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibios b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibios
new file mode 100644
index 0000000..0e165cf
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibios
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiossimulator
new file mode 100644
index 0000000..a95ef45
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiossimulator
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibmacos
new file mode 100644
index 0000000..be6ab47
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibmacos
Binary files differ
diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.spirv b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.spirv
new file mode 100644
index 0000000..06f9b60
--- /dev/null
+++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.spirv
Binary files differ
diff --git a/vendor/gioui.org/shader/shader.go b/vendor/gioui.org/shader/shader.go
new file mode 100644
index 0000000..e1263c1
--- /dev/null
+++ b/vendor/gioui.org/shader/shader.go
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package shader
+
+type Sources struct {
+ Name string
+ SPIRV string
+ GLSL100ES string
+ GLSL150 string
+ DXBC string
+ MetalLib string
+ Uniforms UniformsReflection
+ Inputs []InputLocation
+ Textures []TextureBinding
+ StorageBuffers []BufferBinding
+ Images []ImageBinding
+ WorkgroupSize [3]int
+}
+
+type UniformsReflection struct {
+ Locations []UniformLocation
+ Size int
+}
+
+type ImageBinding struct {
+ Name string
+ Binding int
+}
+
+type BufferBinding struct {
+ Name string
+ Binding int
+}
+
+type TextureBinding struct {
+ Name string
+ Binding int
+}
+
+type UniformLocation struct {
+ Name string
+ Type DataType
+ Size int
+ Offset int
+}
+
+type InputLocation struct {
+ // For GLSL.
+ Name string
+ Location int
+ // For HLSL.
+ Semantic string
+ SemanticIndex int
+
+ Type DataType
+ Size int
+}
+
+type DataType uint8
+
+const (
+ DataTypeFloat DataType = iota
+ DataTypeInt
+ DataTypeShort
+)
diff --git a/vendor/gioui.org/text/lru.go b/vendor/gioui.org/text/lru.go
index 85dbf9c..10448b1 100644
--- a/vendor/gioui.org/text/lru.go
+++ b/vendor/gioui.org/text/lru.go
@@ -3,7 +3,7 @@
package text
import (
- "gioui.org/op"
+ "gioui.org/op/clip"
"golang.org/x/image/math/fixed"
)
@@ -26,7 +26,7 @@ type layoutElem struct {
type path struct {
next, prev *path
key pathKey
- val op.CallOp
+ val clip.PathSpec
}
type layoutKey struct {
@@ -81,16 +81,16 @@ func (l *layoutCache) insert(lt *layoutElem) {
lt.next.prev = lt
}
-func (c *pathCache) Get(k pathKey) (op.CallOp, bool) {
+func (c *pathCache) Get(k pathKey) (clip.PathSpec, bool) {
if v, ok := c.m[k]; ok {
c.remove(v)
c.insert(v)
return v.val, true
}
- return op.CallOp{}, false
+ return clip.PathSpec{}, false
}
-func (c *pathCache) Put(k pathKey, v op.CallOp) {
+func (c *pathCache) Put(k pathKey, v clip.PathSpec) {
if c.m == nil {
c.m = make(map[pathKey]*path)
c.head = new(path)
diff --git a/vendor/gioui.org/text/shaper.go b/vendor/gioui.org/text/shaper.go
index c4ec48c..59020e0 100644
--- a/vendor/gioui.org/text/shaper.go
+++ b/vendor/gioui.org/text/shaper.go
@@ -6,21 +6,19 @@ import (
"io"
"strings"
- "gioui.org/op"
"golang.org/x/image/math/fixed"
+
+ "gioui.org/op/clip"
)
// Shaper implements layout and shaping of text.
type Shaper interface {
// Layout a text according to a set of options.
Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error)
- // Shape a line of text and return a clipping operation for its outline.
- Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp
-
- // LayoutString is like Layout, but for strings.
+ // LayoutString is Layout for strings.
LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line
- // ShapeString is like Shape for lines previously laid out by LayoutString.
- ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp
+ // Shape a line of text and return a clipping operation for its outline.
+ Shape(font Font, size fixed.Int26_6, layout Layout) clip.PathSpec
}
// A FontFace is a Font and a matching Face.
@@ -49,8 +47,7 @@ type faceCache struct {
}
func (c *Cache) lookup(font Font) *faceCache {
- var f *faceCache
- f = c.faceForStyle(font)
+ f := c.faceForStyle(font)
if f == nil {
font.Typeface = c.def
f = c.faceForStyle(font)
@@ -59,24 +56,42 @@ func (c *Cache) lookup(font Font) *faceCache {
}
func (c *Cache) faceForStyle(font Font) *faceCache {
- tf := c.faces[font]
- if tf == nil {
- font := font
- font.Weight = Normal
- tf = c.faces[font]
+ if closest, ok := c.closestFont(font); ok {
+ return c.faces[closest]
}
- if tf == nil {
- font := font
- font.Style = Regular
- tf = c.faces[font]
+ font.Style = Regular
+ if closest, ok := c.closestFont(font); ok {
+ return c.faces[closest]
}
- if tf == nil {
- font := font
- font.Style = Regular
- font.Weight = Normal
- tf = c.faces[font]
+ return nil
+}
+
+// closestFont returns the closest Font by weight, in case of equality the
+// lighter weight will be returned.
+func (c *Cache) closestFont(lookup Font) (Font, bool) {
+ if c.faces[lookup] != nil {
+ return lookup, true
}
- return tf
+ found := false
+ var match Font
+ for cf := range c.faces {
+ if cf.Typeface != lookup.Typeface || cf.Variant != lookup.Variant || cf.Style != lookup.Style {
+ continue
+ }
+ if !found {
+ found = true
+ match = cf
+ continue
+ }
+ cDist := weightDistance(lookup.Weight, cf.Weight)
+ mDist := weightDistance(lookup.Weight, match.Weight)
+ if cDist < mDist {
+ match = cf
+ } else if cDist == mDist && cf.Weight < match.Weight {
+ match = cf
+ }
+ }
+ return match, found
}
func NewCache(collection []FontFace) *Cache {
@@ -84,9 +99,6 @@ func NewCache(collection []FontFace) *Cache {
faces: make(map[Font]*faceCache),
}
for i, ff := range collection {
- if ff.Font.Weight == 0 {
- ff.Font.Weight = Normal
- }
if i == 0 {
c.def = ff.Font.Typeface
}
@@ -95,24 +107,23 @@ func NewCache(collection []FontFace) *Cache {
return c
}
-func (s *Cache) Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) {
- cache := s.lookup(font)
+// Layout implements the Shaper interface.
+func (c *Cache) Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) {
+ cache := c.lookup(font)
return cache.face.Layout(size, maxWidth, txt)
}
-func (s *Cache) Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp {
- cache := s.lookup(font)
- return cache.face.Shape(size, layout)
-}
-
-func (s *Cache) LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line {
- cache := s.lookup(font)
+// LayoutString is a caching implementation of the Shaper interface.
+func (c *Cache) LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line {
+ cache := c.lookup(font)
return cache.layout(size, maxWidth, str)
}
-func (s *Cache) ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp {
- cache := s.lookup(font)
- return cache.shape(size, str, layout)
+// Shape is a caching implementation of the Shaper interface. Shape assumes that the layout
+// argument is unchanged from a call to Layout or LayoutString.
+func (c *Cache) Shape(font Font, size fixed.Int26_6, layout Layout) clip.PathSpec {
+ cache := c.lookup(font)
+ return cache.shape(size, layout)
}
func (f *faceCache) layout(ppem fixed.Int26_6, maxWidth int, str string) []Line {
@@ -132,13 +143,13 @@ func (f *faceCache) layout(ppem fixed.Int26_6, maxWidth int, str string) []Line
return l
}
-func (f *faceCache) shape(ppem fixed.Int26_6, str string, layout []Glyph) op.CallOp {
+func (f *faceCache) shape(ppem fixed.Int26_6, layout Layout) clip.PathSpec {
if f == nil {
- return op.CallOp{}
+ return clip.PathSpec{}
}
pk := pathKey{
ppem: ppem,
- str: str,
+ str: layout.Text,
}
if clip, ok := f.pathCache.Get(pk); ok {
return clip
diff --git a/vendor/gioui.org/text/text.go b/vendor/gioui.org/text/text.go
index 3ec7d1f..12d39f5 100644
--- a/vendor/gioui.org/text/text.go
+++ b/vendor/gioui.org/text/text.go
@@ -5,15 +5,13 @@ package text
import (
"io"
- "gioui.org/op"
+ "gioui.org/op/clip"
"golang.org/x/image/math/fixed"
)
// A Line contains the measurements of a line of text.
type Line struct {
- Layout []Glyph
- // Len is the length in UTF8 bytes of the line.
- Len int
+ Layout Layout
// Width is the width of the line.
Width fixed.Int26_6
// Ascent is the height above the baseline.
@@ -25,15 +23,16 @@ type Line struct {
Bounds fixed.Rectangle26_6
}
-type Glyph struct {
- Rune rune
- Advance fixed.Int26_6
+type Layout struct {
+ Text string
+ Advances []fixed.Int26_6
}
// Style is the font style.
type Style int
-// Weight is a font weight, in CSS units.
+// Weight is a font weight, in CSS units subtracted 400 so the zero value
+// is normal text weight.
type Weight int
// Font specify a particular typeface variant, style and weight.
@@ -45,10 +44,11 @@ type Font struct {
Weight Weight
}
-// Face implements text layout and shaping for a particular font.
+// Face implements text layout and shaping for a particular font. All
+// methods must be safe for concurrent use.
type Face interface {
Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error)
- Shape(ppem fixed.Int26_6, str []Glyph) op.CallOp
+ Shape(ppem fixed.Int26_6, str Layout) clip.PathSpec
}
// Typeface identifies a particular typeface design. The empty
@@ -72,9 +72,22 @@ const (
)
const (
- Normal Weight = 400
- Medium Weight = 500
- Bold Weight = 600
+ Thin Weight = 100 - 400
+ Hairline Weight = Thin
+ ExtraLight Weight = 200 - 400
+ UltraLight Weight = ExtraLight
+ Light Weight = 300 - 400
+ Normal Weight = 400 - 400
+ Medium Weight = 500 - 400
+ SemiBold Weight = 600 - 400
+ DemiBold Weight = SemiBold
+ Bold Weight = 700 - 400
+ ExtraBold Weight = 800 - 400
+ UltraBold Weight = ExtraBold
+ Black Weight = 900 - 400
+ Heavy Weight = Black
+ ExtraBlack Weight = 950 - 400
+ UltraBlack Weight = ExtraBlack
)
func (a Alignment) String() string {
@@ -86,6 +99,56 @@ func (a Alignment) String() string {
case Middle:
return "Middle"
default:
- panic("unreachable")
+ panic("invalid Alignment")
}
}
+
+func (s Style) String() string {
+ switch s {
+ case Regular:
+ return "Regular"
+ case Italic:
+ return "Italic"
+ default:
+ panic("invalid Style")
+ }
+}
+
+func (w Weight) String() string {
+ switch w {
+ case Thin:
+ return "Thin"
+ case ExtraLight:
+ return "ExtraLight"
+ case Light:
+ return "Light"
+ case Normal:
+ return "Normal"
+ case Medium:
+ return "Medium"
+ case SemiBold:
+ return "SemiBold"
+ case Bold:
+ return "Bold"
+ case ExtraBold:
+ return "ExtraBold"
+ case Black:
+ return "Black"
+ case ExtraBlack:
+ return "ExtraBlack"
+ default:
+ panic("invalid Weight")
+ }
+}
+
+// weightDistance returns the distance value between two font weights.
+func weightDistance(wa Weight, wb Weight) int {
+ // Avoid dealing with negative Weight values.
+ a := int(wa) + 400
+ b := int(wb) + 400
+ diff := a - b
+ if diff < 0 {
+ return -diff
+ }
+ return diff
+}