yes
This commit is contained in:
@@ -180,7 +180,7 @@ vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP
|
|||||||
vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP
|
vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP
|
||||||
vkGetPipelineCacheData | ⚙️ WIP
|
vkGetPipelineCacheData | ⚙️ WIP
|
||||||
vkGetQueryPoolResults | ⚙️ WIP
|
vkGetQueryPoolResults | ⚙️ WIP
|
||||||
vkGetRenderAreaGranularity | ⚙️ WIP
|
vkGetRenderAreaGranularity | ✅ Implemented
|
||||||
vkGetSwapchainImagesKHR | ✅ Implemented
|
vkGetSwapchainImagesKHR | ✅ Implemented
|
||||||
vkInvalidateMappedMemoryRanges | ✅ Implemented
|
vkInvalidateMappedMemoryRanges | ✅ Implemented
|
||||||
vkMapMemory | ✅ Implemented
|
vkMapMemory | ✅ Implemented
|
||||||
@@ -200,7 +200,7 @@ vkUpdateDescriptorSets | ✅ Implemented
|
|||||||
vkWaitForFences | ✅ Implemented
|
vkWaitForFences | ✅ Implemented
|
||||||
</details>
|
</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
|
## License
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -26,8 +26,8 @@
|
|||||||
.hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3",
|
.hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3",
|
||||||
},
|
},
|
||||||
.SPIRV_Interpreter = .{
|
.SPIRV_Interpreter = .{
|
||||||
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#9c355fe126d0142ac6d1fae48633993864c90d61",
|
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#8677e8a6833d39e9169460fd75de2d7c70fab4b8",
|
||||||
.hash = "SPIRV_Interpreter-0.0.1-ajmpn867BQA-J2PA4fGQGnb3WcNtqd_3_W7-goaEocC5",
|
.hash = "SPIRV_Interpreter-0.0.1-ajmpn_HtBQBR82M2KlR_yuUvdHUVdXrYVGRh5YNqVFqQ",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
//.SPIRV_Interpreter = .{
|
//.SPIRV_Interpreter = .{
|
||||||
|
|||||||
@@ -231,21 +231,39 @@ pub fn bindDescriptorSets(interface: *Interface, bind_point: vk.PipelineBindPoin
|
|||||||
|
|
||||||
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));
|
||||||
|
var dynamic_offset_index: usize = 0;
|
||||||
for (impl.first_set.., impl.sets[0..]) |i, set| {
|
for (impl.first_set.., impl.sets[0..]) |i, set| {
|
||||||
if (set == null)
|
if (set == null)
|
||||||
break;
|
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;
|
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
|
||||||
errdefer allocator.destroy(cmd);
|
errdefer allocator.destroy(cmd);
|
||||||
cmd.* = .{
|
cmd.* = .{
|
||||||
.bind_point = bind_point,
|
.bind_point = bind_point,
|
||||||
.first_set = first_set,
|
.first_set = first_set,
|
||||||
.sets = sets,
|
.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;
|
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ const DescriptorImage = struct {
|
|||||||
object: ?*SoftImageView,
|
object: ?*SoftImageView,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DescriptorSampler = struct {
|
||||||
|
object: ?*SoftSampler,
|
||||||
|
};
|
||||||
|
|
||||||
const DescriptorTexel = struct {
|
const DescriptorTexel = struct {
|
||||||
object: ?*SoftBufferView,
|
object: ?*SoftBufferView,
|
||||||
};
|
};
|
||||||
@@ -42,6 +46,7 @@ const Descriptor = union(enum) {
|
|||||||
buffer: []DescriptorBuffer,
|
buffer: []DescriptorBuffer,
|
||||||
texture: []DescriptorTexture,
|
texture: []DescriptorTexture,
|
||||||
image: []DescriptorImage,
|
image: []DescriptorImage,
|
||||||
|
sampler: []DescriptorSampler,
|
||||||
texel_buffer: []DescriptorTexel,
|
texel_buffer: []DescriptorTexel,
|
||||||
unsupported: struct {},
|
unsupported: struct {},
|
||||||
};
|
};
|
||||||
@@ -70,14 +75,19 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
|
|||||||
for (layout.bindings) |binding| {
|
for (layout.bindings) |binding| {
|
||||||
const struct_size: usize = switch (binding.descriptor_type) {
|
const struct_size: usize = switch (binding.descriptor_type) {
|
||||||
.uniform_buffer,
|
.uniform_buffer,
|
||||||
|
.uniform_buffer_dynamic,
|
||||||
.storage_buffer,
|
.storage_buffer,
|
||||||
.storage_buffer_dynamic,
|
.storage_buffer_dynamic,
|
||||||
=> @sizeOf(DescriptorBuffer),
|
=> @sizeOf(DescriptorBuffer),
|
||||||
|
|
||||||
|
.sampled_image,
|
||||||
.storage_image,
|
.storage_image,
|
||||||
.input_attachment,
|
.input_attachment,
|
||||||
=> @sizeOf(DescriptorImage),
|
=> @sizeOf(DescriptorImage),
|
||||||
|
|
||||||
|
.sampler,
|
||||||
|
=> @sizeOf(DescriptorSampler),
|
||||||
|
|
||||||
.storage_texel_buffer,
|
.storage_texel_buffer,
|
||||||
.uniform_texel_buffer,
|
.uniform_texel_buffer,
|
||||||
=> @sizeOf(DescriptorTexel),
|
=> @sizeOf(DescriptorTexel),
|
||||||
@@ -103,6 +113,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
|
|||||||
for (descriptors, layout.bindings) |*descriptor, binding| {
|
for (descriptors, layout.bindings) |*descriptor, binding| {
|
||||||
switch (binding.descriptor_type) {
|
switch (binding.descriptor_type) {
|
||||||
.uniform_buffer,
|
.uniform_buffer,
|
||||||
|
.uniform_buffer_dynamic,
|
||||||
.storage_buffer,
|
.storage_buffer,
|
||||||
.storage_buffer_dynamic,
|
.storage_buffer_dynamic,
|
||||||
=> descriptor.* = blk: {
|
=> descriptor.* = blk: {
|
||||||
@@ -119,6 +130,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
|
|||||||
break :blk desc;
|
break :blk desc;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.sampled_image,
|
||||||
.storage_image,
|
.storage_image,
|
||||||
.input_attachment,
|
.input_attachment,
|
||||||
=> descriptor.* = blk: {
|
=> descriptor.* = blk: {
|
||||||
@@ -131,6 +143,22 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
|
|||||||
break :blk desc;
|
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,
|
.storage_texel_buffer,
|
||||||
.uniform_texel_buffer,
|
.uniform_texel_buffer,
|
||||||
=> descriptor.* = blk: {
|
=> descriptor.* = blk: {
|
||||||
@@ -148,9 +176,12 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
|
|||||||
const desc: Descriptor = .{
|
const desc: Descriptor = .{
|
||||||
.texture = local_allocator.alloc(DescriptorTexture, binding.array_size) catch return VkError.OutOfHostMemory,
|
.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.* = .{
|
d.* = .{
|
||||||
.sampler = null,
|
.sampler = if (i < binding.immutable_samplers.len)
|
||||||
|
@as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(binding.immutable_samplers[i]))))
|
||||||
|
else
|
||||||
|
null,
|
||||||
.view = null,
|
.view = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -179,6 +210,7 @@ fn descriptorLen(descriptor: Descriptor) usize {
|
|||||||
return switch (descriptor) {
|
return switch (descriptor) {
|
||||||
.buffer => |buffer| buffer.len,
|
.buffer => |buffer| buffer.len,
|
||||||
.image => |image| image.len,
|
.image => |image| image.len,
|
||||||
|
.sampler => |sampler| sampler.len,
|
||||||
.texel_buffer => |texel_buffer| texel_buffer.len,
|
.texel_buffer => |texel_buffer| texel_buffer.len,
|
||||||
.texture => |texture| texture.len,
|
.texture => |texture| texture.len,
|
||||||
.unsupported => 0,
|
.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| {
|
.texel_buffer => |dst_texel_buffer| {
|
||||||
const src_texel_buffer = switch (src_desc) {
|
const src_texel_buffer = switch (src_desc) {
|
||||||
.texel_buffer => |texel_buffer| texel_buffer,
|
.texel_buffer => |texel_buffer| texel_buffer,
|
||||||
@@ -255,6 +298,33 @@ fn copyDescriptorRange(dst_desc: *Descriptor, dst_array_element: usize, src_desc
|
|||||||
return true;
|
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 {
|
pub fn copy(interface: *Interface, src_interface: *const Interface, data: vk.CopyDescriptorSet) VkError!void {
|
||||||
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||||
const src: *const Self = @alignCast(@fieldParentPtr("interface", src_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 src_remaining = src_len - src_array_element;
|
||||||
const copy_count = @min(descriptor_count, dst_remaining, src_remaining);
|
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", .{});
|
base.unsupported("descriptor type for copy", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -301,11 +371,12 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
|
|||||||
|
|
||||||
switch (write_data.descriptor_type) {
|
switch (write_data.descriptor_type) {
|
||||||
.uniform_buffer,
|
.uniform_buffer,
|
||||||
|
.uniform_buffer_dynamic,
|
||||||
.storage_buffer,
|
.storage_buffer,
|
||||||
.storage_buffer_dynamic,
|
.storage_buffer_dynamic,
|
||||||
=> {
|
=> {
|
||||||
for (write_data.p_buffer_info, 0..write_data.descriptor_count) |buffer_info, i| {
|
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.* = .{
|
desc.* = .{
|
||||||
.object = null,
|
.object = null,
|
||||||
.offset = buffer_info.offset,
|
.offset = buffer_info.offset,
|
||||||
@@ -321,11 +392,12 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.sampled_image,
|
||||||
.storage_image,
|
.storage_image,
|
||||||
.input_attachment,
|
.input_attachment,
|
||||||
=> {
|
=> {
|
||||||
for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| {
|
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 };
|
desc.* = .{ .object = null };
|
||||||
if (image_info.image_view != .null_handle) {
|
if (image_info.image_view != .null_handle) {
|
||||||
const image_view = try NonDispatchable(ImageView).fromHandleObject(image_info.image_view);
|
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,
|
.storage_texel_buffer,
|
||||||
.uniform_texel_buffer,
|
.uniform_texel_buffer,
|
||||||
=> {
|
=> {
|
||||||
for (write_data.p_texel_buffer_view, 0..write_data.descriptor_count) |view, i| {
|
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 };
|
desc.* = .{ .object = null };
|
||||||
if (view != .null_handle) {
|
if (view != .null_handle) {
|
||||||
const buffer_view = try NonDispatchable(BufferView).fromHandleObject(view);
|
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,
|
.combined_image_sampler,
|
||||||
=> {
|
=> {
|
||||||
for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| {
|
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.* = .{
|
desc.* = .{
|
||||||
.sampler = null,
|
.sampler = if (array_element < immutable_samplers.len)
|
||||||
|
@as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(immutable_samplers[array_element]))))
|
||||||
|
else
|
||||||
|
null,
|
||||||
.view = null,
|
.view = null,
|
||||||
};
|
};
|
||||||
if (image_info.image_view != .null_handle) {
|
if (image_info.image_view != .null_handle) {
|
||||||
const image_view = try NonDispatchable(ImageView).fromHandleObject(image_info.image_view);
|
const image_view = try NonDispatchable(ImageView).fromHandleObject(image_info.image_view);
|
||||||
desc.view = @as(*SoftImageView, @alignCast(@fieldParentPtr("interface", 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);
|
const sampler = try NonDispatchable(Sampler).fromHandleObject(image_info.sampler);
|
||||||
desc.sampler = @as(*SoftSampler, @alignCast(@fieldParentPtr("interface", sampler)));
|
desc.sampler = @as(*SoftSampler, @alignCast(@fieldParentPtr("interface", sampler)));
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-42
@@ -382,6 +382,7 @@ const CubeCoordinate = struct {
|
|||||||
face: u32,
|
face: u32,
|
||||||
u: f32,
|
u: f32,
|
||||||
v: f32,
|
v: f32,
|
||||||
|
w: f32 = 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn resolveCubeCoordinate(x: f32, y: f32, z: f32) CubeCoordinate {
|
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(
|
fn readSampledFloat4(
|
||||||
image: *SoftImage,
|
image: *SoftImage,
|
||||||
image_view: *SoftImageView,
|
image_view: *SoftImageView,
|
||||||
|
sampler: *SoftSampler,
|
||||||
dim: spv.SpvDim,
|
dim: spv.SpvDim,
|
||||||
coord: CubeCoordinate,
|
coord: CubeCoordinate,
|
||||||
ix: i32,
|
ix: i32,
|
||||||
iy: i32,
|
iy: i32,
|
||||||
|
iz: i32,
|
||||||
) VkError!zm.F32x4 {
|
) VkError!zm.F32x4 {
|
||||||
const width_f: f32 = @floatFromInt(image.interface.extent.width);
|
const range = image_view.interface.subresource_range;
|
||||||
const height_f: f32 = @floatFromInt(image.interface.extent.height);
|
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 texel = if (dim == .Cube) blk: {
|
||||||
const dir = cubeDirection(
|
const dir = cubeDirection(
|
||||||
@@ -472,61 +477,127 @@ fn readSampledFloat4(
|
|||||||
break :blk resolveCubeCoordinate(dir.x, dir.y, dir.z);
|
break :blk resolveCubeCoordinate(dir.x, dir.y, dir.z);
|
||||||
} else coord;
|
} 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(
|
const result = try image.readFloat4(
|
||||||
.{
|
.{
|
||||||
.x = if (dim == .Cube)
|
.x = sx,
|
||||||
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, image.interface.extent.width - 1)
|
.y = sy,
|
||||||
else
|
.z = z,
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
|
.aspect_mask = range.aspect_mask,
|
||||||
.mip_level = image_view.interface.subresource_range.base_mip_level,
|
.mip_level = range.base_mip_level,
|
||||||
.array_layer = image_view.interface.subresource_range.base_array_layer + texel.face,
|
.array_layer = layer,
|
||||||
},
|
},
|
||||||
image_view.interface.format,
|
image_view.interface.format,
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sampleNearestFloat4(image: *SoftImage, image_view: *SoftImageView, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
|
fn sampleAddress(coord: i32, extent: u32, mode: vk.SamplerAddressMode) i32 {
|
||||||
const width_f: f32 = @floatFromInt(image.interface.extent.width);
|
return sampleAddressOrBorder(coord, extent, mode).?;
|
||||||
const height_f: f32 = @floatFromInt(image.interface.extent.height);
|
}
|
||||||
|
|
||||||
|
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(
|
return readSampledFloat4(
|
||||||
image,
|
image,
|
||||||
image_view,
|
image_view,
|
||||||
|
sampler,
|
||||||
dim,
|
dim,
|
||||||
coord,
|
coord,
|
||||||
@intFromFloat(coord.u * width_f),
|
@intFromFloat(coord.u * width_f),
|
||||||
@intFromFloat(coord.v * height_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 {
|
fn sampleLinearFloat4(image: *SoftImage, image_view: *SoftImageView, sampler: *SoftSampler, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
|
||||||
const width_f: f32 = @floatFromInt(image.interface.extent.width);
|
const extent = image.getMipLevelExtent(image_view.interface.subresource_range.base_mip_level);
|
||||||
const height_f: f32 = @floatFromInt(image.interface.extent.height);
|
const width_f: f32 = @floatFromInt(extent.width);
|
||||||
|
const height_f: f32 = @floatFromInt(extent.height);
|
||||||
const x = coord.u * width_f - 0.5;
|
const x = coord.u * width_f - 0.5;
|
||||||
const y = coord.v * height_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 x0: i32 = @intFromFloat(@floor(x));
|
||||||
const y0: i32 = @intFromFloat(@floor(y));
|
const y0: i32 = @intFromFloat(@floor(y));
|
||||||
|
const z0: i32 = @intFromFloat(@floor(z));
|
||||||
const x1 = x0 + 1;
|
const x1 = x0 + 1;
|
||||||
const y1 = y0 + 1;
|
const y1 = y0 + 1;
|
||||||
|
const z1 = z0 + 1;
|
||||||
const wx = x - @as(f32, @floatFromInt(x0));
|
const wx = x - @as(f32, @floatFromInt(x0));
|
||||||
const wy = y - @as(f32, @floatFromInt(y0));
|
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 p000 = try readSampledFloat4(image, image_view, sampler, dim, coord, x0, y0, z0);
|
||||||
const p10 = try readSampledFloat4(image, image_view, dim, coord, x1, y0);
|
const p100 = try readSampledFloat4(image, image_view, sampler, dim, coord, x1, y0, z0);
|
||||||
const p01 = try readSampledFloat4(image, image_view, dim, coord, x0, y1);
|
const p010 = try readSampledFloat4(image, image_view, sampler, dim, coord, x0, y1, z0);
|
||||||
const p11 = try readSampledFloat4(image, image_view, dim, coord, x1, y1);
|
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 row00 = p000 * zm.f32x4s(1.0 - wx) + p100 * zm.f32x4s(wx);
|
||||||
const row1 = p01 * zm.f32x4s(1.0 - wx) + p11 * zm.f32x4s(wx);
|
const row10 = p010 * zm.f32x4s(1.0 - wx) + p110 * zm.f32x4s(wx);
|
||||||
return row0 * zm.f32x4s(1.0 - wy) + row1 * zm.f32x4s(wy);
|
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) {
|
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) {
|
if (dim == .Cube) {
|
||||||
const coord = resolveCubeCoordinate(x, y, z);
|
const coord = resolveCubeCoordinate(x, y, z);
|
||||||
pixel = switch (sampler.interface.mag_filter) {
|
pixel = switch (sampler.interface.mag_filter) {
|
||||||
.linear => sampleLinearFloat4(image, image_view, dim, coord),
|
.linear => sampleLinearFloat4(image, image_view, sampler, dim, coord),
|
||||||
else => sampleNearestFloat4(image, image_view, dim, coord),
|
else => sampleNearestFloat4(image, image_view, sampler, dim, coord),
|
||||||
} catch return SpvRuntimeError.Unknown;
|
} catch return SpvRuntimeError.Unknown;
|
||||||
} else {
|
} else {
|
||||||
pixel = image.readFloat4(
|
const coord: CubeCoordinate = .{
|
||||||
.{
|
.u = x,
|
||||||
.x = std.math.clamp(@as(i32, @intFromFloat(x * @as(f32, @floatFromInt(image.interface.extent.width)))), 0, image.interface.extent.width - 1),
|
.v = y,
|
||||||
.y = std.math.clamp(@as(i32, @intFromFloat(y * @as(f32, @floatFromInt(image.interface.extent.height)))), 0, image.interface.extent.height - 1),
|
.w = z,
|
||||||
.z = std.math.clamp(@as(i32, @intFromFloat(z * @as(f32, @floatFromInt(image.interface.extent.depth)))), 0, image.interface.extent.depth - 1),
|
.face = 0,
|
||||||
},
|
};
|
||||||
.{
|
pixel = switch (sampler.interface.mag_filter) {
|
||||||
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
|
.linear => sampleLinearFloat4(image, image_view, sampler, dim, coord),
|
||||||
.mip_level = image_view.interface.subresource_range.base_mip_level,
|
else => sampleNearestFloat4(image, image_view, sampler, dim, coord),
|
||||||
.array_layer = image_view.interface.subresource_range.base_array_layer,
|
} catch return SpvRuntimeError.Unknown;
|
||||||
},
|
|
||||||
image_view.interface.format,
|
|
||||||
) catch return SpvRuntimeError.Unknown;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
|
|||||||
|
|
||||||
interface.vtable = &.{
|
interface.vtable = &.{
|
||||||
.destroy = destroy,
|
.destroy = destroy,
|
||||||
|
.getRenderAreaGranularity = getRenderAreaGranularity,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.* = .{
|
self.* = .{
|
||||||
@@ -30,3 +31,10 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
|||||||
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||||
allocator.destroy(self);
|
allocator.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getRenderAreaGranularity(_: *Interface) vk.Extent2D {
|
||||||
|
return .{
|
||||||
|
.width = 1,
|
||||||
|
.height = 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -157,9 +157,10 @@ inline fn run(data: RunData) !void {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try setupWorkgroupBuiltins(data.self, rt, group_count_vec, group_id_vec);
|
|
||||||
|
|
||||||
for (0..data.invocations_per_workgroup) |i| {
|
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);
|
const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic);
|
||||||
|
|
||||||
try setupSubgroupBuiltins(data.self, rt, .{
|
try setupSubgroupBuiltins(data.self, rt, .{
|
||||||
@@ -202,6 +203,7 @@ fn runBarrierWorkgroup(
|
|||||||
const allocator = data.self.device.device_allocator.allocator();
|
const allocator = data.self.device.device_allocator.allocator();
|
||||||
|
|
||||||
for (runtimes, 0..) |*rt, i| {
|
for (runtimes, 0..) |*rt, i| {
|
||||||
|
rt.resetInvocation(allocator);
|
||||||
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
|
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
|
||||||
try setupWorkgroupBuiltins(data.self, rt, group_count, group_id);
|
try setupWorkgroupBuiltins(data.self, rt, group_count, group_id);
|
||||||
try setupSubgroupBuiltins(data.self, rt, group_id, i);
|
try setupSubgroupBuiltins(data.self, rt, group_id, i);
|
||||||
|
|||||||
+48
-10
@@ -19,10 +19,12 @@ const Self = @This();
|
|||||||
|
|
||||||
pub const GRAPHICS_PIPELINE_STATE = 0;
|
pub const GRAPHICS_PIPELINE_STATE = 0;
|
||||||
pub const COMPUTE_PIPELINE_STATE = 1;
|
pub const COMPUTE_PIPELINE_STATE = 1;
|
||||||
|
pub const MAX_DYNAMIC_DESCRIPTORS_PER_SET = 64;
|
||||||
|
|
||||||
pub const PipelineState = struct {
|
pub const PipelineState = struct {
|
||||||
pipeline: ?*SoftPipeline,
|
pipeline: ?*SoftPipeline,
|
||||||
sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*SoftDescriptorSet,
|
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,
|
push_constant_blob: [lib.PUSH_CONSTANT_SIZE]u8,
|
||||||
data: union {
|
data: union {
|
||||||
compute: struct {},
|
compute: struct {},
|
||||||
@@ -45,6 +47,7 @@ pub fn setup(self: *Self, device: *SoftDevice) void {
|
|||||||
state.* = .{
|
state.* = .{
|
||||||
.pipeline = null,
|
.pipeline = null,
|
||||||
.sets = [_]?*SoftDescriptorSet{null} ** base.VULKAN_MAX_DESCRIPTOR_SETS,
|
.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),
|
.push_constant_blob = @splat(0),
|
||||||
.data = switch (i) {
|
.data = switch (i) {
|
||||||
GRAPHICS_PIPELINE_STATE => .{
|
GRAPHICS_PIPELINE_STATE => .{
|
||||||
@@ -71,37 +74,66 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
|
|||||||
switch (binding) {
|
switch (binding) {
|
||||||
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
|
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
|
||||||
if (buffer_data.object) |buffer| {
|
if (buffer_data.object) |buffer| {
|
||||||
const map = buffer.mapAsSliceWithAddedOffset(u8, buffer_data.offset, buffer_data.size) catch continue :bindings;
|
const binding_layout = set.?.interface.layout.bindings[binding_index];
|
||||||
try rt.writeDescriptorSet(
|
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,
|
map,
|
||||||
@as(u32, @intCast(set_index)),
|
@as(u32, @intCast(set_index)),
|
||||||
@as(u32, @intCast(binding_index)),
|
@as(u32, @intCast(binding_index)),
|
||||||
@as(u32, @intCast(descriptor_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| {
|
.image => |image_data_array| for (image_data_array, 0..) |image_data, descriptor_index| {
|
||||||
if (image_data.object) |image_view| {
|
if (image_data.object) |image_view| {
|
||||||
const addr: usize = @intFromPtr(image_view);
|
const addr: usize = @intFromPtr(image_view);
|
||||||
try rt.writeDescriptorSet(
|
rt.writeDescriptorSet(
|
||||||
std.mem.asBytes(&addr),
|
std.mem.asBytes(&addr),
|
||||||
@as(u32, @intCast(set_index)),
|
@as(u32, @intCast(set_index)),
|
||||||
@as(u32, @intCast(binding_index)),
|
@as(u32, @intCast(binding_index)),
|
||||||
@as(u32, @intCast(descriptor_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| {
|
.texel_buffer => |texel_data_array| for (texel_data_array, 0..) |texel_data, descriptor_index| {
|
||||||
if (texel_data.object) |buffer_view| {
|
if (texel_data.object) |buffer_view| {
|
||||||
const addr: usize = @intFromPtr(buffer_view);
|
const addr: usize = @intFromPtr(buffer_view);
|
||||||
try rt.writeDescriptorSet(
|
rt.writeDescriptorSet(
|
||||||
std.mem.asBytes(&addr),
|
std.mem.asBytes(&addr),
|
||||||
@as(u32, @intCast(set_index)),
|
@as(u32, @intCast(set_index)),
|
||||||
@as(u32, @intCast(binding_index)),
|
@as(u32, @intCast(binding_index)),
|
||||||
@as(u32, @intCast(descriptor_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,
|
sampler: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
var data: SampledImage = undefined;
|
var data: SampledImage = .{
|
||||||
|
.image = 0,
|
||||||
|
.sampler = 0,
|
||||||
|
};
|
||||||
|
|
||||||
if (texture_data.view) |image_view| {
|
if (texture_data.view) |image_view| {
|
||||||
const addr: usize = @intFromPtr(image_view);
|
const addr: usize = @intFromPtr(image_view);
|
||||||
@@ -122,12 +157,15 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
|
|||||||
data.sampler = addr;
|
data.sampler = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
try rt.writeDescriptorSet(
|
rt.writeDescriptorSet(
|
||||||
std.mem.asBytes(&data),
|
std.mem.asBytes(&data),
|
||||||
@as(u32, @intCast(set_index)),
|
@as(u32, @intCast(set_index)),
|
||||||
@as(u32, @intCast(binding_index)),
|
@as(u32, @intCast(binding_index)),
|
||||||
@as(u32, @intCast(descriptor_index)),
|
@as(u32, @intCast(descriptor_index)),
|
||||||
);
|
) catch |err| switch (err) {
|
||||||
|
error.NotFound => {},
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {},
|
else => {},
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ pub const Vertex = struct {
|
|||||||
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]?struct {
|
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]?struct {
|
||||||
interpolation_type: enum { smooth, flat, noperspective },
|
interpolation_type: enum { smooth, flat, noperspective },
|
||||||
blob: []u8,
|
blob: []u8,
|
||||||
|
size: usize,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const Renderer = @import("Renderer.zig");
|
|||||||
const Vertex = Renderer.Vertex;
|
const Vertex = Renderer.Vertex;
|
||||||
|
|
||||||
const VkError = base.VkError;
|
const VkError = base.VkError;
|
||||||
|
const INTERFACE_BLOB_PADDING = @sizeOf(F32x4);
|
||||||
|
|
||||||
const ClipPlane = enum {
|
const ClipPlane = enum {
|
||||||
Left,
|
Left,
|
||||||
@@ -142,9 +143,10 @@ fn isVertexInsidePlane(vertex: *const Vertex, plane: ClipPlane) bool {
|
|||||||
return clipDistance(vertex.position, plane) >= 0.0;
|
return clipDistance(vertex.position, plane) >= 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interpolateBlob(allocator: std.mem.Allocator, a: []const u8, b: []const u8, t: f32) VkError![]u8 {
|
fn interpolateBlob(allocator: std.mem.Allocator, a: []const u8, b: []const u8, size: usize, t: f32) VkError![]u8 {
|
||||||
const len = @min(a.len, b.len);
|
const len = @min(size, a.len, b.len);
|
||||||
const result = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory;
|
const result = allocator.alloc(u8, len + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory;
|
||||||
|
@memset(result, 0);
|
||||||
|
|
||||||
var byte_index: usize = 0;
|
var byte_index: usize = 0;
|
||||||
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
|
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)
|
if (byte_index < len)
|
||||||
@memcpy(result[byte_index..], a[byte_index..len]);
|
@memcpy(result[byte_index..len], a[byte_index..len]);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -182,7 +184,8 @@ fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex,
|
|||||||
.blob = if (out_a.interpolation_type == .flat)
|
.blob = if (out_a.interpolation_type == .flat)
|
||||||
allocator.dupe(u8, out_a.blob) catch return VkError.OutOfDeviceMemory
|
allocator.dupe(u8, out_a.blob) catch return VkError.OutOfDeviceMemory
|
||||||
else
|
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),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const SoftImage = @import("../SoftImage.zig");
|
|||||||
|
|
||||||
const VkError = base.VkError;
|
const VkError = base.VkError;
|
||||||
const SpvRuntimeError = spv.Runtime.RuntimeError;
|
const SpvRuntimeError = spv.Runtime.RuntimeError;
|
||||||
|
const INTERFACE_BLOB_PADDING = @sizeOf(zm.F32x4);
|
||||||
|
|
||||||
pub fn shaderInvocation(
|
pub fn shaderInvocation(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
@@ -19,9 +20,11 @@ pub fn shaderInvocation(
|
|||||||
position: zm.F32x4,
|
position: zm.F32x4,
|
||||||
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation,
|
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation,
|
||||||
) SpvRuntimeError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 {
|
) 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();
|
const io = draw_call.renderer.device.interface.io();
|
||||||
|
|
||||||
_ = position;
|
|
||||||
const pipeline = draw_call.renderer.state.pipeline orelse return undefined;
|
const pipeline = draw_call.renderer.state.pipeline orelse return undefined;
|
||||||
|
|
||||||
const shader = pipeline.stages.getPtr(.fragment) 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;
|
mutex.lock(io) catch return SpvRuntimeError.Unknown;
|
||||||
defer mutex.unlock(io);
|
defer mutex.unlock(io);
|
||||||
|
|
||||||
|
rt.resetInvocation(allocator);
|
||||||
try rt.populatePushConstants(draw_call.renderer.state.push_constant_blob[0..]);
|
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);
|
const entry = try rt.getEntryPointByName(shader.entry);
|
||||||
|
|
||||||
@@ -41,9 +49,23 @@ pub fn shaderInvocation(
|
|||||||
SpvRuntimeError.NotFound => continue,
|
SpvRuntimeError.NotFound => continue,
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
try rt.writeInput(inputs[location].blob, result_word);
|
|
||||||
if (inputs[location].free_responsability)
|
var input = fragment_inputs[location];
|
||||||
allocator.free(inputs[location].blob);
|
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) {
|
rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
|
||||||
@@ -66,6 +88,14 @@ pub fn shaderInvocation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try rt.flushDescriptorSets(allocator);
|
try rt.flushDescriptorSets(allocator);
|
||||||
|
freeOwnedInputs(allocator, fragment_inputs);
|
||||||
|
|
||||||
return outputs;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const vk = @import("vulkan");
|
const vk = @import("vulkan");
|
||||||
const base = @import("base");
|
const base = @import("base");
|
||||||
|
const zm = base.zm;
|
||||||
|
|
||||||
const clip = @import("clip.zig");
|
const clip = @import("clip.zig");
|
||||||
|
|
||||||
const bresenham = @import("rasterizer/bresenham.zig");
|
const bresenham = @import("rasterizer/bresenham.zig");
|
||||||
const edge_function = @import("rasterizer/edge_function.zig");
|
const edge_function = @import("rasterizer/edge_function.zig");
|
||||||
const common = @import("rasterizer/common.zig");
|
const common = @import("rasterizer/common.zig");
|
||||||
|
const fragment = @import("fragment.zig");
|
||||||
|
const blitter = @import("blitter.zig");
|
||||||
|
|
||||||
const Renderer = @import("Renderer.zig");
|
const Renderer = @import("Renderer.zig");
|
||||||
const Vertex = Renderer.Vertex;
|
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 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_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;
|
const color_attachment_access = allocator.alloc(?common.RenderTargetAccess, color_attachments.len) catch return VkError.OutOfDeviceMemory;
|
||||||
@memset(color_attachment_access, null);
|
@memset(color_attachment_access, null);
|
||||||
|
|
||||||
@@ -76,6 +79,15 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
};
|
};
|
||||||
|
|
||||||
switch (topology) {
|
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| {
|
.triangle_list => for (0..@divTrunc(draw_call.vertices.len, 3)) |triangle_index| {
|
||||||
const first_vertex = triangle_index * 3;
|
const first_vertex = triangle_index * 3;
|
||||||
const v0 = &draw_call.vertices[first_vertex + 0];
|
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;
|
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(
|
fn clipTransformAndRasterizeLine(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
draw_call: *DrawCall,
|
draw_call: *DrawCall,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ pub const RenderTargetAccess = struct {
|
|||||||
|
|
||||||
pub const VertexInterpolation = struct {
|
pub const VertexInterpolation = struct {
|
||||||
blob: []const u8,
|
blob: []const u8,
|
||||||
|
size: usize,
|
||||||
free_responsability: bool,
|
free_responsability: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,20 +50,25 @@ pub fn interpolateVertexOutputs(
|
|||||||
b1: f32,
|
b1: f32,
|
||||||
b2: f32,
|
b2: f32,
|
||||||
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation {
|
) 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| {
|
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||||
const out0 = v0.outputs[location] orelse continue;
|
const out0 = v0.outputs[location] orelse continue;
|
||||||
const out1 = v1.outputs[location] orelse continue;
|
const out1 = v1.outputs[location] orelse continue;
|
||||||
const out2 = v2.outputs[location] orelse continue;
|
const out2 = v2.outputs[location] orelse continue;
|
||||||
|
|
||||||
if (out0.interpolation_type == .flat or out0.blob.len == 0) {
|
if (out0.interpolation_type == .flat or out0.size == 0) {
|
||||||
inputs[location] = .{ .blob = out0.blob, .free_responsability = false };
|
inputs[location] = .{ .blob = out0.blob, .size = out0.size, .free_responsability = false };
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const len = @min(out0.blob.len, out1.blob.len, out2.blob.len);
|
const len = @min(out0.size, out1.size, out2.size);
|
||||||
const input = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory;
|
const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory;
|
||||||
|
@memset(input, 0);
|
||||||
|
|
||||||
var byte_index: usize = 0;
|
var byte_index: usize = 0;
|
||||||
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
|
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
|
||||||
@@ -80,9 +86,9 @@ pub fn interpolateVertexOutputs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (byte_index < len)
|
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;
|
return inputs;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const spv = @import("spv");
|
const spv = @import("spv");
|
||||||
const base = @import("base");
|
const base = @import("base");
|
||||||
|
const vk = @import("vulkan");
|
||||||
|
|
||||||
const F32x4 = Renderer.F32x4;
|
const F32x4 = base.zm.F32x4;
|
||||||
|
|
||||||
const SpvRuntimeError = spv.Runtime.RuntimeError;
|
const SpvRuntimeError = spv.Runtime.RuntimeError;
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ const Renderer = @import("Renderer.zig");
|
|||||||
const SoftPipeline = @import("../SoftPipeline.zig");
|
const SoftPipeline = @import("../SoftPipeline.zig");
|
||||||
|
|
||||||
const VkError = base.VkError;
|
const VkError = base.VkError;
|
||||||
|
const INTERFACE_BLOB_PADDING = @sizeOf(F32x4);
|
||||||
|
|
||||||
pub const RunData = struct {
|
pub const RunData = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
@@ -38,12 +40,14 @@ pub fn runWrapper(data: RunData) void {
|
|||||||
inline fn run(data: RunData) !void {
|
inline fn run(data: RunData) !void {
|
||||||
const shader = data.pipeline.stages.getPtrAssertContains(.vertex);
|
const shader = data.pipeline.stages.getPtrAssertContains(.vertex);
|
||||||
const rt = &shader.runtimes[data.batch_id].rt;
|
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);
|
const entry = try rt.getEntryPointByName(shader.entry);
|
||||||
|
|
||||||
var invocation_index: usize = data.batch_id;
|
var invocation_index: usize = data.batch_id;
|
||||||
while (invocation_index < data.vertex_count) : (invocation_index += data.batch_size) {
|
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 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;
|
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| {
|
if (data.pipeline.interface.mode.graphics.input_assembly.attribute_description) |attributes| {
|
||||||
for (attributes) |attribute| {
|
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 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];
|
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);
|
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,
|
SpvRuntimeError.NotFound => continue,
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
|
const memory_size = try rt.getResultMemorySize(result_word);
|
||||||
output.outputs[location] = .{
|
output.outputs[location] = .{
|
||||||
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat)) .flat else .smooth, // TODO : handle noperspective
|
.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);
|
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(&vertex_index_u32), .VertexIndex);
|
||||||
try rt.writeBuiltIn(std.mem.asBytes(&instance_index_u32), .InstanceIndex);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 {
|
pub fn begin(self: *Self, info: *const vk.CommandBufferBeginInfo) VkError!void {
|
||||||
if (!self.pool.flags.reset_command_buffer_bit) {
|
const implicitly_reset = self.state == .Executable;
|
||||||
self.transitionState(.Recording, &.{.Initial}) catch return VkError.ValidationFailed;
|
|
||||||
} else {
|
self.transitionState(.Recording, &.{ .Initial, .Executable }) catch return VkError.ValidationFailed;
|
||||||
try self.reset(.{});
|
if (implicitly_reset) {
|
||||||
self.transitionState(.Recording, &.{ .Initial, .Recording, .Executable, .Invalid }) catch return VkError.ValidationFailed;
|
try self.dispatch_table.reset(self, .{});
|
||||||
|
self.begin_info = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.dispatch_table.begin(self, info);
|
try self.dispatch_table.begin(self, info);
|
||||||
self.begin_info = info.*;
|
self.begin_info = info.*;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const vk = @import("vulkan");
|
|||||||
|
|
||||||
const VulkanAllocator = @import("VulkanAllocator.zig");
|
const VulkanAllocator = @import("VulkanAllocator.zig");
|
||||||
|
|
||||||
|
const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
|
||||||
const VkError = @import("error_set.zig").VkError;
|
const VkError = @import("error_set.zig").VkError;
|
||||||
const Device = @import("Device.zig");
|
const Device = @import("Device.zig");
|
||||||
const Sampler = @import("Sampler.zig");
|
const Sampler = @import("Sampler.zig");
|
||||||
@@ -16,7 +17,7 @@ const BindingLayout = struct {
|
|||||||
array_size: usize,
|
array_size: usize,
|
||||||
|
|
||||||
/// This slice points to an array located after the binding layouts array
|
/// This slice points to an array located after the binding layouts array
|
||||||
immutable_samplers: []*const Sampler,
|
immutable_samplers: []const *const Sampler,
|
||||||
|
|
||||||
driver_data: *anyopaque,
|
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 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;
|
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 stages: vk.ShaderStageFlags = .{};
|
||||||
|
var dynamic_descriptor_count: usize = 0;
|
||||||
|
var dynamic_offset_count: usize = 0;
|
||||||
|
|
||||||
if (info.p_bindings) |binding_infos| {
|
if (info.p_bindings) |binding_infos| {
|
||||||
const sorted_bindings = command_allocator.dupe(vk.DescriptorSetLayoutBinding, binding_infos[0..info.binding_count]) catch return VkError.OutOfHostMemory;
|
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,
|
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] = .{
|
bindings[binding_index] = .{
|
||||||
.descriptor_type = binding_info.descriptor_type,
|
.descriptor_type = binding_info.descriptor_type,
|
||||||
.array_size = descriptor_count,
|
.array_size = descriptor_count,
|
||||||
.dynamic_index = 0,
|
.dynamic_index = dynamic_index,
|
||||||
.immutable_samplers = immutable_samplers[0..],
|
.immutable_samplers = binding_immutable_samplers,
|
||||||
.driver_data = undefined,
|
.driver_data = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,8 +130,8 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Descr
|
|||||||
.owner = device,
|
.owner = device,
|
||||||
.heap = heap,
|
.heap = heap,
|
||||||
.bindings = bindings,
|
.bindings = bindings,
|
||||||
.dynamic_offset_count = 0,
|
.dynamic_offset_count = dynamic_offset_count,
|
||||||
.dynamic_descriptor_count = 0,
|
.dynamic_descriptor_count = dynamic_descriptor_count,
|
||||||
.stages = stages,
|
.stages = stages,
|
||||||
.ref_count = std.atomic.Value(usize).init(1),
|
.ref_count = std.atomic.Value(usize).init(1),
|
||||||
.vtable = undefined,
|
.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 {
|
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
||||||
self.unref(allocator);
|
self.unref(allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, _| {
|
for (base_attachements, attachments, 0..info.attachment_count) |base_attachment, *attachment, _| {
|
||||||
attachment.* = try NonDispatchable(ImageView).fromHandleObject(base_attachment);
|
attachment.* = try NonDispatchable(ImageView).fromHandleObject(base_attachment);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (info.attachment_count != 0) {
|
||||||
return VkError.ValidationFailed;
|
return VkError.ValidationFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ vtable: *const VTable,
|
|||||||
|
|
||||||
pub const VTable = struct {
|
pub const VTable = struct {
|
||||||
destroy: *const fn (*Self, std.mem.Allocator) void,
|
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 {
|
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, _| {
|
for (base_attachements, attachments, 0..info.attachment_count) |base_attachment, *attachment, _| {
|
||||||
attachment.* = base_attachment;
|
attachment.* = base_attachment;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (info.attachment_count != 0) {
|
||||||
return VkError.ValidationFailed;
|
return VkError.ValidationFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,3 +94,7 @@ pub fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
|||||||
allocator.free(self.subpasses);
|
allocator.free(self.subpasses);
|
||||||
self.vtable.destroy(self, allocator);
|
self.vtable.destroy(self, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn getRenderAreaGranularity(self: *Self) vk.Extent2D {
|
||||||
|
return self.vtable.getRenderAreaGranularity(self);
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ min_filter: vk.Filter,
|
|||||||
address_mode_u: vk.SamplerAddressMode,
|
address_mode_u: vk.SamplerAddressMode,
|
||||||
address_mode_v: vk.SamplerAddressMode,
|
address_mode_v: vk.SamplerAddressMode,
|
||||||
address_mode_w: vk.SamplerAddressMode,
|
address_mode_w: vk.SamplerAddressMode,
|
||||||
|
border_color: vk.BorderColor,
|
||||||
|
|
||||||
vtable: *const VTable,
|
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_u = info.address_mode_u,
|
||||||
.address_mode_v = info.address_mode_v,
|
.address_mode_v = info.address_mode_v,
|
||||||
.address_mode_w = info.address_mode_w,
|
.address_mode_w = info.address_mode_w,
|
||||||
|
.border_color = info.border_color,
|
||||||
.vtable = undefined,
|
.vtable = undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ pub inline fn texelSize(format: vk.Format) usize {
|
|||||||
return lib.c.vkuFormatTexelBlockSize(@intCast(@intFromEnum(format)));
|
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 {
|
pub fn supportsColorAttachemendBlend(format: vk.Format) bool {
|
||||||
return switch (format) {
|
return switch (format) {
|
||||||
// Vulkan 1.1 mandatory
|
// Vulkan 1.1 mandatory
|
||||||
|
|||||||
@@ -1470,11 +1470,9 @@ pub export fn apeGetRenderAreaGranularity(p_device: vk.Device, p_pass: vk.Render
|
|||||||
defer entryPointEndLogTrace();
|
defer entryPointEndLogTrace();
|
||||||
|
|
||||||
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err);
|
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err);
|
||||||
|
const pass = NonDispatchable(RenderPass).fromHandleObject(p_pass) catch |err| return errorLogger(err);
|
||||||
|
|
||||||
notImplementedWarning();
|
granularity.* = pass.getRenderAreaGranularity();
|
||||||
|
|
||||||
_ = p_pass;
|
|
||||||
_ = granularity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub export fn apeInvalidateMappedMemoryRanges(p_device: vk.Device, count: u32, p_ranges: [*]const vk.MappedMemoryRange) callconv(vk.vulkan_call_conv) vk.Result {
|
pub export fn apeInvalidateMappedMemoryRanges(p_device: vk.Device, count: u32, p_ranges: [*]const vk.MappedMemoryRange) callconv(vk.vulkan_call_conv) vk.Result {
|
||||||
|
|||||||
Reference in New Issue
Block a user