diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2021-03-09 17:40:29 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2021-03-09 17:40:29 +0000 |
| commit | 40c7bc1f0f58d3e19d9b4d610454745cfbb005aa (patch) | |
| tree | 709c7e99854d92988ca5d296afd539559a13761c /src | |
| parent | intial commit (diff) | |
| download | openxPriments-40c7bc1f0f58d3e19d9b4d610454745cfbb005aa.tar.gz openxPriments-40c7bc1f0f58d3e19d9b4d610454745cfbb005aa.zip | |
Vulkan wip
Diffstat (limited to 'src')
| -rw-r--r-- | src/c.zig | 6 | ||||
| -rw-r--r-- | src/graphics.zig | 554 | ||||
| -rw-r--r-- | src/main.zig | 434 | ||||
| -rw-r--r-- | src/renderdoc.zig | 77 | ||||
| -rw-r--r-- | src/xrvk.zig | 93 |
5 files changed, 891 insertions, 273 deletions
diff --git a/src/c.zig b/src/c.zig deleted file mode 100644 index 31521b1..0000000 --- a/src/c.zig +++ /dev/null @@ -1,6 +0,0 @@ -const xr = @import("openxr"); -const vk = @import("vulkan"); - -pub extern fn xrGetInstanceProcAddr(instance: xr.Instance, procname: [*:0]const u8, function: *xr.PfnVoidFunction) xr.Result; -pub extern fn vkGetInstanceProcAddr(instance: vk.Instance, procname: [*:0]const u8) vk.PfnVoidFunction; -pub extern fn vkGetDeviceProcAddr(instance: vk.Device, procname: [*:0]const u8) vk.PfnVoidFunction; diff --git a/src/graphics.zig b/src/graphics.zig index 40b85f5..03fb71d 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1,140 +1,472 @@ const std = @import("std"); -const xr = @import("openxr"); -const vk = @import("vulkan"); -const c = @import("c.zig"); +usingnamespace @import("xrvk.zig"); const Allocator = std.mem.Allocator; -var vk_binding: xr.GraphicsBindingVulkan2KHR = undefined; -var vk_queue: vk.Queue = undefined; +pub const Graphics = struct { + xri: xr.InstanceDispatch, + vki: vk.InstanceDispatch, + vkd: vk.DeviceDispatch, -const InstanceDispatch = struct { - vkDestroyInstance: vk.PfnDestroyInstance, - vkGetPhysicalDeviceQueueFamilyProperties: vk.PfnGetPhysicalDeviceQueueFamilyProperties, - usingnamespace vk.InstanceWrapper(@This()); -}; + instance: vk.Instance, + device: vk.Device, + physical_device: vk.PhysicalDevice, -const DeviceDispatch = struct { - vkDestroyDevice: vk.PfnDestroyDevice, - vkGetDeviceQueue: vk.PfnGetDeviceQueue, - usingnamespace vk.DeviceWrapper(@This()); -}; + queue: vk.Queue, + family_index: u32, + command_pool: vk.CommandPool, + + allocator: *Allocator, + + pub fn init(allocator: *Allocator, xri: xr.InstanceDispatch, instance: xr.Instance, system: xr.SystemId) !Graphics { + var self: Graphics = undefined; + + self.xri = xri; + self.allocator = allocator; + + var result: xr.VkResult = undefined; + + var requirements = xr.GraphicsRequirementsVulkan2KHR.empty(); + try xri.getVulkanGraphicsRequirements2KHR(instance, system, &requirements); + + // CREATE INSTANCE + + const app_info = vk.ApplicationInfo{ + .p_application_name = "test", + .application_version = vk.makeVersion(0, 0, 0), + .p_engine_name = "test", + .engine_version = vk.makeVersion(0, 0, 0), + .api_version = vk.API_VERSION_1_2, + }; -pub fn get_binding(xri: anytype, instance: xr.Instance, system: xr.SystemId, allocator: *Allocator) !*c_void { - var result: xr.VkResult = undefined; - vk_binding = xr.GraphicsBindingVulkan2KHR.empty(); - - var requirements = xr.GraphicsRequirementsVulkan2KHR.empty(); - try xri.getVulkanGraphicsRequirements2KHR(instance, system, &requirements); - - // CREATE INSTANCE - - const app_info = vk.ApplicationInfo{ - .p_application_name = "test", - .application_version = vk.makeVersion(0, 0, 0), - .p_engine_name = "test", - .engine_version = vk.makeVersion(0, 0, 0), - .api_version = vk.API_VERSION_1_2, - }; - - const instance_create_info = vk.InstanceCreateInfo{ - .flags = .{}, - .p_application_info = &app_info, - .enabled_layer_count = 0, - .pp_enabled_layer_names = undefined, - .enabled_extension_count = 0, - .pp_enabled_extension_names = @ptrCast([*]const [*:0]const u8, ""), - }; - - try xri.createVulkanInstanceKHR( - instance, - .{ + const instance_layers = [_][*:0]const u8{ + "VK_LAYER_KHRONOS_validation", + }; + const instance_extensions = [_][*:0]const u8{ + "VK_EXT_debug_utils", + }; + + const instance_create_info = vk.InstanceCreateInfo{ + .flags = .{}, + .p_application_info = &app_info, + .enabled_layer_count = instance_layers.len, + .pp_enabled_layer_names = &instance_layers, + .enabled_extension_count = instance_extensions.len, + .pp_enabled_extension_names = &instance_extensions, + }; + + try xri.createVulkanInstanceKHR(instance, .{ .create_flags = .{}, .system_id = system, - .pfn_get_instance_proc_addr = c.vkGetInstanceProcAddr, + .pfn_get_instance_proc_addr = vk.getInstanceProcAddr, .vulkan_create_info = &instance_create_info, .vulkan_allocator = null, // .vulkan_allocator = &vk_allocator, - }, - &vk_binding.instance, - &result - ); - const vki = try InstanceDispatch.load(vk_binding.instance, c.vkGetInstanceProcAddr); - // defer vki.destroyInstance(inst) catch unreachable; + }, &self.instance, &result); + self.vki = try vk.InstanceDispatch.load(self.instance, vk.getInstanceProcAddr); + errdefer self.vki.destroyInstance(self.instance, null); - // CREATE PHYSICAL DEVICE + // CREATE PHYSICAL DEVICE - try xri.getVulkanGraphicsDevice2KHR( - instance, - .{ .system_id = system, .vulkan_instance = vk_binding.instance }, - &vk_binding.physical_device - ); + try xri.getVulkanGraphicsDevice2KHR(instance, .{ .system_id = system, .vulkan_instance = self.instance }, &self.physical_device); - // CHOOSE QUEUE + // CHOOSE QUEUE - var family_count: u32 = undefined; - vki.getPhysicalDeviceQueueFamilyProperties(vk_binding.physical_device, &family_count, null); + var family_count: u32 = undefined; + self.vki.getPhysicalDeviceQueueFamilyProperties(self.physical_device, &family_count, null); - const families = try allocator.alloc(vk.QueueFamilyProperties, family_count); - defer allocator.free(families); - vki.getPhysicalDeviceQueueFamilyProperties(vk_binding.physical_device, &family_count, families.ptr); + const families = try allocator.alloc(vk.QueueFamilyProperties, family_count); + defer allocator.free(families); + self.vki.getPhysicalDeviceQueueFamilyProperties(self.physical_device, &family_count, families.ptr); - const family_index = for (families) |properties, i| { - const family = @intCast(u32, i); + self.family_index = for (families) |properties, i| { + const family = @intCast(u32, i); - if (properties.queue_flags.contains(.{.graphics_bit = true})) { - break family; - } - } else { - return error.NoGraphicsFamily; - }; + if (properties.queue_flags.contains(.{ .graphics_bit = true })) { + break family; + } + } else { + return error.NoGraphicsFamily; + }; + + const priority = [_]f32{0}; + const queue_create_info = [_]vk.DeviceQueueCreateInfo{ + .{ + .flags = .{}, + .queue_count = 1, + .p_queue_priorities = &priority, + .queue_family_index = self.family_index, + }, + }; - const priority = [_]f32{0}; - const queue_create_info = [_]vk.DeviceQueueCreateInfo{ - .{ + // CREATE DEVICE + + const device_create_info = vk.DeviceCreateInfo{ .flags = .{}, - .queue_count = 1, - .p_queue_priorities = &priority, - .queue_family_index = family_index, - }, - }; - - // CREATE DEVICE - - const zero = [_:0]u8{0}; - const device_create_info = vk.DeviceCreateInfo{ - .flags = .{}, - .queue_create_info_count = queue_create_info.len, - .p_queue_create_infos = &queue_create_info, - .enabled_layer_count = 0, - .pp_enabled_layer_names = @ptrCast([*]const [*:0]const u8, &zero), - .enabled_extension_count = 0, - .pp_enabled_extension_names = @ptrCast([*]const [*:0]const u8, &zero), - .p_enabled_features = null, - }; - - try xri.createVulkanDeviceKHR( - instance, - .{ + .queue_create_info_count = queue_create_info.len, + .p_queue_create_infos = &queue_create_info, + .enabled_layer_count = 0, + .pp_enabled_layer_names = undefined, + .enabled_extension_count = 0, + .pp_enabled_extension_names = undefined, + .p_enabled_features = null, + }; + + try xri.createVulkanDeviceKHR(instance, .{ .system_id = system, .create_flags = .{}, - .pfn_get_instance_proc_addr = c.vkGetInstanceProcAddr, - .vulkan_physical_device = vk_binding.physical_device, + .pfn_get_instance_proc_addr = vk.getInstanceProcAddr, + .vulkan_physical_device = self.physical_device, .vulkan_create_info = &device_create_info, .vulkan_allocator = null, // .vulkan_allocator = &vk_allocator, - }, - &vk_binding.device, - &result - ); - const vkd = try DeviceDispatch.load(vk_binding.device, c.vkGetDeviceProcAddr); - // defer vki.destroyDevice(vk_binding.device) catch unreachable; + }, &self.device, &result); + self.vkd = try vk.DeviceDispatch.load(self.device, vk.getDeviceProcAddr); + errdefer self.vkd.destroyDevice(self.device, null); + + // GET QUEUE + + self.queue = self.vkd.getDeviceQueue(self.device, self.family_index, 0); + + // CREATE COMMAND POOL + self.command_pool = try self.vkd.createCommandPool( + self.device, + .{ + .flags = .{ .reset_command_buffer_bit = true }, + .queue_family_index = self.family_index + }, + null + ); + errdefer self.vkd.destroyCommandPool(self.device, self.command_pool, null); + + return self; + } + + pub fn deinit(self: *const Graphics) void { + self.vkd.destroyCommandPool(self.device, self.command_pool, null); + self.vkd.destroyDevice(self.device, null); + self.vki.destroyInstance(self.instance, null); + } + + fn debug_callback( + msg_severity: vk.c.VkDebugUtilsMessageSeverityFlagBitsEXT, + msg_type: vk.c.VkDebugUtilsMessageTypeFlagsEXT, + c_data: ?*const vk.c.VkDebugUtilsMessengerCallbackDataEXT, + c_self: ?*c_void + ) callconv(.C) vk.Bool32 { + const level = if (msg_severity == .VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) "error" else "warn"; + const self = @ptrCast(*Graphics, @alignCast(@alignOf(Graphics), c_self)); + + if (c_data) |data| { + std.debug.print("[{}] {}: {}\n", .{ + level, + "??", + // @as([*:0]const u8, data.pMessageIdName), + @as([*:0]const u8, data.pMessage), + }); + } + return 0; + } + + pub fn setup_debugging(self: *Graphics) !void { + if (self.vki.vkCreateDebugUtilsMessengerEXT) |pfn| { + var messenger: vk.c.VkDebugUtilsMessengerEXT = undefined; + const messenger_create_info = vk.c.VkDebugUtilsMessengerCreateInfoEXT{ + .sType = .VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .pNext = null, + .flags = 0, + .messageSeverity = 0x1110, + .messageType = 0x3, + .pfnUserCallback = @This().debug_callback, + .pUserData = @ptrCast(*c_void, self), + }; + + _ = pfn( + @intToPtr(vk.c.VkInstance, @enumToInt(self.instance)), + &messenger_create_info, + null, + &messenger + ); + } else { + return error.FunctionUnsupported; + } + } + + pub fn get_binding(self: *const Graphics) xr.GraphicsBindingVulkan2KHR { + return .{ + .instance = self.instance, + .device = self.device, + .physical_device = self.physical_device, + .queue_family_index = self.family_index, + .queue_index = 0, + }; + } + + pub fn render_view(self: *const Graphics, view: xr.CompositionLayerProjectionView, swapchain: Swapchain, i: u32) !void { + const buf = swapchain.command_buffers[0]; + const fence = swapchain.fence; + + // reset + try self.vkd.resetFences(self.device, 1, @ptrCast([*]const vk.Fence, &fence)); + try self.vkd.resetCommandBuffer(buf, .{}); + + // begin + try self.vkd.beginCommandBuffer(buf, .{ + .flags = .{}, + .p_inheritance_info = null, + }); + + // main + + if (false) { + const image_ranges = [_]vk.ImageSubresourceRange{ + .{ + .aspect_mask = .{ .color_bit = true }, + .base_mip_level = 0, + .level_count = vk.REMAINING_MIP_LEVELS, + .base_array_layer = 0, + .layer_count = vk.REMAINING_ARRAY_LAYERS, + }, + }; + + var image_barrier = [_]vk.ImageMemoryBarrier{ + .{ + .src_access_mask = .{}, + .dst_access_mask = .{ .transfer_write_bit = true }, + .old_layout = .color_attachment_optimal, + .new_layout = .transfer_dst_optimal, + .src_queue_family_index = self.family_index, + .dst_queue_family_index = self.family_index, + .image = undefined, + .subresource_range = image_ranges[0], + }, + .{ + .src_access_mask = .{ .transfer_write_bit = true }, + .dst_access_mask = .{}, + .old_layout = .transfer_dst_optimal, + .new_layout = .color_attachment_optimal, + .src_queue_family_index = self.family_index, + .dst_queue_family_index = self.family_index, + .image = undefined, + .subresource_range = image_ranges[0], + }, + }; + image_barrier[0].image = swapchain.images[i]; + image_barrier[1].image = swapchain.images[i]; + + self.vkd.cmdPipelineBarrier( + buf, + .{ .transfer_bit = true }, .{ .transfer_bit = true }, .{}, + 0, @ptrCast([*]vk.MemoryBarrier, &image_barrier), + 0, @ptrCast([*]vk.BufferMemoryBarrier, &image_barrier), + 1, image_barrier[0..] + ); + + self.vkd.cmdClearColorImage( + buf, swapchain.images[i], .transfer_dst_optimal, + .{ .uint_32 = .{ @intCast(u32, (i * 80)) % 256, 30, 34, 255 } }, + image_ranges.len, &image_ranges + ); + + self.vkd.cmdPipelineBarrier( + buf, + .{ .transfer_bit = true }, .{ .transfer_bit = true }, .{}, + 0, @ptrCast([*]vk.MemoryBarrier, &image_barrier), + 0, @ptrCast([*]vk.BufferMemoryBarrier, &image_barrier), + 1, image_barrier[1..] + ); + } else { + - // GET QUEUE + } + + // end + try self.vkd.endCommandBuffer(buf); + + // exec + std.debug.print("using command buffer {}\n", .{ i }); + const submits = [_]vk.SubmitInfo{ + .{ + .wait_semaphore_count = 0, + .p_wait_semaphores = undefined, + .p_wait_dst_stage_mask = undefined, + + .command_buffer_count = 1, + .p_command_buffers = swapchain.command_buffers[0..].ptr, + + .signal_semaphore_count = 0, + .p_signal_semaphores = undefined, + }, + }; + try self.vkd.queueSubmit(self.queue, submits.len, &submits, swapchain.fence); + + // wait + while (true) { + const res = try self.vkd.waitForFences(self.device, 1, @ptrCast([*]const vk.Fence, &fence), vk.TRUE, 1_000_000_000); + if (res == .success) { + break; + } + std.debug.print("timed out waiting for fence...\n", .{}); + } + } +}; + +pub const Swapchain = struct { + handle: xr.Swapchain, + images: []vk.Image, + image_rect: xr.Rect2Di, + command_buffers: []vk.CommandBuffer, + fence: vk.Fence, + + graphics: *const Graphics, + + pub fn init(gfx: *const Graphics, session: xr.Session, config: xr.ViewConfigurationView) !Swapchain { + var self: Swapchain = undefined; - vk_queue = vkd.getDeviceQueue(vk_binding.device, family_index, 0); - vk_binding.queue_family_index = family_index; - vk_binding.queue_index = 0; + self.graphics = gfx; + + // xri.enumerateSwapchainFormats(...); + + self.image_rect = .{ + .offset = .{}, + .extent = .{ + .width = @intCast(i32, config.recommended_image_rect_width), + .height = @intCast(i32, config.recommended_image_rect_height), + }, + }; + + const create_result = try gfx.xri.createSwapchain(session, .{ + .create_flags = .{}, + .usage_flags = .{ + .color_attachment_bit = true, + // .depth_stencil_attachment_bit = true, + .transfer_dst_bit = true, + .sampled_bit = true, + }, + .format = @enumToInt(vk.Format.r8g8b8a8_unorm), + .sample_count = config.recommended_swapchain_sample_count, + .width = config.recommended_image_rect_width, + .height = config.recommended_image_rect_height, + .face_count = 1, + .array_size = 1, + .mip_count = 1, + }); + self.handle = create_result.swapchain; + + const image_result = try gfx.xri.enumerateSwapchainImages(self.handle, 0, null); + const images = try gfx.allocator.alloc(xr.SwapchainImageVulkan2KHR, image_result.image_count_output); + defer gfx.allocator.free(images); + + _ = try gfx.xri.enumerateSwapchainImages(self.handle, image_result.image_count_output, @ptrCast([*]xr.SwapchainImageBaseHeader, images.ptr)); + + self.images = try gfx.allocator.alloc(vk.Image, images.len); + errdefer gfx.allocator.free(self.images); + + for (images) |xr_image, i| { + self.images[i] = xr_image.image; + } - return @ptrCast(*c_void, &vk_binding); -} + self.command_buffers = try gfx.allocator.alloc(vk.CommandBuffer, images.len); + errdefer gfx.allocator.free(self.command_buffers); + try gfx.vkd.allocateCommandBuffers(gfx.device, .{ + .command_pool = gfx.command_pool, + .command_buffer_count = image_result.image_count_output, + .level = .primary, + }, self.command_buffers.ptr); + std.debug.print( "allocating command buffers: {}\n", .{ image_result.image_count_output }); + + std.debug.print( "created swapchain {}x{}, SS{}, {} images\n", .{ + config.recommended_image_rect_width, + config.recommended_image_rect_height, + config.recommended_swapchain_sample_count, + images.len, + }); + + self.fence = try gfx.vkd.createFence(gfx.device, .{ .flags = .{} }, null); + + // try self.record_buffers(); + + return self; + } + + fn record_buffers(self: Swapchain) !void { + const gfx = self.graphics; + + const image_ranges = [_]vk.ImageSubresourceRange{ + .{ + .aspect_mask = .{ .color_bit = true }, + .base_mip_level = 0, + .level_count = 1, + .base_array_layer = 0, + .layer_count = 1, + }, + }; + + var image_barrier = [_]vk.ImageMemoryBarrier{ + .{ + .src_access_mask = .{}, + .dst_access_mask = .{ .transfer_write_bit = true }, + .old_layout = .color_attachment_optimal, + .new_layout = .general, + .src_queue_family_index = gfx.family_index, + .dst_queue_family_index = gfx.family_index, + .image = undefined, + .subresource_range = image_ranges[0], + }, + .{ + .src_access_mask = .{ .transfer_write_bit = true }, + .dst_access_mask = .{}, + .old_layout = .general, + .new_layout = .color_attachment_optimal, + .src_queue_family_index = gfx.family_index, + .dst_queue_family_index = gfx.family_index, + .image = undefined, + .subresource_range = image_ranges[0], + }, + }; + + for (self.command_buffers) |buf, i| { + std.debug.print("recording buffer {}\n", .{ i }); + try gfx.vkd.beginCommandBuffer(buf, .{ + .flags = .{ .simultaneous_use_bit = true }, + .p_inheritance_info = null, + }); + + image_barrier[0].image = self.images[i]; + image_barrier[1].image = self.images[i]; + + gfx.vkd.cmdPipelineBarrier( + buf, + .{ .transfer_bit = true }, .{ .transfer_bit = true }, .{}, + 0, @ptrCast([*]vk.MemoryBarrier, &image_barrier), + 0, @ptrCast([*]vk.BufferMemoryBarrier, &image_barrier), + 1, image_barrier[0..] + ); + + gfx.vkd.cmdClearColorImage( + buf, self.images[i], .general, + .{ .float32 = .{ i / 4.0, 1.0, 0.5, 1.0 } }, + image_ranges.len, &image_ranges + ); + + gfx.vkd.cmdPipelineBarrier( + buf, + .{ .transfer_bit = true }, .{ .transfer_bit = true }, .{}, + 0, @ptrCast([*]vk.MemoryBarrier, &image_barrier), + 0, @ptrCast([*]vk.BufferMemoryBarrier, &image_barrier), + 1, image_barrier[1..] + ); + + + try gfx.vkd.endCommandBuffer(buf); + } + } + + pub fn deinit(self: Swapchain) void { + const gfx = self.graphics; + + gfx.allocator.free(self.images); + gfx.allocator.free(self.command_buffers); + gfx.xri.destroySwapchain(self.handle) catch {}; + } +}; diff --git a/src/main.zig b/src/main.zig index b7c7174..1d10253 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,179 +1,301 @@ +usingnamespace @import("xrvk.zig"); const std = @import("std"); -const xr = @import("openxr"); -const c = @import("c.zig"); +const gfx = @import("graphics.zig"); +const renderdoc = @import("renderdoc.zig"); const Allocator = std.mem.Allocator; -const graphics = @import("./graphics.zig"); +pub fn main() !void { + try renderdoc.load_api(); + var session = try Session.init(std.heap.page_allocator); + try session.loop(); + session.deinit(); +} -const BaseDispatch = struct { - xrCreateInstance: xr.PfnCreateInstance, - usingnamespace xr.BaseWrapper(@This()); -}; +const Session = struct { + xrb: xr.BaseDispatch, + xri: xr.InstanceDispatch, -const InstanceDispatch = struct { - xrDestroyInstance: xr.PfnDestroyInstance, - xrGetSystem: xr.PfnGetSystem, - xrGetSystemProperties: xr.PfnGetSystemProperties, - xrCreateSession: xr.PfnCreateSession, - xrBeginSession: xr.PfnBeginSession, - xrEndSession: xr.PfnEndSession, - xrWaitFrame: xr.PfnWaitFrame, - xrBeginFrame: xr.PfnBeginFrame, - xrEndFrame: xr.PfnEndFrame, - xrPollEvent: xr.PfnPollEvent, - xrLocateViews: xr.PfnLocateViews, - xrCreateReferenceSpace: xr.PfnCreateReferenceSpace, - xrCreateVulkanInstanceKHR: xr.PfnCreateVulkanInstanceKHR, - xrGetVulkanGraphicsDevice2KHR: xr.PfnGetVulkanGraphicsDevice2KHR, - xrGetVulkanGraphicsRequirements2KHR: xr.PfnGetVulkanGraphicsRequirements2KHR, - xrCreateVulkanDeviceKHR: xr.PfnCreateVulkanDeviceKHR, - usingnamespace xr.InstanceWrapper(@This()); -}; + instance: xr.Instance, + session: xr.Session, + system: xr.SystemId, -fn xrGetProcAddr(instance: xr.Instance, name: [*:0]const u8) !xr.PfnVoidFunction { - var out: xr.PfnVoidFunction = undefined; - const result = c.xrGetInstanceProcAddr(instance, name, &out); - return switch (result) { - .success => out, - .error_handle_invalid => error.HandleInvalid, - .error_instance_lost => error.InstanceLost, - .error_runtime_failure => error.RuntimeFailure, - .error_out_of_memory => error.OutOfMemory, - .error_function_unsupported => error.FunctionUnsupported, - .error_validation_failure => error.ValidationFailure, - else => error.Unknown, - }; -} + allocator: *Allocator, -pub fn main() !void { - const allocator = std.heap.page_allocator; - - var name: [128]u8 = undefined; - std.mem.copy(u8, name[0..], "openxr-zig-test" ++ [_]u8{0}); - const zero = [_:0]u8{0}; - - const extensions = [_][*:0]const u8 { - "XR_KHR_vulkan_enable2", - }; + state: xr.SessionState, + views: ?ViewInfo, + graphics: gfx.Graphics, + layers: []*const xr.CompositionLayerBaseHeader, + scene_space: xr.Space, - const xrb = try BaseDispatch.load(xrGetProcAddr); - const inst = try xrb.createInstance(.{ - .create_flags = .{}, - .application_info = .{ - .application_name = name, - .application_version = 0, - .engine_name = name, - .engine_version = 0, - .api_version = xr.makeVersion(1, 0, 0), - }, - .enabled_api_layer_count = 0, - .enabled_api_layer_names = @ptrCast([*]const [*:0]const u8, &zero), - .enabled_extension_count = extensions.len, - .enabled_extension_names = &extensions, - }); - - const xri = try InstanceDispatch.load(inst, xrGetProcAddr); - defer xri.destroyInstance(inst) catch unreachable; - - const system = try xri.getSystem(inst, .{ .form_factor = .head_mounted_display }); - - var system_properties = xr.SystemProperties.empty(); - try xri.getSystemProperties(inst, system, &system_properties); - - std.debug.print( - \\system {}: - \\ vendor Id: {} - \\ systemName: {} - \\ gfx - \\ max swapchain image resolution: {}x{} - \\ max layer count: {} - \\ tracking - \\ orientation tracking: {} - \\ positional tracking: {} - \\ - , .{ - system, - system_properties.vendor_id, - system_properties.system_name, - system_properties.graphics_properties.max_swapchain_image_width, - system_properties.graphics_properties.max_swapchain_image_height, - system_properties.graphics_properties.max_layer_count, - system_properties.tracking_properties.orientation_tracking, - system_properties.tracking_properties.position_tracking, - }); - - const session = try xri.createSession(inst, .{ - .next = try graphics.get_binding(xri, inst, system, allocator), - .create_flags = .{}, - .system_id = system, - }); - - _ = try xri.beginSession(session, .{ - .primary_view_configuration_type = .primary_stereo, - }); - - const sceneSpace = (try xri.createReferenceSpace(session, .{ - .reference_space_type = .stage, - .pose_in_reference_space = .{ - .orientation = .{ .w = 1 }, - }, - })).space; - - var projViews = [_]xr.CompositionLayerProjectionView{xr.CompositionLayerProjectionView.empty()} ** 2; - - const compositionLayer = xr.CompositionLayerProjection{ - .layer_flags = .{ .blend_texture_source_alpha_bit = true }, - .space = sceneSpace, - .view_count = 2, - .views = &projViews, - }; - const layers = [_]*const xr.CompositionLayerBaseHeader{ - @ptrCast(*const xr.CompositionLayerBaseHeader, &compositionLayer), + first_render: bool = true, + + const ViewInfo = struct { + configurations: []xr.ViewConfigurationView, + projections: []xr.CompositionLayerProjectionView, + swapchains: []gfx.Swapchain, }; - while (true) { - // todo: fix API - var frameWaitInfo = xr.FrameWaitInfo.empty(); - var frameState = xr.FrameState.empty(); + pub fn init(allocator: *Allocator) !Session { + var name: [128]u8 = undefined; + std.mem.copy(u8, name[0..], "openxr-zig-test" ++ [_]u8{0}); + const zero = [_:0]u8{}; - _ = try xri.waitFrame(session, &frameWaitInfo, &frameState); + const layers = [_][*:0]const u8{ + "XR_APILAYER_LUNARG_core_validation", + }; + + const extensions = [_][*:0]const u8{ + "XR_KHR_vulkan_enable2", + }; - _ = try xri.beginFrame(session, &xr.FrameBeginInfo{}); + const xrb = try xr.BaseDispatch.load(xr.getProcAddr); + const instance = try xrb.createInstance(.{ + .create_flags = .{}, + .application_info = .{ + .application_name = name, + .application_version = 0, + .engine_name = name, + .engine_version = 0, + .api_version = xr.makeVersion(1, 0, 0), + }, + .enabled_api_layer_count = 0, + .enabled_api_layer_names = @ptrCast([*]const [*:0]const u8, &zero), + .enabled_extension_count = extensions.len, + .enabled_extension_names = &extensions, + }); - var layer_count: u32 = 0; + const xri = try xr.InstanceDispatch.load(instance, xr.getProcAddr); + errdefer xri.destroyInstance(instance) catch unreachable; - if (frameState.should_render > 0) { - var views = [_]xr.View{xr.View.empty()} ** 2; - var viewState = xr.ViewState.empty(); + const system = try xri.getSystem(instance, .{ .form_factor = .head_mounted_display }); - _ = try xri.locateViews(session, .{ - .view_configuration_type = .primary_stereo, - .display_time = frameState.predicted_display_time, - .space = sceneSpace, - }, &viewState, 2, &views); + var system_properties = xr.SystemProperties.empty(); + try xri.getSystemProperties(instance, system, &system_properties); - std.debug.print("views: {}\n", .{ views }); + std.debug.print( + \\system {}: + \\ vendor Id: {} + \\ systemName: {} + \\ gfx + \\ max swapchain image resolution: {}x{} + \\ max layer count: {} + \\ tracking + \\ orientation tracking: {} + \\ positional tracking: {} + \\ + , .{ + system, + system_properties.vendor_id, + system_properties.system_name, + system_properties.graphics_properties.max_swapchain_image_width, + system_properties.graphics_properties.max_swapchain_image_height, + system_properties.graphics_properties.max_layer_count, + system_properties.tracking_properties.orientation_tracking, + system_properties.tracking_properties.position_tracking, + }); + + var graphics = try gfx.Graphics.init(allocator, xri, instance, system); + try graphics.setup_debugging(); + + const binding = graphics.get_binding(); + const session = try xri.createSession(instance, .{ + .next = &binding, + .create_flags = .{}, + .system_id = system, + }); + errdefer xri.destroySession(session) catch unreachable; - for (views) |view, i| { - projViews[i].pose = view.pose; - projViews[i].fov = view.fov; - projViews[i].sub_image = .{ - .swapchain = xr.Swapchain.null_handle, - .image_rect = .{}, - .image_array_index = 0, - }; + const scene_space = (try xri.createReferenceSpace(session, .{ + .reference_space_type = .stage, + .pose_in_reference_space = .{ + .orientation = .{ .w = 1 }, + }, + })).space; + + return Session{ + .xrb = xrb, + .xri = xri, + + .instance = instance, + .session = session, + .system = system, + .allocator = allocator, + + .state = .idle, + .views = null, + .graphics = graphics, + .scene_space = scene_space, + .layers = &[_]*const xr.CompositionLayerBaseHeader{}, + }; + } + + pub fn deinit(self: *const Session) void { + if (self.views) |views| { + for (views.swapchains) |swapchain| { + swapchain.deinit(); } + self.allocator.free(views.configurations); + self.allocator.free(views.projections); + self.allocator.free(views.swapchains); + } - layer_count = 1; + for (self.layers) |layer| { + self.allocator.destroy(layer); } - _ = try xri.endFrame(session, .{ - .display_time = frameState.predicted_display_time, - .environment_blend_mode = .@"opaque", - .layer_count = layer_count, - .layers = &layers, - }); + _ = self.xri.destroySession(self.session) catch null; + _ = self.xri.destroyInstance(self.instance) catch null; } - try xri.endSession(session); -} + pub fn loop(self: *Session) !void { + const view_count = try self.xri.enumerateViewConfigurationViews(self.instance, self.system, .primary_stereo, 0, null); + const views = ViewInfo{ + .configurations = try self.allocator.alloc(xr.ViewConfigurationView, view_count), + .projections = try self.allocator.alloc(xr.CompositionLayerProjectionView, view_count), + .swapchains = try self.allocator.alloc(gfx.Swapchain, view_count), + }; + + _ = try self.xri.enumerateViewConfigurationViews(self.instance, self.system, .primary_stereo, view_count, views.configurations.ptr); + + for (views.configurations) |config, i| { + if (renderdoc.api) |api| api.StartFrameCapture(null, null); + views.projections[i] = xr.CompositionLayerProjectionView.empty(); + views.swapchains[i] = try gfx.Swapchain.init(&self.graphics, self.session, config); + if (renderdoc.api) |api| _ = api.EndFrameCapture(null, null); + } + + const composition_layer = try self.allocator.create(xr.CompositionLayerProjection); + composition_layer.* = .{ + .layer_flags = .{ .blend_texture_source_alpha_bit = true }, + .space = self.scene_space, + .view_count = view_count, + .views = views.projections.ptr, + }; + + self.views = views; + self.layers = try self.allocator.alloc(*xr.CompositionLayerBaseHeader, 1); + self.layers[0] = @ptrCast(*const xr.CompositionLayerBaseHeader, composition_layer); + + while (true) main: { + while (true) { + var event = xr.EventDataBuffer.empty(); + const poll_result = try self.xri.pollEvent(self.instance, &event); + + if (poll_result == .event_unavailable) { + break; + } + + switch (event.type) { + .event_data_session_state_changed => { + const state_event = @ptrCast(*const xr.EventDataSessionStateChanged, &event); + std.debug.print("session state {} → {}\n", .{ self.state, state_event.state }); + self.state = state_event.state; + switch (self.state) { + .ready => { + _ = try self.xri.beginSession(self.session, .{ .primary_view_configuration_type = .primary_stereo }); + }, + .stopping => { + _ = try self.xri.endSession(self.session); + break :main; + }, + .idle, .synchronized, .visible, .focused, .exiting => {}, + .loss_pending => return error.LossPending, + else => return error.InvalidState, + } + }, + .event_data_interaction_profile_changed, .event_data_reference_space_change_pending => { + std.debug.print("skipping event: {}\n", .{event.type}); + }, + + .event_data_instance_loss_pending => break, + .event_data_events_lost => return error.EventsLost, + + else => { + std.debug.print("unknown event: {}\n", .{event.type}); + }, + } + } + + if (!try self.render_frame()) { + std.os.nanosleep(0, 250_000_000); + } + } + } + + pub fn render_frame(self: *Session) !bool { + if (self.views) |views| { + var frame_state = xr.FrameState.empty(); + _ = try self.xri.waitFrame(self.session, &xr.FrameWaitInfo{}, &frame_state); + + _ = try self.xri.beginFrame(self.session, &xr.FrameBeginInfo{}); + + var layer_count: u32 = 0; + + if (frame_state.should_render > 0) { + std.debug.print("renderin'\n", .{}); + if (self.first_render) if (renderdoc.api) |api| api.StartFrameCapture(null, null); + + var located_views = [_]xr.View{xr.View.empty()} ** 2; + var view_state = xr.ViewState.empty(); + + _ = try self.xri.locateViews(self.session, .{ + .view_configuration_type = .primary_stereo, + .display_time = frame_state.predicted_display_time, + .space = self.scene_space, + }, &view_state, 2, &located_views); + + if (!view_state.view_state_flags.contains(.{ .position_valid_bit = true, .orientation_valid_bit = true })) { + return error.InvalidPositionOrOrientation; + } + + for (located_views) |located_view, i| { + const swapchain = views.swapchains[i]; + const acquire_result = try self.xri.acquireSwapchainImage(swapchain.handle, &xr.SwapchainImageAcquireInfo.empty()); + + _ = try self.xri.waitSwapchainImage(swapchain.handle, .{ .timeout = -1 }); + + views.projections[i] = .{ + .pose = located_view.pose, + .fov = located_view.fov, + .sub_image = .{ + .swapchain = swapchain.handle, + .image_rect = swapchain.image_rect, + .image_array_index = 0, + }, + }; + + std.debug.print("submitting view {}: {}\n", .{ i, views.projections[i].sub_image }); + + try self.graphics.render_view(views.projections[i], swapchain, acquire_result.index); + + _ = try self.xri.releaseSwapchainImage(swapchain.handle, &xr.SwapchainImageReleaseInfo.empty()); + } + + _ = try self.xri.endFrame(self.session, .{ + .display_time = frame_state.predicted_display_time, + .environment_blend_mode = .@"opaque", + .layer_count = @intCast(u32, self.layers.len), + .layers = self.layers.ptr, + }); + + if (self.first_render) { + if (renderdoc.api) |api| _ = api.EndFrameCapture(null, null); + self.first_render = false; + } + + return true; + } + + _ = try self.xri.endFrame(self.session, .{ + .display_time = frame_state.predicted_display_time, + .environment_blend_mode = .@"opaque", + .layer_count = 0, + .layers = null, + }); + + return false; + } + + unreachable; + } +}; diff --git a/src/renderdoc.zig b/src/renderdoc.zig new file mode 100644 index 0000000..48cbb16 --- /dev/null +++ b/src/renderdoc.zig @@ -0,0 +1,77 @@ +const std = @import("std"); +const c = @cImport({ + @cInclude("renderdoc_app.h"); + @cInclude("dlfcn.h"); +}); + +fn fix_fields(comptime T: type) type { + const TypeId = std.builtin.TypeId; + var ti = @typeInfo(T); + + const num_fields = ti.Struct.fields.len; + var fields = [_]TypeId.StructField{undefined} ** num_fields; + for (ti.Struct.fields) |field, i| { + var field_ti = @typeInfo(field.field_type); + + switch (field_ti) { + .Optional => |opt| { + fields[i] = TypeId.StructField{ + .name = field.name, + .field_type = opt.child, + .default_value = @as(opt.child, fields[i].default_value), + .is_comptime = field.is_comptime, + .alignment = field.alignment, + }; + }, + else => { + fields[i] = field; + } + } + } + + ti.Struct.fields = fields[0..]; + return @Type(.{ + .Struct = .{ + .layout = .Extern, + .fields = fields, + .decls = [_]TypeId.Declaration{}, + .is_tuple = false, + }, + }); +} + +const API = struct { + StartFrameCapture: fn (c.RENDERDOC_DevicePointer, c.RENDERDOC_WindowHandle) callconv(.C) void, + EndFrameCapture: fn (c.RENDERDOC_DevicePointer, c.RENDERDOC_WindowHandle) callconv(.C) u32, +}; + +pub var api: ?API = null; + +extern fn RENDERDOC_GetAPI(c.RENDERDOC_Version, [*c]?*c_void) callconv(.C) c_int; + +pub fn load_api() !void { + var tmp_api: ?*c.RENDERDOC_API_1_1_2 = null; + if (RENDERDOC_GetAPI(.eRENDERDOC_API_Version_1_1_2, @ptrCast([*c]?*c_void, &tmp_api)) != 1) { + return error.Unknown; + } + + if (tmp_api) |_api| { + var local_api : API = undefined; + var have_all: bool = true; + if (_api.StartFrameCapture) |fp| { + local_api.StartFrameCapture = fp; + } else { + return error.NoStart; + } + + if (_api.EndFrameCapture) |fp| { + local_api.EndFrameCapture = fp; + } else { + return error.NoEnd; + } + + api = local_api; + } else { + return error.ApiEmpty; + } +} diff --git a/src/xrvk.zig b/src/xrvk.zig new file mode 100644 index 0000000..a433b36 --- /dev/null +++ b/src/xrvk.zig @@ -0,0 +1,93 @@ +const rvk = @import("vulkan"); +const rxr = @import("openxr"); + +pub extern fn xrGetInstanceProcAddr(instance: xr.Instance, procname: [*:0]const u8, function: *xr.PfnVoidFunction) xr.Result; +pub extern fn vkGetInstanceProcAddr(instance: vk.Instance, procname: [*:0]const u8) vk.PfnVoidFunction; +pub extern fn vkGetDeviceProcAddr(instance: vk.Device, procname: [*:0]const u8) vk.PfnVoidFunction; + +pub const vk = struct { + usingnamespace rvk; + + pub const c = @cImport(@cInclude("vulkan/vulkan_core.h")); + + pub const getInstanceProcAddr = vkGetInstanceProcAddr; + pub const getDeviceProcAddr = vkGetDeviceProcAddr; + + pub const InstanceDispatch = struct { + vkDestroyInstance: PfnDestroyInstance, + vkGetPhysicalDeviceQueueFamilyProperties: PfnGetPhysicalDeviceQueueFamilyProperties, + vkCreateDebugUtilsMessengerEXT: c.PFN_vkCreateDebugUtilsMessengerEXT, + usingnamespace InstanceWrapper(@This()); + }; + + pub const DeviceDispatch = struct { + vkDestroyDevice: PfnDestroyDevice, + vkGetDeviceQueue: PfnGetDeviceQueue, + vkQueueSubmit: PfnQueueSubmit, + vkCreateCommandPool: PfnCreateCommandPool, + vkDestroyCommandPool: PfnDestroyCommandPool, + vkAllocateCommandBuffers: PfnAllocateCommandBuffers, + vkBeginCommandBuffer: PfnBeginCommandBuffer, + vkResetCommandBuffer: PfnResetCommandBuffer, + vkEndCommandBuffer: PfnEndCommandBuffer, + vkCmdClearColorImage: PfnCmdClearColorImage, + vkCmdPipelineBarrier: PfnCmdPipelineBarrier, + vkCreateFence: PfnCreateFence, + vkWaitForFences: PfnWaitForFences, + vkResetFences: PfnResetFences, + vkDestroyFence: PfnDestroyFence, + usingnamespace DeviceWrapper(@This()); + }; +}; + +pub const xr = struct { + usingnamespace rxr; + + pub const BaseDispatch = struct { + xrCreateInstance: PfnCreateInstance, + usingnamespace BaseWrapper(@This()); + }; + + pub const InstanceDispatch = struct { + xrDestroyInstance: PfnDestroyInstance, + xrGetSystem: PfnGetSystem, + xrGetSystemProperties: PfnGetSystemProperties, + xrCreateSession: PfnCreateSession, + xrDestroySession: PfnDestroySession, + xrBeginSession: PfnBeginSession, + xrEndSession: PfnEndSession, + xrWaitFrame: PfnWaitFrame, + xrBeginFrame: PfnBeginFrame, + xrEndFrame: PfnEndFrame, + xrPollEvent: PfnPollEvent, + xrLocateViews: PfnLocateViews, + xrCreateReferenceSpace: PfnCreateReferenceSpace, + xrCreateVulkanInstanceKHR: PfnCreateVulkanInstanceKHR, + xrGetVulkanGraphicsDevice2KHR: PfnGetVulkanGraphicsDevice2KHR, + xrGetVulkanGraphicsRequirements2KHR: PfnGetVulkanGraphicsRequirements2KHR, + xrCreateVulkanDeviceKHR: PfnCreateVulkanDeviceKHR, + xrEnumerateViewConfigurationViews: PfnEnumerateViewConfigurationViews, + xrCreateSwapchain: PfnCreateSwapchain, + xrDestroySwapchain: PfnDestroySwapchain, + xrEnumerateSwapchainImages: PfnEnumerateSwapchainImages, + xrAcquireSwapchainImage: PfnAcquireSwapchainImage, + xrWaitSwapchainImage: PfnWaitSwapchainImage, + xrReleaseSwapchainImage: PfnReleaseSwapchainImage, + usingnamespace InstanceWrapper(@This()); + }; + + pub fn getProcAddr(instance: Instance, name: [*:0]const u8) !PfnVoidFunction { + var out: PfnVoidFunction = undefined; + const result = xrGetInstanceProcAddr(instance, name, &out); + return switch (result) { + .success => out, + .error_handle_invalid => error.HandleInvalid, + .error_instance_lost => error.InstanceLost, + .error_runtime_failure => error.RuntimeFailure, + .error_out_of_memory => error.OutOfMemory, + .error_function_unsupported => error.FunctionUnsupported, + .error_validation_failure => error.ValidationFailure, + else => error.Unknown, + }; + } +}; |
