mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
parent
de35a5a6f1
commit
b2f874a244
@ -534,11 +534,15 @@ func (c *writePixelsCommand) Exec(graphicsDriver graphicsdriver.Graphics, indexO
|
||||
type readPixelsCommand struct {
|
||||
result []byte
|
||||
img *Image
|
||||
x int
|
||||
y int
|
||||
width int
|
||||
height int
|
||||
}
|
||||
|
||||
// Exec executes a readPixelsCommand.
|
||||
func (c *readPixelsCommand) Exec(graphicsDriver graphicsdriver.Graphics, indexOffset int) error {
|
||||
if err := c.img.image.ReadPixels(c.result); err != nil {
|
||||
if err := c.img.image.ReadPixels(c.result, c.x, c.y, c.width, c.height); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -151,10 +151,14 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [g
|
||||
|
||||
// ReadPixels reads the image's pixels.
|
||||
// ReadPixels returns an error when an error happens in the graphics driver.
|
||||
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte) error {
|
||||
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte, x, y, width, height int) error {
|
||||
i.resolveBufferedWritePixels()
|
||||
c := &readPixelsCommand{
|
||||
img: i,
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height,
|
||||
result: buf,
|
||||
}
|
||||
theCommandQueue.Enqueue(c)
|
||||
@ -200,7 +204,7 @@ func (i *Image) dumpTo(w io.Writer, graphicsDriver graphicsdriver.Graphics, blac
|
||||
}
|
||||
|
||||
pix := make([]byte, 4*i.width*i.height)
|
||||
if err := i.ReadPixels(graphicsDriver, pix); err != nil {
|
||||
if err := i.ReadPixels(graphicsDriver, pix, 0, 0, i.width, i.height); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ func TestClear(t *testing.T) {
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeClear, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
|
||||
|
||||
pix := make([]byte, 4*w*h)
|
||||
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
|
||||
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < h/2; j++ {
|
||||
@ -111,7 +111,7 @@ func TestShader(t *testing.T) {
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, s, nil, false)
|
||||
|
||||
pix := make([]byte, 4*w*h)
|
||||
if err := dst.ReadPixels(g, pix); err != nil {
|
||||
if err := dst.ReadPixels(g, pix, 0, 0, w, h); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < h; j++ {
|
||||
|
@ -1528,7 +1528,7 @@ func (i *Image) ensureReadingStagingBuffer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Image) ReadPixels(buf []byte) error {
|
||||
func (i *Image) ReadPixels(buf []byte, x, y, width, height int) error {
|
||||
if i.screen {
|
||||
return errors.New("directx: Pixels cannot be called on the screen")
|
||||
}
|
||||
@ -1562,12 +1562,12 @@ func (i *Image) ReadPixels(buf []byte) error {
|
||||
}
|
||||
i.graphics.needFlushCopyCommandList = true
|
||||
i.graphics.copyCommandList.CopyTextureRegion_PlacedFootPrint_SubresourceIndex(
|
||||
&dst, 0, 0, 0, &src, &_D3D12_BOX{
|
||||
&dst, uint32(x), uint32(y), 0, &src, &_D3D12_BOX{
|
||||
left: 0,
|
||||
top: 0,
|
||||
front: 0,
|
||||
right: uint32(i.width),
|
||||
bottom: uint32(i.height),
|
||||
right: uint32(width),
|
||||
bottom: uint32(height),
|
||||
back: 1,
|
||||
})
|
||||
|
||||
@ -1581,8 +1581,8 @@ func (i *Image) ReadPixels(buf []byte) error {
|
||||
h.Len = int(i.totalBytes)
|
||||
h.Cap = int(i.totalBytes)
|
||||
|
||||
for j := 0; j < i.height; j++ {
|
||||
copy(buf[j*i.width*4:(j+1)*i.width*4], dstBytes[j*int(i.layouts.Footprint.RowPitch):])
|
||||
for j := 0; j < height; j++ {
|
||||
copy(buf[j*width*4:(j+1)*width*4], dstBytes[j*int(i.layouts.Footprint.RowPitch):])
|
||||
}
|
||||
|
||||
i.readingStagingBuffer.Unmap(0, nil)
|
||||
|
@ -69,7 +69,7 @@ type Image interface {
|
||||
ID() ImageID
|
||||
Dispose()
|
||||
IsInvalidated() bool
|
||||
ReadPixels(buf []byte) error
|
||||
ReadPixels(buf []byte, x, y, width, height int) error
|
||||
WritePixels(args []*WritePixelsArgs) error
|
||||
}
|
||||
|
||||
|
@ -1104,16 +1104,17 @@ func (i *Image) syncTexture() {
|
||||
cb.WaitUntilCompleted()
|
||||
}
|
||||
|
||||
func (i *Image) ReadPixels(buf []byte) error {
|
||||
if got, want := len(buf), 4*i.width*i.height; got != want {
|
||||
func (i *Image) ReadPixels(buf []byte, x, y, width, height int) error {
|
||||
if got, want := len(buf), 4*width*height; got != want {
|
||||
return fmt.Errorf("metal: len(buf) must be %d but %d at ReadPixels", want, got)
|
||||
}
|
||||
|
||||
i.graphics.flushIfNeeded(false)
|
||||
i.syncTexture()
|
||||
|
||||
i.texture.GetBytes(&buf[0], uintptr(4*i.width), mtl.Region{
|
||||
Size: mtl.Size{Width: i.width, Height: i.height, Depth: 1},
|
||||
i.texture.GetBytes(&buf[0], uintptr(4*width), mtl.Region{
|
||||
Origin: mtl.Origin{X: x, Y: y},
|
||||
Size: mtl.Size{Width: width, Height: height, Depth: 1},
|
||||
}, 0)
|
||||
return nil
|
||||
}
|
||||
|
@ -162,10 +162,10 @@ func (c *context) bindFramebufferImpl(f framebufferNative) {
|
||||
gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f))
|
||||
}
|
||||
|
||||
func (c *context) framebufferPixels(buf []byte, f *framebuffer, width, height int) {
|
||||
func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) {
|
||||
gl.Flush()
|
||||
c.bindFramebuffer(f.native)
|
||||
gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(buf))
|
||||
gl.ReadPixels(int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(buf))
|
||||
}
|
||||
|
||||
func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
|
||||
|
@ -233,14 +233,14 @@ func (c *context) bindFramebufferImpl(f framebufferNative) {
|
||||
gl.bindFramebuffer.Invoke(gles.FRAMEBUFFER, js.Value(f))
|
||||
}
|
||||
|
||||
func (c *context) framebufferPixels(buf []byte, f *framebuffer, width, height int) {
|
||||
func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) {
|
||||
gl := c.gl
|
||||
|
||||
c.bindFramebuffer(f.native)
|
||||
|
||||
l := 4 * width * height
|
||||
p := jsutil.TemporaryUint8ArrayFromUint8Slice(l, nil)
|
||||
gl.readPixels.Invoke(0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, p)
|
||||
gl.readPixels.Invoke(x, y, width, height, gles.RGBA, gles.UNSIGNED_BYTE, p)
|
||||
copy(buf, uint8ArrayToSlice(p, l))
|
||||
}
|
||||
|
||||
|
@ -148,12 +148,12 @@ func (c *context) bindFramebufferImpl(f framebufferNative) {
|
||||
c.ctx.BindFramebuffer(gles.FRAMEBUFFER, uint32(f))
|
||||
}
|
||||
|
||||
func (c *context) framebufferPixels(buf []byte, f *framebuffer, width, height int) {
|
||||
func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) {
|
||||
c.ctx.Flush()
|
||||
|
||||
c.bindFramebuffer(f.native)
|
||||
|
||||
c.ctx.ReadPixels(buf, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE)
|
||||
c.ctx.ReadPixels(buf, int32(x), int32(y), int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE)
|
||||
}
|
||||
|
||||
func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
|
||||
|
@ -62,12 +62,12 @@ func (i *Image) setViewport() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Image) ReadPixels(buf []byte) error {
|
||||
func (i *Image) ReadPixels(buf []byte, x, y, width, height int) error {
|
||||
if err := i.ensureFramebuffer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.graphics.context.framebufferPixels(buf, i.framebuffer, i.width, i.height)
|
||||
i.graphics.context.framebufferPixels(buf, i.framebuffer, x, y, width, height)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,13 @@ func (p *Pixels) ReadPixels(pixels []byte, x, y, width, height, imageWidth, imag
|
||||
p.pixelsRecords.readPixels(pixels, x, y, width, height, imageWidth, imageHeight)
|
||||
}
|
||||
|
||||
func (p *Pixels) Region() image.Rectangle {
|
||||
if p.pixelsRecords == nil {
|
||||
return image.Rectangle{}
|
||||
}
|
||||
return p.pixelsRecords.region()
|
||||
}
|
||||
|
||||
// drawTrianglesHistoryItem is an item for history of draw-image commands.
|
||||
type drawTrianglesHistoryItem struct {
|
||||
images [graphics.ShaderImageCount]*Image
|
||||
@ -115,6 +122,10 @@ type Image struct {
|
||||
// stale indicates whether the image needs to be synced with GPU as soon as possible.
|
||||
stale bool
|
||||
|
||||
// staleRegion indicates the region to restore.
|
||||
// staleRegion is valid only when stale is true.
|
||||
staleRegion image.Rectangle
|
||||
|
||||
imageType ImageType
|
||||
|
||||
// priority indicates whether the image is restored in high priority when context-lost happens.
|
||||
@ -255,10 +266,12 @@ func (i *Image) BasePixelsForTesting() *Pixels {
|
||||
}
|
||||
|
||||
// makeStale makes the image stale.
|
||||
func (i *Image) makeStale() {
|
||||
func (i *Image) makeStale(rect image.Rectangle) {
|
||||
i.stale = true
|
||||
i.staleRegion = i.staleRegion.Union(i.basePixels.Region()).Union(rect)
|
||||
|
||||
i.basePixels = Pixels{}
|
||||
i.clearDrawTrianglesHistory()
|
||||
i.stale = true
|
||||
|
||||
// Don't have to call makeStale recursively here.
|
||||
// Restoring is done after topological sorting is done.
|
||||
@ -301,7 +314,7 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
|
||||
}
|
||||
|
||||
if !needsRestoring() || !i.needsRestoring() {
|
||||
i.makeStale()
|
||||
i.makeStale(image.Rect(x, y, x+width, y+height))
|
||||
return
|
||||
}
|
||||
|
||||
@ -317,6 +330,7 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
|
||||
}
|
||||
i.clearDrawTrianglesHistory()
|
||||
i.stale = false
|
||||
i.staleRegion = image.Rectangle{}
|
||||
return
|
||||
}
|
||||
|
||||
@ -375,7 +389,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [g
|
||||
}
|
||||
|
||||
if srcstale || !needsRestoring() || !i.needsRestoring() {
|
||||
i.makeStale()
|
||||
i.makeStale(image.Rect(0, 0, i.width, i.height))
|
||||
} else {
|
||||
i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, shader, uniforms, evenOdd)
|
||||
}
|
||||
@ -406,7 +420,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageCount]*Imag
|
||||
// TODO: Would it be possible to merge draw image history items?
|
||||
const maxDrawTrianglesHistoryCount = 1024
|
||||
if len(i.drawTrianglesHistory)+1 > maxDrawTrianglesHistoryCount {
|
||||
i.makeStale()
|
||||
i.makeStale(image.Rect(0, 0, i.width, i.height))
|
||||
return
|
||||
}
|
||||
// All images must be resolved and not stale each after frame.
|
||||
@ -465,7 +479,7 @@ func (i *Image) makeStaleIfDependingOn(target *Image) {
|
||||
return
|
||||
}
|
||||
if i.dependsOn(target) {
|
||||
i.makeStale()
|
||||
i.makeStale(image.Rect(0, 0, i.width, i.height))
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,20 +489,27 @@ func (i *Image) makeStaleIfDependingOnShader(shader *Shader) {
|
||||
return
|
||||
}
|
||||
if i.dependsOnShader(shader) {
|
||||
i.makeStale()
|
||||
i.makeStale(image.Rect(0, 0, i.width, i.height))
|
||||
}
|
||||
}
|
||||
|
||||
// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
|
||||
func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error {
|
||||
pix := make([]byte, 4*i.width*i.height)
|
||||
if err := i.image.ReadPixels(graphicsDriver, pix); err != nil {
|
||||
if !i.stale {
|
||||
panic("restorable: the image must be stale at readPixelsFromGPU")
|
||||
}
|
||||
|
||||
i.basePixels = Pixels{}
|
||||
if r := i.staleRegion; !r.Empty() {
|
||||
pix := make([]byte, 4*r.Dx()*r.Dy())
|
||||
if err := i.image.ReadPixels(graphicsDriver, pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
|
||||
return err
|
||||
}
|
||||
i.basePixels = Pixels{}
|
||||
i.basePixels.AddOrReplace(pix, 0, 0, i.width, i.height)
|
||||
i.basePixels.AddOrReplace(pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy())
|
||||
}
|
||||
i.clearDrawTrianglesHistory()
|
||||
i.stale = false
|
||||
i.staleRegion = image.Rectangle{}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -566,6 +587,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
|
||||
i.basePixels = Pixels{}
|
||||
i.clearDrawTrianglesHistory()
|
||||
i.stale = false
|
||||
i.staleRegion = image.Rectangle{}
|
||||
return nil
|
||||
case ImageTypeVolatile:
|
||||
i.image = graphicscommand.NewImage(w, h, false)
|
||||
@ -608,7 +630,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
|
||||
if len(i.drawTrianglesHistory) > 0 {
|
||||
i.basePixels = Pixels{}
|
||||
pix := make([]byte, 4*w*h)
|
||||
if err := gimg.ReadPixels(graphicsDriver, pix); err != nil {
|
||||
if err := gimg.ReadPixels(graphicsDriver, pix, 0, 0, w, h); err != nil {
|
||||
return err
|
||||
}
|
||||
i.basePixels.AddOrReplace(pix, 0, 0, w, h)
|
||||
@ -617,6 +639,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
|
||||
i.image = gimg
|
||||
i.clearDrawTrianglesHistory()
|
||||
i.stale = false
|
||||
i.staleRegion = image.Rectangle{}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -630,6 +653,7 @@ func (i *Image) Dispose() {
|
||||
i.basePixels = Pixels{}
|
||||
i.clearDrawTrianglesHistory()
|
||||
i.stale = false
|
||||
i.staleRegion = image.Rectangle{}
|
||||
}
|
||||
|
||||
// isInvalidated returns a boolean value indicating whether the image is invalidated.
|
||||
|
@ -128,3 +128,11 @@ func (pr *pixelsRecords) apply(img *graphicscommand.Image) {
|
||||
img.WritePixels(r.pix, r.rect.Min.X, r.rect.Min.Y, r.rect.Dx(), r.rect.Dy())
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *pixelsRecords) region() image.Rectangle {
|
||||
var rect image.Rectangle
|
||||
for _, r := range pr.records {
|
||||
rect = rect.Union(r.rect)
|
||||
}
|
||||
return rect
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user