yes
This commit is contained in:
@@ -81,15 +81,15 @@ vkCmdPushConstants | ✅ Implemented
|
||||
vkCmdResetEvent | ✅ Implemented
|
||||
vkCmdResetQueryPool | ⚙️ WIP
|
||||
vkCmdResolveImage | ✅ Implemented
|
||||
vkCmdSetBlendConstants | ⚙️ WIP
|
||||
vkCmdSetBlendConstants | ✅ Implemented
|
||||
vkCmdSetDepthBias | ⚙️ WIP
|
||||
vkCmdSetDepthBounds | ⚙️ WIP
|
||||
vkCmdSetEvent | ✅ Implemented
|
||||
vkCmdSetLineWidth | ⚙️ WIP
|
||||
vkCmdSetScissor | ✅ Implemented
|
||||
vkCmdSetStencilCompareMask | ⚙️ WIP
|
||||
vkCmdSetStencilReference | ⚙️ WIP
|
||||
vkCmdSetStencilWriteMask | ⚙️ WIP
|
||||
vkCmdSetStencilCompareMask | ✅ Implemented
|
||||
vkCmdSetStencilReference | ✅ Implemented
|
||||
vkCmdSetStencilWriteMask | ✅ Implemented
|
||||
vkCmdSetViewport | ✅ Implemented
|
||||
vkCmdUpdateBuffer | ⚙️ WIP
|
||||
vkCmdWaitEvents | ✅ Implemented
|
||||
|
||||
+7
-7
@@ -25,16 +25,16 @@
|
||||
.url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7",
|
||||
.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 = .{
|
||||
// // For development
|
||||
// .path = "../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 = .{
|
||||
// For development
|
||||
.path = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#ee5a400010b3a5852606b2f8593bd5c745bf0a26",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
|
||||
.paths = .{
|
||||
|
||||
@@ -75,7 +75,11 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
|
||||
.resetEvent = resetEvent,
|
||||
.resolveImage = resolveImage,
|
||||
.setEvent = setEvent,
|
||||
.setBlendConstants = setBlendConstants,
|
||||
.setScissor = setScissor,
|
||||
.setStencilCompareMask = setStencilCompareMask,
|
||||
.setStencilReference = setStencilReference,
|
||||
.setStencilWriteMask = setStencilWriteMask,
|
||||
.setViewport = setViewport,
|
||||
.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;
|
||||
}
|
||||
|
||||
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 {
|
||||
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||
const allocator = self.command_allocator.allocator();
|
||||
|
||||
@@ -129,6 +129,8 @@ inline fn run(data: RunData) !void {
|
||||
if (!uses_control_barrier)
|
||||
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
|
||||
|
||||
try rt.populatePushConstants(data.self.state.push_constant_blob[0..]);
|
||||
|
||||
var group_index: usize = data.batch_id;
|
||||
while (group_index < data.group_count) : (group_index += data.self.batch_size) {
|
||||
var modulo: usize = group_index;
|
||||
@@ -205,6 +207,7 @@ fn runBarrierWorkgroup(
|
||||
for (runtimes, 0..) |*rt, i| {
|
||||
rt.resetInvocation(allocator);
|
||||
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 setupSubgroupBuiltins(data.self, rt, group_id, i);
|
||||
statuses[i] = try rt.beginEntryPoint(allocator, entry);
|
||||
|
||||
@@ -43,11 +43,18 @@ pub const DynamicState = struct {
|
||||
viewports: ?[]const vk.Viewport,
|
||||
scissor: ?[]const vk.Rect2D,
|
||||
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 {
|
||||
position: F32x4,
|
||||
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]?struct {
|
||||
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct {
|
||||
interpolation_type: enum { smooth, flat, noperspective },
|
||||
blob: []u8,
|
||||
size: usize,
|
||||
@@ -67,6 +74,7 @@ pub const DrawCall = struct {
|
||||
render_pass: *SoftRenderPass,
|
||||
framebuffer: *SoftFramebuffer,
|
||||
|
||||
allocator_mutex: std.Io.Mutex,
|
||||
rasterizer_wait_group: std.Io.Group,
|
||||
|
||||
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,
|
||||
.render_pass = render_pass,
|
||||
.framebuffer = framebuffer,
|
||||
.allocator_mutex = .init,
|
||||
.rasterizer_wait_group = .init,
|
||||
.stats = .{
|
||||
.polygons_drawn = 0,
|
||||
@@ -93,7 +102,9 @@ pub const DrawCall = struct {
|
||||
};
|
||||
|
||||
for (self.vertices) |*vertex| {
|
||||
@memset(vertex.outputs[0..], null);
|
||||
for (&vertex.outputs) |*location| {
|
||||
@memset(location, null);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -102,8 +113,10 @@ pub const DrawCall = struct {
|
||||
fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
|
||||
for (self.vertices) |*vertex| {
|
||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||
if (vertex.outputs[location]) |output| {
|
||||
allocator.free(output.blob);
|
||||
for (0..4) |component| {
|
||||
if (vertex.outputs[location][component]) |output| {
|
||||
allocator.free(output.blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +143,13 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self {
|
||||
.viewports = null,
|
||||
.scissor = 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,
|
||||
};
|
||||
@@ -189,9 +209,10 @@ fn drawCall(self: *Self, bounded_allocator: *BoundedAllocator, vertex_count: usi
|
||||
for (vertex_shader.runtimes[0..]) |*runtime| {
|
||||
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
|
||||
}
|
||||
const fragment_shader = pipeline.stages.getPtrAssertContains(.fragment);
|
||||
for (fragment_shader.runtimes[0..]) |*runtime| {
|
||||
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
|
||||
if (pipeline.stages.getPtr(.fragment)) |fragment_shader| {
|
||||
for (fragment_shader.runtimes[0..]) |*runtime| {
|
||||
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| {
|
||||
|
||||
@@ -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 g: u5 = @intFromFloat(@round(color[1] * 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).* =
|
||||
(@as(u16, b) << 0) |
|
||||
(@as(u16, g) << 5) |
|
||||
|
||||
+15
-11
@@ -173,20 +173,24 @@ fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex,
|
||||
.outputs = undefined,
|
||||
};
|
||||
|
||||
@memset(result.outputs[0..], null);
|
||||
for (&result.outputs) |*location| {
|
||||
@memset(location, null);
|
||||
}
|
||||
|
||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||
const out_a = a.outputs[location] orelse continue;
|
||||
const out_b = b.outputs[location] orelse continue;
|
||||
for (0..4) |component| {
|
||||
const out_a = a.outputs[location][component] orelse continue;
|
||||
const out_b = b.outputs[location][component] orelse continue;
|
||||
|
||||
result.outputs[location] = .{
|
||||
.interpolation_type = out_a.interpolation_type,
|
||||
.blob = if (out_a.interpolation_type == .flat)
|
||||
allocator.dupe(u8, out_a.blob) catch return VkError.OutOfDeviceMemory
|
||||
else
|
||||
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),
|
||||
};
|
||||
result.outputs[location][component] = .{
|
||||
.interpolation_type = out_a.interpolation_type,
|
||||
.blob = if (out_a.interpolation_type == .flat)
|
||||
allocator.dupe(u8, out_a.blob) catch return VkError.OutOfDeviceMemory
|
||||
else
|
||||
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),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -4,7 +4,7 @@ const base = @import("base");
|
||||
const zm = base.zm;
|
||||
const spv = @import("spv");
|
||||
|
||||
const VertexInterpolation = @import("rasterizer/common.zig").VertexInterpolation;
|
||||
const VertexInterpolationLocation = @import("rasterizer/common.zig").VertexInterpolationLocation;
|
||||
|
||||
const Renderer = @import("Renderer.zig");
|
||||
const SoftImage = @import("../SoftImage.zig");
|
||||
@@ -18,7 +18,7 @@ pub fn shaderInvocation(
|
||||
draw_call: *Renderer.DrawCall,
|
||||
batch_id: usize,
|
||||
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 {
|
||||
var fragment_inputs = inputs;
|
||||
errdefer freeOwnedInputs(allocator, fragment_inputs);
|
||||
@@ -45,26 +45,28 @@ pub fn shaderInvocation(
|
||||
const entry = try rt.getEntryPointByName(shader.entry);
|
||||
|
||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||
const result_word = rt.getResultByLocation(@intCast(location), .input) catch |err| switch (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,
|
||||
for (0..4) |component| {
|
||||
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch |err| switch (err) {
|
||||
SpvRuntimeError.NotFound => continue,
|
||||
else => return err,
|
||||
};
|
||||
input = fragment_inputs[location];
|
||||
}
|
||||
|
||||
if (input.blob.len != 0) {
|
||||
try rt.writeInput(input.blob, result_word);
|
||||
var input = fragment_inputs[location][component];
|
||||
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);
|
||||
|
||||
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) {
|
||||
SpvRuntimeError.NotFound => continue,
|
||||
else => return err,
|
||||
@@ -93,9 +118,11 @@ pub fn shaderInvocation(
|
||||
return outputs;
|
||||
}
|
||||
|
||||
fn freeOwnedInputs(allocator: std.mem.Allocator, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation) void {
|
||||
for (inputs) |input| {
|
||||
if (input.free_responsability)
|
||||
allocator.free(input.blob);
|
||||
fn freeOwnedInputs(allocator: std.mem.Allocator, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation) void {
|
||||
for (inputs) |location| {
|
||||
for (location) |input| {
|
||||
if (input.free_responsability)
|
||||
allocator.free(input.blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+100
-41
@@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const base = @import("base");
|
||||
const spv = @import("spv");
|
||||
const zm = base.zm;
|
||||
|
||||
const clip = @import("clip.zig");
|
||||
@@ -9,7 +10,6 @@ const bresenham = @import("rasterizer/bresenham.zig");
|
||||
const edge_function = @import("rasterizer/edge_function.zig");
|
||||
const common = @import("rasterizer/common.zig");
|
||||
const fragment = @import("fragment.zig");
|
||||
const blitter = @import("blitter.zig");
|
||||
|
||||
const Renderer = @import("Renderer.zig");
|
||||
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_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(
|
||||
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),
|
||||
.row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format),
|
||||
.texel_size = base.format.texelSize(color_format),
|
||||
.width = color_extent.width,
|
||||
.height = color_extent.height,
|
||||
.format = color_format,
|
||||
};
|
||||
}
|
||||
@@ -61,20 +64,58 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
||||
break :blk null;
|
||||
|
||||
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_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(
|
||||
depth_range.aspect_mask,
|
||||
depth_aspect,
|
||||
depth_range.base_mip_level,
|
||||
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 .{
|
||||
.mutex = .init,
|
||||
.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),
|
||||
.texel_size = base.format.texelSize(depth_format),
|
||||
.format = depth_format,
|
||||
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_aspect, depth_range.base_mip_level, depth_format),
|
||||
.texel_size = base.format.texelSize(depth_aspect_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,
|
||||
color_attachment_access,
|
||||
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| {
|
||||
@@ -103,6 +145,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
||||
v2,
|
||||
color_attachment_access,
|
||||
if (depth_attachment_access) |*access| access else null,
|
||||
if (stencil_attachment_access) |*access| access else null,
|
||||
);
|
||||
},
|
||||
.triangle_fan => if (draw_call.vertices.len >= 3) {
|
||||
@@ -120,6 +163,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
||||
v2,
|
||||
color_attachment_access,
|
||||
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,
|
||||
color_attachment_access,
|
||||
if (depth_attachment_access) |*access| access else null,
|
||||
if (stencil_attachment_access) |*access| access else null,
|
||||
);
|
||||
} else {
|
||||
try clipTransformAndRasterizeTriangle(
|
||||
@@ -150,6 +195,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
||||
v2,
|
||||
color_attachment_access,
|
||||
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,
|
||||
color_attachment_access,
|
||||
if (depth_attachment_access) |*access| access else null,
|
||||
if (stencil_attachment_access) |*access| access else null,
|
||||
);
|
||||
},
|
||||
.line_strip => if (draw_call.vertices.len >= 2) {
|
||||
@@ -180,6 +227,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
||||
v1,
|
||||
color_attachment_access,
|
||||
if (depth_attachment_access) |*access| access else null,
|
||||
if (stencil_attachment_access) |*access| access else null,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -195,6 +243,7 @@ fn clipTransformAndRasterizePoint(
|
||||
vertex: *Vertex,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
) VkError!void {
|
||||
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)
|
||||
@@ -208,6 +257,8 @@ fn clipTransformAndRasterizePoint(
|
||||
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 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;
|
||||
while (py <= max_y) : (py += 1) {
|
||||
@@ -216,30 +267,27 @@ fn clipTransformAndRasterizePoint(
|
||||
if (!common.scissorContainsPixel(draw_call.scissor, px, py))
|
||||
continue;
|
||||
|
||||
if (depth_attachment_access) |depth| {
|
||||
const offset = @as(usize, @intCast(px)) * depth.texel_size + @as(usize, @intCast(py)) * depth.row_pitch;
|
||||
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
||||
if (transformed.position[2] >= depth_value[0])
|
||||
continue;
|
||||
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 = undefined;
|
||||
@memset(std.mem.asBytes(&outputs), 0);
|
||||
if (has_fragment_shader) {
|
||||
outputs = fragment.shaderInvocation(
|
||||
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(
|
||||
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]);
|
||||
try common.writeToTargets(outputs, draw_call, color_attachment_access, depth_attachment_access, stencil_attachment_access, true, @intCast(px), @intCast(py), transformed.position[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,6 +299,7 @@ fn clipTransformAndRasterizeLine(
|
||||
v1: *Vertex,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
) VkError!void {
|
||||
const clipped_line = (try clip.clipLine(allocator, v0, v1)) orelse return;
|
||||
|
||||
@@ -267,6 +316,7 @@ fn clipTransformAndRasterizeLine(
|
||||
&tv1,
|
||||
color_attachment_access,
|
||||
depth_attachment_access,
|
||||
stencil_attachment_access,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -279,6 +329,7 @@ fn clipTransformAndRasterizeTriangle(
|
||||
v2: *Vertex,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
) VkError!void {
|
||||
const clipped_polygon = try clip.clipTriangle(allocator, v0, v1, v2);
|
||||
|
||||
@@ -303,6 +354,7 @@ fn clipTransformAndRasterizeTriangle(
|
||||
&tv2,
|
||||
color_attachment_access,
|
||||
depth_attachment_access,
|
||||
stencil_attachment_access,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -316,29 +368,32 @@ fn rasterizeTriangle(
|
||||
v2: *Vertex,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
) 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;
|
||||
|
||||
draw_call.stats.polygons_drawn += 1;
|
||||
|
||||
const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
|
||||
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 => {
|
||||
try bresenham.drawLine(allocator, draw_call, v0, v1, color_attachment_access, depth_attachment_access);
|
||||
try bresenham.drawLine(allocator, draw_call, v1, v2, color_attachment_access, depth_attachment_access);
|
||||
try bresenham.drawLine(allocator, draw_call, v2, v0, 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, stencil_attachment_access);
|
||||
try bresenham.drawLine(allocator, draw_call, v2, v0, color_attachment_access, depth_attachment_access, stencil_attachment_access);
|
||||
},
|
||||
.point => {}, // TODO
|
||||
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 rasterization = pipeline_data.rasterization;
|
||||
const cull_mode = rasterization.cull_mode;
|
||||
const cull_mode = pipeline_data.rasterization.cull_mode;
|
||||
|
||||
if (!cull_mode.front_bit and !cull_mode.back_bit)
|
||||
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)
|
||||
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);
|
||||
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,
|
||||
.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 {
|
||||
|
||||
@@ -3,7 +3,6 @@ const base = @import("base");
|
||||
const spv = @import("spv");
|
||||
const zm = base.zm;
|
||||
|
||||
const blitter = @import("../blitter.zig");
|
||||
const common = @import("common.zig");
|
||||
const fragment = @import("../fragment.zig");
|
||||
|
||||
@@ -30,6 +29,8 @@ const RunData = struct {
|
||||
end_step: usize,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
has_fragment_shader: bool,
|
||||
};
|
||||
|
||||
pub fn drawLine(
|
||||
@@ -39,6 +40,7 @@ pub fn drawLine(
|
||||
v1: *Renderer.Vertex,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
) VkError!void {
|
||||
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 pipeline = draw_call.renderer.state.pipeline orelse return;
|
||||
|
||||
const runtimes_count = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
|
||||
const fragment_stage = pipeline.stages.getPtr(.fragment);
|
||||
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1;
|
||||
if (runtimes_count == 0)
|
||||
return;
|
||||
|
||||
@@ -104,15 +106,14 @@ pub fn drawLine(
|
||||
.end_step = end_step,
|
||||
.color_attachment_access = color_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});
|
||||
}
|
||||
|
||||
// Not syncing workers between triangles when rendering without depth buffer
|
||||
// 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;
|
||||
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 {
|
||||
@@ -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 z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]);
|
||||
|
||||
// Early depth test to avoid unnecesary computations
|
||||
if (data.depth_attachment_access) |depth| {
|
||||
const offset = @as(usize, @intCast(pixel_x)) * depth.texel_size + @as(usize, @intCast(pixel_y)) * depth.row_pitch;
|
||||
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
||||
if (z >= depth_value[0])
|
||||
continue;
|
||||
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8 = undefined;
|
||||
@memset(std.mem.asBytes(&outputs), 0);
|
||||
if (data.has_fragment_shader) {
|
||||
outputs = fragment.shaderInvocation(
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
const outputs = fragment.shaderInvocation(
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ pub const RenderTargetAccess = struct {
|
||||
base: []u8,
|
||||
row_pitch: usize,
|
||||
texel_size: usize,
|
||||
width: u32,
|
||||
height: u32,
|
||||
format: vk.Format,
|
||||
};
|
||||
|
||||
@@ -25,6 +27,8 @@ pub const VertexInterpolation = struct {
|
||||
free_responsability: bool,
|
||||
};
|
||||
|
||||
pub const VertexInterpolationLocation = [4]VertexInterpolation;
|
||||
|
||||
pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
|
||||
const min_x: i64 = @as(i64, scissor.offset.x);
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
allocator: std.mem.Allocator,
|
||||
v0: *const Renderer.Vertex,
|
||||
@@ -49,46 +164,48 @@ pub fn interpolateVertexOutputs(
|
||||
b0: f32,
|
||||
b1: f32,
|
||||
b2: f32,
|
||||
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation {
|
||||
var inputs = [_]VertexInterpolation{.{
|
||||
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation {
|
||||
var inputs = [_]VertexInterpolationLocation{[_]VertexInterpolation{.{
|
||||
.blob = &.{},
|
||||
.size = 0,
|
||||
.free_responsability = false,
|
||||
}} ** spv.SPIRV_MAX_OUTPUT_LOCATIONS;
|
||||
}} ** 4} ** spv.SPIRV_MAX_OUTPUT_LOCATIONS;
|
||||
|
||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||
const out0 = v0.outputs[location] orelse continue;
|
||||
const out1 = v1.outputs[location] orelse continue;
|
||||
const out2 = v2.outputs[location] orelse continue;
|
||||
for (0..4) |component| {
|
||||
const out0 = v0.outputs[location][component] 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) {
|
||||
inputs[location] = .{ .blob = out0.blob, .size = out0.size, .free_responsability = false };
|
||||
continue;
|
||||
if (out0.interpolation_type == .flat or out0.size == 0) {
|
||||
inputs[location][component] = .{ .blob = out0.blob, .size = out0.size, .free_responsability = false };
|
||||
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;
|
||||
@@ -99,7 +216,7 @@ pub fn interpolateLineOutputs(
|
||||
v0: *const Renderer.Vertex,
|
||||
v1: *const Renderer.Vertex,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
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(
|
||||
@@ -117,33 +303,84 @@ pub fn writeToTargets(
|
||||
draw_call: *Renderer.DrawCall,
|
||||
color_attachment_access: []const ?RenderTargetAccess,
|
||||
depth_attachment_access: ?*RenderTargetAccess,
|
||||
stencil_attachment_access: ?*RenderTargetAccess,
|
||||
front_face: bool,
|
||||
x: usize,
|
||||
y: usize,
|
||||
z: f32,
|
||||
) VkError!void {
|
||||
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| {
|
||||
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;
|
||||
defer depth.mutex.unlock(io);
|
||||
|
||||
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);
|
||||
if (depth_stencil_state) |state| {
|
||||
depth_passed = depthTestAndUpdate(depth, x, y, z, state);
|
||||
if (!depth_passed.? and stencil_state == null)
|
||||
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| {
|
||||
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)) {
|
||||
blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format);
|
||||
} 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 fragment = @import("../fragment.zig");
|
||||
const blitter = @import("../blitter.zig");
|
||||
|
||||
const Renderer = @import("../Renderer.zig");
|
||||
|
||||
@@ -28,6 +27,9 @@ const RunData = struct {
|
||||
v2: Renderer.Vertex,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
front_face: bool,
|
||||
has_fragment_shader: bool,
|
||||
};
|
||||
|
||||
pub fn drawTriangle(
|
||||
@@ -38,6 +40,8 @@ pub fn drawTriangle(
|
||||
v2: *Renderer.Vertex,
|
||||
color_attachment_access: []const ?common.RenderTargetAccess,
|
||||
depth_attachment_access: ?*common.RenderTargetAccess,
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
front_face: bool,
|
||||
) VkError!void {
|
||||
const io = draw_call.renderer.device.interface.io();
|
||||
|
||||
@@ -51,8 +55,10 @@ pub fn drawTriangle(
|
||||
return;
|
||||
|
||||
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
||||
|
||||
const runtimes_count = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
|
||||
const fragment_stage = pipeline.stages.getPtr(.fragment);
|
||||
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 width: usize = @intCast(max_x - min_x + 1);
|
||||
@@ -97,22 +103,35 @@ pub fn drawTriangle(
|
||||
.max_y = run_max_y,
|
||||
.color_attachment_access = color_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});
|
||||
}
|
||||
}
|
||||
|
||||
// Not syncing workers between triangles when rendering without depth buffer
|
||||
// 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;
|
||||
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
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 {
|
||||
@call(.always_inline, run, .{data}) catch |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 w2 = edgeFunction(data.v0.position, data.v1.position, p);
|
||||
|
||||
const inside = if (data.area > 0.0)
|
||||
w0 >= 0.0 and w1 >= 0.0 and w2 >= 0.0
|
||||
else
|
||||
w0 <= 0.0 and w1 <= 0.0 and w2 <= 0.0;
|
||||
const inside =
|
||||
edgeContainsPixel(data.v1.position, data.v2.position, w0, data.area) and
|
||||
edgeContainsPixel(data.v2.position, data.v0.position, w1, data.area) and
|
||||
edgeContainsPixel(data.v0.position, data.v1.position, w2, data.area);
|
||||
|
||||
if (!inside)
|
||||
continue;
|
||||
@@ -152,31 +171,27 @@ inline fn run(data: RunData) !void {
|
||||
const b2 = w2 / data.area;
|
||||
const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]);
|
||||
|
||||
// Early depth test to avoid unnecesary computations
|
||||
if (data.depth_attachment_access) |depth| {
|
||||
const offset = @as(usize, @intCast(x)) * depth.texel_size + @as(usize, @intCast(y)) * depth.row_pitch;
|
||||
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
||||
if (z >= depth_value[0])
|
||||
continue;
|
||||
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8 = undefined;
|
||||
@memset(std.mem.asBytes(&outputs), 0);
|
||||
if (data.has_fragment_shader) {
|
||||
outputs = fragment.shaderInvocation(
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
const outputs = fragment.shaderInvocation(
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ inline fn run(data: RunData) !void {
|
||||
|
||||
var invocation_index: usize = data.batch_id;
|
||||
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);
|
||||
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);
|
||||
|
||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||
const result_word = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
|
||||
SpvRuntimeError.NotFound => continue,
|
||||
else => return err,
|
||||
};
|
||||
const memory_size = try rt.getResultMemorySize(result_word);
|
||||
output.outputs[location] = .{
|
||||
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat)) .flat else .smooth, // TODO : handle noperspective
|
||||
.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);
|
||||
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);
|
||||
output.outputs[location][component] = .{
|
||||
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat) or resultIsInteger(rt, result_word)) .flat else .smooth, // TODO : handle noperspective
|
||||
.blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory,
|
||||
.size = memory_size,
|
||||
};
|
||||
@memset(output.outputs[location][component].?.blob, 0);
|
||||
try rt.readOutput(output.outputs[location][component].?.blob, result_word);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(
|
||||
rt: *spv.Runtime,
|
||||
allocator: std.mem.Allocator,
|
||||
@@ -117,6 +142,54 @@ fn writeVertexInput(
|
||||
format: vk.Format,
|
||||
location: u32,
|
||||
) !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);
|
||||
|
||||
if (raw_input.len >= input_memory_size) {
|
||||
|
||||
@@ -69,7 +69,11 @@ pub const DispatchTable = struct {
|
||||
resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) 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,
|
||||
setBlendConstants: *const fn (*Self, [4]f32) 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,
|
||||
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 {
|
||||
std.debug.assert(sets.len < lib.VULKAN_MAX_DESCRIPTOR_SETS);
|
||||
var inner_sets = [_]?*DescriptorSet{null} ** 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)
|
||||
return VkError.ValidationFailed;
|
||||
|
||||
var inner_sets: [lib.VULKAN_MAX_DESCRIPTOR_SETS]?*DescriptorSet = @splat(null);
|
||||
for (sets, inner_sets[0..sets.len]) |set, *inner_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);
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
try self.dispatch_table.setViewport(self, first, viewports);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,11 @@ mode: union(enum) {
|
||||
front_face: vk.FrontFace,
|
||||
line_width: f32,
|
||||
},
|
||||
color_blend: struct {
|
||||
attachments: ?[]vk.PipelineColorBlendAttachmentState,
|
||||
constants: [4]f32,
|
||||
},
|
||||
depth_stencil: ?vk.PipelineDepthStencilStateCreateInfo,
|
||||
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,
|
||||
.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: {
|
||||
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| {
|
||||
allocator.free(attribute_description);
|
||||
}
|
||||
if (graphics.color_blend.attachments) |attachments| {
|
||||
allocator.free(attachments);
|
||||
}
|
||||
},
|
||||
}
|
||||
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 constants = [4]f32{ p_constants[0], p_constants[1], p_constants[2], p_constants[3] };
|
||||
|
||||
notImplementedWarning();
|
||||
|
||||
_ = cmd;
|
||||
_ = constants;
|
||||
cmd.setBlendConstants(constants) catch |err| return errorLogger(err);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
||||
|
||||
notImplementedWarning();
|
||||
|
||||
_ = cmd;
|
||||
_ = face_mask;
|
||||
_ = compare_mask;
|
||||
cmd.setStencilCompareMask(face_mask, compare_mask) catch |err| return errorLogger(err);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
||||
|
||||
notImplementedWarning();
|
||||
|
||||
_ = cmd;
|
||||
_ = face_mask;
|
||||
_ = reference;
|
||||
cmd.setStencilReference(face_mask, reference) catch |err| return errorLogger(err);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
|
||||
|
||||
notImplementedWarning();
|
||||
|
||||
_ = cmd;
|
||||
_ = face_mask;
|
||||
_ = write_mask;
|
||||
cmd.setStencilWriteMask(face_mask, write_mask) catch |err| return errorLogger(err);
|
||||
}
|
||||
|
||||
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