internal/ui: refactoring: add bigOffscreenImage

Updates #2399
This commit is contained in:
Hajime Hoshi 2023-02-17 23:56:00 +09:00
parent d5baddc372
commit 56597800dd

View File

@ -42,19 +42,13 @@ type Image struct {
dotsBuffer map[[2]int][4]byte dotsBuffer map[[2]int][4]byte
// bigOffscreenBuffer is a double-sized offscreen for anti-alias rendering. // bigOffscreenBuffer is a double-sized offscreen for anti-alias rendering.
bigOffscreenBuffer *Image bigOffscreenBuffer *bigOffscreenImage
bigOffscreenBufferBlend graphicsdriver.Blend
bigOffscreenBufferDirty bool
// modifyCallback is a callback called when DrawTriangles or WritePixels is called. // modifyCallback is a callback called when DrawTriangles or WritePixels is called.
// modifyCallback is useful to detect whether the image is manipulated or not after a certain time. // modifyCallback is useful to detect whether the image is manipulated or not after a certain time.
modifyCallback func() modifyCallback func()
// These temporary vertices must not be reused until the vertices are sent to the graphics command queue. tmpVerticesForFill []float32
tmpVerticesForFlushing []float32
tmpVerticesForCopying []float32
tmpVerticesForFill []float32
} }
func NewImage(width, height int, imageType atlas.ImageType) *Image { func NewImage(width, height int, imageType atlas.ImageType) *Image {
@ -71,9 +65,8 @@ func (i *Image) MarkDisposed() {
return return
} }
if i.bigOffscreenBuffer != nil { if i.bigOffscreenBuffer != nil {
i.bigOffscreenBuffer.MarkDisposed() i.bigOffscreenBuffer.markDisposed()
i.bigOffscreenBuffer = nil i.bigOffscreenBuffer = nil
i.bigOffscreenBufferDirty = false
} }
i.mipmap.MarkDisposed() i.mipmap.MarkDisposed()
i.mipmap = nil i.mipmap = nil
@ -90,10 +83,6 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
// Flush the other buffer to make the buffers exclusive. // Flush the other buffer to make the buffers exclusive.
i.flushDotsBufferIfNeeded() i.flushDotsBufferIfNeeded()
if i.bigOffscreenBufferBlend != blend {
i.flushBigOffscreenBufferIfNeeded()
}
if i.bigOffscreenBuffer == nil { if i.bigOffscreenBuffer == nil {
var imageType atlas.ImageType var imageType atlas.ImageType
switch i.imageType { switch i.imageType {
@ -104,44 +93,10 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
default: default:
panic(fmt.Sprintf("ui: unexpected image type: %d", imageType)) panic(fmt.Sprintf("ui: unexpected image type: %d", imageType))
} }
i.bigOffscreenBuffer = NewImage(i.width*bigOffscreenScale, i.height*bigOffscreenScale, imageType) i.bigOffscreenBuffer = newBigOffscreenImage(i, i.width, i.height, imageType)
} }
i.bigOffscreenBufferBlend = blend i.bigOffscreenBuffer.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, canSkipMipmap, false)
// Copy the current rendering result to get the correct blending result.
if blend != graphicsdriver.BlendSourceOver && !i.bigOffscreenBufferDirty {
srcs := [graphics.ShaderImageCount]*Image{i}
if len(i.tmpVerticesForCopying) < 4*graphics.VertexFloatCount {
i.tmpVerticesForCopying = make([]float32, 4*graphics.VertexFloatCount)
}
graphics.QuadVertices(
i.tmpVerticesForCopying,
0, 0, float32(i.width), float32(i.height),
bigOffscreenScale, 0, 0, bigOffscreenScale, 0, 0,
1, 1, 1, 1)
is := graphics.QuadIndices()
dstRegion := graphicsdriver.Region{
X: 0,
Y: 0,
Width: float32(i.width * bigOffscreenScale),
Height: float32(i.height * bigOffscreenScale),
}
i.bigOffscreenBuffer.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true, false)
}
for i := 0; i < len(vertices); i += graphics.VertexFloatCount {
vertices[i] *= bigOffscreenScale
vertices[i+1] *= bigOffscreenScale
}
dstRegion.X *= bigOffscreenScale
dstRegion.Y *= bigOffscreenScale
dstRegion.Width *= bigOffscreenScale
dstRegion.Height *= bigOffscreenScale
i.bigOffscreenBuffer.DrawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, canSkipMipmap, false)
i.bigOffscreenBufferDirty = true
return return
} }
@ -298,37 +253,9 @@ func (i *Image) flushDotsBufferIfNeeded() {
} }
func (i *Image) flushBigOffscreenBufferIfNeeded() { func (i *Image) flushBigOffscreenBufferIfNeeded() {
if !i.bigOffscreenBufferDirty { if i.bigOffscreenBuffer != nil {
return i.bigOffscreenBuffer.flush()
} }
// Mark the offscreen clearn earlier to avoid recursive calls.
i.bigOffscreenBufferDirty = false
srcs := [graphics.ShaderImageCount]*Image{i.bigOffscreenBuffer}
if len(i.tmpVerticesForFlushing) < 4*graphics.VertexFloatCount {
i.tmpVerticesForFlushing = make([]float32, 4*graphics.VertexFloatCount)
}
graphics.QuadVertices(
i.tmpVerticesForFlushing,
0, 0, float32(i.width*bigOffscreenScale), float32(i.height*bigOffscreenScale),
1.0/bigOffscreenScale, 0, 0, 1.0/bigOffscreenScale, 0, 0,
1, 1, 1, 1)
is := graphics.QuadIndices()
dstRegion := graphicsdriver.Region{
X: 0,
Y: 0,
Width: float32(i.width),
Height: float32(i.height),
}
blend := graphicsdriver.BlendSourceOver
if i.bigOffscreenBufferBlend != graphicsdriver.BlendSourceOver {
blend = graphicsdriver.BlendCopy
}
i.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, LinearFilterShader, nil, false, true, false)
i.bigOffscreenBuffer.clear()
i.bigOffscreenBufferDirty = false
} }
func DumpImages(dir string) (string, error) { func DumpImages(dir string) (string, error) {
@ -363,6 +290,7 @@ func (i *Image) Fill(r, g, b, a float32, x, y, width, height int) {
if len(i.tmpVerticesForFill) < 4*graphics.VertexFloatCount { if len(i.tmpVerticesForFill) < 4*graphics.VertexFloatCount {
i.tmpVerticesForFill = make([]float32, 4*graphics.VertexFloatCount) i.tmpVerticesForFill = make([]float32, 4*graphics.VertexFloatCount)
} }
// i.tmpVerticesForFill can be reused as this is sent to DrawTriangles immediately.
graphics.QuadVertices( graphics.QuadVertices(
i.tmpVerticesForFill, i.tmpVerticesForFill,
1, 1, float32(whiteImage.width-1), float32(whiteImage.height-1), 1, 1, float32(whiteImage.width-1), float32(whiteImage.height-1),
@ -374,3 +302,107 @@ func (i *Image) Fill(r, g, b, a float32, x, y, width, height int) {
i.DrawTriangles(srcs, i.tmpVerticesForFill, is, graphicsdriver.BlendCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true, false) i.DrawTriangles(srcs, i.tmpVerticesForFill, is, graphicsdriver.BlendCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true, false)
} }
type bigOffscreenImage struct {
orig *Image
image *Image
width int
height int
blend graphicsdriver.Blend
dirty bool
tmpVerticesForFlushing []float32
tmpVerticesForCopying []float32
}
func newBigOffscreenImage(orig *Image, width, height int, imageType atlas.ImageType) *bigOffscreenImage {
return &bigOffscreenImage{
orig: orig,
image: NewImage(width*bigOffscreenScale, height*bigOffscreenScale, imageType),
width: width,
height: height,
}
}
func (i *bigOffscreenImage) markDisposed() {
i.image.MarkDisposed()
i.image = nil
i.dirty = false
}
func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms []uint32, evenOdd bool, canSkipMipmap bool, antialias bool) {
if i.blend != blend {
i.flush()
}
i.blend = blend
// Copy the current rendering result to get the correct blending result.
if blend != graphicsdriver.BlendSourceOver && !i.dirty {
srcs := [graphics.ShaderImageCount]*Image{i.orig}
if len(i.tmpVerticesForCopying) < 4*graphics.VertexFloatCount {
i.tmpVerticesForCopying = make([]float32, 4*graphics.VertexFloatCount)
}
// i.tmpVerticesForCopying can be resused as this is sent to DrawTriangles immediately.
graphics.QuadVertices(
i.tmpVerticesForCopying,
0, 0, float32(i.orig.width), float32(i.orig.height),
bigOffscreenScale, 0, 0, bigOffscreenScale, 0, 0,
1, 1, 1, 1)
is := graphics.QuadIndices()
dstRegion := graphicsdriver.Region{
X: 0,
Y: 0,
Width: float32(i.orig.width * bigOffscreenScale),
Height: float32(i.orig.height * bigOffscreenScale),
}
i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true, false)
}
for i := 0; i < len(vertices); i += graphics.VertexFloatCount {
vertices[i] *= bigOffscreenScale
vertices[i+1] *= bigOffscreenScale
}
dstRegion.X *= bigOffscreenScale
dstRegion.Y *= bigOffscreenScale
dstRegion.Width *= bigOffscreenScale
dstRegion.Height *= bigOffscreenScale
i.image.DrawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, canSkipMipmap, false)
i.dirty = true
}
func (i *bigOffscreenImage) flush() {
if !i.dirty {
return
}
// Mark the offscreen clearn earlier to avoid recursive calls.
i.dirty = false
srcs := [graphics.ShaderImageCount]*Image{i.image}
if len(i.tmpVerticesForFlushing) < 4*graphics.VertexFloatCount {
i.tmpVerticesForFlushing = make([]float32, 4*graphics.VertexFloatCount)
}
// i.tmpVerticesForFlushing can be reused as this is sent to DrawTriangles in this function.
graphics.QuadVertices(
i.tmpVerticesForFlushing,
0, 0, float32(i.width*bigOffscreenScale), float32(i.height*bigOffscreenScale),
1.0/bigOffscreenScale, 0, 0, 1.0/bigOffscreenScale, 0, 0,
1, 1, 1, 1)
is := graphics.QuadIndices()
dstRegion := graphicsdriver.Region{
X: 0,
Y: 0,
Width: float32(i.width),
Height: float32(i.height),
}
blend := graphicsdriver.BlendSourceOver
if i.blend != graphicsdriver.BlendSourceOver {
blend = graphicsdriver.BlendCopy
}
i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, LinearFilterShader, nil, false, true, false)
i.image.clear()
i.dirty = false
}