improving push constants support, fixing image resolving, lots of rasterizer fixes
This commit is contained in:
+2
-2
@@ -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 = .{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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,
|
||||
@@ -437,6 +474,7 @@ fn clipTransformAndRasterizeLine(
|
||||
depth_attachment_access,
|
||||
stencil_attachment_access,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn clipTransformAndRasterizeTriangle(
|
||||
@@ -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}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user