hajimehoshi / ebiten

Ebitengine - A dead simple 2D game engine for Go
https://ebitengine.org
Apache License 2.0
10.74k stars 649 forks source link

ebiten: remove `*ebiten.Image` functions that are needed for `image.Image` #2945

Open hajimehoshi opened 5 months ago

hajimehoshi commented 5 months ago

Operating System

What feature would you like to be added?

*ebiten.Image now implements image.Image which includes At and ColorModel. Especially At is problematic as At seems handy though it is very expensive to sync pixel data from GPU to CPU. So, we don't want encourage users to use At. *ebiten.Image implementing image.Image was not a good API in terms of execution costs.

Thus, we propose these API changes.

As these are breaking changes, we will do in v3.

/CC @Zyko0 @superloach @tinne26

Why is this needed?

An expensive API should not be handy since people often misuse such APIs.

hajimehoshi commented 5 months ago

https://github.com/hajimehoshi/ebiten/issues/2902 might be related

bjorndm commented 4 months ago

For an indexed bitmap editor for pixel art it is handy that ebiten.Image is an image.Image. If this is removed we will need to add conversion functions in both ways.

hajimehoshi commented 4 months ago

Yeah, this additional function call is an intentional change to make the execution cost more explicit.

bjorndm commented 4 months ago

As long as indexed bitmaps are properly supported both ways, this is fine by me

hajimehoshi commented 4 months ago

I have no idea about the bitmap editor, so I cannot say anything about this.

hajimehoshi commented 4 months ago

As ReadPixels is very special as it flushes the internal command queue, I might want to move this method to somewhere where we can call it in a limited situation (under Update or Draw). Actually, reading pixels was a blocker to implement handling events (https://github.com/hajimehoshi/ebiten/issues/1704#issuecomment-1751803675).

Another possible idea is to make ReadPixels asynchronous:

type ReadPixelsResult struct {
    Bytes []byte
    Err   error
}

func (*Image) ReadPixels(buf []byte) (<-chan ReadPixelsResult)
eihigh commented 4 months ago

Another possible idea is to make ReadPixels asynchronous:

I have a few questions:

hajimehoshi commented 4 months ago

What is the role of the buf argument in this case?

To avoid or reduce internal allocations.

What is the case where ReadPixels returns an error, for example?

The graphics driver might fail for some reasons. In most cases there is nothing a user can do.

If we just add a return value, compatibility is certainly preserved, but if the signature is going to change, it would be more straightforward to change the function name.

No, adding a returning value breaks the compatibility. There can be an interface that defines ReadPixels.

eihigh commented 4 months ago

To avoid or reduce internal allocations.

Will the allocated buf be written from another goroutine?

hajimehoshi commented 4 months ago

Will the allocated buf be written from another goroutine?

Yes and no. We can treat this like append: if the buf is not enough, ReadPixels can extend the buffer.

eihigh commented 4 months ago

OK, thanks for responding. To be honest I am not sure about the design of the asynchronous API, but my opinion is in favor of this proposal.

eihigh commented 4 months ago

Another example of Go's non-blocking API is a Start/Wait pair like exec.Command, I'm not sure if it's appropriate for Ebitengine, but just FYI.