mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-11 19:48:54 +01:00
graphics: Add buffered package
Moved the command queue to the package.
This commit is contained in:
parent
0a872b342a
commit
79b32c7601
112
image.go
112
image.go
@ -18,63 +18,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// imageQueue represents a queue for image operations that are ordered before the game starts (BeginFrame).
|
|
||||||
// Before the game starts, the package shareable doesn't determine the minimum/maximum texture sizes (#879).
|
|
||||||
// Instead of accessing the package shareable, defer the image operations until the game starts (#921).
|
|
||||||
imageQueue []func()
|
|
||||||
imageQueueM sync.Mutex
|
|
||||||
needsEnqueueImageOps = true
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkNeedsEnqueueImageOp(location string) {
|
|
||||||
imageQueueM.Lock()
|
|
||||||
defer imageQueueM.Unlock()
|
|
||||||
|
|
||||||
if needsEnqueueImageOps {
|
|
||||||
panic(fmt.Sprintf("ebiten: %s is not available before the game starts", location))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enqueueImageOpIfNeeded(f func() func()) bool {
|
|
||||||
imageQueueM.Lock()
|
|
||||||
defer imageQueueM.Unlock()
|
|
||||||
|
|
||||||
if !needsEnqueueImageOps {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
imageQueue = append(imageQueue, f())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func flushImageOpsIfNeeded() {
|
|
||||||
imageQueueM.Lock()
|
|
||||||
|
|
||||||
if !needsEnqueueImageOps {
|
|
||||||
if len(imageQueue) > 0 {
|
|
||||||
panic("ebiten: len(imageQueue) must be 0 after the game starts")
|
|
||||||
}
|
|
||||||
imageQueueM.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set this flag false first, or the image operations will be queued again.
|
|
||||||
needsEnqueueImageOps = false
|
|
||||||
imageQueueM.Unlock()
|
|
||||||
|
|
||||||
// As a new item will not be enqueued any longer, mutex does not have to, or should not be used.
|
|
||||||
for _, f := range imageQueue {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
imageQueue = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image represents a rectangle set of pixels.
|
// Image represents a rectangle set of pixels.
|
||||||
// The pixel format is alpha-premultiplied RGBA.
|
// The pixel format is alpha-premultiplied RGBA.
|
||||||
// Image implements image.Image and draw.Image.
|
// Image implements image.Image and draw.Image.
|
||||||
@ -135,15 +83,6 @@ func (i *Image) Clear() error {
|
|||||||
func (i *Image) Fill(clr color.Color) error {
|
func (i *Image) Fill(clr color.Color) error {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
|
||||||
rgba := color.RGBAModel.Convert(clr).(color.RGBA)
|
|
||||||
if enqueueImageOpIfNeeded(func() func() {
|
|
||||||
return func() {
|
|
||||||
i.Fill(rgba)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -155,7 +94,7 @@ func (i *Image) Fill(clr color.Color) error {
|
|||||||
|
|
||||||
i.resolvePendingPixels(false)
|
i.resolvePendingPixels(false)
|
||||||
|
|
||||||
i.mipmap.fill(rgba)
|
i.mipmap.fill(color.RGBAModel.Convert(clr).(color.RGBA))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,15 +138,6 @@ func (i *Image) Fill(clr color.Color) error {
|
|||||||
func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
|
func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
|
||||||
if enqueueImageOpIfNeeded(func() func() {
|
|
||||||
op := *options
|
|
||||||
return func() {
|
|
||||||
i.DrawImage(img, &op)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if img.isDisposed() {
|
if img.isDisposed() {
|
||||||
panic("ebiten: the given image to DrawImage must not be disposed")
|
panic("ebiten: the given image to DrawImage must not be disposed")
|
||||||
}
|
}
|
||||||
@ -355,19 +285,6 @@ const MaxIndicesNum = graphics.IndicesNum
|
|||||||
func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions) {
|
func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions) {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
|
||||||
if enqueueImageOpIfNeeded(func() func() {
|
|
||||||
vs := make([]Vertex, len(vertices))
|
|
||||||
copy(vs, vertices)
|
|
||||||
is := make([]uint16, len(indices))
|
|
||||||
copy(is, indices)
|
|
||||||
op := *options
|
|
||||||
return func() {
|
|
||||||
i.DrawTriangles(vs, is, img, &op)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -467,8 +384,6 @@ func (i *Image) ColorModel() color.Model {
|
|||||||
//
|
//
|
||||||
// At can't be called outside the main loop (ebiten.Run's updating function) starts (as of version 1.4.0-alpha).
|
// At can't be called outside the main loop (ebiten.Run's updating function) starts (as of version 1.4.0-alpha).
|
||||||
func (i *Image) At(x, y int) color.Color {
|
func (i *Image) At(x, y int) color.Color {
|
||||||
checkNeedsEnqueueImageOp("(*Image).At")
|
|
||||||
|
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
return color.RGBA{}
|
return color.RGBA{}
|
||||||
}
|
}
|
||||||
@ -488,8 +403,6 @@ func (i *Image) At(x, y int) color.Color {
|
|||||||
//
|
//
|
||||||
// If the image is disposed, Set does nothing.
|
// If the image is disposed, Set does nothing.
|
||||||
func (img *Image) Set(x, y int, clr color.Color) {
|
func (img *Image) Set(x, y int, clr color.Color) {
|
||||||
checkNeedsEnqueueImageOp("(*Image).Set")
|
|
||||||
|
|
||||||
img.copyCheck()
|
img.copyCheck()
|
||||||
if img.isDisposed() {
|
if img.isDisposed() {
|
||||||
return
|
return
|
||||||
@ -554,14 +467,6 @@ func (i *Image) resolvePendingPixels(draw bool) {
|
|||||||
func (i *Image) Dispose() error {
|
func (i *Image) Dispose() error {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
|
||||||
if enqueueImageOpIfNeeded(func() func() {
|
|
||||||
return func() {
|
|
||||||
i.Dispose()
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -587,16 +492,6 @@ func (i *Image) Dispose() error {
|
|||||||
func (i *Image) ReplacePixels(p []byte) error {
|
func (i *Image) ReplacePixels(p []byte) error {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
|
||||||
if enqueueImageOpIfNeeded(func() func() {
|
|
||||||
px := make([]byte, len(p))
|
|
||||||
copy(px, p)
|
|
||||||
return func() {
|
|
||||||
i.ReplacePixels(px)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -609,7 +504,10 @@ func (i *Image) ReplacePixels(p []byte) error {
|
|||||||
if l := 4 * s.X * s.Y; len(p) != l {
|
if l := 4 * s.X * s.Y; len(p) != l {
|
||||||
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
|
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
|
||||||
}
|
}
|
||||||
i.mipmap.replacePixels(p)
|
|
||||||
|
px := make([]byte, len(p))
|
||||||
|
copy(px, p)
|
||||||
|
i.mipmap.replacePixels(px)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
internal/buffered/command.go
Normal file
64
internal/buffered/command.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2019 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 buffered
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type command struct {
|
||||||
|
f func()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
delayedCommandsFlushable bool
|
||||||
|
|
||||||
|
// delayedCommands represents a queue for image operations that are ordered before the game starts
|
||||||
|
// (BeginFrame). Before the game starts, the package shareable doesn't determine the minimum/maximum texture
|
||||||
|
// sizes (#879).
|
||||||
|
//
|
||||||
|
// TODO: Flush the commands only when necessary (#921).
|
||||||
|
delayedCommands []*command
|
||||||
|
delayedCommandsM sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeDelayedCommandFlushable() {
|
||||||
|
delayedCommandsM.Lock()
|
||||||
|
delayedCommandsFlushable = true
|
||||||
|
delayedCommandsM.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueueDelayedCommand(f func()) {
|
||||||
|
delayedCommandsM.Lock()
|
||||||
|
delayedCommands = append(delayedCommands, &command{
|
||||||
|
f: f,
|
||||||
|
})
|
||||||
|
delayedCommandsM.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func flushDelayedCommands() bool {
|
||||||
|
delayedCommandsM.Lock()
|
||||||
|
defer delayedCommandsM.Unlock()
|
||||||
|
|
||||||
|
if !delayedCommandsFlushable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range delayedCommands {
|
||||||
|
c.f()
|
||||||
|
}
|
||||||
|
delayedCommands = nil
|
||||||
|
return true
|
||||||
|
}
|
106
internal/buffered/image.go
Normal file
106
internal/buffered/image.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2019 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 buffered
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/shareable"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Image struct {
|
||||||
|
img *shareable.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func BeginFrame() error {
|
||||||
|
if err := shareable.BeginFrame(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
makeDelayedCommandFlushable()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EndFrame() error {
|
||||||
|
if !flushDelayedCommands() {
|
||||||
|
panic("buffered: the command queue must be available at EndFrame")
|
||||||
|
}
|
||||||
|
return shareable.EndFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImage(width, height int, volatile bool) *Image {
|
||||||
|
i := &Image{}
|
||||||
|
enqueueDelayedCommand(func() {
|
||||||
|
i.img = shareable.NewImage(width, height, volatile)
|
||||||
|
})
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScreenFramebufferImage(width, height int) *Image {
|
||||||
|
i := &Image{}
|
||||||
|
enqueueDelayedCommand(func() {
|
||||||
|
i.img = shareable.NewScreenFramebufferImage(width, height)
|
||||||
|
})
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) MarkDisposed() {
|
||||||
|
enqueueDelayedCommand(func() {
|
||||||
|
i.img.MarkDisposed()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) At(x, y int) (r, g, b, a byte) {
|
||||||
|
if !flushDelayedCommands() {
|
||||||
|
panic("buffered: the command queue is not available yet at At")
|
||||||
|
}
|
||||||
|
return i.img.At(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Dump(name string) error {
|
||||||
|
if !flushDelayedCommands() {
|
||||||
|
panic("buffered: the command queue is not available yet at Dump")
|
||||||
|
}
|
||||||
|
return i.img.Dump(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Fill(clr color.RGBA) {
|
||||||
|
enqueueDelayedCommand(func() {
|
||||||
|
i.img.Fill(clr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) ClearFramebuffer() {
|
||||||
|
enqueueDelayedCommand(func() {
|
||||||
|
i.img.ClearFramebuffer()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) ReplacePixels(pix []byte) {
|
||||||
|
enqueueDelayedCommand(func() {
|
||||||
|
i.img.ReplacePixels(pix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
|
||||||
|
if i == src {
|
||||||
|
panic("buffered: Image.DrawTriangles: src must be different from the receiver")
|
||||||
|
}
|
||||||
|
enqueueDelayedCommand(func() {
|
||||||
|
i.img.DrawTriangles(src.img, vertices, indices, colorm, mode, filter, address)
|
||||||
|
})
|
||||||
|
}
|
24
mipmap.go
24
mipmap.go
@ -21,18 +21,18 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/buffered"
|
||||||
"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/shareable"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type levelToImage map[int]*shareable.Image
|
type levelToImage map[int]*buffered.Image
|
||||||
|
|
||||||
type mipmap struct {
|
type mipmap struct {
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
volatile bool
|
volatile bool
|
||||||
orig *shareable.Image
|
orig *buffered.Image
|
||||||
imgs map[image.Rectangle]levelToImage
|
imgs map[image.Rectangle]levelToImage
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ func newMipmap(width, height int, volatile bool) *mipmap {
|
|||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
volatile: volatile,
|
volatile: volatile,
|
||||||
orig: shareable.NewImage(width, height, volatile),
|
orig: buffered.NewImage(width, height, volatile),
|
||||||
imgs: map[image.Rectangle]levelToImage{},
|
imgs: map[image.Rectangle]levelToImage{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ func newScreenFramebufferMipmap(width, height int) *mipmap {
|
|||||||
return &mipmap{
|
return &mipmap{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
orig: shareable.NewScreenFramebufferImage(width, height),
|
orig: buffered.NewScreenFramebufferImage(width, height),
|
||||||
imgs: map[image.Rectangle]levelToImage{},
|
imgs: map[image.Rectangle]levelToImage{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
|
|||||||
vs := quadVertices(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca)
|
vs := quadVertices(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressClampToZero)
|
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressClampToZero)
|
||||||
} else if shared := src.level(bounds, level); shared != nil {
|
} else if buf := src.level(bounds, level); buf != nil {
|
||||||
w, h := sizeForLevel(bounds.Dx(), bounds.Dy(), level)
|
w, h := sizeForLevel(bounds.Dx(), bounds.Dy(), level)
|
||||||
s := pow2(level)
|
s := pow2(level)
|
||||||
a *= s
|
a *= s
|
||||||
@ -135,7 +135,7 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
|
|||||||
d *= s
|
d *= s
|
||||||
vs := quadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca)
|
vs := quadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
m.orig.DrawTriangles(shared, vs, is, colorm, mode, filter, driver.AddressClampToZero)
|
m.orig.DrawTriangles(buf, vs, is, colorm, mode, filter, driver.AddressClampToZero)
|
||||||
}
|
}
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
@ -164,11 +164,13 @@ func (m *mipmap) drawTriangles(src *mipmap, bounds image.Rectangle, vertices []V
|
|||||||
vs[i*graphics.VertexFloatNum+10] = v.ColorB
|
vs[i*graphics.VertexFloatNum+10] = v.ColorB
|
||||||
vs[i*graphics.VertexFloatNum+11] = v.ColorA
|
vs[i*graphics.VertexFloatNum+11] = v.ColorA
|
||||||
}
|
}
|
||||||
m.orig.DrawTriangles(src.orig, vs, indices, colorm, mode, filter, address)
|
is := make([]uint16, len(indices))
|
||||||
|
copy(is, indices)
|
||||||
|
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, address)
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
func (m *mipmap) level(r image.Rectangle, level int) *buffered.Image {
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
panic("ebiten: level must be non-zero at level")
|
panic("ebiten: level must be non-zero at level")
|
||||||
}
|
}
|
||||||
@ -186,7 +188,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
|||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
|
|
||||||
var src *shareable.Image
|
var src *buffered.Image
|
||||||
var vs []float32
|
var vs []float32
|
||||||
var filter driver.Filter
|
var filter driver.Filter
|
||||||
switch {
|
switch {
|
||||||
@ -226,7 +228,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
|||||||
imgs[level] = nil
|
imgs[level] = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s := shareable.NewImage(w2, h2, m.volatile)
|
s := buffered.NewImage(w2, h2, m.volatile)
|
||||||
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressClampToZero)
|
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressClampToZero)
|
||||||
imgs[level] = s
|
imgs[level] = s
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/buffered"
|
||||||
"github.com/hajimehoshi/ebiten/internal/clock"
|
"github.com/hajimehoshi/ebiten/internal/clock"
|
||||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||||
"github.com/hajimehoshi/ebiten/internal/graphicscommand"
|
"github.com/hajimehoshi/ebiten/internal/graphicscommand"
|
||||||
@ -77,13 +78,10 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
|||||||
|
|
||||||
// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.
|
// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.
|
||||||
|
|
||||||
if err := shareable.BeginFrame(); err != nil {
|
if err := buffered.BeginFrame(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Images are available after shareable is initialized.
|
|
||||||
flushImageOpsIfNeeded()
|
|
||||||
|
|
||||||
for i := 0; i < updateCount; i++ {
|
for i := 0; i < updateCount; i++ {
|
||||||
c.offscreen.Clear()
|
c.offscreen.Clear()
|
||||||
// Mipmap images should be disposed by fill.
|
// Mipmap images should be disposed by fill.
|
||||||
@ -129,7 +127,7 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
|||||||
}
|
}
|
||||||
_ = c.screen.DrawImage(c.offscreen, op)
|
_ = c.screen.DrawImage(c.offscreen, op)
|
||||||
|
|
||||||
if err := shareable.EndFrame(); err != nil {
|
if err := buffered.EndFrame(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user