sideros/src/rendering/GraphicsPipeline.zig
2025-08-07 14:04:41 +02:00

513 lines
19 KiB
Zig

const std = @import("std");
const Mesh = @import("Mesh.zig");
const Camera = @import("Camera.zig");
const vk = @import("vulkan.zig");
const Texture = vk.Texture;
const c = vk.c;
const math = @import("math");
layout: c.VkPipelineLayout,
handle: c.VkPipeline,
texture_set_layout: c.VkDescriptorSetLayout,
descriptor_pool: c.VkDescriptorPool,
descriptor_set: c.VkDescriptorSet,
descriptor_set_layout: c.VkDescriptorSetLayout,
projection_buffer: vk.Buffer,
light_buffer: vk.Buffer,
view_buffer: vk.Buffer,
view_memory: [*c]u8,
transform_memory: [*c]u8,
view_pos_memory: [*c]u8,
texture_sampler: vk.Sampler,
diffuse_sampler: vk.Sampler,
textures: std.ArrayList(c.VkDescriptorSet),
light_pos: [*]f32,
const Self = @This();
pub fn init(allocator: std.mem.Allocator, device: vk.Device, swapchain: vk.Swapchain, render_pass: vk.RenderPass, vertex_shader: c.VkShaderModule, fragment_shader: c.VkShaderModule) !Self {
const vertex_shader_stage_info: c.VkPipelineShaderStageCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = c.VK_SHADER_STAGE_VERTEX_BIT,
.module = vertex_shader,
.pName = "main",
};
const fragment_shader_stage_info: c.VkPipelineShaderStageCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = c.VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragment_shader,
.pName = "main",
};
// TODO: shouldn't this be closer to usage?
const shader_stage_infos: []const c.VkPipelineShaderStageCreateInfo = &.{ vertex_shader_stage_info, fragment_shader_stage_info };
const vertex_attributes: []const c.VkVertexInputAttributeDescription = Mesh.Vertex.attributeDescriptions();
const vertex_bindings: []const c.VkVertexInputBindingDescription = &.{Mesh.Vertex.bindingDescription()};
const vertex_input_info: c.VkPipelineVertexInputStateCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = vertex_bindings.ptr,
.vertexAttributeDescriptionCount = 3,
.pVertexAttributeDescriptions = vertex_attributes.ptr,
};
const input_assembly_info: c.VkPipelineInputAssemblyStateCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = c.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = c.VK_FALSE,
};
const viewport: c.VkViewport = .{
.x = 0.0,
.y = 0.0,
.width = @floatFromInt(swapchain.extent.width),
.height = @floatFromInt(swapchain.extent.height),
.minDepth = 0.0,
.maxDepth = 1.0,
};
const scissor: c.VkRect2D = .{
.offset = .{
.x = 0.0,
.y = 0.0,
},
.extent = swapchain.extent,
};
const viewport_state_info: c.VkPipelineViewportStateCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scissor,
};
const rasterizer_info: c.VkPipelineRasterizationStateCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = c.VK_FALSE,
.rasterizerDiscardEnable = c.VK_FALSE,
.polygonMode = c.VK_POLYGON_MODE_FILL,
.lineWidth = 1.0,
.cullMode = c.VK_CULL_MODE_BACK_BIT,
.frontFace = c.VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = c.VK_FALSE,
};
const multisampling_info: c.VkPipelineMultisampleStateCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = c.VK_FALSE,
.rasterizationSamples = c.VK_SAMPLE_COUNT_1_BIT,
};
const color_blend_attachment: c.VkPipelineColorBlendAttachmentState = .{
.colorWriteMask = c.VK_COLOR_COMPONENT_R_BIT | c.VK_COLOR_COMPONENT_G_BIT | c.VK_COLOR_COMPONENT_B_BIT | c.VK_COLOR_COMPONENT_A_BIT,
.blendEnable = c.VK_TRUE,
.srcColorBlendFactor = c.VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = c.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = c.VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = c.VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = c.VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = c.VK_BLEND_OP_ADD,
};
const color_blend_info: c.VkPipelineColorBlendStateCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = c.VK_FALSE,
.logicOp = c.VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &color_blend_attachment,
.blendConstants = .{ 0.0, 0.0, 0.0, 0.0 },
};
const projection_binding = c.VkDescriptorSetLayoutBinding{
.binding = 0,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT,
};
const view_binding = c.VkDescriptorSetLayoutBinding{
.binding = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT,
};
const transform_binding = c.VkDescriptorSetLayoutBinding{
.binding = 4,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT,
};
const light_binding = c.VkDescriptorSetLayoutBinding{
.binding = 2,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT,
};
const view_pos_binding = c.VkDescriptorSetLayoutBinding{
.binding = 3,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT,
};
const texture_sampler_binding = c.VkDescriptorSetLayoutBinding{
.binding = 0,
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT,
};
const diffuse_sampler_binding = c.VkDescriptorSetLayoutBinding{
.binding = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT,
};
const bindings = [_]c.VkDescriptorSetLayoutBinding{projection_binding, view_binding, transform_binding, light_binding, view_pos_binding};
const texture_bindings = [_]c.VkDescriptorSetLayoutBinding{texture_sampler_binding, diffuse_sampler_binding};
var descriptor_set_layout: c.VkDescriptorSetLayout = undefined;
var texture_descriptor_set_layout: c.VkDescriptorSetLayout = undefined;
const descriptor_set_layout_info = c.VkDescriptorSetLayoutCreateInfo{
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 5,
.pBindings = bindings[0..].ptr,
};
const texture_descriptor_set_layout_info = c.VkDescriptorSetLayoutCreateInfo{
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 2,
.pBindings = texture_bindings[0..].ptr,
};
try vk.mapError(c.vkCreateDescriptorSetLayout(device.handle, &descriptor_set_layout_info, null, &descriptor_set_layout));
try vk.mapError(c.vkCreateDescriptorSetLayout(device.handle, &texture_descriptor_set_layout_info, null, &texture_descriptor_set_layout));
var set_layouts = [_]c.VkDescriptorSetLayout{descriptor_set_layout, texture_descriptor_set_layout};
const layout_info: c.VkPipelineLayoutCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 2,
.pSetLayouts = set_layouts[0..].ptr,
.pushConstantRangeCount = 0,
.pPushConstantRanges = null,
};
var layout: c.VkPipelineLayout = undefined;
try vk.mapError(c.vkCreatePipelineLayout(device.handle, &layout_info, null, @ptrCast(&layout)));
const pipeline_info: c.VkGraphicsPipelineCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = shader_stage_infos.ptr,
.pVertexInputState = &vertex_input_info,
.pInputAssemblyState = &input_assembly_info,
.pViewportState = &viewport_state_info,
.pRasterizationState = &rasterizer_info,
.pMultisampleState = &multisampling_info,
.pDepthStencilState = null,
.pColorBlendState = &color_blend_info,
.pDynamicState = null,
.layout = layout,
.renderPass = render_pass.handle,
.subpass = 0,
.basePipelineHandle = null,
.basePipelineIndex = -1,
};
var pipeline: c.VkPipeline = undefined;
try vk.mapError(c.vkCreateGraphicsPipelines(device.handle, null, 1, &pipeline_info, null, @ptrCast(&pipeline)));
const size = c.VkDescriptorPoolSize{
.type = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 5,
};
const sampler_size = c.VkDescriptorPoolSize{
.type = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 2,
};
const sizes = [_]c.VkDescriptorPoolSize {size, sampler_size};
const descriptor_pool_info = c.VkDescriptorPoolCreateInfo{
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = 2,
.poolSizeCount = 2,
.pPoolSizes = sizes[0..].ptr,
};
var descriptor_pool: c.VkDescriptorPool = undefined;
try vk.mapError(c.vkCreateDescriptorPool(device.handle, &descriptor_pool_info, null, &descriptor_pool));
const descriptor_allocate_info = c.VkDescriptorSetAllocateInfo{
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptor_pool,
.descriptorSetCount = 1,
.pSetLayouts = set_layouts[0..].ptr,
};
var descriptor_set: c.VkDescriptorSet = undefined;
try vk.mapError(c.vkAllocateDescriptorSets(device.handle, &descriptor_allocate_info, &descriptor_set));
const projection_buffer = try device.initBuffer(vk.BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(math.Matrix));
var data: [*c]u8 = undefined;
try vk.mapError(c.vkMapMemory(
device.handle,
projection_buffer.memory,
0,
projection_buffer.size,
0,
@ptrCast(&data),
));
@memcpy(data[0..@sizeOf(math.Matrix)], std.mem.asBytes(&Camera.getProjection(swapchain.extent.width, swapchain.extent.height)));
const descriptor_buffer_info = c.VkDescriptorBufferInfo{
.buffer = projection_buffer.handle,
.offset = 0,
.range = projection_buffer.size,
};
const write_descriptor_set = c.VkWriteDescriptorSet{
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor_set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &descriptor_buffer_info,
};
c.vkUpdateDescriptorSets(device.handle, 1, &write_descriptor_set, 0, null);
const view_buffer = try device.initBuffer(vk.BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(math.Matrix));
var view_data: [*c]u8 = undefined;
try vk.mapError(c.vkMapMemory(
device.handle,
view_buffer.memory,
0,
view_buffer.size,
0,
@ptrCast(&view_data),
));
const view_descriptor_buffer_info = c.VkDescriptorBufferInfo{
.buffer = view_buffer.handle,
.offset = 0,
.range = view_buffer.size,
};
const write_view_descriptor_set = c.VkWriteDescriptorSet{
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor_set,
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &view_descriptor_buffer_info,
};
c.vkUpdateDescriptorSets(device.handle, 1, &write_view_descriptor_set, 0, null);
const transform_buffer = try device.initBuffer(vk.BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(math.Transform) - @sizeOf(math.Quaternion));
var transform_data: [*c]u8 = undefined;
try vk.mapError(c.vkMapMemory(
device.handle,
transform_buffer.memory,
0,
transform_buffer.size,
0,
@ptrCast(&transform_data),
));
const transform_descriptor_buffer_info = c.VkDescriptorBufferInfo{
.buffer = transform_buffer.handle,
.offset = 0,
.range = transform_buffer.size,
};
const write_transform_descriptor_set = c.VkWriteDescriptorSet{
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor_set,
.dstBinding = 4,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &transform_descriptor_buffer_info,
};
c.vkUpdateDescriptorSets(device.handle, 1, &write_transform_descriptor_set, 0, null);
const light_buffer = try device.initBuffer(vk.BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf([3]f32));
var light_data: [*c]u8 = undefined;
try vk.mapError(c.vkMapMemory(
device.handle,
light_buffer.memory,
0,
light_buffer.size,
0,
@ptrCast(&light_data),
));
const light_pos: [*]f32 = @alignCast(@ptrCast(light_data));
const light_descriptor_buffer_info = c.VkDescriptorBufferInfo{
.buffer = light_buffer.handle,
.offset = 0,
.range = light_buffer.size,
};
const write_light_descriptor_set = c.VkWriteDescriptorSet{
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor_set,
.dstBinding = 2,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &light_descriptor_buffer_info,
};
c.vkUpdateDescriptorSets(device.handle, 1, &write_light_descriptor_set, 0, null);
const view_pos_buffer = try device.initBuffer(vk.BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf([3]f32));
var view_pos_data: [*c]u8 = undefined;
try vk.mapError(c.vkMapMemory(
device.handle,
view_pos_buffer.memory,
0,
view_pos_buffer.size,
0,
@ptrCast(&view_pos_data),
));
const view_pos_descriptor_buffer_info = c.VkDescriptorBufferInfo{
.buffer = view_pos_buffer.handle,
.offset = 0,
.range = view_pos_buffer.size,
};
const write_view_pos_descriptor_set = c.VkWriteDescriptorSet{
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor_set,
.dstBinding = 3,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &view_pos_descriptor_buffer_info,
};
c.vkUpdateDescriptorSets(device.handle, 1, &write_view_pos_descriptor_set, 0, null);
return Self{
.layout = layout,
.handle = pipeline,
.texture_set_layout = texture_descriptor_set_layout,
.descriptor_pool = descriptor_pool,
.descriptor_set = descriptor_set,
.descriptor_set_layout = descriptor_set_layout,
.projection_buffer = projection_buffer,
.view_buffer = view_buffer,
.view_memory = view_data,
.light_buffer = light_buffer,
.view_pos_memory = view_pos_data,
.transform_memory = transform_data,
.texture_sampler = try vk.Sampler.init(device),
.diffuse_sampler = try vk.Sampler.init(device),
.textures = std.ArrayList(c.VkDescriptorSet).init(allocator),
.light_pos = light_pos,
};
}
pub fn addTexture(self: *Self, device: anytype, texture: Texture, diffuse: Texture) !usize {
var set_layouts = [_]c.VkDescriptorSetLayout{self.texture_set_layout};
const descriptor_allocate_info = c.VkDescriptorSetAllocateInfo{
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = self.descriptor_pool,
.descriptorSetCount = 1,
.pSetLayouts = set_layouts[0..].ptr,
};
var descriptor_set: c.VkDescriptorSet = undefined;
try vk.mapError(c.vkAllocateDescriptorSets(device.handle, &descriptor_allocate_info, &descriptor_set));
const texture_info: c.VkDescriptorImageInfo = .{
.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = texture.image_view,
.sampler = self.texture_sampler.handle,
};
const diffuse_info: c.VkDescriptorImageInfo = .{
.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = diffuse.image_view,
.sampler = self.diffuse_sampler.handle,
};
const write_texture_descriptor_set = c.VkWriteDescriptorSet{
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor_set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &texture_info,
};
const write_diffuse_descriptor_set = c.VkWriteDescriptorSet{
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor_set,
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &diffuse_info,
};
const writes = [_]c.VkWriteDescriptorSet {write_texture_descriptor_set, write_diffuse_descriptor_set};
c.vkUpdateDescriptorSets(device.handle, 2, writes[0..].ptr, 0, null);
const index = self.textures.items.len;
try self.textures.append(descriptor_set);
return index;
}
pub fn bind(self: Self, device: vk.Device, frame: usize) void {
std.debug.assert(frame < 2);
c.vkCmdBindPipeline(device.command_buffers[frame], c.VK_PIPELINE_BIND_POINT_GRAPHICS, self.handle);
}
pub fn deinit(self: Self, device: vk.Device) void {
self.textures.deinit();
self.texture_sampler.deinit(device);
self.diffuse_sampler.deinit(device);
self.projection_buffer.deinit(device.handle);
c.vkDestroyDescriptorSetLayout(device.handle, self.descriptor_set_layout, null);
c.vkDestroyDescriptorPool(device.handle, self.descriptor_pool, null);
c.vkDestroyPipeline(device.handle, self.handle, null);
c.vkDestroyPipelineLayout(device.handle, self.layout, null);
}