yes
Build / build (push) Successful in 33s
Test / build_and_test (push) Successful in 24s

This commit is contained in:
2026-06-04 02:45:06 +02:00
parent 7da8eec788
commit decdc79ae7
21 changed files with 540 additions and 108 deletions
+2 -2
View File
@@ -180,7 +180,7 @@ vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP
vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP
vkGetPipelineCacheData | ⚙️ WIP
vkGetQueryPoolResults | ⚙️ WIP
vkGetRenderAreaGranularity | ⚙️ WIP
vkGetRenderAreaGranularity | ✅ Implemented
vkGetSwapchainImagesKHR | ✅ Implemented
vkInvalidateMappedMemoryRanges | ✅ Implemented
vkMapMemory | ✅ Implemented
@@ -200,7 +200,7 @@ vkUpdateDescriptorSets | ✅ Implemented
vkWaitForFences | ✅ Implemented
</details>
[Here](https://vulkan-driver-cts-report.kbz8.me/) shalt thou find a most meticulous account of the Vulkan 1.0 conformance trials, set forth for thy scrutiny.
[Here](https://vulkan-driver.kbz8.me/cts/soft/) shalt thou find a most meticulous account of the Vulkan 1.0 conformance trials, set forth for thy scrutiny.
## License
+2 -2
View File
@@ -26,8 +26,8 @@
.hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3",
},
.SPIRV_Interpreter = .{
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#9c355fe126d0142ac6d1fae48633993864c90d61",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn867BQA-J2PA4fGQGnb3WcNtqd_3_W7-goaEocC5",
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#8677e8a6833d39e9169460fd75de2d7c70fab4b8",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn_HtBQBR82M2KlR_yuUvdHUVdXrYVGRh5YNqVFqQ",
.lazy = true,
},
//.SPIRV_Interpreter = .{
+20 -2
View File
@@ -231,21 +231,39 @@ pub fn bindDescriptorSets(interface: *Interface, bind_point: vk.PipelineBindPoin
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
var dynamic_offset_index: usize = 0;
for (impl.first_set.., impl.sets[0..]) |i, set| {
if (set == null)
break;
device.pipeline_states[@intCast(@intFromEnum(impl.bind_point))].sets[i] = @alignCast(@fieldParentPtr("interface", set.?));
const state = &device.pipeline_states[@intCast(@intFromEnum(impl.bind_point))];
const soft_set: *SoftDescriptorSet = @alignCast(@fieldParentPtr("interface", set.?));
state.sets[i] = soft_set;
const dynamic_count = soft_set.interface.layout.dynamic_descriptor_count;
if (dynamic_count > ExecutionDevice.MAX_DYNAMIC_DESCRIPTORS_PER_SET or
dynamic_offset_index + dynamic_count > impl.dynamic_offsets.len)
{
return VkError.ValidationFailed;
}
@memcpy(
state.dynamic_offsets[i][0..dynamic_count],
impl.dynamic_offsets[dynamic_offset_index .. dynamic_offset_index + dynamic_count],
);
dynamic_offset_index += dynamic_count;
}
}
};
const dynamic_offsets_copy = allocator.dupe(u32, dynamic_offsets) catch return VkError.OutOfHostMemory;
errdefer allocator.free(dynamic_offsets_copy);
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{
.bind_point = bind_point,
.first_set = first_set,
.sets = sets,
.dynamic_offsets = dynamic_offsets,
.dynamic_offsets = dynamic_offsets_copy,
};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
+105 -9
View File
@@ -34,6 +34,10 @@ const DescriptorImage = struct {
object: ?*SoftImageView,
};
const DescriptorSampler = struct {
object: ?*SoftSampler,
};
const DescriptorTexel = struct {
object: ?*SoftBufferView,
};
@@ -42,6 +46,7 @@ const Descriptor = union(enum) {
buffer: []DescriptorBuffer,
texture: []DescriptorTexture,
image: []DescriptorImage,
sampler: []DescriptorSampler,
texel_buffer: []DescriptorTexel,
unsupported: struct {},
};
@@ -70,14 +75,19 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
for (layout.bindings) |binding| {
const struct_size: usize = switch (binding.descriptor_type) {
.uniform_buffer,
.uniform_buffer_dynamic,
.storage_buffer,
.storage_buffer_dynamic,
=> @sizeOf(DescriptorBuffer),
.sampled_image,
.storage_image,
.input_attachment,
=> @sizeOf(DescriptorImage),
.sampler,
=> @sizeOf(DescriptorSampler),
.storage_texel_buffer,
.uniform_texel_buffer,
=> @sizeOf(DescriptorTexel),
@@ -103,6 +113,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
for (descriptors, layout.bindings) |*descriptor, binding| {
switch (binding.descriptor_type) {
.uniform_buffer,
.uniform_buffer_dynamic,
.storage_buffer,
.storage_buffer_dynamic,
=> descriptor.* = blk: {
@@ -119,6 +130,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
break :blk desc;
},
.sampled_image,
.storage_image,
.input_attachment,
=> descriptor.* = blk: {
@@ -131,6 +143,22 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
break :blk desc;
},
.sampler,
=> descriptor.* = blk: {
const desc: Descriptor = .{
.sampler = local_allocator.alloc(DescriptorSampler, binding.array_size) catch return VkError.OutOfHostMemory,
};
for (desc.sampler[0..], 0..) |*d, i| {
d.* = .{
.object = if (i < binding.immutable_samplers.len)
@as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(binding.immutable_samplers[i]))))
else
null,
};
}
break :blk desc;
},
.storage_texel_buffer,
.uniform_texel_buffer,
=> descriptor.* = blk: {
@@ -148,9 +176,12 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
const desc: Descriptor = .{
.texture = local_allocator.alloc(DescriptorTexture, binding.array_size) catch return VkError.OutOfHostMemory,
};
for (desc.texture[0..]) |*d| {
for (desc.texture[0..], 0..) |*d, i| {
d.* = .{
.sampler = null,
.sampler = if (i < binding.immutable_samplers.len)
@as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(binding.immutable_samplers[i]))))
else
null,
.view = null,
};
}
@@ -179,6 +210,7 @@ fn descriptorLen(descriptor: Descriptor) usize {
return switch (descriptor) {
.buffer => |buffer| buffer.len,
.image => |image| image.len,
.sampler => |sampler| sampler.len,
.texel_buffer => |texel_buffer| texel_buffer.len,
.texture => |texture| texture.len,
.unsupported => 0,
@@ -227,6 +259,17 @@ fn copyDescriptorRange(dst_desc: *Descriptor, dst_array_element: usize, src_desc
);
},
.sampler => |dst_sampler| {
const src_sampler = switch (src_desc) {
.sampler => |sampler| sampler,
else => return false,
};
@memcpy(
dst_sampler[dst_array_element .. dst_array_element + descriptor_count],
src_sampler[src_array_element .. src_array_element + descriptor_count],
);
},
.texel_buffer => |dst_texel_buffer| {
const src_texel_buffer = switch (src_desc) {
.texel_buffer => |texel_buffer| texel_buffer,
@@ -255,6 +298,33 @@ fn copyDescriptorRange(dst_desc: *Descriptor, dst_array_element: usize, src_desc
return true;
}
fn copyDescriptorRangePreservingImmutableSamplers(self: *Self, dst_binding: usize, dst_desc: *Descriptor, dst_array_element: usize, src_desc: Descriptor, src_array_element: usize, descriptor_count: usize) bool {
const immutable_samplers = self.interface.layout.bindings[dst_binding].immutable_samplers;
if (immutable_samplers.len == 0) {
return copyDescriptorRange(dst_desc, dst_array_element, src_desc, src_array_element, descriptor_count);
}
const dst_texture = switch (dst_desc.*) {
.texture => |texture| texture,
else => return copyDescriptorRange(dst_desc, dst_array_element, src_desc, src_array_element, descriptor_count),
};
const src_texture = switch (src_desc) {
.texture => |texture| texture,
else => return false,
};
for (0..descriptor_count) |i| {
const dst_index = dst_array_element + i;
const src_index = src_array_element + i;
dst_texture[dst_index].view = src_texture[src_index].view;
if (dst_index < immutable_samplers.len) {
dst_texture[dst_index].sampler = @as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(immutable_samplers[dst_index]))));
}
}
return true;
}
pub fn copy(interface: *Interface, src_interface: *const Interface, data: vk.CopyDescriptorSet) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const src: *const Self = @alignCast(@fieldParentPtr("interface", src_interface));
@@ -285,7 +355,7 @@ pub fn copy(interface: *Interface, src_interface: *const Interface, data: vk.Cop
const src_remaining = src_len - src_array_element;
const copy_count = @min(descriptor_count, dst_remaining, src_remaining);
if (!copyDescriptorRange(dst_desc, dst_array_element, src_desc, src_array_element, copy_count)) {
if (!self.copyDescriptorRangePreservingImmutableSamplers(dst_binding, dst_desc, dst_array_element, src_desc, src_array_element, copy_count)) {
base.unsupported("descriptor type for copy", .{});
return;
}
@@ -301,11 +371,12 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
switch (write_data.descriptor_type) {
.uniform_buffer,
.uniform_buffer_dynamic,
.storage_buffer,
.storage_buffer_dynamic,
=> {
for (write_data.p_buffer_info, 0..write_data.descriptor_count) |buffer_info, i| {
const desc = &self.descriptors[write_data.dst_binding].buffer[i];
const desc = &self.descriptors[write_data.dst_binding].buffer[write_data.dst_array_element + i];
desc.* = .{
.object = null,
.offset = buffer_info.offset,
@@ -321,11 +392,12 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
}
},
.sampled_image,
.storage_image,
.input_attachment,
=> {
for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| {
const desc = &self.descriptors[write_data.dst_binding].image[i];
const desc = &self.descriptors[write_data.dst_binding].image[write_data.dst_array_element + i];
desc.* = .{ .object = null };
if (image_info.image_view != .null_handle) {
const image_view = try NonDispatchable(ImageView).fromHandleObject(image_info.image_view);
@@ -334,11 +406,30 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
}
},
.sampler,
=> {
for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| {
const desc = &self.descriptors[write_data.dst_binding].sampler[write_data.dst_array_element + i];
const immutable_samplers = self.interface.layout.bindings[write_data.dst_binding].immutable_samplers;
const array_element = write_data.dst_array_element + i;
desc.* = .{
.object = if (array_element < immutable_samplers.len)
@as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(immutable_samplers[array_element]))))
else
null,
};
if (immutable_samplers.len == 0 and image_info.sampler != .null_handle) {
const sampler = try NonDispatchable(Sampler).fromHandleObject(image_info.sampler);
desc.object = @as(*SoftSampler, @alignCast(@fieldParentPtr("interface", sampler)));
}
}
},
.storage_texel_buffer,
.uniform_texel_buffer,
=> {
for (write_data.p_texel_buffer_view, 0..write_data.descriptor_count) |view, i| {
const desc = &self.descriptors[write_data.dst_binding].texel_buffer[i];
const desc = &self.descriptors[write_data.dst_binding].texel_buffer[write_data.dst_array_element + i];
desc.* = .{ .object = null };
if (view != .null_handle) {
const buffer_view = try NonDispatchable(BufferView).fromHandleObject(view);
@@ -350,16 +441,21 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
.combined_image_sampler,
=> {
for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| {
const desc = &self.descriptors[write_data.dst_binding].texture[i];
const desc = &self.descriptors[write_data.dst_binding].texture[write_data.dst_array_element + i];
const immutable_samplers = self.interface.layout.bindings[write_data.dst_binding].immutable_samplers;
const array_element = write_data.dst_array_element + i;
desc.* = .{
.sampler = null,
.sampler = if (array_element < immutable_samplers.len)
@as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(immutable_samplers[array_element]))))
else
null,
.view = null,
};
if (image_info.image_view != .null_handle) {
const image_view = try NonDispatchable(ImageView).fromHandleObject(image_info.image_view);
desc.view = @as(*SoftImageView, @alignCast(@fieldParentPtr("interface", image_view)));
}
if (image_info.sampler != .null_handle) {
if (immutable_samplers.len == 0 and image_info.sampler != .null_handle) {
const sampler = try NonDispatchable(Sampler).fromHandleObject(image_info.sampler);
desc.sampler = @as(*SoftSampler, @alignCast(@fieldParentPtr("interface", sampler)));
}
+110 -42
View File
@@ -382,6 +382,7 @@ const CubeCoordinate = struct {
face: u32,
u: f32,
v: f32,
w: f32 = 0.0,
};
fn resolveCubeCoordinate(x: f32, y: f32, z: f32) CubeCoordinate {
@@ -455,13 +456,17 @@ fn cubeDirection(face: u32, u: f32, v: f32) struct { x: f32, y: f32, z: f32 } {
fn readSampledFloat4(
image: *SoftImage,
image_view: *SoftImageView,
sampler: *SoftSampler,
dim: spv.SpvDim,
coord: CubeCoordinate,
ix: i32,
iy: i32,
iz: i32,
) VkError!zm.F32x4 {
const width_f: f32 = @floatFromInt(image.interface.extent.width);
const height_f: f32 = @floatFromInt(image.interface.extent.height);
const range = image_view.interface.subresource_range;
const extent = image.getMipLevelExtent(range.base_mip_level);
const width_f: f32 = @floatFromInt(extent.width);
const height_f: f32 = @floatFromInt(extent.height);
const texel = if (dim == .Cube) blk: {
const dir = cubeDirection(
@@ -472,61 +477,127 @@ fn readSampledFloat4(
break :blk resolveCubeCoordinate(dir.x, dir.y, dir.z);
} else coord;
const z: i32, const layer: u32 = switch (image_view.interface.view_type) {
.@"1d_array" => .{ 0, range.base_array_layer + @as(u32, @intCast(sampleAddress(@intFromFloat(coord.v), viewLayerCount(image, range), .clamp_to_edge))) },
.@"2d_array", .cube_array => .{ 0, range.base_array_layer + @as(u32, @intCast(sampleAddress(@intFromFloat(coord.w), viewLayerCount(image, range), .clamp_to_edge))) },
.@"3d" => .{ sampleAddressOrBorder(iz, extent.depth, sampler.interface.address_mode_w) orelse return samplerBorderColor(sampler), range.base_array_layer },
.cube => .{ 0, range.base_array_layer + texel.face },
else => .{ 0, range.base_array_layer },
};
const sx = if (dim == .Cube)
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, @as(i32, @intCast(extent.width)) - 1)
else
sampleAddressOrBorder(ix, extent.width, sampler.interface.address_mode_u) orelse return samplerBorderColor(sampler);
const sy = if (dim == .Cube)
std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, @as(i32, @intCast(extent.height)) - 1)
else
sampleAddressOrBorder(iy, extent.height, sampler.interface.address_mode_v) orelse return samplerBorderColor(sampler);
const result = try image.readFloat4(
.{
.x = if (dim == .Cube)
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, image.interface.extent.width - 1)
else
std.math.clamp(ix, 0, image.interface.extent.width - 1),
.y = if (dim == .Cube)
std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, image.interface.extent.height - 1)
else
std.math.clamp(iy, 0, image.interface.extent.height - 1),
.z = 0,
.x = sx,
.y = sy,
.z = z,
},
.{
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer + texel.face,
.aspect_mask = range.aspect_mask,
.mip_level = range.base_mip_level,
.array_layer = layer,
},
image_view.interface.format,
);
return result;
}
fn sampleNearestFloat4(image: *SoftImage, image_view: *SoftImageView, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
const width_f: f32 = @floatFromInt(image.interface.extent.width);
const height_f: f32 = @floatFromInt(image.interface.extent.height);
fn sampleAddress(coord: i32, extent: u32, mode: vk.SamplerAddressMode) i32 {
return sampleAddressOrBorder(coord, extent, mode).?;
}
fn sampleAddressOrBorder(coord: i32, extent: u32, mode: vk.SamplerAddressMode) ?i32 {
const extent_i: i32 = @intCast(extent);
return switch (mode) {
.repeat => @mod(coord, extent_i),
.mirrored_repeat => blk: {
const period = extent_i * 2;
const mirrored = @mod(coord, period);
break :blk if (mirrored < extent_i) mirrored else period - mirrored - 1;
},
.clamp_to_border => if (coord < 0 or coord >= extent_i) null else coord,
else => std.math.clamp(coord, 0, extent_i - 1),
};
}
fn samplerBorderColor(sampler: *SoftSampler) zm.F32x4 {
return switch (sampler.interface.border_color) {
.float_opaque_white, .int_opaque_white => .{ 1.0, 1.0, 1.0, 1.0 },
.float_opaque_black, .int_opaque_black => .{ 0.0, 0.0, 0.0, 1.0 },
else => .{ 0.0, 0.0, 0.0, 0.0 },
};
}
fn viewLayerCount(image: *SoftImage, range: vk.ImageSubresourceRange) u32 {
return if (range.layer_count == vk.REMAINING_ARRAY_LAYERS)
image.interface.array_layers - range.base_array_layer
else
range.layer_count;
}
fn sampleNearestFloat4(image: *SoftImage, image_view: *SoftImageView, sampler: *SoftSampler, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
const extent = image.getMipLevelExtent(image_view.interface.subresource_range.base_mip_level);
const width_f: f32 = @floatFromInt(extent.width);
const height_f: f32 = @floatFromInt(extent.height);
return readSampledFloat4(
image,
image_view,
sampler,
dim,
coord,
@intFromFloat(coord.u * width_f),
@intFromFloat(coord.v * height_f),
@intFromFloat(coord.w * @as(f32, @floatFromInt(extent.depth))),
);
}
fn sampleLinearFloat4(image: *SoftImage, image_view: *SoftImageView, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
const width_f: f32 = @floatFromInt(image.interface.extent.width);
const height_f: f32 = @floatFromInt(image.interface.extent.height);
fn sampleLinearFloat4(image: *SoftImage, image_view: *SoftImageView, sampler: *SoftSampler, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
const extent = image.getMipLevelExtent(image_view.interface.subresource_range.base_mip_level);
const width_f: f32 = @floatFromInt(extent.width);
const height_f: f32 = @floatFromInt(extent.height);
const x = coord.u * width_f - 0.5;
const y = coord.v * height_f - 0.5;
const z = coord.w * @as(f32, @floatFromInt(extent.depth)) - 0.5;
const x0: i32 = @intFromFloat(@floor(x));
const y0: i32 = @intFromFloat(@floor(y));
const z0: i32 = @intFromFloat(@floor(z));
const x1 = x0 + 1;
const y1 = y0 + 1;
const z1 = z0 + 1;
const wx = x - @as(f32, @floatFromInt(x0));
const wy = y - @as(f32, @floatFromInt(y0));
const wz = z - @as(f32, @floatFromInt(z0));
const p00 = try readSampledFloat4(image, image_view, dim, coord, x0, y0);
const p10 = try readSampledFloat4(image, image_view, dim, coord, x1, y0);
const p01 = try readSampledFloat4(image, image_view, dim, coord, x0, y1);
const p11 = try readSampledFloat4(image, image_view, dim, coord, x1, y1);
const p000 = try readSampledFloat4(image, image_view, sampler, dim, coord, x0, y0, z0);
const p100 = try readSampledFloat4(image, image_view, sampler, dim, coord, x1, y0, z0);
const p010 = try readSampledFloat4(image, image_view, sampler, dim, coord, x0, y1, z0);
const p110 = try readSampledFloat4(image, image_view, sampler, dim, coord, x1, y1, z0);
const row0 = p00 * zm.f32x4s(1.0 - wx) + p10 * zm.f32x4s(wx);
const row1 = p01 * zm.f32x4s(1.0 - wx) + p11 * zm.f32x4s(wx);
return row0 * zm.f32x4s(1.0 - wy) + row1 * zm.f32x4s(wy);
const row00 = p000 * zm.f32x4s(1.0 - wx) + p100 * zm.f32x4s(wx);
const row10 = p010 * zm.f32x4s(1.0 - wx) + p110 * zm.f32x4s(wx);
const slice0 = row00 * zm.f32x4s(1.0 - wy) + row10 * zm.f32x4s(wy);
if (image_view.interface.view_type != .@"3d")
return slice0;
const p001 = try readSampledFloat4(image, image_view, sampler, dim, coord, x0, y0, z1);
const p101 = try readSampledFloat4(image, image_view, sampler, dim, coord, x1, y0, z1);
const p011 = try readSampledFloat4(image, image_view, sampler, dim, coord, x0, y1, z1);
const p111 = try readSampledFloat4(image, image_view, sampler, dim, coord, x1, y1, z1);
const row01 = p001 * zm.f32x4s(1.0 - wx) + p101 * zm.f32x4s(wx);
const row11 = p011 * zm.f32x4s(1.0 - wx) + p111 * zm.f32x4s(wx);
const slice1 = row01 * zm.f32x4s(1.0 - wy) + row11 * zm.f32x4s(wy);
return slice0 * zm.f32x4s(1.0 - wz) + slice1 * zm.f32x4s(wz);
}
fn sampleImageFloat4(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32) SpvRuntimeError!spv.Runtime.Vec4(f32) {
@@ -546,23 +617,20 @@ fn sampleImageFloat4(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim,
if (dim == .Cube) {
const coord = resolveCubeCoordinate(x, y, z);
pixel = switch (sampler.interface.mag_filter) {
.linear => sampleLinearFloat4(image, image_view, dim, coord),
else => sampleNearestFloat4(image, image_view, dim, coord),
.linear => sampleLinearFloat4(image, image_view, sampler, dim, coord),
else => sampleNearestFloat4(image, image_view, sampler, dim, coord),
} catch return SpvRuntimeError.Unknown;
} else {
pixel = image.readFloat4(
.{
.x = std.math.clamp(@as(i32, @intFromFloat(x * @as(f32, @floatFromInt(image.interface.extent.width)))), 0, image.interface.extent.width - 1),
.y = std.math.clamp(@as(i32, @intFromFloat(y * @as(f32, @floatFromInt(image.interface.extent.height)))), 0, image.interface.extent.height - 1),
.z = std.math.clamp(@as(i32, @intFromFloat(z * @as(f32, @floatFromInt(image.interface.extent.depth)))), 0, image.interface.extent.depth - 1),
},
.{
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer,
},
image_view.interface.format,
) catch return SpvRuntimeError.Unknown;
const coord: CubeCoordinate = .{
.u = x,
.v = y,
.w = z,
.face = 0,
};
pixel = switch (sampler.interface.mag_filter) {
.linear => sampleLinearFloat4(image, image_view, sampler, dim, coord),
else => sampleNearestFloat4(image, image_view, sampler, dim, coord),
} catch return SpvRuntimeError.Unknown;
}
}
+8
View File
@@ -18,6 +18,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
interface.vtable = &.{
.destroy = destroy,
.getRenderAreaGranularity = getRenderAreaGranularity,
};
self.* = .{
@@ -30,3 +31,10 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
allocator.destroy(self);
}
pub fn getRenderAreaGranularity(_: *Interface) vk.Extent2D {
return .{
.width = 1,
.height = 1,
};
}
+4 -2
View File
@@ -157,9 +157,10 @@ inline fn run(data: RunData) !void {
continue;
}
try setupWorkgroupBuiltins(data.self, rt, group_count_vec, group_id_vec);
for (0..data.invocations_per_workgroup) |i| {
rt.resetInvocation(allocator);
try setupWorkgroupBuiltins(data.self, rt, group_count_vec, group_id_vec);
const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic);
try setupSubgroupBuiltins(data.self, rt, .{
@@ -202,6 +203,7 @@ fn runBarrierWorkgroup(
const allocator = data.self.device.device_allocator.allocator();
for (runtimes, 0..) |*rt, i| {
rt.resetInvocation(allocator);
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
try setupWorkgroupBuiltins(data.self, rt, group_count, group_id);
try setupSubgroupBuiltins(data.self, rt, group_id, i);
+48 -10
View File
@@ -19,10 +19,12 @@ const Self = @This();
pub const GRAPHICS_PIPELINE_STATE = 0;
pub const COMPUTE_PIPELINE_STATE = 1;
pub const MAX_DYNAMIC_DESCRIPTORS_PER_SET = 64;
pub const PipelineState = struct {
pipeline: ?*SoftPipeline,
sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*SoftDescriptorSet,
dynamic_offsets: [base.VULKAN_MAX_DESCRIPTOR_SETS][MAX_DYNAMIC_DESCRIPTORS_PER_SET]u32,
push_constant_blob: [lib.PUSH_CONSTANT_SIZE]u8,
data: union {
compute: struct {},
@@ -45,6 +47,7 @@ pub fn setup(self: *Self, device: *SoftDevice) void {
state.* = .{
.pipeline = null,
.sets = [_]?*SoftDescriptorSet{null} ** base.VULKAN_MAX_DESCRIPTOR_SETS,
.dynamic_offsets = [_][MAX_DYNAMIC_DESCRIPTORS_PER_SET]u32{[_]u32{0} ** MAX_DYNAMIC_DESCRIPTORS_PER_SET} ** base.VULKAN_MAX_DESCRIPTOR_SETS,
.push_constant_blob = @splat(0),
.data = switch (i) {
GRAPHICS_PIPELINE_STATE => .{
@@ -71,37 +74,66 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
switch (binding) {
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
if (buffer_data.object) |buffer| {
const map = buffer.mapAsSliceWithAddedOffset(u8, buffer_data.offset, buffer_data.size) catch continue :bindings;
try rt.writeDescriptorSet(
const binding_layout = set.?.interface.layout.bindings[binding_index];
const dynamic_offset: vk.DeviceSize = switch (binding_layout.descriptor_type) {
.uniform_buffer_dynamic, .storage_buffer_dynamic => state.dynamic_offsets[set_index][binding_layout.dynamic_index + descriptor_index],
else => 0,
};
const map = buffer.mapAsSliceWithAddedOffset(u8, buffer_data.offset + dynamic_offset, buffer_data.size) catch continue :bindings;
rt.writeDescriptorSet(
map,
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
) catch |err| switch (err) {
error.NotFound => {},
else => return err,
};
}
},
.image => |image_data_array| for (image_data_array, 0..) |image_data, descriptor_index| {
if (image_data.object) |image_view| {
const addr: usize = @intFromPtr(image_view);
try rt.writeDescriptorSet(
rt.writeDescriptorSet(
std.mem.asBytes(&addr),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
) catch |err| switch (err) {
error.NotFound => {},
else => return err,
};
}
},
.sampler => |sampler_data_array| for (sampler_data_array, 0..) |sampler_data, descriptor_index| {
if (sampler_data.object) |sampler| {
const addr: usize = @intFromPtr(sampler);
rt.writeDescriptorSet(
std.mem.asBytes(&addr),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
) catch |err| switch (err) {
error.NotFound => {},
else => return err,
};
}
},
.texel_buffer => |texel_data_array| for (texel_data_array, 0..) |texel_data, descriptor_index| {
if (texel_data.object) |buffer_view| {
const addr: usize = @intFromPtr(buffer_view);
try rt.writeDescriptorSet(
rt.writeDescriptorSet(
std.mem.asBytes(&addr),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
) catch |err| switch (err) {
error.NotFound => {},
else => return err,
};
}
},
@@ -111,7 +143,10 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
sampler: usize,
};
var data: SampledImage = undefined;
var data: SampledImage = .{
.image = 0,
.sampler = 0,
};
if (texture_data.view) |image_view| {
const addr: usize = @intFromPtr(image_view);
@@ -122,12 +157,15 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
data.sampler = addr;
}
try rt.writeDescriptorSet(
rt.writeDescriptorSet(
std.mem.asBytes(&data),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
) catch |err| switch (err) {
error.NotFound => {},
else => return err,
};
},
else => {},
+1
View File
@@ -50,6 +50,7 @@ pub const Vertex = struct {
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]?struct {
interpolation_type: enum { smooth, flat, noperspective },
blob: []u8,
size: usize,
},
};
+8 -5
View File
@@ -10,6 +10,7 @@ const Renderer = @import("Renderer.zig");
const Vertex = Renderer.Vertex;
const VkError = base.VkError;
const INTERFACE_BLOB_PADDING = @sizeOf(F32x4);
const ClipPlane = enum {
Left,
@@ -142,9 +143,10 @@ fn isVertexInsidePlane(vertex: *const Vertex, plane: ClipPlane) bool {
return clipDistance(vertex.position, plane) >= 0.0;
}
fn interpolateBlob(allocator: std.mem.Allocator, a: []const u8, b: []const u8, t: f32) VkError![]u8 {
const len = @min(a.len, b.len);
const result = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory;
fn interpolateBlob(allocator: std.mem.Allocator, a: []const u8, b: []const u8, size: usize, t: f32) VkError![]u8 {
const len = @min(size, a.len, b.len);
const result = allocator.alloc(u8, len + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory;
@memset(result, 0);
var byte_index: usize = 0;
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
@@ -160,7 +162,7 @@ fn interpolateBlob(allocator: std.mem.Allocator, a: []const u8, b: []const u8, t
}
if (byte_index < len)
@memcpy(result[byte_index..], a[byte_index..len]);
@memcpy(result[byte_index..len], a[byte_index..len]);
return result;
}
@@ -182,7 +184,8 @@ fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex,
.blob = if (out_a.interpolation_type == .flat)
allocator.dupe(u8, out_a.blob) catch return VkError.OutOfDeviceMemory
else
try interpolateBlob(allocator, out_a.blob, out_b.blob, t),
try interpolateBlob(allocator, out_a.blob, out_b.blob, @min(out_a.size, out_b.size), t),
.size = @min(out_a.size, out_b.size),
};
}
+34 -4
View File
@@ -11,6 +11,7 @@ const SoftImage = @import("../SoftImage.zig");
const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
const INTERFACE_BLOB_PADDING = @sizeOf(zm.F32x4);
pub fn shaderInvocation(
allocator: std.mem.Allocator,
@@ -19,9 +20,11 @@ pub fn shaderInvocation(
position: zm.F32x4,
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation,
) SpvRuntimeError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 {
var fragment_inputs = inputs;
errdefer freeOwnedInputs(allocator, fragment_inputs);
const io = draw_call.renderer.device.interface.io();
_ = position;
const pipeline = draw_call.renderer.state.pipeline orelse return undefined;
const shader = pipeline.stages.getPtr(.fragment) orelse return undefined;
@@ -32,7 +35,12 @@ pub fn shaderInvocation(
mutex.lock(io) catch return SpvRuntimeError.Unknown;
defer mutex.unlock(io);
rt.resetInvocation(allocator);
try rt.populatePushConstants(draw_call.renderer.state.push_constant_blob[0..]);
rt.writeBuiltIn(std.mem.asBytes(&position), .FragCoord) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
const entry = try rt.getEntryPointByName(shader.entry);
@@ -41,9 +49,23 @@ pub fn shaderInvocation(
SpvRuntimeError.NotFound => continue,
else => return err,
};
try rt.writeInput(inputs[location].blob, result_word);
if (inputs[location].free_responsability)
allocator.free(inputs[location].blob);
var input = fragment_inputs[location];
if (input.blob.len == 0) {
const memory_size = try rt.getResultMemorySize(result_word);
const zeroes = allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return SpvRuntimeError.OutOfMemory;
@memset(zeroes, 0);
fragment_inputs[location] = .{
.blob = zeroes,
.size = memory_size,
.free_responsability = true,
};
input = fragment_inputs[location];
}
if (input.blob.len != 0) {
try rt.writeInput(input.blob, result_word);
}
}
rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
@@ -66,6 +88,14 @@ pub fn shaderInvocation(
}
try rt.flushDescriptorSets(allocator);
freeOwnedInputs(allocator, fragment_inputs);
return outputs;
}
fn freeOwnedInputs(allocator: std.mem.Allocator, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation) void {
for (inputs) |input| {
if (input.free_responsability)
allocator.free(input.blob);
}
}
+68 -1
View File
@@ -1,12 +1,15 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const zm = base.zm;
const clip = @import("clip.zig");
const bresenham = @import("rasterizer/bresenham.zig");
const edge_function = @import("rasterizer/edge_function.zig");
const common = @import("rasterizer/common.zig");
const fragment = @import("fragment.zig");
const blitter = @import("blitter.zig");
const Renderer = @import("Renderer.zig");
const Vertex = Renderer.Vertex;
@@ -21,7 +24,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_attachments = draw_call.render_pass.interface.subpasses[renderer.subpass_index].color_attachments orelse return VkError.InvalidAttachmentDrv;
const color_attachments = draw_call.render_pass.interface.subpasses[renderer.subpass_index].color_attachments orelse &.{};
const color_attachment_access = allocator.alloc(?common.RenderTargetAccess, color_attachments.len) catch return VkError.OutOfDeviceMemory;
@memset(color_attachment_access, null);
@@ -76,6 +79,15 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
};
switch (topology) {
.point_list => for (draw_call.vertices) |*vertex| {
try clipTransformAndRasterizePoint(
allocator,
draw_call,
vertex,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
);
},
.triangle_list => for (0..@divTrunc(draw_call.vertices.len, 3)) |triangle_index| {
const first_vertex = triangle_index * 3;
const v0 = &draw_call.vertices[first_vertex + 0];
@@ -177,6 +189,61 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
}
fn clipTransformAndRasterizePoint(
allocator: std.mem.Allocator,
draw_call: *DrawCall,
vertex: *Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
const x, const y, const z, const w = vertex.position;
if (w == 0.0 or x < -w or x > w or y < -w or y > w or z < 0.0 or z > w)
return;
var transformed = vertex.*;
clip.viewportTransformVertex(draw_call.viewport, &transformed);
const point_size = 1.0;
const min_x: i32 = @intFromFloat(@floor(transformed.position[0] - (point_size / 2.0)));
const max_x: i32 = @intFromFloat(@ceil(transformed.position[0] + (point_size / 2.0)) - 1.0);
const min_y: i32 = @intFromFloat(@floor(transformed.position[1] - (point_size / 2.0)));
const max_y: i32 = @intFromFloat(@ceil(transformed.position[1] + (point_size / 2.0)) - 1.0);
var py = min_y;
while (py <= max_y) : (py += 1) {
var px = min_x;
while (px <= max_x) : (px += 1) {
if (!common.scissorContainsPixel(draw_call.scissor, px, py))
continue;
if (depth_attachment_access) |depth| {
const offset = @as(usize, @intCast(px)) * depth.texel_size + @as(usize, @intCast(py)) * depth.row_pitch;
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
if (transformed.position[2] >= depth_value[0])
continue;
}
const outputs = fragment.shaderInvocation(
allocator,
draw_call,
0,
zm.f32x4(@floatFromInt(px), @floatFromInt(py), transformed.position[2], 1.0),
try common.interpolateVertexOutputs(allocator, &transformed, &transformed, &transformed, 1.0, 0.0, 0.0),
) catch |err| {
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
if (comptime base.config.logs == .verbose) {
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
}
return;
};
try common.writeToTargets(outputs, draw_call, color_attachment_access, depth_attachment_access, @intCast(px), @intCast(py), transformed.position[2]);
}
}
}
fn clipTransformAndRasterizeLine(
allocator: std.mem.Allocator,
draw_call: *DrawCall,
+13 -7
View File
@@ -21,6 +21,7 @@ pub const RenderTargetAccess = struct {
pub const VertexInterpolation = struct {
blob: []const u8,
size: usize,
free_responsability: bool,
};
@@ -49,20 +50,25 @@ pub fn interpolateVertexOutputs(
b1: f32,
b2: f32,
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation {
var inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation = undefined;
var inputs = [_]VertexInterpolation{.{
.blob = &.{},
.size = 0,
.free_responsability = false,
}} ** spv.SPIRV_MAX_OUTPUT_LOCATIONS;
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
const out0 = v0.outputs[location] orelse continue;
const out1 = v1.outputs[location] orelse continue;
const out2 = v2.outputs[location] orelse continue;
if (out0.interpolation_type == .flat or out0.blob.len == 0) {
inputs[location] = .{ .blob = out0.blob, .free_responsability = false };
if (out0.interpolation_type == .flat or out0.size == 0) {
inputs[location] = .{ .blob = out0.blob, .size = out0.size, .free_responsability = false };
continue;
}
const len = @min(out0.blob.len, out1.blob.len, out2.blob.len);
const input = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory;
const len = @min(out0.size, out1.size, out2.size);
const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory;
@memset(input, 0);
var byte_index: usize = 0;
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
@@ -80,9 +86,9 @@ pub fn interpolateVertexOutputs(
}
if (byte_index < len)
@memcpy(input[byte_index..], out0.blob[byte_index..len]);
@memcpy(input[byte_index..len], out0.blob[byte_index..len]);
inputs[location] = .{ .blob = input, .free_responsability = true };
inputs[location] = .{ .blob = input, .size = len, .free_responsability = true };
}
return inputs;
+53 -6
View File
@@ -1,8 +1,9 @@
const std = @import("std");
const spv = @import("spv");
const base = @import("base");
const vk = @import("vulkan");
const F32x4 = Renderer.F32x4;
const F32x4 = base.zm.F32x4;
const SpvRuntimeError = spv.Runtime.RuntimeError;
@@ -10,6 +11,7 @@ const Renderer = @import("Renderer.zig");
const SoftPipeline = @import("../SoftPipeline.zig");
const VkError = base.VkError;
const INTERFACE_BLOB_PADDING = @sizeOf(F32x4);
pub const RunData = struct {
allocator: std.mem.Allocator,
@@ -38,12 +40,14 @@ pub fn runWrapper(data: RunData) void {
inline fn run(data: RunData) !void {
const shader = data.pipeline.stages.getPtrAssertContains(.vertex);
const rt = &shader.runtimes[data.batch_id].rt;
try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]);
const entry = try rt.getEntryPointByName(shader.entry);
var invocation_index: usize = data.batch_id;
while (invocation_index < data.vertex_count) : (invocation_index += data.batch_size) {
rt.resetInvocation(data.allocator);
try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]);
const vertex_index: usize = if (data.indices) |indices| @intCast(indices[invocation_index]) else data.first_vertex + invocation_index;
const instance_index = data.first_instance + data.instance_index;
@@ -54,8 +58,6 @@ inline fn run(data: RunData) !void {
if (data.pipeline.interface.mode.graphics.input_assembly.attribute_description) |attributes| {
for (attributes) |attribute| {
const location_result = try rt.getResultByLocation(attribute.location, .input);
const binding_info = (data.pipeline.interface.mode.graphics.input_assembly.binding_description orelse return)[attribute.binding];
const vertex_buffer = data.draw_call.renderer.state.data.graphics.vertex_buffers[attribute.binding];
@@ -66,7 +68,7 @@ inline fn run(data: RunData) !void {
const buffer_memory_map: []u8 = try buffer_memory.map(offset, buffer_memory_size);
try rt.writeInput(buffer_memory_map, location_result);
try writeVertexInput(rt, data.allocator, buffer_memory_map, attribute.format, attribute.location);
}
}
@@ -86,10 +88,13 @@ inline fn run(data: RunData) !void {
SpvRuntimeError.NotFound => continue,
else => return err,
};
const memory_size = try rt.getResultMemorySize(result_word);
output.outputs[location] = .{
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat)) .flat else .smooth, // TODO : handle noperspective
.blob = data.allocator.alloc(u8, try rt.getResultMemorySize(result_word)) catch return VkError.OutOfDeviceMemory,
.blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory,
.size = memory_size,
};
@memset(output.outputs[location].?.blob, 0);
try rt.readOutput(output.outputs[location].?.blob, result_word);
}
@@ -104,3 +109,45 @@ fn setupBuiltins(rt: *spv.Runtime, vertex_index: usize, instance_index: usize) !
try rt.writeBuiltIn(std.mem.asBytes(&vertex_index_u32), .VertexIndex);
try rt.writeBuiltIn(std.mem.asBytes(&instance_index_u32), .InstanceIndex);
}
fn writeVertexInput(
rt: *spv.Runtime,
allocator: std.mem.Allocator,
raw_input: []const u8,
format: vk.Format,
location: u32,
) !void {
const input_memory_size = try rt.getInputLocationMemorySize(location);
if (raw_input.len >= input_memory_size) {
try rt.writeInputLocation(raw_input[0..input_memory_size], location);
return;
}
const input = allocator.alloc(u8, input_memory_size) catch return VkError.OutOfDeviceMemory;
defer allocator.free(input);
@memset(input, 0);
@memcpy(input[0..raw_input.len], raw_input);
fillMissingVertexComponents(input, raw_input.len, format);
try rt.writeInputLocation(input, location);
}
fn fillMissingVertexComponents(input: []u8, raw_input_size: usize, format: vk.Format) void {
if (input.len < @sizeOf(F32x4) or raw_input_size > 3 * @sizeOf(f32))
return;
const component_count = base.format.componentCount(format);
if (component_count >= 4)
return;
const alpha_offset = 3 * @sizeOf(f32);
if (base.format.isUnnormalizedInteger(format)) {
const one: u32 = 1;
@memcpy(input[alpha_offset .. alpha_offset + @sizeOf(u32)], std.mem.asBytes(&one));
} else {
const one: f32 = 1.0;
@memcpy(input[alpha_offset .. alpha_offset + @sizeOf(f32)], std.mem.asBytes(&one));
}
}
+7 -5
View File
@@ -108,12 +108,14 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
}
pub fn begin(self: *Self, info: *const vk.CommandBufferBeginInfo) VkError!void {
if (!self.pool.flags.reset_command_buffer_bit) {
self.transitionState(.Recording, &.{.Initial}) catch return VkError.ValidationFailed;
} else {
try self.reset(.{});
self.transitionState(.Recording, &.{ .Initial, .Recording, .Executable, .Invalid }) catch return VkError.ValidationFailed;
const implicitly_reset = self.state == .Executable;
self.transitionState(.Recording, &.{ .Initial, .Executable }) catch return VkError.ValidationFailed;
if (implicitly_reset) {
try self.dispatch_table.reset(self, .{});
self.begin_info = null;
}
try self.dispatch_table.begin(self, info);
self.begin_info = info.*;
}
+42 -5
View File
@@ -3,6 +3,7 @@ const vk = @import("vulkan");
const VulkanAllocator = @import("VulkanAllocator.zig");
const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
const VkError = @import("error_set.zig").VkError;
const Device = @import("Device.zig");
const Sampler = @import("Sampler.zig");
@@ -16,7 +17,7 @@ const BindingLayout = struct {
array_size: usize,
/// This slice points to an array located after the binding layouts array
immutable_samplers: []*const Sampler,
immutable_samplers: []const *const Sampler,
driver_data: *anyopaque,
};
@@ -68,8 +69,21 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Descr
const bindings = local_allocator.alloc(BindingLayout, binding_count) catch return VkError.OutOfHostMemory;
const immutable_samplers = local_allocator.alloc(*const Sampler, immutable_samplers_count) catch return VkError.OutOfHostMemory;
var immutable_samplers_offset: usize = 0;
for (bindings) |*binding| {
binding.* = .{
.descriptor_type = .sampler,
.array_size = 0,
.dynamic_index = 0,
.immutable_samplers = &.{},
.driver_data = undefined,
};
}
var stages: vk.ShaderStageFlags = .{};
var dynamic_descriptor_count: usize = 0;
var dynamic_offset_count: usize = 0;
if (info.p_bindings) |binding_infos| {
const sorted_bindings = command_allocator.dupe(vk.DescriptorSetLayoutBinding, binding_infos[0..info.binding_count]) catch return VkError.OutOfHostMemory;
@@ -84,11 +98,27 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Descr
else => binding_info.descriptor_count,
};
const binding_immutable_samplers = if (bindingHasImmutableSamplers(binding_info)) blk: {
const base = binding_info.p_immutable_samplers orelse return VkError.ValidationFailed;
const binding_immutable_samplers = immutable_samplers[immutable_samplers_offset .. immutable_samplers_offset + descriptor_count];
immutable_samplers_offset += descriptor_count;
for (binding_immutable_samplers, base[0..descriptor_count]) |*dst, src| {
dst.* = try NonDispatchable(Sampler).fromHandleObject(src);
}
break :blk binding_immutable_samplers;
} else &.{};
const dynamic_index = dynamic_descriptor_count;
if (bindingHasDynamicOffset(binding_info)) {
dynamic_descriptor_count += descriptor_count;
dynamic_offset_count += descriptor_count;
}
bindings[binding_index] = .{
.descriptor_type = binding_info.descriptor_type,
.array_size = descriptor_count,
.dynamic_index = 0,
.immutable_samplers = immutable_samplers[0..],
.dynamic_index = dynamic_index,
.immutable_samplers = binding_immutable_samplers,
.driver_data = undefined,
};
@@ -100,8 +130,8 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Descr
.owner = device,
.heap = heap,
.bindings = bindings,
.dynamic_offset_count = 0,
.dynamic_descriptor_count = 0,
.dynamic_offset_count = dynamic_offset_count,
.dynamic_descriptor_count = dynamic_descriptor_count,
.stages = stages,
.ref_count = std.atomic.Value(usize).init(1),
.vtable = undefined,
@@ -119,6 +149,13 @@ inline fn bindingHasImmutableSamplers(binding: vk.DescriptorSetLayoutBinding) bo
};
}
inline fn bindingHasDynamicOffset(binding: vk.DescriptorSetLayoutBinding) bool {
return switch (binding.descriptor_type) {
.uniform_buffer_dynamic, .storage_buffer_dynamic => true,
else => false,
};
}
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
self.unref(allocator);
}
+1 -1
View File
@@ -34,7 +34,7 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Frame
for (base_attachements, attachments, 0..info.attachment_count) |base_attachment, *attachment, _| {
attachment.* = try NonDispatchable(ImageView).fromHandleObject(base_attachment);
}
} else {
} else if (info.attachment_count != 0) {
return VkError.ValidationFailed;
}
+6 -1
View File
@@ -28,6 +28,7 @@ vtable: *const VTable,
pub const VTable = struct {
destroy: *const fn (*Self, std.mem.Allocator) void,
getRenderAreaGranularity: *const fn (*Self) vk.Extent2D,
};
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.RenderPassCreateInfo) VkError!Self {
@@ -40,7 +41,7 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Rende
for (base_attachements, attachments, 0..info.attachment_count) |base_attachment, *attachment, _| {
attachment.* = base_attachment;
}
} else {
} else if (info.attachment_count != 0) {
return VkError.ValidationFailed;
}
@@ -93,3 +94,7 @@ pub fn destroy(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.subpasses);
self.vtable.destroy(self, allocator);
}
pub inline fn getRenderAreaGranularity(self: *Self) vk.Extent2D {
return self.vtable.getRenderAreaGranularity(self);
}
+2
View File
@@ -16,6 +16,7 @@ min_filter: vk.Filter,
address_mode_u: vk.SamplerAddressMode,
address_mode_v: vk.SamplerAddressMode,
address_mode_w: vk.SamplerAddressMode,
border_color: vk.BorderColor,
vtable: *const VTable,
@@ -32,6 +33,7 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Sampl
.address_mode_u = info.address_mode_u,
.address_mode_v = info.address_mode_v,
.address_mode_w = info.address_mode_w,
.border_color = info.border_color,
.vtable = undefined,
};
}
+4
View File
@@ -40,6 +40,10 @@ pub inline fn texelSize(format: vk.Format) usize {
return lib.c.vkuFormatTexelBlockSize(@intCast(@intFromEnum(format)));
}
pub inline fn componentCount(format: vk.Format) usize {
return @intCast(lib.c.vkuFormatComponentCount(@intCast(@intFromEnum(format))));
}
pub fn supportsColorAttachemendBlend(format: vk.Format) bool {
return switch (format) {
// Vulkan 1.1 mandatory
+2 -4
View File
@@ -1470,11 +1470,9 @@ pub export fn apeGetRenderAreaGranularity(p_device: vk.Device, p_pass: vk.Render
defer entryPointEndLogTrace();
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err);
const pass = NonDispatchable(RenderPass).fromHandleObject(p_pass) catch |err| return errorLogger(err);
notImplementedWarning();
_ = p_pass;
_ = granularity;
granularity.* = pass.getRenderAreaGranularity();
}
pub export fn apeInvalidateMappedMemoryRanges(p_device: vk.Device, count: u32, p_ranges: [*]const vk.MappedMemoryRange) callconv(vk.vulkan_call_conv) vk.Result {