256 lines
10 KiB
Zig
256 lines
10 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vulkan");
|
|
const base = @import("base");
|
|
const lib = @import("../lib.zig");
|
|
const spv = @import("spv");
|
|
|
|
const SoftDescriptorSet = @import("../SoftDescriptorSet.zig");
|
|
const SoftDevice = @import("../SoftDevice.zig");
|
|
const SoftFramebuffer = @import("../SoftFramebuffer.zig");
|
|
const SoftPipeline = @import("../SoftPipeline.zig");
|
|
const SoftRenderPass = @import("../SoftRenderPass.zig");
|
|
|
|
const ComputeDispatcher = @import("ComputeDispatcher.zig");
|
|
const Renderer = @import("Renderer.zig");
|
|
|
|
const VkError = base.VkError;
|
|
|
|
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 ActiveOcclusionQuery = struct {
|
|
pool: *base.QueryPool,
|
|
query: u32,
|
|
};
|
|
|
|
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 {},
|
|
graphics: struct {
|
|
index_buffer: Renderer.IndexBuffer,
|
|
vertex_buffers: [lib.MAX_VERTEX_INPUT_BINDINGS]Renderer.VertexBuffer,
|
|
},
|
|
},
|
|
};
|
|
|
|
compute: ComputeDispatcher,
|
|
renderer: Renderer,
|
|
|
|
pipeline_states: [2]PipelineState,
|
|
active_occlusion_queries: std.ArrayList(ActiveOcclusionQuery),
|
|
|
|
/// Initializating an execution device and
|
|
/// not creating one to avoid dangling pointers
|
|
pub fn setup(self: *Self, device: *SoftDevice) void {
|
|
for (self.pipeline_states[0..], 0..) |*state, i| {
|
|
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 => .{
|
|
.graphics = .{
|
|
.index_buffer = undefined,
|
|
.vertex_buffers = undefined,
|
|
},
|
|
},
|
|
COMPUTE_PIPELINE_STATE => .{ .compute = .{} },
|
|
else => unreachable,
|
|
},
|
|
};
|
|
}
|
|
self.active_occlusion_queries = .empty;
|
|
self.compute = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.compute)]);
|
|
self.renderer = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.graphics)], &self.active_occlusion_queries);
|
|
}
|
|
|
|
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
|
self.active_occlusion_queries.deinit(allocator);
|
|
}
|
|
|
|
const SampledImageDescriptor = struct {
|
|
image: usize,
|
|
sampler: usize,
|
|
};
|
|
|
|
const DescriptorPayload = union(enum) {
|
|
raw: []const u8,
|
|
sampled_image: SampledImageDescriptor,
|
|
};
|
|
|
|
fn writeDescriptorValue(value: anytype, payload: DescriptorPayload, descriptor_index: u32) spv.Runtime.RuntimeError!void {
|
|
const dst = switch (value.*) {
|
|
.Array => |arr| blk: {
|
|
if (descriptor_index >= arr.values.len)
|
|
return spv.Runtime.RuntimeError.NotFound;
|
|
break :blk &arr.values[descriptor_index];
|
|
},
|
|
else => blk: {
|
|
if (descriptor_index != 0)
|
|
return spv.Runtime.RuntimeError.NotFound;
|
|
break :blk value;
|
|
},
|
|
};
|
|
|
|
switch (payload) {
|
|
.raw => |data| {
|
|
_ = try dst.write(data);
|
|
},
|
|
.sampled_image => |data| switch (dst.*) {
|
|
.Image => _ = try dst.write(std.mem.asBytes(&data.image)),
|
|
.Sampler => _ = try dst.write(std.mem.asBytes(&data.sampler)),
|
|
.SampledImage => _ = try dst.write(std.mem.asBytes(&data)),
|
|
else => return spv.Runtime.RuntimeError.InvalidValueType,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn writeDescriptorSet(rt: *spv.Runtime, payload: DescriptorPayload, set: u32, binding: u32, descriptor_index: u32) spv.Runtime.RuntimeError!void {
|
|
var found = false;
|
|
for (rt.mod.bindings.items) |entry| {
|
|
if (entry.set != set or entry.binding != binding)
|
|
continue;
|
|
const variable = if (rt.results[entry.result].variant) |*variant| switch (variant.*) {
|
|
.Variable => |*variable| variable,
|
|
else => continue,
|
|
} else continue;
|
|
|
|
writeDescriptorValue(&variable.value, payload, descriptor_index) catch |err| switch (err) {
|
|
spv.Runtime.RuntimeError.InvalidValueType,
|
|
spv.Runtime.RuntimeError.OutOfBounds,
|
|
=> continue,
|
|
else => return err,
|
|
};
|
|
found = true;
|
|
}
|
|
|
|
if (!found)
|
|
return spv.Runtime.RuntimeError.NotFound;
|
|
}
|
|
|
|
pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
|
|
sets: for (state.sets[0..], 0..) |set, set_index| {
|
|
if (set == null)
|
|
continue :sets;
|
|
|
|
bindings: for (set.?.descriptors[0..], 0..) |binding, binding_index| {
|
|
switch (binding) {
|
|
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
|
|
if (buffer_data.object) |buffer| {
|
|
const memory = buffer.interface.memory orelse continue;
|
|
if (@intFromPtr(memory.vtable) == 0)
|
|
continue;
|
|
|
|
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;
|
|
writeDescriptorSet(
|
|
rt,
|
|
.{ .raw = 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);
|
|
writeDescriptorSet(
|
|
rt,
|
|
.{ .raw = 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);
|
|
writeDescriptorSet(
|
|
rt,
|
|
.{ .raw = 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);
|
|
writeDescriptorSet(
|
|
rt,
|
|
.{ .sampled_image = .{ .image = addr, .sampler = 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,
|
|
};
|
|
}
|
|
},
|
|
|
|
.texture => |texture_data_array| for (texture_data_array, 0..) |texture_data, descriptor_index| {
|
|
var data: SampledImageDescriptor = .{
|
|
.image = 0,
|
|
.sampler = 0,
|
|
};
|
|
|
|
if (texture_data.view) |image_view| {
|
|
const addr: usize = @intFromPtr(image_view);
|
|
data.image = addr;
|
|
}
|
|
|
|
if (texture_data.sampler) |sampler| {
|
|
const addr: usize = @intFromPtr(sampler);
|
|
data.sampler = addr;
|
|
}
|
|
|
|
writeDescriptorSet(
|
|
rt,
|
|
.{ .sampled_image = 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 => {},
|
|
}
|
|
}
|
|
}
|
|
}
|