adopt github.com/faiface/mainthread package + frame prototype

This commit is contained in:
faiface 2017-01-20 17:45:19 +01:00
parent de21a40184
commit 7a7b2f2588
11 changed files with 141 additions and 148 deletions

View File

@ -1,7 +1,7 @@
package pixel
import (
"github.com/faiface/pixel/pixelgl"
"github.com/faiface/mainthread"
"github.com/go-gl/glfw/v3.2/glfw"
)
@ -23,7 +23,7 @@ func (w *Window) JustReleased(button Button) bool {
// MousePosition returns the current mouse position relative to the window.
func (w *Window) MousePosition() Vec {
var x, y, width, height float64
pixelgl.Do(func() {
mainthread.Call(func() {
x, y = w.window.GetCursorPos()
wi, hi := w.window.GetSize()
width, height = float64(wi), float64(hi)
@ -187,7 +187,7 @@ const (
)
func (w *Window) initInput() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
switch action {
case glfw.Press:
@ -220,7 +220,7 @@ func (w *Window) updateInput() {
w.currInp.scroll -= w.tempInp.scroll
// get events (usually calls callbacks, but callbacks can be called outside too)
pixelgl.Do(func() {
mainthread.Call(func() {
glfw.PollEvents()
})

View File

@ -1,7 +1,7 @@
package pixel
import (
"github.com/faiface/pixel/pixelgl"
"github.com/faiface/mainthread"
"github.com/go-gl/glfw/v3.2/glfw"
)
@ -12,7 +12,7 @@ type Monitor struct {
// PrimaryMonitor returns the main monitor (usually the one with the taskbar and stuff).
func PrimaryMonitor() *Monitor {
monitor := pixelgl.DoVal(func() interface{} {
monitor := mainthread.CallVal(func() interface{} {
return glfw.GetPrimaryMonitor()
}).(*glfw.Monitor)
return &Monitor{
@ -23,7 +23,7 @@ func PrimaryMonitor() *Monitor {
// Monitors returns a slice of all currently available monitors.
func Monitors() []*Monitor {
var monitors []*Monitor
pixelgl.Do(func() {
mainthread.Call(func() {
for _, monitor := range glfw.GetMonitors() {
monitors = append(monitors, &Monitor{monitor: monitor})
}
@ -33,7 +33,7 @@ func Monitors() []*Monitor {
// Name returns a human-readable name of a monitor.
func (m *Monitor) Name() string {
name := pixelgl.DoVal(func() interface{} {
name := mainthread.CallVal(func() interface{} {
return m.monitor.GetName()
}).(string)
return name
@ -42,7 +42,7 @@ func (m *Monitor) Name() string {
// PhysicalSize returns the size of the display area of a monitor in millimeters.
func (m *Monitor) PhysicalSize() (width, height float64) {
var wi, hi int
pixelgl.Do(func() {
mainthread.Call(func() {
wi, hi = m.monitor.GetPhysicalSize()
})
width = float64(wi)
@ -53,7 +53,7 @@ func (m *Monitor) PhysicalSize() (width, height float64) {
// Position returns the position of the upper-left corner of a monitor in screen coordinates.
func (m *Monitor) Position() (x, y float64) {
var xi, yi int
pixelgl.Do(func() {
mainthread.Call(func() {
xi, yi = m.monitor.GetPos()
})
x = float64(xi)
@ -63,7 +63,7 @@ func (m *Monitor) Position() (x, y float64) {
// Size returns the resolution of a monitor in pixels.
func (m *Monitor) Size() (width, height float64) {
mode := pixelgl.DoVal(func() interface{} {
mode := mainthread.CallVal(func() interface{} {
return m.monitor.GetVideoMode()
}).(*glfw.VidMode)
width = float64(mode.Width)
@ -73,7 +73,7 @@ func (m *Monitor) Size() (width, height float64) {
// BitDepth returns the number of bits per color of a monitor.
func (m *Monitor) BitDepth() (red, green, blue int) {
mode := pixelgl.DoVal(func() interface{} {
mode := mainthread.CallVal(func() interface{} {
return m.monitor.GetVideoMode()
}).(*glfw.VidMode)
red = mode.RedBits
@ -84,7 +84,7 @@ func (m *Monitor) BitDepth() (red, green, blue int) {
// RefreshRate returns the refresh frequency of a monitor in Hz (refreshes/second).
func (m *Monitor) RefreshRate() (rate float64) {
mode := pixelgl.DoVal(func() interface{} {
mode := mainthread.CallVal(func() interface{} {
return m.monitor.GetVideoMode()
}).(*glfw.VidMode)
rate = float64(mode.RefreshRate)

View File

@ -4,6 +4,7 @@ import (
"image"
"image/draw"
"github.com/faiface/mainthread"
"github.com/faiface/pixel/pixelgl"
)
@ -29,7 +30,7 @@ func NewPicture(img image.Image, smooth bool) *Picture {
}
var texture *pixelgl.Texture
pixelgl.Do(func() {
mainthread.Call(func() {
texture = pixelgl.NewTexture(
img.Bounds().Dx(),
img.Bounds().Dy(),

67
pixelgl/frame.go Normal file
View File

@ -0,0 +1,67 @@
package pixelgl
import (
"fmt"
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
)
type Frame struct {
fb binder
tex *Texture
width, height int
}
func NewFrame(width, height int, smooth bool) *Frame {
f := &Frame{
fb: binder{
restoreLoc: gl.FRAMEBUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindFramebuffer(gl.FRAMEBUFFER, obj)
},
},
width: width,
height: height,
}
gl.GenFramebuffers(1, &f.fb.obj)
fmt.Println(f.fb.obj)
f.tex = NewTexture(width, height, smooth, make([]uint8, width*height*4))
f.fb.bind()
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex.tex.obj, 0)
f.fb.restore()
runtime.SetFinalizer(f, (*Frame).delete)
return f
}
func (f *Frame) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteFramebuffers(1, &f.fb.obj)
})
}
func (f *Frame) Width() int {
return f.width
}
func (f *Frame) Height() int {
return f.height
}
func (f *Frame) Begin() {
f.fb.bind()
}
func (f *Frame) End() {
f.fb.restore()
}
func (f *Frame) Texture() *Texture {
return f.tex
}

View File

@ -2,6 +2,20 @@ package pixelgl
import "github.com/go-gl/gl/v3.3-core/gl"
// Init initializes OpenGL by loading the function pointers from the active OpenGL context.
// This function must be manually run inside the main thread (Do, DoErr, DoVal, etc.).
//
// It must be called under the presence of an active OpenGL context, e.g., always after calling
// window.MakeContextCurrent(). Also, always call this function when switching contexts.
func Init() {
err := gl.Init()
if err != nil {
panic(err)
}
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
}
// Clear clears the current framebuffer or window with the given color.
func Clear(r, g, b, a float32) {
gl.ClearColor(r, g, b, a)

View File

@ -4,6 +4,7 @@ import (
"fmt"
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
@ -110,7 +111,7 @@ func NewShader(vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader st
}
func (s *Shader) delete() {
DoNoBlock(func() {
mainthread.CallNonBlock(func() {
gl.DeleteProgram(s.program.obj)
})
}

View File

@ -3,6 +3,7 @@ package pixelgl
import (
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
@ -67,7 +68,7 @@ func NewTexture(width, height int, smooth bool, pixels []uint8) *Texture {
}
func (t *Texture) delete() {
DoNoBlock(func() {
mainthread.CallNonBlock(func() {
gl.DeleteTextures(1, &t.tex.obj)
})
}

View File

@ -1,102 +0,0 @@
package pixelgl
import (
"runtime"
"github.com/go-gl/gl/v3.3-core/gl"
)
// Due to the limitations of OpenGL and operating systems, all OpenGL related calls must be
// done from the main thread.
var (
callQueue = make(chan func(), 8)
respChan = make(chan interface{}, 4)
)
func init() {
runtime.LockOSThread()
}
// Run is essentialy the "main" function of the pixelgl package. Run this function from the
// main function (because that's guaranteed to run in the main thread).
//
// This function reserves the main thread for the OpenGL stuff and runs a supplied run function
// in a separate goroutine.
//
// Run returns when the provided run function finishes.
func Run(run func()) {
done := make(chan struct{})
go func() {
run()
close(done)
}()
loop:
for {
select {
case f := <-callQueue:
f()
case <-done:
break loop
}
}
}
// Init initializes OpenGL by loading the function pointers from the active OpenGL context.
// This function must be manually run inside the main thread (Do, DoErr, DoVal, etc.).
//
// It must be called under the presence of an active OpenGL context, e.g., always after calling
// window.MakeContextCurrent(). Also, always call this function when switching contexts.
func Init() {
err := gl.Init()
if err != nil {
panic(err)
}
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
}
// DoNoBlock executes a function inside the main OpenGL thread. DoNoBlock does not wait until
// the function finishes.
func DoNoBlock(f func()) {
callQueue <- f
}
// Do executes a function inside the main OpenGL thread. Do blocks until the function finishes.
//
// All OpenGL calls must be done in the dedicated thread.
func Do(f func()) {
callQueue <- func() {
f()
respChan <- struct{}{}
}
<-respChan
}
// DoErr executes a function inside the main OpenGL thread and returns an error to the called.
// DoErr blocks until the function finishes.
//
// All OpenGL calls must be done in the dedicated thread.
func DoErr(f func() error) error {
callQueue <- func() {
respChan <- f()
}
err := <-respChan
if err != nil {
return err.(error)
}
return nil
}
// DoVal executes a function inside the main OpenGL thread and returns a value to the caller.
// DoVal blocks until the function finishes.
//
// All OpenGL calls must be done in the main thread.
func DoVal(f func() interface{}) interface{} {
callQueue <- func() {
respChan <- f()
}
return <-respChan
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/pkg/errors"
)
@ -252,7 +253,7 @@ func newVertexArray(shader *Shader, cap int) *vertexArray {
}
func (va *vertexArray) delete() {
DoNoBlock(func() {
mainthread.CallNonBlock(func() {
gl.DeleteVertexArrays(1, &va.vao.obj)
gl.DeleteBuffers(1, &va.vbo.obj)
})

4
run.go
View File

@ -1,7 +1,7 @@
package pixel
import (
"github.com/faiface/pixel/pixelgl"
"github.com/faiface/mainthread"
"github.com/go-gl/glfw/v3.2/glfw"
)
@ -28,5 +28,5 @@ import (
// function.
func Run(run func()) {
defer glfw.Terminate()
pixelgl.Run(run)
mainthread.Run(run)
}

View File

@ -5,6 +5,7 @@ import (
"runtime"
"github.com/faiface/mainthread"
"github.com/faiface/pixel/pixelgl"
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/go-gl/mathgl/mgl32"
@ -79,6 +80,9 @@ type Window struct {
buttons [KeyLast + 1]bool
scroll Vec
}
//DEBUG
Frame *pixelgl.Frame
}
var currentWindow *Window
@ -94,7 +98,7 @@ func NewWindow(config WindowConfig) (*Window, error) {
w := &Window{config: config}
err := pixelgl.DoErr(func() error {
err := mainthread.CallErr(func() error {
err := glfw.Init()
if err != nil {
return err
@ -127,7 +131,7 @@ func NewWindow(config WindowConfig) (*Window, error) {
return nil, errors.Wrap(err, "creating window failed")
}
pixelgl.Do(func() {
mainthread.Call(func() {
w.begin()
w.end()
@ -160,25 +164,24 @@ func NewWindow(config WindowConfig) (*Window, error) {
// Destroy destroys a window. The window can't be used any further.
func (w *Window) Destroy() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.Destroy()
})
}
// Clear clears the window with a color.
func (w *Window) Clear(c color.Color) {
pixelgl.DoNoBlock(func() {
mainthread.CallNonBlock(func() {
w.begin()
defer w.end()
c := NRGBAModel.Convert(c).(NRGBA)
pixelgl.Clear(float32(c.R), float32(c.G), float32(c.B), float32(c.A))
w.end()
})
}
// Update swaps buffers and polls events.
func (w *Window) Update() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.begin()
if w.config.VSync {
glfw.SwapInterval(1)
@ -197,7 +200,7 @@ func (w *Window) Update() {
// This is usefull when overriding the user's attempt to close a window, or just to close a
// window from within a program.
func (w *Window) SetClosed(closed bool) {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.SetShouldClose(closed)
})
}
@ -206,14 +209,14 @@ func (w *Window) SetClosed(closed bool) {
//
// The closed flag is automatically set when a user attempts to close a window.
func (w *Window) Closed() bool {
return pixelgl.DoVal(func() interface{} {
return mainthread.CallVal(func() interface{} {
return w.window.ShouldClose()
}).(bool)
}
// SetTitle changes the title of a window.
func (w *Window) SetTitle(title string) {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.SetTitle(title)
})
}
@ -221,14 +224,14 @@ func (w *Window) SetTitle(title string) {
// SetSize resizes a window to the specified size in pixels. In case of a fullscreen window,
// it changes the resolution of that window.
func (w *Window) SetSize(width, height float64) {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.SetSize(int(width), int(height))
})
}
// Size returns the size of the client area of a window (the part you can draw on).
func (w *Window) Size() (width, height float64) {
pixelgl.Do(func() {
mainthread.Call(func() {
wi, hi := w.window.GetSize()
width = float64(wi)
height = float64(hi)
@ -238,14 +241,14 @@ func (w *Window) Size() (width, height float64) {
// Show makes a window visible if it was hidden.
func (w *Window) Show() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.Show()
})
}
// Hide hides a window if it was visible.
func (w *Window) Hide() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.Hide()
})
}
@ -259,7 +262,7 @@ func (w *Window) Hide() {
func (w *Window) SetFullscreen(monitor *Monitor) {
if w.Monitor() != monitor {
if monitor == nil {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.SetMonitor(
nil,
w.restore.xpos,
@ -270,7 +273,7 @@ func (w *Window) SetFullscreen(monitor *Monitor) {
)
})
} else {
pixelgl.Do(func() {
mainthread.Call(func() {
w.restore.xpos, w.restore.ypos = w.window.GetPos()
w.restore.width, w.restore.height = w.window.GetSize()
@ -297,7 +300,7 @@ func (w *Window) IsFullscreen() bool {
// Monitor returns a monitor a fullscreen window is on. If the window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
monitor := pixelgl.DoVal(func() interface{} {
monitor := mainthread.CallVal(func() interface{} {
return w.window.GetMonitor()
}).(*glfw.Monitor)
if monitor == nil {
@ -310,28 +313,28 @@ func (w *Window) Monitor() *Monitor {
// Focus brings a window to the front and sets input focus.
func (w *Window) Focus() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.Focus()
})
}
// Focused returns true if a window has input focus.
func (w *Window) Focused() bool {
return pixelgl.DoVal(func() interface{} {
return mainthread.CallVal(func() interface{} {
return w.window.GetAttrib(glfw.Focused) == glfw.True
}).(bool)
}
// Maximize puts a windowed window to a maximized state.
func (w *Window) Maximize() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.Maximize()
})
}
// Restore restores a windowed window from a maximized state.
func (w *Window) Restore() {
pixelgl.Do(func() {
mainthread.Call(func() {
w.window.Restore()
})
}
@ -346,11 +349,19 @@ func (w *Window) begin() {
if w.shader != nil {
w.shader.Begin()
}
pixelgl.Viewport(0, 0, int32(w.width), int32(w.height))
if w.Frame != nil {
w.Frame.Begin()
pixelgl.Viewport(0, 0, int32(w.Frame.Width()), int32(w.Frame.Height()))
} else {
pixelgl.Viewport(0, 0, int32(w.width), int32(w.height))
}
}
// Note: must be called inside the main thread.
func (w *Window) end() {
if w.Frame != nil {
w.Frame.End()
}
if w.shader != nil {
w.shader.End()
}
@ -373,7 +384,7 @@ func (wt *windowTriangles) Draw() {
col := wt.w.col
bnd := wt.w.bnd
pixelgl.DoNoBlock(func() {
mainthread.CallNonBlock(func() {
wt.w.begin()
wt.w.shader.SetUniformAttr(transformMat3, mat)
@ -438,7 +449,7 @@ func (wt *windowTriangles) updateData(offset int, t Triangles) {
func (wt *windowTriangles) submitData() {
data := wt.data // avoid race condition
pixelgl.DoNoBlock(func() {
mainthread.CallNonBlock(func() {
wt.vs.Begin()
dataLen := len(data) / wt.vs.Stride()
if dataLen > wt.vs.Len() {
@ -603,12 +614,11 @@ void main() {
vec2 boundsMin = bounds.xy;
vec2 boundsMax = bounds.zw;
float tx = boundsMin.x * (1 - Texture.x) + boundsMax.x * Texture.x;
float ty = boundsMin.y * (1 - Texture.y) + boundsMax.y * Texture.y;
if (Texture == vec2(-1, -1)) {
color = maskColor * Color;
} else {
float tx = boundsMin.x * (1 - Texture.x) + boundsMax.x * Texture.x;
float ty = boundsMin.y * (1 - Texture.y) + boundsMax.y * Texture.y;
color = maskColor * Color * texture(tex, vec2(tx, 1 - ty));
}
}