From fe391bc6787585845daadedb3c9ee3f06592868e Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Wed, 27 May 2026 23:31:05 +0200 Subject: [PATCH] adding proper subpass management and image resolve --- README.md | 26 +++--- build.zig | 18 +--- build.zig.zon | 5 -- src/soft/SoftCommandBuffer.zig | 99 ++++++++++++++++------ src/soft/SoftPhysicalDevice.zig | 102 ++++++++++++++++++----- src/soft/c_includes.h | 1 - src/soft/device/Renderer.zig | 5 +- src/soft/device/blitter.zig | 79 ++++++++++++++---- src/soft/device/rasterizer.zig | 2 +- src/soft/device/rasterizer/bresenham.zig | 2 +- src/soft/lib.zig | 1 + src/vulkan/CommandBuffer.zig | 12 +++ src/vulkan/lib_vulkan.zig | 21 +---- 13 files changed, 257 insertions(+), 116 deletions(-) delete mode 100644 src/soft/c_includes.h diff --git a/README.md b/README.md index 79478e7..1dbd8f1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A driver as slow as Lance Stroll. -Here lies the source code of a rather calamitous attempt at the Vulkan specification, shaped into an Installable Client Driver for a software-based renderer, all written in Zig. +Here lies the source code of a rather calamitous attempt at the Vulkan specification, shaped into an Installable Client Driver all written in Zig. It was forged for my own learning and amusement alone. Pray, do not wield it in any earnest project, lest thy hopes and frame rates both find themselves entombed. @@ -14,7 +14,12 @@ To understand Vulkan - not as a humble API mere mortals call upon, but as a laby It does not seek to produce a performant or production-worthy driver. \ *The gods are merciful, but not that merciful.* -## Build +## Soft [software implementation] + +Soft be a software implementation of the Vulkan specification, abiding within this driver’s own codebase.\ +It maketh use of a bespoke SPIR-V interpreter and renderer, by whose workings its labours are carried forth. + +### Build If thou art truly determined: ``` @@ -26,8 +31,7 @@ The precise ritual varies by system - consult the tomes of your operating system Use at your own risk. If thy machine shudders, weeps, or attempts to flee - know that it was warned. -## Vulkan 1.0 specification - +#### Vulkan 1.0 specification
The present standing of thy Vulkan 1.0 specification's implementation @@ -55,7 +59,7 @@ vkCmdBindVertexBuffers | ✅ Implemented vkCmdBlitImage | ✅ Implemented vkCmdClearAttachments | ✅ Implemented vkCmdClearColorImage | ✅ Implemented -vkCmdClearDepthStencilImage | ⚙️ WIP +vkCmdClearDepthStencilImage | ✅ Implemented vkCmdCopyBuffer | ✅ Implemented vkCmdCopyBufferToImage | ✅ Implemented vkCmdCopyImage | ✅ Implemented @@ -71,12 +75,12 @@ vkCmdEndQuery | ⚙️ WIP vkCmdEndRenderPass | ✅ Implemented vkCmdExecuteCommands | ✅ Implemented vkCmdFillBuffer | ✅ Implemented -vkCmdNextSubpass | ⚙️ WIP +vkCmdNextSubpass | ✅ Implemented vkCmdPipelineBarrier | ✅ Implemented vkCmdPushConstants | ✅ Implemented vkCmdResetEvent | ✅ Implemented vkCmdResetQueryPool | ⚙️ WIP -vkCmdResolveImage | ⚙️ WIP +vkCmdResolveImage | ✅ Implemented vkCmdSetBlendConstants | ⚙️ WIP vkCmdSetDepthBias | ⚙️ WIP vkCmdSetDepthBounds | ⚙️ WIP @@ -108,7 +112,7 @@ vkCreatePipelineCache | ⚙️ WIP vkCreatePipelineLayout | ✅ Implemented vkCreateQueryPool | ⚙️ WIP vkCreateRenderPass | ✅ Implemented -vkCreateSampler | ⚙️ WIP +vkCreateSampler | ✅ Implemented vkCreateSemaphore | ⚙️ WIP vkCreateShaderModule | ✅ Implemented vkCreateSwapchainKHR | ✅ Implemented @@ -156,7 +160,7 @@ vkGetDeviceQueue | ✅ Implemented vkGetEventStatus | ✅ Implemented vkGetFenceStatus | ✅ Implemented vkGetImageMemoryRequirements | ✅ Implemented -vkGetImageSparseMemoryRequirements | ⚙️ WIP +vkGetImageSparseMemoryRequirements | ❎ Unsupported vkGetImageSubresourceLayout | ✅ Implemented vkGetInstanceProcAddr | ✅ Implemented vkGetPhysicalDeviceFeatures | ✅ Implemented @@ -165,7 +169,7 @@ vkGetPhysicalDeviceImageFormatProperties | ✅ Implemented vkGetPhysicalDeviceMemoryProperties | ✅ Implemented vkGetPhysicalDeviceProperties | ✅ Implemented vkGetPhysicalDeviceQueueFamilyProperties | ✅ Implemented -vkGetPhysicalDeviceSparseImageFormatProperties | ⚙️ WIP +vkGetPhysicalDeviceSparseImageFormatProperties | ❎ Unsupported vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ✅ Implemented vkGetPhysicalDeviceSurfaceFormatsKHR | ✅ Implemented vkGetPhysicalDeviceSurfacePresentModesKHR | ✅ Implemented @@ -181,7 +185,7 @@ vkGetSwapchainImagesKHR | ✅ Implemented vkInvalidateMappedMemoryRanges | ✅ Implemented vkMapMemory | ✅ Implemented vkMergePipelineCaches | ⚙️ WIP -vkQueueBindSparse | ⚙️ WIP +vkQueueBindSparse | ❎ Unsupported vkQueuePresentKHR | ✅ Implemented vkQueueSubmit | ✅ Implemented vkQueueWaitIdle | ✅ Implemented diff --git a/build.zig b/build.zig index f9d2f1f..9f53a93 100644 --- a/build.zig +++ b/build.zig @@ -176,14 +176,11 @@ pub fn build(b: *std.Build) !void { fn customSoft( b: *std.Build, lib: *Step.Compile, - target: std.Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode, + _: std.Build.ResolvedTarget, + _: std.builtin.OptimizeMode, options: *Step.Options, use_llvm: bool, ) !void { - const cpuinfo = b.lazyDependency("cpuinfo", .{}) orelse return error.UnresolvedDependency; - lib.root_module.linkLibrary(cpuinfo.artifact("cpuinfo")); - const spv = b.lazyDependency("SPIRV_Interpreter", .{ .@"no-example" = true, .@"no-test" = true, @@ -191,17 +188,6 @@ fn customSoft( }) orelse return error.UnresolvedDependency; lib.root_module.addImport("spv", spv.module("spv")); - const c_includes = b.addTranslateC(.{ - .root_source_file = b.path("src/soft/c_includes.h"), - .target = target, - .optimize = optimize, - .link_libc = false, - }); - - c_includes.addIncludePath(cpuinfo.path("include")); - - lib.root_module.addImport("soft_c", c_includes.createModule()); - const single_threaded_option = b.option(bool, "single-threaded", "Single threaded runtime mode") orelse false; const debug_allocator_option = b.option(bool, "debug-allocator", "Debug device allocator") orelse false; const shaders_simd_option = b.option(bool, "shader-simd", "Shaders SIMD acceleration") orelse true; diff --git a/build.zig.zon b/build.zig.zon index bef8b74..83005a2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -25,11 +25,6 @@ .url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7", .hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3", }, - .cpuinfo = .{ - .url = "git+https://github.com/Kbz-8/cpuinfo.git#c9bea4f6c166a495ee0ce117821f9627d4aed118", - .hash = "cpuinfo-0.0.1-RLgIQYrTMgGqfQMOd1nAa2EuglXOh5gR9bNzwMzQTemt", - .lazy = true, - }, .SPIRV_Interpreter = .{ .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#1ffa20d07c59da34300dd97e16b72a7320f5af6c", .hash = "SPIRV_Interpreter-0.0.1-ajmpn25qBQByByo4yetC7ZS63JxLvuwXMknTMWnbaYAb", diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig index 24b665f..3bac8ea 100644 --- a/src/soft/SoftCommandBuffer.zig +++ b/src/soft/SoftCommandBuffer.zig @@ -68,10 +68,12 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v .endRenderPass = endRenderPass, .executeCommands = executeCommands, .fillBuffer = fillBuffer, + .nextSubpass = nextSubpass, .pipelineBarrier = pipelineBarrier, .pushConstants = pushConstants, .reset = reset, .resetEvent = resetEvent, + .resolveImage = resolveImage, .setEvent = setEvent, .setScissor = setScissor, .setViewport = setViewport, @@ -144,6 +146,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra const impl: *Impl = @ptrCast(@alignCast(context)); device.renderer.render_pass = impl.render_pass; device.renderer.framebuffer = impl.framebuffer; + device.renderer.subpass_index = 0; for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| { const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image)); @@ -378,20 +381,44 @@ pub fn clearAttachment(interface: *Interface, attachment: vk.ClearAttachment, re pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void { const impl: *Impl = @ptrCast(@alignCast(context)); - if (device.renderer.framebuffer) |framebuffer| { - const image_view = framebuffer.interface.attachments[impl.attachment.color_attachment]; - const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.image)); - const clear_format = try image.getClearFormat(); + const framebuffer = device.renderer.framebuffer orelse return; + const render_pass = device.renderer.render_pass orelse return; + const subpass = render_pass.interface.subpasses[device.renderer.subpass_index]; - try blitter.clear( - impl.attachment.clear_value, - clear_format, - image, - image_view.format, - image_view.subresource_range, - null, - ); - } + const image_view = blk: { + if (impl.attachment.aspect_mask.toInt() == (vk.ImageAspectFlags{ .color_bit = true }).toInt()) { + const fb_attachment_index = (subpass.color_attachments orelse return)[impl.attachment.color_attachment].attachment; + + if (fb_attachment_index != vk.ATTACHMENT_UNUSED) + break :blk framebuffer.interface.attachments[impl.attachment.color_attachment]; + } else if (impl.attachment.aspect_mask.depth_bit or impl.attachment.aspect_mask.stencil_bit) { + if (render_pass.interface.subpasses[device.renderer.subpass_index].depth_stencil_attachments) |desc| { + if (desc.attachment != vk.ATTACHMENT_UNUSED) + break :blk framebuffer.interface.attachments[desc.attachment]; + } + } + return; + }; + + const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.image)); + const clear_format = try image.getClearFormat(); + + const range: vk.ImageSubresourceRange = .{ + .aspect_mask = impl.attachment.aspect_mask, + .base_mip_level = image_view.subresource_range.base_mip_level, + .level_count = image_view.subresource_range.level_count, + .base_array_layer = impl.rect.base_array_layer + image_view.subresource_range.base_array_layer, + .layer_count = impl.rect.layer_count, + }; + + try blitter.clear( + impl.attachment.clear_value, + clear_format, + image, + image_view.format, + range, + impl.rect.rect, + ); } }; @@ -821,27 +848,26 @@ pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.Device self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; } -pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags, dst_stage: vk.PipelineStageFlags, dependency: vk.DependencyFlags, memory_barriers: []const vk.MemoryBarrier, buffer_barriers: []const vk.BufferMemoryBarrier, image_barriers: []const vk.ImageMemoryBarrier) VkError!void { +pub fn nextSubpass(interface: *Interface, _: vk.SubpassContents) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const allocator = self.command_allocator.allocator(); const CommandImpl = struct { const Impl = @This(); - pub fn execute(_: *anyopaque, _: *ExecutionDevice) VkError!void {} + pub fn execute(_: *anyopaque, device: *ExecutionDevice) VkError!void { + device.renderer.subpass_index += 1; + } }; const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(cmd); cmd.* = .{}; self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; +} - _ = src_stage; - _ = dst_stage; - _ = dependency; - _ = memory_barriers; - _ = buffer_barriers; - _ = image_barriers; +pub fn pipelineBarrier(_: *Interface, _: vk.PipelineStageFlags, _: vk.PipelineStageFlags, _: vk.DependencyFlags, _: []const vk.MemoryBarrier, _: []const vk.BufferMemoryBarrier, _: []const vk.ImageMemoryBarrier) VkError!void { + // No-op } pub fn pushConstants(interface: *Interface, stages: vk.ShaderStageFlags, offset: u32, blob: []const u8) VkError!void { @@ -880,12 +906,10 @@ pub fn pushConstants(interface: *Interface, stages: vk.ShaderStageFlags, offset: self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; } -pub fn resetEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void { +pub fn resetEvent(interface: *Interface, event: *base.Event, _: vk.PipelineStageFlags) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const allocator = self.command_allocator.allocator(); - _ = stage; - const CommandImpl = struct { const Impl = @This(); @@ -905,6 +929,33 @@ pub fn resetEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineS self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; } +pub fn resolveImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst: *base.Image, _: vk.ImageLayout, region: vk.ImageResolve) VkError!void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const allocator = self.command_allocator.allocator(); + + const CommandImpl = struct { + const Impl = @This(); + + src: *const SoftImage, + dst: *SoftImage, + region: vk.ImageResolve, + + pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void { + const impl: *Impl = @ptrCast(@alignCast(context)); + try blitter.resolve(impl.src, impl.dst, impl.region); + } + }; + + const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory; + errdefer allocator.destroy(cmd); + cmd.* = .{ + .src = @alignCast(@fieldParentPtr("interface", src)), + .dst = @alignCast(@fieldParentPtr("interface", dst)), + .region = region, + }; + self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; +} + pub fn setEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const allocator = self.command_allocator.allocator(); diff --git a/src/soft/SoftPhysicalDevice.zig b/src/soft/SoftPhysicalDevice.zig index 3f1545d..89c64c3 100644 --- a/src/soft/SoftPhysicalDevice.zig +++ b/src/soft/SoftPhysicalDevice.zig @@ -1,8 +1,8 @@ const std = @import("std"); +const builtin = @import("builtin"); const vk = @import("vulkan"); const base = @import("base"); const lib = @import("lib.zig"); -const cpuinfo = lib.c; const SoftDevice = @import("SoftDevice.zig"); @@ -189,9 +189,6 @@ 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_etc2 = .false, - .texture_compression_bc = .false, - .texture_compression_astc_ldr = .false, }; var queue_family_props = [_]vk.QueueFamilyProperties{ @@ -201,30 +198,47 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S .timestamp_valid_bits = 0, .min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 }, }, - .{ - .queue_flags = .{ .graphics_bit = true }, - .queue_count = 1, - .timestamp_valid_bits = 0, - .min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 }, - }, - .{ - .queue_flags = .{ .transfer_bit = true }, - .queue_count = 1, - .timestamp_valid_bits = 0, - .min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 }, - }, - // TODO: maybe add a compute specialized queue }; interface.queue_family_props.appendSlice(allocator, queue_family_props[0..]) catch return VkError.OutOfHostMemory; if (device_name[0] == 0) { const name = blk: { - if (cpuinfo.cpuinfo_initialize()) { - const package = cpuinfo.cpuinfo_get_package(0).*; - const non_sentinel_name = package.name[0..(std.mem.len(@as([*:0]const u8, @ptrCast(&package.name))))]; - break :blk std.fmt.allocPrint(command_allocator, "{s} ({d} threads)", .{ non_sentinel_name, package.processor_count }) catch return VkError.OutOfHostMemory; + + // If arch is x86 we try to get precise CPU name through CPUID + // and fallback to vendor name if not available + if (comptime builtin.cpu.arch.isX86()) { + const max_extended_leaf = cpuid(0x80000000, 0).eax; + + if (max_extended_leaf >= 0x80000004) { + var brand: [49]u8 = @splat(0); + + for (0..3) |i| { + const regs = cpuid(0x80000002 + @as(u32, @intCast(i)), 0); + const offset = i * 16; + + writeU32Le(brand[0..], offset + 0, regs.eax); + writeU32Le(brand[0..], offset + 4, regs.ebx); + writeU32Le(brand[0..], offset + 8, regs.ecx); + writeU32Le(brand[0..], offset + 12, regs.edx); + } + + const brand_str = std.mem.trim(u8, brand[0..48], " \x00"); + break :blk command_allocator.dupe(u8, brand_str) catch return VkError.OutOfHostMemory; + } else { + var vendor: [13]u8 = @splat(0); + + const leaf0 = cpuid(0, 0); + + writeU32Le(vendor[0..], 0, leaf0.ebx); + writeU32Le(vendor[0..], 4, leaf0.edx); + writeU32Le(vendor[0..], 8, leaf0.ecx); + + const vendor_str = std.mem.trim(u8, vendor[0..12], " \x00"); + break :blk command_allocator.dupe(u8, vendor_str) catch return VkError.OutOfHostMemory; + } } - break :blk command_allocator.dupe(u8, "Unkown") catch return VkError.OutOfHostMemory; + + break :blk command_allocator.dupe(u8, lib.PHYSICAL_DEVICE_DEFAULT_NAME) catch return VkError.OutOfHostMemory; }; defer command_allocator.free(name); @@ -857,3 +871,47 @@ fn checkFormatUsage(usage: vk.ImageUsageFlags, features: vk.FormatFeatureFlags) pub fn getSurfaceSupportKHR(_: *Interface, _: u32, _: *SurfaceKHR) VkError!bool { return true; } + +const CpuidRegs = packed struct { + eax: u32, + ebx: u32, + ecx: u32, + edx: u32, +}; + +fn cpuid(leaf_id: u32, subleaf_id: u32) CpuidRegs { + comptime { + switch (builtin.cpu.arch) { + .x86, .x86_64 => {}, + else => @compileError("cpuid is only available on x86/x86_64"), + } + } + + var eax: u32 = undefined; + var ebx: u32 = undefined; + var ecx: u32 = undefined; + var edx: u32 = undefined; + + asm volatile ("cpuid" + : [_] "={eax}" (eax), + [_] "={ebx}" (ebx), + [_] "={ecx}" (ecx), + [_] "={edx}" (edx), + : [_] "{eax}" (leaf_id), + [_] "{ecx}" (subleaf_id), + ); + + return .{ + .eax = eax, + .ebx = ebx, + .ecx = ecx, + .edx = edx, + }; +} + +fn writeU32Le(dst: []u8, offset: usize, value: u32) void { + dst[offset + 0] = @truncate(value); + dst[offset + 1] = @truncate(value >> 8); + dst[offset + 2] = @truncate(value >> 16); + dst[offset + 3] = @truncate(value >> 24); +} diff --git a/src/soft/c_includes.h b/src/soft/c_includes.h deleted file mode 100644 index e4d0278..0000000 --- a/src/soft/c_includes.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/src/soft/device/Renderer.zig b/src/soft/device/Renderer.zig index f6b5947..dd17d34 100644 --- a/src/soft/device/Renderer.zig +++ b/src/soft/device/Renderer.zig @@ -82,7 +82,7 @@ pub const DrawCall = struct { .viewport = undefined, .scissor = undefined, .color_attachments = framebuffer.interface.attachments[0..], - .depth_attachment = if (render_pass.interface.subpasses[0].depth_stencil_attachments) |desc| framebuffer.interface.attachments[desc.attachment] else null, + .depth_attachment = if (render_pass.interface.subpasses[renderer.subpass_index].depth_stencil_attachments) |desc| framebuffer.interface.attachments[desc.attachment] else null, .render_pass = render_pass, .framebuffer = framebuffer, .rasterizer_wait_group = .init, @@ -117,6 +117,8 @@ render_pass: ?*SoftRenderPass, framebuffer: ?*SoftFramebuffer, dynamic_state: DynamicState, +subpass_index: usize, + pub fn init(device: *SoftDevice, state: *PipelineState) Self { return .{ .device = device, @@ -128,6 +130,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self { .scissor = null, .line_width = null, }, + .subpass_index = 0, }; } diff --git a/src/soft/device/blitter.zig b/src/soft/device/blitter.zig index a3b2870..ff46ce1 100644 --- a/src/soft/device/blitter.zig +++ b/src/soft/device/blitter.zig @@ -50,6 +50,18 @@ fn computeOffset3D(x: usize, y: usize, z: usize, slice_bytes: usize, pitch_bytes } pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_format: vk.Format, range: vk.ImageSubresourceRange, render_area: ?vk.Rect2D) VkError!void { + if (range.aspect_mask.depth_bit and range.aspect_mask.stencil_bit) { + var depth_range = range; + depth_range.aspect_mask = .{ .depth_bit = true }; + try clear(pixel, format, dst, view_format, depth_range, render_area); + + var stencil_range = range; + stencil_range.aspect_mask = .{ .stencil_bit = true }; + try clear(pixel, format, dst, view_format, stencil_range, render_area); + + return; + } + const dst_format = base.format.fromAspect(view_format, range.aspect_mask); if (dst_format == .undefined) { return; @@ -64,23 +76,38 @@ pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_form }; var clamped_pixel: vk.ClearValue = pixel; - if (base.format.isSnorm(view_format) or base.format.isUnorm(view_format)) { + if (range.aspect_mask.color_bit and (base.format.isSnorm(view_format) or base.format.isUnorm(view_format))) { const min_value: f32 = if (base.format.isSnorm(view_format)) -1.0 else 0.0; - if (range.aspect_mask.color_bit) { - clamped_pixel.color.float_32[0] = std.math.clamp(pixel.color.float_32[0], min_value, 1.0); - clamped_pixel.color.float_32[1] = std.math.clamp(pixel.color.float_32[1], min_value, 1.0); - clamped_pixel.color.float_32[2] = std.math.clamp(pixel.color.float_32[2], min_value, 1.0); - clamped_pixel.color.float_32[3] = std.math.clamp(pixel.color.float_32[3], min_value, 1.0); - } - - if (range.aspect_mask.depth_bit) { - clamped_pixel.depth_stencil.depth = std.math.clamp(pixel.depth_stencil.depth, min_value, 1.0); - } + clamped_pixel.color.float_32[0] = std.math.clamp(pixel.color.float_32[0], min_value, 1.0); + clamped_pixel.color.float_32[1] = std.math.clamp(pixel.color.float_32[1], min_value, 1.0); + clamped_pixel.color.float_32[2] = std.math.clamp(pixel.color.float_32[2], min_value, 1.0); + clamped_pixel.color.float_32[3] = std.math.clamp(pixel.color.float_32[3], min_value, 1.0); } + if (range.aspect_mask.depth_bit) { + clamped_pixel.depth_stencil.depth = std.math.clamp(pixel.depth_stencil.depth, 0.0, 1.0); + } + + const depth_clear: F32x4 = @splat(clamped_pixel.depth_stencil.depth); + const stencil_clear: U32x4 = @splat(clamped_pixel.depth_stencil.stencil); + + const src_format: vk.Format = if (range.aspect_mask.stencil_bit) + .r32g32b32a32_uint + else if (range.aspect_mask.depth_bit) + .r32g32b32a32_sfloat + else + format; + + const src_map: []const u8 = if (range.aspect_mask.stencil_bit) + std.mem.asBytes(&stencil_clear) + else if (range.aspect_mask.depth_bit) + std.mem.asBytes(&depth_clear) + else + std.mem.asBytes(&clamped_pixel); + const state: State = .{ - .src_format = format, + .src_format = src_format, .dst_format = dst_format, .filter = .nearest, .allow_srgb_conversion = true, @@ -120,10 +147,10 @@ pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_form const dst_map = try dst.mapAsSliceWithAddedOffset(u8, dst_texel_offset, vk.WHOLE_SIZE); blit(state, .{ - .src_map = std.mem.asBytes(&clamped_pixel), + .src_map = src_map, .dst_map = dst_map, - .src_slice_pitch_bytes = base.format.texelSize(format), + .src_slice_pitch_bytes = base.format.texelSize(src_format), .src_row_pitch_bytes = 0, .dst_slice_pitch_bytes = dst.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), .dst_row_pitch_bytes = dst.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), @@ -456,6 +483,26 @@ fn blit(state: State, data: BlitData) void { } } +/// Using image blitting to resolve +pub inline fn resolve(src: *const SoftImage, dst: *SoftImage, region: vk.ImageResolve) VkError!void { + var blit_region: vk.ImageBlit = .{ + .src_offsets = .{ region.src_offset, region.src_offset }, + .src_subresource = region.src_subresource, + .dst_offsets = .{ region.dst_offset, region.dst_offset }, + .dst_subresource = region.dst_subresource, + }; + + blit_region.src_offsets[1].x += @intCast(region.extent.width); + blit_region.src_offsets[1].y += @intCast(region.extent.height); + blit_region.src_offsets[1].z += @intCast(region.extent.depth); + + blit_region.dst_offsets[1].x += @intCast(region.extent.width); + blit_region.dst_offsets[1].y += @intCast(region.extent.height); + blit_region.dst_offsets[1].z += @intCast(region.extent.depth); + + try blitRegion(src, dst, blit_region, .nearest); +} + fn applyScaleAndClamp(base_color: F32x4, state: State, apply_srgb_convertion: bool) F32x4 { var color: F32x4 = base_color; @@ -764,8 +811,7 @@ pub fn writeFloat4(color: F32x4, map: []u8, dst_format: vk.Format) void { .s8_uint, => map[0] = @intFromFloat(@round(color[0] * std.math.maxInt(u8))), - .r8_snorm, - => map[0] = @intFromFloat(@round(color[0] * std.math.maxInt(i8))), + .r8_snorm => map[0] = @intFromFloat(@round(color[0] * std.math.maxInt(i8))), .r16_sint, .r16_uint, @@ -1150,7 +1196,6 @@ pub fn writeInt4(c: U32x4, map: []u8, dst_format: vk.Format) void { .r8g8b8_uscaled, .r8g8_uscaled, .r8_uscaled, - .s8_uint, => color = @min(color, U32x4{ 0xFF, 0xFF, 0xFF, 0xFF }), .r16g16b16a16_uint, diff --git a/src/soft/device/rasterizer.zig b/src/soft/device/rasterizer.zig index e59f399..d93c9d6 100644 --- a/src/soft/device/rasterizer.zig +++ b/src/soft/device/rasterizer.zig @@ -20,7 +20,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics; const topology = pipeline_data.input_assembly.topology; - const color_attachment = if (draw_call.render_pass.interface.subpasses[0].color_attachments) |attachments| attachments[0].attachment else return VkError.InvalidAttachmentDrv; + const color_attachment = if (draw_call.render_pass.interface.subpasses[renderer.subpass_index].color_attachments) |attachments| attachments[0].attachment else return VkError.InvalidAttachmentDrv; const render_target_view: *base.ImageView = draw_call.color_attachments[color_attachment]; const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image)); diff --git a/src/soft/device/rasterizer/bresenham.zig b/src/soft/device/rasterizer/bresenham.zig index e8bdaeb..62a6205 100644 --- a/src/soft/device/rasterizer/bresenham.zig +++ b/src/soft/device/rasterizer/bresenham.zig @@ -121,7 +121,7 @@ fn runWrapper(data: RunData) void { } inline fn run(data: RunData) !void { - const color_attachment = if (data.draw_call.render_pass.interface.subpasses[0].color_attachments) |attachments| attachments[0].attachment else return VkError.InvalidAttachmentDrv; + const color_attachment = if (data.draw_call.render_pass.interface.subpasses[data.draw_call.renderer.subpass_index].color_attachments) |attachments| attachments[0].attachment else return VkError.InvalidAttachmentDrv; const render_target_view: *base.ImageView = data.draw_call.color_attachments[color_attachment]; const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image)); diff --git a/src/soft/lib.zig b/src/soft/lib.zig index 034e592..77d101b 100644 --- a/src/soft/lib.zig +++ b/src/soft/lib.zig @@ -68,6 +68,7 @@ pub const MAX_IMAGE_LEVELS_3D = 12; pub const MAX_IMAGE_LEVELS_CUBE = 15; pub const MAX_IMAGE_ARRAY_LAYERS = 2048; +pub const PHYSICAL_DEVICE_DEFAULT_NAME = "StrollSoft device"; pub const PHYSICAL_DEVICE_FALLBACK_HEAP_SIZE = 0x10000000; // 256MB pub const std_options = base.std_options; diff --git a/src/vulkan/CommandBuffer.zig b/src/vulkan/CommandBuffer.zig index 0859d0c..547eacb 100644 --- a/src/vulkan/CommandBuffer.zig +++ b/src/vulkan/CommandBuffer.zig @@ -62,10 +62,12 @@ pub const DispatchTable = struct { endRenderPass: *const fn (*Self) VkError!void, executeCommands: *const fn (*Self, *Self) VkError!void, fillBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, vk.DeviceSize, u32) VkError!void, + nextSubpass: *const fn (*Self, vk.SubpassContents) VkError!void, pipelineBarrier: *const fn (*Self, vk.PipelineStageFlags, vk.PipelineStageFlags, vk.DependencyFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void, pushConstants: *const fn (*Self, vk.ShaderStageFlags, u32, []const u8) VkError!void, reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void, resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void, + resolveImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, vk.ImageResolve) VkError!void, setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void, setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void, setViewport: *const fn (*Self, u32, []const vk.Viewport) VkError!void, @@ -250,6 +252,10 @@ pub inline fn fillBuffer(self: *Self, buffer: *Buffer, offset: vk.DeviceSize, si try self.dispatch_table.fillBuffer(self, buffer, offset, size, data); } +pub inline fn nextSubpass(self: *Self, contents: vk.SubpassContents) VkError!void { + try self.dispatch_table.nextSubpass(self, contents); +} + pub inline fn pipelineBarrier( self: *Self, src_stage: vk.PipelineStageFlags, @@ -270,6 +276,12 @@ pub inline fn resetEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlag try self.dispatch_table.resetEvent(self, event, stage); } +pub inline fn resolveImage(self: *Self, src: *Image, src_layout: vk.ImageLayout, dst: *Image, dst_layout: vk.ImageLayout, regions: []const vk.ImageResolve) VkError!void { + for (regions[0..]) |region| { + try self.dispatch_table.resolveImage(self, src, src_layout, dst, dst_layout, region); + } +} + pub inline fn setEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void { try self.dispatch_table.setEvent(self, event, stage); } diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index 2ca1c7c..cb46b1b 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -1912,11 +1912,7 @@ pub export fn strollCmdNextSubpass(p_cmd: vk.CommandBuffer, contents: vk.Subpass defer entryPointEndLogTrace(); const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); - - notImplementedWarning(); - - _ = cmd; - _ = contents; + cmd.nextSubpass(contents) catch |err| return errorLogger(err); } pub export fn strollCmdPipelineBarrier( @@ -1991,18 +1987,9 @@ pub export fn strollCmdResolveImage( defer entryPointEndLogTrace(); const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); - const src = Dispatchable(Image).fromHandleObject(p_src) catch |err| return errorLogger(err); - const dst = Dispatchable(Image).fromHandleObject(p_dst) catch |err| return errorLogger(err); - - notImplementedWarning(); - - _ = cmd; - _ = src; - _ = src_layout; - _ = dst; - _ = dst_layout; - _ = count; - _ = regions; + const src = NonDispatchable(Image).fromHandleObject(p_src) catch |err| return errorLogger(err); + const dst = NonDispatchable(Image).fromHandleObject(p_dst) catch |err| return errorLogger(err); + cmd.resolveImage(src, src_layout, dst, dst_layout, regions[0..count]) catch |err| return errorLogger(err); } pub export fn strollCmdSetBlendConstants(p_cmd: vk.CommandBuffer, p_constants: [*]f32) callconv(vk.vulkan_call_conv) void {