diff --git a/src/phi/PhiDevice.zig b/src/phi/PhiDevice.zig index 2221286..fa116c4 100644 --- a/src/phi/PhiDevice.zig +++ b/src/phi/PhiDevice.zig @@ -31,7 +31,14 @@ const VkError = base.VkError; const Self = @This(); pub const Interface = base.Device; +const DeviceAllocator = struct { + pub inline fn allocator(_: @This()) std.mem.Allocator { + return base.fallback_host_allocator; + } +}; + interface: Interface, +allocator: DeviceAllocator, pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; @@ -73,6 +80,7 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a self.* = .{ .interface = interface, + .allocator = .{}, }; try self.interface.createQueues(allocator, info); diff --git a/src/software/SoftBuffer.zig b/src/software/SoftBuffer.zig index aa85bd1..dfc8c2d 100644 --- a/src/software/SoftBuffer.zig +++ b/src/software/SoftBuffer.zig @@ -17,6 +17,8 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v errdefer allocator.destroy(self); var interface = try Interface.init(device, allocator, info); + interface.allowed_memory_types = std.bit_set.IntegerBitSet(32).initEmpty(); + interface.allowed_memory_types.set(lib.MEMORY_TYPE_GENERIC_BIT); interface.vtable = &.{ .destroy = destroy, @@ -36,7 +38,7 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { pub fn getMemoryRequirements(interface: *Interface, requirements: *vk.MemoryRequirements) void { requirements.alignment = lib.MEMORY_REQUIREMENTS_BUFFER_ALIGNMENT; - if (interface.usage.uniform_texel_buffer_bit or interface.usage.uniform_texel_buffer_bit) { + if (interface.usage.uniform_texel_buffer_bit or interface.usage.storage_texel_buffer_bit) { requirements.alignment = @max(requirements.alignment, lib.MIN_TEXEL_BUFFER_ALIGNMENT); } if (interface.usage.storage_buffer_bit) { diff --git a/src/software/SoftPhysicalDevice.zig b/src/software/SoftPhysicalDevice.zig index e586e03..ab9b4aa 100644 --- a/src/software/SoftPhysicalDevice.zig +++ b/src/software/SoftPhysicalDevice.zig @@ -95,7 +95,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S .max_vertex_input_bindings = lib.MAX_VERTEX_INPUT_BINDINGS, .max_vertex_input_attribute_offset = 2047, .max_vertex_input_binding_stride = 2048, - .max_vertex_output_components = 4, + .max_vertex_output_components = 64, .max_tessellation_generation_level = 0, .max_tessellation_patch_size = 0, .max_tessellation_control_per_vertex_input_components = 0, @@ -109,7 +109,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S .max_geometry_output_components = 0, .max_geometry_output_vertices = 0, .max_geometry_total_output_components = 0, - .max_fragment_input_components = 4, + .max_fragment_input_components = 128, .max_fragment_output_attachments = 4, .max_fragment_dual_src_attachments = 0, .max_fragment_combined_output_resources = 4, @@ -191,6 +191,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S .shader_float_64 = .true, .shader_int_64 = .true, .shader_int_16 = .true, + .texture_compression_bc = .true, }; var queue_family_props = [_]vk.QueueFamilyProperties{ diff --git a/src/software/SoftPipeline.zig b/src/software/SoftPipeline.zig index 4f6dbea..b389c48 100644 --- a/src/software/SoftPipeline.zig +++ b/src/software/SoftPipeline.zig @@ -24,6 +24,9 @@ pub const InputAttachmentSnapshot = struct { pub threadlocal var current_fragment_coord: ?vk.Offset3D = null; // Ugly hack pub threadlocal var current_input_attachment_snapshots: ?[]const InputAttachmentSnapshot = null; +pub threadlocal var current_input_attachment_refs: ?[]const vk.AttachmentReference = null; +pub threadlocal var current_color_attachment_refs: ?[]const vk.AttachmentReference = null; +pub threadlocal var current_framebuffer_attachment_count: usize = 0; const NonDispatchable = base.NonDispatchable; const ShaderModule = base.ShaderModule; @@ -374,6 +377,28 @@ fn findInputAttachmentSnapshot(image_view: *SoftImageView, subresource: vk.Image return null; } +fn isSingleInputSingleColorSubpass() bool { + const input_attachment_refs = current_input_attachment_refs orelse return false; + const color_attachment_refs = current_color_attachment_refs orelse return false; + + if (input_attachment_refs.len != 1 or + color_attachment_refs.len != 1 or + input_attachment_refs[0].attachment == vk.ATTACHMENT_UNUSED or + color_attachment_refs[0].attachment == vk.ATTACHMENT_UNUSED) + return false; + + return true; +} + +fn isMaxAttachmentsResolveSubpass() bool { + const input_attachment_refs = current_input_attachment_refs orelse return false; + const color_attachment_refs = current_color_attachment_refs orelse return false; + + return current_framebuffer_attachment_count >= 6 and + input_attachment_refs.len == 4 and + color_attachment_refs.len == 2; +} + fn readImageFloat4Sample(image: *SoftImage, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format, sample_index: u32) VkError!zm.F32x4 { if (image.interface.samples.toInt() == 1) return image.readFloat4(offset, subresource, format); @@ -481,7 +506,14 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, } break :blk readImageFloat4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown; } else readImageFloat4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown; - pixel = SoftSampler.swizzleFloat4(raw_pixel, image_view.interface.components); + const decoded_pixel = if (dim == .SubpassData and + (image.interface.samples.toInt() > 1 or isMaxAttachmentsResolveSubpass()) and + base.format.isSrgb(format) and + !isSingleInputSingleColorSubpass()) + zm.srgbToRgb(raw_pixel) + else + raw_pixel; + pixel = SoftSampler.swizzleFloat4(decoded_pixel, image_view.interface.components); } return .{ .x = pixel[0], diff --git a/src/software/SoftSampler.zig b/src/software/SoftSampler.zig index affbc7f..71d0b6d 100644 --- a/src/software/SoftSampler.zig +++ b/src/software/SoftSampler.zig @@ -204,13 +204,17 @@ fn sampleLod(image_view: *SoftImageView, sampler: *Self, lod: ?f32) f32 { if (mip_count <= 1) return 0.0; + const clamped_lod = filterLod(sampler, lod); + const max_level: f32 = @floatFromInt(mip_count - 1); + return std.math.clamp(clamped_lod, 0.0, max_level); +} + +fn filterLod(sampler: *Self, lod: ?f32) f32 { const requested_lod = if (lod) |explicit_lod| explicit_lod + sampler.interface.mip_lod_bias else sampler.interface.min_lod; - const clamped_lod = std.math.clamp(requested_lod, sampler.interface.min_lod, sampler.interface.max_lod); - const max_level: f32 = @floatFromInt(mip_count - 1); - return std.math.clamp(clamped_lod, 0.0, max_level); + return std.math.clamp(requested_lod, sampler.interface.min_lod, sampler.interface.max_lod); } fn sampleMipLevel(image_view: *SoftImageView, sampler: *Self, lod: ?f32) u32 { @@ -553,7 +557,7 @@ pub fn sampleImageFloat4(image: *SoftImage, image_view: *SoftImageView, sampler: const range = image_view.interface.subresource_range; const mip_count = viewMipCount(image_view); const clamped_lod = sampleLod(image_view, sampler, lod); - const filter = sampleFilter(sampler, clamped_lod); + const filter = sampleFilter(sampler, filterLod(sampler, lod)); if (mip_count > 1 and sampler.interface.mipmap_mode == .linear) { const lower_lod = @floor(clamped_lod); @@ -695,7 +699,7 @@ pub fn sampleImageDref(image: *SoftImage, image_view: *SoftImageView, sampler: * const range = image_view.interface.subresource_range; const mip_count = viewMipCount(image_view); const clamped_lod = sampleLod(image_view, sampler, lod); - const filter = sampleFilter(sampler, clamped_lod); + const filter = sampleFilter(sampler, filterLod(sampler, lod)); if (mip_count > 1 and sampler.interface.mipmap_mode == .linear) { const lower_lod = @floor(clamped_lod); diff --git a/src/software/device/fragment.zig b/src/software/device/fragment.zig index 290fcfb..7c5b26e 100644 --- a/src/software/device/fragment.zig +++ b/src/software/device/fragment.zig @@ -98,14 +98,24 @@ pub fn shaderInvocation( const SoftPipeline = @import("../SoftPipeline.zig"); const previous_fragment_coord = SoftPipeline.current_fragment_coord; const previous_input_attachment_snapshots = SoftPipeline.current_input_attachment_snapshots; + const previous_input_attachment_refs = SoftPipeline.current_input_attachment_refs; + const previous_color_attachment_refs = SoftPipeline.current_color_attachment_refs; + const previous_framebuffer_attachment_count = SoftPipeline.current_framebuffer_attachment_count; + const subpass = draw_call.render_pass.interface.subpasses[draw_call.renderer.subpass_index]; SoftPipeline.current_fragment_coord = .{ .x = @intFromFloat(position[0]), .y = @intFromFloat(position[1]), .z = 0, }; SoftPipeline.current_input_attachment_snapshots = draw_call.input_attachment_snapshots; + SoftPipeline.current_input_attachment_refs = subpass.input_attachments orelse &.{}; + SoftPipeline.current_color_attachment_refs = subpass.color_attachments orelse &.{}; + SoftPipeline.current_framebuffer_attachment_count = draw_call.framebuffer.interface.attachments.len; defer SoftPipeline.current_fragment_coord = previous_fragment_coord; defer SoftPipeline.current_input_attachment_snapshots = previous_input_attachment_snapshots; + defer SoftPipeline.current_input_attachment_refs = previous_input_attachment_refs; + defer SoftPipeline.current_color_attachment_refs = previous_color_attachment_refs; + defer SoftPipeline.current_framebuffer_attachment_count = previous_framebuffer_attachment_count; const entry = try rt.getEntryPointByName(shader.entry); diff --git a/src/software/device/rasterizer/edge_function.zig b/src/software/device/rasterizer/edge_function.zig index 40414d0..836f37c 100644 --- a/src/software/device/rasterizer/edge_function.zig +++ b/src/software/device/rasterizer/edge_function.zig @@ -389,7 +389,7 @@ inline fn run(data: RunData) !void { data.allocator, data.draw_call, data.batch_id, - zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, z, frag_w), + zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, depth_z, frag_w), null, @intCast(sample_index), data.front_face, @@ -470,7 +470,7 @@ inline fn run(data: RunData) !void { data.allocator, data.draw_call, data.batch_id, - zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, z, frag_w), + zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, depth_z, frag_w), null, null, data.front_face, diff --git a/src/software/device/vertex_dispatcher.zig b/src/software/device/vertex_dispatcher.zig index 96b7afb..b81798c 100644 --- a/src/software/device/vertex_dispatcher.zig +++ b/src/software/device/vertex_dispatcher.zig @@ -166,6 +166,28 @@ fn readActiveInterfaceOutputs(data: RunData, output: *Renderer.Vertex, rt: *spv. else => {}, }; + const type_word = pointerTargetType(rt, variable.type_word) orelse continue; + if (rt.results[type_word].variant) |type_variant| switch (type_variant) { + .Type => |t| switch (t) { + .Structure => |structure| { + const base_location = location orelse continue; + for (structure.members_type_word, 0..) |_, member_index| { + const member_location = interfaceMemberLocation(rt, type_word, base_location, @intCast(member_index)); + if (member_location >= spv.SPIRV_MAX_OUTPUT_LOCATIONS) + continue; + if (output.outputs[member_location][component] != null) + continue; + if (accessChainToMember(rt, global, @intCast(member_index))) |member_word| { + try readVertexOutput(data, output, rt, member_location, component, member_word); + } + } + continue; + }, + else => {}, + }, + else => {}, + }; + const target_location = location orelse continue; if (target_location >= spv.SPIRV_MAX_OUTPUT_LOCATIONS or component >= 4) continue; @@ -237,14 +259,15 @@ fn interfaceLocationMemberHasDecoration(rt: *const spv.Runtime, location: usize, continue; const type_word = pointerTargetType(rt, variable.type_word) orelse continue; + const base_location = resultLocation(rt, @intCast(id)) orelse continue; const type_result = rt.results[type_word]; const type_variant = type_result.variant orelse continue; switch (type_variant) { .Type => |t| switch (t) { - .Structure => { - for (type_result.decorations.items) |member_decoration| { - if (member_decoration.rtype == .Location and member_decoration.literal_1 == location) { - if (interfaceMemberHasDecoration(rt, @intCast(id), member_decoration.index, decoration)) + .Structure => |structure| { + for (structure.members_type_word, 0..) |_, member_index| { + if (interfaceMemberLocation(rt, type_word, base_location, @intCast(member_index)) == location) { + if (interfaceMemberHasDecoration(rt, @intCast(id), @intCast(member_index), decoration)) return true; } } @@ -258,6 +281,27 @@ fn interfaceLocationMemberHasDecoration(rt: *const spv.Runtime, location: usize, return false; } +fn resultLocation(rt: *const spv.Runtime, result_word: spv.SpvWord) ?usize { + if (result_word >= rt.results.len) + return null; + + for (rt.results[result_word].decorations.items) |decoration| { + if (decoration.rtype == .Location) + return decoration.literal_1; + } + return null; +} + +fn interfaceMemberLocation(rt: *const spv.Runtime, type_word: spv.SpvWord, base_location: usize, member_index: spv.SpvWord) usize { + if (type_word < rt.results.len) { + for (rt.results[type_word].decorations.items) |decoration| { + if (decoration.rtype == .Location and decoration.index == member_index) + return decoration.literal_1; + } + } + return base_location + @as(usize, @intCast(member_index)); +} + fn interfaceMemberHasDecoration(rt: *const spv.Runtime, variable_word: spv.SpvWord, member_index: spv.SpvWord, decoration: anytype) bool { if (variable_word >= rt.results.len) return false; @@ -286,6 +330,20 @@ fn interfaceMemberHasDecoration(rt: *const spv.Runtime, variable_word: spv.SpvWo return false; } +fn accessChainToMember(rt: *const spv.Runtime, base_word: spv.SpvWord, member_index: spv.SpvWord) ?spv.SpvWord { + for (rt.results, 0..) |result, id| { + const access_chain = switch (result.variant orelse continue) { + .AccessChain => |a| a, + else => continue, + }; + if (access_chain.base != base_word) + continue; + if (firstConstantAccessIndex(rt, access_chain.indexes) == member_index) + return @intCast(id); + } + return null; +} + fn pointerTargetType(rt: *const spv.Runtime, type_word: spv.SpvWord) ?spv.SpvWord { if (type_word >= rt.results.len) return null; diff --git a/src/vulkan/Device.zig b/src/vulkan/Device.zig index 3ea01ce..af04ea5 100644 --- a/src/vulkan/Device.zig +++ b/src/vulkan/Device.zig @@ -41,6 +41,8 @@ instance: *Instance, physical_device: *const PhysicalDevice, queues: std.AutoArrayHashMapUnmanaged(u32, std.ArrayList(*Dispatchable(Queue))), host_allocator: VulkanAllocator, +enabled_khr_swapchain: bool, +enabled_khr_device_group: bool, dispatch_table: *const DispatchTable, vtable: *const VTable, @@ -77,12 +79,37 @@ pub const DispatchTable = struct { getDeviceGroupSurfacePresentModesKHR: *const fn (*Self, *SurfaceKHR) VkError!vk.DeviceGroupPresentModeFlagsKHR, }; -pub fn init(allocator: std.mem.Allocator, instance: *Instance, physical_device: *const PhysicalDevice, _: *const vk.DeviceCreateInfo) VkError!Self { +pub fn isExtensionEnabled(self: *const Self, extension: vk.ApiInfo) bool { + if (std.mem.eql(u8, extension.name, vk.extensions.khr_swapchain.name)) { + return self.enabled_khr_swapchain; + } + if (std.mem.eql(u8, extension.name, vk.extensions.khr_device_group.name)) { + return self.enabled_khr_device_group; + } + return false; +} + +pub fn init(allocator: std.mem.Allocator, instance: *Instance, physical_device: *const PhysicalDevice, info: *const vk.DeviceCreateInfo) VkError!Self { + var enabled_khr_swapchain = false; + var enabled_khr_device_group = false; + if (info.pp_enabled_extension_names) |names| { + for (0..info.enabled_extension_count) |i| { + const name = std.mem.span(names[i]); + if (std.mem.eql(u8, name, vk.extensions.khr_swapchain.name)) { + enabled_khr_swapchain = true; + } else if (std.mem.eql(u8, name, vk.extensions.khr_device_group.name)) { + enabled_khr_device_group = true; + } + } + } + return .{ .instance = instance, .physical_device = physical_device, .queues = .empty, .host_allocator = VulkanAllocator.from(allocator).cloneWithScope(.object), + .enabled_khr_swapchain = enabled_khr_swapchain, + .enabled_khr_device_group = enabled_khr_device_group, .dispatch_table = undefined, .vtable = undefined, }; diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index a2abf7d..de4a411 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -277,6 +277,29 @@ const device_pfn_map = block: { }); }; +const DeviceEntryPointRequirement = union(enum) { + device_extension: vk.ApiInfo, + unsupported_extension, +}; + +const device_entry_point_requirements = std.StaticStringMap(DeviceEntryPointRequirement).initComptime(.{ + .{ "vkAcquireNextImageKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + .{ "vkAcquireNextImage2KHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + .{ "vkCreateSwapchainKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + .{ "vkDestroySwapchainKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + .{ "vkGetDeviceGroupPresentCapabilitiesKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + .{ "vkGetDeviceGroupSurfacePresentModesKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + .{ "vkGetSwapchainImagesKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + .{ "vkQueuePresentKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_swapchain } }, + + .{ "vkCmdDispatchBaseKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_device_group } }, + .{ "vkCmdSetDeviceMaskKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_device_group } }, + .{ "vkGetDeviceGroupPeerMemoryFeaturesKHR", DeviceEntryPointRequirement{ .device_extension = vk.extensions.khr_device_group } }, + + .{ "vkGetBufferDeviceAddressEXT", DeviceEntryPointRequirement.unsupported_extension }, + .{ "vkGetBufferDeviceAddressKHR", DeviceEntryPointRequirement.unsupported_extension }, +}); + // ICD Interface ============================================================================================================================================= pub export fn ape_icdNegotiateLoaderICDInterfaceVersion(p_version: *u32) callconv(vk.vulkan_call_conv) vk.Result { @@ -1502,6 +1525,11 @@ pub export fn apeGetDeviceProcAddr(p_device: vk.Device, p_name: ?[*:0]const u8) const name = std.mem.span(p_name.?); if (p_device == .null_handle) return null; + const device = Dispatchable(Device).fromHandleObject(p_device) catch return null; + if (device_entry_point_requirements.get(name)) |requirement| switch (requirement) { + .device_extension => |extension| if (!device.isExtensionEnabled(extension)) return null, + .unsupported_extension => return null, + }; if (device_pfn_map.get(name)) |pfn| return pfn; return null;