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