package material import ( "fmt" "log" "zworld/engine/renderapi/command" "zworld/engine/renderapi/descriptor" "zworld/engine/renderapi/device" "zworld/engine/renderapi/pipeline" "zworld/engine/renderapi/renderpass" "zworld/engine/renderapi/shader" "zworld/engine/renderapi/texture" "zworld/engine/renderapi/vertex" "zworld/engine/util" "github.com/vkngwrapper/core/v2/core1_0" ) // Materials combine pipelines and descriptors into a common unit. type Material[D descriptor.Set] struct { device device.T dlayout descriptor.SetLayoutTyped[D] shader shader.T layout pipeline.Layout pipe pipeline.T pass renderpass.T } type Args struct { Shader shader.T Pass renderpass.T Subpass renderpass.Name Constants []pipeline.PushConstant Pointers vertex.Pointers Primitive vertex.Primitive DepthTest bool DepthWrite bool DepthClamp bool DepthBias float32 DepthSlope float32 DepthFunc core1_0.CompareOp CullMode vertex.CullMode } func New[D descriptor.Set](device device.T, args Args, descriptors D) *Material[D] { if device == nil { panic("device is nil") } if args.Shader == nil { panic("shader is nil") } for i, ptr := range args.Pointers { if index, kind, exists := args.Shader.Input(ptr.Name); exists { ptr.Bind(index, kind) args.Pointers[i] = ptr } else { log.Printf("no attribute in shader %s\n", ptr.Name) } } if args.Primitive == 0 { args.Primitive = vertex.Triangles } // create new descriptor set layout // ... this could be cached ... descLayout := descriptor.New(device, descriptors, args.Shader) // crete pipeline layout // ... this could be cached ... layout := pipeline.NewLayout(device, []descriptor.SetLayout{descLayout}, args.Constants) pipelineName := fmt.Sprintf("%s/%s", args.Pass.Name(), args.Shader.Name()) pipe := pipeline.New(device, pipeline.Args{ Key: pipelineName, Layout: layout, Pass: args.Pass, Subpass: args.Subpass, Shader: args.Shader, Pointers: args.Pointers, Primitive: args.Primitive, DepthTest: args.DepthTest, DepthWrite: args.DepthWrite, DepthClamp: args.DepthClamp, DepthFunc: args.DepthFunc, CullMode: args.CullMode, }) return &Material[D]{ device: device, shader: args.Shader, dlayout: descLayout, layout: layout, pipe: pipe, pass: args.Pass, } } func (m *Material[D]) Bind(cmd command.Buffer) { cmd.CmdBindGraphicsPipeline(m.pipe) } func (m *Material[D]) TextureSlots() []texture.Slot { return m.shader.Textures() } func (m *Material[D]) Destroy() { m.dlayout.Destroy() m.pipe.Destroy() m.layout.Destroy() } func (m *Material[D]) Instantiate(pool descriptor.Pool) *Instance[D] { set := m.dlayout.Instantiate(pool) return &Instance[D]{ material: m, set: set, } } func (m *Material[D]) InstantiateMany(pool descriptor.Pool, n int) []*Instance[D] { return util.Map(util.Range(0, n, 1), func(i int) *Instance[D] { return m.Instantiate(pool) }) }