sideros/src/rendering/Device.zig
2025-08-18 23:43:10 +02:00

345 lines
13 KiB
Zig

const std = @import("std");
const vk = @import("vulkan.zig");
const Mesh = @import("Mesh.zig");
const Texture = vk.Texture;
const c = vk.c;
const frames_in_flight = vk.frames_in_flight;
handle: c.VkDevice,
graphics_queue: c.VkQueue,
present_queue: c.VkQueue,
command_pool: c.VkCommandPool,
command_buffers: [frames_in_flight]c.VkCommandBuffer,
image_available: [frames_in_flight]c.VkSemaphore,
render_finished: [frames_in_flight]c.VkSemaphore,
in_flight_fence: [frames_in_flight]c.VkFence,
graphics_family: u32,
present_family: u32,
device_properties: c.VkPhysicalDeviceProperties,
memory_properties: c.VkPhysicalDeviceMemoryProperties,
msaa_samples: c.VkSampleCountFlags,
const Self = @This();
pub fn resetCommand(self: Self, frame: usize) !void {
std.debug.assert(frame < frames_in_flight);
try vk.mapError(c.vkResetCommandBuffer(self.command_buffers[frame], 0));
}
pub fn beginCommand(self: Self, frame: usize) !void {
std.debug.assert(frame < frames_in_flight);
const begin_info: c.VkCommandBufferBeginInfo = .{
.sType = c.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = c.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
try vk.mapError(c.vkBeginCommandBuffer(self.command_buffers[frame], &begin_info));
}
pub fn endCommand(self: Self, frame: usize) !void {
std.debug.assert(frame < frames_in_flight);
try vk.mapError(c.vkEndCommandBuffer(self.command_buffers[frame]));
}
pub fn beginSingleTimeCommands(self: Self) !c.VkCommandBuffer {
const command_buffer_info: c.VkCommandBufferAllocateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = self.command_pool,
.level = c.VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
var command_buffer: c.VkCommandBuffer = undefined;
try vk.mapError(c.vkAllocateCommandBuffers(self.handle, &command_buffer_info, @ptrCast(&command_buffer)));
const begin_info: c.VkCommandBufferBeginInfo = .{
.sType = c.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = c.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
try vk.mapError(c.vkBeginCommandBuffer(command_buffer, &begin_info));
return command_buffer;
}
pub fn endSingleTimeCommands(self: Self, command_buffer: c.VkCommandBuffer) !void {
try vk.mapError(c.vkEndCommandBuffer(command_buffer));
const submit_info: c.VkSubmitInfo = .{
.sType = c.VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &command_buffer,
};
try vk.mapError(c.vkQueueSubmit(self.graphics_queue, 1, &submit_info, null));
try vk.mapError(c.vkQueueWaitIdle(self.graphics_queue));
c.vkFreeCommandBuffers(self.handle, self.command_pool, 1, &command_buffer);
}
pub fn copyBufferToImage(self: Self, buffer: vk.Buffer, image: c.VkImage, width: u32, height: u32) !void {
const command_buffer = try self.beginSingleTimeCommands();
const region: c.VkBufferImageCopy = .{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = .{
.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = .{
.x = 0, .y = 0, .z = 0,
},
.imageExtent = .{
.width = width, .height = height, .depth = 1,
},
};
c.vkCmdCopyBufferToImage(
command_buffer,
buffer.handle,
image,
c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
try self.endSingleTimeCommands(command_buffer);
}
pub fn transitionImageLayout(self: Self, image: c.VkImage, format: c.VkFormat, old_layout: c.VkImageLayout, new_layout: c.VkImageLayout) !void {
_ = format;
const command_buffer = try self.beginSingleTimeCommands();
var barrier: c.VkImageMemoryBarrier = .{
.sType = c.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = old_layout,
.newLayout = new_layout,
.srcQueueFamilyIndex = c.VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = c.VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = .{
.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = 0,
.dstAccessMask = 0,
};
var sourceStage: c.VkPipelineStageFlags = undefined;
var destinationStage: c.VkPipelineStageFlags = undefined;
if (old_layout == c.VK_IMAGE_LAYOUT_UNDEFINED and new_layout == c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = c.VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = c.VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = c.VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (old_layout == c.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and new_layout == c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = c.VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = c.VK_ACCESS_SHADER_READ_BIT;
sourceStage = c.VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = c.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
return error.UnsupportedTransition;
}
c.vkCmdPipelineBarrier(
command_buffer,
sourceStage,
destinationStage,
0,
0, null,
0, null,
1, &barrier
);
try self.endSingleTimeCommands(command_buffer);
}
pub fn drawTerrain(self: Self, indices: u32, frame: usize, vertex_buffer: vk.Buffer, index_buffer: vk.Buffer) void {
std.debug.assert(frame < frames_in_flight);
c.vkCmdBindIndexBuffer(self.command_buffers[frame], index_buffer.handle, 0, c.VK_INDEX_TYPE_UINT32);
self.bindVertexBuffer(vertex_buffer, frame);
c.vkCmdDrawIndexed(self.command_buffers[frame], indices, 1, 0, 0, 0);
}
pub fn draw(self: Self, indices: u32, frame: usize, mesh: Mesh) void {
std.debug.assert(frame < frames_in_flight);
c.vkCmdDrawIndexed(self.command_buffers[frame], indices, 1, mesh.index_buffer, mesh.vertex_buffer, 0);
}
pub fn findMemoryType(self: Self, filter: u32, properties: c.VkMemoryPropertyFlags) error{NoSuitableMemory}!u32 {
const memory_properties = self.memory_properties;
for (0..memory_properties.memoryTypeCount) |i| {
if ((filter & (@as(u32, 1) << @intCast(i))) != 0 and (memory_properties.memoryTypes[i].propertyFlags & properties) == properties) {
return @intCast(i);
}
}
return error.NoSuitableMemory;
}
pub fn waitFence(self: Self, frame: usize) !void {
//std.debug.assert(frame < n);
try vk.mapError(c.vkWaitForFences(self.handle, 1, &self.in_flight_fence[frame], c.VK_TRUE, std.math.maxInt(u64)));
try vk.mapError(c.vkResetFences(self.handle, 1, &self.in_flight_fence[frame]));
}
pub fn waitIdle(self: Self) void {
const mapErrorRes = vk.mapError(c.vkDeviceWaitIdle(self.handle));
if (mapErrorRes) {} else |err| {
std.debug.panic("Vulkan wait idle error: {any}\n", .{err});
}
}
pub fn bindIndexBuffer(self: Self, buffer: vk.Buffer, frame: usize) void {
std.debug.assert(frame < frames_in_flight);
c.vkCmdBindIndexBuffer(self.command_buffers[frame], buffer.handle, 0, c.VK_INDEX_TYPE_UINT16);
}
pub fn bindVertexBuffer(self: Self, buffer: vk.Buffer, frame: usize) void {
std.debug.assert(frame < frames_in_flight);
const offset: u64 = 0;
c.vkCmdBindVertexBuffers(self.command_buffers[frame], 0, 1, &buffer.handle, &offset);
}
pub fn bindDescriptorSets(self: Self, pipeline: vk.GraphicsPipeline, frame: usize) void {
const sets = [_]c.VkDescriptorSet {pipeline.descriptor_set, pipeline.textures};
c.vkCmdBindDescriptorSets(self.command_buffers[frame], c.VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 2, sets[0..].ptr, 0, null);
}
pub fn bindTerrainSets(self: Self, pipeline: vk.TerrainPipeline, frame: usize) void {
const sets = [_]c.VkDescriptorSet {pipeline.descriptor_set, pipeline.map};
c.vkCmdBindDescriptorSets(self.command_buffers[frame], c.VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 2, sets[0..].ptr, 0, null);
}
pub fn updateBuffer(self: Self, comptime T: type, buffer: vk.Buffer, data: [*]T, frame: usize) void {
c.vkCmdUpdateBuffer(self.command_buffers[frame], buffer.handle, 0, @sizeOf(T), @ptrCast(@alignCast(data)));
}
pub fn pick_memory_type(self: Self, type_bits: u32, flags: u32) u32 {
var memory_type_index: u32 = 0;
for (0..self.memory_properties.memoryTypeCount) |index| {
const memory_type = self.memory_properties.memoryTypes[index];
if (((type_bits & (@as(u64, 1) << @intCast(index))) != 0) and (memory_type.propertyFlags & flags) != 0 and (memory_type.propertyFlags & c.VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD) == 0) {
memory_type_index = @intCast(index);
}
}
return memory_type_index;
}
pub fn initBuffer(self: Self, usage: vk.BufferUsage, flags: vk.BufferFlags, size: usize) !vk.Buffer {
const family_indices: []const u32 = &.{self.graphics_family};
const create_info: c.VkBufferCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.sharingMode = c.VK_SHARING_MODE_EXCLUSIVE,
.usage = @bitCast(usage),
.queueFamilyIndexCount = 1,
.pQueueFamilyIndices = family_indices.ptr,
};
var buffer: c.VkBuffer = undefined;
try vk.mapError(c.vkCreateBuffer(self.handle, &create_info, null, &buffer));
var memory_requirements: c.VkMemoryRequirements = undefined;
c.vkGetBufferMemoryRequirements(self.handle, buffer, &memory_requirements);
const alloc_info: c.VkMemoryAllocateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memory_requirements.size,
.memoryTypeIndex = self.pick_memory_type(memory_requirements.memoryTypeBits, @bitCast(flags)),
};
var device_memory: c.VkDeviceMemory = undefined;
try vk.mapError(c.vkAllocateMemory(self.handle, &alloc_info, null, &device_memory));
try vk.mapError(c.vkBindBufferMemory(self.handle, buffer, device_memory, 0));
return .{
.handle = buffer,
.size = size,
.memory = device_memory,
};
}
pub fn submit(self: Self, swapchain: vk.Swapchain, image: usize, frame: usize) !void {
std.debug.assert(frame < frames_in_flight);
const wait_semaphores: [1]c.VkSemaphore = .{self.image_available[frame]};
const signal_semaphores: [1]c.VkSemaphore = .{self.render_finished[frame]};
const swapchains: [1]c.VkSwapchainKHR = .{swapchain.handle};
_ = swapchains;
const stages: []const u32 = &[_]u32{c.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
const submit_info: c.VkSubmitInfo = .{
.sType = c.VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = wait_semaphores[0..].ptr,
.pWaitDstStageMask = stages.ptr,
.commandBufferCount = 1,
.pCommandBuffers = &self.command_buffers[frame],
.signalSemaphoreCount = 1,
.pSignalSemaphores = signal_semaphores[0..].ptr,
};
_ = c.vkResetFences(self.handle, 1, &self.in_flight_fence[frame]);
try vk.mapError(c.vkQueueSubmit(self.graphics_queue, 1, &submit_info, self.in_flight_fence[frame]));
const present_info: c.VkPresentInfoKHR = .{
.sType = c.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = signal_semaphores[0..].ptr,
.swapchainCount = 1,
.pSwapchains = &swapchain.handle,
.pImageIndices = @ptrCast(&image),
.pResults = null,
};
try vk.mapError(c.vkQueuePresentKHR(self.present_queue, &present_info));
}
pub fn initShader(self: Self, comptime name: []const u8) !c.VkShaderModule {
const code = @embedFile(name);
const create_info: c.VkShaderModuleCreateInfo = .{
.sType = c.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = code.len,
.pCode = @ptrCast(@alignCast(code)),
};
var shader_module: c.VkShaderModule = undefined;
try vk.mapError(c.vkCreateShaderModule(self.handle, &create_info, null, @ptrCast(&shader_module)));
return shader_module;
}
pub fn pushConstant(self: Self, pipeline: vk.GraphicsPipeline, stage: u32, offset: u32, size: u32, data: [*c]u8, frame: usize) void {
c.vkCmdPushConstants(self.command_buffers[frame], pipeline.layout, stage, offset, size, data);
}
pub fn deinitShader(self: Self, shader: c.VkShaderModule) void {
c.vkDestroyShaderModule(self.handle, shader, null);
}
pub fn deinit(self: Self) void {
inline for (0..frames_in_flight) |index| {
c.vkDestroySemaphore(self.handle, self.image_available[index], null);
c.vkDestroySemaphore(self.handle, self.render_finished[index], null);
c.vkDestroyFence(self.handle, self.in_flight_fence[index], null);
}
c.vkDestroyCommandPool(self.handle, self.command_pool, null);
c.vkDestroyDevice(self.handle, null);
}