adding proper subpass management and image resolve
Test / build_and_test (push) Successful in 29s
Build / build (push) Successful in 1m5s

This commit is contained in:
2026-05-27 23:31:05 +02:00
parent 6edb856d06
commit fe391bc678
13 changed files with 257 additions and 116 deletions
+15 -11
View File
@@ -4,7 +4,7 @@
A driver as slow as Lance Stroll. 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. 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. \ It does not seek to produce a performant or production-worthy driver. \
*The gods are merciful, but not that merciful.* *The gods are merciful, but not that merciful.*
## Build ## Soft [software implementation]
Soft be a software implementation of the Vulkan specification, abiding within this drivers 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: 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. 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
<details> <details>
<summary> <summary>
The present standing of thy Vulkan 1.0 specification's implementation The present standing of thy Vulkan 1.0 specification's implementation
@@ -55,7 +59,7 @@ vkCmdBindVertexBuffers | ✅ Implemented
vkCmdBlitImage | ✅ Implemented vkCmdBlitImage | ✅ Implemented
vkCmdClearAttachments | ✅ Implemented vkCmdClearAttachments | ✅ Implemented
vkCmdClearColorImage | ✅ Implemented vkCmdClearColorImage | ✅ Implemented
vkCmdClearDepthStencilImage | ⚙️ WIP vkCmdClearDepthStencilImage | ✅ Implemented
vkCmdCopyBuffer | ✅ Implemented vkCmdCopyBuffer | ✅ Implemented
vkCmdCopyBufferToImage | ✅ Implemented vkCmdCopyBufferToImage | ✅ Implemented
vkCmdCopyImage | ✅ Implemented vkCmdCopyImage | ✅ Implemented
@@ -71,12 +75,12 @@ vkCmdEndQuery | ⚙️ WIP
vkCmdEndRenderPass | ✅ Implemented vkCmdEndRenderPass | ✅ Implemented
vkCmdExecuteCommands | ✅ Implemented vkCmdExecuteCommands | ✅ Implemented
vkCmdFillBuffer | ✅ Implemented vkCmdFillBuffer | ✅ Implemented
vkCmdNextSubpass | ⚙️ WIP vkCmdNextSubpass | ✅ Implemented
vkCmdPipelineBarrier | ✅ Implemented vkCmdPipelineBarrier | ✅ Implemented
vkCmdPushConstants | ✅ Implemented vkCmdPushConstants | ✅ Implemented
vkCmdResetEvent | ✅ Implemented vkCmdResetEvent | ✅ Implemented
vkCmdResetQueryPool | ⚙️ WIP vkCmdResetQueryPool | ⚙️ WIP
vkCmdResolveImage | ⚙️ WIP vkCmdResolveImage | ✅ Implemented
vkCmdSetBlendConstants | ⚙️ WIP vkCmdSetBlendConstants | ⚙️ WIP
vkCmdSetDepthBias | ⚙️ WIP vkCmdSetDepthBias | ⚙️ WIP
vkCmdSetDepthBounds | ⚙️ WIP vkCmdSetDepthBounds | ⚙️ WIP
@@ -108,7 +112,7 @@ vkCreatePipelineCache | ⚙️ WIP
vkCreatePipelineLayout | ✅ Implemented vkCreatePipelineLayout | ✅ Implemented
vkCreateQueryPool | ⚙️ WIP vkCreateQueryPool | ⚙️ WIP
vkCreateRenderPass | ✅ Implemented vkCreateRenderPass | ✅ Implemented
vkCreateSampler | ⚙️ WIP vkCreateSampler | ✅ Implemented
vkCreateSemaphore | ⚙️ WIP vkCreateSemaphore | ⚙️ WIP
vkCreateShaderModule | ✅ Implemented vkCreateShaderModule | ✅ Implemented
vkCreateSwapchainKHR | ✅ Implemented vkCreateSwapchainKHR | ✅ Implemented
@@ -156,7 +160,7 @@ vkGetDeviceQueue | ✅ Implemented
vkGetEventStatus | ✅ Implemented vkGetEventStatus | ✅ Implemented
vkGetFenceStatus | ✅ Implemented vkGetFenceStatus | ✅ Implemented
vkGetImageMemoryRequirements | ✅ Implemented vkGetImageMemoryRequirements | ✅ Implemented
vkGetImageSparseMemoryRequirements | ⚙️ WIP vkGetImageSparseMemoryRequirements | ❎ Unsupported
vkGetImageSubresourceLayout | ✅ Implemented vkGetImageSubresourceLayout | ✅ Implemented
vkGetInstanceProcAddr | ✅ Implemented vkGetInstanceProcAddr | ✅ Implemented
vkGetPhysicalDeviceFeatures | ✅ Implemented vkGetPhysicalDeviceFeatures | ✅ Implemented
@@ -165,7 +169,7 @@ vkGetPhysicalDeviceImageFormatProperties | ✅ Implemented
vkGetPhysicalDeviceMemoryProperties | ✅ Implemented vkGetPhysicalDeviceMemoryProperties | ✅ Implemented
vkGetPhysicalDeviceProperties | ✅ Implemented vkGetPhysicalDeviceProperties | ✅ Implemented
vkGetPhysicalDeviceQueueFamilyProperties | ✅ Implemented vkGetPhysicalDeviceQueueFamilyProperties | ✅ Implemented
vkGetPhysicalDeviceSparseImageFormatProperties | ⚙️ WIP vkGetPhysicalDeviceSparseImageFormatProperties | ❎ Unsupported
vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ✅ Implemented vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ✅ Implemented
vkGetPhysicalDeviceSurfaceFormatsKHR | ✅ Implemented vkGetPhysicalDeviceSurfaceFormatsKHR | ✅ Implemented
vkGetPhysicalDeviceSurfacePresentModesKHR | ✅ Implemented vkGetPhysicalDeviceSurfacePresentModesKHR | ✅ Implemented
@@ -181,7 +185,7 @@ vkGetSwapchainImagesKHR | ✅ Implemented
vkInvalidateMappedMemoryRanges | ✅ Implemented vkInvalidateMappedMemoryRanges | ✅ Implemented
vkMapMemory | ✅ Implemented vkMapMemory | ✅ Implemented
vkMergePipelineCaches | ⚙️ WIP vkMergePipelineCaches | ⚙️ WIP
vkQueueBindSparse | ⚙️ WIP vkQueueBindSparse | ❎ Unsupported
vkQueuePresentKHR | ✅ Implemented vkQueuePresentKHR | ✅ Implemented
vkQueueSubmit | ✅ Implemented vkQueueSubmit | ✅ Implemented
vkQueueWaitIdle | ✅ Implemented vkQueueWaitIdle | ✅ Implemented
+2 -16
View File
@@ -176,14 +176,11 @@ pub fn build(b: *std.Build) !void {
fn customSoft( fn customSoft(
b: *std.Build, b: *std.Build,
lib: *Step.Compile, lib: *Step.Compile,
target: std.Build.ResolvedTarget, _: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode, _: std.builtin.OptimizeMode,
options: *Step.Options, options: *Step.Options,
use_llvm: bool, use_llvm: bool,
) !void { ) !void {
const cpuinfo = b.lazyDependency("cpuinfo", .{}) orelse return error.UnresolvedDependency;
lib.root_module.linkLibrary(cpuinfo.artifact("cpuinfo"));
const spv = b.lazyDependency("SPIRV_Interpreter", .{ const spv = b.lazyDependency("SPIRV_Interpreter", .{
.@"no-example" = true, .@"no-example" = true,
.@"no-test" = true, .@"no-test" = true,
@@ -191,17 +188,6 @@ fn customSoft(
}) orelse return error.UnresolvedDependency; }) orelse return error.UnresolvedDependency;
lib.root_module.addImport("spv", spv.module("spv")); 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 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 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; const shaders_simd_option = b.option(bool, "shader-simd", "Shaders SIMD acceleration") orelse true;
-5
View File
@@ -25,11 +25,6 @@
.url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7", .url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7",
.hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3", .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 = .{ .SPIRV_Interpreter = .{
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#1ffa20d07c59da34300dd97e16b72a7320f5af6c", .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#1ffa20d07c59da34300dd97e16b72a7320f5af6c",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn25qBQByByo4yetC7ZS63JxLvuwXMknTMWnbaYAb", .hash = "SPIRV_Interpreter-0.0.1-ajmpn25qBQByByo4yetC7ZS63JxLvuwXMknTMWnbaYAb",
+67 -16
View File
@@ -68,10 +68,12 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.endRenderPass = endRenderPass, .endRenderPass = endRenderPass,
.executeCommands = executeCommands, .executeCommands = executeCommands,
.fillBuffer = fillBuffer, .fillBuffer = fillBuffer,
.nextSubpass = nextSubpass,
.pipelineBarrier = pipelineBarrier, .pipelineBarrier = pipelineBarrier,
.pushConstants = pushConstants, .pushConstants = pushConstants,
.reset = reset, .reset = reset,
.resetEvent = resetEvent, .resetEvent = resetEvent,
.resolveImage = resolveImage,
.setEvent = setEvent, .setEvent = setEvent,
.setScissor = setScissor, .setScissor = setScissor,
.setViewport = setViewport, .setViewport = setViewport,
@@ -144,6 +146,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
const impl: *Impl = @ptrCast(@alignCast(context)); const impl: *Impl = @ptrCast(@alignCast(context));
device.renderer.render_pass = impl.render_pass; device.renderer.render_pass = impl.render_pass;
device.renderer.framebuffer = impl.framebuffer; device.renderer.framebuffer = impl.framebuffer;
device.renderer.subpass_index = 0;
for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| { for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| {
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image));
@@ -378,21 +381,45 @@ pub fn clearAttachment(interface: *Interface, attachment: vk.ClearAttachment, re
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void { pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context)); const impl: *Impl = @ptrCast(@alignCast(context));
if (device.renderer.framebuffer) |framebuffer| { const framebuffer = device.renderer.framebuffer orelse return;
const image_view = framebuffer.interface.attachments[impl.attachment.color_attachment]; const render_pass = device.renderer.render_pass orelse return;
const subpass = render_pass.interface.subpasses[device.renderer.subpass_index];
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 image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.image));
const clear_format = try image.getClearFormat(); 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( try blitter.clear(
impl.attachment.clear_value, impl.attachment.clear_value,
clear_format, clear_format,
image, image,
image_view.format, image_view.format,
image_view.subresource_range, range,
null, impl.rect.rect,
); );
} }
}
}; };
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory; const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
@@ -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; 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 self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
const CommandImpl = struct { const CommandImpl = struct {
const Impl = @This(); 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; const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd); errdefer allocator.destroy(cmd);
cmd.* = .{}; cmd.* = .{};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
_ = src_stage; pub fn pipelineBarrier(_: *Interface, _: vk.PipelineStageFlags, _: vk.PipelineStageFlags, _: vk.DependencyFlags, _: []const vk.MemoryBarrier, _: []const vk.BufferMemoryBarrier, _: []const vk.ImageMemoryBarrier) VkError!void {
_ = dst_stage; // No-op
_ = dependency;
_ = memory_barriers;
_ = buffer_barriers;
_ = image_barriers;
} }
pub fn pushConstants(interface: *Interface, stages: vk.ShaderStageFlags, offset: u32, blob: []const u8) VkError!void { 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; 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 self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
_ = stage;
const CommandImpl = struct { const CommandImpl = struct {
const Impl = @This(); 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; 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 { pub fn setEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
+80 -22
View File
@@ -1,8 +1,8 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const lib = @import("lib.zig"); const lib = @import("lib.zig");
const cpuinfo = lib.c;
const SoftDevice = @import("SoftDevice.zig"); 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_float_64 = .true,
.shader_int_64 = .true, .shader_int_64 = .true,
.shader_int_16 = .true, .shader_int_16 = .true,
.texture_compression_etc2 = .false,
.texture_compression_bc = .false,
.texture_compression_astc_ldr = .false,
}; };
var queue_family_props = [_]vk.QueueFamilyProperties{ 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, .timestamp_valid_bits = 0,
.min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 }, .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; interface.queue_family_props.appendSlice(allocator, queue_family_props[0..]) catch return VkError.OutOfHostMemory;
if (device_name[0] == 0) { if (device_name[0] == 0) {
const name = blk: { const name = blk: {
if (cpuinfo.cpuinfo_initialize()) {
const package = cpuinfo.cpuinfo_get_package(0).*; // If arch is x86 we try to get precise CPU name through CPUID
const non_sentinel_name = package.name[0..(std.mem.len(@as([*:0]const u8, @ptrCast(&package.name))))]; // and fallback to vendor name if not available
break :blk std.fmt.allocPrint(command_allocator, "{s} ({d} threads)", .{ non_sentinel_name, package.processor_count }) catch return VkError.OutOfHostMemory; 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);
} }
break :blk command_allocator.dupe(u8, "Unkown") catch return VkError.OutOfHostMemory;
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, lib.PHYSICAL_DEVICE_DEFAULT_NAME) catch return VkError.OutOfHostMemory;
}; };
defer command_allocator.free(name); 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 { pub fn getSurfaceSupportKHR(_: *Interface, _: u32, _: *SurfaceKHR) VkError!bool {
return true; 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);
}
-1
View File
@@ -1 +0,0 @@
#include <cpuinfo.h>
+4 -1
View File
@@ -82,7 +82,7 @@ pub const DrawCall = struct {
.viewport = undefined, .viewport = undefined,
.scissor = undefined, .scissor = undefined,
.color_attachments = framebuffer.interface.attachments[0..], .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, .render_pass = render_pass,
.framebuffer = framebuffer, .framebuffer = framebuffer,
.rasterizer_wait_group = .init, .rasterizer_wait_group = .init,
@@ -117,6 +117,8 @@ render_pass: ?*SoftRenderPass,
framebuffer: ?*SoftFramebuffer, framebuffer: ?*SoftFramebuffer,
dynamic_state: DynamicState, dynamic_state: DynamicState,
subpass_index: usize,
pub fn init(device: *SoftDevice, state: *PipelineState) Self { pub fn init(device: *SoftDevice, state: *PipelineState) Self {
return .{ return .{
.device = device, .device = device,
@@ -128,6 +130,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self {
.scissor = null, .scissor = null,
.line_width = null, .line_width = null,
}, },
.subpass_index = 0,
}; };
} }
+55 -10
View File
@@ -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 { 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); const dst_format = base.format.fromAspect(view_format, range.aspect_mask);
if (dst_format == .undefined) { if (dst_format == .undefined) {
return; return;
@@ -64,10 +76,9 @@ pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_form
}; };
var clamped_pixel: vk.ClearValue = pixel; 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; 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[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[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[2] = std.math.clamp(pixel.color.float_32[2], min_value, 1.0);
@@ -75,12 +86,28 @@ pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_form
} }
if (range.aspect_mask.depth_bit) { if (range.aspect_mask.depth_bit) {
clamped_pixel.depth_stencil.depth = std.math.clamp(pixel.depth_stencil.depth, min_value, 1.0); 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 = .{ const state: State = .{
.src_format = format, .src_format = src_format,
.dst_format = dst_format, .dst_format = dst_format,
.filter = .nearest, .filter = .nearest,
.allow_srgb_conversion = true, .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); const dst_map = try dst.mapAsSliceWithAddedOffset(u8, dst_texel_offset, vk.WHOLE_SIZE);
blit(state, .{ blit(state, .{
.src_map = std.mem.asBytes(&clamped_pixel), .src_map = src_map,
.dst_map = dst_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, .src_row_pitch_bytes = 0,
.dst_slice_pitch_bytes = dst.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), .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), .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 { fn applyScaleAndClamp(base_color: F32x4, state: State, apply_srgb_convertion: bool) F32x4 {
var color: F32x4 = base_color; var color: F32x4 = base_color;
@@ -764,8 +811,7 @@ pub fn writeFloat4(color: F32x4, map: []u8, dst_format: vk.Format) void {
.s8_uint, .s8_uint,
=> map[0] = @intFromFloat(@round(color[0] * std.math.maxInt(u8))), => map[0] = @intFromFloat(@round(color[0] * std.math.maxInt(u8))),
.r8_snorm, .r8_snorm => map[0] = @intFromFloat(@round(color[0] * std.math.maxInt(i8))),
=> map[0] = @intFromFloat(@round(color[0] * std.math.maxInt(i8))),
.r16_sint, .r16_sint,
.r16_uint, .r16_uint,
@@ -1150,7 +1196,6 @@ pub fn writeInt4(c: U32x4, map: []u8, dst_format: vk.Format) void {
.r8g8b8_uscaled, .r8g8b8_uscaled,
.r8g8_uscaled, .r8g8_uscaled,
.r8_uscaled, .r8_uscaled,
.s8_uint,
=> color = @min(color, U32x4{ 0xFF, 0xFF, 0xFF, 0xFF }), => color = @min(color, U32x4{ 0xFF, 0xFF, 0xFF, 0xFF }),
.r16g16b16a16_uint, .r16g16b16a16_uint,
+1 -1
View File
@@ -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 pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
const topology = pipeline_data.input_assembly.topology; 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_view: *base.ImageView = draw_call.color_attachments[color_attachment];
const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image)); const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image));
+1 -1
View File
@@ -121,7 +121,7 @@ fn runWrapper(data: RunData) void {
} }
inline fn run(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_view: *base.ImageView = data.draw_call.color_attachments[color_attachment];
const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image)); const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image));
+1
View File
@@ -68,6 +68,7 @@ pub const MAX_IMAGE_LEVELS_3D = 12;
pub const MAX_IMAGE_LEVELS_CUBE = 15; pub const MAX_IMAGE_LEVELS_CUBE = 15;
pub const MAX_IMAGE_ARRAY_LAYERS = 2048; 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 PHYSICAL_DEVICE_FALLBACK_HEAP_SIZE = 0x10000000; // 256MB
pub const std_options = base.std_options; pub const std_options = base.std_options;
+12
View File
@@ -62,10 +62,12 @@ pub const DispatchTable = struct {
endRenderPass: *const fn (*Self) VkError!void, endRenderPass: *const fn (*Self) VkError!void,
executeCommands: *const fn (*Self, *Self) VkError!void, executeCommands: *const fn (*Self, *Self) VkError!void,
fillBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, vk.DeviceSize, u32) 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, 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, pushConstants: *const fn (*Self, vk.ShaderStageFlags, u32, []const u8) VkError!void,
reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void, reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void,
resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) 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, setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void, setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void,
setViewport: *const fn (*Self, u32, []const vk.Viewport) 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); 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( pub inline fn pipelineBarrier(
self: *Self, self: *Self,
src_stage: vk.PipelineStageFlags, 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); 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 { pub inline fn setEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void {
try self.dispatch_table.setEvent(self, event, stage); try self.dispatch_table.setEvent(self, event, stage);
} }
+4 -17
View File
@@ -1912,11 +1912,7 @@ pub export fn strollCmdNextSubpass(p_cmd: vk.CommandBuffer, contents: vk.Subpass
defer entryPointEndLogTrace(); defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
cmd.nextSubpass(contents) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = contents;
} }
pub export fn strollCmdPipelineBarrier( pub export fn strollCmdPipelineBarrier(
@@ -1991,18 +1987,9 @@ pub export fn strollCmdResolveImage(
defer entryPointEndLogTrace(); defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); 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 src = NonDispatchable(Image).fromHandleObject(p_src) catch |err| return errorLogger(err);
const dst = Dispatchable(Image).fromHandleObject(p_dst) 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);
notImplementedWarning();
_ = cmd;
_ = src;
_ = src_layout;
_ = dst;
_ = dst_layout;
_ = count;
_ = regions;
} }
pub export fn strollCmdSetBlendConstants(p_cmd: vk.CommandBuffer, p_constants: [*]f32) callconv(vk.vulkan_call_conv) void { pub export fn strollCmdSetBlendConstants(p_cmd: vk.CommandBuffer, p_constants: [*]f32) callconv(vk.vulkan_call_conv) void {