From b6d5b8a1a6a7238182d08b0104bf088058922da8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 28 Jul 2024 16:33:58 +0900 Subject: [PATCH 001/188] all: update Oto (use AudioWorklet) Updates ebitengine/oto#241 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ac8c82711..5ded82839 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.2 + github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728073255-2e3e23098cb4 github.com/ebitengine/purego v0.8.0-alpha.3 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index 7131682fc..d4484fcd2 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2 h1:ex2UbULSPuLt76yyaR2JBXICx83Ph6Oz5ugN7h1Jo/I= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728073255-2e3e23098cb4 h1:QZX4IdJ77SfHCuGjOAc8UzfRv1oXo/cGLarfy523Mac= +github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728073255-2e3e23098cb4/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= github.com/ebitengine/purego v0.8.0-alpha.3 h1:qoFlpGuVwJ6J85kuj6Qpyp0DBgxsNYfSY9efidSNFgA= github.com/ebitengine/purego v0.8.0-alpha.3/go.mod h1:b94LtM1jUWDZPKDyENVhB0WsLdLWFApjbNw5AyxmKyI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From 09b41846f9bf2b58682f2669bac6ccb3a8d7651e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 28 Jul 2024 17:13:13 +0900 Subject: [PATCH 002/188] all: update Oto (bug fix) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5ded82839..590fd3eaf 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728073255-2e3e23098cb4 + github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728081213-ac2412e6f21a github.com/ebitengine/purego v0.8.0-alpha.3 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index d4484fcd2..c521089eb 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728073255-2e3e23098cb4 h1:QZX4IdJ77SfHCuGjOAc8UzfRv1oXo/cGLarfy523Mac= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728073255-2e3e23098cb4/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728081213-ac2412e6f21a h1:sB/CjXCL9t4WEkAxNPuWCsIRKY+W9Iu0cNR77aaUl0o= +github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728081213-ac2412e6f21a/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= github.com/ebitengine/purego v0.8.0-alpha.3 h1:qoFlpGuVwJ6J85kuj6Qpyp0DBgxsNYfSY9efidSNFgA= github.com/ebitengine/purego v0.8.0-alpha.3/go.mod h1:b94LtM1jUWDZPKDyENVhB0WsLdLWFApjbNw5AyxmKyI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From 563f2e0e0b11865f3e386d19837b67c49aabdf26 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 28 Jul 2024 20:20:13 +0900 Subject: [PATCH 003/188] all: update Oto --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 590fd3eaf..8267e69ae 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728081213-ac2412e6f21a + github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728111929-ac8b8b4825a4 github.com/ebitengine/purego v0.8.0-alpha.3 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index c521089eb..51292750c 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728081213-ac2412e6f21a h1:sB/CjXCL9t4WEkAxNPuWCsIRKY+W9Iu0cNR77aaUl0o= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728081213-ac2412e6f21a/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728111929-ac8b8b4825a4 h1:2Ap2Ntca53wLNn4AxF2CT+flMTJitvCtUchTHvkb5YU= +github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728111929-ac8b8b4825a4/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= github.com/ebitengine/purego v0.8.0-alpha.3 h1:qoFlpGuVwJ6J85kuj6Qpyp0DBgxsNYfSY9efidSNFgA= github.com/ebitengine/purego v0.8.0-alpha.3/go.mod h1:b94LtM1jUWDZPKDyENVhB0WsLdLWFApjbNw5AyxmKyI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From ef5ac4175d890f43d6696174289e9f1f7af528d6 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Jul 2024 02:28:37 +0900 Subject: [PATCH 004/188] all: use bufio.Reader to improve reading speed --- examples/video/main.go | 3 ++- internal/graphicsdriver/metal/mtl/mtl_darwin_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/video/main.go b/examples/video/main.go index 4c9f66661..445a6fb50 100644 --- a/examples/video/main.go +++ b/examples/video/main.go @@ -15,6 +15,7 @@ package main import ( + "bufio" "bytes" _ "embed" "fmt" @@ -81,7 +82,7 @@ func main() { fmt.Println("Play the default video. You can specify a video file as an argument.") } - player, err := newMPEGPlayer(in) + player, err := newMPEGPlayer(bufio.NewReader(in)) if err != nil { log.Fatal(err) } diff --git a/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go b/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go index 39e2c1bfb..48cd7a703 100644 --- a/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go +++ b/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go @@ -15,6 +15,7 @@ package mtl_test import ( + "bufio" "fmt" "image" "image/color" @@ -147,7 +148,7 @@ func readPNG(name string) (image.Image, error) { defer func() { _ = f.Close() }() - return png.Decode(f) + return png.Decode(bufio.NewReader(f)) } // imageEq reports whether images m, n are considered equivalent. Two images are considered From 0281ac7bd2f0139b43705e792848ec42b7af40d5 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Jul 2024 16:29:50 +0900 Subject: [PATCH 005/188] all: use bufio.NewWriter to improve writing speed --- ebitenutil/gen.go | 7 ++++++- examples/blocks/main.go | 5 ++++- examples/keyboard/keyboard/gen.go | 18 +++++++++++++++--- examples/shaderprecomp/fxc/gen.go | 11 ++++++++++- examples/shaderprecomp/metallib/gen.go | 6 ++++-- genkeys.go | 8 +++++++- internal/gamepaddb/gen.go | 6 +++++- internal/png/gen.go | 15 +++++++++++---- 8 files changed, 62 insertions(+), 14 deletions(-) diff --git a/ebitenutil/gen.go b/ebitenutil/gen.go index 9cb0b1965..984931772 100644 --- a/ebitenutil/gen.go +++ b/ebitenutil/gen.go @@ -17,6 +17,7 @@ package main import ( + "bufio" "fmt" "image" "image/color" @@ -77,7 +78,11 @@ func run() error { } defer f.Close() - if err := png.Encode(f, dst); err != nil { + w := bufio.NewWriter(f) + if err := png.Encode(w, dst); err != nil { + return err + } + if err := w.Flush(); err != nil { return err } diff --git a/examples/blocks/main.go b/examples/blocks/main.go index e914837b6..703b6bdbc 100644 --- a/examples/blocks/main.go +++ b/examples/blocks/main.go @@ -15,6 +15,7 @@ package main import ( + "bufio" "flag" "log" "os" @@ -33,9 +34,11 @@ func main() { if err != nil { log.Fatal(err) } - if err := pprof.StartCPUProfile(f); err != nil { + w := bufio.NewWriter(f) + if err := pprof.StartCPUProfile(w); err != nil { log.Fatal(err) } + defer w.Flush() defer pprof.StopCPUProfile() } diff --git a/examples/keyboard/keyboard/gen.go b/examples/keyboard/keyboard/gen.go index 8c60f4f28..809e85e16 100644 --- a/examples/keyboard/keyboard/gen.go +++ b/examples/keyboard/keyboard/gen.go @@ -17,6 +17,7 @@ package main import ( + "bufio" "bytes" "image" "image/png" @@ -220,7 +221,11 @@ func outputKeyboardImage() (map[ebiten.Key]image.Rectangle, error) { } defer out.Close() - if err := png.Encode(out, img); err != nil { + w := bufio.NewWriter(out) + if err := png.Encode(w, img); err != nil { + return nil, err + } + if err := w.Flush(); err != nil { return nil, err } @@ -278,10 +283,17 @@ func outputKeyRectsGo(k map[ebiten.Key]image.Rectangle) error { if err != nil { return err } - return tmpl.Execute(f, map[string]any{ + w := bufio.NewWriter(f) + if err := tmpl.Execute(w, map[string]any{ "License": license, "KeyRectsMap": k, - }) + }); err != nil { + return err + } + if err := w.Flush(); err != nil { + return err + } + return nil } type game struct { diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go index 4f7c4203f..0025fe7b3 100644 --- a/examples/shaderprecomp/fxc/gen.go +++ b/examples/shaderprecomp/fxc/gen.go @@ -20,6 +20,7 @@ package main import ( + "bufio" "errors" "fmt" "os" @@ -88,7 +89,15 @@ func generateHSLSFiles(source *shaderprecomp.ShaderSource, index int, tmpdir str } defer psf.Close() - if err := shaderprecomp.CompileToHLSL(vsf, psf, source); err != nil { + vsfw := bufio.NewWriter(vsf) + psfw := bufio.NewWriter(psf) + if err := shaderprecomp.CompileToHLSL(vsfw, psfw, source); err != nil { + return "", "", err + } + if err := vsfw.Flush(); err != nil { + return "", "", err + } + if err := psfw.Flush(); err != nil { return "", "", err } diff --git a/examples/shaderprecomp/metallib/gen.go b/examples/shaderprecomp/metallib/gen.go index 50264e8f2..fdb65e738 100644 --- a/examples/shaderprecomp/metallib/gen.go +++ b/examples/shaderprecomp/metallib/gen.go @@ -20,6 +20,7 @@ package main import ( + "bufio" "fmt" "os" "os/exec" @@ -68,10 +69,11 @@ func compile(source *shaderprecomp.ShaderSource, index int, tmpdir string) error } defer f.Close() - if err := shaderprecomp.CompileToMSL(f, source); err != nil { + w := bufio.NewWriter(f) + if err := shaderprecomp.CompileToMSL(w, source); err != nil { return err } - if err := f.Sync(); err != nil { + if err := w.Flush(); err != nil { return err } diff --git a/genkeys.go b/genkeys.go index f53916378..59a9d6e1d 100644 --- a/genkeys.go +++ b/genkeys.go @@ -19,6 +19,7 @@ package main import ( + "bufio" "log" "os" "path/filepath" @@ -809,7 +810,8 @@ func main() { buildConstraints = "//go:build !android && !ios && !js && !nintendosdk && !playstation5" } // NOTE: According to godoc, maps are automatically sorted by key. - if err := tmpl.Execute(f, struct { + w := bufio.NewWriter(f) + if err := tmpl.Execute(w, struct { License string DoNotEdit string BuildConstraints string @@ -840,5 +842,9 @@ func main() { }); err != nil { log.Fatal(err) } + + if err := w.Flush(); err != nil { + log.Fatal(err) + } } } diff --git a/internal/gamepaddb/gen.go b/internal/gamepaddb/gen.go index befe2c823..e7a69fa3f 100644 --- a/internal/gamepaddb/gen.go +++ b/internal/gamepaddb/gen.go @@ -155,7 +155,8 @@ func run() error { } defer f.Close() - if err := tmpl.Execute(f, struct { + w := bufio.NewWriter(f) + if err := tmpl.Execute(w, struct { License string DoNotEdit string BuildConstraints string @@ -170,6 +171,9 @@ func run() error { }); err != nil { return err } + if err := w.Flush(); err != nil { + return err + } } return nil diff --git a/internal/png/gen.go b/internal/png/gen.go index b4e4dfce4..1623bd667 100644 --- a/internal/png/gen.go +++ b/internal/png/gen.go @@ -17,6 +17,7 @@ package main import ( + "bufio" "fmt" "go/ast" "go/format" @@ -93,6 +94,8 @@ func run() error { } defer out.Close() + w := bufio.NewWriter(out) + // TODO: Remove call of RegisterDecoder data, err := os.ReadFile(filepath.Join(dir, f)) @@ -129,14 +132,14 @@ func run() error { return true }, nil) - fmt.Fprintln(out, "// Code generated by gen.go. DO NOT EDIT.") - fmt.Fprintln(out) - format.Node(out, fset, tree) + fmt.Fprintln(w, "// Code generated by gen.go. DO NOT EDIT.") + fmt.Fprintln(w) + format.Node(w, fset, tree) if f == "reader.go" { // The min function was removed as of Go 1.22, but this is needed for old Go. // TODO: Remove this when Go 1.21 is the minimum supported version. - fmt.Fprintln(out, ` + fmt.Fprintln(w, ` func min(a, b int) int { if a < b { return a @@ -144,6 +147,10 @@ func min(a, b int) int { return b }`) } + + if err := w.Flush(); err != nil { + return err + } } return nil } From c8aea2df166fabdae12e490f0580c5b320b9183a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Jul 2024 17:37:30 +0900 Subject: [PATCH 006/188] examples/blocks: bug fix: do not ignore error --- examples/blocks/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/blocks/main.go b/examples/blocks/main.go index 703b6bdbc..6f7ad7fce 100644 --- a/examples/blocks/main.go +++ b/examples/blocks/main.go @@ -38,7 +38,11 @@ func main() { if err := pprof.StartCPUProfile(w); err != nil { log.Fatal(err) } - defer w.Flush() + defer func() { + if err := w.Flush(); err != nil { + log.Fatal(err) + } + }() defer pprof.StopCPUProfile() } From 47b8af554c8ae7b1da4a2c47744c32bebb857548 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 31 Jul 2024 00:15:04 +0900 Subject: [PATCH 007/188] all: update Oto to v3.3.0-alpha.3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8267e69ae..68d099c16 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728111929-ac8b8b4825a4 + github.com/ebitengine/oto/v3 v3.3.0-alpha.3 github.com/ebitengine/purego v0.8.0-alpha.3 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index 51292750c..a8d60ad61 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728111929-ac8b8b4825a4 h1:2Ap2Ntca53wLNn4AxF2CT+flMTJitvCtUchTHvkb5YU= -github.com/ebitengine/oto/v3 v3.3.0-alpha.2.0.20240728111929-ac8b8b4825a4/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3 h1:L8Odh8gVr4F+0CzSfqOfw/nEnXXWkB+UhGOKUYrP+Nk= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= github.com/ebitengine/purego v0.8.0-alpha.3 h1:qoFlpGuVwJ6J85kuj6Qpyp0DBgxsNYfSY9efidSNFgA= github.com/ebitengine/purego v0.8.0-alpha.3/go.mod h1:b94LtM1jUWDZPKDyENVhB0WsLdLWFApjbNw5AyxmKyI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From 95ad1b158c3f130c970ab61e4eb329969db1715b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 31 Jul 2024 11:10:34 +0900 Subject: [PATCH 008/188] internal/file: refactoring Updates #3045 --- internal/file/file_js.go | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/internal/file/file_js.go b/internal/file/file_js.go index d285c0166..ddb3c3506 100644 --- a/internal/file/file_js.go +++ b/internal/file/file_js.go @@ -43,7 +43,7 @@ func (f *FileEntryFS) Open(name string) (fs.File, error) { } if name == "." { - return &dir{entry: f.rootEntry}, nil + return &dir{dirEntry: f.rootEntry}, nil } var chEntry chan js.Value @@ -69,7 +69,7 @@ func (f *FileEntryFS) Open(name string) (fs.File, error) { chEntry = make(chan js.Value) f.rootEntry.Call("getDirectory", name, nil, cbSuccess, cbFailure) if entry := <-chEntry; entry.Truthy() { - return &dir{entry: entry}, nil + return &dir{dirEntry: entry}, nil } return nil, &fs.PathError{ @@ -109,8 +109,8 @@ func (f *file) ensureFile() js.Value { func (f *file) Stat() (fs.FileInfo, error) { return &fileInfo{ - entry: f.entry, - file: f.ensureFile(), + name: f.entry.Get("name").String(), + file: f.ensureFile(), }, nil } @@ -163,21 +163,21 @@ func (f *file) Close() error { } type dir struct { - entry js.Value - entries []js.Value - offset int + dirEntry js.Value + fileEntries []js.Value + offset int } func (d *dir) Stat() (fs.FileInfo, error) { return &fileInfo{ - entry: d.entry, + name: d.dirEntry.Get("name").String(), }, nil } func (d *dir) Read(buf []byte) (int, error) { return 0, &fs.PathError{ Op: "read", - Path: d.entry.Get("name").String(), + Path: d.dirEntry.Get("name").String(), Err: errors.New("is a directory"), } } @@ -187,7 +187,7 @@ func (d *dir) Close() error { } func (d *dir) ReadDir(count int) ([]fs.DirEntry, error) { - if d.entries == nil { + if d.fileEntries == nil { ch := make(chan struct{}) var rec js.Func cb := js.FuncOf(func(this js.Value, args []js.Value) any { @@ -205,14 +205,14 @@ func (d *dir) ReadDir(count int) ([]fs.DirEntry, error) { if !ent.Get("isFile").Bool() && !ent.Get("isDirectory").Bool() { continue } - d.entries = append(d.entries, ent) + d.fileEntries = append(d.fileEntries, ent) } rec.Value.Call("call") return nil }) defer cb.Release() - reader := d.entry.Call("createReader") + reader := d.dirEntry.Call("createReader") rec = js.FuncOf(func(this js.Value, args []js.Value) any { reader.Call("readEntries", cb) return nil @@ -223,7 +223,7 @@ func (d *dir) ReadDir(count int) ([]fs.DirEntry, error) { <-ch } - n := len(d.entries) - d.offset + n := len(d.fileEntries) - d.offset if n == 0 { if count <= 0 { @@ -238,11 +238,12 @@ func (d *dir) ReadDir(count int) ([]fs.DirEntry, error) { ents := make([]fs.DirEntry, n) for i := range ents { + entry := d.fileEntries[d.offset+i] fi := &fileInfo{ - entry: d.entries[d.offset+i], + name: entry.Get("name").String(), } - if fi.entry.Get("isFile").Bool() { - fi.file = getFile(fi.entry) + if entry.Get("isFile").Bool() { + fi.file = getFile(entry) } ents[i] = fs.FileInfoToDirEntry(fi) } @@ -252,12 +253,12 @@ func (d *dir) ReadDir(count int) ([]fs.DirEntry, error) { } type fileInfo struct { - entry js.Value - file js.Value + name string + file js.Value } func (f *fileInfo) Name() string { - return f.entry.Get("name").String() + return f.name } func (f *fileInfo) Size() int64 { From 1843f6acc1638a9dfba0f9de611e18ccc3bce43a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 31 Jul 2024 11:03:23 +0900 Subject: [PATCH 009/188] internal/ui: bug fix: dropping multiple files didn't work on Firefox and Safari Closes #3045 --- internal/file/file_js.go | 163 +++++++++++++++++++++++++-------------- internal/ui/ui_js.go | 11 ++- 2 files changed, 112 insertions(+), 62 deletions(-) diff --git a/internal/file/file_js.go b/internal/file/file_js.go index ddb3c3506..ddb3fc21f 100644 --- a/internal/file/file_js.go +++ b/internal/file/file_js.go @@ -24,13 +24,24 @@ import ( ) type FileEntryFS struct { - rootEntry js.Value + rootEntries []js.Value } -func NewFileEntryFS(root js.Value) *FileEntryFS { - return &FileEntryFS{ - rootEntry: root, +func NewFileEntryFS(rootEntries []js.Value) (*FileEntryFS, error) { + // Check all the full paths are the same. + var fullpath string + for _, ent := range rootEntries { + if fullpath == "" { + fullpath = ent.Get("fullPath").String() + continue + } + if fullpath != ent.Get("fullPath").String() { + return nil, errors.New("file: all the full paths must be the same") + } } + return &FileEntryFS{ + rootEntries: rootEntries, + }, nil } func (f *FileEntryFS) Open(name string) (fs.File, error) { @@ -43,33 +54,55 @@ func (f *FileEntryFS) Open(name string) (fs.File, error) { } if name == "." { - return &dir{dirEntry: f.rootEntry}, nil + var dirName string + for _, ent := range f.rootEntries { + if dirName == "" { + dirName = ent.Get("name").String() + continue + } + if dirName != ent.Get("name").String() { + return nil, &fs.PathError{ + Op: "open", + Path: name, + Err: errors.New("invalid directory"), + } + } + } + return &dir{ + name: dirName, + dirEntries: f.rootEntries, + }, nil } - var chEntry chan js.Value - cbSuccess := js.FuncOf(func(this js.Value, args []js.Value) any { - chEntry <- args[0] - close(chEntry) - return nil - }) - defer cbSuccess.Release() + for _, ent := range f.rootEntries { + var chEntry chan js.Value + cbSuccess := js.FuncOf(func(this js.Value, args []js.Value) any { + chEntry <- args[0] + close(chEntry) + return nil + }) + defer cbSuccess.Release() - cbFailure := js.FuncOf(func(this js.Value, args []js.Value) any { - close(chEntry) - return nil - }) - defer cbFailure.Release() + cbFailure := js.FuncOf(func(this js.Value, args []js.Value) any { + close(chEntry) + return nil + }) + defer cbFailure.Release() - chEntry = make(chan js.Value) - f.rootEntry.Call("getFile", name, nil, cbSuccess, cbFailure) - if entry := <-chEntry; entry.Truthy() { - return &file{entry: entry}, nil - } + chEntry = make(chan js.Value) + ent.Call("getFile", name, nil, cbSuccess, cbFailure) + if entry := <-chEntry; entry.Truthy() { + return &file{entry: entry}, nil + } - chEntry = make(chan js.Value) - f.rootEntry.Call("getDirectory", name, nil, cbSuccess, cbFailure) - if entry := <-chEntry; entry.Truthy() { - return &dir{dirEntry: entry}, nil + chEntry = make(chan js.Value) + ent.Call("getDirectory", name, nil, cbSuccess, cbFailure) + if entry := <-chEntry; entry.Truthy() { + return &dir{ + name: entry.Get("name").String(), + dirEntries: []js.Value{entry}, + }, nil + } } return nil, &fs.PathError{ @@ -163,21 +196,22 @@ func (f *file) Close() error { } type dir struct { - dirEntry js.Value + name string + dirEntries []js.Value fileEntries []js.Value offset int } func (d *dir) Stat() (fs.FileInfo, error) { return &fileInfo{ - name: d.dirEntry.Get("name").String(), + name: d.name, }, nil } func (d *dir) Read(buf []byte) (int, error) { return 0, &fs.PathError{ Op: "read", - Path: d.dirEntry.Get("name").String(), + Path: d.name, Err: errors.New("is a directory"), } } @@ -188,39 +222,48 @@ func (d *dir) Close() error { func (d *dir) ReadDir(count int) ([]fs.DirEntry, error) { if d.fileEntries == nil { - ch := make(chan struct{}) - var rec js.Func - cb := js.FuncOf(func(this js.Value, args []js.Value) any { - entries := args[0] - if entries.Length() == 0 { - close(ch) + names := map[string]struct{}{} + for _, dirEntry := range d.dirEntries { + ch := make(chan struct{}) + var rec js.Func + cb := js.FuncOf(func(this js.Value, args []js.Value) any { + entries := args[0] + if entries.Length() == 0 { + close(ch) + return nil + } + for i := 0; i < entries.Length(); i++ { + ent := entries.Index(i) + name := ent.Get("name").String() + // A name can be empty when this directory is a root directory. + if name == "" { + continue + } + // Avoid entry duplications. Entry duplications happen when multiple files are dropped on Chrome. + if _, ok := names[name]; ok { + continue + } + if !ent.Get("isFile").Bool() && !ent.Get("isDirectory").Bool() { + continue + } + d.fileEntries = append(d.fileEntries, ent) + names[name] = struct{}{} + } + rec.Value.Call("call") return nil - } - for i := 0; i < entries.Length(); i++ { - ent := entries.Index(i) - // A name can be empty when this directory is a root directory. - if ent.Get("name").String() == "" { - continue - } - if !ent.Get("isFile").Bool() && !ent.Get("isDirectory").Bool() { - continue - } - d.fileEntries = append(d.fileEntries, ent) - } + }) + defer cb.Release() + + reader := dirEntry.Call("createReader") + rec = js.FuncOf(func(this js.Value, args []js.Value) any { + reader.Call("readEntries", cb) + return nil + }) + defer rec.Release() + rec.Value.Call("call") - return nil - }) - defer cb.Release() - - reader := d.dirEntry.Call("createReader") - rec = js.FuncOf(func(this js.Value, args []js.Value) any { - reader.Call("readEntries", cb) - return nil - }) - defer rec.Release() - - rec.Value.Call("call") - <-ch + <-ch + } } n := len(d.fileEntries) - d.offset diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index 1b846c585..f3c90da44 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -707,14 +707,21 @@ func (u *UserInterface) appendDroppedFiles(data js.Value) { defer u.dropFileM.Unlock() items := data.Get("items") + var entries []js.Value for i := 0; i < items.Length(); i++ { kind := items.Index(i).Get("kind").String() switch kind { case "file": - fs := items.Index(i).Call("webkitGetAsEntry").Get("filesystem").Get("root") - u.inputState.DroppedFiles = file.NewFileEntryFS(fs) + entries = append(entries, items.Index(i).Call("webkitGetAsEntry").Get("filesystem").Get("root")) + } + } + if len(entries) > 0 { + fs, err := file.NewFileEntryFS(entries) + if err != nil { + u.setError(err) return } + u.inputState.DroppedFiles = fs } } From 3106f98b52642aa4fef984d7a4703b8f7385940f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 31 Jul 2024 21:35:20 +0900 Subject: [PATCH 010/188] internal/graphicsdriver/opengl/gl: add more error messages to load libraries --- internal/graphicsdriver/opengl/gl/procaddr_linbsd.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go index 63527eed4..c41c18ef8 100644 --- a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go +++ b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go @@ -45,6 +45,9 @@ func (c *defaultContext) init() error { } } + // TODO: Use multiple %w-s as of Go 1.20. + var errors []string + // Try OpenGL first. OpenGL is preferable as this doesn't cause context losses. if !preferES { // Usually libGL.so or libGL.so.1 is used. libGL.so.2 might exist only on NetBSD. @@ -57,6 +60,7 @@ func (c *defaultContext) init() error { libGL = lib return nil } + errors = append(errors, fmt.Sprintf("%s: %v", name, err)) } } @@ -68,9 +72,10 @@ func (c *defaultContext) init() error { c.isES = true return nil } + errors = append(errors, fmt.Sprintf("%s: %v", name, err)) } - return fmt.Errorf("gl: failed to load libGL.so and libGLESv2.so") + return fmt.Errorf("gl: failed to load libGL.so and libGLESv2.so: %s", strings.Join(errors, ", ")) } func (c *defaultContext) getProcAddress(name string) (uintptr, error) { From 856b339298bc19ccd814c8ed2dd283ab4a028e2d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 31 Jul 2024 23:54:16 +0900 Subject: [PATCH 011/188] internal/ui: bug fix: show a black dot at the red button when needed When SetWindowClosingHandled(true) is called, a black dot should be shown at the red button to close the window. Closes #3020 --- internal/ui/ui_darwin.go | 11 +++++++++++ internal/ui/ui_glfw.go | 23 +++++++++++++++++++++++ internal/ui/ui_linbsd.go | 4 ++++ internal/ui/ui_windows.go | 4 ++++ 4 files changed, 42 insertions(+) diff --git a/internal/ui/ui_darwin.go b/internal/ui/ui_darwin.go index f1fd13f3f..ed4ff8fe6 100644 --- a/internal/ui/ui_darwin.go +++ b/internal/ui/ui_darwin.go @@ -238,6 +238,7 @@ var ( sel_origResizable = objc.RegisterName("isOrigResizable") sel_setCollectionBehavior = objc.RegisterName("setCollectionBehavior:") sel_setDelegate = objc.RegisterName("setDelegate:") + sel_setDocumentEdited = objc.RegisterName("setDocumentEdited:") sel_setOrigDelegate = objc.RegisterName("setOrigDelegate:") sel_setOrigResizable = objc.RegisterName("setOrigResizable:") sel_toggleFullScreen = objc.RegisterName("toggleFullScreen:") @@ -433,3 +434,13 @@ func initializeWindowAfterCreation(w *glfw.Window) error { func (u *UserInterface) skipTaskbar() error { return nil } + +// setDocumentEdited must be called from the main thread. +func (u *UserInterface) setDocumentEdited(edited bool) error { + w, err := u.window.GetCocoaWindow() + if err != nil { + return err + } + objc.ID(w).Send(sel_setDocumentEdited, edited) + return nil +} diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 368d7a102..28dc21df3 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -568,6 +568,22 @@ func (u *UserInterface) setWindowClosingHandled(handled bool) { u.m.Lock() u.windowClosingHandled = handled u.m.Unlock() + + if !u.isRunning() { + return + } + if u.isTerminated() { + return + } + u.mainThread.Call(func() { + if u.isTerminated() { + return + } + if err := u.setDocumentEdited(handled); err != nil { + u.setError(err) + return + } + }) } // isFullscreen must be called from the main thread. @@ -874,6 +890,13 @@ func (u *UserInterface) createWindow() error { return err } + u.m.Lock() + closingHandled := u.windowClosingHandled + u.m.Unlock() + if err := u.setDocumentEdited(closingHandled); err != nil { + return err + } + return nil } diff --git a/internal/ui/ui_linbsd.go b/internal/ui/ui_linbsd.go index 378f27f35..7d07f2bc3 100644 --- a/internal/ui/ui_linbsd.go +++ b/internal/ui/ui_linbsd.go @@ -200,3 +200,7 @@ func initializeWindowAfterCreation(w *glfw.Window) error { func (u *UserInterface) skipTaskbar() error { return nil } + +func (u *UserInterface) setDocumentEdited(edited bool) error { + return nil +} diff --git a/internal/ui/ui_windows.go b/internal/ui/ui_windows.go index 4d5d821ef..b9bdf9ef0 100644 --- a/internal/ui/ui_windows.go +++ b/internal/ui/ui_windows.go @@ -242,6 +242,10 @@ func (u *UserInterface) skipTaskbar() error { return nil } +func (u *UserInterface) setDocumentEdited(edited bool) error { + return nil +} + func init() { if microsoftgdk.IsXbox() { // TimeBeginPeriod might not be defined in Xbox. From 297efea68b3f689687c134bd9f30fc1d9a7b91c0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 1 Aug 2024 03:50:14 +0900 Subject: [PATCH 012/188] internal/graphicsdriver/opengl/gl: use C version for Android Unfortunately PureGo's dlopen didn't work on some Android devices. Use the Cgo version instead in the same way as v2.7. Closes #3052 --- .../graphicsdriver/opengl/gl/default_cgo.go | 2 +- .../opengl/gl/default_purego.go | 2 +- .../opengl/gl/procaddr_android.go | 67 +++++++++++++++++++ .../opengl/gl/procaddr_linbsd.go | 18 ++--- 4 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 internal/graphicsdriver/opengl/gl/procaddr_android.go diff --git a/internal/graphicsdriver/opengl/gl/default_cgo.go b/internal/graphicsdriver/opengl/gl/default_cgo.go index 234f9a707..a1304285d 100644 --- a/internal/graphicsdriver/opengl/gl/default_cgo.go +++ b/internal/graphicsdriver/opengl/gl/default_cgo.go @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2014 Eric Woroshow // SPDX-FileCopyrightText: 2022 The Ebitengine Authors -//go:build nintendosdk +//go:build android || nintendosdk package gl diff --git a/internal/graphicsdriver/opengl/gl/default_purego.go b/internal/graphicsdriver/opengl/gl/default_purego.go index 1e18d5730..fb2e09c25 100644 --- a/internal/graphicsdriver/opengl/gl/default_purego.go +++ b/internal/graphicsdriver/opengl/gl/default_purego.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (darwin || freebsd || linux || netbsd || openbsd || windows) && !nintendosdk && !playstation5 +//go:build (darwin || freebsd || (linux && !android) || netbsd || openbsd || windows) && !nintendosdk && !playstation5 package gl diff --git a/internal/graphicsdriver/opengl/gl/procaddr_android.go b/internal/graphicsdriver/opengl/gl/procaddr_android.go new file mode 100644 index 000000000..32f991c9b --- /dev/null +++ b/internal/graphicsdriver/opengl/gl/procaddr_android.go @@ -0,0 +1,67 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gl + +// Unfortunately, PureGo's dlopen didn't work well on some Android devices (#3052). +// Use Cgo instead until PureGo is fixed. + +// #include +// #include +import "C" + +import ( + "fmt" + "strings" + "unsafe" +) + +var ( + libGLES unsafe.Pointer +) + +func (c *defaultContext) init() error { + // TODO: Use multiple %w-s as of Go 1.20. + var errors []string + + // Try OpenGL ES. + for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL) + if lib != nil { + libGLES = lib + c.isES = true + return nil + } + if cerr := C.dlerror(); cerr != nil { + errors = append(errors, fmt.Sprintf("%s: %v", name, C.GoString(cerr))) + } + } + + return fmt.Errorf("gl: failed to load libGLESv2.so: %s", strings.Join(errors, ", ")) +} + +func (c *defaultContext) getProcAddress(name string) (uintptr, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + addr := C.dlsym(libGLES, cname) + if addr == nil { + if cerr := C.dlerror(); cerr != nil { + return 0, fmt.Errorf("gl: failed to load %s: %v", name, C.GoString(cerr)) + } + return 0, fmt.Errorf("gl: failed to load %s", name) + } + return uintptr(addr), nil +} diff --git a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go index c41c18ef8..51e15e44b 100644 --- a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go +++ b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || linux || netbsd || openbsd) && !nintendosdk && !playstation5 +//go:build (freebsd || (linux && !android) || netbsd || openbsd) && !nintendosdk && !playstation5 package gl import ( "fmt" "os" - "runtime" "strings" "github.com/ebitengine/purego" @@ -32,16 +31,11 @@ var ( func (c *defaultContext) init() error { var preferES bool - if runtime.GOOS == "android" { - preferES = true - } - if !preferES { - for _, t := range strings.Split(os.Getenv("EBITENGINE_OPENGL"), ",") { - switch strings.TrimSpace(t) { - case "es": - preferES = true - break - } + for _, t := range strings.Split(os.Getenv("EBITENGINE_OPENGL"), ",") { + switch strings.TrimSpace(t) { + case "es": + preferES = true + break } } From 1fc2dedaaa6289fdafe6f5d3d9d7537fc6740cd8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 2 Aug 2024 13:33:55 +0900 Subject: [PATCH 013/188] all: update gomobile --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 68d099c16..f5793f152 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/hajimehoshi/ebiten/v2 go 1.19 require ( - github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 + github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.3 github.com/ebitengine/purego v0.8.0-alpha.3 diff --git a/go.sum b/go.sum index a8d60ad61..ec8619f5a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU= -github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= +github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48tiQRlrr71NsbfxBcFM9/8bEHS9/JbsQg= +github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.3 h1:L8Odh8gVr4F+0CzSfqOfw/nEnXXWkB+UhGOKUYrP+Nk= From 719838b7ab14c162b393b744cae2647169d35990 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 2 Aug 2024 15:27:53 +0900 Subject: [PATCH 014/188] .github/workflows: update wasmbrowsertest to fix timeout issue The issue agnivade/wasmbrowsertest#60 was fixed. Updates #1313 --- .github/workflows/test.yml | 2 +- audio/audio_test.go | 9 --------- audio/internal/convert/float32_test.go | 10 ---------- internal/shader/shader_test.go | 8 -------- 4 files changed, 1 insertion(+), 28 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 81ee0f219..516d16569 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,7 @@ jobs: - name: Install wasmbrowsertest run: | - wasmbrowsertest_version=6e494bb3a5ddfe6cccb449250dbdcaa5777b593d + wasmbrowsertest_version=6e5bbb88049c42eb62e19d10e5be9940b9271aab go install github.com/agnivade/wasmbrowsertest@${wasmbrowsertest_version} mv $(go env GOPATH)/bin/wasmbrowsertest${{ runner.os == 'Windows' && '.exe' || '' }} $(go env GOPATH)/bin/go_js_wasm_exec${{ runner.os == 'Windows' && '.exe' || '' }} go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@${wasmbrowsertest_version} diff --git a/audio/audio_test.go b/audio/audio_test.go index 7ce175fb9..a238feadc 100644 --- a/audio/audio_test.go +++ b/audio/audio_test.go @@ -17,7 +17,6 @@ package audio_test import ( "bytes" "io" - "os" "runtime" "testing" "time" @@ -36,14 +35,6 @@ func teardown() { context = nil } -func TestMain(m *testing.M) { - code := m.Run() - // 200[ms] should be enough all the players are consumed. - // TODO: This is a dirty hack. Would it be possible to use virtual time? - time.Sleep(200 * time.Millisecond) - os.Exit(code) -} - // Issue #746 func TestGC(t *testing.T) { setup() diff --git a/audio/internal/convert/float32_test.go b/audio/internal/convert/float32_test.go index c2240c922..268459e19 100644 --- a/audio/internal/convert/float32_test.go +++ b/audio/internal/convert/float32_test.go @@ -18,22 +18,12 @@ import ( "bytes" "io" "math/rand" // TODO: Use math/rand/v2 when the minimum supported version becomes Go 1.22. - "os" "testing" - "time" "unsafe" "github.com/hajimehoshi/ebiten/v2/audio/internal/convert" ) -func TestMain(m *testing.M) { - code := m.Run() - // Tests in this package often fails on GitHub Actions due to unfinished goroutines. - // That's mysterious, but to avoid this, sleep for a while before exiting. - time.Sleep(200 * time.Millisecond) - os.Exit(code) -} - func randInt16s(n int) []int16 { r := make([]int16, n) for i := range r { diff --git a/internal/shader/shader_test.go b/internal/shader/shader_test.go index fd182b0a1..954ba099d 100644 --- a/internal/shader/shader_test.go +++ b/internal/shader/shader_test.go @@ -21,7 +21,6 @@ import ( "runtime" "strings" "testing" - "time" "github.com/hajimehoshi/ebiten/v2/internal/shader" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" @@ -30,13 +29,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl" ) -func TestMain(m *testing.M) { - code := m.Run() - // The Wasm tests on GitHub CI often fail due to some remaining functions. Wait for a while to finish them. - time.Sleep(100 * time.Millisecond) - os.Exit(code) -} - func glslVertexNormalize(str string) string { p := glsl.VertexPrelude(glsl.GLSLVersionDefault) if strings.HasPrefix(str, p) { From 7c4f532b836a514057346c63c0096cfc7b68663a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 3 Aug 2024 19:03:40 +0900 Subject: [PATCH 015/188] internal/shader: bug fix: an index must be a constant for vectors Updates #3011 --- internal/shader/expr.go | 5 ++++ internal/shader/syntax_test.go | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/internal/shader/expr.go b/internal/shader/expr.go index 286e856a5..2941fb0d4 100644 --- a/internal/shader/expr.go +++ b/internal/shader/expr.go @@ -1150,6 +1150,11 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar x := exprs[0] t := ts[0] + if (t.IsFloatVector() || t.IsIntVector()) && idx.Const == nil { + cs.addError(e.Pos(), fmt.Sprintf("index must be a constant for the type %s", t.String())) + return nil, nil, nil, false + } + var typ shaderir.Type switch t.Main { case shaderir.Vec2, shaderir.Vec3, shaderir.Vec4: diff --git a/internal/shader/syntax_test.go b/internal/shader/syntax_test.go index b865bfb8c..f258cbd81 100644 --- a/internal/shader/syntax_test.go +++ b/internal/shader/syntax_test.go @@ -4363,3 +4363,47 @@ func Foo() int { t.Error(err) } } + +func TestSyntaxIndex(t *testing.T) { + // Issue #3011 + if _, err := compileToIR([]byte(`package main + +func Foo() int { + var a int + var b ivec4 + return b[a] +} +`)); err == nil { + t.Error("compileToIR must return an error but did not") + } + if _, err := compileToIR([]byte(`package main + +func Foo() float { + var a int + var b mat4 + return b[a][0] +} +`)); err != nil { + t.Error(err) + } + if _, err := compileToIR([]byte(`package main + +func Foo() int { + const a = 0 + var b ivec4 + return b[a] +} +`)); err != nil { + t.Error(err) + } + if _, err := compileToIR([]byte(`package main + +func Foo() float { + const a = 0 + var b mat4 + return b[a][0] +} +`)); err != nil { + t.Error(err) + } +} From af6072c1b4e2ba6f491b215092795c7687ea81e5 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 3 Aug 2024 19:04:53 +0900 Subject: [PATCH 016/188] internal/shader: add more tests Updates #3011 --- internal/shader/syntax_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/internal/shader/syntax_test.go b/internal/shader/syntax_test.go index f258cbd81..16bd0885b 100644 --- a/internal/shader/syntax_test.go +++ b/internal/shader/syntax_test.go @@ -4368,6 +4368,16 @@ func TestSyntaxIndex(t *testing.T) { // Issue #3011 if _, err := compileToIR([]byte(`package main +func Foo() float { + var a int + var b vec4 + return b[a] +} +`)); err == nil { + t.Error("compileToIR must return an error but did not") + } + if _, err := compileToIR([]byte(`package main + func Foo() int { var a int var b ivec4 @@ -4388,6 +4398,16 @@ func Foo() float { } if _, err := compileToIR([]byte(`package main +func Foo() float { + const a = 0 + var b vec4 + return b[a] +} +`)); err != nil { + t.Error(err) + } + if _, err := compileToIR([]byte(`package main + func Foo() int { const a = 0 var b ivec4 From 37a60572304d40149a11c8b72bd7f3e2a453e0be Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 4 Aug 2024 04:15:59 +0900 Subject: [PATCH 017/188] internal/ui: bug fix: increase maxSkipCount Closes #3055 --- internal/ui/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ui/context.go b/internal/ui/context.go index 9af591f6f..f7d2cb607 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -192,7 +192,7 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, ui *UserInter return err } - const maxSkipCount = 3 + const maxSkipCount = 4 if !forceDraw && !c.isOffscreenModified { if c.skipCount < maxSkipCount { From 9bd17de2d3785db78a874efe803be6f044b541c5 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 4 Aug 2024 04:26:53 +0900 Subject: [PATCH 018/188] internal/graphicsdriver/opengl: remove unused members --- internal/graphicsdriver/opengl/context.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/graphicsdriver/opengl/context.go b/internal/graphicsdriver/opengl/context.go index ff776ec85..858bc2eb6 100644 --- a/internal/graphicsdriver/opengl/context.go +++ b/internal/graphicsdriver/opengl/context.go @@ -90,7 +90,6 @@ type ( type ( uniformLocation int32 - attribLocation int32 ) const ( @@ -111,8 +110,6 @@ type context struct { lastBlend graphicsdriver.Blend maxTextureSize int maxTextureSizeOnce sync.Once - highp bool - highpOnce sync.Once initOnce sync.Once } From 6555375b0631cad252fd67952964ac76a30e1fd9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 4 Aug 2024 19:06:59 +0900 Subject: [PATCH 019/188] all: add .clang-format --- .clang-format | 1 + 1 file changed, 1 insertion(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..3da6bd35a --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +CommentPragmas: '^go:build' From aba652c323b445d2c8db2a9d4a3372f6cdfc71f6 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 4 Aug 2024 19:09:35 +0900 Subject: [PATCH 020/188] internal/graphicsdriver/playstation5: add DrawTriangles --- .../playstation5/graphics_playstation5.cpp | 22 ++++---- .../playstation5/graphics_playstation5.go | 47 ++++++++++++++++ .../playstation5/graphics_playstation5.h | 54 ++++++++++++++++--- 3 files changed, 106 insertions(+), 17 deletions(-) diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp index 2d95896bc..e1fce3fb8 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp @@ -18,24 +18,28 @@ #include "graphics_playstation5.h" -extern "C" ebitengine_Error ebitengine_InitializeGraphics(void) { +extern "C" ebitengine_Error ebitengine_InitializeGraphics(void) { return {}; } + +extern "C" ebitengine_Error ebitengine_NewImage(int *image, int width, + int height) { return {}; } -extern "C" ebitengine_Error ebitengine_NewImage(int* image, int width, int height) { +extern "C" ebitengine_Error +ebitengine_NewScreenFramebufferImage(int *image, int width, int height) { return {}; } -extern "C" ebitengine_Error ebitengine_NewScreenFramebufferImage(int* image, int width, int height) { +extern "C" void ebitengine_DisposeImage(int id) {} + +extern "C" ebitengine_Error +ebitengine_DrawTriangles(ebitengine_DrawTrianglesArgs *args) { return {}; } -extern "C" void ebitengine_DisposeImage(int id) { -} - -extern "C" ebitengine_Error ebitengine_NewShader(int* shader, const char* source) { +extern "C" ebitengine_Error ebitengine_NewShader(int *shader, + const char *source) { return {}; } -extern "C" void ebitengine_DisposeShader(int id) { -} +extern "C" void ebitengine_DisposeShader(int id) {} diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.go b/internal/graphicsdriver/playstation5/graphics_playstation5.go index 63cd09091..187240b42 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.go +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.go @@ -21,6 +21,7 @@ import "C" import ( "fmt" + "runtime" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" @@ -117,6 +118,52 @@ func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader, } func (g *Graphics) DrawTriangles(dst graphicsdriver.ImageID, srcs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shader graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error { + csrcs := make([]C.int, len(srcs)) + for i, src := range srcs { + csrcs[i] = C.int(src) + } + cDstRegions := make([]C.ebitengine_DstRegion, len(dstRegions)) + for i, r := range dstRegions { + cDstRegions[i] = C.ebitengine_DstRegion{ + Region: C.ebitengine_Rectangle{ + MinX: C.int(r.Region.Min.X), + MinY: C.int(r.Region.Min.Y), + MaxX: C.int(r.Region.Max.X), + MaxY: C.int(r.Region.Max.Y), + }, + IndexCount: C.int(r.IndexCount), + } + } + cUniforms := make([]C.uint32_t, len(uniforms)) + for i, u := range uniforms { + cUniforms[i] = C.uint32_t(u) + } + cBlend := C.ebitengine_Blend{ + BlendFactorSourceRGB: C.uint8_t(blend.BlendFactorSourceRGB), + BlendFactorSourceAlpha: C.uint8_t(blend.BlendFactorSourceAlpha), + BlendFactorDestinationRGB: C.uint8_t(blend.BlendFactorDestinationRGB), + BlendFactorDestinationAlpha: C.uint8_t(blend.BlendFactorDestinationAlpha), + BlendOperationRGB: C.uint8_t(blend.BlendOperationRGB), + BlendOperationAlpha: C.uint8_t(blend.BlendOperationAlpha), + } + + args := C.ebitengine_DrawTrianglesArgs{ + Dst: C.int(dst), + Srcs: &csrcs[0], + SrcCount: C.int(len(csrcs)), + Shader: C.int(shader), + DstRegions: &cDstRegions[0], + DstRegionCount: C.int(len(cDstRegions)), + IndexOffset: C.int(indexOffset), + Blend: cBlend, + Uniforms: &cUniforms[0], + UniformCount: C.int(len(cUniforms)), + FillRule: C.int(fillRule), + } + if err := C.ebitengine_DrawTriangles(&args); !C.ebitengine_IsErrorNil(&err) { + return newPlaystation5Error("(*playstation5.Graphics).DrawTriangles", err) + } + runtime.KeepAlive(args) return nil } diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.h b/internal/graphicsdriver/playstation5/graphics_playstation5.h index 8299eff5e..6e0f780b5 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.h +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.h @@ -19,26 +19,64 @@ #include #include +#include #ifdef __cplusplus extern "C" { #endif typedef struct ebitengine_Error { - const char* Message; - int Code; + const char *Message; + int Code; } ebitengine_Error; -static bool ebitengine_IsErrorNil(ebitengine_Error* err) { +static bool ebitengine_IsErrorNil(ebitengine_Error *err) { return err->Message == NULL && err->Code == 0; } -ebitengine_Error ebitengine_InitializeGraphics(void); -ebitengine_Error ebitengine_NewImage(int* image, int width, int height); -ebitengine_Error ebitengine_NewScreenFramebufferImage(int* image, int width, int height); -void ebitengine_DisposeImage(int id); +typedef struct ebitengine_Rectangle { + int MinX; + int MinY; + int MaxX; + int MaxY; +} ebitengine_Rectangle; -ebitengine_Error ebitengine_NewShader(int* shader, const char* source); +typedef struct ebitengine_DstRegion { + ebitengine_Rectangle Region; + int IndexCount; +} ebitengine_DstRegion; + +typedef struct ebitengine_Blend { + uint8_t BlendFactorSourceRGB; + uint8_t BlendFactorSourceAlpha; + uint8_t BlendFactorDestinationRGB; + uint8_t BlendFactorDestinationAlpha; + uint8_t BlendOperationRGB; + uint8_t BlendOperationAlpha; +} ebitengine_Blend; + +typedef struct ebitengine_DrawTrianglesArgs { + int Dst; + int *Srcs; + int SrcCount; + int Shader; + ebitengine_DstRegion *DstRegions; + int DstRegionCount; + int IndexOffset; + ebitengine_Blend Blend; + uint32_t *Uniforms; + int UniformCount; + int FillRule; +} ebitengine_DrawTrianglesArgs; + +ebitengine_Error ebitengine_InitializeGraphics(void); +ebitengine_Error ebitengine_NewImage(int *image, int width, int height); +ebitengine_Error ebitengine_NewScreenFramebufferImage(int *image, int width, + int height); +void ebitengine_DisposeImage(int id); +ebitengine_Error ebitengine_DrawTriangles(ebitengine_DrawTrianglesArgs *args); + +ebitengine_Error ebitengine_NewShader(int *shader, const char *source); void ebitengine_DisposeShader(int id); #ifdef __cplusplus From 1e3ab9e5fca3ca3925c20f18cb7607e124006d46 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 5 Aug 2024 21:22:53 +0900 Subject: [PATCH 021/188] all: update dependencies --- go.mod | 4 ++-- go.sum | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index f5793f152..d025ee273 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,8 @@ require ( github.com/jfreymuth/oggvorbis v1.0.5 github.com/kisielk/errcheck v1.7.0 golang.org/x/image v0.18.0 - golang.org/x/sync v0.7.0 - golang.org/x/sys v0.22.0 + golang.org/x/sync v0.8.0 + golang.org/x/sys v0.23.0 golang.org/x/text v0.16.0 golang.org/x/tools v0.23.0 ) diff --git a/go.sum b/go.sum index ec8619f5a..1aee198c0 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,9 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -73,8 +74,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 2a34d1d47b8732a2e29ef5a2a5120f6d585c2eea Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 5 Aug 2024 21:18:51 +0900 Subject: [PATCH 022/188] internal/graphicsdriver/opengl/gl: use PureGo version for Android again This is basically a revert for 297efea68b3f689687c134bd9f30fc1d9a7b91c0. This updates PureGo for the Android issue. Updates #3052 Updates ebitengine/purego#262 --- go.mod | 2 +- go.sum | 4 +- .../graphicsdriver/opengl/gl/default_cgo.go | 2 +- .../opengl/gl/default_purego.go | 2 +- .../opengl/gl/procaddr_android.go | 67 ------------------- .../opengl/gl/procaddr_linbsd.go | 18 +++-- 6 files changed, 17 insertions(+), 78 deletions(-) delete mode 100644 internal/graphicsdriver/opengl/gl/procaddr_android.go diff --git a/go.mod b/go.mod index d025ee273..d558705ba 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.3 - github.com/ebitengine/purego v0.8.0-alpha.3 + github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.3 diff --git a/go.sum b/go.sum index 1aee198c0..b5d558d53 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.3 h1:L8Odh8gVr4F+0CzSfqOfw/nEnXXWkB+UhGOKUYrP+Nk= github.com/ebitengine/oto/v3 v3.3.0-alpha.3/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= -github.com/ebitengine/purego v0.8.0-alpha.3 h1:qoFlpGuVwJ6J85kuj6Qpyp0DBgxsNYfSY9efidSNFgA= -github.com/ebitengine/purego v0.8.0-alpha.3/go.mod h1:b94LtM1jUWDZPKDyENVhB0WsLdLWFApjbNw5AyxmKyI= +github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 h1:vXEgFw8Ni26tlWLmeI8nFXa7pMLKUTR9hfXcQPCYpQg= +github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38= github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= diff --git a/internal/graphicsdriver/opengl/gl/default_cgo.go b/internal/graphicsdriver/opengl/gl/default_cgo.go index a1304285d..234f9a707 100644 --- a/internal/graphicsdriver/opengl/gl/default_cgo.go +++ b/internal/graphicsdriver/opengl/gl/default_cgo.go @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2014 Eric Woroshow // SPDX-FileCopyrightText: 2022 The Ebitengine Authors -//go:build android || nintendosdk +//go:build nintendosdk package gl diff --git a/internal/graphicsdriver/opengl/gl/default_purego.go b/internal/graphicsdriver/opengl/gl/default_purego.go index fb2e09c25..1e18d5730 100644 --- a/internal/graphicsdriver/opengl/gl/default_purego.go +++ b/internal/graphicsdriver/opengl/gl/default_purego.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (darwin || freebsd || (linux && !android) || netbsd || openbsd || windows) && !nintendosdk && !playstation5 +//go:build (darwin || freebsd || linux || netbsd || openbsd || windows) && !nintendosdk && !playstation5 package gl diff --git a/internal/graphicsdriver/opengl/gl/procaddr_android.go b/internal/graphicsdriver/opengl/gl/procaddr_android.go deleted file mode 100644 index 32f991c9b..000000000 --- a/internal/graphicsdriver/opengl/gl/procaddr_android.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gl - -// Unfortunately, PureGo's dlopen didn't work well on some Android devices (#3052). -// Use Cgo instead until PureGo is fixed. - -// #include -// #include -import "C" - -import ( - "fmt" - "strings" - "unsafe" -) - -var ( - libGLES unsafe.Pointer -) - -func (c *defaultContext) init() error { - // TODO: Use multiple %w-s as of Go 1.20. - var errors []string - - // Try OpenGL ES. - for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL) - if lib != nil { - libGLES = lib - c.isES = true - return nil - } - if cerr := C.dlerror(); cerr != nil { - errors = append(errors, fmt.Sprintf("%s: %v", name, C.GoString(cerr))) - } - } - - return fmt.Errorf("gl: failed to load libGLESv2.so: %s", strings.Join(errors, ", ")) -} - -func (c *defaultContext) getProcAddress(name string) (uintptr, error) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - addr := C.dlsym(libGLES, cname) - if addr == nil { - if cerr := C.dlerror(); cerr != nil { - return 0, fmt.Errorf("gl: failed to load %s: %v", name, C.GoString(cerr)) - } - return 0, fmt.Errorf("gl: failed to load %s", name) - } - return uintptr(addr), nil -} diff --git a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go index 51e15e44b..c41c18ef8 100644 --- a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go +++ b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || (linux && !android) || netbsd || openbsd) && !nintendosdk && !playstation5 +//go:build (freebsd || linux || netbsd || openbsd) && !nintendosdk && !playstation5 package gl import ( "fmt" "os" + "runtime" "strings" "github.com/ebitengine/purego" @@ -31,11 +32,16 @@ var ( func (c *defaultContext) init() error { var preferES bool - for _, t := range strings.Split(os.Getenv("EBITENGINE_OPENGL"), ",") { - switch strings.TrimSpace(t) { - case "es": - preferES = true - break + if runtime.GOOS == "android" { + preferES = true + } + if !preferES { + for _, t := range strings.Split(os.Getenv("EBITENGINE_OPENGL"), ",") { + switch strings.TrimSpace(t) { + case "es": + preferES = true + break + } } } From 8f36a479a38c85cb281958a6bf05069f9700a52f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 6 Aug 2024 02:45:07 +0900 Subject: [PATCH 023/188] all: update Oto Updates ebitengine/oto#242 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d558705ba..e4df5436f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.3 + github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240805173845-e73becceb8f0 github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index b5d558d53..ffcd94288 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48 github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3 h1:L8Odh8gVr4F+0CzSfqOfw/nEnXXWkB+UhGOKUYrP+Nk= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240805173845-e73becceb8f0 h1:HQAdEB1MSgkOvYAiTExKk29fOTAlHw+uGiBpC57O7fo= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240805173845-e73becceb8f0/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 h1:vXEgFw8Ni26tlWLmeI8nFXa7pMLKUTR9hfXcQPCYpQg= github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From 72f50c389b8dc79ad05d5185858e1143fb489c1c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 6 Aug 2024 03:11:48 +0900 Subject: [PATCH 024/188] Revert "all: update Oto" This reverts commit 8f36a479a38c85cb281958a6bf05069f9700a52f. Reason: crash on tests https://github.com/ebitengine/oto/actions/runs/10253513865/job/28366298117 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e4df5436f..d558705ba 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240805173845-e73becceb8f0 + github.com/ebitengine/oto/v3 v3.3.0-alpha.3 github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index ffcd94288..b5d558d53 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48 github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240805173845-e73becceb8f0 h1:HQAdEB1MSgkOvYAiTExKk29fOTAlHw+uGiBpC57O7fo= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240805173845-e73becceb8f0/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3 h1:L8Odh8gVr4F+0CzSfqOfw/nEnXXWkB+UhGOKUYrP+Nk= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 h1:vXEgFw8Ni26tlWLmeI8nFXa7pMLKUTR9hfXcQPCYpQg= github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From 13353dc4658383aea9baf06b102e46577fefc592 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 6 Aug 2024 13:21:57 +0900 Subject: [PATCH 025/188] all: update Oto Updates ebitengine/oto#242 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d558705ba..96b46b6fa 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.3 + github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index b5d558d53..b0dd5eb1b 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48 github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3 h1:L8Odh8gVr4F+0CzSfqOfw/nEnXXWkB+UhGOKUYrP+Nk= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f h1:1a7SoSH0DOZEIRXcWNRCAYV2dj9POTlyqi7zKrmhcTM= +github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 h1:vXEgFw8Ni26tlWLmeI8nFXa7pMLKUTR9hfXcQPCYpQg= github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From 74722298a2cf115a096ad1d368d1f1e92792e667 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 7 Aug 2024 23:33:54 +0900 Subject: [PATCH 026/188] internal/debug: rename functions --- internal/atlas/image.go | 2 +- internal/debug/debug.go | 5 ++-- internal/debug/debug_ebitenginedebug.go | 30 ++++++++++++---------- internal/debug/debug_notebitenginedebug.go | 12 ++++----- internal/gamepad/gamepad_linux.go | 3 +++ internal/graphicscommand/commandqueue.go | 8 +++--- internal/graphicscommand/image.go | 2 +- internal/ui/context.go | 4 +-- 8 files changed, 36 insertions(+), 30 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index cf21e3dfd..da6943789 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -866,7 +866,7 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { }() if debug.IsDebug { - debug.Logf("Internal image sizes:\n") + debug.FrameLogf("Internal image sizes:\n") imgs := make([]*graphicscommand.Image, 0, len(theBackends)) for _, backend := range theBackends { imgs = append(imgs, backend.image) diff --git a/internal/debug/debug.go b/internal/debug/debug.go index 93cd8d1d6..f0c6e1d60 100644 --- a/internal/debug/debug.go +++ b/internal/debug/debug.go @@ -14,7 +14,8 @@ package debug -type Logger interface { - Logf(format string, args ...any) +// FrameLogger defines the interface for logging debug information for each frame. +type FrameLogger interface { + FrameLogf(format string, args ...any) Flush() } diff --git a/internal/debug/debug_ebitenginedebug.go b/internal/debug/debug_ebitenginedebug.go index 8f6ae144d..853481ee0 100644 --- a/internal/debug/debug_ebitenginedebug.go +++ b/internal/debug/debug_ebitenginedebug.go @@ -23,30 +23,32 @@ import ( const IsDebug = true -var theLogger = &logger{} +var theFrameLogger = &frameLogger{} var flushM sync.Mutex -// Logf calls the current global logger's Logf. -// Logf buffers the arguments and doesn't dump the log immediately. +// FrameLogf calls the current global logger's FrameLogf. +// FrameLogf buffers the arguments and doesn't dump the log immediately. // You can dump logs by calling SwitchLogger and Flush. // -// Logf is not concurrent safe. -func Logf(format string, args ...any) { - theLogger.Logf(format, args...) +// FrameLogf is not concurrent safe. +// FrameLogf and SwitchFrameLogger must be called from the same goroutine. +func FrameLogf(format string, args ...any) { + theFrameLogger.FrameLogf(format, args...) } -// SwitchLogger sets a new logger as the current logger and returns the original global logger. +// SwitchFrameLogger sets a new logger as the current logger and returns the original global logger. // The new global logger and the returned logger have separate statuses, so you can use them for different goroutines. // -// SwitchLogger and a returned Logger are not concurrent safe. -func SwitchLogger() Logger { - current := theLogger - theLogger = &logger{} +// SwitchFrameLogger and a returned Logger are not concurrent safe. +// FrameLogf and SwitchFrameLogger must be called from the same goroutine. +func SwitchFrameLogger() FrameLogger { + current := theFrameLogger + theFrameLogger = &frameLogger{} return current } -type logger struct { +type frameLogger struct { items []logItem } @@ -55,14 +57,14 @@ type logItem struct { args []any } -func (l *logger) Logf(format string, args ...any) { +func (l *frameLogger) FrameLogf(format string, args ...any) { l.items = append(l.items, logItem{ format: format, args: args, }) } -func (l *logger) Flush() { +func (l *frameLogger) Flush() { // Flushing is protected by a mutex not to mix another logger's logs. flushM.Lock() defer flushM.Unlock() diff --git a/internal/debug/debug_notebitenginedebug.go b/internal/debug/debug_notebitenginedebug.go index 7f7c7509d..e8fc57030 100644 --- a/internal/debug/debug_notebitenginedebug.go +++ b/internal/debug/debug_notebitenginedebug.go @@ -18,17 +18,17 @@ package debug const IsDebug = false -func Logf(format string, args ...any) { +func FrameLogf(format string, args ...any) { } -func SwitchLogger() Logger { - return dummyLogger{} +func SwitchFrameLogger() FrameLogger { + return dummyFrameLogger{} } -type dummyLogger struct{} +type dummyFrameLogger struct{} -func (dummyLogger) Logf(format string, args ...any) { +func (dummyFrameLogger) FrameLogf(format string, args ...any) { } -func (dummyLogger) Flush() { +func (dummyFrameLogger) Flush() { } diff --git a/internal/gamepad/gamepad_linux.go b/internal/gamepad/gamepad_linux.go index d97d43970..17fd00a13 100644 --- a/internal/gamepad/gamepad_linux.go +++ b/internal/gamepad/gamepad_linux.go @@ -54,6 +54,9 @@ func (g *nativeGamepadsImpl) init(gamepads *gamepads) error { if err == unix.ENOENT { return nil } + if err == unix.EPERM { + return nil + } return fmt.Errorf("gamepad: Stat failed: %w", err) } if stat.Mode&unix.S_IFDIR == 0 { diff --git a/internal/graphicscommand/commandqueue.go b/internal/graphicscommand/commandqueue.go index 0f24dbf42..7176cb240 100644 --- a/internal/graphicscommand/commandqueue.go +++ b/internal/graphicscommand/commandqueue.go @@ -197,7 +197,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo } } - logger := debug.SwitchLogger() + logger := debug.SwitchFrameLogger() var flushErr error runOnRenderThread(func() { @@ -223,7 +223,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo } // flush must be called the render thread. -func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, logger debug.Logger) (err error) { +func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, logger debug.FrameLogger) (err error) { // If endFrame is true, Begin/End should be called to ensure the framebuffer is swapped. if len(q.commands) == 0 && !endFrame { return nil @@ -231,7 +231,7 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo es := q.indices vs := q.vertices - logger.Logf("Graphics commands:\n") + logger.FrameLogf("Graphics commands:\n") if err := graphicsDriver.Begin(); err != nil { return err @@ -294,7 +294,7 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo if err := c.Exec(q, graphicsDriver, indexOffset); err != nil { return err } - logger.Logf(" %s\n", c) + logger.FrameLogf(" %s\n", c) // TODO: indexOffset should be reset if the command type is different // from the previous one. This fix is needed when another drawing command is // introduced than drawTrianglesCommand. diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index be0eecf28..87ff76eff 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -234,6 +234,6 @@ func LogImagesInfo(images []*Image) { if i.screen { screen = " (screen)" } - debug.Logf(" %d: (%d, %d)%s\n", i.id, w, h, screen) + debug.FrameLogf(" %d: (%d, %d)%s\n", i.id, w, h, screen) } } diff --git a/internal/ui/context.go b/internal/ui/context.go index f7d2cb607..bf9ccce50 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -95,7 +95,7 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update return nil } - debug.Logf("----\n") + debug.FrameLogf("----\n") if err := atlas.BeginFrame(graphicsDriver); err != nil { return err @@ -133,7 +133,7 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update updateCount = 1 c.updateCalled = true } - debug.Logf("Update count per frame: %d\n", updateCount) + debug.FrameLogf("Update count per frame: %d\n", updateCount) // Update the game. for i := 0; i < updateCount; i++ { From fab9482e0e05ae21900285d150f1e1cef1380bcf Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 7 Aug 2024 23:47:34 +0900 Subject: [PATCH 027/188] Revert "internal/debug: rename functions" This reverts commit 74722298a2cf115a096ad1d368d1f1e92792e667. Reason: This included an unexpected change in internal/gamepad --- internal/atlas/image.go | 2 +- internal/debug/debug.go | 5 ++-- internal/debug/debug_ebitenginedebug.go | 30 ++++++++++------------ internal/debug/debug_notebitenginedebug.go | 12 ++++----- internal/gamepad/gamepad_linux.go | 3 --- internal/graphicscommand/commandqueue.go | 8 +++--- internal/graphicscommand/image.go | 2 +- internal/ui/context.go | 4 +-- 8 files changed, 30 insertions(+), 36 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index da6943789..cf21e3dfd 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -866,7 +866,7 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { }() if debug.IsDebug { - debug.FrameLogf("Internal image sizes:\n") + debug.Logf("Internal image sizes:\n") imgs := make([]*graphicscommand.Image, 0, len(theBackends)) for _, backend := range theBackends { imgs = append(imgs, backend.image) diff --git a/internal/debug/debug.go b/internal/debug/debug.go index f0c6e1d60..93cd8d1d6 100644 --- a/internal/debug/debug.go +++ b/internal/debug/debug.go @@ -14,8 +14,7 @@ package debug -// FrameLogger defines the interface for logging debug information for each frame. -type FrameLogger interface { - FrameLogf(format string, args ...any) +type Logger interface { + Logf(format string, args ...any) Flush() } diff --git a/internal/debug/debug_ebitenginedebug.go b/internal/debug/debug_ebitenginedebug.go index 853481ee0..8f6ae144d 100644 --- a/internal/debug/debug_ebitenginedebug.go +++ b/internal/debug/debug_ebitenginedebug.go @@ -23,32 +23,30 @@ import ( const IsDebug = true -var theFrameLogger = &frameLogger{} +var theLogger = &logger{} var flushM sync.Mutex -// FrameLogf calls the current global logger's FrameLogf. -// FrameLogf buffers the arguments and doesn't dump the log immediately. +// Logf calls the current global logger's Logf. +// Logf buffers the arguments and doesn't dump the log immediately. // You can dump logs by calling SwitchLogger and Flush. // -// FrameLogf is not concurrent safe. -// FrameLogf and SwitchFrameLogger must be called from the same goroutine. -func FrameLogf(format string, args ...any) { - theFrameLogger.FrameLogf(format, args...) +// Logf is not concurrent safe. +func Logf(format string, args ...any) { + theLogger.Logf(format, args...) } -// SwitchFrameLogger sets a new logger as the current logger and returns the original global logger. +// SwitchLogger sets a new logger as the current logger and returns the original global logger. // The new global logger and the returned logger have separate statuses, so you can use them for different goroutines. // -// SwitchFrameLogger and a returned Logger are not concurrent safe. -// FrameLogf and SwitchFrameLogger must be called from the same goroutine. -func SwitchFrameLogger() FrameLogger { - current := theFrameLogger - theFrameLogger = &frameLogger{} +// SwitchLogger and a returned Logger are not concurrent safe. +func SwitchLogger() Logger { + current := theLogger + theLogger = &logger{} return current } -type frameLogger struct { +type logger struct { items []logItem } @@ -57,14 +55,14 @@ type logItem struct { args []any } -func (l *frameLogger) FrameLogf(format string, args ...any) { +func (l *logger) Logf(format string, args ...any) { l.items = append(l.items, logItem{ format: format, args: args, }) } -func (l *frameLogger) Flush() { +func (l *logger) Flush() { // Flushing is protected by a mutex not to mix another logger's logs. flushM.Lock() defer flushM.Unlock() diff --git a/internal/debug/debug_notebitenginedebug.go b/internal/debug/debug_notebitenginedebug.go index e8fc57030..7f7c7509d 100644 --- a/internal/debug/debug_notebitenginedebug.go +++ b/internal/debug/debug_notebitenginedebug.go @@ -18,17 +18,17 @@ package debug const IsDebug = false -func FrameLogf(format string, args ...any) { +func Logf(format string, args ...any) { } -func SwitchFrameLogger() FrameLogger { - return dummyFrameLogger{} +func SwitchLogger() Logger { + return dummyLogger{} } -type dummyFrameLogger struct{} +type dummyLogger struct{} -func (dummyFrameLogger) FrameLogf(format string, args ...any) { +func (dummyLogger) Logf(format string, args ...any) { } -func (dummyFrameLogger) Flush() { +func (dummyLogger) Flush() { } diff --git a/internal/gamepad/gamepad_linux.go b/internal/gamepad/gamepad_linux.go index 17fd00a13..d97d43970 100644 --- a/internal/gamepad/gamepad_linux.go +++ b/internal/gamepad/gamepad_linux.go @@ -54,9 +54,6 @@ func (g *nativeGamepadsImpl) init(gamepads *gamepads) error { if err == unix.ENOENT { return nil } - if err == unix.EPERM { - return nil - } return fmt.Errorf("gamepad: Stat failed: %w", err) } if stat.Mode&unix.S_IFDIR == 0 { diff --git a/internal/graphicscommand/commandqueue.go b/internal/graphicscommand/commandqueue.go index 7176cb240..0f24dbf42 100644 --- a/internal/graphicscommand/commandqueue.go +++ b/internal/graphicscommand/commandqueue.go @@ -197,7 +197,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo } } - logger := debug.SwitchFrameLogger() + logger := debug.SwitchLogger() var flushErr error runOnRenderThread(func() { @@ -223,7 +223,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo } // flush must be called the render thread. -func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, logger debug.FrameLogger) (err error) { +func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, logger debug.Logger) (err error) { // If endFrame is true, Begin/End should be called to ensure the framebuffer is swapped. if len(q.commands) == 0 && !endFrame { return nil @@ -231,7 +231,7 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo es := q.indices vs := q.vertices - logger.FrameLogf("Graphics commands:\n") + logger.Logf("Graphics commands:\n") if err := graphicsDriver.Begin(); err != nil { return err @@ -294,7 +294,7 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo if err := c.Exec(q, graphicsDriver, indexOffset); err != nil { return err } - logger.FrameLogf(" %s\n", c) + logger.Logf(" %s\n", c) // TODO: indexOffset should be reset if the command type is different // from the previous one. This fix is needed when another drawing command is // introduced than drawTrianglesCommand. diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 87ff76eff..be0eecf28 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -234,6 +234,6 @@ func LogImagesInfo(images []*Image) { if i.screen { screen = " (screen)" } - debug.FrameLogf(" %d: (%d, %d)%s\n", i.id, w, h, screen) + debug.Logf(" %d: (%d, %d)%s\n", i.id, w, h, screen) } } diff --git a/internal/ui/context.go b/internal/ui/context.go index bf9ccce50..f7d2cb607 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -95,7 +95,7 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update return nil } - debug.FrameLogf("----\n") + debug.Logf("----\n") if err := atlas.BeginFrame(graphicsDriver); err != nil { return err @@ -133,7 +133,7 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update updateCount = 1 c.updateCalled = true } - debug.FrameLogf("Update count per frame: %d\n", updateCount) + debug.Logf("Update count per frame: %d\n", updateCount) // Update the game. for i := 0; i < updateCount; i++ { From 1f03971fa9b7c3a8333f979baaec5bd25faaafd7 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 7 Aug 2024 23:33:54 +0900 Subject: [PATCH 028/188] internal/debug: reland: rename functions --- internal/atlas/image.go | 2 +- internal/debug/debug.go | 5 ++-- internal/debug/debug_ebitenginedebug.go | 30 ++++++++++++---------- internal/debug/debug_notebitenginedebug.go | 12 ++++----- internal/graphicscommand/commandqueue.go | 8 +++--- internal/graphicscommand/image.go | 2 +- internal/ui/context.go | 4 +-- 7 files changed, 33 insertions(+), 30 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index cf21e3dfd..da6943789 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -866,7 +866,7 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { }() if debug.IsDebug { - debug.Logf("Internal image sizes:\n") + debug.FrameLogf("Internal image sizes:\n") imgs := make([]*graphicscommand.Image, 0, len(theBackends)) for _, backend := range theBackends { imgs = append(imgs, backend.image) diff --git a/internal/debug/debug.go b/internal/debug/debug.go index 93cd8d1d6..f0c6e1d60 100644 --- a/internal/debug/debug.go +++ b/internal/debug/debug.go @@ -14,7 +14,8 @@ package debug -type Logger interface { - Logf(format string, args ...any) +// FrameLogger defines the interface for logging debug information for each frame. +type FrameLogger interface { + FrameLogf(format string, args ...any) Flush() } diff --git a/internal/debug/debug_ebitenginedebug.go b/internal/debug/debug_ebitenginedebug.go index 8f6ae144d..853481ee0 100644 --- a/internal/debug/debug_ebitenginedebug.go +++ b/internal/debug/debug_ebitenginedebug.go @@ -23,30 +23,32 @@ import ( const IsDebug = true -var theLogger = &logger{} +var theFrameLogger = &frameLogger{} var flushM sync.Mutex -// Logf calls the current global logger's Logf. -// Logf buffers the arguments and doesn't dump the log immediately. +// FrameLogf calls the current global logger's FrameLogf. +// FrameLogf buffers the arguments and doesn't dump the log immediately. // You can dump logs by calling SwitchLogger and Flush. // -// Logf is not concurrent safe. -func Logf(format string, args ...any) { - theLogger.Logf(format, args...) +// FrameLogf is not concurrent safe. +// FrameLogf and SwitchFrameLogger must be called from the same goroutine. +func FrameLogf(format string, args ...any) { + theFrameLogger.FrameLogf(format, args...) } -// SwitchLogger sets a new logger as the current logger and returns the original global logger. +// SwitchFrameLogger sets a new logger as the current logger and returns the original global logger. // The new global logger and the returned logger have separate statuses, so you can use them for different goroutines. // -// SwitchLogger and a returned Logger are not concurrent safe. -func SwitchLogger() Logger { - current := theLogger - theLogger = &logger{} +// SwitchFrameLogger and a returned Logger are not concurrent safe. +// FrameLogf and SwitchFrameLogger must be called from the same goroutine. +func SwitchFrameLogger() FrameLogger { + current := theFrameLogger + theFrameLogger = &frameLogger{} return current } -type logger struct { +type frameLogger struct { items []logItem } @@ -55,14 +57,14 @@ type logItem struct { args []any } -func (l *logger) Logf(format string, args ...any) { +func (l *frameLogger) FrameLogf(format string, args ...any) { l.items = append(l.items, logItem{ format: format, args: args, }) } -func (l *logger) Flush() { +func (l *frameLogger) Flush() { // Flushing is protected by a mutex not to mix another logger's logs. flushM.Lock() defer flushM.Unlock() diff --git a/internal/debug/debug_notebitenginedebug.go b/internal/debug/debug_notebitenginedebug.go index 7f7c7509d..e8fc57030 100644 --- a/internal/debug/debug_notebitenginedebug.go +++ b/internal/debug/debug_notebitenginedebug.go @@ -18,17 +18,17 @@ package debug const IsDebug = false -func Logf(format string, args ...any) { +func FrameLogf(format string, args ...any) { } -func SwitchLogger() Logger { - return dummyLogger{} +func SwitchFrameLogger() FrameLogger { + return dummyFrameLogger{} } -type dummyLogger struct{} +type dummyFrameLogger struct{} -func (dummyLogger) Logf(format string, args ...any) { +func (dummyFrameLogger) FrameLogf(format string, args ...any) { } -func (dummyLogger) Flush() { +func (dummyFrameLogger) Flush() { } diff --git a/internal/graphicscommand/commandqueue.go b/internal/graphicscommand/commandqueue.go index 0f24dbf42..7176cb240 100644 --- a/internal/graphicscommand/commandqueue.go +++ b/internal/graphicscommand/commandqueue.go @@ -197,7 +197,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo } } - logger := debug.SwitchLogger() + logger := debug.SwitchFrameLogger() var flushErr error runOnRenderThread(func() { @@ -223,7 +223,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo } // flush must be called the render thread. -func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, logger debug.Logger) (err error) { +func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, logger debug.FrameLogger) (err error) { // If endFrame is true, Begin/End should be called to ensure the framebuffer is swapped. if len(q.commands) == 0 && !endFrame { return nil @@ -231,7 +231,7 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo es := q.indices vs := q.vertices - logger.Logf("Graphics commands:\n") + logger.FrameLogf("Graphics commands:\n") if err := graphicsDriver.Begin(); err != nil { return err @@ -294,7 +294,7 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo if err := c.Exec(q, graphicsDriver, indexOffset); err != nil { return err } - logger.Logf(" %s\n", c) + logger.FrameLogf(" %s\n", c) // TODO: indexOffset should be reset if the command type is different // from the previous one. This fix is needed when another drawing command is // introduced than drawTrianglesCommand. diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index be0eecf28..87ff76eff 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -234,6 +234,6 @@ func LogImagesInfo(images []*Image) { if i.screen { screen = " (screen)" } - debug.Logf(" %d: (%d, %d)%s\n", i.id, w, h, screen) + debug.FrameLogf(" %d: (%d, %d)%s\n", i.id, w, h, screen) } } diff --git a/internal/ui/context.go b/internal/ui/context.go index f7d2cb607..bf9ccce50 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -95,7 +95,7 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update return nil } - debug.Logf("----\n") + debug.FrameLogf("----\n") if err := atlas.BeginFrame(graphicsDriver); err != nil { return err @@ -133,7 +133,7 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update updateCount = 1 c.updateCalled = true } - debug.Logf("Update count per frame: %d\n", updateCount) + debug.FrameLogf("Update count per frame: %d\n", updateCount) // Update the game. for i := 0; i < updateCount; i++ { From 3624486f8b360efb39f187ff301cb9b7622c3481 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 8 Aug 2024 00:07:40 +0900 Subject: [PATCH 029/188] all: update PureGo to v0.8.0-alpha.4 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 96b46b6fa..8279c9c92 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f - github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 + github.com/ebitengine/purego v0.8.0-alpha.4 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.3 diff --git a/go.sum b/go.sum index b0dd5eb1b..f0d25c20a 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f h1:1a7SoSH0DOZEIRXcWNRCAYV2dj9POTlyqi7zKrmhcTM= github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= -github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187 h1:vXEgFw8Ni26tlWLmeI8nFXa7pMLKUTR9hfXcQPCYpQg= -github.com/ebitengine/purego v0.8.0-alpha.3.0.20240805123034-6cc30db8f187/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= +github.com/ebitengine/purego v0.8.0-alpha.4 h1:Dg9xRGC3giyQedfISyHH94eQM0md4a84+HHr7KBBH/Q= +github.com/ebitengine/purego v0.8.0-alpha.4/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38= github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= From 9c80367f2f0c2fc08dd85e0b4e6d143d9cd904a4 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 8 Aug 2024 11:35:37 +0900 Subject: [PATCH 030/188] internal/gamepad: ignore EACCES error for /dev/input Updates #3057 --- internal/gamepad/gamepad_linux.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/gamepad/gamepad_linux.go b/internal/gamepad/gamepad_linux.go index d97d43970..03dc4ef0c 100644 --- a/internal/gamepad/gamepad_linux.go +++ b/internal/gamepad/gamepad_linux.go @@ -54,6 +54,10 @@ func (g *nativeGamepadsImpl) init(gamepads *gamepads) error { if err == unix.ENOENT { return nil } + // `/dev/input` might not be accessible in some environments (#3057). + if err == unix.EACCES { + return nil + } return fmt.Errorf("gamepad: Stat failed: %w", err) } if stat.Mode&unix.S_IFDIR == 0 { From 956a95c397f838557b16ee33e5ab9d60e6ceaab0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 9 Aug 2024 00:08:22 +0900 Subject: [PATCH 031/188] all: update Oto to v3.3.0-alpha.4 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8279c9c92..2254ad9fc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 - github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f + github.com/ebitengine/oto/v3 v3.3.0-alpha.4 github.com/ebitengine/purego v0.8.0-alpha.4 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 diff --git a/go.sum b/go.sum index f0d25c20a..93b8d9c86 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48 github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f h1:1a7SoSH0DOZEIRXcWNRCAYV2dj9POTlyqi7zKrmhcTM= -github.com/ebitengine/oto/v3 v3.3.0-alpha.3.0.20240806041909-d412d64fb19f/go.mod h1:yYvXK7mgNwsFawY5RsvGI6yhMHtD+0MfaPkDTl9/uv8= +github.com/ebitengine/oto/v3 v3.3.0-alpha.4 h1:w9SD7kK4GgJULkh5pWVTToMA5Ia1bP7VxD4rIjQqb8M= +github.com/ebitengine/oto/v3 v3.3.0-alpha.4/go.mod h1:B+Sz3hzZXcx251YqSPIj+cVMicvlx7Xiq29AEUIbc7E= github.com/ebitengine/purego v0.8.0-alpha.4 h1:Dg9xRGC3giyQedfISyHH94eQM0md4a84+HHr7KBBH/Q= github.com/ebitengine/purego v0.8.0-alpha.4/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= From 1a0f50503d21e50b0f60dda2fc0227cb139937b2 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 9 Aug 2024 14:22:00 +0900 Subject: [PATCH 032/188] .github/workflows: update wasmbrowsertest for the websocket issue See https://github.com/agnivade/wasmbrowsertest/issues/59. Closes #2982 --- .github/workflows/test.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 516d16569..8f785883d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,9 +42,12 @@ jobs: sudo apt-get update sudo apt-get install libasound2-dev libgl1-mesa-dev libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libxxf86vm-dev + - name: Install Chrome + uses: browser-actions/setup-chrome@latest + - name: Install wasmbrowsertest run: | - wasmbrowsertest_version=6e5bbb88049c42eb62e19d10e5be9940b9271aab + wasmbrowsertest_version=06679196c7e76f227e71456cdc16fccd6cc33601 go install github.com/agnivade/wasmbrowsertest@${wasmbrowsertest_version} mv $(go env GOPATH)/bin/wasmbrowsertest${{ runner.os == 'Windows' && '.exe' || '' }} $(go env GOPATH)/bin/go_js_wasm_exec${{ runner.os == 'Windows' && '.exe' || '' }} go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@${wasmbrowsertest_version} @@ -165,10 +168,9 @@ jobs: env GOARCH=386 EBITENGINE_DIRECTX=version=12 go test -shuffle=on -v ./... - name: go test (Wasm) - if: ${{ runner.os != 'macOS' && runner.os != 'Windows' }} + if: ${{ runner.os != 'macOS' }} run: | # Wasm tests don't work on macOS with the headless mode enabled, but the headless mode cannot be disabled in GitHub Actions (#2972). - # Wasm tests don't work on Windows well due to mysterious timeouts (#2982). env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... - name: Install ebitenmobile From a5235eea8601415644acd8b70f1dd32ebb08272b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 01:21:35 +0900 Subject: [PATCH 033/188] internal/graphicsdriver/opengl/gl: always prefer OpenGL ES to OpenGL Closes #2944 --- doc.go | 5 -- .../opengl/gl/procaddr_linbsd.go | 48 ++++++------------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/doc.go b/doc.go index aebd8ad2f..efdfd886f 100644 --- a/doc.go +++ b/doc.go @@ -93,11 +93,6 @@ // The option "featurelevel" is valid only for DirectX 12. // The possible values are "11_0", "11_1", "12_0", "12_1", and "12_2". The default value is "11_0". // -// `EBITENGINE_OPENGL` environment variable specifies various parameters for OpenGL. -// You can specify multiple values separated by a comma. The default value is empty (i.e. no parameters). -// -// "es": Use OpenGL ES. Without this, OpenGL and OpenGL ES are automatically chosen. -// // # Build tags // // `ebitenginedebug` outputs a log of graphics commands. This is useful to know what happens in Ebitengine. In general, the diff --git a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go index c41c18ef8..e7e869ff5 100644 --- a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go +++ b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go @@ -18,8 +18,6 @@ package gl import ( "fmt" - "os" - "runtime" "strings" "github.com/ebitengine/purego" @@ -31,40 +29,10 @@ var ( ) func (c *defaultContext) init() error { - var preferES bool - if runtime.GOOS == "android" { - preferES = true - } - if !preferES { - for _, t := range strings.Split(os.Getenv("EBITENGINE_OPENGL"), ",") { - switch strings.TrimSpace(t) { - case "es": - preferES = true - break - } - } - } - // TODO: Use multiple %w-s as of Go 1.20. var errors []string - // Try OpenGL first. OpenGL is preferable as this doesn't cause context losses. - if !preferES { - // Usually libGL.so or libGL.so.1 is used. libGL.so.2 might exist only on NetBSD. - // TODO: Should "libOpenGL.so.0" [1] and "libGLX.so.0" [2] be added? These were added as of GLFW 3.3.9. - // [1] https://github.com/glfw/glfw/commit/55aad3c37b67f17279378db52da0a3ab81bbf26d - // [2] https://github.com/glfw/glfw/commit/c18851f52ec9704eb06464058a600845ec1eada1 - for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} { - lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL) - if err == nil { - libGL = lib - return nil - } - errors = append(errors, fmt.Sprintf("%s: %v", name, err)) - } - } - - // Try OpenGL ES. + // Try OpenGL ES first. Some machines like Android and Raspberry Pi might work only with OpenGL ES. for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} { lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL) if err == nil { @@ -75,6 +43,20 @@ func (c *defaultContext) init() error { errors = append(errors, fmt.Sprintf("%s: %v", name, err)) } + // Try OpenGL next. + // Usually libGL.so or libGL.so.1 is used. libGL.so.2 might exist only on NetBSD. + // TODO: Should "libOpenGL.so.0" [1] and "libGLX.so.0" [2] be added? These were added as of GLFW 3.3.9. + // [1] https://github.com/glfw/glfw/commit/55aad3c37b67f17279378db52da0a3ab81bbf26d + // [2] https://github.com/glfw/glfw/commit/c18851f52ec9704eb06464058a600845ec1eada1 + for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} { + lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL) + if err == nil { + libGL = lib + return nil + } + errors = append(errors, fmt.Sprintf("%s: %v", name, err)) + } + return fmt.Errorf("gl: failed to load libGL.so and libGLESv2.so: %s", strings.Join(errors, ", ")) } From 361da49887ee3279b7d2fbd9425c92ff03ac75bf Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 01:24:33 +0900 Subject: [PATCH 034/188] .github/workflows: remove unnecessary environment variable Updates #2944 --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8f785883d..e2c3c574b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -153,7 +153,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get install libgles2-mesa-dev - env EBITENGINE_GRAPHICS_LIBRARY=opengl EBITENGINE_OPENGL=es go test -shuffle=on -v -p=1 ./... + env EBITENGINE_GRAPHICS_LIBRARY=opengl go test -shuffle=on -v -p=1 ./... - name: go test (Windows) if: runner.os == 'Windows' @@ -168,7 +168,7 @@ jobs: env GOARCH=386 EBITENGINE_DIRECTX=version=12 go test -shuffle=on -v ./... - name: go test (Wasm) - if: ${{ runner.os != 'macOS' }} + if: runner.os != 'macOS' run: | # Wasm tests don't work on macOS with the headless mode enabled, but the headless mode cannot be disabled in GitHub Actions (#2972). env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... From 38b8ba567783d8dcc969564bbdc2a5a4bc6f1d23 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 03:06:31 +0900 Subject: [PATCH 035/188] vector: lazy point calculation This is a preparation for #2884. Updates #2884 --- vector/path.go | 193 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 132 insertions(+), 61 deletions(-) diff --git a/vector/path.go b/vector/path.go index b381b15ad..e60415c87 100644 --- a/vector/path.go +++ b/vector/path.go @@ -31,6 +31,23 @@ const ( CounterClockwise ) +type opType int + +const ( + opTypeMoveTo opType = iota + opTypeLineTo + opTypeQuadTo + opTypeCubicTo + opTypeClose +) + +type op struct { + typ opType + p1 point + p2 point + p3 point +} + func abs(x float32) float32 { if x < 0 { return -x @@ -48,16 +65,6 @@ type subpath struct { closed bool } -func (s *subpath) currentPosition() (point, bool) { - if len(s.points) == 0 { - return point{}, false - } - if s.closed { - return point{}, false - } - return s.points[len(s.points)-1], true -} - func (s *subpath) pointCount() int { return len(s.points) } @@ -91,15 +98,51 @@ func (s *subpath) close() { // Path represents a collection of path subpathments. type Path struct { + ops []op + subpaths []*subpath } +func (p *Path) ensureSubpaths() []*subpath { + // TODO: Probably it is better to avoid returning a slice since allocation is heavy. + // What about walkSubpaths(func(*subpath))? + + if len(p.subpaths) > 0 || len(p.ops) == 0 { + return p.subpaths + } + + var cur point + for _, op := range p.ops { + switch op.typ { + case opTypeMoveTo: + p.subpaths = append(p.subpaths, &subpath{ + points: []point{op.p1}, + }) + cur = op.p1 + case opTypeLineTo: + p.lineTo(op.p1) + cur = op.p1 + case opTypeQuadTo: + p.quadTo(cur, op.p1, op.p2, 0) + cur = op.p2 + case opTypeCubicTo: + p.cubicTo(cur, op.p1, op.p2, op.p3, 0) + cur = op.p3 + case opTypeClose: + p.close() + cur = point{} + } + } + + return p.subpaths +} + // MoveTo starts a new subpath with the given position (x, y) without adding a subpath, func (p *Path) MoveTo(x, y float32) { - p.subpaths = append(p.subpaths, &subpath{ - points: []point{ - {x: x, y: y}, - }, + p.subpaths = p.subpaths[:0] + p.ops = append(p.ops, op{ + typ: opTypeMoveTo, + p1: point{x: x, y: y}, }) } @@ -107,22 +150,54 @@ func (p *Path) MoveTo(x, y float32) { // and ends to the given position (x, y). // If p doesn't have any subpaths or the last subpath is closed, LineTo sets (x, y) as the start position of a new subpath. func (p *Path) LineTo(x, y float32) { - if len(p.subpaths) == 0 || p.subpaths[len(p.subpaths)-1].closed { - p.subpaths = append(p.subpaths, &subpath{ - points: []point{ - {x: x, y: y}, - }, - }) - return - } - - p.subpaths[len(p.subpaths)-1].appendPoint(point{x: x, y: y}) + p.subpaths = p.subpaths[:0] + p.ops = append(p.ops, op{ + typ: opTypeLineTo, + p1: point{x: x, y: y}, + }) } // QuadTo adds a quadratic Bézier curve to the path. // (x1, y1) is the control point, and (x2, y2) is the destination. func (p *Path) QuadTo(x1, y1, x2, y2 float32) { - p.quadTo(point{x: x1, y: y1}, point{x: x2, y: y2}, 0) + p.subpaths = p.subpaths[:0] + p.ops = append(p.ops, op{ + typ: opTypeQuadTo, + p1: point{x: x1, y: y1}, + p2: point{x: x2, y: y2}, + }) +} + +// CubicTo adds a cubic Bézier curve to the path. +// (x1, y1) and (x2, y2) are the control points, and (x3, y3) is the destination. +func (p *Path) CubicTo(x1, y1, x2, y2, x3, y3 float32) { + p.subpaths = p.subpaths[:0] + p.ops = append(p.ops, op{ + typ: opTypeCubicTo, + p1: point{x: x1, y: y1}, + p2: point{x: x2, y: y2}, + p3: point{x: x3, y: y3}, + }) +} + +// Close adds a new line from the last position of the current subpath to the first position of the current subpath, +// and marks the current subpath closed. +// Following operations for this path will start with a new subpath. +func (p *Path) Close() { + p.subpaths = p.subpaths[:0] + p.ops = append(p.ops, op{ + typ: opTypeClose, + }) +} + +func (p *Path) lineTo(pt point) { + if len(p.subpaths) == 0 || p.subpaths[len(p.subpaths)-1].closed { + p.subpaths = append(p.subpaths, &subpath{ + points: []point{pt}, + }) + return + } + p.subpaths[len(p.subpaths)-1].appendPoint(pt) } // lineForTwoPoints returns parameters for a line passing through p0 and p1. @@ -154,24 +229,13 @@ func crossingPointForTwoLines(p00, p01, p10, p11 point) point { } } -func (p *Path) currentPosition() (point, bool) { - if len(p.subpaths) == 0 { - return point{}, false - } - return p.subpaths[len(p.subpaths)-1].currentPosition() -} - -func (p *Path) quadTo(p1, p2 point, level int) { +func (p *Path) quadTo(p0, p1, p2 point, level int) { if level > 10 { return } - p0, ok := p.currentPosition() - if !ok { - p0 = p1 - } if isPointCloseToSegment(p1, p0, p2, 0.5) { - p.LineTo(p2.x, p2.y) + p.lineTo(p2) return } @@ -187,27 +251,17 @@ func (p *Path) quadTo(p1, p2 point, level int) { x: (p01.x + p12.x) / 2, y: (p01.y + p12.y) / 2, } - p.quadTo(p01, p012, level+1) - p.quadTo(p12, p2, level+1) + p.quadTo(p0, p01, p012, level+1) + p.quadTo(p012, p12, p2, level+1) } -// CubicTo adds a cubic Bézier curve to the path. -// (x1, y1) and (x2, y2) are the control points, and (x3, y3) is the destination. -func (p *Path) CubicTo(x1, y1, x2, y2, x3, y3 float32) { - p.cubicTo(point{x: x1, y: y1}, point{x: x2, y: y2}, point{x: x3, y: y3}, 0) -} - -func (p *Path) cubicTo(p1, p2, p3 point, level int) { +func (p *Path) cubicTo(p0, p1, p2, p3 point, level int) { if level > 10 { return } - p0, ok := p.currentPosition() - if !ok { - p0 = p1 - } if isPointCloseToSegment(p1, p0, p3, 0.5) && isPointCloseToSegment(p2, p0, p3, 0.5) { - p.LineTo(p3.x, p3.y) + p.lineTo(p3) return } @@ -235,8 +289,8 @@ func (p *Path) cubicTo(p1, p2, p3 point, level int) { x: (p012.x + p123.x) / 2, y: (p012.y + p123.y) / 2, } - p.cubicTo(p01, p012, p0123, level+1) - p.cubicTo(p123, p23, p3, level+1) + p.cubicTo(p0, p01, p012, p0123, level+1) + p.cubicTo(p0123, p123, p23, p3, level+1) } func normalize(p point) point { @@ -248,6 +302,26 @@ func cross(p0, p1 point) float32 { return p0.x*p1.y - p1.x*p0.y } +func (p *Path) currentPosition() (point, bool) { + if len(p.ops) == 0 { + return point{}, false + } + op := p.ops[len(p.ops)-1] + switch op.typ { + case opTypeMoveTo: + return op.p1, true + case opTypeLineTo: + return op.p1, true + case opTypeQuadTo: + return op.p2, true + case opTypeCubicTo: + return op.p3, true + case opTypeClose: + return point{}, false + } + return point{}, false +} + // ArcTo adds an arc curve to the path. // (x1, y1) is the first control point, and (x2, y2) is the second control point. func (p *Path) ArcTo(x1, y1, x2, y2, radius float32) { @@ -362,7 +436,7 @@ func (p *Path) Arc(x, y, radius, startAngle, endAngle float32, dir Direction) { p.LineTo(x0, y0) // Calculate the control points for an approximated Bézier curve. - // See https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers. + // See https://learn.microsoft.com/en-us/previous-versions/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers. l := radius * float32(math.Tan(da/4)*4/3) var cx0, cy0, cx1, cy1 float32 if dir == Clockwise { @@ -379,10 +453,7 @@ func (p *Path) Arc(x, y, radius, startAngle, endAngle float32, dir Direction) { p.CubicTo(cx0, cy0, cx1, cy1, x1, y1) } -// Close adds a new line from the last position of the current subpath to the first position of the current subpath, -// and marks the current subpath closed. -// Following operations for this path will start with a new subpath. -func (p *Path) Close() { +func (p *Path) close() { if len(p.subpaths) == 0 { return } @@ -405,7 +476,7 @@ func (p *Path) AppendVerticesAndIndicesForFilling(vertices []ebiten.Vertex, indi // TODO: Add tests. base := uint16(len(vertices)) - for _, subpath := range p.subpaths { + for _, subpath := range p.ensureSubpaths() { if subpath.pointCount() < 3 { continue } @@ -486,7 +557,7 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic return vertices, indices } - for _, subpath := range p.subpaths { + for _, subpath := range p.ensureSubpaths() { if subpath.pointCount() < 2 { continue } From 071024e89f1207be49ad6996249b190342616e5f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 05:01:38 +0900 Subject: [PATCH 036/188] vector: reduce memory allocations in the utility functions --- vector/util.go | 48 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/vector/util.go b/vector/util.go index 10c1f3990..df9782eb8 100644 --- a/vector/util.go +++ b/vector/util.go @@ -18,6 +18,7 @@ import ( "image" "image/color" "math" + "sync" "github.com/hajimehoshi/ebiten/v2" ) @@ -27,6 +28,18 @@ var ( whiteSubImage = whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image) ) +var ( + cachedVertices []ebiten.Vertex + cachedIndices []uint16 + cacheM sync.Mutex +) + +func useCachedVerticesAndIndices(fn func([]ebiten.Vertex, []uint16) (vs []ebiten.Vertex, is []uint16)) { + cacheM.Lock() + defer cacheM.Unlock() + cachedVertices, cachedIndices = fn(cachedVertices[:0], cachedIndices[:0]) +} + func init() { b := whiteImage.Bounds() pix := make([]byte, 4*b.Dx()*b.Dy()) @@ -63,9 +76,12 @@ func StrokeLine(dst *ebiten.Image, x0, y0, x1, y1 float32, strokeWidth float32, path.LineTo(x1, y1) strokeOp := &StrokeOptions{} strokeOp.Width = strokeWidth - vs, is := path.AppendVerticesAndIndicesForStroke(nil, nil, strokeOp) - drawVerticesForUtil(dst, vs, is, clr, antialias) + useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) { + vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp) + drawVerticesForUtil(dst, vs, is, clr, antialias) + return vs, is + }) } // DrawFilledRect fills a rectangle with the specified width and color. @@ -75,9 +91,12 @@ func DrawFilledRect(dst *ebiten.Image, x, y, width, height float32, clr color.Co path.LineTo(x, y+height) path.LineTo(x+width, y+height) path.LineTo(x+width, y) - vs, is := path.AppendVerticesAndIndicesForFilling(nil, nil) - drawVerticesForUtil(dst, vs, is, clr, antialias) + useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) { + vs, is = path.AppendVerticesAndIndicesForFilling(vs, is) + drawVerticesForUtil(dst, vs, is, clr, antialias) + return vs, is + }) } // StrokeRect strokes a rectangle with the specified width and color. @@ -94,18 +113,24 @@ func StrokeRect(dst *ebiten.Image, x, y, width, height float32, strokeWidth floa strokeOp := &StrokeOptions{} strokeOp.Width = strokeWidth strokeOp.MiterLimit = 10 - vs, is := path.AppendVerticesAndIndicesForStroke(nil, nil, strokeOp) - drawVerticesForUtil(dst, vs, is, clr, antialias) + useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) { + vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp) + drawVerticesForUtil(dst, vs, is, clr, antialias) + return vs, is + }) } // DrawFilledCircle fills a circle with the specified center position (cx, cy), the radius (r), width and color. func DrawFilledCircle(dst *ebiten.Image, cx, cy, r float32, clr color.Color, antialias bool) { var path Path path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise) - vs, is := path.AppendVerticesAndIndicesForFilling(nil, nil) - drawVerticesForUtil(dst, vs, is, clr, antialias) + useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) { + vs, is = path.AppendVerticesAndIndicesForFilling(vs, is) + drawVerticesForUtil(dst, vs, is, clr, antialias) + return vs, is + }) } // StrokeCircle strokes a circle with the specified center position (cx, cy), the radius (r), width and color. @@ -118,7 +143,10 @@ func StrokeCircle(dst *ebiten.Image, cx, cy, r float32, strokeWidth float32, clr strokeOp := &StrokeOptions{} strokeOp.Width = strokeWidth - vs, is := path.AppendVerticesAndIndicesForStroke(nil, nil, strokeOp) - drawVerticesForUtil(dst, vs, is, clr, antialias) + useCachedVerticesAndIndices(func(vs []ebiten.Vertex, is []uint16) ([]ebiten.Vertex, []uint16) { + vs, is = path.AppendVerticesAndIndicesForStroke(vs, is, strokeOp) + drawVerticesForUtil(dst, vs, is, clr, antialias) + return vs, is + }) } From e8e458802d2d0e22f08daa7b497ace9fe86573fd Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 05:05:37 +0900 Subject: [PATCH 037/188] examples/vector: reduce allocations --- examples/vector/main.go | 129 +++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 67 deletions(-) diff --git a/examples/vector/main.go b/examples/vector/main.go index 02ac37350..0ec64d52d 100644 --- a/examples/vector/main.go +++ b/examples/vector/main.go @@ -44,7 +44,17 @@ const ( screenHeight = 480 ) -func drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) { +type Game struct { + counter int + + aa bool + line bool + + vertices []ebiten.Vertex + indices []uint16 +} + +func (g *Game) drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) { var path vector.Path // E @@ -116,26 +126,24 @@ func drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) { path.LineTo(290, 20) path.Close() - var vs []ebiten.Vertex - var is []uint16 if line { op := &vector.StrokeOptions{} op.Width = 5 op.LineJoin = vector.LineJoinRound - vs, is = path.AppendVerticesAndIndicesForStroke(nil, nil, op) + g.vertices, g.indices = path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op) } else { - vs, is = path.AppendVerticesAndIndicesForFilling(nil, nil) + g.vertices, g.indices = path.AppendVerticesAndIndicesForFilling(g.vertices[:0], g.indices[:0]) } - for i := range vs { - vs[i].DstX = (vs[i].DstX + float32(x)) - vs[i].DstY = (vs[i].DstY + float32(y)) - vs[i].SrcX = 1 - vs[i].SrcY = 1 - vs[i].ColorR = 0xdb / float32(0xff) - vs[i].ColorG = 0x56 / float32(0xff) - vs[i].ColorB = 0x20 / float32(0xff) - vs[i].ColorA = 1 + for i := range g.vertices { + g.vertices[i].DstX = (g.vertices[i].DstX + float32(x)) + g.vertices[i].DstY = (g.vertices[i].DstY + float32(y)) + g.vertices[i].SrcX = 1 + g.vertices[i].SrcY = 1 + g.vertices[i].ColorR = 0xdb / float32(0xff) + g.vertices[i].ColorG = 0x56 / float32(0xff) + g.vertices[i].ColorB = 0x20 / float32(0xff) + g.vertices[i].ColorA = 1 } op := &ebiten.DrawTrianglesOptions{} @@ -150,10 +158,10 @@ func drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) { // For simplicity, this example always uses FillRuleNonZero, whichever strokes or filling is done. op.FillRule = ebiten.FillRuleNonZero - screen.DrawTriangles(vs, is, whiteSubImage, op) + screen.DrawTriangles(g.vertices, g.indices, whiteSubImage, op) } -func drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool) { +func (g *Game) drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool) { const unit = 16 var path vector.Path @@ -179,35 +187,33 @@ func drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool) { path.LineTo(unit, 4*unit) path.Close() - var vs []ebiten.Vertex - var is []uint16 if line { op := &vector.StrokeOptions{} op.Width = 5 op.LineJoin = vector.LineJoinRound - vs, is = path.AppendVerticesAndIndicesForStroke(nil, nil, op) + g.vertices, g.indices = path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op) } else { - vs, is = path.AppendVerticesAndIndicesForFilling(nil, nil) + g.vertices, g.indices = path.AppendVerticesAndIndicesForFilling(g.vertices[:0], g.indices[:0]) } - for i := range vs { - vs[i].DstX = (vs[i].DstX + float32(x)) - vs[i].DstY = (vs[i].DstY + float32(y)) - vs[i].SrcX = 1 - vs[i].SrcY = 1 - vs[i].ColorR = 0xdb / float32(0xff) - vs[i].ColorG = 0x56 / float32(0xff) - vs[i].ColorB = 0x20 / float32(0xff) - vs[i].ColorA = 1 + for i := range g.vertices { + g.vertices[i].DstX = (g.vertices[i].DstX + float32(x)) + g.vertices[i].DstY = (g.vertices[i].DstY + float32(y)) + g.vertices[i].SrcX = 1 + g.vertices[i].SrcY = 1 + g.vertices[i].ColorR = 0xdb / float32(0xff) + g.vertices[i].ColorG = 0x56 / float32(0xff) + g.vertices[i].ColorB = 0x20 / float32(0xff) + g.vertices[i].ColorA = 1 } op := &ebiten.DrawTrianglesOptions{} op.AntiAlias = aa op.FillRule = ebiten.FillRuleNonZero - screen.DrawTriangles(vs, is, whiteSubImage, op) + screen.DrawTriangles(g.vertices, g.indices, whiteSubImage, op) } -func drawArc(screen *ebiten.Image, count int, aa bool, line bool) { +func (g *Game) drawArc(screen *ebiten.Image, count int, aa bool, line bool) { var path vector.Path path.MoveTo(350, 100) @@ -223,37 +229,35 @@ func drawArc(screen *ebiten.Image, count int, aa bool, line bool) { path.Arc(550, 100, 50, float32(theta1), float32(theta2), vector.Clockwise) path.Close() - var vs []ebiten.Vertex - var is []uint16 if line { op := &vector.StrokeOptions{} op.Width = 5 op.LineJoin = vector.LineJoinRound - vs, is = path.AppendVerticesAndIndicesForStroke(nil, nil, op) + g.vertices, g.indices = path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op) } else { - vs, is = path.AppendVerticesAndIndicesForFilling(nil, nil) + g.vertices, g.indices = path.AppendVerticesAndIndicesForFilling(g.vertices[:0], g.indices[:0]) } - for i := range vs { - vs[i].SrcX = 1 - vs[i].SrcY = 1 - vs[i].ColorR = 0x33 / float32(0xff) - vs[i].ColorG = 0xcc / float32(0xff) - vs[i].ColorB = 0x66 / float32(0xff) - vs[i].ColorA = 1 + for i := range g.vertices { + g.vertices[i].SrcX = 1 + g.vertices[i].SrcY = 1 + g.vertices[i].ColorR = 0x33 / float32(0xff) + g.vertices[i].ColorG = 0xcc / float32(0xff) + g.vertices[i].ColorB = 0x66 / float32(0xff) + g.vertices[i].ColorA = 1 } op := &ebiten.DrawTrianglesOptions{} op.AntiAlias = aa op.FillRule = ebiten.FillRuleNonZero - screen.DrawTriangles(vs, is, whiteSubImage, op) + screen.DrawTriangles(g.vertices, g.indices, whiteSubImage, op) } func maxCounter(index int) int { return 128 + (17*index+32)%64 } -func drawWave(screen *ebiten.Image, counter int, aa bool, line bool) { +func (g *Game) drawWave(screen *ebiten.Image, counter int, aa bool, line bool) { var path vector.Path const npoints = 8 @@ -278,37 +282,28 @@ func drawWave(screen *ebiten.Image, counter int, aa bool, line bool) { path.LineTo(screenWidth, screenHeight) path.LineTo(0, screenHeight) - var vs []ebiten.Vertex - var is []uint16 if line { op := &vector.StrokeOptions{} op.Width = 5 op.LineJoin = vector.LineJoinRound - vs, is = path.AppendVerticesAndIndicesForStroke(nil, nil, op) + g.vertices, g.indices = path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op) } else { - vs, is = path.AppendVerticesAndIndicesForFilling(nil, nil) + g.vertices, g.indices = path.AppendVerticesAndIndicesForFilling(g.vertices[:0], g.indices[:0]) } - for i := range vs { - vs[i].SrcX = 1 - vs[i].SrcY = 1 - vs[i].ColorR = 0x33 / float32(0xff) - vs[i].ColorG = 0x66 / float32(0xff) - vs[i].ColorB = 0xff / float32(0xff) - vs[i].ColorA = 1 + for i := range g.vertices { + g.vertices[i].SrcX = 1 + g.vertices[i].SrcY = 1 + g.vertices[i].ColorR = 0x33 / float32(0xff) + g.vertices[i].ColorG = 0x66 / float32(0xff) + g.vertices[i].ColorB = 0xff / float32(0xff) + g.vertices[i].ColorA = 1 } op := &ebiten.DrawTrianglesOptions{} op.AntiAlias = aa op.FillRule = ebiten.FillRuleNonZero - screen.DrawTriangles(vs, is, whiteSubImage, op) -} - -type Game struct { - counter int - - aa bool - line bool + screen.DrawTriangles(g.vertices, g.indices, whiteSubImage, op) } func (g *Game) Update() error { @@ -331,10 +326,10 @@ func (g *Game) Draw(screen *ebiten.Image) { dst := screen dst.Fill(color.RGBA{0xe0, 0xe0, 0xe0, 0xff}) - drawEbitenText(dst, 0, 50, g.aa, g.line) - drawEbitenLogo(dst, 20, 150, g.aa, g.line) - drawArc(dst, g.counter, g.aa, g.line) - drawWave(dst, g.counter, g.aa, g.line) + g.drawEbitenText(dst, 0, 50, g.aa, g.line) + g.drawEbitenLogo(dst, 20, 150, g.aa, g.line) + g.drawArc(dst, g.counter, g.aa, g.line) + g.drawWave(dst, g.counter, g.aa, g.line) msg := fmt.Sprintf("TPS: %0.2f\nFPS: %0.2f", ebiten.ActualTPS(), ebiten.ActualFPS()) msg += "\nPress A to switch anti-alias." From cdb430b2a51d971c403ec22a9e50b91e43b0bc2e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 05:35:29 +0900 Subject: [PATCH 038/188] vector: reduce allocations --- vector/path.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/path.go b/vector/path.go index e60415c87..85700b622 100644 --- a/vector/path.go +++ b/vector/path.go @@ -557,12 +557,13 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic return vertices, indices } + var rects [][4]point for _, subpath := range p.ensureSubpaths() { if subpath.pointCount() < 2 { continue } - var rects [][4]point + rects = rects[:0] for i := 0; i < subpath.pointCount()-1; i++ { pt := subpath.points[i] From 68380e506ee99ad640c63dba5a440931c61607d7 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 14:02:22 +0900 Subject: [PATCH 039/188] vector: reduce memory allocations by reusing the same Path objects --- vector/path.go | 95 ++++++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/vector/path.go b/vector/path.go index 85700b622..31d997853 100644 --- a/vector/path.go +++ b/vector/path.go @@ -103,6 +103,11 @@ type Path struct { subpaths []*subpath } +func (p *Path) reset() { + p.ops = p.ops[:0] + p.subpaths = p.subpaths[:0] +} + func (p *Path) ensureSubpaths() []*subpath { // TODO: Probably it is better to avoid returning a slice since allocation is heavy. // What about walkSubpaths(func(*subpath))? @@ -558,6 +563,7 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic } var rects [][4]point + var tmpPath Path for _, subpath := range p.ensureSubpaths() { if subpath.pointCount() < 2 { continue @@ -643,46 +649,49 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic delta := math.Pi - da exceed := float32(math.Abs(1/math.Sin(float64(delta/2)))) > op.MiterLimit - var quad Path - quad.MoveTo(c.x, c.y) + // Quadrilateral + tmpPath.reset() + tmpPath.MoveTo(c.x, c.y) if da < math.Pi { - quad.LineTo(rect[1].x, rect[1].y) + tmpPath.LineTo(rect[1].x, rect[1].y) if !exceed { pt := crossingPointForTwoLines(rect[0], rect[1], nextRect[0], nextRect[1]) - quad.LineTo(pt.x, pt.y) + tmpPath.LineTo(pt.x, pt.y) } - quad.LineTo(nextRect[0].x, nextRect[0].y) + tmpPath.LineTo(nextRect[0].x, nextRect[0].y) } else { - quad.LineTo(rect[3].x, rect[3].y) + tmpPath.LineTo(rect[3].x, rect[3].y) if !exceed { pt := crossingPointForTwoLines(rect[2], rect[3], nextRect[2], nextRect[3]) - quad.LineTo(pt.x, pt.y) + tmpPath.LineTo(pt.x, pt.y) } - quad.LineTo(nextRect[2].x, nextRect[2].y) + tmpPath.LineTo(nextRect[2].x, nextRect[2].y) } - vertices, indices = quad.AppendVerticesAndIndicesForFilling(vertices, indices) + vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices) case LineJoinBevel: - var tri Path - tri.MoveTo(c.x, c.y) + // Triangle + tmpPath.reset() + tmpPath.MoveTo(c.x, c.y) if da < math.Pi { - tri.LineTo(rect[1].x, rect[1].y) - tri.LineTo(nextRect[0].x, nextRect[0].y) + tmpPath.LineTo(rect[1].x, rect[1].y) + tmpPath.LineTo(nextRect[0].x, nextRect[0].y) } else { - tri.LineTo(rect[3].x, rect[3].y) - tri.LineTo(nextRect[2].x, nextRect[2].y) + tmpPath.LineTo(rect[3].x, rect[3].y) + tmpPath.LineTo(nextRect[2].x, nextRect[2].y) } - vertices, indices = tri.AppendVerticesAndIndicesForFilling(vertices, indices) + vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices) case LineJoinRound: - var arc Path - arc.MoveTo(c.x, c.y) + // Arc + tmpPath.reset() + tmpPath.MoveTo(c.x, c.y) if da < math.Pi { - arc.Arc(c.x, c.y, op.Width/2, a0, a1, Clockwise) + tmpPath.Arc(c.x, c.y, op.Width/2, a0, a1, Clockwise) } else { - arc.Arc(c.x, c.y, op.Width/2, a0+math.Pi, a1+math.Pi, CounterClockwise) + tmpPath.Arc(c.x, c.y, op.Width/2, a0+math.Pi, a1+math.Pi, CounterClockwise) } - vertices, indices = arc.AppendVerticesAndIndicesForFilling(vertices, indices) + vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices) } } @@ -707,10 +716,11 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic y: (startR[0].y + startR[2].y) / 2, } a := float32(math.Atan2(float64(startR[0].y-startR[2].y), float64(startR[0].x-startR[2].x))) - var arc Path - arc.MoveTo(startR[0].x, startR[0].y) - arc.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, CounterClockwise) - vertices, indices = arc.AppendVerticesAndIndicesForFilling(vertices, indices) + // Arc + tmpPath.reset() + tmpPath.MoveTo(startR[0].x, startR[0].y) + tmpPath.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, CounterClockwise) + vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices) } { c := point{ @@ -718,10 +728,11 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic y: (endR[1].y + endR[3].y) / 2, } a := float32(math.Atan2(float64(endR[1].y-endR[3].y), float64(endR[1].x-endR[3].x))) - var arc Path - arc.MoveTo(endR[1].x, endR[1].y) - arc.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, Clockwise) - vertices, indices = arc.AppendVerticesAndIndicesForFilling(vertices, indices) + // Arc + tmpPath.reset() + tmpPath.MoveTo(endR[1].x, endR[1].y) + tmpPath.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, Clockwise) + vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices) } case LineCapSquare: @@ -731,24 +742,26 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic s, c := math.Sincos(a) dx, dy := float32(c)*op.Width/2, float32(s)*op.Width/2 - var quad Path - quad.MoveTo(startR[0].x, startR[0].y) - quad.LineTo(startR[0].x+dx, startR[0].y+dy) - quad.LineTo(startR[2].x+dx, startR[2].y+dy) - quad.LineTo(startR[2].x, startR[2].y) - vertices, indices = quad.AppendVerticesAndIndicesForFilling(vertices, indices) + // Quadrilateral + tmpPath.reset() + tmpPath.MoveTo(startR[0].x, startR[0].y) + tmpPath.LineTo(startR[0].x+dx, startR[0].y+dy) + tmpPath.LineTo(startR[2].x+dx, startR[2].y+dy) + tmpPath.LineTo(startR[2].x, startR[2].y) + vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices) } { a := math.Atan2(float64(endR[1].y-endR[0].y), float64(endR[1].x-endR[0].x)) s, c := math.Sincos(a) dx, dy := float32(c)*op.Width/2, float32(s)*op.Width/2 - var quad Path - quad.MoveTo(endR[1].x, endR[1].y) - quad.LineTo(endR[1].x+dx, endR[1].y+dy) - quad.LineTo(endR[3].x+dx, endR[3].y+dy) - quad.LineTo(endR[3].x, endR[3].y) - vertices, indices = quad.AppendVerticesAndIndicesForFilling(vertices, indices) + // Quadrilateral + tmpPath.reset() + tmpPath.MoveTo(endR[1].x, endR[1].y) + tmpPath.LineTo(endR[1].x+dx, endR[1].y+dy) + tmpPath.LineTo(endR[3].x+dx, endR[3].y+dy) + tmpPath.LineTo(endR[3].x, endR[3].y) + vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices) } } } From 309c886c2edc897b35a90eafd5fe5bd8d6211850 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 14:46:49 +0900 Subject: [PATCH 040/188] vector: use value type for subpath Updates #3060 --- vector/path.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/vector/path.go b/vector/path.go index 31d997853..738552227 100644 --- a/vector/path.go +++ b/vector/path.go @@ -65,11 +65,11 @@ type subpath struct { closed bool } -func (s *subpath) pointCount() int { +func (s subpath) pointCount() int { return len(s.points) } -func (s *subpath) lastPoint() point { +func (s subpath) lastPoint() point { return s.points[len(s.points)-1] } @@ -100,7 +100,7 @@ func (s *subpath) close() { type Path struct { ops []op - subpaths []*subpath + subpaths []subpath } func (p *Path) reset() { @@ -108,10 +108,7 @@ func (p *Path) reset() { p.subpaths = p.subpaths[:0] } -func (p *Path) ensureSubpaths() []*subpath { - // TODO: Probably it is better to avoid returning a slice since allocation is heavy. - // What about walkSubpaths(func(*subpath))? - +func (p *Path) ensureSubpaths() []subpath { if len(p.subpaths) > 0 || len(p.ops) == 0 { return p.subpaths } @@ -120,7 +117,7 @@ func (p *Path) ensureSubpaths() []*subpath { for _, op := range p.ops { switch op.typ { case opTypeMoveTo: - p.subpaths = append(p.subpaths, &subpath{ + p.subpaths = append(p.subpaths, subpath{ points: []point{op.p1}, }) cur = op.p1 @@ -197,7 +194,7 @@ func (p *Path) Close() { func (p *Path) lineTo(pt point) { if len(p.subpaths) == 0 || p.subpaths[len(p.subpaths)-1].closed { - p.subpaths = append(p.subpaths, &subpath{ + p.subpaths = append(p.subpaths, subpath{ points: []point{pt}, }) return @@ -462,8 +459,7 @@ func (p *Path) close() { if len(p.subpaths) == 0 { return } - subpath := p.subpaths[len(p.subpaths)-1] - subpath.close() + p.subpaths[len(p.subpaths)-1].close() } // AppendVerticesAndIndicesForFilling appends vertices and indices to fill this path and returns them. From fc37cdedeb9ae13dcdd2f3e9e0341d1b8812a3ea Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 15:06:03 +0900 Subject: [PATCH 041/188] vector: reuse previous allocated subpaths Closes #3060 --- vector/path.go | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/vector/path.go b/vector/path.go index 738552227..3146267ac 100644 --- a/vector/path.go +++ b/vector/path.go @@ -65,6 +65,13 @@ type subpath struct { closed bool } +// reset resets the subpath. +// reset doesn't release the allocated memory so that the memory can be reused. +func (s *subpath) reset() { + s.points = s.points[:0] + s.closed = false +} + func (s subpath) pointCount() int { return len(s.points) } @@ -78,10 +85,12 @@ func (s *subpath) appendPoint(pt point) { panic("vector: a closed subpathment cannot append a new point") } - // Do not add a too close point to the last point. - // This can cause unexpected rendering results. - if lp := s.lastPoint(); abs(lp.x-pt.x) < 1e-2 && abs(lp.y-pt.y) < 1e-2 { - return + if len(s.points) > 0 { + // Do not add a too close point to the last point. + // This can cause unexpected rendering results. + if lp := s.lastPoint(); abs(lp.x-pt.x) < 1e-2 && abs(lp.y-pt.y) < 1e-2 { + return + } } s.points = append(s.points, pt) @@ -103,11 +112,26 @@ type Path struct { subpaths []subpath } +// reset resets the path. +// reset doesn't release the allocated memory so that the memory can be reused. func (p *Path) reset() { p.ops = p.ops[:0] p.subpaths = p.subpaths[:0] } +func (p *Path) appendNewSubpath(pt point) { + if cap(p.subpaths) > len(p.subpaths) { + // Reuse the last subpath since the last subpath might have an already allocated slice. + p.subpaths = p.subpaths[:len(p.subpaths)+1] + p.subpaths[len(p.subpaths)-1].reset() + p.subpaths[len(p.subpaths)-1].appendPoint(pt) + return + } + p.subpaths = append(p.subpaths, subpath{ + points: []point{pt}, + }) +} + func (p *Path) ensureSubpaths() []subpath { if len(p.subpaths) > 0 || len(p.ops) == 0 { return p.subpaths @@ -117,9 +141,7 @@ func (p *Path) ensureSubpaths() []subpath { for _, op := range p.ops { switch op.typ { case opTypeMoveTo: - p.subpaths = append(p.subpaths, subpath{ - points: []point{op.p1}, - }) + p.appendNewSubpath(op.p1) cur = op.p1 case opTypeLineTo: p.lineTo(op.p1) @@ -194,9 +216,7 @@ func (p *Path) Close() { func (p *Path) lineTo(pt point) { if len(p.subpaths) == 0 || p.subpaths[len(p.subpaths)-1].closed { - p.subpaths = append(p.subpaths, subpath{ - points: []point{pt}, - }) + p.appendNewSubpath(pt) return } p.subpaths[len(p.subpaths)-1].appendPoint(pt) From fbf40a44553fe9b22e658bc53ffa68bca15b9f84 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 17:04:13 +0900 Subject: [PATCH 042/188] vector: bug fix: isPointCloseToSegment didn't work when two p0 and p1 are the same Closes #3061 --- vector/export_test.go | 32 ++++++++++++++++ vector/path.go | 7 +++- vector/path_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 vector/export_test.go create mode 100644 vector/path_test.go diff --git a/vector/export_test.go b/vector/export_test.go new file mode 100644 index 000000000..69c419826 --- /dev/null +++ b/vector/export_test.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vector + +type Point struct { + X, Y float32 +} + +func IsPointCloseToSegment(p, p0, p1 Point, allow float32) bool { + return isPointCloseToSegment(point{ + x: p.X, + y: p.Y, + }, point{ + x: p0.X, + y: p0.Y, + }, point{ + x: p1.X, + y: p1.Y, + }, allow) +} diff --git a/vector/path.go b/vector/path.go index 3146267ac..20dab7d68 100644 --- a/vector/path.go +++ b/vector/path.go @@ -232,12 +232,17 @@ func lineForTwoPoints(p0, p1 point) (a, b, c float32) { } // isPointCloseToSegment detects the distance between a segment (x0, y0)-(x1, y1) and a point (x, y) is less than allow. +// If p0 and p1 are the same, isPointCloseToSegment returns true when the distance between p0 and p is less than allow. func isPointCloseToSegment(p, p0, p1 point, allow float32) bool { + if p0 == p1 { + return allow*allow >= (p0.x-p.x)*(p0.x-p.x)+(p0.y-p.y)*(p0.y-p.y) + } + a, b, c := lineForTwoPoints(p0, p1) // The distance between a line ax+by+c=0 and (x0, y0) is // |ax0 + by0 + c| / √(a² + b²) - return allow*allow*(a*a+b*b) > (a*p.x+b*p.y+c)*(a*p.x+b*p.y+c) + return allow*allow*(a*a+b*b) >= (a*p.x+b*p.y+c)*(a*p.x+b*p.y+c) } // crossingPointForTwoLines returns a crossing point for two lines. diff --git a/vector/path_test.go b/vector/path_test.go new file mode 100644 index 000000000..daa32da91 --- /dev/null +++ b/vector/path_test.go @@ -0,0 +1,88 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vector_test + +import ( + "testing" + + "github.com/hajimehoshi/ebiten/v2/vector" +) + +func TestIsPointCloseToSegment(t *testing.T) { + testCases := []struct { + p vector.Point + p0 vector.Point + p1 vector.Point + allow float32 + want bool + }{ + { + p: vector.Point{0.5, 0.5}, + p0: vector.Point{0, 0}, + p1: vector.Point{1, 0}, + allow: 1, + want: true, + }, + { + p: vector.Point{0.5, 1.5}, + p0: vector.Point{0, 0}, + p1: vector.Point{1, 0}, + allow: 1, + want: false, + }, + { + p: vector.Point{0.5, 0.5}, + p0: vector.Point{0, 0}, + p1: vector.Point{1, 1}, + allow: 0, + want: true, + }, + { + p: vector.Point{0, 1}, + p0: vector.Point{0, 0}, + p1: vector.Point{1, 1}, + allow: 0.7, + want: false, + }, + { + p: vector.Point{0, 1}, + p0: vector.Point{0, 0}, + p1: vector.Point{1, 1}, + allow: 0.8, + want: true, + }, + { + // p0 and p1 are the same. + p: vector.Point{0, 1}, + p0: vector.Point{0.5, 0.5}, + p1: vector.Point{0.5, 0.5}, + allow: 0.7, + want: false, + }, + { + // p0 and p1 are the same. + p: vector.Point{0, 1}, + p0: vector.Point{0.5, 0.5}, + p1: vector.Point{0.5, 0.5}, + allow: 0.8, + want: true, + }, + } + for _, tc := range testCases { + if got := vector.IsPointCloseToSegment(tc.p, tc.p0, tc.p1, tc.allow); got != tc.want { + t.Errorf("got: %v, want: %v", got, tc.want) + } + } +} From 332da38565f7189244cb97a22b94326882a90548 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 18:18:09 +0900 Subject: [PATCH 043/188] internal/graphicsdriver/playstation5: update DrawTriangles A Go pointer in a C struct could cause some troubles. --- .../playstation5/graphics_playstation5.go | 42 +++++++------------ .../playstation5/graphics_playstation5.h | 27 ++++-------- 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.go b/internal/graphicsdriver/playstation5/graphics_playstation5.go index 187240b42..f04548a2a 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.go +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.go @@ -118,26 +118,24 @@ func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader, } func (g *Graphics) DrawTriangles(dst graphicsdriver.ImageID, srcs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shader graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error { - csrcs := make([]C.int, len(srcs)) + cSrcs := make([]C.int, len(srcs)) for i, src := range srcs { - csrcs[i] = C.int(src) + cSrcs[i] = C.int(src) } + defer runtime.KeepAlive(cSrcs) + cDstRegions := make([]C.ebitengine_DstRegion, len(dstRegions)) + defer runtime.KeepAlive(cDstRegions) for i, r := range dstRegions { cDstRegions[i] = C.ebitengine_DstRegion{ - Region: C.ebitengine_Rectangle{ - MinX: C.int(r.Region.Min.X), - MinY: C.int(r.Region.Min.Y), - MaxX: C.int(r.Region.Max.X), - MaxY: C.int(r.Region.Max.Y), - }, + MinX: C.int(r.Region.Min.X), + MinY: C.int(r.Region.Min.Y), + MaxX: C.int(r.Region.Max.X), + MaxY: C.int(r.Region.Max.Y), IndexCount: C.int(r.IndexCount), } } - cUniforms := make([]C.uint32_t, len(uniforms)) - for i, u := range uniforms { - cUniforms[i] = C.uint32_t(u) - } + cBlend := C.ebitengine_Blend{ BlendFactorSourceRGB: C.uint8_t(blend.BlendFactorSourceRGB), BlendFactorSourceAlpha: C.uint8_t(blend.BlendFactorSourceAlpha), @@ -147,23 +145,15 @@ func (g *Graphics) DrawTriangles(dst graphicsdriver.ImageID, srcs [graphics.Shad BlendOperationAlpha: C.uint8_t(blend.BlendOperationAlpha), } - args := C.ebitengine_DrawTrianglesArgs{ - Dst: C.int(dst), - Srcs: &csrcs[0], - SrcCount: C.int(len(csrcs)), - Shader: C.int(shader), - DstRegions: &cDstRegions[0], - DstRegionCount: C.int(len(cDstRegions)), - IndexOffset: C.int(indexOffset), - Blend: cBlend, - Uniforms: &cUniforms[0], - UniformCount: C.int(len(cUniforms)), - FillRule: C.int(fillRule), + cUniforms := make([]C.uint32_t, len(uniforms)) + defer runtime.KeepAlive(cUniforms) + for i, u := range uniforms { + cUniforms[i] = C.uint32_t(u) } - if err := C.ebitengine_DrawTriangles(&args); !C.ebitengine_IsErrorNil(&err) { + + if err := C.ebitengine_DrawTriangles(C.int(dst), &cSrcs[0], C.int(len(cSrcs)), C.int(shader), &cDstRegions[0], C.int(len(cDstRegions)), C.int(indexOffset), cBlend, &cUniforms[0], C.int(len(cUniforms)), C.int(fillRule)); !C.ebitengine_IsErrorNil(&err) { return newPlaystation5Error("(*playstation5.Graphics).DrawTriangles", err) } - runtime.KeepAlive(args) return nil } diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.h b/internal/graphicsdriver/playstation5/graphics_playstation5.h index 6e0f780b5..0fd002963 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.h +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.h @@ -34,15 +34,11 @@ static bool ebitengine_IsErrorNil(ebitengine_Error *err) { return err->Message == NULL && err->Code == 0; } -typedef struct ebitengine_Rectangle { +typedef struct ebitengine_DstRegion { int MinX; int MinY; int MaxX; int MaxY; -} ebitengine_Rectangle; - -typedef struct ebitengine_DstRegion { - ebitengine_Rectangle Region; int IndexCount; } ebitengine_DstRegion; @@ -55,26 +51,17 @@ typedef struct ebitengine_Blend { uint8_t BlendOperationAlpha; } ebitengine_Blend; -typedef struct ebitengine_DrawTrianglesArgs { - int Dst; - int *Srcs; - int SrcCount; - int Shader; - ebitengine_DstRegion *DstRegions; - int DstRegionCount; - int IndexOffset; - ebitengine_Blend Blend; - uint32_t *Uniforms; - int UniformCount; - int FillRule; -} ebitengine_DrawTrianglesArgs; - ebitengine_Error ebitengine_InitializeGraphics(void); ebitengine_Error ebitengine_NewImage(int *image, int width, int height); ebitengine_Error ebitengine_NewScreenFramebufferImage(int *image, int width, int height); void ebitengine_DisposeImage(int id); -ebitengine_Error ebitengine_DrawTriangles(ebitengine_DrawTrianglesArgs *args); + +ebitengine_Error +ebitengine_DrawTriangles(int dst, int *srcs, int srcCount, int shader, + ebitengine_DstRegion *dstRegions, int dstRegionCount, + int indexOffset, ebitengine_Blend blend, + uint32_t *uniforms, int uniformCount, int fillRule); ebitengine_Error ebitengine_NewShader(int *shader, const char *source); void ebitengine_DisposeShader(int id); From 89933bf0ab1ca02db49ab74305129cbbe441a091 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 10 Aug 2024 21:52:38 +0900 Subject: [PATCH 044/188] internal/graphicsdriver/playstation5: bug fix: compile error --- .../graphicsdriver/playstation5/graphics_playstation5.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp index e1fce3fb8..1ee8ec269 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp @@ -32,8 +32,11 @@ ebitengine_NewScreenFramebufferImage(int *image, int width, int height) { extern "C" void ebitengine_DisposeImage(int id) {} -extern "C" ebitengine_Error -ebitengine_DrawTriangles(ebitengine_DrawTrianglesArgs *args) { +ebitengine_Error +ebitengine_DrawTriangles(int dst, int *srcs, int srcCount, int shader, + ebitengine_DstRegion *dstRegions, int dstRegionCount, + int indexOffset, ebitengine_Blend blend, + uint32_t *uniforms, int uniformCount, int fillRule) { return {}; } From b9c24f786a4cea780a150e296356f9c6b9476ae0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 11 Aug 2024 16:41:11 +0900 Subject: [PATCH 045/188] ebiten: add BenchmarkDrawTriangles --- image_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/image_test.go b/image_test.go index 2f58e7f36..9fbc0d1c6 100644 --- a/image_test.go +++ b/image_test.go @@ -662,6 +662,59 @@ func BenchmarkDrawImage(b *testing.B) { } } +func BenchmarkDrawTriangles(b *testing.B) { + const w, h = 16, 16 + img0 := ebiten.NewImage(w, h) + img1 := ebiten.NewImage(w, h) + op := &ebiten.DrawTrianglesOptions{} + vs := []ebiten.Vertex{ + { + DstX: 0, + DstY: 0, + SrcX: 0, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + { + DstX: w, + DstY: 0, + SrcX: w, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + { + DstX: 0, + DstY: h, + SrcX: 0, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + { + DstX: w, + DstY: h, + SrcX: w, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + } + is := []uint16{0, 1, 2, 1, 2, 3} + for i := 0; i < b.N; i++ { + img0.DrawTriangles(vs, is, img1, op) + } +} + func TestImageLinearGraduation(t *testing.T) { img0 := ebiten.NewImage(2, 2) img0.WritePixels([]byte{ From 5f80f4b3de0d9e6c345cfa7e0ea4fb660b564a11 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 11 Aug 2024 21:27:10 +0900 Subject: [PATCH 046/188] all: refactoring --- internal/graphics/vertex.go | 48 ++++++++++++++--------------- internal/graphicscommand/command.go | 6 ++-- internal/mipmap/mipmap.go | 26 ++++++++-------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/internal/graphics/vertex.go b/internal/graphics/vertex.go index a11fc18c3..680c25afa 100644 --- a/internal/graphics/vertex.go +++ b/internal/graphics/vertex.go @@ -71,32 +71,32 @@ func QuadVertices(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty dst[6] = cb dst[7] = ca - dst[8] = adjustDestinationPixel(ax + tx) - dst[9] = adjustDestinationPixel(cx + ty) - dst[10] = u1 - dst[11] = v0 - dst[12] = cr - dst[13] = cg - dst[14] = cb - dst[15] = ca + dst[VertexFloatCount] = adjustDestinationPixel(ax + tx) + dst[VertexFloatCount+1] = adjustDestinationPixel(cx + ty) + dst[VertexFloatCount+2] = u1 + dst[VertexFloatCount+3] = v0 + dst[VertexFloatCount+4] = cr + dst[VertexFloatCount+5] = cg + dst[VertexFloatCount+6] = cb + dst[VertexFloatCount+7] = ca - dst[16] = adjustDestinationPixel(by + tx) - dst[17] = adjustDestinationPixel(dy + ty) - dst[18] = u0 - dst[19] = v1 - dst[20] = cr - dst[21] = cg - dst[22] = cb - dst[23] = ca + dst[2*VertexFloatCount] = adjustDestinationPixel(by + tx) + dst[2*VertexFloatCount+1] = adjustDestinationPixel(dy + ty) + dst[2*VertexFloatCount+2] = u0 + dst[2*VertexFloatCount+3] = v1 + dst[2*VertexFloatCount+4] = cr + dst[2*VertexFloatCount+5] = cg + dst[2*VertexFloatCount+6] = cb + dst[2*VertexFloatCount+7] = ca - dst[24] = adjustDestinationPixel(ax + by + tx) - dst[25] = adjustDestinationPixel(cx + dy + ty) - dst[26] = u1 - dst[27] = v1 - dst[28] = cr - dst[29] = cg - dst[30] = cb - dst[31] = ca + dst[3*VertexFloatCount] = adjustDestinationPixel(ax + by + tx) + dst[3*VertexFloatCount+1] = adjustDestinationPixel(cx + dy + ty) + dst[3*VertexFloatCount+2] = u1 + dst[3*VertexFloatCount+3] = v1 + dst[3*VertexFloatCount+4] = cr + dst[3*VertexFloatCount+5] = cg + dst[3*VertexFloatCount+6] = cb + dst[3*VertexFloatCount+7] = ca } func adjustDestinationPixel(x float32) float32 { diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index b9a921fc9..71bfc6434 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -183,9 +183,9 @@ func dstRegionFromVertices(vertices []float32) (minX, minY, maxX, maxY float32) maxX = negInf32 maxY = negInf32 - for i := 0; i < len(vertices)/graphics.VertexFloatCount; i++ { - x := vertices[graphics.VertexFloatCount*i] - y := vertices[graphics.VertexFloatCount*i+1] + for i := 0; i < len(vertices); i += graphics.VertexFloatCount { + x := vertices[i] + y := vertices[i+1] if x < minX { minX = x } diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 8463f8468..2e99c7657 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -73,20 +73,20 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Mipmap, verti level := 0 if !canSkipMipmap && srcs[0] != nil && canUseMipmap(srcs[0].imageType) { level = math.MaxInt32 - for i := 0; i < len(indices)/3; i++ { + for i := 0; i < len(indices); i += 3 { const n = graphics.VertexFloatCount - dx0 := vertices[n*indices[3*i]+0] - dy0 := vertices[n*indices[3*i]+1] - sx0 := vertices[n*indices[3*i]+2] - sy0 := vertices[n*indices[3*i]+3] - dx1 := vertices[n*indices[3*i+1]+0] - dy1 := vertices[n*indices[3*i+1]+1] - sx1 := vertices[n*indices[3*i+1]+2] - sy1 := vertices[n*indices[3*i+1]+3] - dx2 := vertices[n*indices[3*i+2]+0] - dy2 := vertices[n*indices[3*i+2]+1] - sx2 := vertices[n*indices[3*i+2]+2] - sy2 := vertices[n*indices[3*i+2]+3] + dx0 := vertices[n*indices[i]+0] + dy0 := vertices[n*indices[i]+1] + sx0 := vertices[n*indices[i]+2] + sy0 := vertices[n*indices[i]+3] + dx1 := vertices[n*indices[i+1]+0] + dy1 := vertices[n*indices[i+1]+1] + sx1 := vertices[n*indices[i+1]+2] + sy1 := vertices[n*indices[i+1]+3] + dx2 := vertices[n*indices[i+2]+0] + dy2 := vertices[n*indices[i+2]+1] + sx2 := vertices[n*indices[i+2]+2] + sy2 := vertices[n*indices[i+2]+3] if l := mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1); level > l { level = l } From 5e820be91107be45d493b6f913eecbfaf61336c1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 11 Aug 2024 22:13:49 +0900 Subject: [PATCH 047/188] internal/buffered: refactoring This is a preparation for adding members to Vertex. Updates #2640 --- internal/buffered/image.go | 82 ++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/internal/buffered/image.go b/internal/buffered/image.go index fa1551552..d02b2b72b 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -246,45 +246,51 @@ func (i *Image) syncPixelsIfNeeded() { cbf := float32(c[2]) / 0xff caf := float32(c[3]) / 0xff - vs[graphics.VertexFloatCount*4*idx] = dx - vs[graphics.VertexFloatCount*4*idx+1] = dy - vs[graphics.VertexFloatCount*4*idx+2] = sx - vs[graphics.VertexFloatCount*4*idx+3] = sy - vs[graphics.VertexFloatCount*4*idx+4] = crf - vs[graphics.VertexFloatCount*4*idx+5] = cgf - vs[graphics.VertexFloatCount*4*idx+6] = cbf - vs[graphics.VertexFloatCount*4*idx+7] = caf - vs[graphics.VertexFloatCount*4*idx+8] = dx + 1 - vs[graphics.VertexFloatCount*4*idx+9] = dy - vs[graphics.VertexFloatCount*4*idx+10] = sx + 1 - vs[graphics.VertexFloatCount*4*idx+11] = sy - vs[graphics.VertexFloatCount*4*idx+12] = crf - vs[graphics.VertexFloatCount*4*idx+13] = cgf - vs[graphics.VertexFloatCount*4*idx+14] = cbf - vs[graphics.VertexFloatCount*4*idx+15] = caf - vs[graphics.VertexFloatCount*4*idx+16] = dx - vs[graphics.VertexFloatCount*4*idx+17] = dy + 1 - vs[graphics.VertexFloatCount*4*idx+18] = sx - vs[graphics.VertexFloatCount*4*idx+19] = sy + 1 - vs[graphics.VertexFloatCount*4*idx+20] = crf - vs[graphics.VertexFloatCount*4*idx+21] = cgf - vs[graphics.VertexFloatCount*4*idx+22] = cbf - vs[graphics.VertexFloatCount*4*idx+23] = caf - vs[graphics.VertexFloatCount*4*idx+24] = dx + 1 - vs[graphics.VertexFloatCount*4*idx+25] = dy + 1 - vs[graphics.VertexFloatCount*4*idx+26] = sx + 1 - vs[graphics.VertexFloatCount*4*idx+27] = sy + 1 - vs[graphics.VertexFloatCount*4*idx+28] = crf - vs[graphics.VertexFloatCount*4*idx+29] = cgf - vs[graphics.VertexFloatCount*4*idx+30] = cbf - vs[graphics.VertexFloatCount*4*idx+31] = caf + vidx := 4 * idx + iidx := 6 * idx - is[6*idx] = uint32(4 * idx) - is[6*idx+1] = uint32(4*idx + 1) - is[6*idx+2] = uint32(4*idx + 2) - is[6*idx+3] = uint32(4*idx + 1) - is[6*idx+4] = uint32(4*idx + 2) - is[6*idx+5] = uint32(4*idx + 3) + vs[graphics.VertexFloatCount*vidx] = dx + vs[graphics.VertexFloatCount*vidx+1] = dy + vs[graphics.VertexFloatCount*vidx+2] = sx + vs[graphics.VertexFloatCount*vidx+3] = sy + vs[graphics.VertexFloatCount*vidx+4] = crf + vs[graphics.VertexFloatCount*vidx+5] = cgf + vs[graphics.VertexFloatCount*vidx+6] = cbf + vs[graphics.VertexFloatCount*vidx+7] = caf + + vs[graphics.VertexFloatCount*(vidx+1)] = dx + 1 + vs[graphics.VertexFloatCount*(vidx+1)+1] = dy + vs[graphics.VertexFloatCount*(vidx+1)+2] = sx + 1 + vs[graphics.VertexFloatCount*(vidx+1)+3] = sy + vs[graphics.VertexFloatCount*(vidx+1)+4] = crf + vs[graphics.VertexFloatCount*(vidx+1)+5] = cgf + vs[graphics.VertexFloatCount*(vidx+1)+6] = cbf + vs[graphics.VertexFloatCount*(vidx+1)+7] = caf + + vs[graphics.VertexFloatCount*(vidx+2)] = dx + vs[graphics.VertexFloatCount*(vidx+2)+1] = dy + 1 + vs[graphics.VertexFloatCount*(vidx+2)+2] = sx + vs[graphics.VertexFloatCount*(vidx+2)+3] = sy + 1 + vs[graphics.VertexFloatCount*(vidx+2)+4] = crf + vs[graphics.VertexFloatCount*(vidx+2)+5] = cgf + vs[graphics.VertexFloatCount*(vidx+2)+6] = cbf + vs[graphics.VertexFloatCount*(vidx+2)+7] = caf + + vs[graphics.VertexFloatCount*(vidx+3)] = dx + 1 + vs[graphics.VertexFloatCount*(vidx+3)+1] = dy + 1 + vs[graphics.VertexFloatCount*(vidx+3)+2] = sx + 1 + vs[graphics.VertexFloatCount*(vidx+3)+3] = sy + 1 + vs[graphics.VertexFloatCount*(vidx+3)+4] = crf + vs[graphics.VertexFloatCount*(vidx+3)+5] = cgf + vs[graphics.VertexFloatCount*(vidx+3)+6] = cbf + vs[graphics.VertexFloatCount*(vidx+3)+7] = caf + + is[iidx] = uint32(vidx) + is[iidx+1] = uint32(vidx + 1) + is[iidx+2] = uint32(vidx + 2) + is[iidx+3] = uint32(vidx + 1) + is[iidx+4] = uint32(vidx + 2) + is[iidx+5] = uint32(vidx + 3) idx++ } From e6807794f23ed35cfbafa1a2313cb0183a6d6fd0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 11 Aug 2024 22:29:13 +0900 Subject: [PATCH 048/188] internal/mipmap: refactoring --- internal/mipmap/mipmap.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 2e99c7657..6e86a0108 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -74,19 +74,21 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Mipmap, verti if !canSkipMipmap && srcs[0] != nil && canUseMipmap(srcs[0].imageType) { level = math.MaxInt32 for i := 0; i < len(indices); i += 3 { - const n = graphics.VertexFloatCount - dx0 := vertices[n*indices[i]+0] - dy0 := vertices[n*indices[i]+1] - sx0 := vertices[n*indices[i]+2] - sy0 := vertices[n*indices[i]+3] - dx1 := vertices[n*indices[i+1]+0] - dy1 := vertices[n*indices[i+1]+1] - sx1 := vertices[n*indices[i+1]+2] - sy1 := vertices[n*indices[i+1]+3] - dx2 := vertices[n*indices[i+2]+0] - dy2 := vertices[n*indices[i+2]+1] - sx2 := vertices[n*indices[i+2]+2] - sy2 := vertices[n*indices[i+2]+3] + idx0 := indices[i] + idx1 := indices[i+1] + idx2 := indices[i+2] + dx0 := vertices[graphics.VertexFloatCount*idx0] + dy0 := vertices[graphics.VertexFloatCount*idx0+1] + sx0 := vertices[graphics.VertexFloatCount*idx0+2] + sy0 := vertices[graphics.VertexFloatCount*idx0+3] + dx1 := vertices[graphics.VertexFloatCount*idx1] + dy1 := vertices[graphics.VertexFloatCount*idx1+1] + sx1 := vertices[graphics.VertexFloatCount*idx1+2] + sy1 := vertices[graphics.VertexFloatCount*idx1+3] + dx2 := vertices[graphics.VertexFloatCount*idx2] + dy2 := vertices[graphics.VertexFloatCount*idx2+1] + sx2 := vertices[graphics.VertexFloatCount*idx2+2] + sy2 := vertices[graphics.VertexFloatCount*idx2+3] if l := mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1); level > l { level = l } From 9a751d7c266fcda76e53d8a2dd1f1e96ab83fbaa Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 11 Aug 2024 22:33:21 +0900 Subject: [PATCH 049/188] internal/mipmap: refactoring --- internal/mipmap/mipmap.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 6e86a0108..2fe10cca7 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -111,11 +111,10 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Mipmap, verti } if level != 0 { if img := src.level(level); img != nil { - const n = graphics.VertexFloatCount s := float32(pow2(level)) - for i := 0; i < len(vertices)/n; i++ { - vertices[i*n+2] /= s - vertices[i*n+3] /= s + for i := 0; i < len(vertices); i += graphics.VertexFloatCount { + vertices[i+2] /= s + vertices[i+3] /= s } imgs[i] = img continue @@ -150,12 +149,10 @@ func (m *Mipmap) level(level int) *buffered.Image { var src *buffered.Image vs := make([]float32, 4*graphics.VertexFloatCount) - shader := atlas.NearestFilterShader switch { case level == 1: src = m.orig graphics.QuadVertices(vs, 0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) - shader = atlas.LinearFilterShader case level > 1: src = m.level(level - 1) if src == nil { @@ -165,7 +162,6 @@ func (m *Mipmap) level(level int) *buffered.Image { w := sizeForLevel(m.width, level-1) h := sizeForLevel(m.height, level-1) graphics.QuadVertices(vs, 0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) - shader = atlas.LinearFilterShader default: panic(fmt.Sprintf("mipmap: invalid level: %d", level)) } @@ -188,7 +184,7 @@ func (m *Mipmap) level(level int) *buffered.Image { s := buffered.NewImage(w2, h2, m.imageType) dstRegion := image.Rect(0, 0, w2, h2) - s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillRuleFillAll) + s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.LinearFilterShader, nil, graphicsdriver.FillRuleFillAll) m.setImg(level, s) return m.imgs[level] From 6cd00f3b88af5e3b023d46d56776c4337e41f888 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 11 Aug 2024 22:41:21 +0900 Subject: [PATCH 050/188] internal/graphicsdriver/opengl: exclude playstation5 --- internal/graphicsdriver/opengl/graphics_debug.go | 2 +- internal/graphicsdriver/opengl/graphics_notdebug.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/graphicsdriver/opengl/graphics_debug.go b/internal/graphicsdriver/opengl/graphics_debug.go index ecbfe0b6d..4044fbc6a 100644 --- a/internal/graphicsdriver/opengl/graphics_debug.go +++ b/internal/graphicsdriver/opengl/graphics_debug.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build ebitenginegldebug +//go:build !playstation5 && ebitenginegldebug package opengl diff --git a/internal/graphicsdriver/opengl/graphics_notdebug.go b/internal/graphicsdriver/opengl/graphics_notdebug.go index 45ddc6524..5e583c3a0 100644 --- a/internal/graphicsdriver/opengl/graphics_notdebug.go +++ b/internal/graphicsdriver/opengl/graphics_notdebug.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !ebitenginegldebug +//go:build !playstation5 && !ebitenginegldebug package opengl From b6ab7a10c1f57218499ced930a881ef1738af946 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 12 Aug 2024 00:27:28 +0900 Subject: [PATCH 051/188] internal/graphics: unify QuadVertices This is a preparation for adding members to Vertex. Updates #2640 --- image.go | 4 +- internal/atlas/image.go | 20 +++------- internal/atlas/image_test.go | 9 ++--- internal/buffered/image_test.go | 2 +- internal/graphics/vertex.go | 55 ++++++++++++++++++++++++-- internal/graphicscommand/image_test.go | 9 ++--- internal/mipmap/mipmap.go | 4 +- internal/ui/image.go | 6 +-- 8 files changed, 71 insertions(+), 38 deletions(-) diff --git a/image.go b/image.go index 7f51a3e34..146c1a13c 100644 --- a/image.go +++ b/image.go @@ -247,7 +247,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { colorm, cr, cg, cb, ca := colorMToScale(options.ColorM.affineColorM()) cr, cg, cb, ca = options.ColorScale.apply(cr, cg, cb, ca) vs := i.ensureTmpVertices(4 * graphics.VertexFloatCount) - graphics.QuadVertices(vs, float32(sx0), float32(sy0), float32(sx1), float32(sy1), a, b, c, d, tx, ty, cr, cg, cb, ca) + graphics.QuadVerticesFromSrcAndMatrix(vs, float32(sx0), float32(sy0), float32(sx1), float32(sy1), a, b, c, d, tx, ty, cr, cg, cb, ca) is := graphics.QuadIndices() srcs := [graphics.ShaderSrcImageCount]*ui.Image{img.image} @@ -829,7 +829,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR vs := i.ensureTmpVertices(4 * graphics.VertexFloatCount) // Do not use srcRegions[0].Dx() and srcRegions[0].Dy() as these might be empty. - graphics.QuadVertices(vs, + graphics.QuadVerticesFromSrcAndMatrix(vs, float32(srcRegions[0].Min.X), float32(srcRegions[0].Min.Y), float32(srcRegions[0].Min.X+width), float32(srcRegions[0].Min.Y+height), a, b, c, d, tx, ty, cr, cg, cb, ca) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index da6943789..0aed86376 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -43,16 +43,6 @@ func min(a, b int) int { return b } -// quadVertices returns vertices to render a quad. These values are passed to graphicscommand.Image. -func quadVertices(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) []float32 { - return []float32{ - dx0, dy0, sx0, sy0, cr, cg, cb, ca, - dx1, dy0, sx1, sy0, cr, cg, cb, ca, - dx0, dy1, sx0, sy1, cr, cg, cb, ca, - dx1, dy1, sx1, sy1, cr, cg, cb, ca, - } -} - func appendDeferred(f func()) { deferredM.Lock() defer deferredM.Unlock() @@ -149,7 +139,8 @@ func (b *backend) extendIfNeeded(width, height int) { srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{b.image} sw, sh := b.image.InternalSize() - vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) @@ -174,7 +165,8 @@ func newClearedImage(width, height int, screen bool) *graphicscommand.Image { } func clearImage(i *graphicscommand.Image, region image.Rectangle) { - vs := quadVertices(float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) is := graphics.QuadIndices() i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) } @@ -353,7 +345,7 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { w, h := float32(i.width), float32(i.height) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) @@ -384,7 +376,7 @@ func (i *Image) putOnSourceBackend() { w, h := float32(i.width), float32(i.height) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 51d829a38..3c5c351ab 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -50,12 +50,9 @@ func quadVertices(sw, sh, x, y int, scalex float32) []float32 { sy0 := float32(0) sx1 := float32(sw) sy1 := float32(sh) - return []float32{ - dx0, dy0, sx0, sy0, 1, 1, 1, 1, - dx1, dy0, sx1, sy0, 1, 1, 1, 1, - dx0, dy1, sx0, sy1, 1, 1, 1, 1, - dx1, dy1, sx1, sy1, 1, 1, 1, 1, - } + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1) + return vs } const bigSize = 2049 diff --git a/internal/buffered/image_test.go b/internal/buffered/image_test.go index cb027129a..2dbd8cd71 100644 --- a/internal/buffered/image_test.go +++ b/internal/buffered/image_test.go @@ -52,7 +52,7 @@ func TestUnsyncedPixels(t *testing.T) { // Flush unsynced pixel cache. src := buffered.NewImage(16, 16, atlas.ImageTypeRegular) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVertices(vs, 0, 0, 16, 16, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, 16, 16, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, 16, 16) sr := [graphics.ShaderSrcImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)} diff --git a/internal/graphics/vertex.go b/internal/graphics/vertex.go index 680c25afa..95d7768b1 100644 --- a/internal/graphics/vertex.go +++ b/internal/graphics/vertex.go @@ -50,16 +50,16 @@ func QuadIndices() []uint32 { return quadIndices } -// QuadVertices sets a float32 slice for a quadrangle. -// QuadVertices sets a slice that never overlaps with other slices returned this function, -// and users can do optimization based on this fact. -func QuadVertices(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) { +// QuadVerticesFromSrcAndMatrix sets a float32 slice for a quadrangle. +func QuadVerticesFromSrcAndMatrix(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) { x := sx1 - sx0 y := sy1 - sy0 ax, by, cx, dy := a*x, b*y, c*x, d*y u0, v0, u1, v1 := sx0, sy0, sx1, sy1 // This function is very performance-sensitive and implement in a very dumb way. + + // Remove the boundary check. dst = dst[:4*VertexFloatCount] dst[0] = adjustDestinationPixel(tx) @@ -99,6 +99,53 @@ func QuadVertices(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty dst[3*VertexFloatCount+7] = ca } +// QuadVerticesFromDstAndSrc sets a float32 slice for a quadrangle. +func QuadVerticesFromDstAndSrc(dst []float32, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) { + dx0 = adjustDestinationPixel(dx0) + dy0 = adjustDestinationPixel(dy0) + dx1 = adjustDestinationPixel(dx1) + dy1 = adjustDestinationPixel(dy1) + + // Remove the boundary check. + dst = dst[:4*VertexFloatCount] + + dst[0] = dx0 + dst[1] = dy0 + dst[2] = sx0 + dst[3] = sy0 + dst[4] = cr + dst[5] = cg + dst[6] = cb + dst[7] = ca + + dst[VertexFloatCount] = dx1 + dst[VertexFloatCount+1] = dy0 + dst[VertexFloatCount+2] = sx1 + dst[VertexFloatCount+3] = sy0 + dst[VertexFloatCount+4] = cr + dst[VertexFloatCount+5] = cg + dst[VertexFloatCount+6] = cb + dst[VertexFloatCount+7] = ca + + dst[2*VertexFloatCount] = dx0 + dst[2*VertexFloatCount+1] = dy1 + dst[2*VertexFloatCount+2] = sx0 + dst[2*VertexFloatCount+3] = sy1 + dst[2*VertexFloatCount+4] = cr + dst[2*VertexFloatCount+5] = cg + dst[2*VertexFloatCount+6] = cb + dst[2*VertexFloatCount+7] = ca + + dst[3*VertexFloatCount] = dx1 + dst[3*VertexFloatCount+1] = dy1 + dst[3*VertexFloatCount+2] = sx1 + dst[3*VertexFloatCount+3] = sy1 + dst[3*VertexFloatCount+4] = cr + dst[3*VertexFloatCount+5] = cg + dst[3*VertexFloatCount+6] = cb + dst[3*VertexFloatCount+7] = ca +} + func adjustDestinationPixel(x float32) float32 { // Avoid the center of the pixel, which is problematic (#929, #1171). // Instead, align the vertices with about 1/3 pixels. diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index a60175edb..946d9c736 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -43,12 +43,9 @@ func TestMain(m *testing.M) { } func quadVertices(w, h float32) []float32 { - return []float32{ - 0, 0, 0, 0, 1, 1, 1, 1, - w, 0, w, 0, 1, 1, 1, 1, - 0, w, 0, h, 1, 1, 1, 1, - w, h, w, h, 1, 1, 1, 1, - } + vs := make([]float32, 8*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1) + return vs } func TestClear(t *testing.T) { diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 2fe10cca7..2145f2e66 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -152,7 +152,7 @@ func (m *Mipmap) level(level int) *buffered.Image { switch { case level == 1: src = m.orig - graphics.QuadVertices(vs, 0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) case level > 1: src = m.level(level - 1) if src == nil { @@ -161,7 +161,7 @@ func (m *Mipmap) level(level int) *buffered.Image { } w := sizeForLevel(m.width, level-1) h := sizeForLevel(m.height, level-1) - graphics.QuadVertices(vs, 0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) default: panic(fmt.Sprintf("mipmap: invalid level: %d", level)) } diff --git a/internal/ui/image.go b/internal/ui/image.go index c49d854cd..987d0bcc3 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -158,7 +158,7 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) { i.tmpVerticesForFill = make([]float32, 4*graphics.VertexFloatCount) } // i.tmpVerticesForFill can be reused as this is sent to DrawTriangles immediately. - graphics.QuadVertices( + graphics.QuadVerticesFromSrcAndMatrix( i.tmpVerticesForFill, 1, 1, float32(i.ui.whiteImage.width-1), float32(i.ui.whiteImage.height-1), float32(i.width), 0, 0, float32(i.height), 0, 0, @@ -235,7 +235,7 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Im i.tmpVerticesForCopying = make([]float32, 4*graphics.VertexFloatCount) } // i.tmpVerticesForCopying can be reused as this is sent to DrawTriangles immediately. - graphics.QuadVertices( + graphics.QuadVerticesFromSrcAndMatrix( i.tmpVerticesForCopying, float32(i.region.Min.X), float32(i.region.Min.Y), float32(i.region.Max.X), float32(i.region.Max.Y), bigOffscreenScale, 0, 0, bigOffscreenScale, 0, 0, @@ -279,7 +279,7 @@ func (i *bigOffscreenImage) flush() { i.tmpVerticesForFlushing = make([]float32, 4*graphics.VertexFloatCount) } // i.tmpVerticesForFlushing can be reused as this is sent to DrawTriangles in this function. - graphics.QuadVertices( + graphics.QuadVerticesFromSrcAndMatrix( i.tmpVerticesForFlushing, 0, 0, float32(i.region.Dx()*bigOffscreenScale), float32(i.region.Dy()*bigOffscreenScale), 1.0/bigOffscreenScale, 0, 0, 1.0/bigOffscreenScale, float32(i.region.Min.X), float32(i.region.Min.Y), From 9e208eee811a97672e371becf87964646e90c505 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 12 Aug 2024 02:42:36 +0900 Subject: [PATCH 052/188] all: use QuadVerticesFromDstAndSrc when possible --- internal/atlas/image.go | 4 ++-- internal/buffered/image_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 0aed86376..ff6d8d75f 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -345,7 +345,7 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { w, h := float32(i.width), float32(i.height) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) @@ -376,7 +376,7 @@ func (i *Image) putOnSourceBackend() { w, h := float32(i.width), float32(i.height) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) diff --git a/internal/buffered/image_test.go b/internal/buffered/image_test.go index 2dbd8cd71..4a9db4c25 100644 --- a/internal/buffered/image_test.go +++ b/internal/buffered/image_test.go @@ -52,7 +52,7 @@ func TestUnsyncedPixels(t *testing.T) { // Flush unsynced pixel cache. src := buffered.NewImage(16, 16, atlas.ImageTypeRegular) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, 16, 16, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, 16, 16, 0, 0, 16, 16, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, 16, 16) sr := [graphics.ShaderSrcImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)} From 435c8b75ebc001f3d1afda243bbe68ba3cfe38d1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 12 Aug 2024 03:56:47 +0900 Subject: [PATCH 053/188] internal/graphicsdriver/opengl: automatically adjust the array buffer layout Updates #2640 --- internal/graphicsdriver/opengl/program.go | 61 +++++++++++++---------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index 1a6aa05b0..df8414480 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -53,14 +53,14 @@ func (a *arrayBufferLayout) names() []string { return ns } -// totalBytes returns the size in bytes for one element of the array buffer. -func (a *arrayBufferLayout) totalBytes() int { +// float32Count returns the total float32 count for one element of the array buffer. +func (a *arrayBufferLayout) float32Count() int { if a.total != 0 { return a.total } t := 0 for _, p := range a.parts { - t += floatSizeInBytes * p.num + t += p.num } a.total = t return a.total @@ -71,10 +71,10 @@ func (a *arrayBufferLayout) enable(context *context) { for i := range a.parts { context.ctx.EnableVertexAttribArray(uint32(i)) } - total := a.totalBytes() + total := a.float32Count() offset := 0 for i, p := range a.parts { - context.ctx.VertexAttribPointer(uint32(i), int32(p.num), gl.FLOAT, false, int32(total), offset) + context.ctx.VertexAttribPointer(uint32(i), int32(p.num), gl.FLOAT, false, int32(floatSizeInBytes*total), offset) offset += floatSizeInBytes * p.num } } @@ -88,28 +88,39 @@ func (a *arrayBufferLayout) disable(context *context) { } // theArrayBufferLayout is the array buffer layout for Ebitengine. -var theArrayBufferLayout = arrayBufferLayout{ - // Note that GL_MAX_VERTEX_ATTRIBS is at least 16. - parts: []arrayBufferLayoutPart{ - { - name: "A0", - num: 2, - }, - { - name: "A1", - num: 2, - }, - { - name: "A2", - num: 4, - }, - }, -} +var theArrayBufferLayout arrayBufferLayout func init() { - vertexFloatCount := theArrayBufferLayout.totalBytes() / floatSizeInBytes - if graphics.VertexFloatCount != vertexFloatCount { - panic(fmt.Sprintf("vertex float num must be %d but %d", graphics.VertexFloatCount, vertexFloatCount)) + theArrayBufferLayout = arrayBufferLayout{ + // Note that GL_MAX_VERTEX_ATTRIBS is at least 16. + parts: []arrayBufferLayoutPart{ + { + name: "A0", + num: 2, + }, + { + name: "A1", + num: 2, + }, + { + name: "A2", + num: 4, + }, + }, + } + n := theArrayBufferLayout.float32Count() + if n > graphics.VertexFloatCount { + panic("opengl: the array buffer layout is too large") + } + if n < graphics.VertexFloatCount { + d := graphics.VertexFloatCount - n + if d > 4 { + panic("opengl: the array buffer layout is too small") + } + theArrayBufferLayout.parts = append(theArrayBufferLayout.parts, arrayBufferLayoutPart{ + name: "A3", + num: d, + }) } } From abaad0b6f213f8dfeb0e5577139a7a24ba0addb2 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 14 Aug 2024 09:15:56 +0900 Subject: [PATCH 054/188] .github/workflows: use the released version of Go 1.23 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e2c3c574b..574bbb129 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - go: ['1.19.x', '1.20.x', '1.21.x', '1.22.x', '1.23.0-rc.2'] + go: ['1.19.x', '1.20.x', '1.21.x', '1.22.x', '1.23.x'] name: Test with Go ${{ matrix.go }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} env: From 949d6abe6ff8d5841f42dd424961d7de102cd4de Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 14 Aug 2024 10:43:28 +0900 Subject: [PATCH 055/188] all: update dependencies --- go.mod | 10 +++++----- go.sum | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 2254ad9fc..ba009dbc7 100644 --- a/go.mod +++ b/go.mod @@ -15,15 +15,15 @@ require ( github.com/jezek/xgb v1.1.1 github.com/jfreymuth/oggvorbis v1.0.5 github.com/kisielk/errcheck v1.7.0 - golang.org/x/image v0.18.0 + golang.org/x/image v0.19.0 golang.org/x/sync v0.8.0 - golang.org/x/sys v0.23.0 - golang.org/x/text v0.16.0 - golang.org/x/tools v0.23.0 + golang.org/x/sys v0.24.0 + golang.org/x/text v0.17.0 + golang.org/x/tools v0.24.0 ) require ( github.com/jfreymuth/vorbis v1.0.2 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect - golang.org/x/mod v0.19.0 // indirect + golang.org/x/mod v0.20.0 // indirect ) diff --git a/go.sum b/go.sum index 93b8d9c86..3f82bd797 100644 --- a/go.sum +++ b/go.sum @@ -37,15 +37,16 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= +golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -74,8 +75,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -92,14 +93,15 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 26038c2ac111a5b67aab390aa3c8f769b914e98f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 16 Aug 2024 23:57:50 +0900 Subject: [PATCH 056/188] internal/ui: bug fix: an unexpected scroll bar was shown Closes #3066 --- internal/ui/ui_js.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index f3c90da44..f9b4a6f2a 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -512,6 +512,7 @@ func (u *UserInterface) init() error { canvasStyle.Set("height", "100%") canvasStyle.Set("margin", "0") canvasStyle.Set("padding", "0") + canvasStyle.Set("display", "block") // Make the canvas focusable. canvas.Call("setAttribute", "tabindex", 1) From 17322c9c3b02d6b8fce0cf00a464e33013af5ca0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 17 Aug 2024 23:05:22 +0900 Subject: [PATCH 057/188] all: reduce files for PS5 --- .../playstation5/graphics_playstation5.go | 2 +- .../playstation5/shader_paystation5.go | 23 ------------------- shaderprecomp/shaderprecomp_playstation5.go | 8 ------- 3 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 internal/graphicsdriver/playstation5/shader_paystation5.go diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.go b/internal/graphicsdriver/playstation5/graphics_playstation5.go index f04548a2a..be1f2bd70 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.go +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.go @@ -99,7 +99,7 @@ func (g *Graphics) SetVsyncEnabled(enabled bool) { } func (g *Graphics) NeedsClearingScreen() bool { - return false + return true } func (g *Graphics) MaxImageSize() int { diff --git a/internal/graphicsdriver/playstation5/shader_paystation5.go b/internal/graphicsdriver/playstation5/shader_paystation5.go deleted file mode 100644 index c4c9ef1e5..000000000 --- a/internal/graphicsdriver/playstation5/shader_paystation5.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build playstation5 - -// This is a separated pure Go file so that the `shaderprecomp` package can use this without Cgo. - -package playstation5 - -func RegisterPrecompiledShaders(source []byte, vertex, pixel []byte) { - // TODO: Implement this. -} diff --git a/shaderprecomp/shaderprecomp_playstation5.go b/shaderprecomp/shaderprecomp_playstation5.go index 71229a048..b6b269d14 100644 --- a/shaderprecomp/shaderprecomp_playstation5.go +++ b/shaderprecomp/shaderprecomp_playstation5.go @@ -20,7 +20,6 @@ import ( "io" "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/playstation5" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/pssl" ) @@ -41,10 +40,3 @@ func CompileToPSSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) er } return nil } - -// RegisterPlayStationShaders registers a precompiled PlayStation Shader for a shader source. -// -// RegisterPlayStationShaders is concurrent-safe. -func RegisterPlayStationShaders(source *ShaderSource, vertexShader, pixelShader []byte) { - playstation5.RegisterPrecompiledShaders(source.source, vertexShader, pixelShader) -} From 9c931c4c97f87e89d4443db457aa05252098927a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 18 Aug 2024 00:52:34 +0900 Subject: [PATCH 058/188] shaderprecomp: remove a file for PS5 --- shaderprecomp/shaderprecomp_playstation5.go | 42 --------------------- 1 file changed, 42 deletions(-) delete mode 100644 shaderprecomp/shaderprecomp_playstation5.go diff --git a/shaderprecomp/shaderprecomp_playstation5.go b/shaderprecomp/shaderprecomp_playstation5.go deleted file mode 100644 index b6b269d14..000000000 --- a/shaderprecomp/shaderprecomp_playstation5.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build playstation5 - -package shaderprecomp - -import ( - "io" - - "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir/pssl" -) - -// CompileToPSSL compiles the shader source to PlayStaton Shader Language to writers. -// -// CompileToPSSL is concurrent-safe. -func CompileToPSSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) error { - ir, err := graphics.CompileShader(source.source) - if err != nil { - return err - } - vs, ps := pssl.Compile(ir) - if _, err = vertexWriter.Write([]byte(vs)); err != nil { - return err - } - if _, err = pixelWriter.Write([]byte(ps)); err != nil { - return err - } - return nil -} From 48f79af884f947c83ccfa5baceafeb53e27dcdb7 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 18 Aug 2024 11:09:02 +0900 Subject: [PATCH 059/188] shaderprecomp: retract shaderprecomp package With #2984, we realized some considerations: * Builtin shaders don't have to or should not be exposed. * The shader complation processes like what the examples/shaderprecomp does can be provided by this package Thus, until we decide a better API design, we'd like to retract the package once. Updates #2861 --- examples/shaderprecomp/defaultshader.go | 33 ----- examples/shaderprecomp/fxc/dummy.fxc | 1 - examples/shaderprecomp/fxc/gen.go | 132 ------------------ examples/shaderprecomp/fxc/generate.go | 19 --- examples/shaderprecomp/main.go | 74 ---------- .../shaderprecomp/metallib/dummy.metallib | 1 - examples/shaderprecomp/metallib/gen.go | 95 ------------- examples/shaderprecomp/metallib/generate.go | 19 --- examples/shaderprecomp/register_darwin.go | 48 ------- examples/shaderprecomp/register_others.go | 27 ---- examples/shaderprecomp/register_windows.go | 61 -------- shaderprecomp/shaderprecomp.go | 43 ------ shaderprecomp/shaderprecomp_darwin.go | 48 ------- shaderprecomp/shaderprecomp_windows.go | 66 --------- 14 files changed, 667 deletions(-) delete mode 100644 examples/shaderprecomp/defaultshader.go delete mode 100644 examples/shaderprecomp/fxc/dummy.fxc delete mode 100644 examples/shaderprecomp/fxc/gen.go delete mode 100644 examples/shaderprecomp/fxc/generate.go delete mode 100644 examples/shaderprecomp/main.go delete mode 100644 examples/shaderprecomp/metallib/dummy.metallib delete mode 100644 examples/shaderprecomp/metallib/gen.go delete mode 100644 examples/shaderprecomp/metallib/generate.go delete mode 100644 examples/shaderprecomp/register_darwin.go delete mode 100644 examples/shaderprecomp/register_others.go delete mode 100644 examples/shaderprecomp/register_windows.go delete mode 100644 shaderprecomp/shaderprecomp.go delete mode 100644 shaderprecomp/shaderprecomp_darwin.go delete mode 100644 shaderprecomp/shaderprecomp_windows.go diff --git a/examples/shaderprecomp/defaultshader.go b/examples/shaderprecomp/defaultshader.go deleted file mode 100644 index 1c7f70197..000000000 --- a/examples/shaderprecomp/defaultshader.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020 The Ebiten Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build ignore - -//kage:unit pixels - -package main - -var Time float -var Cursor vec2 - -func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { - pos := (dstPos.xy - imageDstOrigin()) / imageDstSize() - pos += Cursor / imageDstSize() / 4 - clr := 0.0 - clr += sin(pos.x*cos(Time/15)*80) + cos(pos.y*cos(Time/15)*10) - clr += sin(pos.y*sin(Time/10)*40) + cos(pos.x*sin(Time/25)*40) - clr += sin(pos.x*sin(Time/5)*10) + sin(pos.y*sin(Time/35)*80) - clr *= sin(Time/10) * 0.5 - return vec4(clr, clr*0.5, sin(clr+Time/3)*0.75, 1) -} diff --git a/examples/shaderprecomp/fxc/dummy.fxc b/examples/shaderprecomp/fxc/dummy.fxc deleted file mode 100644 index 2a3f3dd8e..000000000 --- a/examples/shaderprecomp/fxc/dummy.fxc +++ /dev/null @@ -1 +0,0 @@ -This is a dummy .fxc file to trick Go's embed package. diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go deleted file mode 100644 index 0025fe7b3..000000000 --- a/examples/shaderprecomp/fxc/gen.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build ignore - -// This is a program to generate precompiled HLSL blobs (FXC files). -// -// See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc. -package main - -import ( - "bufio" - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - - "github.com/hajimehoshi/ebiten/v2/shaderprecomp" -) - -func main() { - if err := run(); err != nil { - panic(err) - } -} - -func run() error { - if _, err := exec.LookPath("fxc.exe"); err != nil { - if errors.Is(err, exec.ErrNotFound) { - fmt.Fprintln(os.Stderr, "fxc.exe not found. Please install Windows SDK.") - fmt.Fprintln(os.Stderr, "See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc for more details.") - fmt.Fprintln(os.Stderr, "HINT: On PowerShell, you can add a path to the PATH environment variable temporarily like:") - fmt.Fprintln(os.Stderr) - fmt.Fprintln(os.Stderr, ` & (Get-Process -Id $PID).Path { $env:PATH="C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64;"+$env:PATH; go generate .\examples\shaderprecomp\fxc\ }`) - fmt.Fprintln(os.Stderr) - os.Exit(1) - } - return err - } - - tmpdir, err := os.MkdirTemp("", "") - if err != nil { - return err - } - defer os.RemoveAll(tmpdir) - - srcs := shaderprecomp.AppendBuildinShaderSources(nil) - - defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) - if err != nil { - return err - } - srcs = append(srcs, shaderprecomp.NewShaderSource(defaultSrcBytes)) - - for i, src := range srcs { - // Avoid using errgroup.Group. - // Compiling sources in parallel causes a mixed error message on the console. - if err := compile(src, i, tmpdir); err != nil { - return err - } - } - return nil -} - -func generateHSLSFiles(source *shaderprecomp.ShaderSource, index int, tmpdir string) (vs, ps string, err error) { - vsHLSLFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d_vs.hlsl", index)) - vsf, err := os.Create(vsHLSLFilePath) - if err != nil { - return "", "", err - } - defer vsf.Close() - - psHLSLFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d_ps.hlsl", index)) - psf, err := os.Create(psHLSLFilePath) - if err != nil { - return "", "", err - } - defer psf.Close() - - vsfw := bufio.NewWriter(vsf) - psfw := bufio.NewWriter(psf) - if err := shaderprecomp.CompileToHLSL(vsfw, psfw, source); err != nil { - return "", "", err - } - if err := vsfw.Flush(); err != nil { - return "", "", err - } - if err := psfw.Flush(); err != nil { - return "", "", err - } - - return vsHLSLFilePath, psHLSLFilePath, nil -} - -func compile(source *shaderprecomp.ShaderSource, index int, tmpdir string) error { - // Generate HLSL files. Make sure this process doesn't have any handlers of the files. - // Without closing the files, fxc.exe cannot access the files. - vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(source, index, tmpdir) - if err != nil { - return err - } - - vsFXCFilePath := fmt.Sprintf("%d_vs.fxc", index) - cmd := exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLVertexShaderProfile, "/E", shaderprecomp.HLSLVertexShaderEntryPoint, "/Fo", vsFXCFilePath, vsHLSLFilePath) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return err - } - - psFXCFilePath := fmt.Sprintf("%d_ps.fxc", index) - cmd = exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLPixelShaderProfile, "/E", shaderprecomp.HLSLPixelShaderEntryPoint, "/Fo", psFXCFilePath, psHLSLFilePath) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return err - } - - return nil -} diff --git a/examples/shaderprecomp/fxc/generate.go b/examples/shaderprecomp/fxc/generate.go deleted file mode 100644 index 601e6532c..000000000 --- a/examples/shaderprecomp/fxc/generate.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build windows - -//go:generate go run gen.go - -package fxc diff --git a/examples/shaderprecomp/main.go b/examples/shaderprecomp/main.go deleted file mode 100644 index 60d85edce..000000000 --- a/examples/shaderprecomp/main.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - _ "embed" - "log" - - "github.com/hajimehoshi/ebiten/v2" - "github.com/hajimehoshi/ebiten/v2/ebitenutil" -) - -//go:embed defaultshader.go -var defaultShaderSourceBytes []byte - -type Game struct { - defaultShader *ebiten.Shader - counter int -} - -func (g *Game) Update() error { - g.counter++ - - if g.defaultShader == nil { - s, err := ebiten.NewShader(defaultShaderSourceBytes) - if err != nil { - return err - } - g.defaultShader = s - } - return nil -} - -func (g *Game) Draw(screen *ebiten.Image) { - cx, cy := ebiten.CursorPosition() - w, h := screen.Bounds().Dx(), screen.Bounds().Dy() - op := &ebiten.DrawRectShaderOptions{} - op.Uniforms = map[string]interface{}{ - "Time": float32(g.counter) / float32(ebiten.TPS()), - "Cursor": []float32{float32(cx), float32(cy)}, - } - screen.DrawRectShader(w, h, g.defaultShader, op) - - msg := `This is a test for shader precompilation. -Precompilation works only on macOS so far. -Note that this example still works even without shader precompilation.` - ebitenutil.DebugPrint(screen, msg) -} - -func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { - return outsideWidth, outsideHeight -} - -func main() { - if err := registerPrecompiledShaders(); err != nil { - log.Fatal(err) - } - ebiten.SetWindowTitle("Ebitengine Example (Shader Precompilation)") - if err := ebiten.RunGame(&Game{}); err != nil { - log.Fatal(err) - } -} diff --git a/examples/shaderprecomp/metallib/dummy.metallib b/examples/shaderprecomp/metallib/dummy.metallib deleted file mode 100644 index 4f8a5ca8c..000000000 --- a/examples/shaderprecomp/metallib/dummy.metallib +++ /dev/null @@ -1 +0,0 @@ -This is a dummy .metallib file to trick Go's embed package. diff --git a/examples/shaderprecomp/metallib/gen.go b/examples/shaderprecomp/metallib/gen.go deleted file mode 100644 index fdb65e738..000000000 --- a/examples/shaderprecomp/metallib/gen.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build ignore - -// This is a program to generate precompiled Metal libraries. -// -// See https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. -package main - -import ( - "bufio" - "fmt" - "os" - "os/exec" - "path/filepath" - - "github.com/hajimehoshi/ebiten/v2/shaderprecomp" -) - -func main() { - if err := run(); err != nil { - panic(err) - } -} - -func run() error { - tmpdir, err := os.MkdirTemp("", "") - if err != nil { - return err - } - defer os.RemoveAll(tmpdir) - - srcs := shaderprecomp.AppendBuildinShaderSources(nil) - - defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) - if err != nil { - return err - } - srcs = append(srcs, shaderprecomp.NewShaderSource(defaultSrcBytes)) - - for i, src := range srcs { - // Avoid using errgroup.Group. - // Compiling sources in parallel causes a mixed error message on the console. - if err := compile(src, i, tmpdir); err != nil { - return err - } - } - return nil -} - -func compile(source *shaderprecomp.ShaderSource, index int, tmpdir string) error { - metalFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d.metal", index)) - - f, err := os.Create(metalFilePath) - if err != nil { - return err - } - defer f.Close() - - w := bufio.NewWriter(f) - if err := shaderprecomp.CompileToMSL(w, source); err != nil { - return err - } - if err := w.Flush(); err != nil { - return err - } - - irFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d.ir", index)) - cmd := exec.Command("xcrun", "-sdk", "macosx", "metal", "-o", irFilePath, "-c", metalFilePath) - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return err - } - - metallibFilePath := fmt.Sprintf("%d.metallib", index) - cmd = exec.Command("xcrun", "-sdk", "macosx", "metallib", "-o", metallibFilePath, irFilePath) - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return err - } - - return nil -} diff --git a/examples/shaderprecomp/metallib/generate.go b/examples/shaderprecomp/metallib/generate.go deleted file mode 100644 index 658ccc004..000000000 --- a/examples/shaderprecomp/metallib/generate.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build darwin - -//go:generate go run gen.go - -package metallib diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go deleted file mode 100644 index c2fcb0902..000000000 --- a/examples/shaderprecomp/register_darwin.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "embed" - "errors" - "fmt" - "io/fs" - "os" - - "github.com/hajimehoshi/ebiten/v2/shaderprecomp" -) - -//go:embed metallib/*.metallib -var metallibs embed.FS - -func registerPrecompiledShaders() error { - srcs := shaderprecomp.AppendBuildinShaderSources(nil) - srcs = append(srcs, shaderprecomp.NewShaderSource(defaultShaderSourceBytes)) - - for i, src := range srcs { - name := fmt.Sprintf("%d.metallib", i) - lib, err := metallibs.ReadFile("metallib/" + name) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - fmt.Fprintf(os.Stderr, "precompiled Metal library %s was not found. Run 'go generate' for 'metallib' directory to generate them.\n", name) - continue - } - return err - } - shaderprecomp.RegisterMetalLibrary(src, lib) - } - - return nil -} diff --git a/examples/shaderprecomp/register_others.go b/examples/shaderprecomp/register_others.go deleted file mode 100644 index b0e37ea6f..000000000 --- a/examples/shaderprecomp/register_others.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !darwin && !windows - -package main - -import ( - "fmt" - "os" -) - -func registerPrecompiledShaders() error { - fmt.Fprintf(os.Stderr, "precompiled shaders are not available in this environment.\n") - return nil -} diff --git a/examples/shaderprecomp/register_windows.go b/examples/shaderprecomp/register_windows.go deleted file mode 100644 index f27b84ecf..000000000 --- a/examples/shaderprecomp/register_windows.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "embed" - "errors" - "fmt" - "io/fs" - "os" - - "github.com/hajimehoshi/ebiten/v2/shaderprecomp" -) - -// https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/ - -//go:embed fxc/*.fxc -var fxcs embed.FS - -func registerPrecompiledShaders() error { - srcs := shaderprecomp.AppendBuildinShaderSources(nil) - srcs = append(srcs, shaderprecomp.NewShaderSource(defaultShaderSourceBytes)) - - for i, src := range srcs { - vsname := fmt.Sprintf("%d_vs.fxc", i) - vs, err := fxcs.ReadFile("fxc/" + vsname) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", vsname) - continue - } - return err - } - - psname := fmt.Sprintf("%d_ps.fxc", i) - ps, err := fxcs.ReadFile("fxc/" + psname) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", psname) - continue - } - return err - } - - shaderprecomp.RegisterFXCs(src, vs, ps) - } - - return nil -} diff --git a/shaderprecomp/shaderprecomp.go b/shaderprecomp/shaderprecomp.go deleted file mode 100644 index 94eb0b408..000000000 --- a/shaderprecomp/shaderprecomp.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package shaderprecomp - -import ( - "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" -) - -// AppendBuildinShaderSources appends all the built-in shader sources to the given slice. -// -// Do not modify the content of the shader source. -// -// AppendBuildinShaderSources is concurrent-safe. -func AppendBuildinShaderSources(sources []*ShaderSource) []*ShaderSource { - for _, s := range builtinshader.AppendShaderSources(nil) { - sources = append(sources, NewShaderSource(s)) - } - return sources -} - -// ShaderSource is an object encapsulating a shader source code. -type ShaderSource struct { - source []byte -} - -// NewShaderSource creates a new ShaderSource object from the given source code. -func NewShaderSource(source []byte) *ShaderSource { - return &ShaderSource{ - source: source, - } -} diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go deleted file mode 100644 index 51bb06843..000000000 --- a/shaderprecomp/shaderprecomp_darwin.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !playstation5 - -package shaderprecomp - -import ( - "io" - - "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl" -) - -// CompileToMSL compiles the shader source to Metal Shader Language, and writes the result to w. -// -// CompileToMSL is concurrent-safe. -func CompileToMSL(w io.Writer, source *ShaderSource) error { - ir, err := graphics.CompileShader(source.source) - if err != nil { - return err - } - if _, err = w.Write([]byte(msl.Compile(ir))); err != nil { - return err - } - return nil -} - -// RegisterMetalLibrary registers a precompiled Metal library for a shader source. -// library must be the content of a .metallib file. -// For more details, see https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. -// -// RegisterMetalLibrary is concurrent-safe. -func RegisterMetalLibrary(source *ShaderSource, library []byte) { - metal.RegisterPrecompiledLibrary(source.source, library) -} diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go deleted file mode 100644 index 19121e5c6..000000000 --- a/shaderprecomp/shaderprecomp_windows.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !playstation5 - -package shaderprecomp - -import ( - "io" - - "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/directx" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" -) - -const ( - // HLSLVertexShaderProfile is the target profile for vertex shaders. - HLSLVertexShaderProfile = directx.VertexShaderProfile - - // HLSLPixelShaderProfile is the target profile for pixel shaders. - HLSLPixelShaderProfile = directx.PixelShaderProfile - - // HLSLVertexShaderEntryPoint is the entry point name for vertex shaders. - HLSLVertexShaderEntryPoint = directx.VertexShaderEntryPoint - - // HLSLPixelShaderEntryPoint is the entry point name for pixel shaders. - HLSLPixelShaderEntryPoint = directx.PixelShaderEntryPoint -) - -// CompileToHLSL compiles the shader source to High-Level Shader Language to writers. -// -// CompileToHLSL is concurrent-safe. -func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) error { - ir, err := graphics.CompileShader(source.source) - if err != nil { - return err - } - vs, ps := hlsl.Compile(ir) - if _, err = vertexWriter.Write([]byte(vs)); err != nil { - return err - } - if _, err = pixelWriter.Write([]byte(ps)); err != nil { - return err - } - return nil -} - -// RegisterFXCs registers a precompiled HLSL (FXC) for a shader source. -// vertexFXC and pixelFXC must be the content of .fxc files generated by `fxc` command. -// For more details, see https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-using. -// -// RegisterFXCs is concurrent-safe. -func RegisterFXCs(source *ShaderSource, vertexFXC, pixelFXC []byte) { - directx.RegisterPrecompiledFXCs(source.source, vertexFXC, pixelFXC) -} From 8f32cc19c53d245bddd887bf117ad3244cbcd0a6 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 18 Aug 2024 11:13:35 +0900 Subject: [PATCH 060/188] audio: update comments --- audio/audio.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/audio/audio.go b/audio/audio.go index 25bca9d95..22a9d604a 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -14,15 +14,14 @@ // Package audio provides audio players. // -// The stream format must be 16-bit little endian and 2 channels. The format is as follows: +// The stream format must be 16-bit little endian or 32-bit float little endian, and 2 channels. The format is as follows: // // [data] = [sample 1] [sample 2] [sample 3] ... // [sample *] = [channel 1] ... // [channel *] = [byte 1] [byte 2] ... // -// An audio context (audio.Context object) has a sample rate you can specify and all streams you want to play must have the same -// sample rate. However, decoders in e.g. audio/mp3 package adjust sample rate automatically, -// and you don't have to care about it as long as you use those decoders. +// An audio context (audio.Context object) has a sample rate you can specify +// and all streams you want to play must have the same sample rate. // // An audio context can generate 'players' (audio.Player objects), // and you can play sound by calling Play function of players. From ba1af60480aa02fd6455ee27c6fbd8b9f2ce3ee1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 18 Aug 2024 11:14:25 +0900 Subject: [PATCH 061/188] all: update comments --- colorm.go | 2 +- colorm/colorm.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/colorm.go b/colorm.go index b82d68484..ea69b14fd 100644 --- a/colorm.go +++ b/colorm.go @@ -20,7 +20,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/affine" ) -// ColorMDim is a dimension of a ColorM. +// ColorMDim is the dimension of a ColorM. // // Deprecated: as of v2.5. Use the colorm package instead. const ColorMDim = affine.ColorMDim diff --git a/colorm/colorm.go b/colorm/colorm.go index a3e1f0bee..8da54783c 100644 --- a/colorm/colorm.go +++ b/colorm/colorm.go @@ -24,7 +24,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" ) -// Dim is a dimension of a ColorM. +// Dim is the dimension of a ColorM. const Dim = affine.ColorMDim // ColorM represents a matrix to transform coloring when rendering an image. From 789388618da811611b7fd1ac5e90a4d427047469 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 18 Aug 2024 11:54:03 +0900 Subject: [PATCH 062/188] all: add playstation5 dummy package --- playstation5/doc_playstaton5.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 playstation5/doc_playstaton5.go diff --git a/playstation5/doc_playstaton5.go b/playstation5/doc_playstaton5.go new file mode 100644 index 000000000..e718d9478 --- /dev/null +++ b/playstation5/doc_playstaton5.go @@ -0,0 +1,20 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build playstation5 + +// Package playstation5 provides utilities for PlayStation 5. +package playstation5 + +// The actual implementation will be provided by another repository using the -overlay option. From 35f9b1c22425a07bca5b11ba6b8dc26d1a6cd2cb Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 19 Aug 2024 23:43:48 +0900 Subject: [PATCH 063/188] ebiten: add RunGameOptions.DisableHiDPI Closes #2987 --- examples/highdpi/main.go | 21 +++++++++++++++++++-- internal/ui/ui.go | 1 + internal/ui/ui_js.go | 8 ++++++++ run.go | 10 ++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/examples/highdpi/main.go b/examples/highdpi/main.go index 23f653593..83993b1fa 100644 --- a/examples/highdpi/main.go +++ b/examples/highdpi/main.go @@ -15,9 +15,11 @@ package main import ( + "flag" "fmt" _ "image/jpeg" "log" + "runtime" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" @@ -93,7 +95,16 @@ func (g *Game) Draw(screen *ebiten.Image) { op.Filter = ebiten.FilterLinear screen.DrawImage(g.highDPIImage, op) - ebitenutil.DebugPrint(screen, fmt.Sprintf("(Init) Device Scale Ratio: %0.2f", scale)) + msg := fmt.Sprintf("Device Scale Ratio: %0.2f", scale) + if runtime.GOOS == "js" { + if !*flagDisable { + msg += "\nHiDPI rendering is enabled. You can disable HiDPI by -disable flag." + } else { + msg += "\nHiDPI rendering is disabled." + } + } + // TODO: The texts might be too small. Improve legibility. + ebitenutil.DebugPrint(screen, msg) } func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { @@ -103,10 +114,16 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return int(float64(outsideWidth) * s), int(float64(outsideHeight) * s) } +var flagDisable = flag.Bool("disable", false, "disables HiDPI rendering (only on browsers)") + func main() { + flag.Parse() + ebiten.SetWindowSize(640, 480) ebiten.SetWindowTitle("High DPI (Ebitengine Demo)") - if err := ebiten.RunGame(NewGame()); err != nil { + op := &ebiten.RunGameOptions{} + op.DisableHiDPI = *flagDisable + if err := ebiten.RunGameWithOptions(NewGame(), op); err != nil { log.Fatal(err) } } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index c04d1c859..b8f3991d8 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -175,6 +175,7 @@ type RunOptions struct { ScreenTransparent bool SkipTaskbar bool SingleThread bool + DisableHiDPI bool X11ClassName string X11InstanceName string } diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index f9b4a6f2a..a8d58fa8f 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -96,6 +96,7 @@ type userInterfaceImpl struct { cursorShape CursorShape onceUpdateCalled bool lastCaptureExitTime time.Time + hiDPIEnabled bool context *context inputState InputState @@ -465,6 +466,7 @@ func (u *UserInterface) init() error { runnableOnUnfocused: true, savedCursorX: math.NaN(), savedCursorY: math.NaN(), + hiDPIEnabled: true, } // document is undefined on node.js @@ -762,6 +764,8 @@ func (u *UserInterface) shouldFocusFirst(options *RunOptions) bool { func (u *UserInterface) initOnMainThread(options *RunOptions) error { u.setRunning(true) + u.hiDPIEnabled = !options.DisableHiDPI + if u.shouldFocusFirst(options) { canvas.Call("focus") } @@ -815,6 +819,10 @@ func (m *Monitor) Name() string { } func (m *Monitor) DeviceScaleFactor() float64 { + if !theUI.hiDPIEnabled { + return 1 + } + if m.deviceScaleFactor != 0 { return m.deviceScaleFactor } diff --git a/run.go b/run.go index 2291562e5..4b75970e8 100644 --- a/run.go +++ b/run.go @@ -274,6 +274,15 @@ type RunGameOptions struct { // The default (zero) value is false, which means that the single thread mode is disabled. SingleThread bool + // DisableHiDPI indicates whether the rendering for HiDPI is disabled or not. + // If HiDPI is disabled, the device scale factor is always 1 i.e. Monitor's DeviceScaleFactor always returns 1. + // This is useful to get a better performance on HiDPI displays, in the expense of rendering quality. + // + // DisableHiDPI is available only on browsers. + // + // The default (zero) value is false, which means that HiDPI is enabled. + DisableHiDPI bool + // X11DisplayName is a class name in the ICCCM WM_CLASS window property. X11ClassName string @@ -703,6 +712,7 @@ func toUIRunOptions(options *RunGameOptions) *ui.RunOptions { ScreenTransparent: options.ScreenTransparent, SkipTaskbar: options.SkipTaskbar, SingleThread: options.SingleThread, + DisableHiDPI: options.DisableHiDPI, X11ClassName: options.X11ClassName, X11InstanceName: options.X11InstanceName, } From b131264c778abeea9ef763f47ab3185d4b327435 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 20 Aug 2024 21:06:27 +0900 Subject: [PATCH 064/188] text/v2: add comments --- text/v2/text.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/v2/text.go b/text/v2/text.go index 32d750b71..97d996e62 100644 --- a/text/v2/text.go +++ b/text/v2/text.go @@ -126,7 +126,10 @@ type Glyph struct { // Image is a rasterized glyph image. // Image is a grayscale image i.e. RGBA values are the same. - // Image should be used as a render source and should not be modified. + // + // Image should be used as a render source and must not be modified. + // + // Image can be nil. Image *ebiten.Image // X is the X position to render this glyph. From a3d084e2de1cb619b2636dad3b1c82573e4f1034 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 22 Aug 2024 23:36:20 +0900 Subject: [PATCH 065/188] text/v2: add Glyph.Origin{X,Y} Closes #3070 --- examples/text/main.go | 19 +++++++++++--- examples/texti18n/main.go | 52 +++++++++++++++++++++++++++++++++++++++ text/v2/gotext.go | 7 ++++-- text/v2/gox.go | 2 ++ text/v2/text.go | 6 +++++ 5 files changed, 80 insertions(+), 6 deletions(-) diff --git a/examples/text/main.go b/examples/text/main.go index a7e44d908..37322c606 100644 --- a/examples/text/main.go +++ b/examples/text/main.go @@ -21,7 +21,9 @@ import ( "math" "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/examples/resources/fonts" + "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/hajimehoshi/ebiten/v2/vector" ) @@ -58,10 +60,8 @@ func init() { } type Game struct { - counter int - kanjiText []rune - kanjiTextColor color.RGBA - glyphs []text.Glyph + glyphs []text.Glyph + showOrigins bool } func (g *Game) Update() error { @@ -71,10 +71,15 @@ func (g *Game) Update() error { op.LineSpacing = mplusNormalFace.Size * 1.5 g.glyphs = text.AppendGlyphs(g.glyphs, sampleText, mplusNormalFace, op) } + if inpututil.IsKeyJustPressed(ebiten.KeyO) { + g.showOrigins = !g.showOrigins + } return nil } func (g *Game) Draw(screen *ebiten.Image) { + ebitenutil.DebugPrint(screen, "Press O to show/hide origins") + gray := color.RGBA{0x80, 0x80, 0x80, 0xff} { @@ -147,6 +152,12 @@ func (g *Game) Draw(screen *ebiten.Image) { op.ColorScale.Scale(r, g, b, 1) screen.DrawImage(gl.Image, op) } + + if g.showOrigins { + for _, gl := range g.glyphs { + vector.DrawFilledCircle(screen, x+float32(gl.OriginX), y+float32(gl.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + } + } } } diff --git a/examples/texti18n/main.go b/examples/texti18n/main.go index 779490dc3..3eb2b5dc0 100644 --- a/examples/texti18n/main.go +++ b/examples/texti18n/main.go @@ -23,7 +23,9 @@ import ( "golang.org/x/text/language" "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/examples/resources/fonts" + "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/hajimehoshi/ebiten/v2/vector" ) @@ -109,13 +111,19 @@ const ( ) type Game struct { + showOrigins bool } func (g *Game) Update() error { + if inpututil.IsKeyJustPressed(ebiten.KeyO) { + g.showOrigins = !g.showOrigins + } return nil } func (g *Game) Draw(screen *ebiten.Image) { + ebitenutil.DebugPrint(screen, "Press O to show/hide origins") + gray := color.RGBA{0x80, 0x80, 0x80, 0xff} { @@ -133,6 +141,13 @@ func (g *Game) Draw(screen *ebiten.Image) { op := &text.DrawOptions{} op.GeoM.Translate(float64(x), float64(y)) text.Draw(screen, arabicText, f, op) + + if g.showOrigins { + op := &text.LayoutOptions{} + for _, g := range text.AppendGlyphs(nil, arabicText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + } + } } { const hindiText = "चूंकि मानव परिवार के सभी सदस्यों के जन्मजात गौरव और समान" @@ -147,6 +162,13 @@ func (g *Game) Draw(screen *ebiten.Image) { op := &text.DrawOptions{} op.GeoM.Translate(float64(x), float64(y)) text.Draw(screen, hindiText, f, op) + + if g.showOrigins { + op := &text.LayoutOptions{} + for _, g := range text.AppendGlyphs(nil, hindiText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + } + } } { const myanmarText = "လူခပ်သိမ်း၏ မျိုးရိုးဂုဏ်သိက္ခာနှင့်တကွ" @@ -161,6 +183,13 @@ func (g *Game) Draw(screen *ebiten.Image) { op := &text.DrawOptions{} op.GeoM.Translate(float64(x), float64(y)) text.Draw(screen, myanmarText, f, op) + + if g.showOrigins { + op := &text.LayoutOptions{} + for _, g := range text.AppendGlyphs(nil, myanmarText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + } + } } { const thaiText = "โดยที่การยอมรับนับถือเกียรติศักดิ์ประจำตัว" @@ -175,6 +204,13 @@ func (g *Game) Draw(screen *ebiten.Image) { op := &text.DrawOptions{} op.GeoM.Translate(float64(x), float64(y)) text.Draw(screen, thaiText, f, op) + + if g.showOrigins { + op := &text.LayoutOptions{} + for _, g := range text.AppendGlyphs(nil, thaiText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + } + } } { const mongolianText = "ᠬᠦᠮᠦᠨ ᠪᠦᠷ ᠲᠥᠷᠥᠵᠦ ᠮᠡᠨᠳᠡᠯᠡᠬᠦ\nᠡᠷᠬᠡ ᠴᠢᠯᠥᠭᠡ ᠲᠡᠢ᠂ ᠠᠳᠠᠯᠢᠬᠠᠨ" @@ -194,6 +230,14 @@ func (g *Game) Draw(screen *ebiten.Image) { op.GeoM.Translate(float64(x), float64(y)) op.LineSpacing = lineSpacing text.Draw(screen, mongolianText, f, op) + + if g.showOrigins { + op := &text.LayoutOptions{} + op.LineSpacing = lineSpacing + for _, g := range text.AppendGlyphs(nil, mongolianText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + } + } } { const japaneseText = "あのイーハトーヴォの\nすきとおった風、\n夏でも底に冷たさを\nもつ青いそら…\nあHello World.あ" @@ -212,6 +256,14 @@ func (g *Game) Draw(screen *ebiten.Image) { op.GeoM.Translate(float64(x), float64(y)) op.LineSpacing = lineSpacing text.Draw(screen, japaneseText, f, op) + + if g.showOrigins { + op := &text.LayoutOptions{} + op.LineSpacing = lineSpacing + for _, g := range text.AppendGlyphs(nil, japaneseText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + } + } } } diff --git a/text/v2/gotext.go b/text/v2/gotext.go index 7b42576dd..527bd0810 100644 --- a/text/v2/gotext.go +++ b/text/v2/gotext.go @@ -304,10 +304,11 @@ func (g *GoTextFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffse } _, gs := g.Source.shape(line, g) for _, glyph := range gs { - img, imgX, imgY := g.glyphImage(glyph, origin.Add(fixed.Point26_6{ + o := origin.Add(fixed.Point26_6{ X: glyph.shapingGlyph.XOffset, Y: -glyph.shapingGlyph.YOffset, - })) + }) + img, imgX, imgY := g.glyphImage(glyph, o) // Append a glyph even if img is nil. // This is necessary to return index information for control characters. glyphs = append(glyphs, Glyph{ @@ -317,6 +318,8 @@ func (g *GoTextFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffse Image: img, X: float64(imgX), Y: float64(imgY), + OriginX: fixed26_6ToFloat64(o.X), + OriginY: fixed26_6ToFloat64(o.Y), }) origin = origin.Add(fixed.Point26_6{ X: glyph.shapingGlyph.XAdvance, diff --git a/text/v2/gox.go b/text/v2/gox.go index 625215d82..d101d2dc7 100644 --- a/text/v2/gox.go +++ b/text/v2/gox.go @@ -124,6 +124,8 @@ func (s *GoXFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset i Image: img, X: float64(imgX), Y: float64(imgY), + OriginX: fixed26_6ToFloat64(origin.X), + OriginY: fixed26_6ToFloat64(origin.Y), }) origin.X += a prevR = r diff --git a/text/v2/text.go b/text/v2/text.go index 97d996e62..834a3f340 100644 --- a/text/v2/text.go +++ b/text/v2/text.go @@ -141,6 +141,12 @@ type Glyph struct { // The position is determined in a sequence of characters given at AppendGlyphs. // The position's origin is the first character's origin position. Y float64 + + // OriginX is the X position of the origin of this glyph. + OriginX float64 + + // OriginY is the Y position of the origin of this glyph. + OriginY float64 } // Advance returns the advanced distance from the origin position when rendering the given text with the given face. From 6056fc59eba91a15c6352d2ad20b7b071fc65b8f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 23 Aug 2024 00:13:19 +0900 Subject: [PATCH 066/188] text/v2: update comments about CacheGlyphs The example was not clear. --- text/v2/text.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/text/v2/text.go b/text/v2/text.go index 834a3f340..5e4171ee0 100644 --- a/text/v2/text.go +++ b/text/v2/text.go @@ -239,10 +239,7 @@ func Measure(text string, face Face, lineSpacingInPixels float64) (width, height // CacheGlyphs creates all such variations for one rune, while Draw and AppendGlyphs create only necessary glyphs. // // Draw and AppendGlyphs automatically create and cache necessary glyphs, so usually you don't have to call CacheGlyphs explicitly. -// However, for example, when you call Draw for each rune of one big text, Draw tries to create the glyph cache and render it for each rune. -// This is very inefficient because creating a glyph image and rendering it are different operations -// (`(*ebiten.Image).WritePixels` and `(*ebiten.Image).DrawImage`) and can never be merged as one draw call. -// CacheGlyphs creates necessary glyphs without rendering them so that these operations are likely merged into one draw call regardless of the size of the text. +// If you really care about the performance, CacheGlyphs might be useful. // // CacheGlyphs is concurrent-safe. func CacheGlyphs(text string, face Face) { From df266e8acfee0373fe42dd5955cae85990f74ac2 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 23 Aug 2024 03:54:11 +0900 Subject: [PATCH 067/188] text/v2: add Glyph.OriginOffset{X,Y} Closes #3070 --- examples/texti18n/main.go | 20 +++++++++++++------- text/v2/gotext.go | 6 ++++-- text/v2/gox.go | 2 ++ text/v2/text.go | 8 ++++++++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/examples/texti18n/main.go b/examples/texti18n/main.go index 3eb2b5dc0..336a419c4 100644 --- a/examples/texti18n/main.go +++ b/examples/texti18n/main.go @@ -122,7 +122,7 @@ func (g *Game) Update() error { } func (g *Game) Draw(screen *ebiten.Image) { - ebitenutil.DebugPrint(screen, "Press O to show/hide origins") + ebitenutil.DebugPrint(screen, "Press O to show/hide origins.\nRed points are the original origin positions.\nThe green points are the origin positions after applying the offset.") gray := color.RGBA{0x80, 0x80, 0x80, 0xff} @@ -134,7 +134,7 @@ func (g *Game) Draw(screen *ebiten.Image) { Size: 24, Language: language.Arabic, } - x, y := screenWidth-20, 40 + x, y := screenWidth-20, 50 w, h := text.Measure(arabicText, f, 0) // The left upper point is not x but x-w, since the text runs in the rigth-to-left direction. vector.DrawFilledRect(screen, float32(x)-float32(w), float32(y), float32(w), float32(h), gray, false) @@ -145,6 +145,7 @@ func (g *Game) Draw(screen *ebiten.Image) { if g.showOrigins { op := &text.LayoutOptions{} for _, g := range text.AppendGlyphs(nil, arabicText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true) vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) } } @@ -156,7 +157,7 @@ func (g *Game) Draw(screen *ebiten.Image) { Size: 24, Language: language.Hindi, } - x, y := 20, 100 + x, y := 20, 110 w, h := text.Measure(hindiText, f, 0) vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false) op := &text.DrawOptions{} @@ -166,6 +167,7 @@ func (g *Game) Draw(screen *ebiten.Image) { if g.showOrigins { op := &text.LayoutOptions{} for _, g := range text.AppendGlyphs(nil, hindiText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true) vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) } } @@ -177,7 +179,7 @@ func (g *Game) Draw(screen *ebiten.Image) { Size: 24, Language: language.Burmese, } - x, y := 20, 160 + x, y := 20, 170 w, h := text.Measure(myanmarText, f, 0) vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false) op := &text.DrawOptions{} @@ -187,6 +189,7 @@ func (g *Game) Draw(screen *ebiten.Image) { if g.showOrigins { op := &text.LayoutOptions{} for _, g := range text.AppendGlyphs(nil, myanmarText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true) vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) } } @@ -198,7 +201,7 @@ func (g *Game) Draw(screen *ebiten.Image) { Size: 24, Language: language.Thai, } - x, y := 20, 220 + x, y := 20, 230 w, h := text.Measure(thaiText, f, 0) vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false) op := &text.DrawOptions{} @@ -208,6 +211,7 @@ func (g *Game) Draw(screen *ebiten.Image) { if g.showOrigins { op := &text.LayoutOptions{} for _, g := range text.AppendGlyphs(nil, thaiText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true) vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) } } @@ -223,7 +227,7 @@ func (g *Game) Draw(screen *ebiten.Image) { Script: language.MustParseScript("Mong"), } const lineSpacing = 48 - x, y := 20, 280 + x, y := 20, 290 w, h := text.Measure(mongolianText, f, lineSpacing) vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false) op := &text.DrawOptions{} @@ -235,6 +239,7 @@ func (g *Game) Draw(screen *ebiten.Image) { op := &text.LayoutOptions{} op.LineSpacing = lineSpacing for _, g := range text.AppendGlyphs(nil, mongolianText, f, op) { + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true) vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) } } @@ -248,7 +253,7 @@ func (g *Game) Draw(screen *ebiten.Image) { Language: language.Japanese, } const lineSpacing = 48 - x, y := screenWidth-20, 280 + x, y := screenWidth-20, 290 w, h := text.Measure(japaneseText, f, lineSpacing) // The left upper point is not x but x-w, since the text runs in the rigth-to-left direction as the secondary direction. vector.DrawFilledRect(screen, float32(x)-float32(w), float32(y), float32(w), float32(h), gray, false) @@ -262,6 +267,7 @@ func (g *Game) Draw(screen *ebiten.Image) { op.LineSpacing = lineSpacing for _, g := range text.AppendGlyphs(nil, japaneseText, f, op) { vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true) + vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true) } } } diff --git a/text/v2/gotext.go b/text/v2/gotext.go index 527bd0810..227cc92b0 100644 --- a/text/v2/gotext.go +++ b/text/v2/gotext.go @@ -318,8 +318,10 @@ func (g *GoTextFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffse Image: img, X: float64(imgX), Y: float64(imgY), - OriginX: fixed26_6ToFloat64(o.X), - OriginY: fixed26_6ToFloat64(o.Y), + OriginX: fixed26_6ToFloat64(origin.X), + OriginY: fixed26_6ToFloat64(origin.Y), + OriginOffsetX: fixed26_6ToFloat64(glyph.shapingGlyph.XOffset), + OriginOffsetY: fixed26_6ToFloat64(-glyph.shapingGlyph.YOffset), }) origin = origin.Add(fixed.Point26_6{ X: glyph.shapingGlyph.XAdvance, diff --git a/text/v2/gox.go b/text/v2/gox.go index d101d2dc7..13914db94 100644 --- a/text/v2/gox.go +++ b/text/v2/gox.go @@ -126,6 +126,8 @@ func (s *GoXFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset i Y: float64(imgY), OriginX: fixed26_6ToFloat64(origin.X), OriginY: fixed26_6ToFloat64(origin.Y), + OriginOffsetX: 0, + OriginOffsetY: 0, }) origin.X += a prevR = r diff --git a/text/v2/text.go b/text/v2/text.go index 5e4171ee0..302a20d39 100644 --- a/text/v2/text.go +++ b/text/v2/text.go @@ -147,6 +147,14 @@ type Glyph struct { // OriginY is the Y position of the origin of this glyph. OriginY float64 + + // OriginOffsetX is the adjustment value to the X position of the origin of this glyph. + // OriginOffsetX is usually 0, but can be non-zero for some special glyphs or glyphs in the vertical text layout. + OriginOffsetX float64 + + // OriginOffsetY is the adjustment value to the Y position of the origin of this glyph. + // OriginOffsetY is usually 0, but can be non-zero for some special glyphs or glyphs in the vertical text layout. + OriginOffsetY float64 } // Advance returns the advanced distance from the origin position when rendering the given text with the given face. From b71f3f86a8ebcafcf33857a3c5e631d0e5784d63 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 23 Aug 2024 09:58:28 +0900 Subject: [PATCH 068/188] ebiten: add check that graphics.VertexFloatCount and Vertex match --- image.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/image.go b/image.go index 146c1a13c..aa2daf9bf 100644 --- a/image.go +++ b/image.go @@ -18,6 +18,7 @@ import ( "fmt" "image" "image/color" + "unsafe" "github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/atlas" @@ -296,6 +297,8 @@ type Vertex struct { ColorA float32 } +var _ [0]byte = [unsafe.Sizeof(Vertex{}) - unsafe.Sizeof(float32(0))*graphics.VertexFloatCount]byte{} + // Address represents a sampler address mode. type Address int From d3d42b3263f8b2963d9aeb1fb60dbc4b19d0f17e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 23 Aug 2024 10:48:53 +0900 Subject: [PATCH 069/188] internal/shader: refactoring: varying variables --- internal/shader/shader.go | 62 ++++++++++++++++++--------------------- internal/shaderir/type.go | 14 ++++----- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 407e815d5..db76dc5be 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -55,12 +55,12 @@ type compileState struct { ir shaderir.Program - funcs []function + funcs []function + vertexOutParams []shaderir.Type + fragmentInParams []shaderir.Type global block - varyingParsed bool - errs []string } @@ -348,6 +348,30 @@ func (cs *compileState) parse(f *ast.File) { return } + // Parse varying veraibles. + // In testings, there might not be vertex and fragment entry points. + if cs.ir.VertexFunc.Block != nil && cs.ir.FragmentFunc.Block != nil { + if len(cs.fragmentInParams) != len(cs.vertexOutParams) { + cs.addError(0, "the number of vertex entry point's returning values and the number of fragment entry point's params must be the same") + } + for i, t := range cs.vertexOutParams { + if !t.Equal(&cs.fragmentInParams[i]) { + cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") + } + } + } + + if cs.ir.VertexFunc.Block != nil { + // TODO: Check that these params are not arrays or structs + cs.ir.Varyings = append(cs.ir.Varyings, cs.vertexOutParams[1:]...) + } else if cs.ir.FragmentFunc.Block != nil { + cs.ir.Varyings = append(cs.ir.Varyings, cs.fragmentInParams[1:]...) + } + + if len(cs.errs) > 0 { + return + } + for _, f := range cs.funcs { cs.ir.Funcs = append(cs.ir.Funcs, f.ir) } @@ -460,8 +484,10 @@ func (cs *compileState) parseDecl(b *block, fname string, d ast.Decl) ([]shaderi switch d.Name.Name { case cs.vertexEntry: cs.ir.VertexFunc.Block = f.ir.Block + cs.vertexOutParams = f.ir.OutParams case cs.fragmentEntry: cs.ir.FragmentFunc.Block = f.ir.Block + cs.fragmentInParams = f.ir.InParams default: // The function is already registered for their names. for i := range cs.funcs { @@ -767,18 +793,6 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool inParams, outParams, returnType := cs.parseFuncParams(block, d.Name.Name, d) - checkVaryings := func(vs []variable) { - if len(cs.ir.Varyings) != len(vs) { - cs.addError(d.Pos(), "the number of vertex entry point's returning values and the number of fragment entry point's params must be the same") - return - } - for i, t := range cs.ir.Varyings { - if t.Main != vs[i].typ.Main { - cs.addError(d.Pos(), "vertex entry point's returning value types and fragment entry point's param types must match") - } - } - } - if block == &cs.global { switch d.Name.Name { case cs.vertexEntry: @@ -802,15 +816,6 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool return function{}, false } - if cs.varyingParsed { - checkVaryings(outParams[1:]) - } else { - for _, v := range outParams[1:] { - // TODO: Check that these params are not arrays or structs - cs.ir.Varyings = append(cs.ir.Varyings, v.typ) - } - } - cs.varyingParsed = true case cs.fragmentEntry: if len(inParams) == 0 { cs.addError(d.Pos(), "fragment entry point must have at least one vec4 parameter for a position") @@ -825,15 +830,6 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool cs.addError(d.Pos(), "fragment entry point must have one returning vec4 value for a color") return function{}, false } - - if cs.varyingParsed { - checkVaryings(inParams[1:]) - } else { - for _, v := range inParams[1:] { - cs.ir.Varyings = append(cs.ir.Varyings, v.typ) - } - } - cs.varyingParsed = true } } diff --git a/internal/shaderir/type.go b/internal/shaderir/type.go index ede2c91e2..8601b5842 100644 --- a/internal/shaderir/type.go +++ b/internal/shaderir/type.go @@ -25,7 +25,7 @@ type Type struct { Length int } -func (t *Type) Equal(rhs *Type) bool { +func (t Type) Equal(rhs *Type) bool { if t.Main != rhs.Main { return false } @@ -43,7 +43,7 @@ func (t *Type) Equal(rhs *Type) bool { return true } -func (t *Type) String() string { +func (t Type) String() string { switch t.Main { case None: return "none" @@ -87,7 +87,7 @@ func (t *Type) String() string { } } -func (t *Type) Uint32Count() int { +func (t Type) Uint32Count() int { switch t.Main { case Int: return 1 @@ -118,7 +118,7 @@ func (t *Type) Uint32Count() int { } } -func (t *Type) IsFloatVector() bool { +func (t Type) IsFloatVector() bool { switch t.Main { case Vec2, Vec3, Vec4: return true @@ -126,7 +126,7 @@ func (t *Type) IsFloatVector() bool { return false } -func (t *Type) IsIntVector() bool { +func (t Type) IsIntVector() bool { switch t.Main { case IVec2, IVec3, IVec4: return true @@ -134,7 +134,7 @@ func (t *Type) IsIntVector() bool { return false } -func (t *Type) VectorElementCount() int { +func (t Type) VectorElementCount() int { switch t.Main { case Vec2: return 2 @@ -153,7 +153,7 @@ func (t *Type) VectorElementCount() int { } } -func (t *Type) IsMatrix() bool { +func (t Type) IsMatrix() bool { switch t.Main { case Mat2, Mat3, Mat4: return true From 9ad7fe5bed508d4e5cba0d6795653f0f65cd5004 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 23 Aug 2024 11:05:14 +0900 Subject: [PATCH 070/188] internal/shader: refactoring --- internal/shader/shader.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/shader/shader.go b/internal/shader/shader.go index db76dc5be..142985f52 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -363,9 +363,8 @@ func (cs *compileState) parse(f *ast.File) { if cs.ir.VertexFunc.Block != nil { // TODO: Check that these params are not arrays or structs + // The 0th argument is a special variable for position and is not included in varying variables. cs.ir.Varyings = append(cs.ir.Varyings, cs.vertexOutParams[1:]...) - } else if cs.ir.FragmentFunc.Block != nil { - cs.ir.Varyings = append(cs.ir.Varyings, cs.fragmentInParams[1:]...) } if len(cs.errs) > 0 { From f3bd56b0ed061de01b901bbb3df6efa6c89d9bf0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 24 Aug 2024 00:02:49 +0900 Subject: [PATCH 071/188] all: update bitmapfont to v3.2.0-alpha.4 --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index ba009dbc7..40232aaca 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/ebitengine/purego v0.8.0-alpha.4 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 - github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.3 + github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4 github.com/hajimehoshi/go-mp3 v0.3.4 github.com/jakecoffman/cp v1.2.1 github.com/jezek/xgb v1.1.1 diff --git a/go.sum b/go.sum index 3f82bd797..93ba3e434 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZ github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.3 h1:gSxacUXbVZcffXOHl/BWGvf61LpJYLoEaZlsiLlWXPk= -github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.3/go.mod h1:kTSlhZN34vbL4Okwi+H9N99DwGTFZ97f6VJKFNqMwIM= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4 h1:0sMBL2GS9QPgSaExj8UmVpDk667RbT2SXVZprmwEPlY= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4/go.mod h1:/GmYyEKgzzM7dzJBsL7aS5iR83Dr666E5bhQLVVPYsw= github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= @@ -37,7 +37,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -93,7 +92,6 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From dd63eef65e58aca8c949f2169b289b1bf07473b9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 24 Aug 2024 01:04:11 +0900 Subject: [PATCH 072/188] textinput: support every environment even without IME Closes #3072 --- exp/textinput/textinput_noime.go | 58 ++++++++++++++++++++++++++++++++ exp/textinput/textinput_null.go | 25 -------------- internal/ui/context.go | 2 ++ internal/ui/ui.go | 5 +++ 4 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 exp/textinput/textinput_noime.go delete mode 100644 exp/textinput/textinput_null.go diff --git a/exp/textinput/textinput_noime.go b/exp/textinput/textinput_noime.go new file mode 100644 index 000000000..58b4d05fc --- /dev/null +++ b/exp/textinput/textinput_noime.go @@ -0,0 +1,58 @@ +// Copyright 2023 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build (!darwin && !js && !windows) || ios + +package textinput + +import ( + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/internal/ui" +) + +type textInput struct { + rs []rune + lastTick uint64 +} + +var theTextInput textInput + +func (t *textInput) Start(x, y int) (chan State, func()) { + // AppendInputChars is updated only when the tick is updated. + // If the tick is not updated, return nil immediately. + tick := ui.Get().Tick() + if t.lastTick == tick { + return nil, nil + } + defer func() { + t.lastTick = tick + }() + + s := newSession() + + // This is a pseudo implementation with AppendInputChars without IME. + // This is tentative and should be replaced with IME in the future. + t.rs = ebiten.AppendInputChars(t.rs[:0]) + if len(t.rs) == 0 { + return nil, nil + } + s.ch <- State{ + Text: string(t.rs), + Committed: true, + } + // Keep the channel as end() resets s.ch. + ch := s.ch + s.end() + return ch, nil +} diff --git a/exp/textinput/textinput_null.go b/exp/textinput/textinput_null.go deleted file mode 100644 index 22867befc..000000000 --- a/exp/textinput/textinput_null.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2023 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build (!darwin && !js && !windows) || ios - -package textinput - -type textInput struct{} - -var theTextInput textInput - -func (t *textInput) Start(x, y int) (chan State, func()) { - return nil, nil -} diff --git a/internal/ui/context.go b/internal/ui/context.go index bf9ccce50..7f94b2d5d 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -153,6 +153,8 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update if err := ui.error(); err != nil { return err } + + ui.tick.Add(1) } // Update window icons during a frame, since an icon might be *ebiten.Image and diff --git a/internal/ui/ui.go b/internal/ui/ui.go index b8f3991d8..ed7efbee9 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -79,6 +79,7 @@ type UserInterface struct { graphicsLibrary atomic.Int32 running atomic.Bool terminated atomic.Bool + tick atomic.Uint64 whiteImage *Image @@ -230,3 +231,7 @@ func (u *UserInterface) isTerminated() bool { func (u *UserInterface) setTerminated() { u.terminated.Store(true) } + +func (u *UserInterface) Tick() uint64 { + return u.tick.Load() +} From 3547d999b1c577d30c472b37072b1ff9b2e992e5 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 11:45:54 +0900 Subject: [PATCH 073/188] internal/graphicsdriver/opengl/gl: bug fix: crash when log length is 0 --- internal/graphicsdriver/opengl/gl/default_cgo.go | 6 ++++++ internal/graphicsdriver/opengl/gl/default_purego.go | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/internal/graphicsdriver/opengl/gl/default_cgo.go b/internal/graphicsdriver/opengl/gl/default_cgo.go index 234f9a707..244b33ee0 100644 --- a/internal/graphicsdriver/opengl/gl/default_cgo.go +++ b/internal/graphicsdriver/opengl/gl/default_cgo.go @@ -592,6 +592,9 @@ func (c *defaultContext) GetInteger(pname uint32) int { func (c *defaultContext) GetProgramInfoLog(program uint32) string { bufSize := c.GetProgrami(program, INFO_LOG_LENGTH) + if bufSize == 0 { + return "" + } infoLog := make([]byte, bufSize) C.glowGetProgramInfoLog(c.gpGetProgramInfoLog, C.GLuint(program), C.GLsizei(bufSize), nil, (*C.GLchar)(unsafe.Pointer(&infoLog[0]))) return string(infoLog) @@ -605,6 +608,9 @@ func (c *defaultContext) GetProgrami(program uint32, pname uint32) int { func (c *defaultContext) GetShaderInfoLog(shader uint32) string { bufSize := c.GetShaderi(shader, INFO_LOG_LENGTH) + if bufSize == 0 { + return "" + } infoLog := make([]byte, bufSize) C.glowGetShaderInfoLog(c.gpGetShaderInfoLog, C.GLuint(shader), C.GLsizei(bufSize), nil, (*C.GLchar)(unsafe.Pointer(&infoLog[0]))) return string(infoLog) diff --git a/internal/graphicsdriver/opengl/gl/default_purego.go b/internal/graphicsdriver/opengl/gl/default_purego.go index 1e18d5730..05e65b570 100644 --- a/internal/graphicsdriver/opengl/gl/default_purego.go +++ b/internal/graphicsdriver/opengl/gl/default_purego.go @@ -300,6 +300,9 @@ func (c *defaultContext) GetInteger(pname uint32) int { func (c *defaultContext) GetProgramInfoLog(program uint32) string { bufSize := c.GetProgrami(program, INFO_LOG_LENGTH) + if bufSize == 0 { + return "" + } infoLog := make([]byte, bufSize) purego.SyscallN(c.gpGetProgramInfoLog, uintptr(program), uintptr(bufSize), 0, uintptr(unsafe.Pointer(&infoLog[0]))) return string(infoLog) @@ -313,6 +316,9 @@ func (c *defaultContext) GetProgrami(program uint32, pname uint32) int { func (c *defaultContext) GetShaderInfoLog(shader uint32) string { bufSize := c.GetShaderi(shader, INFO_LOG_LENGTH) + if bufSize == 0 { + return "" + } infoLog := make([]byte, bufSize) purego.SyscallN(c.gpGetShaderInfoLog, uintptr(shader), uintptr(bufSize), 0, uintptr(unsafe.Pointer(&infoLog[0]))) return string(infoLog) From 6db3b11b36c7cffb03fcd3c741c00b1f5dd09fcd Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 12:23:13 +0900 Subject: [PATCH 074/188] internal/shader: refactoring --- internal/shader/shader.go | 66 +++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 142985f52..ae50bd8f0 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -55,9 +55,7 @@ type compileState struct { ir shaderir.Program - funcs []function - vertexOutParams []shaderir.Type - fragmentInParams []shaderir.Type + funcs []function global block @@ -292,18 +290,14 @@ func (cs *compileState) parse(f *ast.File) { // Parse function names so that any other function call the others. // The function data is provisional and will be updated soon. + var vertexOutParams []shaderir.Type + var fragmentInParams []shaderir.Type for _, d := range f.Decls { fd, ok := d.(*ast.FuncDecl) if !ok { continue } n := fd.Name.Name - if n == cs.vertexEntry { - continue - } - if n == cs.fragmentEntry { - continue - } for _, f := range cs.funcs { if f.name == n { @@ -321,6 +315,15 @@ func (cs *compileState) parse(f *ast.File) { outT = append(outT, v.typ) } + if n == cs.vertexEntry { + vertexOutParams = outT + continue + } + if n == cs.fragmentEntry { + fragmentInParams = inT + continue + } + cs.funcs = append(cs.funcs, function{ name: n, ir: shaderir.Func{ @@ -333,6 +336,26 @@ func (cs *compileState) parse(f *ast.File) { }) } + // Check varying variables. + // In testings, there might not be vertex and fragment entry points. + if len(vertexOutParams) > 0 && len(fragmentInParams) > 0 { + if len(vertexOutParams) != len(fragmentInParams) { + cs.addError(0, "the number of vertex entry point's returning values and the number of fragment entry point's params must be the same") + } + for i, t := range vertexOutParams { + if !t.Equal(&fragmentInParams[i]) { + cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") + } + } + } + + // Set varying veraibles. + if len(vertexOutParams) > 0 { + // TODO: Check that these params are not arrays or structs + // The 0th argument is a special variable for position and is not included in varying variables. + cs.ir.Varyings = append(cs.ir.Varyings, vertexOutParams[1:]...) + } + // Parse functions. for _, d := range f.Decls { if f, ok := d.(*ast.FuncDecl); ok { @@ -348,29 +371,6 @@ func (cs *compileState) parse(f *ast.File) { return } - // Parse varying veraibles. - // In testings, there might not be vertex and fragment entry points. - if cs.ir.VertexFunc.Block != nil && cs.ir.FragmentFunc.Block != nil { - if len(cs.fragmentInParams) != len(cs.vertexOutParams) { - cs.addError(0, "the number of vertex entry point's returning values and the number of fragment entry point's params must be the same") - } - for i, t := range cs.vertexOutParams { - if !t.Equal(&cs.fragmentInParams[i]) { - cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") - } - } - } - - if cs.ir.VertexFunc.Block != nil { - // TODO: Check that these params are not arrays or structs - // The 0th argument is a special variable for position and is not included in varying variables. - cs.ir.Varyings = append(cs.ir.Varyings, cs.vertexOutParams[1:]...) - } - - if len(cs.errs) > 0 { - return - } - for _, f := range cs.funcs { cs.ir.Funcs = append(cs.ir.Funcs, f.ir) } @@ -483,10 +483,8 @@ func (cs *compileState) parseDecl(b *block, fname string, d ast.Decl) ([]shaderi switch d.Name.Name { case cs.vertexEntry: cs.ir.VertexFunc.Block = f.ir.Block - cs.vertexOutParams = f.ir.OutParams case cs.fragmentEntry: cs.ir.FragmentFunc.Block = f.ir.Block - cs.fragmentInParams = f.ir.InParams default: // The function is already registered for their names. for i := range cs.funcs { From a42a8548b1237714729af5fec90a535199d6568a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 12:49:36 +0900 Subject: [PATCH 075/188] internal/shader: refactoring --- internal/shader/shader.go | 76 +++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/internal/shader/shader.go b/internal/shader/shader.go index ae50bd8f0..526e16820 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -290,8 +290,11 @@ func (cs *compileState) parse(f *ast.File) { // Parse function names so that any other function call the others. // The function data is provisional and will be updated soon. + var vertexInParams []shaderir.Type var vertexOutParams []shaderir.Type var fragmentInParams []shaderir.Type + var fragmentOutParams []shaderir.Type + var fragmentReturnType shaderir.Type for _, d := range f.Decls { fd, ok := d.(*ast.FuncDecl) if !ok { @@ -316,11 +319,14 @@ func (cs *compileState) parse(f *ast.File) { } if n == cs.vertexEntry { + vertexInParams = inT vertexOutParams = outT continue } if n == cs.fragmentEntry { fragmentInParams = inT + fragmentOutParams = outT + fragmentReturnType = ret continue } @@ -347,9 +353,28 @@ func (cs *compileState) parse(f *ast.File) { cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") } } + + // The first out-param is treated as gl_Position in GLSL. + if vertexOutParams[0].Main != shaderir.Vec4 { + cs.addError(0, "vertex entry point must have at least one returning vec4 value for a position") + } + if len(fragmentInParams) == 0 { + cs.addError(0, "fragment entry point must have at least one vec4 parameter for a position") + } + if fragmentInParams[0].Main != shaderir.Vec4 { + cs.addError(0, "fragment entry point must have at least one vec4 parameter for a position") + } + if len(fragmentOutParams) != 0 || fragmentReturnType.Main != shaderir.Vec4 { + cs.addError(0, "fragment entry point must have one returning vec4 value for a color") + } } - // Set varying veraibles. + if len(cs.errs) > 0 { + return + } + + // Set attribute varying veraibles. + cs.ir.Attributes = append(cs.ir.Attributes, vertexInParams...) if len(vertexOutParams) > 0 { // TODO: Check that these params are not arrays or structs // The 0th argument is a special variable for position and is not included in varying variables. @@ -766,7 +791,13 @@ func (cs *compileState) parseFuncParams(block *block, fname string, d *ast.FuncD // If there is only one returning value, it is treated as a returning value. // An array cannot be a returning value, especially for HLSL (#2923). - if len(out) == 1 && out[0].name == "" && out[0].typ.Main != shaderir.Array { + // + // For the vertex entry, a parameter (variable) is used as a returning value. + // For example, GLSL doesn't treat gl_Position as a returning value. + // Thus, the returning value is not set for the vertex entry. + // TODO: This can be resolved by having an indirect function like what the fragment entry already does. + // See internal/shaderir/glsl.adjustProgram. + if len(out) == 1 && out[0].name == "" && out[0].typ.Main != shaderir.Array && fname != cs.vertexEntry { ret = out[0].typ out = nil } @@ -789,47 +820,6 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool } inParams, outParams, returnType := cs.parseFuncParams(block, d.Name.Name, d) - - if block == &cs.global { - switch d.Name.Name { - case cs.vertexEntry: - for _, v := range inParams { - cs.ir.Attributes = append(cs.ir.Attributes, v.typ) - } - - // For the vertex entry, a parameter (variable) is used as a returning value. - // For example, GLSL doesn't treat gl_Position as a returning value. - // TODO: This can be resolved by having an indirect function like what the fragment entry already does. - // See internal/shaderir/glsl.adjustProgram. - if len(outParams) == 0 { - outParams = append(outParams, variable{ - typ: shaderir.Type{Main: shaderir.Vec4}, - }) - } - - // The first out-param is treated as gl_Position in GLSL. - if outParams[0].typ.Main != shaderir.Vec4 { - cs.addError(d.Pos(), "vertex entry point must have at least one returning vec4 value for a position") - return function{}, false - } - - case cs.fragmentEntry: - if len(inParams) == 0 { - cs.addError(d.Pos(), "fragment entry point must have at least one vec4 parameter for a position") - return function{}, false - } - if inParams[0].typ.Main != shaderir.Vec4 { - cs.addError(d.Pos(), "fragment entry point must have at least one vec4 parameter for a position") - return function{}, false - } - - if len(outParams) != 0 || returnType.Main != shaderir.Vec4 { - cs.addError(d.Pos(), "fragment entry point must have one returning vec4 value for a color") - return function{}, false - } - } - } - b, ok := cs.parseBlock(block, d.Name.Name, d.Body.List, inParams, outParams, returnType, true) if !ok { return function{}, false From 65aec12d888d936f956159ded9b1880d6411d9d9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 13:03:30 +0900 Subject: [PATCH 076/188] all: update PureGo to v0.8.0-alpha.5 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 40232aaca..1592cf3c9 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.4 - github.com/ebitengine/purego v0.8.0-alpha.4 + github.com/ebitengine/purego v0.8.0-alpha.5 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4 diff --git a/go.sum b/go.sum index 93ba3e434..85e57ab34 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.4 h1:w9SD7kK4GgJULkh5pWVTToMA5Ia1bP7VxD4rIjQqb8M= github.com/ebitengine/oto/v3 v3.3.0-alpha.4/go.mod h1:B+Sz3hzZXcx251YqSPIj+cVMicvlx7Xiq29AEUIbc7E= -github.com/ebitengine/purego v0.8.0-alpha.4 h1:Dg9xRGC3giyQedfISyHH94eQM0md4a84+HHr7KBBH/Q= -github.com/ebitengine/purego v0.8.0-alpha.4/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= +github.com/ebitengine/purego v0.8.0-alpha.5 h1:M0+PSgsdVNczTB8ijX89HmYqCfb2HUuBEx4A+wNuHto= +github.com/ebitengine/purego v0.8.0-alpha.5/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38= github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= From d570406735eef3a262ab32b054d888142a2f412b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 13:39:36 +0900 Subject: [PATCH 077/188] all: update ebitengine/gomobile --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1592cf3c9..ff2012810 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/hajimehoshi/ebiten/v2 go 1.19 require ( - github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc + github.com/ebitengine/gomobile v0.0.0-20240825043811-96c531f5bd83 github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.4 github.com/ebitengine/purego v0.8.0-alpha.5 diff --git a/go.sum b/go.sum index 85e57ab34..714179a03 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48tiQRlrr71NsbfxBcFM9/8bEHS9/JbsQg= -github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI= +github.com/ebitengine/gomobile v0.0.0-20240825043811-96c531f5bd83 h1:yA0CtFKYZI/db1snCOInRS0Z18QGZU6aBYkqUT0H6RI= +github.com/ebitengine/gomobile v0.0.0-20240825043811-96c531f5bd83/go.mod h1:n2NbB/F4d9wOXFzC7FT1ipERidmYWC5I4YNOYRs5N7I= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.4 h1:w9SD7kK4GgJULkh5pWVTToMA5Ia1bP7VxD4rIjQqb8M= From 7142a3bcd90328a18311ba23a94a9cb43e04dd01 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 15:29:22 +0900 Subject: [PATCH 078/188] internal/graphicsdriver/opengl: bug fix: total must be reset --- internal/graphicsdriver/opengl/program.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index df8414480..04fa576f4 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -66,6 +66,11 @@ func (a *arrayBufferLayout) float32Count() int { return a.total } +func (a *arrayBufferLayout) addPart(part arrayBufferLayoutPart) { + a.parts = append(a.parts, part) + a.total = 0 +} + // enable starts using the array buffer. func (a *arrayBufferLayout) enable(context *context) { for i := range a.parts { @@ -117,7 +122,7 @@ func init() { if d > 4 { panic("opengl: the array buffer layout is too small") } - theArrayBufferLayout.parts = append(theArrayBufferLayout.parts, arrayBufferLayoutPart{ + theArrayBufferLayout.addPart(arrayBufferLayoutPart{ name: "A3", num: d, }) From 107189a00db508e57127598a6357325e6caa4cee Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 15:43:44 +0900 Subject: [PATCH 079/188] internal/shader: use strings.TrimPrefix --- internal/shader/shader_test.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/internal/shader/shader_test.go b/internal/shader/shader_test.go index 954ba099d..cb53d74d5 100644 --- a/internal/shader/shader_test.go +++ b/internal/shader/shader_test.go @@ -31,32 +31,24 @@ import ( func glslVertexNormalize(str string) string { p := glsl.VertexPrelude(glsl.GLSLVersionDefault) - if strings.HasPrefix(str, p) { - str = str[len(p):] - } + str = strings.TrimPrefix(str, p) return strings.TrimSpace(str) } func glslFragmentNormalize(str string) string { p := glsl.FragmentPrelude(glsl.GLSLVersionDefault) - if strings.HasPrefix(str, p) { - str = str[len(p):] - } + str = strings.TrimPrefix(str, p) return strings.TrimSpace(str) } func hlslNormalize(str string) string { - if strings.HasPrefix(str, hlsl.Prelude) { - str = str[len(hlsl.Prelude):] - } + str = strings.TrimPrefix(str, hlsl.Prelude) return strings.TrimSpace(str) } func metalNormalize(str string) string { prelude := msl.Prelude(shaderir.Texels) - if strings.HasPrefix(str, prelude) { - str = str[len(prelude):] - } + str = strings.TrimPrefix(str, prelude) return strings.TrimSpace(str) } From ed45843c130b71595531fd9a6c2ec5f01c3ab9a4 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 17:09:10 +0900 Subject: [PATCH 080/188] internal/graphicsdriver/opengl: assume custom attributes are vec4 Due to HLSL restrictions, all the attributes must have a semantics. Always assuming custom attributes are vec4 makes things simpler. Updates #2640 --- internal/graphicsdriver/opengl/program.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index 04fa576f4..ad37f373a 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -114,17 +114,17 @@ func init() { }, } n := theArrayBufferLayout.float32Count() - if n > graphics.VertexFloatCount { - panic("opengl: the array buffer layout is too large") + diff := graphics.VertexFloatCount - n + if diff == 0 { + return } - if n < graphics.VertexFloatCount { - d := graphics.VertexFloatCount - n - if d > 4 { - panic("opengl: the array buffer layout is too small") - } + if diff%4 != 0 { + panic("opengl: unexpected attribute layout") + } + for i := 0; i < diff/4; i++ { theArrayBufferLayout.addPart(arrayBufferLayoutPart{ - name: "A3", - num: d, + name: fmt.Sprintf("A%d", i+3), + num: 4, }) } } From fef487e09d3df1b308c9d8c0db5b2123dac27dc1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 15:56:44 +0900 Subject: [PATCH 081/188] internal/shaderir/hlsl: refactoring: more flexible generation Updates #2640 --- .../directx/graphics11_windows.go | 78 ++++++++++++------- .../directx/pipeline12_windows.go | 78 ++++++++++++------- internal/shaderir/hlsl/hlsl.go | 49 ++++++++++-- 3 files changed, 141 insertions(+), 64 deletions(-) diff --git a/internal/graphicsdriver/directx/graphics11_windows.go b/internal/graphicsdriver/directx/graphics11_windows.go index 6a111edbf..4d91d703f 100644 --- a/internal/graphicsdriver/directx/graphics11_windows.go +++ b/internal/graphicsdriver/directx/graphics11_windows.go @@ -27,34 +27,56 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) -var inputElementDescsForDX11 = []_D3D11_INPUT_ELEMENT_DESC{ - { - SemanticName: &([]byte("POSITION\000"))[0], - SemanticIndex: 0, - Format: _DXGI_FORMAT_R32G32_FLOAT, - InputSlot: 0, - AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT, - InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, - { - SemanticName: &([]byte("TEXCOORD\000"))[0], - SemanticIndex: 0, - Format: _DXGI_FORMAT_R32G32_FLOAT, - InputSlot: 0, - AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT, - InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, - { - SemanticName: &([]byte("COLOR\000"))[0], - SemanticIndex: 0, - Format: _DXGI_FORMAT_R32G32B32A32_FLOAT, - InputSlot: 0, - AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT, - InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, +var inputElementDescsForDX11 []_D3D11_INPUT_ELEMENT_DESC + +func init() { + inputElementDescsForDX11 = []_D3D11_INPUT_ELEMENT_DESC{ + { + SemanticName: &([]byte("POSITION\000"))[0], + SemanticIndex: 0, + Format: _DXGI_FORMAT_R32G32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + { + SemanticName: &([]byte("TEXCOORD\000"))[0], + SemanticIndex: 0, + Format: _DXGI_FORMAT_R32G32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + { + SemanticName: &([]byte("COLOR\000"))[0], + SemanticIndex: 0, + Format: _DXGI_FORMAT_R32G32B32A32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + } + diff := graphics.VertexFloatCount - 8 + if diff == 0 { + return + } + if diff%4 != 0 { + panic("directx: unexpected attribute layout") + } + for i := 0; i < diff/4; i++ { + inputElementDescsForDX11 = append(inputElementDescsForDX11, _D3D11_INPUT_ELEMENT_DESC{ + SemanticName: &([]byte("COLOR\000"))[0], + SemanticIndex: uint32(i) + 1, + Format: _DXGI_FORMAT_R32G32B32A32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }) + } } func blendFactorToBlend11(f graphicsdriver.BlendFactor, alpha bool) _D3D11_BLEND { diff --git a/internal/graphicsdriver/directx/pipeline12_windows.go b/internal/graphicsdriver/directx/pipeline12_windows.go index 819454786..ce666ee42 100644 --- a/internal/graphicsdriver/directx/pipeline12_windows.go +++ b/internal/graphicsdriver/directx/pipeline12_windows.go @@ -23,34 +23,56 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -var inputElementDescsForDX12 = []_D3D12_INPUT_ELEMENT_DESC{ - { - SemanticName: &([]byte("POSITION\000"))[0], - SemanticIndex: 0, - Format: _DXGI_FORMAT_R32G32_FLOAT, - InputSlot: 0, - AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT, - InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, - { - SemanticName: &([]byte("TEXCOORD\000"))[0], - SemanticIndex: 0, - Format: _DXGI_FORMAT_R32G32_FLOAT, - InputSlot: 0, - AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT, - InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, - { - SemanticName: &([]byte("COLOR\000"))[0], - SemanticIndex: 0, - Format: _DXGI_FORMAT_R32G32B32A32_FLOAT, - InputSlot: 0, - AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT, - InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, +var inputElementDescsForDX12 []_D3D12_INPUT_ELEMENT_DESC + +func init() { + inputElementDescsForDX12 = []_D3D12_INPUT_ELEMENT_DESC{ + { + SemanticName: &([]byte("POSITION\000"))[0], + SemanticIndex: 0, + Format: _DXGI_FORMAT_R32G32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + { + SemanticName: &([]byte("TEXCOORD\000"))[0], + SemanticIndex: 0, + Format: _DXGI_FORMAT_R32G32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + { + SemanticName: &([]byte("COLOR\000"))[0], + SemanticIndex: 0, + Format: _DXGI_FORMAT_R32G32B32A32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + } + diff := graphics.VertexFloatCount - 8 + if diff == 0 { + return + } + if diff%4 != 0 { + panic("directx: unexpected attribute layout") + } + for i := 0; i < diff/4; i++ { + inputElementDescsForDX12 = append(inputElementDescsForDX12, _D3D12_INPUT_ELEMENT_DESC{ + SemanticName: &([]byte("COLOR\000"))[0], + SemanticIndex: uint32(i) + 1, + Format: _DXGI_FORMAT_R32G32B32A32_FLOAT, + InputSlot: 0, + AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT, + InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }) + } } const numDescriptorsPerFrame = 32 diff --git a/internal/shaderir/hlsl/hlsl.go b/internal/shaderir/hlsl/hlsl.go index 22a05e635..d1e558407 100644 --- a/internal/shaderir/hlsl/hlsl.go +++ b/internal/shaderir/hlsl/hlsl.go @@ -52,13 +52,7 @@ func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) strin return n } -const Prelude = `struct Varyings { - float4 Position : SV_POSITION; - float2 M0 : TEXCOORD0; - float4 M1 : COLOR; -}; - -float mod(float x, float y) { +const Prelude = `float mod(float x, float y) { return x - y * floor(x/y); } @@ -95,6 +89,27 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { var lines []string lines = append(lines, strings.Split(Prelude, "\n")...) + + lines = append(lines, "", "struct Varyings {") + lines = append(lines, "\tfloat4 Position : SV_POSITION;") + if len(p.Varyings) > 0 { + for i, v := range p.Varyings { + switch i { + case 0: + lines = append(lines, fmt.Sprintf("\tfloat2 M%d : TEXCOORD0;", i)) + case 1: + lines = append(lines, fmt.Sprintf("\tfloat4 M%d : COLOR0;", i)) + default: + // Use COLOR[n] as a general purpose varying. + if v.Main != shaderir.Vec4 { + lines = append(lines, fmt.Sprintf("\t?(unexpected type: %s) M%d : COLOR%d;", v, i, i-1)) + } else { + lines = append(lines, fmt.Sprintf("\tfloat4 M%d : COLOR%d;", i, i-1)) + } + } + } + } + lines = append(lines, "};") lines = append(lines, "", "{{.Structs}}") if len(p.Uniforms) > 0 { @@ -156,7 +171,25 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { } if p.VertexFunc.Block != nil && len(p.VertexFunc.Block.Stmts) > 0 { vslines = append(vslines, "") - vslines = append(vslines, "Varyings VSMain(float2 A0 : POSITION, float2 A1 : TEXCOORD, float4 A2 : COLOR) {") + var args []string + for i, a := range p.Attributes { + switch i { + case 0: + args = append(args, fmt.Sprintf("float2 A%d : POSITION", i)) + case 1: + args = append(args, fmt.Sprintf("float2 A%d : TEXCOORD0", i)) + case 2: + args = append(args, fmt.Sprintf("float4 A%d : COLOR0", i)) + default: + // Use COLOR[n] as a general purpose varying. + if a.Main != shaderir.Vec4 { + args = append(args, fmt.Sprintf("?(unexpected type: %s) A%d : COLOR%d", a, i, i-2)) + } else { + args = append(args, fmt.Sprintf("float4 A%d : COLOR%d", i, i-2)) + } + } + } + vslines = append(vslines, "Varyings VSMain("+strings.Join(args, ", ")+") {") vslines = append(vslines, fmt.Sprintf("\tVaryings %s;", vsOut)) vslines = append(vslines, c.block(p, p.VertexFunc.Block, p.VertexFunc.Block, 0)...) if last := fmt.Sprintf("\treturn %s;", vsOut); vslines[len(vslines)-1] != last { From bff760af0172a1b20646c70ef2e97930f92567c0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 17:43:40 +0900 Subject: [PATCH 082/188] internal/shader: bug fix: test failures Updates #2640 --- internal/graphicsdriver/directx/shader_windows.go | 2 +- internal/shader/shader_test.go | 8 ++++---- internal/shader/testdata/vertex.expected.hlsl | 2 +- internal/shaderir/hlsl/hlsl.go | 12 +++++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index 311567659..d327af01f 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -102,7 +102,7 @@ func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error) return vsh, psh, nil } - vs, ps := hlsl.Compile(program) + vs, ps, _ := hlsl.Compile(program) var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3) var wg errgroup.Group diff --git a/internal/shader/shader_test.go b/internal/shader/shader_test.go index cb53d74d5..968fe637d 100644 --- a/internal/shader/shader_test.go +++ b/internal/shader/shader_test.go @@ -41,8 +41,8 @@ func glslFragmentNormalize(str string) string { return strings.TrimSpace(str) } -func hlslNormalize(str string) string { - str = strings.TrimPrefix(str, hlsl.Prelude) +func hlslNormalize(str string, prelude string) string { + str = strings.TrimPrefix(str, prelude) return strings.TrimSpace(str) } @@ -180,8 +180,8 @@ func TestCompile(t *testing.T) { } if tc.HLSL != nil { - vs, _ := hlsl.Compile(s) - if got, want := hlslNormalize(vs), hlslNormalize(string(tc.HLSL)); got != want { + vs, _, prelude := hlsl.Compile(s) + if got, want := hlslNormalize(vs, prelude), hlslNormalize(string(tc.HLSL), prelude); got != want { compare(t, "HLSL", got, want) } } diff --git a/internal/shader/testdata/vertex.expected.hlsl b/internal/shader/testdata/vertex.expected.hlsl index 4525c0a8e..5ffd875f5 100644 --- a/internal/shader/testdata/vertex.expected.hlsl +++ b/internal/shader/testdata/vertex.expected.hlsl @@ -2,7 +2,7 @@ cbuffer Uniforms : register(b0) { float2 U0 : packoffset(c0); } -Varyings VSMain(float2 A0 : POSITION, float2 A1 : TEXCOORD, float4 A2 : COLOR) { +Varyings VSMain(float2 A0 : POSITION, float2 A1 : TEXCOORD, float4 A2 : COLOR0) { Varyings varyings; float4x4 l0 = 0.0; varyings.Position = 0.0; diff --git a/internal/shaderir/hlsl/hlsl.go b/internal/shaderir/hlsl/hlsl.go index d1e558407..aa7d11417 100644 --- a/internal/shaderir/hlsl/hlsl.go +++ b/internal/shaderir/hlsl/hlsl.go @@ -52,7 +52,7 @@ func (c *compileContext) structName(p *shaderir.Program, t *shaderir.Type) strin return n } -const Prelude = `float mod(float x, float y) { +const utilFuncs = `float mod(float x, float y) { return x - y * floor(x/y); } @@ -80,7 +80,7 @@ float4x4 float4x4FromScalar(float x) { return float4x4(x, 0, 0, 0, 0, x, 0, 0, 0, 0, x, 0, 0, 0, 0, x); }` -func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { +func Compile(p *shaderir.Program) (vertexShader, pixelShader, prelude string) { offsets := CalcUniformMemoryOffsets(p) c := &compileContext{ @@ -88,7 +88,7 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { } var lines []string - lines = append(lines, strings.Split(Prelude, "\n")...) + lines = append(lines, strings.Split(utilFuncs, "\n")...) lines = append(lines, "", "struct Varyings {") lines = append(lines, "\tfloat4 Position : SV_POSITION;") @@ -96,7 +96,7 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { for i, v := range p.Varyings { switch i { case 0: - lines = append(lines, fmt.Sprintf("\tfloat2 M%d : TEXCOORD0;", i)) + lines = append(lines, fmt.Sprintf("\tfloat2 M%d : TEXCOORD;", i)) case 1: lines = append(lines, fmt.Sprintf("\tfloat4 M%d : COLOR0;", i)) default: @@ -110,6 +110,8 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { } } lines = append(lines, "};") + prelude = strings.Join(lines, "\n") + lines = append(lines, "", "{{.Structs}}") if len(p.Uniforms) > 0 { @@ -177,7 +179,7 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { case 0: args = append(args, fmt.Sprintf("float2 A%d : POSITION", i)) case 1: - args = append(args, fmt.Sprintf("float2 A%d : TEXCOORD0", i)) + args = append(args, fmt.Sprintf("float2 A%d : TEXCOORD", i)) case 2: args = append(args, fmt.Sprintf("float4 A%d : COLOR0", i)) default: From 2cc809516f2ca352209d04a6e54ec119f233922d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 15:56:44 +0900 Subject: [PATCH 083/188] ebiten: add Vertex.Custom0 to Custom3 Closes #2640 --- image.go | 11 +++++ internal/graphics/shader.go | 4 +- internal/graphics/vertex.go | 2 +- internal/shader/shader.go | 20 +++++++-- shader_test.go | 87 +++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 7 deletions(-) diff --git a/image.go b/image.go index aa2daf9bf..9fc5938c4 100644 --- a/image.go +++ b/image.go @@ -295,6 +295,13 @@ type Vertex struct { ColorG float32 ColorB float32 ColorA float32 + + // Custom0/Custom1/Custom2/Custom3 represents general-purpose values passed to the shader. + // In order to use them, Fragment must have an additional vec4 argument. + Custom0 float32 + Custom1 float32 + Custom2 float32 + Custom3 float32 } var _ [0]byte = [unsafe.Sizeof(Vertex{}) - unsafe.Sizeof(float32(0))*graphics.VertexFloatCount]byte{} @@ -668,6 +675,10 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader vs[i*graphics.VertexFloatCount+5] = v.ColorG vs[i*graphics.VertexFloatCount+6] = v.ColorB vs[i*graphics.VertexFloatCount+7] = v.ColorA + vs[i*graphics.VertexFloatCount+8] = v.Custom0 + vs[i*graphics.VertexFloatCount+9] = v.Custom1 + vs[i*graphics.VertexFloatCount+10] = v.Custom2 + vs[i*graphics.VertexFloatCount+11] = v.Custom3 } is := i.ensureTmpIndices(len(indices)) diff --git a/internal/graphics/shader.go b/internal/graphics/shader.go index 8553cb313..7a5686f92 100644 --- a/internal/graphics/shader.go +++ b/internal/graphics/shader.go @@ -154,8 +154,8 @@ func imageSrc%[1]dAt(pos vec2) vec4 { shaderSuffix += ` var __projectionMatrix mat4 -func __vertex(dstPos vec2, srcPos vec2, color vec4) (vec4, vec2, vec4) { - return __projectionMatrix * vec4(dstPos, 0, 1), srcPos, color +func __vertex(dstPos vec2, srcPos vec2, color vec4, custom vec4) (vec4, vec2, vec4, vec4) { + return __projectionMatrix * vec4(dstPos, 0, 1), srcPos, color, custom } ` return shaderSuffix, nil diff --git a/internal/graphics/vertex.go b/internal/graphics/vertex.go index 95d7768b1..6d59f31c8 100644 --- a/internal/graphics/vertex.go +++ b/internal/graphics/vertex.go @@ -39,7 +39,7 @@ const ( ) const ( - VertexFloatCount = 8 + VertexFloatCount = 12 ) var ( diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 526e16820..19c60b0d0 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -345,10 +345,10 @@ func (cs *compileState) parse(f *ast.File) { // Check varying variables. // In testings, there might not be vertex and fragment entry points. if len(vertexOutParams) > 0 && len(fragmentInParams) > 0 { - if len(vertexOutParams) != len(fragmentInParams) { - cs.addError(0, "the number of vertex entry point's returning values and the number of fragment entry point's params must be the same") - } for i, t := range vertexOutParams { + if len(fragmentInParams) <= i { + break + } if !t.Equal(&fragmentInParams[i]) { cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") } @@ -373,7 +373,7 @@ func (cs *compileState) parse(f *ast.File) { return } - // Set attribute varying veraibles. + // Set attribute and varying veraibles. cs.ir.Attributes = append(cs.ir.Attributes, vertexInParams...) if len(vertexOutParams) > 0 { // TODO: Check that these params are not arrays or structs @@ -820,6 +820,18 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool } inParams, outParams, returnType := cs.parseFuncParams(block, d.Name.Name, d) + if d.Name.Name == cs.fragmentEntry { + // The 0th inParams is a special variable for position and is not included in varying variables. + if diff := len(cs.ir.Varyings) - (len(inParams) - 1); diff > 0 { + // inParams is not enough when the vertex shader has more returning values than the fragment shader's arguments. + for i := 0; i < diff; i++ { + inParams = append(inParams, variable{ + name: "_", + typ: cs.ir.Varyings[len(inParams)-1+i], + }) + } + } + } b, ok := cs.parseBlock(block, d.Name.Name, d.Body.List, inParams, outParams, returnType, true) if !ok { return function{}, false diff --git a/shader_test.go b/shader_test.go index e04e7749e..70c292524 100644 --- a/shader_test.go +++ b/shader_test.go @@ -2595,3 +2595,90 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { } } } + +func TestShaderCustomValues(t *testing.T) { + const w, h = 16, 16 + + dst := ebiten.NewImage(w, h) + s, err := ebiten.NewShader([]byte(`//kage:unit pixels + +package main + +func Fragment(dstPos vec4, srcPos vec2, color vec4, custom vec4) vec4 { + return custom +} +`)) + if err != nil { + t.Fatal(err) + } + + clr := color.RGBA{R: 0x10, G: 0x20, B: 0x30, A: 0x40} + dst.DrawTrianglesShader([]ebiten.Vertex{ + { + DstX: 0, + DstY: 0, + SrcX: 0, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + { + DstX: w, + DstY: 0, + SrcX: w, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + { + DstX: 0, + DstY: h, + SrcX: 0, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + { + DstX: w, + DstY: h, + SrcX: w, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + }, []uint16{0, 1, 2, 1, 2, 3}, s, nil) + + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := dst.At(i, j).(color.RGBA) + want := clr + if !sameColors(got, want, 2) { + t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} From 41b762ba2c150f963289a119a48325345c587cce Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 18:44:13 +0900 Subject: [PATCH 084/188] ebiten: update comments Updates #2640 --- image.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/image.go b/image.go index 9fc5938c4..115020b08 100644 --- a/image.go +++ b/image.go @@ -298,6 +298,9 @@ type Vertex struct { // Custom0/Custom1/Custom2/Custom3 represents general-purpose values passed to the shader. // In order to use them, Fragment must have an additional vec4 argument. + // + // These values are valid only when DrawTrianglesShader is used. + // In other cases, these values are ignored. Custom0 float32 Custom1 float32 Custom2 float32 From e2662a8af7aaf742844abc3ef1f30b25bdfc5228 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 18:58:05 +0900 Subject: [PATCH 085/188] internal/shader: bug fix: wrong indexing --- internal/shader/shader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 19c60b0d0..1aa6e7b1b 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -824,10 +824,11 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool // The 0th inParams is a special variable for position and is not included in varying variables. if diff := len(cs.ir.Varyings) - (len(inParams) - 1); diff > 0 { // inParams is not enough when the vertex shader has more returning values than the fragment shader's arguments. + orig := len(inParams) - 1 for i := 0; i < diff; i++ { inParams = append(inParams, variable{ name: "_", - typ: cs.ir.Varyings[len(inParams)-1+i], + typ: cs.ir.Varyings[orig+i], }) } } From 9693ce83821eb10ea434342dfe812435d2dfaf6f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 18:57:33 +0900 Subject: [PATCH 086/188] internal/shader: allow less arguments at Fragment Closes #3073 --- internal/builtinshader/shader.go | 4 +- internal/shader/shader.go | 6 ++ shader_test.go | 103 +++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/internal/builtinshader/shader.go b/internal/builtinshader/shader.go index 3e168f2e5..b543070d8 100644 --- a/internal/builtinshader/shader.go +++ b/internal/builtinshader/shader.go @@ -170,7 +170,7 @@ var ScreenShaderSource = []byte(`//kage:unit pixels package main -func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { +func Fragment(dstPos vec4, srcPos vec2) vec4 { // Blend source colors in a square region, which size is 1/scale. scale := imageDstSize()/imageSrc0Size() pos := srcPos @@ -193,7 +193,7 @@ var ClearShaderSource = []byte(`//kage:unit pixels package main -func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { +func Fragment() vec4 { return vec4(0) } `) diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 1aa6e7b1b..3952383c0 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -821,6 +821,12 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool inParams, outParams, returnType := cs.parseFuncParams(block, d.Name.Name, d) if d.Name.Name == cs.fragmentEntry { + if len(inParams) == 0 { + inParams = append(inParams, variable{ + name: "_", + typ: shaderir.Type{Main: shaderir.Vec4}, + }) + } // The 0th inParams is a special variable for position and is not included in varying variables. if diff := len(cs.ir.Varyings) - (len(inParams) - 1); diff > 0 { // inParams is not enough when the vertex shader has more returning values than the fragment shader's arguments. diff --git a/shader_test.go b/shader_test.go index 70c292524..910f5de04 100644 --- a/shader_test.go +++ b/shader_test.go @@ -2682,3 +2682,106 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4, custom vec4) vec4 { } } } + +func TestShaderFragmentLessArguments(t *testing.T) { + const w, h = 16, 16 + + s0, err := ebiten.NewShader([]byte(`//kage:unit pixels + +package main + +func Fragment() vec4 { + return vec4(1, 0, 0, 1) +} +`)) + if err != nil { + t.Fatal(err) + } + s1, err := ebiten.NewShader([]byte(`//kage:unit pixels + +package main + +func Fragment(dstPos vec4) vec4 { + return vec4(0, 1, 0, 1) +} +`)) + if err != nil { + t.Fatal(err) + } + s2, err := ebiten.NewShader([]byte(`//kage:unit pixels + +package main + +func Fragment(dstPos vec4, srcPos vec2) vec4 { + return vec4(0, 0, 1, 1) +} +`)) + if err != nil { + t.Fatal(err) + } + + dst := ebiten.NewImage(w, h) + for idx, s := range []*ebiten.Shader{s0, s1, s2} { + dst.Clear() + dst.DrawTrianglesShader([]ebiten.Vertex{ + { + DstX: 0, + DstY: 0, + SrcX: 0, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + { + DstX: w, + DstY: 0, + SrcX: w, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + { + DstX: 0, + DstY: h, + SrcX: 0, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + { + DstX: w, + DstY: h, + SrcX: w, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + }, + }, []uint16{0, 1, 2, 1, 2, 3}, s, nil) + + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := dst.At(i, j).(color.RGBA) + var want color.RGBA + switch idx { + case 0: + want = color.RGBA{R: 0xff, A: 0xff} + case 1: + want = color.RGBA{G: 0xff, A: 0xff} + case 2: + want = color.RGBA{B: 0xff, A: 0xff} + } + if !sameColors(got, want, 2) { + t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } + } +} From dc68152f171a10f45db6cf4335d6051c4f5f5ed7 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 19:38:54 +0900 Subject: [PATCH 087/188] internal/shader: better error message for the fragment entry point --- internal/shader/shader.go | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 3952383c0..31619299f 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -290,10 +290,10 @@ func (cs *compileState) parse(f *ast.File) { // Parse function names so that any other function call the others. // The function data is provisional and will be updated soon. - var vertexInParams []shaderir.Type - var vertexOutParams []shaderir.Type - var fragmentInParams []shaderir.Type - var fragmentOutParams []shaderir.Type + var vertexInParams []variable + var vertexOutParams []variable + var fragmentInParams []variable + var fragmentOutParams []variable var fragmentReturnType shaderir.Type for _, d := range f.Decls { fd, ok := d.(*ast.FuncDecl) @@ -310,6 +310,19 @@ func (cs *compileState) parse(f *ast.File) { } inParams, outParams, ret := cs.parseFuncParams(&cs.global, n, fd) + + if n == cs.vertexEntry { + vertexInParams = inParams + vertexOutParams = outParams + continue + } + if n == cs.fragmentEntry { + fragmentInParams = inParams + fragmentOutParams = outParams + fragmentReturnType = ret + continue + } + var inT, outT []shaderir.Type for _, v := range inParams { inT = append(inT, v.typ) @@ -317,19 +330,6 @@ func (cs *compileState) parse(f *ast.File) { for _, v := range outParams { outT = append(outT, v.typ) } - - if n == cs.vertexEntry { - vertexInParams = inT - vertexOutParams = outT - continue - } - if n == cs.fragmentEntry { - fragmentInParams = inT - fragmentOutParams = outT - fragmentReturnType = ret - continue - } - cs.funcs = append(cs.funcs, function{ name: n, ir: shaderir.Func{ @@ -345,25 +345,21 @@ func (cs *compileState) parse(f *ast.File) { // Check varying variables. // In testings, there might not be vertex and fragment entry points. if len(vertexOutParams) > 0 && len(fragmentInParams) > 0 { - for i, t := range vertexOutParams { + for i, p := range vertexOutParams { if len(fragmentInParams) <= i { break } - if !t.Equal(&fragmentInParams[i]) { - cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") + t := fragmentInParams[i].typ + if !p.typ.Equal(&t) { + name := fragmentInParams[i].name + cs.addError(0, fmt.Sprintf("fragment argument %s must be %s but was %s", name, p.typ.String(), t.String())) } } // The first out-param is treated as gl_Position in GLSL. - if vertexOutParams[0].Main != shaderir.Vec4 { + if vertexOutParams[0].typ.Main != shaderir.Vec4 { cs.addError(0, "vertex entry point must have at least one returning vec4 value for a position") } - if len(fragmentInParams) == 0 { - cs.addError(0, "fragment entry point must have at least one vec4 parameter for a position") - } - if fragmentInParams[0].Main != shaderir.Vec4 { - cs.addError(0, "fragment entry point must have at least one vec4 parameter for a position") - } if len(fragmentOutParams) != 0 || fragmentReturnType.Main != shaderir.Vec4 { cs.addError(0, "fragment entry point must have one returning vec4 value for a color") } @@ -374,11 +370,15 @@ func (cs *compileState) parse(f *ast.File) { } // Set attribute and varying veraibles. - cs.ir.Attributes = append(cs.ir.Attributes, vertexInParams...) + for _, p := range vertexInParams { + cs.ir.Attributes = append(cs.ir.Attributes, p.typ) + } if len(vertexOutParams) > 0 { // TODO: Check that these params are not arrays or structs // The 0th argument is a special variable for position and is not included in varying variables. - cs.ir.Varyings = append(cs.ir.Varyings, vertexOutParams[1:]...) + for _, p := range vertexOutParams[1:] { + cs.ir.Varyings = append(cs.ir.Varyings, p.typ) + } } // Parse functions. From 5f595efef02040acfce366f28926e0b3eebd2fd8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 27 Aug 2024 00:02:25 +0900 Subject: [PATCH 088/188] examples/textinput: remove message Updates #3072 --- examples/textinput/main.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/textinput/main.go b/examples/textinput/main.go index 83d82d1cc..19a4f221b 100644 --- a/examples/textinput/main.go +++ b/examples/textinput/main.go @@ -19,7 +19,6 @@ import ( "image/color" "log" "math" - "runtime" "strings" "unicode/utf8" @@ -324,10 +323,6 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { } func main() { - if runtime.GOOS != "darwin" && runtime.GOOS != "js" { - log.Printf("github.com/hajimehoshi/ebiten/v2/exp/textinput is not supported in this environment (GOOS=%s) yet", runtime.GOOS) - } - ebiten.SetWindowSize(screenWidth, screenHeight) ebiten.SetWindowTitle("Text Input (Ebitengine Demo)") if err := ebiten.RunGame(&Game{}); err != nil { From 42209606b1cfe815bbfe9d935a555e8f4e6d1461 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 26 Aug 2024 23:26:58 +0900 Subject: [PATCH 089/188] internal/ui: disable IME and enable it only when necessary Closes #2918 --- exp/textinput/api_windows.go | 10 ++++++ exp/textinput/textinput_windows.go | 58 +++++++++++++++++++++++++++--- internal/ui/api_windows.go | 11 ++++++ internal/ui/ui_darwin.go | 4 +++ internal/ui/ui_glfw.go | 7 ++++ internal/ui/ui_linbsd.go | 4 +++ internal/ui/ui_windows.go | 32 +++++++++++++++++ 7 files changed, 122 insertions(+), 4 deletions(-) diff --git a/exp/textinput/api_windows.go b/exp/textinput/api_windows.go index 7c8617a7e..d7fae6299 100644 --- a/exp/textinput/api_windows.go +++ b/exp/textinput/api_windows.go @@ -15,6 +15,7 @@ package textinput import ( + "errors" "fmt" "runtime" "unsafe" @@ -73,6 +74,7 @@ var ( imm32 = windows.NewLazySystemDLL("imm32.dll") user32 = windows.NewLazySystemDLL("user32.dll") + procImmAssociateContext = imm32.NewProc("ImmAssociateContext") procImmGetCompositionStringW = imm32.NewProc("ImmGetCompositionStringW") procImmGetContext = imm32.NewProc("ImmGetContext") procImmReleaseContext = imm32.NewProc("ImmReleaseContext") @@ -94,6 +96,14 @@ func _GetActiveWindow() windows.HWND { return windows.HWND(r) } +func _ImmAssociateContext(hwnd windows.HWND, hIMC uintptr) (uintptr, error) { + r, _, e := procImmAssociateContext.Call(uintptr(hwnd), hIMC) + if e != nil && !errors.Is(e, windows.ERROR_SUCCESS) { + return 0, fmt.Errorf("textinput: ImmAssociateContext failed: error code: %w", e) + } + return r, nil +} + func _ImmGetCompositionStringW(unnamedParam1 _HIMC, unnamedParam2 uint32, lpBuf unsafe.Pointer, dwBufLen uint32) (uint32, error) { r, _, e := procImmGetCompositionStringW.Call(uintptr(unnamedParam1), uintptr(unnamedParam2), uintptr(lpBuf), uintptr(dwBufLen)) runtime.KeepAlive(lpBuf) diff --git a/exp/textinput/textinput_windows.go b/exp/textinput/textinput_windows.go index 982b02a0c..bc1b4b5d5 100644 --- a/exp/textinput/textinput_windows.go +++ b/exp/textinput/textinput_windows.go @@ -15,6 +15,7 @@ package textinput import ( + "sync" "unsafe" "golang.org/x/sys/windows" @@ -29,8 +30,13 @@ type textInput struct { origWndProc uintptr wndProcCallback uintptr window windows.HWND + immContext uintptr highSurrogate uint16 + + initOnce sync.Once + + err error } var theTextInput textInput @@ -52,11 +58,29 @@ func (t *textInput) Start(x, y int) (chan State, func()) { session.ch <- State{Error: err} session.end() } - return session.ch, session.end + return session.ch, func() { + ui.Get().RunOnMainThread(func() { + // Disable IME again. + if t.immContext != 0 { + return + } + c, err := _ImmAssociateContext(t.window, 0) + if err != nil { + t.err = err + return + } + t.immContext = c + t.end() + }) + } } // start must be called from the main thread. func (t *textInput) start(x, y int) error { + if t.err != nil { + return t.err + } + if t.window == 0 { t.window = _GetActiveWindow() } @@ -72,6 +96,22 @@ func (t *textInput) start(x, y int) error { t.origWndProc = h } + // By default, IME was disabled by setting 0 as the IMM context. + // Restore the context once. + var err error + t.initOnce.Do(func() { + err = ui.Get().RestoreIMMContextOnMainThread() + }) + if err != nil { + return err + } + + if t.immContext != 0 { + if _, err := _ImmAssociateContext(t.window, t.immContext); err != nil { + return err + } + t.immContext = 0 + } h := _ImmGetContext(t.window) if err := _ImmSetCandidateWindow(h, &_CANDIDATEFORM{ dwIndex: 0, @@ -168,14 +208,20 @@ func (t *textInput) send(text string, startInBytes, endInBytes int, committed bo // end must be called from the main thread. func (t *textInput) end() { - if t.session != nil { - t.session.end() - t.session = nil + if t.session == nil { + return } + + t.session.end() + t.session = nil } // update must be called from the main thread. func (t *textInput) update() (ferr error) { + if t.err != nil { + return t.err + } + hIMC := _ImmGetContext(t.window) defer func() { if err := _ImmReleaseContext(t.window, hIMC); err != nil && ferr != nil { @@ -236,6 +282,10 @@ func (t *textInput) update() (ferr error) { // commit must be called from the main thread. func (t *textInput) commit() (ferr error) { + if t.err != nil { + return t.err + } + hIMC := _ImmGetContext(t.window) defer func() { if err := _ImmReleaseContext(t.window, hIMC); err != nil && ferr != nil { diff --git a/internal/ui/api_windows.go b/internal/ui/api_windows.go index 160114953..6a5199910 100644 --- a/internal/ui/api_windows.go +++ b/internal/ui/api_windows.go @@ -68,9 +68,12 @@ type _POINT struct { } var ( + imm32 = windows.NewLazySystemDLL("imm32.dll") ole32 = windows.NewLazySystemDLL("ole32.dll") user32 = windows.NewLazySystemDLL("user32.dll") + procImmAssociateContext = imm32.NewProc("ImmAssociateContext") + procCoCreateInstance = ole32.NewProc("CoCreateInstance") procGetSystemMetrics = user32.NewProc("GetSystemMetrics") @@ -79,6 +82,14 @@ var ( procGetCursorPos = user32.NewProc("GetCursorPos") ) +func _ImmAssociateContext(hwnd windows.HWND, hIMC uintptr) (uintptr, error) { + r, _, e := procImmAssociateContext.Call(uintptr(hwnd), hIMC) + if e != nil && !errors.Is(e, windows.ERROR_SUCCESS) { + return 0, fmt.Errorf("ui: ImmAssociateContext failed: error code: %w", e) + } + return r, nil +} + func _CoCreateInstance(rclsid *windows.GUID, pUnkOuter unsafe.Pointer, dwClsContext uint32, riid *windows.GUID) (unsafe.Pointer, error) { var ptr unsafe.Pointer r, _, _ := procCoCreateInstance.Call(uintptr(unsafe.Pointer(rclsid)), uintptr(pUnkOuter), uintptr(dwClsContext), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(&ptr))) diff --git a/internal/ui/ui_darwin.go b/internal/ui/ui_darwin.go index ed4ff8fe6..6c73bcfc4 100644 --- a/internal/ui/ui_darwin.go +++ b/internal/ui/ui_darwin.go @@ -444,3 +444,7 @@ func (u *UserInterface) setDocumentEdited(edited bool) error { objc.ID(w).Send(sel_setDocumentEdited, edited) return nil } + +func (u *UserInterface) afterWindowCreation() error { + return nil +} diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 28dc21df3..9c3ccf342 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -106,6 +106,9 @@ type userInterfaceImpl struct { showWindowOnce sync.Once bufferOnceSwappedOnce sync.Once + // immContext is used only in Windows. + immContext uintptr + m sync.RWMutex } @@ -897,6 +900,10 @@ func (u *UserInterface) createWindow() error { return err } + if err := u.afterWindowCreation(); err != nil { + return err + } + return nil } diff --git a/internal/ui/ui_linbsd.go b/internal/ui/ui_linbsd.go index 7d07f2bc3..4d8d79149 100644 --- a/internal/ui/ui_linbsd.go +++ b/internal/ui/ui_linbsd.go @@ -204,3 +204,7 @@ func (u *UserInterface) skipTaskbar() error { func (u *UserInterface) setDocumentEdited(edited bool) error { return nil } + +func (u *UserInterface) afterWindowCreation() error { + return nil +} diff --git a/internal/ui/ui_windows.go b/internal/ui/ui_windows.go index b9bdf9ef0..77882e3a8 100644 --- a/internal/ui/ui_windows.go +++ b/internal/ui/ui_windows.go @@ -246,6 +246,38 @@ func (u *UserInterface) setDocumentEdited(edited bool) error { return nil } +func (u *UserInterface) afterWindowCreation() error { + if microsoftgdk.IsXbox() { + return nil + } + + // By default, IME should be disabled (#2918). + w, err := u.window.GetWin32Window() + if err != nil { + return err + } + c, err := _ImmAssociateContext(w, 0) + if err != nil { + return err + } + u.immContext = c + return nil +} + +// RestoreIMMContextOnMainThread is called from the main thread. +// The textinput package invokes RestoreIMMContextOnMainThread to enable IME inputting. +func (u *UserInterface) RestoreIMMContextOnMainThread() error { + w, err := u.window.GetWin32Window() + if err != nil { + return err + } + if _, err := _ImmAssociateContext(w, u.immContext); err != nil { + return err + } + u.immContext = 0 + return nil +} + func init() { if microsoftgdk.IsXbox() { // TimeBeginPeriod might not be defined in Xbox. From f98003bcd564588e1c0ec8de2e2835578c0e599e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 27 Aug 2024 23:55:33 +0900 Subject: [PATCH 090/188] ebiten: add ColorSpace and RunGameOptions.ColorSpace This works only for macOS Metal and WebGL so far. Closes #2871 --- examples/windowsize/main.go | 9 ++++++ graphics.go | 14 ++++++++++ internal/graphicsdriver/graphics.go | 8 ++++++ internal/graphicsdriver/metal/ca/ca_darwin.go | 28 +++++++++++++++---- .../graphicsdriver/metal/graphics_darwin.go | 12 +++++--- internal/graphicsdriver/metal/view_darwin.go | 5 ++-- internal/graphicsdriver/opengl/graphics_js.go | 9 +++++- internal/ui/ui.go | 2 ++ internal/ui/ui_android.go | 1 + internal/ui/ui_darwin.go | 5 ++-- internal/ui/ui_glfw.go | 1 + internal/ui/ui_ios.go | 3 +- internal/ui/ui_js.go | 8 ++++-- internal/ui/ui_linbsd.go | 1 + internal/ui/ui_mobile.go | 4 ++- internal/ui/ui_windows.go | 1 + run.go | 10 +++++++ 17 files changed, 102 insertions(+), 19 deletions(-) diff --git a/examples/windowsize/main.go b/examples/windowsize/main.go index c2bbdb7dc..8722a6e7e 100644 --- a/examples/windowsize/main.go +++ b/examples/windowsize/main.go @@ -50,6 +50,7 @@ var ( flagMaxWindowSize = flag.String("maxwindowsize", "", "maximum window size (e.g., 1920x1080)") flagGraphicsLibrary = flag.String("graphicslibrary", "", "graphics library (e.g. opengl)") flagRunnableOnUnfocused = flag.Bool("runnableonunfocused", true, "whether the app is runnable even on unfocused") + flagColorSpace = flag.String("colorspace", "", "color space ('', 'srgb', or 'display-p3')") ) func init() { @@ -473,6 +474,14 @@ func main() { default: log.Fatalf("unexpected graphics library: %s", *flagGraphicsLibrary) } + switch *flagColorSpace { + case "": + op.ColorSpace = ebiten.ColorSpaceDefault + case "srgb": + op.ColorSpace = ebiten.ColorSpaceSRGB + case "display-p3": + op.ColorSpace = ebiten.ColorSpaceDisplayP3 + } op.InitUnfocused = !*flagInitFocused op.ScreenTransparent = *flagTransparent op.X11ClassName = "Window-Size" diff --git a/graphics.go b/graphics.go index de18ca376..3104309b1 100644 --- a/graphics.go +++ b/graphics.go @@ -72,3 +72,17 @@ type DebugInfo struct { func ReadDebugInfo(d *DebugInfo) { d.GraphicsLibrary = GraphicsLibrary(ui.Get().GraphicsLibrary()) } + +// ColorSpace represents the color space of the screen. +type ColorSpace int + +const ( + // ColorSpaceDefault represents the default color space. + ColorSpaceDefault ColorSpace = iota + + // ColorSpaceSRGB represents the sRGB color space (https://en.wikipedia.org/wiki/SRGB). + ColorSpaceSRGB + + // ColorSpaceDisplayP3 represents the Display P3 color space (https://en.wikipedia.org/wiki/DCI-P3). + ColorSpaceDisplayP3 +) diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index 1c40b16ea..f1960a80f 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -95,3 +95,11 @@ type Shader interface { } type ShaderID int + +type ColorSpace int + +const ( + ColorSpaceDefault ColorSpace = iota + ColorSpaceSRGB + ColorSpaceDisplayP3 +) diff --git a/internal/graphicsdriver/metal/ca/ca_darwin.go b/internal/graphicsdriver/metal/ca/ca_darwin.go index 8c5bce768..2825368e2 100644 --- a/internal/graphicsdriver/metal/ca/ca_darwin.go +++ b/internal/graphicsdriver/metal/ca/ca_darwin.go @@ -29,6 +29,7 @@ import ( "github.com/ebitengine/purego/objc" "github.com/hajimehoshi/ebiten/v2/internal/cocoa" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl" ) @@ -51,7 +52,7 @@ type MetalLayer struct { // NewMetalLayer creates a new Core Animation Metal layer. // // Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc. -func NewMetalLayer() (MetalLayer, error) { +func NewMetalLayer(colorSpace graphicsdriver.ColorSpace) (MetalLayer, error) { coreGraphics, err := purego.Dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", purego.RTLD_LAZY|purego.RTLD_GLOBAL) if err != nil { return MetalLayer{}, err @@ -67,14 +68,31 @@ func NewMetalLayer() (MetalLayer, error) { return MetalLayer{}, err } - kCGColorSpaceDisplayP3, err := purego.Dlsym(coreGraphics, "kCGColorSpaceDisplayP3") - if err != nil { - return MetalLayer{}, err + var colorSpaceSym uintptr + switch colorSpace { + case graphicsdriver.ColorSpaceSRGB: + kCGColorSpaceSRGB, err := purego.Dlsym(coreGraphics, "kCGColorSpaceSRGB") + if err != nil { + return MetalLayer{}, err + } + colorSpaceSym = kCGColorSpaceSRGB + default: + fallthrough + case graphicsdriver.ColorSpaceDisplayP3: + kCGColorSpaceDisplayP3, err := purego.Dlsym(coreGraphics, "kCGColorSpaceDisplayP3") + if err != nil { + return MetalLayer{}, err + } + colorSpaceSym = kCGColorSpaceDisplayP3 } layer := objc.ID(objc.GetClass("CAMetalLayer")).Send(objc.RegisterName("new")) + // setColorspace: is available from iOS 13.0? + // https://github.com/hajimehoshi/ebiten/commit/3af351a2aa31e30affd433429c42130015b302f3 + // TODO: Enable this on iOS as well. if runtime.GOOS != "ios" { - colorspace, _, _ := purego.SyscallN(cgColorSpaceCreateWithName, **(**uintptr)(unsafe.Pointer(&kCGColorSpaceDisplayP3))) // Dlsym returns pointer to symbol so dereference it + // Dlsym returns pointer to symbol so dereference it. + colorspace, _, _ := purego.SyscallN(cgColorSpaceCreateWithName, **(**uintptr)(unsafe.Pointer(&colorSpaceSym))) layer.Send(objc.RegisterName("setColorspace:"), colorspace) purego.SyscallN(cgColorSpaceRelease, colorspace) } diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 685fbe483..0e4660e82 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -35,6 +35,8 @@ import ( type Graphics struct { view view + colorSpace graphicsdriver.ColorSpace + cq mtl.CommandQueue cb mtl.CommandBuffer rce mtl.RenderCommandEncoder @@ -90,7 +92,7 @@ func init() { // NewGraphics creates an implementation of graphicsdriver.Graphics for Metal. // The returned graphics value is nil iff the error is not nil. -func NewGraphics() (graphicsdriver.Graphics, error) { +func NewGraphics(colorSpace graphicsdriver.ColorSpace) (graphicsdriver.Graphics, error) { // On old mac devices like iMac 2011, Metal is not supported (#779). // TODO: Is there a better way to check whether Metal is available or not? // It seems OK to call MTLCreateSystemDefaultDevice multiple times, so this should be fine. @@ -98,12 +100,14 @@ func NewGraphics() (graphicsdriver.Graphics, error) { return nil, fmt.Errorf("metal: mtl.CreateSystemDefaultDevice failed: %w", systemDefaultDeviceErr) } - g := &Graphics{} + g := &Graphics{ + colorSpace: colorSpace, + } if runtime.GOOS != "ios" { // Initializing a Metal device and a layer must be done in the main thread on macOS. // Note that this assumes NewGraphics is called on the main thread on desktops. - if err := g.view.initialize(systemDefaultDevice); err != nil { + if err := g.view.initialize(systemDefaultDevice, colorSpace); err != nil { return nil, err } } @@ -388,7 +392,7 @@ func (g *Graphics) Initialize() error { if runtime.GOOS == "ios" { // Initializing a Metal device and a layer must be done in the render thread on iOS. - if err := g.view.initialize(systemDefaultDevice); err != nil { + if err := g.view.initialize(systemDefaultDevice, g.colorSpace); err != nil { return err } } diff --git a/internal/graphicsdriver/metal/view_darwin.go b/internal/graphicsdriver/metal/view_darwin.go index d45690b81..2a8607beb 100644 --- a/internal/graphicsdriver/metal/view_darwin.go +++ b/internal/graphicsdriver/metal/view_darwin.go @@ -17,6 +17,7 @@ package metal import ( "sync" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl" ) @@ -58,10 +59,10 @@ func (v *view) colorPixelFormat() mtl.PixelFormat { return v.ml.PixelFormat() } -func (v *view) initialize(device mtl.Device) error { +func (v *view) initialize(device mtl.Device, colorSpace graphicsdriver.ColorSpace) error { v.device = device - ml, err := ca.NewMetalLayer() + ml, err := ca.NewMetalLayer(colorSpace) if err != nil { return err } diff --git a/internal/graphicsdriver/opengl/graphics_js.go b/internal/graphicsdriver/opengl/graphics_js.go index 1d98d6119..001f6dfd4 100644 --- a/internal/graphicsdriver/opengl/graphics_js.go +++ b/internal/graphicsdriver/opengl/graphics_js.go @@ -27,7 +27,7 @@ type graphicsPlatform struct { // NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL. // The returned graphics value is nil iff the error is not nil. -func NewGraphics(canvas js.Value) (graphicsdriver.Graphics, error) { +func NewGraphics(canvas js.Value, colorSpace graphicsdriver.ColorSpace) (graphicsdriver.Graphics, error) { var glContext js.Value attr := js.Global().Get("Object").New() @@ -41,6 +41,13 @@ func NewGraphics(canvas js.Value) (graphicsdriver.Graphics, error) { return nil, fmt.Errorf("opengl: getContext for webgl2 failed") } + switch colorSpace { + case graphicsdriver.ColorSpaceSRGB: + glContext.Set("drawingBufferColorSpace", "srgb") + case graphicsdriver.ColorSpaceDisplayP3: + glContext.Set("drawingBufferColorSpace", "display-p3") + } + ctx, err := gl.NewDefaultContext(glContext) if err != nil { return nil, err diff --git a/internal/ui/ui.go b/internal/ui/ui.go index ed7efbee9..7d1436a7a 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -23,6 +23,7 @@ import ( _ "github.com/ebitengine/hideconsole" "github.com/hajimehoshi/ebiten/v2/internal/atlas" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/thread" ) @@ -177,6 +178,7 @@ type RunOptions struct { SkipTaskbar bool SingleThread bool DisableHiDPI bool + ColorSpace graphicsdriver.ColorSpace X11ClassName string X11InstanceName string } diff --git a/internal/ui/ui_android.go b/internal/ui/ui_android.go index 3e9326f48..d688d85fc 100644 --- a/internal/ui/ui_android.go +++ b/internal/ui/ui_android.go @@ -95,6 +95,7 @@ import ( ) type graphicsDriverCreatorImpl struct { + colorSpace graphicsdriver.ColorSpace } func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, GraphicsLibrary, error) { diff --git a/internal/ui/ui_darwin.go b/internal/ui/ui_darwin.go index 6c73bcfc4..c2736e0a1 100644 --- a/internal/ui/ui_darwin.go +++ b/internal/ui/ui_darwin.go @@ -167,6 +167,7 @@ func (u *UserInterface) initializePlatform() error { type graphicsDriverCreatorImpl struct { transparent bool + colorSpace graphicsdriver.ColorSpace } func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, GraphicsLibrary, error) { @@ -189,8 +190,8 @@ func (*graphicsDriverCreatorImpl) newDirectX() (graphicsdriver.Graphics, error) return nil, errors.New("ui: DirectX is not supported in this environment") } -func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { - return metal.NewGraphics() +func (g *graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { + return metal.NewGraphics(g.colorSpace) } func (*graphicsDriverCreatorImpl) newPlayStation5() (graphicsdriver.Graphics, error) { diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 9c3ccf342..1b253618f 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -1089,6 +1089,7 @@ func (u *UserInterface) initOnMainThread(options *RunOptions) error { g, lib, err := newGraphicsDriver(&graphicsDriverCreatorImpl{ transparent: options.ScreenTransparent, + colorSpace: options.ColorSpace, }, options.GraphicsLibrary) if err != nil { return err diff --git a/internal/ui/ui_ios.go b/internal/ui/ui_ios.go index 9da63fecb..190140df7 100644 --- a/internal/ui/ui_ios.go +++ b/internal/ui/ui_ios.go @@ -34,6 +34,7 @@ import ( ) type graphicsDriverCreatorImpl struct { + colorSpace graphicsdriver.ColorSpace } func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, GraphicsLibrary, error) { @@ -57,7 +58,7 @@ func (*graphicsDriverCreatorImpl) newDirectX() (graphicsdriver.Graphics, error) } func (g *graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) { - return metal.NewGraphics() + return metal.NewGraphics(g.colorSpace) } func (*graphicsDriverCreatorImpl) newPlayStation5() (graphicsdriver.Graphics, error) { diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index a8d58fa8f..3fa2dea3f 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -29,7 +29,8 @@ import ( ) type graphicsDriverCreatorImpl struct { - canvas js.Value + canvas js.Value + colorSpace graphicsdriver.ColorSpace } func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, GraphicsLibrary, error) { @@ -38,7 +39,7 @@ func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, Graphics } func (g *graphicsDriverCreatorImpl) newOpenGL() (graphicsdriver.Graphics, error) { - return opengl.NewGraphics(g.canvas) + return opengl.NewGraphics(g.canvas, g.colorSpace) } func (*graphicsDriverCreatorImpl) newDirectX() (graphicsdriver.Graphics, error) { @@ -771,7 +772,8 @@ func (u *UserInterface) initOnMainThread(options *RunOptions) error { } g, lib, err := newGraphicsDriver(&graphicsDriverCreatorImpl{ - canvas: canvas, + canvas: canvas, + colorSpace: options.ColorSpace, }, options.GraphicsLibrary) if err != nil { return err diff --git a/internal/ui/ui_linbsd.go b/internal/ui/ui_linbsd.go index 4d8d79149..c0d77b2b0 100644 --- a/internal/ui/ui_linbsd.go +++ b/internal/ui/ui_linbsd.go @@ -36,6 +36,7 @@ func (u *UserInterface) initializePlatform() error { type graphicsDriverCreatorImpl struct { transparent bool + colorSpace graphicsdriver.ColorSpace } func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, GraphicsLibrary, error) { diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index b86e9a755..c1dd426b8 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -143,7 +143,9 @@ func (u *UserInterface) runMobile(game Game, options *RunOptions) (err error) { u.context = newContext(game) - g, lib, err := newGraphicsDriver(&graphicsDriverCreatorImpl{}, options.GraphicsLibrary) + g, lib, err := newGraphicsDriver(&graphicsDriverCreatorImpl{ + colorSpace: options.ColorSpace, + }, options.GraphicsLibrary) if err != nil { return err } diff --git a/internal/ui/ui_windows.go b/internal/ui/ui_windows.go index 77882e3a8..adadac79e 100644 --- a/internal/ui/ui_windows.go +++ b/internal/ui/ui_windows.go @@ -36,6 +36,7 @@ func (u *UserInterface) initializePlatform() error { type graphicsDriverCreatorImpl struct { transparent bool + colorSpace graphicsdriver.ColorSpace } func (g *graphicsDriverCreatorImpl) newAuto() (graphicsdriver.Graphics, GraphicsLibrary, error) { diff --git a/run.go b/run.go index 4b75970e8..d1f5e3f1a 100644 --- a/run.go +++ b/run.go @@ -22,6 +22,7 @@ import ( "sync/atomic" "github.com/hajimehoshi/ebiten/v2/internal/clock" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -283,6 +284,14 @@ type RunGameOptions struct { // The default (zero) value is false, which means that HiDPI is enabled. DisableHiDPI bool + // ColorSpace indicates the color space of the screen. + // + // ColorSpace is available only with some graphics libraries (macOS Metal and WebGL so far). + // Otherwise, ColorSpace is ignored. + // + // The default (zero) value is ColorSpaceDefault, which means that color space depends on the environment. + ColorSpace ColorSpace + // X11DisplayName is a class name in the ICCCM WM_CLASS window property. X11ClassName string @@ -713,6 +722,7 @@ func toUIRunOptions(options *RunGameOptions) *ui.RunOptions { SkipTaskbar: options.SkipTaskbar, SingleThread: options.SingleThread, DisableHiDPI: options.DisableHiDPI, + ColorSpace: graphicsdriver.ColorSpace(options.ColorSpace), X11ClassName: options.X11ClassName, X11InstanceName: options.X11InstanceName, } From faeb03373ddecf9a54af5e2e2984ea5739960dca Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 12:40:34 +0900 Subject: [PATCH 091/188] examples: remove README --- examples/README.md | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 494ae5b1c..000000000 --- a/examples/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# How to execute the examples - -## Desktops - -```sh -go run github.com/hajimehoshi/ebiten/v2/examples/rotate@latest -``` - -## Android - -Install [gomobile](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile) first. - -```sh -gomobile install github.com/hajimehoshi/ebiten/v2/examples/rotate@latest -``` - -## iOS - -```sh -gomobile build -target=ios -work github.com/hajimehoshi/ebiten/v2/examples/rotate@latest -``` - -Then, open the `WORK` directory, open `main.xcodeproj`, add signing, and run the project. From 67441c482377a92c7c3976ca4e51fd210c75de29 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 13:31:37 +0900 Subject: [PATCH 092/188] internal/testing: avoid os.Exit(0) os.Exit(0) might cause test flakiness. https://github.com/hajimehoshi/ebiten/actions/runs/10650734256/job/29522689113 ``` panic: unexpected call to os.Exit(0) during test goroutine 1 [running]: os.Exit(0x0) /opt/hostedtoolcache/go/1.23.0/x64/src/os/proc.go:67 +0x8 github.com/hajimehoshi/ebiten/v2/internal/testing.MainWithRunLoop(0x19b28c0) /home/runner/work/ebiten/ebiten/internal/testing/testing.go:50 +0xa github.com/hajimehoshi/ebiten/v2/internal/graphicscommand_test.TestMain(...) /home/runner/work/ebiten/ebiten/internal/graphicscommand/image_test.go:42 main.main() _testmain.go:55 +0x5 ``` --- internal/testing/testing.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/testing/testing.go b/internal/testing/testing.go index cea545eb0..bea6f2ce9 100644 --- a/internal/testing/testing.go +++ b/internal/testing/testing.go @@ -47,5 +47,7 @@ func MainWithRunLoop(m *testing.M) { if err := ebiten.RunGame(g); err != nil { panic(err) } - os.Exit(g.code) + if g.code != 0 { + os.Exit(g.code) + } } From 71370a01bd5f87b2f753829afcc3dc7b94b06c59 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 13:58:39 +0900 Subject: [PATCH 093/188] internal/beforemaintest: avoid os.Exit(0) --- internal/beforemaintest/image_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/beforemaintest/image_test.go b/internal/beforemaintest/image_test.go index f2ea4aba3..0e19df6c9 100644 --- a/internal/beforemaintest/image_test.go +++ b/internal/beforemaintest/image_test.go @@ -78,7 +78,9 @@ func TestMain(m *testing.M) { panic(err) } - os.Exit(<-codeCh) + if code := <-codeCh; code != 0 { + os.Exit(code) + } } type testResult struct { From 99b9cff0d08da866f54cf7913904153f0d28a44f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 15:22:01 +0900 Subject: [PATCH 094/188] .github/workflows: specify -test.paniconexit0=false for browsers Closes #3075 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 574bbb129..c79fdc820 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -171,7 +171,7 @@ jobs: if: runner.os != 'macOS' run: | # Wasm tests don't work on macOS with the headless mode enabled, but the headless mode cannot be disabled in GitHub Actions (#2972). - env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... + env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... -test.paniconexit0=false - name: Install ebitenmobile run: | From 2bbceec45146e4f2594f5604f7335aad00ebc36f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 16:20:33 +0900 Subject: [PATCH 095/188] .github/workflows: skip wasm tests for Windows Updates #3076 --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c79fdc820..3bcb17401 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -168,9 +168,10 @@ jobs: env GOARCH=386 EBITENGINE_DIRECTX=version=12 go test -shuffle=on -v ./... - name: go test (Wasm) - if: runner.os != 'macOS' + if: runner.os == 'Linux' run: | # Wasm tests don't work on macOS with the headless mode enabled, but the headless mode cannot be disabled in GitHub Actions (#2972). + # Wasm tests time out on Windows (#3076). env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... -test.paniconexit0=false - name: Install ebitenmobile From 7f1e6cb53850e03af8651c93cb45e6a9e59efd16 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 19:37:08 +0900 Subject: [PATCH 096/188] internal/graphicsdriver/playstation5: add ebitengine_SetVertices --- internal/graphicsdriver/playstation5/graphics_playstation5.cpp | 3 +++ internal/graphicsdriver/playstation5/graphics_playstation5.go | 3 +++ internal/graphicsdriver/playstation5/graphics_playstation5.h | 3 +++ 3 files changed, 9 insertions(+) diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp index 1ee8ec269..0ac926e7c 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp @@ -32,6 +32,9 @@ ebitengine_NewScreenFramebufferImage(int *image, int width, int height) { extern "C" void ebitengine_DisposeImage(int id) {} +void ebitengine_SetVertices(float *vertices, int vertexCount, int *indices, + int indexCount) {} + ebitengine_Error ebitengine_DrawTriangles(int dst, int *srcs, int srcCount, int shader, ebitengine_DstRegion *dstRegions, int dstRegionCount, diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.go b/internal/graphicsdriver/playstation5/graphics_playstation5.go index be1f2bd70..711cd2743 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.go +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.go @@ -72,6 +72,9 @@ func (g *Graphics) SetTransparent(transparent bool) { } func (g *Graphics) SetVertices(vertices []float32, indices []uint32) error { + defer runtime.KeepAlive(vertices) + defer runtime.KeepAlive(indices) + C.ebitengine_SetVertices((*C.float)(&vertices[0]), C.int(len(vertices)), (*C.uint32_t)(&indices[0]), C.int(len(indices))) return nil } diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.h b/internal/graphicsdriver/playstation5/graphics_playstation5.h index 0fd002963..dd36eba53 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.h +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.h @@ -57,6 +57,9 @@ ebitengine_Error ebitengine_NewScreenFramebufferImage(int *image, int width, int height); void ebitengine_DisposeImage(int id); +void ebitengine_SetVertices(float *vertices, int vertexCount, uint32_t *indices, + int indexCount); + ebitengine_Error ebitengine_DrawTriangles(int dst, int *srcs, int srcCount, int shader, ebitengine_DstRegion *dstRegions, int dstRegionCount, From ca54ce69c4d775f7830da04ce9e5c6e34f440d78 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 20:00:41 +0900 Subject: [PATCH 097/188] internal/graphicsdriver/playstation5: bug fix: compile error --- internal/graphicsdriver/playstation5/graphics_playstation5.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp index 0ac926e7c..668a4129f 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp @@ -32,7 +32,7 @@ ebitengine_NewScreenFramebufferImage(int *image, int width, int height) { extern "C" void ebitengine_DisposeImage(int id) {} -void ebitengine_SetVertices(float *vertices, int vertexCount, int *indices, +void ebitengine_SetVertices(float *vertices, int vertexCount, uint32_t *indices, int indexCount) {} ebitengine_Error From 3eda0dd3875bc33ede51779aa58ffce3e3e7d4f8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Sep 2024 20:08:52 +0900 Subject: [PATCH 098/188] internal/graphicsdriver/playstation5: add extern C --- .../graphicsdriver/playstation5/graphics_playstation5.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp index 668a4129f..dd1fee8e5 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.cpp +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.cpp @@ -32,10 +32,10 @@ ebitengine_NewScreenFramebufferImage(int *image, int width, int height) { extern "C" void ebitengine_DisposeImage(int id) {} -void ebitengine_SetVertices(float *vertices, int vertexCount, uint32_t *indices, - int indexCount) {} +extern "C" void ebitengine_SetVertices(float *vertices, int vertexCount, + uint32_t *indices, int indexCount) {} -ebitengine_Error +extern "C" ebitengine_Error ebitengine_DrawTriangles(int dst, int *srcs, int srcCount, int shader, ebitengine_DstRegion *dstRegions, int dstRegionCount, int indexOffset, ebitengine_Blend blend, From b34f9977f6d36ca054178071641933a5ed24745c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 3 Sep 2024 02:18:58 +0900 Subject: [PATCH 099/188] internal/gamepaddb: update the database This uses https://github.com/mdqinc/SDL_GameControllerDB/commit/b4001f8b2dd55a7225c736dae043ab4e737bf0ff --- internal/gamepaddb/gamecontrollerdb.txt | 626 +++++++++--------- .../gamepaddb/gamecontrollerdb_android.txt | 380 +++++------ .../gamepaddb/gamecontrollerdb_linbsd.txt | 97 +-- .../gamecontrollerdb_macos_darwin.txt | 46 +- .../gamepaddb/gamecontrollerdb_windows.txt | 103 +-- 5 files changed, 658 insertions(+), 594 deletions(-) diff --git a/internal/gamepaddb/gamecontrollerdb.txt b/internal/gamepaddb/gamecontrollerdb.txt index 8961ee412..f747d9a12 100644 --- a/internal/gamepaddb/gamecontrollerdb.txt +++ b/internal/gamepaddb/gamecontrollerdb.txt @@ -3,6 +3,7 @@ # Windows 03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000fa190000918d000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, 03000000d0160000040d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, 03000000d0160000050d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, @@ -72,6 +73,7 @@ 03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, @@ -108,13 +110,14 @@ 03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows, 03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, -03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows, 03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, @@ -124,7 +127,7 @@ 03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, 030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ad1b000001f9000000000000,BB 070,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000bc2000005250000000000000,Beitong G3,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a3,righty:a4,start:b15,x:b3,y:b4,platform:Windows, 030000000d0500000208000000000000,Belkin Nostromo N40,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, @@ -141,8 +144,8 @@ 030000006b1400000209000000000000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, -03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, @@ -172,6 +175,7 @@ 03000000317300000100000000000000,DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows, 03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, @@ -223,7 +227,7 @@ 030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000000d0f00001110000000000000,GameStick Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 0300000047530000616d000000000000,GameStop,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows, 030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, @@ -257,7 +261,7 @@ 030000000d0f00002500000000000000,Hori Fighting Commander 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00008400000000000000,Hori Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,platform:Windows, @@ -265,7 +269,7 @@ 030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00008800000000000000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -030000000d0f00008700000000000000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008700000000000000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, @@ -275,8 +279,8 @@ 030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows, -030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -310,7 +314,7 @@ 030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006e00000000000000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, @@ -322,6 +326,7 @@ 03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows, 03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows, 03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows, +03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows, 03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows, 03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, @@ -376,22 +381,22 @@ 03000000380700006352000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700006652000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005032000000000000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700008031000000000000,Mad Catz FightStick Alpha PS3 ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000003807000038b7000000000000,Mad Catz Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700008433000000000000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000003807000056a8000000000000,Mad Catz PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700001888000000000000,Mad Catz SFIV Fightstick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 FightStick,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, @@ -404,6 +409,7 @@ 03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000242e0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, 03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, 03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows, 030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, @@ -435,11 +441,12 @@ 03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows, 03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows, +030000006f0e00001311000000000000,N64 Controller,+rightx:b10,+righty:b3,-rightx:b0,-righty:b11,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,platform:Windows, 030000006b140000010c000000000000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000085320000170d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000085320000190d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, -030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000bd12000001c0000000000000,Nebular,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000eb0300000000000000000000,NeGcon Adapter,a:a2,b:b13,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,lefttrigger:a4,leftx:a1,righttrigger:b11,start:b3,x:a3,y:b12,platform:Windows, @@ -482,7 +489,7 @@ 03000000f0250000c183000000000000,PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d9040000160f000000000000,PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000d620000011a7000000000000,PowerA Core Plus GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -520,28 +527,28 @@ 030000008f0e00000300000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ba2200002010000000000000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Windows, -03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, -030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c0500005f0e000000000000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000f20d000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, @@ -556,7 +563,7 @@ 03000000300f00001210000000000000,Qanba Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000341a00000104000000000000,Qanba Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, 03000000222c00000223000000000000,Qanba Obsidian Arcade Stick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,platform:Windows, 03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, @@ -584,10 +591,11 @@ 030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows, 03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, @@ -595,7 +603,7 @@ 03000000321500000a10000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000410000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,platform:Windows, 03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, @@ -652,7 +660,7 @@ 03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows, 03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, 03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, @@ -662,7 +670,7 @@ 03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows, 03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, -03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows, 03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, @@ -677,7 +685,7 @@ 03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000de280000ff11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, @@ -739,8 +747,8 @@ 03000000632500002305000000000000,USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows, 03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000007e0500003003000000000000,Wii U Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, @@ -799,6 +807,7 @@ 03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows, +030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows, 03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, @@ -851,6 +860,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, @@ -869,6 +879,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, +03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, 03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, 03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X, 030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, @@ -878,9 +889,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000008a3500000302000000010000,Backbone One PlayStationÆ Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, 03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, -03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000d8140000cecf000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X, @@ -895,7 +906,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000ac0500001a06000002020000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, -03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X, 030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -904,20 +915,21 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008400000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008500000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000341a00000302000014010000,Hori Fighting Stick Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008800000000010000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00008700000000010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00008700000000010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -940,8 +952,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,platform:Mac OS X, 03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, @@ -985,10 +997,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, -030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 0300004b4c0500005f0e000000010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, @@ -1001,10 +1013,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Mac OS X, 030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000321500000204000000010000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000321500000010000000010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, -03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, @@ -1028,8 +1040,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, 030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, -030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, @@ -1059,8 +1071,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, 03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X, -030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, 050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, 030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, @@ -1075,7 +1087,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, -030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, @@ -1085,6 +1097,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X, @@ -1104,6 +1117,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a20000000020000,8BitDo M30 Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, @@ -1148,12 +1162,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000631000010010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000631000014010000,8BitDo Ultimate Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, @@ -1161,7 +1178,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, -050000005e040000e002000030110000,8BitDo Zero 2,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, 05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -1182,23 +1198,23 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -03000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -03000000503200000110000011010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000044010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000046010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -03000000503200000210000000000000,Atari Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, -03000000503200000210000011010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, -05000000503200000210000000000000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, -05000000503200000210000045010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, -05000000503200000210000046010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, +03000000503200000210000011010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +05000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +05000000503200000210000045010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +05000000503200000210000046010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, 05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, 030000008a3500000201000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000202000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000302000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000402000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c62400001b89000011010000,BDA MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000c31100000791000011010000,Be1 GC101 Controller 1.03,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e0400008e02000003030000,Be1 GC101 Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -1206,8 +1222,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000bc2000000055000001000000,Betop AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006b1400000209000011010000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux, @@ -1241,6 +1257,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008f0e00000800000010010000,Gasia PlayStation Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Linux, 03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux, 190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux, @@ -1257,17 +1274,16 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, 03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, 030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f00005001000009040000,Hori Fighting Commander OCTA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b3,y:b2,platform:Linux, 030000000d0f00008800000011010000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00008700000011010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00008700000011010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00004d00000011010000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -1280,9 +1296,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -1333,12 +1350,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000380700006652000025010000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000380700005032000011010000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000380700008034000011010000,Mad Catz Fightstick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000380700008433000011010000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000380700001888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700003888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700001647000010040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -1381,6 +1398,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b000001050000,Microsoft Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, 03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, @@ -1394,6 +1412,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000c62400001a89000000010000,MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000250900006688000000010000,MP8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001311000011010000,N64 Controller,+rightx:b10,+righty:b3,-rightx:b0,-righty:b11,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,platform:Linux, 030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006b140000010c000010010000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000853200000706000012010000,Nacon GC-100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -1457,7 +1476,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000d620000011a7000011010000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -1472,6 +1491,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c62400001a54000001010000,PowerA Xbox One Mini Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000000b20000001010000,PowerA Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -1488,18 +1508,18 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 0300004b4c0500005f0e000011010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, @@ -1511,25 +1531,29 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, 03000000222c00000225000011010000,Qanba Dragon Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000222c00000025000011010000,Qanba Dragon Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00000025000011010000,Qanba Dragon Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick (PS5),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux, 03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, 030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux, +0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, 03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000321500000a10000001000000,Razer Raiju Tournament Edition,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -1563,6 +1587,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000a306000020f6000011010000,Saitek PS2700 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, +03000000952e00004b43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004d43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux, 03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux, 03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, @@ -1574,7 +1601,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000bc2000000055000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -1644,8 +1671,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, 03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, -030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 05000000ac0500003232000001000000,VR Box Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, @@ -1680,7 +1707,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000015050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, @@ -1692,6 +1720,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000014050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000130b000017050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, @@ -1706,178 +1736,180 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, # Android -38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38426974446f20417263616465205374,8BitDo Arcade Stick,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, -64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, -34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, -38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, +38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000006500000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a5,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000051060000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, -32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, -38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,platform:Android, -64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android, -66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android, -66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android, -38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, -61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, -62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, -33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, -38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, +38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,platform:Android, +64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android, +66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android, +66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android, +38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, 05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, -38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, -32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38326536643339353865323063616339,8BitDo P30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38426974446f2050333020636c617373,8BitDo P30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,platform:Android, -38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b3,y:b2,platform:Android, -38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38326536643339353865323063616339,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f2050333020636c617373,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,platform:Android, +38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +61333362366131643730353063616330,8BitDo Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b3,y:b2,platform:Android, +38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,platform:Android, +38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,platform:Android, 61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b31,start:b5,x:b30,y:b2,platform:Android, 05000000c82d000012900000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000062280000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000002600000ffff0f00,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, -38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002028000009000000ffff3f00,8BitDo SNES30,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000003512000020ab000000780f00,8BitDo SNES30,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android, 33666663316164653937326237613331,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 38426974646f205a65726f2047616d65,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, -63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, -32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, -4c696e757820342e31392e3137322077,Anbernic Gamepad,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, +33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, +63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +4c696e757820342e31392e3137322077,Anbernic Gamepad,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, 417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, -35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,start:b6,platform:Android, -32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,platform:Android, +32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4f64696e20436f6e74726f6c6c657200,AYN Odin,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b14,dpright:b13,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:+a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, -4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, -30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,x:b2,y:b3,platform:Android, +34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,x:b2,y:b3,platform:Android, 05000000b404000011240000dfff3f00,Flydigi Vader 2,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, -66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, -35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, -65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b18,platform:Android, -31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, -30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b18,platform:Android, +31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, 62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, -31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, -5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, 0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android, -5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android, +5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android, 64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android, -532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android, -65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android, -33346566643039343630376565326335,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, -35313531613435623366313835326238,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, -4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, -38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -39363561613936303237333537383931,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, -64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4c6f6769746563682047616d65706164,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, -4c756e612047616d6570616400000000,Luna Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, -6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,platform:Android, -436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, -65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, -37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, -4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, -535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, -30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, -39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,platform:Android, +532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android, +65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android, +33346566643039343630376565326335,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +35313531613435623366313835326238,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39363561613936303237333537383931,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, +64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4c6f6769746563682047616d65706164,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +4c756e612047616d6570616400000000,Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, +6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,platform:Android, +436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,platform:Android, 31323564663862633234646330373138,Mega Drive,a:b23,b:b22,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, -37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, -64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Android, -33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, +37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b9,x:b2,y:b3,platform:Android, +33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, 30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android, -32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Android, 050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android, 34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,platform:Android, -36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, -4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, -534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,platform:Android, +36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, -37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39383335313438623439373538343266,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b16,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, 4f5559412047616d6520436f6e74726f,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b6,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, -506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, 62653335326261303663356263626339,PlayStation Classic Controller,a:b19,b:b1,back:b17,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b3,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,platform:Android, -536f6e7920496e746572616374697665,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -576972656c65737320436f6e74726f6c,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,platform:Android, -32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,platform:Android, +536f6e7920496e746572616374697665,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +576972656c65737320436f6e74726f6c,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,platform:Android, +32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,platform:Android, 050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 536f6e7920504c415953544154494f4e,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 61363034663839376638653463633865,PS3 Controller,a:b0,b:b1,back:b15,dpdown:a14,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, @@ -1889,89 +1921,89 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, -31326235383662333266633463653332,PS4 Controller,a:b1,b:b16,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,platform:Android, -31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -34613139376634626133336530386430,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, -37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -63393662363836383439353064663939,PS4 Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +31326235383662333266633463653332,PS4 Controller,a:b1,b:b16,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,platform:Android, +31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34613139376634626133336530386430,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +63393662363836383439353064663939,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f80,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000e60c0000ffff3f00,PS5 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, -37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, -61303162353165316365336436343139,PS5 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, -64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, -36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +61303162353165316365336436343139,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000f8270000bf0b0000ffff3f00,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000005070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000007070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, -5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,platform:Android, +5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,platform:Android, 32417865732031314b6579732047616d,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, -526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, -61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, -526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b6,platform:Android, +526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, +61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, +526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b6,platform:Android, 37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, 5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, 526574726f5553422e636f6d20534e45,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, 64643037633038386238303966376137,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, -37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, -36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, +36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 66386565396238363534313863353065,Sanwa PlayOnline Mobile,a:b21,b:b22,back:b23,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b24,platform:Android, -32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, 38613865396530353338373763623431,Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b10,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,platform:Android, -61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, -30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, -66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, 5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, -5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, -63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android, -476f6f676c65204c4c43205374616469,Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, +63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android, +476f6f676c65204c4c43205374616469,Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 5374616469614e3848532d6532633400,Stadia Controller,a:b0,b:b1,back:b15,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, -0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, +0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, 35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android, 33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,platform:Android, -54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android, 38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,platform:Android, 050000004f0400000ed00000fffe3f00,ThrustMaster eSwap Pro Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, -30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, -33333034646336346339646538643633,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, +30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +33333034646336346339646538643633,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, 050000005e0400008e02000000783f00,Xbox 360 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 30396232393162346330326334636566,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38313038323730383864666463383533,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65353331386662343338643939643636,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65613532386633373963616462363038,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, @@ -1979,11 +2011,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, 050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, 050000005e040000fd020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, -35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, +35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e040000000b000000783f80,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, @@ -1992,8 +2024,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e040000120b000000783f80,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, # iOS diff --git a/internal/gamepaddb/gamecontrollerdb_android.txt b/internal/gamepaddb/gamecontrollerdb_android.txt index df45c9026..7b6bb640e 100644 --- a/internal/gamepaddb/gamecontrollerdb_android.txt +++ b/internal/gamepaddb/gamecontrollerdb_android.txt @@ -1,175 +1,177 @@ -38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38426974446f20417263616465205374,8BitDo Arcade Stick,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, -64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, -34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, -38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, +38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000006500000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a5,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000051060000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, -32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, -38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,platform:Android, -64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android, -66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android, -66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android, -38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, -61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, -62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, -33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, -38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, +38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,platform:Android, +64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android, +66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android, +66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android, +38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, 05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, -38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, -32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38326536643339353865323063616339,8BitDo P30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38426974446f2050333020636c617373,8BitDo P30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,platform:Android, -38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b3,y:b2,platform:Android, -38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38326536643339353865323063616339,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f2050333020636c617373,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,platform:Android, +38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +61333362366131643730353063616330,8BitDo Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b3,y:b2,platform:Android, +38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,platform:Android, +38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,platform:Android, 61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b31,start:b5,x:b30,y:b2,platform:Android, 05000000c82d000012900000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000062280000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000002600000ffff0f00,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, -38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002028000009000000ffff3f00,8BitDo SNES30,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000003512000020ab000000780f00,8BitDo SNES30,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android, 33666663316164653937326237613331,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 38426974646f205a65726f2047616d65,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, -63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, -32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, -4c696e757820342e31392e3137322077,Anbernic Gamepad,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, +33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, +63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +4c696e757820342e31392e3137322077,Anbernic Gamepad,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, 417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, -35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,start:b6,platform:Android, -32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,platform:Android, +32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4f64696e20436f6e74726f6c6c657200,AYN Odin,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b14,dpright:b13,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:+a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, -4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, -30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,x:b2,y:b3,platform:Android, +34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,x:b2,y:b3,platform:Android, 05000000b404000011240000dfff3f00,Flydigi Vader 2,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, -66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, -35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, -65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b18,platform:Android, -31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, -30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b18,platform:Android, +31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, 62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, -31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, -5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, 0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android, -5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android, +5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android, 64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android, -532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android, -65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android, -33346566643039343630376565326335,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, -35313531613435623366313835326238,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, -4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, -38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -39363561613936303237333537383931,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, -64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4c6f6769746563682047616d65706164,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, -4c756e612047616d6570616400000000,Luna Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, -6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,platform:Android, -436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, -65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, -37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, -4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, -535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, -30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, -39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,platform:Android, +532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android, +65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android, +33346566643039343630376565326335,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +35313531613435623366313835326238,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39363561613936303237333537383931,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, +64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4c6f6769746563682047616d65706164,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +4c756e612047616d6570616400000000,Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, +6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,platform:Android, +436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,platform:Android, 31323564663862633234646330373138,Mega Drive,a:b23,b:b22,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, -37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, -64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Android, -33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, +37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b9,x:b2,y:b3,platform:Android, +33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, 30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android, -32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Android, 050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android, 34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,platform:Android, -36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, -4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, -534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, -64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,platform:Android, +36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, -37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39383335313438623439373538343266,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b16,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, 4f5559412047616d6520436f6e74726f,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b6,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, -506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, 62653335326261303663356263626339,PlayStation Classic Controller,a:b19,b:b1,back:b17,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b3,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,platform:Android, -536f6e7920496e746572616374697665,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -576972656c65737320436f6e74726f6c,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,platform:Android, -32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,platform:Android, +536f6e7920496e746572616374697665,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +576972656c65737320436f6e74726f6c,PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,platform:Android, +32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,platform:Android, 050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 536f6e7920504c415953544154494f4e,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 61363034663839376638653463633865,PS3 Controller,a:b0,b:b1,back:b15,dpdown:a14,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, @@ -181,89 +183,89 @@ 050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, -31326235383662333266633463653332,PS4 Controller,a:b1,b:b16,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,platform:Android, -31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -34613139376634626133336530386430,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, -37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -63393662363836383439353064663939,PS4 Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +31326235383662333266633463653332,PS4 Controller,a:b1,b:b16,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,platform:Android, +31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34613139376634626133336530386430,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +63393662363836383439353064663939,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f80,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000e60c0000ffff3f00,PS5 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, -37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, -61303162353165316365336436343139,PS5 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, -64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, -36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +61303162353165316365336436343139,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000f8270000bf0b0000ffff3f00,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000005070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000007070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, -5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,platform:Android, +5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,platform:Android, 32417865732031314b6579732047616d,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, -526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, -61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, -526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b6,platform:Android, +526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, +61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, +526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b6,platform:Android, 37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, 5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, 526574726f5553422e636f6d20534e45,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, 64643037633038386238303966376137,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, -37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, -33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, -36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, +36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 66386565396238363534313863353065,Sanwa PlayOnline Mobile,a:b21,b:b22,back:b23,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b24,platform:Android, -32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, 38613865396530353338373763623431,Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b10,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,platform:Android, -61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, -30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, -66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, -62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, 5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, -5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, -63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android, -476f6f676c65204c4c43205374616469,Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, +63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android, +476f6f676c65204c4c43205374616469,Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 5374616469614e3848532d6532633400,Stadia Controller,a:b0,b:b1,back:b15,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, -0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, +0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, 35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android, 33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,platform:Android, -54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android, 38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,platform:Android, 050000004f0400000ed00000fffe3f00,ThrustMaster eSwap Pro Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, -30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, -33333034646336346339646538643633,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, +30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +33333034646336346339646538643633,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, 050000005e0400008e02000000783f00,Xbox 360 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 30396232393162346330326334636566,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38313038323730383864666463383533,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65353331386662343338643939643636,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65613532386633373963616462363038,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, @@ -271,11 +273,11 @@ 050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, 050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, 050000005e040000fd020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, -35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, +35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e040000000b000000783f80,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, @@ -284,6 +286,6 @@ 050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e040000120b000000783f80,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, diff --git a/internal/gamepaddb/gamecontrollerdb_linbsd.txt b/internal/gamepaddb/gamecontrollerdb_linbsd.txt index 16ad405df..0ec4335f3 100644 --- a/internal/gamepaddb/gamecontrollerdb_linbsd.txt +++ b/internal/gamepaddb/gamecontrollerdb_linbsd.txt @@ -10,6 +10,7 @@ 03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a20000000020000,8BitDo M30 Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, @@ -54,12 +55,15 @@ 05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000631000010010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000631000014010000,8BitDo Ultimate Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, @@ -67,7 +71,6 @@ 05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, -050000005e040000e002000030110000,8BitDo Zero 2,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, 05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -88,23 +91,23 @@ 05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -03000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -03000000503200000110000011010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000044010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000046010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -03000000503200000210000000000000,Atari Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, -03000000503200000210000011010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, -05000000503200000210000000000000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, -05000000503200000210000045010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, -05000000503200000210000046010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, +03000000503200000210000011010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +05000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +05000000503200000210000045010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +05000000503200000210000046010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, 05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, 030000008a3500000201000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000202000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000302000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000402000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c62400001b89000011010000,BDA MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000c31100000791000011010000,Be1 GC101 Controller 1.03,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e0400008e02000003030000,Be1 GC101 Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -112,8 +115,8 @@ 05000000bc2000000055000001000000,Betop AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006b1400000209000011010000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux, @@ -147,6 +150,7 @@ 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008f0e00000800000010010000,Gasia PlayStation Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Linux, 03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux, 190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux, @@ -163,17 +167,16 @@ 06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, 03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, 030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f00005001000009040000,Hori Fighting Commander OCTA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b3,y:b2,platform:Linux, 030000000d0f00008800000011010000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00008700000011010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00008700000011010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00004d00000011010000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -186,9 +189,10 @@ 030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -239,12 +243,12 @@ 05000000380700006652000025010000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000380700005032000011010000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000380700008034000011010000,Mad Catz Fightstick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000380700008433000011010000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000380700001888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700003888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700001647000010040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -287,6 +291,7 @@ 050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b000001050000,Microsoft Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, 03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, @@ -300,6 +305,7 @@ 05000000c62400001a89000000010000,MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000250900006688000000010000,MP8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001311000011010000,N64 Controller,+rightx:b10,+righty:b3,-rightx:b0,-righty:b11,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,platform:Linux, 030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006b140000010c000010010000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000853200000706000012010000,Nacon GC-100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -363,7 +369,7 @@ 030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000d620000011a7000011010000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -378,6 +384,7 @@ 03000000c62400001a54000001010000,PowerA Xbox One Mini Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000000b20000001010000,PowerA Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -394,18 +401,18 @@ 050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 0300004b4c0500005f0e000011010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, @@ -417,25 +424,29 @@ 050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, 03000000222c00000225000011010000,Qanba Dragon Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000222c00000025000011010000,Qanba Dragon Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00000025000011010000,Qanba Dragon Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick (PS5),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux, 03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, 030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux, +0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, 03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000321500000a10000001000000,Razer Raiju Tournament Edition,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -469,6 +480,9 @@ 03000000a306000020f6000011010000,Saitek PS2700 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, +03000000952e00004b43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004d43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux, 03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux, 03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, @@ -480,7 +494,7 @@ 03000000bc2000000055000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -550,8 +564,8 @@ 03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, 03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, -030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 05000000ac0500003232000001000000,VR Box Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, @@ -586,7 +600,8 @@ 030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000015050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, @@ -598,6 +613,8 @@ 060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000014050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000130b000017050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, diff --git a/internal/gamepaddb/gamecontrollerdb_macos_darwin.txt b/internal/gamepaddb/gamecontrollerdb_macos_darwin.txt index 945d0e67d..60fb7cf2f 100644 --- a/internal/gamepaddb/gamecontrollerdb_macos_darwin.txt +++ b/internal/gamepaddb/gamecontrollerdb_macos_darwin.txt @@ -45,6 +45,7 @@ 03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, @@ -63,6 +64,7 @@ 03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, +03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, 03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, 03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X, 030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, @@ -72,9 +74,9 @@ 030000008a3500000302000000010000,Backbone One PlayStationÆ Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, 03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, -03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000d8140000cecf000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X, @@ -89,7 +91,7 @@ 03000000ac0500001a06000002020000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, -03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X, 030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -98,20 +100,21 @@ 030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008400000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008500000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000341a00000302000014010000,Hori Fighting Stick Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008800000000010000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00008700000000010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00008700000000010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -134,8 +137,8 @@ 030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,platform:Mac OS X, 03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, @@ -179,10 +182,10 @@ 030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, -030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 0300004b4c0500005f0e000000010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, @@ -195,10 +198,10 @@ 030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Mac OS X, 030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000321500000204000000010000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000321500000010000000010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, -03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, @@ -222,8 +225,8 @@ 03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, 030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, -030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, @@ -253,8 +256,8 @@ 03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, 03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X, -030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, 050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, 030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, @@ -269,7 +272,7 @@ 030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, -030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, @@ -279,6 +282,7 @@ 030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X, diff --git a/internal/gamepaddb/gamecontrollerdb_windows.txt b/internal/gamepaddb/gamecontrollerdb_windows.txt index bcf2fde39..b2cb61444 100644 --- a/internal/gamepaddb/gamecontrollerdb_windows.txt +++ b/internal/gamepaddb/gamecontrollerdb_windows.txt @@ -1,4 +1,5 @@ 03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000fa190000918d000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, 03000000d0160000040d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, 03000000d0160000050d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, @@ -68,6 +69,7 @@ 03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, @@ -104,13 +106,14 @@ 03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows, 03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, -03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows, 03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, @@ -120,7 +123,7 @@ 03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, 030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ad1b000001f9000000000000,BB 070,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000bc2000005250000000000000,Beitong G3,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a3,righty:a4,start:b15,x:b3,y:b4,platform:Windows, 030000000d0500000208000000000000,Belkin Nostromo N40,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, @@ -137,8 +140,8 @@ 030000006b1400000209000000000000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, -03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, @@ -168,6 +171,7 @@ 03000000317300000100000000000000,DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows, 03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, @@ -219,7 +223,7 @@ 030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000000d0f00001110000000000000,GameStick Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 0300000047530000616d000000000000,GameStop,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows, 030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, @@ -253,7 +257,7 @@ 030000000d0f00002500000000000000,Hori Fighting Commander 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00008400000000000000,Hori Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,platform:Windows, @@ -261,7 +265,7 @@ 030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00008800000000000000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -030000000d0f00008700000000000000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008700000000000000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, @@ -271,8 +275,8 @@ 030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows, -030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -306,7 +310,7 @@ 030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006e00000000000000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, @@ -318,6 +322,7 @@ 03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows, 03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows, 03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows, +03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows, 03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows, 03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, @@ -372,22 +377,22 @@ 03000000380700006352000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700006652000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005032000000000000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700008031000000000000,Mad Catz FightStick Alpha PS3 ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000003807000038b7000000000000,Mad Catz Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700008433000000000000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000003807000056a8000000000000,Mad Catz PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700001888000000000000,Mad Catz SFIV Fightstick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 FightStick,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, @@ -400,6 +405,7 @@ 03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000242e0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, 03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, 03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows, 030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, @@ -431,11 +437,12 @@ 03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows, 03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows, +030000006f0e00001311000000000000,N64 Controller,+rightx:b10,+righty:b3,-rightx:b0,-righty:b11,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,platform:Windows, 030000006b140000010c000000000000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000085320000170d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000085320000190d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, -030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000bd12000001c0000000000000,Nebular,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000eb0300000000000000000000,NeGcon Adapter,a:a2,b:b13,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,lefttrigger:a4,leftx:a1,righttrigger:b11,start:b3,x:a3,y:b12,platform:Windows, @@ -478,7 +485,7 @@ 03000000f0250000c183000000000000,PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d9040000160f000000000000,PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000d620000011a7000000000000,PowerA Core Plus GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -516,28 +523,28 @@ 030000008f0e00000300000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ba2200002010000000000000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Windows, -03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, -030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c0500005f0e000000000000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000f20d000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, @@ -552,7 +559,7 @@ 03000000300f00001210000000000000,Qanba Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000341a00000104000000000000,Qanba Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, 03000000222c00000223000000000000,Qanba Obsidian Arcade Stick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,platform:Windows, 03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, @@ -580,10 +587,11 @@ 030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows, 03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, @@ -591,7 +599,7 @@ 03000000321500000a10000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000410000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,platform:Windows, 03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, @@ -648,7 +656,7 @@ 03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows, 03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, 03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, @@ -658,7 +666,7 @@ 03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows, 03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, -03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows, 03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, @@ -673,7 +681,7 @@ 03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000de280000ff11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, @@ -735,8 +743,8 @@ 03000000632500002305000000000000,USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows, 03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000007e0500003003000000000000,Wii U Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, @@ -795,6 +803,7 @@ 03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows, +030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows, 03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, From 81d89e15d884a4ddf8cba0e0d78ff48aaac69a58 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 5 Sep 2024 18:06:42 +0900 Subject: [PATCH 100/188] all: update bitmapfont --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ff2012810..c00e01164 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/ebitengine/purego v0.8.0-alpha.5 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 - github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4 + github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4.0.20240905090502-03ded50a2328 github.com/hajimehoshi/go-mp3 v0.3.4 github.com/jakecoffman/cp v1.2.1 github.com/jezek/xgb v1.1.1 diff --git a/go.sum b/go.sum index 714179a03..982e722a7 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZ github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4 h1:0sMBL2GS9QPgSaExj8UmVpDk667RbT2SXVZprmwEPlY= -github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4/go.mod h1:/GmYyEKgzzM7dzJBsL7aS5iR83Dr666E5bhQLVVPYsw= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4.0.20240905090502-03ded50a2328 h1:AYqx/yoXuPMmNJMXEblljVeqFDQnKFu5QOjHZMhGfMY= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4.0.20240905090502-03ded50a2328/go.mod h1:/GmYyEKgzzM7dzJBsL7aS5iR83Dr666E5bhQLVVPYsw= github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= From e43bb3898b5603ce145db10665cf0d3fb3f320af Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 10:42:36 +0900 Subject: [PATCH 101/188] internal/file: bug fix: VirtualFS.Open(".") should always return a new entry Closes #3081 --- internal/file/file_notjs.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/internal/file/file_notjs.go b/internal/file/file_notjs.go index d6f5a8b12..f9428b85b 100644 --- a/internal/file/file_notjs.go +++ b/internal/file/file_notjs.go @@ -29,15 +29,22 @@ import ( ) type VirtualFS struct { - root virtualFSRoot + paths []string } func NewVirtualFS(paths []string) *VirtualFS { fs := &VirtualFS{} - fs.root.addRealPaths(paths) + fs.paths = make([]string, len(paths)) + copy(fs.paths, paths) return fs } +func (v *VirtualFS) newRootFS() *virtualFSRoot { + var root virtualFSRoot + root.addRealPaths(v.paths) + return &root +} + func (v *VirtualFS) Open(name string) (fs.File, error) { if !fs.ValidPath(name) { return nil, &fs.PathError{ @@ -48,15 +55,16 @@ func (v *VirtualFS) Open(name string) (fs.File, error) { } if name == "." { - return &v.root, nil + return v.newRootFS(), nil } // A valid path must not include a token "." or "..", except for "." itself. es := strings.Split(name, "/") - for _, realPath := range v.root.realPaths { + for _, realPath := range v.paths { if filepath.Base(realPath) != es[0] { continue } + // TODO: Does this work on Windows? return os.Open(filepath.Join(append([]string{realPath}, es[1:]...)...)) } From 0e6702c043f83d096709520ac329799b0e9b5270 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 11:03:39 +0900 Subject: [PATCH 102/188] internal/file: update comments --- internal/file/file_notjs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/file/file_notjs.go b/internal/file/file_notjs.go index f9428b85b..ea58456af 100644 --- a/internal/file/file_notjs.go +++ b/internal/file/file_notjs.go @@ -64,7 +64,8 @@ func (v *VirtualFS) Open(name string) (fs.File, error) { if filepath.Base(realPath) != es[0] { continue } - // TODO: Does this work on Windows? + // os.File should implement fs.File interface, so this should be fine even on Windows. + // See https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/os/file.go;l=695-710 return os.Open(filepath.Join(append([]string{realPath}, es[1:]...)...)) } From 6eb0271f83cb94340d5789344a1bfa22b06e8c54 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 18:37:51 +0900 Subject: [PATCH 103/188] internal/graphicscommand: better log for read/write pixels --- internal/graphicscommand/command.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 71bfc6434..d69d7ed8a 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -221,7 +221,11 @@ type writePixelsCommandArgs struct { } func (c *writePixelsCommand) String() string { - return fmt.Sprintf("write-pixels: dst: %d, len(args): %d", c.dst.id, len(c.args)) + var args []string + for _, a := range c.args { + args = append(args, fmt.Sprintf("region: %s", a.region.String())) + } + return fmt.Sprintf("write-pixels: dst: %d, args: %s", c.dst.id, strings.Join(args, ", ")) } // Exec executes the writePixelsCommand. @@ -270,7 +274,11 @@ func (c *readPixelsCommand) NeedsSync() bool { } func (c *readPixelsCommand) String() string { - return fmt.Sprintf("read-pixels: image: %d", c.img.id) + var args []string + for _, a := range c.args { + args = append(args, fmt.Sprintf("region: %s", a.Region.String())) + } + return fmt.Sprintf("read-pixels: image: %d, args: %v", c.img.id, strings.Join(args, ", ")) } // disposeImageCommand represents a command to dispose an image. From cb63f1e56bb9ab10529e8526233aa8a3aa9801bb Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 7 Sep 2024 16:53:01 +0900 Subject: [PATCH 104/188] mobile/ebitenmobileview: refactoring --- mobile/ebitenmobileview/mobile.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile/ebitenmobileview/mobile.go b/mobile/ebitenmobileview/mobile.go index d83e0090c..4f00062fe 100644 --- a/mobile/ebitenmobileview/mobile.go +++ b/mobile/ebitenmobileview/mobile.go @@ -117,8 +117,7 @@ func DeviceScale() float64 { } type RenderRequester interface { - SetExplicitRenderingMode(explicitRendering bool) - RequestRenderIfNeeded() + ui.RenderRequester } func SetRenderRequester(renderRequester RenderRequester) { From 81c75e1b0aedd1df055dc7939a241a32bdb685f0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 15:23:40 +0900 Subject: [PATCH 105/188] Revert "internal/atlas: refactoring: remove ImageTypeVolatile" This reverts commit def82fd5d30e576134b057c26db84278afa1b66a. Updates #3083 --- gameforui.go | 4 ++++ internal/atlas/image.go | 2 ++ internal/atlas/image_test.go | 6 +++--- internal/ui/context.go | 6 ++++++ internal/ui/image.go | 12 +++++++++++- 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/gameforui.go b/gameforui.go index e496e7843..d86e4fda8 100644 --- a/gameforui.go +++ b/gameforui.go @@ -66,6 +66,10 @@ func (g *gameForUI) NewOffscreenImage(width, height int) *ui.Image { // An image on an atlas is surrounded by a transparent edge, // and the shader program unexpectedly picks the pixel on the edges. imageType := atlas.ImageTypeUnmanaged + if ui.Get().IsScreenClearedEveryFrame() { + // A volatile image is also always isolated. + imageType = atlas.ImageTypeVolatile + } g.offscreen = newImage(image.Rect(0, 0, width, height), imageType) return g.offscreen.image } diff --git a/internal/atlas/image.go b/internal/atlas/image.go index ff6d8d75f..980d24100 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -224,6 +224,8 @@ const ( // A screen image is also unmanaged. ImageTypeScreen + ImageTypeVolatile + // ImageTypeUnmanaged is an unmanaged image that is not on an atlas. ImageTypeUnmanaged ) diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 3c5c351ab..1a7d1d300 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -174,8 +174,8 @@ func TestReputOnSourceBackend(t *testing.T) { } img2.WritePixels(pix, image.Rect(0, 0, size, size)) - // Create an unmanaged image. This should always be on a non-source backend. - img3 := atlas.NewImage(size, size, atlas.ImageTypeUnmanaged) + // Create a volatile image. This should always be on a non-source backend. + img3 := atlas.NewImage(size, size, atlas.ImageTypeVolatile) defer img3.Deallocate() img3.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size)) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { @@ -685,7 +685,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { } func TestImageWritePixelsModify(t *testing.T) { - for _, typ := range []atlas.ImageType{atlas.ImageTypeRegular, atlas.ImageTypeRegular, atlas.ImageTypeUnmanaged} { + for _, typ := range []atlas.ImageType{atlas.ImageTypeRegular, atlas.ImageTypeVolatile, atlas.ImageTypeUnmanaged} { const size = 16 img := atlas.NewImage(size, size, typ) defer img.Deallocate() diff --git a/internal/ui/context.go b/internal/ui/context.go index 7f94b2d5d..c1af72c37 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -180,6 +180,12 @@ func (c *context) newOffscreenImage(w, h int) *Image { } func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, ui *UserInterface, forceDraw bool) error { + if (c.offscreen.imageType == atlas.ImageTypeVolatile) != ui.IsScreenClearedEveryFrame() { + w, h := c.offscreen.width, c.offscreen.height + c.offscreen.Deallocate() + c.offscreen = c.newOffscreenImage(w, h) + } + // isOffscreenModified is updated when an offscreen's modifyCallback. c.isOffscreenModified = false diff --git a/internal/ui/image.go b/internal/ui/image.go index 987d0bcc3..2b56f0e0f 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -15,6 +15,7 @@ package ui import ( + "fmt" "image" "math" @@ -85,7 +86,16 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice if antialias { if i.bigOffscreenBuffer == nil { - i.bigOffscreenBuffer = i.ui.newBigOffscreenImage(i, atlas.ImageTypeUnmanaged) + var imageType atlas.ImageType + switch i.imageType { + case atlas.ImageTypeRegular, atlas.ImageTypeUnmanaged: + imageType = atlas.ImageTypeUnmanaged + case atlas.ImageTypeScreen, atlas.ImageTypeVolatile: + imageType = atlas.ImageTypeVolatile + default: + panic(fmt.Sprintf("ui: unexpected image type: %d", imageType)) + } + i.bigOffscreenBuffer = i.ui.newBigOffscreenImage(i, imageType) } i.bigOffscreenBuffer.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, canSkipMipmap) From 0b01aeea163a0fd970c43f000f96ae022065c1f8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 11:15:54 +0900 Subject: [PATCH 106/188] Revert "internal/restorable: integrate the implementation into internal/atlas" This reverts commit c404b448aa4afddd1284e3e1134745d6dd88acae. Updates #3083 --- internal/atlas/image.go | 7 +-- internal/atlas/shader.go | 58 ++++++------------------- internal/restorable/doc.go | 22 ++++++++++ internal/restorable/shader.go | 82 +++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 47 deletions(-) create mode 100644 internal/restorable/doc.go create mode 100644 internal/restorable/shader.go diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 980d24100..154435ed9 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -27,6 +27,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/packing" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) @@ -143,7 +144,7 @@ func (b *backend) extendIfNeeded(width, height int) { graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) - newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) + newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader().Shader, nil, graphicsdriver.FillRuleFillAll) b.image.Dispose() b.image = newImg @@ -168,7 +169,7 @@ func clearImage(i *graphicscommand.Image, region image.Rectangle) { vs := make([]float32, 4*graphics.VertexFloatCount) graphics.QuadVerticesFromDstAndSrc(vs, float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) is := graphics.QuadIndices() - i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) + i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.ClearShader.Shader, nil, graphicsdriver.FillRuleFillAll) } func (b *backend) clearPixels(region image.Rectangle) { @@ -520,7 +521,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice imgs[i] = src.backend.image } - i.backend.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader(), uniforms, fillRule) + i.backend.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader().Shader, uniforms, fillRule) for _, src := range srcs { if src == nil { diff --git a/internal/atlas/shader.go b/internal/atlas/shader.go index 32d2312f0..566b08534 100644 --- a/internal/atlas/shader.go +++ b/internal/atlas/shader.go @@ -15,20 +15,15 @@ package atlas import ( - "fmt" "runtime" - "golang.org/x/sync/errgroup" - - "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" - "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) type Shader struct { ir *shaderir.Program - shader *graphicscommand.Shader + shader *restorable.Shader } func NewShader(ir *shaderir.Program) *Shader { @@ -46,11 +41,11 @@ func (s *Shader) finalize() { }) } -func (s *Shader) ensureShader() *graphicscommand.Shader { +func (s *Shader) ensureShader() *restorable.Shader { if s.shader != nil { return s.shader } - s.shader = graphicscommand.NewShader(s.ir) + s.shader = restorable.NewShader(s.ir) runtime.SetFinalizer(s, (*Shader).finalize) return s.shader } @@ -75,43 +70,18 @@ func (s *Shader) deallocate() { if s.shader == nil { return } - s.shader.Dispose() + s.shader.Shader.Dispose() + s.shader.Shader = nil s.shader = nil } var ( - NearestFilterShader *Shader - LinearFilterShader *Shader - clearShader *Shader -) - -func init() { - var wg errgroup.Group - wg.Go(func() error { - ir, err := graphics.CompileShader([]byte(builtinshader.ShaderSource(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false))) - if err != nil { - return fmt.Errorf("atlas: compiling the nearest shader failed: %w", err) - } - NearestFilterShader = NewShader(ir) - return nil - }) - wg.Go(func() error { - ir, err := graphics.CompileShader([]byte(builtinshader.ShaderSource(builtinshader.FilterLinear, builtinshader.AddressUnsafe, false))) - if err != nil { - return fmt.Errorf("atlas: compiling the linear shader failed: %w", err) - } - LinearFilterShader = NewShader(ir) - return nil - }) - wg.Go(func() error { - ir, err := graphics.CompileShader([]byte(builtinshader.ClearShaderSource)) - if err != nil { - return fmt.Errorf("atlas: compiling the clear shader failed: %w", err) - } - clearShader = NewShader(ir) - return nil - }) - if err := wg.Wait(); err != nil { - panic(err) + NearestFilterShader = &Shader{ + shader: restorable.NearestFilterShader, + ir: restorable.LinearFilterShaderIR, } -} + LinearFilterShader = &Shader{ + shader: restorable.LinearFilterShader, + ir: restorable.LinearFilterShaderIR, + } +) diff --git a/internal/restorable/doc.go b/internal/restorable/doc.go new file mode 100644 index 000000000..adc4c448f --- /dev/null +++ b/internal/restorable/doc.go @@ -0,0 +1,22 @@ +// Copyright 2017 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package restorable used to offer an Image struct that stores image commands +// and restores its pixel data from the commands when context lost happens. +// +// However, now Ebitengine doesn't handle context losts, and this package is +// just a thin wrapper. +// +// TODO: Integrate this package into internal/atlas and internal/graphicscommand (#805). +package restorable diff --git a/internal/restorable/shader.go b/internal/restorable/shader.go new file mode 100644 index 000000000..c09a8c4fe --- /dev/null +++ b/internal/restorable/shader.go @@ -0,0 +1,82 @@ +// Copyright 2020 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable + +import ( + "fmt" + + "golang.org/x/sync/errgroup" + + "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" +) + +type Shader struct { + Shader *graphicscommand.Shader +} + +func NewShader(ir *shaderir.Program) *Shader { + s := &Shader{ + Shader: graphicscommand.NewShader(ir), + } + return s +} + +var ( + NearestFilterShader *Shader + NearestFilterShaderIR *shaderir.Program + LinearFilterShader *Shader + LinearFilterShaderIR *shaderir.Program + ClearShader *Shader +) + +func init() { + var wg errgroup.Group + var nearestIR, linearIR, clearIR *shaderir.Program + wg.Go(func() error { + ir, err := graphics.CompileShader([]byte(builtinshader.ShaderSource(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false))) + if err != nil { + return fmt.Errorf("restorable: compiling the nearest shader failed: %w", err) + } + nearestIR = ir + return nil + }) + wg.Go(func() error { + ir, err := graphics.CompileShader([]byte(builtinshader.ShaderSource(builtinshader.FilterLinear, builtinshader.AddressUnsafe, false))) + if err != nil { + return fmt.Errorf("restorable: compiling the linear shader failed: %w", err) + } + linearIR = ir + return nil + }) + wg.Go(func() error { + ir, err := graphics.CompileShader([]byte(builtinshader.ClearShaderSource)) + if err != nil { + return fmt.Errorf("restorable: compiling the clear shader failed: %w", err) + } + clearIR = ir + return nil + }) + if err := wg.Wait(); err != nil { + panic(err) + } + NearestFilterShaderIR = nearestIR + NearestFilterShader = NewShader(nearestIR) + LinearFilterShaderIR = linearIR + LinearFilterShader = NewShader(linearIR) + ClearShader = NewShader(clearIR) +} From 5a5feb0401a17d272bf647af46e88704a2f8998b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 11:48:53 +0900 Subject: [PATCH 107/188] Revert "internal/restorable: remove Image" This reverts commit 812cd494de0677578b667f697f9835a0fb9f3bd0. Updates #3083 --- internal/atlas/image.go | 106 ++++++++++++++++++++--------------- internal/restorable/image.go | 27 +++++++++ 2 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 internal/restorable/image.go diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 154435ed9..2b57eb1a3 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -91,8 +91,8 @@ func putImagesOnSourceBackend() { // backend is a big texture atlas that can have multiple images. // backend is a texture in GPU. type backend struct { - // image is an atlas on which there might be multiple images. - image *graphicscommand.Image + // restorable is an atlas on which there might be multiple images. + restorable *restorable.Image width int height int @@ -138,16 +138,19 @@ func (b *backend) extendIfNeeded(width, height int) { // Assume that the screen image is never extended. newImg := newClearedImage(width, height, false) - srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{b.image} - sw, sh := b.image.InternalSize() + // Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels + // information. + srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{b.restorable.Image} + sw, sh := b.restorable.Image.InternalSize() vs := make([]float32, 4*graphics.VertexFloatCount) graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) - newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader().Shader, nil, graphicsdriver.FillRuleFillAll) - b.image.Dispose() + newImg.Image.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader().Shader, nil, graphicsdriver.FillRuleFillAll) + b.restorable.Image.Dispose() + b.restorable.Image = nil - b.image = newImg + b.restorable = newImg b.width = width b.height = height } @@ -155,13 +158,15 @@ func (b *backend) extendIfNeeded(width, height int) { // newClearedImage creates an emtpy image with the given size. // // Note that Dispose is not called automatically. -func newClearedImage(width, height int, screen bool) *graphicscommand.Image { - i := graphicscommand.NewImage(width, height, screen) +func newClearedImage(width, height int, screen bool) *restorable.Image { + i := &restorable.Image{ + Image: graphicscommand.NewImage(width, height, screen), + } // This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some // devices. - iw, ih := i.InternalSize() - clearImage(i, image.Rect(0, 0, iw, ih)) + iw, ih := i.Image.InternalSize() + clearImage(i.Image, image.Rect(0, 0, iw, ih)) return i } @@ -176,7 +181,7 @@ func (b *backend) clearPixels(region image.Rectangle) { if region.Dx() <= 0 || region.Dy() <= 0 { panic("atlas: width/height must be positive") } - clearImage(b.image, region.Intersect(image.Rect(0, 0, b.width, b.height))) + clearImage(b.restorable.Image, region.Intersect(image.Rect(0, 0, b.width, b.height))) } func (b *backend) writePixels(pixels *graphics.ManagedBytes, region image.Rectangle) { @@ -186,7 +191,7 @@ func (b *backend) writePixels(pixels *graphics.ManagedBytes, region image.Rectan if !region.In(image.Rect(0, 0, b.width, b.height)) { panic(fmt.Sprintf("atlas: out of range %v", region)) } - b.image.WritePixels(pixels, region) + b.restorable.Image.WritePixels(pixels, region) } var ( @@ -245,7 +250,7 @@ type Image struct { // usedAsSourceCount represents how long the image is used as a rendering source and kept not modified with // DrawTriangles. // In the current implementation, if an image is being modified by DrawTriangles, the image is separated from - // a graphicscommand.Image on an atlas by ensureIsolatedFromSource. + // a restorable image on an atlas by ensureIsolatedFromSource. // // The type is int64 instead of int to avoid overflow when comparing the limitation. // @@ -460,7 +465,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice for _, src := range srcs { // Compare i and source images after ensuring i is not on an atlas, or // i and a source image might share the same atlas even though i != src. - if src != nil && i.backend.image == src.backend.image { + if src != nil && i.backend.restorable == src.backend.restorable { panic("atlas: Image.DrawTriangles: source must be different from the receiver") } } @@ -483,7 +488,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice vertices[i+3] += oyf } if shader.ir.Unit == shaderir.Texels { - sw, sh := srcs[0].backend.image.InternalSize() + sw, sh := srcs[0].backend.restorable.Image.InternalSize() swf, shf := float32(sw), float32(sh) for i := 0; i < n; i += graphics.VertexFloatCount { vertices[i+2] /= swf @@ -518,10 +523,10 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice if src == nil { continue } - imgs[i] = src.backend.image + imgs[i] = src.backend.restorable.Image } - i.backend.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader().Shader, uniforms, fillRule) + i.backend.restorable.Image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader().Shader, uniforms, fillRule) for _, src := range srcs { if src == nil { @@ -626,29 +631,22 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte // To prevent memory leaks, flush the deferred functions here. flushDeferred() - if err := i.readPixels(graphicsDriver, pixels, region); err != nil { - return false, err - } - return true, nil -} - -func (i *Image) readPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error { - if i.backend == nil { + if i.backend == nil || i.backend.restorable == nil { for i := range pixels { pixels[i] = 0 } - return nil + return true, nil } - if err := i.backend.image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ + if err := i.backend.restorable.Image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ { Pixels: pixels, Region: region.Add(i.regionWithPadding().Min), }, }); err != nil { - return err + return false, err } - return nil + return true, nil } // Deallocate deallocates the internal state. @@ -694,8 +692,8 @@ func (i *Image) deallocate() { } } - i.backend.image.Dispose() - i.backend.image = nil + i.backend.restorable.Image.Dispose() + i.backend.restorable.Image = nil for idx, sh := range theBackends { if sh == i.backend { @@ -748,6 +746,20 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { runtime.SetFinalizer(i, (*Image).finalize) + if i.imageType == ImageTypeScreen { + if asSource { + panic("atlas: a screen image cannot be created as a source") + } + // A screen image doesn't have a padding. + i.backend = &backend{ + restorable: newClearedImage(i.width, i.height, true), + width: i.width, + height: i.height, + } + theBackends = append(theBackends, i.backend) + return + } + wp := i.width + i.paddingSize() hp := i.height + i.paddingSize() @@ -757,10 +769,10 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { } i.backend = &backend{ - image: newClearedImage(wp, hp, i.imageType == ImageTypeScreen), - width: wp, - height: hp, - source: asSource && i.imageType == ImageTypeRegular, + restorable: newClearedImage(wp, hp, false), + width: wp, + height: hp, + source: asSource && i.imageType == ImageTypeRegular, } theBackends = append(theBackends, i.backend) return @@ -805,11 +817,11 @@ loop: } b := &backend{ - image: newClearedImage(width, height, false), - width: width, - height: height, - page: packing.NewPage(width, height, maxSize), - source: asSource, + restorable: newClearedImage(width, height, false), + width: width, + height: height, + page: packing.NewPage(width, height, maxSize), + source: asSource, } theBackends = append(theBackends, b) @@ -829,7 +841,7 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri panic("atlas: DumpScreenshots must be called in between BeginFrame and EndFrame") } - return i.backend.image.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height)) + return i.backend.restorable.Image.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height)) } func EndFrame() error { @@ -864,7 +876,10 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { debug.FrameLogf("Internal image sizes:\n") imgs := make([]*graphicscommand.Image, 0, len(theBackends)) for _, backend := range theBackends { - imgs = append(imgs, backend.image) + if backend.restorable == nil { + continue + } + imgs = append(imgs, backend.restorable.Image) } graphicscommand.LogImagesInfo(imgs) } @@ -934,7 +949,10 @@ func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) (string, err images := make([]*graphicscommand.Image, 0, len(theBackends)) for _, backend := range theBackends { - images = append(images, backend.image) + if backend.restorable == nil { + continue + } + images = append(images, backend.restorable.Image) } return graphicscommand.DumpImages(images, graphicsDriver, dir) } diff --git a/internal/restorable/image.go b/internal/restorable/image.go new file mode 100644 index 000000000..3793a918f --- /dev/null +++ b/internal/restorable/image.go @@ -0,0 +1,27 @@ +// Copyright 2016 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable + +import ( + "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" +) + +// Image represents an image. +type Image struct { + // Image is the underlying image. + // This member is exported on purpose. + // TODO: Move the implementation to internal/atlas package (#805). + Image *graphicscommand.Image +} From 54c117b0defffed9cfd8b06e2c80a95b1223bb59 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 13:47:29 +0900 Subject: [PATCH 108/188] Revert "internal/restorable: integrate Image functions into internal/atlas" This reverts commit 59896e44478e167f182208632121e281bef05a15. Updates #3083 --- internal/atlas/image.go | 61 +++++++++-------------------------- internal/restorable/image.go | 41 +++++++++++++++++++++++ internal/restorable/shader.go | 4 +-- 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 2b57eb1a3..abcd1fda2 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -136,7 +136,7 @@ func (b *backend) extendIfNeeded(width, height int) { } // Assume that the screen image is never extended. - newImg := newClearedImage(width, height, false) + newImg := restorable.NewImage(width, height, false) // Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels // information. @@ -155,45 +155,6 @@ func (b *backend) extendIfNeeded(width, height int) { b.height = height } -// newClearedImage creates an emtpy image with the given size. -// -// Note that Dispose is not called automatically. -func newClearedImage(width, height int, screen bool) *restorable.Image { - i := &restorable.Image{ - Image: graphicscommand.NewImage(width, height, screen), - } - - // This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some - // devices. - iw, ih := i.Image.InternalSize() - clearImage(i.Image, image.Rect(0, 0, iw, ih)) - return i -} - -func clearImage(i *graphicscommand.Image, region image.Rectangle) { - vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVerticesFromDstAndSrc(vs, float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) - is := graphics.QuadIndices() - i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.ClearShader.Shader, nil, graphicsdriver.FillRuleFillAll) -} - -func (b *backend) clearPixels(region image.Rectangle) { - if region.Dx() <= 0 || region.Dy() <= 0 { - panic("atlas: width/height must be positive") - } - clearImage(b.restorable.Image, region.Intersect(image.Rect(0, 0, b.width, b.height))) -} - -func (b *backend) writePixels(pixels *graphics.ManagedBytes, region image.Rectangle) { - if region.Dx() <= 0 || region.Dy() <= 0 { - panic("atlas: width/height must be positive") - } - if !region.In(image.Rect(0, 0, b.width, b.height)) { - panic(fmt.Sprintf("atlas: out of range %v", region)) - } - b.restorable.Image.WritePixels(pixels, region) -} - var ( // backendsM is a mutex for critical sections of the backend and packing.Node objects. backendsM sync.Mutex @@ -578,7 +539,7 @@ func (i *Image) writePixels(pix []byte, region image.Rectangle) { region = region.Add(r.Min) if pix == nil { - i.backend.clearPixels(region) + i.backend.restorable.ClearPixels(region) return } @@ -618,6 +579,16 @@ func (i *Image) writePixels(pix []byte, region image.Rectangle) { i.backend.writePixels(pixb, r) } +func (b *backend) writePixels(pixels *graphics.ManagedBytes, region image.Rectangle) { + if region.Dx() <= 0 || region.Dy() <= 0 { + panic("atlas: width/height must be positive") + } + if !region.In(image.Rect(0, 0, b.width, b.height)) { + panic(fmt.Sprintf("atlas: out of range %v", region)) + } + b.restorable.Image.WritePixels(pixels, region) +} + func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) (ok bool, err error) { backendsM.Lock() defer backendsM.Unlock() @@ -687,7 +658,7 @@ func (i *Image) deallocate() { if !i.backend.page.IsEmpty() { // As this part can be reused, this should be cleared explicitly. r := i.regionWithPadding() - i.backend.clearPixels(r) + i.backend.restorable.ClearPixels(r) return } } @@ -752,7 +723,7 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { } // A screen image doesn't have a padding. i.backend = &backend{ - restorable: newClearedImage(i.width, i.height, true), + restorable: restorable.NewImage(i.width, i.height, true), width: i.width, height: i.height, } @@ -769,7 +740,7 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { } i.backend = &backend{ - restorable: newClearedImage(wp, hp, false), + restorable: restorable.NewImage(wp, hp, false), width: wp, height: hp, source: asSource && i.imageType == ImageTypeRegular, @@ -817,7 +788,7 @@ loop: } b := &backend{ - restorable: newClearedImage(width, height, false), + restorable: restorable.NewImage(width, height, false), width: width, height: height, page: packing.NewPage(width, height, maxSize), diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 3793a918f..14960bfde 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -15,7 +15,11 @@ package restorable import ( + "image" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) // Image represents an image. @@ -24,4 +28,41 @@ type Image struct { // This member is exported on purpose. // TODO: Move the implementation to internal/atlas package (#805). Image *graphicscommand.Image + + width int + height int +} + +// NewImage creates an emtpy image with the given size. +// +// The returned image is cleared. +// +// Note that Dispose is not called automatically. +func NewImage(width, height int, screen bool) *Image { + i := &Image{ + Image: graphicscommand.NewImage(width, height, screen), + width: width, + height: height, + } + + // This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some + // devices. + iw, ih := i.Image.InternalSize() + clearImage(i.Image, image.Rect(0, 0, iw, ih)) + return i +} + +func clearImage(i *graphicscommand.Image, region image.Rectangle) { + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) + is := graphics.QuadIndices() + i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.Shader, nil, graphicsdriver.FillRuleFillAll) +} + +// ClearPixels clears the specified region by WritePixels. +func (i *Image) ClearPixels(region image.Rectangle) { + if region.Dx() <= 0 || region.Dy() <= 0 { + panic("restorable: width/height must be positive") + } + clearImage(i.Image, region.Intersect(image.Rect(0, 0, i.width, i.height))) } diff --git a/internal/restorable/shader.go b/internal/restorable/shader.go index c09a8c4fe..bc2247f50 100644 --- a/internal/restorable/shader.go +++ b/internal/restorable/shader.go @@ -41,7 +41,7 @@ var ( NearestFilterShaderIR *shaderir.Program LinearFilterShader *Shader LinearFilterShaderIR *shaderir.Program - ClearShader *Shader + clearShader *Shader ) func init() { @@ -78,5 +78,5 @@ func init() { NearestFilterShader = NewShader(nearestIR) LinearFilterShaderIR = linearIR LinearFilterShader = NewShader(linearIR) - ClearShader = NewShader(clearIR) + clearShader = NewShader(clearIR) } From 104cc184770206ca15a02522f113a7eeba7cada8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 13:59:19 +0900 Subject: [PATCH 109/188] Revert "internal/restorable: integrate Image.Extend into internal/atlas" This reverts commit 6151fd313f3a58cbf864bcb19209add21228f5e5. Updates #3083 --- internal/atlas/image.go | 33 ++++----------------------------- internal/restorable/image.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index abcd1fda2..9a24f14ad 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -122,39 +122,14 @@ func (b *backend) tryAlloc(width, height int) (*packing.Node, bool) { return nil, false } - b.extendIfNeeded(b.page.Size()) + w, h := b.page.Size() + b.restorable = b.restorable.Extend(w, h) + b.width = w + b.height = h return n, true } -// extendIfNeeded extends the image by the given size if necessary. -// extendIfNeeded creates a new image with the given size and copies the pixels of the given source image. -// extendIfNeeded disposes an old image after its call when a new image is created. -func (b *backend) extendIfNeeded(width, height int) { - if b.width >= width && b.height >= height { - return - } - - // Assume that the screen image is never extended. - newImg := restorable.NewImage(width, height, false) - - // Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels - // information. - srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{b.restorable.Image} - sw, sh := b.restorable.Image.InternalSize() - vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) - is := graphics.QuadIndices() - dr := image.Rect(0, 0, sw, sh) - newImg.Image.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader().Shader, nil, graphicsdriver.FillRuleFillAll) - b.restorable.Image.Dispose() - b.restorable.Image = nil - - b.restorable = newImg - b.width = width - b.height = height -} - var ( // backendsM is a mutex for critical sections of the backend and packing.Node objects. backendsM sync.Mutex diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 14960bfde..7131ca639 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -52,6 +52,32 @@ func NewImage(width, height int, screen bool) *Image { return i } +// Extend extends the image by the given size. +// Extend creates a new image with the given size and copies the pixels of the given source image. +// Extend disposes itself after its call. +func (i *Image) Extend(width, height int) *Image { + if i.width >= width && i.height >= height { + return i + } + + // Assume that the screen image is never extended. + newImg := NewImage(width, height, false) + + // Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels + // information. + srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{i.Image} + sw, sh := i.Image.InternalSize() + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, sw, sh) + newImg.Image.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.Shader, nil, graphicsdriver.FillRuleFillAll) + i.Image.Dispose() + i.Image = nil + + return newImg +} + func clearImage(i *graphicscommand.Image, region image.Rectangle) { vs := make([]float32, 4*graphics.VertexFloatCount) graphics.QuadVerticesFromDstAndSrc(vs, float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) From d733308eb14edc1b51d30419166abec7d4377534 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:02:41 +0900 Subject: [PATCH 110/188] Revert "internal/restorable: integrate Image.WritePixels into internal/atlas" This reverts commit 6cc815018556913bad690bd76acb5a9fdb940475. Updates #3083 --- internal/atlas/image.go | 25 ++----------------------- internal/restorable/image.go | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 9a24f14ad..e275b7e7d 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -94,9 +94,6 @@ type backend struct { // restorable is an atlas on which there might be multiple images. restorable *restorable.Image - width int - height int - // page is an atlas map. Each part is called a node. // If page is nil, the backend's image is isolated and not on an atlas. page *packing.Page @@ -124,8 +121,6 @@ func (b *backend) tryAlloc(width, height int) (*packing.Node, bool) { w, h := b.page.Size() b.restorable = b.restorable.Extend(w, h) - b.width = w - b.height = h return n, true } @@ -522,7 +517,7 @@ func (i *Image) writePixels(pix []byte, region image.Rectangle) { pix2 := graphics.NewManagedBytes(len(pix), func(bs []byte) { copy(bs, pix) }) - i.backend.writePixels(pix2, region) + i.backend.restorable.WritePixels(pix2, region) return } @@ -551,17 +546,7 @@ func (i *Image) writePixels(pix []byte, region image.Rectangle) { copy(bs[4*j*r.Dx():], pix[4*j*region.Dx():4*(j+1)*region.Dx()]) } }) - i.backend.writePixels(pixb, r) -} - -func (b *backend) writePixels(pixels *graphics.ManagedBytes, region image.Rectangle) { - if region.Dx() <= 0 || region.Dy() <= 0 { - panic("atlas: width/height must be positive") - } - if !region.In(image.Rect(0, 0, b.width, b.height)) { - panic(fmt.Sprintf("atlas: out of range %v", region)) - } - b.restorable.Image.WritePixels(pixels, region) + i.backend.restorable.WritePixels(pixb, r) } func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) (ok bool, err error) { @@ -699,8 +684,6 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { // A screen image doesn't have a padding. i.backend = &backend{ restorable: restorable.NewImage(i.width, i.height, true), - width: i.width, - height: i.height, } theBackends = append(theBackends, i.backend) return @@ -716,8 +699,6 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { i.backend = &backend{ restorable: restorable.NewImage(wp, hp, false), - width: wp, - height: hp, source: asSource && i.imageType == ImageTypeRegular, } theBackends = append(theBackends, i.backend) @@ -764,8 +745,6 @@ loop: b := &backend{ restorable: restorable.NewImage(width, height, false), - width: width, - height: height, page: packing.NewPage(width, height, maxSize), source: asSource, } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 7131ca639..4bcda8d52 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -15,6 +15,7 @@ package restorable import ( + "fmt" "image" "github.com/hajimehoshi/ebiten/v2/internal/graphics" @@ -92,3 +93,18 @@ func (i *Image) ClearPixels(region image.Rectangle) { } clearImage(i.Image, region.Intersect(image.Rect(0, 0, i.width, i.height))) } + +// WritePixels replaces the image pixels with the given pixels slice. +// +// The specified region must not be overlapped with other regions by WritePixels. +func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangle) { + if region.Dx() <= 0 || region.Dy() <= 0 { + panic("restorable: width/height must be positive") + } + w, h := i.width, i.height + if !region.In(image.Rect(0, 0, w, h)) { + panic(fmt.Sprintf("restorable: out of range %v", region)) + } + + i.Image.WritePixels(pixels, region) +} From 17a5488c4936f62c62d15cb35a5b6dddbd51c927 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:06:39 +0900 Subject: [PATCH 111/188] Revert "internal/restorable: integrate Image.DrawTriangles into internal/atlas" This reverts commit 3ee905bc4d95e3bb8fe2fdeb7131030684d324d0. Updates #3083 --- internal/atlas/image.go | 11 +++-------- internal/restorable/image.go | 31 +++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index e275b7e7d..51d901ae9 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -372,11 +372,6 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice } func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { - if len(vertices) == 0 { - return - } - - // This slice is not escaped to the heap. This can be checked by `go build -gcflags=-m`. backends := make([]*backend, 0, len(srcs)) for _, src := range srcs { if src == nil { @@ -449,15 +444,15 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice srcRegions[i] = srcRegions[i].Add(r.Min) } - var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image + var imgs [graphics.ShaderSrcImageCount]*restorable.Image for i, src := range srcs { if src == nil { continue } - imgs[i] = src.backend.restorable.Image + imgs[i] = src.backend.restorable } - i.backend.restorable.Image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader().Shader, uniforms, fillRule) + i.backend.restorable.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader(), uniforms, fillRule) for _, src := range srcs { if src == nil { diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 4bcda8d52..9f6188c4d 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -66,13 +66,13 @@ func (i *Image) Extend(width, height int) *Image { // Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels // information. - srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{i.Image} + srcs := [graphics.ShaderSrcImageCount]*Image{i} sw, sh := i.Image.InternalSize() vs := make([]float32, 4*graphics.VertexFloatCount) graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) - newImg.Image.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.Shader, nil, graphicsdriver.FillRuleFillAll) + newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) i.Image.Dispose() i.Image = nil @@ -108,3 +108,30 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl i.Image.WritePixels(pixels, region) } + +// DrawTriangles draws triangles with the given image. +// +// The vertex floats are: +// +// 0: Destination X in pixels +// 1: Destination Y in pixels +// 2: Source X in texels +// 3: Source Y in texels +// 4: Color R [0.0-1.0] +// 5: Color G +// 6: Color B +// 7: Color Y +func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { + if len(vertices) == 0 { + return + } + + var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image + for i, src := range srcs { + if src == nil { + continue + } + imgs[i] = src.Image + } + i.Image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.Shader, uniforms, fillRule) +} From 19d2009a5f750d15aef9c55cd31364dbbed750aa Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:08:47 +0900 Subject: [PATCH 112/188] Revert "internal/restorable: integrate Shader.Dispose into internal/atlas" This reverts commit bfa2c460a228b8d6f9185f24e79a12afd4f12d23. Updates #3083 --- internal/atlas/shader.go | 3 +-- internal/restorable/image.go | 4 ++-- internal/restorable/shader.go | 9 +++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/internal/atlas/shader.go b/internal/atlas/shader.go index 566b08534..40dfbf615 100644 --- a/internal/atlas/shader.go +++ b/internal/atlas/shader.go @@ -70,8 +70,7 @@ func (s *Shader) deallocate() { if s.shader == nil { return } - s.shader.Shader.Dispose() - s.shader.Shader = nil + s.shader.Dispose() s.shader = nil } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 9f6188c4d..d77eb9fe2 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -83,7 +83,7 @@ func clearImage(i *graphicscommand.Image, region image.Rectangle) { vs := make([]float32, 4*graphics.VertexFloatCount) graphics.QuadVerticesFromDstAndSrc(vs, float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) is := graphics.QuadIndices() - i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.Shader, nil, graphicsdriver.FillRuleFillAll) + i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.shader, nil, graphicsdriver.FillRuleFillAll) } // ClearPixels clears the specified region by WritePixels. @@ -133,5 +133,5 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice } imgs[i] = src.Image } - i.Image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.Shader, uniforms, fillRule) + i.Image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule) } diff --git a/internal/restorable/shader.go b/internal/restorable/shader.go index bc2247f50..9715f5d72 100644 --- a/internal/restorable/shader.go +++ b/internal/restorable/shader.go @@ -26,16 +26,21 @@ import ( ) type Shader struct { - Shader *graphicscommand.Shader + shader *graphicscommand.Shader } func NewShader(ir *shaderir.Program) *Shader { s := &Shader{ - Shader: graphicscommand.NewShader(ir), + shader: graphicscommand.NewShader(ir), } return s } +func (s *Shader) Dispose() { + s.shader.Dispose() + s.shader = nil +} + var ( NearestFilterShader *Shader NearestFilterShaderIR *shaderir.Program From becada4afc250ff3b4435fde5030ea4e422d37c1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:14:02 +0900 Subject: [PATCH 113/188] Revert "internal/restorable: refactoring" This reverts commit 81e110461362ee490c26ddf18f41d27446840870. Updates #3083 --- internal/restorable/image.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index d77eb9fe2..0ce4d25c1 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -88,10 +88,7 @@ func clearImage(i *graphicscommand.Image, region image.Rectangle) { // ClearPixels clears the specified region by WritePixels. func (i *Image) ClearPixels(region image.Rectangle) { - if region.Dx() <= 0 || region.Dy() <= 0 { - panic("restorable: width/height must be positive") - } - clearImage(i.Image, region.Intersect(image.Rect(0, 0, i.width, i.height))) + i.WritePixels(nil, region) } // WritePixels replaces the image pixels with the given pixels slice. @@ -106,7 +103,11 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl panic(fmt.Sprintf("restorable: out of range %v", region)) } - i.Image.WritePixels(pixels, region) + if pixels != nil { + i.Image.WritePixels(pixels, region) + } else { + clearImage(i.Image, region) + } } // DrawTriangles draws triangles with the given image. From d533461936828ca73daab19a8bece25fa89c8d03 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:17:47 +0900 Subject: [PATCH 114/188] Revert "internal/restorable: integrate ReadPixels into internal/atlas" This reverts commit e804f9e58ca5a78a8cf26c7f2c8a7e27ccc953d0. Updates #3083 --- internal/atlas/image.go | 7 +------ internal/restorable/image.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 51d901ae9..9a874b8af 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -564,12 +564,7 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte return true, nil } - if err := i.backend.restorable.Image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ - { - Pixels: pixels, - Region: region.Add(i.regionWithPadding().Min), - }, - }); err != nil { + if err := i.backend.restorable.ReadPixels(graphicsDriver, pixels, region.Add(i.regionWithPadding().Min)); err != nil { return false, err } return true, nil diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 0ce4d25c1..34643661c 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -136,3 +136,15 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice } i.Image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule) } + +func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error { + if err := i.Image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ + { + Pixels: pixels, + Region: region, + }, + }); err != nil { + return err + } + return nil +} From 62ed5bed4b166ecf294f3479a43bd47f81dee649 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:25:20 +0900 Subject: [PATCH 115/188] Revert "internal/restorable: integrate some functions into internal/atlas" This reverts commit c5d0ec3de741ab5fc842d5bbb0530a428286ff8d. Updates #3803 --- internal/atlas/image.go | 7 +++---- internal/restorable/image.go | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 9a874b8af..ca8add1c6 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -414,7 +414,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice vertices[i+3] += oyf } if shader.ir.Unit == shaderir.Texels { - sw, sh := srcs[0].backend.restorable.Image.InternalSize() + sw, sh := srcs[0].backend.restorable.InternalSize() swf, shf := float32(sw), float32(sh) for i := 0; i < n; i += graphics.VertexFloatCount { vertices[i+2] /= swf @@ -613,8 +613,7 @@ func (i *Image) deallocate() { } } - i.backend.restorable.Image.Dispose() - i.backend.restorable.Image = nil + i.backend.restorable.Dispose() for idx, sh := range theBackends { if sh == i.backend { @@ -756,7 +755,7 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri panic("atlas: DumpScreenshots must be called in between BeginFrame and EndFrame") } - return i.backend.restorable.Image.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height)) + return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height)) } func EndFrame() error { diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 34643661c..3d533585f 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -73,8 +73,7 @@ func (i *Image) Extend(width, height int) *Image { is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) - i.Image.Dispose() - i.Image = nil + i.Dispose() return newImg } @@ -148,3 +147,19 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte } return nil } + +// Dispose disposes the image. +// +// After disposing, calling the function of the image causes unexpected results. +func (i *Image) Dispose() { + i.Image.Dispose() + i.Image = nil +} + +func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) (string, error) { + return i.Image.Dump(graphicsDriver, path, blackbg, rect) +} + +func (i *Image) InternalSize() (int, int) { + return i.Image.InternalSize() +} From a324cfd3b6e70c23ccafc70ad78a097633fbbdc0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:27:46 +0900 Subject: [PATCH 116/188] Revert "internal/restorable: remove ImageType" This reverts commit 21ef462c379337b74e7ac7fedca2947f5c4fff8a. Updates #3083 --- internal/atlas/image.go | 9 ++++----- internal/restorable/image.go | 24 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index ca8add1c6..f014b528d 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -119,8 +119,7 @@ func (b *backend) tryAlloc(width, height int) (*packing.Node, bool) { return nil, false } - w, h := b.page.Size() - b.restorable = b.restorable.Extend(w, h) + b.restorable = b.restorable.Extend(b.page.Size()) return n, true } @@ -672,7 +671,7 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { } // A screen image doesn't have a padding. i.backend = &backend{ - restorable: restorable.NewImage(i.width, i.height, true), + restorable: restorable.NewImage(i.width, i.height, restorable.ImageTypeScreen), } theBackends = append(theBackends, i.backend) return @@ -687,7 +686,7 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { } i.backend = &backend{ - restorable: restorable.NewImage(wp, hp, false), + restorable: restorable.NewImage(wp, hp, restorable.ImageTypeRegular), source: asSource && i.imageType == ImageTypeRegular, } theBackends = append(theBackends, i.backend) @@ -733,7 +732,7 @@ loop: } b := &backend{ - restorable: restorable.NewImage(width, height, false), + restorable: restorable.NewImage(width, height, restorable.ImageTypeRegular), page: packing.NewPage(width, height, maxSize), source: asSource, } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 3d533585f..6298d761c 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -23,6 +23,16 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) +type ImageType int + +const ( + // ImageTypeRegular indicates the image is a regular image. + ImageTypeRegular ImageType = iota + + // ImageTypeScreen indicates the image is used as an actual screen. + ImageTypeScreen +) + // Image represents an image. type Image struct { // Image is the underlying image. @@ -32,6 +42,8 @@ type Image struct { width int height int + + imageType ImageType } // NewImage creates an emtpy image with the given size. @@ -39,11 +51,12 @@ type Image struct { // The returned image is cleared. // // Note that Dispose is not called automatically. -func NewImage(width, height int, screen bool) *Image { +func NewImage(width, height int, imageType ImageType) *Image { i := &Image{ - Image: graphicscommand.NewImage(width, height, screen), - width: width, - height: height, + Image: graphicscommand.NewImage(width, height, imageType == ImageTypeScreen), + width: width, + height: height, + imageType: imageType, } // This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some @@ -61,8 +74,7 @@ func (i *Image) Extend(width, height int) *Image { return i } - // Assume that the screen image is never extended. - newImg := NewImage(width, height, false) + newImg := NewImage(width, height, i.imageType) // Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels // information. From c2c548087862b455362e29278a2f3dd5cdc932f2 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:29:37 +0900 Subject: [PATCH 117/188] Revert "internal/atlas: move some functions in internal/restorable to internal/atlas" This reverts commit 3651d73e0b2379da2a5dc5d1d32bd75fdb560c52. Updates #3083 --- internal/atlas/image.go | 12 ++---------- internal/restorable/image.go | 4 ++++ internal/restorable/images.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 internal/restorable/images.go diff --git a/internal/atlas/image.go b/internal/atlas/image.go index f014b528d..dbdb00a7e 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -141,8 +141,6 @@ var ( imagesUsedAsDestination smallImageSet - graphicsDriverInitialized bool - deferred []func() // deferredM is a mutex for the slice operations. This must not be used for other usages. @@ -655,10 +653,6 @@ func (i *Image) finalize() { } func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { - if !graphicsDriverInitialized { - panic("atlas: graphics driver must be ready at allocate but not") - } - if i.backend != nil { panic("atlas: the image is already allocated") } @@ -821,7 +815,7 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error { var err error initOnce.Do(func() { - err = graphicscommand.InitializeGraphicsDriverState(graphicsDriver) + err = restorable.InitializeGraphicsDriverState(graphicsDriver) if err != nil { return } @@ -837,10 +831,8 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error { minDestinationSize = 16 } if maxSize == 0 { - maxSize = floorPowerOf2(graphicscommand.MaxImageSize(graphicsDriver)) + maxSize = floorPowerOf2(restorable.MaxImageSize(graphicsDriver)) } - - graphicsDriverInitialized = true }) if err != nil { return err diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 6298d761c..e69bfab57 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -52,6 +52,10 @@ type Image struct { // // Note that Dispose is not called automatically. func NewImage(width, height int, imageType ImageType) *Image { + if !graphicsDriverInitialized { + panic("restorable: graphics driver must be ready at NewImage but not") + } + i := &Image{ Image: graphicscommand.NewImage(width, height, imageType == ImageTypeScreen), width: width, diff --git a/internal/restorable/images.go b/internal/restorable/images.go new file mode 100644 index 000000000..03bac0eaa --- /dev/null +++ b/internal/restorable/images.go @@ -0,0 +1,33 @@ +// Copyright 2017 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable + +import ( + "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" +) + +var graphicsDriverInitialized bool + +// InitializeGraphicsDriverState initializes the graphics driver state. +func InitializeGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) error { + graphicsDriverInitialized = true + return graphicscommand.InitializeGraphicsDriverState(graphicsDriver) +} + +// MaxImageSize returns the maximum size of an image. +func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int { + return graphicscommand.MaxImageSize(graphicsDriver) +} From a9d8f374c83b677a054e32010a81fc83522e49cc Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:33:48 +0900 Subject: [PATCH 118/188] Revert "internal/restorable: move SwapBuffers to internal/atlas" This reverts commit f610cb5724caf7ad73e1b90868c24fa55d869835. Updates #3083 --- internal/atlas/image.go | 14 +------------- internal/restorable/image.go | 2 ++ internal/restorable/images.go | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index dbdb00a7e..d4d4ce4d6 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -22,7 +22,6 @@ import ( "runtime" "sync" - "github.com/hajimehoshi/ebiten/v2/internal/debug" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" @@ -779,18 +778,7 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { } }() - if debug.IsDebug { - debug.FrameLogf("Internal image sizes:\n") - imgs := make([]*graphicscommand.Image, 0, len(theBackends)) - for _, backend := range theBackends { - if backend.restorable == nil { - continue - } - imgs = append(imgs, backend.restorable.Image) - } - graphicscommand.LogImagesInfo(imgs) - } - if err := graphicscommand.FlushCommands(graphicsDriver, true); err != nil { + if err := restorable.SwapBuffers(graphicsDriver); err != nil { return err } return nil diff --git a/internal/restorable/image.go b/internal/restorable/image.go index e69bfab57..d2275a762 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -67,6 +67,7 @@ func NewImage(width, height int, imageType ImageType) *Image { // devices. iw, ih := i.Image.InternalSize() clearImage(i.Image, image.Rect(0, 0, iw, ih)) + theImages.add(i) return i } @@ -168,6 +169,7 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte // // After disposing, calling the function of the image causes unexpected results. func (i *Image) Dispose() { + theImages.remove(i) i.Image.Dispose() i.Image = nil } diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 03bac0eaa..d3e15f738 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -15,10 +15,46 @@ package restorable import ( + "github.com/hajimehoshi/ebiten/v2/internal/debug" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) +// images is a set of Image objects. +type images struct { + images map[*Image]struct{} +} + +// theImages represents the images for the current process. +var theImages = &images{ + images: map[*Image]struct{}{}, +} + +func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { + if debug.IsDebug { + debug.FrameLogf("Internal image sizes:\n") + imgs := make([]*graphicscommand.Image, 0, len(theImages.images)) + for i := range theImages.images { + imgs = append(imgs, i.Image) + } + graphicscommand.LogImagesInfo(imgs) + } + if err := graphicscommand.FlushCommands(graphicsDriver, true); err != nil { + return err + } + return nil +} + +// add adds img to the images. +func (i *images) add(img *Image) { + i.images[img] = struct{}{} +} + +// remove removes img from the images. +func (i *images) remove(img *Image) { + delete(i.images, img) +} + var graphicsDriverInitialized bool // InitializeGraphicsDriverState initializes the graphics driver state. From 169b9fe51ed904ca8d3556cfcadfd93ac07be130 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 14:39:33 +0900 Subject: [PATCH 119/188] Revert "internal/restorable: move DumpImages to internal/atlas" This reverts commit 7c9266d8b66de918be2ed1d15074542c945a68ff. Updates #3083 --- internal/atlas/image.go | 10 +--------- internal/restorable/image.go | 31 ++++++++++++++----------------- internal/restorable/images.go | 14 +++++++++++++- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index d4d4ce4d6..8be47441a 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -23,7 +23,6 @@ import ( "sync" "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/packing" "github.com/hajimehoshi/ebiten/v2/internal/restorable" @@ -840,12 +839,5 @@ func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) (string, err panic("atlas: DumpImages must be called in between BeginFrame and EndFrame") } - images := make([]*graphicscommand.Image, 0, len(theBackends)) - for _, backend := range theBackends { - if backend.restorable == nil { - continue - } - images = append(images, backend.restorable.Image) - } - return graphicscommand.DumpImages(images, graphicsDriver, dir) + return restorable.DumpImages(graphicsDriver, dir) } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index d2275a762..3323a6c5d 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -35,10 +35,7 @@ const ( // Image represents an image. type Image struct { - // Image is the underlying image. - // This member is exported on purpose. - // TODO: Move the implementation to internal/atlas package (#805). - Image *graphicscommand.Image + image *graphicscommand.Image width int height int @@ -57,7 +54,7 @@ func NewImage(width, height int, imageType ImageType) *Image { } i := &Image{ - Image: graphicscommand.NewImage(width, height, imageType == ImageTypeScreen), + image: graphicscommand.NewImage(width, height, imageType == ImageTypeScreen), width: width, height: height, imageType: imageType, @@ -65,8 +62,8 @@ func NewImage(width, height int, imageType ImageType) *Image { // This needs to use 'InternalSize' to render the whole region, or edges are unexpectedly cleared on some // devices. - iw, ih := i.Image.InternalSize() - clearImage(i.Image, image.Rect(0, 0, iw, ih)) + iw, ih := i.image.InternalSize() + clearImage(i.image, image.Rect(0, 0, iw, ih)) theImages.add(i) return i } @@ -84,7 +81,7 @@ func (i *Image) Extend(width, height int) *Image { // Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels // information. srcs := [graphics.ShaderSrcImageCount]*Image{i} - sw, sh := i.Image.InternalSize() + sw, sh := i.image.InternalSize() vs := make([]float32, 4*graphics.VertexFloatCount) graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() @@ -120,9 +117,9 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl } if pixels != nil { - i.Image.WritePixels(pixels, region) + i.image.WritePixels(pixels, region) } else { - clearImage(i.Image, region) + clearImage(i.image, region) } } @@ -148,13 +145,13 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice if src == nil { continue } - imgs[i] = src.Image + imgs[i] = src.image } - i.Image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule) + i.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule) } func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error { - if err := i.Image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ + if err := i.image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ { Pixels: pixels, Region: region, @@ -170,14 +167,14 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte // After disposing, calling the function of the image causes unexpected results. func (i *Image) Dispose() { theImages.remove(i) - i.Image.Dispose() - i.Image = nil + i.image.Dispose() + i.image = nil } func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) (string, error) { - return i.Image.Dump(graphicsDriver, path, blackbg, rect) + return i.image.Dump(graphicsDriver, path, blackbg, rect) } func (i *Image) InternalSize() (int, int) { - return i.Image.InternalSize() + return i.image.InternalSize() } diff --git a/internal/restorable/images.go b/internal/restorable/images.go index d3e15f738..0adb14746 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -35,7 +35,7 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { debug.FrameLogf("Internal image sizes:\n") imgs := make([]*graphicscommand.Image, 0, len(theImages.images)) for i := range theImages.images { - imgs = append(imgs, i.Image) + imgs = append(imgs, i.image) } graphicscommand.LogImagesInfo(imgs) } @@ -45,6 +45,18 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { return nil } +// DumpImages dumps all the current images to the specified directory. +// +// This is for testing usage. +func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) (string, error) { + images := make([]*graphicscommand.Image, 0, len(theImages.images)) + for img := range theImages.images { + images = append(images, img.image) + } + + return graphicscommand.DumpImages(images, graphicsDriver, dir) +} + // add adds img to the images. func (i *images) add(img *Image) { i.images[img] = struct{}{} From 34639d0028c1778916977fca5fda5a7c8d23b47c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 15:27:24 +0900 Subject: [PATCH 120/188] Revert "internal/restorable: remove ImageTypeRestorable" This reverts commit 8c2d4e1408dcba09af72a524b32d92e155b954fa. Updates #3083 --- internal/atlas/image.go | 14 +++++++++++--- internal/restorable/image.go | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 8be47441a..d1eea954d 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -677,9 +677,13 @@ func (i *Image) allocate(forbiddenBackends []*backend, asSource bool) { panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height)) } + typ := restorable.ImageTypeRegular + if i.imageType == ImageTypeVolatile { + typ = restorable.ImageTypeVolatile + } i.backend = &backend{ - restorable: restorable.NewImage(wp, hp, restorable.ImageTypeRegular), - source: asSource && i.imageType == ImageTypeRegular, + restorable: restorable.NewImage(wp, hp, typ), + source: asSource && typ == restorable.ImageTypeRegular, } theBackends = append(theBackends, i.backend) return @@ -723,8 +727,12 @@ loop: height *= 2 } + typ := restorable.ImageTypeRegular + if i.imageType == ImageTypeVolatile { + typ = restorable.ImageTypeVolatile + } b := &backend{ - restorable: restorable.NewImage(width, height, restorable.ImageTypeRegular), + restorable: restorable.NewImage(width, height, typ), page: packing.NewPage(width, height, maxSize), source: asSource, } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 3323a6c5d..73d689ff2 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -31,6 +31,9 @@ const ( // ImageTypeScreen indicates the image is used as an actual screen. ImageTypeScreen + + // ImageTypeVolatile indicates the image is cleared whenever a frame starts. + ImageTypeVolatile ) // Image represents an image. From 6453e552f3a1287aa5f348324915fdcedb167261 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 15:27:50 +0900 Subject: [PATCH 121/188] Revert "internal/restorable: remove unnecessary functions and variables around shaders" This reverts commit 4f3e00ec3af857522828312a27d4a5705d21877a. Updates #3083 --- internal/atlas/image.go | 2 +- internal/atlas/shader.go | 10 ++-------- internal/restorable/images.go | 14 ++++++++++++-- internal/restorable/shader.go | 23 ++++++++++++++++------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index d1eea954d..46e6fbf34 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -408,7 +408,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice vertices[i+2] += oxf vertices[i+3] += oyf } - if shader.ir.Unit == shaderir.Texels { + if shader.ensureShader().Unit() == shaderir.Texels { sw, sh := srcs[0].backend.restorable.InternalSize() swf, shf := float32(sw), float32(sh) for i := 0; i < n; i += graphics.VertexFloatCount { diff --git a/internal/atlas/shader.go b/internal/atlas/shader.go index 40dfbf615..f2ff046ed 100644 --- a/internal/atlas/shader.go +++ b/internal/atlas/shader.go @@ -75,12 +75,6 @@ func (s *Shader) deallocate() { } var ( - NearestFilterShader = &Shader{ - shader: restorable.NearestFilterShader, - ir: restorable.LinearFilterShaderIR, - } - LinearFilterShader = &Shader{ - shader: restorable.LinearFilterShader, - ir: restorable.LinearFilterShaderIR, - } + NearestFilterShader = &Shader{shader: restorable.NearestFilterShader} + LinearFilterShader = &Shader{shader: restorable.LinearFilterShader} ) diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 0adb14746..a1d84d0e2 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -22,12 +22,14 @@ import ( // images is a set of Image objects. type images struct { - images map[*Image]struct{} + images map[*Image]struct{} + shaders map[*Shader]struct{} } // theImages represents the images for the current process. var theImages = &images{ - images: map[*Image]struct{}{}, + images: map[*Image]struct{}{}, + shaders: map[*Shader]struct{}{}, } func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { @@ -62,11 +64,19 @@ func (i *images) add(img *Image) { i.images[img] = struct{}{} } +func (i *images) addShader(shader *Shader) { + i.shaders[shader] = struct{}{} +} + // remove removes img from the images. func (i *images) remove(img *Image) { delete(i.images, img) } +func (i *images) removeShader(shader *Shader) { + delete(i.shaders, shader) +} + var graphicsDriverInitialized bool // InitializeGraphicsDriverState initializes the graphics driver state. diff --git a/internal/restorable/shader.go b/internal/restorable/shader.go index 9715f5d72..acf94504e 100644 --- a/internal/restorable/shader.go +++ b/internal/restorable/shader.go @@ -27,26 +27,37 @@ import ( type Shader struct { shader *graphicscommand.Shader + ir *shaderir.Program } func NewShader(ir *shaderir.Program) *Shader { s := &Shader{ shader: graphicscommand.NewShader(ir), + ir: ir, } + theImages.addShader(s) return s } func (s *Shader) Dispose() { + theImages.removeShader(s) s.shader.Dispose() s.shader = nil + s.ir = nil +} + +func (s *Shader) restore() { + s.shader = graphicscommand.NewShader(s.ir) +} + +func (s *Shader) Unit() shaderir.Unit { + return s.ir.Unit } var ( - NearestFilterShader *Shader - NearestFilterShaderIR *shaderir.Program - LinearFilterShader *Shader - LinearFilterShaderIR *shaderir.Program - clearShader *Shader + NearestFilterShader *Shader + LinearFilterShader *Shader + clearShader *Shader ) func init() { @@ -79,9 +90,7 @@ func init() { if err := wg.Wait(); err != nil { panic(err) } - NearestFilterShaderIR = nearestIR NearestFilterShader = NewShader(nearestIR) - LinearFilterShaderIR = linearIR LinearFilterShader = NewShader(linearIR) clearShader = NewShader(clearIR) } From 81d35df33b206c696735703d0e602bab2b98e7f3 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 15:28:19 +0900 Subject: [PATCH 122/188] Revert "internal/restorable: remove unused functions and variables" This reverts commit 8169253a574a026be324c7341c6ad03d45ce23b6. Updates #3083 --- internal/restorable/doc.go | 54 ++++++- internal/restorable/export_test.go | 23 +++ internal/restorable/image.go | 218 +++++++++++++++++++++++++++- internal/restorable/images.go | 34 ++++- internal/restorable/pixelrecords.go | 159 ++++++++++++++++++++ internal/restorable/rect_test.go | 165 +++++++++++++++++++++ 6 files changed, 646 insertions(+), 7 deletions(-) create mode 100644 internal/restorable/export_test.go create mode 100644 internal/restorable/pixelrecords.go create mode 100644 internal/restorable/rect_test.go diff --git a/internal/restorable/doc.go b/internal/restorable/doc.go index adc4c448f..fb51cae66 100644 --- a/internal/restorable/doc.go +++ b/internal/restorable/doc.go @@ -12,11 +12,57 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package restorable used to offer an Image struct that stores image commands +// Package restorable offers an Image struct that stores image commands // and restores its pixel data from the commands when context lost happens. // -// However, now Ebitengine doesn't handle context losts, and this package is -// just a thin wrapper. +// When a function like DrawImage or Fill is called, an Image tries to record +// the information for restoring. // -// TODO: Integrate this package into internal/atlas and internal/graphicscommand (#805). +// * Context lost +// +// Contest lost is a process that information on GPU memory are removed by OS +// to make more room on GPU memory. +// This can happen e.g. when GPU memory usage is high, or just switching applications +// might cause context lost on mobiles. +// As Ebitengine's image data is on GPU memory, the game can't continue when context lost happens +// without restoring image information. +// The package restorable is the package to record information for such restoring. +// +// * DrawImage +// +// DrawImage function tries to record an item of 'draw image history' in the target image. +// If a target image is stale or volatile, no item is created. +// If an item of the history is created, +// it can be said that the target image depends on the source image. +// In other words, If A.DrawImage(B, ...) is called, +// it can be said that the image A depends on the image B. +// +// * Fill, WritePixels and Dispose +// +// These functions are also drawing functions and the target image stores the pixel data +// instead of draw image history items. There is no dependency here. +// +// * Making images stale +// +// After any of the drawing functions is called, the target image can't be depended on by +// any other images. For example, if an image A depends on an image B, and B is changed +// by a Fill call after that, the image A can't depend on the image B anymore. +// In this case, as the image B can no longer be used to restore the image A, +// the image A becomes 'stale'. +// As all the stale images are resolved before context lost happens, +// draw image history items are kept as they are +// (even if an image C depends on the stale image A, it is still fine). +// +// * Stale image +// +// A stale image is an image that can't be restored from the recorded information. +// All stale images must be resolved by reading pixels from GPU before the frame ends. +// If a source image of DrawImage is a stale image, the target always becomes stale. +// +// * Volatile image +// +// A volatile image is a special image that is always cleared when a frame starts. +// For instance, the game screen passed via the update function is a volatile image. +// A volatile image doesn't have to record the drawing history. +// If a source image of DrawImage is a volatile image, the target always becomes stale. package restorable diff --git a/internal/restorable/export_test.go b/internal/restorable/export_test.go new file mode 100644 index 000000000..aad181758 --- /dev/null +++ b/internal/restorable/export_test.go @@ -0,0 +1,23 @@ +// Copyright 2023 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable + +import ( + "image" +) + +func AppendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) { + appendRegionRemovingDuplicates(regions, region) +} diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 73d689ff2..80864e430 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -23,6 +23,60 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) +type Pixels struct { + pixelsRecords *pixelsRecords +} + +// Apply applies the Pixels state to the given image especially for restoring. +func (p *Pixels) Apply(img *graphicscommand.Image) { + // Pixels doesn't clear the image. This is a caller's responsibility. + + if p.pixelsRecords == nil { + return + } + p.pixelsRecords.apply(img) +} + +func (p *Pixels) AddOrReplace(pix *graphics.ManagedBytes, region image.Rectangle) { + if p.pixelsRecords == nil { + p.pixelsRecords = &pixelsRecords{} + } + p.pixelsRecords.addOrReplace(pix, region) +} + +func (p *Pixels) Clear(region image.Rectangle) { + // Note that we don't care whether the region is actually removed or not here. There is an actual case that + // the region is allocated but nothing is rendered. See TestDisposeImmediately at shareable package. + if p.pixelsRecords == nil { + return + } + p.pixelsRecords.clear(region) +} + +func (p *Pixels) ReadPixels(pixels []byte, region image.Rectangle, imageWidth, imageHeight int) { + if p.pixelsRecords == nil { + for i := range pixels { + pixels[i] = 0 + } + return + } + p.pixelsRecords.readPixels(pixels, region, imageWidth, imageHeight) +} + +func (p *Pixels) AppendRegion(regions []image.Rectangle) []image.Rectangle { + if p.pixelsRecords == nil { + return regions + } + return p.pixelsRecords.appendRegions(regions) +} + +func (p *Pixels) Dispose() { + if p.pixelsRecords == nil { + return + } + p.pixelsRecords.dispose() +} + type ImageType int const ( @@ -33,16 +87,44 @@ const ( ImageTypeScreen // ImageTypeVolatile indicates the image is cleared whenever a frame starts. + // + // Regular non-volatile images need to record drawing history or read its pixels from GPU if necessary so that all + // the images can be restored automatically from the context lost. However, such recording the drawing history or + // reading pixels from GPU are expensive operations. Volatile images can skip such operations, but the image content + // is cleared every frame instead. ImageTypeVolatile ) -// Image represents an image. +// Image represents an image that can be restored when GL context is lost. type Image struct { image *graphicscommand.Image width int height int + basePixels Pixels + + // stale indicates whether the image needs to be synced with GPU as soon as possible. + stale bool + + // staleRegions indicates the regions to restore. + // staleRegions is valid only when stale is true. + // staleRegions is not used when AlwaysReadPixelsFromGPU() returns true. + staleRegions []image.Rectangle + + // pixelsCache is cached byte slices for pixels. + // pixelsCache is just a cache to avoid allocations (#2375). + // + // A key is the region and a value is a byte slice for the region. + // + // It is fine to reuse the same byte slice for the same region for basePixels, + // as old pixels for the same region will be invalidated at basePixel.AddOrReplace. + pixelsCache map[image.Rectangle][]byte + + // regionsCache is cached regions. + // regionsCache is just a cache to avoid allocations (#2375). + regionsCache []image.Rectangle + imageType ImageType } @@ -102,11 +184,20 @@ func clearImage(i *graphicscommand.Image, region image.Rectangle) { i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.shader, nil, graphicsdriver.FillRuleFillAll) } +// makeStale makes the image stale. +func (i *Image) makeStale(rect image.Rectangle) { + i.stale = true +} + // ClearPixels clears the specified region by WritePixels. func (i *Image) ClearPixels(region image.Rectangle) { i.WritePixels(nil, region) } +func (i *Image) needsRestoring() bool { + return i.imageType == ImageTypeRegular +} + // WritePixels replaces the image pixels with the given pixels slice. // // The specified region must not be overlapped with other regions by WritePixels. @@ -119,11 +210,18 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl panic(fmt.Sprintf("restorable: out of range %v", region)) } + // TODO: Avoid making other images stale if possible. (#514) + // For this purpose, images should remember which part of that is used for DrawTriangles. + theImages.makeStaleIfDependingOn(i) + if pixels != nil { i.image.WritePixels(pixels, region) } else { clearImage(i.image, region) } + + // Even if the image is already stale, call makeStale to extend the stale region. + i.makeStale(region) } // DrawTriangles draws triangles with the given image. @@ -142,6 +240,10 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice if len(vertices) == 0 { return } + theImages.makeStaleIfDependingOn(i) + + // Even if the image is already stale, call makeStale to extend the stale region. + i.makeStale(dstRegion) var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image for i, src := range srcs { @@ -165,6 +267,88 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte return nil } +// makeStaleIfDependingOn makes the image stale if the image depends on target. +func (i *Image) makeStaleIfDependingOn(target *Image) { + if i.stale { + return + } + if i.dependsOn(target) { + // There is no new region to make stale. + i.makeStale(image.Rectangle{}) + } +} + +// makeStaleIfDependingOnShader makes the image stale if the image depends on shader. +func (i *Image) makeStaleIfDependingOnShader(shader *Shader) { + if i.stale { + return + } + if i.dependsOnShader(shader) { + // There is no new region to make stale. + i.makeStale(image.Rectangle{}) + } +} + +// dependsOn reports whether the image depends on target. +func (i *Image) dependsOn(target *Image) bool { + return false +} + +// dependsOnShader reports whether the image depends on shader. +func (i *Image) dependsOnShader(shader *Shader) bool { + return false +} + +// dependingImages returns all images that is depended on the image. +func (i *Image) dependingImages() map[*Image]struct{} { + r := map[*Image]struct{}{} + return r +} + +// hasDependency returns a boolean value indicating whether the image depends on another image. +func (i *Image) hasDependency() bool { + return false +} + +// Restore restores *graphicscommand.Image from the pixels using its state. +func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { + w, h := i.width, i.height + // Do not dispose the image here. The image should be already disposed. + + switch i.imageType { + case ImageTypeScreen: + // The screen image should also be recreated because framebuffer might + // be changed. + i.image = graphicscommand.NewImage(w, h, true) + i.basePixels.Dispose() + i.basePixels = Pixels{} + i.stale = false + i.staleRegions = i.staleRegions[:0] + return nil + case ImageTypeVolatile: + i.image = graphicscommand.NewImage(w, h, false) + iw, ih := i.image.InternalSize() + clearImage(i.image, image.Rect(0, 0, iw, ih)) + return nil + } + + if i.stale { + panic("restorable: pixels must not be stale when restoring") + } + + gimg := graphicscommand.NewImage(w, h, false) + // Clear the image explicitly. + iw, ih := gimg.InternalSize() + clearImage(gimg, image.Rect(0, 0, iw, ih)) + + i.basePixels.Apply(gimg) + + i.image = gimg + i.stale = false + i.staleRegions = i.staleRegions[:0] + return nil +} + // Dispose disposes the image. // // After disposing, calling the function of the image causes unexpected results. @@ -172,6 +356,11 @@ func (i *Image) Dispose() { theImages.remove(i) i.image.Dispose() i.image = nil + i.basePixels.Dispose() + i.basePixels = Pixels{} + i.pixelsCache = nil + i.stale = false + i.staleRegions = i.staleRegions[:0] } func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) (string, error) { @@ -181,3 +370,30 @@ func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackb func (i *Image) InternalSize() (int, int) { return i.image.InternalSize() } + +// appendRegionRemovingDuplicates adds a region to a given list of regions, +// but removes any duplicate between the newly added region and any existing regions. +// +// In case the newly added region is fully contained in any pre-existing region, this function does nothing. +// Otherwise, any pre-existing regions that are fully contained in the newly added region are removed. +// +// This is done to avoid unnecessary reading pixels from GPU. +func appendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) { + for _, r := range *regions { + if region.In(r) { + // The newly added rectangle is fully contained in one of the input regions. + // Nothing to add. + return + } + } + // Separate loop, as regions must not get mutated before above return. + n := 0 + for _, r := range *regions { + if r.In(region) { + continue + } + (*regions)[n] = r + n++ + } + *regions = append((*regions)[:n], region) +} diff --git a/internal/restorable/images.go b/internal/restorable/images.go index a1d84d0e2..aff81fdd7 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -22,8 +22,9 @@ import ( // images is a set of Image objects. type images struct { - images map[*Image]struct{} - shaders map[*Shader]struct{} + images map[*Image]struct{} + shaders map[*Shader]struct{} + lastTarget *Image } // theImages represents the images for the current process. @@ -70,13 +71,42 @@ func (i *images) addShader(shader *Shader) { // remove removes img from the images. func (i *images) remove(img *Image) { + i.makeStaleIfDependingOn(img) delete(i.images, img) } func (i *images) removeShader(shader *Shader) { + i.makeStaleIfDependingOnShader(shader) delete(i.shaders, shader) } +// makeStaleIfDependingOn makes all the images stale that depend on target. +// +// When target is modified, all images depending on target can't be restored with target. +// makeStaleIfDependingOn is called in such situation. +func (i *images) makeStaleIfDependingOn(target *Image) { + if target == nil { + panic("restorable: target must not be nil at makeStaleIfDependingOn") + } + if i.lastTarget == target { + return + } + i.lastTarget = target + for img := range i.images { + img.makeStaleIfDependingOn(target) + } +} + +// makeStaleIfDependingOn makes all the images stale that depend on shader. +func (i *images) makeStaleIfDependingOnShader(shader *Shader) { + if shader == nil { + panic("restorable: shader must not be nil at makeStaleIfDependingOnShader") + } + for img := range i.images { + img.makeStaleIfDependingOnShader(shader) + } +} + var graphicsDriverInitialized bool // InitializeGraphicsDriverState initializes the graphics driver state. diff --git a/internal/restorable/pixelrecords.go b/internal/restorable/pixelrecords.go new file mode 100644 index 000000000..b97446d4f --- /dev/null +++ b/internal/restorable/pixelrecords.go @@ -0,0 +1,159 @@ +// Copyright 2019 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable + +import ( + "fmt" + "image" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" +) + +type pixelsRecord struct { + rect image.Rectangle + pix *graphics.ManagedBytes +} + +func (p *pixelsRecord) readPixels(pixels []byte, region image.Rectangle, imageWidth, imageHeight int) { + r := p.rect.Intersect(region.Intersect(image.Rect(0, 0, imageWidth, imageHeight))) + if r.Empty() { + return + } + + dstBaseX := r.Min.X - region.Min.X + dstBaseY := r.Min.Y - region.Min.Y + lineWidth := 4 * r.Dx() + if p.pix != nil { + srcBaseX := r.Min.X - p.rect.Min.X + srcBaseY := r.Min.Y - p.rect.Min.Y + for j := 0; j < r.Dy(); j++ { + dstX := 4 * ((dstBaseY+j)*region.Dx() + dstBaseX) + srcX := 4 * ((srcBaseY+j)*p.rect.Dx() + srcBaseX) + p.pix.Read(pixels[dstX:dstX+lineWidth], srcX, srcX+lineWidth) + } + } else { + for j := 0; j < r.Dy(); j++ { + dstX := 4 * ((dstBaseY+j)*region.Dx() + dstBaseX) + for i := 0; i < lineWidth; i++ { + pixels[i+dstX] = 0 + } + } + } +} + +type pixelsRecords struct { + records []*pixelsRecord +} + +func (pr *pixelsRecords) addOrReplace(pixels *graphics.ManagedBytes, region image.Rectangle) { + if pixels.Len() != 4*region.Dx()*region.Dy() { + msg := fmt.Sprintf("restorable: len(pixels) must be 4*%d*%d = %d but %d", region.Dx(), region.Dy(), 4*region.Dx()*region.Dy(), pixels.Len()) + if pixels == nil { + msg += " (nil)" + } + panic(msg) + } + + // Remove or update the duplicated records first. + var n int + for _, r := range pr.records { + if r.rect.In(region) { + continue + } + pr.records[n] = r + n++ + } + for i := n; i < len(pr.records); i++ { + pr.records[i] = nil + } + pr.records = pr.records[:n] + + // Add the new record. + pr.records = append(pr.records, &pixelsRecord{ + rect: region, + pix: pixels, + }) +} + +func (pr *pixelsRecords) clear(region image.Rectangle) { + if region.Empty() { + return + } + + var n int + var needsClear bool + for _, r := range pr.records { + if r.rect.In(region) { + continue + } + if r.rect.Overlaps(region) { + needsClear = true + } + pr.records[n] = r + n++ + } + for i := n; i < len(pr.records); i++ { + pr.records[i] = nil + } + pr.records = pr.records[:n] + if needsClear { + pr.records = append(pr.records, &pixelsRecord{ + rect: region, + }) + } +} + +func (pr *pixelsRecords) readPixels(pixels []byte, region image.Rectangle, imageWidth, imageHeight int) { + for i := range pixels { + pixels[i] = 0 + } + for _, r := range pr.records { + r.readPixels(pixels, region, imageWidth, imageHeight) + } +} + +func (pr *pixelsRecords) apply(img *graphicscommand.Image) { + // TODO: Isn't this too heavy? Can we merge the operations? + for _, r := range pr.records { + if r.pix != nil { + // Clone a ManagedBytes as the package graphicscommand has a different lifetime management. + img.WritePixels(r.pix.Clone(), r.rect) + } else { + clearImage(img, r.rect) + } + } +} + +func (pr *pixelsRecords) appendRegions(regions []image.Rectangle) []image.Rectangle { + for _, r := range pr.records { + if r.rect.Empty() { + continue + } + regions = append(regions, r.rect) + } + return regions +} + +func (pr *pixelsRecords) dispose() { + for _, r := range pr.records { + if r.pix == nil { + continue + } + // As the package graphicscommands already has cloned ManagedBytes objects, it is OK to release it. + _, f := r.pix.GetAndRelease() + f() + } +} diff --git a/internal/restorable/rect_test.go b/internal/restorable/rect_test.go new file mode 100644 index 000000000..27f094270 --- /dev/null +++ b/internal/restorable/rect_test.go @@ -0,0 +1,165 @@ +// Copyright 2023 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable_test + +import ( + "image" + "testing" + + "github.com/hajimehoshi/ebiten/v2/internal/restorable" +) + +func areEqualRectangles(a, b []image.Rectangle) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func TestRemoveDuplicatedRegions(t *testing.T) { + cases := []struct { + Regions []image.Rectangle + NewRegions []image.Rectangle + Expected []image.Rectangle + }{ + { + Regions: nil, + NewRegions: nil, + Expected: nil, + }, + { + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 2, 2), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 2, 2), + }, + }, + { + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 1, 1), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 2, 2), + }, + }, + { + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 1, 1), + image.Rect(0, 0, 2, 2), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 2, 2), + }, + }, + { + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 1, 3), + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 1, 3), + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + }, + }, + { + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 1, 3), + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + image.Rect(0, 0, 4, 4), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 4, 4), + }, + }, + { + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 1, 3), + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + image.Rect(0, 0, 4, 4), + image.Rect(1, 1, 2, 2), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 4, 4), + }, + }, + { + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 1, 3), + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + image.Rect(0, 0, 4, 4), + image.Rect(0, 0, 5, 5), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 5, 5), + }, + }, + { + Regions: []image.Rectangle{ + image.Rect(0, 0, 1, 3), + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + image.Rect(0, 0, 4, 4), + }, + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 5, 5), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 5, 5), + }, + }, + { + Regions: []image.Rectangle{ + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + image.Rect(0, 0, 4, 4), + image.Rect(0, 0, 5, 5), + }, + NewRegions: []image.Rectangle{ + image.Rect(0, 0, 1, 3), + }, + Expected: []image.Rectangle{ + image.Rect(0, 0, 2, 2), + image.Rect(0, 0, 3, 1), + image.Rect(0, 0, 4, 4), + image.Rect(0, 0, 5, 5), + }, + }, + } + + for _, c := range cases { + got := c.Regions + for _, r := range c.NewRegions { + restorable.AppendRegionRemovingDuplicates(&got, r) + } + want := c.Expected + if !areEqualRectangles(got, want) { + t.Errorf("restorable.RemoveDuplicatedRegions(%#v): got: %#v, want: %#v", c.NewRegions, got, want) + } + } +} From 16d2052836cc9a6658b8dfb4807efe70c0820e3c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 15:28:56 +0900 Subject: [PATCH 123/188] Revert "internal/restorable: remove alwaysReadPixelsFromGPU" This reverts commit f3206721a32bb258eafe692c70ca4cb6e2e2792b. Updates #3083 --- internal/restorable/image.go | 106 ++++++++++++++++++++++++++++++++-- internal/restorable/images.go | 4 ++ 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 80864e430..b2c88ed6d 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -187,6 +187,30 @@ func clearImage(i *graphicscommand.Image, region image.Rectangle) { // makeStale makes the image stale. func (i *Image) makeStale(rect image.Rectangle) { i.stale = true + + // If ReadPixels always reads pixels from GPU, staleRegions are never used. + if alwaysReadPixelsFromGPU() { + return + } + + var addedRegions []image.Rectangle + if !rect.Empty() { + appendRegionRemovingDuplicates(&addedRegions, rect) + } + + for _, rect := range addedRegions { + appendRegionRemovingDuplicates(&i.staleRegions, rect) + } + + // Clear pixels to save memory. + for _, r := range addedRegions { + i.basePixels.Clear(r) + } + + // Don't have to call makeStale recursively here. + // Restoring is done after topological sorting is done. + // If an image depends on another stale image, this means that + // the former image can be restored from the latest state of the latter image. } // ClearPixels clears the specified region by WritePixels. @@ -255,15 +279,35 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice i.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule) } +func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics) error { + if i.stale { + if err := i.readPixelsFromGPU(graphicsDriver); err != nil { + return err + } + } + return nil +} + func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error { - if err := i.image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ - { - Pixels: pixels, - Region: region, - }, - }); err != nil { + if alwaysReadPixelsFromGPU() { + if err := i.image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ + { + Pixels: pixels, + Region: region, + }, + }); err != nil { + return err + } + return nil + } + + if err := i.readPixelsFromGPUIfNeeded(graphicsDriver); err != nil { return err } + if got, want := len(pixels), 4*region.Dx()*region.Dy(); got != want { + return fmt.Errorf("restorable: len(pixels) must be %d but %d at ReadPixels", want, got) + } + i.basePixels.ReadPixels(pixels, region, i.width, i.height) return nil } @@ -289,6 +333,56 @@ func (i *Image) makeStaleIfDependingOnShader(shader *Shader) { } } +// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state. +func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error { + var rs []image.Rectangle + if i.stale { + rs = i.staleRegions + } else { + defer func() { + i.regionsCache = i.regionsCache[:0] + }() + rs = i.regionsCache + } + + args := make([]graphicsdriver.PixelsArgs, 0, len(rs)) + for _, r := range rs { + if r.Empty() { + continue + } + + if i.pixelsCache == nil { + i.pixelsCache = map[image.Rectangle][]byte{} + } + + pix, ok := i.pixelsCache[r] + if !ok { + pix = make([]byte, 4*r.Dx()*r.Dy()) + i.pixelsCache[r] = pix + } + + args = append(args, graphicsdriver.PixelsArgs{ + Pixels: pix, + Region: r, + }) + } + + if err := i.image.ReadPixels(graphicsDriver, args); err != nil { + return err + } + + for _, a := range args { + bs := graphics.NewManagedBytes(len(a.Pixels), func(bs []byte) { + copy(bs, a.Pixels) + }) + i.basePixels.AddOrReplace(bs, a.Region) + } + + i.stale = false + i.staleRegions = i.staleRegions[:0] + return nil +} + // dependsOn reports whether the image depends on target. func (i *Image) dependsOn(target *Image) bool { return false diff --git a/internal/restorable/images.go b/internal/restorable/images.go index aff81fdd7..3cf32dcbc 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -20,6 +20,10 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) +func alwaysReadPixelsFromGPU() bool { + return true +} + // images is a set of Image objects. type images struct { images map[*Image]struct{} From 5e18f191c1acfd8440992141d16ab8c62d0aa8ad Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 15:29:56 +0900 Subject: [PATCH 124/188] Revert "internal/restorable: remove drawTrianglesHistoryItem" This reverts commit afe3f7a8ffbfd591844fcf16801a4e52c871f49d. Updates #3083 --- internal/restorable/image.go | 172 ++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index b2c88ed6d..af8bf4f57 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -77,6 +77,19 @@ func (p *Pixels) Dispose() { p.pixelsRecords.dispose() } +// drawTrianglesHistoryItem is an item for history of draw-image commands. +type drawTrianglesHistoryItem struct { + images [graphics.ShaderSrcImageCount]*Image + vertices []float32 + indices []uint32 + blend graphicsdriver.Blend + dstRegion image.Rectangle + srcRegions [graphics.ShaderSrcImageCount]image.Rectangle + shader *Shader + uniforms []uint32 + fillRule graphicsdriver.FillRule +} + type ImageType int const ( @@ -104,6 +117,10 @@ type Image struct { basePixels Pixels + // drawTrianglesHistory is a set of draw-image commands. + // TODO: This should be merged with the similar command queue in package graphics (#433). + drawTrianglesHistory []*drawTrianglesHistoryItem + // stale indicates whether the image needs to be synced with GPU as soon as possible. stale bool @@ -184,6 +201,11 @@ func clearImage(i *graphicscommand.Image, region image.Rectangle) { i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.shader, nil, graphicsdriver.FillRuleFillAll) } +// BasePixelsForTesting returns the image's basePixels for testing. +func (i *Image) BasePixelsForTesting() *Pixels { + return &i.basePixels +} + // makeStale makes the image stale. func (i *Image) makeStale(rect image.Rectangle) { i.stale = true @@ -194,6 +216,7 @@ func (i *Image) makeStale(rect image.Rectangle) { } var addedRegions []image.Rectangle + i.appendRegionsForDrawTriangles(&addedRegions) if !rect.Empty() { appendRegionRemovingDuplicates(&addedRegions, rect) } @@ -202,6 +225,8 @@ func (i *Image) makeStale(rect image.Rectangle) { appendRegionRemovingDuplicates(&i.staleRegions, rect) } + i.clearDrawTrianglesHistory() + // Clear pixels to save memory. for _, r := range addedRegions { i.basePixels.Clear(r) @@ -279,8 +304,49 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice i.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule) } +// appendDrawTrianglesHistory appends a draw-image history item to the image. +func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { + if i.stale || !i.needsRestoring() { + panic("restorable: an image must not be stale or need restoring at appendDrawTrianglesHistory") + } + if alwaysReadPixelsFromGPU() { + panic("restorable: appendDrawTrianglesHistory must not be called when AlwaysReadPixelsFromGPU() returns true") + } + + // TODO: Would it be possible to merge draw image history items? + const maxDrawTrianglesHistoryCount = 1024 + if len(i.drawTrianglesHistory)+1 > maxDrawTrianglesHistoryCount { + i.makeStale(dstRegion) + return + } + // All images must be resolved and not stale each after frame. + // So we don't have to care if image is stale or not here. + + vs := make([]float32, len(vertices)) + copy(vs, vertices) + + is := make([]uint32, len(indices)) + copy(is, indices) + + us := make([]uint32, len(uniforms)) + copy(us, uniforms) + + item := &drawTrianglesHistoryItem{ + images: srcs, + vertices: vs, + indices: is, + blend: blend, + dstRegion: dstRegion, + srcRegions: srcRegions, + shader: shader, + uniforms: us, + fillRule: fillRule, + } + i.drawTrianglesHistory = append(i.drawTrianglesHistory, item) +} + func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics) error { - if i.stale { + if len(i.drawTrianglesHistory) > 0 || i.stale { if err := i.readPixelsFromGPU(graphicsDriver); err != nil { return err } @@ -339,6 +405,7 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error if i.stale { rs = i.staleRegions } else { + i.appendRegionsForDrawTriangles(&i.regionsCache) defer func() { i.regionsCache = i.regionsCache[:0] }() @@ -378,6 +445,7 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error i.basePixels.AddOrReplace(bs, a.Region) } + i.clearDrawTrianglesHistory() i.stale = false i.staleRegions = i.staleRegions[:0] return nil @@ -385,23 +453,49 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error // dependsOn reports whether the image depends on target. func (i *Image) dependsOn(target *Image) bool { + for _, c := range i.drawTrianglesHistory { + for _, img := range c.images { + if img == nil { + continue + } + if img == target { + return true + } + } + } return false } // dependsOnShader reports whether the image depends on shader. func (i *Image) dependsOnShader(shader *Shader) bool { + for _, c := range i.drawTrianglesHistory { + if c.shader == shader { + return true + } + } return false } // dependingImages returns all images that is depended on the image. func (i *Image) dependingImages() map[*Image]struct{} { r := map[*Image]struct{}{} + for _, c := range i.drawTrianglesHistory { + for _, img := range c.images { + if img == nil { + continue + } + r[img] = struct{}{} + } + } return r } // hasDependency returns a boolean value indicating whether the image depends on another image. func (i *Image) hasDependency() bool { - return false + if i.stale { + return false + } + return len(i.drawTrianglesHistory) > 0 } // Restore restores *graphicscommand.Image from the pixels using its state. @@ -416,6 +510,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { i.image = graphicscommand.NewImage(w, h, true) i.basePixels.Dispose() i.basePixels = Pixels{} + i.clearDrawTrianglesHistory() i.stale = false i.staleRegions = i.staleRegions[:0] return nil @@ -437,7 +532,62 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { i.basePixels.Apply(gimg) + for _, c := range i.drawTrianglesHistory { + var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image + for i, img := range c.images { + if img == nil { + continue + } + if img.hasDependency() { + panic("restorable: all dependencies must be already resolved but not") + } + imgs[i] = img.image + } + gimg.DrawTriangles(imgs, c.vertices, c.indices, c.blend, c.dstRegion, c.srcRegions, c.shader.shader, c.uniforms, c.fillRule) + } + + // In order to clear the draw-triangles history, read pixels from GPU. + if len(i.drawTrianglesHistory) > 0 { + i.appendRegionsForDrawTriangles(&i.regionsCache) + defer func() { + i.regionsCache = i.regionsCache[:0] + }() + + args := make([]graphicsdriver.PixelsArgs, 0, len(i.regionsCache)) + for _, r := range i.regionsCache { + if r.Empty() { + continue + } + + if i.pixelsCache == nil { + i.pixelsCache = map[image.Rectangle][]byte{} + } + + pix, ok := i.pixelsCache[r] + if !ok { + pix = make([]byte, 4*r.Dx()*r.Dy()) + i.pixelsCache[r] = pix + } + args = append(args, graphicsdriver.PixelsArgs{ + Pixels: pix, + Region: r, + }) + } + + if err := gimg.ReadPixels(graphicsDriver, args); err != nil { + return err + } + + for _, a := range args { + bs := graphics.NewManagedBytes(len(a.Pixels), func(bs []byte) { + copy(bs, a.Pixels) + }) + i.basePixels.AddOrReplace(bs, a.Region) + } + } + i.image = gimg + i.clearDrawTrianglesHistory() i.stale = false i.staleRegions = i.staleRegions[:0] return nil @@ -453,6 +603,7 @@ func (i *Image) Dispose() { i.basePixels.Dispose() i.basePixels = Pixels{} i.pixelsCache = nil + i.clearDrawTrianglesHistory() i.stale = false i.staleRegions = i.staleRegions[:0] } @@ -461,10 +612,27 @@ func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackb return i.image.Dump(graphicsDriver, path, blackbg, rect) } +func (i *Image) clearDrawTrianglesHistory() { + // Clear the items explicitly, or the references might remain (#1803). + for idx := range i.drawTrianglesHistory { + i.drawTrianglesHistory[idx] = nil + } + i.drawTrianglesHistory = i.drawTrianglesHistory[:0] +} + func (i *Image) InternalSize() (int, int) { return i.image.InternalSize() } +func (i *Image) appendRegionsForDrawTriangles(regions *[]image.Rectangle) { + for _, d := range i.drawTrianglesHistory { + if d.dstRegion.Empty() { + continue + } + appendRegionRemovingDuplicates(regions, d.dstRegion) + } +} + // appendRegionRemovingDuplicates adds a region to a given list of regions, // but removes any duplicate between the newly added region and any existing regions. // From 935e7a6d5de009528aea12c71e8986f18a846e6f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 15:50:08 +0900 Subject: [PATCH 125/188] Revert "internal/restorable: remove the case when the restoring is needed" This reverts commit c08a2193a92b47c7e5ca786882db1ecfeb7be039. Updates #3083 --- internal/atlas/image.go | 5 + internal/buffered/image.go | 19 +- internal/restorable/export_test.go | 6 + internal/restorable/image.go | 69 +- internal/restorable/images.go | 143 +++- internal/restorable/images_test.go | 1140 ++++++++++++++++++++++++++++ internal/restorable/shader_test.go | 200 +++++ 7 files changed, 1570 insertions(+), 12 deletions(-) create mode 100644 internal/restorable/images_test.go create mode 100644 internal/restorable/shader_test.go diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 46e6fbf34..d8be19e0e 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -833,6 +833,11 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error { return err } + // Restore images first before other image manipulations (#2075). + if err := restorable.RestoreIfNeeded(graphicsDriver); err != nil { + return err + } + flushDeferred() putImagesOnSourceBackend() diff --git a/internal/buffered/image.go b/internal/buffered/image.go index d02b2b72b..a4c22e10a 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -21,6 +21,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" ) var whiteImage *Image @@ -46,6 +47,8 @@ type Image struct { // pixels is cached pixels for ReadPixels. // pixels might be out of sync with GPU. // The data of pixels is the secondary data of pixels for ReadPixels. + // + // pixels is always nil when restorable.AlwaysReadPixelsFromGPU() returns false. pixels []byte // pixelsUnsynced represents whether the pixels in CPU and GPU are not synced. @@ -68,9 +71,6 @@ func (i *Image) Deallocate() { } func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) (bool, error) { - // Do not call flushDotsBufferIfNeeded here. This would slow (image/draw).Draw. - // See ebiten.TestImageDrawOver. - if region.Dx() == 1 && region.Dy() == 1 { if c, ok := i.dotsBuffer[region.Min]; ok { copy(pixels, c[:]) @@ -78,6 +78,19 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte } } + // If restorable.AlwaysReadPixelsFromGPU() returns false, the pixel data is cached in the restorable package. + if !restorable.AlwaysReadPixelsFromGPU() { + i.syncPixelsIfNeeded() + ok, err := i.img.ReadPixels(graphicsDriver, pixels, region) + if err != nil { + return false, err + } + return ok, nil + } + + // Do not call syncPixelsIfNeeded here. This would slow (image/draw).Draw. + // See ebiten.TestImageDrawOver. + if i.pixels == nil { pix := make([]byte, 4*i.width*i.height) ok, err := i.img.ReadPixels(graphicsDriver, pix, image.Rect(0, 0, i.width, i.height)) diff --git a/internal/restorable/export_test.go b/internal/restorable/export_test.go index aad181758..10071b879 100644 --- a/internal/restorable/export_test.go +++ b/internal/restorable/export_test.go @@ -16,8 +16,14 @@ package restorable import ( "image" + + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) +func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { + return resolveStaleImages(graphicsDriver, false) +} + func AppendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) { appendRegionRemovingDuplicates(regions, region) } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index af8bf4f57..6a4015df3 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -211,7 +211,7 @@ func (i *Image) makeStale(rect image.Rectangle) { i.stale = true // If ReadPixels always reads pixels from GPU, staleRegions are never used. - if alwaysReadPixelsFromGPU() { + if AlwaysReadPixelsFromGPU() { return } @@ -270,7 +270,36 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl } // Even if the image is already stale, call makeStale to extend the stale region. - i.makeStale(region) + if !needsRestoring() || !i.needsRestoring() || i.stale { + i.makeStale(region) + return + } + + if region.Eq(image.Rect(0, 0, w, h)) { + if pixels != nil { + // Clone a ManagedBytes as the package graphicscommand has a different lifetime management. + i.basePixels.AddOrReplace(pixels.Clone(), image.Rect(0, 0, w, h)) + } else { + i.basePixels.Clear(image.Rect(0, 0, w, h)) + } + i.clearDrawTrianglesHistory() + i.stale = false + i.staleRegions = i.staleRegions[:0] + return + } + + // Records for DrawTriangles cannot come before records for WritePixels. + if len(i.drawTrianglesHistory) > 0 { + i.makeStale(region) + return + } + + if pixels != nil { + // Clone a ManagedBytes as the package graphicscommand has a different lifetime management. + i.basePixels.AddOrReplace(pixels.Clone(), region) + } else { + i.basePixels.Clear(region) + } } // DrawTriangles draws triangles with the given image. @@ -291,8 +320,24 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice } theImages.makeStaleIfDependingOn(i) + // TODO: Add tests to confirm this logic. + var srcstale bool + for _, src := range srcs { + if src == nil { + continue + } + if src.stale || src.imageType == ImageTypeVolatile { + srcstale = true + break + } + } + // Even if the image is already stale, call makeStale to extend the stale region. - i.makeStale(dstRegion) + if srcstale || !needsRestoring() || !i.needsRestoring() || i.stale { + i.makeStale(dstRegion) + } else { + i.appendDrawTrianglesHistory(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule) + } var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image for i, src := range srcs { @@ -309,7 +354,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*I if i.stale || !i.needsRestoring() { panic("restorable: an image must not be stale or need restoring at appendDrawTrianglesHistory") } - if alwaysReadPixelsFromGPU() { + if AlwaysReadPixelsFromGPU() { panic("restorable: appendDrawTrianglesHistory must not be called when AlwaysReadPixelsFromGPU() returns true") } @@ -355,7 +400,7 @@ func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics } func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error { - if alwaysReadPixelsFromGPU() { + if AlwaysReadPixelsFromGPU() { if err := i.image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ { Pixels: pixels, @@ -451,6 +496,20 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error return nil } +// resolveStale resolves the image's 'stale' state. +func (i *Image) resolveStale(graphicsDriver graphicsdriver.Graphics) error { + if !needsRestoring() { + return nil + } + if !i.needsRestoring() { + return nil + } + if !i.stale { + return nil + } + return i.readPixelsFromGPU(graphicsDriver) +} + // dependsOn reports whether the image depends on target. func (i *Image) dependsOn(target *Image) bool { for _, c := range i.drawTrianglesHistory { diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 3cf32dcbc..c1a34c792 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -20,8 +20,22 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -func alwaysReadPixelsFromGPU() bool { - return true +// forceRestoring reports whether restoring forcely happens or not. +var forceRestoring = false + +// needsRestoring reports whether restoring process works or not. +func needsRestoring() bool { + return forceRestoring +} + +// AlwaysReadPixelsFromGPU reports whether ReadPixels always reads pixels from GPU or not. +func AlwaysReadPixelsFromGPU() bool { + return !needsRestoring() +} + +// EnableRestoringForTesting forces to enable restoring for testing. +func EnableRestoringForTesting() { + forceRestoring = true } // images is a set of Image objects. @@ -46,10 +60,43 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { } graphicscommand.LogImagesInfo(imgs) } - if err := graphicscommand.FlushCommands(graphicsDriver, true); err != nil { + return resolveStaleImages(graphicsDriver, true) +} + +// resolveStaleImages flushes the queued draw commands and resolves all stale images. +// If endFrame is true, the current screen might be used to present when flushing the commands. +func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool) error { + if err := graphicscommand.FlushCommands(graphicsDriver, endFrame); err != nil { return err } - return nil + if !needsRestoring() { + return nil + } + return theImages.resolveStaleImages(graphicsDriver) +} + +// RestoreIfNeeded restores the images. +// +// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. +func RestoreIfNeeded(graphicsDriver graphicsdriver.Graphics) error { + if !needsRestoring() { + return nil + } + + if !forceRestoring { + var r bool + + // TODO: Detect context lost explicitly on Android. + + if !r { + return nil + } + } + + if err := graphicscommand.ResetGraphicsDriverState(graphicsDriver); err != nil { + return err + } + return theImages.restore(graphicsDriver) } // DumpImages dumps all the current images to the specified directory. @@ -84,6 +131,17 @@ func (i *images) removeShader(shader *Shader) { delete(i.shaders, shader) } +// resolveStaleImages resolves stale images. +func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { + i.lastTarget = nil + for img := range i.images { + if err := img.resolveStale(graphicsDriver); err != nil { + return err + } + } + return nil +} + // makeStaleIfDependingOn makes all the images stale that depend on target. // // When target is modified, all images depending on target can't be restored with target. @@ -111,6 +169,83 @@ func (i *images) makeStaleIfDependingOnShader(shader *Shader) { } } +// restore restores the images. +// +// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. +func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error { + if !needsRestoring() { + panic("restorable: restore cannot be called when restoring is disabled") + } + + // Dispose all the shaders ahead of restoring. A current shader ID and a new shader ID can be duplicated. + for s := range i.shaders { + s.shader.Dispose() + s.shader = nil + } + for s := range i.shaders { + s.restore() + } + + // Dispose all the images ahead of restoring. A current texture ID and a new texture ID can be duplicated. + // TODO: Write a test to confirm that ID duplication never happens. + for i := range i.images { + i.image.Dispose() + i.image = nil + } + + // Let's do topological sort based on dependencies of drawing history. + // It is assured that there are not loops since cyclic drawing makes images stale. + type edge struct { + source *Image + target *Image + } + images := map[*Image]struct{}{} + for i := range i.images { + images[i] = struct{}{} + } + edges := map[edge]struct{}{} + for t := range images { + for s := range t.dependingImages() { + edges[edge{source: s, target: t}] = struct{}{} + } + } + + var sorted []*Image + for len(images) > 0 { + // current represents images that have no incoming edges. + current := map[*Image]struct{}{} + for i := range images { + current[i] = struct{}{} + } + for e := range edges { + if _, ok := current[e.target]; ok { + delete(current, e.target) + } + } + for i := range current { + delete(images, i) + sorted = append(sorted, i) + } + removed := []edge{} + for e := range edges { + if _, ok := current[e.source]; ok { + removed = append(removed, e) + } + } + for _, e := range removed { + delete(edges, e) + } + } + + for _, img := range sorted { + if err := img.restore(graphicsDriver); err != nil { + return err + } + } + + return nil +} + var graphicsDriverInitialized bool // InitializeGraphicsDriverState initializes the graphics driver state. diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go new file mode 100644 index 000000000..1be26acf8 --- /dev/null +++ b/internal/restorable/images_test.go @@ -0,0 +1,1140 @@ +// Copyright 2017 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable_test + +import ( + "image" + "image/color" + "testing" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" + etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" + "github.com/hajimehoshi/ebiten/v2/internal/ui" +) + +func TestMain(m *testing.M) { + restorable.EnableRestoringForTesting() + etesting.MainWithRunLoop(m) +} + +func pixelsToColor(p *restorable.Pixels, i, j, imageWidth, imageHeight int) color.RGBA { + var pix [4]byte + p.ReadPixels(pix[:], image.Rect(i, j, i+1, j+1), imageWidth, imageHeight) + return color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]} +} + +func bytesToManagedBytes(src []byte) *graphics.ManagedBytes { + if len(src) == 0 { + panic("restorable: len(src) must be > 0") + } + return graphics.NewManagedBytes(len(src), func(dst []byte) { + copy(dst, src) + }) +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} + +// sameColors compares c1 and c2 and returns a boolean value indicating +// if the two colors are (almost) same. +// +// Pixels read from GPU might include errors (#492), and +// sameColors considers such errors as delta. +func sameColors(c1, c2 color.RGBA, delta int) bool { + return abs(int(c1.R)-int(c2.R)) <= delta && + abs(int(c1.G)-int(c2.G)) <= delta && + abs(int(c1.B)-int(c2.B)) <= delta && + abs(int(c1.A)-int(c2.A)) <= delta +} + +func TestRestore(t *testing.T) { + img0 := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + defer img0.Dispose() + + clr0 := color.RGBA{A: 0xff} + img0.WritePixels(bytesToManagedBytes([]byte{clr0.R, clr0.G, clr0.B, clr0.A}), image.Rect(0, 0, 1, 1)) + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + want := clr0 + got := pixelsToColor(img0.BasePixelsForTesting(), 0, 0, 1, 1) + if !sameColors(got, want, 1) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestRestoreWithoutDraw(t *testing.T) { + img0 := restorable.NewImage(1024, 1024, restorable.ImageTypeRegular) + defer img0.Dispose() + + // If there is no drawing command on img0, img0 is cleared when restored. + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + for j := 0; j < 1024; j++ { + for i := 0; i < 1024; i++ { + want := color.RGBA{} + got := pixelsToColor(img0.BasePixelsForTesting(), i, j, 1024, 1024) + if !sameColors(got, want, 0) { + t.Errorf("got %v, want %v", got, want) + } + } + } +} + +func quadVertices(sw, sh, x, y int) []float32 { + vs := make([]float32, 4*graphics.VertexFloatCount) + dx0 := float32(x) + dy0 := float32(y) + dx1 := float32(x + sw) + dy1 := float32(y + sh) + sx0 := float32(0) + sy0 := float32(0) + sx1 := float32(sw) + sy1 := float32(sh) + graphics.QuadVerticesFromDstAndSrc(vs, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1) + return vs +} + +func TestRestoreChain(t *testing.T) { + const num = 10 + imgs := []*restorable.Image{} + for i := 0; i < num; i++ { + img := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + imgs = append(imgs, img) + } + defer func() { + for _, img := range imgs { + img.Dispose() + } + }() + clr := color.RGBA{A: 0xff} + imgs[0].WritePixels(bytesToManagedBytes([]byte{clr.R, clr.G, clr.B, clr.A}), image.Rect(0, 0, 1, 1)) + for i := 0; i < num-1; i++ { + vs := quadVertices(1, 1, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, 1, 1) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + } + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + want := clr + for i, img := range imgs { + got := pixelsToColor(img.BasePixelsForTesting(), 0, 0, 1, 1) + if !sameColors(got, want, 1) { + t.Errorf("%d: got %v, want %v", i, got, want) + } + } +} + +func TestRestoreChain2(t *testing.T) { + const ( + num = 10 + w = 1 + h = 1 + ) + imgs := []*restorable.Image{} + for i := 0; i < num; i++ { + img := restorable.NewImage(w, h, restorable.ImageTypeRegular) + imgs = append(imgs, img) + } + defer func() { + for _, img := range imgs { + img.Dispose() + } + }() + + clr0 := color.RGBA{R: 0xff, A: 0xff} + imgs[0].WritePixels(bytesToManagedBytes([]byte{clr0.R, clr0.G, clr0.B, clr0.A}), image.Rect(0, 0, w, h)) + clr7 := color.RGBA{G: 0xff, A: 0xff} + imgs[7].WritePixels(bytesToManagedBytes([]byte{clr7.R, clr7.G, clr7.B, clr7.A}), image.Rect(0, 0, w, h)) + clr8 := color.RGBA{B: 0xff, A: 0xff} + imgs[8].WritePixels(bytesToManagedBytes([]byte{clr8.R, clr8.G, clr8.B, clr8.A}), image.Rect(0, 0, w, h)) + + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + imgs[8].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[7]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + imgs[9].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[8]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + for i := 0; i < 7; i++ { + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + } + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + for i, img := range imgs { + want := clr0 + if i == 8 || i == 9 { + want = clr7 + } + got := pixelsToColor(img.BasePixelsForTesting(), 0, 0, w, h) + if !sameColors(got, want, 1) { + t.Errorf("%d: got %v, want %v", i, got, want) + } + } +} + +func TestRestoreOverrideSource(t *testing.T) { + const ( + w = 1 + h = 1 + ) + img0 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img1 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img2 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img3 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + defer func() { + img3.Dispose() + img2.Dispose() + img1.Dispose() + img0.Dispose() + }() + clr0 := color.RGBA{A: 0xff} + clr1 := color.RGBA{B: 0x01, A: 0xff} + img1.WritePixels(bytesToManagedBytes([]byte{clr0.R, clr0.G, clr0.B, clr0.A}), image.Rect(0, 0, w, h)) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + img2.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.WritePixels(bytesToManagedBytes([]byte{clr1.R, clr1.G, clr1.B, clr1.A}), image.Rect(0, 0, w, h)) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + testCases := []struct { + name string + want color.RGBA + got color.RGBA + }{ + { + "0", + clr1, + pixelsToColor(img0.BasePixelsForTesting(), 0, 0, w, h), + }, + { + "1", + clr1, + pixelsToColor(img1.BasePixelsForTesting(), 0, 0, w, h), + }, + { + "2", + clr0, + pixelsToColor(img2.BasePixelsForTesting(), 0, 0, w, h), + }, + { + "3", + clr0, + pixelsToColor(img3.BasePixelsForTesting(), 0, 0, w, h), + }, + } + for _, c := range testCases { + if !sameColors(c.got, c.want, 1) { + t.Errorf("%s: got %v, want %v", c.name, c.got, c.want) + } + } +} + +func TestRestoreComplexGraph(t *testing.T) { + const ( + w = 4 + h = 1 + ) + // 0 -> 3 + // 1 -> 3 + // 1 -> 4 + // 2 -> 4 + // 2 -> 7 + // 3 -> 5 + // 3 -> 6 + // 3 -> 7 + // 4 -> 6 + base := image.NewRGBA(image.Rect(0, 0, w, h)) + base.Pix[0] = 0xff + base.Pix[1] = 0xff + base.Pix[2] = 0xff + base.Pix[3] = 0xff + img0 := newImageFromImage(base) + img1 := newImageFromImage(base) + img2 := newImageFromImage(base) + img3 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img4 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img5 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img6 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img7 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + defer func() { + img7.Dispose() + img6.Dispose() + img5.Dispose() + img4.Dispose() + img3.Dispose() + img2.Dispose() + img1.Dispose() + img0.Dispose() + }() + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + vs := quadVertices(w, h, 0, 0) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 1, 0) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 1, 0) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 2, 0) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 0, 0) + img5.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 0, 0) + img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 1, 0) + img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img4}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 0, 0) + img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + vs = quadVertices(w, h, 2, 0) + img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + testCases := []struct { + name string + out string + image *restorable.Image + }{ + { + "0", + "*---", + img0, + }, + { + "1", + "*---", + img1, + }, + { + "2", + "*---", + img2, + }, + { + "3", + "**--", + img3, + }, + { + "4", + "-**-", + img4, + }, + { + "5", + "**--", + img5, + }, + { + "6", + "****", + img6, + }, + { + "7", + "*-**", + img7, + }, + } + for _, c := range testCases { + for i := 0; i < 4; i++ { + want := color.RGBA{} + if c.out[i] == '*' { + want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + } + got := pixelsToColor(c.image.BasePixelsForTesting(), i, 0, w, h) + if !sameColors(got, want, 1) { + t.Errorf("%s[%d]: got %v, want %v", c.name, i, got, want) + } + } + } +} + +func newImageFromImage(rgba *image.RGBA) *restorable.Image { + s := rgba.Bounds().Size() + img := restorable.NewImage(s.X, s.Y, restorable.ImageTypeRegular) + img.WritePixels(bytesToManagedBytes(rgba.Pix), image.Rect(0, 0, s.X, s.Y)) + return img +} + +func TestRestoreRecursive(t *testing.T) { + const ( + w = 4 + h = 1 + ) + base := image.NewRGBA(image.Rect(0, 0, w, h)) + base.Pix[0] = 0xff + base.Pix[1] = 0xff + base.Pix[2] = 0xff + base.Pix[3] = 0xff + + img0 := newImageFromImage(base) + img1 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + defer func() { + img1.Dispose() + img0.Dispose() + }() + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + testCases := []struct { + name string + out string + image *restorable.Image + }{ + { + "0", + "*-*-", + img0, + }, + { + "1", + "-*--", + img1, + }, + } + for _, c := range testCases { + for i := 0; i < 4; i++ { + want := color.RGBA{} + if c.out[i] == '*' { + want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + } + got := pixelsToColor(c.image.BasePixelsForTesting(), i, 0, w, h) + if !sameColors(got, want, 1) { + t.Errorf("%s[%d]: got %v, want %v", c.name, i, got, want) + } + } + } +} + +func TestWritePixels(t *testing.T) { + img := restorable.NewImage(17, 31, restorable.ImageTypeRegular) + defer img.Dispose() + + pix := make([]byte, 4*4*4) + for i := range pix { + pix[i] = 0xff + } + img.WritePixels(bytesToManagedBytes(pix), image.Rect(5, 7, 9, 11)) + // Check the region (5, 7)-(9, 11). Outside state is indeterminate. + pix = make([]byte, 4*4*4) + for i := range pix { + pix[i] = 0 + } + if err := img.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(5, 7, 9, 11)); err != nil { + t.Fatal(err) + } + for j := 7; j < 11; j++ { + for i := 5; i < 9; i++ { + idx := 4 * ((j-7)*4 + i - 5) + got := color.RGBA{R: pix[idx], G: pix[idx+1], B: pix[idx+2], A: pix[idx+3]} + want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + if got != want { + t.Errorf("(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := img.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(5, 7, 9, 11)); err != nil { + t.Fatal(err) + } + for j := 7; j < 11; j++ { + for i := 5; i < 9; i++ { + idx := 4 * ((j-7)*4 + i - 5) + got := color.RGBA{R: pix[idx], G: pix[idx+1], B: pix[idx+2], A: pix[idx+3]} + want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + if got != want { + t.Errorf("(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} + +func TestDrawTrianglesAndWritePixels(t *testing.T) { + base := image.NewRGBA(image.Rect(0, 0, 1, 1)) + base.Pix[0] = 0xff + base.Pix[1] = 0 + base.Pix[2] = 0 + base.Pix[3] = 0xff + img0 := newImageFromImage(base) + defer img0.Dispose() + img1 := restorable.NewImage(2, 1, restorable.ImageTypeRegular) + defer img1.Dispose() + + vs := quadVertices(1, 1, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, 2, 1) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.WritePixels(bytesToManagedBytes([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}), image.Rect(0, 0, 2, 1)) + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + var pix [4]byte + if err := img1.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix[:], image.Rect(0, 0, 1, 1)); err != nil { + t.Fatal(err) + } + got := color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]} + want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + if !sameColors(got, want, 1) { + t.Errorf("got: %v, want: %v", got, want) + } +} + +func TestDispose(t *testing.T) { + base0 := image.NewRGBA(image.Rect(0, 0, 1, 1)) + img0 := newImageFromImage(base0) + defer img0.Dispose() + + base1 := image.NewRGBA(image.Rect(0, 0, 1, 1)) + img1 := newImageFromImage(base1) + + base2 := image.NewRGBA(image.Rect(0, 0, 1, 1)) + base2.Pix[0] = 0xff + base2.Pix[1] = 0xff + base2.Pix[2] = 0xff + base2.Pix[3] = 0xff + img2 := newImageFromImage(base2) + defer img2.Dispose() + + is := graphics.QuadIndices() + dr := image.Rect(0, 0, 1, 1) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.Dispose() + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + var pix [4]byte + if err := img0.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix[:], image.Rect(0, 0, 1, 1)); err != nil { + t.Fatal(err) + } + got := color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]} + want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + if !sameColors(got, want, 1) { + t.Errorf("got: %v, want: %v", got, want) + } +} + +func TestWritePixelsPart(t *testing.T) { + pix := make([]uint8, 4*2*2) + for i := range pix { + pix[i] = 0xff + } + + img := restorable.NewImage(4, 4, restorable.ImageTypeRegular) + // This doesn't make the image stale. Its base pixels are available. + img.WritePixels(bytesToManagedBytes(pix), image.Rect(1, 1, 3, 3)) + + cases := []struct { + i int + j int + want color.RGBA + }{ + { + i: 0, + j: 0, + want: color.RGBA{}, + }, + { + i: 3, + j: 0, + want: color.RGBA{}, + }, + { + i: 0, + j: 1, + want: color.RGBA{}, + }, + { + i: 1, + j: 1, + want: color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, + }, + { + i: 3, + j: 1, + want: color.RGBA{}, + }, + { + i: 0, + j: 2, + want: color.RGBA{}, + }, + { + i: 2, + j: 2, + want: color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, + }, + { + i: 3, + j: 2, + want: color.RGBA{}, + }, + { + i: 0, + j: 3, + want: color.RGBA{}, + }, + { + i: 3, + j: 3, + want: color.RGBA{}, + }, + } + for _, c := range cases { + got := pixelsToColor(img.BasePixelsForTesting(), c.i, c.j, 4, 4) + want := c.want + if got != want { + t.Errorf("base pixel (%d, %d): got %v, want %v", c.i, c.j, got, want) + } + } +} + +func TestWritePixelsOnly(t *testing.T) { + const w, h = 128, 128 + img0 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + defer img0.Dispose() + img1 := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + defer img1.Dispose() + + for i := 0; i < w*h; i += 5 { + img0.WritePixels(bytesToManagedBytes([]byte{1, 2, 3, 4}), image.Rect(i%w, i/w, i%w+1, i/w+1)) + } + + vs := quadVertices(1, 1, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, 1, 1) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.WritePixels(bytesToManagedBytes([]byte{5, 6, 7, 8}), image.Rect(0, 0, 1, 1)) + + // BasePixelsForTesting is available without GPU accessing. + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + idx := j*w + i + var want color.RGBA + switch { + case idx == 0: + want = color.RGBA{R: 5, G: 6, B: 7, A: 8} + case idx%5 == 0: + want = color.RGBA{R: 1, G: 2, B: 3, A: 4} + } + got := pixelsToColor(img0.BasePixelsForTesting(), i, j, w, h) + if !sameColors(got, want, 0) { + t.Errorf("got %v, want %v", got, want) + } + } + } + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + want := color.RGBA{R: 1, G: 2, B: 3, A: 4} + got := pixelsToColor(img1.BasePixelsForTesting(), 0, 0, w, h) + if !sameColors(got, want, 0) { + t.Errorf("got %v, want %v", got, want) + } +} + +// TODO: How about volatile/screen images? + +// Issue #793 +func TestReadPixelsFromVolatileImage(t *testing.T) { + const w, h = 16, 16 + dst := restorable.NewImage(w, h, restorable.ImageTypeVolatile) + src := restorable.NewImage(w, h, restorable.ImageTypeRegular) + + // First, make sure that dst has pixels + dst.WritePixels(bytesToManagedBytes(make([]byte, 4*w*h)), image.Rect(0, 0, w, h)) + + // Second, draw src to dst. If the implementation is correct, dst becomes stale. + pix := make([]byte, 4*w*h) + for i := range pix { + pix[i] = 0xff + } + src.WritePixels(bytesToManagedBytes(pix), image.Rect(0, 0, w, h)) + vs := quadVertices(1, 1, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + + // Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being + // stale. + want := byte(0xff) + + var result [4]byte + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), result[:], image.Rect(0, 0, 1, 1)); err != nil { + t.Fatal(err) + } + got := result[0] + if got != want { + t.Errorf("got: %v, want: %v", got, want) + } +} + +func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) { + const w, h = 16, 16 + src := restorable.NewImage(w, h, restorable.ImageTypeRegular) + dst := restorable.NewImage(w, h, restorable.ImageTypeRegular) + + vs := quadVertices(w, h, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.WritePixels(bytesToManagedBytes(make([]byte, 4*w*h)), image.Rect(0, 0, w, h)) + // WritePixels for a whole image doesn't panic. +} + +func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) { + const w, h = 16, 16 + src := restorable.NewImage(w, h, restorable.ImageTypeRegular) + dst := restorable.NewImage(w, h, restorable.ImageTypeRegular) + + pix := make([]byte, 4*w*h) + for i := range pix { + pix[i] = 0xff + } + src.WritePixels(bytesToManagedBytes(pix), image.Rect(0, 0, w, h)) + + vs := quadVertices(w, h, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.WritePixels(bytesToManagedBytes(make([]byte, 4*2*2)), image.Rect(0, 0, 2, 2)) + // WritePixels for a part of image doesn't panic. + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + result := make([]byte, 4*w*h) + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), result, image.Rect(0, 0, w, h)); err != nil { + t.Fatal(err) + } + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := color.RGBA{R: result[4*(j*w+i)], G: result[4*(j*w+i)+1], B: result[4*(j*w+i)+2], A: result[4*(j*w+i)+3]} + var want color.RGBA + if i >= 2 || j >= 2 { + want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + } + if got != want { + t.Errorf("color at (%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} + +func TestExtend(t *testing.T) { + pixAt := func(i, j int) byte { + return byte(17*i + 13*j + 0x40) + } + + const w, h = 16, 16 + orig := restorable.NewImage(w, h, restorable.ImageTypeRegular) + pix := make([]byte, 4*w*h) + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + idx := j*w + i + v := pixAt(i, j) + pix[4*idx] = v + pix[4*idx+1] = v + pix[4*idx+2] = v + pix[4*idx+3] = v + } + } + + orig.WritePixels(bytesToManagedBytes(pix), image.Rect(0, 0, w, h)) + extended := orig.Extend(w*2, h*2) // After this, orig is already disposed. + + result := make([]byte, 4*(w*2)*(h*2)) + if err := extended.ReadPixels(ui.Get().GraphicsDriverForTesting(), result, image.Rect(0, 0, w*2, h*2)); err != nil { + t.Fatal(err) + } + for j := 0; j < h*2; j++ { + for i := 0; i < w*2; i++ { + got := result[4*(j*(w*2)+i)] + want := byte(0) + if i < w && j < h { + want = pixAt(i, j) + } + if got != want { + t.Errorf("extended.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} + +func TestDrawTrianglesAndExtend(t *testing.T) { + pixAt := func(i, j int) byte { + return byte(17*i + 13*j + 0x40) + } + + const w, h = 16, 16 + + src := restorable.NewImage(w, h, restorable.ImageTypeRegular) + pix := make([]byte, 4*w*h) + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + idx := j*w + i + v := pixAt(i, j) + pix[4*idx] = v + pix[4*idx+1] = v + pix[4*idx+2] = v + pix[4*idx+3] = v + } + } + src.WritePixels(bytesToManagedBytes(pix), image.Rect(0, 0, w, h)) + + orig := restorable.NewImage(w, h, restorable.ImageTypeRegular) + vs := quadVertices(w, h, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + orig.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + extended := orig.Extend(w*2, h*2) // After this, orig is already disposed. + + result := make([]byte, 4*(w*2)*(h*2)) + if err := extended.ReadPixels(ui.Get().GraphicsDriverForTesting(), result, image.Rect(0, 0, w*2, h*2)); err != nil { + t.Fatal(err) + } + for j := 0; j < h*2; j++ { + for i := 0; i < w*2; i++ { + got := result[4*(j*(w*2)+i)] + want := byte(0) + if i < w && j < h { + want = pixAt(i, j) + } + if got != want { + t.Errorf("extended.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} + +func TestClearPixels(t *testing.T) { + const w, h = 16, 16 + img := restorable.NewImage(w, h, restorable.ImageTypeRegular) + img.WritePixels(bytesToManagedBytes(make([]byte, 4*4*4)), image.Rect(0, 0, 4, 4)) + img.WritePixels(bytesToManagedBytes(make([]byte, 4*4*4)), image.Rect(4, 0, 8, 4)) + img.ClearPixels(image.Rect(0, 0, 4, 4)) + img.ClearPixels(image.Rect(4, 0, 8, 4)) + + // After clearing, the regions will be available again. + img.WritePixels(bytesToManagedBytes(make([]byte, 4*8*4)), image.Rect(0, 0, 8, 4)) +} + +func TestMutateSlices(t *testing.T) { + const w, h = 16, 16 + dst := restorable.NewImage(w, h, restorable.ImageTypeRegular) + src := restorable.NewImage(w, h, restorable.ImageTypeRegular) + pix := make([]byte, 4*w*h) + for i := 0; i < w*h; i++ { + pix[4*i] = byte(i) + pix[4*i+1] = byte(i) + pix[4*i+2] = byte(i) + pix[4*i+3] = 0xff + } + src.WritePixels(bytesToManagedBytes(pix), image.Rect(0, 0, w, h)) + + vs := quadVertices(w, h, 0, 0) + is := make([]uint32, len(graphics.QuadIndices())) + copy(is, graphics.QuadIndices()) + dr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + for i := range vs { + vs[i] = 0 + } + for i := range is { + is[i] = 0 + } + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + srcPix := make([]byte, 4*w*h) + if err := src.ReadPixels(ui.Get().GraphicsDriverForTesting(), srcPix, image.Rect(0, 0, w, h)); err != nil { + t.Fatal(err) + } + dstPix := make([]byte, 4*w*h) + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), dstPix, image.Rect(0, 0, w, h)); err != nil { + t.Fatal(err) + } + + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + idx := 4 * (j*w + i) + want := color.RGBA{R: srcPix[idx], G: srcPix[idx+1], B: srcPix[idx+2], A: srcPix[idx+3]} + got := color.RGBA{R: dstPix[idx], G: dstPix[idx+1], B: dstPix[idx+2], A: dstPix[idx+3]} + if !sameColors(got, want, 1) { + t.Errorf("(%d, %d): got %v, want %v", i, j, got, want) + } + } + } +} + +func TestOverlappedPixels(t *testing.T) { + dst := restorable.NewImage(3, 3, restorable.ImageTypeRegular) + + pix0 := make([]byte, 4*2*2) + for j := 0; j < 2; j++ { + for i := 0; i < 2; i++ { + idx := 4 * (j*2 + i) + pix0[idx] = 0xff + pix0[idx+1] = 0 + pix0[idx+2] = 0 + pix0[idx+3] = 0xff + } + } + dst.WritePixels(bytesToManagedBytes(pix0), image.Rect(0, 0, 2, 2)) + + pix1 := make([]byte, 4*2*2) + for j := 0; j < 2; j++ { + for i := 0; i < 2; i++ { + idx := 4 * (j*2 + i) + pix1[idx] = 0 + pix1[idx+1] = 0xff + pix1[idx+2] = 0 + pix1[idx+3] = 0xff + } + } + dst.WritePixels(bytesToManagedBytes(pix1), image.Rect(1, 1, 3, 3)) + + wantColors := []color.RGBA{ + {0xff, 0, 0, 0xff}, + {0xff, 0, 0, 0xff}, + {0, 0, 0, 0}, + + {0xff, 0, 0, 0xff}, + {0, 0xff, 0, 0xff}, + {0, 0xff, 0, 0xff}, + + {0, 0, 0, 0}, + {0, 0xff, 0, 0xff}, + {0, 0xff, 0, 0xff}, + } + + result := make([]byte, 4*3*3) + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil { + t.Fatal(err) + } + for j := 0; j < 3; j++ { + for i := 0; i < 3; i++ { + idx := 4 * (j*3 + i) + got := color.RGBA{R: result[idx], G: result[idx+1], B: result[idx+2], A: result[idx+3]} + want := wantColors[3*j+i] + if got != want { + t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want) + } + } + } + + dst.WritePixels(nil, image.Rect(1, 0, 3, 2)) + + wantColors = []color.RGBA{ + {0xff, 0, 0, 0xff}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + + {0xff, 0, 0, 0xff}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + + {0, 0, 0, 0}, + {0, 0xff, 0, 0xff}, + {0, 0xff, 0, 0xff}, + } + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil { + t.Fatal(err) + } + for j := 0; j < 3; j++ { + for i := 0; i < 3; i++ { + idx := 4 * (j*3 + i) + got := color.RGBA{R: result[idx], G: result[idx+1], B: result[idx+2], A: result[idx+3]} + want := wantColors[3*j+i] + if got != want { + t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want) + } + } + } + + pix2 := make([]byte, 4*2*2) + for j := 0; j < 2; j++ { + for i := 0; i < 2; i++ { + idx := 4 * (j*2 + i) + pix2[idx] = 0 + pix2[idx+1] = 0 + pix2[idx+2] = 0xff + pix2[idx+3] = 0xff + } + } + dst.WritePixels(bytesToManagedBytes(pix2), image.Rect(1, 1, 3, 3)) + + wantColors = []color.RGBA{ + {0xff, 0, 0, 0xff}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + + {0xff, 0, 0, 0xff}, + {0, 0, 0xff, 0xff}, + {0, 0, 0xff, 0xff}, + + {0, 0, 0, 0}, + {0, 0, 0xff, 0xff}, + {0, 0, 0xff, 0xff}, + } + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil { + t.Fatal(err) + } + for j := 0; j < 3; j++ { + for i := 0; i < 3; i++ { + idx := 4 * (j*3 + i) + got := color.RGBA{R: result[idx], G: result[idx+1], B: result[idx+2], A: result[idx+3]} + want := wantColors[3*j+i] + if got != want { + t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want) + } + } + } + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil { + t.Fatal(err) + } + for j := 0; j < 3; j++ { + for i := 0; i < 3; i++ { + idx := 4 * (j*3 + i) + got := color.RGBA{R: result[idx], G: result[idx+1], B: result[idx+2], A: result[idx+3]} + want := wantColors[3*j+i] + if got != want { + t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want) + } + } + } +} + +// Issue #2324 +func TestDrawTrianglesAndReadPixels(t *testing.T) { + const w, h = 1, 1 + src := restorable.NewImage(w, h, restorable.ImageTypeRegular) + dst := restorable.NewImage(w, h, restorable.ImageTypeRegular) + + src.WritePixels(bytesToManagedBytes([]byte{0x80, 0x80, 0x80, 0x80}), image.Rect(0, 0, 1, 1)) + + vs := quadVertices(w, h, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + + pix := make([]byte, 4*w*h) + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)); err != nil { + t.Fatal(err) + } + if got, want := (color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}); !sameColors(got, want, 1) { + t.Errorf("got: %v, want: %v", got, want) + } +} + +func TestWritePixelsAndDrawTriangles(t *testing.T) { + src := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + dst := restorable.NewImage(2, 1, restorable.ImageTypeRegular) + + src.WritePixels(bytesToManagedBytes([]byte{0x80, 0x80, 0x80, 0x80}), image.Rect(0, 0, 1, 1)) + + // Call WritePixels first. + dst.WritePixels(bytesToManagedBytes([]byte{0x40, 0x40, 0x40, 0x40}), image.Rect(0, 0, 1, 1)) + + // Call DrawTriangles at a different region second. + vs := quadVertices(1, 1, 1, 0) + is := graphics.QuadIndices() + dr := image.Rect(1, 0, 2, 1) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + + // Get the pixels. + pix := make([]byte, 4*2*1) + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, 2, 1)); err != nil { + t.Fatal(err) + } + if got, want := (color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}), (color.RGBA{R: 0x40, G: 0x40, B: 0x40, A: 0x40}); !sameColors(got, want, 1) { + t.Errorf("got: %v, want: %v", got, want) + } + if got, want := (color.RGBA{R: pix[4], G: pix[5], B: pix[6], A: pix[7]}), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}); !sameColors(got, want, 1) { + t.Errorf("got: %v, want: %v", got, want) + } +} diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go new file mode 100644 index 000000000..ea78f2b78 --- /dev/null +++ b/internal/restorable/shader_test.go @@ -0,0 +1,200 @@ +// Copyright 2018 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package restorable_test + +import ( + "image" + "image/color" + "testing" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" + etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" + "github.com/hajimehoshi/ebiten/v2/internal/ui" +) + +func clearImage(img *restorable.Image, w, h int) { + emptyImage := restorable.NewImage(3, 3, restorable.ImageTypeRegular) + defer emptyImage.Dispose() + + dx0 := float32(0) + dy0 := float32(0) + dx1 := float32(w) + dy1 := float32(h) + sx0 := float32(1) + sy0 := float32(1) + sx1 := float32(2) + sy1 := float32(2) + vs := []float32{ + dx0, dy0, sx0, sy0, 0, 0, 0, 0, + dx1, dy0, sx1, sy0, 0, 0, 0, 0, + dx0, dy1, sx0, sy1, 0, 0, 0, 0, + dx1, dy1, sx1, sy1, 0, 0, 0, 0, + } + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{emptyImage}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) +} + +func TestShader(t *testing.T) { + img := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + defer img.Dispose() + + s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) + dr := image.Rect(0, 0, 1, 1) + img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + want := color.RGBA{R: 0xff, A: 0xff} + got := pixelsToColor(img.BasePixelsForTesting(), 0, 0, 1, 1) + if !sameColors(got, want, 1) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestShaderChain(t *testing.T) { + const num = 10 + imgs := []*restorable.Image{} + for i := 0; i < num; i++ { + img := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + defer img.Dispose() + imgs = append(imgs, img) + } + + imgs[0].WritePixels(bytesToManagedBytes([]byte{0xff, 0, 0, 0xff}), image.Rect(0, 0, 1, 1)) + + s := restorable.NewShader(etesting.ShaderProgramImages(1)) + for i := 0; i < num-1; i++ { + dr := image.Rect(0, 0, 1, 1) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + } + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + for i, img := range imgs { + want := color.RGBA{R: 0xff, A: 0xff} + got := pixelsToColor(img.BasePixelsForTesting(), 0, 0, 1, 1) + if !sameColors(got, want, 1) { + t.Errorf("%d: got %v, want %v", i, got, want) + } + } +} + +func TestShaderMultipleSources(t *testing.T) { + var srcs [graphics.ShaderSrcImageCount]*restorable.Image + for i := range srcs { + srcs[i] = restorable.NewImage(1, 1, restorable.ImageTypeRegular) + } + srcs[0].WritePixels(bytesToManagedBytes([]byte{0x40, 0, 0, 0xff}), image.Rect(0, 0, 1, 1)) + srcs[1].WritePixels(bytesToManagedBytes([]byte{0, 0x80, 0, 0xff}), image.Rect(0, 0, 1, 1)) + srcs[2].WritePixels(bytesToManagedBytes([]byte{0, 0, 0xc0, 0xff}), image.Rect(0, 0, 1, 1)) + + dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + + s := restorable.NewShader(etesting.ShaderProgramImages(3)) + dr := image.Rect(0, 0, 1, 1) + dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + + // Clear one of the sources after DrawTriangles. dst should not be affected. + clearImage(srcs[0], 1, 1) + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + want := color.RGBA{R: 0x40, G: 0x80, B: 0xc0, A: 0xff} + got := pixelsToColor(dst.BasePixelsForTesting(), 0, 0, 1, 1) + if !sameColors(got, want, 1) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { + src := restorable.NewImage(3, 1, restorable.ImageTypeRegular) + src.WritePixels(bytesToManagedBytes([]byte{ + 0x40, 0, 0, 0xff, + 0, 0x80, 0, 0xff, + 0, 0, 0xc0, 0xff, + }), image.Rect(0, 0, 3, 1)) + srcs := [graphics.ShaderSrcImageCount]*restorable.Image{src, src, src} + + dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + + s := restorable.NewShader(etesting.ShaderProgramImages(3)) + dr := image.Rect(0, 0, 1, 1) + srcRegions := [graphics.ShaderSrcImageCount]image.Rectangle{ + image.Rect(0, 0, 1, 1), + image.Rect(1, 0, 2, 1), + image.Rect(2, 0, 3, 1), + } + dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, srcRegions, s, nil, graphicsdriver.FillRuleFillAll) + + // Clear one of the sources after DrawTriangles. dst should not be affected. + clearImage(srcs[0], 3, 1) + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + want := color.RGBA{R: 0x40, G: 0x80, B: 0xc0, A: 0xff} + got := pixelsToColor(dst.BasePixelsForTesting(), 0, 0, 1, 1) + if !sameColors(got, want, 1) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestShaderDispose(t *testing.T) { + img := restorable.NewImage(1, 1, restorable.ImageTypeRegular) + defer img.Dispose() + + s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) + dr := image.Rect(0, 0, 1, 1) + img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + + // Dispose the shader. This should invalidate all the images using this shader i.e., all the images become + // stale. + s.Dispose() + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + want := color.RGBA{R: 0xff, A: 0xff} + got := pixelsToColor(img.BasePixelsForTesting(), 0, 0, 1, 1) + if !sameColors(got, want, 1) { + t.Errorf("got %v, want %v", got, want) + } +} From 35f4884a74643c9e725e4ab61d3bd6f70574d4fd Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 16:30:20 +0900 Subject: [PATCH 126/188] ebiten: add RunGameOptions.StrictContextRestration This reverts commit a30f075896b76b2f6fb1536c490aead650e4157f. This change adds a new option StrictContextRestration to make the restoration optional. Closes #3083 --- .../_files/EbitenSurfaceView.java | 26 +++++-- .../_files/EbitenViewController.m | 8 ++- internal/restorable/export_test.go | 5 ++ internal/restorable/images.go | 67 ++++++++++++++----- internal/ui/ui.go | 19 +++--- internal/ui/ui_mobile.go | 30 ++++++--- mobile/ebitenmobileview/mobile.go | 13 ++-- run.go | 37 +++++++--- 8 files changed, 149 insertions(+), 56 deletions(-) diff --git a/cmd/ebitenmobile/_files/EbitenSurfaceView.java b/cmd/ebitenmobile/_files/EbitenSurfaceView.java index a4340a7f8..000e5f7db 100644 --- a/cmd/ebitenmobile/_files/EbitenSurfaceView.java +++ b/cmd/ebitenmobile/_files/EbitenSurfaceView.java @@ -25,10 +25,10 @@ import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import {{.JavaPkg}}.ebitenmobileview.Ebitenmobileview; -import {{.JavaPkg}}.ebitenmobileview.RenderRequester; +import {{.JavaPkg}}.ebitenmobileview.Renderer; import {{.JavaPkg}}.{{.PrefixLower}}.EbitenView; -class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { +class EbitenSurfaceView extends GLSurfaceView implements Renderer { private class EbitenRenderer implements GLSurfaceView.Renderer { @@ -63,6 +63,10 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { onceSurfaceCreated_ = true; return; } + if (hasStrictContextRestoration()) { + Ebitenmobileview.onContextLost(); + return; + } contextLost_ = true; new Handler(Looper.getMainLooper()).post(new Runnable() { @Override @@ -77,6 +81,8 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { } } + private boolean strictContextRestoration_ = false; + public EbitenSurfaceView(Context context) { super(context); initialize(); @@ -90,9 +96,11 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { private void initialize() { setEGLContextClientVersion(3); setEGLConfigChooser(8, 8, 8, 8, 0, 0); + // setRenderer must be called before setRenderRequester. + // Or, the application crashes. setRenderer(new EbitenRenderer()); - setPreserveEGLContextOnPause(true); - Ebitenmobileview.setRenderRequester(this); + + Ebitenmobileview.setRenderer(this); } private void onErrorOnGameUpdate(Exception e) { @@ -114,6 +122,16 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { } } + @Override + public synchronized void setStrictContextRestoration(boolean strictContextRestoration) { + strictContextRestoration_ = strictContextRestoration; + setPreserveEGLContextOnPause(!strictContextRestoration); + } + + private synchronized boolean hasStrictContextRestoration() { + return strictContextRestoration_; + } + @Override public synchronized void requestRenderIfNeeded() { if (getRenderMode() == RENDERMODE_WHEN_DIRTY) { diff --git a/cmd/ebitenmobile/_files/EbitenViewController.m b/cmd/ebitenmobile/_files/EbitenViewController.m index 33d7ae18d..a5f5cf90c 100644 --- a/cmd/ebitenmobile/_files/EbitenViewController.m +++ b/cmd/ebitenmobile/_files/EbitenViewController.m @@ -20,7 +20,7 @@ #import "Ebitenmobileview.objc.h" -@interface {{.PrefixUpper}}EbitenViewController : UIViewController +@interface {{.PrefixUpper}}EbitenViewController : UIViewController @end @implementation {{.PrefixUpper}}EbitenViewController { @@ -149,7 +149,7 @@ displayLink_ = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawFrame)]; [displayLink_ addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - EbitenmobileviewSetRenderRequester(self); + EbitenmobileviewSetRenderer(self); // Run the loop. This will never return. [[NSRunLoop currentRunLoop] run]; @@ -364,6 +364,10 @@ } } +- (void)setStrictContextRestoration:(BOOL)strictContextRestoration { + // Do nothing. +} + - (void)setExplicitRenderingMode:(BOOL)explicitRendering { @synchronized(self) { explicitRendering_ = explicitRendering; diff --git a/internal/restorable/export_test.go b/internal/restorable/export_test.go index 10071b879..655b7ed0c 100644 --- a/internal/restorable/export_test.go +++ b/internal/restorable/export_test.go @@ -20,6 +20,11 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) +// EnableRestoringForTesting forces to enable restoring for testing. +func EnableRestoringForTesting() { + forceRestoring = true +} + func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { return resolveStaleImages(graphicsDriver, false) } diff --git a/internal/restorable/images.go b/internal/restorable/images.go index c1a34c792..5bfdf8282 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -15,17 +15,42 @@ package restorable import ( + "image" + "runtime" + "sync" + "sync/atomic" + "github.com/hajimehoshi/ebiten/v2/internal/debug" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -// forceRestoring reports whether restoring forcely happens or not. +// forceRestoring reports whether restoring forcibly happens or not. +// This is used only for testing. var forceRestoring = false +// disabled indicates that restoring is disabled or not. +// Restoring is enabled by default for some platforms like Android for safety. +// Before SetGame, it is not possible to determine whether restoring is needed or not. +var disabled atomic.Bool + +var disabledOnce sync.Once + +// Disable disables restoring. +func Disable() { + disabled.Store(true) +} + // needsRestoring reports whether restoring process works or not. func needsRestoring() bool { - return forceRestoring + if forceRestoring { + return true + } + // TODO: If Vulkan is introduced, restoring might not be needed. + if runtime.GOOS == "android" { + return !disabled.Load() + } + return false } // AlwaysReadPixelsFromGPU reports whether ReadPixels always reads pixels from GPU or not. @@ -33,16 +58,12 @@ func AlwaysReadPixelsFromGPU() bool { return !needsRestoring() } -// EnableRestoringForTesting forces to enable restoring for testing. -func EnableRestoringForTesting() { - forceRestoring = true -} - // images is a set of Image objects. type images struct { - images map[*Image]struct{} - shaders map[*Shader]struct{} - lastTarget *Image + images map[*Image]struct{} + shaders map[*Shader]struct{} + lastTarget *Image + contextLost atomic.Bool } // theImages represents the images for the current process. @@ -66,6 +87,15 @@ func SwapBuffers(graphicsDriver graphicsdriver.Graphics) error { // resolveStaleImages flushes the queued draw commands and resolves all stale images. // If endFrame is true, the current screen might be used to present when flushing the commands. func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool) error { + // When Disable is called, all the images data should be evicted once. + if disabled.Load() { + disabledOnce.Do(func() { + for img := range theImages.images { + img.makeStale(image.Rectangle{}) + } + }) + } + if err := graphicscommand.FlushCommands(graphicsDriver, endFrame); err != nil { return err } @@ -83,14 +113,8 @@ func RestoreIfNeeded(graphicsDriver graphicsdriver.Graphics) error { return nil } - if !forceRestoring { - var r bool - - // TODO: Detect context lost explicitly on Android. - - if !r { - return nil - } + if !forceRestoring && !theImages.contextLost.Load() { + return nil } if err := graphicscommand.ResetGraphicsDriverState(graphicsDriver); err != nil { @@ -243,6 +267,8 @@ func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error { } } + i.contextLost.Store(false) + return nil } @@ -258,3 +284,8 @@ func InitializeGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) error func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int { return graphicscommand.MaxImageSize(graphicsDriver) } + +// OnContextLost is called when the context lost is detected in an explicit way. +func OnContextLost() { + theImages.contextLost.Store(true) +} diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 7d1436a7a..ce7e48ee2 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -172,15 +172,16 @@ func (u *UserInterface) dumpImages(dir string) (string, error) { } type RunOptions struct { - GraphicsLibrary GraphicsLibrary - InitUnfocused bool - ScreenTransparent bool - SkipTaskbar bool - SingleThread bool - DisableHiDPI bool - ColorSpace graphicsdriver.ColorSpace - X11ClassName string - X11InstanceName string + GraphicsLibrary GraphicsLibrary + InitUnfocused bool + ScreenTransparent bool + SkipTaskbar bool + SingleThread bool + DisableHiDPI bool + ColorSpace graphicsdriver.ColorSpace + X11ClassName string + X11InstanceName string + StrictContextRestoration bool } // InitialWindowPosition returns the position for centering the given second width/height pair within the first width/height pair. diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index c1dd426b8..be0ed62bb 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -28,6 +28,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/hook" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" ) var ( @@ -97,8 +98,11 @@ type userInterfaceImpl struct { inputState InputState touches []TouchForInput - fpsMode atomic.Int32 - renderRequester RenderRequester + fpsMode atomic.Int32 + renderer Renderer + + strictContextRestoration bool + strictContextRestorationOnce sync.Once m sync.RWMutex } @@ -152,6 +156,11 @@ func (u *UserInterface) runMobile(game Game, options *RunOptions) (err error) { u.graphicsDriver = g u.setGraphicsLibrary(lib) close(u.graphicsLibraryInitCh) + u.strictContextRestoration = options.StrictContextRestoration + if !u.strictContextRestoration { + restorable.Disable() + } + u.renderer.SetStrictContextRestoration(u.strictContextRestoration) for { if err := u.update(); err != nil { @@ -239,10 +248,10 @@ func (u *UserInterface) SetFPSMode(mode FPSModeType) { } func (u *UserInterface) updateExplicitRenderingModeIfNeeded(fpsMode FPSModeType) { - if u.renderRequester == nil { + if u.renderer == nil { return } - u.renderRequester.SetExplicitRenderingMode(fpsMode == FPSModeVsyncOffMinimum) + u.renderer.SetExplicitRenderingMode(fpsMode == FPSModeVsyncOffMinimum) } func (u *UserInterface) readInputState(inputState *InputState) { @@ -297,23 +306,24 @@ func (u *UserInterface) Monitor() *Monitor { func (u *UserInterface) UpdateInput(keys map[Key]struct{}, runes []rune, touches []TouchForInput) { u.updateInputStateFromOutside(keys, runes, touches) if FPSModeType(u.fpsMode.Load()) == FPSModeVsyncOffMinimum { - u.renderRequester.RequestRenderIfNeeded() + u.renderer.RequestRenderIfNeeded() } } -type RenderRequester interface { +type Renderer interface { SetExplicitRenderingMode(explicitRendering bool) + SetStrictContextRestoration(strictContextRestoration bool) RequestRenderIfNeeded() } -func (u *UserInterface) SetRenderRequester(renderRequester RenderRequester) { - u.renderRequester = renderRequester +func (u *UserInterface) SetRenderer(renderer Renderer) { + u.renderer = renderer u.updateExplicitRenderingModeIfNeeded(FPSModeType(u.fpsMode.Load())) } func (u *UserInterface) ScheduleFrame() { - if u.renderRequester != nil && FPSModeType(u.fpsMode.Load()) == FPSModeVsyncOffMinimum { - u.renderRequester.RequestRenderIfNeeded() + if u.renderer != nil && FPSModeType(u.fpsMode.Load()) == FPSModeVsyncOffMinimum { + u.renderer.RequestRenderIfNeeded() } } diff --git a/mobile/ebitenmobileview/mobile.go b/mobile/ebitenmobileview/mobile.go index 4f00062fe..9c3ad5ff5 100644 --- a/mobile/ebitenmobileview/mobile.go +++ b/mobile/ebitenmobileview/mobile.go @@ -30,6 +30,7 @@ import ( "sync" "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -112,16 +113,20 @@ func Resume() error { return ui.Get().SetForeground(true) } +func OnContextLost() { + restorable.OnContextLost() +} + func DeviceScale() float64 { return ui.Get().Monitor().DeviceScaleFactor() } -type RenderRequester interface { - ui.RenderRequester +type Renderer interface { + ui.Renderer } -func SetRenderRequester(renderRequester RenderRequester) { - ui.Get().SetRenderRequester(renderRequester) +func SetRenderer(renderer Renderer) { + ui.Get().SetRenderer(renderer) } func SetSetGameNotifier(setGameNotifier SetGameNotifier) { diff --git a/run.go b/run.go index d1f5e3f1a..bb97c5c7f 100644 --- a/run.go +++ b/run.go @@ -297,6 +297,24 @@ type RunGameOptions struct { // X11InstanceName is an instance name in the ICCCM WM_CLASS window property. X11InstanceName string + + // StrictContextRestration indicates whether the context lost should be restored strictly by Ebitengine or not. + // + // StrictContextRestration is available only on Android. Otherwise, StrictContextRestration is ignored. + // + // When StrictContextRestration is false, Ebitengine tries to rely on the OS to restore the context. + // In Android, Ebitengien uses `GLSurfaceView`'s `setPreserveEGLContextOnPause(true)`. + // This works in most cases, but it is still possible that the context is lost in some minor cases. + // With StrictContextRestration false, the activity's launch mode should be singleInstance, + // or the activity no longer works correctly after the context is lost. + // + // When StrictContextRestration is true, Ebitengine tries to restore the context more strictly. + // This is useful when you want to restore the context in any case. + // However, this might cause a performance issue since Ebitengine tries to keep all the information + // to restore the context. + // + // The default (zero) value is false. + StrictContextRestration bool } // RunGameWithOptions starts the main loop and runs the game with the specified options. @@ -716,15 +734,16 @@ func toUIRunOptions(options *RunGameOptions) *ui.RunOptions { options.X11InstanceName = defaultX11InstanceName } return &ui.RunOptions{ - GraphicsLibrary: ui.GraphicsLibrary(options.GraphicsLibrary), - InitUnfocused: options.InitUnfocused, - ScreenTransparent: options.ScreenTransparent, - SkipTaskbar: options.SkipTaskbar, - SingleThread: options.SingleThread, - DisableHiDPI: options.DisableHiDPI, - ColorSpace: graphicsdriver.ColorSpace(options.ColorSpace), - X11ClassName: options.X11ClassName, - X11InstanceName: options.X11InstanceName, + GraphicsLibrary: ui.GraphicsLibrary(options.GraphicsLibrary), + InitUnfocused: options.InitUnfocused, + ScreenTransparent: options.ScreenTransparent, + SkipTaskbar: options.SkipTaskbar, + SingleThread: options.SingleThread, + DisableHiDPI: options.DisableHiDPI, + ColorSpace: graphicsdriver.ColorSpace(options.ColorSpace), + X11ClassName: options.X11ClassName, + X11InstanceName: options.X11InstanceName, + StrictContextRestoration: options.StrictContextRestration, } } From d50a438c07935fbf98f0e2c4ccc61297764bb78e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 6 Sep 2024 21:15:00 +0900 Subject: [PATCH 127/188] internal/restorable: optimize removeDuplicatedRegions Updates #2375 Updates #2626 Updates #3083 --- internal/restorable/export_test.go | 6 -- internal/restorable/image.go | 82 ++++++++------ internal/restorable/rect_test.go | 165 ----------------------------- 3 files changed, 49 insertions(+), 204 deletions(-) delete mode 100644 internal/restorable/rect_test.go diff --git a/internal/restorable/export_test.go b/internal/restorable/export_test.go index 655b7ed0c..2fcf1f666 100644 --- a/internal/restorable/export_test.go +++ b/internal/restorable/export_test.go @@ -15,8 +15,6 @@ package restorable import ( - "image" - "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) @@ -28,7 +26,3 @@ func EnableRestoringForTesting() { func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { return resolveStaleImages(graphicsDriver, false) } - -func AppendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) { - appendRegionRemovingDuplicates(regions, region) -} diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 6a4015df3..086a19013 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -17,6 +17,7 @@ package restorable import ( "fmt" "image" + "sort" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" @@ -215,20 +216,16 @@ func (i *Image) makeStale(rect image.Rectangle) { return } - var addedRegions []image.Rectangle - i.appendRegionsForDrawTriangles(&addedRegions) + origSize := len(i.staleRegions) + i.staleRegions = i.appendRegionsForDrawTriangles(i.staleRegions) if !rect.Empty() { - appendRegionRemovingDuplicates(&addedRegions, rect) - } - - for _, rect := range addedRegions { - appendRegionRemovingDuplicates(&i.staleRegions, rect) + i.staleRegions = append(i.staleRegions, rect) } i.clearDrawTrianglesHistory() // Clear pixels to save memory. - for _, r := range addedRegions { + for _, r := range i.staleRegions[origSize:] { i.basePixels.Clear(r) } @@ -450,13 +447,16 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error if i.stale { rs = i.staleRegions } else { - i.appendRegionsForDrawTriangles(&i.regionsCache) + i.regionsCache = i.appendRegionsForDrawTriangles(i.regionsCache) defer func() { i.regionsCache = i.regionsCache[:0] }() rs = i.regionsCache } + // Remove duplications. Is this heavy? + rs = rs[:removeDuplicatedRegions(rs)] + args := make([]graphicsdriver.PixelsArgs, 0, len(rs)) for _, r := range rs { if r.Empty() { @@ -607,7 +607,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { // In order to clear the draw-triangles history, read pixels from GPU. if len(i.drawTrianglesHistory) > 0 { - i.appendRegionsForDrawTriangles(&i.regionsCache) + i.regionsCache = i.appendRegionsForDrawTriangles(i.regionsCache) defer func() { i.regionsCache = i.regionsCache[:0] }() @@ -683,38 +683,54 @@ func (i *Image) InternalSize() (int, int) { return i.image.InternalSize() } -func (i *Image) appendRegionsForDrawTriangles(regions *[]image.Rectangle) { +func (i *Image) appendRegionsForDrawTriangles(regions []image.Rectangle) []image.Rectangle { for _, d := range i.drawTrianglesHistory { if d.dstRegion.Empty() { continue } - appendRegionRemovingDuplicates(regions, d.dstRegion) + regions = append(regions, d.dstRegion) } + return regions } -// appendRegionRemovingDuplicates adds a region to a given list of regions, -// but removes any duplicate between the newly added region and any existing regions. -// -// In case the newly added region is fully contained in any pre-existing region, this function does nothing. -// Otherwise, any pre-existing regions that are fully contained in the newly added region are removed. -// -// This is done to avoid unnecessary reading pixels from GPU. -func appendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) { - for _, r := range *regions { - if region.In(r) { - // The newly added rectangle is fully contained in one of the input regions. - // Nothing to add. - return - } - } - // Separate loop, as regions must not get mutated before above return. - n := 0 - for _, r := range *regions { - if r.In(region) { +// removeDuplicatedRegions removes duplicated regions and returns a shrunk slice. +// If a region covers preceding regions, the covered regions are removed. +func removeDuplicatedRegions(regions []image.Rectangle) int { + // Sweep and prune algorithm + + sort.Slice(regions, func(i, j int) bool { + return regions[i].Min.X < regions[j].Min.X + }) + + for i, r := range regions { + if r.Empty() { continue } - (*regions)[n] = r + for j := i + 1; j < len(regions); j++ { + rr := regions[j] + if rr.Empty() { + continue + } + if r.Max.X <= rr.Min.X { + break + } + if rr.In(r) { + regions[j] = image.Rectangle{} + } else if r.In(rr) { + regions[i] = image.Rectangle{} + break + } + } + } + + var n int + for _, r := range regions { + if r.Empty() { + continue + } + regions[n] = r n++ } - *regions = append((*regions)[:n], region) + + return n } diff --git a/internal/restorable/rect_test.go b/internal/restorable/rect_test.go deleted file mode 100644 index 27f094270..000000000 --- a/internal/restorable/rect_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2023 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package restorable_test - -import ( - "image" - "testing" - - "github.com/hajimehoshi/ebiten/v2/internal/restorable" -) - -func areEqualRectangles(a, b []image.Rectangle) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} - -func TestRemoveDuplicatedRegions(t *testing.T) { - cases := []struct { - Regions []image.Rectangle - NewRegions []image.Rectangle - Expected []image.Rectangle - }{ - { - Regions: nil, - NewRegions: nil, - Expected: nil, - }, - { - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 2, 2), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 2, 2), - }, - }, - { - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 1, 1), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 2, 2), - }, - }, - { - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 1, 1), - image.Rect(0, 0, 2, 2), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 2, 2), - }, - }, - { - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 1, 3), - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 1, 3), - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - }, - }, - { - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 1, 3), - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - image.Rect(0, 0, 4, 4), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 4, 4), - }, - }, - { - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 1, 3), - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - image.Rect(0, 0, 4, 4), - image.Rect(1, 1, 2, 2), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 4, 4), - }, - }, - { - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 1, 3), - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - image.Rect(0, 0, 4, 4), - image.Rect(0, 0, 5, 5), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 5, 5), - }, - }, - { - Regions: []image.Rectangle{ - image.Rect(0, 0, 1, 3), - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - image.Rect(0, 0, 4, 4), - }, - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 5, 5), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 5, 5), - }, - }, - { - Regions: []image.Rectangle{ - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - image.Rect(0, 0, 4, 4), - image.Rect(0, 0, 5, 5), - }, - NewRegions: []image.Rectangle{ - image.Rect(0, 0, 1, 3), - }, - Expected: []image.Rectangle{ - image.Rect(0, 0, 2, 2), - image.Rect(0, 0, 3, 1), - image.Rect(0, 0, 4, 4), - image.Rect(0, 0, 5, 5), - }, - }, - } - - for _, c := range cases { - got := c.Regions - for _, r := range c.NewRegions { - restorable.AppendRegionRemovingDuplicates(&got, r) - } - want := c.Expected - if !areEqualRectangles(got, want) { - t.Errorf("restorable.RemoveDuplicatedRegions(%#v): got: %#v, want: %#v", c.NewRegions, got, want) - } - } -} From 1b2a2afbfb3b05e97259f621741ca8e63ea5c8ae Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 7 Sep 2024 19:07:13 +0900 Subject: [PATCH 128/188] internal/restorable: rename restoring -> restoration --- internal/restorable/doc.go | 4 +-- internal/restorable/export_test.go | 6 ++--- internal/restorable/image.go | 18 +++++++------- internal/restorable/images.go | 40 +++++++++++++++--------------- internal/restorable/images_test.go | 2 +- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/internal/restorable/doc.go b/internal/restorable/doc.go index fb51cae66..f1873b075 100644 --- a/internal/restorable/doc.go +++ b/internal/restorable/doc.go @@ -16,7 +16,7 @@ // and restores its pixel data from the commands when context lost happens. // // When a function like DrawImage or Fill is called, an Image tries to record -// the information for restoring. +// the information for restoration. // // * Context lost // @@ -26,7 +26,7 @@ // might cause context lost on mobiles. // As Ebitengine's image data is on GPU memory, the game can't continue when context lost happens // without restoring image information. -// The package restorable is the package to record information for such restoring. +// The package restorable is the package to record information for such restoration. // // * DrawImage // diff --git a/internal/restorable/export_test.go b/internal/restorable/export_test.go index 2fcf1f666..324cf8b55 100644 --- a/internal/restorable/export_test.go +++ b/internal/restorable/export_test.go @@ -18,9 +18,9 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -// EnableRestoringForTesting forces to enable restoring for testing. -func EnableRestoringForTesting() { - forceRestoring = true +// EnableRestorationForTesting forces to enable restoration for testing. +func EnableRestorationForTesting() { + forceRestoration = true } func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 086a19013..82cb79aa3 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -28,7 +28,7 @@ type Pixels struct { pixelsRecords *pixelsRecords } -// Apply applies the Pixels state to the given image especially for restoring. +// Apply applies the Pixels state to the given image especially for restoration. func (p *Pixels) Apply(img *graphicscommand.Image) { // Pixels doesn't clear the image. This is a caller's responsibility. @@ -230,7 +230,7 @@ func (i *Image) makeStale(rect image.Rectangle) { } // Don't have to call makeStale recursively here. - // Restoring is done after topological sorting is done. + // Restoration is done after topological sorting is done. // If an image depends on another stale image, this means that // the former image can be restored from the latest state of the latter image. } @@ -240,7 +240,7 @@ func (i *Image) ClearPixels(region image.Rectangle) { i.WritePixels(nil, region) } -func (i *Image) needsRestoring() bool { +func (i *Image) needsRestoration() bool { return i.imageType == ImageTypeRegular } @@ -267,7 +267,7 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl } // Even if the image is already stale, call makeStale to extend the stale region. - if !needsRestoring() || !i.needsRestoring() || i.stale { + if !needsRestoration() || !i.needsRestoration() || i.stale { i.makeStale(region) return } @@ -330,7 +330,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice } // Even if the image is already stale, call makeStale to extend the stale region. - if srcstale || !needsRestoring() || !i.needsRestoring() || i.stale { + if srcstale || !needsRestoration() || !i.needsRestoration() || i.stale { i.makeStale(dstRegion) } else { i.appendDrawTrianglesHistory(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule) @@ -348,8 +348,8 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice // appendDrawTrianglesHistory appends a draw-image history item to the image. func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { - if i.stale || !i.needsRestoring() { - panic("restorable: an image must not be stale or need restoring at appendDrawTrianglesHistory") + if i.stale || !i.needsRestoration() { + panic("restorable: an image must not be stale or need restoration at appendDrawTrianglesHistory") } if AlwaysReadPixelsFromGPU() { panic("restorable: appendDrawTrianglesHistory must not be called when AlwaysReadPixelsFromGPU() returns true") @@ -498,10 +498,10 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error // resolveStale resolves the image's 'stale' state. func (i *Image) resolveStale(graphicsDriver graphicsdriver.Graphics) error { - if !needsRestoring() { + if !needsRestoration() { return nil } - if !i.needsRestoring() { + if !i.needsRestoration() { return nil } if !i.stale { diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 5bfdf8282..90b856023 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -25,28 +25,28 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -// forceRestoring reports whether restoring forcibly happens or not. +// forceRestoration reports whether restoration forcibly happens or not. // This is used only for testing. -var forceRestoring = false +var forceRestoration = false -// disabled indicates that restoring is disabled or not. -// Restoring is enabled by default for some platforms like Android for safety. -// Before SetGame, it is not possible to determine whether restoring is needed or not. +// disabled indicates that restoration is disabled or not. +// Restoration is enabled by default for some platforms like Android for safety. +// Before SetGame, it is not possible to determine whether restoration is needed or not. var disabled atomic.Bool var disabledOnce sync.Once -// Disable disables restoring. +// Disable disables restoration. func Disable() { disabled.Store(true) } -// needsRestoring reports whether restoring process works or not. -func needsRestoring() bool { - if forceRestoring { +// needsRestoration reports whether restoration process works or not. +func needsRestoration() bool { + if forceRestoration { return true } - // TODO: If Vulkan is introduced, restoring might not be needed. + // TODO: If Vulkan is introduced, restoration might not be needed. if runtime.GOOS == "android" { return !disabled.Load() } @@ -55,7 +55,7 @@ func needsRestoring() bool { // AlwaysReadPixelsFromGPU reports whether ReadPixels always reads pixels from GPU or not. func AlwaysReadPixelsFromGPU() bool { - return !needsRestoring() + return !needsRestoration() } // images is a set of Image objects. @@ -99,7 +99,7 @@ func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool) e if err := graphicscommand.FlushCommands(graphicsDriver, endFrame); err != nil { return err } - if !needsRestoring() { + if !needsRestoration() { return nil } return theImages.resolveStaleImages(graphicsDriver) @@ -107,13 +107,13 @@ func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool) e // RestoreIfNeeded restores the images. // -// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. +// Restoration means to make all *graphicscommand.Image objects have their textures and framebuffers. func RestoreIfNeeded(graphicsDriver graphicsdriver.Graphics) error { - if !needsRestoring() { + if !needsRestoration() { return nil } - if !forceRestoring && !theImages.contextLost.Load() { + if !forceRestoration && !theImages.contextLost.Load() { return nil } @@ -195,13 +195,13 @@ func (i *images) makeStaleIfDependingOnShader(shader *Shader) { // restore restores the images. // -// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. +// Restoration means to make all *graphicscommand.Image objects have their textures and framebuffers. func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error { - if !needsRestoring() { - panic("restorable: restore cannot be called when restoring is disabled") + if !needsRestoration() { + panic("restorable: restore cannot be called when restoration is disabled") } - // Dispose all the shaders ahead of restoring. A current shader ID and a new shader ID can be duplicated. + // Dispose all the shaders ahead of restoration. A current shader ID and a new shader ID can be duplicated. for s := range i.shaders { s.shader.Dispose() s.shader = nil @@ -210,7 +210,7 @@ func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error { s.restore() } - // Dispose all the images ahead of restoring. A current texture ID and a new texture ID can be duplicated. + // Dispose all the images ahead of restoration. A current texture ID and a new texture ID can be duplicated. // TODO: Write a test to confirm that ID duplication never happens. for i := range i.images { i.image.Dispose() diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index 1be26acf8..39a099b1a 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -27,7 +27,7 @@ import ( ) func TestMain(m *testing.M) { - restorable.EnableRestoringForTesting() + restorable.EnableRestorationForTesting() etesting.MainWithRunLoop(m) } From 393437b8be5232a39ad0e57e534155ada802709a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 7 Sep 2024 21:29:50 +0900 Subject: [PATCH 129/188] internal/graphicscommand: imporve blend log --- internal/graphicscommand/command.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index d69d7ed8a..01ec96871 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -72,14 +72,23 @@ type drawTrianglesCommand struct { } func (c *drawTrianglesCommand) String() string { - // TODO: Improve readability - blend := fmt.Sprintf("{src-color: %d, src-alpha: %d, dst-color: %d, dst-alpha: %d, op-color: %d, op-alpha: %d}", - c.blend.BlendFactorSourceRGB, - c.blend.BlendFactorSourceAlpha, - c.blend.BlendFactorDestinationRGB, - c.blend.BlendFactorDestinationAlpha, - c.blend.BlendOperationRGB, - c.blend.BlendOperationAlpha) + var blend string + switch c.blend { + case graphicsdriver.BlendSourceOver: + blend = "(source-over)" + case graphicsdriver.BlendClear: + blend = "(clear)" + case graphicsdriver.BlendCopy: + blend = "(copy)" + default: + blend = fmt.Sprintf("{src-rgb: %d, src-alpha: %d, dst-rgb: %d, dst-alpha: %d, op-rgb: %d, op-alpha: %d}", + c.blend.BlendFactorSourceRGB, + c.blend.BlendFactorSourceAlpha, + c.blend.BlendFactorDestinationRGB, + c.blend.BlendFactorDestinationAlpha, + c.blend.BlendOperationRGB, + c.blend.BlendOperationAlpha) + } dst := fmt.Sprintf("%d", c.dst.id) if c.dst.screen { From 46cf09197bea2f4e2f33031e659a19dc8c94ad53 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 7 Sep 2024 21:58:42 +0900 Subject: [PATCH 130/188] internal/graphicscommand: enable to show shader names --- gameforui.go | 2 +- internal/atlas/shader.go | 8 +++++--- internal/atlas/shader_test.go | 6 +++--- internal/graphicscommand/command.go | 7 ++++++- internal/graphicscommand/image_test.go | 4 ++-- internal/graphicscommand/shader.go | 8 +++++--- internal/restorable/shader.go | 14 ++++++++------ internal/restorable/shader_test.go | 10 +++++----- internal/ui/shader.go | 4 ++-- shader.go | 24 ++++++++++++++++++++++-- 10 files changed, 59 insertions(+), 28 deletions(-) diff --git a/gameforui.go b/gameforui.go index d86e4fda8..6fe678341 100644 --- a/gameforui.go +++ b/gameforui.go @@ -46,7 +46,7 @@ func newGameForUI(game Game, transparent bool) *gameForUI { transparent: transparent, } - s, err := NewShader(builtinshader.ScreenShaderSource) + s, err := newShader(builtinshader.ScreenShaderSource, "screen") if err != nil { panic(fmt.Sprintf("ebiten: compiling the screen shader failed: %v", err)) } diff --git a/internal/atlas/shader.go b/internal/atlas/shader.go index f2ff046ed..86f9670f3 100644 --- a/internal/atlas/shader.go +++ b/internal/atlas/shader.go @@ -24,12 +24,14 @@ import ( type Shader struct { ir *shaderir.Program shader *restorable.Shader + name string } -func NewShader(ir *shaderir.Program) *Shader { +func NewShader(ir *shaderir.Program, name string) *Shader { // A shader is initialized lazily, and the lock is not needed. return &Shader{ - ir: ir, + ir: ir, + name: name, } } @@ -45,7 +47,7 @@ func (s *Shader) ensureShader() *restorable.Shader { if s.shader != nil { return s.shader } - s.shader = restorable.NewShader(s.ir) + s.shader = restorable.NewShader(s.ir, s.name) runtime.SetFinalizer(s, (*Shader).finalize) return s.shader } diff --git a/internal/atlas/shader_test.go b/internal/atlas/shader_test.go index 4a0a701bc..5955806b8 100644 --- a/internal/atlas/shader_test.go +++ b/internal/atlas/shader_test.go @@ -36,12 +36,12 @@ func TestShaderFillTwice(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) g := ui.Get().GraphicsDriverForTesting() - s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff)) + s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff), "") dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillRuleFillAll) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) - s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff)) + s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff), "") dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) @@ -89,7 +89,7 @@ func TestImageDrawTwice(t *testing.T) { } func TestGCShader(t *testing.T) { - s := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff)) + s := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff), "") // Use the shader to initialize it. const w, h = 1, 1 diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 01ec96871..536a02c52 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -107,7 +107,12 @@ func (c *drawTrianglesCommand) String() string { } } - return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, fill rule: %s, shader id: %d", dst, strings.Join(srcstrs[:], ", "), len(c.dstRegions), c.numIndices(), blend, c.fillRule, c.shader.id) + shader := fmt.Sprintf("%d", c.shader.id) + if c.shader.name != "" { + shader += " (" + c.shader.name + ")" + } + + return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, fill rule: %s, shader: %s", dst, strings.Join(srcstrs[:], ", "), len(c.dstRegions), c.numIndices(), blend, c.fillRule, shader) } // Exec executes the drawTrianglesCommand. diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index 946d9c736..fb3151dea 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -35,7 +35,7 @@ func init() { if err != nil { panic(fmt.Sprintf("graphicscommand: compiling the nearest shader failed: %v", err)) } - nearestFilterShader = graphicscommand.NewShader(ir) + nearestFilterShader = graphicscommand.NewShader(ir, "") } func TestMain(m *testing.M) { @@ -109,7 +109,7 @@ func TestShader(t *testing.T) { dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) g := ui.Get().GraphicsDriverForTesting() - s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) + s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff), "") dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) diff --git a/internal/graphicscommand/shader.go b/internal/graphicscommand/shader.go index 989cb9319..d55c82eb2 100644 --- a/internal/graphicscommand/shader.go +++ b/internal/graphicscommand/shader.go @@ -31,12 +31,14 @@ type Shader struct { shader graphicsdriver.Shader ir *shaderir.Program id int + name string } -func NewShader(ir *shaderir.Program) *Shader { +func NewShader(ir *shaderir.Program, name string) *Shader { s := &Shader{ - ir: ir, - id: genNextShaderID(), + ir: ir, + id: genNextShaderID(), + name: name, } c := &newShaderCommand{ result: s, diff --git a/internal/restorable/shader.go b/internal/restorable/shader.go index acf94504e..3a13643b9 100644 --- a/internal/restorable/shader.go +++ b/internal/restorable/shader.go @@ -28,12 +28,14 @@ import ( type Shader struct { shader *graphicscommand.Shader ir *shaderir.Program + name string } -func NewShader(ir *shaderir.Program) *Shader { +func NewShader(ir *shaderir.Program, name string) *Shader { s := &Shader{ - shader: graphicscommand.NewShader(ir), + shader: graphicscommand.NewShader(ir, name), ir: ir, + name: name, } theImages.addShader(s) return s @@ -47,7 +49,7 @@ func (s *Shader) Dispose() { } func (s *Shader) restore() { - s.shader = graphicscommand.NewShader(s.ir) + s.shader = graphicscommand.NewShader(s.ir, s.name) } func (s *Shader) Unit() shaderir.Unit { @@ -90,7 +92,7 @@ func init() { if err := wg.Wait(); err != nil { panic(err) } - NearestFilterShader = NewShader(nearestIR) - LinearFilterShader = NewShader(linearIR) - clearShader = NewShader(clearIR) + NearestFilterShader = NewShader(nearestIR, "nearest") + LinearFilterShader = NewShader(linearIR, "linear") + clearShader = NewShader(clearIR, "clear") } diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index ea78f2b78..05f6fbdb4 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -53,7 +53,7 @@ func TestShader(t *testing.T) { img := restorable.NewImage(1, 1, restorable.ImageTypeRegular) defer img.Dispose() - s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) + s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff), "") dr := image.Rect(0, 0, 1, 1) img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) @@ -82,7 +82,7 @@ func TestShaderChain(t *testing.T) { imgs[0].WritePixels(bytesToManagedBytes([]byte{0xff, 0, 0, 0xff}), image.Rect(0, 0, 1, 1)) - s := restorable.NewShader(etesting.ShaderProgramImages(1)) + s := restorable.NewShader(etesting.ShaderProgramImages(1), "") for i := 0; i < num-1; i++ { dr := image.Rect(0, 0, 1, 1) imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) @@ -115,7 +115,7 @@ func TestShaderMultipleSources(t *testing.T) { dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular) - s := restorable.NewShader(etesting.ShaderProgramImages(3)) + s := restorable.NewShader(etesting.ShaderProgramImages(3), "") dr := image.Rect(0, 0, 1, 1) dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) @@ -147,7 +147,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular) - s := restorable.NewShader(etesting.ShaderProgramImages(3)) + s := restorable.NewShader(etesting.ShaderProgramImages(3), "") dr := image.Rect(0, 0, 1, 1) srcRegions := [graphics.ShaderSrcImageCount]image.Rectangle{ image.Rect(0, 0, 1, 1), @@ -177,7 +177,7 @@ func TestShaderDispose(t *testing.T) { img := restorable.NewImage(1, 1, restorable.ImageTypeRegular) defer img.Dispose() - s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) + s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff), "") dr := image.Rect(0, 0, 1, 1) img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) diff --git a/internal/ui/shader.go b/internal/ui/shader.go index 0d9031a92..10589e72d 100644 --- a/internal/ui/shader.go +++ b/internal/ui/shader.go @@ -32,9 +32,9 @@ type Shader struct { uniformUint32Count int } -func NewShader(ir *shaderir.Program) *Shader { +func NewShader(ir *shaderir.Program, name string) *Shader { return &Shader{ - shader: atlas.NewShader(ir), + shader: atlas.NewShader(ir, name), uniformNames: ir.UniformNames[graphics.PreservedUniformVariablesCount:], uniformTypes: ir.Uniforms[graphics.PreservedUniformVariablesCount:], } diff --git a/shader.go b/shader.go index 7ae669da4..93f4fa673 100644 --- a/shader.go +++ b/shader.go @@ -38,12 +38,16 @@ type Shader struct { // // For the details about the shader, see https://ebitengine.org/en/documents/shader.html. func NewShader(src []byte) (*Shader, error) { + return newShader(src, "") +} + +func newShader(src []byte, name string) (*Shader, error) { ir, err := graphics.CompileShader(src) if err != nil { return nil, err } return &Shader{ - shader: ui.NewShader(ir), + shader: ui.NewShader(ir, name), unit: ir.Unit, }, nil } @@ -108,7 +112,23 @@ func builtinShader(filter builtinshader.Filter, address builtinshader.Address, u } } else { src := builtinshader.ShaderSource(filter, address, useColorM) - s, err := NewShader(src) + var name string + switch filter { + case builtinshader.FilterNearest: + name = "nearest" + case builtinshader.FilterLinear: + name = "linear" + } + switch address { + case builtinshader.AddressClampToZero: + name += "-clamptozero" + case builtinshader.AddressRepeat: + name += "-repeat" + } + if useColorM { + name += "-colorm" + } + s, err := newShader(src, name) if err != nil { panic(fmt.Sprintf("ebiten: NewShader for a built-in shader failed: %v", err)) } From 4fa8265c5866131a5f3bac08b37c52c849f5ddfa Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 7 Sep 2024 23:24:55 +0900 Subject: [PATCH 131/188] internal: add comments --- internal/atlas/image.go | 1 + internal/graphicscommand/shader.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index d8be19e0e..650d2d228 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -156,6 +156,7 @@ const ( // A screen image is also unmanaged. ImageTypeScreen + // ImageTypeVolatile is a volatile image that is cleared every frame. ImageTypeVolatile // ImageTypeUnmanaged is an unmanaged image that is not on an atlas. diff --git a/internal/graphicscommand/shader.go b/internal/graphicscommand/shader.go index d55c82eb2..40f2f2f05 100644 --- a/internal/graphicscommand/shader.go +++ b/internal/graphicscommand/shader.go @@ -31,7 +31,9 @@ type Shader struct { shader graphicsdriver.Shader ir *shaderir.Program id int - name string + + // name is used only for logging. + name string } func NewShader(ir *shaderir.Program, name string) *Shader { From 1488e5e68539c0f146b3c2d8afe95ea2e03db438 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 7 Sep 2024 23:36:44 +0900 Subject: [PATCH 132/188] internal/graphicscommand: add attributes to images --- internal/graphicscommand/command.go | 19 ++++++++++++++----- internal/graphicscommand/image.go | 23 ++++++++++++++--------- internal/graphicscommand/image_test.go | 16 ++++++++-------- internal/restorable/image.go | 13 +++++++++---- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 536a02c52..434ba509d 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -93,6 +93,8 @@ func (c *drawTrianglesCommand) String() string { dst := fmt.Sprintf("%d", c.dst.id) if c.dst.screen { dst += " (screen)" + } else if c.dst.attribute != "" { + dst += " (" + c.dst.attribute + ")" } var srcstrs [graphics.ShaderSrcImageCount]string @@ -104,6 +106,8 @@ func (c *drawTrianglesCommand) String() string { srcstrs[i] = fmt.Sprintf("%d", src.id) if src.screen { srcstrs[i] += " (screen)" + } else if src.attribute != "" { + srcstrs[i] += " (" + src.attribute + ")" } } @@ -335,14 +339,19 @@ func (c *disposeShaderCommand) NeedsSync() bool { // newImageCommand represents a command to create an empty image with given width and height. type newImageCommand struct { - result *Image - width int - height int - screen bool + result *Image + width int + height int + screen bool + attribute string } func (c *newImageCommand) String() string { - return fmt.Sprintf("new-image: result: %d, width: %d, height: %d, screen: %t", c.result.id, c.width, c.height, c.screen) + str := fmt.Sprintf("new-image: result: %d, width: %d, height: %d, screen: %t", c.result.id, c.width, c.height, c.screen) + if c.attribute != "" { + str += ", attribute: " + c.attribute + } + return str } // Exec executes a newImageCommand. diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 87ff76eff..b835217fe 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -37,6 +37,9 @@ type Image struct { internalHeight int screen bool + // attribute is used only for logs. + attribute string + // id is an identifier for the image. This is used only when dumping the information. // // This is duplicated with graphicsdriver.Image's ID, but this id is still necessary because this image might not @@ -57,18 +60,20 @@ func genNextImageID() int { // NewImage returns a new image. // // Note that the image is not initialized yet. -func NewImage(width, height int, screenFramebuffer bool) *Image { +func NewImage(width, height int, screenFramebuffer bool, attribute string) *Image { i := &Image{ - width: width, - height: height, - screen: screenFramebuffer, - id: genNextImageID(), + width: width, + height: height, + screen: screenFramebuffer, + id: genNextImageID(), + attribute: attribute, } c := &newImageCommand{ - result: i, - width: width, - height: height, - screen: screenFramebuffer, + result: i, + width: width, + height: height, + screen: screenFramebuffer, + attribute: attribute, } theCommandQueueManager.enqueueCommand(c) return i diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index fb3151dea..f13f04958 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -50,8 +50,8 @@ func quadVertices(w, h float32) []float32 { func TestClear(t *testing.T) { const w, h = 1024, 1024 - src := graphicscommand.NewImage(w/2, h/2, false) - dst := graphicscommand.NewImage(w, h, false) + src := graphicscommand.NewImage(w/2, h/2, false, "") + dst := graphicscommand.NewImage(w, h, false, "") vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() @@ -81,9 +81,9 @@ func TestClear(t *testing.T) { func TestWritePixelsPartAfterDrawTriangles(t *testing.T) { const w, h = 32, 32 - clr := graphicscommand.NewImage(w, h, false) - src := graphicscommand.NewImage(w/2, h/2, false) - dst := graphicscommand.NewImage(w, h, false) + clr := graphicscommand.NewImage(w, h, false, "") + src := graphicscommand.NewImage(w/2, h/2, false, "") + dst := graphicscommand.NewImage(w, h, false, "") vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) @@ -101,8 +101,8 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) { func TestShader(t *testing.T) { const w, h = 16, 16 - clr := graphicscommand.NewImage(w, h, false) - dst := graphicscommand.NewImage(w, h, false) + clr := graphicscommand.NewImage(w, h, false, "") + dst := graphicscommand.NewImage(w, h, false, "") vs := quadVertices(w, h) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) @@ -136,7 +136,7 @@ func TestShader(t *testing.T) { // Issue #3036 func TestSuccessiveWritePixels(t *testing.T) { const w, h = 32, 32 - dst := graphicscommand.NewImage(w, h, false) + dst := graphicscommand.NewImage(w, h, false, "") dst.WritePixels(graphics.NewManagedBytes(4, func(bs []byte) { for i := range bs { diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 82cb79aa3..f8cbdf3ef 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -156,8 +156,13 @@ func NewImage(width, height int, imageType ImageType) *Image { panic("restorable: graphics driver must be ready at NewImage but not") } + var attribute string + switch imageType { + case ImageTypeVolatile: + attribute = "volatile" + } i := &Image{ - image: graphicscommand.NewImage(width, height, imageType == ImageTypeScreen), + image: graphicscommand.NewImage(width, height, imageType == ImageTypeScreen, attribute), width: width, height: height, imageType: imageType, @@ -566,7 +571,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { case ImageTypeScreen: // The screen image should also be recreated because framebuffer might // be changed. - i.image = graphicscommand.NewImage(w, h, true) + i.image = graphicscommand.NewImage(w, h, true, "") i.basePixels.Dispose() i.basePixels = Pixels{} i.clearDrawTrianglesHistory() @@ -574,7 +579,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { i.staleRegions = i.staleRegions[:0] return nil case ImageTypeVolatile: - i.image = graphicscommand.NewImage(w, h, false) + i.image = graphicscommand.NewImage(w, h, false, "volatile") iw, ih := i.image.InternalSize() clearImage(i.image, image.Rect(0, 0, iw, ih)) return nil @@ -584,7 +589,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { panic("restorable: pixels must not be stale when restoring") } - gimg := graphicscommand.NewImage(w, h, false) + gimg := graphicscommand.NewImage(w, h, false, "") // Clear the image explicitly. iw, ih := gimg.InternalSize() clearImage(gimg, image.Rect(0, 0, iw, ih)) From 4e6fdc6db5846b095781611b430ba2d5325a570f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 00:10:45 +0900 Subject: [PATCH 133/188] all: update dependencies --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c00e01164..cd060413d 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/ebitengine/purego v0.8.0-alpha.5 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 - github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4.0.20240905090502-03ded50a2328 + github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.5 github.com/hajimehoshi/go-mp3 v0.3.4 github.com/jakecoffman/cp v1.2.1 github.com/jezek/xgb v1.1.1 @@ -17,7 +17,7 @@ require ( github.com/kisielk/errcheck v1.7.0 golang.org/x/image v0.19.0 golang.org/x/sync v0.8.0 - golang.org/x/sys v0.24.0 + golang.org/x/sys v0.25.0 golang.org/x/text v0.17.0 golang.org/x/tools v0.24.0 ) diff --git a/go.sum b/go.sum index 982e722a7..eb3e0f9dc 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZ github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4.0.20240905090502-03ded50a2328 h1:AYqx/yoXuPMmNJMXEblljVeqFDQnKFu5QOjHZMhGfMY= -github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.4.0.20240905090502-03ded50a2328/go.mod h1:/GmYyEKgzzM7dzJBsL7aS5iR83Dr666E5bhQLVVPYsw= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.5 h1:gtIcN2INlD2qlfUiECuvbI0moNIoANgIY7MwgW4cFGE= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.5/go.mod h1:/GmYyEKgzzM7dzJBsL7aS5iR83Dr666E5bhQLVVPYsw= github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= @@ -74,8 +74,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 29ef2c84ef291c0efea94905de0ed81b72aa4fc9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 11:31:52 +0900 Subject: [PATCH 134/188] internal/atlas: add comments --- internal/atlas/image.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 650d2d228..31a4f83a0 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -157,6 +157,7 @@ const ( ImageTypeScreen // ImageTypeVolatile is a volatile image that is cleared every frame. + // A volatile image is also unmanaged. ImageTypeVolatile // ImageTypeUnmanaged is an unmanaged image that is not on an atlas. From 30a2817ab53e096b9069bdaaa8a921456a8ef6c0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 11:42:02 +0900 Subject: [PATCH 135/188] internal/restorable: add Hint to optimize drawImageHistoryItem size --- image.go | 9 +-- internal/atlas/image.go | 14 ++--- internal/atlas/image_test.go | 47 +++++++-------- internal/atlas/shader_test.go | 11 ++-- internal/buffered/image.go | 6 +- internal/buffered/image_test.go | 3 +- internal/mipmap/mipmap.go | 7 ++- internal/restorable/image.go | 39 +++++++++++-- internal/restorable/images_test.go | 91 ++++++++++++++++++++---------- internal/restorable/shader_test.go | 12 ++-- internal/ui/image.go | 15 +++-- 11 files changed, 163 insertions(+), 91 deletions(-) diff --git a/image.go b/image.go index 115020b08..bf2235636 100644 --- a/image.go +++ b/image.go @@ -26,6 +26,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -266,7 +267,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { }) } - i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false) + i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false, restorable.HintNone) } // Vertex represents a vertex passed to DrawTriangles. @@ -550,7 +551,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o }) } - i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), filter != builtinshader.FilterLinear, options.AntiAlias) + i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), filter != builtinshader.FilterLinear, options.AntiAlias, restorable.HintNone) } // DrawTrianglesShaderOptions represents options for DrawTrianglesShader. @@ -722,7 +723,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader i.tmpUniforms = i.tmpUniforms[:0] i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms) - i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), true, options.AntiAlias) + i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), true, options.AntiAlias, restorable.HintNone) } // DrawRectShaderOptions represents options for DrawRectShader. @@ -855,7 +856,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR i.tmpUniforms = i.tmpUniforms[:0] i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms) - i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false) + i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false, restorable.HintNone) } // SubImage returns an image representing the portion of the image p visible through r. diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 31a4f83a0..0a395ef1a 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -285,7 +285,7 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) newI.moveTo(i) } @@ -315,7 +315,7 @@ func (i *Image) putOnSourceBackend() { graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) newI.moveTo(i) i.usedAsSourceCount = 0 @@ -347,7 +347,7 @@ func (i *Image) regionWithPadding() image.Rectangle { // 5: Color G // 6: Color B // 7: Color Y -func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint restorable.Hint) { backendsM.Lock() defer backendsM.Unlock() @@ -360,15 +360,15 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice copy(us, uniforms) appendDeferred(func() { - i.drawTriangles(srcs, vs, is, blend, dstRegion, srcRegions, shader, us, fillRule) + i.drawTriangles(srcs, vs, is, blend, dstRegion, srcRegions, shader, us, fillRule, hint) }) return } - i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule) + i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, hint) } -func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { +func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint restorable.Hint) { backends := make([]*backend, 0, len(srcs)) for _, src := range srcs { if src == nil { @@ -449,7 +449,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice imgs[i] = src.backend.restorable } - i.backend.restorable.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader(), uniforms, fillRule) + i.backend.restorable.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader(), uniforms, fillRule, hint) for _, src := range srcs { if src == nil { diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 1a7d1d300..0a021b95e 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -25,6 +25,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" t "github.com/hajimehoshi/ebiten/v2/internal/testing" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -102,7 +103,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { vs := quadVertices(size/2, size/2, size/4, size/4, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img4.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -110,7 +111,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // img5 is not allocated now, but is allocated at DrawTriangles. vs = quadVertices(0, 0, size/2, size/2, 1) dr = image.Rect(0, 0, size/2, size/2) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -144,7 +145,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // Check further drawing doesn't cause panic. // This bug was fixed by 03dcd948. vs = quadVertices(0, 0, size/2, size/2, 1) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } func TestReputOnSourceBackend(t *testing.T) { @@ -188,7 +189,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Render onto img1. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -200,7 +201,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -208,7 +209,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Finally, img1 is on a source backend. atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -237,7 +238,7 @@ func TestReputOnSourceBackend(t *testing.T) { } vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -267,7 +268,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Use img1 as a render target again. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -279,7 +280,7 @@ func TestReputOnSourceBackend(t *testing.T) { atlas.PutImagesOnSourceBackendForTesting() img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size)) vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -288,7 +289,7 @@ func TestReputOnSourceBackend(t *testing.T) { // img1 is not on an atlas due to WritePixels. vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -297,7 +298,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -400,7 +401,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) dst.WritePixels(pix, image.Rect(0, 0, w, h)) pix = make([]byte, 4*w*h) @@ -447,7 +448,7 @@ func TestSmallImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix = make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -494,7 +495,7 @@ func TestLongImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, scale) is := graphics.QuadIndices() dr := image.Rect(0, 0, dstW, dstH) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix = make([]byte, 4*dstW*dstH) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH)) @@ -610,7 +611,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { vs := quadVertices(size, size, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - src.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + src.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -619,7 +620,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -653,7 +654,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { // Call DrawTriangles multiple times. // The number of DrawTriangles doesn't matter as long as these are called in one frame. for i := 0; i < 2; i++ { - src2.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + src2.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) @@ -672,7 +673,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -798,14 +799,14 @@ func TestDestinationCountOverflow(t *testing.T) { // Use dst0 as a destination for a while. for i := 0; i < 31; i++ { - dst0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) atlas.PutImagesOnSourceBackendForTesting() } // Use dst0 as a source for a while. // As dst0 is used as a destination too many times (31 is a maximum), dst0's backend should never be a source backend. for i := 0; i < 100; i++ { - dst1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) atlas.PutImagesOnSourceBackendForTesting() if dst0.IsOnSourceBackendForTesting() { t.Errorf("dst0 cannot be on a source backend: %d", i) @@ -831,7 +832,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) for _, img := range srcs { - img.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } atlas.PutImagesOnSourceBackendForTesting() @@ -839,7 +840,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { // Check iterating the registered image works correctly. for i := 0; i < 100; i++ { for _, src := range srcs { - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } atlas.PutImagesOnSourceBackendForTesting() } @@ -887,7 +888,7 @@ func TestDallocateUnmanagedImageBackends(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Get the difference of the number of backends before and after the images are deallocated. c := atlas.BackendCountForTesting() diff --git a/internal/atlas/shader_test.go b/internal/atlas/shader_test.go index 5955806b8..a594585fa 100644 --- a/internal/atlas/shader_test.go +++ b/internal/atlas/shader_test.go @@ -23,6 +23,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -37,12 +38,12 @@ func TestShaderFillTwice(t *testing.T) { dr := image.Rect(0, 0, w, h) g := ui.Get().GraphicsDriverForTesting() s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff), "") - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff), "") - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(g, pix, image.Rect(0, 0, w, h)) @@ -69,11 +70,11 @@ func TestImageDrawTwice(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -97,7 +98,7 @@ func TestGCShader(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Ensure other objects are GCed, as GC appends deferred functions for collected objects. ensureGC() diff --git a/internal/buffered/image.go b/internal/buffered/image.go index a4c22e10a..62c3e8fab 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -196,7 +196,7 @@ func (i *Image) WritePixels(pix []byte, region image.Rectangle) { // DrawTriangles draws the src image with the given vertices. // // Copying vertices and indices is the caller's responsibility. -func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint restorable.Hint) { for _, src := range srcs { if i == src { panic("buffered: Image.DrawTriangles: source images must be different from the receiver") @@ -218,7 +218,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice imgs[i] = img.img } - i.img.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule) + i.img.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, hint) // After rendering, the pixel cache is no longer valid. i.pixels = nil @@ -311,7 +311,7 @@ func (i *Image) syncPixelsIfNeeded() { srcs := [graphics.ShaderSrcImageCount]*atlas.Image{whiteImage.img} dr := image.Rect(0, 0, i.width, i.height) blend := graphicsdriver.BlendCopy - i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // TODO: Use clear if Go 1.21 is available. for pos := range i.dotsBuffer { diff --git a/internal/buffered/image_test.go b/internal/buffered/image_test.go index 4a9db4c25..f36b2717a 100644 --- a/internal/buffered/image_test.go +++ b/internal/buffered/image_test.go @@ -22,6 +22,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/buffered" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" t "github.com/hajimehoshi/ebiten/v2/internal/testing" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -56,7 +57,7 @@ func TestUnsyncedPixels(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, 16, 16) sr := [graphics.ShaderSrcImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)} - dst.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Check the result is correct. var got [4]byte diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 2145f2e66..e5f68d62f 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -23,6 +23,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/buffered" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" ) func canUseMipmap(imageType atlas.ImageType) bool { @@ -65,7 +66,7 @@ func (m *Mipmap) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byt return m.orig.ReadPixels(graphicsDriver, pixels, region) } -func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Mipmap, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool) { +func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Mipmap, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool, hint restorable.Hint) { if len(indices) == 0 { return } @@ -123,7 +124,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Mipmap, verti imgs[i] = src.orig } - m.orig.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule) + m.orig.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, hint) m.deallocateMipmaps() } @@ -184,7 +185,7 @@ func (m *Mipmap) level(level int) *buffered.Image { s := buffered.NewImage(w2, h2, m.imageType) dstRegion := image.Rect(0, 0, w2, h2) - s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.LinearFilterShader, nil, graphicsdriver.FillRuleFillAll) + s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) m.setImg(level, s) return m.imgs[level] diff --git a/internal/restorable/image.go b/internal/restorable/image.go index f8cbdf3ef..eb9bab33c 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -109,6 +109,18 @@ const ( ImageTypeVolatile ) +// Hint is a hint to optimize the info to restore the image. +type Hint int + +const ( + // HintNone indicates that there is no hint. + HintNone Hint = iota + + // HintOverwriteDstRegion indicates that the destination region is overwritten. + // HintOverwriteDstRegion helps to reduce the size of the draw-image history. + HintOverwriteDstRegion +) + // Image represents an image that can be restored when GL context is lost. type Image struct { image *graphicscommand.Image @@ -194,7 +206,7 @@ func (i *Image) Extend(width, height int) *Image { graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) - newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, HintOverwriteDstRegion) i.Dispose() return newImg @@ -316,7 +328,7 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl // 5: Color G // 6: Color B // 7: Color Y -func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint Hint) { if len(vertices) == 0 { return } @@ -338,7 +350,8 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice if srcstale || !needsRestoration() || !i.needsRestoration() || i.stale { i.makeStale(dstRegion) } else { - i.appendDrawTrianglesHistory(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule) + // TODO: Consider overwritten. + i.appendDrawTrianglesHistory(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, hint) } var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image @@ -352,7 +365,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice } // appendDrawTrianglesHistory appends a draw-image history item to the image. -func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) { +func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint Hint) { if i.stale || !i.needsRestoration() { panic("restorable: an image must not be stale or need restoration at appendDrawTrianglesHistory") } @@ -360,6 +373,24 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*I panic("restorable: appendDrawTrianglesHistory must not be called when AlwaysReadPixelsFromGPU() returns true") } + // If the command overwrites the destination region, remove the history items that are in the region. + if hint == HintOverwriteDstRegion { + for idx, c := range i.drawTrianglesHistory { + if c.dstRegion.In(dstRegion) { + i.drawTrianglesHistory[idx] = nil + } + } + var n int + for _, c := range i.drawTrianglesHistory { + if c == nil { + continue + } + i.drawTrianglesHistory[n] = c + n++ + } + i.drawTrianglesHistory = i.drawTrianglesHistory[:n] + } + // TODO: Would it be possible to merge draw image history items? const maxDrawTrianglesHistoryCount = 1024 if len(i.drawTrianglesHistory)+1 > maxDrawTrianglesHistoryCount { diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index 39a099b1a..8c434b119 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -140,7 +140,7 @@ func TestRestoreChain(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, 1, 1) - imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) @@ -183,10 +183,10 @@ func TestRestoreChain2(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - imgs[8].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[7]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) - imgs[9].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[8]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + imgs[8].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[7]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + imgs[9].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[8]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) for i := 0; i < 7; i++ { - imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -227,10 +227,10 @@ func TestRestoreOverrideSource(t *testing.T) { img1.WritePixels(bytesToManagedBytes([]byte{clr0.R, clr0.G, clr0.B, clr0.A}), image.Rect(0, 0, w, h)) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img2.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img2.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img0.WritePixels(bytesToManagedBytes([]byte{clr1.R, clr1.G, clr1.B, clr1.A}), image.Rect(0, 0, w, h)) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -310,23 +310,23 @@ func TestRestoreComplexGraph(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) vs := quadVertices(w, h, 0, 0) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 1, 0) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 1, 0) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 2, 0) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 0, 0) - img5.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img5.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 0, 0) - img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 1, 0) - img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img4}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img4}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 0, 0) - img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 2, 0) - img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -419,8 +419,8 @@ func TestRestoreRecursive(t *testing.T) { }() is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -519,7 +519,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, 2, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img1.WritePixels(bytesToManagedBytes([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}), image.Rect(0, 0, 2, 1)) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -557,8 +557,8 @@ func TestDispose(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, 1, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img1.Dispose() if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -667,7 +667,7 @@ func TestWritePixelsOnly(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, 1, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img0.WritePixels(bytesToManagedBytes([]byte{5, 6, 7, 8}), image.Rect(0, 0, 1, 1)) // BasePixelsForTesting is available without GPU accessing. @@ -721,7 +721,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being // stale. @@ -745,7 +745,7 @@ func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) dst.WritePixels(bytesToManagedBytes(make([]byte, 4*w*h)), image.Rect(0, 0, w, h)) // WritePixels for a whole image doesn't panic. } @@ -764,7 +764,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) dst.WritePixels(bytesToManagedBytes(make([]byte, 4*2*2)), image.Rect(0, 0, 2, 2)) // WritePixels for a part of image doesn't panic. @@ -858,7 +858,7 @@ func TestDrawTrianglesAndExtend(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - orig.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + orig.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) extended := orig.Extend(w*2, h*2) // After this, orig is already disposed. result := make([]byte, 4*(w*2)*(h*2)) @@ -908,7 +908,7 @@ func TestMutateSlices(t *testing.T) { is := make([]uint32, len(graphics.QuadIndices())) copy(is, graphics.QuadIndices()) dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) for i := range vs { vs[i] = 0 } @@ -1100,7 +1100,7 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)); err != nil { @@ -1124,7 +1124,7 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) { vs := quadVertices(1, 1, 1, 0) is := graphics.QuadIndices() dr := image.Rect(1, 0, 2, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Get the pixels. pix := make([]byte, 4*2*1) @@ -1138,3 +1138,36 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) { t.Errorf("got: %v, want: %v", got, want) } } + +func TestOverwriteDstRegion(t *testing.T) { + const w, h = 1, 1 + src0 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + src1 := restorable.NewImage(w, h, restorable.ImageTypeRegular) + dst := restorable.NewImage(w, h, restorable.ImageTypeRegular) + + src0.WritePixels(bytesToManagedBytes([]byte{0x40, 0x40, 0x40, 0x40}), image.Rect(0, 0, 1, 1)) + src1.WritePixels(bytesToManagedBytes([]byte{0x80, 0x80, 0x80, 0x80}), image.Rect(0, 0, 1, 1)) + + vs := quadVertices(w, h, 0, 0) + is := graphics.QuadIndices() + dr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + // This tests that HintOverwriteDstRegion removes the previous DrawTriangles. + // In practice, BlendCopy should be used instead of BlendSourceOver in this case. + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) + + if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + if err := restorable.RestoreIfNeeded(ui.Get().GraphicsDriverForTesting()); err != nil { + t.Fatal(err) + } + + pix := make([]byte, 4*w*h) + if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)); err != nil { + t.Fatal(err) + } + if got, want := (color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}); !sameColors(got, want, 1) { + t.Errorf("got: %v, want: %v", got, want) + } +} diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index 05f6fbdb4..2e9901502 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -46,7 +46,7 @@ func clearImage(img *restorable.Image, w, h int) { } is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{emptyImage}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{emptyImage}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } func TestShader(t *testing.T) { @@ -55,7 +55,7 @@ func TestShader(t *testing.T) { s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff), "") dr := image.Rect(0, 0, 1, 1) - img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) @@ -85,7 +85,7 @@ func TestShaderChain(t *testing.T) { s := restorable.NewShader(etesting.ShaderProgramImages(1), "") for i := 0; i < num-1; i++ { dr := image.Rect(0, 0, 1, 1) - imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -117,7 +117,7 @@ func TestShaderMultipleSources(t *testing.T) { s := restorable.NewShader(etesting.ShaderProgramImages(3), "") dr := image.Rect(0, 0, 1, 1) - dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Clear one of the sources after DrawTriangles. dst should not be affected. clearImage(srcs[0], 1, 1) @@ -154,7 +154,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { image.Rect(1, 0, 2, 1), image.Rect(2, 0, 3, 1), } - dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, srcRegions, s, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, srcRegions, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Clear one of the sources after DrawTriangles. dst should not be affected. clearImage(srcs[0], 3, 1) @@ -179,7 +179,7 @@ func TestShaderDispose(t *testing.T) { s := restorable.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff), "") dr := image.Rect(0, 0, 1, 1) - img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Dispose the shader. This should invalidate all the images using this shader i.e., all the images become // stale. diff --git a/internal/ui/image.go b/internal/ui/image.go index 2b56f0e0f..94a03555f 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -23,6 +23,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/mipmap" + "github.com/hajimehoshi/ebiten/v2/internal/restorable" ) // panicOnErrorOnReadingPixels indicates whether reading pixels panics on an error or not. @@ -77,7 +78,7 @@ func (i *Image) Deallocate() { i.mipmap.Deallocate() } -func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool, antialias bool) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool, antialias bool, hint restorable.Hint) { if i.modifyCallback != nil { i.modifyCallback() } @@ -113,7 +114,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice srcMipmaps[i] = src.mipmap } - i.mipmap.DrawTriangles(srcMipmaps, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule, canSkipMipmap) + i.mipmap.DrawTriangles(srcMipmaps, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule, canSkipMipmap, hint) } func (i *Image) WritePixels(pix []byte, region image.Rectangle) { @@ -183,7 +184,7 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) { blend = graphicsdriver.BlendSourceOver } // i.lastBlend is updated in DrawTriangles. - i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) + i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion) } type bigOffscreenImage struct { @@ -252,7 +253,7 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Im 1, 1, 1, 1) is := graphics.QuadIndices() dstRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale) - i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) + i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion) } for idx := 0; idx < len(vertices); idx += graphics.VertexFloatCount { @@ -268,7 +269,7 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Im dstRegion.Max.X *= bigOffscreenScale dstRegion.Max.Y *= bigOffscreenScale - i.image.DrawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, canSkipMipmap, false) + i.image.DrawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, canSkipMipmap, false, restorable.HintNone) i.dirty = true } @@ -297,10 +298,12 @@ func (i *bigOffscreenImage) flush() { is := graphics.QuadIndices() dstRegion := i.region blend := graphicsdriver.BlendSourceOver + hint := restorable.HintNone if i.blend != graphicsdriver.BlendSourceOver { blend = graphicsdriver.BlendCopy + hint = restorable.HintOverwriteDstRegion } - i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) + i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, hint) i.image.clear() i.dirty = false From 4824dc036072f0801f5fd3862b58f58140803945 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 12:56:42 +0900 Subject: [PATCH 136/188] internal/restorable: resolve a stale state when possible --- image.go | 35 +++++++++++++++++++++++++++++++++-- internal/restorable/image.go | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/image.go b/image.go index bf2235636..0b0a5b611 100644 --- a/image.go +++ b/image.go @@ -267,7 +267,31 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { }) } - i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false, restorable.HintNone) + dr := i.adjustedBounds() + hint := restorable.HintNone + if overwritesDstRegion(options.Blend, dr, geoM, sx0, sy0, sx1, sy1) { + hint = restorable.HintOverwriteDstRegion + } + + i.image.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false, hint) +} + +// overwritesDstRegion reports whether the given parameters overwrite the destination region completely. +func overwritesDstRegion(blend Blend, dstRegion image.Rectangle, geoM GeoM, sx0, sy0, sx1, sy1 int) bool { + if blend != BlendCopy { + return false + } + // Check the result vertices is not a rotated rectangle. + if geoM.b != 0 || geoM.c != 0 { + return false + } + // Check the result vertices completely covers dstRegion. + x0, y0 := geoM.Apply(float64(sx0), float64(sy0)) + x1, y1 := geoM.Apply(float64(sx1), float64(sy1)) + if float64(dstRegion.Min.X) < x0 || float64(dstRegion.Min.Y) < y0 || float64(dstRegion.Max.X) > x1 || float64(dstRegion.Max.Y) > y1 { + return false + } + return true } // Vertex represents a vertex passed to DrawTriangles. @@ -856,7 +880,14 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR i.tmpUniforms = i.tmpUniforms[:0] i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms) - i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false, restorable.HintNone) + dr := i.adjustedBounds() + hint := restorable.HintNone + // Do not use srcRegions[0].Dx() and srcRegions[0].Dy() as these might be empty. + if overwritesDstRegion(options.Blend, dr, geoM, srcRegions[0].Min.X, srcRegions[0].Min.Y, srcRegions[0].Min.X+width, srcRegions[0].Min.Y+height) { + hint = restorable.HintOverwriteDstRegion + } + + i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false, hint) } // SubImage returns an image representing the portion of the image p visible through r. diff --git a/internal/restorable/image.go b/internal/restorable/image.go index eb9bab33c..232ddaab7 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -347,10 +347,25 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice } // Even if the image is already stale, call makeStale to extend the stale region. - if srcstale || !needsRestoration() || !i.needsRestoration() || i.stale { + if srcstale || !needsRestoration() || !i.needsRestoration() { i.makeStale(dstRegion) - } else { - // TODO: Consider overwritten. + } else if i.stale { + var overwrite bool + if hint == HintOverwriteDstRegion { + overwrite = i.areStaleRegionsIncludedIn(dstRegion) + } + if overwrite { + i.basePixels.Clear(dstRegion) + i.clearDrawTrianglesHistory() + i.stale = false + i.staleRegions = i.staleRegions[:0] + } else { + // Even if the image is already stale, call makeStale to extend the stale region. + i.makeStale(dstRegion) + } + } + + if !i.stale { i.appendDrawTrianglesHistory(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, hint) } @@ -364,6 +379,18 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice i.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule) } +func (i *Image) areStaleRegionsIncludedIn(r image.Rectangle) bool { + if !i.stale { + return false + } + for _, sr := range i.staleRegions { + if !sr.In(r) { + return false + } + } + return true +} + // appendDrawTrianglesHistory appends a draw-image history item to the image. func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint Hint) { if i.stale || !i.needsRestoration() { From d84b030300da51545c78f27187826d8ede6050dd Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 14:52:07 +0900 Subject: [PATCH 137/188] ebiten: give HintOverwriteDstRegion when BlendClear is used --- image.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/image.go b/image.go index 0b0a5b611..e27577676 100644 --- a/image.go +++ b/image.go @@ -278,7 +278,8 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { // overwritesDstRegion reports whether the given parameters overwrite the destination region completely. func overwritesDstRegion(blend Blend, dstRegion image.Rectangle, geoM GeoM, sx0, sy0, sx1, sy1 int) bool { - if blend != BlendCopy { + // TODO: More precisely, BlendFactorDestinationRGB, BlendFactorDestinationAlpha, and operations should be checked. + if blend != BlendCopy && blend != BlendClear { return false } // Check the result vertices is not a rotated rectangle. From 167c3435f7ba542f136b41d799b12ef94690bfef Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 16:03:56 +0900 Subject: [PATCH 138/188] internal/restorable: refactoring --- internal/restorable/images.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 90b856023..8c7f45ced 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -242,9 +242,7 @@ func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error { current[i] = struct{}{} } for e := range edges { - if _, ok := current[e.target]; ok { - delete(current, e.target) - } + delete(current, e.target) } for i := range current { delete(images, i) From 20f8df7fc1238a9651d377137037f620303ac3c8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 16:03:56 +0900 Subject: [PATCH 139/188] internal/restorable: refactoring: remove lastTarget This didn't improve performance very much. Rather, this made the code a little hard to read and could have a potential issue. --- internal/restorable/images.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 8c7f45ced..67fe301f1 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -62,7 +62,6 @@ func AlwaysReadPixelsFromGPU() bool { type images struct { images map[*Image]struct{} shaders map[*Shader]struct{} - lastTarget *Image contextLost atomic.Bool } @@ -157,7 +156,6 @@ func (i *images) removeShader(shader *Shader) { // resolveStaleImages resolves stale images. func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { - i.lastTarget = nil for img := range i.images { if err := img.resolveStale(graphicsDriver); err != nil { return err @@ -174,10 +172,6 @@ func (i *images) makeStaleIfDependingOn(target *Image) { if target == nil { panic("restorable: target must not be nil at makeStaleIfDependingOn") } - if i.lastTarget == target { - return - } - i.lastTarget = target for img := range i.images { img.makeStaleIfDependingOn(target) } From b36160d9e7a205c14f35edca3b11983eb34663d9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 16:11:57 +0900 Subject: [PATCH 140/188] internal/restorable: bug fix: make all the images stale correctly --- internal/restorable/images.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 67fe301f1..4943c0cee 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -90,7 +90,7 @@ func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool) e if disabled.Load() { disabledOnce.Do(func() { for img := range theImages.images { - img.makeStale(image.Rectangle{}) + img.makeStale(image.Rect(0, 0, img.width, img.height)) } }) } From 9ef9ea0469e30755a0ccb32961cd04f718624740 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 17:03:18 +0900 Subject: [PATCH 141/188] internal/restorable: refactoring --- internal/restorable/image.go | 16 +++++++--------- internal/restorable/images.go | 14 +++++++------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 232ddaab7..1312c2733 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -482,12 +482,12 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte return nil } -// makeStaleIfDependingOn makes the image stale if the image depends on target. -func (i *Image) makeStaleIfDependingOn(target *Image) { +// makeStaleIfDependingOn makes the image stale if the image depends on src. +func (i *Image) makeStaleIfDependingOn(src *Image) { if i.stale { return } - if i.dependsOn(target) { + if i.dependsOn(src) { // There is no new region to make stale. i.makeStale(image.Rectangle{}) } @@ -573,16 +573,14 @@ func (i *Image) resolveStale(graphicsDriver graphicsdriver.Graphics) error { return i.readPixelsFromGPU(graphicsDriver) } -// dependsOn reports whether the image depends on target. -func (i *Image) dependsOn(target *Image) bool { +// dependsOn reports whether the image depends on src. +func (i *Image) dependsOn(src *Image) bool { for _, c := range i.drawTrianglesHistory { for _, img := range c.images { - if img == nil { + if img != src { continue } - if img == target { - return true - } + return true } } return false diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 4943c0cee..cec31dc79 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -164,16 +164,16 @@ func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) erro return nil } -// makeStaleIfDependingOn makes all the images stale that depend on target. +// makeStaleIfDependingOn makes all the images stale that depend on src. // -// When target is modified, all images depending on target can't be restored with target. -// makeStaleIfDependingOn is called in such situation. -func (i *images) makeStaleIfDependingOn(target *Image) { - if target == nil { - panic("restorable: target must not be nil at makeStaleIfDependingOn") +// When src is modified, all images depending on src can't be restored with src. +// makeStaleIfDependingOn is called in such situation.src. +func (i *images) makeStaleIfDependingOn(src *Image) { + if src == nil { + panic("restorable: src must not be nil at makeStaleIfDependingOn") } for img := range i.images { - img.makeStaleIfDependingOn(target) + img.makeStaleIfDependingOn(src) } } From 26f0479f16594968935c5983a547a2fd6d5d2bd3 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 17:06:25 +0900 Subject: [PATCH 142/188] internal/restorable: typo --- internal/restorable/images.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/restorable/images.go b/internal/restorable/images.go index cec31dc79..1dedfce78 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -167,7 +167,7 @@ func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) erro // makeStaleIfDependingOn makes all the images stale that depend on src. // // When src is modified, all images depending on src can't be restored with src. -// makeStaleIfDependingOn is called in such situation.src. +// makeStaleIfDependingOn is called in such situation. func (i *images) makeStaleIfDependingOn(src *Image) { if src == nil { panic("restorable: src must not be nil at makeStaleIfDependingOn") From aa8e11241446df1d53f5ab4ebd8a2a542e60f8ae Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 17:42:45 +0900 Subject: [PATCH 143/188] internal/restorable: restore a stale image at clearing pixels --- internal/restorable/image.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 1312c2733..85e5a0a7a 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -283,8 +283,7 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl clearImage(i.image, region) } - // Even if the image is already stale, call makeStale to extend the stale region. - if !needsRestoration() || !i.needsRestoration() || i.stale { + if !needsRestoration() || !i.needsRestoration() { i.makeStale(region) return } @@ -302,6 +301,12 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl return } + if i.stale { + // Even if the image is already stale, call makeStale to extend the stale region. + i.makeStale(region) + return + } + // Records for DrawTriangles cannot come before records for WritePixels. if len(i.drawTrianglesHistory) > 0 { i.makeStale(region) From f32648b1440adc4068222f4584983ea39510ed8a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 17:48:59 +0900 Subject: [PATCH 144/188] internal/restorable: skip adding stale regions when the image doesn't need to be restored --- internal/restorable/image.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 85e5a0a7a..d30ef3cb8 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -233,6 +233,10 @@ func (i *Image) makeStale(rect image.Rectangle) { return } + if !i.needsRestoration() { + return + } + origSize := len(i.staleRegions) i.staleRegions = i.appendRegionsForDrawTriangles(i.staleRegions) if !rect.Empty() { @@ -465,7 +469,7 @@ func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics } func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error { - if AlwaysReadPixelsFromGPU() { + if AlwaysReadPixelsFromGPU() || !i.needsRestoration() { if err := i.image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{ { Pixels: pixels, From bbe3cba1100bd1fcc560838996fd731e8a5e336d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 18:17:23 +0900 Subject: [PATCH 145/188] internal/restorable: remove draw-triangles history items at WritePixels --- internal/restorable/image.go | 40 +++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index d30ef3cb8..e685f7e76 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -295,9 +295,9 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl if region.Eq(image.Rect(0, 0, w, h)) { if pixels != nil { // Clone a ManagedBytes as the package graphicscommand has a different lifetime management. - i.basePixels.AddOrReplace(pixels.Clone(), image.Rect(0, 0, w, h)) + i.basePixels.AddOrReplace(pixels.Clone(), region) } else { - i.basePixels.Clear(image.Rect(0, 0, w, h)) + i.basePixels.Clear(region) } i.clearDrawTrianglesHistory() i.stale = false @@ -311,6 +311,8 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl return } + i.removeDrawTrianglesHistoryItems(region) + // Records for DrawTriangles cannot come before records for WritePixels. if len(i.drawTrianglesHistory) > 0 { i.makeStale(region) @@ -400,6 +402,25 @@ func (i *Image) areStaleRegionsIncludedIn(r image.Rectangle) bool { return true } +// removeDrawTrianglesHistoryItems removes draw-image history items whose destination regions are in the given region. +// This is useful when the destination region is overwritten soon later. +func (i *Image) removeDrawTrianglesHistoryItems(region image.Rectangle) { + for idx, c := range i.drawTrianglesHistory { + if c.dstRegion.In(region) { + i.drawTrianglesHistory[idx] = nil + } + } + var n int + for _, c := range i.drawTrianglesHistory { + if c == nil { + continue + } + i.drawTrianglesHistory[n] = c + n++ + } + i.drawTrianglesHistory = i.drawTrianglesHistory[:n] +} + // appendDrawTrianglesHistory appends a draw-image history item to the image. func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint Hint) { if i.stale || !i.needsRestoration() { @@ -411,20 +432,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*I // If the command overwrites the destination region, remove the history items that are in the region. if hint == HintOverwriteDstRegion { - for idx, c := range i.drawTrianglesHistory { - if c.dstRegion.In(dstRegion) { - i.drawTrianglesHistory[idx] = nil - } - } - var n int - for _, c := range i.drawTrianglesHistory { - if c == nil { - continue - } - i.drawTrianglesHistory[n] = c - n++ - } - i.drawTrianglesHistory = i.drawTrianglesHistory[:n] + i.removeDrawTrianglesHistoryItems(dstRegion) } // TODO: Would it be possible to merge draw image history items? From 73565034a9d4e026c3aa33ca67b3c5b7316e366f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 19:29:41 +0900 Subject: [PATCH 146/188] internal/restorable: refactoring --- internal/restorable/image.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index e685f7e76..4a277cec8 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -80,7 +80,7 @@ func (p *Pixels) Dispose() { // drawTrianglesHistoryItem is an item for history of draw-image commands. type drawTrianglesHistoryItem struct { - images [graphics.ShaderSrcImageCount]*Image + srcImages [graphics.ShaderSrcImageCount]*Image vertices []float32 indices []uint32 blend graphicsdriver.Blend @@ -454,7 +454,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*I copy(us, uniforms) item := &drawTrianglesHistoryItem{ - images: srcs, + srcImages: srcs, vertices: vs, indices: is, blend: blend, @@ -593,7 +593,7 @@ func (i *Image) resolveStale(graphicsDriver graphicsdriver.Graphics) error { // dependsOn reports whether the image depends on src. func (i *Image) dependsOn(src *Image) bool { for _, c := range i.drawTrianglesHistory { - for _, img := range c.images { + for _, img := range c.srcImages { if img != src { continue } @@ -617,7 +617,7 @@ func (i *Image) dependsOnShader(shader *Shader) bool { func (i *Image) dependingImages() map[*Image]struct{} { r := map[*Image]struct{}{} for _, c := range i.drawTrianglesHistory { - for _, img := range c.images { + for _, img := range c.srcImages { if img == nil { continue } @@ -671,7 +671,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { for _, c := range i.drawTrianglesHistory { var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image - for i, img := range c.images { + for i, img := range c.srcImages { if img == nil { continue } From af9bd6a28212ab6cf139a565bb95e589401909dc Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 21:41:20 +0900 Subject: [PATCH 147/188] all: specify src-regions correctly This parameter was not important and actually not used with usual shaders. However, now this information will be important for the restorable package optimization later. There was a performance issue that was caused by srcRegions, but now this should not happen thanks to FilterUniformVariables. Updates #1293 --- internal/atlas/image.go | 7 ++- internal/atlas/image_test.go | 59 +++++++++++-------- internal/atlas/shader_test.go | 5 +- internal/buffered/image.go | 3 +- internal/graphicscommand/image_test.go | 12 ++-- internal/mipmap/mipmap.go | 5 +- internal/restorable/images_test.go | 78 ++++++++++++++++---------- internal/restorable/shader_test.go | 13 ++++- internal/ui/image.go | 9 ++- 9 files changed, 121 insertions(+), 70 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 0a395ef1a..71165ea6e 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -284,8 +284,9 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) + sr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) + newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) newI.moveTo(i) } @@ -315,7 +316,8 @@ func (i *Image) putOnSourceBackend() { graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) + sr := image.Rect(0, 0, i.width, i.height) + newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) newI.moveTo(i) i.usedAsSourceCount = 0 @@ -433,6 +435,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice // A source region can be deliberately empty when this is not needed in order to avoid unexpected // performance issue (#1293). + // TODO: This should no longer be needed but is kept just in case. Remove this later. if srcRegions[i].Empty() { continue } diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 0a021b95e..7d9f783f3 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -103,7 +103,8 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { vs := quadVertices(size/2, size/2, size/4, size/4, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, size/2, size/2) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img4.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -111,7 +112,8 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // img5 is not allocated now, but is allocated at DrawTriangles. vs = quadVertices(0, 0, size/2, size/2, 1) dr = image.Rect(0, 0, size/2, size/2) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr = image.Rect(0, 0, size/2, size/2) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -145,7 +147,9 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // Check further drawing doesn't cause panic. // This bug was fixed by 03dcd948. vs = quadVertices(0, 0, size/2, size/2, 1) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + dr = image.Rect(0, 0, size, size) + sr = image.Rect(0, 0, size/2, size/2) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } func TestReputOnSourceBackend(t *testing.T) { @@ -186,10 +190,11 @@ func TestReputOnSourceBackend(t *testing.T) { // Use img1 as a render target. is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) + sr := image.Rect(0, 0, size, size) // Render onto img1. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -201,7 +206,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -209,7 +214,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Finally, img1 is on a source backend. atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -238,7 +243,7 @@ func TestReputOnSourceBackend(t *testing.T) { } vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -268,7 +273,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Use img1 as a render target again. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -280,7 +285,7 @@ func TestReputOnSourceBackend(t *testing.T) { atlas.PutImagesOnSourceBackendForTesting() img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size)) vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -289,7 +294,7 @@ func TestReputOnSourceBackend(t *testing.T) { // img1 is not on an atlas due to WritePixels. vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -298,7 +303,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -401,7 +406,8 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) dst.WritePixels(pix, image.Rect(0, 0, w, h)) pix = make([]byte, 4*w*h) @@ -448,7 +454,8 @@ func TestSmallImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix = make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -495,7 +502,8 @@ func TestLongImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, scale) is := graphics.QuadIndices() dr := image.Rect(0, 0, dstW, dstH) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix = make([]byte, 4*dstW*dstH) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH)) @@ -611,7 +619,8 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { vs := quadVertices(size, size, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - src.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, size, size) + src.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -620,7 +629,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -648,13 +657,14 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { vs := quadVertices(size, size, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) + sr := image.Rect(0, 0, size, size) // Use src2 as a rendering target, and make src2 on a destination backend. // // Call DrawTriangles multiple times. // The number of DrawTriangles doesn't matter as long as these are called in one frame. for i := 0; i < 2; i++ { - src2.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + src2.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) @@ -673,7 +683,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -796,17 +806,18 @@ func TestDestinationCountOverflow(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) + sr := image.Rect(0, 0, w, h) // Use dst0 as a destination for a while. for i := 0; i < 31; i++ { - dst0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + dst0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) atlas.PutImagesOnSourceBackendForTesting() } // Use dst0 as a source for a while. // As dst0 is used as a destination too many times (31 is a maximum), dst0's backend should never be a source backend. for i := 0; i < 100; i++ { - dst1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + dst1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) atlas.PutImagesOnSourceBackendForTesting() if dst0.IsOnSourceBackendForTesting() { t.Errorf("dst0 cannot be on a source backend: %d", i) @@ -831,8 +842,9 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) + sr := image.Rect(0, 0, w, h) for _, img := range srcs { - img.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } atlas.PutImagesOnSourceBackendForTesting() @@ -840,7 +852,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { // Check iterating the registered image works correctly. for i := 0; i < 100; i++ { for _, src := range srcs { - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } atlas.PutImagesOnSourceBackendForTesting() } @@ -888,7 +900,8 @@ func TestDallocateUnmanagedImageBackends(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Get the difference of the number of backends before and after the images are deallocated. c := atlas.BackendCountForTesting() diff --git a/internal/atlas/shader_test.go b/internal/atlas/shader_test.go index a594585fa..569d0f93b 100644 --- a/internal/atlas/shader_test.go +++ b/internal/atlas/shader_test.go @@ -70,11 +70,12 @@ func TestImageDrawTwice(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) diff --git a/internal/buffered/image.go b/internal/buffered/image.go index 62c3e8fab..5c74a41ae 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -310,8 +310,9 @@ func (i *Image) syncPixelsIfNeeded() { srcs := [graphics.ShaderSrcImageCount]*atlas.Image{whiteImage.img} dr := image.Rect(0, 0, i.width, i.height) + sr := image.Rect(0, 0, whiteImage.width, whiteImage.height) blend := graphicsdriver.BlendCopy - i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // TODO: Use clear if Go 1.21 is available. for pos := range i.dotsBuffer { diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index f13f04958..8f8462522 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -56,7 +56,8 @@ func TestClear(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + sr := image.Rect(0, 0, w/2, h/2) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), []graphicsdriver.PixelsArgs{ @@ -87,8 +88,10 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + sr0 := image.Rect(0, 0, w, h) + sr1 := image.Rect(0, 0, w/2, h/2) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr0}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr1}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) bs := graphics.NewManagedBytes(4, func(bs []byte) { for i := range bs { bs[i] = 0 @@ -106,7 +109,8 @@ func TestShader(t *testing.T) { vs := quadVertices(w, h) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) g := ui.Get().GraphicsDriverForTesting() s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff), "") diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index e5f68d62f..389b47f79 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -185,7 +185,10 @@ func (m *Mipmap) level(level int) *buffered.Image { s := buffered.NewImage(w2, h2, m.imageType) dstRegion := image.Rect(0, 0, w2, h2) - s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) + w := sizeForLevel(m.width, level-1) + h := sizeForLevel(m.height, level-1) + srcRegion := image.Rect(0, 0, w, h) + s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{srcRegion}, atlas.LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) m.setImg(level, s) return m.imgs[level] diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index 8c434b119..402918c6e 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -140,7 +140,8 @@ func TestRestoreChain(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, 1, 1) - imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, 1, 1) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) @@ -183,10 +184,11 @@ func TestRestoreChain2(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - imgs[8].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[7]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) - imgs[9].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[8]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + imgs[8].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[7]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + imgs[9].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[8]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) for i := 0; i < 7; i++ { - imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -227,10 +229,11 @@ func TestRestoreOverrideSource(t *testing.T) { img1.WritePixels(bytesToManagedBytes([]byte{clr0.R, clr0.G, clr0.B, clr0.A}), image.Rect(0, 0, w, h)) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img2.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + img2.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img0.WritePixels(bytesToManagedBytes([]byte{clr1.R, clr1.G, clr1.B, clr1.A}), image.Rect(0, 0, w, h)) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -309,24 +312,25 @@ func TestRestoreComplexGraph(t *testing.T) { }() is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) + sr := image.Rect(0, 0, w, h) vs := quadVertices(w, h, 0, 0) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 1, 0) - img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 1, 0) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 2, 0) - img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 0, 0) - img5.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img5.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 0, 0) - img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 1, 0) - img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img4}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img4}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 0, 0) - img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) vs = quadVertices(w, h, 2, 0) - img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -419,8 +423,9 @@ func TestRestoreRecursive(t *testing.T) { }() is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -519,7 +524,8 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, 2, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, 1, 1) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img1.WritePixels(bytesToManagedBytes([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}), image.Rect(0, 0, 2, 1)) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -557,8 +563,9 @@ func TestDispose(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, 1, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) - img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, 1, 1) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img1.Dispose() if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -667,7 +674,8 @@ func TestWritePixelsOnly(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, 1, 1) - img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) img0.WritePixels(bytesToManagedBytes([]byte{5, 6, 7, 8}), image.Rect(0, 0, 1, 1)) // BasePixelsForTesting is available without GPU accessing. @@ -721,7 +729,8 @@ func TestReadPixelsFromVolatileImage(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being // stale. @@ -745,7 +754,8 @@ func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) dst.WritePixels(bytesToManagedBytes(make([]byte, 4*w*h)), image.Rect(0, 0, w, h)) // WritePixels for a whole image doesn't panic. } @@ -764,7 +774,8 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) dst.WritePixels(bytesToManagedBytes(make([]byte, 4*2*2)), image.Rect(0, 0, 2, 2)) // WritePixels for a part of image doesn't panic. @@ -858,7 +869,8 @@ func TestDrawTrianglesAndExtend(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - orig.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + orig.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) extended := orig.Extend(w*2, h*2) // After this, orig is already disposed. result := make([]byte, 4*(w*2)*(h*2)) @@ -908,7 +920,8 @@ func TestMutateSlices(t *testing.T) { is := make([]uint32, len(graphics.QuadIndices())) copy(is, graphics.QuadIndices()) dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) for i := range vs { vs[i] = 0 } @@ -1100,7 +1113,8 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)); err != nil { @@ -1124,7 +1138,8 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) { vs := quadVertices(1, 1, 1, 0) is := graphics.QuadIndices() dr := image.Rect(1, 0, 2, 1) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, 1, 1) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Get the pixels. pix := make([]byte, 4*2*1) @@ -1151,10 +1166,11 @@ func TestOverwriteDstRegion(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, w, h) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // This tests that HintOverwriteDstRegion removes the previous DrawTriangles. // In practice, BlendCopy should be used instead of BlendSourceOver in this case. - dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) + dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion) if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { t.Fatal(err) diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index 2e9901502..41d2c74e9 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -46,7 +46,8 @@ func clearImage(img *restorable.Image, w, h int) { } is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{emptyImage}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, 3, 3) + img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{emptyImage}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } func TestShader(t *testing.T) { @@ -85,7 +86,8 @@ func TestShaderChain(t *testing.T) { s := restorable.NewShader(etesting.ShaderProgramImages(1), "") for i := 0; i < num-1; i++ { dr := image.Rect(0, 0, 1, 1) - imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + sr := image.Rect(0, 0, 1, 1) + imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) } if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil { @@ -117,7 +119,12 @@ func TestShaderMultipleSources(t *testing.T) { s := restorable.NewShader(etesting.ShaderProgramImages(3), "") dr := image.Rect(0, 0, 1, 1) - dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) + srs := [graphics.ShaderSrcImageCount]image.Rectangle{ + image.Rect(0, 0, 1, 1), + image.Rect(0, 0, 1, 1), + image.Rect(0, 0, 1, 1), + } + dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, srs, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone) // Clear one of the sources after DrawTriangles. dst should not be affected. clearImage(srcs[0], 1, 1) diff --git a/internal/ui/image.go b/internal/ui/image.go index 94a03555f..3ffc47571 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -183,8 +183,9 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) { if a == 1 && i.lastBlend == graphicsdriver.BlendSourceOver { blend = graphicsdriver.BlendSourceOver } + sr := image.Rect(0, 0, i.ui.whiteImage.width, i.ui.whiteImage.height) // i.lastBlend is updated in DrawTriangles. - i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion) + i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion) } type bigOffscreenImage struct { @@ -253,7 +254,8 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Im 1, 1, 1, 1) is := graphics.QuadIndices() dstRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale) - i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion) + srcRegion := i.region + i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{srcRegion}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion) } for idx := 0; idx < len(vertices); idx += graphics.VertexFloatCount { @@ -297,13 +299,14 @@ func (i *bigOffscreenImage) flush() { 1, 1, 1, 1) is := graphics.QuadIndices() dstRegion := i.region + srcRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale) blend := graphicsdriver.BlendSourceOver hint := restorable.HintNone if i.blend != graphicsdriver.BlendSourceOver { blend = graphicsdriver.BlendCopy hint = restorable.HintOverwriteDstRegion } - i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, hint) + i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{srcRegion}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, hint) i.image.clear() i.dirty = false From 6a51e5b0038246a692ac78f251418af420bfc627 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 8 Sep 2024 01:04:51 +0900 Subject: [PATCH 148/188] internal/restorable: optimize WritePixels This adds (*Image).makeStaleIfDependingOnWithRegion to reduce the possibility of making an image stale. --- internal/restorable/image.go | 33 ++++++++++++++++++++++++++++++--- internal/restorable/images.go | 13 +++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 4a277cec8..eab5ccc9b 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -277,9 +277,7 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl panic(fmt.Sprintf("restorable: out of range %v", region)) } - // TODO: Avoid making other images stale if possible. (#514) - // For this purpose, images should remember which part of that is used for DrawTriangles. - theImages.makeStaleIfDependingOn(i) + theImages.makeStaleIfDependingOnAtRegion(i, region) if pixels != nil { i.image.WritePixels(pixels, region) @@ -343,6 +341,9 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice if len(vertices) == 0 { return } + + // makeStaleIfDependingOnAtRegion is not available here. + // This might create cyclic dependency. theImages.makeStaleIfDependingOn(i) // TODO: Add tests to confirm this logic. @@ -510,6 +511,17 @@ func (i *Image) makeStaleIfDependingOn(src *Image) { } } +// makeStaleIfDependingOnAtRegion makes the image stale if the image depends on src at srcRegion. +func (i *Image) makeStaleIfDependingOnAtRegion(src *Image, srcRegion image.Rectangle) { + if i.stale { + return + } + if i.dependsOnAtRegion(src, srcRegion) { + // There is no new region to make stale. + i.makeStale(image.Rectangle{}) + } +} + // makeStaleIfDependingOnShader makes the image stale if the image depends on shader. func (i *Image) makeStaleIfDependingOnShader(shader *Shader) { if i.stale { @@ -603,6 +615,21 @@ func (i *Image) dependsOn(src *Image) bool { return false } +// dependsOnAtRegion reports whether the image depends on src at srcRegion. +func (i *Image) dependsOnAtRegion(src *Image, srcRegion image.Rectangle) bool { + for _, c := range i.drawTrianglesHistory { + for i, img := range c.srcImages { + if img != src { + continue + } + if c.srcRegions[i].Overlaps(srcRegion) { + return true + } + } + } + return false +} + // dependsOnShader reports whether the image depends on shader. func (i *Image) dependsOnShader(shader *Shader) bool { for _, c := range i.drawTrianglesHistory { diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 1dedfce78..dce2bfa32 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -177,6 +177,19 @@ func (i *images) makeStaleIfDependingOn(src *Image) { } } +// makeStaleIfDependingOnAtRegion makes all the images stale that depend on src at srcRegion. +// +// When src is modified, all images depending on src can't be restored with src at srcRegion. +// makeStaleIfDependingOnAtRegion is called in such situation. +func (i *images) makeStaleIfDependingOnAtRegion(src *Image, srcRegion image.Rectangle) { + if src == nil { + panic("restorable: src must not be nil at makeStaleIfDependingOnAtRegion") + } + for img := range i.images { + img.makeStaleIfDependingOnAtRegion(src, srcRegion) + } +} + // makeStaleIfDependingOn makes all the images stale that depend on shader. func (i *images) makeStaleIfDependingOnShader(shader *Shader) { if shader == nil { From fcef0a7c29ef6dfd148921e0aaeaf10fce21153b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 15:20:04 +0900 Subject: [PATCH 149/188] cmd/ebitenmobile: call setPreserveEGLContextOnPause(true) whatever the option is We found that an app freezes for a little while when resuming it. In order to improve user experience, always use setPreserveEGLContextOnPause(true) whichever StrictContextRestoration is true or false. --- cmd/ebitenmobile/_files/EbitenSurfaceView.java | 2 +- run.go | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cmd/ebitenmobile/_files/EbitenSurfaceView.java b/cmd/ebitenmobile/_files/EbitenSurfaceView.java index 000e5f7db..63a82d995 100644 --- a/cmd/ebitenmobile/_files/EbitenSurfaceView.java +++ b/cmd/ebitenmobile/_files/EbitenSurfaceView.java @@ -96,6 +96,7 @@ class EbitenSurfaceView extends GLSurfaceView implements Renderer { private void initialize() { setEGLContextClientVersion(3); setEGLConfigChooser(8, 8, 8, 8, 0, 0); + setPreserveEGLContextOnPause(true); // setRenderer must be called before setRenderRequester. // Or, the application crashes. setRenderer(new EbitenRenderer()); @@ -125,7 +126,6 @@ class EbitenSurfaceView extends GLSurfaceView implements Renderer { @Override public synchronized void setStrictContextRestoration(boolean strictContextRestoration) { strictContextRestoration_ = strictContextRestoration; - setPreserveEGLContextOnPause(!strictContextRestoration); } private synchronized boolean hasStrictContextRestoration() { diff --git a/run.go b/run.go index bb97c5c7f..b2bb37143 100644 --- a/run.go +++ b/run.go @@ -302,17 +302,20 @@ type RunGameOptions struct { // // StrictContextRestration is available only on Android. Otherwise, StrictContextRestration is ignored. // - // When StrictContextRestration is false, Ebitengine tries to rely on the OS to restore the context. // In Android, Ebitengien uses `GLSurfaceView`'s `setPreserveEGLContextOnPause(true)`. // This works in most cases, but it is still possible that the context is lost in some minor cases. - // With StrictContextRestration false, the activity's launch mode should be singleInstance, - // or the activity no longer works correctly after the context is lost. // - // When StrictContextRestration is true, Ebitengine tries to restore the context more strictly. - // This is useful when you want to restore the context in any case. + // When StrictContextRestration is true, Ebitengine tries to restore the context more strictly + // for such minor cases. // However, this might cause a performance issue since Ebitengine tries to keep all the information // to restore the context. // + // When StrictContextRestration is false, Ebitengine does nothing special to restore the context and + // relies on the OS's behavior. + // + // As a side note, especially when StrictContextRestration is false, the activity's launch mode should + // be singleInstance, or the activity no longer works correctly after the context is lost. + // // The default (zero) value is false. StrictContextRestration bool } From 07d29fa729ccbc8aa6c6b522722bd5d528c3e13e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 16:27:54 +0900 Subject: [PATCH 150/188] cmd/ebitenmobile: bug fix: consider EbitenSurfaceView recreation On Android Emulator (Small Desktop API 32), EbitenRenderer can be easily recreated by resizing the window. Thus, EbitenRenderer should not have any flags like strictContextRestoration. Also, the flag onceSurfaceCreated_ doesn't work there. --- .../_files/EbitenSurfaceView.java | 20 +++++-------------- internal/ui/ui_mobile.go | 13 +++++++----- mobile/ebitenmobileview/mobile.go | 4 ++++ 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/cmd/ebitenmobile/_files/EbitenSurfaceView.java b/cmd/ebitenmobile/_files/EbitenSurfaceView.java index 63a82d995..f645ddb0a 100644 --- a/cmd/ebitenmobile/_files/EbitenSurfaceView.java +++ b/cmd/ebitenmobile/_files/EbitenSurfaceView.java @@ -59,12 +59,13 @@ class EbitenSurfaceView extends GLSurfaceView implements Renderer { @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { - if (!onceSurfaceCreated_) { - onceSurfaceCreated_ = true; + // As EbitenSurfaceView can be recreated anytime, this flag for strict context restoration must be checked every time. + if (Ebitenmobileview.usesStrictContextRestoration()) { + Ebitenmobileview.onContextLost(); return; } - if (hasStrictContextRestoration()) { - Ebitenmobileview.onContextLost(); + if (!onceSurfaceCreated_) { + onceSurfaceCreated_ = true; return; } contextLost_ = true; @@ -81,8 +82,6 @@ class EbitenSurfaceView extends GLSurfaceView implements Renderer { } } - private boolean strictContextRestoration_ = false; - public EbitenSurfaceView(Context context) { super(context); initialize(); @@ -123,15 +122,6 @@ class EbitenSurfaceView extends GLSurfaceView implements Renderer { } } - @Override - public synchronized void setStrictContextRestoration(boolean strictContextRestoration) { - strictContextRestoration_ = strictContextRestoration; - } - - private synchronized boolean hasStrictContextRestoration() { - return strictContextRestoration_; - } - @Override public synchronized void requestRenderIfNeeded() { if (getRenderMode() == RENDERMODE_WHEN_DIRTY) { diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index be0ed62bb..d755798e3 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -101,7 +101,7 @@ type userInterfaceImpl struct { fpsMode atomic.Int32 renderer Renderer - strictContextRestoration bool + strictContextRestoration atomic.Bool strictContextRestorationOnce sync.Once m sync.RWMutex @@ -156,11 +156,11 @@ func (u *UserInterface) runMobile(game Game, options *RunOptions) (err error) { u.graphicsDriver = g u.setGraphicsLibrary(lib) close(u.graphicsLibraryInitCh) - u.strictContextRestoration = options.StrictContextRestoration - if !u.strictContextRestoration { + if options.StrictContextRestoration { + u.strictContextRestoration.Store(true) + } else { restorable.Disable() } - u.renderer.SetStrictContextRestoration(u.strictContextRestoration) for { if err := u.update(); err != nil { @@ -312,7 +312,6 @@ func (u *UserInterface) UpdateInput(keys map[Key]struct{}, runes []rune, touches type Renderer interface { SetExplicitRenderingMode(explicitRendering bool) - SetStrictContextRestoration(strictContextRestoration bool) RequestRenderIfNeeded() } @@ -331,6 +330,10 @@ func (u *UserInterface) updateIconIfNeeded() error { return nil } +func (u *UserInterface) UsesStrictContextRestoration() bool { + return u.strictContextRestoration.Load() +} + func IsScreenTransparentAvailable() bool { return false } diff --git a/mobile/ebitenmobileview/mobile.go b/mobile/ebitenmobileview/mobile.go index 9c3ad5ff5..7344b817e 100644 --- a/mobile/ebitenmobileview/mobile.go +++ b/mobile/ebitenmobileview/mobile.go @@ -132,3 +132,7 @@ func SetRenderer(renderer Renderer) { func SetSetGameNotifier(setGameNotifier SetGameNotifier) { theState.setSetGameNotifier(setGameNotifier) } + +func UsesStrictContextRestoration() bool { + return ui.Get().UsesStrictContextRestoration() +} From 7cc2f8ffcd836c4f8cbb0590b54b3bcb35974af5 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 17:41:17 +0900 Subject: [PATCH 151/188] cmd/ebitenmobile: add comments --- cmd/ebitenmobile/_files/EbitenSurfaceView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/ebitenmobile/_files/EbitenSurfaceView.java b/cmd/ebitenmobile/_files/EbitenSurfaceView.java index f645ddb0a..f3a6bb161 100644 --- a/cmd/ebitenmobile/_files/EbitenSurfaceView.java +++ b/cmd/ebitenmobile/_files/EbitenSurfaceView.java @@ -115,6 +115,8 @@ class EbitenSurfaceView extends GLSurfaceView implements Renderer { @Override public synchronized void setExplicitRenderingMode(boolean explicitRendering) { + // TODO: Remove this logic when FPSModeVsyncOffMinimum is removed. + // This doesn't work when EbitenSurfaceView is recreated anyway. if (explicitRendering) { setRenderMode(RENDERMODE_WHEN_DIRTY); } else { From 2a4374e0127098d7494731a0b9f0782b123fd30a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 18:05:18 +0900 Subject: [PATCH 152/188] cmd/ebitenmobile: remove setStrictContextRestoration from EbitenViewController --- cmd/ebitenmobile/_files/EbitenViewController.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmd/ebitenmobile/_files/EbitenViewController.m b/cmd/ebitenmobile/_files/EbitenViewController.m index a5f5cf90c..e873272bd 100644 --- a/cmd/ebitenmobile/_files/EbitenViewController.m +++ b/cmd/ebitenmobile/_files/EbitenViewController.m @@ -364,10 +364,6 @@ } } -- (void)setStrictContextRestoration:(BOOL)strictContextRestoration { - // Do nothing. -} - - (void)setExplicitRenderingMode:(BOOL)explicitRendering { @synchronized(self) { explicitRendering_ = explicitRendering; From a786f23e28e7f7f5eae8425d0d37656f84985e8f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 20:55:03 +0900 Subject: [PATCH 153/188] cmd/ebitenmobile: add comments --- cmd/ebitenmobile/_files/EbitenSurfaceView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/ebitenmobile/_files/EbitenSurfaceView.java b/cmd/ebitenmobile/_files/EbitenSurfaceView.java index f3a6bb161..f682794eb 100644 --- a/cmd/ebitenmobile/_files/EbitenSurfaceView.java +++ b/cmd/ebitenmobile/_files/EbitenSurfaceView.java @@ -96,8 +96,7 @@ class EbitenSurfaceView extends GLSurfaceView implements Renderer { setEGLContextClientVersion(3); setEGLConfigChooser(8, 8, 8, 8, 0, 0); setPreserveEGLContextOnPause(true); - // setRenderer must be called before setRenderRequester. - // Or, the application crashes. + // setRenderer must be called before setRenderer. Or, setRenderMode in setExplicitRenderingMode will crash. setRenderer(new EbitenRenderer()); Ebitenmobileview.setRenderer(this); From 15dfb02f9fa7cf633a87332c60020485a1a2e231 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 22:43:38 +0900 Subject: [PATCH 154/188] ebiten: update the comment for CursorPosition CursorPosition should return a mouse cursor position on browsers even in mobiles. Closes #3087 --- input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.go b/input.go index 70d02bf47..7b919cef8 100644 --- a/input.go +++ b/input.go @@ -79,7 +79,7 @@ func KeyName(key Key) string { // // CursorPosition returns (0, 0) before the main loop on desktops and browsers. // -// CursorPosition always returns (0, 0) on mobiles. +// CursorPosition always returns (0, 0) on mobile native applications. // // CursorPosition is concurrent-safe. func CursorPosition() (x, y int) { From e058bb6fd323b4029a054af8a16007f31aee42d7 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 23:07:30 +0900 Subject: [PATCH 155/188] all: update go-text Unfortunately this adds a breaking change to (*GoTextFaceSource).UnsafeInternal. Updates #3082 --- go.mod | 2 +- go.sum | 6 ++-- text/v2/gotext.go | 7 ++--- text/v2/gotextfacesource.go | 43 ++++++++++++++-------------- text/v2/gotextseg.go | 28 +++++++++---------- text/v2/gox.go | 4 ++- text/v2/metadata.go | 56 ++++++++++++++++++------------------- 7 files changed, 74 insertions(+), 72 deletions(-) diff --git a/go.mod b/go.mod index cd060413d..dde067e15 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/ebitengine/oto/v3 v3.3.0-alpha.4 github.com/ebitengine/purego v0.8.0-alpha.5 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f - github.com/go-text/typesetting v0.1.1 + github.com/go-text/typesetting v0.1.1-0.20240522210117-2c045476f496 github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.5 github.com/hajimehoshi/go-mp3 v0.3.4 github.com/jakecoffman/cp v1.2.1 diff --git a/go.sum b/go.sum index eb3e0f9dc..aeccacb52 100644 --- a/go.sum +++ b/go.sum @@ -8,9 +8,9 @@ github.com/ebitengine/purego v0.8.0-alpha.5 h1:M0+PSgsdVNczTB8ijX89HmYqCfb2HUuBE github.com/ebitengine/purego v0.8.0-alpha.5/go.mod h1:SQ56/omnSL8DdaBSKswoBvsMjgaWQyxyeMtb48sOskI= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38= -github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= -github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= -github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= +github.com/go-text/typesetting v0.1.1-0.20240522210117-2c045476f496 h1:zgx3rOyOdRoA2GXWpfJkH7Zg248ookseRifdn9VSp5g= +github.com/go-text/typesetting v0.1.1-0.20240522210117-2c045476f496/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.5 h1:gtIcN2INlD2qlfUiECuvbI0moNIoANgIY7MwgW4cFGE= diff --git a/text/v2/gotext.go b/text/v2/gotext.go index 227cc92b0..97a9d1996 100644 --- a/text/v2/gotext.go +++ b/text/v2/gotext.go @@ -20,9 +20,8 @@ import ( "fmt" "github.com/go-text/typesetting/di" + "github.com/go-text/typesetting/font" glanguage "github.com/go-text/typesetting/language" - "github.com/go-text/typesetting/opentype/api/font" - "github.com/go-text/typesetting/opentype/loader" "github.com/go-text/typesetting/shaping" "golang.org/x/image/math/fixed" "golang.org/x/text/language" @@ -89,7 +88,7 @@ func (g *GoTextFace) SetVariation(tag Tag, value float32) { g.variations = append(g.variations, font.Variation{}) copy(g.variations[idx+1:], g.variations[idx:]) g.variations[idx] = font.Variation{ - Tag: loader.Tag(tag), + Tag: font.Tag(tag), Value: value, } g.variationsString = "" @@ -136,7 +135,7 @@ func (g *GoTextFace) SetFeature(tag Tag, value uint32) { g.features = append(g.features, shaping.FontFeature{}) copy(g.features[idx+1:], g.features[idx:]) g.features[idx] = shaping.FontFeature{ - Tag: loader.Tag(tag), + Tag: font.Tag(tag), Value: value, } g.featuresString = "" diff --git a/text/v2/gotextfacesource.go b/text/v2/gotextfacesource.go index 3e84adde6..a349088f4 100644 --- a/text/v2/gotextfacesource.go +++ b/text/v2/gotextfacesource.go @@ -20,10 +20,8 @@ import ( "sync" "github.com/go-text/typesetting/font" + "github.com/go-text/typesetting/font/opentype" "github.com/go-text/typesetting/language" - "github.com/go-text/typesetting/opentype/api" - ofont "github.com/go-text/typesetting/opentype/api/font" - "github.com/go-text/typesetting/opentype/loader" "github.com/go-text/typesetting/shaping" "golang.org/x/image/math/fixed" @@ -44,7 +42,7 @@ type glyph struct { shapingGlyph *shaping.Glyph startIndex int endIndex int - scaledSegments []api.Segment + scaledSegments []opentype.Segment bounds fixed.Rectangle26_6 } @@ -55,7 +53,7 @@ type goTextOutputCacheValue struct { } type goTextGlyphImageCacheKey struct { - gid api.GID + gid opentype.GID xoffset fixed.Int26_6 yoffset fixed.Int26_6 variations string @@ -63,7 +61,7 @@ type goTextGlyphImageCacheKey struct { // GoTextFaceSource is a source of a GoTextFace. This can be shared by multiple GoTextFace objects. type GoTextFaceSource struct { - f font.Face + f *font.Face metadata Metadata outputCache map[goTextOutputCacheKey]*goTextOutputCacheValue @@ -101,18 +99,18 @@ func NewGoTextFaceSource(source io.Reader) (*GoTextFaceSource, error) { return nil, err } - l, err := loader.NewLoader(src) + l, err := opentype.NewLoader(src) if err != nil { return nil, err } - f, err := ofont.NewFont(l) + f, err := font.NewFont(l) if err != nil { return nil, err } s := &GoTextFaceSource{ - f: &ofont.Face{Font: f}, + f: font.NewFace(f), } s.addr = s s.metadata = metadataFromLoader(l) @@ -127,19 +125,19 @@ func NewGoTextFaceSourcesFromCollection(source io.Reader) ([]*GoTextFaceSource, return nil, err } - ls, err := loader.NewLoaders(src) + ls, err := opentype.NewLoaders(src) if err != nil { return nil, err } sources := make([]*GoTextFaceSource, len(ls)) for i, l := range ls { - f, err := ofont.NewFont(l) + f, err := font.NewFont(l) if err != nil { return nil, err } s := &GoTextFaceSource{ - f: &ofont.Face{Font: f}, + f: &font.Face{Font: f}, } s.addr = s s.metadata = metadataFromLoader(l) @@ -160,9 +158,12 @@ func (g *GoTextFaceSource) Metadata() Metadata { } // UnsafeInternal returns its font.Face. +// The return value type is any since github.com/go-text/typesettings's API is now unstable. // -// This is unsafe since this might make internal cache states out of sync. -func (g *GoTextFaceSource) UnsafeInternal() font.Face { +// UnsafeInternal is unsafe since this might make internal cache states out of sync. +// +// UnsafeInternal might have breaking changes even in the same major version. +func (g *GoTextFaceSource) UnsafeInternal() any { return g.f } @@ -220,22 +221,22 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu for _, gl := range out.Glyphs { gl := gl - var segs []api.Segment + var segs []opentype.Segment switch data := g.f.GlyphData(gl.GlyphID).(type) { - case api.GlyphOutline: + case font.GlyphOutline: if out.Direction.IsSideways() { data.Sideways(fixed26_6ToFloat32(-gl.YOffset) / fixed26_6ToFloat32(out.Size) * float32(f.Upem())) } segs = data.Segments - case api.GlyphSVG: + case font.GlyphSVG: segs = data.Outline.Segments - case api.GlyphBitmap: + case font.GlyphBitmap: if data.Outline != nil { segs = data.Outline.Segments } } - scaledSegs := make([]api.Segment, len(segs)) + scaledSegs := make([]opentype.Segment, len(segs)) scale := float32(g.scale(fixed26_6ToFloat64(out.Size))) for i, seg := range segs { scaledSegs[i] = seg @@ -293,9 +294,9 @@ func (g *GoTextFaceSource) getOrCreateGlyphImage(goTextFace *GoTextFace, key goT } type singleFontmap struct { - face font.Face + face *font.Face } -func (s *singleFontmap) ResolveFace(r rune) font.Face { +func (s *singleFontmap) ResolveFace(r rune) *font.Face { return s.face } diff --git a/text/v2/gotextseg.go b/text/v2/gotextseg.go index 4875bb4ba..40c5ecd88 100644 --- a/text/v2/gotextseg.go +++ b/text/v2/gotextseg.go @@ -19,7 +19,7 @@ import ( "image/draw" "math" - "github.com/go-text/typesetting/opentype/api" + "github.com/go-text/typesetting/font/opentype" "golang.org/x/image/math/fixed" gvector "golang.org/x/image/vector" @@ -27,7 +27,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" ) -func segmentsToBounds(segs []api.Segment) fixed.Rectangle26_6 { +func segmentsToBounds(segs []opentype.Segment) fixed.Rectangle26_6 { if len(segs) == 0 { return fixed.Rectangle26_6{} } @@ -40,9 +40,9 @@ func segmentsToBounds(segs []api.Segment) fixed.Rectangle26_6 { for _, seg := range segs { n := 1 switch seg.Op { - case api.SegmentOpQuadTo: + case opentype.SegmentOpQuadTo: n = 2 - case api.SegmentOpCubeTo: + case opentype.SegmentOpCubeTo: n = 3 } for i := 0; i < n; i++ { @@ -75,7 +75,7 @@ func segmentsToBounds(segs []api.Segment) fixed.Rectangle26_6 { } } -func segmentsToImage(segs []api.Segment, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image { +func segmentsToImage(segs []opentype.Segment, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image { if len(segs) == 0 { return nil } @@ -97,16 +97,16 @@ func segmentsToImage(segs []api.Segment, subpixelOffset fixed.Point26_6, glyphBo rast.DrawOp = draw.Src for _, seg := range segs { switch seg.Op { - case api.SegmentOpMoveTo: + case opentype.SegmentOpMoveTo: rast.MoveTo(seg.Args[0].X+biasX, seg.Args[0].Y+biasY) - case api.SegmentOpLineTo: + case opentype.SegmentOpLineTo: rast.LineTo(seg.Args[0].X+biasX, seg.Args[0].Y+biasY) - case api.SegmentOpQuadTo: + case opentype.SegmentOpQuadTo: rast.QuadTo( seg.Args[0].X+biasX, seg.Args[0].Y+biasY, seg.Args[1].X+biasX, seg.Args[1].Y+biasY, ) - case api.SegmentOpCubeTo: + case opentype.SegmentOpCubeTo: rast.CubeTo( seg.Args[0].X+biasX, seg.Args[0].Y+biasY, seg.Args[1].X+biasX, seg.Args[1].Y+biasY, @@ -125,19 +125,19 @@ func segmentsToImage(segs []api.Segment, subpixelOffset fixed.Point26_6, glyphBo return ebiten.NewImageFromImage(dst) } -func appendVectorPathFromSegments(path *vector.Path, segs []api.Segment, x, y float32) { +func appendVectorPathFromSegments(path *vector.Path, segs []opentype.Segment, x, y float32) { for _, seg := range segs { switch seg.Op { - case api.SegmentOpMoveTo: + case opentype.SegmentOpMoveTo: path.MoveTo(seg.Args[0].X+x, seg.Args[0].Y+y) - case api.SegmentOpLineTo: + case opentype.SegmentOpLineTo: path.LineTo(seg.Args[0].X+x, seg.Args[0].Y+y) - case api.SegmentOpQuadTo: + case opentype.SegmentOpQuadTo: path.QuadTo( seg.Args[0].X+x, seg.Args[0].Y+y, seg.Args[1].X+x, seg.Args[1].Y+y, ) - case api.SegmentOpCubeTo: + case opentype.SegmentOpCubeTo: path.CubicTo( seg.Args[0].X+x, seg.Args[0].Y+y, seg.Args[1].X+x, seg.Args[1].Y+y, diff --git a/text/v2/gox.go b/text/v2/gox.go index 13914db94..b60348cae 100644 --- a/text/v2/gox.go +++ b/text/v2/gox.go @@ -79,7 +79,9 @@ func (s *GoXFace) Metrics() Metrics { // UnsafeInternal returns its internal font.Face. // -// This is unsafe since this might make internal cache states out of sync. +// UnsafeInternal is unsafe since this might make internal cache states out of sync. +// +// UnsafeInternal might have breaking changes even in the same major version. func (s *GoXFace) UnsafeInternal() font.Face { s.copyCheck() return s.f.f diff --git a/text/v2/metadata.go b/text/v2/metadata.go index 27c72af0a..abd89c1bd 100644 --- a/text/v2/metadata.go +++ b/text/v2/metadata.go @@ -15,8 +15,8 @@ package text import ( - "github.com/go-text/typesetting/opentype/api/metadata" - "github.com/go-text/typesetting/opentype/loader" + "github.com/go-text/typesetting/font" + "github.com/go-text/typesetting/font/opentype" ) // Metadata represents a font face's metadata. @@ -27,47 +27,47 @@ type Metadata struct { Stretch Stretch } -func metadataFromLoader(l *loader.Loader) Metadata { - f, a, _ := metadata.Describe(l, nil) +func metadataFromLoader(l *opentype.Loader) Metadata { + d, _ := font.Describe(l, nil) return Metadata{ - Family: f, - Style: Style(a.Style), - Weight: Weight(a.Weight), - Stretch: Stretch(a.Stretch), + Family: d.Family, + Style: Style(d.Aspect.Style), + Weight: Weight(d.Aspect.Weight), + Stretch: Stretch(d.Aspect.Stretch), } } type Style uint8 const ( - StyleNormal Style = Style(metadata.StyleNormal) - StyleItalic Style = Style(metadata.StyleItalic) + StyleNormal Style = Style(font.StyleNormal) + StyleItalic Style = Style(font.StyleItalic) ) type Weight float32 const ( - WeightThin Weight = Weight(metadata.WeightThin) - WeightExtraLight Weight = Weight(metadata.WeightExtraLight) - WeightLight Weight = Weight(metadata.WeightLight) - WeightNormal Weight = Weight(metadata.WeightNormal) - WeightMedium Weight = Weight(metadata.WeightMedium) - WeightSemibold Weight = Weight(metadata.WeightSemibold) - WeightBold Weight = Weight(metadata.WeightBold) - WeightExtraBold Weight = Weight(metadata.WeightExtraBold) - WeightBlack Weight = Weight(metadata.WeightBlack) + WeightThin Weight = Weight(font.WeightThin) + WeightExtraLight Weight = Weight(font.WeightExtraLight) + WeightLight Weight = Weight(font.WeightLight) + WeightNormal Weight = Weight(font.WeightNormal) + WeightMedium Weight = Weight(font.WeightMedium) + WeightSemibold Weight = Weight(font.WeightSemibold) + WeightBold Weight = Weight(font.WeightBold) + WeightExtraBold Weight = Weight(font.WeightExtraBold) + WeightBlack Weight = Weight(font.WeightBlack) ) type Stretch float32 const ( - StretchUltraCondensed Stretch = Stretch(metadata.StretchUltraCondensed) - StretchExtraCondensed Stretch = Stretch(metadata.StretchExtraCondensed) - StretchCondensed Stretch = Stretch(metadata.StretchCondensed) - StretchSemiCondensed Stretch = Stretch(metadata.StretchSemiCondensed) - StretchNormal Stretch = Stretch(metadata.StretchNormal) - StretchSemiExpanded Stretch = Stretch(metadata.StretchSemiExpanded) - StretchExpanded Stretch = Stretch(metadata.StretchExpanded) - StretchExtraExpanded Stretch = Stretch(metadata.StretchExtraExpanded) - StretchUltraExpanded Stretch = Stretch(metadata.StretchUltraExpanded) + StretchUltraCondensed Stretch = Stretch(font.StretchUltraCondensed) + StretchExtraCondensed Stretch = Stretch(font.StretchExtraCondensed) + StretchCondensed Stretch = Stretch(font.StretchCondensed) + StretchSemiCondensed Stretch = Stretch(font.StretchSemiCondensed) + StretchNormal Stretch = Stretch(font.StretchNormal) + StretchSemiExpanded Stretch = Stretch(font.StretchSemiExpanded) + StretchExpanded Stretch = Stretch(font.StretchExpanded) + StretchExtraExpanded Stretch = Stretch(font.StretchExtraExpanded) + StretchUltraExpanded Stretch = Stretch(font.StretchUltraExpanded) ) From df821f01774638bf8cb78f387ad81fb0a4b5adcb Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 10 Sep 2024 01:42:46 +0900 Subject: [PATCH 156/188] ebiten: add a warning about StrictContextRestoration Updates #3089 --- run.go | 1 + 1 file changed, 1 insertion(+) diff --git a/run.go b/run.go index b2bb37143..cf53aea7e 100644 --- a/run.go +++ b/run.go @@ -301,6 +301,7 @@ type RunGameOptions struct { // StrictContextRestration indicates whether the context lost should be restored strictly by Ebitengine or not. // // StrictContextRestration is available only on Android. Otherwise, StrictContextRestration is ignored. + // Thus, StrictContextRestration should be used with mobile.SetGameWithOptions, rather than RunGameWithOptions. // // In Android, Ebitengien uses `GLSurfaceView`'s `setPreserveEGLContextOnPause(true)`. // This works in most cases, but it is still possible that the context is lost in some minor cases. From 26feb2623754c17107005ac6376e2b4363a7be53 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 10 Sep 2024 12:20:11 +0900 Subject: [PATCH 157/188] audio: add more info for errors Updates ebitengine/oto#93 --- audio/context.go | 2 +- audio/error_ios.go | 6 +++--- audio/error_notios.go | 2 +- audio/player.go | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/audio/context.go b/audio/context.go index 72c48fa90..f0983a221 100644 --- a/audio/context.go +++ b/audio/context.go @@ -26,7 +26,7 @@ func newContext(sampleRate int) (context, chan struct{}, error) { ChannelCount: channelCount, Format: oto.FormatFloat32LE, }) - err = addErrorInfoForContextCreation(err) + err = addErrorInfo(err) return &contextProxy{ctx}, ready, err } diff --git a/audio/error_ios.go b/audio/error_ios.go index 404359452..e111d316b 100644 --- a/audio/error_ios.go +++ b/audio/error_ios.go @@ -35,9 +35,9 @@ import ( "fmt" ) -// addErrorInfoForContextCreation adds an additional information to the error when creating an audio context. -// See also hajimehoshi/oto#93. -func addErrorInfoForContextCreation(err error) error { +// addErrorInfo adds an additional information to the error when creating an audio context. +// See also ebitengine/oto#93. +func addErrorInfo(err error) error { if err == nil { return nil } diff --git a/audio/error_notios.go b/audio/error_notios.go index 1afda4f2e..97f1570a2 100644 --- a/audio/error_notios.go +++ b/audio/error_notios.go @@ -16,6 +16,6 @@ package audio -func addErrorInfoForContextCreation(err error) error { +func addErrorInfo(err error) error { return err } diff --git a/audio/player.go b/audio/player.go index 252c182b0..f6436b445 100644 --- a/audio/player.go +++ b/audio/player.go @@ -112,7 +112,7 @@ func (f *playerFactory) suspend() error { if f.context == nil { return nil } - return f.context.Suspend() + return addErrorInfo(f.context.Suspend()) } func (f *playerFactory) resume() error { @@ -122,7 +122,7 @@ func (f *playerFactory) resume() error { if f.context == nil { return nil } - return f.context.Resume() + return addErrorInfo(f.context.Resume()) } func (f *playerFactory) error() error { @@ -132,7 +132,7 @@ func (f *playerFactory) error() error { if f.context == nil { return nil } - return f.context.Err() + return addErrorInfo(f.context.Err()) } func (f *playerFactory) initContextIfNeeded() (<-chan struct{}, error) { @@ -263,7 +263,7 @@ func (p *playerImpl) Close() error { }() p.player.Pause() p.stopwatch.stop() - return p.player.Close() + return addErrorInfo(p.player.Close()) } return nil } @@ -293,7 +293,7 @@ func (p *playerImpl) SetPosition(offset time.Duration) error { pos := p.stream.timeDurationToPos(offset) if _, err := p.player.Seek(pos, io.SeekStart); err != nil { - return err + return addErrorInfo(err) } p.lastSamples = -1 // Just after setting a position, the buffer size should be 0 as no data is sent. @@ -312,7 +312,7 @@ func (p *playerImpl) Err() error { if p.player == nil { return nil } - return p.player.Err() + return addErrorInfo(p.player.Err()) } func (p *playerImpl) SetBufferSize(bufferSize time.Duration) { From 813e3b28afb154a66bd8d7ffd91aadbd11545a6b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 9 Sep 2024 23:51:13 +0900 Subject: [PATCH 158/188] text/v2: add Metrics.XHeight and Metrics.CapHeight Closes #3082 --- text/v2/gotext.go | 15 ++ text/v2/gox.go | 28 ++- text/v2/multi.go | 6 + text/v2/testdata/LICENSE.md | 310 +++++++++++++++++++++++++++ text/v2/testdata/MPLUS1p-Regular.ttf | Bin 0 -> 1754752 bytes text/v2/testdata/Roboto-Regular.ttf | Bin 0 -> 168260 bytes text/v2/text.go | 6 + text/v2/text_test.go | 77 +++++++ 8 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 text/v2/testdata/LICENSE.md create mode 100644 text/v2/testdata/MPLUS1p-Regular.ttf create mode 100644 text/v2/testdata/Roboto-Regular.ttf diff --git a/text/v2/gotext.go b/text/v2/gotext.go index 97a9d1996..f26abe7cb 100644 --- a/text/v2/gotext.go +++ b/text/v2/gotext.go @@ -201,6 +201,21 @@ func (g *GoTextFace) Metrics() Metrics { m.VDescent = float64(-v.Descender) * scale } + m.XHeight = float64(g.Source.f.LineMetric(font.XHeight)) * scale + m.CapHeight = float64(g.Source.f.LineMetric(font.CapHeight)) * scale + + // XHeight and CapHeight might not be correct for some old fonts (go-text/typesetting#169). + if m.XHeight <= 0 { + if _, gs := g.Source.shape("x", g); len(gs) > 0 { + m.XHeight = fixed26_6ToFloat64(-gs[0].bounds.Min.Y) + } + } + if m.CapHeight <= 0 { + if _, gs := g.Source.shape("H", g); len(gs) > 0 { + m.CapHeight = fixed26_6ToFloat64(-gs[0].bounds.Min.Y) + } + } + return m } diff --git a/text/v2/gox.go b/text/v2/gox.go index b60348cae..8143663f5 100644 --- a/text/v2/gox.go +++ b/text/v2/gox.go @@ -45,6 +45,8 @@ type GoXFace struct { glyphImageCache glyphImageCache[goXFaceGlyphImageCacheKey] + cachedMetrics Metrics + addr *GoXFace } @@ -69,12 +71,28 @@ func (s *GoXFace) copyCheck() { func (s *GoXFace) Metrics() Metrics { s.copyCheck() - m := s.f.Metrics() - return Metrics{ - HLineGap: fixed26_6ToFloat64(m.Height - m.Ascent - m.Descent), - HAscent: fixed26_6ToFloat64(m.Ascent), - HDescent: fixed26_6ToFloat64(m.Descent), + if s.cachedMetrics != (Metrics{}) { + return s.cachedMetrics } + + fm := s.f.Metrics() + m := Metrics{ + HLineGap: fixed26_6ToFloat64(fm.Height - fm.Ascent - fm.Descent), + HAscent: fixed26_6ToFloat64(fm.Ascent), + HDescent: fixed26_6ToFloat64(fm.Descent), + XHeight: fixed26_6ToFloat64(fm.XHeight), + CapHeight: fixed26_6ToFloat64(fm.CapHeight), + } + + // There is an issue that XHeight is negative for some old fonts (golang/go#69378). + if fm.XHeight < 0 { + m.XHeight *= -1 + } + if fm.CapHeight < 0 { + m.CapHeight *= -1 + } + s.cachedMetrics = m + return m } // UnsafeInternal returns its internal font.Face. diff --git a/text/v2/multi.go b/text/v2/multi.go index d84ace585..2903c4afc 100644 --- a/text/v2/multi.go +++ b/text/v2/multi.go @@ -75,6 +75,12 @@ func (m *MultiFace) Metrics() Metrics { if mt1.VDescent > mt.VDescent { mt.VDescent = mt1.VDescent } + if mt1.XHeight > mt.XHeight { + mt.XHeight = mt1.XHeight + } + if mt1.CapHeight > mt.CapHeight { + mt.CapHeight = mt1.CapHeight + } } return mt } diff --git a/text/v2/testdata/LICENSE.md b/text/v2/testdata/LICENSE.md new file mode 100644 index 000000000..b71f33a73 --- /dev/null +++ b/text/v2/testdata/LICENSE.md @@ -0,0 +1,310 @@ +# Licenses + +## `MPLUS1p-Regular.ttf` + +https://fonts.google.com/specimen/M+PLUS+1p + +``` +Copyright 2016 The M+ Project Authors. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. +``` + +## `Roboto-Regular.ttf` + +https://fonts.google.com/specimen/Roboto + + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +``` diff --git a/text/v2/testdata/MPLUS1p-Regular.ttf b/text/v2/testdata/MPLUS1p-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..85827c56816d65b7e55f502ceabdca2d465d57ee GIT binary patch literal 1754752 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%0A2HMQXF0 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

%x#wM2$ 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⋘k?&4UVVi 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

uZ6T)*B<&buCz%V~yST#%t9c`)T!s`UQve3XTGk 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�>})9H3f7Zv;*-60^m7;qZw8e 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%0A~op(@-xJ9i#~R#pr|d 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 z zkv%UD{tXn+GIa30mPTJh-AaX(@e1zLOR&ORMZS>c^E{KR@iI2KVc4lImPc_Ehro46 z`6!MX#oAD8L|exY5*}U99xSfp76lB!-i2G4#X* z#fvVDlRt{e6CB|~IljClTQgk)DT8z%77LJz$ZugDGQgn{qTbm>V;BzERPX>;2uA~` z4iPGlAOtP|4T7z#i(MP$FbH-59pD2Z*H~NN$AI6?sSkJ<>a%`cgtSYKEnKM6%gGOf z5;yts5w>a>&8U<5@v3NXCEa6WINi{3-rEbU95y`F`h_GhnLG;%R^-#w@G(D>;`Cc0!C-B|$FIl_b20e`^IZzW~oQ0X+Z*gx@iBH8@C3^}b{SSMV%mer}FbzadFrdSGP= z-J{dV>ocGtkBlJQ=|-f~BgMN;r26-iJ2kh@ZC-gub@%jr=+FDenS%yH@fh7nRfpqA zKC1_=efw>4LnQeApa1-RFfyYz)W7PBzpw8MwICfVXPWv`{G&>4J)iSt?$A$y3xv0$ zL!CUfs_umfG9R#XZGI3woQum*^gp>4or~0gU6Jdsf{TK3t5KIdmvPK*YQT67Q8|bY zQ1Ag?&f^;>O@p7*>WG8u``X&N*0hkz5>deZbHmzUdQm+r70eJJsU=cgdi<6`t60t4 zpZT%4`16K{rZtml*7kIGYskm_9`Ws)Cth*0Fy!sD*Z8#wecatV9!+i}e=06U_G)+4 zWLjJ!VxBEvi^jDVatevvo_NBY>57y8k(MPml4o+%x(CWVaGe>fSSEe`c>=juO^5!} z9?5lUQMaJ_>@#Lbox0j0s{`*`Qx)l4ysRxEQ_6&9Bj02;%4~{f&Q=RvgMos$7IMES zP+VT3DWP!sFj!`|hjS7{MduYPglK#&VNPe@^%t0rz);KPCVo@IDA2|xJr=CIg^_Ji z0~3S#J#augI`pYvx?=`bySVTfuV=+~;%bHTEJrFL0wbrG4fRHiW_+!gX05CCR)%T5 z@`|Fcv?O2{9?ta5@TH4b$SzR7d77m)RMH!9;~J-2RbAb#bF)_UvFpck9zf_%xYhv5 z0Iz6q!q!I%`ikVBuZY{KWnv}yrtgzm6*E6QzDTMkM>wK{nD=~+)b~*{AfP_y=9|aO zD=n7twAs6WA!{X+A`u(FKW_lgFa0cK|fPm28-x=k7qZYz0f+hseJQ_@CJF8g(P@pQtg? z+|sd_rAgr_8TY(=`t-}F>TpHrsh3W|?XM)!QmydL_BKBI_~Xwa-dZ;6`HHd{XwrzU zN+#VYqCfse7C$K|{f;6}Mcq4;6ZsyX*3@QK5~ni7!MF=v88MC?Zo|(qLD>N@&o$*$b{5}26UC)<8c`DD*D%7kDMVM4|%;(tJp*E z<`Qi-r_<*5c>Lt^N?ncPbwOE!r1YyCGra+&#pT>@VFhy7>+SY*BqA8FJC<}CSIk=c+z>*F#f$0ci{^3Z*s#B#4U=d!BjiI3- zziMDUJ0{ZT&EkYiyqZ=YFB4&Fmqc@0nkf zbeRo&jU7gU>DqOVkzKaRlKBYQNTW4_7DWg7r7~)Y(OS6DQu1$+l^ItHxCFKZ{pKzr z;qm{68|F)NHbF*fhmyHs$Cj?)fxdslpJYhlT;4x&)PI$t{$HVH`Lg5Vx>By7x1+YU z&F`<#+w^RUQgOV690~im)#Zg1Wku|bvwijCk=X7A6`&-uYH#f4=2_YWohMEG)69e5 z#hPFNz@s!7@HDPP1g8NMMbWRQggKKUFGXQs#q6pG*xZJjZEICYRVut>6G;At;w z%C?GucH;VLX;<%~JxyyM28!;bWzfC%p`-LCY5T6GU3)$4_ODsqrw5iuw$EZkdvHPn z4X`TY(xOj;HYfs$LJ?3^FzEL&kaL_T?&uGq7_%zOVH&)CuW#eVTc45nd~f^UKmHJT zN3h=!ufN2@EROhbTQ?sZ{A>(Aw4Z3MqHJ1Pge0hH8_#@q3Fn=IWa(I1_T0i&+Ptfp!;LlV6;%vbvO z+;Z!fI8w2}3Gw%)h6M{GYuECEawcexX33 zzNMV&x1Wv8ue0c6FincgN8b_nQcx881h@h>4_8|Q&gvAGUc!(|6m(UMfrAG@Af+@q zKUPL>j=G%5MZSP)X7ulSk%+dkuU&%{BnBGlx&^5i3<~QjmN62MK(*0+AolyxxHr+` z21Tz1OZp5H2xe6h4t};n{Jj9%k~rZ`!+44B@CoPKfKkCN`bFy{bdF%Y9E%DRu}1?B zfFo3tfdXVIG>@Z%XcPL94Tf?yMlssvi5u2D8R@Dj~-8Hwap192ZY@Ln#*1XF!WHJ%ST{2^% zo4nH*^hdl7VbU&uX`gM%Ybn^4L{)quup8_30WV?^dUTTsUFOY4^Fl0YOr zl%>B)ncGZ=4daH?>8f89uWTE>X;@!26JToi0 z$e~qo;`~~MStPW8S~GFVi>&fuzqF^Pv7Xan^NJLxfGgbhkhQZ$s|>C_a_Ts5FfNYI zlgSqlmJYj5R$fsf?XoB)#y^&$dW&UyN?FCU)o4Tpk3Au=Dxy=(<`G{^H7R66GtC;E zP+I?QrXV3HJ)N@*Xj0*=1f%$qm1rTy`%@nx<9KEK0ql^TLD5 zIvS-7%(BPr;)e3_=a(`$iDJ%!=Z!ccki}ox8iCB3-eurvc|SEg*Sz_9^d@;H%r%P? zu?$MIB&P6iN=>CL!o4*|jj| z=T;bjR$l}z7oG+AW^Q^g2WT#Zn7_VYKj!;v{s?Ff`4NogI$Y2I(2Xq)fDL#Q?oeAd z8i2_LV({DoU~dPEN6Tm8a$n)c;fHbJ3E+nM9?UoDRI%2C!Fk!|e-mc3gnl{P$ggE0 zQEfubFS=V(&KGD1VU-N+mx*|;!^P|>*~sx)8jGeBc~nNafTmHo+t?*drDX%0^BA@^BfSV^U&zm7rAxSL8X<@OS$wq2id!S+*Is@UA6unOC|?>#a?^O;8#3NL(qrH>*#`_rX+V_ zf2ppheSlNt)-vpB2lxC(JNio&tSb|WC6%%Gf=TtmOFsEz$#DIo1@RbohGi8Bo=Q;u zO0NN|lZv{>%F0ruESNrX^k^j&H@=tlA|O{jZv&lxkn&UtsPGSf77hWyF#GJth`f=( zg4R3i{f<$taGbe{H7Wr?`Ek^!R5&=C_=dv+CyqK@EQ!CR4Jd{ISad+y3^BXYM}F>G zmEVMezg|upBA=$Q5Hs!NSGkNXS{a1K{c)NM4Fv};-N!n9+pSNQ-?1X<)Ckgpg4F{& zW=hhgm1~R35fRmD+2ngRnRU`z)yi6Bl?RV;5S16%ObnM5&Zwt{qe{O( zFzB>|LR*8D>-DXAy-wE?3NfYM-YC^+6Ok$@pFyLEoo-`IMT{E`2%>DYOfNO50(CLE zpsF)0Dux-kY(ZhQQP9JcSd}u_IGJ407OoY_`K+h&x&;P-uqvU!6QVi)WK<4+TUc36 zry@8vfj#C0U}FKG)fPkx_EBI)8Sz{Squk)mz<*KB^veqbRw>{Y;ZOMk8+{5i(s^na z3lIJPNyAh+?1aqFp4!5cNod#$Lr z-qCipV900IsVzxuu(r#%Y+ValHMeEmGGkY5P@7a)%?;W?n1}Og%d}&Fn7dhY!@M6* zprYL2x0)|Vk@~pX6ZylHnWTGgMkE8b$c#aEGIQl^0iUU}dE_2nZLX4C;B&Zb zg@h`h3)QvX|T%|GuLR&_m6%W=l4HeO+&PHkClBG1}&he%?ZM!5QY@Ja|%$$SJ z^G>6y&gk~mlHYqgv%m-T`~&vV@z^kzI%`%{)M2v<0-4yXI%?pAYI(sfZ(JxOkGTSY zl*_8mSi`je)FKxq{NsYW+7QRU*M@|;pxRok(P@I}st^aXko@djke&DC4Ae@P2jPc! z!8#BYQ1Un)g0rHKGk}6;VBGV#pJMsEWrbk}qDokF_z27cA-o!hm{l^AnFex2ea*%X z@PDwu3#XL7=R|}OPMEH=eb+7&z3j5PZVAS0C!}PY9xJZ)as4!nlub7q8J^mS6JcBo zL`<#iu-fbFEmsyl_Eb@isc9nb+DUJpy!qw_9>_GI>fkLQB)#hJ;r;vn&}4SRQUlUO z$BeAnx?Vy8<1+qYwUU1_+w4yz6ouJR+KRecpi!=}5(31>kH`O8ftKT1M-mLO7lL*{ zn8}!|tcvM;ev8BE)ZKfp4&R+thsCdRneDQW`l?B4$(l+CD99nDh z=nK|k6`$1&RN--6-?IU+U1~gu0gq6^PpO4EC$H#rNl~g0Iy(;Knn1DtC~#Z-?6?{r1lai`6BK>DB8BH5Kwg zv39haL>pyTVc1{Ggb6AZQT`429Z}(DwGw+n=*4*iZ8$i|m(0pleZ0E)P{QBLsnJG%WTbIaohX!A_P7U@9e?!AK=T zQO{*S-5C*!eM~htrh_1dG!%tI81s^X-vPOS z@%YqSs*3~oxy@_X=FQfPf-#H9g_BtnsOPOJh|}a=0pKDKa^bgUqp1!U^yT1ZpB7>* z@c;7T=zi}v4{z8&ur5%Mzw7kao+FZwm$(_qM@m?B7>?Kwy_9f+*V)QZ^GYpJPmQ~b zZ}qs+-P23TZEAt7j8Xdy%nuQgKY#PhH*#^Yt61^~ljkT¨@@aJZflv~vTGlzj% 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

)SFCMoJ4JSvEw&UKBeu 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

mj#Krf*24+R$M@i22_rY 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%0A&FvY&Cn_o- z1wX<`TP!QOd`>G`Np4=-@OtC54GkFq@J>!72m$G1pubAJ#g0g0v?0Z93%dE$1HyxB z#N-JL3VtaQi;bwPMB8DtQsOnn<9}+eB};; z1@0IQZ(%zq=bGVCPwbw-vhlHM9;`oS^8rYz;Ps*AD@YeCo3a3aO1Rr7wvVvwhgBtb z;KMltJ@EMB=+RUAK0EmJzI|Uq1B}kW#aljm#Y3b$&wkXn4~G%j$WM^`U{kePpmPim zfY=DR_0(*gU&I$JFRpqBrv$A^Mlv zS?jK~+Px{hR!{yd*y0!&AQO{R_JXnssa2uoS7@dFK`)7z>i3h&B3DD;Ee>OF7ewk* z3*ra!g;6ChR&XLozVfu=VN`|Ek;B2DK^M@v42seyyDWIAK4Oojh_$K3kg_zpWeKOe z#;)%e<}2k>)XEKi|k3))=iOb13)_3coG zqv?XRs|!mdEzxl`oeeecJT6MUp7lGe3e#n7C1L9C_e~{7o1-qsFZg|-)UC0XZ8lM& zmLVlKl{bd#%@NaY7XPEHGVB(`2o`!h*yv2gNfN#^24Bzmp0c{&fBKd=deN5JU_;DR zn9D$l;8Ft3uG9*1FjxYt2S`KN8I&5zMjEhmVZRKXFEdpr;=}uaaF;8iEG-$S2}xWQvFEs)=4TGlh3RyHzDG9TBl1NZ#$m0+tbGpk-oq!Ls!kM z?O3o%vt!-vzwnPOO$7kkV^P=Adt`-6({f!kTRDsohj+q!))=bLatTFhX*ZZN=76Ne zFeRL8f+?1S8obrcKm;bX$N1(l4znx|_rFwf=LS4BPT(>=WP{I+v;96$<#<-N5oWk2 zM%)Zk&9;`oXUPDpSWHl2Vh;o(b4Ak*A#foEWtSN9Aso-pfT#mtQMo)^NhpNww(u3I z>M&kk0lrzddLQcpwFgt$jg0~LZz!dJzsDD?0RSuoxJ=ItDzUS$Qo$kjY){y#P%&Fy zRBfGi_0{Ab4;^|?OE6Rw3f2Q$Mh`bg{&;LwJ?bns=-9?k7nkVrGi!C_>LO!Ur|oi# zOt}-T*1pYEhyEV$2FKo>57`O!n4ZaNM2Ka0V zP7#a2E+AOSVrFr7sL(y?e#AstJ{lYF>m6DfVJLN#2#xN)k2=n62w7NGM`3}|(^w&p zayYtbQP+5V$nLKiaoBNzYYT zZ8^(qKx3aefBxl{e;#YE?;gKFdCO;Bp))vtdzQk}30cc~RxA0c+3zRj1z~O5TgZ=U zYyTS21^l%Rt5bLX{W^R(t&UoMwGR?O5yeeYRPt4HB8XkouY)s1*zCkz5^_OC+;9`0 z+{f_?ygCPd!g>}XDbCqZJ{`~kAOKpk!BVpv31a~Mo7D;G_0bgK%uB&Qcp=uYXcs)K zUx$9XK67F~+Y+j#BeZt?8>;*d?MlSnga>)4ug}x7qi=Ul45 z6=q$TqiTqYZcG-5bmg|Egk`NF4w913wIlY=(rYI2$L}{-?wKu^x3}Ua#3rthhFV4Y z_e<&$;#fn#(+RHL4UuN3>O2xLPprRwU0<{|es|PwlUR&#^5!8H=AwxlncE{l4JK=m zM@mWp5oZ@a&YutrPJl0+E<8%MoJyn6ePhHxxFjAnjw3wyqKO01TCl4y@I!74D99h+ z{$WKI7?5azf_DRqF#wqW^0IUUospFt_@(gTsTW?5KJrMiOzmidI=Sx=3&FtbsUqBUEGqK)QYLbZ&SP~eLYf2pdgaDaS;Qucu#4uY z%Ol2cFcb=gEj4I$IN%O@yJo}SpG@&1m{ zM^AQj+Q;|DddQM!lzgB-d@L3_8K{68?lrY1XCA-ZBBGS*rD4AAxkNNx8bMwsKk&+Bf?+HMyhl;Y z%Hk45aga_6dbL;6+)s&pE~WlRMncD)nAKy*7>51~h{8!E$p9f@|<7IRVco8)P0l_XP+uq~>V=jkGY+8OTdV9)UE2 ze;^Gd#^Y0U_$8nn`VYJkXZr>szff|FD;o)w;1?Rv+6s4p9N0`>Q&E6^7YcXvtggK2 z6TiqETfc?{1`=~gVD$CEKdmC)M7PA)c;-qwH5CrNK@sv9>QW%o!pJKwtStArYj2UE z&E!5~e00V%x2Vc}s^xx1iFCpe<&-X00db>P&DS4NEmXCQN2YJ|B19k?e6sv`b#0BM zGFZK1yi&e`Cyvwz!-ec=6;%PP+aKr35U=u>$zHjr(gc4PMU4FNN@YnYlZlEQepihF zePHDo?Jk$STw(nuY-oWP;t!ZjFQ1)9*RQXHt-zZAVu~{{_?R{WvnISoCD~QfE>Y)> zh1sblSmDC-$^i=k_b-7zx%z^f=7kNWEm-(fRL0~A+>LJ7w{IV+zZo8LX5Tt%7HYqH zkF(J`rnp2>_{!bnlj`y^4gZw7tU^^uGO})0Rl`}sdrdN%D#fxJQ^A??S^k9Urn~Px z-B4b2xj(kGXh}8ch`proXM`XsOl7aRxbFGp`2SoAtbSaN_29 z6v(I`aS*Q3UAxs?>o=L-*LrK9zxW%@E2jL4zz{nF2!Jbcwg54)3k58v0R_ooLwEBW z`s1_Pz^xb#s562pfGt}6M+K3b~w#UzJUDa&wEFrSU0yqV;*$* zX)u+QRg@QWk14K6p{Y}dNfXdDj4&g>SjVV`;dp2zV?CNM2~D1grkyI#ZBKNTPi-x* z)xPoylA$#=GYf_q_+$)ObsqVF&0fijGKYdXNTQt-jQZ zV#P2qwb<C_WSbEKGd|FM<)b``KroMJ?o|Ph0jiKij;oIRlf|L@dN zyAPo2kA8RHz;{O>19tw)w#@dnFUj+a1>a!9{mAY3eES1dD{7mH=PpJr-2;QG(vaK&iUP3WcKky_2H45U=6O)pZnW{}h26U^9JNmOkk4A> z>!EglBdPdZ<5}_oqmR6BmYgk=BJzlD%C!fz5AQui{!seo-HZAp<-wK&x(|}jGw z+XM$qC}Lq_2!2Uc8{luj{lb?JgcLvHCk&22L2~5)nKB<`sri2YgVUyIU;dOh``&x+ zAx)K6J&DmNmUI5bOS!rE$68)^VOr{*t>JLv%S~SIwI@^k4;avV;tikgqZ7VR$alg{ zldLKljoz!#Y&7tz$9u<;i=1+(Q!0{-oI5heLrEu9Tp)De<>W=eT#!O&Ype8^y$I=3=AtkL^027 zx8w(60SO-_lMxJw;f+~0YZBpOge&v%{bCM4g$8aEXQ=5kl!dA~A9|}0!-JcnX%AF~ z{eq#YL)u}$sp(-$phO+lRtZG7Pm2eRJi;Nctk}H=s?M((Bftt=sa(Cj73D zZkaVvUr6kd^Gf7ad7yC5#`3cQWg5M?n^jsQkriKXj zf|FIhw%Rb9Fl^|w`&-B*14lP6?)x8B$%Z{X1|w%mvZgfd)!04)CLepS=HX%KqZl*G zoQ&$MA&re>=ox?I4IRR_fM_`3B-~l|&50Q^(32<+I`Cz$_Y@0=$wFxXc@o{m+Rf$H zqrFR5CG66Y{G$AP21*c&!UBmzEGaG#nPr}mr^WdKhiE-CsH3{`v^gA-KP|P8&KlZ7|P!SEq^%bdDDkQXpJ_Y}}4itMW^sl{5eqwC9%cYxO#h zzowMEL2%Dr(cg7T2~p2mLoXilSBewn{x z1y?Q9OrBDZj~LL$IeUHbuvFZj+5PZ?Cq}lKT}_RV9@CQG!F%tW$x~WSG(KbzY3H3I z%$c`-k=^IrWcKNoj=6XC?88&11}6pDCbjJt8Efvg#+yhO4?DfxH)Tm|WHdc3He*Jt zw+sA@5(*A-e1an}xSKu@haM3#6bvBx0J1=-WAG4wX{eYTJmo?#Stg=1@u1KQLH;4+ z#glZhbsAJBQpHkuAaf2Y@Yl?_+eO7q5>2epk}T$;(%ZQF*S{9(=19w;C1o>LEQH63 zdBxmcg^^(CpY%VZOVt%qf{dk0KdgH?nKWf_&b#CL@THi4jQOh z$cohfzEDLC*$zoSV#?R0=nZ*nz#%G|?M4`bF7xY@m-}(9i|7JbI&d*G<*p)=-Gx9Ah=xHe+@^YRXbv?`xNkU#m=27ie}j-R!bnqbi8_8M&xliOPr zRMjSH$kEL~?zGL^an0z!e&Xgm1Tl};vzOR+E4t$!++Z^d9~xse*O*b6SW?N7l<_lv z%Xippr6q_G)Uoj~AgCRvk$fa7%`Ym@D2RXYb#i5ayZ}F&H^37ehs_!J5O(__2V?kal19U4c9SQG6Tbq38%1~T z5q)P^0;suf+v3Gv2B9wT@1Db+86FS$hmqC>IOAGMzRF`G2l=8<@r2Db)@C!>P{Ojs zyv1U1>&XWiuZ)LN>&F3^$D%fLmB+Gq@}504xcP8+OyjH~fy6H@sL{zQfqE+7+E)g+ z7E3>^Cq@H6&cr?R`9qe>fW>12ftdqNh(=^}1ZA^Qu?XzM(B2J3SBPO?4292SKMQBd za#lOg-{rt=k_y3p@?`3%)bdp7%?v!ynFh3eW}%|8C@(=ER$d9GTq^#5#=Zl-$!h(d z^QIXcX`7~P(vh}lo2G3#k~FJrcG9$II-zAlnX-oJoHQhBmXNXUlLF{J4Bi3BhB!|~6k@23kN6E#OM<41DpZFb1j7PbM2Lz-mo4S^LcZ$b zi;VEk0E)+#$Q$ePA0N;tNe*1AXZ(n3y;9jtTmX*YuLE3O`Z=K9dKAu?hT3?O7V z;fcxg5*j?gRz(Uuu&whDnZgO6kdDqbCmCqY6J!H2H~@P<;uI6mF3lt{=8+SpC6l&w z*m4b_EvT&j?5bNdnnMfh+0 z##GBJSQqY!=Lh#D2+0gzGwf_Rf6s#kCllkDKP~;I zrwZ+ehdw_ih`Hpioe-B~jZF~c7*myaZ8gEl2%E$#S?KCCts zEgzb*zq2)V+fJG8Usx=y^yil)wl+qvb8CEIL1J>OOJYq;MZeW9PvAW^J(}6@OwSPH zxtfvhcIV0l6?in=~~g^UlG8 z2mc&1q_eXV{jm?d%ui938k_l!l2mB|Z&|{O;i~Ny{}iiSS#tg8(#C|C-Knx0GY#b} zOs6<27OmL7zxn<5Cuiq)Tsd_NpU>;xeD~c%C@T#wFL|-M+t?qp(ZlZB=ad3<=Lr_Lp*9p2I)wl#Tny1=*+ee!ga(IRY+4|B*gFg0m5Y_D1I zuSSf-xFo(M2saNz0W!b94)6t{c8z=i21*@enXe;Sz&14T3JN=sM@=AYM;R4cU}Y%&UN3RRrcSARN1B?4gDe}BahA}jWi4T)AvkWv zr=O}7C%(ElH`X14QoV^)@go_52xXnrO)W*;;4*lC@%Hi(fCWQtbymNAWJ)f{QxJ%U zAMxhFpMP&-cDDr|Wp?*q-k4wlb9-<;bGti;HDySH-!EhSbh*}gJa;-AYoRjWkA(~2 zcU-;tF!SHOzaunC<=Wo?JzlpoqxH=IrEQ1pG(Tbw^s3E-Z~XbMzeCus8d7T~c|xjq z!waI=lcW)M7P4#6ql9QRdgSSL`jPwZC!a)BsOKj2fV$btbSLZucpWcCP5`KGg$==E zM65{*2fgZ8!i@6;?;2Pf=Zgc7YHCKrsezUrF3}fmcgTPSW$tOn#BxH($lommRF2Jg z?himN=G(Jp186*q%DNZwUqkVOXy}^OjrZqwuMYikbHwk%Yj}(BOAYh9v(O6$F$nbo~h~GLJ z@hI@y%i-uBbHHP0Eev$pQ9h?6wi<2PK=Qq`Rja`R$2Gqq@?h-n8_)Uv4$k?YfnZpgX9S zmEmN1IN33d#jc?%WwG87t|W+zrt8*5zX1N39hE~o(53%w~1M{`v{ za4M|t*yQ!5M*eaUq+bYsvzJP`!O4m~GC4hhu!z(*fM$=>b`mA&1rQeXm9jkn2+f~< z8jzSFtpf)ZcbaGNCK*eU@xB~yTdl7K#&p-?d(ljo#At&N6}AxDiPo?82EQ9x2GFcT z%VaLdU3X9BJ!oF!qq1~5&e4gr>m)N8@t}Q~A0RrG9f=YKe43+GV>E>r%UdnK{q_U8 z{PJ6&$BC4JHta$#c~Lw}5WfVBuK{{(=Ot1hAZ{3IQ;Gl^jCc?P0zl`c5%H73yDyp6 z)uYNE9(Vwm8r$uK%Hxd-13izTa{M$5{Xa%W-V}xeLW-v1oAI$N=K6PW4}*h@lQ*zu zKQHlmq_J`CA-nn<#ukKyqc3Urv^J&4Xn@FsSTZi!gy<5&3jr-m(c6XF*xL-yOIr-e zie4J}qKU^(NXSvtm@B(y9^H?>RTS2YDXUKq!My9`(hRif#pAxAikxDJJXXvTFxR81 zURdMxlzdEyTKGRQqX}aN!s<)J zsV_|w@70izMgPuy2nra;-9SWE#qO+xQ&KcYNZ?Bj7~fcjk?B%t&~$vR zeNxq$k$7c>$osm`7w9M~iBCYhn2X1mN;a|9hFS3q&Y*T=An+cwBTS(G@ZtH5h5&DHR(`_Hch#?L!jBCNX#&&V zerOpLMy{HzH6nX6TxyL3A1!XWPo*+%u$Ok9(GBn0g zNR>IQoFG$aO{54HLY}_o;vOC4Iwo4`Ek_%zvz+r&>KiTf&^7z#_c00C1+iXZv(B@8 zw>p@0T^sp+#ri+}TWg#34EHCo`YP zz+~dNSJAC-RQ$>Iy#k&9NK1k>C3Uw+kvSK(0Yu2wQeSh94I7x!?rv4_ma!>kd?@p? zf=|AJ74q+y@fAt;O!ctU!S!$>Z&M4Oh<`(gNlPyBe#VoAZ`Ndkm-g_bW&*S%6 zoMoGt_G_Q{ZP=O+>8HUE{C}4S+S5THW#~tZYFwm|yPe@k?~A7D-X$-$fP1`=hg^kD ztMO)Ll2%!q&HRYZW}TXmw?^bhvtB~*25eH zedtsRdlF-Ehl=!CT~ZQC$@?VzwI*}XlnFJMDem+)PIkMOkMLDE_3ce&SEbv}L^(dJ zH}N{pNDmucD<5%G=%gV4KIu)MvI`xO?Gd-*wvLfdeR1Fb`mU7b|;B=~gl9pWF_Qi-i;P6jI zet!sB25$fDpr!M#J3PPt@=xUB2`Hz$0Mi9|*GMF=0e}cVFk*;_NTx`r1qQZ;?@-Qw z?_Z-?b{;x}?#?q0d|6OjV!i}SqRB;~`%>5Zmq9z$``o$3uE21ZcoxM6NlT8_=G1OG z{_(`x5_sm*vrz%^C!2ibsWn5r@0x73BEtow@}m8b&(12x6F^!4K=ACQwNPHSAA-s~ zcslB_(WfWgH=pnTd=-Yt`mzA}`}5Czz8$z9r`ZqUAxuS$s!_3wH@2cMDJbrv=B;>k z8~W5vIzWSG*r;y=vkrEU-Pl=5M!hgEMnAC@eA+ zZtQkC<#~${bPEaJUhgfzW&0Sd=*U$Q2@b!P*d+#Sk@#TpV5f9tzv!Lnq`Of_Mpcj@_#CXnm zz2`^%uq2p$7thS?dVpjhAIIGx>0wl^tzV>6hLErB^7M7RihlBl1xcf{!cIsvbS z_#i}0beT+x#`l7$JG)^6lAZbPyS6|7JkyHzHQ_zcmd=2rcooZ&Jtw`JH+xTFHQ-WF z&nBc%&}oo=!jbI4Vqz;ete(_zLrq5*m=77}>%;GTAC`GYh?>UWt!vi&jSf%De}33d z!J)mUd??M3f!|MZ4`YOf9~Em#CFuz+sUthR=Q2F;S*U~m48`KhFf{bHQC;!8$FX9` zi=N@gN%8ia`EVjvoB(!yy3&$o4s%a?u=xS$g~TcbnlF0!0`!3$P^Trh|o1vfYT!bFw|GRBOn{jEhB0TespFf$v*>bLSk~FW^aG zk$m3dqIf7wfPM#gwJzfEdv6vdVe$pZl3ZmLBmUDdW960)$Bp~2Vv*~JR3Z~5Fg)XZ zaTcd1Hy`#SaNhGfQ0~KK55ka0#GGadGP3X=^_%kKksiDf;8ntVLFu&_O)Fl*9@olb*Ngk(?T(Dz*+W z2X;#zTrZN33?Badcjm!JHjbs{`BBhi>kay3_8iEWJ#EUI=gX|>?W5ce-milI2Dg9zecw~Cj2}ytOa>iQ7&=L$Rwnwm*HMFd zSX$-6?mM`~8S)|7>9uQlg+KiDR}20fiT`FQajT`)rFWqlOPZZ^FCgBg0_KESGCoL%D&sP2d!rXfEYOZEiUaviD1YuQZmDWuX)iEUhX-~Cr+T4fbd)_x*)CT~nSjDToHI1f9~kf1Kw{Dmqq@mQ7}&V&mxJ4;Ei<)MhD2iHLb zkNqjQ4s0zVi~ktO-p=bGKoQkypWUdWd-3A{@hxQUQA7-fr*8yvv zK^|D?6Nmpqap;xQ^i(k)-z}BvE?=&f z1H(&*%Tu_lMw{Cvm-3pAzmk6D4M4n3;7S^lk+cg%Gf&OH_iE5-T&aoYw9}rv1Obzh zpvuS<6qjn#v4VNI0VUH7gIzBAfd-4pV`*EsHzM&8T>EG zVP#Y9QRqe(4{L+&{Gv9+7~b&AqD0uGFc+`->8-a~UwiFB@RELbqoF-?rpTw=Khyom zFgh+?`JE^#H_5~x1mv=39L$$BnTg}sy=?f5HJ|CAKgOv>>K<9;d3ej3+w z+KWNPHxzhy`s~=aSRwD3+#yAIMX`yr^c$#?ZmiIltraOk)F?<%aQBYhs)Y*EsS!ur z+GsvFGG3-|xQbnK@kb|36F+$@rpQEx z?p&hcSc9EKNs99#KvM)#DTpwOV7gf*rd0r;@!p)%-IpKGE9lWV(D62sRmQxs;Pd~S zIrCy(viz>;oijGclk<+AITKtNt(}?pu`BNgj$<{_Z641V*CVFPgoasLVQmMDU3kQG zhB+E>Z0?51{(9Eu3x|u4%A+b4bW4P)CGkP%5Rlv>u6p0+X8Hxtx2-`x8#MCDE`J&P z=iJ4MEtluu!?-0nYJ{yK8}T%wjMJdMlabW!Y14w^KKh8+8Tp(n$oMobi*h3A^9qqU zQvOIxKw}s-oF%g?f#5^{ujiD8FvVYzy2gC;5e%(7?SKBW&j!wnxw0Cc#?=%wng!ly z6nMDCt&r?+uY!k$M_=V$Cl7N#oQnLxFpIs$tk{~-V}{DN^B_f*&EOhXM+d~ z)fYuPx?(E?iT0&&DDxa!CfAyE-~86{+dqy5y=to4-W@93iwie?TJ0%onSh&hZ?7z#JkU4MFurBJ znU@rKH~|OBM8eb%Aw+BR-X|va0a_9MM@~u_K!Ja#vmI5y9}&KN$%SrHfBA(%qo05N zH{OXxZOSdIE6rbCT2Nn@d()Tiyc40rIXtO>E9p^rRCNw%V{=1ma=VEX^$7cw!gajG z%a8B{E&T>v-xK5$CkXOPT!u-_t?^);3|O8Aw+4R)Q({BO1CyCv87#(v(Jiai!QG@n zlRa#NV3jd*GZT}TtS&AZP#LI$T>|kWn_i+(dRXZ%{5}vxA`3HYC?Dnj6A%rOb03uYfSpRi8c>qcB1lzpsQGqw|4^py*PS45Ux-S zb?@bJDxcHXJmcrS-8tR;TeZvmiV+A73`QtzSL zy)$+L!qCvPwyMsxyTA8jS5v{t$KoptbLMzXqQXf2gJ*|xoT1C0NRf~l)^?Gh&MkC+ z5ba$L>s)S?59{8B#-NbYH9R`WT3@>$s_)@=SvGYk%&>tGLd2E&JAZA%k@wlvP>Pr8 zS1227_&d)4#*fEB+5DpD)lhFrWQ}DC7-CCY=w6SC;rWLQ3ItEGTJMjj)lj9LvP$3L zYga>|t&t=U_A3I!=@ni*RoiJav*m!X;P>> z1@9QDF7X!kT)0-7zBqA1@}7Aa#ZZW)aUb$D_;jt_GS4CRoH@al zhc;&L;HA(bSx;^P!^D&fpDVv@ZTfnqDe_WT7~l*9+T{Ft;OI73M>P~CgpDBs8Ly=P zXFqz8mnyv!E}c?@H6rM`#4xKV_H_M19DolB2u;l9Pay@Q%CUHpg|8xdV8nYk?cPLHd0el9MKBwStsB82w(>qSBwGMJ}0K)|HA2P56Zq4j~0j z&Q2OFh&tdVVW)vwhc^u9qXQ}lA_-)mAv6d~6{i*00YKf;r!x<(#+@|{0|pG^zk7e8 zxpC=IEU{%|$ zk_49vt;F*fL6X3mTxm&98yP#AQz0|otcMfsvOCb2O~ zJUnsk=={A|w`R&!%UWIlbTG2+k1Yc#7%g;^+tLyy+Phk80~T)FIHFD(NL;e4BF+=% zoi23nCV7?~)wu_rwWK6keB*N1N>)HhNY%DK`p}gNgw}5Lq&71#TStE%-`$ zEp7rj^LlV=SRb8IWrj_1xh5TP zxo))uyERaacCRLd^Xa9xV&>@4XpB|>=8L$;jtGBj#Pz)~c3K{}JSwHV?H9D>1Tb^x z;;2d%LXBpx)Y6f(M3hMuSpt{{+1giCw4%G4pmiTXiRc9utwS#r+nhPt*Mu^?(o@wY z&Q29dlkAc@l`p$}sqXGUD$)6u#c|njvg84LV^&W5qRuJ;-Z@YnYDDw<)^VX9Ed^D@ zuK#Qrf2ZuVB{dxd&&mECJaT-SY3Qv_A<@nF;~I^S7wZ=J-YUS)Jz7OVo{#TC?hA#@ z&4u|O$b=%NIgphUQWDhN70J|GgC;$A8aOZDM}z4jyvmNgqcM+CVhUS1R%n;`knrWj zi7;G~lZvgZW4R!?(5g;*~2`Lhjks z*cqc2?{jUOwPK=ijn})ue%FMpv(4vGMeOuJa~@zaAp`y68z`z0A)YFx*8$QdLS8yKK&$p2eA_}TIZZYcz4)4ItOtxX=^BKjN zgJaCVx4V2h{Re#9eZ9R3UNs9AEQmY_x=mf+)!AGwZCmi&IQMQC~_e&5nNQF2>SKG-=YeX++?J+Bu8gb z$hvND~$E^fq;K%%q#;KkVP{ z2e9@4nuL!+`r#9sIKG&dVA{g3(AgC#u{1`I z>q5#M&)5sV9gD>HIDq*{4sgwY^mxg?6)Z(V9iTqpAO%KQ6^Z1nvwmq|Hf zl9zemHC%lB{r6ix^5aeZ^?|Ob#lxEK2#AXclXyOc7cW-kD+d(8_R{Oc=S+ull-)kc z+1iTFow#GunR$;rqqUZ7-Ht!XjMdDItF_jor7$^O=3petifA_JMIMMwy60S1MW)9XA6OUmID{E{^aeq@D41aX=I!u09Z z!a$0AcV2o!de%LU?10x%+}>Q2se~MRRnN9<&U+aD=FLHp$RJG%Y_d2<>nWeHL|o^r zYmN_C-J2coW3FnZy{WK}jF5Uyjs-v5S3c3Nur2G3_wTr4#;M=$Xh6T7{pt%KHeK&R)19 z*o|^vhRgu`E@+nvC@r+Z2cL5J&?>UIXT=ofv^&WPgEiA_GY_yQMITL?G$Q(%DzEMU zjn78oN?JUBjn0P_P9p_~DBb{EM_m&WLF9RpAqPM&P9sTkX&O!dfB)qz9a zZ+VMWdGB@YM}I_~Af7VXDXwFVMB=0iA(U%{tT#~~QqbvinnSNDAqoP9+JILA7K=j^ zuDOdp2_@}hByZ@A$FYe`=C``w$yUb?&EMO5PSC6-Ddb_Zn!cgIpH{4RaoIBKlHi*x z-B-e@)XQH_nDF(9$VX?Zxybma5U?H2GhH{MiAWt6k$X}&7oodZCS#o}a#MW|!VGUf z>F=ysg^gTF?Wb*-x!&w_Myjx5k5OPsJc z@RJX}Kgif|`~aE~EPy5}4;e_av$JNl)jGSTIr4yP=H+4Vu*d;(Id6iLWk$Hk5FUn% z5a$8|#zXqc;BvT#ngdcn$qR)4rsC3g8L;kkcucEPoyGsvaijT8t6vzu;jX*T#Elyd zTn^x^4HGA4v<<)`sFLd~Qs8)wnRMIIa*<19y>aUT-M06jXFB+vZO$CqEC)7dH0YTW zt+!BuwnUyTj^cR)UM1_Fhm=j{4 zYs3is(G1HwC!t3CG~Vn(NpGxN`5of7B0m4;{CY<{)0q#$%+Diry4Q!tF-e^>gX3V# z^m$N+U$YZM(7or+*NhKt;w~`T+1BR7LPuwy2pUxr)ol)aR;kVAsEm!SGEJaf$E!TD zZZVeI52}X~MwsSsumi&^0p{RbL6cEP?}{zb1~E&${@+}4mwu%CVfSwIW7E7v8PEC~ zj%M6C_XA^^H{F6JyX`sNTzs+VO2*MZ<8$&`=Y7lm#UlC_J%>>xJ`BA9&mk2AII7UC zZs|Y!?h;S+w(S;A@s>IFbWV^=9+sTbJ}!0QrwhJn_ttK|r`ijDv88iD@|0l={6*RX z^lS7bli2n}NKFtTL{>#qlhvAG0`#gExI|)}LT~6gg-Z`{GF(65Y%TK8;$)dc>L0tGZ!wj{qV!dlLJ&OXn0Ct0*3Ci|EakX zS9`tHUbHmg2XZM@Z;3yuf@F;CMvy-=@X*!Q9(3e-i0 z=U2j0lZt5}@xXwO2`AH|!0_{MOxs&H+xKcWLgilP7I>kXH*enDf1UqxQg0l%bm@!0 z?IU;OcXtJb?871nEcqJ$CGlkt07qGaQZ~LmUpgVcAIM z;H8)UH)D*|3~YUT8WW{>*lH0{cTa9DG+3Ce7lHLk;ah`(n_jF+AmqalX$%l?f8-va zA00w+S_uqCrN<_|a)bt^_X|V`rBA;sE~~GQi2cb@$cyKo1Q?yVEVvXjE2#qhj)mlr z;)}`(IZ8N@UdcC<>&nXm%kmwtk0+3ulkmkCWH1tUr)b@<)+B+kM&`2dk=OIxtXYmJ z-$dN-s}+(_H!wc81dW^`UFkTl8Q4lP$Vnxg)>#hlTlBDvWqDz~J=lFDt?Kdq?W$F-wi?QznQ&82X(SI4c$-aG}>KuO23ub^z&%TFr%%KqBp z5toYQyM_#LVRPZltCefv5=koWEcj`})+9iKLn!kY&N#5+OoVX6G)|+0>?)C48r(%B zQOP`?kdXG~fN3{fXwWv#+}_sKK4-|~!Gjx^Vrjm_#V?JI)|rZb?dj=ZhB^lfaGu=;uaA0|3_PMy4O|(Ywf0+heZpSw z$Zgxud2(v=n5E0d*Oe4jRu+~xICP11A5L)P4k6vEA{mCT1ow^dz!0%95;%1Td{uB6 zAXP@cMFlsEFh+G~)rS&gX!;L+Jg4!87W|OL5$6*mYnry8A#>k+vubck^4IX_s4@YY*@v%fcU{h z@ZkB3wA+(t)zCW)ZjD?U7zpx?(6SLhMELwH(84R*==%jFyyJ2;jSg&VYHe-$Y&1H0 z0mXbbaJTG%Va=m<_>Zlk(^0y4JzRa2#nwNoahSC3*?Of^^HBgbSYE) zPc!apy3+ayxHw(tJie*f!wl|O3Tx^PEu?tP*4`EZ%pj`Ib^k$$!P^XrLkZU^)7m6UH^KlNajgZR-pP7|I$ZyIv9aMK~L;Lp$|!kUhb4&Z}fUira;axdN+ zaLk+M2-FTnt0V6`fr6prkYwXcxQr^Sj7Z1=>W6$lR33>hMAQ@Lre1&c|1VvQF4q4% z-TQOT4;+;E{8%)13^?Z>2Cp!Byg(R-rpKi#3#=n~9&@rdL5R;wQUZkv;O!^`ipjkQ zui)b>Q=>O-f!Fgk+ZvZEPqTPNApqG)wRJXq%Gebs5@v++m0sNi^K41Un3xufMO-AH zx+G!Cs@aHM6n>-^K#H=7QUYR7P9}s&gA5IP2wxh(i7=55_zdCDD@&VeXx!$QA1if?X&|17!Rk#sv3s&fWcvU zG1CY?fB^c$(__7P6Zx{YOOlks(G0dJ6wQP_qo2^&chDx1V+lNxl-Lq4?r92o!F_)Z z{QO|t_HL1^z`t!<6Y9zwJtnDKm%{h*nAn(9aiJoov((=n6i_F>1;5X_NXN$GKc+ma zL5G#)2?e=0&|A)WyVH{DiV-5uXx9cQ64hAqr^-YH6NF{Orj$&+D7_q6X5{g%CCPhS zOhbfO2bBpC{h)-S=`@@$kCR#KTo}W(Z&RR|$}VUwb}Ag~A_GeGy?x%qJwNT)gO9Rj z=Na(T3;v}oJC$2I0v%hGJ6o3eFOW670T5?ZZHe5Pi5PoYMRiG#6rn|gIjH*d>C?g2 zIQ1>=#D|y7^2}UD`gfPj^vqg@0-dgoj^8r}4w8?Um_E8y(S&j%kImZQu!l(8d}5A> z4kO7}vQ?G@J3P?mRIYnZPwM1JMvIIeA>V{h6xcx`?M)BB--J&6FP>s)o7dH0TL>@w zD(hOHBgt+?oShgW5iV>*Vp+SdGmvFe3}qV1;(ET1bQ!%>c$>w}xs2nS{yHyyZ0y+T zk(g8u0Xq90&klP%lGF{y7#;Qm3fIuNH~VlkxRY*vatX7Ekb(6dHEOlGv^eBHvR3*X zDA5UZOJ+hlsPhkQ}oXx3PV=KF5Q?m`b*4a&=j%rFL1 zzszDOaTJM)tD6QG&7SxTa5cZK$s4Kv@Vl!3(wE@hFI~FC%rx=~3sW;P6XWp8B8}ys5A`!Kf*kRq zd=|0@;9*(KoFg_*W^;SKobXJca|>hc5*0eS`bvaxZJvD<%4)`+ z1d0vHJ-6Yr`}Q>-Q3#!`5qrMS-Nnw0A@#Z>!QtzZ+}O0-^$m6cE4O53x!Z4cEqB~- zgDxRw-ei~V<4OCbPe05_L}Wz9W!a8VCxGMyDj}a8_UPyoxpZ0B(IJxFTOWRn(WlA) zt{1cdUtQRj-cgf#ah&NQaWhwKek_o@HeBCaQ-8M|I=sWrM+w!(< zD>#6ElB2lbkZ7o#N%M7j1_z&G53|);>hF)=+*R5B%40VTaXZ`Foy{!{UE6;hWuwjG z#Kw754lT1Uf)jw(37U6;T0XYxHsZK_Vi799M2NYo1n!3_1yKK#jYDF~RwrIFHGnbF zb}pz1Czr)D&hsDf&m$l3w+*V&qGjI4-;&0+#il@O_M3__)W7G6==)T@iNED;%7+i z!xQSk0Nq=1?Bnv0NAUsN5q-z775rT95^!+>*#Tai5xE1rSgjU|*&W~;W$D$*NmXQX zcqy)7IRjm8ZFNSF5E3i|iGTb$F>9#F)kM-R(P0|tJ0lfX)T|SR0E+e%Uf71LEa3V< zu7%^7@_qaE;c2j&_dzA<4<+pI<^Vp@=|Ta%Ql=0WHlU3~&1kr!AOSzxjD9K#F~dJi zD`1djYjsobN<%|U1G*{VQ0Ov#Echp&Vxsh_yCJM!O)+`0i02GfK}g)V@MN=_fhUM+ z63E~zG{}Z8gwWehIG5^ZBahS*vgZvke{ZINIx>(vBt_*^#Nk6^F-1t9(;2g@lu$$v z3?MUTc4~Z8@*(`cJPq1YRFN9{d=5M7I6uwLtBI2e zro~rUv(o?WaLj)|DtWduJGZV}Zb%h1iKKIi<@GY<&}YP{`^GeUQwqbdBA5|Lft3C$Hey&tfp8}M;pdxQ ze8_Q=X0#2KVOQP~{DuJE(%|>sfBzq=$zjvY+MPXZ#k=UUc}w$E_99c^pi3IF(@=qy zxe9BXH6eD&bs+<8 z(B}T#^2LL)WH(ODo(^g!1Yi+88ghzq!9w;{IaAF6e3C8;Yf;wAH3O?%V<3%?<^!iM zHF^LLxQ@g>Cfo*i2l0ZLx?=go!i?az)V+1W{0{2g&JJwPP_0DEI1MOLgoRR%GCx>r$_+4#nF0Xg{aOH4EmVV(v zlGBy@>+c&q zrK@#^>;9X^IY!R-oD3p8H)(hpsMp5_by?w`SzBFM0A*ee3S^tB$*?L|ha6U^ec?dj zFjJ*LD{{zJh%$bGk@$bG4;J*2Q9xY^5Srh* zK}L6ipceq_o71{?9Ga!~1s^2a(DNs_40&4AUsJaCWXrtLY%#|SYan<&eH#5nh@fg1 zT{*S0giRu#%hQM222XM09=kpGyW9P0Oj3-qX^ci!Ra)!3ad35NJV$}kwD2sH1SLE{ z_Qj0tH=*AYb|VN)euhj5Tqtd6hR=MumJmb&uA7=%&>BW^?$EV^T!6YD48pz%*g*co z!A*V55w8Qbk6wWHeK&#S3GQ97f_We-0nZ7Ple*9H3;3_~Q>M(H-?XoJ$&wpy+=4G& z(Cf3S<&$`Wf<-J9L-hNiWqC`BiTodB*k;>o!4pe%CaJUs4v^|K8xk3;Hj}N^06YSn|724!a4Gtc<(gSn#_ zzg!kOW@+wjLln8hfb{4 zzB#0vCygW|u1qK>^i||4bker^5~IadVOJOo7OS>NhyUYg$ywd$ zmt-N|eEbVqd+BOVcQod}30LLrxjz~VKhE84U+B=}8mdL{j$~PBh03UvC!5PuRaIJZ zrOM_Wn3|g5+&2W-7TOt6Bqidgv3Y@)M=3!Hw6Y@cxUhNw@KLGGV|zzbRFQyd10>?Y z&t4LdGC|NMM2N2O<~S1YB#A%bqkQE0MVaf#%SHW)vOS7Im{%O!4y0Ke2yDB0^)gzo z$!o|i9sM`r{mapr4NyDL>h3S^?=EaE@k#kmS`oUU2I5E9*T3Z2S;h;o_p(Xk=@=bC>oDOAW2M!ENoYmAx z&v2`#6a^d5D2=L^2FXSCAqjCkO~ZD#C{aZw%5E;8=tYO6(0BiZDL`nnFhgpMz9J z17m?I0qp?d5z@Y-7Cd}pE@|;@j`RzK7~9!w1@mm0X#biuGrn=nT(f3>!);Rebc9+^ z>?~>a%8c@oW)x`Nhhw6%7L3DU@|FfSuzGWwI3Io7Y;C^U&xB0y8HS;6Fj?%Kopy^U zc+Oq`W@YB;)y$~KbF)X#!;au=?hF`mPtFb~Tlnm3vVyqP=`wD~HS2Vd zFrGawRT~7YAy;hIb$q$8XTH>nO2nIsHovtV+7J4N-NmEe_gA@@0rJCJF?`p3-esL3=|-Q z`_jbF+OZ3&>*Htvi5LK1IV^XL%vjrj+(a$W!Cb&gk}C2cjOdZgM`;i$rzr^-Y=(E3 zr&!7{Qv;9?g37cS*E(Wa9kVaDOZ zV-p2oWc34-Ra~Pepw@yFs_SID;;C3hIYh;EdFdW!xS3@~!Z~9`x40d(=;;?&b zm>H4BOrkpBt%z|<>0mb5m^D=UV};hj2zP>_UT{p}g-T%UEJ1xh`@x%5T#&S9_{vx{ zfVhfH;$)D20D{%sfKuM7x|bLJa!6_O*t@m^5O|D{FDcR7E-)z z4MlLw;?&)X7eA1?YHU|c^%zD{H**B%KQLm`cGE=2VE^^`kt2Gd5&5x$5dYUnL2ae>4YguB64+)MfT*y2DJD1*s_Y&)wAWU6^32x^UD|Y}dHM1ix#ORl{nWH+uGzC))7A|bP#$sQi|}3RVAs5PuEAFL zTk8PFaCkWea8c@eJP8%n#D1wDtTwKZbP&WPTqX~m9?A-_{b3^b+M zkn<#YZ1fHUAU;%>B{(Vm-BG5omy^NbuP8H5Dr8vKfZ~tCj#>fANYAkpm~_@urA22p zI84=9!*F0VpwFDq01_F#HmIeJx>sW=QS6*rga>Q#{bwjsk>@GCdtSDk@6 zUll6|jLYRT*z|>Uj-29Z2;3YdwaY#vzhJ1{<)~{Y(^$1NEj_=w-Tt3`YO8hgA_s#( zUY|6po#LwJ6Tx^yYqN3v04)pi2hI8;kOgp@KpW5zTih=4Q~~%(1yaxmeQ6;D1|l?~ z)a#Ad!d@R16)CHk#xAZ|?{E-*Q7bU3i&nPD;Ngg}Hm}}H7o-$?m zo;|@osUFne-=NK{lS%Iw_nD)=wNAH5VKlTcfsz#Af_w6p%=+pRe^psyU5u>I&(!AF zGX2b>9A}}w3rm#PVb7BZTOUN?!qu|kYOOcE$b+$y85F^3=-HYYOIdAyOL3WLMyZTf z&5KQI9BHj7t%Hpu_lNM=8ICbHL6}6)OU}Of*kO=yq{wGm`y+XiXr5 z;KWSKbUOV?2m}#=nyG?HEta1kktXbIm}IRoK`>b_ALvG17(fv~8X=`d(rPfTi2MhE zYXLE!u@5o$l%Z)GrJ4OR`A^U|fQ=a8D-dq@*5YyF(DqcBLGiXsP@>fpjEX;nSo zyD%YPB5GjxNRJYDL7qybD6~NnkwBEH7p3HvSSz%#d4dX^0r{8=m?K^yuPrS}kyoj# z1r}3oPIZi^F-dAZ-=iAz_< zrCJ80j%bW476wL2vZ@Q+Qhu7C-jF)Tm6kUkkm?(jJZ3?1Ql2nd@9>nA1L{Y_=Pa+R zk8)koZ+WEH@Zl2rN(J~|Y}7$~Oky=cStBYYa93>N7w#V}goqEf;|%;UBYdrd&+v&2 zX0QEG8rbmQzsd;_8VwNdZajp_N)f&cvcilp@MY!t?7RsdHsjq*X<5p#MO6ZEjUu)- zhF2{@OBOdYH8cFd`AS7X^QgGw9BFnP;uVd^Di;W9&*MQa##C2(6;zws>LIRKh! zb{<$U3d902i6sjG&vYIe<#rMXv*XyH+Luz5qEMtZ8#42KVA>*(QIHlEkw)9Il2^-8 zv^(L2y9p+e_!qRsqkH_KeY-`8MFn=fCrg@^WKFJjm)FfGP0ZZWh_sfX3^ATHG&!qm zL-V@QWZ4wqvzGGe>QGTEy0NjnsTJ>^Frju-R2u@5x@WCp(gfeM?CqnMGKXcqZ)oxw zL@6fpEIy>w-F(>L9OfD}%+={z*Z~uchmPd(J;!^C^1{1HLN%85KJE|Dioo{Z^K(K z@k?+6eg$?!H=}-6`?Zo~-)jQs&v!#^j~y172H!S29Ok!8qsR;Ib~tnvG%P|hzu}lR z%~69S_ma?(k3EmWouCCXuqRP1QX6P8eL zc<7J7KZ30OH9u=I6LS|bT{(s26{d_z^lC+(vf&J^V;O-2SFfP<2|_e^O5mnB4F8V# zWgufRQnQk-P+qyxoIe^`+qDx!ycshCBS!Ls(^^c$zKj6eFl+BmX_SHLMz5vOU%h8M zg1N6`X3_|UrqUS*I1NRP34UKz2A&L(c6WA`MdNf&(s~RPW#n(G)DDZyV#7(3CV}Z^ zn{w)??jn|nUHij!F4og&Es>_5`2Cde;nYMuu|A+tN(e!n*@kkLTK;-9rrAPNn~N6VCN9^qtO5n1rpT(Gy;DDT$)5V@PTp$@B>j8 zVec^{FkywyMG2Ql=d>SvfIs>P#T*1(T8x;ND-mqiTi23|r!UsndEXakS*ndaCXsDy6u-u$~ zLRA6V>r|ya=2nkHY^jsyPMYMq$v0s_JMySJ3CT$BF7`D{mfY6ac}x04zt&C$urOb% zzlq7!TjZUUuWB@dO{U%%E6k=0ld8;FU6D4xFD=XLN*61d2k@i@`cm_gW12<_)wwkT zYf3X}m1cd0DNS9}u%o?1E!jm`|w!I4i-82$S^60!Ic`UO2ch zF@~0ef*B{A1OF7NEXV|_P_7H0Ni`TMR=1krzZSZORZzsP%Ts)xmVvtQ>!=#C#;-Ty zQ4kbnB3@>;^WUoC2Mbo)i z75G;ODNdoApf36_4xqy@r7Sf&Cnr02pL6y}wfZC*S&o<;FV3{e^J{JKF=DkKJ0nXZ zO3d!C3{0$!H5EQzRsHpzbYQ;syFPhnMxlkx7cJK74{5anv|6BE7Of#R1NF$rNd>5x zFwOvD+epB`y8aZ9C8^IenkodcP{5876Iwg^7-6b?2m`|3gvb;6Od#Jwuf3MxO)0GM z!`S=Z7?gSSnP-?fMe2K!WITodxoziXOOyPkTaFu~+0#xQKi=~C>oAMGFCQ|G+3*vc zE|Y2e?aTKjWL}5cXoG$V*e)?!l;)}`GmNFIGFh#rr=ww8PP@DcWhvo0n-Kb- zfq78T+7);Q)FqGuHnWqXN8CF-80Uw;PZ8@(nT!>-kb6iZkJ1CIFI6Ht#vvF4OAcO&)+VR`|FI)eHW?*cxbT>VDf#Y`PkQ?2D(~v~@yE8m{t9W=uWlf(U{G)M zma>^aZUN&8tE1BDs&;<-v9sD`wQH>n)|oS{pl(Pe^fee+5CeZs1dTeU--3RN``y;B zyWeWa0g|(YbtRP;?nKwIE})74*ay76H~1kgGwYy1_!BDOfp4lgAd?6b_{TsY`X~5@ zX;npL8ry^k$_f@wQIcFM!H1CWz4w0YmL~e*6zftXzPQxe{&)IxvR`zp zv8>1$FH4F^Nvw|7JRQ~Za zJ~;!*Kg@x|A5zhw->}<0>=BXtqpFwZSQ3FN16l|iW@_oAz@IXpD4{U@NEwkZV+6S) zk_N1Za^WTUFOZu{Gle3O7w!#?ihwXx2(_VvKe{K%$E_;5cXjMzM+DUkHG%Y1oo{cs z<(8Ezb>8$lM`V@x>h5h?vu6JMt$$~h)#_law_|YTw0$aNS$&9Ooi^L$2pXX6cCXhS zQ)adpEAbwi?Sj2Z?huM6AN;z+GEWI0j_ zN_9%m1?5+_Lt&+r;&Egy9zo=?X%A*-HXy05Cz>tfLlJe@8+4FA6ISDV3}S1EugwQf z-b_?P$b6En1BgM=LAj_1-9nZWJ>_I_@oi&JztKBKj|P(oU8dxXpN2BUGr#`Y_S9jih z+!DrP(d#W`Mq}_foemN=26awdaRpe%Uvsr+IDVj*$GA~Aoj`Qu9`HWPz&MJ)wP44) zknR%|tR`j)t9zpq9-Oytdpu>ODkxRsn28Hj=%;H)LI^~83qFDth7T}JEApR0N{4=% zeju%ZM~V%yY;<2?W{%+xXqnp@dgGgVQ!nVSAI-g0z~4S!+ab@-+;V$o-lSFXtrOW5RAsQb~%fG}YQ^@}$f0Q|9X0I+M0Ipe2RcUujIJJ}b@V&>5Po z`PsHYSy~bK+0xQ1bzz~kAj_5l@~eh&!2bXU9s_(x9FhJJwHMR-ZK0M)na%^;j98T- zwT#e;I3gs}C;IPNXO^`?1x5~Oy9Q8$!N0@@CYQu|Cah9r;WmOHkY5)5=j``GXdI2S z;P;|Sx3u92!viA58y}w~5*~kc+$oiYf?$lNYl79 z^{%p2w$8UsnY&BJ+{YBgfFT<)WDF+k;*23fhI4VfaJk48E-Yu?^PZEm=@@Xo-}j}Q zC8TM4-uHdv|F}&nv}UpS*)n3~%=X^RFXw%I@(wch^$k4}TotOWYTt6g$yF#Kg;0*A zHMmR4vITaVtF~hz>njz<#nvF{4`5J;$;&_d@PksucCh8A(}gzp8=+-{gR8Ka6e_Ku z!eS;Al_gI2v8;5fRtTN9>x#WTG*ZqAejVCV8+h zSXty(8O&j?q0&F)65C5~RjP;!{AebRB<#o*xx=zi! z&Gu+p19R|DX4{bTe0JK~^v^ntUKRXgwS`cJD)CPiO5iW%w)%l%FxGgU%BjKi++661 zG88=fW2k&19Q#~Ahq9nRy_}wF0SBt1`dcNk%MxG#-lhdhpKzg$4&2;SQ!EyCzf$4_ zWHaskN&Po@!b!gp;oZ5@?0#S&v{;Zso5fH41uy%mjqbc?+2qNdJMXOBS$Fhkg{e&C z^|6f{!VAqXGcv^zk{2v-hi;Air{qzND9X>8Ez^g!ig{(Ou%qnqTsqAWJWIs5DpG{A z7rkf6p}iWB1)5qf?+^;ZV(Id5eMbnU&bNg+rWV*MLb~;`bj^eebE#DzU@{8C^M@-9 z$oy^Hitag?Bj$3ZjOUHLE_YV9w0k-+V7MDJJ_pc#43cheaF8|?aSbw#nk2Kiz;wjc zJ=Sv=p$St|7`1hMP6Fot^eIq?AgRG>f|Fq4i;~;@mJgQWsB|_wYNx_u134!v!&+5X zmyte$$Eo3D&svGb%z{VV6nLyB=d!t$s>0fA=5!vX7JoPXB9*=I_U+Zbeuz(m0oKuR&Irkmblw%-4&tQf) z*tjzYoNP+`cp%JXzKUXH*7;OK2mm%@3)tt+ zN5+Kxy7i_S8{JY)XCs{0owO3N33SV`*HX7^oP$dTaDf>DEMdIw|4EmeePP6 z=GyH*tls_m*R8ufT2>kgbQDavvnWeh+yZKPQ)6S}yB5ofZQ9Zji=A+%BJCT!(WsXU zmzBNfwTb*0>H=bH><@oX8ZF zs(qz(GP%97(wCfJ)(w+vPLS)j{>|mCaC_Z#qHL#x=nrxce2zwI^V-UcR-;l@s�z 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 z zV;);$t}Li3RVQ`mjJf4xMxL=2(jjU1pzyRHhY!Il=Eq{N*Y0fQ8@n+YsGq2>YFEvW z%pl`R4X0@=PdprnTu&9p%wK}-!{^^=gUPE^aaLt99TgbnmYbDZt{)vng4eEQ!y#`k zX3vndyY6--_mBdwn~svT?3u-Hhp^$*Ye{fezX4l}47x?t4fD0tx7-`H;}-bhx84!4 zja0GMN@7vt&OLlxMz$s@-q5qMYet8@q+MU-R+wfE_Nvpjm*_iYbgk?$D5G^L>cn(w zWnEF?miAPAYV+#6ru2eTER=oTTFl!X0IK6h+W&{StN$@UZY-hRtZ3@kIbJ%2TEc1C z&f&y297KQ_zTqGF9j+r;P&`7!zn5GKo@{ltuAw`O_^xkTPkh4$Y6^-}+Uyx6l~dDx z4%@jQdK2xueRJfUTUqv)ShLzYTH#Tq8AzO{x#{74o#e=oz;-jq)27sij#jC3$&Jzq zUti)MlANM4xR-`VgZ+plq!T4S-gW?Qt12rsQEup5zP!_*NXST5$0ZgPmlvHNLTlp8NMrs5jSl=*qpE4L(X;fJei*l%%=&Rhg=UrMVu7F}v^2 z#u>;lv!@hqLN-U<@LplR-~lQaIU{JKoVFOGCLDG6KZlynG2LIZ`5k(NU1Nju8OlS* zHP0VizKq%=oopvr{pWVF8n2y~tLby>I(*HK?O-ugCcUAK2%cf3k)HeFRaSM}lZG0d zwUo|mV&@C>WkzF}zL1Pz+X@gg%Wbl`kLs88x6$VD9z(6}qq_q8JN-Jk6B>u$$&Ey{ zptprQk(BLuwZ;l}y%N&b9VmY<%l&HD4VpqSiPX}!`X*GIX6{Cd%OcH6OIvn}MA!r9L~*T!Zh z;Cvb7pnzg5Lb+S0DafPTCrTIYTh-;dRi`a z<5Sn-T-D;`PKRrX>A62%Rj7`83QJ#Z zVQsfpK6N-^ciPJQ+(qd}zLyM)e&mrpE4lN5efv7=>Bitm`I_YXbZTvHUk_JdPP^?A zkYcd;A4s8S?Wg^PlAU*W7e053e#XJarsu|n>D;~Q@(SR{-BFNN;_Z`krID!#NyJ$no|MrPIo0iSdTyq{SQ1-U5-lq? z#utH8iGE4hO8=aR)QAG^ zA6uRY2%J>60sR@UH|$i}nTMk86p{vZvcm0cpeY&!{I_pv=E{}ToJ zcTVIKIBJYXM_WK;lUIGM==|g7Gi6={R)l!2oe8JfRdOTbKY|MZO~*IlR%qqv0QOU4`L