graphicsdriver: Refactoring: ReplacePixels takes multiple arguments

This reduces the number of graphics commands, and this works more
efficiently if the driver has an efficient way.
This commit is contained in:
Hajime Hoshi 2019-11-21 23:42:46 +09:00
parent 161771cc99
commit 65fdf48cbf
5 changed files with 56 additions and 61 deletions

View File

@ -44,7 +44,15 @@ type Image interface {
Pixels() ([]byte, error) Pixels() ([]byte, error)
SetAsDestination() SetAsDestination()
SetAsSource() SetAsSource()
ReplacePixels(pixels []byte, x, y, width, height int) ReplacePixels(args []*ReplacePixelsArgs)
}
type ReplacePixelsArgs struct {
Pixels []byte
X int
Y int
Width int
Height int
} }
type VDirection int type VDirection int

View File

@ -434,21 +434,17 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image,
// replacePixelsCommand represents a command to replace pixels of an image. // replacePixelsCommand represents a command to replace pixels of an image.
type replacePixelsCommand struct { type replacePixelsCommand struct {
dst *Image dst *Image
pixels []byte args []*driver.ReplacePixelsArgs
x int
y int
width int
height int
} }
func (c *replacePixelsCommand) String() string { func (c *replacePixelsCommand) String() string {
return fmt.Sprintf("replace-pixels: dst: %d, x: %d, y: %d, width: %d, height: %d", c.dst.id, c.x, c.y, c.width, c.height) return fmt.Sprintf("replace-pixels: dst: %d, len(args): %d", c.dst.id, len(c.args))
} }
// Exec executes the replacePixelsCommand. // Exec executes the replacePixelsCommand.
func (c *replacePixelsCommand) Exec(indexOffset int) error { func (c *replacePixelsCommand) Exec(indexOffset int) error {
c.dst.image.ReplacePixels(c.pixels, c.x, c.y, c.width, c.height) c.dst.image.ReplacePixels(c.args)
return nil return nil
} }

View File

@ -45,6 +45,8 @@ type Image struct {
screen bool screen bool
id int id int
bufferedRP []*driver.ReplacePixelsArgs
lastCommand lastCommand lastCommand lastCommand
} }
@ -90,6 +92,18 @@ func NewScreenFramebufferImage(width, height int) *Image {
return i return i
} }
func (i *Image) resolveBufferedReplacePixels() {
if len(i.bufferedRP) == 0 {
return
}
c := &replacePixelsCommand{
dst: i,
args: i.bufferedRP,
}
theCommandQueue.Enqueue(c)
i.bufferedRP = nil
}
func (i *Image) Dispose() { func (i *Image) Dispose() {
c := &disposeCommand{ c := &disposeCommand{
target: i, target: i,
@ -134,6 +148,9 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
} }
} }
src.resolveBufferedReplacePixels()
i.resolveBufferedReplacePixels()
theCommandQueue.EnqueueDrawTrianglesCommand(i, src, vertices, indices, clr, mode, filter, address) theCommandQueue.EnqueueDrawTrianglesCommand(i, src, vertices, indices, clr, mode, filter, address)
if i.lastCommand == lastCommandNone && !i.screen { if i.lastCommand == lastCommandNone && !i.screen {
@ -146,6 +163,7 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
// Pixels returns the image's pixels. // Pixels returns the image's pixels.
// Pixels might return nil when OpenGL error happens. // Pixels might return nil when OpenGL error happens.
func (i *Image) Pixels() []byte { func (i *Image) Pixels() []byte {
i.resolveBufferedReplacePixels()
c := &pixelsCommand{ c := &pixelsCommand{
result: nil, result: nil,
img: i, img: i,
@ -162,15 +180,13 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
panic("graphicscommand: ReplacePixels for a part after DrawTriangles is forbidden") panic("graphicscommand: ReplacePixels for a part after DrawTriangles is forbidden")
} }
} }
c := &replacePixelsCommand{ i.bufferedRP = append(i.bufferedRP, &driver.ReplacePixelsArgs{
dst: i, Pixels: pixels,
pixels: pixels, X: x,
x: x, Y: y,
y: y, Width: width,
width: width, Height: height,
height: height, })
}
theCommandQueue.Enqueue(c)
i.lastCommand = lastCommandReplacePixels i.lastCommand = lastCommandReplacePixels
} }

View File

@ -809,7 +809,7 @@ func (i *Image) SetAsSource() {
}) })
} }
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
d := i.driver d := i.driver
if d.drawCalled { if d.drawCalled {
d.flush(true, false) d.flush(true, false)
@ -817,10 +817,12 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
} }
d.t.Call(func() error { d.t.Call(func() error {
i.texture.ReplaceRegion(mtl.Region{ for _, a := range args {
Origin: mtl.Origin{X: x, Y: y, Z: 0}, i.texture.ReplaceRegion(mtl.Region{
Size: mtl.Size{Width: width, Height: height, Depth: 1}, Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0},
}, 0, unsafe.Pointer(&pixels[0]), 4*width) Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1},
}, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width)
}
return nil return nil
}) })
} }

View File

@ -15,17 +15,10 @@
package opengl package opengl
import ( import (
"github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
) )
type bufferedRP struct {
pixels []byte
x int
y int
width int
height int
}
type Image struct { type Image struct {
driver *Driver driver *Driver
textureNative textureNative textureNative textureNative
@ -34,8 +27,6 @@ type Image struct {
width int width int
height int height int
screen bool screen bool
bufferedRPs []bufferedRP
} }
func (i *Image) IsInvalidated() bool { func (i *Image) IsInvalidated() bool {
@ -55,7 +46,6 @@ func (i *Image) Dispose() {
} }
func (i *Image) SetAsDestination() { func (i *Image) SetAsDestination() {
i.resolveReplacePixels()
i.driver.state.destination = i i.driver.state.destination = i
} }
@ -68,7 +58,6 @@ func (i *Image) setViewport() error {
} }
func (i *Image) Pixels() ([]byte, error) { func (i *Image) Pixels() ([]byte, error) {
i.resolveReplacePixels()
if err := i.ensureFramebuffer(); err != nil { if err := i.ensureFramebuffer(); err != nil {
return nil, err return nil, err
} }
@ -101,34 +90,14 @@ func (i *Image) ensureFramebuffer() error {
return nil return nil
} }
func (i *Image) ReplacePixels(p []byte, x, y, width, height int) { func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
if i.screen { if i.screen {
panic("opengl: ReplacePixels cannot be called on the screen, that doesn't have a texture") panic("opengl: ReplacePixels cannot be called on the screen, that doesn't have a texture")
} }
if len(args) == 0 {
i.bufferedRPs = append(i.bufferedRPs, bufferedRP{
pixels: p,
x: x,
y: y,
width: width,
height: height,
})
}
func (i *Image) SetAsSource() {
i.resolveReplacePixels()
i.driver.state.source = i
}
func (i *Image) resolveReplacePixels() {
if len(i.bufferedRPs) == 0 {
return return
} }
defer func() {
i.bufferedRPs = nil
}()
// glFlush is necessary on Android. // glFlush is necessary on Android.
// glTexSubImage2D didn't work without this hack at least on Nexus 5x and NuAns NEO [Reloaded] (#211). // glTexSubImage2D didn't work without this hack at least on Nexus 5x and NuAns NEO [Reloaded] (#211).
if i.driver.drawCalled { if i.driver.drawCalled {
@ -137,15 +106,19 @@ func (i *Image) resolveReplacePixels() {
i.driver.drawCalled = false i.driver.drawCalled = false
if !canUsePBO { if !canUsePBO {
for _, rp := range i.bufferedRPs { for _, a := range args {
i.driver.context.texSubImage2D(i.textureNative, rp.pixels, rp.x, rp.y, rp.width, rp.height) i.driver.context.texSubImage2D(i.textureNative, a.Pixels, a.X, a.Y, a.Width, a.Height)
} }
return return
} }
thePBOState.mapPBO(i) thePBOState.mapPBO(i)
for _, rp := range i.bufferedRPs { for _, a := range args {
thePBOState.draw(rp.pixels, rp.x, rp.y, rp.width, rp.height) thePBOState.draw(a.Pixels, a.X, a.Y, a.Width, a.Height)
} }
thePBOState.unmapPBO() thePBOState.unmapPBO()
} }
func (i *Image) SetAsSource() {
i.driver.state.source = i
}