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)
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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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
}