updated ebiten version from 2.7.9 to 2.9.9
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
module github.com/roodletoof/whiteboard
|
module github.com/roodletoof/whiteboard
|
||||||
|
|
||||||
go 1.23.1
|
go 1.24.0
|
||||||
|
|
||||||
require github.com/hajimehoshi/ebiten/v2 v2.7.9
|
require github.com/hajimehoshi/ebiten/v2 v2.9.9
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 // indirect
|
github.com/ebitengine/gomobile v0.0.0-20250923094054-ea854a63cce1 // indirect
|
||||||
github.com/ebitengine/hideconsole v1.0.0 // indirect
|
github.com/ebitengine/hideconsole v1.0.0 // indirect
|
||||||
github.com/ebitengine/purego v0.7.0 // indirect
|
github.com/ebitengine/purego v0.9.0 // indirect
|
||||||
github.com/jezek/xgb v1.1.1 // indirect
|
github.com/jezek/xgb v1.1.1 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/sys v0.36.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU=
|
github.com/ebitengine/gomobile v0.0.0-20250923094054-ea854a63cce1 h1:+kz5iTT3L7uU+VhlMfTb8hHcxLO3TlaELlX8wa4XjA0=
|
||||||
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M=
|
github.com/ebitengine/gomobile v0.0.0-20250923094054-ea854a63cce1/go.mod h1:lKJoeixeJwnFmYsBny4vvCJGVFc3aYDalhuDsfZzWHI=
|
||||||
github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE=
|
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/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
|
||||||
github.com/ebitengine/purego v0.7.0 h1:HPZpl61edMGCEW6XK2nsR6+7AnJ3unUxpTZBkkIXnMc=
|
github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k=
|
||||||
github.com/ebitengine/purego v0.7.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/hajimehoshi/ebiten/v2 v2.7.9 h1:DYH/usAa9dMHcGkBIIEApJsVqDekrJBxYHmsBuly8Iw=
|
github.com/hajimehoshi/ebiten/v2 v2.9.9 h1:JdDag6Ndj12iD4lxQGG8kbsrh7ssj4Sbzth6r929H/M=
|
||||||
github.com/hajimehoshi/ebiten/v2 v2.7.9/go.mod h1:Ulbq5xDmdx47P24EJ+Mb31Zps7vQq+guieG9mghQUaA=
|
github.com/hajimehoshi/ebiten/v2 v2.9.9/go.mod h1:DAt4tnkYYpCvu3x9i1X/nK/vOruNXIlYq/tBXxnhrXM=
|
||||||
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
|
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
|
||||||
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA=
|
||||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
Copyright 2009 The Go Authors.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
|
|||||||
copyright notice, this list of conditions and the following disclaimer
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
in the documentation and/or other materials provided with the
|
in the documentation and/or other materials provided with the
|
||||||
distribution.
|
distribution.
|
||||||
* Neither the name of Google Inc. nor the names of its
|
* Neither the name of Google LLC nor the names of its
|
||||||
contributors may be used to endorse or promote products derived from
|
contributors may be used to endorse or promote products derived from
|
||||||
this software without specific prior written permission.
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -85,4 +85,4 @@ response to lifecycle events. Such packages should call:
|
|||||||
|
|
||||||
in an init function inside that package.
|
in an init function inside that package.
|
||||||
*/
|
*/
|
||||||
package app // import "github.com/ebitengine/gomobile/app"
|
package app
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
// means it has lost the focus.
|
// means it has lost the focus.
|
||||||
//
|
//
|
||||||
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
||||||
package lifecycle // import "github.com/ebitengine/gomobile/event/lifecycle"
|
package lifecycle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
// Package paint defines an event for the app being ready to paint.
|
// Package paint defines an event for the app being ready to paint.
|
||||||
//
|
//
|
||||||
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
||||||
package paint // import "github.com/ebitengine/gomobile/event/paint"
|
package paint
|
||||||
|
|
||||||
// Event indicates that the app is ready to paint the next frame of the GUI.
|
// Event indicates that the app is ready to paint the next frame of the GUI.
|
||||||
//
|
//
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
// orientation of the app's window.
|
// orientation of the app's window.
|
||||||
//
|
//
|
||||||
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
||||||
package size // import "github.com/ebitengine/gomobile/event/size"
|
package size
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
// Package touch defines an event for touch input.
|
// Package touch defines an event for touch input.
|
||||||
//
|
//
|
||||||
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
// See the github.com/ebitengine/gomobile/app package for details on the event model.
|
||||||
package touch // import "github.com/ebitengine/gomobile/event/touch"
|
package touch
|
||||||
|
|
||||||
// The best source on android input events is the NDK: include/android/input.h
|
// The best source on android input events is the NDK: include/android/input.h
|
||||||
//
|
//
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ the screen. Positions on the plane are measured in typographic points,
|
|||||||
Any interface that draws to the screen using types from the geom package
|
Any interface that draws to the screen using types from the geom package
|
||||||
scales the number of pixels to maintain a Pt as 1/72 of an inch.
|
scales the number of pixels to maintain a Pt as 1/72 of an inch.
|
||||||
*/
|
*/
|
||||||
package geom // import "github.com/ebitengine/gomobile/geom"
|
package geom
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Notes on the various underlying coordinate systems.
|
Notes on the various underlying coordinate systems.
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|||||||
+8
-9
@@ -10,7 +10,6 @@ import (
|
|||||||
"debug/pe"
|
"debug/pe"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -18,7 +17,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var debug = log.New(ioutil.Discard, "gl: ", log.LstdFlags)
|
var debug = log.New(io.Discard, "gl: ", log.LstdFlags)
|
||||||
|
|
||||||
func downloadDLLs() (path string, err error) {
|
func downloadDLLs() (path string, err error) {
|
||||||
url := "https://dl.google.com/go/mobile/angle-bd3f8780b-" + runtime.GOARCH + ".tgz"
|
url := "https://dl.google.com/go/mobile/angle-bd3f8780b-" + runtime.GOARCH + ".tgz"
|
||||||
@@ -54,11 +53,11 @@ func downloadDLLs() (path string, err error) {
|
|||||||
}
|
}
|
||||||
switch header.Name {
|
switch header.Name {
|
||||||
case "angle-" + runtime.GOARCH + "/libglesv2.dll":
|
case "angle-" + runtime.GOARCH + "/libglesv2.dll":
|
||||||
bytesGLESv2, err = ioutil.ReadAll(tr)
|
bytesGLESv2, err = io.ReadAll(tr)
|
||||||
case "angle-" + runtime.GOARCH + "/libegl.dll":
|
case "angle-" + runtime.GOARCH + "/libegl.dll":
|
||||||
bytesEGL, err = ioutil.ReadAll(tr)
|
bytesEGL, err = io.ReadAll(tr)
|
||||||
case "angle-" + runtime.GOARCH + "/d3dcompiler_47.dll":
|
case "angle-" + runtime.GOARCH + "/d3dcompiler_47.dll":
|
||||||
bytesD3DCompiler, err = ioutil.ReadAll(tr)
|
bytesD3DCompiler, err = io.ReadAll(tr)
|
||||||
default: // skip
|
default: // skip
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,13 +69,13 @@ func downloadDLLs() (path string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeDLLs := func(path string) error {
|
writeDLLs := func(path string) error {
|
||||||
if err := ioutil.WriteFile(filepath.Join(path, "libglesv2.dll"), bytesGLESv2, 0755); err != nil {
|
if err := os.WriteFile(filepath.Join(path, "libglesv2.dll"), bytesGLESv2, 0755); err != nil {
|
||||||
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
|
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(filepath.Join(path, "libegl.dll"), bytesEGL, 0755); err != nil {
|
if err := os.WriteFile(filepath.Join(path, "libegl.dll"), bytesEGL, 0755); err != nil {
|
||||||
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
|
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(filepath.Join(path, "d3dcompiler_47.dll"), bytesD3DCompiler, 0755); err != nil {
|
if err := os.WriteFile(filepath.Join(path, "d3dcompiler_47.dll"), bytesD3DCompiler, 0755); err != nil {
|
||||||
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
|
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -152,7 +151,7 @@ func chromePath() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, installdir := range installdirs {
|
for _, installdir := range installdirs {
|
||||||
versiondirs, err := ioutil.ReadDir(installdir)
|
versiondirs, err := os.ReadDir(installdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ error messages. For example,
|
|||||||
The gldebug tracing has very high overhead, so make sure to remove
|
The gldebug tracing has very high overhead, so make sure to remove
|
||||||
the build tag before deploying any binaries.
|
the build tag before deploying any binaries.
|
||||||
*/
|
*/
|
||||||
package gl // import "github.com/ebitengine/gomobile/gl"
|
package gl
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Implementation details.
|
Implementation details.
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ type Context interface {
|
|||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendFunc.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendFunc.xhtml
|
||||||
BlendFunc(sfactor, dfactor Enum)
|
BlendFunc(sfactor, dfactor Enum)
|
||||||
|
|
||||||
// BlendFunc sets the pixel RGB and alpha blending factors separately.
|
// BlendFuncSeparate sets the pixel RGB and alpha blending factors separately.
|
||||||
//
|
//
|
||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendFuncSeparate.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendFuncSeparate.xhtml
|
||||||
BlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha Enum)
|
BlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha Enum)
|
||||||
@@ -192,7 +192,7 @@ type Context interface {
|
|||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenTextures.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenTextures.xhtml
|
||||||
CreateTexture() Texture
|
CreateTexture() Texture
|
||||||
|
|
||||||
// CreateTVertexArray creates a vertex array.
|
// CreateVertexArray creates a vertex array.
|
||||||
//
|
//
|
||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenVertexArrays.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenVertexArrays.xhtml
|
||||||
CreateVertexArray() VertexArray
|
CreateVertexArray() VertexArray
|
||||||
@@ -599,7 +599,7 @@ type Context interface {
|
|||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilFunc.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilFunc.xhtml
|
||||||
StencilFunc(fn Enum, ref int, mask uint32)
|
StencilFunc(fn Enum, ref int, mask uint32)
|
||||||
|
|
||||||
// StencilFunc sets the front or back stencil test reference value.
|
// StencilFuncSeparate sets the front or back stencil test reference value.
|
||||||
//
|
//
|
||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilFuncSeparate.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilFuncSeparate.xhtml
|
||||||
StencilFuncSeparate(face, fn Enum, ref int, mask uint32)
|
StencilFuncSeparate(face, fn Enum, ref int, mask uint32)
|
||||||
@@ -737,7 +737,7 @@ type Context interface {
|
|||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
|
||||||
Uniform4i(dst Uniform, v0, v1, v2, v3 int32)
|
Uniform4i(dst Uniform, v0, v1, v2, v3 int32)
|
||||||
|
|
||||||
// Uniform4i writes an ivec4 uniform array of len(src)/4 elements.
|
// Uniform4iv writes an ivec4 uniform array of len(src)/4 elements.
|
||||||
//
|
//
|
||||||
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
|
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
|
||||||
Uniform4iv(dst Uniform, src []int32)
|
Uniform4iv(dst Uniform, src []int32)
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -160,7 +160,7 @@ func (ctx *context) cString(str string) (uintptr, func()) {
|
|||||||
return uintptr(ptr), func() { C.free(ptr) }
|
return uintptr(ptr), func() { C.free(ptr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// cString creates a pointer to a C string off the Go heap.
|
// cStringPtr creates a pointer to a C string off the Go heap.
|
||||||
// ret is a **char.
|
// ret is a **char.
|
||||||
func (ctx *context) cStringPtr(str string) (uintptr, func()) {
|
func (ctx *context) cStringPtr(str string) (uintptr, func()) {
|
||||||
s, free := ctx.cString(str)
|
s, free := ctx.cString(str)
|
||||||
|
|||||||
+24
-7
@@ -26,16 +26,31 @@ except for float arguments and return values.
|
|||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
|
|
||||||
- **FreeBSD**: amd64, arm64
|
### Tier 1
|
||||||
- **Linux**: amd64, arm64
|
|
||||||
- **macOS / iOS**: amd64, arm64
|
|
||||||
- **Windows**: 386*, amd64, arm*, arm64
|
|
||||||
|
|
||||||
`*` These architectures only support SyscallN and NewCallback
|
Tier 1 platforms are the primary targets officially supported by PureGo. When a new version of PureGo is released, any critical bugs found on Tier 1 platforms are treated as release blockers. The release will be postponed until such issues are resolved.
|
||||||
|
|
||||||
|
- **Android**: amd64, arm64
|
||||||
|
- **iOS**: amd64, arm64
|
||||||
|
- **Linux**: amd64, arm64
|
||||||
|
- **macOS**: amd64, arm64
|
||||||
|
- **Windows**: amd64, arm64
|
||||||
|
|
||||||
|
### Tier 2
|
||||||
|
|
||||||
|
Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs on Tier 2 platforms do not block new PureGo releases. However, fixes contributed by external contributors are very welcome and encouraged.
|
||||||
|
|
||||||
|
- **Android**: 386, arm
|
||||||
|
- **FreeBSD**: amd64, arm64
|
||||||
|
- **Linux**: 386, arm, loong64
|
||||||
|
- **Windows**: 386*, arm*
|
||||||
|
|
||||||
|
`*` These architectures only support `SyscallN` and `NewCallback`
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
This example only works on macOS and Linux. For a complete example look at [libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports Windows and FreeBSD.
|
The example below only showcases purego use for macOS and Linux. The other platforms require special handling which can
|
||||||
|
be seen in the complete example at [examples/libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports FreeBSD and Windows.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -74,7 +89,7 @@ Then to run: `CGO_ENABLED=0 go run main.go`
|
|||||||
## Questions
|
## Questions
|
||||||
|
|
||||||
If you have questions about how to incorporate purego in your project or want to discuss
|
If you have questions about how to incorporate purego in your project or want to discuss
|
||||||
how it works join the [Discord](https://discord.com/channels/842049801528016967/1123106378731487345)!
|
how it works join the [Discord](https://discord.gg/HzGZVD6BkY)!
|
||||||
|
|
||||||
### External Code
|
### External Code
|
||||||
|
|
||||||
@@ -83,6 +98,7 @@ License that can be found [in the Go Source](https://github.com/golang/go/blob/m
|
|||||||
This is a list of the copied files:
|
This is a list of the copied files:
|
||||||
|
|
||||||
* `abi_*.h` from package `runtime/cgo`
|
* `abi_*.h` from package `runtime/cgo`
|
||||||
|
* `wincallback.go` from package `runtime`
|
||||||
* `zcallback_darwin_*.s` from package `runtime`
|
* `zcallback_darwin_*.s` from package `runtime`
|
||||||
* `internal/fakecgo/abi_*.h` from package `runtime/cgo`
|
* `internal/fakecgo/abi_*.h` from package `runtime/cgo`
|
||||||
* `internal/fakecgo/asm_GOARCH.s` from package `runtime/cgo`
|
* `internal/fakecgo/asm_GOARCH.s` from package `runtime/cgo`
|
||||||
@@ -91,6 +107,7 @@ This is a list of the copied files:
|
|||||||
* `internal/fakecgo/iscgo.go` from package `runtime/cgo`
|
* `internal/fakecgo/iscgo.go` from package `runtime/cgo`
|
||||||
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
|
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
|
||||||
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
|
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
|
||||||
|
* `internal/fakecgo/netbsd.go` from package `runtime/cgo`
|
||||||
|
|
||||||
The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of
|
The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of
|
||||||
`#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636))
|
`#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636))
|
||||||
|
|||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Macros for transitioning from the host ABI to Go ABI0.
|
||||||
|
//
|
||||||
|
// These macros save and restore the callee-saved registers
|
||||||
|
// from the stack, but they don't adjust stack pointer, so
|
||||||
|
// the user should prepare stack space in advance.
|
||||||
|
// SAVE_R22_TO_R31(offset) saves R22 ~ R31 to the stack space
|
||||||
|
// of ((offset)+0*8)(R3) ~ ((offset)+9*8)(R3).
|
||||||
|
//
|
||||||
|
// SAVE_F24_TO_F31(offset) saves F24 ~ F31 to the stack space
|
||||||
|
// of ((offset)+0*8)(R3) ~ ((offset)+7*8)(R3).
|
||||||
|
//
|
||||||
|
// Note: g is R22
|
||||||
|
|
||||||
|
#define SAVE_R22_TO_R31(offset) \
|
||||||
|
MOVV g, ((offset)+(0*8))(R3) \
|
||||||
|
MOVV R23, ((offset)+(1*8))(R3) \
|
||||||
|
MOVV R24, ((offset)+(2*8))(R3) \
|
||||||
|
MOVV R25, ((offset)+(3*8))(R3) \
|
||||||
|
MOVV R26, ((offset)+(4*8))(R3) \
|
||||||
|
MOVV R27, ((offset)+(5*8))(R3) \
|
||||||
|
MOVV R28, ((offset)+(6*8))(R3) \
|
||||||
|
MOVV R29, ((offset)+(7*8))(R3) \
|
||||||
|
MOVV R30, ((offset)+(8*8))(R3) \
|
||||||
|
MOVV R31, ((offset)+(9*8))(R3)
|
||||||
|
|
||||||
|
#define SAVE_F24_TO_F31(offset) \
|
||||||
|
MOVD F24, ((offset)+(0*8))(R3) \
|
||||||
|
MOVD F25, ((offset)+(1*8))(R3) \
|
||||||
|
MOVD F26, ((offset)+(2*8))(R3) \
|
||||||
|
MOVD F27, ((offset)+(3*8))(R3) \
|
||||||
|
MOVD F28, ((offset)+(4*8))(R3) \
|
||||||
|
MOVD F29, ((offset)+(5*8))(R3) \
|
||||||
|
MOVD F30, ((offset)+(6*8))(R3) \
|
||||||
|
MOVD F31, ((offset)+(7*8))(R3)
|
||||||
|
|
||||||
|
#define RESTORE_R22_TO_R31(offset) \
|
||||||
|
MOVV ((offset)+(0*8))(R3), g \
|
||||||
|
MOVV ((offset)+(1*8))(R3), R23 \
|
||||||
|
MOVV ((offset)+(2*8))(R3), R24 \
|
||||||
|
MOVV ((offset)+(3*8))(R3), R25 \
|
||||||
|
MOVV ((offset)+(4*8))(R3), R26 \
|
||||||
|
MOVV ((offset)+(5*8))(R3), R27 \
|
||||||
|
MOVV ((offset)+(6*8))(R3), R28 \
|
||||||
|
MOVV ((offset)+(7*8))(R3), R29 \
|
||||||
|
MOVV ((offset)+(8*8))(R3), R30 \
|
||||||
|
MOVV ((offset)+(9*8))(R3), R31
|
||||||
|
|
||||||
|
#define RESTORE_F24_TO_F31(offset) \
|
||||||
|
MOVD ((offset)+(0*8))(R3), F24 \
|
||||||
|
MOVD ((offset)+(1*8))(R3), F25 \
|
||||||
|
MOVD ((offset)+(2*8))(R3), F26 \
|
||||||
|
MOVD ((offset)+(3*8))(R3), F27 \
|
||||||
|
MOVD ((offset)+(4*8))(R3), F28 \
|
||||||
|
MOVD ((offset)+(5*8))(R3), F29 \
|
||||||
|
MOVD ((offset)+(6*8))(R3), F30 \
|
||||||
|
MOVD ((offset)+(7*8))(R3), F31
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build cgo && (darwin || freebsd || linux)
|
//go:build cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -1,11 +1,13 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build darwin || freebsd || linux || netbsd
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose.
|
// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose.
|
||||||
|
//
|
||||||
|
// This type is not available on Windows as there is no counterpart to it on Windows.
|
||||||
type Dlerror struct {
|
type Dlerror struct {
|
||||||
s string
|
s string
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-10
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build (darwin || freebsd || linux || netbsd) && !android && !faketime
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
@@ -33,6 +33,10 @@ func init() {
|
|||||||
// A second call to Dlopen with the same path will return the same handle, but the internal
|
// A second call to Dlopen with the same path will return the same handle, but the internal
|
||||||
// reference count for the handle will be incremented. Therefore, all
|
// reference count for the handle will be incremented. Therefore, all
|
||||||
// Dlopen calls should be balanced with a Dlclose call.
|
// Dlopen calls should be balanced with a Dlclose call.
|
||||||
|
//
|
||||||
|
// This function is not available on Windows.
|
||||||
|
// Use [golang.org/x/sys/windows.LoadLibrary], [golang.org/x/sys/windows.LoadLibraryEx],
|
||||||
|
// [golang.org/x/sys/windows.NewLazyDLL], or [golang.org/x/sys/windows.NewLazySystemDLL] for Windows instead.
|
||||||
func Dlopen(path string, mode int) (uintptr, error) {
|
func Dlopen(path string, mode int) (uintptr, error) {
|
||||||
u := fnDlopen(path, mode)
|
u := fnDlopen(path, mode)
|
||||||
if u == 0 {
|
if u == 0 {
|
||||||
@@ -45,6 +49,9 @@ func Dlopen(path string, mode int) (uintptr, error) {
|
|||||||
// It returns the address where that symbol is loaded into memory. If the symbol is not found,
|
// It returns the address where that symbol is loaded into memory. If the symbol is not found,
|
||||||
// in the specified library or any of the libraries that were automatically loaded by Dlopen
|
// in the specified library or any of the libraries that were automatically loaded by Dlopen
|
||||||
// when that library was loaded, Dlsym returns zero.
|
// when that library was loaded, Dlsym returns zero.
|
||||||
|
//
|
||||||
|
// This function is not available on Windows.
|
||||||
|
// Use [golang.org/x/sys/windows.GetProcAddress] for Windows instead.
|
||||||
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
||||||
u := fnDlsym(handle, name)
|
u := fnDlsym(handle, name)
|
||||||
if u == 0 {
|
if u == 0 {
|
||||||
@@ -56,6 +63,9 @@ func Dlsym(handle uintptr, name string) (uintptr, error) {
|
|||||||
// Dlclose decrements the reference count on the dynamic library handle.
|
// Dlclose decrements the reference count on the dynamic library handle.
|
||||||
// If the reference count drops to zero and no other loaded libraries
|
// If the reference count drops to zero and no other loaded libraries
|
||||||
// use symbols in it, then the dynamic library is unloaded.
|
// use symbols in it, then the dynamic library is unloaded.
|
||||||
|
//
|
||||||
|
// This function is not available on Windows.
|
||||||
|
// Use [golang.org/x/sys/windows.FreeLibrary] for Windows instead.
|
||||||
func Dlclose(handle uintptr) error {
|
func Dlclose(handle uintptr) error {
|
||||||
if fnDlclose(handle) {
|
if fnDlclose(handle) {
|
||||||
return Dlerror{fnDlerror()}
|
return Dlerror{fnDlerror()}
|
||||||
@@ -63,11 +73,6 @@ func Dlclose(handle uintptr) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname openLibrary openLibrary
|
|
||||||
func openLibrary(name string) (uintptr, error) {
|
|
||||||
return Dlopen(name, RTLD_NOW|RTLD_GLOBAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||||
return Dlsym(handle, name)
|
return Dlsym(handle, name)
|
||||||
}
|
}
|
||||||
@@ -78,17 +83,17 @@ func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
|||||||
// appear to work if you link directly to the C function on darwin arm64.
|
// appear to work if you link directly to the C function on darwin arm64.
|
||||||
|
|
||||||
//go:linkname dlopen dlopen
|
//go:linkname dlopen dlopen
|
||||||
var dlopen uintptr
|
var dlopen uint8
|
||||||
var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
|
var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
|
||||||
|
|
||||||
//go:linkname dlsym dlsym
|
//go:linkname dlsym dlsym
|
||||||
var dlsym uintptr
|
var dlsym uint8
|
||||||
var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
|
var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
|
||||||
|
|
||||||
//go:linkname dlclose dlclose
|
//go:linkname dlclose dlclose
|
||||||
var dlclose uintptr
|
var dlclose uint8
|
||||||
var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
|
var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
|
||||||
|
|
||||||
//go:linkname dlerror dlerror
|
//go:linkname dlerror dlerror
|
||||||
var dlerror uintptr
|
var dlerror uint8
|
||||||
var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
|
var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||||
|
|
||||||
|
package purego
|
||||||
|
|
||||||
|
import "github.com/ebitengine/purego/internal/cgo"
|
||||||
|
|
||||||
|
// Source for constants: https://android.googlesource.com/platform/bionic/+/refs/heads/main/libc/include/dlfcn.h
|
||||||
|
|
||||||
|
const (
|
||||||
|
is64bit = 1 << (^uintptr(0) >> 63) / 2
|
||||||
|
is32bit = 1 - is64bit
|
||||||
|
RTLD_DEFAULT = is32bit * 0xffffffff
|
||||||
|
RTLD_LAZY = 0x00000001
|
||||||
|
RTLD_NOW = is64bit * 0x00000002
|
||||||
|
RTLD_LOCAL = 0x00000000
|
||||||
|
RTLD_GLOBAL = is64bit*0x00100 | is32bit*0x00000002
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dlopen(path string, mode int) (uintptr, error) {
|
||||||
|
return cgo.Dlopen(path, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
||||||
|
return cgo.Dlsym(handle, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dlclose(handle uintptr) error {
|
||||||
|
return cgo.Dlclose(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||||
|
return Dlsym(handle, name)
|
||||||
|
}
|
||||||
+5
-10
@@ -6,19 +6,14 @@ package purego
|
|||||||
// Source for constants: https://opensource.apple.com/source/dyld/dyld-360.14/include/dlfcn.h.auto.html
|
// Source for constants: https://opensource.apple.com/source/dyld/dyld-360.14/include/dlfcn.h.auto.html
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RTLD_DEFAULT = ^uintptr(0) - 1 // Pseudo-handle for dlsym so search for any loaded symbol
|
RTLD_DEFAULT = 1<<64 - 2 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||||
RTLD_LAZY = 0x1 // Relocations are performed at an implementation-dependent time.
|
RTLD_LAZY = 0x1 // Relocations are performed at an implementation-dependent time.
|
||||||
RTLD_NOW = 0x2 // Relocations are performed when the object is loaded.
|
RTLD_NOW = 0x2 // Relocations are performed when the object is loaded.
|
||||||
RTLD_LOCAL = 0x4 // All symbols are not made available for relocation processing by other modules.
|
RTLD_LOCAL = 0x4 // All symbols are not made available for relocation processing by other modules.
|
||||||
RTLD_GLOBAL = 0x8 // All symbols are available for relocation processing of other modules.
|
RTLD_GLOBAL = 0x8 // All symbols are available for relocation processing of other modules.
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
|
||||||
|
|
||||||
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
|
|
||||||
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
|
|
||||||
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
|
|
||||||
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
|
|
||||||
|
|||||||
+6
-5
@@ -5,9 +5,10 @@ package purego
|
|||||||
|
|
||||||
// Constants as defined in https://github.com/freebsd/freebsd-src/blob/main/include/dlfcn.h
|
// Constants as defined in https://github.com/freebsd/freebsd-src/blob/main/include/dlfcn.h
|
||||||
const (
|
const (
|
||||||
RTLD_DEFAULT = ^uintptr(0) - 2 // Pseudo-handle for dlsym so search for any loaded symbol
|
intSize = 32 << (^uint(0) >> 63) // 32 or 64
|
||||||
RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time.
|
RTLD_DEFAULT = 1<<intSize - 2 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||||
RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded.
|
RTLD_LAZY = 0x00000001 // Relocations are performed at an implementation-dependent time.
|
||||||
RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules.
|
RTLD_NOW = 0x00000002 // Relocations are performed when the object is loaded.
|
||||||
RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules.
|
RTLD_LOCAL = 0x00000000 // All symbols are not made available for relocation processing by other modules.
|
||||||
|
RTLD_GLOBAL = 0x00000100 // All symbols are available for relocation processing of other modules.
|
||||||
)
|
)
|
||||||
|
|||||||
+2
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !android
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
// Source for constants: https://codebrowser.dev/glibc/glibc/bits/dlfcn.h.html
|
// Source for constants: https://codebrowser.dev/glibc/glibc/bits/dlfcn.h.html
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
package purego
|
||||||
|
|
||||||
|
// Source for constants: https://github.com/NetBSD/src/blob/trunk/include/dlfcn.h
|
||||||
|
|
||||||
|
const (
|
||||||
|
intSize = 32 << (^uint(0) >> 63) // 32 or 64
|
||||||
|
RTLD_DEFAULT = 1<<intSize - 2 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||||
|
RTLD_LAZY = 0x00000001 // Relocations are performed at an implementation-dependent time.
|
||||||
|
RTLD_NOW = 0x00000002 // Relocations are performed when the object is loaded.
|
||||||
|
RTLD_LOCAL = 0x00000000 // All symbols are not made available for relocation processing by other modules.
|
||||||
|
RTLD_GLOBAL = 0x00000100 // All symbols are available for relocation processing of other modules.
|
||||||
|
)
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build !cgo
|
//go:build !cgo && !faketime
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
package purego
|
||||||
|
|
||||||
|
//go:cgo_import_dynamic purego_dlopen dlopen "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_dlsym dlsym "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_dlerror dlerror "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_dlclose dlclose "libc.so"
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build faketime
|
||||||
|
|
||||||
|
package purego
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func Dlopen(path string, mode int) (uintptr, error) {
|
||||||
|
return 0, errors.New("Dlopen is not supported in the playground")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
||||||
|
return 0, errors.New("Dlsym is not supported in the playground")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dlclose(handle uintptr) error {
|
||||||
|
return errors.New("Dlclose is not supported in the playground")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||||
|
return Dlsym(handle, name)
|
||||||
|
}
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || !cgo && (freebsd || linux)
|
//go:build darwin || !cgo && (freebsd || linux || netbsd) && !faketime
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
+160
-80
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux || windows
|
//go:build darwin || freebsd || linux || netbsd || windows
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
@@ -10,14 +10,20 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/ebitengine/purego/internal/strings"
|
"github.com/ebitengine/purego/internal/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var thePool = sync.Pool{New: func() any {
|
||||||
|
return new(syscall15Args)
|
||||||
|
}}
|
||||||
|
|
||||||
// RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
|
// RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
|
||||||
// It panics if it can't find the name symbol.
|
// It panics if it can't find the name symbol.
|
||||||
func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
|
func RegisterLibFunc(fptr any, handle uintptr, name string) {
|
||||||
sym, err := loadSymbol(handle, name)
|
sym, err := loadSymbol(handle, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -60,7 +66,7 @@ func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
|
|||||||
//
|
//
|
||||||
// There is a special case when the last argument of fptr is a variadic interface (or []interface}
|
// There is a special case when the last argument of fptr is a variadic interface (or []interface}
|
||||||
// it will be expanded into a call to the C function as if it had the arguments in that slice.
|
// it will be expanded into a call to the C function as if it had the arguments in that slice.
|
||||||
// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
|
// This means that using arg ...any is like a cast to the function with the arguments inside arg.
|
||||||
// This is not the same as C variadic.
|
// This is not the same as C variadic.
|
||||||
//
|
//
|
||||||
// # Memory
|
// # Memory
|
||||||
@@ -105,7 +111,7 @@ func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
|
|||||||
// defer free(mustFree)
|
// defer free(mustFree)
|
||||||
//
|
//
|
||||||
// [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C
|
// [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C
|
||||||
func RegisterFunc(fptr interface{}, cfn uintptr) {
|
func RegisterFunc(fptr any, cfn uintptr) {
|
||||||
fn := reflect.ValueOf(fptr).Elem()
|
fn := reflect.ValueOf(fptr).Elem()
|
||||||
ty := fn.Type()
|
ty := fn.Type()
|
||||||
if ty.Kind() != reflect.Func {
|
if ty.Kind() != reflect.Func {
|
||||||
@@ -117,6 +123,10 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
|
|||||||
if cfn == 0 {
|
if cfn == 0 {
|
||||||
panic("purego: cfn is nil")
|
panic("purego: cfn is nil")
|
||||||
}
|
}
|
||||||
|
if ty.NumOut() == 1 && (ty.Out(0).Kind() == reflect.Float32 || ty.Out(0).Kind() == reflect.Float64) &&
|
||||||
|
runtime.GOARCH != "arm64" && runtime.GOARCH != "amd64" && runtime.GOARCH != "loong64" {
|
||||||
|
panic("purego: float returns are not supported")
|
||||||
|
}
|
||||||
{
|
{
|
||||||
// this code checks how many registers and stack this function will use
|
// this code checks how many registers and stack this function will use
|
||||||
// to avoid crashing with too many arguments
|
// to avoid crashing with too many arguments
|
||||||
@@ -126,19 +136,33 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
|
|||||||
for i := 0; i < ty.NumIn(); i++ {
|
for i := 0; i < ty.NumIn(); i++ {
|
||||||
arg := ty.In(i)
|
arg := ty.In(i)
|
||||||
switch arg.Kind() {
|
switch arg.Kind() {
|
||||||
|
case reflect.Func:
|
||||||
|
// This only does preliminary testing to ensure the CDecl argument
|
||||||
|
// is the first argument. Full testing is done when the callback is actually
|
||||||
|
// created in NewCallback.
|
||||||
|
for j := 0; j < arg.NumIn(); j++ {
|
||||||
|
in := arg.In(j)
|
||||||
|
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if j != 0 {
|
||||||
|
panic("purego: CDecl must be the first argument")
|
||||||
|
}
|
||||||
|
}
|
||||||
case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Ptr, reflect.UnsafePointer, reflect.Slice,
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Ptr, reflect.UnsafePointer,
|
||||||
reflect.Func, reflect.Bool:
|
reflect.Slice, reflect.Bool:
|
||||||
if ints < numOfIntegerRegisters() {
|
if ints < numOfIntegerRegisters() {
|
||||||
ints++
|
ints++
|
||||||
} else {
|
} else {
|
||||||
stack++
|
stack++
|
||||||
}
|
}
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
|
const is32bit = unsafe.Sizeof(uintptr(0)) == 4
|
||||||
if is32bit {
|
if is32bit {
|
||||||
panic("purego: floats only supported on 64bit platforms")
|
panic("purego: floats only supported on 64bit platforms")
|
||||||
}
|
}
|
||||||
if floats < numOfFloats {
|
if floats < numOfFloatRegisters {
|
||||||
floats++
|
floats++
|
||||||
} else {
|
} else {
|
||||||
stack++
|
stack++
|
||||||
@@ -182,21 +206,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
|
v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
|
||||||
if len(args) > 0 {
|
|
||||||
if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok {
|
|
||||||
// subtract one from args bc the last argument in args is []interface{}
|
|
||||||
// which we are currently expanding
|
|
||||||
tmp := make([]reflect.Value, len(args)-1+len(variadic))
|
|
||||||
n := copy(tmp, args[:len(args)-1])
|
|
||||||
for i, v := range variadic {
|
|
||||||
tmp[n+i] = reflect.ValueOf(v)
|
|
||||||
}
|
|
||||||
args = tmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var sysargs [maxArgs]uintptr
|
var sysargs [maxArgs]uintptr
|
||||||
stack := sysargs[numOfIntegerRegisters():]
|
var floats [numOfFloatRegisters]uintptr
|
||||||
var floats [numOfFloats]uintptr
|
|
||||||
var numInts int
|
var numInts int
|
||||||
var numFloats int
|
var numFloats int
|
||||||
var numStack int
|
var numStack int
|
||||||
@@ -204,7 +215,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
|
|||||||
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
||||||
// Windows arm64 uses the same calling convention as macOS and Linux
|
// Windows arm64 uses the same calling convention as macOS and Linux
|
||||||
addStack = func(x uintptr) {
|
addStack = func(x uintptr) {
|
||||||
stack[numStack] = x
|
sysargs[numOfIntegerRegisters()+numStack] = x
|
||||||
numStack++
|
numStack++
|
||||||
}
|
}
|
||||||
addInt = func(x uintptr) {
|
addInt = func(x uintptr) {
|
||||||
@@ -237,69 +248,91 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
|
|||||||
addFloat = addStack
|
addFloat = addStack
|
||||||
}
|
}
|
||||||
|
|
||||||
var keepAlive []interface{}
|
var keepAlive []any
|
||||||
defer func() {
|
defer func() {
|
||||||
runtime.KeepAlive(keepAlive)
|
runtime.KeepAlive(keepAlive)
|
||||||
runtime.KeepAlive(args)
|
runtime.KeepAlive(args)
|
||||||
}()
|
}()
|
||||||
var syscall syscall15Args
|
|
||||||
|
var arm64_r8 uintptr
|
||||||
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
|
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
|
||||||
outType := ty.Out(0)
|
outType := ty.Out(0)
|
||||||
if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
|
if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64") && outType.Size() > maxRegAllocStructSize {
|
||||||
val := reflect.New(outType)
|
val := reflect.New(outType)
|
||||||
keepAlive = append(keepAlive, val)
|
keepAlive = append(keepAlive, val)
|
||||||
addInt(val.Pointer())
|
addInt(val.Pointer())
|
||||||
} else if runtime.GOARCH == "arm64" && outType.Size() > maxRegAllocStructSize {
|
} else if runtime.GOARCH == "arm64" && outType.Size() > maxRegAllocStructSize {
|
||||||
if !isAllSameFloat(outType) || outType.NumField() > 4 {
|
isAllFloats, numFields := isAllSameFloat(outType)
|
||||||
|
if !isAllFloats || numFields > 4 {
|
||||||
val := reflect.New(outType)
|
val := reflect.New(outType)
|
||||||
keepAlive = append(keepAlive, val)
|
keepAlive = append(keepAlive, val)
|
||||||
syscall.arm64_r8 = val.Pointer()
|
arm64_r8 = val.Pointer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, v := range args {
|
for i, v := range args {
|
||||||
switch v.Kind() {
|
if variadic, ok := args[i].Interface().([]any); ok {
|
||||||
case reflect.String:
|
if i != len(args)-1 {
|
||||||
ptr := strings.CString(v.String())
|
panic("purego: can only expand last parameter")
|
||||||
keepAlive = append(keepAlive, ptr)
|
|
||||||
addInt(uintptr(unsafe.Pointer(ptr)))
|
|
||||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
addInt(uintptr(v.Uint()))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
addInt(uintptr(v.Int()))
|
|
||||||
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
|
|
||||||
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
|
|
||||||
addInt(v.Pointer())
|
|
||||||
case reflect.Func:
|
|
||||||
addInt(NewCallback(v.Interface()))
|
|
||||||
case reflect.Bool:
|
|
||||||
if v.Bool() {
|
|
||||||
addInt(1)
|
|
||||||
} else {
|
|
||||||
addInt(0)
|
|
||||||
}
|
}
|
||||||
case reflect.Float32:
|
for _, x := range variadic {
|
||||||
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
|
keepAlive = addValue(reflect.ValueOf(x), keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
|
||||||
case reflect.Float64:
|
}
|
||||||
addFloat(uintptr(math.Float64bits(v.Float())))
|
continue
|
||||||
case reflect.Struct:
|
|
||||||
keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive)
|
|
||||||
default:
|
|
||||||
panic("purego: unsupported kind: " + v.Kind().String())
|
|
||||||
}
|
}
|
||||||
|
if runtime.GOARCH == "arm64" && runtime.GOOS == "darwin" &&
|
||||||
|
(numInts >= numOfIntegerRegisters() || numFloats >= numOfFloatRegisters) && v.Kind() != reflect.Struct { // hit the stack
|
||||||
|
fields := make([]reflect.StructField, len(args[i:]))
|
||||||
|
|
||||||
|
for j, val := range args[i:] {
|
||||||
|
if val.Kind() == reflect.String {
|
||||||
|
ptr := strings.CString(v.String())
|
||||||
|
keepAlive = append(keepAlive, ptr)
|
||||||
|
val = reflect.ValueOf(ptr)
|
||||||
|
args[i+j] = val
|
||||||
|
}
|
||||||
|
fields[j] = reflect.StructField{
|
||||||
|
Name: "X" + strconv.Itoa(j),
|
||||||
|
Type: val.Type(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
structType := reflect.StructOf(fields)
|
||||||
|
structInstance := reflect.New(structType).Elem()
|
||||||
|
for j, val := range args[i:] {
|
||||||
|
structInstance.Field(j).Set(val)
|
||||||
|
}
|
||||||
|
placeRegisters(structInstance, addFloat, addInt)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
|
||||||
}
|
}
|
||||||
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
|
||||||
// Use the normal arm64 calling convention even on Windows
|
syscall := thePool.Get().(*syscall15Args)
|
||||||
syscall = syscall15Args{
|
defer thePool.Put(syscall)
|
||||||
|
|
||||||
|
if runtime.GOARCH == "loong64" {
|
||||||
|
*syscall = syscall15Args{
|
||||||
cfn,
|
cfn,
|
||||||
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5],
|
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5],
|
||||||
sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
|
sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
|
||||||
sysargs[12], sysargs[13], sysargs[14],
|
sysargs[12], sysargs[13], sysargs[14],
|
||||||
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
|
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
|
||||||
syscall.arm64_r8,
|
0,
|
||||||
}
|
}
|
||||||
runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall))
|
runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall))
|
||||||
|
} else if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
||||||
|
// Use the normal arm64 calling convention even on Windows
|
||||||
|
*syscall = syscall15Args{
|
||||||
|
cfn,
|
||||||
|
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5],
|
||||||
|
sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
|
||||||
|
sysargs[12], sysargs[13], sysargs[14],
|
||||||
|
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
|
||||||
|
arm64_r8,
|
||||||
|
}
|
||||||
|
runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall))
|
||||||
} else {
|
} else {
|
||||||
|
*syscall = syscall15Args{}
|
||||||
// This is a fallback for Windows amd64, 386, and arm. Note this may not support floats
|
// This is a fallback for Windows amd64, 386, and arm. Note this may not support floats
|
||||||
syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4],
|
syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4],
|
||||||
sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
|
sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
|
||||||
@@ -322,8 +355,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
|
|||||||
// We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer
|
// We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer
|
||||||
v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1)))
|
v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1)))
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
// It is safe to have the address of syscall.r1 not escape because it is immediately dereferenced with .Elem()
|
v = reflect.NewAt(outType, unsafe.Pointer(&syscall.a1)).Elem()
|
||||||
v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&syscall.a1))).Elem()
|
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
// wrap this C function in a nicely typed Go function
|
// wrap this C function in a nicely typed Go function
|
||||||
v = reflect.New(outType)
|
v = reflect.New(outType)
|
||||||
@@ -339,32 +371,85 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
|
|||||||
// On 32bit platforms syscall.r2 is the upper part of a 64bit return.
|
// On 32bit platforms syscall.r2 is the upper part of a 64bit return.
|
||||||
v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
|
v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
v = getStruct(outType, syscall)
|
v = getStruct(outType, *syscall)
|
||||||
default:
|
default:
|
||||||
panic("purego: unsupported return kind: " + outType.Kind().String())
|
panic("purego: unsupported return kind: " + outType.Kind().String())
|
||||||
}
|
}
|
||||||
return []reflect.Value{v}
|
if len(args) > 0 {
|
||||||
|
// reuse args slice instead of allocating one when possible
|
||||||
|
args[0] = v
|
||||||
|
return args[:1]
|
||||||
|
} else {
|
||||||
|
return []reflect.Value{v}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
fn.Set(v)
|
fn.Set(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat func(x uintptr), addStack func(x uintptr), numInts *int, numFloats *int, numStack *int) []any {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
ptr := strings.CString(v.String())
|
||||||
|
keepAlive = append(keepAlive, ptr)
|
||||||
|
addInt(uintptr(unsafe.Pointer(ptr)))
|
||||||
|
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
addInt(uintptr(v.Uint()))
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
addInt(uintptr(v.Int()))
|
||||||
|
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
|
||||||
|
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
|
||||||
|
addInt(v.Pointer())
|
||||||
|
case reflect.Func:
|
||||||
|
addInt(NewCallback(v.Interface()))
|
||||||
|
case reflect.Bool:
|
||||||
|
if v.Bool() {
|
||||||
|
addInt(1)
|
||||||
|
} else {
|
||||||
|
addInt(0)
|
||||||
|
}
|
||||||
|
case reflect.Float32:
|
||||||
|
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
|
||||||
|
case reflect.Float64:
|
||||||
|
addFloat(uintptr(math.Float64bits(v.Float())))
|
||||||
|
case reflect.Struct:
|
||||||
|
keepAlive = addStruct(v, numInts, numFloats, numStack, addInt, addFloat, addStack, keepAlive)
|
||||||
|
default:
|
||||||
|
panic("purego: unsupported kind: " + v.Kind().String())
|
||||||
|
}
|
||||||
|
return keepAlive
|
||||||
|
}
|
||||||
|
|
||||||
// maxRegAllocStructSize is the biggest a struct can be while still fitting in registers.
|
// maxRegAllocStructSize is the biggest a struct can be while still fitting in registers.
|
||||||
// if it is bigger than this than enough space must be allocated on the heap and then passed into
|
// if it is bigger than this than enough space must be allocated on the heap and then passed into
|
||||||
// the function as the first parameter on amd64 or in R8 on arm64.
|
// the function as the first parameter on amd64 or in R8 on arm64.
|
||||||
|
//
|
||||||
|
// If you change this make sure to update it in objc_runtime_darwin.go
|
||||||
const maxRegAllocStructSize = 16
|
const maxRegAllocStructSize = 16
|
||||||
|
|
||||||
func isAllSameFloat(ty reflect.Type) bool {
|
func isAllSameFloat(ty reflect.Type) (allFloats bool, numFields int) {
|
||||||
first := ty.Field(0).Type.Kind()
|
allFloats = true
|
||||||
|
root := ty.Field(0).Type
|
||||||
|
for root.Kind() == reflect.Struct {
|
||||||
|
root = root.Field(0).Type
|
||||||
|
}
|
||||||
|
first := root.Kind()
|
||||||
if first != reflect.Float32 && first != reflect.Float64 {
|
if first != reflect.Float32 && first != reflect.Float64 {
|
||||||
return false
|
allFloats = false
|
||||||
}
|
}
|
||||||
for i := 0; i < ty.NumField(); i++ {
|
for i := 0; i < ty.NumField(); i++ {
|
||||||
f := ty.Field(i)
|
f := ty.Field(i).Type
|
||||||
if f.Type.Kind() != first {
|
if f.Kind() == reflect.Struct {
|
||||||
return false
|
var structNumFields int
|
||||||
|
allFloats, structNumFields = isAllSameFloat(f)
|
||||||
|
numFields += structNumFields
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
numFields++
|
||||||
|
if f.Kind() != first {
|
||||||
|
allFloats = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return allFloats, numFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkStructFieldsSupported(ty reflect.Type) {
|
func checkStructFieldsSupported(ty reflect.Type) {
|
||||||
@@ -386,24 +471,19 @@ func checkStructFieldsSupported(ty reflect.Type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const is32bit = unsafe.Sizeof(uintptr(0)) == 4
|
|
||||||
|
|
||||||
func roundUpTo8(val uintptr) uintptr {
|
func roundUpTo8(val uintptr) uintptr {
|
||||||
return (val + 7) &^ 7
|
return (val + 7) &^ 7
|
||||||
}
|
}
|
||||||
|
|
||||||
func numOfIntegerRegisters() int {
|
func numOfIntegerRegisters() int {
|
||||||
switch runtime.GOARCH {
|
switch runtime.GOARCH {
|
||||||
case "arm64":
|
case "arm64", "loong64":
|
||||||
return 8
|
return 8
|
||||||
case "amd64":
|
case "amd64":
|
||||||
return 6
|
return 6
|
||||||
// TODO: figure out why 386 tests are not working
|
|
||||||
/*case "386":
|
|
||||||
return 0
|
|
||||||
case "arm":
|
|
||||||
return 4*/
|
|
||||||
default:
|
default:
|
||||||
panic("purego: unknown GOARCH (" + runtime.GOARCH + ")")
|
// since this platform isn't supported and can therefore only access
|
||||||
|
// integer registers it is fine to return the maxArgs
|
||||||
|
return maxArgs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
package purego
|
||||||
|
|
||||||
|
//go:generate go run wincallback.go
|
||||||
+1
-5
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux || windows
|
//go:build darwin || freebsd || linux || netbsd || windows
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
@@ -11,7 +11,3 @@ import (
|
|||||||
|
|
||||||
//go:linkname runtime_cgocall runtime.cgocall
|
//go:linkname runtime_cgocall runtime.cgocall
|
||||||
func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go
|
func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go
|
||||||
|
|
||||||
//go:linkname runtime_noescape runtime.noescape
|
|
||||||
//go:noescape
|
|
||||||
func runtime_noescape(p unsafe.Pointer) unsafe.Pointer // from runtime/stubs.go
|
|
||||||
|
|||||||
+36
-2
@@ -1,17 +1,51 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build freebsd || linux
|
//go:build freebsd || linux || netbsd
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo LDFLAGS: -ldl
|
#cgo !netbsd LDFLAGS: -ldl
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <stdlib.h>
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dlopen(filename string, flag int) (uintptr, error) {
|
||||||
|
cfilename := C.CString(filename)
|
||||||
|
defer C.free(unsafe.Pointer(cfilename))
|
||||||
|
handle := C.dlopen(cfilename, C.int(flag))
|
||||||
|
if handle == nil {
|
||||||
|
return 0, errors.New(C.GoString(C.dlerror()))
|
||||||
|
}
|
||||||
|
return uintptr(handle), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dlsym(handle uintptr, symbol string) (uintptr, error) {
|
||||||
|
csymbol := C.CString(symbol)
|
||||||
|
defer C.free(unsafe.Pointer(csymbol))
|
||||||
|
symbolAddr := C.dlsym(*(*unsafe.Pointer)(unsafe.Pointer(&handle)), csymbol)
|
||||||
|
if symbolAddr == nil {
|
||||||
|
return 0, errors.New(C.GoString(C.dlerror()))
|
||||||
|
}
|
||||||
|
return uintptr(symbolAddr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dlclose(handle uintptr) error {
|
||||||
|
result := C.dlclose(*(*unsafe.Pointer)(unsafe.Pointer(&handle)))
|
||||||
|
if result != 0 {
|
||||||
|
return errors.New(C.GoString(C.dlerror()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// all that is needed is to assign each dl function because then its
|
// all that is needed is to assign each dl function because then its
|
||||||
// symbol will then be made available to the linker and linked to inside dlfcn.go
|
// symbol will then be made available to the linker and linked to inside dlfcn.go
|
||||||
var (
|
var (
|
||||||
|
|||||||
+6
-6
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build freebsd || (linux && !(arm64 || amd64))
|
//go:build freebsd || (linux && !(arm64 || amd64 || loong64)) || netbsd
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ package cgo
|
|||||||
// because Cgo and assembly files can't be in the same package.
|
// because Cgo and assembly files can't be in the same package.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo LDFLAGS: -ldl
|
#cgo !netbsd LDFLAGS: -ldl
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
@@ -20,7 +20,7 @@ typedef struct syscall15Args {
|
|||||||
uintptr_t fn;
|
uintptr_t fn;
|
||||||
uintptr_t a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15;
|
uintptr_t a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15;
|
||||||
uintptr_t f1, f2, f3, f4, f5, f6, f7, f8;
|
uintptr_t f1, f2, f3, f4, f5, f6, f7, f8;
|
||||||
uintptr_t r1, r2, err;
|
uintptr_t err;
|
||||||
} syscall15Args;
|
} syscall15Args;
|
||||||
|
|
||||||
void syscall15(struct syscall15Args *args) {
|
void syscall15(struct syscall15Args *args) {
|
||||||
@@ -31,7 +31,7 @@ void syscall15(struct syscall15Args *args) {
|
|||||||
*(void**)(&func_name) = (void*)(args->fn);
|
*(void**)(&func_name) = (void*)(args->fn);
|
||||||
uintptr_t r1 = func_name(args->a1,args->a2,args->a3,args->a4,args->a5,args->a6,args->a7,args->a8,args->a9,
|
uintptr_t r1 = func_name(args->a1,args->a2,args->a3,args->a4,args->a5,args->a6,args->a7,args->a8,args->a9,
|
||||||
args->a10,args->a11,args->a12,args->a13,args->a14,args->a15);
|
args->a10,args->a11,args->a12,args->a13,args->a14,args->a15);
|
||||||
args->r1 = r1;
|
args->a1 = r1;
|
||||||
args->err = errno;
|
args->err = errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,8 +48,8 @@ func Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14,
|
|||||||
C.uintptr_t(fn), C.uintptr_t(a1), C.uintptr_t(a2), C.uintptr_t(a3),
|
C.uintptr_t(fn), C.uintptr_t(a1), C.uintptr_t(a2), C.uintptr_t(a3),
|
||||||
C.uintptr_t(a4), C.uintptr_t(a5), C.uintptr_t(a6),
|
C.uintptr_t(a4), C.uintptr_t(a5), C.uintptr_t(a6),
|
||||||
C.uintptr_t(a7), C.uintptr_t(a8), C.uintptr_t(a9), C.uintptr_t(a10), C.uintptr_t(a11), C.uintptr_t(a12),
|
C.uintptr_t(a7), C.uintptr_t(a8), C.uintptr_t(a9), C.uintptr_t(a10), C.uintptr_t(a11), C.uintptr_t(a12),
|
||||||
C.uintptr_t(a13), C.uintptr_t(a14), C.uintptr_t(a15), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
C.uintptr_t(a13), C.uintptr_t(a14), C.uintptr_t(a15), 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
}
|
}
|
||||||
C.syscall15(&args)
|
C.syscall15(&args)
|
||||||
return uintptr(args.r1), uintptr(args.r2), uintptr(args.err)
|
return uintptr(args.a1), 0, uintptr(args.err)
|
||||||
}
|
}
|
||||||
|
|||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Macros for transitioning from the host ABI to Go ABI0.
|
||||||
|
//
|
||||||
|
// These macros save and restore the callee-saved registers
|
||||||
|
// from the stack, but they don't adjust stack pointer, so
|
||||||
|
// the user should prepare stack space in advance.
|
||||||
|
// SAVE_R22_TO_R31(offset) saves R22 ~ R31 to the stack space
|
||||||
|
// of ((offset)+0*8)(R3) ~ ((offset)+9*8)(R3).
|
||||||
|
//
|
||||||
|
// SAVE_F24_TO_F31(offset) saves F24 ~ F31 to the stack space
|
||||||
|
// of ((offset)+0*8)(R3) ~ ((offset)+7*8)(R3).
|
||||||
|
//
|
||||||
|
// Note: g is R22
|
||||||
|
|
||||||
|
#define SAVE_R22_TO_R31(offset) \
|
||||||
|
MOVV g, ((offset)+(0*8))(R3) \
|
||||||
|
MOVV R23, ((offset)+(1*8))(R3) \
|
||||||
|
MOVV R24, ((offset)+(2*8))(R3) \
|
||||||
|
MOVV R25, ((offset)+(3*8))(R3) \
|
||||||
|
MOVV R26, ((offset)+(4*8))(R3) \
|
||||||
|
MOVV R27, ((offset)+(5*8))(R3) \
|
||||||
|
MOVV R28, ((offset)+(6*8))(R3) \
|
||||||
|
MOVV R29, ((offset)+(7*8))(R3) \
|
||||||
|
MOVV R30, ((offset)+(8*8))(R3) \
|
||||||
|
MOVV R31, ((offset)+(9*8))(R3)
|
||||||
|
|
||||||
|
#define SAVE_F24_TO_F31(offset) \
|
||||||
|
MOVD F24, ((offset)+(0*8))(R3) \
|
||||||
|
MOVD F25, ((offset)+(1*8))(R3) \
|
||||||
|
MOVD F26, ((offset)+(2*8))(R3) \
|
||||||
|
MOVD F27, ((offset)+(3*8))(R3) \
|
||||||
|
MOVD F28, ((offset)+(4*8))(R3) \
|
||||||
|
MOVD F29, ((offset)+(5*8))(R3) \
|
||||||
|
MOVD F30, ((offset)+(6*8))(R3) \
|
||||||
|
MOVD F31, ((offset)+(7*8))(R3)
|
||||||
|
|
||||||
|
#define RESTORE_R22_TO_R31(offset) \
|
||||||
|
MOVV ((offset)+(0*8))(R3), g \
|
||||||
|
MOVV ((offset)+(1*8))(R3), R23 \
|
||||||
|
MOVV ((offset)+(2*8))(R3), R24 \
|
||||||
|
MOVV ((offset)+(3*8))(R3), R25 \
|
||||||
|
MOVV ((offset)+(4*8))(R3), R26 \
|
||||||
|
MOVV ((offset)+(5*8))(R3), R27 \
|
||||||
|
MOVV ((offset)+(6*8))(R3), R28 \
|
||||||
|
MOVV ((offset)+(7*8))(R3), R29 \
|
||||||
|
MOVV ((offset)+(8*8))(R3), R30 \
|
||||||
|
MOVV ((offset)+(9*8))(R3), R31
|
||||||
|
|
||||||
|
#define RESTORE_F24_TO_F31(offset) \
|
||||||
|
MOVD ((offset)+(0*8))(R3), F24 \
|
||||||
|
MOVD ((offset)+(1*8))(R3), F25 \
|
||||||
|
MOVD ((offset)+(2*8))(R3), F26 \
|
||||||
|
MOVD ((offset)+(3*8))(R3), F27 \
|
||||||
|
MOVD ((offset)+(4*8))(R3), F28 \
|
||||||
|
MOVD ((offset)+(5*8))(R3), F29 \
|
||||||
|
MOVD ((offset)+(6*8))(R3), F30 \
|
||||||
|
MOVD ((offset)+(7*8))(R3), F31
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
#include "abi_loong64.h"
|
||||||
|
|
||||||
|
// Called by C code generated by cmd/cgo.
|
||||||
|
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
|
||||||
|
// Saves C callee-saved registers and calls cgocallback with three arguments.
|
||||||
|
// fn is the PC of a func(a unsafe.Pointer) function.
|
||||||
|
TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
|
||||||
|
/*
|
||||||
|
* We still need to save all callee save register as before, and then
|
||||||
|
* push 3 args for fn (R4, R5, R7), skipping R6.
|
||||||
|
* Also note that at procedure entry in gc world, 8(R29) will be the
|
||||||
|
* first arg.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ADDV $(-23*8), R3
|
||||||
|
MOVV R4, (1*8)(R3) // fn unsafe.Pointer
|
||||||
|
MOVV R5, (2*8)(R3) // a unsafe.Pointer
|
||||||
|
MOVV R7, (3*8)(R3) // ctxt uintptr
|
||||||
|
|
||||||
|
SAVE_R22_TO_R31((4*8))
|
||||||
|
SAVE_F24_TO_F31((14*8))
|
||||||
|
MOVV R1, (22*8)(R3)
|
||||||
|
|
||||||
|
// Initialize Go ABI environment
|
||||||
|
JAL runtime·load_g(SB)
|
||||||
|
|
||||||
|
JAL runtime·cgocallback(SB)
|
||||||
|
|
||||||
|
RESTORE_R22_TO_R31((4*8))
|
||||||
|
RESTORE_F24_TO_F31((14*8))
|
||||||
|
MOVV (22*8)(R3), R1
|
||||||
|
|
||||||
|
ADDV $(23*8), R3
|
||||||
|
|
||||||
|
RET
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
// Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go.
|
// Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go.
|
||||||
// This allows code that calls into C to function properly when CGO_ENABLED=0.
|
// This allows code that calls into C to function properly when CGO_ENABLED=0.
|
||||||
@@ -30,4 +30,3 @@
|
|||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
//go:generate go run gen.go
|
//go:generate go run gen.go
|
||||||
//go:generate gofmt -s -w symbols.go
|
|
||||||
|
|||||||
+2
@@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|||||||
+2
@@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|||||||
+2
@@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|||||||
+3
-1
@@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
@@ -14,7 +16,7 @@ func _cgo_sys_thread_start(ts *ThreadStart) {
|
|||||||
var size size_t
|
var size size_t
|
||||||
var err int
|
var err int
|
||||||
|
|
||||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
// fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||||
sigfillset(&ign)
|
sigfillset(&ign)
|
||||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||||
|
|
||||||
|
|||||||
+10
-4
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func x_cgo_notify_runtime_init_done() {
|
func x_cgo_notify_runtime_init_done() {
|
||||||
pthread_mutex_lock(&runtime_init_mu)
|
pthread_mutex_lock(&runtime_init_mu)
|
||||||
runtime_init_done = 1
|
runtime_init_done = 1
|
||||||
@@ -28,6 +29,8 @@ func x_cgo_notify_runtime_init_done() {
|
|||||||
|
|
||||||
// Store the g into a thread-specific value associated with the pthread key pthread_g.
|
// Store the g into a thread-specific value associated with the pthread key pthread_g.
|
||||||
// And pthread_key_destructor will dropm when the thread is exiting.
|
// And pthread_key_destructor will dropm when the thread is exiting.
|
||||||
|
//
|
||||||
|
//go:norace
|
||||||
func x_cgo_bindm(g unsafe.Pointer) {
|
func x_cgo_bindm(g unsafe.Pointer) {
|
||||||
// We assume this will always succeed, otherwise, there might be extra M leaking,
|
// We assume this will always succeed, otherwise, there might be extra M leaking,
|
||||||
// when a C thread exits after a cgo call.
|
// when a C thread exits after a cgo call.
|
||||||
@@ -50,9 +53,11 @@ func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe
|
|||||||
var err int
|
var err int
|
||||||
|
|
||||||
for tries = 0; tries < 20; tries++ {
|
for tries = 0; tries < 20; tries++ {
|
||||||
err = int(pthread_create(thread, attr, pfn, unsafe.Pointer(arg)))
|
// inlined this call because it ran out of stack when inlining was disabled
|
||||||
|
err = int(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(pfn), uintptr(unsafe.Pointer(arg)), 0))
|
||||||
if err == 0 {
|
if err == 0 {
|
||||||
pthread_detach(*thread)
|
// inlined this call because it ran out of stack when inlining was disabled
|
||||||
|
call5(pthread_detachABI0, uintptr(*thread), 0, 0, 0, 0)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if err != int(syscall.EAGAIN) {
|
if err != int(syscall.EAGAIN) {
|
||||||
@@ -60,7 +65,8 @@ func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe
|
|||||||
}
|
}
|
||||||
ts.Sec = 0
|
ts.Sec = 0
|
||||||
ts.Nsec = (tries + 1) * 1000 * 1000 // Milliseconds.
|
ts.Nsec = (tries + 1) * 1000 * 1000 // Milliseconds.
|
||||||
nanosleep(&ts, nil)
|
// inlined this call because it ran out of stack when inlining was disabled
|
||||||
|
call5(nanosleepABI0, uintptr(unsafe.Pointer(&ts)), 0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
return int(syscall.EAGAIN)
|
return int(syscall.EAGAIN)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|||||||
+2
@@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
|
package fakecgo
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||||
|
var attr pthread_attr_t
|
||||||
|
var ign, oset sigset_t
|
||||||
|
var p pthread_t
|
||||||
|
var size size_t
|
||||||
|
var err int
|
||||||
|
|
||||||
|
sigfillset(&ign)
|
||||||
|
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||||
|
|
||||||
|
pthread_attr_init(&attr)
|
||||||
|
pthread_attr_getstacksize(&attr, &size)
|
||||||
|
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||||
|
ts.g.stackhi = uintptr(size)
|
||||||
|
|
||||||
|
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||||
|
|
||||||
|
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||||
|
|
||||||
|
if err != 0 {
|
||||||
|
print("fakecgo: pthread_create failed: ")
|
||||||
|
println(err)
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||||
|
//
|
||||||
|
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||||
|
var x_threadentry_trampoline byte
|
||||||
|
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||||
|
ts := *(*ThreadStart)(v)
|
||||||
|
free(v)
|
||||||
|
|
||||||
|
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||||
|
|
||||||
|
// faking funcs in go is a bit a... involved - but the following works :)
|
||||||
|
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||||
|
(*(*func())(unsafe.Pointer(&fn)))()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// here we will store a pointer to the provided setg func
|
||||||
|
var setg_func uintptr
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func x_cgo_init(g *G, setg uintptr) {
|
||||||
|
var size size_t
|
||||||
|
var attr *pthread_attr_t
|
||||||
|
|
||||||
|
/* The memory sanitizer distributed with versions of clang
|
||||||
|
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||||
|
may return an address that is later overwritten by the msan
|
||||||
|
library. Avoid this problem by forcing a call to malloc
|
||||||
|
here, before we ever call malloc.
|
||||||
|
|
||||||
|
This is only required for the memory sanitizer, so it's
|
||||||
|
unfortunate that we always run it. It should be possible
|
||||||
|
to remove this when we no longer care about versions of
|
||||||
|
clang before 3.8. The test for this is
|
||||||
|
misc/cgo/testsanitizers.
|
||||||
|
|
||||||
|
GCC works hard to eliminate a seemingly unnecessary call to
|
||||||
|
malloc, so we actually use the memory we allocate. */
|
||||||
|
|
||||||
|
setg_func = setg
|
||||||
|
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||||
|
if attr == nil {
|
||||||
|
println("fakecgo: malloc failed")
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
pthread_attr_init(attr)
|
||||||
|
pthread_attr_getstacksize(attr, &size)
|
||||||
|
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||||
|
pthread_attr_destroy(attr)
|
||||||
|
free(unsafe.Pointer(attr))
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !cgo && (amd64 || arm64)
|
||||||
|
|
||||||
|
package fakecgo
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||||
|
var attr pthread_attr_t
|
||||||
|
var ign, oset sigset_t
|
||||||
|
var p pthread_t
|
||||||
|
var size size_t
|
||||||
|
var err int
|
||||||
|
|
||||||
|
// fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||||
|
sigfillset(&ign)
|
||||||
|
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||||
|
|
||||||
|
pthread_attr_init(&attr)
|
||||||
|
pthread_attr_getstacksize(&attr, &size)
|
||||||
|
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||||
|
ts.g.stackhi = uintptr(size)
|
||||||
|
|
||||||
|
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||||
|
|
||||||
|
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||||
|
|
||||||
|
if err != 0 {
|
||||||
|
print("fakecgo: pthread_create failed: ")
|
||||||
|
println(err)
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||||
|
//
|
||||||
|
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||||
|
var x_threadentry_trampoline byte
|
||||||
|
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||||
|
var ss stack_t
|
||||||
|
ts := *(*ThreadStart)(v)
|
||||||
|
free(v)
|
||||||
|
|
||||||
|
// On NetBSD, a new thread inherits the signal stack of the
|
||||||
|
// creating thread. That confuses minit, so we remove that
|
||||||
|
// signal stack here before calling the regular mstart. It's
|
||||||
|
// a bit baroque to remove a signal stack here only to add one
|
||||||
|
// in minit, but it's a simple change that keeps NetBSD
|
||||||
|
// working like other OS's. At this point all signals are
|
||||||
|
// blocked, so there is no race.
|
||||||
|
ss.ss_flags = SS_DISABLE
|
||||||
|
sigaltstack(&ss, nil)
|
||||||
|
|
||||||
|
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||||
|
|
||||||
|
// faking funcs in go is a bit a... involved - but the following works :)
|
||||||
|
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||||
|
(*(*func())(unsafe.Pointer(&fn)))()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// here we will store a pointer to the provided setg func
|
||||||
|
var setg_func uintptr
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func x_cgo_init(g *G, setg uintptr) {
|
||||||
|
var size size_t
|
||||||
|
var attr *pthread_attr_t
|
||||||
|
|
||||||
|
/* The memory sanitizer distributed with versions of clang
|
||||||
|
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||||
|
may return an address that is later overwritten by the msan
|
||||||
|
library. Avoid this problem by forcing a call to malloc
|
||||||
|
here, before we ever call malloc.
|
||||||
|
|
||||||
|
This is only required for the memory sanitizer, so it's
|
||||||
|
unfortunate that we always run it. It should be possible
|
||||||
|
to remove this when we no longer care about versions of
|
||||||
|
clang before 3.8. The test for this is
|
||||||
|
misc/cgo/testsanitizers.
|
||||||
|
|
||||||
|
GCC works hard to eliminate a seemingly unnecessary call to
|
||||||
|
malloc, so we actually use the memory we allocate. */
|
||||||
|
|
||||||
|
setg_func = setg
|
||||||
|
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||||
|
if attr == nil {
|
||||||
|
println("fakecgo: malloc failed")
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
pthread_attr_init(attr)
|
||||||
|
pthread_attr_getstacksize(attr, &size)
|
||||||
|
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
|
||||||
|
// but this should be OK since we are taking the address of the first variable in this function.
|
||||||
|
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||||
|
pthread_attr_destroy(attr)
|
||||||
|
free(unsafe.Pointer(attr))
|
||||||
|
}
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
@@ -27,7 +27,11 @@ func x_cgo_thread_start(arg *ThreadStart) {
|
|||||||
println("fakecgo: out of memory in thread_start")
|
println("fakecgo: out of memory in thread_start")
|
||||||
abort()
|
abort()
|
||||||
}
|
}
|
||||||
// *ts = *arg would cause a writebarrier so use memmove instead
|
// *ts = *arg would cause a writebarrier so copy using slices
|
||||||
memmove(unsafe.Pointer(ts), unsafe.Pointer(arg), unsafe.Sizeof(*ts))
|
s1 := unsafe.Slice((*uintptr)(unsafe.Pointer(ts)), unsafe.Sizeof(*ts)/8)
|
||||||
|
s2 := unsafe.Slice((*uintptr)(unsafe.Pointer(arg)), unsafe.Sizeof(*arg)/8)
|
||||||
|
for i := range s2 {
|
||||||
|
s1[i] = s2[i]
|
||||||
|
}
|
||||||
_cgo_sys_thread_start(ts) // OS-dependent half
|
_cgo_sys_thread_start(ts) // OS-dependent half
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
// The runtime package contains an uninitialized definition
|
// The runtime package contains an uninitialized definition
|
||||||
// for runtime·iscgo. Override it to tell the runtime we're here.
|
// for runtime·iscgo. Override it to tell the runtime we're here.
|
||||||
|
|||||||
+6
-2
@@ -1,12 +1,16 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
type (
|
type (
|
||||||
size_t uintptr
|
size_t uintptr
|
||||||
|
// Sources:
|
||||||
|
// Darwin (32 bytes) - https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/_types.h#L74
|
||||||
|
// FreeBSD (32 bytes) - https://github.com/DoctorWkt/xv6-freebsd/blob/d2a294c2a984baed27676068b15ed9a29b06ab6f/include/signal.h#L98C9-L98C21
|
||||||
|
// Linux (128 bytes) - https://github.com/torvalds/linux/blob/ab75170520d4964f3acf8bb1f91d34cbc650688e/arch/x86/include/asm/signal.h#L25
|
||||||
sigset_t [128]byte
|
sigset_t [128]byte
|
||||||
pthread_attr_t [64]byte
|
pthread_attr_t [64]byte
|
||||||
pthread_t int
|
pthread_t int
|
||||||
|
|||||||
+6
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -18,3 +20,7 @@ var (
|
|||||||
PTHREAD_COND_INITIALIZER = pthread_cond_t{sig: 0x3CB0B1BB}
|
PTHREAD_COND_INITIALIZER = pthread_cond_t{sig: 0x3CB0B1BB}
|
||||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{sig: 0x32AAABA7}
|
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{sig: 0x32AAABA7}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type stack_t struct {
|
||||||
|
/* not implemented */
|
||||||
|
}
|
||||||
|
|||||||
+6
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -12,3 +14,7 @@ var (
|
|||||||
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
|
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
|
||||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
|
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type stack_t struct {
|
||||||
|
/* not implemented */
|
||||||
|
}
|
||||||
|
|||||||
+6
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -12,3 +14,7 @@ var (
|
|||||||
PTHREAD_COND_INITIALIZER = pthread_cond_t{}
|
PTHREAD_COND_INITIALIZER = pthread_cond_t{}
|
||||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}
|
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type stack_t struct {
|
||||||
|
/* not implemented */
|
||||||
|
}
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
|
package fakecgo
|
||||||
|
|
||||||
|
type (
|
||||||
|
pthread_cond_t uintptr
|
||||||
|
pthread_mutex_t uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
|
||||||
|
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Source: https://github.com/NetBSD/src/blob/613e27c65223fd2283b6ed679da1197e12f50e27/sys/compat/linux/arch/m68k/linux_signal.h#L133
|
||||||
|
type stack_t struct {
|
||||||
|
ss_sp uintptr
|
||||||
|
ss_flags int32
|
||||||
|
ss_size uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source: https://github.com/NetBSD/src/blob/613e27c65223fd2283b6ed679da1197e12f50e27/sys/sys/signal.h#L261
|
||||||
|
const SS_DISABLE = 0x004
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build netbsd
|
||||||
|
|
||||||
|
package fakecgo
|
||||||
|
|
||||||
|
import _ "unsafe" // for go:linkname
|
||||||
|
|
||||||
|
// Supply environ and __progname, because we don't
|
||||||
|
// link against the standard NetBSD crt0.o and the
|
||||||
|
// libc dynamic library needs them.
|
||||||
|
|
||||||
|
//go:linkname _environ environ
|
||||||
|
//go:linkname _progname __progname
|
||||||
|
//go:linkname ___ps_strings __ps_strings
|
||||||
|
|
||||||
|
var (
|
||||||
|
_environ uintptr
|
||||||
|
_progname uintptr
|
||||||
|
___ps_strings uintptr
|
||||||
|
)
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
|
|||||||
+71
-24
@@ -3,7 +3,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
@@ -15,170 +15,217 @@ import (
|
|||||||
// setg_trampoline calls setg with the G provided
|
// setg_trampoline calls setg with the G provided
|
||||||
func setg_trampoline(setg uintptr, G uintptr)
|
func setg_trampoline(setg uintptr, G uintptr)
|
||||||
|
|
||||||
//go:linkname memmove runtime.memmove
|
|
||||||
func memmove(to, from unsafe.Pointer, n uintptr)
|
|
||||||
|
|
||||||
// call5 takes fn the C function and 5 arguments and calls the function with those arguments
|
// call5 takes fn the C function and 5 arguments and calls the function with those arguments
|
||||||
func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr
|
func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func malloc(size uintptr) unsafe.Pointer {
|
func malloc(size uintptr) unsafe.Pointer {
|
||||||
ret := call5(mallocABI0, uintptr(size), 0, 0, 0, 0)
|
ret := call5(mallocABI0, uintptr(size), 0, 0, 0, 0)
|
||||||
// this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
|
// this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
|
||||||
return *(*unsafe.Pointer)(unsafe.Pointer(&ret))
|
return *(*unsafe.Pointer)(unsafe.Pointer(&ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func free(ptr unsafe.Pointer) {
|
func free(ptr unsafe.Pointer) {
|
||||||
call5(freeABI0, uintptr(ptr), 0, 0, 0, 0)
|
call5(freeABI0, uintptr(ptr), 0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func setenv(name *byte, value *byte, overwrite int32) int32 {
|
func setenv(name *byte, value *byte, overwrite int32) int32 {
|
||||||
return int32(call5(setenvABI0, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), uintptr(overwrite), 0, 0))
|
return int32(call5(setenvABI0, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), uintptr(overwrite), 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func unsetenv(name *byte) int32 {
|
func unsetenv(name *byte) int32 {
|
||||||
return int32(call5(unsetenvABI0, uintptr(unsafe.Pointer(name)), 0, 0, 0, 0))
|
return int32(call5(unsetenvABI0, uintptr(unsafe.Pointer(name)), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func sigfillset(set *sigset_t) int32 {
|
func sigfillset(set *sigset_t) int32 {
|
||||||
return int32(call5(sigfillsetABI0, uintptr(unsafe.Pointer(set)), 0, 0, 0, 0))
|
return int32(call5(sigfillsetABI0, uintptr(unsafe.Pointer(set)), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func nanosleep(ts *syscall.Timespec, rem *syscall.Timespec) int32 {
|
func nanosleep(ts *syscall.Timespec, rem *syscall.Timespec) int32 {
|
||||||
return int32(call5(nanosleepABI0, uintptr(unsafe.Pointer(ts)), uintptr(unsafe.Pointer(rem)), 0, 0, 0))
|
return int32(call5(nanosleepABI0, uintptr(unsafe.Pointer(ts)), uintptr(unsafe.Pointer(rem)), 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func abort() {
|
func abort() {
|
||||||
call5(abortABI0, 0, 0, 0, 0, 0)
|
call5(abortABI0, 0, 0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
|
func sigaltstack(ss *stack_t, old_ss *stack_t) int32 {
|
||||||
|
return int32(call5(sigaltstackABI0, uintptr(unsafe.Pointer(ss)), uintptr(unsafe.Pointer(old_ss)), 0, 0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_attr_init(attr *pthread_attr_t) int32 {
|
func pthread_attr_init(attr *pthread_attr_t) int32 {
|
||||||
return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_create(thread *pthread_t, attr *pthread_attr_t, start unsafe.Pointer, arg unsafe.Pointer) int32 {
|
func pthread_create(thread *pthread_t, attr *pthread_attr_t, start unsafe.Pointer, arg unsafe.Pointer) int32 {
|
||||||
return int32(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(start), uintptr(arg), 0))
|
return int32(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(start), uintptr(arg), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_detach(thread pthread_t) int32 {
|
func pthread_detach(thread pthread_t) int32 {
|
||||||
return int32(call5(pthread_detachABI0, uintptr(thread), 0, 0, 0, 0))
|
return int32(call5(pthread_detachABI0, uintptr(thread), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 {
|
func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 {
|
||||||
return int32(call5(pthread_sigmaskABI0, uintptr(how), uintptr(unsafe.Pointer(ign)), uintptr(unsafe.Pointer(oset)), 0, 0))
|
return int32(call5(pthread_sigmaskABI0, uintptr(how), uintptr(unsafe.Pointer(ign)), uintptr(unsafe.Pointer(oset)), 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_self() pthread_t {
|
func pthread_self() pthread_t {
|
||||||
return pthread_t(call5(pthread_selfABI0, 0, 0, 0, 0, 0))
|
return pthread_t(call5(pthread_selfABI0, 0, 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_get_stacksize_np(thread pthread_t) size_t {
|
func pthread_get_stacksize_np(thread pthread_t) size_t {
|
||||||
return size_t(call5(pthread_get_stacksize_npABI0, uintptr(thread), 0, 0, 0, 0))
|
return size_t(call5(pthread_get_stacksize_npABI0, uintptr(thread), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 {
|
func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 {
|
||||||
return int32(call5(pthread_attr_getstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(stacksize)), 0, 0, 0))
|
return int32(call5(pthread_attr_getstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(stacksize)), 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_attr_setstacksize(attr *pthread_attr_t, size size_t) int32 {
|
func pthread_attr_setstacksize(attr *pthread_attr_t, size size_t) int32 {
|
||||||
return int32(call5(pthread_attr_setstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(size), 0, 0, 0))
|
return int32(call5(pthread_attr_setstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(size), 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
|
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
|
||||||
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_mutex_lock(mutex *pthread_mutex_t) int32 {
|
func pthread_mutex_lock(mutex *pthread_mutex_t) int32 {
|
||||||
return int32(call5(pthread_mutex_lockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
return int32(call5(pthread_mutex_lockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_mutex_unlock(mutex *pthread_mutex_t) int32 {
|
func pthread_mutex_unlock(mutex *pthread_mutex_t) int32 {
|
||||||
return int32(call5(pthread_mutex_unlockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
return int32(call5(pthread_mutex_unlockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_cond_broadcast(cond *pthread_cond_t) int32 {
|
func pthread_cond_broadcast(cond *pthread_cond_t) int32 {
|
||||||
return int32(call5(pthread_cond_broadcastABI0, uintptr(unsafe.Pointer(cond)), 0, 0, 0, 0))
|
return int32(call5(pthread_cond_broadcastABI0, uintptr(unsafe.Pointer(cond)), 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:norace
|
||||||
func pthread_setspecific(key pthread_key_t, value unsafe.Pointer) int32 {
|
func pthread_setspecific(key pthread_key_t, value unsafe.Pointer) int32 {
|
||||||
return int32(call5(pthread_setspecificABI0, uintptr(key), uintptr(value), 0, 0, 0))
|
return int32(call5(pthread_setspecificABI0, uintptr(key), uintptr(value), 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname _malloc _malloc
|
//go:linkname _malloc _malloc
|
||||||
var _malloc uintptr
|
var _malloc uint8
|
||||||
var mallocABI0 = uintptr(unsafe.Pointer(&_malloc))
|
var mallocABI0 = uintptr(unsafe.Pointer(&_malloc))
|
||||||
|
|
||||||
//go:linkname _free _free
|
//go:linkname _free _free
|
||||||
var _free uintptr
|
var _free uint8
|
||||||
var freeABI0 = uintptr(unsafe.Pointer(&_free))
|
var freeABI0 = uintptr(unsafe.Pointer(&_free))
|
||||||
|
|
||||||
//go:linkname _setenv _setenv
|
//go:linkname _setenv _setenv
|
||||||
var _setenv uintptr
|
var _setenv uint8
|
||||||
var setenvABI0 = uintptr(unsafe.Pointer(&_setenv))
|
var setenvABI0 = uintptr(unsafe.Pointer(&_setenv))
|
||||||
|
|
||||||
//go:linkname _unsetenv _unsetenv
|
//go:linkname _unsetenv _unsetenv
|
||||||
var _unsetenv uintptr
|
var _unsetenv uint8
|
||||||
var unsetenvABI0 = uintptr(unsafe.Pointer(&_unsetenv))
|
var unsetenvABI0 = uintptr(unsafe.Pointer(&_unsetenv))
|
||||||
|
|
||||||
//go:linkname _sigfillset _sigfillset
|
//go:linkname _sigfillset _sigfillset
|
||||||
var _sigfillset uintptr
|
var _sigfillset uint8
|
||||||
var sigfillsetABI0 = uintptr(unsafe.Pointer(&_sigfillset))
|
var sigfillsetABI0 = uintptr(unsafe.Pointer(&_sigfillset))
|
||||||
|
|
||||||
//go:linkname _nanosleep _nanosleep
|
//go:linkname _nanosleep _nanosleep
|
||||||
var _nanosleep uintptr
|
var _nanosleep uint8
|
||||||
var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep))
|
var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep))
|
||||||
|
|
||||||
//go:linkname _abort _abort
|
//go:linkname _abort _abort
|
||||||
var _abort uintptr
|
var _abort uint8
|
||||||
var abortABI0 = uintptr(unsafe.Pointer(&_abort))
|
var abortABI0 = uintptr(unsafe.Pointer(&_abort))
|
||||||
|
|
||||||
|
//go:linkname _sigaltstack _sigaltstack
|
||||||
|
var _sigaltstack uint8
|
||||||
|
var sigaltstackABI0 = uintptr(unsafe.Pointer(&_sigaltstack))
|
||||||
|
|
||||||
//go:linkname _pthread_attr_init _pthread_attr_init
|
//go:linkname _pthread_attr_init _pthread_attr_init
|
||||||
var _pthread_attr_init uintptr
|
var _pthread_attr_init uint8
|
||||||
var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init))
|
var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init))
|
||||||
|
|
||||||
//go:linkname _pthread_create _pthread_create
|
//go:linkname _pthread_create _pthread_create
|
||||||
var _pthread_create uintptr
|
var _pthread_create uint8
|
||||||
var pthread_createABI0 = uintptr(unsafe.Pointer(&_pthread_create))
|
var pthread_createABI0 = uintptr(unsafe.Pointer(&_pthread_create))
|
||||||
|
|
||||||
//go:linkname _pthread_detach _pthread_detach
|
//go:linkname _pthread_detach _pthread_detach
|
||||||
var _pthread_detach uintptr
|
var _pthread_detach uint8
|
||||||
var pthread_detachABI0 = uintptr(unsafe.Pointer(&_pthread_detach))
|
var pthread_detachABI0 = uintptr(unsafe.Pointer(&_pthread_detach))
|
||||||
|
|
||||||
//go:linkname _pthread_sigmask _pthread_sigmask
|
//go:linkname _pthread_sigmask _pthread_sigmask
|
||||||
var _pthread_sigmask uintptr
|
var _pthread_sigmask uint8
|
||||||
var pthread_sigmaskABI0 = uintptr(unsafe.Pointer(&_pthread_sigmask))
|
var pthread_sigmaskABI0 = uintptr(unsafe.Pointer(&_pthread_sigmask))
|
||||||
|
|
||||||
//go:linkname _pthread_self _pthread_self
|
//go:linkname _pthread_self _pthread_self
|
||||||
var _pthread_self uintptr
|
var _pthread_self uint8
|
||||||
var pthread_selfABI0 = uintptr(unsafe.Pointer(&_pthread_self))
|
var pthread_selfABI0 = uintptr(unsafe.Pointer(&_pthread_self))
|
||||||
|
|
||||||
//go:linkname _pthread_get_stacksize_np _pthread_get_stacksize_np
|
//go:linkname _pthread_get_stacksize_np _pthread_get_stacksize_np
|
||||||
var _pthread_get_stacksize_np uintptr
|
var _pthread_get_stacksize_np uint8
|
||||||
var pthread_get_stacksize_npABI0 = uintptr(unsafe.Pointer(&_pthread_get_stacksize_np))
|
var pthread_get_stacksize_npABI0 = uintptr(unsafe.Pointer(&_pthread_get_stacksize_np))
|
||||||
|
|
||||||
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
|
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
|
||||||
var _pthread_attr_getstacksize uintptr
|
var _pthread_attr_getstacksize uint8
|
||||||
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
|
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
|
||||||
|
|
||||||
//go:linkname _pthread_attr_setstacksize _pthread_attr_setstacksize
|
//go:linkname _pthread_attr_setstacksize _pthread_attr_setstacksize
|
||||||
var _pthread_attr_setstacksize uintptr
|
var _pthread_attr_setstacksize uint8
|
||||||
var pthread_attr_setstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_setstacksize))
|
var pthread_attr_setstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_setstacksize))
|
||||||
|
|
||||||
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
|
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
|
||||||
var _pthread_attr_destroy uintptr
|
var _pthread_attr_destroy uint8
|
||||||
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
|
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
|
||||||
|
|
||||||
//go:linkname _pthread_mutex_lock _pthread_mutex_lock
|
//go:linkname _pthread_mutex_lock _pthread_mutex_lock
|
||||||
var _pthread_mutex_lock uintptr
|
var _pthread_mutex_lock uint8
|
||||||
var pthread_mutex_lockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_lock))
|
var pthread_mutex_lockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_lock))
|
||||||
|
|
||||||
//go:linkname _pthread_mutex_unlock _pthread_mutex_unlock
|
//go:linkname _pthread_mutex_unlock _pthread_mutex_unlock
|
||||||
var _pthread_mutex_unlock uintptr
|
var _pthread_mutex_unlock uint8
|
||||||
var pthread_mutex_unlockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_unlock))
|
var pthread_mutex_unlockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_unlock))
|
||||||
|
|
||||||
//go:linkname _pthread_cond_broadcast _pthread_cond_broadcast
|
//go:linkname _pthread_cond_broadcast _pthread_cond_broadcast
|
||||||
var _pthread_cond_broadcast uintptr
|
var _pthread_cond_broadcast uint8
|
||||||
var pthread_cond_broadcastABI0 = uintptr(unsafe.Pointer(&_pthread_cond_broadcast))
|
var pthread_cond_broadcastABI0 = uintptr(unsafe.Pointer(&_pthread_cond_broadcast))
|
||||||
|
|
||||||
//go:linkname _pthread_setspecific _pthread_setspecific
|
//go:linkname _pthread_setspecific _pthread_setspecific
|
||||||
var _pthread_setspecific uintptr
|
var _pthread_setspecific uint8
|
||||||
var pthread_setspecificABI0 = uintptr(unsafe.Pointer(&_pthread_setspecific))
|
var pthread_setspecificABI0 = uintptr(unsafe.Pointer(&_pthread_setspecific))
|
||||||
|
|||||||
+3
@@ -3,6 +3,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
//go:cgo_import_dynamic purego_malloc malloc "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_malloc malloc "/usr/lib/libSystem.B.dylib"
|
||||||
@@ -12,6 +14,7 @@ package fakecgo
|
|||||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_sigfillset sigfillset "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_nanosleep nanosleep "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_abort abort "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_abort abort "/usr/lib/libSystem.B.dylib"
|
||||||
|
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_pthread_create pthread_create "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "/usr/lib/libSystem.B.dylib"
|
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "/usr/lib/libSystem.B.dylib"
|
||||||
|
|||||||
+3
@@ -3,6 +3,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
//go:cgo_import_dynamic purego_malloc malloc "libc.so.7"
|
//go:cgo_import_dynamic purego_malloc malloc "libc.so.7"
|
||||||
@@ -12,6 +14,7 @@ package fakecgo
|
|||||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.7"
|
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.7"
|
||||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.7"
|
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.7"
|
||||||
//go:cgo_import_dynamic purego_abort abort "libc.so.7"
|
//go:cgo_import_dynamic purego_abort abort "libc.so.7"
|
||||||
|
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "libc.so.7"
|
||||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
|
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
|
||||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
|
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
|
||||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"
|
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"
|
||||||
|
|||||||
+3
@@ -3,6 +3,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
package fakecgo
|
package fakecgo
|
||||||
|
|
||||||
//go:cgo_import_dynamic purego_malloc malloc "libc.so.6"
|
//go:cgo_import_dynamic purego_malloc malloc "libc.so.6"
|
||||||
@@ -12,6 +14,7 @@ package fakecgo
|
|||||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.6"
|
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.6"
|
||||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.6"
|
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.6"
|
||||||
//go:cgo_import_dynamic purego_abort abort "libc.so.6"
|
//go:cgo_import_dynamic purego_abort abort "libc.so.6"
|
||||||
|
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "libc.so.6"
|
||||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so.0"
|
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so.0"
|
||||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so.0"
|
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so.0"
|
||||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so.0"
|
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so.0"
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
|
|
||||||
|
package fakecgo
|
||||||
|
|
||||||
|
//go:cgo_import_dynamic purego_malloc malloc "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_free free "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_setenv setenv "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_abort abort "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "libc.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so"
|
||||||
|
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so"
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || linux || freebsd
|
//go:build !cgo && (darwin || linux || freebsd)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
trampoline for emulating required C functions for cgo in go (see cgo.go)
|
trampoline for emulating required C functions for cgo in go (see cgo.go)
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux)
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
#include "go_asm.h"
|
#include "go_asm.h"
|
||||||
|
|||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build !cgo && linux
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
#include "go_asm.h"
|
||||||
|
|
||||||
|
// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
|
||||||
|
|
||||||
|
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
|
||||||
|
MOVV R4, 8(R3)
|
||||||
|
MOVV R5, 16(R3)
|
||||||
|
MOVV ·x_cgo_init_call(SB), R6
|
||||||
|
MOVV (R6), R7
|
||||||
|
CALL (R7)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
|
||||||
|
MOVV R4, 8(R3)
|
||||||
|
MOVV ·x_cgo_thread_start_call(SB), R5
|
||||||
|
MOVV (R5), R6
|
||||||
|
CALL (R6)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
|
||||||
|
MOVV R4, 8(R3)
|
||||||
|
MOVV ·x_cgo_setenv_call(SB), R5
|
||||||
|
MOVV (R5), R6
|
||||||
|
CALL (R6)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
|
||||||
|
MOVV R4, 8(R3)
|
||||||
|
MOVV ·x_cgo_unsetenv_call(SB), R5
|
||||||
|
MOVV (R5), R6
|
||||||
|
CALL (R6)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0
|
||||||
|
CALL ·x_cgo_notify_runtime_init_done(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0
|
||||||
|
CALL ·x_cgo_bindm(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func setg_trampoline(setg uintptr, g uintptr)
|
||||||
|
TEXT ·setg_trampoline(SB), NOSPLIT, $0
|
||||||
|
MOVV G+8(FP), R4
|
||||||
|
MOVV setg+0(FP), R5
|
||||||
|
CALL (R5)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT threadentry_trampoline(SB), NOSPLIT, $16
|
||||||
|
MOVV R4, 8(R3)
|
||||||
|
MOVV ·threadentry_call(SB), R5
|
||||||
|
MOVV (R5), R6
|
||||||
|
CALL (R6)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·call5(SB), NOSPLIT, $0-0
|
||||||
|
MOVV fn+0(FP), R9
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV a4+32(FP), R7
|
||||||
|
MOVV a5+40(FP), R8
|
||||||
|
CALL (R9)
|
||||||
|
MOVV R4, ret+48(FP)
|
||||||
|
RET
|
||||||
+5
-1
@@ -3,7 +3,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
@@ -37,6 +37,10 @@ TEXT _abort(SB), NOSPLIT|NOFRAME, $0-0
|
|||||||
JMP purego_abort(SB)
|
JMP purego_abort(SB)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
TEXT _sigaltstack(SB), NOSPLIT|NOFRAME, $0-0
|
||||||
|
JMP purego_sigaltstack(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT _pthread_attr_init(SB), NOSPLIT|NOFRAME, $0-0
|
TEXT _pthread_attr_init(SB), NOSPLIT|NOFRAME, $0-0
|
||||||
JMP purego_pthread_attr_init(SB)
|
JMP purego_pthread_attr_init(SB)
|
||||||
RET
|
RET
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build !cgo && (darwin || freebsd || linux)
|
//go:build !cgo && (darwin || freebsd || linux || netbsd)
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
|
|||||||
+267
@@ -0,0 +1,267 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
package objc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/ebitengine/purego"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The end-goal of these defaults is to get an Objective-C memory-managed block object
|
||||||
|
// that won't try to free() a Go pointer, but will call our custom blockFunctionCache.Delete()
|
||||||
|
// when the reference count drops to zero, so the associated function is also unreferenced.
|
||||||
|
|
||||||
|
// blockBaseClass is the name of the class that block objects will be initialized with.
|
||||||
|
blockBaseClass = "__NSMallocBlock__"
|
||||||
|
// blockFlags is the set of flags that block objects will be initialized with.
|
||||||
|
blockFlags = blockHasCopyDispose | blockHasSignature
|
||||||
|
|
||||||
|
// blockHasCopyDispose is a flag that tells the Objective-C runtime the block exports Copy and/or Dispose helpers.
|
||||||
|
blockHasCopyDispose = 1 << 25
|
||||||
|
|
||||||
|
// blockHasSignature is a flag that tells the Objective-C runtime the block exports a function signature.
|
||||||
|
blockHasSignature = 1 << 30
|
||||||
|
)
|
||||||
|
|
||||||
|
// blockDescriptor is the Go representation of an Objective-C block descriptor.
|
||||||
|
// It is a component to be referenced by blockDescriptor.
|
||||||
|
//
|
||||||
|
// The layout of this struct matches Block_literal_1 described in https://clang.llvm.org/docs/Block-ABI-Apple.html#high-level
|
||||||
|
type blockDescriptor struct {
|
||||||
|
_ uintptr
|
||||||
|
size uintptr
|
||||||
|
_ uintptr
|
||||||
|
dispose uintptr
|
||||||
|
signature *uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockLayout is the Go representation of the structure abstracted by a block pointer.
|
||||||
|
// From the Objective-C point of view, a pointer to this struct is equivalent to an ID that
|
||||||
|
// references a block.
|
||||||
|
//
|
||||||
|
// The layout of this struct matches __block_literal_1 described in https://clang.llvm.org/docs/Block-ABI-Apple.html#high-level
|
||||||
|
type blockLayout struct {
|
||||||
|
isa Class
|
||||||
|
flags uint32
|
||||||
|
_ uint32
|
||||||
|
invoke uintptr
|
||||||
|
descriptor *blockDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockFunctionCache is a thread safe cache of block layouts.
|
||||||
|
//
|
||||||
|
// The function closures themselves are kept alive by caching them internally until the Objective-C runtime indicates that
|
||||||
|
// they can be released (presumably when the reference count reaches zero). This approach is used instead of appending the function
|
||||||
|
// object to the block allocation, where it is out of the visible domain of Go's GC.
|
||||||
|
type blockFunctionCache struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
|
functions map[Block]reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load retrieves a function (in the form of a reflect.Value, so Call can be invoked) associated with the key Block.
|
||||||
|
func (b *blockFunctionCache) Load(key Block) reflect.Value {
|
||||||
|
b.mutex.RLock()
|
||||||
|
defer b.mutex.RUnlock()
|
||||||
|
return b.functions[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store associates a function (in the form of a reflect.Value) with the key Block.
|
||||||
|
func (b *blockFunctionCache) Store(key Block, value reflect.Value) Block {
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
b.functions[key] = value
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removed the function associated with the key Block.
|
||||||
|
func (b *blockFunctionCache) Delete(key Block) {
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
delete(b.functions, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBlockFunctionCache initializes a new blockFunctionCache
|
||||||
|
func newBlockFunctionCache() *blockFunctionCache {
|
||||||
|
return &blockFunctionCache{functions: map[Block]reflect.Value{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockCache is a thread safe cache of block layouts.
|
||||||
|
//
|
||||||
|
// It takes advantage of the block being the first argument of a block call being the block closure,
|
||||||
|
// only invoking [github.com/ebitengine/purego.NewCallback] when it encounters a new function type (rather than on for every block creation).
|
||||||
|
// This should mitigate block creations putting pressure on the callback limit.
|
||||||
|
type blockCache struct {
|
||||||
|
sync.Mutex
|
||||||
|
descriptorTemplate blockDescriptor
|
||||||
|
layoutTemplate blockLayout
|
||||||
|
layouts map[reflect.Type]blockLayout
|
||||||
|
Functions *blockFunctionCache
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode returns a blocks type as if it was given to @encode(typ)
|
||||||
|
func (*blockCache) encode(typ reflect.Type) *uint8 {
|
||||||
|
// this algorithm was copied from encodeFunc,
|
||||||
|
// but altered to panic on error, and to only accept a block-type signature.
|
||||||
|
if typ == nil || typ.Kind() != reflect.Func {
|
||||||
|
panic("objc: not a function")
|
||||||
|
}
|
||||||
|
|
||||||
|
var encoding string
|
||||||
|
switch typ.NumOut() {
|
||||||
|
case 0:
|
||||||
|
encoding = encVoid
|
||||||
|
default:
|
||||||
|
returnType, err := encodeType(typ.Out(0), false)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("objc: %v", err))
|
||||||
|
}
|
||||||
|
encoding = returnType
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.NumIn() == 0 || typ.In(0) != reflect.TypeOf(Block(0)) {
|
||||||
|
panic(fmt.Sprintf("objc: A Block implementation must take a Block as its first argument; got %v", typ.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
encoding += encId
|
||||||
|
for i := 1; i < typ.NumIn(); i++ {
|
||||||
|
argType, err := encodeType(typ.In(i), false)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("objc: %v", err))
|
||||||
|
}
|
||||||
|
encoding = fmt.Sprint(encoding, argType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the encoding as a C-style string.
|
||||||
|
return &append([]uint8(encoding), 0)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLayout retrieves a blockLayout VALUE constructed with the supplied function type.
|
||||||
|
// It will panic if the type is not a valid block function.
|
||||||
|
func (b *blockCache) getLayout(typ reflect.Type) blockLayout {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
|
|
||||||
|
// return the cached layout, if it exists.
|
||||||
|
if layout, ok := b.layouts[typ]; ok {
|
||||||
|
return layout
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise: create a layout, and populate it with the default templates
|
||||||
|
layout := b.layoutTemplate
|
||||||
|
layout.descriptor = &blockDescriptor{}
|
||||||
|
*layout.descriptor = b.descriptorTemplate
|
||||||
|
|
||||||
|
// getting the signature now will panic on invalid types before we invest in creating a callback.
|
||||||
|
layout.descriptor.signature = b.encode(typ)
|
||||||
|
|
||||||
|
// create a global callback.
|
||||||
|
// this single callback can dispatch to any function with the same signature,
|
||||||
|
// since the user-provided functions are associated with the actual block allocations.
|
||||||
|
layout.invoke = purego.NewCallback(
|
||||||
|
reflect.MakeFunc(
|
||||||
|
typ,
|
||||||
|
func(args []reflect.Value) (results []reflect.Value) {
|
||||||
|
return b.Functions.Load(args[0].Interface().(Block)).Call(args)
|
||||||
|
},
|
||||||
|
).Interface(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// store it and return it
|
||||||
|
b.layouts[typ] = layout
|
||||||
|
return layout
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBlockCache initializes a block cache.
|
||||||
|
// It should not be called until AFTER libobjc is fully initialized.
|
||||||
|
func newBlockCache() *blockCache {
|
||||||
|
cache := &blockCache{
|
||||||
|
descriptorTemplate: blockDescriptor{
|
||||||
|
size: unsafe.Sizeof(blockLayout{}),
|
||||||
|
},
|
||||||
|
layoutTemplate: blockLayout{
|
||||||
|
isa: GetClass(blockBaseClass),
|
||||||
|
flags: blockFlags,
|
||||||
|
},
|
||||||
|
layouts: map[reflect.Type]blockLayout{},
|
||||||
|
Functions: newBlockFunctionCache(),
|
||||||
|
}
|
||||||
|
cache.descriptorTemplate.dispose = purego.NewCallback(cache.Functions.Delete)
|
||||||
|
return cache
|
||||||
|
}
|
||||||
|
|
||||||
|
// theBlocksCache is the global block cache
|
||||||
|
var theBlocksCache *blockCache
|
||||||
|
|
||||||
|
// Block is an opaque pointer to an Objective-C object containing a function with its associated closure.
|
||||||
|
type Block ID
|
||||||
|
|
||||||
|
// Copy creates a copy of a block on the Objective-C heap (or increments the reference count if already on the heap).
|
||||||
|
// Use [Block.Release] to free the copy when it is no longer in use.
|
||||||
|
func (b Block) Copy() Block {
|
||||||
|
return _Block_copy(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke calls the implementation of a block.
|
||||||
|
func (b Block) Invoke(args ...any) {
|
||||||
|
fn := theBlocksCache.Functions.Load(b)
|
||||||
|
|
||||||
|
reflectedArgs := make([]reflect.Value, len(args)+1)
|
||||||
|
reflectedArgs[0] = reflect.ValueOf(b)
|
||||||
|
for i := range args {
|
||||||
|
reflectedArgs[i+1] = reflect.ValueOf(args[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.Call(reflectedArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release decrements the Block's reference count, and if it is the last reference, frees it.
|
||||||
|
func (b Block) Release() {
|
||||||
|
_Block_release(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlock takes a Go function that takes a Block as its first argument.
|
||||||
|
// It returns an Block that can be called by Objective-C code.
|
||||||
|
// The function panics if an error occurs.
|
||||||
|
// Use [Block.Release] to free this block when it is no longer in use.
|
||||||
|
func NewBlock(fn any) Block {
|
||||||
|
// get or create a block layout for the callback.
|
||||||
|
layout := theBlocksCache.getLayout(reflect.TypeOf(fn))
|
||||||
|
// we created the layout in Go memory, so we'll copy it to a newly-created Objective-C object.
|
||||||
|
block := Block(unsafe.Pointer(&layout)).Copy()
|
||||||
|
// associate the fn with the block we created before returning it.
|
||||||
|
return theBlocksCache.Functions.Store(block, reflect.ValueOf(fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvokeBlock is a convenience method for calling the implementation of a block.
|
||||||
|
// The block implementation must return 1 value.
|
||||||
|
func InvokeBlock[T any](block Block, args ...any) (result T, err error) {
|
||||||
|
block = block.Copy()
|
||||||
|
defer block.Release()
|
||||||
|
|
||||||
|
fn := theBlocksCache.Functions.Load(block)
|
||||||
|
if fn.Type().NumIn() != len(args)+1 {
|
||||||
|
return result, fmt.Errorf("objc: block callback expects %d arguments, got %d", fn.Type().NumIn()-1, len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
reflectedArgs := make([]reflect.Value, len(args)+1)
|
||||||
|
reflectedArgs[0] = reflect.ValueOf(block)
|
||||||
|
for i := range args {
|
||||||
|
reflectedArgs[i+1] = reflect.ValueOf(args[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
callResult := fn.Call(reflectedArgs)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
result, ok = callResult[0].Interface().(T)
|
||||||
|
if !ok {
|
||||||
|
return result, fmt.Errorf("objc: the returned value type %s was not %T", callResult[0].Type().String(), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
+193
-60
@@ -11,41 +11,59 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/ebitengine/purego"
|
"github.com/ebitengine/purego"
|
||||||
|
"github.com/ebitengine/purego/internal/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: support try/catch?
|
// TODO: support try/catch?
|
||||||
// https://stackoverflow.com/questions/7062599/example-of-how-objective-cs-try-catch-implementation-is-executed-at-runtime
|
// https://stackoverflow.com/questions/7062599/example-of-how-objective-cs-try-catch-implementation-is-executed-at-runtime
|
||||||
var (
|
var (
|
||||||
objc_msgSend_fn uintptr
|
objc_msgSend_fn uintptr
|
||||||
objc_msgSend func(obj ID, cmd SEL, args ...interface{}) ID
|
objc_msgSend_stret_fn uintptr
|
||||||
objc_msgSendSuper2_fn uintptr
|
objc_msgSend func(obj ID, cmd SEL, args ...any) ID
|
||||||
objc_msgSendSuper2 func(super *objc_super, cmd SEL, args ...interface{}) ID
|
objc_msgSendSuper2_fn uintptr
|
||||||
objc_getClass func(name string) Class
|
objc_msgSendSuper2_stret_fn uintptr
|
||||||
objc_getProtocol func(name string) *Protocol
|
objc_msgSendSuper2 func(super *objc_super, cmd SEL, args ...any) ID
|
||||||
objc_allocateClassPair func(super Class, name string, extraBytes uintptr) Class
|
objc_getClass func(name string) Class
|
||||||
objc_registerClassPair func(class Class)
|
objc_getProtocol func(name string) *Protocol
|
||||||
sel_registerName func(name string) SEL
|
objc_allocateProtocol func(name string) *Protocol
|
||||||
class_getSuperclass func(class Class) Class
|
objc_registerProtocol func(protocol *Protocol)
|
||||||
class_getInstanceVariable func(class Class, name string) Ivar
|
objc_allocateClassPair func(super Class, name string, extraBytes uintptr) Class
|
||||||
class_getInstanceSize func(class Class) uintptr
|
objc_registerClassPair func(class Class)
|
||||||
class_addMethod func(class Class, name SEL, imp IMP, types string) bool
|
sel_registerName func(name string) SEL
|
||||||
class_addIvar func(class Class, name string, size uintptr, alignment uint8, types string) bool
|
class_getSuperclass func(class Class) Class
|
||||||
class_addProtocol func(class Class, protocol *Protocol) bool
|
class_getInstanceVariable func(class Class, name string) Ivar
|
||||||
ivar_getOffset func(ivar Ivar) uintptr
|
class_getInstanceSize func(class Class) uintptr
|
||||||
ivar_getName func(ivar Ivar) string
|
class_addMethod func(class Class, name SEL, imp IMP, types string) bool
|
||||||
object_getClass func(obj ID) Class
|
class_addIvar func(class Class, name string, size uintptr, alignment uint8, types string) bool
|
||||||
object_getIvar func(obj ID, ivar Ivar) ID
|
class_addProtocol func(class Class, protocol *Protocol) bool
|
||||||
object_setIvar func(obj ID, ivar Ivar, value ID)
|
ivar_getOffset func(ivar Ivar) uintptr
|
||||||
protocol_getName func(protocol *Protocol) string
|
ivar_getName func(ivar Ivar) string
|
||||||
protocol_isEqual func(p *Protocol, p2 *Protocol) bool
|
object_getClass func(obj ID) Class
|
||||||
|
object_getIvar func(obj ID, ivar Ivar) ID
|
||||||
|
object_setIvar func(obj ID, ivar Ivar, value ID)
|
||||||
|
protocol_getName func(protocol *Protocol) string
|
||||||
|
protocol_isEqual func(p *Protocol, p2 *Protocol) bool
|
||||||
|
protocol_addMethodDescription func(p *Protocol, name SEL, types string, isRequiredMethod bool, isInstanceMethod bool)
|
||||||
|
protocol_copyMethodDescriptionList func(p *Protocol, isRequiredMethod bool, isInstanceMethod bool, outCount *uint32) *MethodDescription
|
||||||
|
protocol_copyProtocolList func(p *Protocol, outCount *uint32) **Protocol
|
||||||
|
protocol_copyPropertyList2 func(p *Protocol, outCount *uint32, isRequiredProperty, isInstanceProperty bool) *Property
|
||||||
|
protocol_addProtocol func(p *Protocol, p2 *Protocol)
|
||||||
|
protocol_addProperty func(p *Protocol, name string, attributes []PropertyAttribute, attributeCount uint32, isRequiredProperty bool, isInstanceProperty bool)
|
||||||
|
property_getName func(p Property) string
|
||||||
|
property_getAttributes func(p Property) string
|
||||||
|
|
||||||
|
free func(ptr unsafe.Pointer)
|
||||||
|
_Block_copy func(Block) Block
|
||||||
|
_Block_release func(Block)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
objc, err := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL)
|
objc, err := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL|purego.RTLD_NOW)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("objc: %w", err))
|
panic(fmt.Errorf("objc: %w", err))
|
||||||
}
|
}
|
||||||
@@ -53,6 +71,16 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("objc: %w", err))
|
panic(fmt.Errorf("objc: %w", err))
|
||||||
}
|
}
|
||||||
|
if runtime.GOARCH == "amd64" {
|
||||||
|
objc_msgSend_stret_fn, err = purego.Dlsym(objc, "objc_msgSend_stret")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("objc: %w", err))
|
||||||
|
}
|
||||||
|
objc_msgSendSuper2_stret_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2_stret")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("objc: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
purego.RegisterFunc(&objc_msgSend, objc_msgSend_fn)
|
purego.RegisterFunc(&objc_msgSend, objc_msgSend_fn)
|
||||||
objc_msgSendSuper2_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2")
|
objc_msgSendSuper2_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,6 +90,8 @@ func init() {
|
|||||||
purego.RegisterLibFunc(&object_getClass, objc, "object_getClass")
|
purego.RegisterLibFunc(&object_getClass, objc, "object_getClass")
|
||||||
purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass")
|
purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass")
|
||||||
purego.RegisterLibFunc(&objc_getProtocol, objc, "objc_getProtocol")
|
purego.RegisterLibFunc(&objc_getProtocol, objc, "objc_getProtocol")
|
||||||
|
purego.RegisterLibFunc(&objc_allocateProtocol, objc, "objc_allocateProtocol")
|
||||||
|
purego.RegisterLibFunc(&objc_registerProtocol, objc, "objc_registerProtocol")
|
||||||
purego.RegisterLibFunc(&objc_allocateClassPair, objc, "objc_allocateClassPair")
|
purego.RegisterLibFunc(&objc_allocateClassPair, objc, "objc_allocateClassPair")
|
||||||
purego.RegisterLibFunc(&objc_registerClassPair, objc, "objc_registerClassPair")
|
purego.RegisterLibFunc(&objc_registerClassPair, objc, "objc_registerClassPair")
|
||||||
purego.RegisterLibFunc(&sel_registerName, objc, "sel_registerName")
|
purego.RegisterLibFunc(&sel_registerName, objc, "sel_registerName")
|
||||||
@@ -75,8 +105,21 @@ func init() {
|
|||||||
purego.RegisterLibFunc(&ivar_getName, objc, "ivar_getName")
|
purego.RegisterLibFunc(&ivar_getName, objc, "ivar_getName")
|
||||||
purego.RegisterLibFunc(&protocol_getName, objc, "protocol_getName")
|
purego.RegisterLibFunc(&protocol_getName, objc, "protocol_getName")
|
||||||
purego.RegisterLibFunc(&protocol_isEqual, objc, "protocol_isEqual")
|
purego.RegisterLibFunc(&protocol_isEqual, objc, "protocol_isEqual")
|
||||||
|
purego.RegisterLibFunc(&protocol_addMethodDescription, objc, "protocol_addMethodDescription")
|
||||||
|
purego.RegisterLibFunc(&protocol_copyMethodDescriptionList, objc, "protocol_copyMethodDescriptionList")
|
||||||
|
purego.RegisterLibFunc(&protocol_copyProtocolList, objc, "protocol_copyProtocolList")
|
||||||
|
purego.RegisterLibFunc(&protocol_addProtocol, objc, "protocol_addProtocol")
|
||||||
|
purego.RegisterLibFunc(&protocol_addProperty, objc, "protocol_addProperty")
|
||||||
|
purego.RegisterLibFunc(&protocol_copyPropertyList2, objc, "protocol_copyPropertyList2")
|
||||||
|
purego.RegisterLibFunc(&property_getName, objc, "property_getName")
|
||||||
|
purego.RegisterLibFunc(&property_getAttributes, objc, "property_getAttributes")
|
||||||
purego.RegisterLibFunc(&object_getIvar, objc, "object_getIvar")
|
purego.RegisterLibFunc(&object_getIvar, objc, "object_getIvar")
|
||||||
purego.RegisterLibFunc(&object_setIvar, objc, "object_setIvar")
|
purego.RegisterLibFunc(&object_setIvar, objc, "object_setIvar")
|
||||||
|
purego.RegisterLibFunc(&free, purego.RTLD_DEFAULT, "free")
|
||||||
|
|
||||||
|
purego.RegisterLibFunc(&_Block_copy, objc, "_Block_copy")
|
||||||
|
purego.RegisterLibFunc(&_Block_release, objc, "_Block_release")
|
||||||
|
theBlocksCache = newBlockCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID is an opaque pointer to some Objective-C object
|
// ID is an opaque pointer to some Objective-C object
|
||||||
@@ -90,7 +133,7 @@ func (id ID) Class() Class {
|
|||||||
// Send is a convenience method for sending messages to objects. This function takes a SEL
|
// Send is a convenience method for sending messages to objects. This function takes a SEL
|
||||||
// instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
|
// instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
|
||||||
// of RegisterName.
|
// of RegisterName.
|
||||||
func (id ID) Send(sel SEL, args ...interface{}) ID {
|
func (id ID) Send(sel SEL, args ...any) ID {
|
||||||
return objc_msgSend(id, sel, args...)
|
return objc_msgSend(id, sel, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,12 +147,22 @@ func (id ID) SetIvar(ivar Ivar, value ID) {
|
|||||||
object_setIvar(id, ivar, value)
|
object_setIvar(id, ivar, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keep in sync with func.go
|
||||||
|
const maxRegAllocStructSize = 16
|
||||||
|
|
||||||
// Send is a convenience method for sending messages to objects that can return any type.
|
// Send is a convenience method for sending messages to objects that can return any type.
|
||||||
// This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock.
|
// This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock.
|
||||||
// It is best to cache the result of RegisterName.
|
// It is best to cache the result of RegisterName.
|
||||||
func Send[T any](id ID, sel SEL, args ...any) T {
|
func Send[T any](id ID, sel SEL, args ...any) T {
|
||||||
var fn func(id ID, sel SEL, args ...any) T
|
var fn func(id ID, sel SEL, args ...any) T
|
||||||
purego.RegisterFunc(&fn, objc_msgSend_fn)
|
var zero T
|
||||||
|
if runtime.GOARCH == "amd64" &&
|
||||||
|
reflect.ValueOf(zero).Kind() == reflect.Struct &&
|
||||||
|
reflect.ValueOf(zero).Type().Size() > maxRegAllocStructSize {
|
||||||
|
purego.RegisterFunc(&fn, objc_msgSend_stret_fn)
|
||||||
|
} else {
|
||||||
|
purego.RegisterFunc(&fn, objc_msgSend_fn)
|
||||||
|
}
|
||||||
return fn(id, sel, args...)
|
return fn(id, sel, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +177,7 @@ type objc_super struct {
|
|||||||
// SendSuper is a convenience method for sending message to object's super. This function takes a SEL
|
// SendSuper is a convenience method for sending message to object's super. This function takes a SEL
|
||||||
// instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
|
// instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
|
||||||
// of RegisterName.
|
// of RegisterName.
|
||||||
func (id ID) SendSuper(sel SEL, args ...interface{}) ID {
|
func (id ID) SendSuper(sel SEL, args ...any) ID {
|
||||||
super := &objc_super{
|
super := &objc_super{
|
||||||
receiver: id,
|
receiver: id,
|
||||||
superClass: id.Class(),
|
superClass: id.Class(),
|
||||||
@@ -141,7 +194,14 @@ func SendSuper[T any](id ID, sel SEL, args ...any) T {
|
|||||||
superClass: id.Class(),
|
superClass: id.Class(),
|
||||||
}
|
}
|
||||||
var fn func(objcSuper *objc_super, sel SEL, args ...any) T
|
var fn func(objcSuper *objc_super, sel SEL, args ...any) T
|
||||||
purego.RegisterFunc(&fn, objc_msgSendSuper2_fn)
|
var zero T
|
||||||
|
if runtime.GOARCH == "amd64" &&
|
||||||
|
reflect.ValueOf(zero).Kind() == reflect.Struct &&
|
||||||
|
reflect.ValueOf(zero).Type().Size() > maxRegAllocStructSize {
|
||||||
|
purego.RegisterFunc(&fn, objc_msgSendSuper2_stret_fn)
|
||||||
|
} else {
|
||||||
|
purego.RegisterFunc(&fn, objc_msgSendSuper2_fn)
|
||||||
|
}
|
||||||
return fn(super, sel, args...)
|
return fn(super, sel, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,13 +223,6 @@ func GetClass(name string) Class {
|
|||||||
return objc_getClass(name)
|
return objc_getClass(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllocateClassPair creates a new class and metaclass. Then returns the new class, or Nil if the class could not be created
|
|
||||||
//
|
|
||||||
// Deprecated: use RegisterClass instead
|
|
||||||
func AllocateClassPair(super Class, name string, extraBytes uintptr) Class {
|
|
||||||
return objc_allocateClassPair(super, name, extraBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MethodDef represents the Go function and the selector that ObjC uses to access that function.
|
// MethodDef represents the Go function and the selector that ObjC uses to access that function.
|
||||||
type MethodDef struct {
|
type MethodDef struct {
|
||||||
Cmd SEL
|
Cmd SEL
|
||||||
@@ -360,7 +413,7 @@ func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
|
|||||||
switch typ {
|
switch typ {
|
||||||
case reflect.TypeOf(Class(0)):
|
case reflect.TypeOf(Class(0)):
|
||||||
return encClass, nil
|
return encClass, nil
|
||||||
case reflect.TypeOf(ID(0)):
|
case reflect.TypeOf(ID(0)), reflect.TypeOf(Block(0)):
|
||||||
return encId, nil
|
return encId, nil
|
||||||
case reflect.TypeOf(SEL(0)):
|
case reflect.TypeOf(SEL(0)):
|
||||||
return encSelector, nil
|
return encSelector, nil
|
||||||
@@ -414,7 +467,7 @@ func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
|
|||||||
}
|
}
|
||||||
encoding += tmp
|
encoding += tmp
|
||||||
}
|
}
|
||||||
encoding = encStructEnd
|
encoding += encStructEnd
|
||||||
return encoding, nil
|
return encoding, nil
|
||||||
case reflect.UnsafePointer:
|
case reflect.UnsafePointer:
|
||||||
return encUnsafePtr, nil
|
return encUnsafePtr, nil
|
||||||
@@ -426,7 +479,7 @@ func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encodeFunc returns a functions type as if it was given to @encode(fn)
|
// encodeFunc returns a functions type as if it was given to @encode(fn)
|
||||||
func encodeFunc(fn interface{}) (string, error) {
|
func encodeFunc(fn any) (string, error) {
|
||||||
typ := reflect.TypeOf(fn)
|
typ := reflect.TypeOf(fn)
|
||||||
if typ.Kind() != reflect.Func {
|
if typ.Kind() != reflect.Func {
|
||||||
return "", errors.New("not a func")
|
return "", errors.New("not a func")
|
||||||
@@ -476,20 +529,6 @@ func (c Class) AddMethod(name SEL, imp IMP, types string) bool {
|
|||||||
return class_addMethod(c, name, imp, types)
|
return class_addMethod(c, name, imp, types)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddIvar adds a new instance variable to a class.
|
|
||||||
// It may only be called after AllocateClassPair and before Register.
|
|
||||||
// Adding an instance variable to an existing class is not supported.
|
|
||||||
// The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
|
|
||||||
// It takes the instance of the type of the Ivar and a string representing the type.
|
|
||||||
//
|
|
||||||
// Deprecated: use RegisterClass instead
|
|
||||||
func (c Class) AddIvar(name string, ty interface{}, types string) bool {
|
|
||||||
typeOf := reflect.TypeOf(ty)
|
|
||||||
size := typeOf.Size()
|
|
||||||
alignment := uint8(math.Log2(float64(typeOf.Align())))
|
|
||||||
return class_addIvar(c, name, size, alignment, types)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddProtocol adds a protocol to a class.
|
// AddProtocol adds a protocol to a class.
|
||||||
// Returns true if the protocol was added successfully, otherwise false (for example,
|
// Returns true if the protocol was added successfully, otherwise false (for example,
|
||||||
// the class already conforms to that protocol).
|
// the class already conforms to that protocol).
|
||||||
@@ -507,14 +546,6 @@ func (c Class) InstanceVariable(name string) Ivar {
|
|||||||
return class_getInstanceVariable(c, name)
|
return class_getInstanceVariable(c, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register registers a class that was allocated using AllocateClassPair.
|
|
||||||
// It can now be used to make objects by sending it either alloc and init or new.
|
|
||||||
//
|
|
||||||
// Deprecated: use RegisterClass instead
|
|
||||||
func (c Class) Register() {
|
|
||||||
objc_registerClassPair(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ivar an opaque type that represents an instance variable.
|
// Ivar an opaque type that represents an instance variable.
|
||||||
type Ivar uintptr
|
type Ivar uintptr
|
||||||
|
|
||||||
@@ -530,6 +561,39 @@ func (i Ivar) Name() string {
|
|||||||
return ivar_getName(i)
|
return ivar_getName(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MethodDescription holds the name and type definition of a method.
|
||||||
|
type MethodDescription struct {
|
||||||
|
name, types uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of this method.
|
||||||
|
func (m MethodDescription) Name() string {
|
||||||
|
return strings.GoString(m.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types returns the OBJC runtime encoded type description.
|
||||||
|
func (m MethodDescription) Types() string {
|
||||||
|
return strings.GoString(m.types)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertyAttribute contains the null-terminated Name and Value pair of a Properties internal description.
|
||||||
|
type PropertyAttribute struct {
|
||||||
|
Name, Value *byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property is an opaque type for Objective-C property metadata.
|
||||||
|
type Property uintptr
|
||||||
|
|
||||||
|
// Name returns the name of this property.
|
||||||
|
func (p Property) Name() string {
|
||||||
|
return property_getName(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes returns a comma separated list of PropertyAttribute
|
||||||
|
func (p Property) Attributes() string {
|
||||||
|
return property_getAttributes(p)
|
||||||
|
}
|
||||||
|
|
||||||
// Protocol is a type that declares methods that can be implemented by any class.
|
// Protocol is a type that declares methods that can be implemented by any class.
|
||||||
type Protocol [0]func()
|
type Protocol [0]func()
|
||||||
|
|
||||||
@@ -538,11 +602,69 @@ func GetProtocol(name string) *Protocol {
|
|||||||
return objc_getProtocol(name)
|
return objc_getProtocol(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllocateProtocol creates a new protocol in the OBJC runtime or nil if the protocol already exists.
|
||||||
|
func AllocateProtocol(name string) *Protocol {
|
||||||
|
return objc_allocateProtocol(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register registers the protocol created using AllocateProtocol with the runtime. This must be done
|
||||||
|
// before it is used anywhere and can only be called once.
|
||||||
|
func (p *Protocol) Register() {
|
||||||
|
objc_registerProtocol(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyMethodDescriptionList returns a list of methods that this protocol has given it isRequiredMethod and isInstanceMethod.
|
||||||
|
func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod bool) []MethodDescription {
|
||||||
|
count := uint32(0)
|
||||||
|
desc := protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &count)
|
||||||
|
methods := clone(unsafe.Slice(desc, count))
|
||||||
|
free(unsafe.Pointer(desc))
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyProtocolList returns a list of the protocols that this protocol inherits from.
|
||||||
|
func (p *Protocol) CopyProtocolList() []*Protocol {
|
||||||
|
count := uint32(0)
|
||||||
|
desc := protocol_copyProtocolList(p, &count)
|
||||||
|
protocols := clone(unsafe.Slice(desc, count))
|
||||||
|
free(unsafe.Pointer(desc))
|
||||||
|
return protocols
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyPropertyList returns a list of properties that this protocol has given it isRequiredProperty and isInstanceProperty.
|
||||||
|
func (p *Protocol) CopyPropertyList(isRequiredProperty, isInstanceProperty bool) []Property {
|
||||||
|
count := uint32(0)
|
||||||
|
desc := protocol_copyPropertyList2(p, &count, isRequiredProperty, isInstanceProperty)
|
||||||
|
protocols := clone(unsafe.Slice(desc, count))
|
||||||
|
free(unsafe.Pointer(desc))
|
||||||
|
return protocols
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of this protocol.
|
||||||
|
func (p *Protocol) Name() string {
|
||||||
|
return protocol_getName(p)
|
||||||
|
}
|
||||||
|
|
||||||
// Equals return true if the two protocols are the same.
|
// Equals return true if the two protocols are the same.
|
||||||
func (p *Protocol) Equals(p2 *Protocol) bool {
|
func (p *Protocol) Equals(p2 *Protocol) bool {
|
||||||
return protocol_isEqual(p, p2)
|
return protocol_isEqual(p, p2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddMethodDescription adds a method to a protocol. This can only be called between AllocateProtocol and Protocol.Register.
|
||||||
|
func (p *Protocol) AddMethodDescription(name SEL, types string, isRequiredMethod, isInstanceMethod bool) {
|
||||||
|
protocol_addMethodDescription(p, name, types, isRequiredMethod, isInstanceMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProtocol marks the protocol as inheriting from another. This can only be called between AllocateProtocol and Protocol.Register.
|
||||||
|
func (p *Protocol) AddProtocol(protocol *Protocol) {
|
||||||
|
protocol_addProtocol(p, protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProperty adds a property to the protocol. This can only be called between AllocateProtocol and Protocol.Register.
|
||||||
|
func (p *Protocol) AddProperty(name string, attributes []PropertyAttribute, isRequiredProperty, isInstanceProperty bool) {
|
||||||
|
protocol_addProperty(p, name, attributes, uint32(len(attributes)), isRequiredProperty, isInstanceProperty)
|
||||||
|
}
|
||||||
|
|
||||||
// IMP is a function pointer that can be called by Objective-C code.
|
// IMP is a function pointer that can be called by Objective-C code.
|
||||||
type IMP uintptr
|
type IMP uintptr
|
||||||
|
|
||||||
@@ -550,7 +672,7 @@ type IMP uintptr
|
|||||||
// It returns an IMP function pointer that can be called by Objective-C code.
|
// It returns an IMP function pointer that can be called by Objective-C code.
|
||||||
// The function panics if an error occurs.
|
// The function panics if an error occurs.
|
||||||
// The function pointer is never deallocated.
|
// The function pointer is never deallocated.
|
||||||
func NewIMP(fn interface{}) IMP {
|
func NewIMP(fn any) IMP {
|
||||||
ty := reflect.TypeOf(fn)
|
ty := reflect.TypeOf(fn)
|
||||||
if ty.Kind() != reflect.Func {
|
if ty.Kind() != reflect.Func {
|
||||||
panic("objc: not a function")
|
panic("objc: not a function")
|
||||||
@@ -567,3 +689,14 @@ func NewIMP(fn interface{}) IMP {
|
|||||||
}
|
}
|
||||||
return IMP(purego.NewCallback(fn))
|
return IMP(purego.NewCallback(fn))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove and use slices.Clone when minimum version for purego is 1.21
|
||||||
|
func clone[S ~[]E, E any](s S) S {
|
||||||
|
// Preserve nilness in case it matters.
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Avoid s[:0:0] as it leads to unwanted liveness when cloning a
|
||||||
|
// zero-length slice of a large array; see https://go.dev/issue/68488.
|
||||||
|
return append(S{}, s...)
|
||||||
|
}
|
||||||
|
|||||||
+10
-18
@@ -85,7 +85,7 @@ const (
|
|||||||
_MEMORY = 0b1111
|
_MEMORY = 0b1111
|
||||||
)
|
)
|
||||||
|
|
||||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
|
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
|
||||||
if v.Type().Size() == 0 {
|
if v.Type().Size() == 0 {
|
||||||
return keepAlive
|
return keepAlive
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
|
|||||||
return keepAlive
|
return keepAlive
|
||||||
}
|
}
|
||||||
|
|
||||||
func postMerger(t reflect.Type) bool {
|
func postMerger(t reflect.Type) (passInMemory bool) {
|
||||||
// (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other
|
// (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other
|
||||||
// eightbyte isn’t SSEUP, the whole argument is passed in memory.
|
// eightbyte isn’t SSEUP, the whole argument is passed in memory.
|
||||||
if t.Kind() != reflect.Struct {
|
if t.Kind() != reflect.Struct {
|
||||||
@@ -120,19 +120,7 @@ func postMerger(t reflect.Type) bool {
|
|||||||
if t.Size() <= 2*8 {
|
if t.Size() <= 2*8 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
first := getFirst(t).Kind()
|
return true // Go does not have an SSE/SSEUP type so this is always true
|
||||||
if first != reflect.Float32 && first != reflect.Float64 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFirst(t reflect.Type) reflect.Type {
|
|
||||||
first := t.Field(0).Type
|
|
||||||
if first.Kind() == reflect.Struct {
|
|
||||||
return getFirst(first)
|
|
||||||
}
|
|
||||||
return first
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) {
|
func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) {
|
||||||
@@ -196,7 +184,7 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
|
|||||||
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
|
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
|
||||||
shift += 32
|
shift += 32
|
||||||
class |= _INTEGER
|
class |= _INTEGER
|
||||||
case reflect.Int64:
|
case reflect.Int64, reflect.Int:
|
||||||
val = uint64(f.Int())
|
val = uint64(f.Int())
|
||||||
shift = 64
|
shift = 64
|
||||||
class = _INTEGER
|
class = _INTEGER
|
||||||
@@ -212,7 +200,7 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
|
|||||||
val |= f.Uint() << shift
|
val |= f.Uint() << shift
|
||||||
shift += 32
|
shift += 32
|
||||||
class |= _INTEGER
|
class |= _INTEGER
|
||||||
case reflect.Uint64:
|
case reflect.Uint64, reflect.Uint, reflect.Uintptr:
|
||||||
val = f.Uint()
|
val = f.Uint()
|
||||||
shift = 64
|
shift = 64
|
||||||
class = _INTEGER
|
class = _INTEGER
|
||||||
@@ -257,7 +245,7 @@ func placeStack(v reflect.Value, addStack func(uintptr)) {
|
|||||||
addStack(f.Pointer())
|
addStack(f.Pointer())
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
addStack(uintptr(f.Int()))
|
addStack(uintptr(f.Int()))
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
addStack(uintptr(f.Uint()))
|
addStack(uintptr(f.Uint()))
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
addStack(uintptr(math.Float32bits(float32(f.Float()))))
|
addStack(uintptr(math.Float32bits(float32(f.Float()))))
|
||||||
@@ -270,3 +258,7 @@ func placeStack(v reflect.Value, addStack func(uintptr)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||||
|
panic("purego: not needed on amd64")
|
||||||
|
}
|
||||||
|
|||||||
+22
-12
@@ -16,17 +16,17 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
|||||||
return reflect.New(outType).Elem()
|
return reflect.New(outType).Elem()
|
||||||
case outSize <= 8:
|
case outSize <= 8:
|
||||||
r1 := syscall.a1
|
r1 := syscall.a1
|
||||||
if isAllSameFloat(outType) {
|
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||||
r1 = syscall.f1
|
r1 = syscall.f1
|
||||||
if outType.NumField() == 2 {
|
if numFields == 2 {
|
||||||
r1 = syscall.f2<<32 | syscall.f1
|
r1 = syscall.f2<<32 | syscall.f1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
|
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
|
||||||
case outSize <= 16:
|
case outSize <= 16:
|
||||||
r1, r2 := syscall.a1, syscall.a2
|
r1, r2 := syscall.a1, syscall.a2
|
||||||
if isAllSameFloat(outType) {
|
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||||
switch outType.NumField() {
|
switch numFields {
|
||||||
case 4:
|
case 4:
|
||||||
r1 = syscall.f2<<32 | syscall.f1
|
r1 = syscall.f2<<32 | syscall.f1
|
||||||
r2 = syscall.f4<<32 | syscall.f3
|
r2 = syscall.f4<<32 | syscall.f3
|
||||||
@@ -42,8 +42,8 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
|||||||
}
|
}
|
||||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
|
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
|
||||||
default:
|
default:
|
||||||
if isAllSameFloat(outType) && outType.NumField() <= 4 {
|
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats && numFields <= 4 {
|
||||||
switch outType.NumField() {
|
switch numFields {
|
||||||
case 4:
|
case 4:
|
||||||
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{syscall.f1, syscall.f2, syscall.f3, syscall.f4})).Elem()
|
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{syscall.f1, syscall.f2, syscall.f3, syscall.f4})).Elem()
|
||||||
case 3:
|
case 3:
|
||||||
@@ -65,7 +65,7 @@ const (
|
|||||||
_INT = 0b11
|
_INT = 0b11
|
||||||
)
|
)
|
||||||
|
|
||||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
|
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
|
||||||
if v.Type().Size() == 0 {
|
if v.Type().Size() == 0 {
|
||||||
return keepAlive
|
return keepAlive
|
||||||
}
|
}
|
||||||
@@ -73,8 +73,8 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
|
|||||||
if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 {
|
if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 {
|
||||||
// if this doesn't fit entirely in registers then
|
// if this doesn't fit entirely in registers then
|
||||||
// each element goes onto the stack
|
// each element goes onto the stack
|
||||||
if hfa && *numFloats+v.NumField() > numOfFloats {
|
if hfa && *numFloats+v.NumField() > numOfFloatRegisters {
|
||||||
*numFloats = numOfFloats
|
*numFloats = numOfFloatRegisters
|
||||||
} else if hva && *numInts+v.NumField() > numOfIntegerRegisters() {
|
} else if hva && *numInts+v.NumField() > numOfIntegerRegisters() {
|
||||||
*numInts = numOfIntegerRegisters()
|
*numInts = numOfIntegerRegisters()
|
||||||
}
|
}
|
||||||
@@ -107,6 +107,8 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
|
|||||||
} else {
|
} else {
|
||||||
f = v.Index(k)
|
f = v.Index(k)
|
||||||
}
|
}
|
||||||
|
align := byte(f.Type().Align()*8 - 1)
|
||||||
|
shift = (shift + align) &^ align
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
shift = 0
|
shift = 0
|
||||||
flushed = true
|
flushed = true
|
||||||
@@ -137,10 +139,11 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
|
|||||||
val |= f.Uint() << shift
|
val |= f.Uint() << shift
|
||||||
shift += 32
|
shift += 32
|
||||||
class |= _INT
|
class |= _INT
|
||||||
case reflect.Uint64:
|
case reflect.Uint64, reflect.Uint, reflect.Uintptr:
|
||||||
addInt(uintptr(f.Uint()))
|
addInt(uintptr(f.Uint()))
|
||||||
shift = 0
|
shift = 0
|
||||||
flushed = true
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
val |= uint64(f.Int()&0xFF) << shift
|
val |= uint64(f.Int()&0xFF) << shift
|
||||||
shift += 8
|
shift += 8
|
||||||
@@ -153,10 +156,11 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
|
|||||||
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
|
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
|
||||||
shift += 32
|
shift += 32
|
||||||
class |= _INT
|
class |= _INT
|
||||||
case reflect.Int64:
|
case reflect.Int64, reflect.Int:
|
||||||
addInt(uintptr(f.Int()))
|
addInt(uintptr(f.Int()))
|
||||||
shift = 0
|
shift = 0
|
||||||
flushed = true
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
if class == _FLOAT {
|
if class == _FLOAT {
|
||||||
addFloat(uintptr(val))
|
addFloat(uintptr(val))
|
||||||
@@ -170,6 +174,12 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
|
|||||||
addFloat(uintptr(math.Float64bits(float64(f.Float()))))
|
addFloat(uintptr(math.Float64bits(float64(f.Float()))))
|
||||||
shift = 0
|
shift = 0
|
||||||
flushed = true
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
|
case reflect.Ptr:
|
||||||
|
addInt(f.Pointer())
|
||||||
|
shift = 0
|
||||||
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
place(f)
|
place(f)
|
||||||
default:
|
default:
|
||||||
@@ -187,7 +197,7 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func placeStack(v reflect.Value, keepAlive []interface{}, addInt func(uintptr)) []interface{} {
|
func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
|
||||||
// Struct is too big to be placed in registers.
|
// Struct is too big to be placed in registers.
|
||||||
// Copy to heap and place the pointer in register
|
// Copy to heap and place the pointer in register
|
||||||
ptrStruct := reflect.New(v.Type())
|
ptrStruct := reflect.New(v.Type())
|
||||||
|
|||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
package purego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
||||||
|
outSize := outType.Size()
|
||||||
|
switch {
|
||||||
|
case outSize == 0:
|
||||||
|
return reflect.New(outType).Elem()
|
||||||
|
case outSize <= 8:
|
||||||
|
r1 := syscall.a1
|
||||||
|
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||||
|
r1 = syscall.f1
|
||||||
|
if numFields == 2 {
|
||||||
|
r1 = syscall.f2<<32 | syscall.f1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
|
||||||
|
case outSize <= 16:
|
||||||
|
r1, r2 := syscall.a1, syscall.a2
|
||||||
|
if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
|
||||||
|
switch numFields {
|
||||||
|
case 4:
|
||||||
|
r1 = syscall.f2<<32 | syscall.f1
|
||||||
|
r2 = syscall.f4<<32 | syscall.f3
|
||||||
|
case 3:
|
||||||
|
r1 = syscall.f2<<32 | syscall.f1
|
||||||
|
r2 = syscall.f3
|
||||||
|
case 2:
|
||||||
|
r1 = syscall.f1
|
||||||
|
r2 = syscall.f2
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
|
||||||
|
default:
|
||||||
|
// create struct from the Go pointer created above
|
||||||
|
// weird pointer dereference to circumvent go vet
|
||||||
|
return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_NO_CLASS = 0b00
|
||||||
|
_FLOAT = 0b01
|
||||||
|
_INT = 0b11
|
||||||
|
)
|
||||||
|
|
||||||
|
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
|
||||||
|
if v.Type().Size() == 0 {
|
||||||
|
return keepAlive
|
||||||
|
}
|
||||||
|
|
||||||
|
if size := v.Type().Size(); size <= 16 {
|
||||||
|
placeRegisters(v, addFloat, addInt)
|
||||||
|
} else {
|
||||||
|
keepAlive = placeStack(v, keepAlive, addInt)
|
||||||
|
}
|
||||||
|
return keepAlive // the struct was allocated so don't panic
|
||||||
|
}
|
||||||
|
|
||||||
|
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||||
|
var val uint64
|
||||||
|
var shift byte
|
||||||
|
var flushed bool
|
||||||
|
class := _NO_CLASS
|
||||||
|
var place func(v reflect.Value)
|
||||||
|
place = func(v reflect.Value) {
|
||||||
|
var numFields int
|
||||||
|
if v.Kind() == reflect.Struct {
|
||||||
|
numFields = v.Type().NumField()
|
||||||
|
} else {
|
||||||
|
numFields = v.Type().Len()
|
||||||
|
}
|
||||||
|
for k := 0; k < numFields; k++ {
|
||||||
|
flushed = false
|
||||||
|
var f reflect.Value
|
||||||
|
if v.Kind() == reflect.Struct {
|
||||||
|
f = v.Field(k)
|
||||||
|
} else {
|
||||||
|
f = v.Index(k)
|
||||||
|
}
|
||||||
|
align := byte(f.Type().Align()*8 - 1)
|
||||||
|
shift = (shift + align) &^ align
|
||||||
|
if shift >= 64 {
|
||||||
|
shift = 0
|
||||||
|
flushed = true
|
||||||
|
if class == _FLOAT {
|
||||||
|
addFloat(uintptr(val))
|
||||||
|
} else {
|
||||||
|
addInt(uintptr(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch f.Type().Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
place(f)
|
||||||
|
case reflect.Bool:
|
||||||
|
if f.Bool() {
|
||||||
|
val |= 1
|
||||||
|
}
|
||||||
|
shift += 8
|
||||||
|
class |= _INT
|
||||||
|
case reflect.Uint8:
|
||||||
|
val |= f.Uint() << shift
|
||||||
|
shift += 8
|
||||||
|
class |= _INT
|
||||||
|
case reflect.Uint16:
|
||||||
|
val |= f.Uint() << shift
|
||||||
|
shift += 16
|
||||||
|
class |= _INT
|
||||||
|
case reflect.Uint32:
|
||||||
|
val |= f.Uint() << shift
|
||||||
|
shift += 32
|
||||||
|
class |= _INT
|
||||||
|
case reflect.Uint64, reflect.Uint, reflect.Uintptr:
|
||||||
|
addInt(uintptr(f.Uint()))
|
||||||
|
shift = 0
|
||||||
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
|
case reflect.Int8:
|
||||||
|
val |= uint64(f.Int()&0xFF) << shift
|
||||||
|
shift += 8
|
||||||
|
class |= _INT
|
||||||
|
case reflect.Int16:
|
||||||
|
val |= uint64(f.Int()&0xFFFF) << shift
|
||||||
|
shift += 16
|
||||||
|
class |= _INT
|
||||||
|
case reflect.Int32:
|
||||||
|
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
|
||||||
|
shift += 32
|
||||||
|
class |= _INT
|
||||||
|
case reflect.Int64, reflect.Int:
|
||||||
|
addInt(uintptr(f.Int()))
|
||||||
|
shift = 0
|
||||||
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
|
case reflect.Float32:
|
||||||
|
if class == _FLOAT {
|
||||||
|
addFloat(uintptr(val))
|
||||||
|
val = 0
|
||||||
|
shift = 0
|
||||||
|
}
|
||||||
|
val |= uint64(math.Float32bits(float32(f.Float()))) << shift
|
||||||
|
shift += 32
|
||||||
|
class |= _FLOAT
|
||||||
|
case reflect.Float64:
|
||||||
|
addFloat(uintptr(math.Float64bits(float64(f.Float()))))
|
||||||
|
shift = 0
|
||||||
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
|
case reflect.Ptr:
|
||||||
|
addInt(f.Pointer())
|
||||||
|
shift = 0
|
||||||
|
flushed = true
|
||||||
|
class = _NO_CLASS
|
||||||
|
case reflect.Array:
|
||||||
|
place(f)
|
||||||
|
default:
|
||||||
|
panic("purego: unsupported kind " + f.Kind().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
place(v)
|
||||||
|
if !flushed {
|
||||||
|
if class == _FLOAT {
|
||||||
|
addFloat(uintptr(val))
|
||||||
|
} else {
|
||||||
|
addInt(uintptr(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
|
||||||
|
// Struct is too big to be placed in registers.
|
||||||
|
// Copy to heap and place the pointer in register
|
||||||
|
ptrStruct := reflect.New(v.Type())
|
||||||
|
ptrStruct.Elem().Set(v)
|
||||||
|
ptr := ptrStruct.Elem().Addr().UnsafePointer()
|
||||||
|
keepAlive = append(keepAlive, ptr)
|
||||||
|
addInt(uintptr(ptr))
|
||||||
|
return keepAlive
|
||||||
|
}
|
||||||
+6
-2
@@ -1,16 +1,20 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build !amd64 && !arm64
|
//go:build !amd64 && !arm64 && !loong64
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
|
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
|
||||||
panic("purego: struct arguments are not supported")
|
panic("purego: struct arguments are not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
|
||||||
panic("purego: struct returns are not supported")
|
panic("purego: struct returns are not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
|
||||||
|
panic("purego: not needed on other platforms")
|
||||||
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build darwin || freebsd || linux || netbsd
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
#include "abi_amd64.h"
|
#include "abi_amd64.h"
|
||||||
@@ -122,6 +122,9 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
|||||||
|
|
||||||
PUSHQ R10 // push the stack pointer below registers
|
PUSHQ R10 // push the stack pointer below registers
|
||||||
|
|
||||||
|
// Switch from the host ABI to the Go ABI.
|
||||||
|
PUSH_REGS_HOST_TO_ABI0()
|
||||||
|
|
||||||
// determine index into runtime·cbs table
|
// determine index into runtime·cbs table
|
||||||
MOVQ $callbackasm(SB), DX
|
MOVQ $callbackasm(SB), DX
|
||||||
SUBQ DX, AX
|
SUBQ DX, AX
|
||||||
@@ -130,9 +133,6 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
|||||||
DIVL CX
|
DIVL CX
|
||||||
SUBQ $1, AX // subtract 1 because return PC is to the next slot
|
SUBQ $1, AX // subtract 1 because return PC is to the next slot
|
||||||
|
|
||||||
// Switch from the host ABI to the Go ABI.
|
|
||||||
PUSH_REGS_HOST_TO_ABI0()
|
|
||||||
|
|
||||||
// Create a struct callbackArgs on our stack to be passed as
|
// Create a struct callbackArgs on our stack to be passed as
|
||||||
// the "frame" to cgocallback and on to callbackWrap.
|
// the "frame" to cgocallback and on to callbackWrap.
|
||||||
// $24 to make enough room for the arguments to runtime.cgocallback
|
// $24 to make enough room for the arguments to runtime.cgocallback
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux || windows
|
//go:build darwin || freebsd || linux || netbsd || windows
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
#include "go_asm.h"
|
#include "go_asm.h"
|
||||||
|
|||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "funcdata.h"
|
||||||
|
|
||||||
|
#define STACK_SIZE 64
|
||||||
|
#define PTR_ADDRESS (STACK_SIZE - 8)
|
||||||
|
|
||||||
|
// syscall15X calls a function in libc on behalf of the syscall package.
|
||||||
|
// syscall15X takes a pointer to a struct like:
|
||||||
|
// struct {
|
||||||
|
// fn uintptr
|
||||||
|
// a1 uintptr
|
||||||
|
// a2 uintptr
|
||||||
|
// a3 uintptr
|
||||||
|
// a4 uintptr
|
||||||
|
// a5 uintptr
|
||||||
|
// a6 uintptr
|
||||||
|
// a7 uintptr
|
||||||
|
// a8 uintptr
|
||||||
|
// a9 uintptr
|
||||||
|
// a10 uintptr
|
||||||
|
// a11 uintptr
|
||||||
|
// a12 uintptr
|
||||||
|
// a13 uintptr
|
||||||
|
// a14 uintptr
|
||||||
|
// a15 uintptr
|
||||||
|
// r1 uintptr
|
||||||
|
// r2 uintptr
|
||||||
|
// err uintptr
|
||||||
|
// }
|
||||||
|
// syscall15X must be called on the g0 stack with the
|
||||||
|
// C calling convention (use libcCall).
|
||||||
|
GLOBL ·syscall15XABI0(SB), NOPTR|RODATA, $8
|
||||||
|
DATA ·syscall15XABI0(SB)/8, $syscall15X(SB)
|
||||||
|
TEXT syscall15X(SB), NOSPLIT, $0
|
||||||
|
// push structure pointer
|
||||||
|
SUBV $STACK_SIZE, R3
|
||||||
|
MOVV R4, PTR_ADDRESS(R3)
|
||||||
|
MOVV R4, R13
|
||||||
|
|
||||||
|
MOVD syscall15Args_f1(R13), F0 // f1
|
||||||
|
MOVD syscall15Args_f2(R13), F1 // f2
|
||||||
|
MOVD syscall15Args_f3(R13), F2 // f3
|
||||||
|
MOVD syscall15Args_f4(R13), F3 // f4
|
||||||
|
MOVD syscall15Args_f5(R13), F4 // f5
|
||||||
|
MOVD syscall15Args_f6(R13), F5 // f6
|
||||||
|
MOVD syscall15Args_f7(R13), F6 // f7
|
||||||
|
MOVD syscall15Args_f8(R13), F7 // f8
|
||||||
|
|
||||||
|
MOVV syscall15Args_a1(R13), R4 // a1
|
||||||
|
MOVV syscall15Args_a2(R13), R5 // a2
|
||||||
|
MOVV syscall15Args_a3(R13), R6 // a3
|
||||||
|
MOVV syscall15Args_a4(R13), R7 // a4
|
||||||
|
MOVV syscall15Args_a5(R13), R8 // a5
|
||||||
|
MOVV syscall15Args_a6(R13), R9 // a6
|
||||||
|
MOVV syscall15Args_a7(R13), R10 // a7
|
||||||
|
MOVV syscall15Args_a8(R13), R11 // a8
|
||||||
|
|
||||||
|
// push a9-a15 onto stack
|
||||||
|
MOVV syscall15Args_a9(R13), R12
|
||||||
|
MOVV R12, 0(R3)
|
||||||
|
MOVV syscall15Args_a10(R13), R12
|
||||||
|
MOVV R12, 8(R3)
|
||||||
|
MOVV syscall15Args_a11(R13), R12
|
||||||
|
MOVV R12, 16(R3)
|
||||||
|
MOVV syscall15Args_a12(R13), R12
|
||||||
|
MOVV R12, 24(R3)
|
||||||
|
MOVV syscall15Args_a13(R13), R12
|
||||||
|
MOVV R12, 32(R3)
|
||||||
|
MOVV syscall15Args_a14(R13), R12
|
||||||
|
MOVV R12, 40(R3)
|
||||||
|
MOVV syscall15Args_a15(R13), R12
|
||||||
|
MOVV R12, 48(R3)
|
||||||
|
|
||||||
|
MOVV syscall15Args_fn(R13), R12
|
||||||
|
JAL (R12)
|
||||||
|
|
||||||
|
// pop structure pointer
|
||||||
|
MOVV PTR_ADDRESS(R3), R13
|
||||||
|
ADDV $STACK_SIZE, R3
|
||||||
|
|
||||||
|
// save R4, R5
|
||||||
|
MOVV R4, syscall15Args_a1(R13)
|
||||||
|
MOVV R5, syscall15Args_a2(R13)
|
||||||
|
|
||||||
|
// save f0-f3
|
||||||
|
MOVD F0, syscall15Args_f1(R13)
|
||||||
|
MOVD F1, syscall15Args_f2(R13)
|
||||||
|
MOVD F2, syscall15Args_f3(R13)
|
||||||
|
MOVD F3, syscall15Args_f4(R13)
|
||||||
|
RET
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux
|
//go:build darwin || freebsd || linux || netbsd
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
#include "go_asm.h"
|
#include "go_asm.h"
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "funcdata.h"
|
||||||
|
#include "abi_loong64.h"
|
||||||
|
|
||||||
|
TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
|
||||||
|
NO_LOCAL_POINTERS
|
||||||
|
|
||||||
|
SUBV $(16*8), R3, R14
|
||||||
|
MOVD F0, 0(R14)
|
||||||
|
MOVD F1, 8(R14)
|
||||||
|
MOVD F2, 16(R14)
|
||||||
|
MOVD F3, 24(R14)
|
||||||
|
MOVD F4, 32(R14)
|
||||||
|
MOVD F5, 40(R14)
|
||||||
|
MOVD F6, 48(R14)
|
||||||
|
MOVD F7, 56(R14)
|
||||||
|
MOVV R4, 64(R14)
|
||||||
|
MOVV R5, 72(R14)
|
||||||
|
MOVV R6, 80(R14)
|
||||||
|
MOVV R7, 88(R14)
|
||||||
|
MOVV R8, 96(R14)
|
||||||
|
MOVV R9, 104(R14)
|
||||||
|
MOVV R10, 112(R14)
|
||||||
|
MOVV R11, 120(R14)
|
||||||
|
|
||||||
|
// Adjust SP by frame size.
|
||||||
|
SUBV $(22*8), R3
|
||||||
|
|
||||||
|
// It is important to save R30 because the go assembler
|
||||||
|
// uses it for move instructions for a variable.
|
||||||
|
// This line:
|
||||||
|
// MOVV ·callbackWrap_call(SB), R4
|
||||||
|
// Creates the instructions:
|
||||||
|
// PCALAU12I off1(PC), R30
|
||||||
|
// MOVV off2(R30), R4
|
||||||
|
// R30 is a callee saved register so we are responsible
|
||||||
|
// for ensuring its value doesn't change. So save it and
|
||||||
|
// restore it at the end of this function.
|
||||||
|
// R1 is the link register. crosscall2 doesn't save it
|
||||||
|
// so it's saved here.
|
||||||
|
MOVV R1, 0(R3)
|
||||||
|
MOVV R30, 8(R3)
|
||||||
|
|
||||||
|
// Create a struct callbackArgs on our stack.
|
||||||
|
MOVV $(callbackArgs__size)(R3), R13
|
||||||
|
MOVV R12, callbackArgs_index(R13) // callback index
|
||||||
|
MOVV R14, callbackArgs_args(R13) // address of args vector
|
||||||
|
MOVV $0, callbackArgs_result(R13) // result
|
||||||
|
|
||||||
|
// Move parameters into registers
|
||||||
|
// Get the ABIInternal function pointer
|
||||||
|
// without <ABIInternal> by using a closure.
|
||||||
|
MOVV ·callbackWrap_call(SB), R4
|
||||||
|
MOVV (R4), R4 // fn unsafe.Pointer
|
||||||
|
MOVV R13, R5 // frame (&callbackArgs{...})
|
||||||
|
MOVV $0, R7 // ctxt uintptr
|
||||||
|
|
||||||
|
JAL crosscall2(SB)
|
||||||
|
|
||||||
|
// Get callback result.
|
||||||
|
MOVV $(callbackArgs__size)(R3), R13
|
||||||
|
MOVV callbackArgs_result(R13), R4
|
||||||
|
|
||||||
|
// Restore LR and R30
|
||||||
|
MOVV 0(R3), R1
|
||||||
|
MOVV 8(R3), R30
|
||||||
|
ADDV $(22*8), R3
|
||||||
|
|
||||||
|
RET
|
||||||
+19
-3
@@ -1,19 +1,35 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || linux || windows
|
//go:build darwin || freebsd || linux || netbsd || windows
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
|
// CDecl marks a function as being called using the __cdecl calling convention as defined in
|
||||||
|
// the [MSDocs] when passed to NewCallback. It must be the first argument to the function.
|
||||||
|
// This is only useful on 386 Windows, but it is safe to use on other platforms.
|
||||||
|
//
|
||||||
|
// [MSDocs]: https://learn.microsoft.com/en-us/cpp/cpp/cdecl?view=msvc-170
|
||||||
|
type CDecl struct{}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxArgs = 15
|
maxArgs = 15
|
||||||
numOfFloats = 8 // arm64 and amd64 both have 8 float registers
|
numOfFloatRegisters = 8 // arm64 and amd64 both have 8 float registers
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type syscall15Args struct {
|
||||||
|
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr
|
||||||
|
f1, f2, f3, f4, f5, f6, f7, f8 uintptr
|
||||||
|
arm64_r8 uintptr
|
||||||
|
}
|
||||||
|
|
||||||
// SyscallN takes fn, a C function pointer and a list of arguments as uintptr.
|
// SyscallN takes fn, a C function pointer and a list of arguments as uintptr.
|
||||||
// There is an internal maximum number of arguments that SyscallN can take. It panics
|
// There is an internal maximum number of arguments that SyscallN can take. It panics
|
||||||
// when the maximum is exceeded. It returns the result and the libc error code if there is one.
|
// when the maximum is exceeded. It returns the result and the libc error code if there is one.
|
||||||
//
|
//
|
||||||
|
// In order to call this function properly make sure to follow all the rules specified in [unsafe.Pointer]
|
||||||
|
// especially point 4.
|
||||||
|
//
|
||||||
// NOTE: SyscallN does not properly call functions that have both integer and float parameters.
|
// NOTE: SyscallN does not properly call functions that have both integer and float parameters.
|
||||||
// See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607
|
// See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607
|
||||||
// for an explanation of why that is.
|
// for an explanation of why that is.
|
||||||
|
|||||||
+3
-13
@@ -1,31 +1,21 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build cgo && !(amd64 || arm64)
|
//go:build cgo && !(amd64 || arm64 || loong64)
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "unsafe" // for go:linkname
|
|
||||||
|
|
||||||
"github.com/ebitengine/purego/internal/cgo"
|
"github.com/ebitengine/purego/internal/cgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var syscall15XABI0 = uintptr(cgo.Syscall15XABI0)
|
var syscall15XABI0 = uintptr(cgo.Syscall15XABI0)
|
||||||
|
|
||||||
// this is only here to make the assembly files happy :)
|
|
||||||
type syscall15Args struct {
|
|
||||||
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr
|
|
||||||
f1, f2, f3, f4, f5, f6, f7, f8 uintptr
|
|
||||||
r1, r2, err uintptr
|
|
||||||
arm64_r8 uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||||
return cgo.Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
|
return cgo.Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCallback(_ interface{}) uintptr {
|
func NewCallback(_ any) uintptr {
|
||||||
panic("purego: NewCallback on Linux is only supported on amd64/arm64")
|
panic("purego: NewCallback on Linux is only supported on amd64/arm64/loong64")
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-17
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||||
|
|
||||||
//go:build darwin || freebsd || (linux && (amd64 || arm64))
|
//go:build darwin || freebsd || (linux && (amd64 || arm64 || loong64)) || netbsd
|
||||||
|
|
||||||
package purego
|
package purego
|
||||||
|
|
||||||
@@ -14,20 +14,17 @@ import (
|
|||||||
|
|
||||||
var syscall15XABI0 uintptr
|
var syscall15XABI0 uintptr
|
||||||
|
|
||||||
type syscall15Args struct {
|
|
||||||
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr
|
|
||||||
f1, f2, f3, f4, f5, f6, f7, f8 uintptr
|
|
||||||
arm64_r8 uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:nosplit
|
|
||||||
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||||
args := syscall15Args{
|
args := thePool.Get().(*syscall15Args)
|
||||||
|
defer thePool.Put(args)
|
||||||
|
|
||||||
|
*args = syscall15Args{
|
||||||
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
|
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
|
||||||
a1, a2, a3, a4, a5, a6, a7, a8,
|
a1, a2, a3, a4, a5, a6, a7, a8,
|
||||||
0,
|
0,
|
||||||
}
|
}
|
||||||
runtime_cgocall(syscall15XABI0, unsafe.Pointer(&args))
|
|
||||||
|
runtime_cgocall(syscall15XABI0, unsafe.Pointer(args))
|
||||||
return args.a1, args.a2, 0
|
return args.a1, args.a2, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +34,17 @@ func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a
|
|||||||
// of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated
|
// of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated
|
||||||
// for these callbacks is never released. At least 2000 callbacks can always be created. Although this function
|
// for these callbacks is never released. At least 2000 callbacks can always be created. Although this function
|
||||||
// provides similar functionality to windows.NewCallback it is distinct.
|
// provides similar functionality to windows.NewCallback it is distinct.
|
||||||
func NewCallback(fn interface{}) uintptr {
|
func NewCallback(fn any) uintptr {
|
||||||
|
ty := reflect.TypeOf(fn)
|
||||||
|
for i := 0; i < ty.NumIn(); i++ {
|
||||||
|
in := ty.In(i)
|
||||||
|
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i != 0 {
|
||||||
|
panic("purego: CDecl must be the first argument")
|
||||||
|
}
|
||||||
|
}
|
||||||
return compileCallback(fn)
|
return compileCallback(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +74,7 @@ type callbackArgs struct {
|
|||||||
result uintptr
|
result uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileCallback(fn interface{}) uintptr {
|
func compileCallback(fn any) uintptr {
|
||||||
val := reflect.ValueOf(fn)
|
val := reflect.ValueOf(fn)
|
||||||
if val.Kind() != reflect.Func {
|
if val.Kind() != reflect.Func {
|
||||||
panic("purego: the type must be a function but was not")
|
panic("purego: the type must be a function but was not")
|
||||||
@@ -79,7 +86,12 @@ func compileCallback(fn interface{}) uintptr {
|
|||||||
for i := 0; i < ty.NumIn(); i++ {
|
for i := 0; i < ty.NumIn(); i++ {
|
||||||
in := ty.In(i)
|
in := ty.In(i)
|
||||||
switch in.Kind() {
|
switch in.Kind() {
|
||||||
case reflect.Struct, reflect.Interface, reflect.Func, reflect.Slice,
|
case reflect.Struct:
|
||||||
|
if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case reflect.Interface, reflect.Func, reflect.Slice,
|
||||||
reflect.Chan, reflect.Complex64, reflect.Complex128,
|
reflect.Chan, reflect.Complex64, reflect.Complex128,
|
||||||
reflect.String, reflect.Map, reflect.Invalid:
|
reflect.String, reflect.Map, reflect.Invalid:
|
||||||
panic("purego: unsupported argument type: " + in.Kind().String())
|
panic("purego: unsupported argument type: " + in.Kind().String())
|
||||||
@@ -137,25 +149,30 @@ func callbackWrap(a *callbackArgs) {
|
|||||||
var intsN int // intsN represents the number of integer arguments processed
|
var intsN int // intsN represents the number of integer arguments processed
|
||||||
// stack points to the index into frame of the current stack element.
|
// stack points to the index into frame of the current stack element.
|
||||||
// The stack begins after the float and integer registers.
|
// The stack begins after the float and integer registers.
|
||||||
stack := numOfIntegerRegisters() + numOfFloats
|
stack := numOfIntegerRegisters() + numOfFloatRegisters
|
||||||
for i := range args {
|
for i := range args {
|
||||||
var pos int
|
var pos int
|
||||||
switch fnType.In(i).Kind() {
|
switch fnType.In(i).Kind() {
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
if floatsN >= numOfFloats {
|
if floatsN >= numOfFloatRegisters {
|
||||||
pos = stack
|
pos = stack
|
||||||
stack++
|
stack++
|
||||||
} else {
|
} else {
|
||||||
pos = floatsN
|
pos = floatsN
|
||||||
}
|
}
|
||||||
floatsN++
|
floatsN++
|
||||||
|
case reflect.Struct:
|
||||||
|
// This is the CDecl field
|
||||||
|
args[i] = reflect.Zero(fnType.In(i))
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
|
|
||||||
if intsN >= numOfIntegerRegisters() {
|
if intsN >= numOfIntegerRegisters() {
|
||||||
pos = stack
|
pos = stack
|
||||||
stack++
|
stack++
|
||||||
} else {
|
} else {
|
||||||
// the integers begin after the floats in frame
|
// the integers begin after the floats in frame
|
||||||
pos = intsN + numOfFloats
|
pos = intsN + numOfFloatRegisters
|
||||||
}
|
}
|
||||||
intsN++
|
intsN++
|
||||||
}
|
}
|
||||||
@@ -200,7 +217,7 @@ func callbackasmAddr(i int) uintptr {
|
|||||||
panic("purego: unsupported architecture")
|
panic("purego: unsupported architecture")
|
||||||
case "386", "amd64":
|
case "386", "amd64":
|
||||||
entrySize = 5
|
entrySize = 5
|
||||||
case "arm", "arm64":
|
case "arm", "arm64", "loong64":
|
||||||
// On ARM and ARM64, each entry is a MOV instruction
|
// On ARM and ARM64, each entry is a MOV instruction
|
||||||
// followed by a branch instruction
|
// followed by a branch instruction
|
||||||
entrySize = 8
|
entrySize = 8
|
||||||
|
|||||||
+18
-17
@@ -4,20 +4,12 @@
|
|||||||
package purego
|
package purego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"syscall"
|
"syscall"
|
||||||
_ "unsafe" // only for go:linkname
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var syscall15XABI0 uintptr
|
var syscall15XABI0 uintptr
|
||||||
|
|
||||||
type syscall15Args struct {
|
|
||||||
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr
|
|
||||||
f1, f2, f3, f4, f5, f6, f7, f8 uintptr
|
|
||||||
arm64_r8 uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||||
r1, r2, errno := syscall.Syscall15(fn, 15, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
|
r1, r2, errno := syscall.Syscall15(fn, 15, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
|
||||||
return r1, r2, uintptr(errno)
|
return r1, r2, uintptr(errno)
|
||||||
@@ -30,16 +22,25 @@ func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a
|
|||||||
// allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024
|
// allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024
|
||||||
// callbacks can always be created. Although this function is similiar to the darwin version it may act
|
// callbacks can always be created. Although this function is similiar to the darwin version it may act
|
||||||
// differently.
|
// differently.
|
||||||
func NewCallback(fn interface{}) uintptr {
|
func NewCallback(fn any) uintptr {
|
||||||
|
isCDecl := false
|
||||||
|
ty := reflect.TypeOf(fn)
|
||||||
|
for i := 0; i < ty.NumIn(); i++ {
|
||||||
|
in := ty.In(i)
|
||||||
|
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i != 0 {
|
||||||
|
panic("purego: CDecl must be the first argument")
|
||||||
|
}
|
||||||
|
isCDecl = true
|
||||||
|
}
|
||||||
|
if isCDecl {
|
||||||
|
return syscall.NewCallbackCDecl(fn)
|
||||||
|
}
|
||||||
return syscall.NewCallback(fn)
|
return syscall.NewCallback(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname openLibrary openLibrary
|
|
||||||
func openLibrary(name string) (uintptr, error) {
|
|
||||||
handle, err := windows.LoadLibrary(name)
|
|
||||||
return uintptr(handle), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||||
return windows.GetProcAddress(windows.Handle(handle), name)
|
return syscall.GetProcAddress(syscall.Handle(handle), name)
|
||||||
}
|
}
|
||||||
|
|||||||
+2002
-2002
File diff suppressed because it is too large
Load Diff
+4002
-4002
File diff suppressed because it is too large
Load Diff
+4014
File diff suppressed because it is too large
Load Diff
+1
@@ -0,0 +1 @@
|
|||||||
|
CommentPragmas: '^go:build'
|
||||||
@@ -7,3 +7,8 @@
|
|||||||
.vscode
|
.vscode
|
||||||
go.work
|
go.work
|
||||||
go.work.sum
|
go.work.sum
|
||||||
|
|
||||||
|
*.fxc
|
||||||
|
!dummy.fxc
|
||||||
|
*.metallib
|
||||||
|
!dummy.metallib
|
||||||
|
|||||||
+131
-2
@@ -24,7 +24,7 @@ Ebitengine (formerly known as Ebiten) is an open source game engine for the Go p
|
|||||||
* [iOS](https://ebitengine.org/en/documents/mobile.html)
|
* [iOS](https://ebitengine.org/en/documents/mobile.html)
|
||||||
* [WebAssembly](https://ebitengine.org/en/documents/webassembly.html)
|
* [WebAssembly](https://ebitengine.org/en/documents/webassembly.html)
|
||||||
* Nintendo Switch
|
* Nintendo Switch
|
||||||
* Xbox
|
* Xbox (Xbox support is limited and not available to everyone. Negotiations are currently underway to make it accessible to all.)
|
||||||
|
|
||||||
For installation on desktops, see [the installation instruction](https://ebitengine.org/en/documents/install.html).
|
For installation on desktops, see [the installation instruction](https://ebitengine.org/en/documents/install.html).
|
||||||
|
|
||||||
@@ -60,4 +60,133 @@ For installation on desktops, see [the installation instruction](https://ebiteng
|
|||||||
|
|
||||||
Ebitengine is licensed under Apache license version 2.0. See [LICENSE](LICENSE) file.
|
Ebitengine is licensed under Apache license version 2.0. See [LICENSE](LICENSE) file.
|
||||||
|
|
||||||
[The Ebitengine logo](https://ebitengine.org/images/logo.png) by Hajime Hoshi is licensed under [the Creative Commons Attribution-NoDerivatives 4.0](https://creativecommons.org/licenses/by-nd/4.0/).
|
[The Ebitengine logo](https://ebitengine.org/images/logo.png) by Hajime Hoshi is licensed under [the Creative Commons Attribution 4.0](https://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
|
### GLFW
|
||||||
|
|
||||||
|
https://github.com/glfw/glfw
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright (c) 2002-2006 Marcus Geelnard
|
||||||
|
|
||||||
|
Copyright (c) 2006-2019 Camilla Löwy
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would
|
||||||
|
be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not
|
||||||
|
be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Go
|
||||||
|
|
||||||
|
https://cs.opensource.google/go/go
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
```
|
||||||
|
|
||||||
|
### go-gl/gl
|
||||||
|
|
||||||
|
https://github.com/go-gl/gl
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Eric Woroshow
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
```
|
||||||
|
|
||||||
|
### go-gl/glfw
|
||||||
|
|
||||||
|
https://github.com/go-gl/glfw
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright (c) 2012 The glfw3-go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
```
|
||||||
|
|||||||
+68
-104
@@ -179,6 +179,33 @@ func (b BlendFactor) internalBlendFactor(source bool) graphicsdriver.BlendFactor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func internalBlendFactorToBlendFactor(blendFactor graphicsdriver.BlendFactor) BlendFactor {
|
||||||
|
switch blendFactor {
|
||||||
|
case graphicsdriver.BlendFactorZero:
|
||||||
|
return BlendFactorZero
|
||||||
|
case graphicsdriver.BlendFactorOne:
|
||||||
|
return BlendFactorOne
|
||||||
|
case graphicsdriver.BlendFactorSourceColor:
|
||||||
|
return BlendFactorSourceColor
|
||||||
|
case graphicsdriver.BlendFactorOneMinusSourceColor:
|
||||||
|
return BlendFactorOneMinusSourceColor
|
||||||
|
case graphicsdriver.BlendFactorSourceAlpha:
|
||||||
|
return BlendFactorSourceAlpha
|
||||||
|
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
|
||||||
|
return BlendFactorOneMinusSourceAlpha
|
||||||
|
case graphicsdriver.BlendFactorDestinationColor:
|
||||||
|
return BlendFactorDestinationColor
|
||||||
|
case graphicsdriver.BlendFactorOneMinusDestinationColor:
|
||||||
|
return BlendFactorOneMinusDestinationColor
|
||||||
|
case graphicsdriver.BlendFactorDestinationAlpha:
|
||||||
|
return BlendFactorDestinationAlpha
|
||||||
|
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
|
||||||
|
return BlendFactorOneMinusDestinationAlpha
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("ebiten: invalid blend factor: %d", blendFactor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BlendOperation is an operation for source and destination color values.
|
// BlendOperation is an operation for source and destination color values.
|
||||||
type BlendOperation byte
|
type BlendOperation byte
|
||||||
|
|
||||||
@@ -233,6 +260,34 @@ func (b BlendOperation) internalBlendOperation() graphicsdriver.BlendOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func internalBlendOperationToBlendOperation(blendOperation graphicsdriver.BlendOperation) BlendOperation {
|
||||||
|
switch blendOperation {
|
||||||
|
case graphicsdriver.BlendOperationAdd:
|
||||||
|
return BlendOperationAdd
|
||||||
|
case graphicsdriver.BlendOperationSubtract:
|
||||||
|
return BlendOperationSubtract
|
||||||
|
case graphicsdriver.BlendOperationReverseSubtract:
|
||||||
|
return BlendOperationReverseSubtract
|
||||||
|
case graphicsdriver.BlendOperationMin:
|
||||||
|
return BlendOperationMin
|
||||||
|
case graphicsdriver.BlendOperationMax:
|
||||||
|
return BlendOperationMax
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("ebiten: invalid blend operation: %d", blendOperation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func internalBlendToBlend(blend graphicsdriver.Blend) Blend {
|
||||||
|
return Blend{
|
||||||
|
BlendFactorSourceRGB: internalBlendFactorToBlendFactor(blend.BlendFactorSourceRGB),
|
||||||
|
BlendFactorSourceAlpha: internalBlendFactorToBlendFactor(blend.BlendFactorSourceAlpha),
|
||||||
|
BlendFactorDestinationRGB: internalBlendFactorToBlendFactor(blend.BlendFactorDestinationRGB),
|
||||||
|
BlendFactorDestinationAlpha: internalBlendFactorToBlendFactor(blend.BlendFactorDestinationAlpha),
|
||||||
|
BlendOperationRGB: internalBlendOperationToBlendOperation(blend.BlendOperationRGB),
|
||||||
|
BlendOperationAlpha: internalBlendOperationToBlendOperation(blend.BlendOperationAlpha),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This name convention follows CSS compositing: https://drafts.fxtf.org/compositing-2/.
|
// This name convention follows CSS compositing: https://drafts.fxtf.org/compositing-2/.
|
||||||
//
|
//
|
||||||
// In the comments,
|
// In the comments,
|
||||||
@@ -242,169 +297,78 @@ var (
|
|||||||
//
|
//
|
||||||
// c_out = c_src + c_dst × (1 - α_src)
|
// c_out = c_src + c_dst × (1 - α_src)
|
||||||
// α_out = α_src + α_dst × (1 - α_src)
|
// α_out = α_src + α_dst × (1 - α_src)
|
||||||
BlendSourceOver = Blend{
|
BlendSourceOver = internalBlendToBlend(graphicsdriver.BlendSourceOver)
|
||||||
BlendFactorSourceRGB: BlendFactorOne,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorOne,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendClear is a preset Blend for Porter Duff's 'clear'.
|
// BlendClear is a preset Blend for Porter Duff's 'clear'.
|
||||||
//
|
//
|
||||||
// c_out = 0
|
// c_out = 0
|
||||||
// α_out = 0
|
// α_out = 0
|
||||||
BlendClear = Blend{
|
BlendClear = internalBlendToBlend(graphicsdriver.BlendClear)
|
||||||
BlendFactorSourceRGB: BlendFactorZero,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorZero,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorZero,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorZero,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendCopy is a preset Blend for Porter Duff's 'copy'.
|
// BlendCopy is a preset Blend for Porter Duff's 'copy'.
|
||||||
//
|
//
|
||||||
// c_out = c_src
|
// c_out = c_src
|
||||||
// α_out = α_src
|
// α_out = α_src
|
||||||
BlendCopy = Blend{
|
BlendCopy = internalBlendToBlend(graphicsdriver.BlendCopy)
|
||||||
BlendFactorSourceRGB: BlendFactorOne,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorOne,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorZero,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorZero,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendDestination is a preset Blend for Porter Duff's 'destination'.
|
// BlendDestination is a preset Blend for Porter Duff's 'destination'.
|
||||||
//
|
//
|
||||||
// c_out = c_dst
|
// c_out = c_dst
|
||||||
// α_out = α_dst
|
// α_out = α_dst
|
||||||
BlendDestination = Blend{
|
BlendDestination = internalBlendToBlend(graphicsdriver.BlendDestination)
|
||||||
BlendFactorSourceRGB: BlendFactorZero,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorZero,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorOne,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorOne,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendDestinationOver is a preset Blend for Porter Duff's 'destination-over'.
|
// BlendDestinationOver is a preset Blend for Porter Duff's 'destination-over'.
|
||||||
//
|
//
|
||||||
// c_out = c_src × (1 - α_dst) + c_dst
|
// c_out = c_src × (1 - α_dst) + c_dst
|
||||||
// α_out = α_src × (1 - α_dst) + α_dst
|
// α_out = α_src × (1 - α_dst) + α_dst
|
||||||
BlendDestinationOver = Blend{
|
BlendDestinationOver = internalBlendToBlend(graphicsdriver.BlendDestinationOver)
|
||||||
BlendFactorSourceRGB: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorOne,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorOne,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendSourceIn is a preset Blend for Porter Duff's 'source-in'.
|
// BlendSourceIn is a preset Blend for Porter Duff's 'source-in'.
|
||||||
//
|
//
|
||||||
// c_out = c_src × α_dst
|
// c_out = c_src × α_dst
|
||||||
// α_out = α_src × α_dst
|
// α_out = α_src × α_dst
|
||||||
BlendSourceIn = Blend{
|
BlendSourceIn = internalBlendToBlend(graphicsdriver.BlendSourceIn)
|
||||||
BlendFactorSourceRGB: BlendFactorDestinationAlpha,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorDestinationAlpha,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorZero,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorZero,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendDestinationIn is a preset Blend for Porter Duff's 'destination-in'.
|
// BlendDestinationIn is a preset Blend for Porter Duff's 'destination-in'.
|
||||||
//
|
//
|
||||||
// c_out = c_dst × α_src
|
// c_out = c_dst × α_src
|
||||||
// α_out = α_dst × α_src
|
// α_out = α_dst × α_src
|
||||||
BlendDestinationIn = Blend{
|
BlendDestinationIn = internalBlendToBlend(graphicsdriver.BlendDestinationIn)
|
||||||
BlendFactorSourceRGB: BlendFactorZero,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorZero,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorSourceAlpha,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorSourceAlpha,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendSourceOut is a preset Blend for Porter Duff's 'source-out'.
|
// BlendSourceOut is a preset Blend for Porter Duff's 'source-out'.
|
||||||
//
|
//
|
||||||
// c_out = c_src × (1 - α_dst)
|
// c_out = c_src × (1 - α_dst)
|
||||||
// α_out = α_src × (1 - α_dst)
|
// α_out = α_src × (1 - α_dst)
|
||||||
BlendSourceOut = Blend{
|
BlendSourceOut = internalBlendToBlend(graphicsdriver.BlendSourceOut)
|
||||||
BlendFactorSourceRGB: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorZero,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorZero,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendDestinationOut is a preset Blend for Porter Duff's 'destination-out'.
|
// BlendDestinationOut is a preset Blend for Porter Duff's 'destination-out'.
|
||||||
//
|
//
|
||||||
// c_out = c_dst × (1 - α_src)
|
// c_out = c_dst × (1 - α_src)
|
||||||
// α_out = α_dst × (1 - α_src)
|
// α_out = α_dst × (1 - α_src)
|
||||||
BlendDestinationOut = Blend{
|
BlendDestinationOut = internalBlendToBlend(graphicsdriver.BlendDestinationOut)
|
||||||
BlendFactorSourceRGB: BlendFactorZero,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorZero,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendSourceAtop is a preset Blend for Porter Duff's 'source-atop'.
|
// BlendSourceAtop is a preset Blend for Porter Duff's 'source-atop'.
|
||||||
//
|
//
|
||||||
// c_out = c_src × α_dst + c_dst × (1 - α_src)
|
// c_out = c_src × α_dst + c_dst × (1 - α_src)
|
||||||
// α_out = α_src × α_dst + α_dst × (1 - α_src)
|
// α_out = α_src × α_dst + α_dst × (1 - α_src)
|
||||||
BlendSourceAtop = Blend{
|
BlendSourceAtop = internalBlendToBlend(graphicsdriver.BlendSourceAtop)
|
||||||
BlendFactorSourceRGB: BlendFactorDestinationAlpha,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorDestinationAlpha,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendDestinationAtop is a preset Blend for Porter Duff's 'destination-atop'.
|
// BlendDestinationAtop is a preset Blend for Porter Duff's 'destination-atop'.
|
||||||
//
|
//
|
||||||
// c_out = c_src × (1 - α_dst) + c_dst × α_src
|
// c_out = c_src × (1 - α_dst) + c_dst × α_src
|
||||||
// α_out = α_src × (1 - α_dst) + α_dst × α_src
|
// α_out = α_src × (1 - α_dst) + α_dst × α_src
|
||||||
BlendDestinationAtop = Blend{
|
BlendDestinationAtop = internalBlendToBlend(graphicsdriver.BlendDestinationAtop)
|
||||||
BlendFactorSourceRGB: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorSourceAlpha,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorSourceAlpha,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendXor is a preset Blend for Porter Duff's 'xor'.
|
// BlendXor is a preset Blend for Porter Duff's 'xor'.
|
||||||
//
|
//
|
||||||
// c_out = c_src × (1 - α_dst) + c_dst × (1 - α_src)
|
// c_out = c_src × (1 - α_dst) + c_dst × (1 - α_src)
|
||||||
// α_out = α_src × (1 - α_dst) + α_dst × (1 - α_src)
|
// α_out = α_src × (1 - α_dst) + α_dst × (1 - α_src)
|
||||||
BlendXor = Blend{
|
BlendXor = internalBlendToBlend(graphicsdriver.BlendXor)
|
||||||
BlendFactorSourceRGB: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorOneMinusDestinationAlpha,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorOneMinusSourceAlpha,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlendLighter is a preset Blend for Porter Duff's 'lighter'.
|
// BlendLighter is a preset Blend for Porter Duff's 'lighter'.
|
||||||
// This is sum of source and destination (a.k.a. 'plus' or 'additive')
|
// This is sum of source and destination (a.k.a. 'plus' or 'additive')
|
||||||
//
|
//
|
||||||
// c_out = c_src + c_dst
|
// c_out = c_src + c_dst
|
||||||
// α_out = α_src + α_dst
|
// α_out = α_src + α_dst
|
||||||
BlendLighter = Blend{
|
BlendLighter = internalBlendToBlend(graphicsdriver.BlendLighter)
|
||||||
BlendFactorSourceRGB: BlendFactorOne,
|
|
||||||
BlendFactorSourceAlpha: BlendFactorOne,
|
|
||||||
BlendFactorDestinationRGB: BlendFactorOne,
|
|
||||||
BlendFactorDestinationAlpha: BlendFactorOne,
|
|
||||||
BlendOperationRGB: BlendOperationAdd,
|
|
||||||
BlendOperationAlpha: BlendOperationAdd,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
"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.
|
// Deprecated: as of v2.5. Use the colorm package instead.
|
||||||
const ColorMDim = affine.ColorMDim
|
const ColorMDim = affine.ColorMDim
|
||||||
|
|||||||
+23
-5
@@ -19,17 +19,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CursorModeType represents a render and coordinate mode of a mouse cursor.
|
// CursorModeType represents a render and coordinate mode of a mouse cursor.
|
||||||
type CursorModeType = ui.CursorMode
|
type CursorModeType int
|
||||||
|
|
||||||
// CursorModeTypes
|
// CursorModeTypes
|
||||||
const (
|
const (
|
||||||
CursorModeVisible CursorModeType = ui.CursorModeVisible
|
CursorModeVisible CursorModeType = CursorModeType(ui.CursorModeVisible)
|
||||||
CursorModeHidden CursorModeType = ui.CursorModeHidden
|
CursorModeHidden CursorModeType = CursorModeType(ui.CursorModeHidden)
|
||||||
CursorModeCaptured CursorModeType = ui.CursorModeCaptured
|
CursorModeCaptured CursorModeType = CursorModeType(ui.CursorModeCaptured)
|
||||||
)
|
)
|
||||||
|
|
||||||
// CursorShapeType represents a shape of a mouse cursor.
|
// CursorShapeType represents a shape of a mouse cursor.
|
||||||
type CursorShapeType = ui.CursorShape
|
type CursorShapeType int
|
||||||
|
|
||||||
// CursorShapeTypes
|
// CursorShapeTypes
|
||||||
const (
|
const (
|
||||||
@@ -44,3 +44,21 @@ const (
|
|||||||
CursorShapeMove CursorShapeType = CursorShapeType(ui.CursorShapeMove)
|
CursorShapeMove CursorShapeType = CursorShapeType(ui.CursorShapeMove)
|
||||||
CursorShapeNotAllowed CursorShapeType = CursorShapeType(ui.CursorShapeNotAllowed)
|
CursorShapeNotAllowed CursorShapeType = CursorShapeType(ui.CursorShapeNotAllowed)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CursorShape returns the current cursor shape.
|
||||||
|
//
|
||||||
|
// CursorShape returns CursorShapeDefault on mobiles.
|
||||||
|
//
|
||||||
|
// CursorShape is concurrent-safe.
|
||||||
|
func CursorShape() CursorShapeType {
|
||||||
|
return CursorShapeType(ui.Get().CursorShape())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCursorShape sets the cursor shape.
|
||||||
|
//
|
||||||
|
// If the platform doesn't implement the given shape, the default cursor shape is used.
|
||||||
|
//
|
||||||
|
// SetCursorShape is concurrent-safe.
|
||||||
|
func SetCursorShape(shape CursorShapeType) {
|
||||||
|
ui.Get().SetCursorShape(ui.CursorShape(shape))
|
||||||
|
}
|
||||||
|
|||||||
-5
@@ -93,11 +93,6 @@
|
|||||||
// The option "featurelevel" is valid only for DirectX 12.
|
// 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".
|
// 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
|
// # Build tags
|
||||||
//
|
//
|
||||||
// `ebitenginedebug` outputs a log of graphics commands. This is useful to know what happens in Ebitengine. In general, the
|
// `ebitenginedebug` outputs a log of graphics commands. This is useful to know what happens in Ebitengine. In general, the
|
||||||
|
|||||||
+26
-57
@@ -15,59 +15,27 @@
|
|||||||
package ebiten
|
package ebiten
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
"math"
|
"math"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/inputstate"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
const screenShaderSrc = `//kage:unit pixels
|
var screenFilterEnabled atomic.Bool
|
||||||
|
|
||||||
package main
|
func init() {
|
||||||
|
screenFilterEnabled.Store(true)
|
||||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
|
||||||
// Blend source colors in a square region, which size is 1/scale.
|
|
||||||
scale := imageDstSize()/imageSrc0Size()
|
|
||||||
pos := srcPos
|
|
||||||
p0 := pos - 1/2.0/scale
|
|
||||||
p1 := pos + 1/2.0/scale
|
|
||||||
|
|
||||||
// Texels must be in the source rect, so it is not necessary to check.
|
|
||||||
c0 := imageSrc0UnsafeAt(p0)
|
|
||||||
c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y))
|
|
||||||
c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y))
|
|
||||||
c3 := imageSrc0UnsafeAt(p1)
|
|
||||||
|
|
||||||
// p is the p1 value in one pixel assuming that the pixel's upper-left is (0, 0) and the lower-right is (1, 1).
|
|
||||||
rate := clamp(fract(p1)*scale, 0, 1)
|
|
||||||
return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
var screenFilterEnabled = int32(1)
|
|
||||||
|
|
||||||
func isScreenFilterEnabled() bool {
|
|
||||||
return atomic.LoadInt32(&screenFilterEnabled) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func setScreenFilterEnabled(enabled bool) {
|
|
||||||
v := int32(0)
|
|
||||||
if enabled {
|
|
||||||
v = 1
|
|
||||||
}
|
|
||||||
atomic.StoreInt32(&screenFilterEnabled, v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type gameForUI struct {
|
type gameForUI struct {
|
||||||
game Game
|
game Game
|
||||||
offscreen *Image
|
offscreen *Image
|
||||||
screen *Image
|
screen *Image
|
||||||
screenShader *Shader
|
imageDumper imageDumper
|
||||||
imageDumper imageDumper
|
transparent bool
|
||||||
transparent bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGameForUI(game Game, transparent bool) *gameForUI {
|
func newGameForUI(game Game, transparent bool) *gameForUI {
|
||||||
@@ -75,13 +43,6 @@ func newGameForUI(game Game, transparent bool) *gameForUI {
|
|||||||
game: game,
|
game: game,
|
||||||
transparent: transparent,
|
transparent: transparent,
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := NewShader([]byte(screenShaderSrc))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("ebiten: compiling the screen shader failed: %v", err))
|
|
||||||
}
|
|
||||||
g.screenShader = s
|
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,13 +90,12 @@ func (g *gameForUI) Layout(outsideWidth, outsideHeight float64) (float64, float6
|
|||||||
outsideHeight = 1
|
outsideHeight = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add a new Layout function taking float values (#2285).
|
|
||||||
sw, sh := g.game.Layout(int(outsideWidth), int(outsideHeight))
|
sw, sh := g.game.Layout(int(outsideWidth), int(outsideHeight))
|
||||||
return float64(sw), float64(sh)
|
return float64(sw), float64(sh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gameForUI) UpdateInputState(fn func(*ui.InputState)) {
|
func (g *gameForUI) UpdateInputState(fn func(*ui.InputState)) {
|
||||||
theInputState.update(fn)
|
inputstate.Get().Update(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gameForUI) Update() error {
|
func (g *gameForUI) Update() error {
|
||||||
@@ -166,21 +126,30 @@ func (g *gameForUI) DrawFinalScreen(scale, offsetX, offsetY float64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DefaultDrawFinalScreen(g.screen, g.offscreen, geoM)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDrawFinalScreen is the default implementation of [FinalScreenDrawer.DrawFinalScreen],
|
||||||
|
// used when a [Game] doesn't implement [FinalScreenDrawer].
|
||||||
|
//
|
||||||
|
// You can use DefaultDrawFinalScreen when you need the default implementation of [FinalScreenDrawer.DrawFinalScreen]
|
||||||
|
// in your implementation of [FinalScreenDrawer], for example.
|
||||||
|
func DefaultDrawFinalScreen(screen FinalScreen, offscreen *Image, geoM GeoM) {
|
||||||
|
scale := geoM.Element(0, 0)
|
||||||
switch {
|
switch {
|
||||||
case !isScreenFilterEnabled(), math.Floor(scale) == scale:
|
case !screenFilterEnabled.Load(), math.Floor(scale) == scale:
|
||||||
op := &DrawImageOptions{}
|
op := &DrawImageOptions{}
|
||||||
op.GeoM = geoM
|
op.GeoM = geoM
|
||||||
g.screen.DrawImage(g.offscreen, op)
|
screen.DrawImage(offscreen, op)
|
||||||
case scale < 1:
|
case scale < 1:
|
||||||
op := &DrawImageOptions{}
|
op := &DrawImageOptions{}
|
||||||
op.GeoM = geoM
|
op.GeoM = geoM
|
||||||
op.Filter = FilterLinear
|
op.Filter = FilterLinear
|
||||||
g.screen.DrawImage(g.offscreen, op)
|
screen.DrawImage(offscreen, op)
|
||||||
default:
|
default:
|
||||||
op := &DrawRectShaderOptions{}
|
op := &DrawImageOptions{}
|
||||||
op.Images[0] = g.offscreen
|
|
||||||
op.GeoM = geoM
|
op.GeoM = geoM
|
||||||
w, h := g.offscreen.Bounds().Dx(), g.offscreen.Bounds().Dy()
|
op.Filter = FilterPixelated
|
||||||
g.screen.DrawRectShader(w, h, g.screenShader, op)
|
screen.DrawImage(offscreen, op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -121,7 +121,7 @@ func (g *GeoM) Translate(tx, ty float64) {
|
|||||||
g.ty += ty
|
g.ty += ty
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate rotates the matrix by theta.
|
// Rotate rotates the matrix clockwise by theta.
|
||||||
// The unit is radian.
|
// The unit is radian.
|
||||||
func (g *GeoM) Rotate(theta float64) {
|
func (g *GeoM) Rotate(theta float64) {
|
||||||
if theta == 0 {
|
if theta == 0 {
|
||||||
|
|||||||
+26
@@ -15,6 +15,7 @@
|
|||||||
package ebiten
|
package ebiten
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/builtinshader"
|
"github.com/hajimehoshi/ebiten/v2/internal/builtinshader"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||||
)
|
)
|
||||||
@@ -28,6 +29,10 @@ const (
|
|||||||
|
|
||||||
// FilterLinear represents linear filter
|
// FilterLinear represents linear filter
|
||||||
FilterLinear Filter = Filter(builtinshader.FilterLinear)
|
FilterLinear Filter = Filter(builtinshader.FilterLinear)
|
||||||
|
|
||||||
|
// FilterPixelated represents a pixelated filter.
|
||||||
|
// FilterPixelated is similar to FilterNearest, but it preserves the pixelated appearance even when scaled to non-integer sizes.
|
||||||
|
FilterPixelated Filter = Filter(builtinshader.FilterPixelated)
|
||||||
)
|
)
|
||||||
|
|
||||||
// GraphicsLibrary represents graphics libraries supported by the engine.
|
// GraphicsLibrary represents graphics libraries supported by the engine.
|
||||||
@@ -66,9 +71,30 @@ var _ [GraphicsLibraryAuto]int = [0]int{}
|
|||||||
type DebugInfo struct {
|
type DebugInfo struct {
|
||||||
// GraphicsLibrary represents the graphics library currently in use.
|
// GraphicsLibrary represents the graphics library currently in use.
|
||||||
GraphicsLibrary GraphicsLibrary
|
GraphicsLibrary GraphicsLibrary
|
||||||
|
|
||||||
|
// TotalGPUImageMemoryUsageInBytes is the total image memory usage for GPU in bytes.
|
||||||
|
// TotalGPUImageMemoryUsageInBytes is approximately the total memory usage for GPU.
|
||||||
|
TotalGPUImageMemoryUsageInBytes int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadDebugInfo writes debug info (e.g. current graphics library) into a provided struct.
|
// ReadDebugInfo writes debug info (e.g. current graphics library) into a provided struct.
|
||||||
|
//
|
||||||
|
// ReadDebugInfo is concurrent-safe.
|
||||||
func ReadDebugInfo(d *DebugInfo) {
|
func ReadDebugInfo(d *DebugInfo) {
|
||||||
d.GraphicsLibrary = GraphicsLibrary(ui.Get().GraphicsLibrary())
|
d.GraphicsLibrary = GraphicsLibrary(ui.Get().GraphicsLibrary())
|
||||||
|
d.TotalGPUImageMemoryUsageInBytes = atlas.TotalGPUImageMemoryUsageInBytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
|||||||
+513
-75
@@ -18,6 +18,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"math"
|
||||||
|
"slices"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
@@ -25,6 +30,7 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
"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/shaderir"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||||
)
|
)
|
||||||
@@ -43,19 +49,56 @@ type Image struct {
|
|||||||
// tmpVertices must not be reused until ui.Image.Draw* is called.
|
// tmpVertices must not be reused until ui.Image.Draw* is called.
|
||||||
tmpVertices []float32
|
tmpVertices []float32
|
||||||
|
|
||||||
|
// tmpIndices must not be reused until ui.Image.Draw* is called.
|
||||||
|
tmpIndices []uint32
|
||||||
|
|
||||||
// tmpUniforms must not be reused until ui.Image.Draw* is called.
|
// tmpUniforms must not be reused until ui.Image.Draw* is called.
|
||||||
tmpUniforms []uint32
|
tmpUniforms []uint32
|
||||||
|
|
||||||
|
// subImageCache is a cache for sub-images.
|
||||||
|
// subImageCache is valid only when the image is not a sub-image.
|
||||||
|
subImageCache map[image.Rectangle]*Image
|
||||||
|
|
||||||
|
// subImageGCLastTick is the last tick when old sub images are removed from the cache.
|
||||||
|
subImageGCLastTick int64
|
||||||
|
|
||||||
|
// subImageCacheM is a mutex for subImageCache.
|
||||||
|
// subImageCache can be accessed from the image and its sub-images at the same time,
|
||||||
|
// so the map must be protected by a mutex.
|
||||||
|
subImageCacheM sync.Mutex
|
||||||
|
|
||||||
|
// atime is the last access time.
|
||||||
|
// atime needs to be an atomic value since a sub-image atime can be accessed from its original image.
|
||||||
|
atime atomic.Int64
|
||||||
|
|
||||||
|
// usageCallbacks are callbacks that are invoked when the image is used.
|
||||||
|
// usageCallbacks is valid only when the image is not a sub-image.
|
||||||
|
usageCallbacks map[int64]usageCallback
|
||||||
|
|
||||||
|
// inUsageCallbacks reports whether the image is in usageCallbacks.
|
||||||
|
inUsageCallbacks atomic.Bool
|
||||||
|
|
||||||
|
// usageCallbacksM is a mutex for usageCallbacks.
|
||||||
|
usageCallbacksM sync.Mutex
|
||||||
|
|
||||||
// Do not add a 'buffering' member that are resolved lazily.
|
// Do not add a 'buffering' member that are resolved lazily.
|
||||||
// This tends to forget resolving the buffer easily (#2362).
|
// This tends to forget resolving the buffer easily (#2362).
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type usageCallback struct {
|
||||||
|
fn func(image *Image)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Image) copyCheck() {
|
func (i *Image) copyCheck() {
|
||||||
if i.addr != i {
|
if i.addr != i {
|
||||||
panic("ebiten: illegal use of non-zero Image copied by value")
|
panic("ebiten: illegal use of non-zero Image copied by value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Image) updateAccessTime() {
|
||||||
|
i.atime.Store(Tick())
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the size of the image.
|
// Size returns the size of the image.
|
||||||
//
|
//
|
||||||
// Deprecated: as of v2.5. Use Bounds().Dx() and Bounds().Dy() or Bounds().Size() instead.
|
// Deprecated: as of v2.5. Use Bounds().Dx() and Bounds().Dy() or Bounds().Size() instead.
|
||||||
@@ -88,6 +131,10 @@ func (i *Image) Fill(clr color.Color) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
|
||||||
|
i.updateAccessTime()
|
||||||
|
|
||||||
var crf, cgf, cbf, caf float32
|
var crf, cgf, cbf, caf float32
|
||||||
cr, cg, cb, ca := clr.RGBA()
|
cr, cg, cb, ca := clr.RGBA()
|
||||||
crf = float32(cr) / 0xffff
|
crf = float32(cr) / 0xffff
|
||||||
@@ -97,11 +144,11 @@ func (i *Image) Fill(clr color.Color) {
|
|||||||
i.image.Fill(crf, cgf, cbf, caf, i.adjustedBounds())
|
i.image.Fill(crf, cgf, cbf, caf, i.adjustedBounds())
|
||||||
}
|
}
|
||||||
|
|
||||||
func canSkipMipmap(geom GeoM, filter builtinshader.Filter) bool {
|
func canSkipMipmap(det float32, filter builtinshader.Filter) bool {
|
||||||
if filter != builtinshader.FilterLinear {
|
if filter != builtinshader.FilterLinear {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return geom.det2x2() >= 0.999
|
return math.Abs(float64(det)) >= 0.999
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawImageOptions represents options for DrawImage.
|
// DrawImageOptions represents options for DrawImage.
|
||||||
@@ -139,6 +186,16 @@ type DrawImageOptions struct {
|
|||||||
// Filter is a type of texture filter.
|
// Filter is a type of texture filter.
|
||||||
// The default (zero) value is FilterNearest.
|
// The default (zero) value is FilterNearest.
|
||||||
Filter Filter
|
Filter Filter
|
||||||
|
|
||||||
|
// DisableMipmaps disables mipmaps.
|
||||||
|
// When Filter is FilterLinear and GeoM shrinks the image, mipmaps are used by default.
|
||||||
|
// Mipmap is useful to render a shrunk image with high quality.
|
||||||
|
// However, mipmaps can be expensive, especially on mobiles.
|
||||||
|
// When DisableMipmaps is true, mipmap is not used.
|
||||||
|
// When Filter is not FilterLinear, DisableMipmaps is ignored.
|
||||||
|
//
|
||||||
|
// The default (zero) value is false.
|
||||||
|
DisableMipmaps bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjustPosition converts the position in the *ebiten.Image coordinate to the *ui.Image coordinate.
|
// adjustPosition converts the position in the *ebiten.Image coordinate to the *ui.Image coordinate.
|
||||||
@@ -220,6 +277,12 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
img.invokeUsageCallbacks()
|
||||||
|
|
||||||
|
i.updateAccessTime()
|
||||||
|
img.updateAccessTime()
|
||||||
|
|
||||||
if options == nil {
|
if options == nil {
|
||||||
options = &DrawImageOptions{}
|
options = &DrawImageOptions{}
|
||||||
}
|
}
|
||||||
@@ -237,6 +300,10 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
|||||||
geoM.Translate(float64(offsetX), float64(offsetY))
|
geoM.Translate(float64(offsetX), float64(offsetY))
|
||||||
}
|
}
|
||||||
a, b, c, d, tx, ty := geoM.elements32()
|
a, b, c, d, tx, ty := geoM.elements32()
|
||||||
|
det := a*d - b*c
|
||||||
|
if det == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
bounds := img.Bounds()
|
bounds := img.Bounds()
|
||||||
sx0, sy0 := img.adjustPosition(bounds.Min.X, bounds.Min.Y)
|
sx0, sy0 := img.adjustPosition(bounds.Min.X, bounds.Min.Y)
|
||||||
@@ -244,10 +311,10 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
|||||||
colorm, cr, cg, cb, ca := colorMToScale(options.ColorM.affineColorM())
|
colorm, cr, cg, cb, ca := colorMToScale(options.ColorM.affineColorM())
|
||||||
cr, cg, cb, ca = options.ColorScale.apply(cr, cg, cb, ca)
|
cr, cg, cb, ca = options.ColorScale.apply(cr, cg, cb, ca)
|
||||||
vs := i.ensureTmpVertices(4 * graphics.VertexFloatCount)
|
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()
|
is := graphics.QuadIndices()
|
||||||
|
|
||||||
srcs := [graphics.ShaderImageCount]*ui.Image{img.image}
|
srcs := [graphics.ShaderSrcImageCount]*ui.Image{img.image}
|
||||||
|
|
||||||
useColorM := !colorm.IsIdentity()
|
useColorM := !colorm.IsIdentity()
|
||||||
shader := builtinShader(filter, builtinshader.AddressUnsafe, useColorM)
|
shader := builtinShader(filter, builtinshader.AddressUnsafe, useColorM)
|
||||||
@@ -262,7 +329,36 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, canSkipMipmap(geoM, filter), false)
|
dr := i.adjustedBounds()
|
||||||
|
hint := restorable.HintNone
|
||||||
|
if overwritesDstRegion(options.Blend, dr, geoM, sx0, sy0, sx1, sy1) {
|
||||||
|
hint = restorable.HintOverwriteDstRegion
|
||||||
|
}
|
||||||
|
|
||||||
|
skipMipmap := options.DisableMipmaps
|
||||||
|
if !skipMipmap {
|
||||||
|
skipMipmap = canSkipMipmap(det, filter)
|
||||||
|
}
|
||||||
|
i.image.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, skipMipmap, 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 {
|
||||||
|
// 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.
|
||||||
|
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.
|
// Vertex represents a vertex passed to DrawTriangles.
|
||||||
@@ -274,6 +370,10 @@ type Vertex struct {
|
|||||||
// SrcX and SrcY represents a point on a source image.
|
// SrcX and SrcY represents a point on a source image.
|
||||||
// Be careful that SrcX/SrcY coordinates are on the image's bounds.
|
// Be careful that SrcX/SrcY coordinates are on the image's bounds.
|
||||||
// This means that an upper-left point of a sub-image might not be (0, 0).
|
// This means that an upper-left point of a sub-image might not be (0, 0).
|
||||||
|
//
|
||||||
|
// Before passing vertices to a Kage shader, SrcX/SrcY are converted to texture coordinates of the first image,
|
||||||
|
// which is DrawRectShaderOptions.Image[0] or DrawTrianglesShaderOptions.Images[0].
|
||||||
|
// If the image is nil, SrcX/SrcY are not converted and used as-is.
|
||||||
SrcX float32
|
SrcX float32
|
||||||
SrcY float32
|
SrcY float32
|
||||||
|
|
||||||
@@ -291,8 +391,20 @@ type Vertex struct {
|
|||||||
ColorG float32
|
ColorG float32
|
||||||
ColorB float32
|
ColorB float32
|
||||||
ColorA 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.
|
||||||
|
//
|
||||||
|
// These values are valid only when DrawTrianglesShader is used.
|
||||||
|
// In other cases, these values are ignored.
|
||||||
|
Custom0 float32
|
||||||
|
Custom1 float32
|
||||||
|
Custom2 float32
|
||||||
|
Custom3 float32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ [0]byte = [unsafe.Sizeof(Vertex{}) - unsafe.Sizeof(float32(0))*graphics.VertexFloatCount]byte{}
|
||||||
|
|
||||||
// Address represents a sampler address mode.
|
// Address represents a sampler address mode.
|
||||||
type Address int
|
type Address int
|
||||||
|
|
||||||
@@ -308,19 +420,46 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// FillRule is the rule whether an overlapped region is rendered with DrawTriangles(Shader).
|
// FillRule is the rule whether an overlapped region is rendered with DrawTriangles(Shader).
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9.
|
||||||
type FillRule int
|
type FillRule int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FillRuleFillAll indicates all the triangles are rendered regardless of overlaps.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9.
|
||||||
|
FillRuleFillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll)
|
||||||
|
|
||||||
|
// FillRuleNonZero means that triangles are rendered based on the non-zero rule.
|
||||||
|
// If and only if the number of overlaps is not 0, the region is rendered.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9.
|
||||||
|
FillRuleNonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero)
|
||||||
|
|
||||||
|
// FillRuleEvenOdd means that triangles are rendered based on the even-odd rule.
|
||||||
|
// If and only if the number of overlaps is odd, the region is rendered.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9.
|
||||||
|
FillRuleEvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd)
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// FillAll indicates all the triangles are rendered regardless of overlaps.
|
// FillAll indicates all the triangles are rendered regardless of overlaps.
|
||||||
FillAll FillRule = FillRule(graphicsdriver.FillAll)
|
//
|
||||||
|
// Deprecated: as of v2.8. Use FillRuleFillAll instead.
|
||||||
|
FillAll = FillRuleFillAll
|
||||||
|
|
||||||
// NonZero means that triangles are rendered based on the non-zero rule.
|
// NonZero means that triangles are rendered based on the non-zero rule.
|
||||||
// If and only if the number of overlaps is not 0, the region is rendered.
|
// If and only if the number of overlaps is not 0, the region is rendered.
|
||||||
NonZero FillRule = FillRule(graphicsdriver.NonZero)
|
//
|
||||||
|
// Deprecated: as of v2.8. Use FillRuleNonZero instead.
|
||||||
|
NonZero = FillRuleNonZero
|
||||||
|
|
||||||
// EvenOdd means that triangles are rendered based on the even-odd rule.
|
// EvenOdd means that triangles are rendered based on the even-odd rule.
|
||||||
// If and only if the number of overlaps is odd, the region is rendered.
|
// If and only if the number of overlaps is odd, the region is rendered.
|
||||||
EvenOdd FillRule = FillRule(graphicsdriver.EvenOdd)
|
//
|
||||||
|
// Deprecated: as of v2.8. Use FillRuleEvenOdd instead.
|
||||||
|
EvenOdd = FillRuleEvenOdd
|
||||||
)
|
)
|
||||||
|
|
||||||
// ColorScaleMode is the mode of color scales in vertices.
|
// ColorScaleMode is the mode of color scales in vertices.
|
||||||
@@ -371,11 +510,13 @@ type DrawTrianglesOptions struct {
|
|||||||
|
|
||||||
// FillRule indicates the rule how an overlapped region is rendered.
|
// FillRule indicates the rule how an overlapped region is rendered.
|
||||||
//
|
//
|
||||||
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon.
|
// The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
|
||||||
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||||
// See examples/vector for actual usages.
|
// See examples/vector for actual usages.
|
||||||
//
|
//
|
||||||
// The default (zero) value is FillAll.
|
// The default (zero) value is FillRuleFillAll.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9. Use [github.com/hajimehoshi/ebiten/v2/vector.FillPath] instead.
|
||||||
FillRule FillRule
|
FillRule FillRule
|
||||||
|
|
||||||
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
||||||
@@ -384,8 +525,20 @@ type DrawTrianglesOptions struct {
|
|||||||
// AntiAlias increases internal draw calls and might affect performance.
|
// AntiAlias increases internal draw calls and might affect performance.
|
||||||
// Use the build tag `ebitenginedebug` to check the number of draw calls if you care.
|
// Use the build tag `ebitenginedebug` to check the number of draw calls if you care.
|
||||||
//
|
//
|
||||||
// The default (zero) value is false.
|
// The default (zero) value is false.//
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9. Use [github.com/hajimehoshi/ebiten/v2/vector.FillPath] instead.
|
||||||
AntiAlias bool
|
AntiAlias bool
|
||||||
|
|
||||||
|
// DisableMipmaps disables mipmaps.
|
||||||
|
// When Filter is FilterLinear and GeoM shrinks the image, mipmaps are used by default.
|
||||||
|
// Mipmap is useful to render a shrunk image with high quality.
|
||||||
|
// However, mipmaps can be expensive, especially on mobiles.
|
||||||
|
// When DisableMipmaps is true, mipmap is not used.
|
||||||
|
// When Filter is not FilterLinear, DisableMipmaps is ignored.
|
||||||
|
//
|
||||||
|
// The default (zero) value is false.
|
||||||
|
DisableMipmaps bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxIndicesCount is the maximum number of indices for DrawTriangles and DrawTrianglesShader.
|
// MaxIndicesCount is the maximum number of indices for DrawTriangles and DrawTrianglesShader.
|
||||||
@@ -428,6 +581,36 @@ const MaxVertexCount = graphicscommand.MaxVertexCount
|
|||||||
//
|
//
|
||||||
// When the image i is disposed, DrawTriangles does nothing.
|
// When the image i is disposed, DrawTriangles does nothing.
|
||||||
func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions) {
|
func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions) {
|
||||||
|
is := i.ensureTmpIndices(len(indices))
|
||||||
|
for i := range is {
|
||||||
|
is[i] = uint32(indices[i])
|
||||||
|
}
|
||||||
|
i.DrawTriangles32(vertices, is, img, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawTriangles32 draws triangles with the specified vertices and their indices.
|
||||||
|
// DrawTriangles32 is the version of DrawTriangles with uint32 indices.
|
||||||
|
//
|
||||||
|
// img is used as a source image. img cannot be nil.
|
||||||
|
// If you want to draw triangles with a solid color, use a small white image
|
||||||
|
// and adjust the color elements in the vertices. For an actual implementation,
|
||||||
|
// see the example 'vector'.
|
||||||
|
//
|
||||||
|
// Vertex contains color values, which are interpreted as straight-alpha colors by default.
|
||||||
|
// This depends on the option's ColorScaleMode.
|
||||||
|
//
|
||||||
|
// If len(vertices) is more than MaxVertexCount, the exceeding part is ignored.
|
||||||
|
//
|
||||||
|
// If len(indices) is not multiple of 3, DrawTriangles32 panics.
|
||||||
|
//
|
||||||
|
// If a value in indices is out of range of vertices, or not less than MaxVertexCount, DrawTriangles32 panics.
|
||||||
|
//
|
||||||
|
// The rule in which DrawTriangles32 works effectively is same as DrawImage's.
|
||||||
|
//
|
||||||
|
// When the given image is disposed, DrawTriangles32 panics.
|
||||||
|
//
|
||||||
|
// When the image i is disposed, DrawTriangles32 does nothing.
|
||||||
|
func (i *Image) DrawTriangles32(vertices []Vertex, indices []uint32, img *Image, options *DrawTrianglesOptions) {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
|
||||||
if img != nil && img.isDisposed() {
|
if img != nil && img.isDisposed() {
|
||||||
@@ -437,6 +620,16 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(indices) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
img.invokeUsageCallbacks()
|
||||||
|
|
||||||
|
img.updateAccessTime()
|
||||||
|
i.updateAccessTime()
|
||||||
|
|
||||||
if len(vertices) > graphicscommand.MaxVertexCount {
|
if len(vertices) > graphicscommand.MaxVertexCount {
|
||||||
// The last part cannot be specified by indices. Just omit them.
|
// The last part cannot be specified by indices. Just omit them.
|
||||||
vertices = vertices[:graphicscommand.MaxVertexCount]
|
vertices = vertices[:graphicscommand.MaxVertexCount]
|
||||||
@@ -469,38 +662,40 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
|||||||
vs := i.ensureTmpVertices(len(vertices) * graphics.VertexFloatCount)
|
vs := i.ensureTmpVertices(len(vertices) * graphics.VertexFloatCount)
|
||||||
dst := i
|
dst := i
|
||||||
if options.ColorScaleMode == ColorScaleModeStraightAlpha {
|
if options.ColorScaleMode == ColorScaleModeStraightAlpha {
|
||||||
for i, v := range vertices {
|
// Avoid using `for i, v := range vertices` as adding `v` creates a copy from `vertices` unnecessarily on each loop (#3103).
|
||||||
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
|
for i := range vertices {
|
||||||
vs[i*graphics.VertexFloatCount] = dx
|
// Create a temporary slice to reduce boundary checks.
|
||||||
vs[i*graphics.VertexFloatCount+1] = dy
|
vs := vs[i*graphics.VertexFloatCount : i*graphics.VertexFloatCount+8]
|
||||||
sx, sy := img.adjustPositionF32(v.SrcX, v.SrcY)
|
dx, dy := dst.adjustPositionF32(vertices[i].DstX, vertices[i].DstY)
|
||||||
vs[i*graphics.VertexFloatCount+2] = sx
|
vs[0] = dx
|
||||||
vs[i*graphics.VertexFloatCount+3] = sy
|
vs[1] = dy
|
||||||
vs[i*graphics.VertexFloatCount+4] = v.ColorR * v.ColorA * cr
|
sx, sy := img.adjustPositionF32(vertices[i].SrcX, vertices[i].SrcY)
|
||||||
vs[i*graphics.VertexFloatCount+5] = v.ColorG * v.ColorA * cg
|
vs[2] = sx
|
||||||
vs[i*graphics.VertexFloatCount+6] = v.ColorB * v.ColorA * cb
|
vs[3] = sy
|
||||||
vs[i*graphics.VertexFloatCount+7] = v.ColorA * ca
|
vs[4] = vertices[i].ColorR * vertices[i].ColorA * cr
|
||||||
|
vs[5] = vertices[i].ColorG * vertices[i].ColorA * cg
|
||||||
|
vs[6] = vertices[i].ColorB * vertices[i].ColorA * cb
|
||||||
|
vs[7] = vertices[i].ColorA * ca
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i, v := range vertices {
|
// See comment above (#3103).
|
||||||
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
|
for i := range vertices {
|
||||||
vs[i*graphics.VertexFloatCount] = dx
|
// Create a temporary slice to reduce boundary checks.
|
||||||
vs[i*graphics.VertexFloatCount+1] = dy
|
vs := vs[i*graphics.VertexFloatCount : i*graphics.VertexFloatCount+8]
|
||||||
sx, sy := img.adjustPositionF32(v.SrcX, v.SrcY)
|
dx, dy := dst.adjustPositionF32(vertices[i].DstX, vertices[i].DstY)
|
||||||
vs[i*graphics.VertexFloatCount+2] = sx
|
vs[0] = dx
|
||||||
vs[i*graphics.VertexFloatCount+3] = sy
|
vs[1] = dy
|
||||||
vs[i*graphics.VertexFloatCount+4] = v.ColorR * cr
|
sx, sy := img.adjustPositionF32(vertices[i].SrcX, vertices[i].SrcY)
|
||||||
vs[i*graphics.VertexFloatCount+5] = v.ColorG * cg
|
vs[2] = sx
|
||||||
vs[i*graphics.VertexFloatCount+6] = v.ColorB * cb
|
vs[3] = sy
|
||||||
vs[i*graphics.VertexFloatCount+7] = v.ColorA * ca
|
vs[4] = vertices[i].ColorR * cr
|
||||||
|
vs[5] = vertices[i].ColorG * cg
|
||||||
|
vs[6] = vertices[i].ColorB * cb
|
||||||
|
vs[7] = vertices[i].ColorA * ca
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is := make([]uint32, len(indices))
|
|
||||||
for i := range is {
|
|
||||||
is[i] = uint32(indices[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
srcs := [graphics.ShaderImageCount]*ui.Image{img.image}
|
srcs := [graphics.ShaderSrcImageCount]*ui.Image{img.image}
|
||||||
|
|
||||||
useColorM := !colorm.IsIdentity()
|
useColorM := !colorm.IsIdentity()
|
||||||
shader := builtinShader(filter, address, useColorM)
|
shader := builtinShader(filter, address, useColorM)
|
||||||
@@ -515,7 +710,11 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), filter != builtinshader.FilterLinear, options.AntiAlias)
|
skipMipmap := options.DisableMipmaps
|
||||||
|
if !skipMipmap {
|
||||||
|
skipMipmap = filter != builtinshader.FilterLinear
|
||||||
|
}
|
||||||
|
i.image.DrawTriangles(srcs, vs, indices, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), skipMipmap, options.AntiAlias, restorable.HintNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
|
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
|
||||||
@@ -533,7 +732,7 @@ type DrawTrianglesShaderOptions struct {
|
|||||||
|
|
||||||
// Uniforms is a set of uniform variables for the shader.
|
// Uniforms is a set of uniform variables for the shader.
|
||||||
// The keys are the names of the uniform variables.
|
// The keys are the names of the uniform variables.
|
||||||
// The values must be a numeric type, or a slice or an array of a numeric type.
|
// The values must be a numeric/boolean type, or a slice or an array of a numeric/boolean type.
|
||||||
// If the uniform variable type is an array, a vector or a matrix,
|
// If the uniform variable type is an array, a vector or a matrix,
|
||||||
// you have to specify linearly flattened values as a slice or an array.
|
// you have to specify linearly flattened values as a slice or an array.
|
||||||
// For example, if the uniform variable type is [4]vec4, the length will be 16.
|
// For example, if the uniform variable type is [4]vec4, the length will be 16.
|
||||||
@@ -542,16 +741,19 @@ type DrawTrianglesShaderOptions struct {
|
|||||||
Uniforms map[string]any
|
Uniforms map[string]any
|
||||||
|
|
||||||
// Images is a set of the source images.
|
// Images is a set of the source images.
|
||||||
// All the images' sizes must be the same.
|
// In the texel mode, all the image sizes must be the same.
|
||||||
|
// The pixel mode allows images of different sizes.
|
||||||
Images [4]*Image
|
Images [4]*Image
|
||||||
|
|
||||||
// FillRule indicates the rule how an overlapped region is rendered.
|
// FillRule indicates the rule how an overlapped region is rendered.
|
||||||
//
|
//
|
||||||
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon.
|
// The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
|
||||||
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||||
// See examples/vector for actual usages.
|
// See examples/vector for actual usages.
|
||||||
//
|
//
|
||||||
// The default (zero) value is FillAll.
|
// The default (zero) value is FillRuleFillAll.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9. Use [github.com/hajimehoshi/ebiten/v2/vector.FillPath] instead.
|
||||||
FillRule FillRule
|
FillRule FillRule
|
||||||
|
|
||||||
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
||||||
@@ -561,11 +763,13 @@ type DrawTrianglesShaderOptions struct {
|
|||||||
// Use the build tag `ebitenginedebug` to check the number of draw calls if you care.
|
// Use the build tag `ebitenginedebug` to check the number of draw calls if you care.
|
||||||
//
|
//
|
||||||
// The default (zero) value is false.
|
// The default (zero) value is false.
|
||||||
|
//
|
||||||
|
// Deprecated: as of v2.9. Use [github.com/hajimehoshi/ebiten/v2/vector.FillPath] instead.
|
||||||
AntiAlias bool
|
AntiAlias bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the number of images.
|
// Check the number of images.
|
||||||
var _ [len(DrawTrianglesShaderOptions{}.Images) - graphics.ShaderImageCount]struct{} = [0]struct{}{}
|
var _ [len(DrawTrianglesShaderOptions{}.Images) - graphics.ShaderSrcImageCount]struct{} = [0]struct{}{}
|
||||||
|
|
||||||
// DrawTrianglesShader draws triangles with the specified vertices and their indices with the specified shader.
|
// DrawTrianglesShader draws triangles with the specified vertices and their indices with the specified shader.
|
||||||
//
|
//
|
||||||
@@ -592,6 +796,39 @@ var _ [len(DrawTrianglesShaderOptions{}.Images) - graphics.ShaderImageCount]stru
|
|||||||
//
|
//
|
||||||
// When the image i is disposed, DrawTrianglesShader does nothing.
|
// When the image i is disposed, DrawTrianglesShader does nothing.
|
||||||
func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) {
|
func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) {
|
||||||
|
is := i.ensureTmpIndices(len(indices))
|
||||||
|
for i := range is {
|
||||||
|
is[i] = uint32(indices[i])
|
||||||
|
}
|
||||||
|
i.DrawTrianglesShader32(vertices, is, shader, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawTrianglesShader32 draws triangles with the specified vertices and their indices with the specified shader.
|
||||||
|
// DrawTrianglesShader32 is the version of DrawTrianglesShader with uint32 indices.
|
||||||
|
//
|
||||||
|
// Vertex contains color values, which can be interpreted for any purpose by the shader.
|
||||||
|
//
|
||||||
|
// For the details about the shader, see https://ebitengine.org/en/documents/shader.html.
|
||||||
|
//
|
||||||
|
// If the shader unit is texels, one of the specified image is non-nil and its size is different from (width, height),
|
||||||
|
// DrawTrianglesShader32 panics.
|
||||||
|
// If one of the specified image is non-nil and is disposed, DrawTrianglesShader32 panics.
|
||||||
|
//
|
||||||
|
// If len(vertices) is more than MaxVertexCount, the exceeding part is ignored.
|
||||||
|
//
|
||||||
|
// If len(indices) is not multiple of 3, DrawTrianglesShader32 panics.
|
||||||
|
//
|
||||||
|
// If a value in indices is out of range of vertices, or not less than MaxVertexCount, DrawTrianglesShader32 panics.
|
||||||
|
//
|
||||||
|
// When a specified image is non-nil and is disposed, DrawTrianglesShader32 panics.
|
||||||
|
//
|
||||||
|
// If a specified uniform variable's length or type doesn't match with an expected one, DrawTrianglesShader32 panics.
|
||||||
|
//
|
||||||
|
// Even if a result is an invalid color as a premultiplied-alpha color, i.e. an alpha value exceeds other color values,
|
||||||
|
// the value is kept and is not clamped.
|
||||||
|
//
|
||||||
|
// When the image i is disposed, DrawTrianglesShader32 does nothing.
|
||||||
|
func (i *Image) DrawTrianglesShader32(vertices []Vertex, indices []uint32, shader *Shader, options *DrawTrianglesShaderOptions) {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
@@ -602,6 +839,30 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
|||||||
panic("ebiten: the given shader to DrawTrianglesShader must not be disposed")
|
panic("ebiten: the given shader to DrawTrianglesShader must not be disposed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(indices) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
if options != nil {
|
||||||
|
for _, img := range options.Images {
|
||||||
|
if img == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
img.invokeUsageCallbacks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if options != nil {
|
||||||
|
for _, img := range options.Images {
|
||||||
|
if img == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
img.updateAccessTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.updateAccessTime()
|
||||||
|
|
||||||
if len(vertices) > graphicscommand.MaxVertexCount {
|
if len(vertices) > graphicscommand.MaxVertexCount {
|
||||||
// The last part cannot be specified by indices. Just omit them.
|
// The last part cannot be specified by indices. Just omit them.
|
||||||
vertices = vertices[:graphicscommand.MaxVertexCount]
|
vertices = vertices[:graphicscommand.MaxVertexCount]
|
||||||
@@ -629,28 +890,30 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
|||||||
vs := i.ensureTmpVertices(len(vertices) * graphics.VertexFloatCount)
|
vs := i.ensureTmpVertices(len(vertices) * graphics.VertexFloatCount)
|
||||||
dst := i
|
dst := i
|
||||||
src := options.Images[0]
|
src := options.Images[0]
|
||||||
for i, v := range vertices {
|
// Avoid using `for i, v := range vertices` as adding `v` creates a copy from `vertices` unnecessarily on each loop (#3103).
|
||||||
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
|
for i := range vertices {
|
||||||
vs[i*graphics.VertexFloatCount] = dx
|
// Create a temporary slice to reduce boundary checks.
|
||||||
vs[i*graphics.VertexFloatCount+1] = dy
|
vs := vs[i*graphics.VertexFloatCount : i*graphics.VertexFloatCount+12]
|
||||||
sx, sy := v.SrcX, v.SrcY
|
dx, dy := dst.adjustPositionF32(vertices[i].DstX, vertices[i].DstY)
|
||||||
|
vs[0] = dx
|
||||||
|
vs[1] = dy
|
||||||
|
sx, sy := vertices[i].SrcX, vertices[i].SrcY
|
||||||
if src != nil {
|
if src != nil {
|
||||||
sx, sy = src.adjustPositionF32(sx, sy)
|
sx, sy = src.adjustPositionF32(sx, sy)
|
||||||
}
|
}
|
||||||
vs[i*graphics.VertexFloatCount+2] = sx
|
vs[2] = sx
|
||||||
vs[i*graphics.VertexFloatCount+3] = sy
|
vs[3] = sy
|
||||||
vs[i*graphics.VertexFloatCount+4] = v.ColorR
|
vs[4] = vertices[i].ColorR
|
||||||
vs[i*graphics.VertexFloatCount+5] = v.ColorG
|
vs[5] = vertices[i].ColorG
|
||||||
vs[i*graphics.VertexFloatCount+6] = v.ColorB
|
vs[6] = vertices[i].ColorB
|
||||||
vs[i*graphics.VertexFloatCount+7] = v.ColorA
|
vs[7] = vertices[i].ColorA
|
||||||
|
vs[8] = vertices[i].Custom0
|
||||||
|
vs[9] = vertices[i].Custom1
|
||||||
|
vs[10] = vertices[i].Custom2
|
||||||
|
vs[11] = vertices[i].Custom3
|
||||||
}
|
}
|
||||||
|
|
||||||
is := make([]uint32, len(indices))
|
var imgs [graphics.ShaderSrcImageCount]*ui.Image
|
||||||
for i := range is {
|
|
||||||
is[i] = uint32(indices[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
var imgs [graphics.ShaderImageCount]*ui.Image
|
|
||||||
var imgSize image.Point
|
var imgSize image.Point
|
||||||
for i, img := range options.Images {
|
for i, img := range options.Images {
|
||||||
if img == nil {
|
if img == nil {
|
||||||
@@ -672,7 +935,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
|||||||
imgs[i] = img.image
|
imgs[i] = img.image
|
||||||
}
|
}
|
||||||
|
|
||||||
var srcRegions [graphics.ShaderImageCount]image.Rectangle
|
var srcRegions [graphics.ShaderSrcImageCount]image.Rectangle
|
||||||
for i, img := range options.Images {
|
for i, img := range options.Images {
|
||||||
if img == nil {
|
if img == nil {
|
||||||
continue
|
continue
|
||||||
@@ -683,7 +946,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
|||||||
i.tmpUniforms = i.tmpUniforms[:0]
|
i.tmpUniforms = i.tmpUniforms[:0]
|
||||||
i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms)
|
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, indices, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), true, options.AntiAlias, restorable.HintNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawRectShaderOptions represents options for DrawRectShader.
|
// DrawRectShaderOptions represents options for DrawRectShader.
|
||||||
@@ -710,7 +973,7 @@ type DrawRectShaderOptions struct {
|
|||||||
|
|
||||||
// Uniforms is a set of uniform variables for the shader.
|
// Uniforms is a set of uniform variables for the shader.
|
||||||
// The keys are the names of the uniform variables.
|
// The keys are the names of the uniform variables.
|
||||||
// The values must be a numeric type, or a slice or an array of a numeric type.
|
// The values must be a numeric/boolean type, or a slice or an array of a numeric/boolean type.
|
||||||
// If the uniform variable type is an array, a vector or a matrix,
|
// If the uniform variable type is an array, a vector or a matrix,
|
||||||
// you have to specify linearly flattened values as a slice or an array.
|
// you have to specify linearly flattened values as a slice or an array.
|
||||||
// For example, if the uniform variable type is [4]vec4, the length will be 16.
|
// For example, if the uniform variable type is [4]vec4, the length will be 16.
|
||||||
@@ -724,7 +987,7 @@ type DrawRectShaderOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check the number of images.
|
// Check the number of images.
|
||||||
var _ [len(DrawRectShaderOptions{}.Images)]struct{} = [graphics.ShaderImageCount]struct{}{}
|
var _ [len(DrawRectShaderOptions{}.Images)]struct{} = [graphics.ShaderSrcImageCount]struct{}{}
|
||||||
|
|
||||||
// DrawRectShader draws a rectangle with the specified width and height with the specified shader.
|
// DrawRectShader draws a rectangle with the specified width and height with the specified shader.
|
||||||
//
|
//
|
||||||
@@ -760,6 +1023,26 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
|||||||
panic("ebiten: the given shader to DrawRectShader must not be disposed")
|
panic("ebiten: the given shader to DrawRectShader must not be disposed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options != nil {
|
||||||
|
for _, img := range options.Images {
|
||||||
|
if img == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
img.invokeUsageCallbacks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
|
||||||
|
if options != nil {
|
||||||
|
for _, img := range options.Images {
|
||||||
|
if img == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
img.updateAccessTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.updateAccessTime()
|
||||||
|
|
||||||
if options == nil {
|
if options == nil {
|
||||||
options = &DrawRectShaderOptions{}
|
options = &DrawRectShaderOptions{}
|
||||||
}
|
}
|
||||||
@@ -771,7 +1054,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
|||||||
blend = options.CompositeMode.blend().internalBlend()
|
blend = options.CompositeMode.blend().internalBlend()
|
||||||
}
|
}
|
||||||
|
|
||||||
var imgs [graphics.ShaderImageCount]*ui.Image
|
var imgs [graphics.ShaderSrcImageCount]*ui.Image
|
||||||
for i, img := range options.Images {
|
for i, img := range options.Images {
|
||||||
if img == nil {
|
if img == nil {
|
||||||
continue
|
continue
|
||||||
@@ -785,7 +1068,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
|||||||
imgs[i] = img.image
|
imgs[i] = img.image
|
||||||
}
|
}
|
||||||
|
|
||||||
var srcRegions [graphics.ShaderImageCount]image.Rectangle
|
var srcRegions [graphics.ShaderSrcImageCount]image.Rectangle
|
||||||
for i, img := range options.Images {
|
for i, img := range options.Images {
|
||||||
if img == nil {
|
if img == nil {
|
||||||
if shader.unit == shaderir.Pixels && i == 0 {
|
if shader.unit == shaderir.Pixels && i == 0 {
|
||||||
@@ -803,11 +1086,14 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
|||||||
geoM.Translate(float64(offsetX), float64(offsetY))
|
geoM.Translate(float64(offsetX), float64(offsetY))
|
||||||
}
|
}
|
||||||
a, b, c, d, tx, ty := geoM.elements32()
|
a, b, c, d, tx, ty := geoM.elements32()
|
||||||
|
if det := a*d - b*c; det == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
cr, cg, cb, ca := options.ColorScale.elements()
|
cr, cg, cb, ca := options.ColorScale.elements()
|
||||||
vs := i.ensureTmpVertices(4 * graphics.VertexFloatCount)
|
vs := i.ensureTmpVertices(4 * graphics.VertexFloatCount)
|
||||||
|
|
||||||
// Do not use srcRegions[0].Dx() and srcRegions[0].Dy() as these might be empty.
|
// 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), float32(srcRegions[0].Min.Y),
|
||||||
float32(srcRegions[0].Min.X+width), float32(srcRegions[0].Min.Y+height),
|
float32(srcRegions[0].Min.X+width), float32(srcRegions[0].Min.Y+height),
|
||||||
a, b, c, d, tx, ty, cr, cg, cb, ca)
|
a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||||
@@ -816,7 +1102,14 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
|||||||
i.tmpUniforms = i.tmpUniforms[:0]
|
i.tmpUniforms = i.tmpUniforms[:0]
|
||||||
i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms)
|
i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms)
|
||||||
|
|
||||||
i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, true, false)
|
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, dr, srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false, hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubImage returns an image representing the portion of the image p visible through r.
|
// SubImage returns an image representing the portion of the image p visible through r.
|
||||||
@@ -838,24 +1131,53 @@ func (i *Image) SubImage(r image.Rectangle) image.Image {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i.isSubImage() {
|
||||||
|
return i.original.SubImage(r.Intersect(i.Bounds()))
|
||||||
|
}
|
||||||
|
|
||||||
r = r.Intersect(i.Bounds())
|
r = r.Intersect(i.Bounds())
|
||||||
// Need to check Empty explicitly. See the standard image package implementations.
|
// Need to check Empty explicitly. See the standard image package implementations.
|
||||||
if r.Empty() {
|
if r.Empty() {
|
||||||
r = image.ZR
|
r = image.Rectangle{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var orig = i
|
i.subImageCacheM.Lock()
|
||||||
if i.isSubImage() {
|
defer i.subImageCacheM.Unlock()
|
||||||
orig = i.original
|
|
||||||
|
// The image might already be disposed in another goroutine.
|
||||||
|
// Recheck this.
|
||||||
|
if i.isDisposed() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if img, ok := i.subImageCache[r]; ok {
|
||||||
|
img.updateAccessTime()
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
if tick := Tick(); i.subImageGCLastTick < tick {
|
||||||
|
i.subImageGCLastTick = tick
|
||||||
|
|
||||||
|
for _, img := range i.subImageCache {
|
||||||
|
if img.atime.Load()+60 < tick {
|
||||||
|
delete(i.subImageCache, img.bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img := &Image{
|
img := &Image{
|
||||||
image: i.image,
|
image: i.image,
|
||||||
bounds: r,
|
bounds: r,
|
||||||
original: orig,
|
original: i,
|
||||||
}
|
}
|
||||||
img.addr = img
|
img.addr = img
|
||||||
|
|
||||||
|
if i.subImageCache == nil {
|
||||||
|
i.subImageCache = map[image.Rectangle]*Image{}
|
||||||
|
}
|
||||||
|
i.subImageCache[r] = img
|
||||||
|
img.updateAccessTime()
|
||||||
|
|
||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,6 +1228,8 @@ func (i *Image) ReadPixels(pixels []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
|
||||||
i.image.ReadPixels(pixels, i.adjustedBounds())
|
i.image.ReadPixels(pixels, i.adjustedBounds())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,6 +1274,8 @@ func (i *Image) at(x, y int) (r, g, b, a byte) {
|
|||||||
return 0, 0, 0, 0
|
return 0, 0, 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
|
||||||
x, y = i.adjustPosition(x, y)
|
x, y = i.adjustPosition(x, y)
|
||||||
var pix [4]byte
|
var pix [4]byte
|
||||||
i.image.ReadPixels(pix[:], image.Rect(x, y, x+1, y+1))
|
i.image.ReadPixels(pix[:], image.Rect(x, y, x+1, y+1))
|
||||||
@@ -960,15 +1286,24 @@ func (i *Image) at(x, y int) (r, g, b, a byte) {
|
|||||||
//
|
//
|
||||||
// Set implements the standard draw.Image's Set.
|
// Set implements the standard draw.Image's Set.
|
||||||
//
|
//
|
||||||
|
// If (x, y) is outside the image bounds, Set does nothing.
|
||||||
|
//
|
||||||
// Even if a result is an invalid color as a premultiplied-alpha color, i.e. an alpha value exceeds other color values,
|
// Even if a result is an invalid color as a premultiplied-alpha color, i.e. an alpha value exceeds other color values,
|
||||||
// the value is kept and is not clamped.
|
// the value is kept and is not clamped.
|
||||||
//
|
//
|
||||||
// If the image is disposed, Set does nothing.
|
// If the image is disposed, Set does nothing.
|
||||||
|
//
|
||||||
|
// For performance, it is recommended to use WritePixels instead of Set whenever possible.
|
||||||
func (i *Image) Set(x, y int, clr color.Color) {
|
func (i *Image) Set(x, y int, clr color.Color) {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
|
||||||
|
i.updateAccessTime()
|
||||||
|
|
||||||
if !image.Pt(x, y).In(i.Bounds()) {
|
if !image.Pt(x, y).In(i.Bounds()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1003,6 +1338,10 @@ func (i *Image) Dispose() {
|
|||||||
}
|
}
|
||||||
i.image.Deallocate()
|
i.image.Deallocate()
|
||||||
i.image = nil
|
i.image = nil
|
||||||
|
i.subImageCacheM.Lock()
|
||||||
|
i.subImageCache = nil
|
||||||
|
i.subImageCacheM.Unlock()
|
||||||
|
i.usageCallbacks = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deallocate clears the image and deallocates the internal state of the image.
|
// Deallocate clears the image and deallocates the internal state of the image.
|
||||||
@@ -1026,6 +1365,7 @@ func (i *Image) Deallocate() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
i.image.Deallocate()
|
i.image.Deallocate()
|
||||||
|
i.usageCallbacks = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WritePixels replaces the pixels of the image.
|
// WritePixels replaces the pixels of the image.
|
||||||
@@ -1048,6 +1388,8 @@ func (i *Image) WritePixels(pixels []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.invokeUsageCallbacks()
|
||||||
|
|
||||||
// Do not need to copy pixels here.
|
// Do not need to copy pixels here.
|
||||||
// * In internal/mipmap, pixels are copied when necessary.
|
// * In internal/mipmap, pixels are copied when necessary.
|
||||||
// * In internal/atlas, pixels are copied to make its paddings.
|
// * In internal/atlas, pixels are copied to make its paddings.
|
||||||
@@ -1110,7 +1452,7 @@ func NewImageWithOptions(bounds image.Rectangle, options *NewImageOptions) *Imag
|
|||||||
|
|
||||||
func newImage(bounds image.Rectangle, imageType atlas.ImageType) *Image {
|
func newImage(bounds image.Rectangle, imageType atlas.ImageType) *Image {
|
||||||
if isRunGameEnded() {
|
if isRunGameEnded() {
|
||||||
panic(fmt.Sprintf("ebiten: NewImage cannot be called after RunGame finishes"))
|
panic("ebiten: NewImage cannot be called after RunGame finishes")
|
||||||
}
|
}
|
||||||
|
|
||||||
width, height := bounds.Dx(), bounds.Dy()
|
width, height := bounds.Dx(), bounds.Dy()
|
||||||
@@ -1246,6 +1588,102 @@ func (i *Image) ensureTmpVertices(n int) []float32 {
|
|||||||
return i.tmpVertices[:n]
|
return i.tmpVertices[:n]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Image) ensureTmpIndices(n int) []uint32 {
|
||||||
|
if cap(i.tmpIndices) < n {
|
||||||
|
i.tmpIndices = make([]uint32, n)
|
||||||
|
}
|
||||||
|
return i.tmpIndices[:n]
|
||||||
|
}
|
||||||
|
|
||||||
// private implements FinalScreen.
|
// private implements FinalScreen.
|
||||||
func (*Image) private() {
|
func (*Image) private() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not use usage callbacks except for Ebitengine packages.
|
||||||
|
// There is no guarantee for compatibility of this function.
|
||||||
|
|
||||||
|
var currentCallbackToken atomic.Int64
|
||||||
|
|
||||||
|
//go:linkname originalImage
|
||||||
|
func originalImage(img *Image) *Image {
|
||||||
|
if img.isSubImage() {
|
||||||
|
return img.original
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname addUsageCallback
|
||||||
|
func addUsageCallback(img *Image, callback func(image *Image)) int64 {
|
||||||
|
return img.addUsageCallback(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) addUsageCallback(callback func(image *Image)) int64 {
|
||||||
|
if i.isSubImage() {
|
||||||
|
return i.original.addUsageCallback(callback)
|
||||||
|
}
|
||||||
|
token := currentCallbackToken.Add(1)
|
||||||
|
|
||||||
|
i.usageCallbacksM.Lock()
|
||||||
|
defer i.usageCallbacksM.Unlock()
|
||||||
|
|
||||||
|
if i.usageCallbacks == nil {
|
||||||
|
i.usageCallbacks = map[int64]usageCallback{}
|
||||||
|
}
|
||||||
|
i.usageCallbacks[token] = usageCallback{
|
||||||
|
fn: callback,
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname removeUsageCallback
|
||||||
|
func removeUsageCallback(img *Image, token int64) {
|
||||||
|
img.removeUsageCallback(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) removeUsageCallback(token int64) {
|
||||||
|
if i.isSubImage() {
|
||||||
|
i.original.removeUsageCallback(token)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
i.usageCallbacksM.Lock()
|
||||||
|
defer i.usageCallbacksM.Unlock()
|
||||||
|
delete(i.usageCallbacks, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
var theTmpUsageCallbackSlicePool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
slice := make([]usageCallback, 0, 16)
|
||||||
|
return &slice
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) invokeUsageCallbacks() {
|
||||||
|
if i.isSubImage() {
|
||||||
|
i.original.invokeUsageCallbacks()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not allow recursive calls.
|
||||||
|
if !i.inUsageCallbacks.CompareAndSwap(false, true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer i.inUsageCallbacks.Store(false)
|
||||||
|
|
||||||
|
tmpUsageCallbackSlice := theTmpUsageCallbackSlicePool.Get().(*[]usageCallback)
|
||||||
|
|
||||||
|
func() {
|
||||||
|
i.usageCallbacksM.Lock()
|
||||||
|
defer i.usageCallbacksM.Unlock()
|
||||||
|
for _, cb := range i.usageCallbacks {
|
||||||
|
*tmpUsageCallbackSlice = append(*tmpUsageCallbackSlice, cb)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, cb := range *tmpUsageCallbackSlice {
|
||||||
|
cb.fn(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
*tmpUsageCallbackSlice = slices.Delete(*tmpUsageCallbackSlice, 0, len(*tmpUsageCallbackSlice))
|
||||||
|
theTmpUsageCallbackSlicePool.Put(tmpUsageCallbackSlice)
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user