mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 10:48:53 +01:00
driver: Add ImageID and use this
This is a preparation to introduce shaders. Shader programs require images as uniform variables, but the current way would make API complex unnecessarily.
This commit is contained in:
parent
7ccc29e3c7
commit
8fd377f1e3
@ -30,7 +30,7 @@ type Graphics interface {
|
||||
NewImage(width, height int) (Image, error)
|
||||
NewScreenFramebufferImage(width, height int) (Image, error)
|
||||
Reset() error
|
||||
Draw(indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address) error
|
||||
Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address) error
|
||||
SetVsyncEnabled(enabled bool)
|
||||
FramebufferYDirection() YDirection
|
||||
NeedsRestoring() bool
|
||||
@ -43,14 +43,15 @@ type Graphics interface {
|
||||
var GraphicsNotReady = errors.New("graphics not ready")
|
||||
|
||||
type Image interface {
|
||||
ID() ImageID
|
||||
Dispose()
|
||||
IsInvalidated() bool
|
||||
Pixels() ([]byte, error)
|
||||
SetAsDestination()
|
||||
SetAsSource()
|
||||
ReplacePixels(args []*ReplacePixelsArgs)
|
||||
}
|
||||
|
||||
type ImageID int
|
||||
|
||||
type ReplacePixelsArgs struct {
|
||||
Pixels []byte
|
||||
X int
|
||||
|
@ -374,9 +374,7 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.dst.image.SetAsDestination()
|
||||
c.src.image.SetAsSource()
|
||||
if err := theGraphicsDriver.Draw(c.nindices, indexOffset, c.mode, c.color, c.filter, c.address); err != nil {
|
||||
if err := theGraphicsDriver.Draw(c.dst.image.ID(), c.src.image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -43,6 +43,11 @@ type Image struct {
|
||||
internalWidth int
|
||||
internalHeight int
|
||||
screen bool
|
||||
|
||||
// id is an indentifier for the image. This is used only when dummping the information.
|
||||
//
|
||||
// This is duplicated with driver.Image's ID, but this id is still necessary because this image might not
|
||||
// have its driver.Image.
|
||||
id int
|
||||
|
||||
bufferedRP []*driver.ReplacePixelsArgs
|
||||
|
@ -294,6 +294,10 @@ type Graphics struct {
|
||||
|
||||
vb mtl.Buffer
|
||||
ib mtl.Buffer
|
||||
|
||||
images map[driver.ImageID]*Image
|
||||
nextImageID driver.ImageID
|
||||
|
||||
src *Image
|
||||
dst *Image
|
||||
|
||||
@ -399,6 +403,12 @@ func (g *Graphics) checkSize(width, height int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Graphics) genNextImageID() driver.ImageID {
|
||||
id := g.nextImageID
|
||||
g.nextImageID++
|
||||
return id
|
||||
}
|
||||
|
||||
func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
|
||||
g.checkSize(width, height)
|
||||
td := mtl.TextureDescriptor{
|
||||
@ -413,12 +423,15 @@ func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
|
||||
t = g.view.getMTLDevice().MakeTexture(td)
|
||||
return nil
|
||||
})
|
||||
return &Image{
|
||||
i := &Image{
|
||||
id: g.genNextImageID(),
|
||||
graphics: g,
|
||||
width: width,
|
||||
height: height,
|
||||
texture: t,
|
||||
}, nil
|
||||
}
|
||||
g.addImage(i)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) {
|
||||
@ -426,12 +439,29 @@ func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, e
|
||||
g.view.setDrawableSize(width, height)
|
||||
return nil
|
||||
})
|
||||
return &Image{
|
||||
i := &Image{
|
||||
id: g.genNextImageID(),
|
||||
graphics: g,
|
||||
width: width,
|
||||
height: height,
|
||||
screen: true,
|
||||
}, nil
|
||||
}
|
||||
g.addImage(i)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (g *Graphics) addImage(img *Image) {
|
||||
if g.images == nil {
|
||||
g.images = map[driver.ImageID]*Image{}
|
||||
}
|
||||
if _, ok := g.images[img.id]; ok {
|
||||
panic(fmt.Sprintf("opengl: image ID %d was already registered", img.id))
|
||||
}
|
||||
g.images[img.id] = img
|
||||
}
|
||||
|
||||
func (g *Graphics) removeImage(img *Image) {
|
||||
delete(g.images, img.id)
|
||||
}
|
||||
|
||||
func (g *Graphics) SetTransparent(transparent bool) {
|
||||
@ -579,7 +609,10 @@ func (g *Graphics) Reset() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
|
||||
func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
|
||||
dst := g.images[dstID]
|
||||
src := g.images[srcID]
|
||||
|
||||
g.drawCalled = true
|
||||
|
||||
if err := g.t.Call(func() error {
|
||||
@ -592,7 +625,7 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
|
||||
rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore
|
||||
|
||||
var t mtl.Texture
|
||||
if g.dst.screen {
|
||||
if dst.screen {
|
||||
if g.screenDrawable == (ca.MetalDrawable{}) {
|
||||
drawable := g.view.drawable()
|
||||
if drawable == (ca.MetalDrawable{}) {
|
||||
@ -602,7 +635,7 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
|
||||
}
|
||||
t = g.screenDrawable.Texture()
|
||||
} else {
|
||||
t = g.dst.texture
|
||||
t = dst.texture
|
||||
}
|
||||
rpd.ColorAttachments[0].Texture = t
|
||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
|
||||
@ -612,11 +645,11 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
|
||||
}
|
||||
rce := g.cb.MakeRenderCommandEncoder(rpd)
|
||||
|
||||
if g.dst.screen && filter == driver.FilterScreen {
|
||||
if dst.screen && filter == driver.FilterScreen {
|
||||
rce.SetRenderPipelineState(g.screenRPS)
|
||||
} else {
|
||||
rce.SetRenderPipelineState(g.rpss[rpsKey{
|
||||
screen: g.dst.screen,
|
||||
screen: dst.screen,
|
||||
useColorM: colorM != nil,
|
||||
filter: filter,
|
||||
address: address,
|
||||
@ -625,7 +658,7 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
|
||||
}
|
||||
// In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't
|
||||
// match. Then, the Y direction must be inverted.
|
||||
w, h := g.dst.viewportSize()
|
||||
w, h := dst.viewportSize()
|
||||
rce.SetViewport(mtl.Viewport{
|
||||
OriginX: 0,
|
||||
OriginY: float64(h),
|
||||
@ -640,8 +673,8 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
|
||||
rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1)
|
||||
|
||||
sourceSize := [...]float32{
|
||||
float32(graphics.InternalImageSize(g.src.width)),
|
||||
float32(graphics.InternalImageSize(g.src.height)),
|
||||
float32(graphics.InternalImageSize(src.width)),
|
||||
float32(graphics.InternalImageSize(src.height)),
|
||||
}
|
||||
rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2)
|
||||
|
||||
@ -649,11 +682,11 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
|
||||
rce.SetFragmentBytes(unsafe.Pointer(&esBody[0]), unsafe.Sizeof(esBody[0])*uintptr(len(esBody)), 3)
|
||||
rce.SetFragmentBytes(unsafe.Pointer(&esTranslate[0]), unsafe.Sizeof(esTranslate[0])*uintptr(len(esTranslate)), 4)
|
||||
|
||||
scale := float32(g.dst.width) / float32(g.src.width)
|
||||
scale := float32(dst.width) / float32(src.width)
|
||||
rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5)
|
||||
|
||||
if g.src != nil {
|
||||
rce.SetFragmentTexture(g.src.texture, 0)
|
||||
if src != nil {
|
||||
rce.SetFragmentTexture(src.texture, 0)
|
||||
} else {
|
||||
rce.SetFragmentTexture(mtl.Texture{}, 0)
|
||||
}
|
||||
@ -668,13 +701,6 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graphics) ResetSource() {
|
||||
g.t.Call(func() error {
|
||||
g.src = nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Graphics) SetVsyncEnabled(enabled bool) {
|
||||
g.view.setDisplaySyncEnabled(enabled)
|
||||
}
|
||||
@ -733,6 +759,7 @@ func (g *Graphics) MaxImageSize() int {
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
id driver.ImageID
|
||||
graphics *Graphics
|
||||
width int
|
||||
height int
|
||||
@ -740,6 +767,10 @@ type Image struct {
|
||||
texture mtl.Texture
|
||||
}
|
||||
|
||||
func (i *Image) ID() driver.ImageID {
|
||||
return i.id
|
||||
}
|
||||
|
||||
// viewportSize must be called from the main thread.
|
||||
func (i *Image) viewportSize() (int, int) {
|
||||
if i.screen {
|
||||
@ -756,6 +787,7 @@ func (i *Image) Dispose() {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
i.graphics.removeImage(i)
|
||||
}
|
||||
|
||||
func (i *Image) IsInvalidated() bool {
|
||||
@ -795,20 +827,6 @@ func (i *Image) Pixels() ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (i *Image) SetAsDestination() {
|
||||
i.graphics.t.Call(func() error {
|
||||
i.graphics.dst = i
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Image) SetAsSource() {
|
||||
i.graphics.t.Call(func() error {
|
||||
i.graphics.src = i
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
|
||||
g := i.graphics
|
||||
if g.drawCalled {
|
||||
|
@ -30,8 +30,10 @@ func Get() *Graphics {
|
||||
}
|
||||
|
||||
type Graphics struct {
|
||||
nextImageID driver.ImageID
|
||||
state openGLState
|
||||
context context
|
||||
images map[driver.ImageID]*Image
|
||||
|
||||
// drawCalled is true just after Draw is called. This holds true until ReplacePixels is called.
|
||||
drawCalled bool
|
||||
@ -71,8 +73,15 @@ func (g *Graphics) checkSize(width, height int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Graphics) genNextImageID() driver.ImageID {
|
||||
id := g.nextImageID
|
||||
g.nextImageID++
|
||||
return id
|
||||
}
|
||||
|
||||
func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
|
||||
i := &Image{
|
||||
id: g.genNextImageID(),
|
||||
graphics: g,
|
||||
width: width,
|
||||
height: height,
|
||||
@ -85,20 +94,37 @@ func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
|
||||
return nil, err
|
||||
}
|
||||
i.textureNative = t
|
||||
g.addImage(i)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) {
|
||||
g.checkSize(width, height)
|
||||
i := &Image{
|
||||
id: g.genNextImageID(),
|
||||
graphics: g,
|
||||
width: width,
|
||||
height: height,
|
||||
screen: true,
|
||||
}
|
||||
g.addImage(i)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (g *Graphics) addImage(img *Image) {
|
||||
if g.images == nil {
|
||||
g.images = map[driver.ImageID]*Image{}
|
||||
}
|
||||
if _, ok := g.images[img.id]; ok {
|
||||
panic(fmt.Sprintf("opengl: image ID %d was already registered", img.id))
|
||||
}
|
||||
g.images[img.id] = img
|
||||
}
|
||||
|
||||
func (g *Graphics) removeImage(img *Image) {
|
||||
delete(g.images, img.id)
|
||||
}
|
||||
|
||||
// Reset resets or initializes the current OpenGL state.
|
||||
func (g *Graphics) Reset() error {
|
||||
return g.state.reset(&g.context)
|
||||
@ -112,15 +138,9 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
|
||||
g.context.elementArrayBufferSubData(indices)
|
||||
}
|
||||
|
||||
func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
|
||||
destination := g.state.destination
|
||||
if destination == nil {
|
||||
panic("destination image is not set")
|
||||
}
|
||||
source := g.state.source
|
||||
if source == nil {
|
||||
panic("source image is not set")
|
||||
}
|
||||
func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
|
||||
destination := g.images[dst]
|
||||
source := g.images[src]
|
||||
|
||||
g.drawCalled = true
|
||||
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
)
|
||||
|
||||
type Image struct {
|
||||
id driver.ImageID
|
||||
graphics *Graphics
|
||||
textureNative textureNative
|
||||
framebuffer *framebuffer
|
||||
@ -29,6 +30,10 @@ type Image struct {
|
||||
screen bool
|
||||
}
|
||||
|
||||
func (i *Image) ID() driver.ImageID {
|
||||
return i.id
|
||||
}
|
||||
|
||||
func (i *Image) IsInvalidated() bool {
|
||||
return !i.graphics.context.isTexture(i.textureNative)
|
||||
}
|
||||
@ -43,10 +48,8 @@ func (i *Image) Dispose() {
|
||||
if !i.textureNative.equal(*new(textureNative)) {
|
||||
i.graphics.context.deleteTexture(i.textureNative)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Image) SetAsDestination() {
|
||||
i.graphics.state.destination = i
|
||||
i.graphics.removeImage(i)
|
||||
}
|
||||
|
||||
func (i *Image) setViewport() error {
|
||||
@ -119,7 +122,3 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
|
||||
|
||||
i.graphics.context.replacePixelsWithPBO(i.pbo, i.textureNative, w, h, args)
|
||||
}
|
||||
|
||||
func (i *Image) SetAsSource() {
|
||||
i.graphics.state.source = i
|
||||
}
|
||||
|
@ -137,9 +137,6 @@ type openGLState struct {
|
||||
lastProgram program
|
||||
lastUniforms map[string]interface{}
|
||||
lastActiveTexture int
|
||||
|
||||
source *Image
|
||||
destination *Image
|
||||
}
|
||||
|
||||
var (
|
||||
@ -292,8 +289,5 @@ func (g *Graphics) useProgram(program program, uniforms map[string]interface{})
|
||||
g.context.bindTexture(u)
|
||||
}
|
||||
}
|
||||
|
||||
g.state.source = nil
|
||||
g.state.destination = nil
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user