improving push constants support, fixing image resolving, lots of rasterizer fixes
Build / build (push) Failing after 0s
Test / build_and_test (push) Failing after 0s

This commit is contained in:
2026-06-28 13:26:15 +02:00
parent b13a256eb6
commit 7c6e0dc2a8
17 changed files with 547 additions and 97 deletions
+2 -2
View File
@@ -32,8 +32,8 @@
// Soft dependencies
.SPIRV_Interpreter = .{
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#cba8a4723d9b21c9c58494be8ce477124946f52a",
.hash = "SPIRV_Interpreter-0.0.1-ajmpnyQ4CABQ7B36QdzikdLDH1E5LMk-ib8O2gAHgwbE",
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#b9b6087fef5a61a3d9e3187ff7c66dffc54603da",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn9JxCADthSmyktTCV_jIA8iJU_aiM_kboXqfPDDD",
.lazy = true,
},
//.SPIRV_Interpreter = .{
+14 -8
View File
@@ -1152,17 +1152,23 @@ pub fn pushConstants(interface: *Interface, stages: vk.ShaderStageFlags, offset:
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
const state = &device.pipeline_states[
if (impl.stages.vertex_bit or impl.stages.fragment_bit)
ExecutionDevice.GRAPHICS_PIPELINE_STATE
else
ExecutionDevice.COMPUTE_PIPELINE_STATE
];
const size = @min(lib.PUSH_CONSTANT_SIZE - impl.offset, impl.blob.len);
if (impl.stages.vertex_bit or
impl.stages.tessellation_control_bit or
impl.stages.tessellation_evaluation_bit or
impl.stages.geometry_bit or
impl.stages.fragment_bit)
{
const state = &device.pipeline_states[ExecutionDevice.GRAPHICS_PIPELINE_STATE];
@memcpy(state.push_constant_blob[impl.offset .. impl.offset + size], impl.blob[0..size]);
}
if (impl.stages.compute_bit) {
const state = &device.pipeline_states[ExecutionDevice.COMPUTE_PIPELINE_STATE];
@memcpy(state.push_constant_blob[impl.offset .. impl.offset + size], impl.blob[0..size]);
}
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
+2 -2
View File
@@ -118,8 +118,8 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
.max_compute_work_group_invocations = 128,
.max_compute_work_group_size = .{ 128, 128, 64 },
.sub_pixel_precision_bits = 4,
.sub_texel_precision_bits = 4,
.mipmap_precision_bits = 4,
.sub_texel_precision_bits = 8,
.mipmap_precision_bits = 8,
.max_draw_indexed_index_value = 4294967295,
.max_draw_indirect_count = 65535,
.max_sampler_lod_bias = 2.0,
+52 -29
View File
@@ -273,6 +273,7 @@ fn applySpecialization(runtime: *spv.Runtime, allocator: std.mem.Allocator, spec
.size = @intCast(entry.size),
}, data) catch return VkError.OutOfDeviceMemory;
}
runtime.applySpecializationLayout(allocator) catch return VkError.OutOfDeviceMemory;
}
fn imageApi() spv.Runtime.ImageAPI {
@@ -338,13 +339,48 @@ fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D {
return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z };
}
fn bufferViewRange(buffer_view: *const SoftBufferView) SpvRuntimeError!usize {
const offset: usize = @intCast(buffer_view.interface.offset);
if (offset > buffer_view.interface.buffer.size)
return SpvRuntimeError.Unknown;
if (buffer_view.interface.range == vk.WHOLE_SIZE)
return @intCast(buffer_view.interface.buffer.size - offset);
return @intCast(buffer_view.interface.range);
}
fn mapBufferViewTexel(buffer_view: *const SoftBufferView, x: i32) SpvRuntimeError![]u8 {
if (x < 0)
return SpvRuntimeError.Unknown;
const texel_size = base.format.texelSize(buffer_view.interface.format);
const texel_index: usize = @intCast(x);
const range = try bufferViewRange(buffer_view);
const texel_offset = std.math.mul(usize, texel_index, texel_size) catch return SpvRuntimeError.Unknown;
if (texel_offset > range or texel_size > range - texel_offset)
return SpvRuntimeError.Unknown;
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
return buffer.mapAsSliceWithOffset(
u8,
@as(usize, @intCast(buffer_view.interface.offset)) + texel_offset,
texel_size,
) catch return SpvRuntimeError.Unknown;
}
fn bufferViewFromContext(context: *anyopaque) SpvRuntimeError!*SoftBufferView {
const addr = @intFromPtr(context);
if (!std.mem.isAligned(addr, @alignOf(SoftBufferView)))
return SpvRuntimeError.Unknown;
return @ptrCast(@alignCast(context));
}
fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, lod: ?i32) SpvRuntimeError!spv.Runtime.Vec4(f32) {
var pixel = zm.f32x4s(0.0);
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
pixel = blitter.readFloat4(map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
const buffer_view = try bufferViewFromContext(context);
pixel = blitter.readFloat4(try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format);
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
@@ -387,10 +423,8 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32,
fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, lod: ?i32) SpvRuntimeError!spv.Runtime.Vec4(u32) {
var pixel = @Vector(4, u32){ 0, 0, 0, 0 };
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
pixel = blitter.readInt4(map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
const buffer_view = try bufferViewFromContext(context);
pixel = blitter.readInt4(try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format);
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
@@ -433,10 +467,8 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l
fn writeImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(f32)) SpvRuntimeError!void {
const vec_pixel = zm.f32x4(pixel.x, pixel.y, pixel.z, pixel.w);
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
blitter.writeFloat4(vec_pixel, map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
const buffer_view = try bufferViewFromContext(context);
blitter.writeFloat4(vec_pixel, try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format);
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
@@ -461,10 +493,8 @@ fn writeImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32
fn writeImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(u32)) SpvRuntimeError!void {
const vec_pixel = @Vector(4, u32){ pixel.x, pixel.y, pixel.z, pixel.w };
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
blitter.writeInt4(vec_pixel, map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
const buffer_view = try bufferViewFromContext(context);
blitter.writeInt4(vec_pixel, try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format);
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
@@ -490,10 +520,8 @@ fn sampleImageFloat4(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim,
var pixel = zm.f32x4s(0.0);
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
_ = map;
const buffer_view = try bufferViewFromContext(context);
_ = try bufferViewRange(buffer_view);
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
@@ -514,10 +542,8 @@ fn sampleImageInt4(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x
var pixel = @Vector(4, u32){ 0, 0, 0, 0 };
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
_ = map;
const buffer_view = try bufferViewFromContext(context);
_ = try bufferViewRange(buffer_view);
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
@@ -546,11 +572,8 @@ fn sampleImageDref(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x
fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool, lod: ?i32) SpvRuntimeError!spv.Runtime.Vec4(u32) {
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const range = if (buffer_view.interface.range == vk.WHOLE_SIZE)
buffer_view.interface.buffer.size - buffer_view.interface.offset
else
buffer_view.interface.range;
const buffer_view = try bufferViewFromContext(context);
const range = try bufferViewRange(buffer_view);
return .{
.x = @intCast(@divTrunc(range, base.format.texelSize(buffer_view.interface.format))),
.y = 0,
+1
View File
@@ -163,6 +163,7 @@ fn applySpecialization(runtime: *spv.Runtime, allocator: std.mem.Allocator, spec
data,
) catch return VkError.OutOfDeviceMemory;
}
runtime.applySpecializationLayout(allocator) catch return VkError.OutOfDeviceMemory;
}
fn cloneSpecs(allocator: std.mem.Allocator, specialization: ?*const vk.SpecializationInfo) VkError![]SpecConstant {
+1 -1
View File
@@ -204,7 +204,7 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
const addr: usize = @intFromPtr(buffer_view);
writeDescriptorSet(
rt,
.{ .raw = std.mem.asBytes(&addr) },
.{ .sampled_image = .{ .image = addr, .sampler = addr } },
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
+2 -2
View File
@@ -166,12 +166,12 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries
}
pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void {
var bounded_allocator: BoundedAllocator = .init(self.device.device_allocator.allocator(), @"1GiB");
var bounded_allocator: BoundedAllocator = .init(self.device.device_allocator.allocator(), 4 * @"1GiB");
try self.drawCall(&bounded_allocator, vertex_count, instance_count, first_vertex, first_instance, null, null);
}
pub fn drawIndexed(self: *Self, index_count: usize, instance_count: usize, first_index: usize, first_instance: usize, vertex_offset: i32) VkError!void {
var bounded_allocator: BoundedAllocator = .init(self.device.device_allocator.allocator(), @"1GiB");
var bounded_allocator: BoundedAllocator = .init(self.device.device_allocator.allocator(), 4 * @"1GiB");
const allocator = bounded_allocator.allocator();
const indexed_draw = try self.readIndexBuffer(allocator, index_count, first_index, vertex_offset);
+11 -1
View File
@@ -186,9 +186,19 @@ fn sample(src: []const u8, pos: F32x4, dim: F32x4, slice_bytes: usize, pitch_byt
z = std.math.clamp(z, 0, @as(usize, @intFromFloat(dim[2])) - 1);
}
const src_map = src[computeOffset3D(x, y, z, slice_bytes, pitch_bytes, src_texel_size)..];
const offset = computeOffset3D(x, y, z, slice_bytes, pitch_bytes, src_texel_size);
const src_map = src[offset..];
if (state.src_samples > 1 and state.dst_samples == 1 and !base.format.isUnnormalizedInteger(state.src_format)) {
const sample_stride = slice_bytes * @as(usize, @intFromFloat(dim[2]));
color = zm.f32x4s(0.0);
for (0..state.src_samples) |sample_index| {
color += readFloat4(src_map[sample_index * sample_stride ..], state.src_format);
}
color /= zm.f32x4s(@floatFromInt(state.src_samples));
} else {
color = readFloat4(src_map, state.src_format);
}
} else {
var x: f32 = pos[0];
var y: f32 = pos[1];
+28 -3
View File
@@ -30,6 +30,7 @@ pub fn shaderInvocation(
batch_id: usize,
position: zm.F32x4,
point_coord: ?@Vector(2, f32),
sample_id: ?u32,
front_face: bool,
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
derivative_inputs: ?DerivativeInputs,
@@ -67,6 +68,13 @@ pub fn shaderInvocation(
else => return err,
};
}
if (sample_id) |id| {
const sample_id_i32: i32 = @intCast(id);
rt.writeBuiltIn(allocator, std.mem.asBytes(&sample_id_i32), .SampleId) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
}
rt.writeBuiltIn(allocator, std.mem.asBytes(&front_face), .FrontFacing) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
@@ -85,14 +93,28 @@ pub fn shaderInvocation(
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
for (0..4) |component| {
var input = fragment_inputs[location][component];
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
SpvRuntimeError.NotFound => {
if (input.blob.len != 0) {
rt.writeInputLocation(input.blob, @intCast(location)) catch |write_err| switch (write_err) {
SpvRuntimeError.NotFound => {},
else => return write_err,
};
}
continue;
},
else => return err,
};
var input = fragment_inputs[location][component];
const has_result_value = rt.results[result_word].variant != null;
const memory_size = if (has_result_value)
try rt.getResultMemorySize(result_word)
else if (input.blob.len == 0)
try rt.getInputLocationMemorySize(@intCast(location))
else
input.blob.len;
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] = .{
@@ -104,6 +126,9 @@ pub fn shaderInvocation(
}
if (input.blob.len != 0) {
if (!has_result_value or input.blob.len < memory_size)
try rt.writeInputLocation(input.blob, @intCast(location))
else
try rt.writeInput(allocator, input.blob, result_word);
if (derivatives) |derivative| {
const dx = derivative.dx[location][component];
+57 -12
View File
@@ -171,6 +171,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
v0,
v1,
v2,
v0,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
@@ -196,6 +197,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
v0,
v1,
v2,
v0,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
@@ -227,6 +229,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
v0,
v1,
v2,
v0,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
@@ -239,6 +242,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
v1,
v0,
v2,
v0,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
@@ -266,6 +270,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
false,
);
}
},
@@ -288,6 +293,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
true,
);
}
}
@@ -341,13 +347,31 @@ fn clipTransformAndRasterizePoint(
var transformed = vertex.*;
clip.viewportTransformVertex(draw_call.viewport, &transformed);
const point_size = transformed.point_size;
const min_x: i32 = @intFromFloat(@ceil(transformed.position[0] - (point_size / 2.0) - 0.5));
const max_x: i32 = @intFromFloat(@ceil(transformed.position[0] + (point_size / 2.0) - 0.5) - 1.0);
const min_y: i32 = @intFromFloat(@ceil(transformed.position[1] - (point_size / 2.0) - 0.5));
const max_y: i32 = @intFromFloat(@ceil(transformed.position[1] + (point_size / 2.0) - 0.5) - 1.0);
const point_min_x = transformed.position[0] - (point_size / 2.0);
const point_min_y = transformed.position[1] - (point_size / 2.0);
try rasterizeTransformedPoint(
allocator,
draw_call,
&transformed,
color_attachment_access,
depth_attachment_access,
stencil_attachment_access,
);
}
fn rasterizeTransformedPoint(
allocator: std.mem.Allocator,
draw_call: *DrawCall,
vertex: *Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
const point_size = vertex.point_size;
const min_x: i32 = @intFromFloat(@ceil(vertex.position[0] - (point_size / 2.0) - 0.5));
const max_x: i32 = @intFromFloat(@ceil(vertex.position[0] + (point_size / 2.0) - 0.5) - 1.0);
const min_y: i32 = @intFromFloat(@ceil(vertex.position[1] - (point_size / 2.0) - 0.5));
const max_y: i32 = @intFromFloat(@ceil(vertex.position[1] + (point_size / 2.0) - 0.5) - 1.0);
const point_min_x = vertex.position[0] - (point_size / 2.0);
const point_min_y = vertex.position[1] - (point_size / 2.0);
const pipeline = draw_call.renderer.state.pipeline orelse return;
const has_fragment_shader = pipeline.stages.getPtr(.fragment) != null;
@@ -375,10 +399,11 @@ fn clipTransformAndRasterizePoint(
allocator,
draw_call,
0,
zm.f32x4(frag_x, frag_y, transformed.position[2], 1.0 / transformed.position[3]),
zm.f32x4(frag_x, frag_y, vertex.position[2], 1.0 / vertex.position[3]),
point_coord,
null,
true,
try common.interpolateVertexOutputs(allocator, &transformed, &transformed, &transformed, 1.0, 0.0, 0.0),
try common.interpolateVertexOutputs(allocator, vertex, vertex, vertex, vertex, 1.0, 0.0, 0.0),
null,
) catch |err| {
if (err == SpvRuntimeError.Killed)
@@ -403,7 +428,7 @@ fn clipTransformAndRasterizePoint(
true,
@intCast(px),
@intCast(py),
fragment_result.depth orelse transformed.position[2],
fragment_result.depth orelse vertex.position[2],
null,
fragment_result.sample_mask,
);
@@ -419,6 +444,7 @@ fn clipTransformAndRasterizeLine(
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
include_last_endpoint: bool,
) VkError!void {
const clipped_line = (try clip.clipLine(allocator, v0, v1)) orelse return;
@@ -428,6 +454,17 @@ fn clipTransformAndRasterizeLine(
clip.viewportTransformVertex(draw_call.viewport, &tv0);
clip.viewportTransformVertex(draw_call.viewport, &tv1);
if (include_last_endpoint) {
try bresenham.drawLineIncludingEndpoint(
allocator,
draw_call,
&tv0,
&tv1,
color_attachment_access,
depth_attachment_access,
stencil_attachment_access,
);
} else {
try bresenham.drawLine(
allocator,
draw_call,
@@ -438,6 +475,7 @@ fn clipTransformAndRasterizeLine(
stencil_attachment_access,
);
}
}
fn clipTransformAndRasterizeTriangle(
renderer: *Renderer,
@@ -446,6 +484,7 @@ fn clipTransformAndRasterizeTriangle(
v0: *Vertex,
v1: *Vertex,
v2: *Vertex,
provoking_vertex: *Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
@@ -471,6 +510,7 @@ fn clipTransformAndRasterizeTriangle(
&tv0,
&tv1,
&tv2,
provoking_vertex,
color_attachment_access,
depth_attachment_access,
stencil_attachment_access,
@@ -485,6 +525,7 @@ fn rasterizeTriangle(
v0: *Vertex,
v1: *Vertex,
v2: *Vertex,
provoking_vertex: *Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
@@ -499,13 +540,17 @@ fn rasterizeTriangle(
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, stencil_attachment_access, front_face),
.fill => try edge_function.drawTriangle(allocator, draw_call, v0, v1, v2, provoking_vertex, 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, 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
.point => {
try rasterizeTransformedPoint(allocator, draw_call, v0, color_attachment_access, depth_attachment_access, stencil_attachment_access);
try rasterizeTransformedPoint(allocator, draw_call, v1, color_attachment_access, depth_attachment_access, stencil_attachment_access);
try rasterizeTransformedPoint(allocator, draw_call, v2, color_attachment_access, depth_attachment_access, stencil_attachment_access);
},
else => base.unsupported("polygon mode {any}", .{pipeline_data.rasterization.polygon_mode}),
}
}
+90 -2
View File
@@ -1,6 +1,7 @@
const std = @import("std");
const base = @import("base");
const spv = @import("spv");
const vk = @import("vulkan");
const zm = base.zm;
const common = @import("common.zig");
@@ -41,6 +42,31 @@ pub fn drawLine(
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
try drawLineWithEndpointMode(allocator, draw_call, v0, v1, color_attachment_access, depth_attachment_access, stencil_attachment_access, false);
}
pub fn drawLineIncludingEndpoint(
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
v0: *Renderer.Vertex,
v1: *Renderer.Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
try drawLineWithEndpointMode(allocator, draw_call, v0, v1, color_attachment_access, depth_attachment_access, stencil_attachment_access, true);
}
fn drawLineWithEndpointMode(
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
v0: *Renderer.Vertex,
v1: *Renderer.Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
include_last_endpoint: bool,
) VkError!void {
const io = draw_call.renderer.device.interface.io();
@@ -76,7 +102,7 @@ pub fn drawLine(
if (runtimes_count == 0)
return;
const step_count: usize = @intCast(@max(d_x, 0) + 1);
const step_count: usize = @intCast(if (include_last_endpoint) @max(d_x, 0) + 1 else @max(d_x, 1));
const runs_count = @min(runtimes_count, step_count);
const steps_per_run = @divTrunc(step_count + runs_count - 1, runs_count);
@@ -125,6 +151,62 @@ fn bresenhamYAtStep(y0: i32, d_x: i32, d_err: i32, y_step: i32, step: usize) i32
return y0 + (y_step * y_offset);
}
fn standardSamplePosition(sample_count: usize, sample_index: usize) struct { x: f32, y: f32 } {
return switch (sample_count) {
1 => .{ .x = 0.5, .y = 0.5 },
2 => switch (sample_index) {
0 => .{ .x = 0.75, .y = 0.75 },
1 => .{ .x = 0.25, .y = 0.25 },
else => .{ .x = 0.5, .y = 0.5 },
},
4 => switch (sample_index) {
0 => .{ .x = 0.375, .y = 0.125 },
1 => .{ .x = 0.875, .y = 0.375 },
2 => .{ .x = 0.125, .y = 0.625 },
3 => .{ .x = 0.625, .y = 0.875 },
else => .{ .x = 0.5, .y = 0.5 },
},
else => .{ .x = 0.5, .y = 0.5 },
};
}
fn lineCoverageMask(data: RunData, pixel_x: i32, pixel_y: i32, sample_count: usize) vk.SampleMask {
if (sample_count <= 1)
return 1;
const a = data.start_vertex.position;
const b = data.end_vertex.position;
const ab_x = b[0] - a[0];
const ab_y = b[1] - a[1];
const ab_len2 = ab_x * ab_x + ab_y * ab_y;
if (ab_len2 == 0.0)
return 1;
var mask: vk.SampleMask = 0;
for (0..sample_count) |sample_index| {
if (sample_index >= @bitSizeOf(vk.SampleMask))
break;
const sample_pos = standardSamplePosition(sample_count, sample_index);
const sample_x = @as(f32, @floatFromInt(pixel_x)) + sample_pos.x;
const sample_y = @as(f32, @floatFromInt(pixel_y)) + sample_pos.y;
const ap_x = sample_x - a[0];
const ap_y = sample_y - a[1];
const t = std.math.clamp(((ap_x * ab_x) + (ap_y * ab_y)) / ab_len2, 0.0, 1.0);
const closest_x = a[0] + ab_x * t;
const closest_y = a[1] + ab_y * t;
const dx = sample_x - closest_x;
const dy = sample_y - closest_y;
if (dx * dx + dy * dy <= 0.25) {
const bit_index: u5 = @intCast(sample_index);
mask |= @as(vk.SampleMask, 1) << bit_index;
}
}
return if (mask == 0) 1 else mask;
}
fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"Rasterization stage").err("line fill mode catched a '{s}'", .{@errorName(err)});
@@ -165,6 +247,7 @@ inline fn run(data: RunData) !void {
data.batch_id,
zm.f32x4(@as(f32, @floatFromInt(pixel_x)) + 0.5, @as(f32, @floatFromInt(pixel_y)) + 0.5, z, frag_w),
null,
null,
true,
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
null,
@@ -193,7 +276,12 @@ inline fn run(data: RunData) !void {
@intCast(pixel_x),
@intCast(pixel_y),
fragment_result.depth orelse z,
null,
lineCoverageMask(
data,
pixel_x,
pixel_y,
data.draw_call.renderer.state.pipeline.?.interface.mode.graphics.multisample.rasterization_samples.toInt(),
),
fragment_result.sample_mask,
);
}
+105 -9
View File
@@ -201,6 +201,7 @@ pub fn interpolateVertexOutputs(
v0: *const Renderer.Vertex,
v1: *const Renderer.Vertex,
v2: *const Renderer.Vertex,
provoking_vertex: *const Renderer.Vertex,
b0: f32,
b1: f32,
b2: f32,
@@ -218,7 +219,8 @@ pub fn interpolateVertexOutputs(
const out2 = v2.outputs[location][component] orelse continue;
if (out0.interpolation_type == .flat or out0.size == 0) {
inputs[location][component] = .{ .blob = out0.blob, .size = out0.size, .free_responsability = false };
const flat_out = provoking_vertex.outputs[location][component] orelse out0;
inputs[location][component] = .{ .blob = flat_out.blob, .size = flat_out.size, .free_responsability = false };
continue;
}
@@ -236,14 +238,14 @@ pub fn interpolateVertexOutputs(
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));
base.utils.writePacked(F32x4, input[byte_index..], interpolateF32x4(out0.interpolation_type, value0, value1, value2, v0, v1, v2, 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));
base.utils.writePacked(f32, input[byte_index..], interpolateF32(out0.interpolation_type, value0, value1, value2, v0, v1, v2, b0, b1, b2));
}
if (byte_index < len)
@@ -262,7 +264,7 @@ pub fn interpolateLineOutputs(
v1: *const Renderer.Vertex,
t: f32,
) 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, v0, 1.0 - t, t, 0.0);
}
pub fn interpolateVertexOutputDerivatives(
@@ -270,6 +272,9 @@ pub fn interpolateVertexOutputDerivatives(
v0: *const Renderer.Vertex,
v1: *const Renderer.Vertex,
v2: *const Renderer.Vertex,
b0: f32,
b1: f32,
b2: f32,
db0: f32,
db1: f32,
db2: f32,
@@ -299,14 +304,14 @@ pub fn interpolateVertexOutputDerivatives(
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, db0, db1, db2));
base.utils.writePacked(F32x4, input[byte_index..], interpolateDerivativeF32x4(out0.interpolation_type, value0, value1, value2, v0, v1, v2, b0, b1, b2, db0, db1, db2));
}
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 * db0) + (value1 * db1) + (value2 * db2));
base.utils.writePacked(f32, input[byte_index..], interpolateDerivativeF32(out0.interpolation_type, value0, value1, value2, v0, v1, v2, b0, b1, b2, db0, db1, db2));
}
}
@@ -317,10 +322,68 @@ pub fn interpolateVertexOutputDerivatives(
return inputs;
}
inline fn interpolateF32x4(value0: F32x4, value1: F32x4, value2: F32x4, b0: f32, b1: f32, b2: f32) F32x4 {
fn perspectiveWeights(v0: *const Renderer.Vertex, v1: *const Renderer.Vertex, v2: *const Renderer.Vertex, b0: f32, b1: f32, b2: f32) struct { w0: f32, w1: f32, w2: f32 } {
const iw0 = 1.0 / v0.position[3];
const iw1 = 1.0 / v1.position[3];
const iw2 = 1.0 / v2.position[3];
const denominator = (b0 * iw0) + (b1 * iw1) + (b2 * iw2);
if (denominator == 0.0)
return .{ .w0 = b0, .w1 = b1, .w2 = b2 };
return .{
.w0 = (b0 * iw0) / denominator,
.w1 = (b1 * iw1) / denominator,
.w2 = (b2 * iw2) / denominator,
};
}
inline fn interpolateF32(interpolation_type: anytype, value0: f32, value1: f32, value2: f32, v0: *const Renderer.Vertex, v1: *const Renderer.Vertex, v2: *const Renderer.Vertex, b0: f32, b1: f32, b2: f32) f32 {
if (interpolation_type == .smooth) {
const weights = perspectiveWeights(v0, v1, v2, b0, b1, b2);
return (value0 * weights.w0) + (value1 * weights.w1) + (value2 * weights.w2);
}
return (value0 * b0) + (value1 * b1) + (value2 * b2);
}
inline fn interpolateF32x4(interpolation_type: anytype, value0: F32x4, value1: F32x4, value2: F32x4, v0: *const Renderer.Vertex, v1: *const Renderer.Vertex, v2: *const Renderer.Vertex, b0: f32, b1: f32, b2: f32) F32x4 {
if (interpolation_type == .smooth) {
const weights = perspectiveWeights(v0, v1, v2, b0, b1, b2);
return (value0 * zm.f32x4s(weights.w0)) + (value1 * zm.f32x4s(weights.w1)) + (value2 * zm.f32x4s(weights.w2));
}
return (value0 * zm.f32x4s(b0)) + (value1 * zm.f32x4s(b1)) + (value2 * zm.f32x4s(b2));
}
inline fn interpolateDerivativeF32(interpolation_type: anytype, value0: f32, value1: f32, value2: f32, v0: *const Renderer.Vertex, v1: *const Renderer.Vertex, v2: *const Renderer.Vertex, b0: f32, b1: f32, b2: f32, db0: f32, db1: f32, db2: f32) f32 {
if (interpolation_type != .smooth)
return (value0 * db0) + (value1 * db1) + (value2 * db2);
const iw0 = 1.0 / v0.position[3];
const iw1 = 1.0 / v1.position[3];
const iw2 = 1.0 / v2.position[3];
const n = (value0 * b0 * iw0) + (value1 * b1 * iw1) + (value2 * b2 * iw2);
const d = (b0 * iw0) + (b1 * iw1) + (b2 * iw2);
const dn = (value0 * db0 * iw0) + (value1 * db1 * iw1) + (value2 * db2 * iw2);
const dd = (db0 * iw0) + (db1 * iw1) + (db2 * iw2);
if (d == 0.0)
return 0.0;
return ((dn * d) - (n * dd)) / (d * d);
}
inline fn interpolateDerivativeF32x4(interpolation_type: anytype, value0: F32x4, value1: F32x4, value2: F32x4, v0: *const Renderer.Vertex, v1: *const Renderer.Vertex, v2: *const Renderer.Vertex, b0: f32, b1: f32, b2: f32, db0: f32, db1: f32, db2: f32) F32x4 {
if (interpolation_type != .smooth)
return (value0 * zm.f32x4s(db0)) + (value1 * zm.f32x4s(db1)) + (value2 * zm.f32x4s(db2));
const iw0 = 1.0 / v0.position[3];
const iw1 = 1.0 / v1.position[3];
const iw2 = 1.0 / v2.position[3];
const n = (value0 * zm.f32x4s(b0 * iw0)) + (value1 * zm.f32x4s(b1 * iw1)) + (value2 * zm.f32x4s(b2 * iw2));
const d = (b0 * iw0) + (b1 * iw1) + (b2 * iw2);
const dn = (value0 * zm.f32x4s(db0 * iw0)) + (value1 * zm.f32x4s(db1 * iw1)) + (value2 * zm.f32x4s(db2 * iw2));
const dd = (db0 * iw0) + (db1 * iw1) + (db2 * iw2);
if (d == 0.0)
return zm.f32x4s(0.0);
return ((dn * zm.f32x4s(d)) - (n * zm.f32x4s(dd))) / zm.f32x4s(d * d);
}
inline fn fragmentOutputFloat4(output: [@sizeOf(F32x4)]u8, format: vk.Format) F32x4 {
const color = std.mem.bytesToValue(F32x4, &output);
_ = format;
@@ -418,8 +481,13 @@ pub fn writeToTargets(
const io = draw_call.renderer.device.interface.io();
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
const depth_stencil_state = pipeline_data.depth_stencil;
const effective_fragment_sample_mask = alphaToCoverageMask(
pipeline_data.multisample,
outputs,
fragment_sample_mask,
);
if (!sampleMaskEnablesAnySample(pipeline_data.multisample, coverage_sample_mask, fragment_sample_mask))
if (!sampleMaskEnablesAnySample(pipeline_data.multisample, coverage_sample_mask, effective_fragment_sample_mask))
return;
if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height)
@@ -432,7 +500,7 @@ pub fn writeToTargets(
const sample_count = pipeline_data.multisample.rasterization_samples.toInt();
for (0..sample_count) |sample_index| {
if (!sampleMaskEnablesSample(pipeline_data.multisample, coverage_sample_mask, fragment_sample_mask, sample_index))
if (!sampleMaskEnablesSample(pipeline_data.multisample, coverage_sample_mask, effective_fragment_sample_mask, sample_index))
continue;
var stencil_state: ?vk.StencilOpState = null;
@@ -511,6 +579,34 @@ pub fn writeToTargets(
}
}
fn alphaToCoverageMask(
multisample: anytype,
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8,
fragment_sample_mask: ?vk.SampleMask,
) ?vk.SampleMask {
if (multisample.alpha_to_coverage_enable == .false)
return fragment_sample_mask;
const sample_count = multisample.rasterization_samples.toInt();
if (sample_count <= 1)
return fragment_sample_mask;
const color = std.mem.bytesToValue(F32x4, &outputs[0]);
const alpha = std.math.clamp(color[3], 0.0, 1.0);
const covered_samples: usize = @intFromFloat(@round(alpha * @as(f32, @floatFromInt(sample_count))));
var alpha_mask: vk.SampleMask = 0;
for (0..covered_samples) |sample_index| {
if (sample_index >= @bitSizeOf(vk.SampleMask))
break;
const bit_index: u5 = @intCast(sample_index);
alpha_mask |= @as(vk.SampleMask, 1) << bit_index;
}
return if (fragment_sample_mask) |mask| mask & alpha_mask else alpha_mask;
}
fn sampleMaskEnablesAnySample(multisample: anytype, coverage_sample_mask: ?vk.SampleMask, fragment_sample_mask: ?vk.SampleMask) bool {
const sample_count = multisample.rasterization_samples.toInt();
if (multisample.sample_mask == null and coverage_sample_mask == null and fragment_sample_mask == null)
@@ -13,6 +13,11 @@ const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
const F32x4 = zm.F32x4;
const SamplePosition = struct {
x: f32,
y: f32,
};
const RunData = struct {
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
@@ -25,12 +30,15 @@ const RunData = struct {
v0: Renderer.Vertex,
v1: Renderer.Vertex,
v2: Renderer.Vertex,
provoking_vertex: 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,
fragment_uses_derivatives: bool,
fragment_uses_sample_id: bool,
fragment_uses_centroid: bool,
};
pub fn drawTriangle(
@@ -39,6 +47,7 @@ pub fn drawTriangle(
v0: *Renderer.Vertex,
v1: *Renderer.Vertex,
v2: *Renderer.Vertex,
provoking_vertex: *Renderer.Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
stencil_attachment_access: ?*common.RenderTargetAccess,
@@ -61,6 +70,14 @@ pub fn drawTriangle(
stage.module.module.reflection_infos.needs_derivatives
else
false;
const fragment_uses_sample_id = if (fragment_stage) |stage|
stage.module.module.builtins.get(.SampleId) != null
else
false;
const fragment_uses_centroid = if (fragment_stage) |stage|
fragmentStageUsesInputDecoration(stage, .Centroid)
else
false;
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1;
if (runtimes_count == 0)
@@ -102,6 +119,7 @@ pub fn drawTriangle(
.v0 = v0.*,
.v1 = v1.*,
.v2 = v2.*,
.provoking_vertex = provoking_vertex.*,
.area = area,
.min_x = run_min_x,
.max_x = run_max_x,
@@ -113,6 +131,8 @@ pub fn drawTriangle(
.front_face = front_face,
.has_fragment_shader = fragment_stage != null,
.fragment_uses_derivatives = fragment_uses_derivatives,
.fragment_uses_sample_id = fragment_uses_sample_id,
.fragment_uses_centroid = fragment_uses_centroid,
};
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
@@ -139,7 +159,7 @@ inline fn edgeContainsPixel(a: F32x4, b: F32x4, edge_value: f32, area: f32) bool
edge_value < 0.0 or (edge_value == 0.0 and isInclusiveEdge(b, a));
}
inline fn standardSamplePosition(sample_count: usize, sample_index: usize) struct { x: f32, y: f32 } {
inline fn standardSamplePosition(sample_count: usize, sample_index: usize) SamplePosition {
return switch (sample_count) {
1 => .{ .x = 0.5, .y = 0.5 },
2 => switch (sample_index) {
@@ -158,6 +178,32 @@ inline fn standardSamplePosition(sample_count: usize, sample_index: usize) struc
};
}
fn fragmentStageUsesInputDecoration(stage: anytype, decoration: anytype) bool {
const rt = &stage.runtimes[0].rt;
for (rt.mod.input_locations) |location| {
for (location) |result_word| {
if (result_word == 0)
continue;
if (rt.hasResultDecoration(result_word, decoration))
return true;
}
}
return false;
}
fn firstCoveredSamplePosition(sample_count: usize, coverage_sample_mask: vk.SampleMask) SamplePosition {
for (0..sample_count) |sample_index| {
if (sample_index >= @bitSizeOf(vk.SampleMask))
break;
const bit_index: u5 = @intCast(sample_index);
if ((coverage_sample_mask & (@as(vk.SampleMask, 1) << bit_index)) != 0)
return standardSamplePosition(sample_count, sample_index);
}
return .{ .x = 0.5, .y = 0.5 };
}
fn triangleCoverageMask(data: RunData, x: i32, y: i32, sample_count: usize) vk.SampleMask {
var mask: vk.SampleMask = 0;
for (0..sample_count) |sample_index| {
@@ -226,14 +272,84 @@ 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]);
const frag_w = (b0 / data.v0.position[3]) + (b1 / data.v1.position[3]) + (b2 / data.v2.position[3]);
const interpolation_barycentrics = if (data.fragment_uses_centroid and sample_count > 1) blk: {
const sample_pos = firstCoveredSamplePosition(sample_count, coverage_sample_mask);
const centroid_p = zm.f32x4(
@as(f32, @floatFromInt(x)) + sample_pos.x,
@as(f32, @floatFromInt(y)) + sample_pos.y,
0.0,
1.0,
);
const centroid_w0 = edgeFunction(data.v1.position, data.v2.position, centroid_p);
const centroid_w1 = edgeFunction(data.v2.position, data.v0.position, centroid_p);
const centroid_w2 = edgeFunction(data.v0.position, data.v1.position, centroid_p);
break :blk .{
centroid_w0 / data.area,
centroid_w1 / data.area,
centroid_w2 / data.area,
};
} else .{ b0, b1, b2 };
const input_b0 = interpolation_barycentrics[0];
const input_b1 = interpolation_barycentrics[1];
const input_b2 = interpolation_barycentrics[2];
var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
.depth = null,
.sample_mask = null,
};
if (data.has_fragment_shader and data.fragment_uses_sample_id and sample_count > 1) {
for (0..sample_count) |sample_index| {
if (sample_index >= @bitSizeOf(vk.SampleMask))
break;
const bit_index: u5 = @intCast(sample_index);
const sample_coverage_mask = @as(vk.SampleMask, 1) << bit_index;
if ((coverage_sample_mask & sample_coverage_mask) == 0)
continue;
const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, &data.provoking_vertex, input_b0, input_b1, input_b2);
const sample_result = fragment.shaderInvocation(
data.allocator,
data.draw_call,
data.batch_id,
zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, z, frag_w),
null,
@intCast(sample_index),
data.front_face,
inputs,
null,
) catch |err| {
if (err == SpvRuntimeError.Killed)
continue;
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(
sample_result.outputs,
data.draw_call,
data.color_attachment_access,
data.depth_attachment_access,
data.stencil_attachment_access,
data.front_face,
@intCast(x),
@intCast(y),
sample_result.depth orelse z,
sample_coverage_mask,
sample_result.sample_mask,
);
}
continue;
}
if (data.has_fragment_shader) {
const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2);
const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, &data.provoking_vertex, input_b0, input_b1, input_b2);
const derivative_inputs: ?fragment.DerivativeInputs = if (data.fragment_uses_derivatives) blk: {
var derivatives: fragment.DerivativeInputs = undefined;
@@ -246,6 +362,9 @@ inline fn run(data: RunData) !void {
&data.v0,
&data.v1,
&data.v2,
b0,
b1,
b2,
(dx_w0 / data.area) - b0,
(dx_w1 / data.area) - b1,
(dx_w2 / data.area) - b2,
@@ -260,6 +379,9 @@ inline fn run(data: RunData) !void {
&data.v0,
&data.v1,
&data.v2,
b0,
b1,
b2,
(dy_w0 / data.area) - b0,
(dy_w1 / data.area) - b1,
(dy_w2 / data.area) - b2,
@@ -273,6 +395,7 @@ inline fn run(data: RunData) !void {
data.batch_id,
zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, z, frag_w),
null,
null,
data.front_face,
inputs,
derivative_inputs,
+34 -11
View File
@@ -68,10 +68,7 @@ inline fn run(data: RunData) !void {
const vertex_index: usize = vertex_index_u32;
const instance_index = data.first_instance + data.instance_index;
setupBuiltins(rt, data.allocator, vertex_index_u32, instance_index) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
try setupBuiltins(rt, data.allocator, vertex_index_u32, instance_index);
if (data.pipeline.interface.mode.graphics.input_assembly.attribute_description) |attributes| {
for (attributes) |attribute| {
@@ -118,10 +115,7 @@ inline fn run(data: RunData) !void {
const memory_size = try rt.getResultMemorySize(result_word);
const result_is_integer = blk: {
const result_type = rt.getResultPrimitiveType(result_word) catch break :blk false;
break :blk result_type == .SInt or result_type == .UInt;
};
const result_is_integer = resultIsInteger(rt, result_word);
output.outputs[location][component] = .{
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat) or result_is_integer) .flat else .smooth, // TODO : handle noperspective
@@ -149,7 +143,7 @@ fn readPosition(rt: *spv.Runtime, output: []u8) !void {
if (rt.readBuiltIn(output, .Position)) {
return;
} else |err| switch (err) {
SpvRuntimeError.InvalidSpirV => {},
SpvRuntimeError.InvalidSpirV, SpvRuntimeError.NotFound => {},
else => return err,
}
@@ -212,11 +206,40 @@ fn isConstantZero(rt: *spv.Runtime, result_word: spv.SpvWord) bool {
}
}
fn resultIsInteger(rt: *spv.Runtime, result_word: spv.SpvWord) bool {
const value = (rt.results[result_word].getConstValue() catch return false);
return valueIsInteger(value);
}
fn valueIsInteger(value: anytype) bool {
return switch (value.*) {
.Int,
.Vector2i32,
.Vector3i32,
.Vector4i32,
.Vector2u32,
.Vector3u32,
.Vector4u32,
=> true,
.Vector,
.Matrix,
=> |values| values.len != 0 and valueIsInteger(&values[0]),
.Array => |array| array.values.len != 0 and valueIsInteger(&array.values[0]),
else => false,
};
}
fn setupBuiltins(rt: *spv.Runtime, allocator: std.mem.Allocator, vertex_index_u32: u32, instance_index: usize) !void {
const instance_index_u32: u32 = @intCast(instance_index);
try rt.writeBuiltIn(allocator, std.mem.asBytes(&vertex_index_u32), .VertexIndex);
try rt.writeBuiltIn(allocator, std.mem.asBytes(&instance_index_u32), .InstanceIndex);
rt.writeBuiltIn(allocator, std.mem.asBytes(&vertex_index_u32), .VertexIndex) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
rt.writeBuiltIn(allocator, std.mem.asBytes(&instance_index_u32), .InstanceIndex) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
}
fn writeVertexInput(rt: *spv.Runtime, allocator: std.mem.Allocator, raw_input: []const u8, format: vk.Format, location: u32) !void {
+1 -1
View File
@@ -63,7 +63,7 @@ pub const MIN_UNIFORM_BUFFER_ALIGNMENT = 256;
pub const MIN_STORAGE_BUFFER_ALIGNMENT = 256;
pub const MAX_VERTEX_INPUT_BINDINGS = 16;
pub const MAX_VERTEX_INPUT_ATTRIBUTES = 32;
pub const MAX_VERTEX_INPUT_ATTRIBUTES = 16;
pub const PUSH_CONSTANT_SIZE = 256;
+6
View File
@@ -53,6 +53,8 @@ mode: union(enum) {
multisample: struct {
rasterization_samples: vk.SampleCountFlags,
sample_mask: ?[]vk.SampleMask,
alpha_to_coverage_enable: vk.Bool32,
alpha_to_one_enable: vk.Bool32,
},
color_blend: struct {
attachments: ?[]vk.PipelineColorBlendAttachmentState,
@@ -198,6 +200,8 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
break :blk .{
.rasterization_samples = .{ .@"1_bit" = true },
.sample_mask = null,
.alpha_to_coverage_enable = .false,
.alpha_to_one_enable = .false,
};
}
@@ -209,6 +213,8 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
sample_mask = allocator.dupe(vk.SampleMask, mask[0..mask_word_count]) catch return VkError.OutOfHostMemory;
break :blk_mask sample_mask;
} else null,
.alpha_to_coverage_enable = state.alpha_to_coverage_enable,
.alpha_to_one_enable = state.alpha_to_one_enable,
};
},
.color_blend = blk: {
+4
View File
@@ -1591,6 +1591,10 @@ pub export fn apeGetPipelineCacheData(p_device: vk.Device, p_cache: vk.PipelineC
const available = cache.availableDataSize();
const result = if (data) |ptr| blk: {
if (size.* < @sizeOf(PipelineCache.Header)) {
size.* = 0;
return .incomplete;
}
const bytes = @as([*]u8, @ptrCast(ptr))[0..size.*];
break :blk cache.getData(bytes);
} else cache.getData(null);