From f21f6d357efc0728d30f30935787a42684d7302b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 22 Oct 2016 19:16:16 +0900 Subject: [PATCH] graphics: Bug fix: Command splitting threshold is more strict on browsers? --- imageparts.go | 31 ++++++++++++++---- internal/endian/endian.go | 25 ++++++++++++++ internal/endian/js.go | 36 +++++++++++++++++++++ internal/endian/nojs.go | 34 +++++++++++++++++++ internal/graphics/command.go | 6 ++-- internal/graphics/image.go | 2 +- internal/graphics/opengl/context_desktop.go | 10 ++---- internal/graphics/opengl/context_js.go | 9 ++---- internal/graphics/opengl/context_mobile.go | 19 ++--------- internal/restorable/image.go | 6 ++-- 10 files changed, 134 insertions(+), 44 deletions(-) create mode 100644 internal/endian/endian.go create mode 100644 internal/endian/js.go create mode 100644 internal/endian/nojs.go diff --git a/imageparts.go b/imageparts.go index ce307baad..29236e8ca 100644 --- a/imageparts.go +++ b/imageparts.go @@ -18,6 +18,7 @@ import ( "image" "math" + "github.com/hajimehoshi/ebiten/internal/endian" "github.com/hajimehoshi/ebiten/internal/graphics" ) @@ -82,9 +83,9 @@ type textureQuads struct { height int } -func (t *textureQuads) vertices() []int16 { +func (t *textureQuads) vertices() []uint8 { l := t.parts.Len() - vertices := make([]int16, 0, l*16) + vertices := make([]uint8, 0, l*32) p := t.parts w, h := t.width, t.height width2p := int(graphics.NextPowerOf2Int32(int32(w))) @@ -100,11 +101,27 @@ func (t *textureQuads) vertices() []int16 { continue } u0, v0, u1, v1 := u(sx0, width2p), v(sy0, height2p), u(sx1, width2p), v(sy1, height2p) - vertices = append(vertices, - x0, y0, u0, v0, - x1, y0, u1, v0, - x0, y1, u0, v1, - x1, y1, u1, v1) + if endian.IsLittle() { + vertices = append(vertices, + uint8(x0), uint8(x0>>8), uint8(y0), uint8(y0>>8), + uint8(u0), uint8(u0>>8), uint8(v0), uint8(v0>>8), + uint8(x1), uint8(x1>>8), uint8(y0), uint8(y0>>8), + uint8(u1), uint8(u1>>8), uint8(v0), uint8(v0>>8), + uint8(x0), uint8(x0>>8), uint8(y1), uint8(y1>>8), + uint8(u0), uint8(u0>>8), uint8(v1), uint8(v1>>8), + uint8(x1), uint8(x1>>8), uint8(y1), uint8(y1>>8), + uint8(u1), uint8(u1>>8), uint8(v1), uint8(v1>>8)) + } else { + vertices = append(vertices, + uint8(x0>>8), uint8(x0), uint8(y0>>8), uint8(y0), + uint8(u0>>8), uint8(u0), uint8(v0>>8), uint8(v0), + uint8(x1>>8), uint8(x1), uint8(y0>>8), uint8(y0), + uint8(u1>>8), uint8(u1), uint8(v0>>8), uint8(v0), + uint8(x0>>8), uint8(x0), uint8(y1>>8), uint8(y1), + uint8(u0>>8), uint8(u0), uint8(v1>>8), uint8(v1), + uint8(x1>>8), uint8(x1), uint8(y1>>8), uint8(y1), + uint8(u1>>8), uint8(u1), uint8(v1>>8), uint8(v1)) + } } return vertices } diff --git a/internal/endian/endian.go b/internal/endian/endian.go new file mode 100644 index 000000000..3bf9c037a --- /dev/null +++ b/internal/endian/endian.go @@ -0,0 +1,25 @@ +// Copyright 2016 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package endian + +var isLittleEndian = false + +func IsLittle() bool { + return isLittleEndian +} + +func IsBig() bool { + return !isLittleEndian +} diff --git a/internal/endian/js.go b/internal/endian/js.go new file mode 100644 index 000000000..6695ab621 --- /dev/null +++ b/internal/endian/js.go @@ -0,0 +1,36 @@ +// Copyright 2016 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build js + +package endian + +import ( + "github.com/gopherjs/gopherjs/js" +) + +func init() { + a := js.Global.Get("ArrayBuffer").New(4) + a8 := js.Global.Get("Uint8Array").New(a) + a32 := js.Global.Get("Uint32Array").New(a) + a32.SetIndex(0, 1) + switch a8.Index(0).Int() { + case 1: + isLittleEndian = true + case 0: + isLittleEndian = false + default: + panic("not reach") + } +} diff --git a/internal/endian/nojs.go b/internal/endian/nojs.go new file mode 100644 index 000000000..985fe9564 --- /dev/null +++ b/internal/endian/nojs.go @@ -0,0 +1,34 @@ +// Copyright 2016 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !js + +package endian + +import ( + "unsafe" +) + +func init() { + i := int(1) + b := (*uint8)(unsafe.Pointer(&i)) + switch *b { + case 1: + isLittleEndian = true + case 0: + isLittleEndian = false + default: + panic("not reach") + } +} diff --git a/internal/graphics/command.go b/internal/graphics/command.go index 80c8e5a48..957815a55 100644 --- a/internal/graphics/command.go +++ b/internal/graphics/command.go @@ -96,7 +96,7 @@ func (q *commandQueue) Flush(context *opengl.Context) error { // glViewport must be called at least at every frame on iOS. context.ResetViewportSize() for _, g := range q.commandGroups() { - vertices := []int16{} + vertices := []uint8{} for _, c := range g { switch c := c.(type) { case *drawImageCommand: @@ -155,14 +155,14 @@ func (c *fillCommand) Exec(context *opengl.Context, indexOffsetInBytes int) erro type drawImageCommand struct { dst *Image src *Image - vertices []int16 + vertices []uint8 geo Matrix color Matrix mode opengl.CompositeMode } const ( - quadVertexNum = 16 // 4 * 2 [vertices] * 2 [tex_coords] + quadVertexNum = 32 // 4 * 2 [vertices] * 2 [tex_coords] * 2[bytes] ) func (c *drawImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { diff --git a/internal/graphics/image.go b/internal/graphics/image.go index a285e476b..42dc21adf 100644 --- a/internal/graphics/image.go +++ b/internal/graphics/image.go @@ -94,7 +94,7 @@ func (i *Image) Fill(clr color.RGBA) error { return nil } -func (i *Image) DrawImage(src *Image, vertices []int16, geo, clr Matrix, mode opengl.CompositeMode) error { +func (i *Image) DrawImage(src *Image, vertices []uint8, geo, clr Matrix, mode opengl.CompositeMode) error { c := &drawImageCommand{ dst: i, src: src, diff --git a/internal/graphics/opengl/context_desktop.go b/internal/graphics/opengl/context_desktop.go index 754cab01d..973eda3aa 100644 --- a/internal/graphics/opengl/context_desktop.go +++ b/internal/graphics/opengl/context_desktop.go @@ -468,6 +468,7 @@ func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage Bu case int: gl.BufferData(uint32(bufferType), v, nil, uint32(bufferUsage)) case []uint16: + // TODO: What about the endianness? gl.BufferData(uint32(bufferType), 2*len(v), gl.Ptr(v), uint32(bufferUsage)) default: panic("not reach") @@ -485,14 +486,9 @@ func (c *Context) BindElementArrayBuffer(b Buffer) { }) } -func (c *Context) BufferSubData(bufferType BufferType, data interface{}) { +func (c *Context) BufferSubData(bufferType BufferType, data []uint8) { _ = c.runOnContextThread(func() error { - switch data := data.(type) { - case []int16: - gl.BufferSubData(uint32(bufferType), 0, 2*len(data), gl.Ptr(data)) - default: - panic("not reach") - } + gl.BufferSubData(uint32(bufferType), 0, len(data), gl.Ptr(data)) return nil }) } diff --git a/internal/graphics/opengl/context_js.go b/internal/graphics/opengl/context_js.go index 35d67d33a..17c76174c 100644 --- a/internal/graphics/opengl/context_js.go +++ b/internal/graphics/opengl/context_js.go @@ -387,14 +387,9 @@ func (c *Context) BindElementArrayBuffer(b Buffer) { gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.Object) } -func (c *Context) BufferSubData(bufferType BufferType, data interface{}) { +func (c *Context) BufferSubData(bufferType BufferType, data []uint8) { gl := c.gl - switch data := data.(type) { - case []int16: - gl.BufferSubData(int(bufferType), 0, data) - default: - panic("not reach") - } + gl.BufferSubData(int(bufferType), 0, data) } func (c *Context) DeleteBuffer(b Buffer) { diff --git a/internal/graphics/opengl/context_mobile.go b/internal/graphics/opengl/context_mobile.go index c8d209d5c..4ced338c8 100644 --- a/internal/graphics/opengl/context_mobile.go +++ b/internal/graphics/opengl/context_mobile.go @@ -357,6 +357,7 @@ func (c *Context) DisableVertexAttribArray(p Program, location string) { } func uint16ToBytes(v []uint16) []uint8 { + // TODO: Consider endian? b := make([]uint8, len(v)*2) for i, x := range v { b[2*i] = uint8(x) @@ -365,15 +366,6 @@ func uint16ToBytes(v []uint16) []uint8 { return b } -func int16ToBytes(v []int16) []uint8 { - b := make([]uint8, len(v)*2) - for i, x := range v { - b[2*i] = uint8(uint16(x)) - b[2*i+1] = uint8(uint16(x) >> 8) - } - return b -} - func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer { gl := c.gl b := gl.CreateBuffer() @@ -394,14 +386,9 @@ func (c *Context) BindElementArrayBuffer(b Buffer) { gl.BindBuffer(mgl.ELEMENT_ARRAY_BUFFER, mgl.Buffer(b)) } -func (c *Context) BufferSubData(bufferType BufferType, data interface{}) { +func (c *Context) BufferSubData(bufferType BufferType, data []uint8) { gl := c.gl - switch data := data.(type) { - case []int16: - gl.BufferSubData(mgl.Enum(bufferType), 0, int16ToBytes(data)) - default: - panic("not reach") - } + gl.BufferSubData(mgl.Enum(bufferType), 0, data) } func (c *Context) DeleteBuffer(b Buffer) { diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 30b0b2969..b892ae1c1 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -25,7 +25,7 @@ import ( type drawImageHistoryItem struct { image *graphics.Image - vertices []int16 + vertices []uint8 geom graphics.Matrix colorm graphics.Matrix mode opengl.CompositeMode @@ -145,7 +145,7 @@ func (p *Image) ReplacePixels(pixels []uint8) error { return nil } -func (p *Image) DrawImage(img *Image, vertices []int16, geom graphics.Matrix, colorm graphics.Matrix, mode opengl.CompositeMode) error { +func (p *Image) DrawImage(img *Image, vertices []uint8, geom graphics.Matrix, colorm graphics.Matrix, mode opengl.CompositeMode) error { if img.stale || img.volatile { p.makeStale() } else { @@ -157,7 +157,7 @@ func (p *Image) DrawImage(img *Image, vertices []int16, geom graphics.Matrix, co return nil } -func (p *Image) appendDrawImageHistory(image *graphics.Image, vertices []int16, geom graphics.Matrix, colorm graphics.Matrix, mode opengl.CompositeMode) { +func (p *Image) appendDrawImageHistory(image *graphics.Image, vertices []uint8, geom graphics.Matrix, colorm graphics.Matrix, mode opengl.CompositeMode) { if p.stale { return }