Refactoring: removing new* functions

This commit is contained in:
Hajime Hoshi 2014-12-08 04:22:50 +09:00
parent 1c52a28a83
commit 1c3273e0a6
10 changed files with 103 additions and 153 deletions

View File

@ -5,40 +5,16 @@ import (
"github.com/hajimehoshi/ebiten/graphics" "github.com/hajimehoshi/ebiten/graphics"
"github.com/hajimehoshi/ebiten/graphics/matrix" "github.com/hajimehoshi/ebiten/graphics/matrix"
"github.com/hajimehoshi/ebiten/ui" "github.com/hajimehoshi/ebiten/ui"
"sync"
) )
type ContextUpdater struct { type ContextUpdater struct {
context *context context *context
} }
func NewContextUpdater(screenWidth, screenHeight, screenScale int) *ContextUpdater { func Initialize(screenWidth, screenHeight, screenScale int) (*ContextUpdater, error) {
return &ContextUpdater{
context: newContext(screenWidth, screenHeight, screenScale),
}
}
func (u *ContextUpdater) Update(drawer ui.Drawer) error {
return u.context.update(drawer)
}
var onceInit sync.Once
type context struct {
screenId graphics.RenderTargetID
defaultId graphics.RenderTargetID
currentId graphics.RenderTargetID
screenWidth int
screenHeight int
screenScale int
}
func newContext(screenWidth, screenHeight, screenScale int) *context {
onceInit.Do(func() {
gl.Init() gl.Init()
gl.Enable(gl.TEXTURE_2D) gl.Enable(gl.TEXTURE_2D)
gl.Enable(gl.BLEND) gl.Enable(gl.BLEND)
})
c := &context{ c := &context{
screenWidth: screenWidth, screenWidth: screenWidth,
@ -47,22 +23,34 @@ func newContext(screenWidth, screenHeight, screenScale int) *context {
} }
// The defualt framebuffer should be 0. // The defualt framebuffer should be 0.
defaultRenderTarget := &renderTarget{ c.defaultId = idsInstance.addRenderTarget(&renderTarget{
width: screenWidth * screenScale, width: screenWidth * screenScale,
height: screenHeight * screenScale, height: screenHeight * screenScale,
flipY: true, flipY: true,
} })
c.defaultId = idsInstance.addRenderTarget(defaultRenderTarget)
var err error var err error
c.screenId, err = idsInstance.newRenderTarget(screenWidth, screenHeight, graphics.FilterNearest) c.screenId, err = idsInstance.createRenderTarget(screenWidth, screenHeight, graphics.FilterNearest)
if err != nil { if err != nil {
panic("opengl.newContext: initializing the offscreen failed: " + err.Error()) return nil, err
} }
c.ResetOffscreen() c.ResetOffscreen()
c.Clear() c.Clear()
return c return &ContextUpdater{c}, nil
}
func (u *ContextUpdater) Update(drawer ui.Drawer) error {
return u.context.update(drawer)
}
type context struct {
screenId graphics.RenderTargetID
defaultId graphics.RenderTargetID
currentId graphics.RenderTargetID
screenWidth int
screenHeight int
screenScale int
} }
func (c *context) dispose() { func (c *context) dispose() {

View File

@ -27,11 +27,11 @@ var idsInstance = &ids{
} }
func NewRenderTargetID(width, height int, filter graphics.Filter) (graphics.RenderTargetID, error) { func NewRenderTargetID(width, height int, filter graphics.Filter) (graphics.RenderTargetID, error) {
return idsInstance.newRenderTarget(width, height, filter) return idsInstance.createRenderTarget(width, height, filter)
} }
func NewTextureID(img image.Image, filter graphics.Filter) (graphics.TextureID, error) { func NewTextureID(img image.Image, filter graphics.Filter) (graphics.TextureID, error) {
return idsInstance.newTexture(img, filter) return idsInstance.createTexture(img, filter)
} }
func (i *ids) textureAt(id graphics.TextureID) *texture { func (i *ids) textureAt(id graphics.TextureID) *texture {
@ -52,8 +52,8 @@ func (i *ids) toTexture(id graphics.RenderTargetID) graphics.TextureID {
return i.renderTargetToTexture[id] return i.renderTargetToTexture[id]
} }
func (i *ids) newTexture(img image.Image, filter graphics.Filter) (graphics.TextureID, error) { func (i *ids) createTexture(img image.Image, filter graphics.Filter) (graphics.TextureID, error) {
texture, err := newTextureFromImage(img, filter) texture, err := createTextureFromImage(img, filter)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -66,12 +66,12 @@ func (i *ids) newTexture(img image.Image, filter graphics.Filter) (graphics.Text
return textureId, nil return textureId, nil
} }
func (i *ids) newRenderTarget(width, height int, filter graphics.Filter) (graphics.RenderTargetID, error) { func (i *ids) createRenderTarget(width, height int, filter graphics.Filter) (graphics.RenderTargetID, error) {
texture, err := newTexture(width, height, filter) texture, err := createTexture(width, height, filter)
if err != nil { if err != nil {
return 0, err return 0, err
} }
framebuffer := newFramebuffer(gl.Texture(texture.native)) framebuffer := createFramebuffer(gl.Texture(texture.native))
// The current binded framebuffer can be changed. // The current binded framebuffer can be changed.
i.currentRenderTargetId = -1 i.currentRenderTargetId = -1
r := &renderTarget{ r := &renderTarget{
@ -128,18 +128,12 @@ func (i *ids) fillRenderTarget(id graphics.RenderTargetID, r, g, b uint8) {
gl.Clear(gl.COLOR_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT)
} }
func (i *ids) drawTexture( func (i *ids) drawTexture(target graphics.RenderTargetID, id graphics.TextureID, parts []graphics.TexturePart, geo matrix.Geometry, color matrix.Color) {
target graphics.RenderTargetID,
id graphics.TextureID,
parts []graphics.TexturePart,
geo matrix.Geometry,
color matrix.Color) {
texture := i.textureAt(id) texture := i.textureAt(id)
i.setViewportIfNeeded(target) i.setViewportIfNeeded(target)
r := i.renderTargetAt(target) r := i.renderTargetAt(target)
projectionMatrix := r.projectionMatrix() projectionMatrix := r.projectionMatrix()
quads := shader.TextureQuads(parts, texture.width, texture.height) shader.DrawTexture(texture.native, texture.width, texture.height, projectionMatrix, parts, geo, color)
shader.DrawTexture(texture.native, projectionMatrix, quads, geo, color)
} }
func (i *ids) setViewportIfNeeded(id graphics.RenderTargetID) { func (i *ids) setViewportIfNeeded(id graphics.RenderTargetID) {

View File

@ -2,6 +2,7 @@ package shader
import ( import (
"github.com/go-gl/gl" "github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten/graphics"
"github.com/hajimehoshi/ebiten/graphics/matrix" "github.com/hajimehoshi/ebiten/graphics/matrix"
"sync" "sync"
) )
@ -18,14 +19,15 @@ func glMatrix(matrix [4][4]float64) [16]float32 {
var once sync.Once var once sync.Once
func DrawTexture(native gl.Texture, projectionMatrix [4][4]float64, quads []TextureQuad, geo matrix.Geometry, color matrix.Color) { func DrawTexture(native gl.Texture, width, height int, projectionMatrix [4][4]float64, parts []graphics.TexturePart, geo matrix.Geometry, color matrix.Color) {
once.Do(func() { once.Do(func() {
initialize() initialize()
}) })
if len(quads) == 0 { if len(parts) == 0 {
return return
} }
quads := textureQuads(parts, width, height)
// TODO: Check performance // TODO: Check performance
shaderProgram := use(glMatrix(projectionMatrix), geo, color) shaderProgram := use(glMatrix(projectionMatrix), geo, color)

View File

@ -43,62 +43,40 @@ func initialize() {
programColorMatrix.native.Use() programColorMatrix.native.Use()
} }
type qualifierVariableType int // NOTE: This caches are now used only for programColorMatrix
var attribLocationCache = map[string]gl.AttribLocation{}
const ( var uniformLocationCache = map[string]gl.UniformLocation{}
qualifierVariableTypeAttribute qualifierVariableType = iota
qualifierVariableTypeUniform
)
var (
shaderLocationCache = map[qualifierVariableType]map[string]gl.AttribLocation{
qualifierVariableTypeAttribute: {},
qualifierVariableTypeUniform: {},
}
)
func getLocation(program gl.Program, name string, qvType qualifierVariableType) gl.AttribLocation {
if location, ok := shaderLocationCache[qvType][name]; ok {
return location
}
location := gl.AttribLocation(-1)
switch qvType {
case qualifierVariableTypeAttribute:
location = program.GetAttribLocation(name)
case qualifierVariableTypeUniform:
location = gl.AttribLocation(program.GetUniformLocation(name))
default:
panic("no reach")
}
if location == -1 {
panic("GetAttribLocation failed")
}
shaderLocationCache[qvType][name] = location
return location
}
func getAttributeLocation(program gl.Program, name string) gl.AttribLocation { func getAttributeLocation(program gl.Program, name string) gl.AttribLocation {
return getLocation(program, name, qualifierVariableTypeAttribute) if location, ok := attribLocationCache[name]; ok {
return location
}
location := program.GetAttribLocation(name)
attribLocationCache[name] = location
return location
} }
func getUniformLocation(program gl.Program, name string) gl.UniformLocation { func getUniformLocation(program gl.Program, name string) gl.UniformLocation {
return gl.UniformLocation(getLocation(program, name, qualifierVariableTypeUniform)) if location, ok := uniformLocationCache[name]; ok {
return location
}
location := program.GetUniformLocation(name)
uniformLocationCache[name] = location
return location
} }
func use(projectionMatrix [16]float32, geometryMatrix matrix.Geometry, colorMatrix matrix.Color) gl.Program { func use(projectionMatrix [16]float32, geo matrix.Geometry, color matrix.Color) gl.Program {
// TODO: Check the performance. // TODO: Check the performance.
program := programColorMatrix program := programColorMatrix
getUniformLocation(program.native, "projection_matrix").UniformMatrix4fv(false, projectionMatrix) getUniformLocation(program.native, "projection_matrix").UniformMatrix4fv(false, projectionMatrix)
a := float32(geometryMatrix.Elements[0][0]) a := float32(geo.Elements[0][0])
b := float32(geometryMatrix.Elements[0][1]) b := float32(geo.Elements[0][1])
c := float32(geometryMatrix.Elements[1][0]) c := float32(geo.Elements[1][0])
d := float32(geometryMatrix.Elements[1][1]) d := float32(geo.Elements[1][1])
tx := float32(geometryMatrix.Elements[0][2]) tx := float32(geo.Elements[0][2])
ty := float32(geometryMatrix.Elements[1][2]) ty := float32(geo.Elements[1][2])
glModelviewMatrix := [...]float32{ glModelviewMatrix := [...]float32{
a, c, 0, 0, a, c, 0, 0,
b, d, 0, 0, b, d, 0, 0,
@ -112,7 +90,7 @@ func use(projectionMatrix [16]float32, geometryMatrix matrix.Geometry, colorMatr
e := [4][5]float32{} e := [4][5]float32{}
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
for j := 0; j < 5; j++ { for j := 0; j < 5; j++ {
e[i][j] = float32(colorMatrix.Elements[i][j]) e[i][j] = float32(color.Elements[i][j])
} }
} }

View File

@ -4,7 +4,7 @@ import (
"github.com/hajimehoshi/ebiten/graphics" "github.com/hajimehoshi/ebiten/graphics"
) )
type TextureQuad struct { type textureQuad struct {
VertexX1 float32 VertexX1 float32
VertexX2 float32 VertexX2 float32
VertexY1 float32 VertexY1 float32
@ -38,8 +38,8 @@ func v(y int, height int) float32 {
return float32(y) / float32(AdjustSizeForTexture(height)) return float32(y) / float32(AdjustSizeForTexture(height))
} }
func TextureQuads(parts []graphics.TexturePart, width, height int) []TextureQuad { func textureQuads(parts []graphics.TexturePart, width, height int) []textureQuad {
quads := []TextureQuad{} quads := []textureQuad{}
for _, part := range parts { for _, part := range parts {
x1 := float32(part.LocationX) x1 := float32(part.LocationX)
x2 := float32(part.LocationX + part.Source.Width) x2 := float32(part.LocationX + part.Source.Width)
@ -49,7 +49,7 @@ func TextureQuads(parts []graphics.TexturePart, width, height int) []TextureQuad
u2 := u(part.Source.X+part.Source.Width, width) u2 := u(part.Source.X+part.Source.Width, width)
v1 := v(part.Source.Y, height) v1 := v(part.Source.Y, height)
v2 := v(part.Source.Y+part.Source.Height, height) v2 := v(part.Source.Y+part.Source.Height, height)
quad := TextureQuad{x1, x2, y1, y2, u1, u2, v1, v2} quad := textureQuad{x1, x2, y1, y2, u1, u2, v1, v2}
quads = append(quads, quad) quads = append(quads, quad)
} }
return quads return quads

View File

@ -27,12 +27,11 @@ type renderTarget struct {
flipY bool flipY bool
} }
func newFramebuffer(nativeTexture gl.Texture) gl.Framebuffer { func createFramebuffer(nativeTexture gl.Texture) gl.Framebuffer {
framebuffer := gl.GenFramebuffer() framebuffer := gl.GenFramebuffer()
framebuffer.Bind() framebuffer.Bind()
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, nativeTexture, 0)
gl.TEXTURE_2D, nativeTexture, 0)
if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE {
panic("creating framebuffer failed") panic("creating framebuffer failed")
} }

View File

@ -17,8 +17,7 @@ func adjustImageForTexture(img image.Image) *image.NRGBA {
shader.AdjustSizeForTexture(height), shader.AdjustSizeForTexture(height),
}, },
} }
if nrgba, ok := img.(*image.NRGBA); ok && if nrgba, ok := img.(*image.NRGBA); ok && img.Bounds() == adjustedImageBounds {
img.Bounds() == adjustedImageBounds {
return nrgba return nrgba
} }
@ -37,7 +36,7 @@ type texture struct {
height int height int
} }
func newNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter graphics.Filter) gl.Texture { func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter graphics.Filter) gl.Texture {
nativeTexture := gl.GenTexture() nativeTexture := gl.GenTexture()
if nativeTexture < 0 { if nativeTexture < 0 {
panic("glGenTexture failed") panic("glGenTexture failed")
@ -67,17 +66,17 @@ func newNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter gr
return nativeTexture return nativeTexture
} }
func newTexture(width, height int, filter graphics.Filter) (*texture, error) { func createTexture(width, height int, filter graphics.Filter) (*texture, error) {
w := shader.AdjustSizeForTexture(width) w := shader.AdjustSizeForTexture(width)
h := shader.AdjustSizeForTexture(height) h := shader.AdjustSizeForTexture(height)
native := newNativeTexture(w, h, nil, filter) native := createNativeTexture(w, h, nil, filter)
return &texture{native, width, height}, nil return &texture{native, width, height}, nil
} }
func newTextureFromImage(img image.Image, filter graphics.Filter) (*texture, error) { func createTextureFromImage(img image.Image, filter graphics.Filter) (*texture, error) {
adjustedImage := adjustImageForTexture(img) adjustedImage := adjustImageForTexture(img)
size := adjustedImage.Bounds().Size() size := adjustedImage.Bounds().Size()
native := newNativeTexture(size.X, size.Y, adjustedImage.Pix, filter) native := createNativeTexture(size.X, size.Y, adjustedImage.Pix, filter)
return &texture{native, size.X, size.Y}, nil return &texture{native, size.X, size.Y}, nil
} }

View File

@ -4,7 +4,6 @@ import (
glfw "github.com/go-gl/glfw3" glfw "github.com/go-gl/glfw3"
"github.com/hajimehoshi/ebiten/graphics" "github.com/hajimehoshi/ebiten/graphics"
"github.com/hajimehoshi/ebiten/graphics/opengl" "github.com/hajimehoshi/ebiten/graphics/opengl"
"github.com/hajimehoshi/ebiten/input"
"github.com/hajimehoshi/ebiten/ui" "github.com/hajimehoshi/ebiten/ui"
"image" "image"
"runtime" "runtime"
@ -13,37 +12,11 @@ import (
type canvas struct { type canvas struct {
window *glfw.Window window *glfw.Window
contextUpdater *opengl.ContextUpdater contextUpdater *opengl.ContextUpdater
keyboard *keyboard keyboard keyboard
funcs chan func() funcs chan func()
funcsDone chan struct{} funcsDone chan struct{}
} }
func newCanvas(width, height, scale int, title string) *canvas {
window, err := glfw.CreateWindow(width*scale, height*scale, title, nil, nil)
if err != nil {
panic(err)
}
canvas := &canvas{
window: window,
keyboard: newKeyboard(),
funcs: make(chan func()),
funcsDone: make(chan struct{}),
}
input.SetKeyboard(canvas.keyboard)
graphics.SetTextureFactory(canvas)
// For retina displays, recalculate the scale with the framebuffer size.
windowWidth, _ := window.GetFramebufferSize()
realScale := windowWidth / width
canvas.run()
canvas.use(func() {
canvas.contextUpdater = opengl.NewContextUpdater(width, height, realScale)
})
return canvas
}
func (c *canvas) Draw(d ui.Drawer) (err error) { func (c *canvas) Draw(d ui.Drawer) (err error) {
c.use(func() { c.use(func() {
err = c.contextUpdater.Update(d) err = c.contextUpdater.Update(d)
@ -74,7 +47,7 @@ func (c *canvas) NewRenderTargetID(width, height int, filter graphics.Filter) (g
return id, err return id, err
} }
func (c *canvas) run() { func (c *canvas) run(width, height, scale int) {
go func() { go func() {
runtime.LockOSThread() runtime.LockOSThread()
c.window.MakeContextCurrent() c.window.MakeContextCurrent()

View File

@ -6,18 +6,11 @@ import (
) )
type keyboard struct { type keyboard struct {
pressedKeys map[input.Key]struct{} keyPressed [input.KeyMax]bool
}
func newKeyboard() *keyboard {
return &keyboard{
pressedKeys: map[input.Key]struct{}{},
}
} }
func (k *keyboard) IsKeyPressed(key input.Key) bool { func (k *keyboard) IsKeyPressed(key input.Key) bool {
_, ok := k.pressedKeys[key] return k.keyPressed[key]
return ok
} }
var glfwKeyCodeToKey = map[glfw.Key]input.Key{ var glfwKeyCodeToKey = map[glfw.Key]input.Key{
@ -30,10 +23,6 @@ var glfwKeyCodeToKey = map[glfw.Key]input.Key{
func (k *keyboard) update(window *glfw.Window) { func (k *keyboard) update(window *glfw.Window) {
for g, u := range glfwKeyCodeToKey { for g, u := range glfwKeyCodeToKey {
if window.GetKey(g) == glfw.Press { k.keyPressed[u] = window.GetKey(g) == glfw.Press
k.pressedKeys[u] = struct{}{}
} else {
delete(k.pressedKeys, u)
}
} }
} }

View File

@ -4,6 +4,9 @@ import (
"errors" "errors"
"fmt" "fmt"
glfw "github.com/go-gl/glfw3" glfw "github.com/go-gl/glfw3"
"github.com/hajimehoshi/ebiten/graphics"
"github.com/hajimehoshi/ebiten/graphics/opengl"
"github.com/hajimehoshi/ebiten/input"
"github.com/hajimehoshi/ebiten/ui" "github.com/hajimehoshi/ebiten/ui"
) )
@ -22,8 +25,33 @@ func (u *UI) Start(width, height, scale int, title string) (ui.Canvas, error) {
return nil, errors.New("glfw.Init() fails") return nil, errors.New("glfw.Init() fails")
} }
glfw.WindowHint(glfw.Resizable, glfw.False) glfw.WindowHint(glfw.Resizable, glfw.False)
u.canvas = newCanvas(width, height, scale, title) window, err := glfw.CreateWindow(width*scale, height*scale, title, nil, nil)
return u.canvas, nil if err != nil {
return nil, err
}
c := &canvas{
window: window,
funcs: make(chan func()),
funcsDone: make(chan struct{}),
}
input.SetKeyboard(&c.keyboard)
graphics.SetTextureFactory(c)
c.run(width, height, scale)
// For retina displays, recalculate the scale with the framebuffer size.
windowWidth, _ := window.GetFramebufferSize()
realScale := windowWidth / width
c.use(func() {
c.contextUpdater, err = opengl.Initialize(width, height, realScale)
})
if err != nil {
return nil, err
}
u.canvas = c
return c, nil
} }
func (u *UI) DoEvents() { func (u *UI) DoEvents() {