Files
sanspie a6822156cd
ci-platforms / windows amd64 smoke (push) Skipped
ci / release (push) Successful in 8m58s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci / checks (push) Successful in 4m46s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 13m28s
Finish Stage 12 editor fixes and refreshed platformer
2026-11-05 17:36:42 +03:00

94 lines
2.6 KiB
Go

// Package texshadow keeps texture upload data needed after a context restore.
// Rendered pixels are deliberately not mirrored; their owner redraws them.
package texshadow
import "aego/gpu"
type level struct {
data []byte
dirty []uint64
active bool
}
type Shadow struct {
desc gpu.TextureDesc
levels []level
}
func New(desc gpu.TextureDesc) Shadow {
s := Shadow{desc: desc, levels: make([]level, desc.MipLevels())}
s.desc.Data = nil
if desc.Data != nil {
n := desc.Width * desc.Height * desc.LayerCount() * desc.Format.UploadBytesPerPixel()
s.levels[0].data = append([]byte(nil), desc.Data[:n]...)
}
return s
}
func (s *Shadow) Base() []byte { return s.levels[0].data }
// Update expects a region already checked by gpu.ValidateTextureUpdate.
// A base-level update regenerates the mip chain, replacing earlier manual edits.
func (s *Shadow) Update(u gpu.TextureUpdate) {
w, h, _ := s.desc.SubresourceSize(u.Subresource)
bpp := s.desc.Format.UploadBytesPerPixel()
l := &s.levels[u.Subresource.Mip]
pixels := w * h * s.desc.LayerCount()
if l.data == nil {
l.data = make([]byte, pixels*bpp)
}
if u.Subresource.Mip != 0 {
if l.dirty == nil {
l.dirty = make([]uint64, (pixels+63)/64)
} else if !l.active {
clear(l.dirty)
}
l.active = true
} else {
for i := 1; i < len(s.levels); i++ {
s.levels[i].active = false
}
}
for y := 0; y < u.Height; y++ {
p := (u.Subresource.Layer*h+u.Y+y)*w + u.X
copy(l.data[p*bpp:(p+u.Width)*bpp], u.Data[y*u.Width*bpp:(y+1)*u.Width*bpp])
if u.Subresource.Mip != 0 {
for x := p; x < p+u.Width; x++ {
l.dirty[x/64] |= 1 << (x % 64)
}
}
}
}
// ManualMipRuns replays only explicitly written texels after the backend generates mipmaps from Base; whole zero-filled partial mip buffers would corrupt generated texels on context restore.
func (s *Shadow) ManualMipRuns(fn func(gpu.TextureUpdate)) {
bpp := s.desc.Format.UploadBytesPerPixel()
for mip := 1; mip < len(s.levels); mip++ {
l := &s.levels[mip]
if !l.active {
continue
}
w, h, _ := s.desc.SubresourceSize(gpu.TextureSubresource{Mip: mip})
for layer := 0; layer < s.desc.LayerCount(); layer++ {
for y := 0; y < h; y++ {
row := (layer*h + y) * w
for x := 0; x < w; {
p := row + x
if l.dirty[p/64]&(1<<(p%64)) == 0 {
x++
continue
}
start := x
for x++; x < w; x++ {
p = row + x
if l.dirty[p/64]&(1<<(p%64)) == 0 {
break
}
}
fn(gpu.TextureUpdate{Subresource: gpu.TextureSubresource{Layer: layer, Mip: mip}, X: start, Y: y, Width: x - start, Height: 1, Data: l.data[(row+start)*bpp : (row+x)*bpp]})
}
}
}
}
}