chore(v2): vendor dependencies for offline/China builds

go mod vendor pins onnxruntime_go v1.12.1, Gio and the rest into v2/vendor
so go run/build work without hitting proxy.golang.org (blocked/slow in
China). Verified: CGO_ENABLED=1 go build -mod=vendor ./internal/spike and
GOOS=windows go build -mod=vendor ./internal/ui both pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ila
2026-07-23 16:35:01 +08:00
co-authored by Claude Opus 4.8
parent 97c1c4a974
commit f58728cddd
972 changed files with 597802 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
// 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 any) []byte {
v := reflect.ValueOf(s)
sz := int(v.Elem().Type().Size())
return unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz)
}
// 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 unsafe.Slice((*byte)(unsafe.Pointer(&s[0])), blen)
}
// Slice returns a byte slice view of a slice.
func Slice(s any) []byte {
v := reflect.ValueOf(s)
first := v.Index(0)
sz := int(first.Type().Size())
res := unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz*v.Cap())
return res[:sz*v.Len()]
}
+21
View File
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Unlicense OR MIT
// Package cocoainit initializes support for multithreaded
// programs in Cocoa.
package cocoainit
/*
#cgo CFLAGS: -xobjective-c -fobjc-arc
#cgo LDFLAGS: -framework Foundation
#import <Foundation/Foundation.h>
static inline void activate_cocoa_multithreading() {
[[NSThread new] start];
}
#pragma GCC visibility push(hidden)
*/
import "C"
func init() {
C.activate_cocoa_multithreading()
}
File diff suppressed because it is too large Load Diff
+57
View File
@@ -0,0 +1,57 @@
// Package debug provides general debug feature management for Gio, including
// the ability to toggle debug features using the GIODEBUG environment variable.
package debug
import (
"fmt"
"os"
"strings"
"sync"
"sync/atomic"
)
const (
debugVariable = "GIODEBUG"
textSubsystem = "text"
silentFeature = "silent"
)
// Text controls whether the text subsystem has debug logging enabled.
var Text atomic.Bool
var parseOnce sync.Once
// Parse processes the current value of GIODEBUG. If it is unset, it does nothing.
// Otherwise it process its value, printing usage info the stderr if the value is
// not understood. Parse will be automatically invoked when the first application
// window is created, allowing applications to manipulate GIODEBUG programmatically
// before it is parsed.
func Parse() {
parseOnce.Do(func() {
val, ok := os.LookupEnv(debugVariable)
if !ok {
return
}
print := false
silent := false
for part := range strings.SplitSeq(val, ",") {
switch part {
case textSubsystem:
Text.Store(true)
case silentFeature:
silent = true
default:
print = true
}
}
if print && !silent {
fmt.Fprintf(os.Stderr,
`Usage of %s:
A comma-delimited list of debug subsystems to enable. Currently recognized systems:
- %s: text debug info including system font resolution
- %s: silence this usage message even if GIODEBUG contains invalid content
`, debugVariable, textSubsystem, silentFeature)
}
})
}
+247
View File
@@ -0,0 +1,247 @@
// SPDX-License-Identifier: Unlicense OR MIT
//go:build linux || windows || freebsd || openbsd
// +build linux windows freebsd openbsd
package egl
import (
"errors"
"fmt"
"runtime"
"slices"
"strings"
"gioui.org/gpu"
)
type Context struct {
disp _EGLDisplay
eglCtx *eglContext
eglSurf _EGLSurface
}
type eglContext struct {
config _EGLConfig
ctx _EGLContext
visualID int
srgb bool
surfaceless bool
}
var (
nilEGLDisplay _EGLDisplay
nilEGLSurface _EGLSurface
nilEGLContext _EGLContext
nilEGLConfig _EGLConfig
EGL_DEFAULT_DISPLAY NativeDisplayType
)
const (
_EGL_ALPHA_SIZE = 0x3021
_EGL_BLUE_SIZE = 0x3022
_EGL_CONFIG_CAVEAT = 0x3027
_EGL_CONTEXT_CLIENT_VERSION = 0x3098
_EGL_DEPTH_SIZE = 0x3025
_EGL_GL_COLORSPACE_KHR = 0x309d
_EGL_GL_COLORSPACE_SRGB_KHR = 0x3089
_EGL_GREEN_SIZE = 0x3023
_EGL_EXTENSIONS = 0x3055
_EGL_NATIVE_VISUAL_ID = 0x302e
_EGL_NONE = 0x3038
_EGL_OPENGL_ES2_BIT = 0x4
_EGL_RED_SIZE = 0x3024
_EGL_RENDERABLE_TYPE = 0x3040
_EGL_SURFACE_TYPE = 0x3033
_EGL_WINDOW_BIT = 0x4
)
func (c *Context) Release() {
c.ReleaseSurface()
if c.eglCtx != nil {
eglDestroyContext(c.disp, c.eglCtx.ctx)
c.eglCtx = nil
}
eglTerminate(c.disp)
c.disp = nilEGLDisplay
}
func (c *Context) Present() error {
if !eglSwapBuffers(c.disp, c.eglSurf) {
return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
}
return nil
}
func NewContext(disp NativeDisplayType) (*Context, error) {
if err := loadEGL(); err != nil {
return nil, err
}
eglDisp := eglGetDisplay(disp)
// eglGetDisplay can return EGL_NO_DISPLAY yet no error
// (EGL_SUCCESS), in which case a default EGL display might be
// available.
if eglDisp == nilEGLDisplay {
eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY)
}
if eglDisp == nilEGLDisplay {
return nil, fmt.Errorf("eglGetDisplay failed: 0x%x", eglGetError())
}
eglCtx, err := createContext(eglDisp)
if err != nil {
return nil, err
}
c := &Context{
disp: eglDisp,
eglCtx: eglCtx,
}
return c, nil
}
func (c *Context) RenderTarget() (gpu.RenderTarget, error) {
return gpu.OpenGLRenderTarget{}, nil
}
func (c *Context) API() gpu.API {
return gpu.OpenGL{}
}
func (c *Context) ReleaseSurface() {
if c.eglSurf == nilEGLSurface {
return
}
// Make sure any in-flight GL commands are complete.
eglWaitClient()
c.ReleaseCurrent()
eglDestroySurface(c.disp, c.eglSurf)
c.eglSurf = nilEGLSurface
}
func (c *Context) VisualID() int {
return c.eglCtx.visualID
}
func (c *Context) CreateSurface(win NativeWindowType) error {
eglSurf, err := createSurface(c.disp, c.eglCtx, win)
c.eglSurf = eglSurf
return err
}
func (c *Context) ReleaseCurrent() {
if c.disp != nilEGLDisplay {
eglMakeCurrent(c.disp, nilEGLSurface, nilEGLSurface, nilEGLContext)
}
}
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())
}
return nil
}
func (c *Context) EnableVSync(enable bool) {
if enable {
eglSwapInterval(c.disp, 1)
} else {
eglSwapInterval(c.disp, 0)
}
}
func hasExtension(exts []string, ext string) bool {
return slices.Contains(exts, ext)
}
func createContext(disp _EGLDisplay) (*eglContext, error) {
major, minor, ret := eglInitialize(disp)
if !ret {
return nil, fmt.Errorf("eglInitialize failed: 0x%x", eglGetError())
}
// sRGB framebuffer support on EGL 1.5 or if EGL_KHR_gl_colorspace is supported.
exts := strings.Split(eglQueryString(disp, _EGL_EXTENSIONS), " ")
srgb := major > 1 || minor >= 5 || hasExtension(exts, "EGL_KHR_gl_colorspace")
attribs := []_EGLint{
_EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT,
_EGL_SURFACE_TYPE, _EGL_WINDOW_BIT,
_EGL_BLUE_SIZE, 8,
_EGL_GREEN_SIZE, 8,
_EGL_RED_SIZE, 8,
_EGL_CONFIG_CAVEAT, _EGL_NONE,
}
if srgb {
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
// 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) need alpha for sRGB to work.
attribs = append(attribs, _EGL_ALPHA_SIZE, 8)
}
}
attribs = append(attribs, _EGL_NONE)
eglCfg, ret := eglChooseConfig(disp, attribs)
if !ret {
return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError())
}
if eglCfg == nilEGLConfig {
supportsNoCfg := hasExtension(exts, "EGL_KHR_no_config_context")
if !supportsNoCfg {
return nil, errors.New("eglChooseConfig returned no configs")
}
}
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,
_EGL_NONE,
}
eglCtx := eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
if eglCtx == nilEGLContext {
// Fall back to OpenGL ES 2 and rely on extensions.
ctxAttribs := []_EGLint{
_EGL_CONTEXT_CLIENT_VERSION, 2,
_EGL_NONE,
}
eglCtx = eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
if eglCtx == nilEGLContext {
return nil, fmt.Errorf("eglCreateContext failed: 0x%x", eglGetError())
}
}
return &eglContext{
config: _EGLConfig(eglCfg),
ctx: _EGLContext(eglCtx),
visualID: int(visID),
srgb: srgb,
surfaceless: hasExtension(exts, "EGL_KHR_surfaceless_context"),
}, nil
}
func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (_EGLSurface, error) {
var surfAttribs []_EGLint
if eglCtx.srgb {
surfAttribs = append(surfAttribs, _EGL_GL_COLORSPACE_KHR, _EGL_GL_COLORSPACE_SRGB_KHR)
}
surfAttribs = append(surfAttribs, _EGL_NONE)
eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
if eglSurf == nilEGLSurface && eglCtx.srgb {
// Try again without sRGB.
eglCtx.srgb = false
surfAttribs = []_EGLint{_EGL_NONE}
eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
}
if eglSurf == nilEGLSurface {
return nilEGLSurface, fmt.Errorf("newContext: eglCreateWindowSurface failed 0x%x (sRGB=%v)", eglGetError(), eglCtx.srgb)
}
return eglSurf, nil
}
+109
View File
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: Unlicense OR MIT
//go:build linux || freebsd || openbsd
// +build linux freebsd openbsd
package egl
/*
#cgo linux,!android pkg-config: egl
#cgo freebsd openbsd android LDFLAGS: -lEGL
#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 CFLAGS: -DEGL_NO_X11
#include <EGL/egl.h>
#include <EGL/eglext.h>
*/
import "C"
type (
_EGLint = C.EGLint
_EGLDisplay = C.EGLDisplay
_EGLConfig = C.EGLConfig
_EGLContext = C.EGLContext
_EGLSurface = C.EGLSurface
NativeDisplayType = C.EGLNativeDisplayType
NativeWindowType = C.EGLNativeWindowType
)
func loadEGL() error {
return nil
}
func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) {
var cfg C.EGLConfig
var ncfg C.EGLint
if C.eglChooseConfig(disp, &attribs[0], &cfg, 1, &ncfg) != C.EGL_TRUE {
return nilEGLConfig, false
}
return _EGLConfig(cfg), true
}
func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext {
ctx := C.eglCreateContext(disp, cfg, shareCtx, &attribs[0])
return _EGLContext(ctx)
}
func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool {
return C.eglDestroySurface(disp, surf) == C.EGL_TRUE
}
func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool {
return C.eglDestroyContext(disp, ctx) == C.EGL_TRUE
}
func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) {
var val _EGLint
ret := C.eglGetConfigAttrib(disp, cfg, attr, &val)
return val, ret == C.EGL_TRUE
}
func eglGetError() _EGLint {
return C.eglGetError()
}
func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) {
var maj, min _EGLint
ret := C.eglInitialize(disp, &maj, &min)
return maj, min, ret == C.EGL_TRUE
}
func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool {
return C.eglMakeCurrent(disp, draw, read, ctx) == C.EGL_TRUE
}
func eglReleaseThread() bool {
return C.eglReleaseThread() == C.EGL_TRUE
}
func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool {
return C.eglSwapBuffers(disp, surf) == C.EGL_TRUE
}
func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool {
return C.eglSwapInterval(disp, interval) == C.EGL_TRUE
}
func eglTerminate(disp _EGLDisplay) bool {
return C.eglTerminate(disp) == C.EGL_TRUE
}
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
return C.GoString(C.eglQueryString(disp, name))
}
func eglGetDisplay(disp NativeDisplayType) _EGLDisplay {
return C.eglGetDisplay(disp)
}
func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface {
eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0])
return eglSurf
}
func eglWaitClient() bool {
return C.eglWaitClient() == C.EGL_TRUE
}
+187
View File
@@ -0,0 +1,187 @@
// SPDX-License-Identifier: Unlicense OR MIT
package egl
import (
"fmt"
"runtime"
"sync"
"unsafe"
syscall "golang.org/x/sys/windows"
)
type (
_EGLint int32
_EGLDisplay uintptr
_EGLConfig uintptr
_EGLContext uintptr
_EGLSurface uintptr
NativeDisplayType uintptr
NativeWindowType uintptr
)
var (
libEGL = syscall.DLL{}
_eglChooseConfig *syscall.Proc
_eglCreateContext *syscall.Proc
_eglCreateWindowSurface *syscall.Proc
_eglDestroyContext *syscall.Proc
_eglDestroySurface *syscall.Proc
_eglGetConfigAttrib *syscall.Proc
_eglGetDisplay *syscall.Proc
_eglGetError *syscall.Proc
_eglInitialize *syscall.Proc
_eglMakeCurrent *syscall.Proc
_eglReleaseThread *syscall.Proc
_eglSwapInterval *syscall.Proc
_eglSwapBuffers *syscall.Proc
_eglTerminate *syscall.Proc
_eglQueryString *syscall.Proc
_eglWaitClient *syscall.Proc
)
var loadOnce = sync.OnceValue(loadDLLs)
func loadEGL() error {
return loadOnce()
}
func loadDLLs() error {
if err := loadDLL(&libEGL, "libEGL.dll"); err != nil {
return err
}
procs := map[string]**syscall.Proc{
"eglChooseConfig": &_eglChooseConfig,
"eglCreateContext": &_eglCreateContext,
"eglCreateWindowSurface": &_eglCreateWindowSurface,
"eglDestroyContext": &_eglDestroyContext,
"eglDestroySurface": &_eglDestroySurface,
"eglGetConfigAttrib": &_eglGetConfigAttrib,
"eglGetDisplay": &_eglGetDisplay,
"eglGetError": &_eglGetError,
"eglInitialize": &_eglInitialize,
"eglMakeCurrent": &_eglMakeCurrent,
"eglReleaseThread": &_eglReleaseThread,
"eglSwapInterval": &_eglSwapInterval,
"eglSwapBuffers": &_eglSwapBuffers,
"eglTerminate": &_eglTerminate,
"eglQueryString": &_eglQueryString,
"eglWaitClient": &_eglWaitClient,
}
for name, proc := range procs {
p, err := libEGL.FindProc(name)
if err != nil {
return fmt.Errorf("failed to locate %s in %s: %w", name, libEGL.Name, err)
}
*proc = p
}
return nil
}
func loadDLL(dll *syscall.DLL, name string) error {
handle, err := syscall.LoadLibraryEx(name, 0, syscall.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
if err != nil {
return fmt.Errorf("egl: failed to load %s: %v", name, err)
}
dll.Handle = handle
dll.Name = name
return nil
}
func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) {
var cfg _EGLConfig
var ncfg _EGLint
a := &attribs[0]
r, _, _ := _eglChooseConfig.Call(uintptr(disp), uintptr(unsafe.Pointer(a)), uintptr(unsafe.Pointer(&cfg)), 1, uintptr(unsafe.Pointer(&ncfg)))
issue34474KeepAlive(a)
return cfg, r != 0
}
func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext {
a := &attribs[0]
c, _, _ := _eglCreateContext.Call(uintptr(disp), uintptr(cfg), uintptr(shareCtx), uintptr(unsafe.Pointer(a)))
issue34474KeepAlive(a)
return _EGLContext(c)
}
func eglCreateWindowSurface(disp _EGLDisplay, cfg _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface {
a := &attribs[0]
s, _, _ := _eglCreateWindowSurface.Call(uintptr(disp), uintptr(cfg), uintptr(win), uintptr(unsafe.Pointer(a)))
issue34474KeepAlive(a)
return _EGLSurface(s)
}
func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool {
r, _, _ := _eglDestroySurface.Call(uintptr(disp), uintptr(surf))
return r != 0
}
func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool {
r, _, _ := _eglDestroyContext.Call(uintptr(disp), uintptr(ctx))
return r != 0
}
func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) {
var val uintptr
r, _, _ := _eglGetConfigAttrib.Call(uintptr(disp), uintptr(cfg), uintptr(attr), uintptr(unsafe.Pointer(&val)))
return _EGLint(val), r != 0
}
func eglGetDisplay(disp NativeDisplayType) _EGLDisplay {
d, _, _ := _eglGetDisplay.Call(uintptr(disp))
return _EGLDisplay(d)
}
func eglGetError() _EGLint {
e, _, _ := _eglGetError.Call()
return _EGLint(e)
}
func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) {
var maj, min uintptr
r, _, _ := _eglInitialize.Call(uintptr(disp), uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)))
return _EGLint(maj), _EGLint(min), r != 0
}
func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool {
r, _, _ := _eglMakeCurrent.Call(uintptr(disp), uintptr(draw), uintptr(read), uintptr(ctx))
return r != 0
}
func eglReleaseThread() bool {
r, _, _ := _eglReleaseThread.Call()
return r != 0
}
func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool {
r, _, _ := _eglSwapInterval.Call(uintptr(disp), uintptr(interval))
return r != 0
}
func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool {
r, _, _ := _eglSwapBuffers.Call(uintptr(disp), uintptr(surf))
return r != 0
}
func eglTerminate(disp _EGLDisplay) bool {
r, _, _ := _eglTerminate.Call(uintptr(disp))
return r != 0
}
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name))
return syscall.BytePtrToString((*byte)(unsafe.Pointer(r)))
}
func eglWaitClient() bool {
r, _, _ := _eglWaitClient.Call()
return r != 0
}
// issue34474KeepAlive calls runtime.KeepAlive as a
// workaround for golang.org/issue/34474.
func issue34474KeepAlive(v any) {
runtime.KeepAlive(v)
}
+177
View File
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package f32 is an internal version of the public package f32 with
extra types for internal use.
*/
package f32
import (
"image"
"math"
"gioui.org/f32"
)
type Point = f32.Point
type Affine2D = f32.Affine2D
var NewAffine2D = f32.NewAffine2D
var AffineId = f32.AffineId
// A Rectangle contains the points (X, Y) where Min.X <= X < Max.X,
// Min.Y <= Y < Max.Y.
type Rectangle struct {
Min, Max Point
}
// String return a string representation of r.
func (r Rectangle) String() string {
return r.Min.String() + "-" + r.Max.String()
}
// Rect is a shorthand for Rectangle{Point{x0, y0}, Point{x1, y1}}.
// The returned Rectangle has x0 and y0 swapped if necessary so that
// it's correctly formed.
func Rect(x0, y0, x1, y1 float32) Rectangle {
if x0 > x1 {
x0, x1 = x1, x0
}
if y0 > y1 {
y0, y1 = y1, y0
}
return Rectangle{Point{x0, y0}, Point{x1, y1}}
}
// Pt is shorthand for Point{X: x, Y: y}.
var Pt = f32.Pt
// Size returns r's width and height.
func (r Rectangle) Size() Point {
return Point{X: r.Dx(), Y: r.Dy()}
}
// Dx returns r's width.
func (r Rectangle) Dx() float32 {
return r.Max.X - r.Min.X
}
// Dy returns r's Height.
func (r Rectangle) Dy() float32 {
return r.Max.Y - r.Min.Y
}
// Intersect returns the intersection of r and s.
func (r Rectangle) Intersect(s Rectangle) 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
}
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
}
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
}
// Canon returns the canonical version of r, where Min is to
// the upper left of Max.
func (r Rectangle) Canon() Rectangle {
if r.Max.X < r.Min.X {
r.Min.X, r.Max.X = r.Max.X, r.Min.X
}
if r.Max.Y < r.Min.Y {
r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y
}
return r
}
// Empty reports whether r represents the empty area.
func (r Rectangle) Empty() bool {
return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y
}
// Add offsets r with the vector p.
func (r Rectangle) Add(p Point) Rectangle {
return Rectangle{
Point{r.Min.X + p.X, r.Min.Y + p.Y},
Point{r.Max.X + p.X, r.Max.Y + p.Y},
}
}
// Sub offsets r with the vector -p.
func (r Rectangle) Sub(p Point) Rectangle {
return Rectangle{
Point{r.Min.X - p.X, r.Min.Y - p.Y},
Point{r.Max.X - p.X, r.Max.Y - p.Y},
}
}
// Round returns the smallest integer rectangle that
// contains r.
func (r Rectangle) Round() 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)),
},
}
}
// fRect converts a rectangle to a f32internal.Rectangle.
func FRect(r image.Rectangle) Rectangle {
return Rectangle{
Min: FPt(r.Min), Max: FPt(r.Max),
}
}
// Fpt converts an point to a f32.Point.
func FPt(p image.Point) Point {
return Point{
X: float32(p.X), Y: float32(p.Y),
}
}
func ceil(v float32) int {
return int(math.Ceil(float64(v)))
}
func floor(v float32) int {
return int(math.Floor(float64(v)))
}
+191
View File
@@ -0,0 +1,191 @@
// SPDX-License-Identifier: Unlicense OR MIT
package f32color
import (
"image/color"
"math"
)
//go:generate go run ./f32colorgen -out tables.go
// RGBA is a 32 bit floating point linear premultiplied color space.
type RGBA struct {
R, G, B, A float32
}
// Array returns rgba values in a [4]float32 array.
func (rgba RGBA) Array() [4]float32 {
return [4]float32{rgba.R, rgba.G, rgba.B, rgba.A}
}
// Float32 returns r, g, b, a values.
func (col RGBA) Float32() (r, g, b, a float32) {
return col.R, col.G, col.B, col.A
}
// SRGBA converts from linear to sRGB color space.
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
}
// LinearFromSRGB converts from col in the sRGB colorspace to RGBA.
func LinearFromSRGB(col color.NRGBA) RGBA {
af := float32(col.A) / 0xFF
return RGBA{
R: srgb8ToLinear[col.R] * af, // sRGBToLinear(float32(col.R)/0xff) * af,
G: srgb8ToLinear[col.G] * af, // sRGBToLinear(float32(col.G)/0xff) * af,
B: srgb8ToLinear[col.B] * af, // 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.
func linearTosRGB(c float32) float32 {
// Formula from EXT_sRGB.
switch {
case c <= 0:
return 0
case 0 < c && c < 0.0031308:
return 12.92 * c
case 0.0031308 <= c && c < 1:
return 1.055*float32(math.Pow(float64(c), 0.41666)) - 0.055
}
return 1
}
// sRGBToLinear transforms color value from sRGB to linear.
func sRGBToLinear(c float32) float32 {
// Formula from EXT_sRGB.
if c <= 0.04045 {
return c / 12.92
} else {
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)
d = mix(c, color.NRGBA{A: c.A, R: lum, G: lum, B: lum}, r)
d = MulAlpha(d, 128+32)
return
}
// Hovered blends dark colors towards white, and light colors towards
// black. It is approximate because it operates in non-linear sRGB space.
func Hovered(c color.NRGBA) (h color.NRGBA) {
if c.A == 0 {
// Provide a reasonable default for transparent widgets.
return color.NRGBA{A: 0x44, R: 0x88, G: 0x88, B: 0x88}
}
const ratio = 0x20
m := color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: c.A}
if approxLuminance(c) > 128 {
m = color.NRGBA{A: c.A}
}
return mix(m, c, ratio)
}
// mix mixes c1 and c2 weighted by (1 - a/256) and a/256 respectively.
func mix(c1, c2 color.NRGBA, a uint8) color.NRGBA {
ai := int(a)
return color.NRGBA{
R: byte((int(c1.R)*ai + int(c2.R)*(256-ai)) / 256),
G: byte((int(c1.G)*ai + int(c2.G)*(256-ai)) / 256),
B: byte((int(c1.B)*ai + int(c2.B)*(256-ai)) / 256),
A: byte((int(c1.A)*ai + int(c2.A)*(256-ai)) / 256),
}
}
// 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)
}
+25
View File
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Unlicense OR MIT
// Code generated by f32colorgen. DO NOT EDIT.
package f32color
// table corresponds to sRGBToLinear(float32(index)/0xff)
var srgb8ToLinear = [...]float32{
0, 0.000303527, 0.000607054, 0.000910581, 0.001214108, 0.001517635, 0.001821162, 0.0021246888, 0.002428216, 0.002731743, 0.00303527, 0.0033465363, 0.0036765079, 0.004024718, 0.004391443, 0.004776954,
0.005181518, 0.0056053926, 0.006048834, 0.0065120924, 0.0069954116, 0.007499033, 0.008023194, 0.008568126, 0.009134059, 0.00972122, 0.010329825, 0.010960096, 0.011612247, 0.012286489, 0.0129830325, 0.013702083,
0.014443846, 0.015208517, 0.015996296, 0.016807377, 0.017641956, 0.01850022, 0.019382365, 0.020288566, 0.021219013, 0.022173887, 0.023153368, 0.024157634, 0.025186861, 0.026241226, 0.027320895, 0.028426042,
0.029556837, 0.030713446, 0.031896036, 0.033104766, 0.03433981, 0.035601318, 0.03688945, 0.03820437, 0.039546244, 0.040915202, 0.042311415, 0.043735035, 0.04518621, 0.04666509, 0.048171826, 0.049706567,
0.05126947, 0.05286066, 0.05448029, 0.056128502, 0.05780544, 0.059511248, 0.06124608, 0.06301004, 0.06480329, 0.06662596, 0.06847819, 0.07036012, 0.07227187, 0.07421359, 0.0761854, 0.078187436,
0.080219835, 0.08228272, 0.08437622, 0.08650047, 0.08865561, 0.09084174, 0.09305899, 0.09530749, 0.09758737, 0.09989875, 0.102241755, 0.10461651, 0.10702312, 0.10946173, 0.11193245, 0.11443539,
0.11697068, 0.11953844, 0.122138806, 0.12477185, 0.12743771, 0.1301365, 0.13286835, 0.13563335, 0.13843164, 0.14126332, 0.14412849, 0.14702728, 0.1499598, 0.15292618, 0.15592648, 0.15896088,
0.16202942, 0.16513222, 0.16826941, 0.17144111, 0.1746474, 0.17788842, 0.18116425, 0.18447499, 0.18782078, 0.19120169, 0.19461782, 0.19806932, 0.20155625, 0.20507872, 0.20863685, 0.21223074,
0.21586055, 0.21952623, 0.223228, 0.2269659, 0.23074009, 0.23455067, 0.23839766, 0.24228121, 0.24620141, 0.25015837, 0.25415218, 0.25818294, 0.26225075, 0.2663557, 0.27049786, 0.2746774,
0.27889434, 0.28314883, 0.2874409, 0.29177073, 0.29613835, 0.30054384, 0.30498737, 0.30946898, 0.31398878, 0.31854683, 0.32314327, 0.32777816, 0.33245158, 0.33716366, 0.34191447, 0.3467041,
0.35153273, 0.35640025, 0.3613069, 0.36625272, 0.37123778, 0.37626222, 0.3813261, 0.38642955, 0.39157256, 0.39675534, 0.40197787, 0.4072403, 0.4125427, 0.41788515, 0.42326775, 0.42869058,
0.4341537, 0.43965724, 0.44520128, 0.45078585, 0.4564111, 0.46207705, 0.46778387, 0.47353154, 0.47932023, 0.48515, 0.4910209, 0.49693304, 0.5028866, 0.50888145, 0.5149178, 0.5209957,
0.5271153, 0.53327656, 0.5394796, 0.5457246, 0.55201155, 0.5583405, 0.56471163, 0.5711249, 0.5775806, 0.58407855, 0.59061897, 0.5972019, 0.6038274, 0.6104957, 0.61720663, 0.6239605,
0.6307572, 0.63759696, 0.64447975, 0.6514057, 0.6583749, 0.66538733, 0.6724432, 0.67954254, 0.6866855, 0.6938719, 0.7011021, 0.70837593, 0.71569365, 0.7230553, 0.7304609, 0.73791057,
0.74540436, 0.7529423, 0.76052463, 0.7681513, 0.77582234, 0.7835379, 0.79129803, 0.79910284, 0.80695236, 0.8148467, 0.82278585, 0.83076996, 0.8387991, 0.84687334, 0.8549927, 0.8631573,
0.8713672, 0.87962234, 0.8879232, 0.89626944, 0.90466136, 0.9130987, 0.92158204, 0.9301109, 0.9386859, 0.9473066, 0.9559735, 0.9646863, 0.9734455, 0.9822506, 0.9911022, 1,
}
+95
View File
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: Unlicense OR MIT
package fling
import (
"math"
"runtime"
"time"
"gioui.org/unit"
)
type Animation struct {
// Current offset in pixels.
x float32
// Initial time.
t0 time.Time
// Initial velocity in pixels pr second.
v0 float32
}
const (
// dp/second.
minFlingVelocity = unit.Dp(50)
maxFlingVelocity = unit.Dp(8000)
thresholdVelocity = 1
)
// Start a fling given a starting velocity. Returns whether a
// fling was started.
func (f *Animation) Start(c unit.Metric, now time.Time, velocity float32) bool {
min := float32(c.Dp(minFlingVelocity))
v := velocity
if -min <= v && v <= min {
return false
}
max := float32(c.Dp(maxFlingVelocity))
if v > max {
v = max
} else if v < -max {
v = -max
}
f.init(now, v)
return true
}
func (f *Animation) init(now time.Time, v0 float32) {
f.t0 = now
f.v0 = v0
f.x = 0
}
func (f *Animation) Active() bool {
return f.v0 != 0
}
// Tick computes and returns a fling distance since
// the last time Tick was called.
func (f *Animation) Tick(now time.Time) int {
if !f.Active() {
return 0
}
var k float32
if runtime.GOOS == "darwin" {
k = -2 // iOS
} else {
k = -4.2 // Android and default
}
t := now.Sub(f.t0)
// The acceleration x''(t) of a point mass with a drag
// force, f, proportional with velocity, x'(t), is
// governed by the equation
//
// x''(t) = kx'(t)
//
// Given the starting position x(0) = 0, the starting
// velocity x'(0) = v0, the position is then
// given by
//
// x(t) = v0*e^(k*t)/k - v0/k
//
ekt := float32(math.Exp(float64(k) * t.Seconds()))
x := f.v0*ekt/k - f.v0/k
dist := x - f.x
idist := int(dist)
f.x += float32(idist)
// Solving for the velocity x'(t) gives us
//
// x'(t) = v0*e^(k*t)
v := f.v0 * ekt
if -thresholdVelocity < v && v < thresholdVelocity {
f.v0 = 0
}
return idist
}
+332
View File
@@ -0,0 +1,332 @@
// SPDX-License-Identifier: Unlicense OR MIT
package fling
import (
"math"
"strconv"
"strings"
"time"
)
// Extrapolation computes a 1-dimensional velocity estimate
// for a set of timestamped points using the least squares
// fit of a 2nd order polynomial. The same method is used
// by Android.
type Extrapolation struct {
// Index into points.
idx int
// Circular buffer of samples.
samples []sample
lastValue float32
// Pre-allocated cache for samples.
cache [historySize]sample
// Filtered values and times
values [historySize]float32
times [historySize]float32
}
type sample struct {
t time.Duration
v float32
}
type matrix struct {
rows, cols int
data []float32
}
type Estimate struct {
Velocity float32
Distance float32
}
type coefficients [degree + 1]float32
const (
degree = 2
historySize = 20
maxAge = 100 * time.Millisecond
maxSampleGap = 40 * time.Millisecond
)
// SampleDelta adds a relative sample to the estimation.
func (e *Extrapolation) SampleDelta(t time.Duration, delta float32) {
val := delta + e.lastValue
e.Sample(t, val)
}
// Sample adds an absolute sample to the estimation.
func (e *Extrapolation) Sample(t time.Duration, val float32) {
e.lastValue = val
if e.samples == nil {
e.samples = e.cache[:0]
}
s := sample{
t: t,
v: val,
}
if e.idx == len(e.samples) && e.idx < cap(e.samples) {
e.samples = append(e.samples, s)
} else {
e.samples[e.idx] = s
}
e.idx++
if e.idx == cap(e.samples) {
e.idx = 0
}
}
// Velocity returns an estimate of the implied velocity and
// distance for the points sampled, or zero if the estimation method
// failed.
func (e *Extrapolation) Estimate() Estimate {
if len(e.samples) == 0 {
return Estimate{}
}
values := e.values[:0]
times := e.times[:0]
first := e.get(0)
t := first.t
// Walk backwards collecting samples.
for i := range e.samples {
p := e.get(-i)
age := first.t - p.t
if age >= maxAge || t-p.t >= maxSampleGap {
// If the samples are too old or
// too much time passed between samples
// assume they're not part of the fling.
break
}
t = p.t
values = append(values, first.v-p.v)
times = append(times, float32((-age).Seconds()))
}
coef, ok := polyFit(times, values)
if !ok {
return Estimate{}
}
dist := values[len(values)-1] - values[0]
return Estimate{
Velocity: coef[1],
Distance: dist,
}
}
func (e *Extrapolation) get(i int) sample {
idx := (e.idx + i - 1 + len(e.samples)) % len(e.samples)
return e.samples[idx]
}
// fit computes the least squares polynomial fit for
// the set of points in X, Y. If the fitting fails
// because of contradicting or insufficient data,
// fit returns false.
func polyFit(X, Y []float32) (coefficients, bool) {
if len(X) != len(Y) {
panic("X and Y lengths differ")
}
if len(X) <= degree {
// Not enough points to fit a curve.
return coefficients{}, false
}
// Use a method similar to Android's VelocityTracker.cpp:
// https://android.googlesource.com/platform/frameworks/base/+/56a2301/libs/androidfw/VelocityTracker.cpp
// where all weights are 1.
// First, expand the X vector to the matrix A in column-major order.
A := newMatrix(degree+1, len(X))
for i, x := range X {
A.set(0, i, 1)
for j := 1; j < A.rows; j++ {
A.set(j, i, A.get(j-1, i)*x)
}
}
Q, Rt, ok := decomposeQR(A)
if !ok {
return coefficients{}, false
}
// Solve R*B = Qt*Y for B, which is then the polynomial coefficients.
// Since R is upper triangular, we can proceed from bottom right to
// upper left.
// https://en.wikipedia.org/wiki/Non-linear_least_squares
var B coefficients
for i := Q.rows - 1; i >= 0; i-- {
B[i] = dot(Q.col(i), Y)
for j := Q.rows - 1; j > i; j-- {
B[i] -= Rt.get(i, j) * B[j]
}
B[i] /= Rt.get(i, i)
}
return B, true
}
// decomposeQR computes and returns Q, Rt where Q*transpose(Rt) = A, if
// possible. R is guaranteed to be upper triangular and only the square
// part of Rt is returned.
func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
// Gram-Schmidt QR decompose A where Q*R = A.
// https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process
Q := newMatrix(A.rows, A.cols) // Column-major.
Rt := newMatrix(A.rows, A.rows) // R transposed, row-major.
for i := range Q.rows {
// Copy A column.
for j := range Q.cols {
Q.set(i, j, A.get(i, j))
}
// Subtract projections. Note that int the projection
//
// proju a = <u, a>/<u, u> u
//
// the normalized column e replaces u, where <e, e> = 1:
//
// proje a = <e, a>/<e, e> e = <e, a> e
for j := range i {
d := dot(Q.col(j), Q.col(i))
for k := range Q.cols {
Q.set(i, k, Q.get(i, k)-d*Q.get(j, k))
}
}
// Normalize Q columns.
n := norm(Q.col(i))
if n < 0.000001 {
// Degenerate data, no solution.
return nil, nil, false
}
invNorm := 1 / n
for j := range Q.cols {
Q.set(i, j, Q.get(i, j)*invNorm)
}
// Update Rt.
for j := i; j < Rt.cols; j++ {
Rt.set(i, j, dot(Q.col(i), A.col(j)))
}
}
return Q, Rt, true
}
func norm(V []float32) float32 {
var n float32
for _, v := range V {
n += v * v
}
return float32(math.Sqrt(float64(n)))
}
func dot(V1, V2 []float32) float32 {
var d float32
for i, v1 := range V1 {
d += v1 * V2[i]
}
return d
}
func newMatrix(rows, cols int) *matrix {
return &matrix{
rows: rows,
cols: cols,
data: make([]float32, rows*cols),
}
}
func (m *matrix) set(row, col int, v float32) {
if row < 0 || row >= m.rows {
panic("row out of range")
}
if col < 0 || col >= m.cols {
panic("col out of range")
}
m.data[row*m.cols+col] = v
}
func (m *matrix) get(row, col int) float32 {
if row < 0 || row >= m.rows {
panic("row out of range")
}
if col < 0 || col >= m.cols {
panic("col out of range")
}
return m.data[row*m.cols+col]
}
func (m *matrix) col(c int) []float32 {
return m.data[c*m.cols : (c+1)*m.cols]
}
func (m *matrix) approxEqual(m2 *matrix) bool {
if m.rows != m2.rows || m.cols != m2.cols {
return false
}
const epsilon = 0.00001
for row := range m.rows {
for col := range m.cols {
d := m2.get(row, col) - m.get(row, col)
if d < -epsilon || d > epsilon {
return false
}
}
}
return true
}
func (m *matrix) transpose() *matrix {
t := &matrix{
rows: m.cols,
cols: m.rows,
data: make([]float32, len(m.data)),
}
for i := range m.rows {
for j := range m.cols {
t.set(j, i, m.get(i, j))
}
}
return t
}
func (m *matrix) mul(m2 *matrix) *matrix {
if m.rows != m2.cols {
panic("mismatched matrices")
}
mm := &matrix{
rows: m.rows,
cols: m2.cols,
data: make([]float32, m.rows*m2.cols),
}
for i := range mm.rows {
for j := range mm.cols {
var v float32
for k := range m.rows {
v += m.get(k, j) * m2.get(i, k)
}
mm.set(i, j, v)
}
}
return mm
}
func (m *matrix) String() string {
var b strings.Builder
for i := range m.rows {
for j := range m.cols {
v := m.get(i, j)
b.WriteString(strconv.FormatFloat(float64(v), 'g', -1, 32))
b.WriteString(", ")
}
b.WriteString("\n")
}
return b.String()
}
func (c coefficients) approxEqual(c2 coefficients) bool {
const epsilon = 0.00001
for i, v := range c {
d := v - c2[i]
if d < -epsilon || d > epsilon {
return false
}
}
return true
}
+131
View File
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gl
type (
Attrib uint
Enum uint
)
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
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
FRAGMENT_SHADER = 0x8b30
FRAMEBUFFER = 0x8d40
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
INVALID_INDEX = ^uint(0)
GREATER = 0x204
GEQUAL = 0x206
LINEAR = 0x2601
LINEAR_MIPMAP_LINEAR = 0x2703
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
RENDERBUFFER_BINDING = 0x8ca7
RENDERBUFFER_HEIGHT = 0x8d43
RENDERBUFFER_WIDTH = 0x8d42
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
TEXTURE_WRAP_T = 0x2803
TEXTURE0 = 0x84c0
TEXTURE1 = 0x84c1
TRIANGLE_STRIP = 0x5
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
)
+748
View File
@@ -0,0 +1,748 @@
// 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
_getExtension js.Value
_activeTexture js.Value
_attachShader js.Value
_beginQuery js.Value
_beginQueryEXT js.Value
_bindAttribLocation js.Value
_bindBuffer js.Value
_bindBufferBase js.Value
_bindFramebuffer js.Value
_bindRenderbuffer js.Value
_bindTexture js.Value
_blendEquation js.Value
_blendFunc js.Value
_bufferData js.Value
_bufferSubData js.Value
_checkFramebufferStatus js.Value
_clear js.Value
_clearColor js.Value
_clearDepth js.Value
_compileShader js.Value
_copyTexSubImage2D js.Value
_createBuffer js.Value
_createFramebuffer js.Value
_createProgram js.Value
_createQuery js.Value
_createRenderbuffer js.Value
_createShader js.Value
_createTexture js.Value
_deleteBuffer js.Value
_deleteFramebuffer js.Value
_deleteProgram js.Value
_deleteQuery js.Value
_deleteQueryEXT js.Value
_deleteShader js.Value
_deleteRenderbuffer js.Value
_deleteTexture js.Value
_depthFunc js.Value
_depthMask js.Value
_disableVertexAttribArray js.Value
_disable js.Value
_drawArrays js.Value
_drawElements js.Value
_enable js.Value
_enableVertexAttribArray js.Value
_endQuery js.Value
_endQueryEXT js.Value
_finish js.Value
_flush js.Value
_framebufferRenderbuffer js.Value
_framebufferTexture2D js.Value
_generateMipmap js.Value
_getRenderbufferParameteri js.Value
_getFramebufferAttachmentParameter js.Value
_getParameter js.Value
_getIndexedParameter js.Value
_getProgramParameter js.Value
_getProgramInfoLog js.Value
_getQueryParameter js.Value
_getQueryObjectEXT js.Value
_getShaderParameter js.Value
_getShaderInfoLog js.Value
_getSupportedExtensions js.Value
_getUniformBlockIndex js.Value
_getUniformLocation js.Value
_getVertexAttrib js.Value
_getVertexAttribOffset js.Value
_invalidateFramebuffer js.Value
_isEnabled js.Value
_linkProgram js.Value
_pixelStorei js.Value
_renderbufferStorage js.Value
_readPixels js.Value
_scissor js.Value
_shaderSource js.Value
_texImage2D js.Value
_texStorage2D js.Value
_texSubImage2D js.Value
_texParameteri js.Value
_uniformBlockBinding js.Value
_uniform1f js.Value
_uniform1i js.Value
_uniform2f js.Value
_uniform3f js.Value
_uniform4f js.Value
_useProgram js.Value
_vertexAttribPointer js.Value
_viewport js.Value
}
type Context js.Value
func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
webgl := js.Value(ctx)
f := &Functions{
Ctx: webgl,
uint8Array: js.Global().Get("Uint8Array"),
_getExtension: _bind(webgl, `getExtension`),
_activeTexture: _bind(webgl, `activeTexture`),
_attachShader: _bind(webgl, `attachShader`),
_beginQuery: _bind(webgl, `beginQuery`),
_beginQueryEXT: _bind(webgl, `beginQueryEXT`),
_bindAttribLocation: _bind(webgl, `bindAttribLocation`),
_bindBuffer: _bind(webgl, `bindBuffer`),
_bindBufferBase: _bind(webgl, `bindBufferBase`),
_bindFramebuffer: _bind(webgl, `bindFramebuffer`),
_bindRenderbuffer: _bind(webgl, `bindRenderbuffer`),
_bindTexture: _bind(webgl, `bindTexture`),
_blendEquation: _bind(webgl, `blendEquation`),
_blendFunc: _bind(webgl, `blendFunc`),
_bufferData: _bind(webgl, `bufferData`),
_bufferSubData: _bind(webgl, `bufferSubData`),
_checkFramebufferStatus: _bind(webgl, `checkFramebufferStatus`),
_clear: _bind(webgl, `clear`),
_clearColor: _bind(webgl, `clearColor`),
_clearDepth: _bind(webgl, `clearDepth`),
_compileShader: _bind(webgl, `compileShader`),
_copyTexSubImage2D: _bind(webgl, `copyTexSubImage2D`),
_createBuffer: _bind(webgl, `createBuffer`),
_createFramebuffer: _bind(webgl, `createFramebuffer`),
_createProgram: _bind(webgl, `createProgram`),
_createQuery: _bind(webgl, `createQuery`),
_createRenderbuffer: _bind(webgl, `createRenderbuffer`),
_createShader: _bind(webgl, `createShader`),
_createTexture: _bind(webgl, `createTexture`),
_deleteBuffer: _bind(webgl, `deleteBuffer`),
_deleteFramebuffer: _bind(webgl, `deleteFramebuffer`),
_deleteProgram: _bind(webgl, `deleteProgram`),
_deleteQuery: _bind(webgl, `deleteQuery`),
_deleteQueryEXT: _bind(webgl, `deleteQueryEXT`),
_deleteShader: _bind(webgl, `deleteShader`),
_deleteRenderbuffer: _bind(webgl, `deleteRenderbuffer`),
_deleteTexture: _bind(webgl, `deleteTexture`),
_depthFunc: _bind(webgl, `depthFunc`),
_depthMask: _bind(webgl, `depthMask`),
_disableVertexAttribArray: _bind(webgl, `disableVertexAttribArray`),
_disable: _bind(webgl, `disable`),
_drawArrays: _bind(webgl, `drawArrays`),
_drawElements: _bind(webgl, `drawElements`),
_enable: _bind(webgl, `enable`),
_enableVertexAttribArray: _bind(webgl, `enableVertexAttribArray`),
_endQuery: _bind(webgl, `endQuery`),
_endQueryEXT: _bind(webgl, `endQueryEXT`),
_finish: _bind(webgl, `finish`),
_flush: _bind(webgl, `flush`),
_framebufferRenderbuffer: _bind(webgl, `framebufferRenderbuffer`),
_framebufferTexture2D: _bind(webgl, `framebufferTexture2D`),
_generateMipmap: _bind(webgl, `generateMipmap`),
_getRenderbufferParameteri: _bind(webgl, `getRenderbufferParameteri`),
_getFramebufferAttachmentParameter: _bind(webgl, `getFramebufferAttachmentParameter`),
_getParameter: _bind(webgl, `getParameter`),
_getIndexedParameter: _bind(webgl, `getIndexedParameter`),
_getProgramParameter: _bind(webgl, `getProgramParameter`),
_getProgramInfoLog: _bind(webgl, `getProgramInfoLog`),
_getQueryParameter: _bind(webgl, `getQueryParameter`),
_getQueryObjectEXT: _bind(webgl, `getQueryObjectEXT`),
_getShaderParameter: _bind(webgl, `getShaderParameter`),
_getShaderInfoLog: _bind(webgl, `getShaderInfoLog`),
_getSupportedExtensions: _bind(webgl, `getSupportedExtensions`),
_getUniformBlockIndex: _bind(webgl, `getUniformBlockIndex`),
_getUniformLocation: _bind(webgl, `getUniformLocation`),
_getVertexAttrib: _bind(webgl, `getVertexAttrib`),
_getVertexAttribOffset: _bind(webgl, `getVertexAttribOffset`),
_invalidateFramebuffer: _bind(webgl, `invalidateFramebuffer`),
_isEnabled: _bind(webgl, `isEnabled`),
_linkProgram: _bind(webgl, `linkProgram`),
_pixelStorei: _bind(webgl, `pixelStorei`),
_renderbufferStorage: _bind(webgl, `renderbufferStorage`),
_readPixels: _bind(webgl, `readPixels`),
_scissor: _bind(webgl, `scissor`),
_shaderSource: _bind(webgl, `shaderSource`),
_texImage2D: _bind(webgl, `texImage2D`),
_texStorage2D: _bind(webgl, `texStorage2D`),
_texSubImage2D: _bind(webgl, `texSubImage2D`),
_texParameteri: _bind(webgl, `texParameteri`),
_uniformBlockBinding: _bind(webgl, `uniformBlockBinding`),
_uniform1f: _bind(webgl, `uniform1f`),
_uniform1i: _bind(webgl, `uniform1i`),
_uniform2f: _bind(webgl, `uniform2f`),
_uniform3f: _bind(webgl, `uniform3f`),
_uniform4f: _bind(webgl, `uniform4f`),
_useProgram: _bind(webgl, `useProgram`),
_vertexAttribPointer: _bind(webgl, `vertexAttribPointer`),
_viewport: _bind(webgl, `viewport`),
}
if err := f.Init(); err != nil {
return nil, err
}
return f, nil
}
func _bind(ctx js.Value, p string) js.Value {
if o := ctx.Get(p); o.Truthy() {
return o.Call("bind", ctx)
}
return js.Undefined()
}
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._getExtension.Invoke(name)
}
func (f *Functions) ActiveTexture(t Enum) {
f._activeTexture.Invoke(int(t))
}
func (f *Functions) AttachShader(p Program, s Shader) {
f._attachShader.Invoke(js.Value(p), js.Value(s))
}
func (f *Functions) BeginQuery(target Enum, query Query) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._beginQuery.Invoke(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._bindAttribLocation.Invoke(js.Value(p), int(a), name)
}
func (f *Functions) BindBuffer(target Enum, b Buffer) {
f._bindBuffer.Invoke(int(target), js.Value(b))
}
func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) {
f._bindBufferBase.Invoke(int(target), index, js.Value(b))
}
func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
f._bindFramebuffer.Invoke(int(target), js.Value(fb))
}
func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
f._bindRenderbuffer.Invoke(int(target), js.Value(rb))
}
func (f *Functions) BindTexture(target Enum, t Texture) {
f._bindTexture.Invoke(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._blendEquation.Invoke(int(mode))
}
func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
f._blendFunc.Invoke(int(srcRGB), int(dstRGB), int(srcA), int(dstA))
}
func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
if data == nil {
f._bufferData.Invoke(int(target), size, int(usage))
} else {
if len(data) != size {
panic("size mismatch")
}
f._bufferData.Invoke(int(target), f.byteArrayOf(data), int(usage))
}
}
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src))
}
func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
status := Enum(f._checkFramebufferStatus.Invoke(int(target)).Int())
if status != FRAMEBUFFER_COMPLETE && f.Ctx.Call("isContextLost").Bool() {
// If the context is lost, we say that everything is fine. That saves internal/opengl/opengl.go from panic.
return FRAMEBUFFER_COMPLETE
}
return status
}
func (f *Functions) Clear(mask Enum) {
f._clear.Invoke(int(mask))
}
func (f *Functions) ClearColor(red, green, blue, alpha float32) {
f._clearColor.Invoke(red, green, blue, alpha)
}
func (f *Functions) ClearDepthf(d float32) {
f._clearDepth.Invoke(d)
}
func (f *Functions) CompileShader(s Shader) {
f._compileShader.Invoke(js.Value(s))
}
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
f._copyTexSubImage2D.Invoke(int(target), level, xoffset, yoffset, x, y, width, height)
}
func (f *Functions) CreateBuffer() Buffer {
return Buffer(f._createBuffer.Invoke())
}
func (f *Functions) CreateFramebuffer() Framebuffer {
return Framebuffer(f._createFramebuffer.Invoke())
}
func (f *Functions) CreateProgram() Program {
return Program(f._createProgram.Invoke())
}
func (f *Functions) CreateQuery() Query {
return Query(f._createQuery.Invoke())
}
func (f *Functions) CreateRenderbuffer() Renderbuffer {
return Renderbuffer(f._createRenderbuffer.Invoke())
}
func (f *Functions) CreateShader(ty Enum) Shader {
return Shader(f._createShader.Invoke(int(ty)))
}
func (f *Functions) CreateTexture() Texture {
return Texture(f._createTexture.Invoke())
}
func (f *Functions) CreateVertexArray() VertexArray {
panic("not supported")
}
func (f *Functions) DeleteBuffer(v Buffer) {
f._deleteBuffer.Invoke(js.Value(v))
}
func (f *Functions) DeleteFramebuffer(v Framebuffer) {
f._deleteFramebuffer.Invoke(js.Value(v))
}
func (f *Functions) DeleteProgram(p Program) {
f._deleteProgram.Invoke(js.Value(p))
}
func (f *Functions) DeleteQuery(query Query) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._deleteQuery.Invoke(js.Value(query))
} else {
f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query))
}
}
func (f *Functions) DeleteShader(s Shader) {
f._deleteShader.Invoke(js.Value(s))
}
func (f *Functions) DeleteRenderbuffer(v Renderbuffer) {
f._deleteRenderbuffer.Invoke(js.Value(v))
}
func (f *Functions) DeleteTexture(v Texture) {
f._deleteTexture.Invoke(js.Value(v))
}
func (f *Functions) DeleteVertexArray(a VertexArray) {
panic("not implemented")
}
func (f *Functions) DepthFunc(fn Enum) {
f._depthFunc.Invoke(int(fn))
}
func (f *Functions) DepthMask(mask bool) {
f._depthMask.Invoke(mask)
}
func (f *Functions) DisableVertexAttribArray(a Attrib) {
f._disableVertexAttribArray.Invoke(int(a))
}
func (f *Functions) Disable(cap Enum) {
f._disable.Invoke(int(cap))
}
func (f *Functions) DrawArrays(mode Enum, first, count int) {
f._drawArrays.Invoke(int(mode), first, count)
}
func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
f._drawElements.Invoke(int(mode), count, int(ty), offset)
}
func (f *Functions) DispatchCompute(x, y, z int) {
panic("not implemented")
}
func (f *Functions) Enable(cap Enum) {
f._enable.Invoke(int(cap))
}
func (f *Functions) EnableVertexAttribArray(a Attrib) {
f._enableVertexAttribArray.Invoke(int(a))
}
func (f *Functions) EndQuery(target Enum) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._endQuery.Invoke(int(target))
} else {
f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target))
}
}
func (f *Functions) Finish() {
f._finish.Invoke()
}
func (f *Functions) Flush() {
f._flush.Invoke()
}
func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
f._framebufferRenderbuffer.Invoke(int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer))
}
func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
f._framebufferTexture2D.Invoke(int(target), int(attachment), int(texTarget), js.Value(t), level)
}
func (f *Functions) GenerateMipmap(target Enum) {
f._generateMipmap.Invoke(int(target))
}
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._getRenderbufferParameteri.Invoke(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._getFramebufferAttachmentParameter.Invoke(int(target), int(attachment), int(pname)))
}
func (f *Functions) GetBinding(pname Enum) Object {
obj := f._getParameter.Invoke(int(pname))
if !obj.Truthy() {
return Object{}
}
return Object(obj)
}
func (f *Functions) GetBindingi(pname Enum, idx int) Object {
obj := f._getIndexedParameter.Invoke(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._getParameter.Invoke(int(pname)))
}
func (f *Functions) GetFloat(pname Enum) float32 {
return float32(f._getParameter.Invoke(int(pname)).Float())
}
func (f *Functions) GetInteger4(pname Enum) [4]int {
arr := f._getParameter.Invoke(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._getParameter.Invoke(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._getProgramParameter.Invoke(js.Value(p), int(pname)))
}
func (f *Functions) GetProgramInfoLog(p Program) string {
return f._getProgramInfoLog.Invoke(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._getQueryParameter.Invoke(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._getShaderParameter.Invoke(js.Value(s), int(pname)))
}
func (f *Functions) GetShaderInfoLog(s Shader) string {
return f._getShaderInfoLog.Invoke(js.Value(s)).String()
}
func (f *Functions) GetString(pname Enum) string {
switch pname {
case EXTENSIONS:
extsjs := f._getSupportedExtensions.Invoke()
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._getParameter.Invoke(int(pname)).String()
}
}
func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
return uint(paramVal(f._getUniformBlockIndex.Invoke(js.Value(p), name)))
}
func (f *Functions) GetUniformLocation(p Program, name string) Uniform {
return Uniform(f._getUniformLocation.Invoke(js.Value(p), name))
}
func (f *Functions) GetVertexAttrib(index int, pname Enum) int {
return paramVal(f._getVertexAttrib.Invoke(index, int(pname)))
}
func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
obj := f._getVertexAttrib.Invoke(index, int(pname))
if !obj.Truthy() {
return Object{}
}
return Object(obj)
}
func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
return uintptr(f._getVertexAttribOffset.Invoke(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._invalidateFramebuffer.Invoke(int(target), f.int32Buf)
}
}
func (f *Functions) IsEnabled(cap Enum) bool {
return f._isEnabled.Invoke(int(cap)).Truthy()
}
func (f *Functions) LinkProgram(p Program) {
f._linkProgram.Invoke(js.Value(p))
}
func (f *Functions) PixelStorei(pname Enum, param int) {
f._pixelStorei.Invoke(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._renderbufferStorage.Invoke(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._readPixels.Invoke(x, y, width, height, int(format), int(ty), ba)
js.CopyBytesToGo(data, ba)
}
func (f *Functions) Scissor(x, y, width, height int32) {
f._scissor.Invoke(x, y, width, height)
}
func (f *Functions) ShaderSource(s Shader, src string) {
f._shaderSource.Invoke(js.Value(s), src)
}
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) {
f._texImage2D.Invoke(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._texStorage2D.Invoke(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._texSubImage2D.Invoke(int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data))
}
func (f *Functions) TexParameteri(target, pname Enum, param int) {
f._texParameteri.Invoke(int(target), int(pname), int(param))
}
func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
f._uniformBlockBinding.Invoke(js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding))
}
func (f *Functions) Uniform1f(dst Uniform, v float32) {
f._uniform1f.Invoke(js.Value(dst), v)
}
func (f *Functions) Uniform1i(dst Uniform, v int) {
f._uniform1i.Invoke(js.Value(dst), v)
}
func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
f._uniform2f.Invoke(js.Value(dst), v0, v1)
}
func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
f._uniform3f.Invoke(js.Value(dst), v0, v1, v2)
}
func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
f._uniform4f.Invoke(js.Value(dst), v0, v1, v2, v3)
}
func (f *Functions) UseProgram(p Program) {
f._useProgram.Invoke(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._vertexAttribPointer.Invoke(int(dst), size, int(ty), normalized, stride, offset)
}
func (f *Functions) Viewport(x, y, width, height int) {
f._viewport.Invoke(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()
case js.TypeUndefined:
return 0
case js.TypeNull:
return 0
default:
panic("unknown parameter type")
}
}
+1323
View File
File diff suppressed because it is too large Load Diff
+721
View File
@@ -0,0 +1,721 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gl
import (
"fmt"
"math"
"runtime"
"sync"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
func loadGLESv2Procs() error {
dllName := "libGLESv2.dll"
handle, err := windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
if err != nil {
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
}
gles := windows.DLL{Handle: handle, Name: dllName}
// d3dcompiler_47.dll is needed internally for shader compilation to function.
dllName = "d3dcompiler_47.dll"
_, err = windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
if err != nil {
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
}
procs := map[string]**windows.Proc{
"glActiveTexture": &_glActiveTexture,
"glAttachShader": &_glAttachShader,
"glBeginQuery": &_glBeginQuery,
"glBindAttribLocation": &_glBindAttribLocation,
"glBindBuffer": &_glBindBuffer,
"glBindBufferBase": &_glBindBufferBase,
"glBindFramebuffer": &_glBindFramebuffer,
"glBindRenderbuffer": &_glBindRenderbuffer,
"glBindTexture": &_glBindTexture,
"glBindVertexArray": &_glBindVertexArray,
"glBlendEquation": &_glBlendEquation,
"glBlendFuncSeparate": &_glBlendFuncSeparate,
"glBufferData": &_glBufferData,
"glBufferSubData": &_glBufferSubData,
"glCheckFramebufferStatus": &_glCheckFramebufferStatus,
"glClear": &_glClear,
"glClearColor": &_glClearColor,
"glClearDepthf": &_glClearDepthf,
"glDeleteQueries": &_glDeleteQueries,
"glDeleteVertexArrays": &_glDeleteVertexArrays,
"glCompileShader": &_glCompileShader,
"glCopyTexSubImage2D": &_glCopyTexSubImage2D,
"glGenerateMipmap": &_glGenerateMipmap,
"glGenBuffers": &_glGenBuffers,
"glGenFramebuffers": &_glGenFramebuffers,
"glGenVertexArrays": &_glGenVertexArrays,
"glGetUniformBlockIndex": &_glGetUniformBlockIndex,
"glCreateProgram": &_glCreateProgram,
"glGenRenderbuffers": &_glGenRenderbuffers,
"glCreateShader": &_glCreateShader,
"glGenTextures": &_glGenTextures,
"glDeleteBuffers": &_glDeleteBuffers,
"glDeleteFramebuffers": &_glDeleteFramebuffers,
"glDeleteProgram": &_glDeleteProgram,
"glDeleteShader": &_glDeleteShader,
"glDeleteRenderbuffers": &_glDeleteRenderbuffers,
"glDeleteTextures": &_glDeleteTextures,
"glDepthFunc": &_glDepthFunc,
"glDepthMask": &_glDepthMask,
"glDisableVertexAttribArray": &_glDisableVertexAttribArray,
"glDisable": &_glDisable,
"glDrawArrays": &_glDrawArrays,
"glDrawElements": &_glDrawElements,
"glEnable": &_glEnable,
"glEnableVertexAttribArray": &_glEnableVertexAttribArray,
"glEndQuery": &_glEndQuery,
"glFinish": &_glFinish,
"glFlush": &_glFlush,
"glFramebufferRenderbuffer": &_glFramebufferRenderbuffer,
"glFramebufferTexture2D": &_glFramebufferTexture2D,
"glGenQueries": &_glGenQueries,
"glGetError": &_glGetError,
"glGetRenderbufferParameteriv": &_glGetRenderbufferParameteriv,
"glGetFloatv": &_glGetFloatv,
"glGetFramebufferAttachmentParameteriv": &_glGetFramebufferAttachmentParameteriv,
"glGetIntegerv": &_glGetIntegerv,
"glGetIntegeri_v": &_glGetIntegeri_v,
"glGetProgramiv": &_glGetProgramiv,
"glGetProgramInfoLog": &_glGetProgramInfoLog,
"glGetQueryObjectuiv": &_glGetQueryObjectuiv,
"glGetShaderiv": &_glGetShaderiv,
"glGetShaderInfoLog": &_glGetShaderInfoLog,
"glGetString": &_glGetString,
"glGetUniformLocation": &_glGetUniformLocation,
"glGetVertexAttribiv": &_glGetVertexAttribiv,
"glGetVertexAttribPointerv": &_glGetVertexAttribPointerv,
"glInvalidateFramebuffer": &_glInvalidateFramebuffer,
"glIsEnabled": &_glIsEnabled,
"glLinkProgram": &_glLinkProgram,
"glPixelStorei": &_glPixelStorei,
"glReadPixels": &_glReadPixels,
"glRenderbufferStorage": &_glRenderbufferStorage,
"glScissor": &_glScissor,
"glShaderSource": &_glShaderSource,
"glTexImage2D": &_glTexImage2D,
"glTexStorage2D": &_glTexStorage2D,
"glTexSubImage2D": &_glTexSubImage2D,
"glTexParameteri": &_glTexParameteri,
"glUniformBlockBinding": &_glUniformBlockBinding,
"glUniform1f": &_glUniform1f,
"glUniform1i": &_glUniform1i,
"glUniform2f": &_glUniform2f,
"glUniform3f": &_glUniform3f,
"glUniform4f": &_glUniform4f,
"glUseProgram": &_glUseProgram,
"glVertexAttribPointer": &_glVertexAttribPointer,
"glViewport": &_glViewport,
}
for name, proc := range procs {
p, err := gles.FindProc(name)
if err != nil {
return fmt.Errorf("failed to locate %s in %s: %w", name, gles.Name, err)
}
*proc = p
}
return nil
}
var (
glInitOnce sync.Once
_glActiveTexture *windows.Proc
_glAttachShader *windows.Proc
_glBeginQuery *windows.Proc
_glBindAttribLocation *windows.Proc
_glBindBuffer *windows.Proc
_glBindBufferBase *windows.Proc
_glBindFramebuffer *windows.Proc
_glBindRenderbuffer *windows.Proc
_glBindTexture *windows.Proc
_glBindVertexArray *windows.Proc
_glBlendEquation *windows.Proc
_glBlendFuncSeparate *windows.Proc
_glBufferData *windows.Proc
_glBufferSubData *windows.Proc
_glCheckFramebufferStatus *windows.Proc
_glClear *windows.Proc
_glClearColor *windows.Proc
_glClearDepthf *windows.Proc
_glDeleteQueries *windows.Proc
_glDeleteVertexArrays *windows.Proc
_glCompileShader *windows.Proc
_glCopyTexSubImage2D *windows.Proc
_glGenerateMipmap *windows.Proc
_glGenBuffers *windows.Proc
_glGenFramebuffers *windows.Proc
_glGenVertexArrays *windows.Proc
_glGetUniformBlockIndex *windows.Proc
_glCreateProgram *windows.Proc
_glGenRenderbuffers *windows.Proc
_glCreateShader *windows.Proc
_glGenTextures *windows.Proc
_glDeleteBuffers *windows.Proc
_glDeleteFramebuffers *windows.Proc
_glDeleteProgram *windows.Proc
_glDeleteShader *windows.Proc
_glDeleteRenderbuffers *windows.Proc
_glDeleteTextures *windows.Proc
_glDepthFunc *windows.Proc
_glDepthMask *windows.Proc
_glDisableVertexAttribArray *windows.Proc
_glDisable *windows.Proc
_glDrawArrays *windows.Proc
_glDrawElements *windows.Proc
_glEnable *windows.Proc
_glEnableVertexAttribArray *windows.Proc
_glEndQuery *windows.Proc
_glFinish *windows.Proc
_glFlush *windows.Proc
_glFramebufferRenderbuffer *windows.Proc
_glFramebufferTexture2D *windows.Proc
_glGenQueries *windows.Proc
_glGetError *windows.Proc
_glGetRenderbufferParameteriv *windows.Proc
_glGetFloatv *windows.Proc
_glGetFramebufferAttachmentParameteriv *windows.Proc
_glGetIntegerv *windows.Proc
_glGetIntegeri_v *windows.Proc
_glGetProgramiv *windows.Proc
_glGetProgramInfoLog *windows.Proc
_glGetQueryObjectuiv *windows.Proc
_glGetShaderiv *windows.Proc
_glGetShaderInfoLog *windows.Proc
_glGetString *windows.Proc
_glGetUniformLocation *windows.Proc
_glGetVertexAttribiv *windows.Proc
_glGetVertexAttribPointerv *windows.Proc
_glInvalidateFramebuffer *windows.Proc
_glIsEnabled *windows.Proc
_glLinkProgram *windows.Proc
_glPixelStorei *windows.Proc
_glReadPixels *windows.Proc
_glRenderbufferStorage *windows.Proc
_glScissor *windows.Proc
_glShaderSource *windows.Proc
_glTexImage2D *windows.Proc
_glTexStorage2D *windows.Proc
_glTexSubImage2D *windows.Proc
_glTexParameteri *windows.Proc
_glUniformBlockBinding *windows.Proc
_glUniform1f *windows.Proc
_glUniform1i *windows.Proc
_glUniform2f *windows.Proc
_glUniform3f *windows.Proc
_glUniform4f *windows.Proc
_glUseProgram *windows.Proc
_glVertexAttribPointer *windows.Proc
_glViewport *windows.Proc
)
type Functions struct {
// Query caches.
int32s [100]int32
float32s [100]float32
uintptrs [100]uintptr
}
type Context any
func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
if ctx != nil {
panic("non-nil context")
}
var err error
glInitOnce.Do(func() {
err = loadGLESv2Procs()
})
return new(Functions), err
}
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 (f *Functions) GenerateMipmap(target Enum) {
syscall.Syscall(_glGenerateMipmap.Addr(), 1, uintptr(target), 0, 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)
if n == 0 {
return ""
}
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 any) {
runtime.KeepAlive(v)
}
+76
View File
@@ -0,0 +1,76 @@
//go: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))
}
+90
View File
@@ -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))
}
+87
View File
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gl
import (
"errors"
"fmt"
"strings"
)
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)
if err != nil {
return Program{}, err
}
defer ctx.DeleteShader(fs)
prog := ctx.CreateProgram()
if !prog.Valid() {
return Program{}, errors.New("glCreateProgram failed")
}
ctx.AttachShader(prog, vs)
ctx.AttachShader(prog, fs)
for i, a := range attribs {
ctx.BindAttribLocation(prog, Attrib(i), a)
}
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 prog, nil
}
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 prog, nil
}
func CreateShader(ctx *Functions, typ Enum, src string) (Shader, error) {
sh := ctx.CreateShader(typ)
if !sh.Valid() {
return Shader{}, errors.New("glCreateShader failed")
}
ctx.ShaderSource(sh, src)
ctx.CompileShader(sh)
if ctx.GetShaderi(sh, COMPILE_STATUS) == 0 {
log := ctx.GetShaderInfoLog(sh)
ctx.DeleteShader(sh)
return Shader{}, fmt.Errorf("shader compilation failed: %s", strings.TrimSpace(log))
}
return sh, nil
}
func ParseGLVersion(glVer string) (version [2]int, gles bool, err error) {
var ver [2]int
if _, err := fmt.Sscanf(glVer, "OpenGL ES %d.%d", &ver[0], &ver[1]); err == nil {
return ver, true, nil
} else if _, err := fmt.Sscanf(glVer, "WebGL %d.%d", &ver[0], &ver[1]); err == nil {
// WebGL major version v corresponds to OpenGL ES version v + 1
ver[0]++
return ver, true, nil
} else if _, err := fmt.Sscanf(glVer, "%d.%d", &ver[0], &ver[1]); err == nil {
return ver, false, nil
}
return ver, false, fmt.Errorf("failed to parse OpenGL ES version (%s)", glVer)
}
+497
View File
@@ -0,0 +1,497 @@
// SPDX-License-Identifier: Unlicense OR MIT
package ops
import (
"encoding/binary"
"image"
"math"
"gioui.org/f32"
"gioui.org/internal/byteslice"
"gioui.org/internal/scene"
)
type Ops struct {
// version is incremented at each Reset.
version uint32
// data contains the serialized operations.
data []byte
// refs hold external references for operations.
refs []any
// stringRefs provides space for string references, pointers to which will
// be stored in refs. Storing a string directly in refs would cause a heap
// allocation, to store the string header in an interface value. The backing
// array of stringRefs, on the other hand, gets reused between calls to
// reset, making string references free on average.
//
// Appending to stringRefs might reallocate the backing array, which will
// leave pointers to the old array in refs. This temporarily causes a slight
// increase in memory usage, but this, too, amortizes away as the capacity
// of stringRefs approaches its stable maximum.
stringRefs []string
// nextStateID is the id allocated for the next
// StateOp.
nextStateID uint32
// multipOp indicates a multi-op such as clip.Path is being added.
multipOp bool
macroStack stack
stacks [_StackKind]stack
}
type OpType byte
type Shape byte
// Start at a high number for easier debugging.
const firstOpIndex = 200
const (
TypeMacro OpType = iota + firstOpIndex
TypeCall
TypeDefer
TypeTransform
TypePopTransform
TypePushOpacity
TypePopOpacity
TypeImage
TypePaint
TypeColor
TypeLinearGradient
TypePass
TypePopPass
TypeInput
TypeKeyInputHint
TypeSave
TypeLoad
TypeAux
TypeClip
TypePopClip
TypeCursor
TypePath
TypeStroke
TypeSemanticLabel
TypeSemanticDesc
TypeSemanticClass
TypeSemanticSelected
TypeSemanticEnabled
TypeActionInput
)
type StackID struct {
id uint32
prev uint32
}
// StateOp represents a saved operation snapshot to be restored
// later.
type StateOp struct {
id uint32
macroID uint32
ops *Ops
}
// stack tracks the integer identities of stack operations to ensure correct
// pairing of their push and pop methods.
type stack struct {
currentID uint32
nextID uint32
}
type StackKind uint8
// ClipOp is the shadow of clip.Op.
type ClipOp struct {
Bounds image.Rectangle
Outline bool
Shape Shape
}
const (
ClipStack StackKind = iota
TransStack
PassStack
OpacityStack
_StackKind
)
const (
Path Shape = iota
Ellipse
Rect
)
const (
TypeMacroLen = 1 + 4 + 4
TypeCallLen = 1 + 4 + 4 + 4 + 4
TypeDeferLen = 1
TypeTransformLen = 1 + 1 + 4*6
TypePopTransformLen = 1
TypePushOpacityLen = 1 + 4
TypePopOpacityLen = 1
TypeRedrawLen = 1 + 8
TypeImageLen = 1 + 1
TypePaintLen = 1
TypeColorLen = 1 + 4
TypeLinearGradientLen = 1 + 8*2 + 4*2
TypePassLen = 1
TypePopPassLen = 1
TypeInputLen = 1
TypeKeyInputHintLen = 1 + 1
TypeSaveLen = 1 + 4
TypeLoadLen = 1 + 4
TypeAuxLen = 1
TypeClipLen = 1 + 4*4 + 1 + 1
TypePopClipLen = 1
TypeCursorLen = 2
TypePathLen = 8 + 1
TypeStrokeLen = 1 + 4
TypeSemanticLabelLen = 1
TypeSemanticDescLen = 1
TypeSemanticClassLen = 2
TypeSemanticSelectedLen = 2
TypeSemanticEnabledLen = 2
TypeActionInputLen = 1 + 1
)
func (op *ClipOp) Decode(data []byte) {
if len(data) < TypeClipLen || OpType(data[0]) != TypeClip {
panic("invalid op")
}
data = data[:TypeClipLen]
bo := binary.LittleEndian
op.Bounds.Min.X = int(int32(bo.Uint32(data[1:])))
op.Bounds.Min.Y = int(int32(bo.Uint32(data[5:])))
op.Bounds.Max.X = int(int32(bo.Uint32(data[9:])))
op.Bounds.Max.Y = int(int32(bo.Uint32(data[13:])))
op.Outline = data[17] == 1
op.Shape = Shape(data[18])
}
func Reset(o *Ops) {
o.macroStack = stack{}
o.stacks = [_StackKind]stack{}
// Leave references to the GC.
for i := range o.refs {
o.refs[i] = nil
}
for i := range o.stringRefs {
o.stringRefs[i] = ""
}
o.data = o.data[:0]
o.refs = o.refs[:0]
o.stringRefs = o.stringRefs[:0]
o.nextStateID = 0
o.version++
}
func Write(o *Ops, n int) []byte {
if o.multipOp {
panic("cannot mix multi ops with single ones")
}
o.data = append(o.data, make([]byte, n)...)
return o.data[len(o.data)-n:]
}
func BeginMulti(o *Ops) {
if o.multipOp {
panic("cannot interleave multi ops")
}
o.multipOp = true
}
func EndMulti(o *Ops) {
if !o.multipOp {
panic("cannot end non multi ops")
}
o.multipOp = false
}
func WriteMulti(o *Ops, n int) []byte {
if !o.multipOp {
panic("cannot use multi ops in single ops")
}
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
bo.PutUint32(data[1:], uint32(pc.data))
bo.PutUint32(data[5:], uint32(pc.refs))
}
func AddCall(o *Ops, callOps *Ops, pc PC, end 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))
bo.PutUint32(data[9:], uint32(end.data))
bo.PutUint32(data[13:], uint32(end.refs))
}
func PushOp(o *Ops, kind StackKind) (StackID, uint32) {
return o.stacks[kind].push(), o.macroStack.currentID
}
func PopOp(o *Ops, kind StackKind, sid StackID, macroID uint32) {
if o.macroStack.currentID != macroID {
panic("stack push and pop must not cross macro boundary")
}
o.stacks[kind].pop(sid)
}
func Write1(o *Ops, n int, ref1 any) []byte {
o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1)
return o.data[len(o.data)-n:]
}
func Write1String(o *Ops, n int, ref1 string) []byte {
o.data = append(o.data, make([]byte, n)...)
o.stringRefs = append(o.stringRefs, ref1)
o.refs = append(o.refs, &o.stringRefs[len(o.stringRefs)-1])
return o.data[len(o.data)-n:]
}
func Write2(o *Ops, n int, ref1, ref2 any) []byte {
o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1, ref2)
return o.data[len(o.data)-n:]
}
func Write2String(o *Ops, n int, ref1 any, ref2 string) []byte {
o.data = append(o.data, make([]byte, n)...)
o.stringRefs = append(o.stringRefs, ref2)
o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1])
return o.data[len(o.data)-n:]
}
func Write3(o *Ops, n int, ref1, ref2, ref3 any) []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: uint32(len(o.data)), refs: uint32(len(o.refs))}
}
func (s *stack) push() StackID {
s.nextID++
sid := StackID{
id: s.nextID,
prev: s.currentID,
}
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 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:]))
c := math.Float32frombits(bo.Uint32(data[4*2:]))
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), push
}
func DecodeOpacity(data []byte) float32 {
if OpType(data[0]) != TypePushOpacity {
panic("invalid op")
}
bo := binary.LittleEndian
return math.Float32frombits(bo.Uint32(data[1:]))
}
// 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:]))
}
type opProp struct {
Size byte
NumRefs byte
}
var opProps = [0x100]opProp{
TypeMacro: {Size: TypeMacroLen, NumRefs: 0},
TypeCall: {Size: TypeCallLen, NumRefs: 1},
TypeDefer: {Size: TypeDeferLen, NumRefs: 0},
TypeTransform: {Size: TypeTransformLen, NumRefs: 0},
TypePopTransform: {Size: TypePopTransformLen, NumRefs: 0},
TypePushOpacity: {Size: TypePushOpacityLen, NumRefs: 0},
TypePopOpacity: {Size: TypePopOpacityLen, NumRefs: 0},
TypeImage: {Size: TypeImageLen, NumRefs: 2},
TypePaint: {Size: TypePaintLen, NumRefs: 0},
TypeColor: {Size: TypeColorLen, NumRefs: 0},
TypeLinearGradient: {Size: TypeLinearGradientLen, NumRefs: 0},
TypePass: {Size: TypePassLen, NumRefs: 0},
TypePopPass: {Size: TypePopPassLen, NumRefs: 0},
TypeInput: {Size: TypeInputLen, NumRefs: 1},
TypeKeyInputHint: {Size: TypeKeyInputHintLen, NumRefs: 1},
TypeSave: {Size: TypeSaveLen, NumRefs: 0},
TypeLoad: {Size: TypeLoadLen, NumRefs: 0},
TypeAux: {Size: TypeAuxLen, NumRefs: 0},
TypeClip: {Size: TypeClipLen, NumRefs: 0},
TypePopClip: {Size: TypePopClipLen, NumRefs: 0},
TypeCursor: {Size: TypeCursorLen, NumRefs: 0},
TypePath: {Size: TypePathLen, NumRefs: 0},
TypeStroke: {Size: TypeStrokeLen, NumRefs: 0},
TypeSemanticLabel: {Size: TypeSemanticLabelLen, NumRefs: 1},
TypeSemanticDesc: {Size: TypeSemanticDescLen, NumRefs: 1},
TypeSemanticClass: {Size: TypeSemanticClassLen, NumRefs: 0},
TypeSemanticSelected: {Size: TypeSemanticSelectedLen, NumRefs: 0},
TypeSemanticEnabled: {Size: TypeSemanticEnabledLen, NumRefs: 0},
TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0},
}
func (t OpType) props() (size, numRefs uint32) {
v := opProps[t]
return uint32(v.Size), uint32(v.NumRefs)
}
func (t OpType) Size() uint32 {
return uint32(opProps[t].Size)
}
func (t OpType) NumRefs() uint32 {
return uint32(opProps[t].NumRefs)
}
func (t OpType) String() string {
switch t {
case TypeMacro:
return "Macro"
case TypeCall:
return "Call"
case TypeDefer:
return "Defer"
case TypeTransform:
return "Transform"
case TypePopTransform:
return "PopTransform"
case TypePushOpacity:
return "PushOpacity"
case TypePopOpacity:
return "PopOpacity"
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 TypeInput:
return "Input"
case TypeKeyInputHint:
return "KeyInputHint"
case TypeSave:
return "Save"
case TypeLoad:
return "Load"
case TypeAux:
return "Aux"
case TypeClip:
return "Clip"
case TypePopClip:
return "PopClip"
case TypeCursor:
return "Cursor"
case TypePath:
return "Path"
case TypeStroke:
return "Stroke"
case TypeSemanticLabel:
return "SemanticDescription"
default:
panic("unknown OpType")
}
}
+190
View File
@@ -0,0 +1,190 @@
// SPDX-License-Identifier: Unlicense OR MIT
package ops
import (
"encoding/binary"
)
// Reader parses an ops list.
type Reader struct {
pc PC
stack []macro
ops *Ops
deferOps Ops
deferDone bool
}
// EncodedOp represents an encoded op returned by
// Reader.
type EncodedOp struct {
Key Key
Data []byte
Refs []any
}
// Key is a unique key for a given op.
type Key struct {
ops *Ops
pc uint32
version uint32
}
// Shadow of op.MacroOp.
type macroOp struct {
ops *Ops
start PC
end PC
}
// PC is an instruction counter for an operation list.
type PC struct {
data uint32
refs uint32
}
type macro struct {
ops *Ops
retPC PC
endPC PC
}
type opMacroDef struct {
endpc PC
}
func (pc PC) Add(op OpType) PC {
size, numRefs := op.props()
return PC{
data: pc.data + size,
refs: pc.refs + numRefs,
}
}
// Reset start reading from the beginning of ops.
func (r *Reader) Reset(ops *Ops) {
r.ResetAt(ops, PC{})
}
// 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]
if r.pc == b.endPC {
r.ops = b.ops
r.pc = b.retPC
r.stack = r.stack[:len(r.stack)-1]
continue
}
}
data := r.ops.data
data = data[r.pc.data:]
refs := r.ops.refs
if len(data) == 0 {
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 := OpType(data[0])
n, nrefs := t.props()
data = data[:n]
refs = refs[r.pc.refs:]
refs = refs[:nrefs]
switch t {
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 - TypeAuxLen
data = data[:n]
case TypeCall:
if deferring {
deferring = false
// Copy macro for deferred execution.
if nrefs != 1 {
panic("internal error: unexpected number of macro refs")
}
deferData := Write1(&r.deferOps, int(n), refs[0])
copy(deferData, data)
r.pc.data += n
r.pc.refs += nrefs
continue
}
var op macroOp
op.decode(data, refs)
retPC := r.pc
retPC.data += n
retPC.refs += nrefs
r.stack = append(r.stack, macro{
ops: r.ops,
retPC: retPC,
endPC: op.end,
})
r.ops = op.ops
r.pc = op.start
continue
case TypeMacro:
var op opMacroDef
op.decode(data)
if op.endpc != (PC{}) {
r.pc = op.endpc
} else {
// Treat an incomplete macro as containing all remaining ops.
r.pc.data = uint32(len(r.ops.data))
r.pc.refs = uint32(len(r.ops.refs))
}
continue
}
r.pc.data += n
r.pc.refs += nrefs
return EncodedOp{Key: key, Data: data, Refs: refs}, true
}
}
func (op *opMacroDef) decode(data []byte) {
if len(data) < TypeMacroLen || OpType(data[0]) != TypeMacro {
panic("invalid op")
}
bo := binary.LittleEndian
data = data[:TypeMacroLen]
op.endpc.data = bo.Uint32(data[1:])
op.endpc.refs = bo.Uint32(data[5:])
}
func (m *macroOp) decode(data []byte, refs []any) {
if len(data) < TypeCallLen || len(refs) < 1 || OpType(data[0]) != TypeCall {
panic("invalid op")
}
bo := binary.LittleEndian
data = data[:TypeCallLen]
m.ops = refs[0].(*Ops)
m.start.data = bo.Uint32(data[1:])
m.start.refs = bo.Uint32(data[5:])
m.end.data = bo.Uint32(data[9:])
m.end.refs = bo.Uint32(data[13:])
}
+251
View File
@@ -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/internal/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
}
+760
View File
@@ -0,0 +1,760 @@
// 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/internal/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) 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) {
pen := qs.pen()
m, segments := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle)
for range segments {
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 {
strokePathRoundJoin(&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 normPt(p f32.Point, l float32) f32.Point {
if (p.X == 0 && p.Y == 0) || l == 0 {
return f32.Point{}
}
isVerticalUnit := p.X == 0 && (p.Y == l || p.Y == -l)
isHorizontalUnit := p.Y == 0 && (p.X == l || p.X == -l)
if isVerticalUnit || isHorizontalUnit {
if math.Signbit(float64(l)) {
return f32.Point{X: -p.X, Y: -p.Y}
} else {
return f32.Point{X: p.X, Y: p.Y}
}
}
d := math.Hypot(float64(p.X), float64(p.Y))
l64 := float64(l)
if math.Abs(d-l64) < 1e-10 {
if math.Signbit(float64(l)) {
return f32.Point{X: -p.X, Y: -p.Y}
} else {
return f32.Point{X: p.X, Y: p.Y}
}
}
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 perpDot(p, q f32.Point) float32 {
return p.X*q.Y - p.Y*q.X
}
func angleBetween(n0, n1 f32.Point) float64 {
return math.Atan2(float64(n1.Y), float64(n1.X)) -
math.Atan2(float64(n0.Y), float64(n0.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
}
// strokePathRoundJoin joins the two paths rhs and lhs, creating an arc.
func strokePathRoundJoin(rhs, lhs *StrokeQuads, hw float32, pivot, n0, n1 f32.Point, r0, r1 float32) {
rp := pivot.Add(n1)
lp := pivot.Sub(n1)
angle := angleBetween(n0, n1)
switch {
case angle <= 0:
// Path bends to the right, ie. CW (or 180 degree turn).
c := pivot.Sub(lhs.pen())
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.
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) (transform f32.Affine2D, segments int) {
const segmentsPerCircle = 16
const anglePerSegment = 2 * math.Pi / segmentsPerCircle
s := angle / anglePerSegment
if s < 0 {
s = -s
}
segments = int(math.Ceil(float64(s)))
if segments <= 0 {
segments = 1
}
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)
}
}
θ := angle / float32(segments)
ref := f32.AffineId() // transform from absolute frame to ellipse-based one
rot := f32.AffineId() // rotation matrix for each segment
inv := f32.AffineId() // 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), segments
}
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))
scratch := make([]QuadSegment, 0, 10)
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:
from, ctrl0, ctrl1, to := scene.DecodeCubic(cmd)
scratch = SplitCubic(from, ctrl0, ctrl1, to, scratch[:0])
for _, q := range scratch {
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, quads []QuadSegment) []QuadSegment {
// 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
}
maxDist := l * 0.001
approxCubeTo(&quads, 0, maxDist*maxDist, from, ctrl0, ctrl1, to)
return quads
}
// approxCubeTo approximates a cubic Bézier by a series of quadratic
// curves.
func approxCubeTo(quads *[]QuadSegment, splits int, maxDistSq 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
// using, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to
// C = (q0 + q1)/4
q0 := ctrl0.Mul(3).Sub(from)
q1 := ctrl1.Mul(3).Sub(to)
c := q0.Add(q1).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|
// reusing, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to
// d = sqrt(3)/36 * |-q1 + q0|
//
// To save a square root, compare d² with the squared tolerance.
v := q0.Sub(q1)
d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36)
if d2 <= maxDistSq {
*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, maxDistSq, from, c0, c01, c0112)
splits = approxCubeTo(quads, splits, maxDistSq, c0112, c12, c2, to)
return splits
}
+2145
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
// 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
}
+49
View File
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Unlicense OR MIT
//go:build ((linux && !android) || freebsd) && !nowayland
// +build linux,!android freebsd
// +build !nowayland
package vk
/*
#cgo linux pkg-config: wayland-client
#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
}
+47
View File
@@ -0,0 +1,47 @@
// 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
}