From faae8e86e0ea4df93b91e62ed4c020ba6eb48bd9 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Tue, 12 May 2026 03:01:17 +0200 Subject: [PATCH] implementing push constants --- build.zig.zon | 14 +++++----- src/soft/SoftBuffer.zig | 3 --- src/soft/SoftCommandBuffer.zig | 38 ++++++++++++++++++++++++++- src/soft/SoftDeviceMemory.zig | 3 --- src/soft/SoftPhysicalDevice.zig | 10 +++---- src/soft/device/Device.zig | 2 ++ src/soft/device/rasterizer.zig | 4 +++ src/soft/device/vertex_dispatcher.zig | 1 + src/soft/lib.zig | 6 ++--- src/vulkan/CommandBuffer.zig | 5 ++++ src/vulkan/Pipeline.zig | 16 +++++++++-- src/vulkan/PipelineLayout.zig | 13 --------- src/vulkan/lib_vulkan.zig | 12 +++------ 13 files changed, 81 insertions(+), 46 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index d5bba44..529fb08 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -30,16 +30,16 @@ .hash = "cpuinfo-0.0.1-RLgIQYrTMgGqfQMOd1nAa2EuglXOh5gR9bNzwMzQTemt", .lazy = true, }, - .SPIRV_Interpreter = .{ - .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#9d20363ae852e1b400cb62508cb672bcfd5b3716", - .hash = "SPIRV_Interpreter-0.0.1-ajmpn6QuBQDDfo3uv6HauRc4DLNp2b0pZkfwyuzF-w9d", - .lazy = true, - }, //.SPIRV_Interpreter = .{ - // // For development - // .path = "../SPIRV-Interpreter", + // .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#ca33cfe3e997503208f031d270018c10d0611989", + // .hash = "SPIRV_Interpreter-0.0.1-ajmpnwBCBQBqUKcAUTwaaxbvYmX2s0KFzF_9Bc_ntqs4", // .lazy = true, //}, + .SPIRV_Interpreter = .{ + // For development + .path = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#3139f3cfdd844818ab42ebce1d430a1f524025d5", + .lazy = true, + }, }, .paths = .{ diff --git a/src/soft/SoftBuffer.zig b/src/soft/SoftBuffer.zig index 73d3b15..c593f45 100644 --- a/src/soft/SoftBuffer.zig +++ b/src/soft/SoftBuffer.zig @@ -13,9 +13,6 @@ pub const Interface = base.Buffer; interface: Interface, pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.BufferCreateInfo) VkError!*Self { - if (info.size > lib.MAX_MEMORY_ALLOCATION_SIZE) - return VkError.OutOfDeviceMemory; - const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig index 2a114da..838867a 100644 --- a/src/soft/SoftCommandBuffer.zig +++ b/src/soft/SoftCommandBuffer.zig @@ -68,6 +68,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v .executeCommands = executeCommands, .fillBuffer = fillBuffer, .pipelineBarrier = pipelineBarrier, + .pushConstants = pushConstants, .reset = reset, .resetEvent = resetEvent, .setEvent = setEvent, @@ -150,7 +151,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra } switch (desc.stencil_load_op) { - .clear => clear_mask = .{ .stencil_bit = true }, + .clear => clear_mask.stencil_bit = true, else => {}, } @@ -813,6 +814,41 @@ pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags, _ = image_barriers; } +pub fn pushConstants(interface: *Interface, stages: vk.ShaderStageFlags, offset: u32, blob: []const u8) VkError!void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const allocator = self.command_allocator.allocator(); + + const CommandImpl = struct { + const Impl = @This(); + + stages: vk.ShaderStageFlags, + offset: u32, + blob: []const u8, + + pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void { + const impl: *Impl = @ptrCast(@alignCast(context)); + + const size = @min(lib.PUSH_CONSTANT_SIZE - impl.offset, impl.blob.len); + // TODO: pipeline layout offset + if (impl.stages.vertex_bit or impl.stages.fragment_bit) { + @memcpy(device.pipeline_states[ExecutionDevice.GRAPHICS_PIPELINE_STATE].push_constant_blob[impl.offset..size], impl.blob[0..size]); + } + if (impl.stages.compute_bit) { + @memcpy(device.pipeline_states[ExecutionDevice.COMPUTE_PIPELINE_STATE].push_constant_blob[impl.offset..size], impl.blob[0..size]); + } + } + }; + + const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory; + errdefer allocator.destroy(cmd); + cmd.* = .{ + .stages = stages, + .offset = offset, + .blob = allocator.dupe(u8, blob) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy + }; + self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; +} + pub fn resetEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const allocator = self.command_allocator.allocator(); diff --git a/src/soft/SoftDeviceMemory.zig b/src/soft/SoftDeviceMemory.zig index 9b36fbd..5c3f588 100644 --- a/src/soft/SoftDeviceMemory.zig +++ b/src/soft/SoftDeviceMemory.zig @@ -13,9 +13,6 @@ interface: Interface, data: []u8, pub fn create(device: *SoftDevice, allocator: std.mem.Allocator, size: vk.DeviceSize, memory_type_index: u32) VkError!*Self { - if (size > lib.MAX_MEMORY_ALLOCATION_SIZE) - return VkError.OutOfDeviceMemory; - const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); diff --git a/src/soft/SoftPhysicalDevice.zig b/src/soft/SoftPhysicalDevice.zig index f8004d4..27c3843 100644 --- a/src/soft/SoftPhysicalDevice.zig +++ b/src/soft/SoftPhysicalDevice.zig @@ -68,8 +68,8 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S .max_texel_buffer_elements = 65536, .max_uniform_buffer_range = 16384, .max_storage_buffer_range = 134217728, - .max_push_constants_size = 128, - .max_memory_allocation_count = lib.MAX_ALLOCATION_COUNT, + .max_push_constants_size = lib.PUSH_CONSTANT_SIZE, + .max_memory_allocation_count = std.math.maxInt(u32), .max_sampler_allocation_count = 4096, .buffer_image_granularity = 131072, .sparse_address_space_size = 0, @@ -180,7 +180,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S }; interface.mem_props.memory_heap_count = 1; interface.mem_props.memory_heaps[0] = .{ - .size = lib.PHYSICAL_DEVICE_HEAP_SIZE, + .size = std.process.totalSystemMemory() catch lib.PHYSICAL_DEVICE_FALLBACK_HEAP_SIZE, .flags = .{ .device_local_bit = true }, }; @@ -189,8 +189,8 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S .shader_float_64 = .true, .shader_int_64 = .true, .shader_int_16 = .true, - .texture_compression_etc2 = .true, - .texture_compression_bc = .true, + .texture_compression_etc2 = .false, + .texture_compression_bc = .false, }; var queue_family_props = [_]vk.QueueFamilyProperties{ diff --git a/src/soft/device/Device.zig b/src/soft/device/Device.zig index 4a341f1..3e2f0d5 100644 --- a/src/soft/device/Device.zig +++ b/src/soft/device/Device.zig @@ -22,6 +22,7 @@ pub const COMPUTE_PIPELINE_STATE = 1; pub const PipelineState = struct { pipeline: ?*SoftPipeline, sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*SoftDescriptorSet, + push_constant_blob: [lib.PUSH_CONSTANT_SIZE]u8, data: union { compute: struct {}, graphics: struct { @@ -43,6 +44,7 @@ pub fn init(self: *Self, device: *SoftDevice) void { state.* = .{ .pipeline = null, .sets = [_]?*SoftDescriptorSet{null} ** base.VULKAN_MAX_DESCRIPTOR_SETS, + .push_constant_blob = @splat(0), .data = switch (i) { GRAPHICS_PIPELINE_STATE => .{ .graphics = .{ diff --git a/src/soft/device/rasterizer.zig b/src/soft/device/rasterizer.zig index 3f0fbb0..0f37221 100644 --- a/src/soft/device/rasterizer.zig +++ b/src/soft/device/rasterizer.zig @@ -21,6 +21,8 @@ fn interpolateF32x4(value0: F32x4, value1: F32x4, value2: F32x4, b0: f32, b1: f3 return (value0 * @as(F32x4, @splat(b0))) + (value1 * @as(F32x4, @splat(b1))) + (value2 * @as(F32x4, @splat(b2))); } +var calls: usize = 0; + fn interpolateVertexOutputs( allocator: std.mem.Allocator, v0: *const Renderer.Vertex, @@ -43,6 +45,8 @@ fn interpolateVertexOutputs( } const len = @min(out0.blob.len, out1.blob.len, out2.blob.len); + calls += 1; + std.debug.print("test {d}\n", .{calls}); const input = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory; var byte_index: usize = 0; diff --git a/src/soft/device/vertex_dispatcher.zig b/src/soft/device/vertex_dispatcher.zig index 036d60e..717f17f 100644 --- a/src/soft/device/vertex_dispatcher.zig +++ b/src/soft/device/vertex_dispatcher.zig @@ -39,6 +39,7 @@ inline fn run(data: RunData) !void { const shader = data.pipeline.stages.getPtrAssertContains(.vertex); const rt = &shader.runtimes[data.batch_id]; + try rt.populatePushConstants(data.renderer.state.push_constant_blob[0..]); const entry = try rt.getEntryPointByName(shader.entry); diff --git a/src/soft/lib.zig b/src/soft/lib.zig index 6609f3b..034e592 100644 --- a/src/soft/lib.zig +++ b/src/soft/lib.zig @@ -60,15 +60,15 @@ pub const MIN_STORAGE_BUFFER_ALIGNMENT = 256; pub const MAX_VERTEX_INPUT_BINDINGS = 16; pub const MAX_VERTEX_INPUT_ATTRIBUTES = 32; +pub const PUSH_CONSTANT_SIZE = 256; + pub const MAX_IMAGE_LEVELS_1D = 15; pub const MAX_IMAGE_LEVELS_2D = 15; pub const MAX_IMAGE_LEVELS_3D = 12; pub const MAX_IMAGE_LEVELS_CUBE = 15; pub const MAX_IMAGE_ARRAY_LAYERS = 2048; -pub const PHYSICAL_DEVICE_HEAP_SIZE = 0x80000000; // 2 GiB -pub const MAX_MEMORY_ALLOCATION_SIZE = 0x80000000; // 2 GiB -pub const MAX_ALLOCATION_COUNT = 4096; +pub const PHYSICAL_DEVICE_FALLBACK_HEAP_SIZE = 0x10000000; // 256MB pub const std_options = base.std_options; diff --git a/src/vulkan/CommandBuffer.zig b/src/vulkan/CommandBuffer.zig index b81cdb8..82474bb 100644 --- a/src/vulkan/CommandBuffer.zig +++ b/src/vulkan/CommandBuffer.zig @@ -62,6 +62,7 @@ pub const DispatchTable = struct { executeCommands: *const fn (*Self, *Self) VkError!void, fillBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, vk.DeviceSize, u32) VkError!void, pipelineBarrier: *const fn (*Self, vk.PipelineStageFlags, vk.PipelineStageFlags, vk.DependencyFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void, + pushConstants: *const fn (*Self, vk.ShaderStageFlags, u32, []const u8) VkError!void, reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void, resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void, setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void, @@ -253,6 +254,10 @@ pub inline fn pipelineBarrier( try self.dispatch_table.pipelineBarrier(self, src_stage, dst_stage, dependency, memory_barriers, buffer_barriers, image_barriers); } +pub inline fn pushConstants(self: *Self, stages: vk.ShaderStageFlags, offset: u32, blob: []const u8) VkError!void { + try self.dispatch_table.pushConstants(self, stages, offset, blob); +} + pub inline fn resetEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void { try self.dispatch_table.resetEvent(self, event, stage); } diff --git a/src/vulkan/Pipeline.zig b/src/vulkan/Pipeline.zig index 0958846..a718be6 100644 --- a/src/vulkan/Pipeline.zig +++ b/src/vulkan/Pipeline.zig @@ -1,12 +1,13 @@ const std = @import("std"); const vk = @import("vulkan"); -const NonDispatchable = @import("NonDispatchable.zig"); +const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable; const VkError = @import("error_set.zig").VkError; const Device = @import("Device.zig"); const PipelineCache = @import("PipelineCache.zig"); +const PipelineLayout = @import("PipelineLayout.zig"); const Self = @This(); pub const ObjectType: vk.ObjectType = .pipeline; @@ -28,6 +29,7 @@ owner: *Device, vtable: *const VTable, bind_point: vk.PipelineBindPoint, stages: vk.ShaderStageFlags, +layout: *PipelineLayout, mode: union(enum) { compute: struct {}, graphics: struct { @@ -55,14 +57,18 @@ pub const VTable = struct { }; pub fn initCompute(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.ComputePipelineCreateInfo) VkError!Self { - _ = allocator; _ = cache; + const layout = try NonDispatchable(PipelineLayout).fromHandleObject(info.layout); + layout.ref(); + errdefer layout.unref(allocator); + return .{ .owner = device, .vtable = undefined, .bind_point = .compute, .stages = info.stage.stage, + .layout = layout, .mode = .{ .compute = .{} }, }; } @@ -70,6 +76,10 @@ pub fn initCompute(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipel pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.GraphicsPipelineCreateInfo) VkError!Self { _ = cache; + const layout = try NonDispatchable(PipelineLayout).fromHandleObject(info.layout); + layout.ref(); + errdefer layout.unref(allocator); + var stages: vk.ShaderStageFlags = .{}; if (info.p_stages) |p_stages| { for (p_stages[0..info.stage_count]) |stage| { @@ -82,6 +92,7 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe .vtable = undefined, .bind_point = .graphics, .stages = stages, + .layout = layout, .mode = .{ .graphics = .{ .input_assembly = .{ @@ -172,5 +183,6 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { } }, } + self.layout.unref(allocator); self.vtable.destroy(self, allocator); } diff --git a/src/vulkan/PipelineLayout.zig b/src/vulkan/PipelineLayout.zig index 3baba94..34095aa 100644 --- a/src/vulkan/PipelineLayout.zig +++ b/src/vulkan/PipelineLayout.zig @@ -23,19 +23,6 @@ dynamic_descriptor_offsets: [lib.VULKAN_MAX_DESCRIPTOR_SETS]usize, push_ranges_count: usize, push_ranges: [lib.VULKAN_MAX_PUSH_CONSTANT_RANGES]vk.PushConstantRange, -/// Mesa's common Vulkan runtime states: -/// -/// It's often necessary to store a pointer to the descriptor set layout in -/// the descriptor so that any entrypoint which has access to a descriptor -/// set also has the layout. While layouts are often passed into various -/// entrypoints, they're notably missing from vkUpdateDescriptorSets(). In -/// order to implement descriptor writes, you either need to stash a pointer -/// to the descriptor set layout in the descriptor set or you need to copy -/// all of the relevant information. Storing a pointer is a lot cheaper. -/// -/// Because descriptor set layout lifetimes and descriptor set lifetimes are -/// not guaranteed to coincide, we have to reference count if we're going to -/// do this. ref_count: std.atomic.Value(usize), vtable: *const VTable, diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index 177e5cc..d79a0e1 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -1957,20 +1957,14 @@ pub export fn strollCmdPipelineBarrier( ) catch |err| return errorLogger(err); } -pub export fn strollCmdPushConstants(p_cmd: vk.CommandBuffer, layout: vk.PipelineLayout, flags: vk.ShaderStageFlags, offset: u32, size: u32, values: *const anyopaque) callconv(vk.vulkan_call_conv) void { +pub export fn strollCmdPushConstants(p_cmd: vk.CommandBuffer, layout: vk.PipelineLayout, flags: vk.ShaderStageFlags, offset: u32, size: u32, data: [*]const u8) callconv(vk.vulkan_call_conv) void { entryPointBeginLogTrace(.vkCmdPushConstants); defer entryPointEndLogTrace(); const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); + cmd.pushConstants(flags, offset, data[0..size]) catch |err| return errorLogger(err); - notImplementedWarning(); - - _ = cmd; - _ = layout; - _ = flags; - _ = offset; - _ = size; - _ = values; + _ = layout; // Pipelines embed their layout which is more trustworthy } pub export fn strollCmdResetQueryPool(p_cmd: vk.CommandBuffer, p_pool: vk.QueryPool, first: u32, count: u32) callconv(vk.vulkan_call_conv) void {