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:
Hajime Hoshi 2020-05-19 23:48:43 +09:00
parent 7ccc29e3c7
commit 8fd377f1e3
7 changed files with 104 additions and 69 deletions

View File

@ -30,7 +30,7 @@ type Graphics interface {
NewImage(width, height int) (Image, error) NewImage(width, height int) (Image, error)
NewScreenFramebufferImage(width, height int) (Image, error) NewScreenFramebufferImage(width, height int) (Image, error)
Reset() 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) SetVsyncEnabled(enabled bool)
FramebufferYDirection() YDirection FramebufferYDirection() YDirection
NeedsRestoring() bool NeedsRestoring() bool
@ -43,14 +43,15 @@ type Graphics interface {
var GraphicsNotReady = errors.New("graphics not ready") var GraphicsNotReady = errors.New("graphics not ready")
type Image interface { type Image interface {
ID() ImageID
Dispose() Dispose()
IsInvalidated() bool IsInvalidated() bool
Pixels() ([]byte, error) Pixels() ([]byte, error)
SetAsDestination()
SetAsSource()
ReplacePixels(args []*ReplacePixelsArgs) ReplacePixels(args []*ReplacePixelsArgs)
} }
type ImageID int
type ReplacePixelsArgs struct { type ReplacePixelsArgs struct {
Pixels []byte Pixels []byte
X int X int

View File

@ -374,9 +374,7 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
return nil return nil
} }
c.dst.image.SetAsDestination() 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 {
c.src.image.SetAsSource()
if err := theGraphicsDriver.Draw(c.nindices, indexOffset, c.mode, c.color, c.filter, c.address); err != nil {
return err return err
} }
return nil return nil

View File

@ -43,7 +43,12 @@ type Image struct {
internalWidth int internalWidth int
internalHeight int internalHeight int
screen bool screen bool
id int
// 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 bufferedRP []*driver.ReplacePixelsArgs

View File

@ -292,8 +292,12 @@ type Graphics struct {
screenDrawable ca.MetalDrawable screenDrawable ca.MetalDrawable
vb mtl.Buffer vb mtl.Buffer
ib mtl.Buffer ib mtl.Buffer
images map[driver.ImageID]*Image
nextImageID driver.ImageID
src *Image src *Image
dst *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) { func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
g.checkSize(width, height) g.checkSize(width, height)
td := mtl.TextureDescriptor{ td := mtl.TextureDescriptor{
@ -413,12 +423,15 @@ func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
t = g.view.getMTLDevice().MakeTexture(td) t = g.view.getMTLDevice().MakeTexture(td)
return nil return nil
}) })
return &Image{ i := &Image{
id: g.genNextImageID(),
graphics: g, graphics: g,
width: width, width: width,
height: height, height: height,
texture: t, texture: t,
}, nil }
g.addImage(i)
return i, nil
} }
func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) { 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) g.view.setDrawableSize(width, height)
return nil return nil
}) })
return &Image{ i := &Image{
id: g.genNextImageID(),
graphics: g, graphics: g,
width: width, width: width,
height: height, height: height,
screen: true, 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) { func (g *Graphics) SetTransparent(transparent bool) {
@ -579,7 +609,10 @@ func (g *Graphics) Reset() error {
return nil 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 g.drawCalled = true
if err := g.t.Call(func() error { 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 rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore
var t mtl.Texture var t mtl.Texture
if g.dst.screen { if dst.screen {
if g.screenDrawable == (ca.MetalDrawable{}) { if g.screenDrawable == (ca.MetalDrawable{}) {
drawable := g.view.drawable() drawable := g.view.drawable()
if drawable == (ca.MetalDrawable{}) { if drawable == (ca.MetalDrawable{}) {
@ -602,7 +635,7 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
} }
t = g.screenDrawable.Texture() t = g.screenDrawable.Texture()
} else { } else {
t = g.dst.texture t = dst.texture
} }
rpd.ColorAttachments[0].Texture = t rpd.ColorAttachments[0].Texture = t
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{} 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) rce := g.cb.MakeRenderCommandEncoder(rpd)
if g.dst.screen && filter == driver.FilterScreen { if dst.screen && filter == driver.FilterScreen {
rce.SetRenderPipelineState(g.screenRPS) rce.SetRenderPipelineState(g.screenRPS)
} else { } else {
rce.SetRenderPipelineState(g.rpss[rpsKey{ rce.SetRenderPipelineState(g.rpss[rpsKey{
screen: g.dst.screen, screen: dst.screen,
useColorM: colorM != nil, useColorM: colorM != nil,
filter: filter, filter: filter,
address: address, 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 // 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. // match. Then, the Y direction must be inverted.
w, h := g.dst.viewportSize() w, h := dst.viewportSize()
rce.SetViewport(mtl.Viewport{ rce.SetViewport(mtl.Viewport{
OriginX: 0, OriginX: 0,
OriginY: float64(h), 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) rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1)
sourceSize := [...]float32{ sourceSize := [...]float32{
float32(graphics.InternalImageSize(g.src.width)), float32(graphics.InternalImageSize(src.width)),
float32(graphics.InternalImageSize(g.src.height)), float32(graphics.InternalImageSize(src.height)),
} }
rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2) 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(&esBody[0]), unsafe.Sizeof(esBody[0])*uintptr(len(esBody)), 3)
rce.SetFragmentBytes(unsafe.Pointer(&esTranslate[0]), unsafe.Sizeof(esTranslate[0])*uintptr(len(esTranslate)), 4) 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) rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5)
if g.src != nil { if src != nil {
rce.SetFragmentTexture(g.src.texture, 0) rce.SetFragmentTexture(src.texture, 0)
} else { } else {
rce.SetFragmentTexture(mtl.Texture{}, 0) rce.SetFragmentTexture(mtl.Texture{}, 0)
} }
@ -668,13 +701,6 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode
return nil return nil
} }
func (g *Graphics) ResetSource() {
g.t.Call(func() error {
g.src = nil
return nil
})
}
func (g *Graphics) SetVsyncEnabled(enabled bool) { func (g *Graphics) SetVsyncEnabled(enabled bool) {
g.view.setDisplaySyncEnabled(enabled) g.view.setDisplaySyncEnabled(enabled)
} }
@ -733,6 +759,7 @@ func (g *Graphics) MaxImageSize() int {
} }
type Image struct { type Image struct {
id driver.ImageID
graphics *Graphics graphics *Graphics
width int width int
height int height int
@ -740,6 +767,10 @@ type Image struct {
texture mtl.Texture texture mtl.Texture
} }
func (i *Image) ID() driver.ImageID {
return i.id
}
// viewportSize must be called from the main thread. // viewportSize must be called from the main thread.
func (i *Image) viewportSize() (int, int) { func (i *Image) viewportSize() (int, int) {
if i.screen { if i.screen {
@ -756,6 +787,7 @@ func (i *Image) Dispose() {
} }
return nil return nil
}) })
i.graphics.removeImage(i)
} }
func (i *Image) IsInvalidated() bool { func (i *Image) IsInvalidated() bool {
@ -795,20 +827,6 @@ func (i *Image) Pixels() ([]byte, error) {
return b, nil 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) { func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
g := i.graphics g := i.graphics
if g.drawCalled { if g.drawCalled {

View File

@ -30,8 +30,10 @@ func Get() *Graphics {
} }
type Graphics struct { type Graphics struct {
state openGLState nextImageID driver.ImageID
context context 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 is true just after Draw is called. This holds true until ReplacePixels is called.
drawCalled bool 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) { func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
i := &Image{ i := &Image{
id: g.genNextImageID(),
graphics: g, graphics: g,
width: width, width: width,
height: height, height: height,
@ -85,20 +94,37 @@ func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
return nil, err return nil, err
} }
i.textureNative = t i.textureNative = t
g.addImage(i)
return i, nil return i, nil
} }
func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) { func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) {
g.checkSize(width, height) g.checkSize(width, height)
i := &Image{ i := &Image{
id: g.genNextImageID(),
graphics: g, graphics: g,
width: width, width: width,
height: height, height: height,
screen: true, screen: true,
} }
g.addImage(i)
return i, nil 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. // Reset resets or initializes the current OpenGL state.
func (g *Graphics) Reset() error { func (g *Graphics) Reset() error {
return g.state.reset(&g.context) return g.state.reset(&g.context)
@ -112,15 +138,9 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
g.context.elementArrayBufferSubData(indices) 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 { 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.state.destination destination := g.images[dst]
if destination == nil { source := g.images[src]
panic("destination image is not set")
}
source := g.state.source
if source == nil {
panic("source image is not set")
}
g.drawCalled = true g.drawCalled = true

View File

@ -20,6 +20,7 @@ import (
) )
type Image struct { type Image struct {
id driver.ImageID
graphics *Graphics graphics *Graphics
textureNative textureNative textureNative textureNative
framebuffer *framebuffer framebuffer *framebuffer
@ -29,6 +30,10 @@ type Image struct {
screen bool screen bool
} }
func (i *Image) ID() driver.ImageID {
return i.id
}
func (i *Image) IsInvalidated() bool { func (i *Image) IsInvalidated() bool {
return !i.graphics.context.isTexture(i.textureNative) return !i.graphics.context.isTexture(i.textureNative)
} }
@ -43,10 +48,8 @@ func (i *Image) Dispose() {
if !i.textureNative.equal(*new(textureNative)) { if !i.textureNative.equal(*new(textureNative)) {
i.graphics.context.deleteTexture(i.textureNative) i.graphics.context.deleteTexture(i.textureNative)
} }
}
func (i *Image) SetAsDestination() { i.graphics.removeImage(i)
i.graphics.state.destination = i
} }
func (i *Image) setViewport() error { 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) i.graphics.context.replacePixelsWithPBO(i.pbo, i.textureNative, w, h, args)
} }
func (i *Image) SetAsSource() {
i.graphics.state.source = i
}

View File

@ -137,9 +137,6 @@ type openGLState struct {
lastProgram program lastProgram program
lastUniforms map[string]interface{} lastUniforms map[string]interface{}
lastActiveTexture int lastActiveTexture int
source *Image
destination *Image
} }
var ( var (
@ -292,8 +289,5 @@ func (g *Graphics) useProgram(program program, uniforms map[string]interface{})
g.context.bindTexture(u) g.context.bindTexture(u)
} }
} }
g.state.source = nil
g.state.destination = nil
return nil return nil
} }