333 lines
15 KiB
Zig
333 lines
15 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vulkan");
|
|
|
|
const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
|
|
|
|
const VkError = @import("error_set.zig").VkError;
|
|
|
|
const Device = @import("Device.zig");
|
|
const PipelineCache = @import("PipelineCache.zig");
|
|
const PipelineLayout = @import("PipelineLayout.zig");
|
|
const RenderPass = @import("RenderPass.zig");
|
|
|
|
const Self = @This();
|
|
pub const ObjectType: vk.ObjectType = .pipeline;
|
|
|
|
const DynamicState = struct {
|
|
viewport: bool = false,
|
|
scissor: bool = false,
|
|
line_width: bool = false,
|
|
depth_bias: bool = false,
|
|
blend_constants: bool = false,
|
|
depth_bounds: bool = false,
|
|
stencil_compare_mask: bool = false,
|
|
stencil_write_mask: bool = false,
|
|
stencil_reference: bool = false,
|
|
};
|
|
|
|
owner: *Device,
|
|
|
|
vtable: *const VTable,
|
|
bind_point: vk.PipelineBindPoint,
|
|
stages: vk.ShaderStageFlags,
|
|
layout: *PipelineLayout,
|
|
mode: union(enum) {
|
|
compute: struct {},
|
|
graphics: struct {
|
|
input_assembly: struct {
|
|
binding_description: ?[]vk.VertexInputBindingDescription,
|
|
attribute_description: ?[]vk.VertexInputAttributeDescription,
|
|
topology: vk.PrimitiveTopology,
|
|
primitive_restart_enable: vk.Bool32,
|
|
},
|
|
viewport_state: struct {
|
|
viewports: ?[]vk.Viewport,
|
|
scissor: ?[]vk.Rect2D,
|
|
},
|
|
rasterization: struct {
|
|
rasterizer_discard_enable: bool,
|
|
polygon_mode: vk.PolygonMode,
|
|
cull_mode: vk.CullModeFlags,
|
|
front_face: vk.FrontFace,
|
|
line_width: f32,
|
|
depth_bias_enable: vk.Bool32,
|
|
depth_bias_constant_factor: f32,
|
|
depth_bias_clamp: f32,
|
|
depth_bias_slope_factor: f32,
|
|
},
|
|
multisample: struct {
|
|
rasterization_samples: vk.SampleCountFlags,
|
|
sample_mask: ?[]vk.SampleMask,
|
|
alpha_to_coverage_enable: vk.Bool32,
|
|
alpha_to_one_enable: vk.Bool32,
|
|
},
|
|
color_blend: struct {
|
|
attachments: ?[]vk.PipelineColorBlendAttachmentState,
|
|
constants: [4]f32,
|
|
},
|
|
depth_stencil: ?vk.PipelineDepthStencilStateCreateInfo,
|
|
dynamic_state: DynamicState,
|
|
},
|
|
},
|
|
|
|
pub const VTable = struct {
|
|
destroy: *const fn (*Self, std.mem.Allocator) void,
|
|
};
|
|
|
|
pub fn initCompute(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.ComputePipelineCreateInfo) VkError!Self {
|
|
_ = cache;
|
|
|
|
const layout = try NonDispatchable(PipelineLayout).fromHandleObject(info.layout);
|
|
layout.ref();
|
|
errdefer layout.unref(allocator);
|
|
|
|
return .{
|
|
.owner = device,
|
|
// SAFETY: the backend assigns the vtable before returning the compute pipeline.
|
|
.vtable = undefined,
|
|
.bind_point = .compute,
|
|
.stages = info.stage.stage,
|
|
.layout = layout,
|
|
.mode = .{ .compute = .{} },
|
|
};
|
|
}
|
|
|
|
pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.GraphicsPipelineCreateInfo) VkError!Self {
|
|
_ = cache;
|
|
|
|
const layout = try NonDispatchable(PipelineLayout).fromHandleObject(info.layout);
|
|
layout.ref();
|
|
errdefer layout.unref(allocator);
|
|
|
|
var stages: vk.ShaderStageFlags = .{};
|
|
if (info.p_stages) |p_stages| {
|
|
for (p_stages[0..info.stage_count]) |stage| {
|
|
stages = stages.merge(stage.stage);
|
|
}
|
|
}
|
|
|
|
var binding_description: ?[]vk.VertexInputBindingDescription = null;
|
|
errdefer if (binding_description) |value| allocator.free(value);
|
|
|
|
var attribute_description: ?[]vk.VertexInputAttributeDescription = null;
|
|
errdefer if (attribute_description) |value| allocator.free(value);
|
|
|
|
var viewports: ?[]vk.Viewport = null;
|
|
errdefer if (viewports) |value| allocator.free(value);
|
|
|
|
var scissors: ?[]vk.Rect2D = null;
|
|
errdefer if (scissors) |value| allocator.free(value);
|
|
|
|
var sample_mask: ?[]vk.SampleMask = null;
|
|
errdefer if (sample_mask) |value| allocator.free(value);
|
|
|
|
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
|
|
errdefer if (color_attachments) |value| allocator.free(value);
|
|
|
|
const dynamic_state = try parseDynamicState(info.p_dynamic_state);
|
|
const rasterizer_discard_enable = if (info.p_rasterization_state) |state|
|
|
state.rasterizer_discard_enable == .true
|
|
else
|
|
false;
|
|
const has_color_attachments, const has_depth_stencil_attachment = try renderPassAttachmentState(info);
|
|
|
|
return .{
|
|
.owner = device,
|
|
// SAFETY: the backend assigns the vtable before returning the graphics pipeline.
|
|
.vtable = undefined,
|
|
.bind_point = .graphics,
|
|
.stages = stages,
|
|
.layout = layout,
|
|
.mode = .{
|
|
.graphics = .{
|
|
.input_assembly = .{
|
|
.binding_description = blk: {
|
|
if (info.p_vertex_input_state) |vertex_input_state| {
|
|
if (vertex_input_state.p_vertex_binding_descriptions) |vertex_binding_descriptions| {
|
|
binding_description = allocator.dupe(vk.VertexInputBindingDescription, vertex_binding_descriptions[0..vertex_input_state.vertex_binding_description_count]) catch return VkError.OutOfHostMemory;
|
|
break :blk binding_description;
|
|
}
|
|
} else {
|
|
return VkError.ValidationFailed;
|
|
}
|
|
break :blk null;
|
|
},
|
|
.attribute_description = blk: {
|
|
if (info.p_vertex_input_state) |vertex_input_state| {
|
|
if (vertex_input_state.p_vertex_attribute_descriptions) |vertex_attribute_descriptions| {
|
|
attribute_description = allocator.dupe(vk.VertexInputAttributeDescription, vertex_attribute_descriptions[0..vertex_input_state.vertex_attribute_description_count]) catch return VkError.OutOfHostMemory;
|
|
break :blk attribute_description;
|
|
}
|
|
} else {
|
|
return VkError.ValidationFailed;
|
|
}
|
|
break :blk null;
|
|
},
|
|
.topology = if (info.p_input_assembly_state) |state| state.topology else return VkError.ValidationFailed,
|
|
.primitive_restart_enable = if (info.p_input_assembly_state) |state| state.primitive_restart_enable else return VkError.ValidationFailed,
|
|
},
|
|
.viewport_state = .{
|
|
.viewports = blk: {
|
|
if (rasterizer_discard_enable or dynamic_state.viewport) {
|
|
break :blk null;
|
|
}
|
|
if (info.p_viewport_state) |viewport_state| {
|
|
if (viewport_state.viewport_count != 0) {
|
|
const p_viewports = viewport_state.p_viewports orelse return VkError.ValidationFailed;
|
|
const copy = allocator.dupe(vk.Viewport, p_viewports[0..viewport_state.viewport_count]) catch return VkError.OutOfHostMemory;
|
|
viewports = copy;
|
|
break :blk viewports;
|
|
}
|
|
}
|
|
break :blk null;
|
|
},
|
|
.scissor = blk: {
|
|
if (rasterizer_discard_enable or dynamic_state.scissor) {
|
|
break :blk null;
|
|
}
|
|
if (info.p_viewport_state) |viewport_state| {
|
|
if (viewport_state.scissor_count != 0) {
|
|
const p_scissors = viewport_state.p_scissors orelse return VkError.ValidationFailed;
|
|
const copy = allocator.dupe(vk.Rect2D, p_scissors[0..viewport_state.scissor_count]) catch return VkError.OutOfHostMemory;
|
|
scissors = copy;
|
|
break :blk scissors;
|
|
}
|
|
}
|
|
break :blk null;
|
|
},
|
|
},
|
|
.rasterization = .{
|
|
.rasterizer_discard_enable = rasterizer_discard_enable,
|
|
.polygon_mode = if (info.p_rasterization_state) |state| state.polygon_mode else return VkError.ValidationFailed,
|
|
.cull_mode = if (info.p_rasterization_state) |state| state.cull_mode else return VkError.ValidationFailed,
|
|
.front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,
|
|
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
|
|
.depth_bias_enable = if (info.p_rasterization_state) |state| state.depth_bias_enable else return VkError.ValidationFailed,
|
|
.depth_bias_constant_factor = if (info.p_rasterization_state) |state| state.depth_bias_constant_factor else return VkError.ValidationFailed,
|
|
.depth_bias_clamp = if (info.p_rasterization_state) |state| state.depth_bias_clamp else return VkError.ValidationFailed,
|
|
.depth_bias_slope_factor = if (info.p_rasterization_state) |state| state.depth_bias_slope_factor else return VkError.ValidationFailed,
|
|
},
|
|
.multisample = blk: {
|
|
if (rasterizer_discard_enable) {
|
|
break :blk .{
|
|
.rasterization_samples = .{ .@"1_bit" = true },
|
|
.sample_mask = null,
|
|
.alpha_to_coverage_enable = .false,
|
|
.alpha_to_one_enable = .false,
|
|
};
|
|
}
|
|
|
|
const state = info.p_multisample_state orelse return VkError.ValidationFailed;
|
|
const mask_word_count: usize = @divTrunc(state.rasterization_samples.toInt() + 31, 32);
|
|
break :blk .{
|
|
.rasterization_samples = state.rasterization_samples,
|
|
.sample_mask = if (state.p_sample_mask) |mask| blk_mask: {
|
|
sample_mask = allocator.dupe(vk.SampleMask, mask[0..mask_word_count]) catch return VkError.OutOfHostMemory;
|
|
break :blk_mask sample_mask;
|
|
} else null,
|
|
.alpha_to_coverage_enable = state.alpha_to_coverage_enable,
|
|
.alpha_to_one_enable = state.alpha_to_one_enable,
|
|
};
|
|
},
|
|
.color_blend = blk: {
|
|
if (rasterizer_discard_enable or !has_color_attachments) {
|
|
break :blk .{
|
|
.attachments = null,
|
|
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
|
|
};
|
|
}
|
|
|
|
if (info.p_color_blend_state) |state| {
|
|
break :blk .{
|
|
.attachments = if (state.attachment_count != 0) blk_attachments: {
|
|
const attachments = state.p_attachments orelse return VkError.ValidationFailed;
|
|
color_attachments = allocator.dupe(vk.PipelineColorBlendAttachmentState, attachments[0..state.attachment_count]) catch return VkError.OutOfHostMemory;
|
|
break :blk_attachments color_attachments;
|
|
} else null,
|
|
.constants = state.blend_constants,
|
|
};
|
|
}
|
|
|
|
break :blk .{
|
|
.attachments = null,
|
|
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
|
|
};
|
|
},
|
|
.depth_stencil = if (rasterizer_discard_enable or !has_depth_stencil_attachment)
|
|
null
|
|
else if (info.p_depth_stencil_state) |state|
|
|
state.*
|
|
else
|
|
null,
|
|
.dynamic_state = dynamic_state,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
fn parseDynamicState(info: ?*const vk.PipelineDynamicStateCreateInfo) VkError!DynamicState {
|
|
var state: DynamicState = .{};
|
|
const dynamic_state = info orelse return state;
|
|
if (dynamic_state.dynamic_state_count == 0) {
|
|
return state;
|
|
}
|
|
|
|
const states = dynamic_state.p_dynamic_states orelse return VkError.ValidationFailed;
|
|
for (states[0..dynamic_state.dynamic_state_count]) |info_state| {
|
|
switch (info_state) {
|
|
.viewport => state.viewport = true,
|
|
.scissor => state.scissor = true,
|
|
.line_width => state.line_width = true,
|
|
.depth_bias => state.depth_bias = true,
|
|
.blend_constants => state.blend_constants = true,
|
|
.depth_bounds => state.depth_bounds = true,
|
|
.stencil_compare_mask => state.stencil_compare_mask = true,
|
|
.stencil_write_mask => state.stencil_write_mask = true,
|
|
.stencil_reference => state.stencil_reference = true,
|
|
else => return VkError.Unknown,
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
fn renderPassAttachmentState(info: *const vk.GraphicsPipelineCreateInfo) VkError!struct { bool, bool } {
|
|
if (info.render_pass == .null_handle) {
|
|
return .{ true, true };
|
|
}
|
|
|
|
const render_pass = try NonDispatchable(RenderPass).fromHandleObject(info.render_pass);
|
|
return .{
|
|
try render_pass.subpassHasColorAttachments(info.subpass),
|
|
try render_pass.subpassHasDepthStencilAttachment(info.subpass),
|
|
};
|
|
}
|
|
|
|
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
|
switch (self.mode) {
|
|
.compute => {},
|
|
.graphics => |graphics| {
|
|
if (graphics.input_assembly.binding_description) |binding_description| {
|
|
allocator.free(binding_description);
|
|
}
|
|
if (graphics.input_assembly.attribute_description) |attribute_description| {
|
|
allocator.free(attribute_description);
|
|
}
|
|
if (graphics.viewport_state.viewports) |viewports| {
|
|
allocator.free(viewports);
|
|
}
|
|
if (graphics.viewport_state.scissor) |scissor| {
|
|
allocator.free(scissor);
|
|
}
|
|
if (graphics.multisample.sample_mask) |sample_mask| {
|
|
allocator.free(sample_mask);
|
|
}
|
|
if (graphics.color_blend.attachments) |attachments| {
|
|
allocator.free(attachments);
|
|
}
|
|
},
|
|
}
|
|
self.layout.unref(allocator);
|
|
self.vtable.destroy(self, allocator);
|
|
}
|