From 8fd377f1e3ede4449011274ccb5172683f1d7f3d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 19 May 2020 23:48:43 +0900 Subject: [PATCH] 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. --- internal/driver/graphics.go | 7 +- internal/graphicscommand/command.go | 4 +- internal/graphicscommand/image.go | 7 +- internal/graphicsdriver/metal/graphics.go | 94 +++++++++++++--------- internal/graphicsdriver/opengl/graphics.go | 42 +++++++--- internal/graphicsdriver/opengl/image.go | 13 ++- internal/graphicsdriver/opengl/program.go | 6 -- 7 files changed, 104 insertions(+), 69 deletions(-) diff --git a/internal/driver/graphics.go b/internal/driver/graphics.go index 30fbe3ae6..cd6e68e0a 100644 --- a/internal/driver/graphics.go +++ b/internal/driver/graphics.go @@ -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 diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index d7210c2e9..1cf0b3c6b 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -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 diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 26b82d612..8e42cf8c8 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -43,7 +43,12 @@ type Image struct { internalWidth int internalHeight int 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 diff --git a/internal/graphicsdriver/metal/graphics.go b/internal/graphicsdriver/metal/graphics.go index 110821631..f4fdcca14 100644 --- a/internal/graphicsdriver/metal/graphics.go +++ b/internal/graphicsdriver/metal/graphics.go @@ -292,8 +292,12 @@ type Graphics struct { screenDrawable ca.MetalDrawable - vb mtl.Buffer - ib mtl.Buffer + 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 { diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 88bacae72..358e8a073 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -30,8 +30,10 @@ func Get() *Graphics { } type Graphics struct { - state openGLState - context context + 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 diff --git a/internal/graphicsdriver/opengl/image.go b/internal/graphicsdriver/opengl/image.go index ee9bb3f09..6b3d6257d 100644 --- a/internal/graphicsdriver/opengl/image.go +++ b/internal/graphicsdriver/opengl/image.go @@ -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 -} diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index 3e0b66707..43540d698 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -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 }