153 lines
3.6 KiB
Go
153 lines
3.6 KiB
Go
package render
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"aego/core/errs"
|
|
"aego/core/log"
|
|
"aego/gpu"
|
|
"aego/render/view"
|
|
)
|
|
|
|
type PassContext struct {
|
|
Renderer *Renderer
|
|
View *view.View
|
|
Pass gpu.Pass
|
|
Frame *ViewFrame
|
|
}
|
|
|
|
type Pass interface {
|
|
Execute(*PassContext)
|
|
}
|
|
|
|
type PassDesc struct {
|
|
ID string
|
|
Before []string
|
|
After []string
|
|
Pass Pass
|
|
}
|
|
|
|
func sortPasses(descs []PassDesc) ([]PassDesc, error) {
|
|
n := len(descs)
|
|
index := make(map[string]int, n)
|
|
for i, d := range descs {
|
|
if d.ID == "" || d.Pass == nil {
|
|
return nil, errs.New(errs.EngineSystemBad, "pass needs an id and an implementation", log.F("pass", d.ID))
|
|
}
|
|
if _, dup := index[d.ID]; dup {
|
|
return nil, errs.New(errs.EngineSystemDup, "duplicate pass id", log.F("pass", d.ID))
|
|
}
|
|
index[d.ID] = i
|
|
}
|
|
adj := make([][]int, n)
|
|
indeg := make([]int, n)
|
|
edge := func(from, to int) {
|
|
adj[from] = append(adj[from], to)
|
|
indeg[to]++
|
|
}
|
|
for i, d := range descs {
|
|
for _, id := range d.Before {
|
|
j, ok := index[id]
|
|
if !ok {
|
|
return nil, errs.New(errs.EngineSystemDep, "unknown pass in ordering constraint",
|
|
log.F("pass", d.ID), log.F("dependency", id))
|
|
}
|
|
edge(i, j)
|
|
}
|
|
for _, id := range d.After {
|
|
j, ok := index[id]
|
|
if !ok {
|
|
return nil, errs.New(errs.EngineSystemDep, "unknown pass in ordering constraint",
|
|
log.F("pass", d.ID), log.F("dependency", id))
|
|
}
|
|
edge(j, i)
|
|
}
|
|
}
|
|
done := make([]bool, n)
|
|
out := make([]PassDesc, 0, n)
|
|
for len(out) < n {
|
|
pick := -1
|
|
for i := 0; i < n; i++ {
|
|
if !done[i] && indeg[i] == 0 {
|
|
pick = i
|
|
break
|
|
}
|
|
}
|
|
if pick < 0 {
|
|
var cyc []string
|
|
for i := 0; i < n; i++ {
|
|
if !done[i] {
|
|
cyc = append(cyc, descs[i].ID)
|
|
}
|
|
}
|
|
return nil, errs.New(errs.EngineSystemCycle, "render pass order has a cycle",
|
|
log.F("passes", strings.Join(cyc, ",")))
|
|
}
|
|
done[pick] = true
|
|
out = append(out, descs[pick])
|
|
for _, to := range adj[pick] {
|
|
indeg[to]--
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
type spritePass struct{}
|
|
|
|
func (spritePass) Execute(ctx *PassContext) {
|
|
r := ctx.Renderer
|
|
batches := r.batcher.Build()
|
|
if len(batches) == 0 {
|
|
return
|
|
}
|
|
r.batcher.Upload()
|
|
r.setUniform(ctx.Pass, ctx.View.ViewProjection(r.caps))
|
|
vp := ctx.View.LetterboxViewport()
|
|
for i := range batches {
|
|
b := &batches[i]
|
|
if b.Texture.IsZero() {
|
|
continue
|
|
}
|
|
ctx.Pass.SetPipeline(r.spritePipes[b.Blend])
|
|
ctx.Pass.SetVertexBufferAt(0, r.batcher.Buffer(), b.First*spriteStride)
|
|
ctx.Pass.SetUniformBuffer(0, r.ub)
|
|
ctx.Pass.SetTexture(0, b.Texture, b.Sampler)
|
|
if b.Scissor {
|
|
ctx.Pass.SetScissor(b.Clip, true)
|
|
} else {
|
|
ctx.Pass.SetScissor(vp, false)
|
|
}
|
|
ctx.Pass.DrawInstanced(4, b.Count)
|
|
r.stats.Batches++
|
|
r.stats.Instances += uint32(b.Count)
|
|
}
|
|
ctx.Pass.SetScissor(vp, false)
|
|
r.stats.InstanceBytes += uint32(r.batcher.Len() * spriteStride)
|
|
}
|
|
|
|
type debugPass struct{}
|
|
|
|
func (debugPass) Execute(ctx *PassContext) {
|
|
r := ctx.Renderer
|
|
worldFirst, worldCount, screenFirst, screenCount := r.dbg.Upload()
|
|
if worldCount == 0 && screenCount == 0 {
|
|
return
|
|
}
|
|
ctx.Pass.SetPipeline(r.debugPipe)
|
|
ctx.Pass.SetUniformBuffer(0, r.ub)
|
|
if worldCount > 0 {
|
|
r.setUniform(ctx.Pass, ctx.View.ViewProjection(r.caps))
|
|
ctx.Pass.SetVertexBuffer(0, r.dbg.Buffer())
|
|
ctx.Pass.Draw(worldFirst, worldCount)
|
|
r.stats.DebugVerts += uint32(worldCount)
|
|
}
|
|
if screenCount > 0 {
|
|
screen := view.ScreenView(ctx.View.TargetW, ctx.View.TargetH, ctx.View.PixelScale)
|
|
screen.Target = ctx.View.Target
|
|
r.setUniform(ctx.Pass, screen.ViewProjection(r.caps))
|
|
ctx.Pass.SetVertexBuffer(0, r.dbg.Buffer())
|
|
ctx.Pass.Draw(screenFirst, screenCount)
|
|
r.stats.DebugVerts += uint32(screenCount)
|
|
}
|
|
}
|