Files
aego/asset/loader/material_cancel_test.go
sanspie 5eb1832907
ci-platforms / darwin amd64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 15m28s
ci / release (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci / checks (push) Successful in 7m47s
Complete Stage 12 for v0.12.0
2026-11-05 11:46:53 +03:00

137 lines
5.6 KiB
Go

package loader
import (
"testing"
"aego/asset"
"aego/core/ids"
"aego/core/mathx"
"aego/render/shaderx"
"aego/vfs"
)
func writeMaterialFixture(t *testing.T, files vfs.FS, path string, data []byte) {
t.Helper()
if err := files.WriteFile(path, data); err != nil {
t.Fatal(err)
}
}
func meshMaterialShader(t *testing.T) []byte {
t.Helper()
u, ds := shaderx.Parse([]byte("#pragma aego abi 1\n#pragma domain mesh2d\n#pragma material\nuniform float Value;\n#pragma end\nvoid fragment(){aego_out=vec4(Value);}\n"), "mesh.aeshader", nil)
if len(ds) != 0 {
t.Fatal(ds)
}
return asset.EncodeShader(asset.BuildShader(u))
}
func TestNewInvalidMaterialRevisionCancelsQueuedCandidate(t *testing.T) {
for _, switched := range []bool{false, true} {
label := "same-shader"
if switched {
label = "changed-shader"
}
for _, invalid := range []string{"type", "missing-shader", "domain", "corrupt-artifact"} {
t.Run(label+"/"+invalid, func(t *testing.T) {
l, c, files, se, me, m := materialFixture(t, "uniform float Value; // @default(1)", "Value=4")
old := m.Shader()
other := asset.Entry{ID: ids.AssetID{Lo: 103}, Name: "other", Kind: asset.ArtShader, Artifacts: []asset.Artifact{{Kind: asset.ArtShader, Path: "cache://other.aesh"}}}
mesh := asset.Entry{ID: ids.AssetID{Lo: 104}, Name: "mesh", Kind: asset.ArtShader, Artifacts: []asset.Artifact{{Kind: asset.ArtShader, Path: "cache://mesh.aesh"}}}
writeMaterialFixture(t, files, "cache://other.aesh", loaderShader(t, "uniform float Value; // @default(2)"))
writeMaterialFixture(t, files, "cache://mesh.aesh", meshMaterialShader(t))
if err := l.Swap([]asset.Entry{other, mesh}, nil); err != nil {
t.Fatal(err)
}
target := se.ID
if switched {
target = other.ID
}
writeMaterialFixture(t, files, "cache://reload.aemt", loaderMaterial(t, target, "Value=8"))
if err := l.Swap([]asset.Entry{me}, nil); err != nil {
t.Fatal(err)
}
if !c.Pending(m) {
t.Fatal("valid revision was not queued")
}
var bad []byte
switch invalid {
case "type":
bad = loaderMaterial(t, target, "Value=[1,2]")
case "missing-shader":
bad = loaderMaterial(t, ids.AssetID{Lo: 999}, "Value=9")
case "domain":
bad = loaderMaterial(t, mesh.ID, "Value=9")
case "corrupt-artifact":
bad = []byte("truncated AEMT")
}
writeMaterialFixture(t, files, "cache://reload.aemt", bad)
err := l.Swap([]asset.Entry{me}, nil)
if (err != nil) != (invalid == "corrupt-artifact") {
t.Fatalf("unexpected artifact preparation result: %v", err)
}
c.Begin()
c.End()
v, _ := m.Get("Value")
if m.Shader() != old || v[0] != 4 || l.mat.uses[me.ID] != se.ID || c.Pending(m) {
t.Fatalf("older success survived newer invalid source: shader=%s use=%s value=%v pending=%v", m.Shader().ID, l.mat.uses[me.ID], v[0], c.Pending(m))
}
})
}
}
}
func TestTextureRefreshPreservesQueuedMaterialSource(t *testing.T) {
decl := "uniform float Value; // @default(1)\nuniform sampler2D Noise;"
l, c, files, _, me, m := materialFixture(t, decl, "Value=4")
other := asset.Entry{ID: ids.AssetID{Lo: 103}, Name: "other", Kind: asset.ArtShader, Artifacts: []asset.Artifact{{Kind: asset.ArtShader, Path: "cache://other.aesh"}}}
tex := asset.Entry{ID: ids.AssetID{Lo: 104}, Name: "noise", Kind: asset.ArtTexture, Artifacts: []asset.Artifact{{Kind: asset.ArtTexture, Path: "cache://noise.aetex"}}}
writeMaterialFixture(t, files, "cache://other.aesh", loaderShader(t, decl))
writeMaterialFixture(t, files, "cache://noise.aetex", textureBytes(2, 2, mathx.Color{R: 1, A: 1}))
if err := l.Swap([]asset.Entry{other, tex}, nil); err != nil {
t.Fatal(err)
}
writeMaterialFixture(t, files, "cache://reload.aemt", loaderMaterial(t, other.ID, "Value=8 Noise=asset(\""+tex.ID.String()+"\")"))
if err := l.Swap([]asset.Entry{me}, nil); err != nil {
t.Fatal(err)
}
writeMaterialFixture(t, files, "cache://noise.aetex", textureBytes(2, 2, mathx.Color{G: 1, A: 1}))
if err := l.Swap([]asset.Entry{tex}, nil); err != nil {
t.Fatal(err)
}
c.Begin()
c.End()
v, _ := m.Get("Value")
if m.Shader().ID != other.ID || l.mat.uses[me.ID] != m.Shader().ID || v[0] != 8 {
t.Fatalf("texture refresh discarded pending source: shader=%s use=%s value=%v", m.Shader().ID, l.mat.uses[me.ID], v[0])
}
current, ok := l.Texture(tex.ID)
if !ok || !m.UsesTexture(current.Texture) {
t.Fatal("committed material retained the retired texture revision")
}
}
func TestMaterialSwitchUsesShaderRevisionFromSameSwap(t *testing.T) {
l, c, files, _, me, m := materialFixture(t, "uniform float Value; // @default(1)", "Value=4")
other := asset.Entry{ID: ids.AssetID{Lo: 103}, Name: "other", Kind: asset.ArtShader, Artifacts: []asset.Artifact{{Kind: asset.ArtShader, Path: "cache://other.aesh"}}}
writeMaterialFixture(t, files, "cache://other.aesh", loaderShader(t, "uniform float Value; // @default(2)"))
if err := l.Swap([]asset.Entry{other}, nil); err != nil {
t.Fatal(err)
}
if _, ok := l.Shader(other.ID); !ok {
t.Fatal("other shader did not load")
}
writeMaterialFixture(t, files, "cache://other.aesh", loaderShader(t, "uniform float Value; // @default(3)\nuniform float Added; // @default(9)"))
writeMaterialFixture(t, files, "cache://reload.aemt", loaderMaterial(t, other.ID, "Value=8"))
if err := l.Swap([]asset.Entry{other, me}, nil); err != nil {
t.Fatal(err)
}
c.Begin()
c.End()
active, ok := l.Shader(other.ID)
added, present := m.Get("Added")
if !ok || m.Shader() != active || !present || added[0] != 9 {
t.Fatalf("material attached to old shader revision: live=%p registered=%p added=%v present=%v", m.Shader(), active, added, present)
}
}