zworld/engine/renderapi/pipeline/pipeline.go

300 lines
10 KiB
Go
Raw Normal View History

2024-01-14 22:56:06 +08:00
package pipeline
import (
"fmt"
"log"
"zworld/engine/renderapi/device"
"zworld/engine/renderapi/renderpass/attachment"
"zworld/engine/renderapi/shader"
"zworld/engine/renderapi/types"
"zworld/engine/renderapi/vertex"
"zworld/engine/util"
"github.com/vkngwrapper/core/v2/core1_0"
"github.com/vkngwrapper/core/v2/driver"
)
type T interface {
device.Resource[core1_0.Pipeline]
Layout() Layout
}
type pipeline struct {
ptr core1_0.Pipeline
device device.T
layout Layout
args Args
}
func New(device device.T, args Args) T {
args.defaults()
log.Println("creating pipeline", args.Key)
// todo: pipeline cache
// could probably be controlled a global setting?
modules := util.Map(args.Shader.Modules(), func(shader shader.Module) core1_0.PipelineShaderStageCreateInfo {
return core1_0.PipelineShaderStageCreateInfo{
Module: shader.Ptr(),
Name: shader.Entrypoint(),
Stage: core1_0.ShaderStageFlags(shader.Stage()),
}
})
log.Println(" depth test:", args.DepthTest)
log.Println(" depth func:", args.DepthFunc)
log.Println(" depth write:", args.DepthWrite)
log.Println(" attributes", args.Pointers)
attrs := pointersToVertexAttributes(args.Pointers, 0)
subpass := args.Pass.Subpass(args.Subpass)
log.Println(" subpass:", subpass.Name, subpass.Index())
blendStates := util.Map(subpass.ColorAttachments, func(name attachment.Name) core1_0.PipelineColorBlendAttachmentState {
attach := args.Pass.Attachment(name)
// todo: move into attachment object
// or into the material/pipeline object?
blend := attach.Blend()
return core1_0.PipelineColorBlendAttachmentState{
// additive blending
BlendEnabled: blend.Enabled,
ColorBlendOp: blend.Color.Operation,
SrcColorBlendFactor: blend.Color.SrcFactor,
DstColorBlendFactor: blend.Color.DstFactor,
AlphaBlendOp: blend.Alpha.Operation,
SrcAlphaBlendFactor: blend.Alpha.SrcFactor,
DstAlphaBlendFactor: blend.Alpha.DstFactor,
ColorWriteMask: core1_0.ColorComponentRed | core1_0.ColorComponentGreen |
core1_0.ColorComponentBlue | core1_0.ColorComponentAlpha,
}
})
info := core1_0.GraphicsPipelineCreateInfo{
// layout
Layout: args.Layout.Ptr(),
Subpass: subpass.Index(),
// renderapi pass
RenderPass: args.Pass.Ptr(),
// Stages
Stages: modules,
// Vertex input state
VertexInputState: &core1_0.PipelineVertexInputStateCreateInfo{
VertexBindingDescriptions: []core1_0.VertexInputBindingDescription{
{
Binding: 0,
Stride: args.Pointers.Stride(),
InputRate: core1_0.VertexInputRateVertex,
},
},
VertexAttributeDescriptions: attrs,
},
// Input assembly
InputAssemblyState: &core1_0.PipelineInputAssemblyStateCreateInfo{
Topology: core1_0.PrimitiveTopology(args.Primitive),
},
// viewport state
// does not seem to matter so much since we set it dynamically every frame
ViewportState: &core1_0.PipelineViewportStateCreateInfo{
Viewports: []core1_0.Viewport{
{
Width: 1000,
Height: 1000,
MinDepth: 0,
MaxDepth: 1,
},
},
Scissors: []core1_0.Rect2D{
// scissor
{
Offset: core1_0.Offset2D{},
Extent: core1_0.Extent2D{
Width: 1000,
Height: 1000,
},
},
},
},
// rasterization state
RasterizationState: &core1_0.PipelineRasterizationStateCreateInfo{
DepthClampEnable: args.DepthClamp,
DepthBiasEnable: false,
RasterizerDiscardEnable: false,
PolygonMode: args.PolygonFillMode,
CullMode: core1_0.CullModeFlags(args.CullMode),
LineWidth: 1,
// clockwise in vulkans right-handed coordinates is equivalent to the
// traditional opengl counter-clockwise winding, which is in line with
// the left-handed world space coordinate system.
FrontFace: core1_0.FrontFaceClockwise,
},
// multisample
MultisampleState: &core1_0.PipelineMultisampleStateCreateInfo{
RasterizationSamples: core1_0.Samples1,
},
// depth & stencil
DepthStencilState: &core1_0.PipelineDepthStencilStateCreateInfo{
// enable depth testing with less or
DepthTestEnable: args.DepthTest,
DepthWriteEnable: args.DepthWrite,
DepthCompareOp: args.DepthFunc,
DepthBoundsTestEnable: false,
Back: core1_0.StencilOpState{
FailOp: core1_0.StencilKeep,
PassOp: core1_0.StencilKeep,
CompareOp: core1_0.CompareOpAlways,
},
StencilTestEnable: args.StencilTest,
Front: core1_0.StencilOpState{
FailOp: core1_0.StencilKeep,
PassOp: core1_0.StencilKeep,
CompareOp: core1_0.CompareOpAlways,
},
},
// color blending
ColorBlendState: &core1_0.PipelineColorBlendStateCreateInfo{
LogicOpEnabled: false,
LogicOp: core1_0.LogicOpClear,
Attachments: blendStates,
},
// dynamic state: viewport & scissor
DynamicState: &core1_0.PipelineDynamicStateCreateInfo{
DynamicStates: []core1_0.DynamicState{
core1_0.DynamicStateViewport,
core1_0.DynamicStateScissor,
},
},
}
ptrs, result, err := device.Ptr().CreateGraphicsPipelines(nil, nil, []core1_0.GraphicsPipelineCreateInfo{info})
if err != nil {
panic(err)
}
if result != core1_0.VKSuccess {
panic("failed to create pipeline")
}
if args.Key != "" {
device.SetDebugObjectName(driver.VulkanHandle(ptrs[0].Handle()), core1_0.ObjectTypePipeline, args.Key)
}
return &pipeline{
ptr: ptrs[0],
device: device,
layout: args.Layout,
args: args,
}
}
func (p *pipeline) Ptr() core1_0.Pipeline {
return p.ptr
}
func (p *pipeline) Layout() Layout {
return p.layout
}
func (p *pipeline) Destroy() {
p.ptr.Destroy(nil)
p.ptr = nil
}
func pointersToVertexAttributes(ptrs vertex.Pointers, binding int) []core1_0.VertexInputAttributeDescription {
attrs := make([]core1_0.VertexInputAttributeDescription, 0, len(ptrs))
for _, ptr := range ptrs {
if ptr.Binding < 0 {
continue
}
attrs = append(attrs, core1_0.VertexInputAttributeDescription{
Binding: binding,
Location: uint32(ptr.Binding),
Format: convertFormat(ptr),
Offset: ptr.Offset,
})
}
return attrs
}
type ptrType struct {
Source types.Type
Target types.Type
Elements int
Normalize bool
}
var formatMap = map[ptrType]core1_0.Format{
{types.Float, types.Float, 1, false}: core1_0.FormatR32SignedFloat,
{types.Float, types.Float, 2, false}: core1_0.FormatR32G32SignedFloat,
{types.Float, types.Float, 3, false}: core1_0.FormatR32G32B32SignedFloat,
{types.Float, types.Float, 4, false}: core1_0.FormatR32G32B32A32SignedFloat,
{types.Int8, types.Int8, 1, false}: core1_0.FormatR8SignedInt,
{types.Int8, types.Int8, 2, false}: core1_0.FormatR8G8SignedInt,
{types.Int8, types.Int8, 3, false}: core1_0.FormatR8G8B8SignedInt,
{types.Int8, types.Int8, 4, false}: core1_0.FormatR8G8B8A8SignedInt,
{types.Int8, types.Float, 4, true}: core1_0.FormatR8SignedNormalized,
{types.Int8, types.Float, 2, true}: core1_0.FormatR8G8SignedNormalized,
{types.Int8, types.Float, 3, true}: core1_0.FormatR8G8B8SignedNormalized,
{types.Int8, types.Float, 4, true}: core1_0.FormatR8G8B8A8SignedNormalized,
{types.UInt8, types.UInt8, 1, false}: core1_0.FormatR8UnsignedInt,
{types.UInt8, types.UInt8, 2, false}: core1_0.FormatR8G8UnsignedInt,
{types.UInt8, types.UInt8, 3, false}: core1_0.FormatR8G8B8UnsignedInt,
{types.UInt8, types.UInt8, 4, false}: core1_0.FormatR8G8B8A8UnsignedInt,
{types.UInt8, types.Float, 1, false}: core1_0.FormatR8UnsignedScaled,
{types.UInt8, types.Float, 2, false}: core1_0.FormatR8G8UnsignedScaled,
{types.UInt8, types.Float, 3, false}: core1_0.FormatR8G8B8UnsignedScaled,
{types.UInt8, types.Float, 4, false}: core1_0.FormatR8G8B8A8UnsignedScaled,
{types.UInt8, types.Float, 1, true}: core1_0.FormatR8UnsignedNormalized,
{types.UInt8, types.Float, 2, true}: core1_0.FormatR8G8UnsignedNormalized,
{types.UInt8, types.Float, 3, true}: core1_0.FormatR8G8B8UnsignedNormalized,
{types.UInt8, types.Float, 4, true}: core1_0.FormatR8G8B8A8UnsignedNormalized,
{types.Int16, types.Int16, 1, false}: core1_0.FormatR16SignedInt,
{types.Int16, types.Int16, 2, false}: core1_0.FormatR16G16SignedInt,
{types.Int16, types.Int16, 3, false}: core1_0.FormatR16G16B16SignedInt,
{types.Int16, types.Int16, 4, false}: core1_0.FormatR16G16B16A16SignedInt,
{types.Int16, types.Float, 4, true}: core1_0.FormatR16SignedNormalized,
{types.Int16, types.Float, 2, true}: core1_0.FormatR16G16SignedNormalized,
{types.Int16, types.Float, 3, true}: core1_0.FormatR16G16B16SignedNormalized,
{types.Int16, types.Float, 4, true}: core1_0.FormatR16G16B16A16SignedNormalized,
{types.UInt16, types.UInt16, 1, false}: core1_0.FormatR16UnsignedInt,
{types.UInt16, types.UInt16, 2, false}: core1_0.FormatR16G16UnsignedInt,
{types.UInt16, types.UInt16, 3, false}: core1_0.FormatR16G16B16UnsignedInt,
{types.UInt16, types.UInt16, 4, false}: core1_0.FormatR16G16B16A16UnsignedInt,
{types.UInt16, types.Float, 1, true}: core1_0.FormatR16UnsignedNormalized,
{types.UInt16, types.Float, 2, true}: core1_0.FormatR16G16UnsignedNormalized,
{types.UInt16, types.Float, 3, true}: core1_0.FormatR16G16B16UnsignedNormalized,
{types.UInt16, types.Float, 4, true}: core1_0.FormatR16G16B16A16UnsignedNormalized,
{types.UInt16, types.Float, 1, false}: core1_0.FormatR16UnsignedScaled,
{types.UInt16, types.Float, 2, false}: core1_0.FormatR16G16UnsignedScaled,
{types.UInt16, types.Float, 3, false}: core1_0.FormatR16G16B16UnsignedScaled,
{types.UInt16, types.Float, 4, false}: core1_0.FormatR16G16B16A16UnsignedScaled,
{types.Int32, types.Int32, 1, false}: core1_0.FormatR32SignedInt,
{types.Int32, types.Int32, 2, false}: core1_0.FormatR32G32SignedInt,
{types.Int32, types.Int32, 3, false}: core1_0.FormatR32G32B32SignedInt,
{types.Int32, types.Int32, 4, false}: core1_0.FormatR32G32B32A32SignedInt,
{types.UInt32, types.UInt32, 1, false}: core1_0.FormatR32UnsignedInt,
{types.UInt32, types.UInt32, 2, false}: core1_0.FormatR32G32UnsignedInt,
{types.UInt32, types.UInt32, 3, false}: core1_0.FormatR32G32B32UnsignedInt,
{types.UInt32, types.UInt32, 4, false}: core1_0.FormatR32G32B32A32UnsignedInt,
}
func convertFormat(ptr vertex.Pointer) core1_0.Format {
kind := ptrType{ptr.Source, ptr.Destination, ptr.Elements, ptr.Normalize}
if fmt, exists := formatMap[kind]; exists {
return fmt
}
panic(fmt.Sprintf("illegal format in pointer %s from %s -> %s x%d (normalize: %t)", ptr.Name, ptr.Source, ptr.Destination, ptr.Elements, ptr.Normalize))
}