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.
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 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:
```
@@ -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
<details>
<summary>
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
+2 -16
View File
@@ -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;
-5
View File
@@ -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",
+67 -16
View File
@@ -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,21 +381,45 @@ 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 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];
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,
image_view.subresource_range,
null,
range,
impl.rect.rect,
);
}
}
};
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;
}
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();
+80 -22
View File
@@ -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);
}
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);
@@ -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);
}
-1
View File
@@ -1 +0,0 @@
#include <cpuinfo.h>
+4 -1
View File
@@ -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,
};
}
+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 {
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,10 +76,9 @@ 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);
@@ -75,12 +86,28 @@ pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_form
}
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 = .{
.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,
+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 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));
+1 -1
View File
@@ -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));
+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_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;
+12
View File
@@ -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);
}
+4 -17
View File
@@ -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 {