yes
This commit is contained in:
@@ -81,15 +81,15 @@ vkCmdPushConstants | ✅ Implemented
|
|||||||
vkCmdResetEvent | ✅ Implemented
|
vkCmdResetEvent | ✅ Implemented
|
||||||
vkCmdResetQueryPool | ⚙️ WIP
|
vkCmdResetQueryPool | ⚙️ WIP
|
||||||
vkCmdResolveImage | ✅ Implemented
|
vkCmdResolveImage | ✅ Implemented
|
||||||
vkCmdSetBlendConstants | ⚙️ WIP
|
vkCmdSetBlendConstants | ✅ Implemented
|
||||||
vkCmdSetDepthBias | ⚙️ WIP
|
vkCmdSetDepthBias | ⚙️ WIP
|
||||||
vkCmdSetDepthBounds | ⚙️ WIP
|
vkCmdSetDepthBounds | ⚙️ WIP
|
||||||
vkCmdSetEvent | ✅ Implemented
|
vkCmdSetEvent | ✅ Implemented
|
||||||
vkCmdSetLineWidth | ⚙️ WIP
|
vkCmdSetLineWidth | ⚙️ WIP
|
||||||
vkCmdSetScissor | ✅ Implemented
|
vkCmdSetScissor | ✅ Implemented
|
||||||
vkCmdSetStencilCompareMask | ⚙️ WIP
|
vkCmdSetStencilCompareMask | ✅ Implemented
|
||||||
vkCmdSetStencilReference | ⚙️ WIP
|
vkCmdSetStencilReference | ✅ Implemented
|
||||||
vkCmdSetStencilWriteMask | ⚙️ WIP
|
vkCmdSetStencilWriteMask | ✅ Implemented
|
||||||
vkCmdSetViewport | ✅ Implemented
|
vkCmdSetViewport | ✅ Implemented
|
||||||
vkCmdUpdateBuffer | ⚙️ WIP
|
vkCmdUpdateBuffer | ⚙️ WIP
|
||||||
vkCmdWaitEvents | ✅ Implemented
|
vkCmdWaitEvents | ✅ Implemented
|
||||||
|
|||||||
+7
-7
@@ -25,16 +25,16 @@
|
|||||||
.url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7",
|
.url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7",
|
||||||
.hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3",
|
.hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3",
|
||||||
},
|
},
|
||||||
.SPIRV_Interpreter = .{
|
|
||||||
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#8677e8a6833d39e9169460fd75de2d7c70fab4b8",
|
|
||||||
.hash = "SPIRV_Interpreter-0.0.1-ajmpn_HtBQBR82M2KlR_yuUvdHUVdXrYVGRh5YNqVFqQ",
|
|
||||||
.lazy = true,
|
|
||||||
},
|
|
||||||
//.SPIRV_Interpreter = .{
|
//.SPIRV_Interpreter = .{
|
||||||
// // For development
|
// .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#8677e8a6833d39e9169460fd75de2d7c70fab4b8",
|
||||||
// .path = "../SPIRV-Interpreter",
|
// .hash = "SPIRV_Interpreter-0.0.1-ajmpn_HtBQBR82M2KlR_yuUvdHUVdXrYVGRh5YNqVFqQ",
|
||||||
// .lazy = true,
|
// .lazy = true,
|
||||||
//},
|
//},
|
||||||
|
.SPIRV_Interpreter = .{
|
||||||
|
// For development
|
||||||
|
.path = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#ee5a400010b3a5852606b2f8593bd5c745bf0a26",
|
||||||
|
.lazy = true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
.paths = .{
|
.paths = .{
|
||||||
|
|||||||
@@ -75,7 +75,11 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
|
|||||||
.resetEvent = resetEvent,
|
.resetEvent = resetEvent,
|
||||||
.resolveImage = resolveImage,
|
.resolveImage = resolveImage,
|
||||||
.setEvent = setEvent,
|
.setEvent = setEvent,
|
||||||
|
.setBlendConstants = setBlendConstants,
|
||||||
.setScissor = setScissor,
|
.setScissor = setScissor,
|
||||||
|
.setStencilCompareMask = setStencilCompareMask,
|
||||||
|
.setStencilReference = setStencilReference,
|
||||||
|
.setStencilWriteMask = setStencilWriteMask,
|
||||||
.setViewport = setViewport,
|
.setViewport = setViewport,
|
||||||
.waitEvent = waitEvent,
|
.waitEvent = waitEvent,
|
||||||
};
|
};
|
||||||
@@ -1059,6 +1063,85 @@ pub fn setViewport(interface: *Interface, first: u32, viewports: []const vk.View
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setBlendConstants(interface: *Interface, constants: [4]f32) VkError!void {
|
||||||
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||||
|
const allocator = self.command_allocator.allocator();
|
||||||
|
|
||||||
|
const CommandImpl = struct {
|
||||||
|
const Impl = @This();
|
||||||
|
|
||||||
|
constants: [4]f32,
|
||||||
|
|
||||||
|
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
|
||||||
|
const impl: *Impl = @ptrCast(@alignCast(context));
|
||||||
|
device.renderer.dynamic_state.blend_constants = impl.constants;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
|
||||||
|
errdefer allocator.destroy(cmd);
|
||||||
|
cmd.* = .{ .constants = constants };
|
||||||
|
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setStencilCompareMask(interface: *Interface, face_mask: vk.StencilFaceFlags, compare_mask: u32) VkError!void {
|
||||||
|
try setStencilDynamicState(interface, face_mask, compare_mask, .compare_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setStencilReference(interface: *Interface, face_mask: vk.StencilFaceFlags, reference: u32) VkError!void {
|
||||||
|
try setStencilDynamicState(interface, face_mask, reference, .reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setStencilWriteMask(interface: *Interface, face_mask: vk.StencilFaceFlags, write_mask: u32) VkError!void {
|
||||||
|
try setStencilDynamicState(interface, face_mask, write_mask, .write_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setStencilDynamicState(interface: *Interface, face_mask: vk.StencilFaceFlags, value: u32, comptime kind: enum { compare_mask, reference, write_mask }) VkError!void {
|
||||||
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||||
|
const allocator = self.command_allocator.allocator();
|
||||||
|
|
||||||
|
const CommandImpl = struct {
|
||||||
|
const Impl = @This();
|
||||||
|
|
||||||
|
face_mask: vk.StencilFaceFlags,
|
||||||
|
value: u32,
|
||||||
|
|
||||||
|
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
|
||||||
|
const impl: *Impl = @ptrCast(@alignCast(context));
|
||||||
|
if (!impl.face_mask.front_bit and !impl.face_mask.back_bit)
|
||||||
|
return;
|
||||||
|
switch (kind) {
|
||||||
|
.compare_mask => {
|
||||||
|
if (impl.face_mask.front_bit)
|
||||||
|
device.renderer.dynamic_state.stencil_front_compare_mask = impl.value;
|
||||||
|
if (impl.face_mask.back_bit)
|
||||||
|
device.renderer.dynamic_state.stencil_back_compare_mask = impl.value;
|
||||||
|
},
|
||||||
|
.reference => {
|
||||||
|
if (impl.face_mask.front_bit)
|
||||||
|
device.renderer.dynamic_state.stencil_front_reference = impl.value;
|
||||||
|
if (impl.face_mask.back_bit)
|
||||||
|
device.renderer.dynamic_state.stencil_back_reference = impl.value;
|
||||||
|
},
|
||||||
|
.write_mask => {
|
||||||
|
if (impl.face_mask.front_bit)
|
||||||
|
device.renderer.dynamic_state.stencil_front_write_mask = impl.value;
|
||||||
|
if (impl.face_mask.back_bit)
|
||||||
|
device.renderer.dynamic_state.stencil_back_write_mask = impl.value;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
|
||||||
|
errdefer allocator.destroy(cmd);
|
||||||
|
cmd.* = .{
|
||||||
|
.face_mask = face_mask,
|
||||||
|
.value = value,
|
||||||
|
};
|
||||||
|
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn waitEvent(interface: *Interface, event: *base.Event, src_stage: vk.PipelineStageFlags, dst_stage: vk.PipelineStageFlags, memory_barriers: []const vk.MemoryBarrier, buffer_barriers: []const vk.BufferMemoryBarrier, image_barriers: []const vk.ImageMemoryBarrier) VkError!void {
|
pub fn waitEvent(interface: *Interface, event: *base.Event, src_stage: vk.PipelineStageFlags, dst_stage: vk.PipelineStageFlags, memory_barriers: []const vk.MemoryBarrier, buffer_barriers: []const vk.BufferMemoryBarrier, image_barriers: []const vk.ImageMemoryBarrier) VkError!void {
|
||||||
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||||
const allocator = self.command_allocator.allocator();
|
const allocator = self.command_allocator.allocator();
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ inline fn run(data: RunData) !void {
|
|||||||
if (!uses_control_barrier)
|
if (!uses_control_barrier)
|
||||||
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
|
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
|
||||||
|
|
||||||
|
try rt.populatePushConstants(data.self.state.push_constant_blob[0..]);
|
||||||
|
|
||||||
var group_index: usize = data.batch_id;
|
var group_index: usize = data.batch_id;
|
||||||
while (group_index < data.group_count) : (group_index += data.self.batch_size) {
|
while (group_index < data.group_count) : (group_index += data.self.batch_size) {
|
||||||
var modulo: usize = group_index;
|
var modulo: usize = group_index;
|
||||||
@@ -205,6 +207,7 @@ fn runBarrierWorkgroup(
|
|||||||
for (runtimes, 0..) |*rt, i| {
|
for (runtimes, 0..) |*rt, i| {
|
||||||
rt.resetInvocation(allocator);
|
rt.resetInvocation(allocator);
|
||||||
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
|
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
|
||||||
|
try rt.populatePushConstants(data.self.state.push_constant_blob[0..]);
|
||||||
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);
|
||||||
statuses[i] = try rt.beginEntryPoint(allocator, entry);
|
statuses[i] = try rt.beginEntryPoint(allocator, entry);
|
||||||
|
|||||||
@@ -43,11 +43,18 @@ pub const DynamicState = struct {
|
|||||||
viewports: ?[]const vk.Viewport,
|
viewports: ?[]const vk.Viewport,
|
||||||
scissor: ?[]const vk.Rect2D,
|
scissor: ?[]const vk.Rect2D,
|
||||||
line_width: ?f32,
|
line_width: ?f32,
|
||||||
|
blend_constants: ?[4]f32,
|
||||||
|
stencil_front_compare_mask: ?u32,
|
||||||
|
stencil_back_compare_mask: ?u32,
|
||||||
|
stencil_front_write_mask: ?u32,
|
||||||
|
stencil_back_write_mask: ?u32,
|
||||||
|
stencil_front_reference: ?u32,
|
||||||
|
stencil_back_reference: ?u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Vertex = struct {
|
pub const Vertex = struct {
|
||||||
position: F32x4,
|
position: F32x4,
|
||||||
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]?struct {
|
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct {
|
||||||
interpolation_type: enum { smooth, flat, noperspective },
|
interpolation_type: enum { smooth, flat, noperspective },
|
||||||
blob: []u8,
|
blob: []u8,
|
||||||
size: usize,
|
size: usize,
|
||||||
@@ -67,6 +74,7 @@ pub const DrawCall = struct {
|
|||||||
render_pass: *SoftRenderPass,
|
render_pass: *SoftRenderPass,
|
||||||
framebuffer: *SoftFramebuffer,
|
framebuffer: *SoftFramebuffer,
|
||||||
|
|
||||||
|
allocator_mutex: std.Io.Mutex,
|
||||||
rasterizer_wait_group: std.Io.Group,
|
rasterizer_wait_group: std.Io.Group,
|
||||||
|
|
||||||
stats: struct {
|
stats: struct {
|
||||||
@@ -86,6 +94,7 @@ pub const DrawCall = struct {
|
|||||||
.depth_attachment = if (render_pass.interface.subpasses[renderer.subpass_index].depth_stencil_attachments) |desc| framebuffer.interface.attachments[desc.attachment] else null,
|
.depth_attachment = if (render_pass.interface.subpasses[renderer.subpass_index].depth_stencil_attachments) |desc| framebuffer.interface.attachments[desc.attachment] else null,
|
||||||
.render_pass = render_pass,
|
.render_pass = render_pass,
|
||||||
.framebuffer = framebuffer,
|
.framebuffer = framebuffer,
|
||||||
|
.allocator_mutex = .init,
|
||||||
.rasterizer_wait_group = .init,
|
.rasterizer_wait_group = .init,
|
||||||
.stats = .{
|
.stats = .{
|
||||||
.polygons_drawn = 0,
|
.polygons_drawn = 0,
|
||||||
@@ -93,7 +102,9 @@ pub const DrawCall = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (self.vertices) |*vertex| {
|
for (self.vertices) |*vertex| {
|
||||||
@memset(vertex.outputs[0..], null);
|
for (&vertex.outputs) |*location| {
|
||||||
|
@memset(location, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -102,8 +113,10 @@ pub const DrawCall = struct {
|
|||||||
fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
|
fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
|
||||||
for (self.vertices) |*vertex| {
|
for (self.vertices) |*vertex| {
|
||||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||||
if (vertex.outputs[location]) |output| {
|
for (0..4) |component| {
|
||||||
allocator.free(output.blob);
|
if (vertex.outputs[location][component]) |output| {
|
||||||
|
allocator.free(output.blob);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,6 +143,13 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self {
|
|||||||
.viewports = null,
|
.viewports = null,
|
||||||
.scissor = null,
|
.scissor = null,
|
||||||
.line_width = null,
|
.line_width = null,
|
||||||
|
.blend_constants = null,
|
||||||
|
.stencil_front_compare_mask = null,
|
||||||
|
.stencil_back_compare_mask = null,
|
||||||
|
.stencil_front_write_mask = null,
|
||||||
|
.stencil_back_write_mask = null,
|
||||||
|
.stencil_front_reference = null,
|
||||||
|
.stencil_back_reference = null,
|
||||||
},
|
},
|
||||||
.subpass_index = 0,
|
.subpass_index = 0,
|
||||||
};
|
};
|
||||||
@@ -189,9 +209,10 @@ fn drawCall(self: *Self, bounded_allocator: *BoundedAllocator, vertex_count: usi
|
|||||||
for (vertex_shader.runtimes[0..]) |*runtime| {
|
for (vertex_shader.runtimes[0..]) |*runtime| {
|
||||||
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
|
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
|
||||||
}
|
}
|
||||||
const fragment_shader = pipeline.stages.getPtrAssertContains(.fragment);
|
if (pipeline.stages.getPtr(.fragment)) |fragment_shader| {
|
||||||
for (fragment_shader.runtimes[0..]) |*runtime| {
|
for (fragment_shader.runtimes[0..]) |*runtime| {
|
||||||
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
|
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vertexShaderStage(allocator, &draw_call, vertex_count, instance_count, first_vertex, first_instance, indices) catch |err| {
|
self.vertexShaderStage(allocator, &draw_call, vertex_count, instance_count, first_vertex, first_instance, indices) catch |err| {
|
||||||
|
|||||||
@@ -1096,7 +1096,7 @@ pub fn writeFloat4(c: F32x4, map: []u8, dst_format: vk.Format) void {
|
|||||||
const r: u5 = @intFromFloat(@round(color[0] * std.math.maxInt(u5)));
|
const r: u5 = @intFromFloat(@round(color[0] * std.math.maxInt(u5)));
|
||||||
const g: u5 = @intFromFloat(@round(color[1] * std.math.maxInt(u5)));
|
const g: u5 = @intFromFloat(@round(color[1] * std.math.maxInt(u5)));
|
||||||
const b: u5 = @intFromFloat(@round(color[2] * std.math.maxInt(u5)));
|
const b: u5 = @intFromFloat(@round(color[2] * std.math.maxInt(u5)));
|
||||||
const a: u1 = @intFromFloat(color[3]);
|
const a: u1 = @intFromFloat(@round(color[3]));
|
||||||
std.mem.bytesAsValue(u16, map).* =
|
std.mem.bytesAsValue(u16, map).* =
|
||||||
(@as(u16, b) << 0) |
|
(@as(u16, b) << 0) |
|
||||||
(@as(u16, g) << 5) |
|
(@as(u16, g) << 5) |
|
||||||
|
|||||||
+15
-11
@@ -173,20 +173,24 @@ fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex,
|
|||||||
.outputs = undefined,
|
.outputs = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@memset(result.outputs[0..], null);
|
for (&result.outputs) |*location| {
|
||||||
|
@memset(location, null);
|
||||||
|
}
|
||||||
|
|
||||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||||
const out_a = a.outputs[location] orelse continue;
|
for (0..4) |component| {
|
||||||
const out_b = b.outputs[location] orelse continue;
|
const out_a = a.outputs[location][component] orelse continue;
|
||||||
|
const out_b = b.outputs[location][component] orelse continue;
|
||||||
|
|
||||||
result.outputs[location] = .{
|
result.outputs[location][component] = .{
|
||||||
.interpolation_type = out_a.interpolation_type,
|
.interpolation_type = out_a.interpolation_type,
|
||||||
.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, @min(out_a.size, out_b.size), 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),
|
.size = @min(out_a.size, out_b.size),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const base = @import("base");
|
|||||||
const zm = base.zm;
|
const zm = base.zm;
|
||||||
const spv = @import("spv");
|
const spv = @import("spv");
|
||||||
|
|
||||||
const VertexInterpolation = @import("rasterizer/common.zig").VertexInterpolation;
|
const VertexInterpolationLocation = @import("rasterizer/common.zig").VertexInterpolationLocation;
|
||||||
|
|
||||||
const Renderer = @import("Renderer.zig");
|
const Renderer = @import("Renderer.zig");
|
||||||
const SoftImage = @import("../SoftImage.zig");
|
const SoftImage = @import("../SoftImage.zig");
|
||||||
@@ -18,7 +18,7 @@ pub fn shaderInvocation(
|
|||||||
draw_call: *Renderer.DrawCall,
|
draw_call: *Renderer.DrawCall,
|
||||||
batch_id: usize,
|
batch_id: usize,
|
||||||
position: zm.F32x4,
|
position: zm.F32x4,
|
||||||
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation,
|
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
|
||||||
) SpvRuntimeError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 {
|
) SpvRuntimeError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 {
|
||||||
var fragment_inputs = inputs;
|
var fragment_inputs = inputs;
|
||||||
errdefer freeOwnedInputs(allocator, fragment_inputs);
|
errdefer freeOwnedInputs(allocator, fragment_inputs);
|
||||||
@@ -45,26 +45,28 @@ pub fn shaderInvocation(
|
|||||||
const entry = try rt.getEntryPointByName(shader.entry);
|
const entry = try rt.getEntryPointByName(shader.entry);
|
||||||
|
|
||||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||||
const result_word = rt.getResultByLocation(@intCast(location), .input) catch |err| switch (err) {
|
for (0..4) |component| {
|
||||||
SpvRuntimeError.NotFound => continue,
|
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch |err| switch (err) {
|
||||||
else => return err,
|
SpvRuntimeError.NotFound => continue,
|
||||||
};
|
else => return err,
|
||||||
|
|
||||||
var input = fragment_inputs[location];
|
|
||||||
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) {
|
var input = fragment_inputs[location][component];
|
||||||
try rt.writeInput(input.blob, result_word);
|
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][component] = .{
|
||||||
|
.blob = zeroes,
|
||||||
|
.size = memory_size,
|
||||||
|
.free_responsability = true,
|
||||||
|
};
|
||||||
|
input = fragment_inputs[location][component];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.blob.len != 0) {
|
||||||
|
try rt.writeInput(input.blob, result_word);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +82,29 @@ pub fn shaderInvocation(
|
|||||||
@memset(std.mem.asBytes(&outputs), 0);
|
@memset(std.mem.asBytes(&outputs), 0);
|
||||||
|
|
||||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||||
|
var has_split_components = false;
|
||||||
|
for (1..4) |component| {
|
||||||
|
_ = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
|
||||||
|
SpvRuntimeError.NotFound => continue,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
has_split_components = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_split_components) {
|
||||||
|
for (0..4) |component| {
|
||||||
|
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
|
||||||
|
SpvRuntimeError.NotFound => continue,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
const memory_size = try rt.getResultMemorySize(result_word);
|
||||||
|
const offset = component * @sizeOf(f32);
|
||||||
|
try rt.readOutput(outputs[location][offset .. offset + memory_size], result_word);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const result_word = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
|
const result_word = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
|
||||||
SpvRuntimeError.NotFound => continue,
|
SpvRuntimeError.NotFound => continue,
|
||||||
else => return err,
|
else => return err,
|
||||||
@@ -93,9 +118,11 @@ pub fn shaderInvocation(
|
|||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn freeOwnedInputs(allocator: std.mem.Allocator, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation) void {
|
fn freeOwnedInputs(allocator: std.mem.Allocator, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation) void {
|
||||||
for (inputs) |input| {
|
for (inputs) |location| {
|
||||||
if (input.free_responsability)
|
for (location) |input| {
|
||||||
allocator.free(input.blob);
|
if (input.free_responsability)
|
||||||
|
allocator.free(input.blob);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-41
@@ -1,6 +1,7 @@
|
|||||||
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 spv = @import("spv");
|
||||||
const zm = base.zm;
|
const zm = base.zm;
|
||||||
|
|
||||||
const clip = @import("clip.zig");
|
const clip = @import("clip.zig");
|
||||||
@@ -9,7 +10,6 @@ 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 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;
|
||||||
@@ -37,6 +37,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
|
|
||||||
const color_range = render_target_view.subresource_range;
|
const color_range = render_target_view.subresource_range;
|
||||||
const color_format = render_target_view.format;
|
const color_format = render_target_view.format;
|
||||||
|
const color_extent = render_target.getMipLevelExtent(color_range.base_mip_level);
|
||||||
|
|
||||||
const color_attachment_subresource_offset = try render_target.getSubresourceOffset(
|
const color_attachment_subresource_offset = try render_target.getSubresourceOffset(
|
||||||
color_range.aspect_mask,
|
color_range.aspect_mask,
|
||||||
@@ -49,6 +50,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
.base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size),
|
.base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size),
|
||||||
.row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format),
|
.row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format),
|
||||||
.texel_size = base.format.texelSize(color_format),
|
.texel_size = base.format.texelSize(color_format),
|
||||||
|
.width = color_extent.width,
|
||||||
|
.height = color_extent.height,
|
||||||
.format = color_format,
|
.format = color_format,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -61,20 +64,58 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
break :blk null;
|
break :blk null;
|
||||||
|
|
||||||
const depth_range = depth_attachment_view.?.subresource_range;
|
const depth_range = depth_attachment_view.?.subresource_range;
|
||||||
|
if (!depth_range.aspect_mask.depth_bit)
|
||||||
|
break :blk null;
|
||||||
|
|
||||||
const depth_format = depth_attachment_view.?.format;
|
const depth_format = depth_attachment_view.?.format;
|
||||||
|
const depth_aspect: vk.ImageAspectFlags = .{ .depth_bit = true };
|
||||||
|
const depth_aspect_format = base.format.fromAspect(depth_format, depth_aspect);
|
||||||
|
const depth_extent = depth_attachment.?.getMipLevelExtent(depth_range.base_mip_level);
|
||||||
|
|
||||||
const attachment_subresource_offset = try depth_attachment.?.getSubresourceOffset(
|
const attachment_subresource_offset = try depth_attachment.?.getSubresourceOffset(
|
||||||
depth_range.aspect_mask,
|
depth_aspect,
|
||||||
depth_range.base_mip_level,
|
depth_range.base_mip_level,
|
||||||
depth_range.base_array_layer,
|
depth_range.base_array_layer,
|
||||||
);
|
);
|
||||||
const attachment_subresource_size = depth_attachment.?.getLayerSize(depth_range.aspect_mask);
|
const attachment_subresource_size = depth_attachment.?.getLayerSize(depth_aspect);
|
||||||
break :blk .{
|
break :blk .{
|
||||||
.mutex = .init,
|
.mutex = .init,
|
||||||
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
||||||
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_range.aspect_mask, depth_range.base_mip_level, depth_format),
|
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_aspect, depth_range.base_mip_level, depth_format),
|
||||||
.texel_size = base.format.texelSize(depth_format),
|
.texel_size = base.format.texelSize(depth_aspect_format),
|
||||||
.format = depth_format,
|
.width = depth_extent.width,
|
||||||
|
.height = depth_extent.height,
|
||||||
|
.format = depth_aspect_format,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var stencil_attachment_access: ?common.RenderTargetAccess = blk: {
|
||||||
|
if (depth_attachment == null)
|
||||||
|
break :blk null;
|
||||||
|
|
||||||
|
const stencil_range = depth_attachment_view.?.subresource_range;
|
||||||
|
if (!stencil_range.aspect_mask.stencil_bit)
|
||||||
|
break :blk null;
|
||||||
|
|
||||||
|
const stencil_format = depth_attachment_view.?.format;
|
||||||
|
const stencil_aspect: vk.ImageAspectFlags = .{ .stencil_bit = true };
|
||||||
|
const stencil_aspect_format = base.format.fromAspect(stencil_format, stencil_aspect);
|
||||||
|
const stencil_extent = depth_attachment.?.getMipLevelExtent(stencil_range.base_mip_level);
|
||||||
|
|
||||||
|
const attachment_subresource_offset = try depth_attachment.?.getSubresourceOffset(
|
||||||
|
stencil_aspect,
|
||||||
|
stencil_range.base_mip_level,
|
||||||
|
stencil_range.base_array_layer,
|
||||||
|
);
|
||||||
|
const attachment_subresource_size = depth_attachment.?.getLayerSize(stencil_aspect);
|
||||||
|
break :blk .{
|
||||||
|
.mutex = .init,
|
||||||
|
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
||||||
|
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(stencil_aspect, stencil_range.base_mip_level, stencil_format),
|
||||||
|
.texel_size = base.format.texelSize(stencil_aspect_format),
|
||||||
|
.width = stencil_extent.width,
|
||||||
|
.height = stencil_extent.height,
|
||||||
|
.format = stencil_aspect_format,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,6 +127,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
vertex,
|
vertex,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
|
if (stencil_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| {
|
||||||
@@ -103,6 +145,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
v2,
|
v2,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
.triangle_fan => if (draw_call.vertices.len >= 3) {
|
.triangle_fan => if (draw_call.vertices.len >= 3) {
|
||||||
@@ -120,6 +163,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
v2,
|
v2,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -139,6 +183,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
v2,
|
v2,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
try clipTransformAndRasterizeTriangle(
|
try clipTransformAndRasterizeTriangle(
|
||||||
@@ -150,6 +195,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
v2,
|
v2,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,6 +212,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
v1,
|
v1,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
.line_strip => if (draw_call.vertices.len >= 2) {
|
.line_strip => if (draw_call.vertices.len >= 2) {
|
||||||
@@ -180,6 +227,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
v1,
|
v1,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -195,6 +243,7 @@ fn clipTransformAndRasterizePoint(
|
|||||||
vertex: *Vertex,
|
vertex: *Vertex,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
const x, const y, const z, const w = vertex.position;
|
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)
|
if (w == 0.0 or x < -w or x > w or y < -w or y > w or z < 0.0 or z > w)
|
||||||
@@ -208,6 +257,8 @@ fn clipTransformAndRasterizePoint(
|
|||||||
const max_x: i32 = @intFromFloat(@ceil(transformed.position[0] + (point_size / 2.0)) - 1.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 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);
|
const max_y: i32 = @intFromFloat(@ceil(transformed.position[1] + (point_size / 2.0)) - 1.0);
|
||||||
|
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
||||||
|
const has_fragment_shader = pipeline.stages.getPtr(.fragment) != null;
|
||||||
|
|
||||||
var py = min_y;
|
var py = min_y;
|
||||||
while (py <= max_y) : (py += 1) {
|
while (py <= max_y) : (py += 1) {
|
||||||
@@ -216,30 +267,27 @@ fn clipTransformAndRasterizePoint(
|
|||||||
if (!common.scissorContainsPixel(draw_call.scissor, px, py))
|
if (!common.scissorContainsPixel(draw_call.scissor, px, py))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (depth_attachment_access) |depth| {
|
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 = undefined;
|
||||||
const offset = @as(usize, @intCast(px)) * depth.texel_size + @as(usize, @intCast(py)) * depth.row_pitch;
|
@memset(std.mem.asBytes(&outputs), 0);
|
||||||
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
if (has_fragment_shader) {
|
||||||
if (transformed.position[2] >= depth_value[0])
|
outputs = fragment.shaderInvocation(
|
||||||
continue;
|
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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputs = fragment.shaderInvocation(
|
try common.writeToTargets(outputs, draw_call, color_attachment_access, depth_attachment_access, stencil_attachment_access, true, @intCast(px), @intCast(py), transformed.position[2]);
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,6 +299,7 @@ fn clipTransformAndRasterizeLine(
|
|||||||
v1: *Vertex,
|
v1: *Vertex,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
const clipped_line = (try clip.clipLine(allocator, v0, v1)) orelse return;
|
const clipped_line = (try clip.clipLine(allocator, v0, v1)) orelse return;
|
||||||
|
|
||||||
@@ -267,6 +316,7 @@ fn clipTransformAndRasterizeLine(
|
|||||||
&tv1,
|
&tv1,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
depth_attachment_access,
|
depth_attachment_access,
|
||||||
|
stencil_attachment_access,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +329,7 @@ fn clipTransformAndRasterizeTriangle(
|
|||||||
v2: *Vertex,
|
v2: *Vertex,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
const clipped_polygon = try clip.clipTriangle(allocator, v0, v1, v2);
|
const clipped_polygon = try clip.clipTriangle(allocator, v0, v1, v2);
|
||||||
|
|
||||||
@@ -303,6 +354,7 @@ fn clipTransformAndRasterizeTriangle(
|
|||||||
&tv2,
|
&tv2,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
depth_attachment_access,
|
depth_attachment_access,
|
||||||
|
stencil_attachment_access,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,29 +368,32 @@ fn rasterizeTriangle(
|
|||||||
v2: *Vertex,
|
v2: *Vertex,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
if (try triangleIsCulled(renderer, v0, v1, v2))
|
const maybe_front_face = try triangleFrontFace(renderer, v0, v1, v2);
|
||||||
|
const front_face = maybe_front_face orelse return;
|
||||||
|
|
||||||
|
if (try triangleIsCulled(renderer, front_face))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
draw_call.stats.polygons_drawn += 1;
|
draw_call.stats.polygons_drawn += 1;
|
||||||
|
|
||||||
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;
|
||||||
switch (pipeline_data.rasterization.polygon_mode) {
|
switch (pipeline_data.rasterization.polygon_mode) {
|
||||||
.fill => try edge_function.drawTriangle(allocator, draw_call, v0, v1, v2, color_attachment_access, depth_attachment_access),
|
.fill => try edge_function.drawTriangle(allocator, draw_call, v0, v1, v2, color_attachment_access, depth_attachment_access, stencil_attachment_access, front_face),
|
||||||
.line => {
|
.line => {
|
||||||
try bresenham.drawLine(allocator, draw_call, v0, v1, color_attachment_access, depth_attachment_access);
|
try bresenham.drawLine(allocator, draw_call, v0, v1, color_attachment_access, depth_attachment_access, stencil_attachment_access);
|
||||||
try bresenham.drawLine(allocator, draw_call, v1, v2, color_attachment_access, depth_attachment_access);
|
try bresenham.drawLine(allocator, draw_call, v1, v2, color_attachment_access, depth_attachment_access, stencil_attachment_access);
|
||||||
try bresenham.drawLine(allocator, draw_call, v2, v0, color_attachment_access, depth_attachment_access);
|
try bresenham.drawLine(allocator, draw_call, v2, v0, color_attachment_access, depth_attachment_access, stencil_attachment_access);
|
||||||
},
|
},
|
||||||
.point => {}, // TODO
|
.point => {}, // TODO
|
||||||
else => base.unsupported("polygon mode {any}", .{pipeline_data.rasterization.polygon_mode}),
|
else => base.unsupported("polygon mode {any}", .{pipeline_data.rasterization.polygon_mode}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn triangleIsCulled(renderer: *Renderer, v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) VkError!bool {
|
fn triangleIsCulled(renderer: *Renderer, front_face: bool) VkError!bool {
|
||||||
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 rasterization = pipeline_data.rasterization;
|
const cull_mode = pipeline_data.rasterization.cull_mode;
|
||||||
const cull_mode = rasterization.cull_mode;
|
|
||||||
|
|
||||||
if (!cull_mode.front_bit and !cull_mode.back_bit)
|
if (!cull_mode.front_bit and !cull_mode.back_bit)
|
||||||
return false;
|
return false;
|
||||||
@@ -346,17 +401,21 @@ fn triangleIsCulled(renderer: *Renderer, v0: *const Vertex, v1: *const Vertex, v
|
|||||||
if (cull_mode.front_bit and cull_mode.back_bit)
|
if (cull_mode.front_bit and cull_mode.back_bit)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
return (cull_mode.front_bit and front_face) or (cull_mode.back_bit and !front_face);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triangleFrontFace(renderer: *Renderer, v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) VkError!?bool {
|
||||||
|
const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
|
||||||
|
const rasterization = pipeline_data.rasterization;
|
||||||
const area = triangleArea(v0, v1, v2);
|
const area = triangleArea(v0, v1, v2);
|
||||||
if (area == 0.0)
|
if (area == 0.0)
|
||||||
return true;
|
return null;
|
||||||
|
|
||||||
const front_face = switch (rasterization.front_face) {
|
return switch (rasterization.front_face) {
|
||||||
.counter_clockwise => area < 0.0,
|
.counter_clockwise => area < 0.0,
|
||||||
.clockwise => area > 0.0,
|
.clockwise => area > 0.0,
|
||||||
else => return false,
|
else => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (cull_mode.front_bit and front_face) or (cull_mode.back_bit and !front_face);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn triangleArea(v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) f32 {
|
inline fn triangleArea(v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) f32 {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ const base = @import("base");
|
|||||||
const spv = @import("spv");
|
const spv = @import("spv");
|
||||||
const zm = base.zm;
|
const zm = base.zm;
|
||||||
|
|
||||||
const blitter = @import("../blitter.zig");
|
|
||||||
const common = @import("common.zig");
|
const common = @import("common.zig");
|
||||||
const fragment = @import("../fragment.zig");
|
const fragment = @import("../fragment.zig");
|
||||||
|
|
||||||
@@ -30,6 +29,8 @@ const RunData = struct {
|
|||||||
end_step: usize,
|
end_step: usize,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
has_fragment_shader: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn drawLine(
|
pub fn drawLine(
|
||||||
@@ -39,6 +40,7 @@ pub fn drawLine(
|
|||||||
v1: *Renderer.Vertex,
|
v1: *Renderer.Vertex,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
const io = draw_call.renderer.device.interface.io();
|
const io = draw_call.renderer.device.interface.io();
|
||||||
|
|
||||||
@@ -69,8 +71,8 @@ pub fn drawLine(
|
|||||||
const y_step: i32 = if (y0 > y1) -1 else 1;
|
const y_step: i32 = if (y0 > y1) -1 else 1;
|
||||||
|
|
||||||
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
||||||
|
const fragment_stage = pipeline.stages.getPtr(.fragment);
|
||||||
const runtimes_count = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
|
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1;
|
||||||
if (runtimes_count == 0)
|
if (runtimes_count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -104,15 +106,14 @@ pub fn drawLine(
|
|||||||
.end_step = end_step,
|
.end_step = end_step,
|
||||||
.color_attachment_access = color_attachment_access,
|
.color_attachment_access = color_attachment_access,
|
||||||
.depth_attachment_access = depth_attachment_access,
|
.depth_attachment_access = depth_attachment_access,
|
||||||
|
.stencil_attachment_access = stencil_attachment_access,
|
||||||
|
.has_fragment_shader = fragment_stage != null,
|
||||||
};
|
};
|
||||||
|
|
||||||
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
|
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not syncing workers between triangles when rendering without depth buffer
|
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
|
||||||
// will lead to pixel rendering order issues between triangles.
|
|
||||||
if (depth_attachment_access == null)
|
|
||||||
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bresenhamYAtStep(y0: i32, d_x: i32, d_err: i32, y_step: i32, step: usize) i32 {
|
fn bresenhamYAtStep(y0: i32, d_x: i32, d_err: i32, y_step: i32, step: usize) i32 {
|
||||||
@@ -151,31 +152,27 @@ inline fn run(data: RunData) !void {
|
|||||||
const t = @as(f32, @floatFromInt(step)) / @as(f32, @floatFromInt(@max(data.d_x, 1)));
|
const t = @as(f32, @floatFromInt(step)) / @as(f32, @floatFromInt(@max(data.d_x, 1)));
|
||||||
const z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]);
|
const z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]);
|
||||||
|
|
||||||
// Early depth test to avoid unnecesary computations
|
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8 = undefined;
|
||||||
if (data.depth_attachment_access) |depth| {
|
@memset(std.mem.asBytes(&outputs), 0);
|
||||||
const offset = @as(usize, @intCast(pixel_x)) * depth.texel_size + @as(usize, @intCast(pixel_y)) * depth.row_pitch;
|
if (data.has_fragment_shader) {
|
||||||
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
outputs = fragment.shaderInvocation(
|
||||||
if (z >= depth_value[0])
|
data.allocator,
|
||||||
continue;
|
data.draw_call,
|
||||||
|
data.batch_id,
|
||||||
|
zm.f32x4(@floatFromInt(pixel_x), @floatFromInt(pixel_y), z, 1.0),
|
||||||
|
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
|
||||||
|
) 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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputs = fragment.shaderInvocation(
|
try common.writeToTargets(outputs, data.draw_call, data.color_attachment_access, data.depth_attachment_access, data.stencil_attachment_access, true, @intCast(pixel_x), @intCast(pixel_y), z);
|
||||||
data.allocator,
|
|
||||||
data.draw_call,
|
|
||||||
data.batch_id,
|
|
||||||
zm.f32x4(@floatFromInt(pixel_x), @floatFromInt(pixel_y), z, 1.0),
|
|
||||||
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
|
|
||||||
) 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, data.draw_call, data.color_attachment_access, data.depth_attachment_access, @intCast(pixel_x), @intCast(pixel_y), z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub const RenderTargetAccess = struct {
|
|||||||
base: []u8,
|
base: []u8,
|
||||||
row_pitch: usize,
|
row_pitch: usize,
|
||||||
texel_size: usize,
|
texel_size: usize,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
format: vk.Format,
|
format: vk.Format,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,6 +27,8 @@ pub const VertexInterpolation = struct {
|
|||||||
free_responsability: bool,
|
free_responsability: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const VertexInterpolationLocation = [4]VertexInterpolation;
|
||||||
|
|
||||||
pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
|
pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
|
||||||
const min_x: i64 = @as(i64, scissor.offset.x);
|
const min_x: i64 = @as(i64, scissor.offset.x);
|
||||||
const min_y: i64 = @as(i64, scissor.offset.y);
|
const min_y: i64 = @as(i64, scissor.offset.y);
|
||||||
@@ -41,6 +45,117 @@ pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
|
|||||||
pixel_y < max_y;
|
pixel_y < max_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn targetContainsPixel(target: RenderTargetAccess, x: i32, y: i32) bool {
|
||||||
|
if (x < 0 or y < 0)
|
||||||
|
return false;
|
||||||
|
const pixel_x: u32 = @intCast(x);
|
||||||
|
const pixel_y: u32 = @intCast(y);
|
||||||
|
return pixel_x < target.width and pixel_y < target.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn targetOffset(target: RenderTargetAccess, x: usize, y: usize) ?usize {
|
||||||
|
if (x >= target.width or y >= target.height)
|
||||||
|
return null;
|
||||||
|
const offset = x * target.texel_size + y * target.row_pitch;
|
||||||
|
if (offset > target.base.len or target.texel_size > target.base.len - offset)
|
||||||
|
return null;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compare(comptime T: type, op: vk.CompareOp, reference: T, value: T) bool {
|
||||||
|
return switch (op) {
|
||||||
|
.never => false,
|
||||||
|
.less => reference < value,
|
||||||
|
.equal => reference == value,
|
||||||
|
.less_or_equal => reference <= value,
|
||||||
|
.greater => reference > value,
|
||||||
|
.not_equal => reference != value,
|
||||||
|
.greater_or_equal => reference >= value,
|
||||||
|
.always => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn applyStencilOp(op: vk.StencilOp, current: u32, reference: u32) u32 {
|
||||||
|
return switch (op) {
|
||||||
|
.keep => current,
|
||||||
|
.zero => 0,
|
||||||
|
.replace => reference,
|
||||||
|
.increment_and_clamp => @min(current +| 1, std.math.maxInt(u8)),
|
||||||
|
.decrement_and_clamp => if (current == 0) 0 else current - 1,
|
||||||
|
.invert => ~current,
|
||||||
|
.increment_and_wrap => current +% 1,
|
||||||
|
.decrement_and_wrap => current -% 1,
|
||||||
|
else => current,
|
||||||
|
} & std.math.maxInt(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn updateStencilValue(stencil: *RenderTargetAccess, offset: usize, state: vk.StencilOpState, op: vk.StencilOp) void {
|
||||||
|
const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8);
|
||||||
|
const op_value = applyStencilOp(op, current, state.reference & std.math.maxInt(u8));
|
||||||
|
const write_mask = state.write_mask & std.math.maxInt(u8);
|
||||||
|
const new_value = (current & ~write_mask) | (op_value & write_mask);
|
||||||
|
blitter.writeInt4(@splat(new_value), stencil.base[offset..], stencil.format);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stencilTestAndUpdate(
|
||||||
|
stencil: *RenderTargetAccess,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
state: vk.StencilOpState,
|
||||||
|
depth_passed: ?bool,
|
||||||
|
) bool {
|
||||||
|
const offset = targetOffset(stencil.*, x, y) orelse return false;
|
||||||
|
const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8);
|
||||||
|
const reference = state.reference & std.math.maxInt(u8);
|
||||||
|
const compare_mask = state.compare_mask & std.math.maxInt(u8);
|
||||||
|
const stencil_passed = compare(u32, state.compare_op, reference & compare_mask, current & compare_mask);
|
||||||
|
|
||||||
|
if (!stencil_passed) {
|
||||||
|
updateStencilValue(stencil, offset, state, state.fail_op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth_passed != null and !depth_passed.?) {
|
||||||
|
updateStencilValue(stencil, offset, state, state.depth_fail_op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStencilValue(stencil, offset, state, state.pass_op);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stencilTest(stencil: *RenderTargetAccess, offset: usize, state: vk.StencilOpState) bool {
|
||||||
|
const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8);
|
||||||
|
const reference = state.reference & std.math.maxInt(u8);
|
||||||
|
const compare_mask = state.compare_mask & std.math.maxInt(u8);
|
||||||
|
return compare(u32, state.compare_op, reference & compare_mask, current & compare_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
|
||||||
|
if (state.depth_test_enable == .false)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const offset = targetOffset(depth.*, x, y) orelse return false;
|
||||||
|
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
||||||
|
const passed = compare(f32, state.depth_compare_op, z, depth_value[0]);
|
||||||
|
if (passed and state.depth_write_enable == .true)
|
||||||
|
blitter.writeFloat4(zm.f32x4s(z), depth.base[offset..], depth.format);
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolveStencilState(draw_call: *Renderer.DrawCall, state: vk.StencilOpState, front: bool) vk.StencilOpState {
|
||||||
|
var resolved = state;
|
||||||
|
const dynamic = draw_call.renderer.dynamic_state;
|
||||||
|
if ((if (front) dynamic.stencil_front_compare_mask else dynamic.stencil_back_compare_mask)) |mask|
|
||||||
|
resolved.compare_mask = mask;
|
||||||
|
if ((if (front) dynamic.stencil_front_write_mask else dynamic.stencil_back_write_mask)) |mask|
|
||||||
|
resolved.write_mask = mask;
|
||||||
|
if ((if (front) dynamic.stencil_front_reference else dynamic.stencil_back_reference)) |reference|
|
||||||
|
resolved.reference = reference;
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn interpolateVertexOutputs(
|
pub fn interpolateVertexOutputs(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
v0: *const Renderer.Vertex,
|
v0: *const Renderer.Vertex,
|
||||||
@@ -49,46 +164,48 @@ pub fn interpolateVertexOutputs(
|
|||||||
b0: f32,
|
b0: f32,
|
||||||
b1: f32,
|
b1: f32,
|
||||||
b2: f32,
|
b2: f32,
|
||||||
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation {
|
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation {
|
||||||
var inputs = [_]VertexInterpolation{.{
|
var inputs = [_]VertexInterpolationLocation{[_]VertexInterpolation{.{
|
||||||
.blob = &.{},
|
.blob = &.{},
|
||||||
.size = 0,
|
.size = 0,
|
||||||
.free_responsability = false,
|
.free_responsability = false,
|
||||||
}} ** spv.SPIRV_MAX_OUTPUT_LOCATIONS;
|
}} ** 4} ** 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;
|
for (0..4) |component| {
|
||||||
const out1 = v1.outputs[location] orelse continue;
|
const out0 = v0.outputs[location][component] orelse continue;
|
||||||
const out2 = v2.outputs[location] orelse continue;
|
const out1 = v1.outputs[location][component] orelse continue;
|
||||||
|
const out2 = v2.outputs[location][component] orelse continue;
|
||||||
|
|
||||||
if (out0.interpolation_type == .flat or out0.size == 0) {
|
if (out0.interpolation_type == .flat or out0.size == 0) {
|
||||||
inputs[location] = .{ .blob = out0.blob, .size = out0.size, .free_responsability = false };
|
inputs[location][component] = .{ .blob = out0.blob, .size = out0.size, .free_responsability = false };
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const len = @min(out0.size, out1.size, out2.size);
|
||||||
|
const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory;
|
||||||
|
@memset(input, 0);
|
||||||
|
|
||||||
|
var byte_index: usize = 0;
|
||||||
|
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
|
||||||
|
const value0 = std.mem.bytesToValue(F32x4, out0.blob[byte_index..]);
|
||||||
|
const value1 = std.mem.bytesToValue(F32x4, out1.blob[byte_index..]);
|
||||||
|
const value2 = std.mem.bytesToValue(F32x4, out2.blob[byte_index..]);
|
||||||
|
base.utils.writePacked(F32x4, input[byte_index..], interpolateF32x4(value0, value1, value2, b0, b1, b2));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (byte_index + @sizeOf(f32) <= len) : (byte_index += @sizeOf(f32)) {
|
||||||
|
const value0 = std.mem.bytesToValue(f32, out0.blob[byte_index..]);
|
||||||
|
const value1 = std.mem.bytesToValue(f32, out1.blob[byte_index..]);
|
||||||
|
const value2 = std.mem.bytesToValue(f32, out2.blob[byte_index..]);
|
||||||
|
base.utils.writePacked(f32, input[byte_index..], (value0 * b0) + (value1 * b1) + (value2 * b2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_index < len)
|
||||||
|
@memcpy(input[byte_index..len], out0.blob[byte_index..len]);
|
||||||
|
|
||||||
|
inputs[location][component] = .{ .blob = input, .size = len, .free_responsability = true };
|
||||||
}
|
}
|
||||||
|
|
||||||
const len = @min(out0.size, out1.size, out2.size);
|
|
||||||
const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory;
|
|
||||||
@memset(input, 0);
|
|
||||||
|
|
||||||
var byte_index: usize = 0;
|
|
||||||
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
|
|
||||||
const value0 = std.mem.bytesToValue(F32x4, out0.blob[byte_index..]);
|
|
||||||
const value1 = std.mem.bytesToValue(F32x4, out1.blob[byte_index..]);
|
|
||||||
const value2 = std.mem.bytesToValue(F32x4, out2.blob[byte_index..]);
|
|
||||||
base.utils.writePacked(F32x4, input[byte_index..], interpolateF32x4(value0, value1, value2, b0, b1, b2));
|
|
||||||
}
|
|
||||||
|
|
||||||
while (byte_index + @sizeOf(f32) <= len) : (byte_index += @sizeOf(f32)) {
|
|
||||||
const value0 = std.mem.bytesToValue(f32, out0.blob[byte_index..]);
|
|
||||||
const value1 = std.mem.bytesToValue(f32, out1.blob[byte_index..]);
|
|
||||||
const value2 = std.mem.bytesToValue(f32, out2.blob[byte_index..]);
|
|
||||||
base.utils.writePacked(f32, input[byte_index..], (value0 * b0) + (value1 * b1) + (value2 * b2));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byte_index < len)
|
|
||||||
@memcpy(input[byte_index..len], out0.blob[byte_index..len]);
|
|
||||||
|
|
||||||
inputs[location] = .{ .blob = input, .size = len, .free_responsability = true };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return inputs;
|
return inputs;
|
||||||
@@ -99,7 +216,7 @@ pub fn interpolateLineOutputs(
|
|||||||
v0: *const Renderer.Vertex,
|
v0: *const Renderer.Vertex,
|
||||||
v1: *const Renderer.Vertex,
|
v1: *const Renderer.Vertex,
|
||||||
t: f32,
|
t: f32,
|
||||||
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation {
|
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation {
|
||||||
return interpolateVertexOutputs(allocator, v0, v1, v0, 1.0 - t, t, 0.0);
|
return interpolateVertexOutputs(allocator, v0, v1, v0, 1.0 - t, t, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +226,76 @@ inline fn interpolateF32x4(value0: F32x4, value1: F32x4, value2: F32x4, b0: f32,
|
|||||||
|
|
||||||
inline fn fragmentOutputFloat4(output: [@sizeOf(F32x4)]u8, format: vk.Format) F32x4 {
|
inline fn fragmentOutputFloat4(output: [@sizeOf(F32x4)]u8, format: vk.Format) F32x4 {
|
||||||
const color = std.mem.bytesToValue(F32x4, &output);
|
const color = std.mem.bytesToValue(F32x4, &output);
|
||||||
return if (base.format.isSrgb(format)) zm.rgbToSrgb(color) else color;
|
_ = format;
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn blendFactor(factor: vk.BlendFactor, src: F32x4, dst: F32x4, constant: F32x4) F32x4 {
|
||||||
|
return switch (factor) {
|
||||||
|
.zero => zm.f32x4s(0.0),
|
||||||
|
.one => zm.f32x4s(1.0),
|
||||||
|
.src_color => src,
|
||||||
|
.one_minus_src_color => zm.f32x4s(1.0) - src,
|
||||||
|
.dst_color => dst,
|
||||||
|
.one_minus_dst_color => zm.f32x4s(1.0) - dst,
|
||||||
|
.src_alpha => zm.f32x4s(src[3]),
|
||||||
|
.one_minus_src_alpha => zm.f32x4s(1.0 - src[3]),
|
||||||
|
.dst_alpha => zm.f32x4s(dst[3]),
|
||||||
|
.one_minus_dst_alpha => zm.f32x4s(1.0 - dst[3]),
|
||||||
|
.constant_color => constant,
|
||||||
|
.one_minus_constant_color => zm.f32x4s(1.0) - constant,
|
||||||
|
.constant_alpha => zm.f32x4s(constant[3]),
|
||||||
|
.one_minus_constant_alpha => zm.f32x4s(1.0 - constant[3]),
|
||||||
|
.src_alpha_saturate => .{ @min(src[3], 1.0 - dst[3]), @min(src[3], 1.0 - dst[3]), @min(src[3], 1.0 - dst[3]), 1.0 },
|
||||||
|
else => zm.f32x4s(0.0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn blendOp(op: vk.BlendOp, src: F32x4, dst: F32x4) F32x4 {
|
||||||
|
return switch (op) {
|
||||||
|
.add => src + dst,
|
||||||
|
.subtract => src - dst,
|
||||||
|
.reverse_subtract => dst - src,
|
||||||
|
.min => @min(src, dst),
|
||||||
|
.max => @max(src, dst),
|
||||||
|
else => src,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn blendColor(src: F32x4, dst: F32x4, state: vk.PipelineColorBlendAttachmentState, constants: [4]f32) F32x4 {
|
||||||
|
if (state.blend_enable == .false)
|
||||||
|
return src;
|
||||||
|
|
||||||
|
const constant = F32x4{ constants[0], constants[1], constants[2], constants[3] };
|
||||||
|
const color_src = if (state.color_blend_op == .min or state.color_blend_op == .max)
|
||||||
|
src
|
||||||
|
else
|
||||||
|
src * blendFactor(state.src_color_blend_factor, src, dst, constant);
|
||||||
|
const color_dst = if (state.color_blend_op == .min or state.color_blend_op == .max)
|
||||||
|
dst
|
||||||
|
else
|
||||||
|
dst * blendFactor(state.dst_color_blend_factor, src, dst, constant);
|
||||||
|
const alpha_src = if (state.alpha_blend_op == .min or state.alpha_blend_op == .max)
|
||||||
|
src
|
||||||
|
else
|
||||||
|
src * blendFactor(state.src_alpha_blend_factor, src, dst, constant);
|
||||||
|
const alpha_dst = if (state.alpha_blend_op == .min or state.alpha_blend_op == .max)
|
||||||
|
dst
|
||||||
|
else
|
||||||
|
dst * blendFactor(state.dst_alpha_blend_factor, src, dst, constant);
|
||||||
|
|
||||||
|
var blended = blendOp(state.color_blend_op, color_src, color_dst);
|
||||||
|
blended[3] = blendOp(state.alpha_blend_op, alpha_src, alpha_dst)[3];
|
||||||
|
return blended;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn applyColorWriteMask(blended: F32x4, dst: F32x4, mask: vk.ColorComponentFlags) F32x4 {
|
||||||
|
return .{
|
||||||
|
if (mask.r_bit) blended[0] else dst[0],
|
||||||
|
if (mask.g_bit) blended[1] else dst[1],
|
||||||
|
if (mask.b_bit) blended[2] else dst[2],
|
||||||
|
if (mask.a_bit) blended[3] else dst[3],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeToTargets(
|
pub fn writeToTargets(
|
||||||
@@ -117,33 +303,84 @@ pub fn writeToTargets(
|
|||||||
draw_call: *Renderer.DrawCall,
|
draw_call: *Renderer.DrawCall,
|
||||||
color_attachment_access: []const ?RenderTargetAccess,
|
color_attachment_access: []const ?RenderTargetAccess,
|
||||||
depth_attachment_access: ?*RenderTargetAccess,
|
depth_attachment_access: ?*RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*RenderTargetAccess,
|
||||||
|
front_face: bool,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
z: f32,
|
z: f32,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
const io = draw_call.renderer.device.interface.io();
|
const io = draw_call.renderer.device.interface.io();
|
||||||
|
const depth_stencil_state = draw_call.renderer.state.pipeline.?.interface.mode.graphics.depth_stencil;
|
||||||
|
|
||||||
// After work depth test to avoid overwritten depth pixels during fragment invocations
|
var stencil_state: ?vk.StencilOpState = null;
|
||||||
|
var stencil_offset: ?usize = null;
|
||||||
|
if (stencil_attachment_access) |stencil| {
|
||||||
|
if (depth_stencil_state) |state| {
|
||||||
|
if (state.stencil_test_enable == .true) {
|
||||||
|
stencil_state = if (front_face)
|
||||||
|
resolveStencilState(draw_call, state.front, true)
|
||||||
|
else
|
||||||
|
resolveStencilState(draw_call, state.back, false);
|
||||||
|
stencil_offset = targetOffset(stencil.*, x, y) orelse return;
|
||||||
|
if (!stencilTest(stencil, stencil_offset.?, stencil_state.?)) {
|
||||||
|
updateStencilValue(stencil, stencil_offset.?, stencil_state.?, stencil_state.?.fail_op);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After work depth test to avoid overwritten depth pixels during fragment invocations.
|
||||||
|
var depth_passed: ?bool = null;
|
||||||
if (depth_attachment_access) |depth| {
|
if (depth_attachment_access) |depth| {
|
||||||
const depth_offset = @as(usize, @intCast(x)) * depth.texel_size + @as(usize, @intCast(y)) * depth.row_pitch;
|
const depth_offset = targetOffset(depth.*, x, y) orelse return;
|
||||||
|
|
||||||
depth.mutex.lock(io) catch return VkError.DeviceLost;
|
depth.mutex.lock(io) catch return VkError.DeviceLost;
|
||||||
defer depth.mutex.unlock(io);
|
defer depth.mutex.unlock(io);
|
||||||
|
|
||||||
const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format);
|
if (depth_stencil_state) |state| {
|
||||||
if (z >= depth_value[0])
|
depth_passed = depthTestAndUpdate(depth, x, y, z, state);
|
||||||
return;
|
if (!depth_passed.? and stencil_state == null)
|
||||||
blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format);
|
return;
|
||||||
|
} else {
|
||||||
|
const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format);
|
||||||
|
if (z >= depth_value[0])
|
||||||
|
return;
|
||||||
|
blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format);
|
||||||
|
depth_passed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stencil_attachment_access) |stencil| {
|
||||||
|
if (stencil_state) |state| {
|
||||||
|
if (depth_passed != null and !depth_passed.?) {
|
||||||
|
updateStencilValue(stencil, stencil_offset.?, state, state.depth_fail_op);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateStencilValue(stencil, stencil_offset.?, state, state.pass_op);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (color_attachment_access, 0..) |maybe_color, location| {
|
for (color_attachment_access, 0..) |maybe_color, location| {
|
||||||
const color = maybe_color orelse continue;
|
const color = maybe_color orelse continue;
|
||||||
const color_offset = @as(usize, @intCast(x)) * color.texel_size + @as(usize, @intCast(y)) * color.row_pitch;
|
const color_offset = targetOffset(color, x, y) orelse continue;
|
||||||
|
|
||||||
if (base.format.isUnnormalizedInteger(color.format)) {
|
if (base.format.isUnnormalizedInteger(color.format)) {
|
||||||
blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format);
|
blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format);
|
||||||
} else {
|
} else {
|
||||||
blitter.writeFloat4(fragmentOutputFloat4(outputs[location], color.format), color.base[color_offset..], color.format);
|
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
|
||||||
|
const src = fragmentOutputFloat4(outputs[location], color.format);
|
||||||
|
const encoded_dst = blitter.readFloat4(color.base[color_offset..], color.format);
|
||||||
|
const dst = if (base.format.isSrgb(color.format)) zm.srgbToRgb(encoded_dst) else encoded_dst;
|
||||||
|
const final_color = if (pipeline_data.color_blend.attachments) |attachments| blk: {
|
||||||
|
if (location >= attachments.len)
|
||||||
|
break :blk src;
|
||||||
|
const constants = draw_call.renderer.dynamic_state.blend_constants orelse pipeline_data.color_blend.constants;
|
||||||
|
const blended = blendColor(src, dst, attachments[location], constants);
|
||||||
|
break :blk applyColorWriteMask(blended, dst, attachments[location].color_write_mask);
|
||||||
|
} else src;
|
||||||
|
const encoded_color = if (base.format.isSrgb(color.format)) zm.rgbToSrgb(final_color) else final_color;
|
||||||
|
blitter.writeFloat4(encoded_color, color.base[color_offset..], color.format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ const zm = base.zm;
|
|||||||
|
|
||||||
const common = @import("common.zig");
|
const common = @import("common.zig");
|
||||||
const fragment = @import("../fragment.zig");
|
const fragment = @import("../fragment.zig");
|
||||||
const blitter = @import("../blitter.zig");
|
|
||||||
|
|
||||||
const Renderer = @import("../Renderer.zig");
|
const Renderer = @import("../Renderer.zig");
|
||||||
|
|
||||||
@@ -28,6 +27,9 @@ const RunData = struct {
|
|||||||
v2: Renderer.Vertex,
|
v2: Renderer.Vertex,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
front_face: bool,
|
||||||
|
has_fragment_shader: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn drawTriangle(
|
pub fn drawTriangle(
|
||||||
@@ -38,6 +40,8 @@ pub fn drawTriangle(
|
|||||||
v2: *Renderer.Vertex,
|
v2: *Renderer.Vertex,
|
||||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||||
|
front_face: bool,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
const io = draw_call.renderer.device.interface.io();
|
const io = draw_call.renderer.device.interface.io();
|
||||||
|
|
||||||
@@ -51,8 +55,10 @@ pub fn drawTriangle(
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
||||||
|
const fragment_stage = pipeline.stages.getPtr(.fragment);
|
||||||
const runtimes_count = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
|
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1;
|
||||||
|
if (runtimes_count == 0)
|
||||||
|
return;
|
||||||
const grid_size: usize = @intFromFloat(@ceil(@sqrt(@as(f32, @floatFromInt(runtimes_count)))));
|
const grid_size: usize = @intFromFloat(@ceil(@sqrt(@as(f32, @floatFromInt(runtimes_count)))));
|
||||||
|
|
||||||
const width: usize = @intCast(max_x - min_x + 1);
|
const width: usize = @intCast(max_x - min_x + 1);
|
||||||
@@ -97,22 +103,35 @@ pub fn drawTriangle(
|
|||||||
.max_y = run_max_y,
|
.max_y = run_max_y,
|
||||||
.color_attachment_access = color_attachment_access,
|
.color_attachment_access = color_attachment_access,
|
||||||
.depth_attachment_access = depth_attachment_access,
|
.depth_attachment_access = depth_attachment_access,
|
||||||
|
.stencil_attachment_access = stencil_attachment_access,
|
||||||
|
.front_face = front_face,
|
||||||
|
.has_fragment_shader = fragment_stage != null,
|
||||||
};
|
};
|
||||||
|
|
||||||
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
|
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not syncing workers between triangles when rendering without depth buffer
|
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
|
||||||
// will lead to pixel rendering order issues between triangles.
|
|
||||||
if (depth_attachment_access == null)
|
|
||||||
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn edgeFunction(a: F32x4, b: F32x4, p: F32x4) f32 {
|
inline fn edgeFunction(a: F32x4, b: F32x4, p: F32x4) f32 {
|
||||||
return ((p[0] - a[0]) * (b[1] - a[1])) - ((p[1] - a[1]) * (b[0] - a[0]));
|
return ((p[0] - a[0]) * (b[1] - a[1])) - ((p[1] - a[1]) * (b[0] - a[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fn isInclusiveEdge(a: F32x4, b: F32x4) bool {
|
||||||
|
const dx = b[0] - a[0];
|
||||||
|
const dy = b[1] - a[1];
|
||||||
|
return dy < 0.0 or (dy == 0.0 and dx > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn edgeContainsPixel(a: F32x4, b: F32x4, edge_value: f32, area: f32) bool {
|
||||||
|
return if (area > 0.0)
|
||||||
|
edge_value > 0.0 or (edge_value == 0.0 and isInclusiveEdge(a, b))
|
||||||
|
else
|
||||||
|
edge_value < 0.0 or (edge_value == 0.0 and isInclusiveEdge(b, a));
|
||||||
|
}
|
||||||
|
|
||||||
fn runWrapper(data: RunData) void {
|
fn runWrapper(data: RunData) void {
|
||||||
@call(.always_inline, run, .{data}) catch |err| {
|
@call(.always_inline, run, .{data}) catch |err| {
|
||||||
std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)});
|
std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)});
|
||||||
@@ -139,10 +158,10 @@ inline fn run(data: RunData) !void {
|
|||||||
const w1 = edgeFunction(data.v2.position, data.v0.position, p);
|
const w1 = edgeFunction(data.v2.position, data.v0.position, p);
|
||||||
const w2 = edgeFunction(data.v0.position, data.v1.position, p);
|
const w2 = edgeFunction(data.v0.position, data.v1.position, p);
|
||||||
|
|
||||||
const inside = if (data.area > 0.0)
|
const inside =
|
||||||
w0 >= 0.0 and w1 >= 0.0 and w2 >= 0.0
|
edgeContainsPixel(data.v1.position, data.v2.position, w0, data.area) and
|
||||||
else
|
edgeContainsPixel(data.v2.position, data.v0.position, w1, data.area) and
|
||||||
w0 <= 0.0 and w1 <= 0.0 and w2 <= 0.0;
|
edgeContainsPixel(data.v0.position, data.v1.position, w2, data.area);
|
||||||
|
|
||||||
if (!inside)
|
if (!inside)
|
||||||
continue;
|
continue;
|
||||||
@@ -152,31 +171,27 @@ inline fn run(data: RunData) !void {
|
|||||||
const b2 = w2 / data.area;
|
const b2 = w2 / data.area;
|
||||||
const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]);
|
const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]);
|
||||||
|
|
||||||
// Early depth test to avoid unnecesary computations
|
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8 = undefined;
|
||||||
if (data.depth_attachment_access) |depth| {
|
@memset(std.mem.asBytes(&outputs), 0);
|
||||||
const offset = @as(usize, @intCast(x)) * depth.texel_size + @as(usize, @intCast(y)) * depth.row_pitch;
|
if (data.has_fragment_shader) {
|
||||||
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
outputs = fragment.shaderInvocation(
|
||||||
if (z >= depth_value[0])
|
data.allocator,
|
||||||
continue;
|
data.draw_call,
|
||||||
|
data.batch_id,
|
||||||
|
zm.f32x4(@floatFromInt(x), @floatFromInt(y), z, 1.0),
|
||||||
|
try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2),
|
||||||
|
) 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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputs = fragment.shaderInvocation(
|
try common.writeToTargets(outputs, data.draw_call, data.color_attachment_access, data.depth_attachment_access, data.stencil_attachment_access, data.front_face, @intCast(x), @intCast(y), z);
|
||||||
data.allocator,
|
|
||||||
data.draw_call,
|
|
||||||
data.batch_id,
|
|
||||||
zm.f32x4(@floatFromInt(x), @floatFromInt(y), z, 1.0),
|
|
||||||
try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2),
|
|
||||||
) 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, data.draw_call, data.color_attachment_access, data.depth_attachment_access, @intCast(x), @intCast(y), z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ inline fn run(data: RunData) !void {
|
|||||||
|
|
||||||
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) {
|
||||||
|
const io = data.draw_call.renderer.device.interface.io();
|
||||||
|
data.draw_call.allocator_mutex.lock(io) catch return VkError.DeviceLost;
|
||||||
|
defer data.draw_call.allocator_mutex.unlock(io);
|
||||||
|
|
||||||
rt.resetInvocation(data.allocator);
|
rt.resetInvocation(data.allocator);
|
||||||
try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]);
|
try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]);
|
||||||
|
|
||||||
@@ -84,18 +88,20 @@ inline fn run(data: RunData) !void {
|
|||||||
try rt.readBuiltIn(std.mem.asBytes(&output.position), .Position);
|
try rt.readBuiltIn(std.mem.asBytes(&output.position), .Position);
|
||||||
|
|
||||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||||
const result_word = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
|
for (0..4) |component| {
|
||||||
SpvRuntimeError.NotFound => continue,
|
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
|
||||||
else => return err,
|
SpvRuntimeError.NotFound => continue,
|
||||||
};
|
else => return err,
|
||||||
const memory_size = try rt.getResultMemorySize(result_word);
|
};
|
||||||
output.outputs[location] = .{
|
const memory_size = try rt.getResultMemorySize(result_word);
|
||||||
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat)) .flat else .smooth, // TODO : handle noperspective
|
output.outputs[location][component] = .{
|
||||||
.blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory,
|
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat) or resultIsInteger(rt, result_word)) .flat else .smooth, // TODO : handle noperspective
|
||||||
.size = memory_size,
|
.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);
|
@memset(output.outputs[location][component].?.blob, 0);
|
||||||
|
try rt.readOutput(output.outputs[location][component].?.blob, result_word);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try rt.flushDescriptorSets(data.allocator);
|
try rt.flushDescriptorSets(data.allocator);
|
||||||
@@ -110,6 +116,25 @@ fn setupBuiltins(rt: *spv.Runtime, vertex_index: usize, instance_index: usize) !
|
|||||||
try rt.writeBuiltIn(std.mem.asBytes(&instance_index_u32), .InstanceIndex);
|
try rt.writeBuiltIn(std.mem.asBytes(&instance_index_u32), .InstanceIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resultIsInteger(rt: *spv.Runtime, result_word: spv.SpvWord) bool {
|
||||||
|
const value = rt.results[result_word].getConstValue() catch return false;
|
||||||
|
return switch (value.*) {
|
||||||
|
.Int,
|
||||||
|
.Vector2i32,
|
||||||
|
.Vector3i32,
|
||||||
|
.Vector4i32,
|
||||||
|
.Vector2u32,
|
||||||
|
.Vector3u32,
|
||||||
|
.Vector4u32,
|
||||||
|
=> true,
|
||||||
|
.Vector => |lanes| lanes.len != 0 and switch (lanes[0]) {
|
||||||
|
.Int => true,
|
||||||
|
else => false,
|
||||||
|
},
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn writeVertexInput(
|
fn writeVertexInput(
|
||||||
rt: *spv.Runtime,
|
rt: *spv.Runtime,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
@@ -117,6 +142,54 @@ fn writeVertexInput(
|
|||||||
format: vk.Format,
|
format: vk.Format,
|
||||||
location: u32,
|
location: u32,
|
||||||
) !void {
|
) !void {
|
||||||
|
var has_split_components = false;
|
||||||
|
for (1..4) |component| {
|
||||||
|
_ = rt.getResultByLocationComponent(location, @intCast(component), .input) catch |err| switch (err) {
|
||||||
|
SpvRuntimeError.NotFound => continue,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
has_split_components = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_split_components) {
|
||||||
|
for (0..4) |component| {
|
||||||
|
const result_word = rt.getResultByLocationComponent(location, @intCast(component), .input) catch |err| switch (err) {
|
||||||
|
SpvRuntimeError.NotFound => continue,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
const input_memory_size = try rt.getResultMemorySize(result_word);
|
||||||
|
const raw_offset = component * @sizeOf(f32);
|
||||||
|
|
||||||
|
if (raw_offset + input_memory_size <= raw_input.len) {
|
||||||
|
try rt.writeInput(raw_input[raw_offset .. raw_offset + input_memory_size], result_word);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = allocator.alloc(u8, input_memory_size) catch return VkError.OutOfDeviceMemory;
|
||||||
|
defer allocator.free(input);
|
||||||
|
|
||||||
|
@memset(input, 0);
|
||||||
|
if (raw_offset < raw_input.len) {
|
||||||
|
const copy_size = @min(input_memory_size, raw_input.len - raw_offset);
|
||||||
|
@memcpy(input[0..copy_size], raw_input[raw_offset .. raw_offset + copy_size]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component == 3 and input_memory_size >= @sizeOf(f32)) {
|
||||||
|
if (base.format.isUnnormalizedInteger(format)) {
|
||||||
|
const one: u32 = 1;
|
||||||
|
@memcpy(input[0..@sizeOf(u32)], std.mem.asBytes(&one));
|
||||||
|
} else {
|
||||||
|
const one: f32 = 1.0;
|
||||||
|
@memcpy(input[0..@sizeOf(f32)], std.mem.asBytes(&one));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try rt.writeInput(input, result_word);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const input_memory_size = try rt.getInputLocationMemorySize(location);
|
const input_memory_size = try rt.getInputLocationMemorySize(location);
|
||||||
|
|
||||||
if (raw_input.len >= input_memory_size) {
|
if (raw_input.len >= input_memory_size) {
|
||||||
|
|||||||
@@ -69,7 +69,11 @@ pub const DispatchTable = struct {
|
|||||||
resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
|
resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
|
||||||
resolveImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, vk.ImageResolve) VkError!void,
|
resolveImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, vk.ImageResolve) VkError!void,
|
||||||
setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
|
setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
|
||||||
|
setBlendConstants: *const fn (*Self, [4]f32) VkError!void,
|
||||||
setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void,
|
setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void,
|
||||||
|
setStencilCompareMask: *const fn (*Self, vk.StencilFaceFlags, u32) VkError!void,
|
||||||
|
setStencilReference: *const fn (*Self, vk.StencilFaceFlags, u32) VkError!void,
|
||||||
|
setStencilWriteMask: *const fn (*Self, vk.StencilFaceFlags, u32) VkError!void,
|
||||||
setViewport: *const fn (*Self, u32, []const vk.Viewport) VkError!void,
|
setViewport: *const fn (*Self, u32, []const vk.Viewport) VkError!void,
|
||||||
waitEvent: *const fn (*Self, *Event, vk.PipelineStageFlags, vk.PipelineStageFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void,
|
waitEvent: *const fn (*Self, *Event, vk.PipelineStageFlags, vk.PipelineStageFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void,
|
||||||
};
|
};
|
||||||
@@ -162,8 +166,10 @@ pub inline fn beginRenderPass(self: *Self, render_pass: *RenderPass, framebuffer
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn bindDescriptorSets(self: *Self, bind_point: vk.PipelineBindPoint, first_set: u32, sets: []const vk.DescriptorSet, dynamic_offsets: []const u32) VkError!void {
|
pub fn bindDescriptorSets(self: *Self, bind_point: vk.PipelineBindPoint, first_set: u32, sets: []const vk.DescriptorSet, dynamic_offsets: []const u32) VkError!void {
|
||||||
std.debug.assert(sets.len < lib.VULKAN_MAX_DESCRIPTOR_SETS);
|
if (sets.len > lib.VULKAN_MAX_DESCRIPTOR_SETS or first_set > lib.VULKAN_MAX_DESCRIPTOR_SETS or first_set + sets.len > lib.VULKAN_MAX_DESCRIPTOR_SETS)
|
||||||
var inner_sets = [_]?*DescriptorSet{null} ** lib.VULKAN_MAX_DESCRIPTOR_SETS;
|
return VkError.ValidationFailed;
|
||||||
|
|
||||||
|
var inner_sets: [lib.VULKAN_MAX_DESCRIPTOR_SETS]?*DescriptorSet = @splat(null);
|
||||||
for (sets, inner_sets[0..sets.len]) |set, *inner_set| {
|
for (sets, inner_sets[0..sets.len]) |set, *inner_set| {
|
||||||
inner_set.* = try NonDispatchable(DescriptorSet).fromHandleObject(set);
|
inner_set.* = try NonDispatchable(DescriptorSet).fromHandleObject(set);
|
||||||
}
|
}
|
||||||
@@ -288,10 +294,26 @@ pub inline fn setEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags)
|
|||||||
try self.dispatch_table.setEvent(self, event, stage);
|
try self.dispatch_table.setEvent(self, event, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn setBlendConstants(self: *Self, constants: [4]f32) VkError!void {
|
||||||
|
try self.dispatch_table.setBlendConstants(self, constants);
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn setScissor(self: *Self, first: u32, scissor: []const vk.Rect2D) VkError!void {
|
pub inline fn setScissor(self: *Self, first: u32, scissor: []const vk.Rect2D) VkError!void {
|
||||||
try self.dispatch_table.setScissor(self, first, scissor);
|
try self.dispatch_table.setScissor(self, first, scissor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn setStencilCompareMask(self: *Self, face_mask: vk.StencilFaceFlags, compare_mask: u32) VkError!void {
|
||||||
|
try self.dispatch_table.setStencilCompareMask(self, face_mask, compare_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn setStencilReference(self: *Self, face_mask: vk.StencilFaceFlags, reference: u32) VkError!void {
|
||||||
|
try self.dispatch_table.setStencilReference(self, face_mask, reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn setStencilWriteMask(self: *Self, face_mask: vk.StencilFaceFlags, write_mask: u32) VkError!void {
|
||||||
|
try self.dispatch_table.setStencilWriteMask(self, face_mask, write_mask);
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn setViewport(self: *Self, first: u32, viewports: []const vk.Viewport) VkError!void {
|
pub inline fn setViewport(self: *Self, first: u32, viewports: []const vk.Viewport) VkError!void {
|
||||||
try self.dispatch_table.setViewport(self, first, viewports);
|
try self.dispatch_table.setViewport(self, first, viewports);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ mode: union(enum) {
|
|||||||
front_face: vk.FrontFace,
|
front_face: vk.FrontFace,
|
||||||
line_width: f32,
|
line_width: f32,
|
||||||
},
|
},
|
||||||
|
color_blend: struct {
|
||||||
|
attachments: ?[]vk.PipelineColorBlendAttachmentState,
|
||||||
|
constants: [4]f32,
|
||||||
|
},
|
||||||
|
depth_stencil: ?vk.PipelineDepthStencilStateCreateInfo,
|
||||||
dynamic_state: DynamicState,
|
dynamic_state: DynamicState,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -142,6 +147,23 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
|||||||
.front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,
|
.front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,
|
||||||
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
|
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
|
||||||
},
|
},
|
||||||
|
.color_blend = blk: {
|
||||||
|
if (info.p_color_blend_state) |state| {
|
||||||
|
break :blk .{
|
||||||
|
.attachments = if (state.p_attachments) |attachments|
|
||||||
|
allocator.dupe(vk.PipelineColorBlendAttachmentState, attachments[0..state.attachment_count]) catch return VkError.OutOfHostMemory
|
||||||
|
else
|
||||||
|
null,
|
||||||
|
.constants = state.blend_constants,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk .{
|
||||||
|
.attachments = null,
|
||||||
|
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.depth_stencil = if (info.p_depth_stencil_state) |state| state.* else null,
|
||||||
.dynamic_state = blk: {
|
.dynamic_state = blk: {
|
||||||
var state: DynamicState = .{};
|
var state: DynamicState = .{};
|
||||||
|
|
||||||
@@ -181,6 +203,9 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
|||||||
if (graphics.input_assembly.attribute_description) |attribute_description| {
|
if (graphics.input_assembly.attribute_description) |attribute_description| {
|
||||||
allocator.free(attribute_description);
|
allocator.free(attribute_description);
|
||||||
}
|
}
|
||||||
|
if (graphics.color_blend.attachments) |attachments| {
|
||||||
|
allocator.free(attachments);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
self.layout.unref(allocator);
|
self.layout.unref(allocator);
|
||||||
|
|||||||
@@ -1997,10 +1997,7 @@ pub export fn apeCmdSetBlendConstants(p_cmd: vk.CommandBuffer, p_constants: [*]f
|
|||||||
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
||||||
const constants = [4]f32{ p_constants[0], p_constants[1], p_constants[2], p_constants[3] };
|
const constants = [4]f32{ p_constants[0], p_constants[1], p_constants[2], p_constants[3] };
|
||||||
|
|
||||||
notImplementedWarning();
|
cmd.setBlendConstants(constants) catch |err| return errorLogger(err);
|
||||||
|
|
||||||
_ = cmd;
|
|
||||||
_ = constants;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub export fn apeCmdSetDepthBias(p_cmd: vk.CommandBuffer, constant_factor: f32, clamp: f32, slope_factor: f32) callconv(vk.vulkan_call_conv) void {
|
pub export fn apeCmdSetDepthBias(p_cmd: vk.CommandBuffer, constant_factor: f32, clamp: f32, slope_factor: f32) callconv(vk.vulkan_call_conv) void {
|
||||||
@@ -2064,12 +2061,7 @@ pub export fn apeCmdSetStencilCompareMask(p_cmd: vk.CommandBuffer, face_mask: vk
|
|||||||
defer entryPointEndLogTrace();
|
defer entryPointEndLogTrace();
|
||||||
|
|
||||||
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
||||||
|
cmd.setStencilCompareMask(face_mask, compare_mask) catch |err| return errorLogger(err);
|
||||||
notImplementedWarning();
|
|
||||||
|
|
||||||
_ = cmd;
|
|
||||||
_ = face_mask;
|
|
||||||
_ = compare_mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub export fn apeCmdSetStencilReference(p_cmd: vk.CommandBuffer, face_mask: vk.StencilFaceFlags, reference: u32) callconv(vk.vulkan_call_conv) void {
|
pub export fn apeCmdSetStencilReference(p_cmd: vk.CommandBuffer, face_mask: vk.StencilFaceFlags, reference: u32) callconv(vk.vulkan_call_conv) void {
|
||||||
@@ -2077,12 +2069,7 @@ pub export fn apeCmdSetStencilReference(p_cmd: vk.CommandBuffer, face_mask: vk.S
|
|||||||
defer entryPointEndLogTrace();
|
defer entryPointEndLogTrace();
|
||||||
|
|
||||||
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
||||||
|
cmd.setStencilReference(face_mask, reference) catch |err| return errorLogger(err);
|
||||||
notImplementedWarning();
|
|
||||||
|
|
||||||
_ = cmd;
|
|
||||||
_ = face_mask;
|
|
||||||
_ = reference;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub export fn apeCmdSetStencilWriteMask(p_cmd: vk.CommandBuffer, face_mask: vk.StencilFaceFlags, write_mask: u32) callconv(vk.vulkan_call_conv) void {
|
pub export fn apeCmdSetStencilWriteMask(p_cmd: vk.CommandBuffer, face_mask: vk.StencilFaceFlags, write_mask: u32) callconv(vk.vulkan_call_conv) void {
|
||||||
@@ -2090,12 +2077,7 @@ pub export fn apeCmdSetStencilWriteMask(p_cmd: vk.CommandBuffer, face_mask: vk.S
|
|||||||
defer entryPointEndLogTrace();
|
defer entryPointEndLogTrace();
|
||||||
|
|
||||||
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
||||||
|
cmd.setStencilWriteMask(face_mask, write_mask) catch |err| return errorLogger(err);
|
||||||
notImplementedWarning();
|
|
||||||
|
|
||||||
_ = cmd;
|
|
||||||
_ = face_mask;
|
|
||||||
_ = write_mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub export fn apeCmdSetViewport(p_cmd: vk.CommandBuffer, first: u32, count: u32, viewports: [*]const vk.Viewport) callconv(vk.vulkan_call_conv) void {
|
pub export fn apeCmdSetViewport(p_cmd: vk.CommandBuffer, first: u32, count: u32, viewports: [*]const vk.Viewport) callconv(vk.vulkan_call_conv) void {
|
||||||
|
|||||||
Reference in New Issue
Block a user