zworld/engine/render/pass/postprocess.go
2024-01-14 22:56:06 +08:00

144 lines
3.3 KiB
Go

package pass
import (
"fmt"
"github.com/vkngwrapper/core/v2/core1_0"
"zworld/engine/object"
"zworld/engine/renderapi"
"zworld/engine/renderapi/command"
"zworld/engine/renderapi/descriptor"
"zworld/engine/renderapi/framebuffer"
"zworld/engine/renderapi/material"
"zworld/engine/renderapi/renderpass"
"zworld/engine/renderapi/renderpass/attachment"
"zworld/engine/renderapi/shader"
"zworld/engine/renderapi/texture"
"zworld/engine/renderapi/vertex"
"zworld/engine/renderapi/vulkan"
)
type PostProcessPass struct {
LUT texture.Ref
app vulkan.App
input vulkan.Target
quad vertex.Mesh
mat *material.Material[*PostProcessDescriptors]
desc []*material.Instance[*PostProcessDescriptors]
fbufs framebuffer.Array
pass renderpass.T
inputTex []texture.T
}
var _ Pass = &PostProcessPass{}
type PostProcessDescriptors struct {
descriptor.Set
Input *descriptor.Sampler
LUT *descriptor.Sampler
}
func NewPostProcessPass(app vulkan.App, target vulkan.Target, input vulkan.Target) *PostProcessPass {
var err error
p := &PostProcessPass{
LUT: texture.PathRef("textures/color_grading/none.png"),
app: app,
input: input,
}
p.quad = vertex.ScreenQuad("blur-pass-quad")
p.pass = renderpass.New(app.Device(), renderpass.Args{
Name: "PostProcess",
ColorAttachments: []attachment.Color{
{
Name: OutputAttachment,
Image: attachment.FromImageArray(target.Surfaces()),
LoadOp: core1_0.AttachmentLoadOpDontCare,
FinalLayout: core1_0.ImageLayoutShaderReadOnlyOptimal,
},
},
Subpasses: []renderpass.Subpass{
{
Name: MainSubpass,
ColorAttachments: []attachment.Name{OutputAttachment},
},
},
})
p.mat = material.New(
app.Device(),
material.Args{
Shader: app.Shaders().Fetch(shader.NewRef("postprocess")),
Pass: p.pass,
Pointers: vertex.ParsePointers(vertex.T{}),
DepthTest: false,
DepthWrite: false,
},
&PostProcessDescriptors{
Input: &descriptor.Sampler{
Stages: core1_0.StageFragment,
},
LUT: &descriptor.Sampler{
Stages: core1_0.StageFragment,
},
})
frames := input.Frames()
p.fbufs, err = framebuffer.NewArray(frames, app.Device(), "blur", target.Width(), target.Height(), p.pass)
if err != nil {
panic(err)
}
p.desc = p.mat.InstantiateMany(app.Pool(), frames)
p.inputTex = make([]texture.T, frames)
for i := 0; i < input.Frames(); i++ {
inputKey := fmt.Sprintf("post-input-%d", i)
p.inputTex[i], err = texture.FromImage(app.Device(), inputKey, p.input.Surfaces()[i], texture.Args{
Filter: texture.FilterNearest,
Wrap: texture.WrapClamp,
})
if err != nil {
// todo: clean up
panic(err)
}
p.desc[i].Descriptors().Input.Set(p.inputTex[i])
}
return p
}
func (p *PostProcessPass) Record(cmds command.Recorder, args renderapi.Args, scene object.Component) {
quad := p.app.Meshes().Fetch(p.quad)
// refresh color lut
lutTex := p.app.Textures().Fetch(p.LUT)
cmds.Record(func(cmd command.Buffer) {
cmd.CmdBeginRenderPass(p.pass, p.fbufs[args.Frame])
desc := p.desc[args.Frame]
desc.Bind(cmd)
desc.Descriptors().LUT.Set(lutTex)
quad.Draw(cmd, 0)
cmd.CmdEndRenderPass()
})
}
func (p *PostProcessPass) Name() string {
return "PostProcess"
}
func (p *PostProcessPass) Destroy() {
for _, tex := range p.inputTex {
tex.Destroy()
}
p.fbufs.Destroy()
p.pass.Destroy()
p.mat.Destroy()
}