Files
VulkanDriver/src/vulkan/Pipeline.zig
T
kbz_8 2375abf688
Test / build_and_test (push) Successful in 5m28s
Build / build (push) Successful in 7m26s
fixing linter errors
2026-07-15 15:39:16 +02:00

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);
}