300 lines
10 KiB
Go
300 lines
10 KiB
Go
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))
|
|
}
|