471 lines
18 KiB
Zig
471 lines
18 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vulkan");
|
|
const base = @import("base");
|
|
|
|
const VkError = base.VkError;
|
|
const Device = base.Device;
|
|
const Buffer = base.Buffer;
|
|
const BufferView = base.BufferView;
|
|
const ImageView = base.ImageView;
|
|
const Sampler = base.Sampler;
|
|
|
|
const SoftBuffer = @import("SoftBuffer.zig");
|
|
const SoftBufferView = @import("SoftBufferView.zig");
|
|
const SoftImageView = @import("SoftImageView.zig");
|
|
const SoftSampler = @import("SoftSampler.zig");
|
|
|
|
const NonDispatchable = base.NonDispatchable;
|
|
|
|
const Self = @This();
|
|
pub const Interface = base.DescriptorSet;
|
|
|
|
const DescriptorBuffer = struct {
|
|
object: ?*SoftBuffer,
|
|
offset: vk.DeviceSize,
|
|
size: vk.DeviceSize,
|
|
};
|
|
|
|
const DescriptorTexture = struct {
|
|
sampler: ?*SoftSampler,
|
|
view: ?*SoftImageView,
|
|
};
|
|
|
|
const DescriptorImage = struct {
|
|
object: ?*SoftImageView,
|
|
};
|
|
|
|
const DescriptorSampler = struct {
|
|
object: ?*SoftSampler,
|
|
};
|
|
|
|
const DescriptorTexel = struct {
|
|
object: ?*SoftBufferView,
|
|
};
|
|
|
|
const Descriptor = union(enum) {
|
|
buffer: []DescriptorBuffer,
|
|
texture: []DescriptorTexture,
|
|
image: []DescriptorImage,
|
|
sampler: []DescriptorSampler,
|
|
texel_buffer: []DescriptorTexel,
|
|
unsupported: struct {},
|
|
};
|
|
|
|
interface: Interface,
|
|
|
|
/// Memory containing actual binding descriptors and their array
|
|
heap: []u8,
|
|
|
|
descriptors: []Descriptor,
|
|
|
|
pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.DescriptorSetLayout) VkError!*Self {
|
|
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
|
|
errdefer allocator.destroy(self);
|
|
|
|
var interface = try Interface.init(device, allocator, layout);
|
|
|
|
interface.vtable = &.{
|
|
.copy = copy,
|
|
.destroy = destroy,
|
|
.write = write,
|
|
};
|
|
|
|
const heap_size = blk: {
|
|
var size: usize = layout.bindings.len * @sizeOf(Descriptor);
|
|
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),
|
|
|
|
.combined_image_sampler,
|
|
=> @sizeOf(DescriptorTexture),
|
|
|
|
else => 0,
|
|
};
|
|
|
|
size += binding.array_size * struct_size;
|
|
}
|
|
break :blk size;
|
|
};
|
|
|
|
const heap = allocator.alloc(u8, heap_size) catch return VkError.OutOfHostMemory;
|
|
errdefer allocator.free(heap);
|
|
|
|
var local_heap = std.heap.FixedBufferAllocator.init(heap);
|
|
const local_allocator = local_heap.allocator();
|
|
|
|
const descriptors = local_allocator.alloc(Descriptor, layout.bindings.len) catch return VkError.OutOfHostMemory;
|
|
for (descriptors, layout.bindings) |*descriptor, binding| {
|
|
switch (binding.descriptor_type) {
|
|
.uniform_buffer,
|
|
.uniform_buffer_dynamic,
|
|
.storage_buffer,
|
|
.storage_buffer_dynamic,
|
|
=> descriptor.* = blk: {
|
|
const desc: Descriptor = .{
|
|
.buffer = local_allocator.alloc(DescriptorBuffer, binding.array_size) catch return VkError.OutOfHostMemory,
|
|
};
|
|
for (desc.buffer[0..]) |*d| {
|
|
d.* = .{
|
|
.object = null,
|
|
.offset = 0,
|
|
.size = 0,
|
|
};
|
|
}
|
|
break :blk desc;
|
|
},
|
|
|
|
.sampled_image,
|
|
.storage_image,
|
|
.input_attachment,
|
|
=> descriptor.* = blk: {
|
|
const desc: Descriptor = .{
|
|
.image = local_allocator.alloc(DescriptorImage, binding.array_size) catch return VkError.OutOfHostMemory,
|
|
};
|
|
for (desc.image[0..]) |*d| {
|
|
d.* = .{ .object = null };
|
|
}
|
|
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: {
|
|
const desc: Descriptor = .{
|
|
.texel_buffer = local_allocator.alloc(DescriptorTexel, binding.array_size) catch return VkError.OutOfHostMemory,
|
|
};
|
|
for (desc.texel_buffer[0..]) |*d| {
|
|
d.* = .{ .object = null };
|
|
}
|
|
break :blk desc;
|
|
},
|
|
|
|
.combined_image_sampler,
|
|
=> descriptor.* = blk: {
|
|
const desc: Descriptor = .{
|
|
.texture = local_allocator.alloc(DescriptorTexture, binding.array_size) catch return VkError.OutOfHostMemory,
|
|
};
|
|
for (desc.texture[0..], 0..) |*d, i| {
|
|
d.* = .{
|
|
.sampler = if (i < binding.immutable_samplers.len)
|
|
@as(*SoftSampler, @alignCast(@fieldParentPtr("interface", @constCast(binding.immutable_samplers[i]))))
|
|
else
|
|
null,
|
|
.view = null,
|
|
};
|
|
}
|
|
break :blk desc;
|
|
},
|
|
|
|
else => descriptor.* = .{ .unsupported = .{} },
|
|
}
|
|
}
|
|
|
|
self.* = .{
|
|
.interface = interface,
|
|
.heap = heap,
|
|
.descriptors = descriptors,
|
|
};
|
|
return self;
|
|
}
|
|
|
|
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
|
allocator.free(self.heap);
|
|
allocator.destroy(self);
|
|
}
|
|
|
|
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,
|
|
};
|
|
}
|
|
|
|
fn advanceToAvailableDescriptor(descriptors: []const Descriptor, binding: *usize, array_element: *usize) bool {
|
|
while (binding.* < descriptors.len) {
|
|
switch (descriptors[binding.*]) {
|
|
.unsupported => return true,
|
|
else => {},
|
|
}
|
|
|
|
const len = descriptorLen(descriptors[binding.*]);
|
|
if (array_element.* < len) return true;
|
|
if (array_element.* > len) return false;
|
|
|
|
binding.* += 1;
|
|
array_element.* = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
fn copyDescriptorRange(dst_desc: *Descriptor, dst_array_element: usize, src_desc: Descriptor, src_array_element: usize, descriptor_count: usize) bool {
|
|
switch (dst_desc.*) {
|
|
.buffer => |dst_buffer| {
|
|
const src_buffer = switch (src_desc) {
|
|
.buffer => |buffer| buffer,
|
|
else => return false,
|
|
};
|
|
@memcpy(
|
|
dst_buffer[dst_array_element .. dst_array_element + descriptor_count],
|
|
src_buffer[src_array_element .. src_array_element + descriptor_count],
|
|
);
|
|
},
|
|
|
|
.image => |dst_image| {
|
|
const src_image = switch (src_desc) {
|
|
.image => |image| image,
|
|
else => return false,
|
|
};
|
|
@memcpy(
|
|
dst_image[dst_array_element .. dst_array_element + descriptor_count],
|
|
src_image[src_array_element .. src_array_element + descriptor_count],
|
|
);
|
|
},
|
|
|
|
.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,
|
|
else => return false,
|
|
};
|
|
@memcpy(
|
|
dst_texel_buffer[dst_array_element .. dst_array_element + descriptor_count],
|
|
src_texel_buffer[src_array_element .. src_array_element + descriptor_count],
|
|
);
|
|
},
|
|
|
|
.texture => |dst_texture| {
|
|
const src_texture = switch (src_desc) {
|
|
.texture => |texture| texture,
|
|
else => return false,
|
|
};
|
|
@memcpy(
|
|
dst_texture[dst_array_element .. dst_array_element + descriptor_count],
|
|
src_texture[src_array_element .. src_array_element + descriptor_count],
|
|
);
|
|
},
|
|
|
|
.unsupported => return false,
|
|
}
|
|
|
|
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));
|
|
|
|
var dst_binding: usize = @intCast(data.dst_binding);
|
|
var src_binding: usize = @intCast(data.src_binding);
|
|
var dst_array_element: usize = @intCast(data.dst_array_element);
|
|
var src_array_element: usize = @intCast(data.src_array_element);
|
|
var descriptor_count: usize = @intCast(data.descriptor_count);
|
|
|
|
while (descriptor_count > 0) {
|
|
if (!advanceToAvailableDescriptor(self.descriptors, &dst_binding, &dst_array_element) or
|
|
!advanceToAvailableDescriptor(src.descriptors, &src_binding, &src_array_element))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const dst_desc = &self.descriptors[dst_binding];
|
|
const src_desc = src.descriptors[src_binding];
|
|
const dst_len = descriptorLen(dst_desc.*);
|
|
const src_len = descriptorLen(src_desc);
|
|
if (dst_len == 0 or src_len == 0) {
|
|
base.unsupported("descriptor type for copy", .{});
|
|
return;
|
|
}
|
|
|
|
const dst_remaining = dst_len - dst_array_element;
|
|
const src_remaining = src_len - src_array_element;
|
|
const copy_count = @min(descriptor_count, dst_remaining, src_remaining);
|
|
|
|
if (!self.copyDescriptorRangePreservingImmutableSamplers(dst_binding, dst_desc, dst_array_element, src_desc, src_array_element, copy_count)) {
|
|
base.unsupported("descriptor type for copy", .{});
|
|
return;
|
|
}
|
|
|
|
descriptor_count -= copy_count;
|
|
dst_array_element += copy_count;
|
|
src_array_element += copy_count;
|
|
}
|
|
}
|
|
|
|
pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!void {
|
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
|
|
|
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[write_data.dst_array_element + i];
|
|
desc.* = .{
|
|
.object = null,
|
|
.offset = buffer_info.offset,
|
|
.size = buffer_info.range,
|
|
};
|
|
if (buffer_info.buffer != .null_handle) {
|
|
const buffer = try NonDispatchable(Buffer).fromHandleObject(buffer_info.buffer);
|
|
desc.object = @as(*SoftBuffer, @alignCast(@fieldParentPtr("interface", buffer)));
|
|
if (desc.size == vk.WHOLE_SIZE) {
|
|
desc.size = if (buffer.memory) |memory| memory.size - desc.offset else return VkError.InvalidDeviceMemoryDrv;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
.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[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);
|
|
desc.object = @as(*SoftImageView, @alignCast(@fieldParentPtr("interface", image_view)));
|
|
}
|
|
}
|
|
},
|
|
|
|
.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[write_data.dst_array_element + i];
|
|
desc.* = .{ .object = null };
|
|
if (view != .null_handle) {
|
|
const buffer_view = try NonDispatchable(BufferView).fromHandleObject(view);
|
|
desc.object = @as(*SoftBufferView, @alignCast(@fieldParentPtr("interface", buffer_view)));
|
|
}
|
|
}
|
|
},
|
|
|
|
.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[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 = 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 (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)));
|
|
}
|
|
}
|
|
},
|
|
|
|
else => {
|
|
self.descriptors[write_data.dst_binding] = .{ .unsupported = .{} };
|
|
base.unsupported("descriptor type {s} for writting", .{@tagName(write_data.descriptor_type)});
|
|
},
|
|
}
|
|
}
|