345 lines
13 KiB
Zig
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,
|
|
®ion
|
|
);
|
|
|
|
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);
|
|
}
|