mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 01:45:36 +00:00
gpu: support rectangular atlas packers
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+16
-14
@@ -411,7 +411,7 @@ func newCompute(ctx driver.Device) (*compute, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Large enough for reasonable fill sizes, yet still spannable by the compute programs.
|
// Large enough for reasonable fill sizes, yet still spannable by the compute programs.
|
||||||
g.output.packer.maxDim = 4096
|
g.output.packer.maxDims = image.Pt(4096, 4096)
|
||||||
copyVert, copyFrag, err := newShaders(ctx, gio.Shader_copy_vert, gio.Shader_copy_frag)
|
copyVert, copyFrag, err := newShaders(ctx, gio.Shader_copy_vert, gio.Shader_copy_frag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Release()
|
g.Release()
|
||||||
@@ -863,9 +863,10 @@ restart:
|
|||||||
// after clearing existing maps.
|
// after clearing existing maps.
|
||||||
reclaimed = true
|
reclaimed = true
|
||||||
} else {
|
} else {
|
||||||
m.packer.maxDim += 256
|
m.packer.maxDims.X += 256
|
||||||
|
m.packer.maxDims.Y += 256
|
||||||
resize = true
|
resize = true
|
||||||
if m.packer.maxDim > g.maxTextureDim {
|
if m.packer.maxDims.X > g.maxTextureDim {
|
||||||
return errors.New("compute: no space left in material atlas")
|
return errors.New("compute: no space left in material atlas")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -896,7 +897,7 @@ restart:
|
|||||||
if len(m.quads) == 0 {
|
if len(m.quads) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
texSize := m.packer.maxDim
|
texSize := m.packer.maxDims
|
||||||
if resize {
|
if resize {
|
||||||
if m.fbo != nil {
|
if m.fbo != nil {
|
||||||
m.fbo.Release()
|
m.fbo.Release()
|
||||||
@@ -907,7 +908,7 @@ restart:
|
|||||||
m.tex = nil
|
m.tex = nil
|
||||||
}
|
}
|
||||||
m.cpuTex.Free()
|
m.cpuTex.Free()
|
||||||
handle, err := g.ctx.NewTexture(driver.TextureFormatRGBA8, texSize, texSize,
|
handle, err := g.ctx.NewTexture(driver.TextureFormatRGBA8, texSize.X, texSize.Y,
|
||||||
driver.FilterNearest, driver.FilterNearest,
|
driver.FilterNearest, driver.FilterNearest,
|
||||||
driver.BufferBindingShaderStorageRead|driver.BufferBindingFramebuffer)
|
driver.BufferBindingShaderStorageRead|driver.BufferBindingFramebuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -921,12 +922,12 @@ restart:
|
|||||||
m.tex = handle
|
m.tex = handle
|
||||||
m.fbo = fbo
|
m.fbo = fbo
|
||||||
if g.useCPU {
|
if g.useCPU {
|
||||||
m.cpuTex = cpu.NewImageRGBA(texSize, texSize)
|
m.cpuTex = cpu.NewImageRGBA(texSize.X, texSize.Y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Transform to clip space: [-1, -1] - [1, 1] and flip Y-axis to cancel the implied transformation
|
// Transform to clip space: [-1, -1] - [1, 1] and flip Y-axis to cancel the implied transformation
|
||||||
// between framebuffer and texture space.
|
// between framebuffer and texture space.
|
||||||
m.vert.uniforms.scale = [2]float32{2 / float32(texSize), -2 / float32(texSize)}
|
m.vert.uniforms.scale = [2]float32{2 / float32(texSize.X), -2 / float32(texSize.Y)}
|
||||||
m.vert.uniforms.pos = [2]float32{-1, +1}
|
m.vert.uniforms.pos = [2]float32{-1, +1}
|
||||||
m.vert.buf.Upload(byteslice.Struct(m.vert.uniforms))
|
m.vert.buf.Upload(byteslice.Struct(m.vert.uniforms))
|
||||||
g.ctx.BindVertexUniforms(m.vert.buf)
|
g.ctx.BindVertexUniforms(m.vert.buf)
|
||||||
@@ -941,7 +942,7 @@ restart:
|
|||||||
d.Action = driver.LoadActionClear
|
d.Action = driver.LoadActionClear
|
||||||
}
|
}
|
||||||
g.ctx.BindFramebuffer(m.fbo, d)
|
g.ctx.BindFramebuffer(m.fbo, d)
|
||||||
g.ctx.Viewport(0, 0, texSize, texSize)
|
g.ctx.Viewport(0, 0, texSize.X, texSize.Y)
|
||||||
g.ctx.BindPipeline(m.pipeline)
|
g.ctx.BindPipeline(m.pipeline)
|
||||||
g.ctx.BindVertexBuffer(m.buffer.buffer, 0)
|
g.ctx.BindVertexBuffer(m.buffer.buffer, 0)
|
||||||
g.ctx.DrawArrays(driver.DrawModeTriangles, 0, len(m.quads))
|
g.ctx.DrawArrays(driver.DrawModeTriangles, 0, len(m.quads))
|
||||||
@@ -975,9 +976,10 @@ restart:
|
|||||||
// after clearing existing maps.
|
// after clearing existing maps.
|
||||||
reclaimed = true
|
reclaimed = true
|
||||||
} else {
|
} else {
|
||||||
a.packer.maxDim += 256
|
a.packer.maxDims.X += 256
|
||||||
|
a.packer.maxDims.Y += 256
|
||||||
resize = true
|
resize = true
|
||||||
if a.packer.maxDim > g.maxTextureDim {
|
if a.packer.maxDims.X > g.maxTextureDim {
|
||||||
return errors.New("compute: no space left in image atlas")
|
return errors.New("compute: no space left in image atlas")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1004,12 +1006,12 @@ restart:
|
|||||||
a.tex.Release()
|
a.tex.Release()
|
||||||
a.tex = nil
|
a.tex = nil
|
||||||
}
|
}
|
||||||
sz := a.packer.maxDim
|
sz := a.packer.maxDims
|
||||||
format := driver.TextureFormatSRGBA
|
format := driver.TextureFormatSRGBA
|
||||||
if !g.srgb {
|
if !g.srgb {
|
||||||
format = driver.TextureFormatRGBA8
|
format = driver.TextureFormatRGBA8
|
||||||
}
|
}
|
||||||
handle, err := g.ctx.NewTexture(format, sz, sz, driver.FilterLinear, driver.FilterLinear, driver.BufferBindingTexture)
|
handle, err := g.ctx.NewTexture(format, sz.X, sz.Y, driver.FilterLinear, driver.FilterLinear, driver.BufferBindingTexture)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("compute: failed to create image atlas: %v", err)
|
return fmt.Errorf("compute: failed to create image atlas: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1066,7 +1068,7 @@ func (g *compute) materialQuad(M f32.Affine2D, img imageOpData, uvPos image.Poin
|
|||||||
|
|
||||||
bounds := boundRectF(boundsf)
|
bounds := boundRectF(boundsf)
|
||||||
uvPosf := layout.FPt(uvPos)
|
uvPosf := layout.FPt(uvPos)
|
||||||
atlasScale := 1 / float32(g.images.packer.maxDim)
|
atlasScale := 1 / float32(g.images.packer.maxDims.X)
|
||||||
uvBounds := f32.Rectangle{
|
uvBounds := f32.Rectangle{
|
||||||
Min: uvPosf.Mul(atlasScale),
|
Min: uvPosf.Mul(atlasScale),
|
||||||
Max: uvPosf.Add(imgSize).Mul(atlasScale),
|
Max: uvPosf.Add(imgSize).Mul(atlasScale),
|
||||||
@@ -1264,7 +1266,7 @@ func (g *compute) downloadMaterials() {
|
|||||||
m.scratch = make([]byte, n)
|
m.scratch = make([]byte, n)
|
||||||
}
|
}
|
||||||
copyFBO.ReadPixels(r, m.scratch)
|
copyFBO.ReadPixels(r, m.scratch)
|
||||||
stride := m.packer.maxDim * 4
|
stride := m.packer.maxDims.X * 4
|
||||||
col := r.Min.X * 4
|
col := r.Min.X * 4
|
||||||
row := stride * r.Min.Y
|
row := stride * r.Min.Y
|
||||||
off := col + row
|
off := col + row
|
||||||
|
|||||||
+3
-3
@@ -505,8 +505,8 @@ func newRenderer(ctx driver.Device) *renderer {
|
|||||||
maxDim = cap
|
maxDim = cap
|
||||||
}
|
}
|
||||||
|
|
||||||
r.packer.maxDim = maxDim
|
r.packer.maxDims = image.Pt(maxDim, maxDim)
|
||||||
r.intersections.maxDim = maxDim
|
r.intersections.maxDims = image.Pt(maxDim, maxDim)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,7 +774,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
// The clip area is at most the entire screen. Hopefully no
|
// The clip area is at most the entire screen. Hopefully no
|
||||||
// screen is larger than GL_MAX_TEXTURE_SIZE.
|
// screen is larger than GL_MAX_TEXTURE_SIZE.
|
||||||
panic(fmt.Errorf("clip area %v is larger than maximum texture size %dx%d", p.clip, r.packer.maxDim, r.packer.maxDim))
|
panic(fmt.Errorf("clip area %v is larger than maximum texture size %v", p.clip, r.packer.maxDims))
|
||||||
}
|
}
|
||||||
p.place = place
|
p.place = place
|
||||||
i++
|
i++
|
||||||
|
|||||||
+5
-5
@@ -9,8 +9,8 @@ import (
|
|||||||
// packer packs a set of many smaller rectangles into
|
// packer packs a set of many smaller rectangles into
|
||||||
// much fewer larger atlases.
|
// much fewer larger atlases.
|
||||||
type packer struct {
|
type packer struct {
|
||||||
maxDim int
|
maxDims image.Point
|
||||||
spaces []image.Rectangle
|
spaces []image.Rectangle
|
||||||
|
|
||||||
sizes []image.Point
|
sizes []image.Point
|
||||||
pos image.Point
|
pos image.Point
|
||||||
@@ -49,7 +49,7 @@ func (p *packer) tryAdd(s image.Point) (placement, bool) {
|
|||||||
var (
|
var (
|
||||||
bestIdx = -1
|
bestIdx = -1
|
||||||
bestSpace image.Rectangle
|
bestSpace image.Rectangle
|
||||||
bestSize = image.Pt(p.maxDim, p.maxDim)
|
bestSize = p.maxDims
|
||||||
)
|
)
|
||||||
// Go backwards to prioritize smaller spaces.
|
// Go backwards to prioritize smaller spaces.
|
||||||
for i, space := range p.spaces {
|
for i, space := range p.spaces {
|
||||||
@@ -61,13 +61,13 @@ func (p *packer) tryAdd(s image.Point) (placement, bool) {
|
|||||||
idx := len(p.sizes) - 1
|
idx := len(p.sizes) - 1
|
||||||
size := p.sizes[idx]
|
size := p.sizes[idx]
|
||||||
if x := space.Min.X + s.X; x > size.X {
|
if x := space.Min.X + s.X; x > size.X {
|
||||||
if x > p.maxDim {
|
if x > p.maxDims.X {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
size.X = x
|
size.X = x
|
||||||
}
|
}
|
||||||
if y := space.Min.Y + s.Y; y > size.Y {
|
if y := space.Min.Y + s.Y; y > size.Y {
|
||||||
if y > p.maxDim {
|
if y > p.maxDims.Y {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
size.Y = y
|
size.Y = y
|
||||||
|
|||||||
Reference in New Issue
Block a user