mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
thread: Rename mainthread -> thread and add struct Thread
This enables thread available not only for the main thread but also any threads. This is a preparation for iOS Metal, that runs drawing functions on a particular thread. Updates #737
This commit is contained in:
parent
9b82ec41de
commit
15a5896efd
@ -17,9 +17,11 @@ package driver
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/thread"
|
||||
)
|
||||
|
||||
type Graphics interface {
|
||||
SetThread(thread *thread.Thread)
|
||||
Begin()
|
||||
End()
|
||||
SetWindow(window uintptr)
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/ca"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/metal/mtl"
|
||||
"github.com/hajimehoshi/ebiten/internal/mainthread"
|
||||
"github.com/hajimehoshi/ebiten/internal/thread"
|
||||
)
|
||||
|
||||
// #cgo CFLAGS: -x objective-c -mmacosx-version-min=10.11
|
||||
@ -301,6 +301,8 @@ type Driver struct {
|
||||
|
||||
maxImageSize int
|
||||
|
||||
t *thread.Thread
|
||||
|
||||
pool unsafe.Pointer
|
||||
}
|
||||
|
||||
@ -310,8 +312,12 @@ func Get() *Driver {
|
||||
return &theDriver
|
||||
}
|
||||
|
||||
func (d *Driver) SetThread(thread *thread.Thread) {
|
||||
d.t = thread
|
||||
}
|
||||
|
||||
func (d *Driver) Begin() {
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
// NSAutoreleasePool is required to release drawable correctly (#847).
|
||||
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/Drawables.html
|
||||
d.pool = C.allocAutoreleasePool()
|
||||
@ -321,7 +327,7 @@ func (d *Driver) Begin() {
|
||||
|
||||
func (d *Driver) End() {
|
||||
d.flush(false, true)
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
d.screenDrawable = ca.MetalDrawable{}
|
||||
C.releaseAutoreleasePool(d.pool)
|
||||
d.pool = nil
|
||||
@ -330,7 +336,7 @@ func (d *Driver) End() {
|
||||
}
|
||||
|
||||
func (d *Driver) SetWindow(window uintptr) {
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
// Note that [NSApp mainWindow] returns nil when the window is borderless.
|
||||
// Then the window is needed to be given.
|
||||
d.window = window
|
||||
@ -339,7 +345,7 @@ func (d *Driver) SetWindow(window uintptr) {
|
||||
}
|
||||
|
||||
func (d *Driver) SetVertices(vertices []float32, indices []uint16) {
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
if d.vb != (mtl.Buffer{}) {
|
||||
d.vb.Release()
|
||||
}
|
||||
@ -357,7 +363,7 @@ func (d *Driver) Flush() {
|
||||
}
|
||||
|
||||
func (d *Driver) flush(wait bool, present bool) {
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
if d.cb == (mtl.CommandBuffer{}) {
|
||||
return nil
|
||||
}
|
||||
@ -378,7 +384,7 @@ func (d *Driver) flush(wait bool, present bool) {
|
||||
|
||||
func (d *Driver) checkSize(width, height int) {
|
||||
m := 0
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
if d.maxImageSize == 0 {
|
||||
d.maxImageSize = 4096
|
||||
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
@ -438,7 +444,7 @@ func (d *Driver) NewImage(width, height int) (driver.Image, error) {
|
||||
Usage: mtl.TextureUsageShaderRead,
|
||||
}
|
||||
var t mtl.Texture
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
t = d.device.MakeTexture(td)
|
||||
return nil
|
||||
})
|
||||
@ -451,7 +457,7 @@ func (d *Driver) NewImage(width, height int) (driver.Image, error) {
|
||||
}
|
||||
|
||||
func (d *Driver) NewScreenFramebufferImage(width, height int) (driver.Image, error) {
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
d.ml.SetDrawableSize(width, height)
|
||||
return nil
|
||||
})
|
||||
@ -464,7 +470,7 @@ func (d *Driver) NewScreenFramebufferImage(width, height int) (driver.Image, err
|
||||
}
|
||||
|
||||
func (d *Driver) Reset() error {
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := d.t.Run(func() error {
|
||||
if d.cq != (mtl.CommandQueue{}) {
|
||||
d.cq.Release()
|
||||
d.cq = mtl.CommandQueue{}
|
||||
@ -606,7 +612,7 @@ func (d *Driver) Reset() error {
|
||||
}
|
||||
|
||||
func (d *Driver) Draw(indexLen int, indexOffset int, mode graphics.CompositeMode, colorM *affine.ColorM, filter graphics.Filter, address graphics.Address) error {
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := d.t.Run(func() error {
|
||||
// NSView can be changed anytime (probably). Set this everyframe.
|
||||
setView(d.window, d.ml)
|
||||
|
||||
@ -690,16 +696,16 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode graphics.CompositeMode
|
||||
}
|
||||
|
||||
func (d *Driver) ResetSource() {
|
||||
mainthread.Run(func() error {
|
||||
d.t.Run(func() error {
|
||||
d.src = nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Driver) SetVsyncEnabled(enabled bool) {
|
||||
// TODO: Now SetVsyncEnabled is called only from the main thread, and mainthread.Run is not available since
|
||||
// TODO: Now SetVsyncEnabled is called only from the main thread, and d.t.Run is not available since
|
||||
// recursive function call via Run is forbidden.
|
||||
// Fix this to use mainthread.Run to avoid confusion.
|
||||
// Fix this to use d.t.Run to avoid confusion.
|
||||
d.ml.SetDisplaySyncEnabled(enabled)
|
||||
}
|
||||
|
||||
@ -732,7 +738,7 @@ func (i *Image) viewportSize() (int, int) {
|
||||
}
|
||||
|
||||
func (i *Image) Dispose() {
|
||||
mainthread.Run(func() error {
|
||||
i.driver.t.Run(func() error {
|
||||
if i.texture != (mtl.Texture{}) {
|
||||
i.texture.Release()
|
||||
i.texture = mtl.Texture{}
|
||||
@ -749,7 +755,7 @@ func (i *Image) IsInvalidated() bool {
|
||||
}
|
||||
|
||||
func (i *Image) syncTexture() {
|
||||
mainthread.Run(func() error {
|
||||
i.driver.t.Run(func() error {
|
||||
if i.driver.cb != (mtl.CommandBuffer{}) {
|
||||
panic("metal: command buffer must be empty at syncTexture: flush is not called yet?")
|
||||
}
|
||||
@ -769,7 +775,7 @@ func (i *Image) Pixels() ([]byte, error) {
|
||||
i.syncTexture()
|
||||
|
||||
b := make([]byte, 4*i.width*i.height)
|
||||
mainthread.Run(func() error {
|
||||
i.driver.t.Run(func() error {
|
||||
i.texture.GetBytes(&b[0], uintptr(4*i.width), mtl.Region{
|
||||
Size: mtl.Size{i.width, i.height, 1},
|
||||
}, 0)
|
||||
@ -779,14 +785,14 @@ func (i *Image) Pixels() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (i *Image) SetAsDestination() {
|
||||
mainthread.Run(func() error {
|
||||
i.driver.t.Run(func() error {
|
||||
i.driver.dst = i
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Image) SetAsSource() {
|
||||
mainthread.Run(func() error {
|
||||
i.driver.t.Run(func() error {
|
||||
i.driver.src = i
|
||||
return nil
|
||||
})
|
||||
@ -795,7 +801,7 @@ func (i *Image) SetAsSource() {
|
||||
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
||||
i.driver.flush(true, false)
|
||||
|
||||
mainthread.Run(func() error {
|
||||
i.driver.t.Run(func() error {
|
||||
i.texture.ReplaceRegion(mtl.Region{
|
||||
Origin: mtl.Origin{x, y, 0},
|
||||
Size: mtl.Size{width, height, 1},
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/thread"
|
||||
)
|
||||
|
||||
func convertOperation(op graphics.Operation) operation {
|
||||
@ -48,6 +49,9 @@ type context struct {
|
||||
lastViewportHeight int
|
||||
lastCompositeMode graphics.CompositeMode
|
||||
maxTextureSize int
|
||||
|
||||
t *thread.Thread
|
||||
|
||||
contextImpl
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ import (
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphicsdriver/opengl/gl"
|
||||
"github.com/hajimehoshi/ebiten/internal/mainthread"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -76,7 +75,7 @@ type contextImpl struct {
|
||||
}
|
||||
|
||||
func (c *context) reset() error {
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := c.t.Run(func() error {
|
||||
if c.init {
|
||||
return nil
|
||||
}
|
||||
@ -95,12 +94,12 @@ func (c *context) reset() error {
|
||||
c.lastViewportWidth = 0
|
||||
c.lastViewportHeight = 0
|
||||
c.lastCompositeMode = graphics.CompositeModeUnknown
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.Enable(gl.BLEND)
|
||||
return nil
|
||||
})
|
||||
c.blendFunc(graphics.CompositeModeSourceOver)
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
f := int32(0)
|
||||
gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f)
|
||||
c.screenFramebuffer = framebufferNative(f)
|
||||
@ -110,7 +109,7 @@ func (c *context) reset() error {
|
||||
}
|
||||
|
||||
func (c *context) blendFunc(mode graphics.CompositeMode) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
if c.lastCompositeMode == mode {
|
||||
return nil
|
||||
}
|
||||
@ -124,7 +123,7 @@ func (c *context) blendFunc(mode graphics.CompositeMode) {
|
||||
|
||||
func (c *context) newTexture(width, height int) (textureNative, error) {
|
||||
var texture textureNative
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := c.t.Run(func() error {
|
||||
var t uint32
|
||||
gl.GenTextures(1, &t)
|
||||
// TODO: Use gl.IsTexture
|
||||
@ -138,7 +137,7 @@ func (c *context) newTexture(width, height int) (textureNative, error) {
|
||||
return 0, err
|
||||
}
|
||||
c.bindTexture(texture)
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
@ -152,7 +151,7 @@ func (c *context) newTexture(width, height int) (textureNative, error) {
|
||||
}
|
||||
|
||||
func (c *context) bindFramebufferImpl(f framebufferNative) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f))
|
||||
return nil
|
||||
})
|
||||
@ -160,12 +159,12 @@ func (c *context) bindFramebufferImpl(f framebufferNative) {
|
||||
|
||||
func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) {
|
||||
var pixels []byte
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.Flush()
|
||||
return nil
|
||||
})
|
||||
c.bindFramebuffer(f.native)
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := c.t.Run(func() error {
|
||||
pixels = make([]byte, 4*width*height)
|
||||
gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels))
|
||||
return nil
|
||||
@ -176,14 +175,14 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte,
|
||||
}
|
||||
|
||||
func (c *context) bindTextureImpl(t textureNative) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.BindTexture(gl.TEXTURE_2D, uint32(t))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) deleteTexture(t textureNative) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
tt := uint32(t)
|
||||
if !gl.IsTexture(tt) {
|
||||
return nil
|
||||
@ -198,7 +197,7 @@ func (c *context) deleteTexture(t textureNative) {
|
||||
|
||||
func (c *context) isTexture(t textureNative) bool {
|
||||
r := false
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
r = gl.IsTexture(uint32(t))
|
||||
return nil
|
||||
})
|
||||
@ -207,7 +206,7 @@ func (c *context) isTexture(t textureNative) bool {
|
||||
|
||||
func (c *context) texSubImage2D(t textureNative, p []byte, x, y, width, height int) {
|
||||
c.bindTexture(t)
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(p))
|
||||
return nil
|
||||
})
|
||||
@ -216,7 +215,7 @@ func (c *context) texSubImage2D(t textureNative, p []byte, x, y, width, height i
|
||||
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
|
||||
var framebuffer framebufferNative
|
||||
var f uint32
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := c.t.Run(func() error {
|
||||
gl.GenFramebuffersEXT(1, &f)
|
||||
// TODO: Use gl.IsFramebuffer
|
||||
if f <= 0 {
|
||||
@ -227,7 +226,7 @@ func (c *context) newFramebuffer(texture textureNative) (framebufferNative, erro
|
||||
return 0, err
|
||||
}
|
||||
c.bindFramebuffer(framebufferNative(f))
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := c.t.Run(func() error {
|
||||
gl.FramebufferTexture2DEXT(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0)
|
||||
s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER)
|
||||
if s != gl.FRAMEBUFFER_COMPLETE {
|
||||
@ -248,14 +247,14 @@ func (c *context) newFramebuffer(texture textureNative) (framebufferNative, erro
|
||||
}
|
||||
|
||||
func (c *context) setViewportImpl(width, height int) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.Viewport(0, 0, int32(width), int32(height))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) deleteFramebuffer(f framebufferNative) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
ff := uint32(f)
|
||||
if !gl.IsFramebufferEXT(ff) {
|
||||
return nil
|
||||
@ -272,7 +271,7 @@ func (c *context) deleteFramebuffer(f framebufferNative) {
|
||||
|
||||
func (c *context) newShader(shaderType shaderType, source string) (shader, error) {
|
||||
var sh shader
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := c.t.Run(func() error {
|
||||
s := gl.CreateShader(uint32(shaderType))
|
||||
if s == 0 {
|
||||
return fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
|
||||
@ -302,7 +301,7 @@ func (c *context) newShader(shaderType shaderType, source string) (shader, error
|
||||
}
|
||||
|
||||
func (c *context) deleteShader(s shader) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.DeleteShader(uint32(s))
|
||||
return nil
|
||||
})
|
||||
@ -310,7 +309,7 @@ func (c *context) deleteShader(s shader) {
|
||||
|
||||
func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
|
||||
var pr program
|
||||
if err := mainthread.Run(func() error {
|
||||
if err := c.t.Run(func() error {
|
||||
p := gl.CreateProgram()
|
||||
if p == 0 {
|
||||
return errors.New("opengl: glCreateProgram failed")
|
||||
@ -341,14 +340,14 @@ func (c *context) newProgram(shaders []shader, attributes []string) (program, er
|
||||
}
|
||||
|
||||
func (c *context) useProgram(p program) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.UseProgram(uint32(p))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) deleteProgram(p program) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
if !gl.IsProgram(uint32(p)) {
|
||||
return nil
|
||||
}
|
||||
@ -368,7 +367,7 @@ func (c *context) getUniformLocationImpl(p program, location string) uniformLoca
|
||||
}
|
||||
|
||||
func (c *context) uniformInt(p program, location string, v int) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
l := int32(c.locationCache.GetUniformLocation(c, p, location))
|
||||
gl.Uniform1i(l, int32(v))
|
||||
return nil
|
||||
@ -376,7 +375,7 @@ func (c *context) uniformInt(p program, location string, v int) {
|
||||
}
|
||||
|
||||
func (c *context) uniformFloat(p program, location string, v float32) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
l := int32(c.locationCache.GetUniformLocation(c, p, location))
|
||||
gl.Uniform1f(l, v)
|
||||
return nil
|
||||
@ -384,7 +383,7 @@ func (c *context) uniformFloat(p program, location string, v float32) {
|
||||
}
|
||||
|
||||
func (c *context) uniformFloats(p program, location string, v []float32) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
l := int32(c.locationCache.GetUniformLocation(c, p, location))
|
||||
switch len(v) {
|
||||
case 2:
|
||||
@ -401,21 +400,21 @@ func (c *context) uniformFloats(p program, location string, v []float32) {
|
||||
}
|
||||
|
||||
func (c *context) vertexAttribPointer(p program, index int, size int, dataType dataType, stride int, offset int) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.VertexAttribPointer(uint32(index), int32(size), uint32(dataType), false, int32(stride), gl.PtrOffset(offset))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) enableVertexAttribArray(p program, index int) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.EnableVertexAttribArray(uint32(index))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) disableVertexAttribArray(p program, index int) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.DisableVertexAttribArray(uint32(index))
|
||||
return nil
|
||||
})
|
||||
@ -423,7 +422,7 @@ func (c *context) disableVertexAttribArray(p program, index int) {
|
||||
|
||||
func (c *context) newArrayBuffer(size int) buffer {
|
||||
var bf buffer
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
var b uint32
|
||||
gl.GenBuffers(1, &b)
|
||||
gl.BindBuffer(uint32(arrayBuffer), b)
|
||||
@ -436,7 +435,7 @@ func (c *context) newArrayBuffer(size int) buffer {
|
||||
|
||||
func (c *context) newElementArrayBuffer(size int) buffer {
|
||||
var bf buffer
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
var b uint32
|
||||
gl.GenBuffers(1, &b)
|
||||
gl.BindBuffer(uint32(elementArrayBuffer), b)
|
||||
@ -448,28 +447,28 @@ func (c *context) newElementArrayBuffer(size int) buffer {
|
||||
}
|
||||
|
||||
func (c *context) bindBuffer(bufferType bufferType, b buffer) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.BindBuffer(uint32(bufferType), uint32(b))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) arrayBufferSubData(data []float32) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.BufferSubData(uint32(arrayBuffer), 0, len(data)*4, gl.Ptr(data))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) elementArrayBufferSubData(data []uint16) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.BufferSubData(uint32(elementArrayBuffer), 0, len(data)*2, gl.Ptr(data))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) deleteBuffer(b buffer) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
bb := uint32(b)
|
||||
gl.DeleteBuffers(1, &bb)
|
||||
return nil
|
||||
@ -477,7 +476,7 @@ func (c *context) deleteBuffer(b buffer) {
|
||||
}
|
||||
|
||||
func (c *context) drawElements(len int, offsetInBytes int) {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, gl.PtrOffset(offsetInBytes))
|
||||
return nil
|
||||
})
|
||||
@ -485,7 +484,7 @@ func (c *context) drawElements(len int, offsetInBytes int) {
|
||||
|
||||
func (c *context) maxTextureSizeImpl() int {
|
||||
size := 0
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
s := int32(0)
|
||||
gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &s)
|
||||
size = int(s)
|
||||
@ -495,7 +494,7 @@ func (c *context) maxTextureSizeImpl() int {
|
||||
}
|
||||
|
||||
func (c *context) flush() {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = c.t.Run(func() error {
|
||||
gl.Flush()
|
||||
return nil
|
||||
})
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/thread"
|
||||
)
|
||||
|
||||
var theDriver Driver
|
||||
@ -33,6 +34,10 @@ type Driver struct {
|
||||
context context
|
||||
}
|
||||
|
||||
func (d *Driver) SetThread(thread *thread.Thread) {
|
||||
d.context.t = thread
|
||||
}
|
||||
|
||||
func (d *Driver) Begin() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
@ -12,30 +12,35 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mainthread
|
||||
package thread
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
// Thread represents an OS thread.
|
||||
type Thread struct {
|
||||
started int32
|
||||
funcs chan func()
|
||||
}
|
||||
|
||||
var (
|
||||
started = int32(0)
|
||||
funcs = make(chan func())
|
||||
)
|
||||
|
||||
// Loop starts the main-thread loop.
|
||||
// New creates a new thread.
|
||||
//
|
||||
// Loop must be called on the main thread.
|
||||
func Loop(ch <-chan error) error {
|
||||
atomic.StoreInt32(&started, 1)
|
||||
// It is assumed that the OS thread is fixed by runtime.LockOSThread when New is called.
|
||||
func New() *Thread {
|
||||
return &Thread{
|
||||
funcs: make(chan func()),
|
||||
}
|
||||
}
|
||||
|
||||
// Loop starts the thread loop.
|
||||
//
|
||||
// Loop must be called on the thread.
|
||||
func (t *Thread) Loop(ch <-chan error) error {
|
||||
atomic.StoreInt32(&t.started, 1)
|
||||
for {
|
||||
select {
|
||||
case f := <-funcs:
|
||||
case f := <-t.funcs:
|
||||
f()
|
||||
case err := <-ch:
|
||||
// ch returns a value not only when an error occur but also it is closed.
|
||||
@ -44,18 +49,17 @@ func Loop(ch <-chan error) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Run calls f on the main thread.
|
||||
// Run calls f on the thread.
|
||||
//
|
||||
// Do not call this from the main thread. This would block forever.
|
||||
func Run(f func() error) error {
|
||||
if atomic.LoadInt32(&started) == 0 {
|
||||
// TODO: This can reach from other goroutine before Loop is called (#809).
|
||||
// panic("mainthread: the mainthread loop is not started yet")
|
||||
// Do not call this from the same thread. This would block forever.
|
||||
func (t *Thread) Run(f func() error) error {
|
||||
if atomic.LoadInt32(&t.started) == 0 {
|
||||
panic("thread: the thread loop is not started yet")
|
||||
}
|
||||
|
||||
ch := make(chan struct{})
|
||||
var err error
|
||||
funcs <- func() {
|
||||
t.funcs <- func() {
|
||||
err = f()
|
||||
close(ch)
|
||||
}
|
@ -29,7 +29,7 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/internal/devicescale"
|
||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/internal/glfw"
|
||||
"github.com/hajimehoshi/ebiten/internal/mainthread"
|
||||
"github.com/hajimehoshi/ebiten/internal/thread"
|
||||
)
|
||||
|
||||
type UserInterface struct {
|
||||
@ -66,6 +66,7 @@ type UserInterface struct {
|
||||
graphics driver.Graphics
|
||||
input Input
|
||||
|
||||
t *thread.Thread
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
@ -172,7 +173,7 @@ func getCachedMonitor(wx, wy int) (*cachedMonitor, bool) {
|
||||
|
||||
func (u *UserInterface) mainThreadLoop(ch <-chan error) error {
|
||||
u.setRunning(true)
|
||||
if err := mainthread.Loop(ch); err != nil {
|
||||
if err := u.t.Loop(ch); err != nil {
|
||||
return err
|
||||
}
|
||||
u.setRunning(false)
|
||||
@ -277,7 +278,7 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
|
||||
|
||||
var v *glfw.VidMode
|
||||
s := 0.0
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
v = u.currentMonitor().GetVideoMode()
|
||||
s = u.glfwScale()
|
||||
return nil
|
||||
@ -289,7 +290,7 @@ func (u *UserInterface) SetScreenSize(width, height int) {
|
||||
if !u.isRunning() {
|
||||
panic("ui: Run is not called yet")
|
||||
}
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
// TODO: What if the window is maximized? (#320)
|
||||
u.setScreenSize(width, height, u.scale, u.isFullscreen(), u.vsync)
|
||||
return nil
|
||||
@ -300,7 +301,7 @@ func (u *UserInterface) SetScreenScale(scale float64) {
|
||||
if !u.isRunning() {
|
||||
panic("ui: Run is not called yet")
|
||||
}
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
// TODO: What if the window is maximized? (#320)
|
||||
u.setScreenSize(u.width, u.height, scale, u.isFullscreen(), u.vsync)
|
||||
return nil
|
||||
@ -312,7 +313,7 @@ func (u *UserInterface) ScreenScale() float64 {
|
||||
return 0
|
||||
}
|
||||
s := 0.0
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
s = u.scale
|
||||
return nil
|
||||
})
|
||||
@ -332,7 +333,7 @@ func (u *UserInterface) IsFullscreen() bool {
|
||||
return u.isInitFullscreen()
|
||||
}
|
||||
b := false
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
b = u.isFullscreen()
|
||||
return nil
|
||||
})
|
||||
@ -344,7 +345,7 @@ func (u *UserInterface) SetFullscreen(fullscreen bool) {
|
||||
u.setInitFullscreen(fullscreen)
|
||||
return
|
||||
}
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
u.setScreenSize(u.width, u.height, u.scale, fullscreen, u.vsync)
|
||||
return nil
|
||||
})
|
||||
@ -369,7 +370,7 @@ func (u *UserInterface) SetVsyncEnabled(enabled bool) {
|
||||
u.m.Unlock()
|
||||
return
|
||||
}
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
u.setScreenSize(u.width, u.height, u.scale, u.isFullscreen(), enabled)
|
||||
return nil
|
||||
})
|
||||
@ -386,7 +387,7 @@ func (u *UserInterface) SetWindowTitle(title string) {
|
||||
if !u.isRunning() {
|
||||
return
|
||||
}
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
u.window.SetTitle(title)
|
||||
return nil
|
||||
})
|
||||
@ -397,7 +398,7 @@ func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
|
||||
u.setInitIconImages(iconImages)
|
||||
return
|
||||
}
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
u.window.SetIcon(iconImages)
|
||||
return nil
|
||||
})
|
||||
@ -413,7 +414,7 @@ func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
|
||||
}
|
||||
// The window width can be bigger than the game screen width (#444).
|
||||
ox := 0.0
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
ox = (float64(u.windowWidth)*u.actualScreenScale() - float64(u.width)*u.actualScreenScale()) / 2
|
||||
return nil
|
||||
})
|
||||
@ -426,7 +427,7 @@ func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
|
||||
gs := 0.0
|
||||
vw := 0.0
|
||||
vh := 0.0
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
m := u.window.GetMonitor()
|
||||
d = devicescale.GetAt(m.GetPos())
|
||||
sx = float64(u.width) * u.actualScreenScale()
|
||||
@ -451,7 +452,7 @@ func (u *UserInterface) adjustPosition(x, y int) (int, int) {
|
||||
}
|
||||
ox, oy, _, _ := u.ScreenPadding()
|
||||
s := 0.0
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
s = u.actualScreenScale()
|
||||
return nil
|
||||
})
|
||||
@ -463,7 +464,7 @@ func (u *UserInterface) IsCursorVisible() bool {
|
||||
return u.isInitCursorVisible()
|
||||
}
|
||||
v := false
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
v = u.window.GetInputMode(glfw.CursorMode) == glfw.CursorNormal
|
||||
return nil
|
||||
})
|
||||
@ -475,7 +476,7 @@ func (u *UserInterface) SetCursorVisible(visible bool) {
|
||||
u.setInitCursorVisible(visible)
|
||||
return
|
||||
}
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
c := glfw.CursorNormal
|
||||
if !visible {
|
||||
c = glfw.CursorHidden
|
||||
@ -490,7 +491,7 @@ func (u *UserInterface) IsWindowDecorated() bool {
|
||||
return u.isInitWindowDecorated()
|
||||
}
|
||||
v := false
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
v = u.window.GetAttrib(glfw.Decorated) == glfw.True
|
||||
return nil
|
||||
})
|
||||
@ -508,7 +509,7 @@ func (u *UserInterface) SetWindowDecorated(decorated bool) {
|
||||
// TODO: Now SetAttrib doesn't exist on GLFW 3.2. Revisit later (#556).
|
||||
// If SetAttrib exists, the implementation would be:
|
||||
//
|
||||
// _ = mainthread.Run(func() error {
|
||||
// _ = u.t.Run(func() error {
|
||||
// v := glfw.False
|
||||
// if decorated {
|
||||
// v = glfw.True
|
||||
@ -523,7 +524,7 @@ func (u *UserInterface) IsWindowResizable() bool {
|
||||
return u.isInitWindowResizable()
|
||||
}
|
||||
v := false
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
v = u.window.GetAttrib(glfw.Resizable) == glfw.True
|
||||
return nil
|
||||
})
|
||||
@ -547,7 +548,7 @@ func (u *UserInterface) DeviceScaleFactor() float64 {
|
||||
return devicescale.GetAt(u.initMonitor.GetPos())
|
||||
}
|
||||
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
m := u.currentMonitor()
|
||||
f = devicescale.GetAt(m.GetPos())
|
||||
return nil
|
||||
@ -555,7 +556,15 @@ func (u *UserInterface) DeviceScaleFactor() float64 {
|
||||
return f
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Lock the main thread.
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func (u *UserInterface) Run(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) error {
|
||||
// Initialize the main thread first so the thread is available at u.run (#809).
|
||||
u.t = thread.New()
|
||||
|
||||
ch := make(chan error)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
@ -575,8 +584,9 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
|
||||
}
|
||||
|
||||
func (u *UserInterface) run(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) error {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
u.graphics = graphics
|
||||
u.graphics.SetThread(u.t)
|
||||
|
||||
if graphics.IsGL() {
|
||||
glfw.WindowHint(glfw.ContextVersionMajor, 2)
|
||||
@ -676,7 +686,7 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
||||
})
|
||||
|
||||
var w uintptr
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
w = u.nativeWindow()
|
||||
return nil
|
||||
})
|
||||
@ -722,7 +732,7 @@ func (u *UserInterface) updateSize(context driver.UIContext) {
|
||||
actualScale := 0.0
|
||||
sizeChanged := false
|
||||
// TODO: Is it possible to reduce 'runOnMainThread' calls?
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
actualScale = u.actualScreenScale()
|
||||
if u.lastActualScale != actualScale {
|
||||
u.forceSetScreenSize(u.width, u.height, u.scale, u.isFullscreen(), u.vsync)
|
||||
@ -744,7 +754,7 @@ func (u *UserInterface) updateSize(context driver.UIContext) {
|
||||
|
||||
func (u *UserInterface) update(context driver.UIContext) error {
|
||||
shouldClose := false
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
shouldClose = u.window.ShouldClose()
|
||||
return nil
|
||||
})
|
||||
@ -752,7 +762,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
||||
return driver.RegularTermination
|
||||
}
|
||||
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
if u.isInitFullscreen() {
|
||||
u.setScreenSize(u.width, u.height, u.scale, true, u.vsync)
|
||||
u.setInitFullscreen(false)
|
||||
@ -763,7 +773,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
||||
// This call is needed for initialization.
|
||||
u.updateSize(context)
|
||||
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
glfw.PollEvents()
|
||||
|
||||
u.input.update(u.window, u.getScale()*u.glfwScale())
|
||||
@ -789,7 +799,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
||||
}
|
||||
|
||||
// Update the screen size when the window is resizable.
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
w, h := u.reqWidth, u.reqHeight
|
||||
if w != 0 || h != 0 {
|
||||
u.setScreenSize(w, h, u.scale, u.isFullscreen(), u.vsync)
|
||||
@ -803,7 +813,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
||||
|
||||
func (u *UserInterface) loop(context driver.UIContext) error {
|
||||
defer func() {
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
glfw.Terminate()
|
||||
return nil
|
||||
})
|
||||
@ -817,7 +827,7 @@ func (u *UserInterface) loop(context driver.UIContext) error {
|
||||
vsync := u.vsync
|
||||
u.m.Unlock()
|
||||
|
||||
_ = mainthread.Run(func() error {
|
||||
_ = u.t.Run(func() error {
|
||||
if !vsync {
|
||||
u.swapBuffers()
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user