Files
aego/asset/pipeline/group.go
T
sanspie 17cb5e4291 Stage 2-5: assets, scene/ECS, editor, 3D, and Aego Script
Stage 2: render pipeline and UI toolkit over gpu/platform.
Stage 3: schema registry, asset pipeline + runtime loader, scene graph and ECS.
Stage 4: editor — docking, inspector, scene/game views, play mode, asset browser, console, profiler; 4.5 adds the 3D foundation (mixed 2D/3D spaces, meshes, depth).
Stage 5: Aego Script — compiler (lexer/parser/types/resolver/checker/asm/compiler), bytecode with verifier and register VM, instance runtime, stdlib, native bindings, and editor/CLI integration (aego run/edit/check/test/dump).
2026-10-08 13:17:28 +03:00

187 lines
4.3 KiB
Go

package pipeline
import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"sort"
"strconv"
"strings"
"aego/asset"
"aego/core/errs"
"aego/core/ids"
"aego/core/log"
)
func groupID(group string) ids.AssetID {
sum := sha256.Sum256([]byte("aego.group/" + group))
return ids.AssetID{Hi: binary.BigEndian.Uint64(sum[0:8]), Lo: binary.BigEndian.Uint64(sum[8:16])}
}
func groupPrefix(kind asset.ArtifactKind) string {
if kind == asset.ArtAtlasPage {
return "atlas"
}
return string(kind)
}
func (p *Pipeline) dropFromGroup(group string, id ids.AssetID) {
members := p.groups[group]
for i, m := range members {
if m == id {
p.groups[group] = append(members[:i:i], members[i+1:]...)
break
}
}
delete(p.groupResults[group], id)
p.stale[group] = true
}
func (p *Pipeline) finalizeGroup(group string) ([]asset.Entry, map[ids.AssetID]error, error) {
members := p.groups[group]
pageID, ok := p.pages[group]
if !ok {
pageID = groupID(group)
p.pages[group] = pageID
}
if len(members) == 0 {
p.index.Delete(pageID)
delete(p.groupKey, group)
return nil, nil, nil
}
sort.Slice(members, func(i, j int) bool {
return p.byID[members[i]].path < p.byID[members[j]].path
})
key := p.groupCacheKey(group, members)
if key == p.groupKey[group] {
return nil, nil, nil
}
var imp GroupImporter
list := make([]GroupMember, 0, len(members))
for _, id := range members {
r, ok := p.byID[id]
if !ok {
continue
}
gi, ok := r.importer.(GroupImporter)
if !ok {
return nil, nil, errs.New(errs.AssetBadImporter, "importer does not support groups",
log.F("importer", r.importer.ID()), log.F("group", group))
}
imp = gi
res := p.groupResults[group][id]
if res == nil {
continue
}
list = append(list, GroupMember{
ID: id,
Name: assetName(r.path),
Path: r.path,
Settings: r.meta.Settings,
Result: res,
})
}
if imp == nil || len(list) == 0 {
return nil, nil, nil
}
var old []asset.Artifact
if prev, ok := p.index.Lookup(pageID); ok {
old = append(old, prev.Artifacts...)
}
ctx := &GroupContext{Group: group, PageID: pageID, Log: p.log, Previous: func(name string) ([]byte, bool) {
for _, a := range old {
if a.Name == name {
data, err := p.artifacts.ReadFile(a.Path)
return data, err == nil
}
}
return nil, false
}}
perMember, pages, err := imp.Finalize(ctx, list)
if err != nil {
return nil, nil, err
}
var pageEntry *asset.Entry
if len(pages) > 0 {
kind := pages[0].Kind
prefix := groupPrefix(kind)
pageEntry = &asset.Entry{ID: pageID, Kind: kind, Name: prefix + "/" + group, Source: prefix + ":" + group}
for i, page := range pages {
name := pageID.String()
switch {
case page.Name != "":
name += "-" + strings.ReplaceAll(page.Name, "/", "_")
case i > 0:
name += "_" + hex.EncodeToString([]byte{byte(i)})
}
path := "cache://artifacts/" + name + artifactExt(page.Kind)
if err := p.artifacts.WriteFile(path, page.Data); err != nil {
return nil, nil, err
}
sum := sha256.Sum256(page.Data)
pageEntry.Artifacts = append(pageEntry.Artifacts, asset.Artifact{
Kind: page.Kind,
Name: page.Name,
Path: path,
Hash: hex.EncodeToString(sum[:8]),
Size: int64(len(page.Data)),
})
}
}
failed := map[ids.AssetID]error{}
var entries []asset.Entry
for _, m := range list {
res, ok := perMember[m.ID]
if !ok {
continue
}
if res.Error != nil {
failed[m.ID] = res.Error
continue
}
r := p.byID[m.ID]
entry, err := p.writeArtifacts(r, res)
if err != nil {
return nil, nil, err
}
if pageEntry != nil {
entry.Deps = append(entry.Deps, pageID)
}
p.index.Put(entry)
entries = append(entries, entry)
}
if pageEntry != nil {
if len(failed) > 0 {
pageEntry.Error = strconv.Itoa(len(failed)) + " member(s) failed to import"
}
p.index.Put(*pageEntry)
entries = append([]asset.Entry{*pageEntry}, entries...)
}
p.groupKey[group] = key
return entries, failed, nil
}
func (p *Pipeline) groupCacheKey(group string, members []ids.AssetID) string {
h := sha256.New()
h.Write([]byte(group))
for _, id := range members {
r, ok := p.byID[id]
if !ok {
continue
}
h.Write([]byte(id.String()))
h.Write([]byte(r.key))
}
if len(members) > 0 {
if gi, ok := p.byID[members[0]].importer.(GroupImporter); ok {
writeUint(h, uint64(gi.GroupVersion()))
}
}
return hex.EncodeToString(h.Sum(nil))
}