diff --git a/src/gfx.zig b/src/gfx.zig index 30085bb..4770709 100644 --- a/src/gfx.zig +++ b/src/gfx.zig @@ -4,282 +4,282 @@ const builtin = @import("builtin"); const vk = @import("vk"); const c = @import("c.zig"); -pub const use_debug_messenger = switch (builtin.mode) { - .Debug, .ReleaseSafe => true, - .ReleaseSmall, .ReleaseFast => false, -}; +const Base = @import("gfx/Base.zig"); +const Instance = @import("gfx/Instance.zig"); +const Device = @import("gfx/Device.zig"); -const InstancePair = std.meta.Tuple(&.{ vk.Instance, InstanceDispatch, vk.DebugUtilsMessengerEXT }); +// const InstancePair = std.meta.Tuple(&.{ vk.Instance, InstanceDispatch, vk.DebugUtilsMessengerEXT }); -/// note: destroy with vki.destroyInstance(instance, null) -pub fn create_instance(vkb: BaseDispatch, app_name: [*:0]const u8) !InstancePair { - var exts = std.BoundedArray([*:0]const u8, 32){}; - var layers = std.BoundedArray([*:0]const u8, 32){}; +// /// note: destroy with vki.destroyInstance(instance, null) +// pub fn create_instance(vkb: BaseDispatch, app_name: [*:0]const u8) !InstancePair { +// var exts = std.BoundedArray([*:0]const u8, 32){}; +// var layers = std.BoundedArray([*:0]const u8, 32){}; +// +// if (use_debug_messenger) { +// try exts.appendSlice(&.{ +// vk.extension_info.ext_debug_utils.name, +// }); +// +// try layers.appendSlice(&.{ +// "VK_LAYER_KHRONOS_validation", +// }); +// } +// +// var glfw_exts_count: u32 = 0; +// const glfw_exts: [*]const [*:0]const u8 = +// @ptrCast(c.glfwGetRequiredInstanceExtensions(&glfw_exts_count)); +// try exts.appendSlice(glfw_exts[0..glfw_exts_count]); +// +// const dumci: vk.DebugUtilsMessengerCreateInfoEXT = .{ +// .message_severity = .{ +// .error_bit_ext = true, +// .info_bit_ext = true, +// .verbose_bit_ext = true, +// .warning_bit_ext = true, +// }, +// .message_type = .{ +// .device_address_binding_bit_ext = true, +// .general_bit_ext = false, +// .performance_bit_ext = true, +// .validation_bit_ext = true, +// }, +// .pfn_user_callback = &debug_callback, +// .p_user_data = null, +// }; +// +// const instance = try vkb.createInstance(&vk.InstanceCreateInfo{ +// .p_application_info = &vk.ApplicationInfo{ +// .p_application_name = app_name, +// .application_version = vk.makeApiVersion(0, 0, 0, 0), +// .p_engine_name = app_name, +// .engine_version = vk.makeApiVersion(0, 0, 0, 0), +// .api_version = vk.API_VERSION_1_3, +// }, +// .enabled_extension_count = @intCast(exts.len), +// .pp_enabled_extension_names = &exts.buffer, +// .enabled_layer_count = @intCast(layers.len), +// .pp_enabled_layer_names = &layers.buffer, +// .p_next = if (use_debug_messenger) &dumci else null, +// }, null); +// +// const vki = try InstanceDispatch.load(instance, vkb.dispatch.vkGetInstanceProcAddr); +// errdefer vki.destroyInstance(instance, null); +// +// const messenger: vk.DebugUtilsMessengerEXT = if (use_debug_messenger) +// try vki.createDebugUtilsMessengerEXT(instance, &dumci, null) +// else +// .null_handle; +// errdefer if (use_debug_messenger) +// vki.destroyDebugUtilsMessengerEXT(instance, messenger, null); +// +// return .{ instance, vki, messenger }; +// } - if (use_debug_messenger) { - try exts.appendSlice(&.{ - vk.extension_info.ext_debug_utils.name, - }); +// /// note: destroy with vki.destroySurfaceKHR(instance, surface, null) +// pub fn create_surface(instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR { +// var surface: vk.SurfaceKHR = undefined; +// if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) { +// return error.SurfaceInitFailed; +// } +// return surface; +// } - try layers.appendSlice(&.{ - "VK_LAYER_KHRONOS_validation", - }); - } +// /// note: destroy with c.glfwDestroyWindow(window) +// pub fn create_window(extent: vk.Extent2D, title: [*:0]const u8) !*c.GLFWwindow { + // c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, "floating_window"); + // c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, "floating_window"); + // c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API); +// +// return c.glfwCreateWindow( +// @intCast(extent.width), +// @intCast(extent.height), +// title, +// null, +// null, +// ) orelse error.WindowInitFailed; +// } - var glfw_exts_count: u32 = 0; - const glfw_exts: [*]const [*:0]const u8 = - @ptrCast(c.glfwGetRequiredInstanceExtensions(&glfw_exts_count)); - try exts.appendSlice(glfw_exts[0..glfw_exts_count]); - - const dumci: vk.DebugUtilsMessengerCreateInfoEXT = .{ - .message_severity = .{ - .error_bit_ext = true, - .info_bit_ext = true, - .verbose_bit_ext = true, - .warning_bit_ext = true, - }, - .message_type = .{ - .device_address_binding_bit_ext = true, - .general_bit_ext = false, - .performance_bit_ext = true, - .validation_bit_ext = true, - }, - .pfn_user_callback = &debug_callback, - .p_user_data = null, - }; - - const instance = try vkb.createInstance(&vk.InstanceCreateInfo{ - .p_application_info = &vk.ApplicationInfo{ - .p_application_name = app_name, - .application_version = vk.makeApiVersion(0, 0, 0, 0), - .p_engine_name = app_name, - .engine_version = vk.makeApiVersion(0, 0, 0, 0), - .api_version = vk.API_VERSION_1_3, - }, - .enabled_extension_count = @intCast(exts.len), - .pp_enabled_extension_names = &exts.buffer, - .enabled_layer_count = @intCast(layers.len), - .pp_enabled_layer_names = &layers.buffer, - .p_next = if (use_debug_messenger) &dumci else null, - }, null); - const vki = try InstanceDispatch.load(instance, vkb.dispatch.vkGetInstanceProcAddr); - errdefer vki.destroyInstance(instance, null); - - const messenger: vk.DebugUtilsMessengerEXT = if (use_debug_messenger) - try vki.createDebugUtilsMessengerEXT(instance, &dumci, null) - else - .null_handle; - errdefer if (use_debug_messenger) - vki.destroyDebugUtilsMessengerEXT(instance, messenger, null); - - return .{ instance, vki, messenger }; -} - -/// note: destroy with vki.destroySurfaceKHR(instance, surface, null) -pub fn create_surface(instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR { - var surface: vk.SurfaceKHR = undefined; - if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) { - return error.SurfaceInitFailed; - } - return surface; -} - -/// note: destroy with c.glfwDestroyWindow(window) -pub fn create_window(extent: vk.Extent2D, title: [*:0]const u8) !*c.GLFWwindow { - c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, "floating_window"); - c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, "floating_window"); - c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API); - - return c.glfwCreateWindow( - @intCast(extent.width), - @intCast(extent.height), - title, - null, - null, - ) orelse error.WindowInitFailed; -} - -const DevicePair = std.meta.Tuple(&.{ vk.PhysicalDevice, vk.Device, DeviceDispatch, u32 }); - -/// note: destroy with vkd.destroyDevice(dev, null) -pub fn create_device( - ally: std.mem.Allocator, - instance: vk.Instance, - surface: vk.SurfaceKHR, - vki: InstanceDispatch, -) !DevicePair { - const required_device_extensions: []const [*:0]const u8 = &.{ - vk.extension_info.khr_swapchain.name, - vk.extension_info.khr_dynamic_rendering.name, - }; - - var pdev_count: u32 = undefined; - _ = try vki.enumeratePhysicalDevices(instance, &pdev_count, null); - const pdevs = try ally.alloc(vk.PhysicalDevice, pdev_count); - defer ally.free(pdevs); - _ = try vki.enumeratePhysicalDevices(instance, &pdev_count, pdevs.ptr); - - pdev_search: for (pdevs) |pdev| { - const props = vki.getPhysicalDeviceProperties(pdev); - if (props.device_type != .discrete_gpu) continue :pdev_search; - - var format_count: u32 = undefined; - _ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, null); - if (format_count == 0) continue :pdev_search; - - var mode_count: u32 = undefined; - _ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &mode_count, null); - if (mode_count == 0) continue :pdev_search; - - var ext_count: u32 = undefined; - _ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null); - const exts = try ally.alloc(vk.ExtensionProperties, ext_count); - defer ally.free(exts); - _ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr); - - for (required_device_extensions) |name| { - for (exts) |ext| { - if (std.mem.eql( - u8, - std.mem.span(name), - std.mem.sliceTo(&ext.extension_name, 0), - )) { - break; - } - } else { - continue :pdev_search; - } - } - - var family_count: u32 = undefined; - vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null); - const families = try ally.alloc(vk.QueueFamilyProperties, family_count); - defer ally.free(families); - vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr); - - // just find one family that does graphics and present, so we can use exclusive sharing - // on the swapchain. apparently most hardware supports this. logic for queue allocation - // and swapchain creation is so much simpler this way. swapchain creation needs to know - // the list of queue family indices which will have access to the images, and there's a - // performance penalty to allow concurrent access to multiple queue families. - // - // multiple _queues_ may have exclusive access, but only if they're in the smae family. - - const graphics_family: u32 = for (families, 0..) |family, idx| { - const graphics = family.queue_flags.graphics_bit; - const present = try vki.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), surface) == vk.TRUE; - if (graphics and present) { - break @intCast(idx); - } - } else { - continue :pdev_search; - }; - - std.log.debug("selecting device {s}", .{std.mem.sliceTo(&props.device_name, 0)}); - - const qci: []const vk.DeviceQueueCreateInfo = &.{ - vk.DeviceQueueCreateInfo{ - .queue_family_index = graphics_family, - .queue_count = 1, - .p_queue_priorities = &[_]f32{1.0}, - }, - }; - - const dev = try vki.createDevice(pdev, &.{ - .queue_create_info_count = @intCast(qci.len), - .p_queue_create_infos = qci.ptr, - .enabled_extension_count = @intCast(required_device_extensions.len), - .pp_enabled_extension_names = required_device_extensions.ptr, - .p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{ - .dynamic_rendering = vk.TRUE, - }, - }, null); - const vkd = try DeviceDispatch.load(dev, vki.dispatch.vkGetDeviceProcAddr); - errdefer vkd.destroyDevice(dev, null); - - return .{ pdev, dev, vkd, graphics_family }; - } - - return error.NoSuitableDevice; -} - -pub fn find_surface_format( - pdev: vk.PhysicalDevice, - vki: InstanceDispatch, - surface: vk.SurfaceKHR, - preferred: vk.SurfaceFormatKHR, -) !vk.SurfaceFormatKHR { - var formats_buf: [64]vk.SurfaceFormatKHR = undefined; - var formats_count: u32 = @intCast(formats_buf.len); - _ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &formats_count, &formats_buf); - const formats = formats_buf[0..formats_count]; - - for (formats) |format| { - if (std.meta.eql(format, preferred)) { - return format; - } - } - - return formats[0]; -} - -pub fn find_present_mode( - pdev: vk.PhysicalDevice, - vki: InstanceDispatch, - surface: vk.SurfaceKHR, - preferred: vk.PresentModeKHR, -) !vk.PresentModeKHR { - var modes_buf: [8]vk.PresentModeKHR = undefined; - var modes_count: u32 = @intCast(modes_buf.len); - _ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &modes_count, &modes_buf); - const modes = modes_buf[0..modes_count]; - - for (modes) |mode| { - if (std.meta.eql(mode, preferred)) { - return mode; - } - } - - return .mailbox_khr; -} - -pub fn find_swap_extent( - pdev: vk.PhysicalDevice, - vki: InstanceDispatch, - surface: vk.SurfaceKHR, - window: *c.GLFWwindow, -) !vk.Extent2D { - const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface); - var extent = caps.current_extent; - - if (extent.width == std.math.maxInt(u32)) { - c.glfwGetFramebufferSize(window, @ptrCast(&extent.width), @ptrCast(&extent.height)); - extent.width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width); - extent.height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height); - } - - return extent; -} - -pub fn find_swap_image_count( - pdev: vk.PhysicalDevice, - vki: InstanceDispatch, - surface: vk.SurfaceKHR, -) !u32 { - const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface); - var count = @max(3, caps.min_image_count + 1); - if (caps.max_image_count > 0) { - count = @min(count, caps.max_image_count); - } - return count; -} +// const DevicePair = std.meta.Tuple(&.{ vk.PhysicalDevice, vk.Device, Device.Wrapper, u32 }); +// +// /// note: destroy with vkd.destroyDevice(dev, null) +// pub fn cr eate_device( +// ally: std.mem.Allocator, +// instance: vk.Instance, +// surface: vk.SurfaceKHR, +// vki: InstanceDispatch, +// ) !DevicePair { +// const required_device_extensions: []const [*:0]const u8 = &.{ +// vk.extension_info.khr_swapchain.name, +// vk.extension_info.khr_dynamic_rendering.name, +// }; +// +// var pdev_count: u32 = undefined; +// _ = try vki.enumeratePhysicalDevices(instance, &pdev_count, null); +// const pdevs = try ally.alloc(vk.PhysicalDevice, pdev_count); +// defer ally.free(pdevs); +// _ = try vki.enumeratePhysicalDevices(instance, &pdev_count, pdevs.ptr); +// +// pdev_search: for (pdevs) |pdev| { +// const props = vki.getPhysicalDeviceProperties(pdev); +// if (props.device_type != .discrete_gpu) continue :pdev_search; +// +// var format_count: u32 = undefined; +// _ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, null); +// if (format_count == 0) continue :pdev_search; +// +// var mode_count: u32 = undefined; +// _ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &mode_count, null); +// if (mode_count == 0) continue :pdev_search; +// +// var ext_count: u32 = undefined; +// _ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null); +// const exts = try ally.alloc(vk.ExtensionProperties, ext_count); +// defer ally.free(exts); +// _ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr); +// +// for (required_device_extensions) |name| { +// for (exts) |ext| { +// if (std.mem.eql( +// u8, +// std.mem.span(name), +// std.mem.sliceTo(&ext.extension_name, 0), +// )) { +// break; +// } +// } else { +// continue :pdev_search; +// } +// } +// +// var family_count: u32 = undefined; +// vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null); +// const families = try ally.alloc(vk.QueueFamilyProperties, family_count); +// defer ally.free(families); +// vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr); +// +// // just find one family that does graphics and present, so we can use exclusive sharing +// // on the swapchain. apparently most hardware supports this. logic for queue allocation +// // and swapchain creation is so much simpler this way. swapchain creation needs to know +// // the list of queue family indices which will have access to the images, and there's a +// // performance penalty to allow concurrent access to multiple queue families. +// // +// // multiple _queues_ may have exclusive access, but only if they're in the smae family. +// +// const graphics_family: u32 = for (families, 0..) |family, idx| { +// const graphics = family.queue_flags.graphics_bit; +// const present = try vki.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), surface) == vk.TRUE; +// if (graphics and present) { +// break @intCast(idx); +// } +// } else { +// continue :pdev_search; +// }; +// +// std.log.debug("selecting device {s}", .{std.mem.sliceTo(&props.device_name, 0)}); +// +// const qci: []const vk.DeviceQueueCreateInfo = &.{ +// vk.DeviceQueueCreateInfo{ +// .queue_family_index = graphics_family, +// .queue_count = 1, +// .p_queue_priorities = &[_]f32{1.0}, +// }, +// }; +// +// const dev = try vki.createDevice(pdev, &.{ +// .queue_create_info_count = @intCast(qci.len), +// .p_queue_create_infos = qci.ptr, +// .enabled_extension_count = @intCast(required_device_extensions.len), +// .pp_enabled_extension_names = required_device_extensions.ptr, +// .p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{ +// .dynamic_rendering = vk.TRUE, +// }, +// }, null); +// const vkd = try Device.Wrapper.load(dev, vki.dispatch.vkGetDeviceProcAddr); +// errdefer vkd.destroyDevice(dev, null); +// +// return .{ pdev, dev, vkd, graphics_family }; +// } +// +// return error.NoSuitableDevice; +// } +// +// pub fn find_surface_format( +// pdev: vk.PhysicalDevice, +// vki: InstanceDispatch, +// surface: vk.SurfaceKHR, +// preferred: vk.SurfaceFormatKHR, +// ) !vk.SurfaceFormatKHR { +// var formats_buf: [64]vk.SurfaceFormatKHR = undefined; +// var formats_count: u32 = @intCast(formats_buf.len); +// _ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &formats_count, &formats_buf); +// const formats = formats_buf[0..formats_count]; +// +// for (formats) |format| { +// if (std.meta.eql(format, preferred)) { +// return format; +// } +// } +// +// return formats[0]; +// } +// +// pub fn find_present_mode( +// pdev: vk.PhysicalDevice, +// vki: InstanceDispatch, +// surface: vk.SurfaceKHR, +// preferred: vk.PresentModeKHR, +// ) !vk.PresentModeKHR { +// var modes_buf: [8]vk.PresentModeKHR = undefined; +// var modes_count: u32 = @intCast(modes_buf.len); +// _ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &modes_count, &modes_buf); +// const modes = modes_buf[0..modes_count]; +// +// for (modes) |mode| { +// if (std.meta.eql(mode, preferred)) { +// return mode; +// } +// } +// +// return .mailbox_khr; +// } +// +// pub fn find_swap_extent( +// pdev: vk.PhysicalDevice, +// vki: InstanceDispatch, +// surface: vk.SurfaceKHR, +// window: *c.GLFWwindow, +// ) !vk.Extent2D { +// const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface); +// var extent = caps.current_extent; +// +// if (extent.width == std.math.maxInt(u32)) { +// c.glfwGetFramebufferSize(window, @ptrCast(&extent.width), @ptrCast(&extent.height)); +// extent.width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width); +// extent.height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height); +// } +// +// return extent; +// } +// +// pub fn find_swap_image_count( +// pdev: vk.PhysicalDevice, +// vki: InstanceDispatch, +// surface: vk.SurfaceKHR, +// ) !u32 { +// const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface); +// var count = @max(3, caps.min_image_count + 1); +// if (caps.max_image_count > 0) { +// count = @min(count, caps.max_image_count); +// } +// return count; +// } pub fn uploadData( comptime T: type, pdev: vk.PhysicalDevice, - vki: InstanceDispatch, + vki: Instance.Wrapper, dev: vk.Device, - vkd: DeviceDispatch, + vkd: Device.Wrapper, queue: vk.Queue, pool: vk.CommandPool, buffer: vk.Buffer, @@ -326,7 +326,7 @@ pub fn copyBuffer( dst: vk.Buffer, src: vk.Buffer, size: vk.DeviceSize, - vkd: DeviceDispatch, + vkd: Device.Wrapper, ) !void { var cmdbuf: vk.CommandBuffer = undefined; try vkd.allocateCommandBuffers(dev, &.{ @@ -371,7 +371,7 @@ pub const VkAllocator = struct { pub fn init( pdev: vk.PhysicalDevice, - vki: InstanceDispatch, + vki: Instance.Wrapper, ) VkAllocator { const props = vki.getPhysicalDeviceMemoryProperties(pdev); @@ -384,7 +384,7 @@ pub const VkAllocator = struct { pub fn alloc( self: VkAllocator, dev: vk.Device, - vkd: DeviceDispatch, + vkd: Device.Wrapper, reqs: vk.MemoryRequirements, flags: vk.MemoryPropertyFlags, ) !vk.DeviceMemory { @@ -403,82 +403,82 @@ pub const VkAllocator = struct { } }; -pub const BaseDispatch = vk.BaseWrapper(.{ - .createInstance = true, - .getInstanceProcAddr = true, -}); +// pub const BaseDispatch = vk.BaseWrapper(.{ +// .createInstance = true, +// .getInstanceProcAddr = true, +// }); -pub const InstanceDispatch = vk.InstanceWrapper(.{ - .destroyInstance = true, - .createDevice = true, - .destroySurfaceKHR = true, - .enumeratePhysicalDevices = true, - .getPhysicalDeviceProperties = true, - .enumerateDeviceExtensionProperties = true, - .getPhysicalDeviceSurfaceFormatsKHR = true, - .getPhysicalDeviceSurfacePresentModesKHR = true, - .getPhysicalDeviceSurfaceCapabilitiesKHR = true, - .getPhysicalDeviceQueueFamilyProperties = true, - .getPhysicalDeviceSurfaceSupportKHR = true, - .getPhysicalDeviceMemoryProperties = true, - .getDeviceProcAddr = true, - .createDebugUtilsMessengerEXT = use_debug_messenger, - .destroyDebugUtilsMessengerEXT = use_debug_messenger, -}); +// pub const Instance.Wrapper = vk.InstanceWrapper(.{ +// .destroyInstance = true, +// .createDevice = true, +// .destroySurfaceKHR = true, +// .enumeratePhysicalDevices = true, +// .getPhysicalDeviceProperties = true, +// .enumerateDeviceExtensionProperties = true, +// .getPhysicalDeviceSurfaceFormatsKHR = true, +// .getPhysicalDeviceSurfacePresentModesKHR = true, +// .getPhysicalDeviceSurfaceCapabilitiesKHR = true, +// .getPhysicalDeviceQueueFamilyProperties = true, +// .getPhysicalDeviceSurfaceSupportKHR = true, +// .getPhysicalDeviceMemoryProperties = true, +// .getDeviceProcAddr = true, +// .createDebugUtilsMessengerEXT = use_debug_messenger, +// .destroyDebugUtilsMessengerEXT = use_debug_messenger, +// }); -pub const DeviceDispatch = vk.DeviceWrapper(.{ - .destroyDevice = true, - .getDeviceQueue = true, - .createSemaphore = true, - .createFence = true, - .createImageView = true, - .destroyImageView = true, - .destroySemaphore = true, - .destroyFence = true, - .getSwapchainImagesKHR = true, - .createSwapchainKHR = true, - .destroySwapchainKHR = true, - .acquireNextImageKHR = true, - .deviceWaitIdle = true, - .waitForFences = true, - .resetFences = true, - .queueSubmit = true, - .queuePresentKHR = true, - .createCommandPool = true, - .destroyCommandPool = true, - .allocateCommandBuffers = true, - .freeCommandBuffers = true, - .queueWaitIdle = true, - .createShaderModule = true, - .destroyShaderModule = true, - .createPipelineLayout = true, - .destroyPipelineLayout = true, - .createGraphicsPipelines = true, - .destroyPipeline = true, - .beginCommandBuffer = true, - .endCommandBuffer = true, - .allocateMemory = true, - .freeMemory = true, - .createBuffer = true, - .destroyBuffer = true, - .getBufferMemoryRequirements = true, - .mapMemory = true, - .unmapMemory = true, - .bindBufferMemory = true, - .cmdBeginRenderPass = true, - .cmdEndRenderPass = true, - .cmdBindPipeline = true, - .cmdDraw = true, - .cmdDrawIndexed = true, - .cmdSetViewport = true, - .cmdSetScissor = true, - .cmdBindVertexBuffers = true, - .cmdBindIndexBuffer = true, - .cmdCopyBuffer = true, - .cmdBeginRenderingKHR = true, - .cmdEndRenderingKHR = true, - .cmdPipelineBarrier = true, -}); +// pub const Device.Wrapper = vk.DeviceWrapper(.{ +// .destroyDevice = true, +// .getDeviceQueue = true, +// .createSemaphore = true, +// .createFence = true, +// .createImageView = true, +// .destroyImageView = true, +// .destroySemaphore = true, +// .destroyFence = true, +// .getSwapchainImagesKHR = true, +// .createSwapchainKHR = true, +// .destroySwapchainKHR = true, +// .acquireNextImageKHR = true, +// .deviceWaitIdle = true, +// .waitForFences = true, +// .resetFences = true, +// .queueSubmit = true, +// .queuePresentKHR = true, +// .createCommandPool = true, +// .destroyCommandPool = true, +// .allocateCommandBuffers = true, +// .freeCommandBuffers = true, +// .queueWaitIdle = true, +// .createShaderModule = true, +// .destroyShaderModule = true, +// .createPipelineLayout = true, +// .destroyPipelineLayout = true, +// .createGraphicsPipelines = true, +// .destroyPipeline = true, +// .beginCommandBuffer = true, +// .endCommandBuffer = true, +// .allocateMemory = true, +// .freeMemory = true, +// .createBuffer = true, +// .destroyBuffer = true, +// .getBufferMemoryRequirements = true, +// .mapMemory = true, +// .unmapMemory = true, +// .bindBufferMemory = true, +// .cmdBeginRenderPass = true, +// .cmdEndRenderPass = true, +// .cmdBindPipeline = true, +// .cmdDraw = true, +// .cmdDrawIndexed = true, +// .cmdSetViewport = true, +// .cmdSetScissor = true, +// .cmdBindVertexBuffers = true, +// .cmdBindIndexBuffer = true, +// .cmdCopyBuffer = true, +// .cmdBeginRenderingKHR = true, +// .cmdEndRenderingKHR = true, +// .cmdPipelineBarrier = true, +// }); pub fn debug_callback( msg_severity: vk.DebugUtilsMessageSeverityFlagsEXT, diff --git a/src/gfx/Base.zig b/src/gfx/Base.zig new file mode 100644 index 0000000..a348067 --- /dev/null +++ b/src/gfx/Base.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const vk = @import("vk"); +const c = @import("../c.zig"); + +const Self = @This(); + +vkb: Wrapper, + +pub fn init() !Self { + if (c.glfwInit() != c.GLFW_TRUE) + return error.GLFWInitFailed; + errdefer c.glfwTerminate(); + + if (c.glfwVulkanSupported() != c.GLFW_TRUE) { + return error.GLFWNoVulkan; + } + + return .{ + .vkb = try Wrapper.load(c.glfwGetInstanceProcAddress), + }; +} + +pub fn deinit(_: Self) void { + c.glfwTerminate(); +} + +pub const Wrapper = vk.BaseWrapper(.{ + .createInstance = true, + .getInstanceProcAddr = true, +}); diff --git a/src/gfx/Context.zig b/src/gfx/Context.zig new file mode 100644 index 0000000..646d492 --- /dev/null +++ b/src/gfx/Context.zig @@ -0,0 +1,5 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const vk = @import("vk"); +const c = @import("../c.zig"); +const gfx = @import("../c.zig"); diff --git a/src/gfx/Device.zig b/src/gfx/Device.zig new file mode 100644 index 0000000..56d79f9 --- /dev/null +++ b/src/gfx/Device.zig @@ -0,0 +1,231 @@ +//! The point here is to select _a_ physical device and create a logical device around it. + +const std = @import("std"); +const builtin = @import("builtin"); + +const vk = @import("vk"); +const c = @import("../c.zig"); + +const Instance = @import("Instance.zig"); +const Window = @import("Window.zig"); + +const Self = @This(); + +const required_extensions: []const [*:0]const u8 = &.{ + vk.extension_info.khr_swapchain.name, + vk.extension_info.khr_dynamic_rendering.name, +}; + +const preferred_surface_format: vk.SurfaceFormatKHR = .{ + .color_space = .srgb_nonlinear_khr, + .format = .r8g8b8a8_sint, +}; + +const preferred_present_mode: vk.PresentModeKHR = .mailbox_khr; + +dev: vk.Device, +pdev: vk.PhysicalDevice, +vkd: Wrapper, + +inst: *const Instance, +win: *const Window, + +format: vk.SurfaceFormatKHR, +mode: vk.PresentModeKHR, +family: u32, +queue: vk.Queue, + +pool: vk.CommandPool, + +pub fn init( + ally: std.mem.Allocator, + inst: *const Instance, + win: *const Window, +) !Self { + var pdev_count: u32 = undefined; + _ = try inst.vki.enumeratePhysicalDevices(inst.ref, &pdev_count, null); + const pdevs = try ally.alloc(vk.PhysicalDevice, pdev_count); + defer ally.free(pdevs); + _ = try inst.vki.enumeratePhysicalDevices(inst.ref, &pdev_count, pdevs.ptr); + + for (pdevs) |pdev| { + return wrap(ally, inst, win, pdev) catch continue; + } + + return error.NoSuitableDevice; +} + +pub fn wrap( + ally: std.mem.Allocator, + inst: *const Instance, + win: *const Window, + pdev: vk.PhysicalDevice, +) !Self { + const props = inst.vki.getPhysicalDeviceProperties(pdev); + if (props.device_type != .discrete_gpu) return error.NotDiscrete; + + var format_count: u32 = undefined; + _ = try inst.vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, win.surface, &format_count, null); + if (format_count == 0) return error.NoSurfaceFormat; + const formats = try ally.alloc(vk.SurfaceFormatKHR, format_count); + defer ally.free(formats); + _ = try inst.vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, win.surface, &format_count, formats.ptr); + + const format = for (formats) |f| { + if (std.meta.eql(f, preferred_surface_format)) + break f; + } else formats[0]; + + var mode_count: u32 = undefined; + _ = try inst.vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, win.surface, &mode_count, null); + if (mode_count == 0) return error.NoSurfaceMode; + const modes = try ally.alloc(vk.PresentModeKHR, mode_count); + defer ally.free(modes); + _ = try inst.vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, win.surface, &mode_count, modes.ptr); + + const mode = for (modes) |m| { + if (std.meta.eql(m, preferred_present_mode)) + break m; + } else modes[0]; + + var ext_count: u32 = undefined; + _ = try inst.vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null); + const exts = try ally.alloc(vk.ExtensionProperties, ext_count); + defer ally.free(exts); + _ = try inst.vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr); + + for (required_extensions) |name| { + for (exts) |ext| { + if (std.mem.eql( + u8, + std.mem.span(name), + std.mem.sliceTo(&ext.extension_name, 0), + )) { + break; + } + } else { + return error.MissingRequiredExtension; + } + } + + var family_count: u32 = undefined; + inst.vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null); + const families = try ally.alloc(vk.QueueFamilyProperties, family_count); + defer ally.free(families); + inst.vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr); + + // just find one family that does graphics and present, so we can use exclusive sharing + // on the swapchain. apparently most hardware supports this. logic for queue allocation + // and swapchain creation is so much simpler this way. swapchain creation needs to know + // the list of queue family indices which will have access to the images, and there's a + // performance penalty to allow concurrent access to multiple queue families. + // + // multiple _queues_ may have exclusive access, but only if they're in the smae family. + + const family: u32 = for (families, 0..) |family, idx| { + const graphics = family.queue_flags.graphics_bit; + const present = try inst.vki.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), win.surface) == vk.TRUE; + if (graphics and present) break @intCast(idx); + } else { + return error.NoSuitableQueueFamily; + }; + + const qci: []const vk.DeviceQueueCreateInfo = &.{ + vk.DeviceQueueCreateInfo{ + .queue_family_index = family, + .queue_count = 1, + .p_queue_priorities = &[_]f32{1.0}, + }, + }; + + const dev = try inst.vki.createDevice(pdev, &.{ + .queue_create_info_count = @intCast(qci.len), + .p_queue_create_infos = qci.ptr, + .enabled_extension_count = @intCast(required_extensions.len), + .pp_enabled_extension_names = required_extensions.ptr, + .p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{ + .dynamic_rendering = vk.TRUE, + }, + }, null); + const vkd = try Wrapper.load(dev, inst.vki.dispatch.vkGetDeviceProcAddr); + errdefer vkd.destroyDevice(dev, null); + + const pool = try vkd.createCommandPool(dev, &.{ + .queue_family_index = family, + }, null); + errdefer vkd.destroyCommandPool(dev, pool, null); + + const queue = vkd.getDeviceQueue(dev, family, 0); + + return .{ + .dev = dev, + .pdev = pdev, + .vkd = vkd, + .inst = inst, + .win = win, + .format = format, + .mode = mode, + .pool = pool, + .family = family, + .queue = queue, + }; +} + +pub fn deinit(self: Self) void { + self.vkd.destroyCommandPool(self.dev, self.pool, null); + self.vkd.destroyDevice(self.dev, null); +} + +pub const Wrapper = vk.DeviceWrapper(.{ + .destroyDevice = true, + .getDeviceQueue = true, + .createSemaphore = true, + .createFence = true, + .createImageView = true, + .destroyImageView = true, + .destroySemaphore = true, + .destroyFence = true, + .getSwapchainImagesKHR = true, + .createSwapchainKHR = true, + .destroySwapchainKHR = true, + .acquireNextImageKHR = true, + .deviceWaitIdle = true, + .waitForFences = true, + .resetFences = true, + .queueSubmit = true, + .queuePresentKHR = true, + .createCommandPool = true, + .destroyCommandPool = true, + .allocateCommandBuffers = true, + .freeCommandBuffers = true, + .queueWaitIdle = true, + .createShaderModule = true, + .destroyShaderModule = true, + .createPipelineLayout = true, + .destroyPipelineLayout = true, + .createGraphicsPipelines = true, + .destroyPipeline = true, + .beginCommandBuffer = true, + .endCommandBuffer = true, + .allocateMemory = true, + .freeMemory = true, + .createBuffer = true, + .destroyBuffer = true, + .getBufferMemoryRequirements = true, + .mapMemory = true, + .unmapMemory = true, + .bindBufferMemory = true, + .cmdBeginRenderPass = true, + .cmdEndRenderPass = true, + .cmdBindPipeline = true, + .cmdDraw = true, + .cmdDrawIndexed = true, + .cmdSetViewport = true, + .cmdSetScissor = true, + .cmdBindVertexBuffers = true, + .cmdBindIndexBuffer = true, + .cmdCopyBuffer = true, + .cmdBeginRenderingKHR = true, + .cmdEndRenderingKHR = true, + .cmdPipelineBarrier = true, +}); diff --git a/src/gfx/Instance.zig b/src/gfx/Instance.zig new file mode 100644 index 0000000..c3f553b --- /dev/null +++ b/src/gfx/Instance.zig @@ -0,0 +1,165 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const vk = @import("vk"); +const c = @import("../c.zig"); + +const Base = @import("Base.zig"); + +const Self = @This(); + +const app_info: vk.ApplicationInfo = .{ + .p_application_name = "zig-glfw-vulkan", + .application_version = vk.makeApiVersion(0, 0, 0, 0), + .p_engine_name = "zig-glfw-vulkan", + .engine_version = vk.makeApiVersion(0, 0, 0, 0), + .api_version = vk.API_VERSION_1_3, +}; + +pub const use_debug_messenger = switch (builtin.mode) { + .Debug, .ReleaseSafe => true, + .ReleaseSmall, .ReleaseFast => false, +}; + +ref: vk.Instance, +vki: Wrapper, +base: *const Base, + +messenger: if (use_debug_messenger) vk.DebugUtilsMessengerEXT else void, + +pub fn init( + base: *const Base, +) !Self { + var exts: std.BoundedArray([*:0]const u8, 32) = .{}; + var layers: std.BoundedArray([*:0]const u8, 32) = .{}; + + if (use_debug_messenger) { + try exts.appendSlice(&.{ + vk.extension_info.ext_debug_utils.name, + }); + + try layers.appendSlice(&.{ + "VK_LAYER_KHRONOS_validation", + }); + } + + var glfw_exts_count: u32 = 0; + const glfw_exts: [*]const [*:0]const u8 = + @ptrCast(c.glfwGetRequiredInstanceExtensions(&glfw_exts_count)); + try exts.appendSlice(glfw_exts[0..glfw_exts_count]); + + const mci: vk.DebugUtilsMessengerCreateInfoEXT = .{ + .message_severity = .{ + .error_bit_ext = true, + .info_bit_ext = true, + .verbose_bit_ext = true, + .warning_bit_ext = true, + }, + .message_type = .{ + .device_address_binding_bit_ext = true, + .general_bit_ext = false, + .performance_bit_ext = true, + .validation_bit_ext = true, + }, + .pfn_user_callback = &debug_callback, + .p_user_data = null, + }; + + const ref = try base.vkb.createInstance(&.{ + .p_application_info = &app_info, + .enabled_extension_count = @intCast(exts.len), + .pp_enabled_extension_names = &exts.buffer, + .enabled_layer_count = @intCast(layers.len), + .pp_enabled_layer_names = &layers.buffer, + .p_next = if (use_debug_messenger) &mci else null, + }, null); + + const vki = try Wrapper.load(ref, base.vkb.dispatch.vkGetInstanceProcAddr); + errdefer vki.destroyInstance(ref, null); + + const messenger = if (use_debug_messenger) + try vki.createDebugUtilsMessengerEXT(ref, &mci, null) + else + void{}; + + errdefer if (use_debug_messenger) + vki.destroyDebugUtilsMessengerEXT(ref, messenger, null); + + return .{ + .ref = ref, + .vki = vki, + .base = base, + .messenger = messenger, + }; +} + +pub fn deinit(self: Self) void { + if (use_debug_messenger) + self.vki.destroyDebugUtilsMessengerEXT(self.ref, self.messenger, null); + self.vki.destroyInstance(self.ref, null); +} + +pub const Wrapper = vk.InstanceWrapper(.{ + .destroyInstance = true, + .createDevice = true, + .destroySurfaceKHR = true, + .enumeratePhysicalDevices = true, + .getPhysicalDeviceProperties = true, + .enumerateDeviceExtensionProperties = true, + .getPhysicalDeviceSurfaceFormatsKHR = true, + .getPhysicalDeviceSurfacePresentModesKHR = true, + .getPhysicalDeviceSurfaceCapabilitiesKHR = true, + .getPhysicalDeviceQueueFamilyProperties = true, + .getPhysicalDeviceSurfaceSupportKHR = true, + .getPhysicalDeviceMemoryProperties = true, + .getDeviceProcAddr = true, + .createDebugUtilsMessengerEXT = use_debug_messenger, + .destroyDebugUtilsMessengerEXT = use_debug_messenger, +}); + +pub fn debug_callback( + msg_severity: vk.DebugUtilsMessageSeverityFlagsEXT, + msg_type: vk.DebugUtilsMessageTypeFlagsEXT, + p_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT, + _: ?*anyopaque, +) callconv(vk.vulkan_call_conv) vk.Bool32 { + // ripped from std.log.defaultLog + + const data = p_data orelse return vk.FALSE; + const message = data.p_message orelse return vk.FALSE; + + const severity_prefix = if (msg_severity.verbose_bit_ext) + "verbose:" + else if (msg_severity.info_bit_ext) + "info:" + else if (msg_severity.warning_bit_ext) + "warning:" + else if (msg_severity.error_bit_ext) + "error:" + else + "?:"; + + const type_prefix = if (msg_type.general_bit_ext) + "" + else if (msg_type.validation_bit_ext) + "validation:" + else if (msg_type.performance_bit_ext) + "performance:" + else if (msg_type.device_address_binding_bit_ext) + "device_address_binding:" + else + "?:"; + + const stderr = std.io.getStdErr().writer(); + var bw = std.io.bufferedWriter(stderr); + const writer = bw.writer(); + + std.debug.getStderrMutex().lock(); + defer std.debug.getStderrMutex().unlock(); + nosuspend { + writer.print("vk-{s}{s} {s}\n", .{ severity_prefix, type_prefix, message }) catch return vk.FALSE; + bw.flush() catch return vk.FALSE; + } + + return vk.FALSE; +} diff --git a/src/gfx/Swapchain.zig b/src/gfx/Swapchain.zig new file mode 100644 index 0000000..64ea09f --- /dev/null +++ b/src/gfx/Swapchain.zig @@ -0,0 +1,169 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const vk = @import("vk"); +const c = @import("../c.zig"); + +const Instance = @import("Instance.zig"); +const Window = @import("Window.zig"); +const Device = @import("Device.zig"); + +const Self = @This(); + +pub const ChainImage = struct { + image: vk.Image = .null_handle, + view: vk.ImageView = .null_handle, + cmdbuf: vk.CommandBuffer = .null_handle, + fence: vk.Fence = .null_handle, + image_available: vk.Semaphore = .null_handle, + render_finished: vk.Semaphore = .null_handle, +}; + +ally: std.mem.Allocator, + +ref: vk.SwapchainKHR, +dev: *const Device, + +extent: vk.Extent2D, +min_image_count: u32, +chain: std.MultiArrayList(ChainImage), + +pub fn create(ally: std.mem.Allocator, dev: *const Device) !Self { + const caps = try dev.inst.vki.getPhysicalDeviceSurfaceCapabilitiesKHR(dev.pdev, dev.win.surface); + var min_image_count = @max(3, caps.min_image_count + 1); + if (caps.max_image_count > 0) { + min_image_count = @min(min_image_count, caps.max_image_count); + } + + return .{ + .ally = ally, + .ref = .null_handle, + .dev = dev, + .extent = undefined, + .min_image_count = min_image_count, + .chain = .{}, + }; +} + +pub fn init(self: *Self) !void { + const caps = try self.dev.inst.vki.getPhysicalDeviceSurfaceCapabilitiesKHR(self.dev.pdev, self.dev.win.surface); + + self.extent = caps.current_extent; + if (caps.current_extent.width == std.math.maxInt(u32)) { + c.glfwGetFramebufferSize(self.dev.win.ref, @ptrCast(&self.extent.width), @ptrCast(&self.extent.height)); + } + self.extent.width = std.math.clamp(self.extent.width, caps.min_image_extent.width, caps.max_image_extent.width); + self.extent.height = std.math.clamp(self.extent.height, caps.min_image_extent.height, caps.max_image_extent.height); + + const prev = self.ref; + self.ref = try self.dev.vkd.createSwapchainKHR(self.dev.dev, &.{ + .surface = self.dev.win.surface, + .min_image_count = self.min_image_count, + .image_format = self.dev.format.format, + .image_color_space = self.dev.format.color_space, + .image_extent = self.extent, + .image_array_layers = 1, + .image_usage = .{ .color_attachment_bit = true }, + .image_sharing_mode = .exclusive, + .pre_transform = .{ .identity_bit_khr = true }, + .composite_alpha = .{ .opaque_bit_khr = true }, + .present_mode = self.dev.mode, + .clipped = vk.TRUE, + .old_swapchain = prev, + }, null); + self.dev.vkd.destroySwapchainKHR(self.dev.dev, prev, null); + + var image_count: u32 = undefined; + _ = try self.dev.vkd.getSwapchainImagesKHR(self.dev.dev, self.ref, &image_count, null); + + // todo try to reuse contents if possible. + // not even sure at this point which parts can be safely reused. the trick to fix the tearing while resizing + // on laptop is probably in doing this correctly, to present any remaining images that can be presented. + + self.deinit_chain(); + + try self.chain.resize(self.ally, image_count); + _ = try self.dev.vkd.getSwapchainImagesKHR( + self.dev.dev, + self.ref, + &image_count, + self.chain.items(.image).ptr, + ); + + try self.init_chain(); +} + +// requires self.chain.len and self.chain.items(.image) be populated by getSwapchainImagesKHR +fn init_chain(self: *Self) !void { + @memset(self.chain.items(.view), .null_handle); + @memset(self.chain.items(.cmdbuf), .null_handle); + @memset(self.chain.items(.fence), .null_handle); + @memset(self.chain.items(.image_available), .null_handle); + @memset(self.chain.items(.render_finished), .null_handle); + errdefer self.deinit_chain(); + + for (self.chain.items(.image), self.chain.items(.view)) |image, *view| { + view.* = try self.dev.vkd.createImageView(self.dev.dev, &.{ + .image = image, + .view_type = .@"2d", + .format = self.dev.format.format, + .components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity }, + .subresource_range = .{ + .aspect_mask = .{ .color_bit = true }, + .base_mip_level = 0, + .level_count = 1, + .base_array_layer = 0, + .layer_count = 1, + }, + }, null); + } + + for (self.chain.items(.fence)) |*fence| { + fence.* = try self.dev.vkd.createFence(self.dev.dev, &.{ + .flags = .{ .signaled_bit = true }, + }, null); + } + + for (self.chain.items(.image_available)) |*sem| { + sem.* = try self.dev.vkd.createSemaphore(self.dev.dev, &.{}, null); + } + + for (self.chain.items(.render_finished)) |*sem| { + sem.* = try self.dev.vkd.createSemaphore(self.dev.dev, &.{}, null); + } + + try self.dev.vkd.allocateCommandBuffers(self.dev.dev, &.{ + .command_pool = self.dev.pool, + .command_buffer_count = @intCast(self.chain.len), + .level = .primary, + }, self.chain.items(.cmdbuf).ptr); +} + +fn deinit_chain(self: Self) void { + for (self.chain.items(.view)) |view| { + self.dev.vkd.destroyImageView(self.dev.dev, view, null); + } + for (self.chain.items(.fence)) |fence| { + self.dev.vkd.destroyFence(self.dev.dev, fence, null); + } + for (self.chain.items(.image_available)) |sem| { + self.dev.vkd.destroySemaphore(self.dev.dev, sem, null); + } + for (self.chain.items(.render_finished)) |sem| { + self.dev.vkd.destroySemaphore(self.dev.dev, sem, null); + } + if (self.chain.len > 0) { + self.dev.vkd.freeCommandBuffers( + self.dev.dev, + self.dev.pool, + @intCast(self.chain.len), + self.chain.items(.cmdbuf).ptr, + ); + } +} + +pub fn deinit(self: *Self) void { + self.deinit_chain(); + self.chain.deinit(self.ally); + self.dev.vkd.destroySwapchainKHR(self.dev.dev, self.ref, null); +} diff --git a/src/gfx/Window.zig b/src/gfx/Window.zig new file mode 100644 index 0000000..d2c2c2b --- /dev/null +++ b/src/gfx/Window.zig @@ -0,0 +1,41 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const vk = @import("vk"); +const c = @import("../c.zig"); + +const Base = @import("Base.zig"); +const Instance = @import("Instance.zig"); + +const Self = @This(); + +ref: *c.GLFWwindow, +surface: vk.SurfaceKHR, + +inst: *const Instance, + +pub fn init(inst: *const Instance, title: [*:0]const u8, extent: vk.Extent2D) !Self { + c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, "floating_window"); + c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, "floating_window"); + c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API); + + const ref = c.glfwCreateWindow(@intCast(extent.width), @intCast(extent.height), title, null, null) orelse + return error.GLFWWindowCreateFailed; + errdefer c.glfwDestroyWindow(ref); + + var surface: vk.SurfaceKHR = undefined; + if (c.glfwCreateWindowSurface(inst.ref, ref, null, &surface) != .success) + return error.GLFWWindowSurfaceFailed; + errdefer inst.vki.destroySurfaceKHR(inst.ref, surface, null); + + return .{ + .ref = ref, + .surface = surface, + .inst = inst, + }; +} + +pub fn deinit(self: Self) void { + self.inst.vki.destroySurfaceKHR(self.inst.ref, self.surface, null); + c.glfwDestroyWindow(self.ref); +} diff --git a/src/main.zig b/src/main.zig index 7f5d3a1..806ca08 100644 --- a/src/main.zig +++ b/src/main.zig @@ -50,113 +50,104 @@ const vertices = [_]Vertex{ const indices = [_]Index{ 4, 5, 6, 6, 5, 7 }; -const ChainImage = struct { - image: vk.Image = .null_handle, - view: vk.ImageView = .null_handle, - cmdbuf: vk.CommandBuffer = .null_handle, - fence: vk.Fence = .null_handle, - image_available: vk.Semaphore = .null_handle, - render_finished: vk.Semaphore = .null_handle, -}; +// pub fn create_swapchain( +// chain: *std.MultiArrayList(ChainImage), +// swapchain: *vk.SwapchainKHR, +// ally: std.mem.Allocator, +// pdev: vk.PhysicalDevice, +// vki: gfx.InstanceDispatch, +// window: *c.GLFWwindow, +// dev: vk.Device, +// vkd: Device.Wrapper, +// pool: vk.CommandPool, +// surface: vk.SurfaceKHR, +// swap_image_count: u32, +// format: vk.SurfaceFormatKHR, +// present_mode: vk.PresentModeKHR, +// ) !vk.Extent2D { +// const extent = try gfx.find_swap_extent(pdev, vki, surface, window); +// +// const prev_swapchain = swapchain.*; +// swapchain.* = try vkd.createSwapchainKHR(dev, &.{ +// .surface = surface, +// .min_image_count = swap_image_count, +// .image_format = format.format, +// .image_color_space = format.color_space, +// .image_extent = extent, +// .image_array_layers = 1, +// .image_usage = .{ .color_attachment_bit = true }, +// .image_sharing_mode = .exclusive, +// .pre_transform = .{ .identity_bit_khr = true }, +// .composite_alpha = .{ .opaque_bit_khr = true }, +// .present_mode = present_mode, +// .clipped = vk.TRUE, +// .old_swapchain = prev_swapchain, +// }, null); +// vkd.destroySwapchainKHR(dev, prev_swapchain, null); +// +// var image_count: u32 = undefined; +// _ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, null); +// try chain.resize(ally, image_count); +// _ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, chain.items(.image).ptr); +// +// // memset so that deinit_chain will succeed with .null_handle if error part-way through a loop. +// @memset(chain.items(.view), .null_handle); +// @memset(chain.items(.cmdbuf), .null_handle); +// @memset(chain.items(.fence), .null_handle); +// @memset(chain.items(.image_available), .null_handle); +// errdefer deinit_chain(chain.*, dev, vkd, pool); +// +// for (chain.items(.image), chain.items(.view)) |image, *view| { +// view.* = try vkd.createImageView(dev, &.{ +// .image = image, +// .view_type = .@"2d", +// .format = format.format, +// .components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity }, +// .subresource_range = .{ +// .aspect_mask = .{ .color_bit = true }, +// .base_mip_level = 0, +// .level_count = 1, +// .base_array_layer = 0, +// .layer_count = 1, +// }, +// }, null); +// } +// +// for (chain.items(.fence)) |*fence| { +// fence.* = try vkd.createFence(dev, &.{ .flags = .{ .signaled_bit = true } }, null); +// } +// +// for (chain.items(.image_available)) |*sem| { +// sem.* = try vkd.createSemaphore(dev, &.{}, null); +// } +// +// for (chain.items(.render_finished)) |*sem| { +// sem.* = try vkd.createSemaphore(dev, &.{}, null); +// } +// +// try vkd.allocateCommandBuffers(dev, &.{ +// .command_buffer_count = @intCast(chain.len), +// .command_pool = pool, +// .level = .primary, +// }, chain.items(.cmdbuf).ptr); +// +// return extent; +// } +// +// pub fn deinit_chain( +// chain: std.MultiArrayList(ChainImage), +// dev: vk.Device, +// vkd: Device.Wrapper, +// pool: vk.CommandPool, +// ) void { +// vkd.freeCommandBuffers(dev, pool, @intCast(chain.len), chain.items(.cmdbuf).ptr); +// for (chain.items(.view)) |view| vkd.destroyImageView(dev, view, null); +// for (chain.items(.fence)) |fence| vkd.destroyFence(dev, fence, null); +// for (chain.items(.image_available)) |sem| vkd.destroySemaphore(dev, sem, null); +// for (chain.items(.render_finished)) |sem| vkd.destroySemaphore(dev, sem, null); +// } -pub fn create_swapchain( - chain: *std.MultiArrayList(ChainImage), - swapchain: *vk.SwapchainKHR, - ally: std.mem.Allocator, - pdev: vk.PhysicalDevice, - vki: gfx.InstanceDispatch, - window: *c.GLFWwindow, - dev: vk.Device, - vkd: gfx.DeviceDispatch, - pool: vk.CommandPool, - surface: vk.SurfaceKHR, - swap_image_count: u32, - format: vk.SurfaceFormatKHR, - present_mode: vk.PresentModeKHR, -) !vk.Extent2D { - const extent = try gfx.find_swap_extent(pdev, vki, surface, window); - - const prev_swapchain = swapchain.*; - swapchain.* = try vkd.createSwapchainKHR(dev, &.{ - .surface = surface, - .min_image_count = swap_image_count, - .image_format = format.format, - .image_color_space = format.color_space, - .image_extent = extent, - .image_array_layers = 1, - .image_usage = .{ .color_attachment_bit = true }, - .image_sharing_mode = .exclusive, - .pre_transform = .{ .identity_bit_khr = true }, - .composite_alpha = .{ .opaque_bit_khr = true }, - .present_mode = present_mode, - .clipped = vk.TRUE, - .old_swapchain = prev_swapchain, - }, null); - vkd.destroySwapchainKHR(dev, prev_swapchain, null); - - var image_count: u32 = undefined; - _ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, null); - try chain.resize(ally, image_count); - _ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, chain.items(.image).ptr); - - // memset so that deinit_chain will succeed with .null_handle if error part-way through a loop. - @memset(chain.items(.view), .null_handle); - @memset(chain.items(.cmdbuf), .null_handle); - @memset(chain.items(.fence), .null_handle); - @memset(chain.items(.image_available), .null_handle); - errdefer deinit_chain(chain.*, dev, vkd, pool); - - for (chain.items(.image), chain.items(.view)) |image, *view| { - view.* = try vkd.createImageView(dev, &.{ - .image = image, - .view_type = .@"2d", - .format = format.format, - .components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity }, - .subresource_range = .{ - .aspect_mask = .{ .color_bit = true }, - .base_mip_level = 0, - .level_count = 1, - .base_array_layer = 0, - .layer_count = 1, - }, - }, null); - } - - for (chain.items(.fence)) |*fence| { - fence.* = try vkd.createFence(dev, &.{ .flags = .{ .signaled_bit = true } }, null); - } - - for (chain.items(.image_available)) |*sem| { - sem.* = try vkd.createSemaphore(dev, &.{}, null); - } - - for (chain.items(.render_finished)) |*sem| { - sem.* = try vkd.createSemaphore(dev, &.{}, null); - } - - try vkd.allocateCommandBuffers(dev, &.{ - .command_buffer_count = @intCast(chain.len), - .command_pool = pool, - .level = .primary, - }, chain.items(.cmdbuf).ptr); - - return extent; -} - -pub fn deinit_chain( - chain: std.MultiArrayList(ChainImage), - dev: vk.Device, - vkd: gfx.DeviceDispatch, - pool: vk.CommandPool, -) void { - vkd.freeCommandBuffers(dev, pool, @intCast(chain.len), chain.items(.cmdbuf).ptr); - for (chain.items(.view)) |view| vkd.destroyImageView(dev, view, null); - for (chain.items(.fence)) |fence| vkd.destroyFence(dev, fence, null); - for (chain.items(.image_available)) |sem| vkd.destroySemaphore(dev, sem, null); - for (chain.items(.render_finished)) |sem| vkd.destroySemaphore(dev, sem, null); -} - -fn render(dev: vk.Device, vkd: gfx.DeviceDispatch, swapchain: vk.SwapchainKHR, frame: ChainImage, queue: vk.Queue) !void { +fn render(dev: vk.Device, vkd: Device.Wrapper, swapchain: vk.SwapchainKHR, frame: Swapchain.ChainImage, queue: vk.Queue) !void { _ = try vkd.waitForFences(dev, 1, @ptrCast(&frame.fence), vk.TRUE, std.math.maxInt(u64)); const result = try vkd.acquireNextImageKHR( @@ -189,130 +180,86 @@ fn render(dev: vk.Device, vkd: gfx.DeviceDispatch, swapchain: vk.SwapchainKHR, f }); } +const Base = @import("gfx/Base.zig"); +const Instance = @import("gfx/Instance.zig"); +const Context = @import("gfx/Context.zig"); +const Window = @import("gfx/Window.zig"); +const Device = @import("gfx/Device.zig"); +const Swapchain = @import("gfx/Swapchain.zig"); + pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const ally = gpa.allocator(); - if (c.glfwInit() != c.GLFW_TRUE) return error.GlfwInitFailed; - defer c.glfwTerminate(); + const base = try Base.init(); + defer base.deinit(); - if (c.glfwVulkanSupported() != c.GLFW_TRUE) { - std.log.err("GLFW could not find libvulkan", .{}); - return error.NoVulkan; - } + const inst = try Instance.init(&base); + defer inst.deinit(); - var extent = vk.Extent2D{ .width = 800, .height = 600 }; + const win = try Window.init(&inst, "zig-glfw-vulkan", .{ .width = 800, .height = 600 }); + defer win.deinit(); - const window = try gfx.create_window(extent, app_name); - defer c.glfwDestroyWindow(window); + const dev = try Device.init(ally, &inst, &win); + defer dev.deinit(); - const vkb = try gfx.BaseDispatch.load(c.glfwGetInstanceProcAddress); + var sc = try Swapchain.create(ally, &dev); + defer sc.deinit(); - const instance, const vki, const messenger = try gfx.create_instance(vkb, app_name); - defer vki.destroyInstance(instance, null); - defer if (gfx.use_debug_messenger) - vki.destroyDebugUtilsMessengerEXT(instance, messenger, null); + const device_local = gfx.VkAllocator.init(dev.pdev, inst.vki); - const surface = try gfx.create_surface(instance, window); - defer vki.destroySurfaceKHR(instance, surface, null); - - const pdev: vk.PhysicalDevice, const dev: vk.Device, const vkd: gfx.DeviceDispatch, const family: u32 = - try gfx.create_device(ally, instance, surface, vki); - defer vkd.destroyDevice(dev, null); - - const queue = vkd.getDeviceQueue(dev, family, 0); - - const pool = try vkd.createCommandPool(dev, &.{ - .queue_family_index = family, - }, null); - defer vkd.destroyCommandPool(dev, pool, null); - - const preferred_format: vk.SurfaceFormatKHR = .{ - .format = .b8g8r8a8_srgb, - .color_space = .srgb_nonlinear_khr, - }; - const format = try gfx.find_surface_format(pdev, vki, surface, preferred_format); - - const present_mode = try gfx.find_present_mode(pdev, vki, surface, .mailbox_khr); - - const swap_image_count = try gfx.find_swap_image_count(pdev, vki, surface); - - var swapchain: vk.SwapchainKHR = .null_handle; - defer vkd.destroySwapchainKHR(dev, swapchain, null); - - var chain = std.MultiArrayList(ChainImage){}; - defer chain.deinit(ally); - defer deinit_chain(chain, dev, vkd, pool); - - const device_local = gfx.VkAllocator.init(pdev, vki); - - const pipeline_layout = try vkd.createPipelineLayout(dev, &.{ + const pipeline_layout = try dev.vkd.createPipelineLayout(dev.dev, &.{ .flags = .{}, .set_layout_count = 0, .p_set_layouts = undefined, .push_constant_range_count = 0, .p_push_constant_ranges = undefined, }, null); - defer vkd.destroyPipelineLayout(dev, pipeline_layout, null); + defer dev.vkd.destroyPipelineLayout(dev.dev, pipeline_layout, null); - const pipeline = try createPipeline(dev, pipeline_layout, format, vkd); - defer vkd.destroyPipeline(dev, pipeline, null); + const pipeline = try createPipeline(dev.dev, pipeline_layout, dev.format, dev.vkd); + defer dev.vkd.destroyPipeline(dev.dev, pipeline, null); - const vertex_buffer = try vkd.createBuffer(dev, &.{ + const vertex_buffer = try dev.vkd.createBuffer(dev.dev, &.{ .size = @sizeOf(@TypeOf(vertices)), .usage = .{ .transfer_dst_bit = true, .vertex_buffer_bit = true }, .sharing_mode = .exclusive, }, null); - defer vkd.destroyBuffer(dev, vertex_buffer, null); - const vertex_mem_reqs = vkd.getBufferMemoryRequirements(dev, vertex_buffer); - // const vertex_memory = try gfx.allocate(pdev, vki, dev, vkd, vertex_mem_reqs, .{ .device_local_bit = true }); - const vertex_memory = try device_local.alloc(dev, vkd, vertex_mem_reqs, .{ .device_local_bit = true }); - defer vkd.freeMemory(dev, vertex_memory, null); - try vkd.bindBufferMemory(dev, vertex_buffer, vertex_memory, 0); + defer dev.vkd.destroyBuffer(dev.dev, vertex_buffer, null); + const vertex_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, vertex_buffer); + // const vertex_memory = try gfx.allocate(pdev, vki, dev, dev.vkd, vertex_mem_reqs, .{ .device_local_bit = true }); + const vertex_memory = try device_local.alloc(dev.dev, dev.vkd, vertex_mem_reqs, .{ .device_local_bit = true }); + defer dev.vkd.freeMemory(dev.dev, vertex_memory, null); + try dev.vkd.bindBufferMemory(dev.dev, vertex_buffer, vertex_memory, 0); - try gfx.uploadData(Vertex, pdev, vki, dev, vkd, queue, pool, vertex_buffer, &vertices); + try gfx.uploadData(Vertex, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, vertex_buffer, &vertices); - const index_buffer = try vkd.createBuffer(dev, &.{ + const index_buffer = try dev.vkd.createBuffer(dev.dev, &.{ .size = @sizeOf(@TypeOf(indices)), .usage = .{ .transfer_dst_bit = true, .index_buffer_bit = true }, .sharing_mode = .exclusive, }, null); - defer vkd.destroyBuffer(dev, index_buffer, null); - const index_mem_reqs = vkd.getBufferMemoryRequirements(dev, index_buffer); - // const index_memory = try gfx.allocate(pdev, vki, dev, vkd, index_mem_reqs, .{ .device_local_bit = true }); - const index_memory = try device_local.alloc(dev, vkd, index_mem_reqs, .{ .device_local_bit = true }); - defer vkd.freeMemory(dev, index_memory, null); - try vkd.bindBufferMemory(dev, index_buffer, index_memory, 0); + defer dev.vkd.destroyBuffer(dev.dev, index_buffer, null); + const index_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, index_buffer); + // const index_memory = try gfx.allocate(pdev, vki, dev, dev.vkd, index_mem_reqs, .{ .device_local_bit = true }); + const index_memory = try device_local.alloc(dev.dev, dev.vkd, index_mem_reqs, .{ .device_local_bit = true }); + defer dev.vkd.freeMemory(dev.dev, index_memory, null); + try dev.vkd.bindBufferMemory(dev.dev, index_buffer, index_memory, 0); - try gfx.uploadData(Index, pdev, vki, dev, vkd, queue, pool, index_buffer, &indices); + try gfx.uploadData(Index, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, index_buffer, &indices); - extent = try create_swapchain( - &chain, - &swapchain, - ally, - pdev, - vki, - window, - dev, - vkd, - pool, - surface, - swap_image_count, - format, - present_mode, - ); - - for (chain.items(.image), chain.items(.view), chain.items(.cmdbuf)) |image, view, cmdbuf| { - try record_cmdbuf(cmdbuf, vkd, image, view, extent, pipeline, vertex_buffer, index_buffer); + try sc.init(); + for (sc.chain.items(.image), sc.chain.items(.view), sc.chain.items(.cmdbuf)) |image, view, cmdbuf| { + try record_cmdbuf(cmdbuf, dev.vkd, image, view, sc.extent, pipeline, vertex_buffer, index_buffer); } var index: u32 = 0; - while (c.glfwWindowShouldClose(window) == c.GLFW_FALSE) { + while (c.glfwWindowShouldClose(win.ref) == c.GLFW_FALSE) { var w: c_int = undefined; var h: c_int = undefined; - c.glfwGetFramebufferSize(window, &w, &h); + c.glfwGetFramebufferSize(win.ref, &w, &h); // Don't present or resize swapchain while the window is minimized if (w == 0 or h == 0) { @@ -320,32 +267,15 @@ pub fn main() !void { continue; } - const frame: ChainImage = chain.get(index); + const frame = sc.chain.get(index); - render(dev, vkd, swapchain, frame, queue) catch |err| switch (err) { + render(dev.dev, dev.vkd, sc.ref, frame, dev.queue) catch |err| switch (err) { error.OutOfDateKHR => { - try vkd.deviceWaitIdle(dev); + try dev.vkd.deviceWaitIdle(dev.dev); - deinit_chain(chain, dev, vkd, pool); - - extent = try create_swapchain( - &chain, - &swapchain, - ally, - pdev, - vki, - window, - dev, - vkd, - pool, - surface, - swap_image_count, - format, - present_mode, - ); - - for (chain.items(.image), chain.items(.view), chain.items(.cmdbuf)) |image, view, cmdbuf| { - try record_cmdbuf(cmdbuf, vkd, image, view, extent, pipeline, vertex_buffer, index_buffer); + try sc.init(); + for (sc.chain.items(.image), sc.chain.items(.view), sc.chain.items(.cmdbuf)) |image, view, cmdbuf| { + try record_cmdbuf(cmdbuf, dev.vkd, image, view, sc.extent, pipeline, vertex_buffer, index_buffer); } index = 0; @@ -357,15 +287,15 @@ pub fn main() !void { c.glfwPollEvents(); - index = @intCast((index + 1) % chain.len); + index = @intCast((index + 1) % sc.chain.len); } - try vkd.deviceWaitIdle(dev); + try dev.vkd.deviceWaitIdle(dev.dev); } fn record_cmdbuf( cmdbuf: vk.CommandBuffer, - vkd: gfx.DeviceDispatch, + vkd: Device.Wrapper, image: vk.Image, view: vk.ImageView, extent: vk.Extent2D, @@ -486,7 +416,7 @@ fn record_cmdbuf( try vkd.endCommandBuffer(cmdbuf); } -fn createPipeline(dev: vk.Device, layout: vk.PipelineLayout, format: vk.SurfaceFormatKHR, vkd: gfx.DeviceDispatch) !vk.Pipeline { +fn createPipeline(dev: vk.Device, layout: vk.PipelineLayout, format: vk.SurfaceFormatKHR, vkd: Device.Wrapper) !vk.Pipeline { const vert = try vkd.createShaderModule(dev, &.{ .code_size = shaders.triangle_vert.len, .p_code = @as([*]const u32, @ptrCast(&shaders.triangle_vert)),