From 6c6d11f0632b958d25fce5bfbac6cca0e621d7e8 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Sun, 26 Apr 2026 03:59:36 +0200 Subject: [PATCH] working on vertex implementation --- build.zig.zon | 4 +- src/soft/SoftCommandBuffer.zig | 2 +- src/soft/SoftDeviceMemory.zig | 5 +- src/soft/device/Device.zig | 2 +- src/soft/device/Renderer.zig | 100 ++++++++++++++++---------- src/soft/device/vertex_dispatcher.zig | 48 +++++++++++++ src/vulkan/Pipeline.zig | 50 ++++++++++++- 7 files changed, 167 insertions(+), 44 deletions(-) create mode 100644 src/soft/device/vertex_dispatcher.zig diff --git a/build.zig.zon b/build.zig.zon index 31047a9..8179baa 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -31,8 +31,8 @@ .hash = "zmath-0.11.0-dev-wjwivdMsAwD-xaLj76YHUq3t9JDH-X16xuMTmnDzqbu2", }, .SPIRV_Interpreter = .{ - .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#664ea9b92bf84bc97ec4a062c171562bf6628263", - .hash = "SPIRV_Interpreter-0.0.1-ajmpn1qKBACshq_ncUUF-zXJzpdNLRzIAPcWRQL57W8l", + .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#5b7380eea014dce11587597d44681640a3a3583b", + .hash = "SPIRV_Interpreter-0.0.1-ajmpnwaZBAA5PJz7wTcWkWPywJAJF672vYjuCZKO4t9j", .lazy = true, }, }, diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig index dd20dfc..139d255 100644 --- a/src/soft/SoftCommandBuffer.zig +++ b/src/soft/SoftCommandBuffer.zig @@ -482,7 +482,7 @@ pub fn draw(interface: *Interface, vertex_count: usize, instance_count: usize, f pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void { const impl: *Impl = @ptrCast(@alignCast(context)); - device.renderer.drawPrimitive(impl.vertex_count, impl.instance_count, impl.first_vertex, impl.first_instance); + try device.renderer.draw(impl.vertex_count, impl.instance_count, impl.first_vertex, impl.first_instance); } }; diff --git a/src/soft/SoftDeviceMemory.zig b/src/soft/SoftDeviceMemory.zig index a5b3989..a4d5ab2 100644 --- a/src/soft/SoftDeviceMemory.zig +++ b/src/soft/SoftDeviceMemory.zig @@ -58,10 +58,9 @@ pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) Vk if (offset >= self.data.len or (size != vk.WHOLE_SIZE and offset + size > self.data.len)) { return VkError.MemoryMapFailed; } - interface.is_mapped = true; return @ptrCast(&self.data[offset]); } -pub fn unmap(interface: *Interface) void { - interface.is_mapped = false; +pub fn unmap(_: *Interface) void { + // no-op } diff --git a/src/soft/device/Device.zig b/src/soft/device/Device.zig index 7ea5ecc..8d9fdf1 100644 --- a/src/soft/device/Device.zig +++ b/src/soft/device/Device.zig @@ -54,7 +54,7 @@ pub fn init(self: *Self, device: *SoftDevice) void { }; } self.compute = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.compute)]); - self.renderer = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.compute)]); + self.renderer = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.graphics)]); } pub fn deinit(self: *Self) void { diff --git a/src/soft/device/Renderer.zig b/src/soft/device/Renderer.zig index 961356c..28da44b 100644 --- a/src/soft/device/Renderer.zig +++ b/src/soft/device/Renderer.zig @@ -15,21 +15,12 @@ const SoftFramebuffer = @import("../SoftFramebuffer.zig"); const SoftPipeline = @import("../SoftPipeline.zig"); const SoftRenderPass = @import("../SoftRenderPass.zig"); +const vertex_dispatcher = @import("vertex_dispatcher.zig"); + const VkError = base.VkError; const Self = @This(); -const VertexInputBindingState = struct { - input_rate: vk.VertexInputRate, - stride: usize, -}; - -const VertexInputAttributeState = struct { - format: vk.Format, - offset: usize, - binding: usize, -}; - pub const VertexBuffer = struct { buffer: *const SoftBuffer, offset: usize, @@ -39,20 +30,7 @@ pub const VertexBuffer = struct { pub const DynamicState = struct { viewport: vk.Viewport, scissor: vk.Rect2D, - line_width: f32, - cull_mode: vk.CullModeFlags, - front_face: vk.FrontFace, - primitive_topology: vk.PrimitiveTopology, - - vertex_input_bindings: [lib.MAX_VERTEX_INPUT_BINDINGS]VertexInputBindingState, - vertex_input_attributes: [lib.MAX_VERTEX_INPUT_ATTRIBUTES]VertexInputAttributeState, -}; - -const Vertex = struct { - position: F32x4, - point_size: f32, - index: usize, }; device: *SoftDevice, @@ -72,23 +50,73 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self { }; } -pub fn drawPrimitive(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) void { - const allocator = self.device.device_allocator.allocator(); +pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void { + _ = first_vertex; + _ = first_instance; - const vertices = self.fetchVertexInput(allocator, vertex_count, instance_count, first_vertex, first_instance); - _ = vertices; + self.inputAssemblyStage() catch |err| { + std.log.scoped(.@"Input assembly stage").err("catched a '{s}'", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpErrorReturnTrace(trace); + } + }; + + self.vertexShaderStage(vertex_count, instance_count) catch |err| { + std.log.scoped(.@"Input assembly stage").err("catched a '{s}'", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpErrorReturnTrace(trace); + } + }; + + self.primitiveAssemblyStage(); + self.fragmentShaderStage(); } pub fn deinit(self: *Self) void { _ = self; } -fn fetchVertexInput(self: *const Self, allocator: std.mem.Allocator, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) []Vertex { - _ = self; - _ = allocator; - _ = vertex_count; - _ = instance_count; - _ = first_vertex; - _ = first_instance; - return undefined; +fn inputAssemblyStage(self: *Self) !void { + const pipeline = self.state.pipeline orelse return; + for ((pipeline.stages.getPtr(.vertex) orelse return).runtimes) |*rt| { + for (pipeline.interface.mode.graphics.input_assembly.attribute_description orelse return) |attribute| { + const location_result = try rt.getResultByLocation(attribute.location, .input); + + const vertex_buffer = self.state.data.graphics.vertex_buffers[attribute.binding]; + const buffer = vertex_buffer.buffer; + const buffer_memory_size = base.format.texelSize(attribute.format); + const buffer_memory = if (buffer.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; + const buffer_memory_map: []u8 = @as([*]u8, @ptrCast(@alignCast(try buffer_memory.map(buffer.interface.offset + attribute.offset, buffer_memory_size))))[0..buffer_memory_size]; + + try rt.writeInput(buffer_memory_map, location_result); + } + } +} + +fn vertexShaderStage(self: *Self, vertex_count: usize, instance_count: usize) !void { + const invocation_count = vertex_count * instance_count; + const pipeline = self.state.pipeline orelse return; + const batch_size = (pipeline.stages.getPtr(.vertex) orelse return).runtimes.len; + + var wg: std.Io.Group = .init; + for (0..@min(batch_size, invocation_count)) |batch_id| { + const run_data: vertex_dispatcher.RunData = .{ + .renderer = self, + .pipeline = pipeline, + .batch_id = batch_id, + .batch_size = batch_size, + .invocation_count = invocation_count, + }; + + wg.async(self.device.interface.io(), vertex_dispatcher.runWrapper, .{run_data}); + } + wg.await(self.device.interface.io()) catch return VkError.DeviceLost; +} + +fn primitiveAssemblyStage(self: *Self) void { + _ = self; +} + +fn fragmentShaderStage(self: *Self) void { + _ = self; } diff --git a/src/soft/device/vertex_dispatcher.zig b/src/soft/device/vertex_dispatcher.zig new file mode 100644 index 0000000..28bc5f5 --- /dev/null +++ b/src/soft/device/vertex_dispatcher.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const spv = @import("spv"); + +const SpvRuntimeError = spv.Runtime.RuntimeError; + +const Renderer = @import("Renderer.zig"); +const SoftPipeline = @import("../SoftPipeline.zig"); + +pub const RunData = struct { + renderer: *Renderer, + pipeline: *SoftPipeline, + batch_id: usize, + batch_size: usize, + invocation_count: usize, +}; + +pub fn runWrapper(data: RunData) void { + @call(.always_inline, run, .{data}) catch |err| { + std.log.scoped(.@"SPIR-V runtime").err("SPIR-V runtime catched a '{s}'", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpErrorReturnTrace(trace); + } + }; +} + +inline fn run(data: RunData) !void { + const allocator = data.renderer.device.device_allocator.allocator(); + + const shader = data.pipeline.stages.getPtrAssertContains(.vertex); + const rt = &shader.runtimes[data.batch_id]; + + const entry = try rt.getEntryPointByName(shader.entry); + + var invocation_index: usize = data.batch_id; + while (invocation_index < data.invocation_count) : (invocation_index += data.batch_size) { + rt.callEntryPoint(allocator, entry) catch |err| switch (err) { + // Some errors can be ignored + SpvRuntimeError.OutOfBounds, + SpvRuntimeError.Killed, + => {}, + else => return err, + }; + + var output: [4]f32 = undefined; + try rt.readBuiltIn(std.mem.asBytes(output[0..output.len]), .Position); + std.debug.print("Output: Vec4{any}\n", .{output}); + } +} diff --git a/src/vulkan/Pipeline.zig b/src/vulkan/Pipeline.zig index 1388244..262efb1 100644 --- a/src/vulkan/Pipeline.zig +++ b/src/vulkan/Pipeline.zig @@ -16,6 +16,16 @@ owner: *Device, vtable: *const VTable, bind_point: vk.PipelineBindPoint, stages: vk.ShaderStageFlags, +mode: union(enum) { + compute: struct {}, + graphics: struct { + input_assembly: struct { + binding_description: ?[]vk.VertexInputBindingDescription, + attribute_description: ?[]vk.VertexInputAttributeDescription, + topology: vk.PrimitiveTopology, + }, + }, +}, pub const VTable = struct { destroy: *const fn (*Self, std.mem.Allocator) void, @@ -30,11 +40,11 @@ pub fn initCompute(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipel .vtable = undefined, .bind_point = .compute, .stages = info.stage.stage, + .mode = .{ .compute = .{} }, }; } pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.GraphicsPipelineCreateInfo) VkError!Self { - _ = allocator; _ = cache; var stages: vk.ShaderStageFlags = .{}; @@ -49,9 +59,47 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe .vtable = undefined, .bind_point = .graphics, .stages = stages, + .mode = .{ + .graphics = .{ + .input_assembly = .{ + .binding_description = blk: { + if (info.p_vertex_input_state) |vertex_input_state| { + if (vertex_input_state.p_vertex_binding_descriptions) |vertex_binding_descriptions| { + break :blk allocator.dupe(vk.VertexInputBindingDescription, vertex_binding_descriptions[0..vertex_input_state.vertex_binding_description_count]) catch return VkError.OutOfHostMemory; + } + } else { + return VkError.ValidationFailed; + } + break :blk null; + }, + .attribute_description = blk: { + if (info.p_vertex_input_state) |vertex_input_state| { + if (vertex_input_state.p_vertex_attribute_descriptions) |vertex_attribute_descriptions| { + break :blk allocator.dupe(vk.VertexInputAttributeDescription, vertex_attribute_descriptions[0..vertex_input_state.vertex_attribute_description_count]) catch return VkError.OutOfHostMemory; + } + } else { + return VkError.ValidationFailed; + } + break :blk null; + }, + .topology = if (info.p_input_assembly_state) |state| state.topology else return VkError.ValidationFailed, + }, + }, + }, }; } pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { + switch (self.mode) { + .compute => {}, + .graphics => |graphics| { + if (graphics.input_assembly.binding_description) |binding_description| { + allocator.free(binding_description); + } + if (graphics.input_assembly.attribute_description) |attribute_description| { + allocator.free(attribute_description); + } + }, + } self.vtable.destroy(self, allocator); }