improving renderpasses support
This commit is contained in:
@@ -33,6 +33,38 @@ interface: Interface,
|
|||||||
command_allocator: std.heap.ArenaAllocator,
|
command_allocator: std.heap.ArenaAllocator,
|
||||||
commands: std.ArrayList(Command),
|
commands: std.ArrayList(Command),
|
||||||
|
|
||||||
|
fn attachmentIsReferencedBySubpass(render_pass: *SoftRenderPass, attachment_index: u32) bool {
|
||||||
|
for (render_pass.interface.subpasses) |subpass| {
|
||||||
|
if (subpass.input_attachments) |attachments| {
|
||||||
|
for (attachments) |attachment| {
|
||||||
|
if (attachment.attachment == attachment_index)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subpass.color_attachments) |attachments| {
|
||||||
|
for (attachments) |attachment| {
|
||||||
|
if (attachment.attachment == attachment_index)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subpass.resolve_attachments) |attachments| {
|
||||||
|
for (attachments) |attachment| {
|
||||||
|
if (attachment.attachment == attachment_index)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subpass.depth_stencil_attachments) |attachment| {
|
||||||
|
if (attachment.attachment == attachment_index)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!*Self {
|
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!*Self {
|
||||||
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
|
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
|
||||||
errdefer allocator.destroy(self);
|
errdefer allocator.destroy(self);
|
||||||
@@ -267,9 +299,13 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
|
|||||||
const impl: *Impl = @ptrCast(@alignCast(context));
|
const impl: *Impl = @ptrCast(@alignCast(context));
|
||||||
device.renderer.render_pass = impl.render_pass;
|
device.renderer.render_pass = impl.render_pass;
|
||||||
device.renderer.framebuffer = impl.framebuffer;
|
device.renderer.framebuffer = impl.framebuffer;
|
||||||
|
device.renderer.render_area = impl.render_area;
|
||||||
device.renderer.subpass_index = 0;
|
device.renderer.subpass_index = 0;
|
||||||
|
|
||||||
for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| {
|
for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| {
|
||||||
|
if (!attachmentIsReferencedBySubpass(impl.render_pass, @intCast(index)))
|
||||||
|
continue;
|
||||||
|
|
||||||
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image));
|
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image));
|
||||||
var clear_mask: vk.ImageAspectFlags = .{};
|
var clear_mask: vk.ImageAspectFlags = .{};
|
||||||
|
|
||||||
@@ -987,6 +1023,7 @@ pub fn endRenderPass(interface: *Interface) VkError!void {
|
|||||||
|
|
||||||
device.renderer.render_pass = null;
|
device.renderer.render_pass = null;
|
||||||
device.renderer.framebuffer = null;
|
device.renderer.framebuffer = null;
|
||||||
|
device.renderer.render_area = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -303,6 +303,14 @@ fn imageMipLevel(image_view: *SoftImageView, lod: ?i32) u32 {
|
|||||||
return range.base_mip_level + @min(mip_lod, max_lod);
|
return range.base_mip_level + @min(mip_lod, max_lod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn imageReadAspect(image_view: *SoftImageView, comptime int_read: bool) vk.ImageAspectFlags {
|
||||||
|
const aspect = image_view.interface.subresource_range.aspect_mask;
|
||||||
|
if (aspect.depth_bit and aspect.stencil_bit) {
|
||||||
|
return if (int_read) .{ .stencil_bit = true } else .{ .depth_bit = true };
|
||||||
|
}
|
||||||
|
return aspect;
|
||||||
|
}
|
||||||
|
|
||||||
fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D {
|
fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D {
|
||||||
const coord = current_fragment_coord orelse return SpvRuntimeError.Unknown;
|
const coord = current_fragment_coord orelse return SpvRuntimeError.Unknown;
|
||||||
return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z };
|
return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z };
|
||||||
@@ -331,14 +339,15 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32,
|
|||||||
.cube => cube_face,
|
.cube => cube_face,
|
||||||
else => 0,
|
else => 0,
|
||||||
};
|
};
|
||||||
|
const aspect_mask = imageReadAspect(image_view, false);
|
||||||
pixel = SoftSampler.swizzleFloat4(image.readFloat4(
|
pixel = SoftSampler.swizzleFloat4(image.readFloat4(
|
||||||
image_coord,
|
image_coord,
|
||||||
.{
|
.{
|
||||||
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
|
.aspect_mask = aspect_mask,
|
||||||
.mip_level = mip_level,
|
.mip_level = mip_level,
|
||||||
.array_layer = array_layer,
|
.array_layer = array_layer,
|
||||||
},
|
},
|
||||||
image_view.interface.format,
|
base.format.fromAspect(image_view.interface.format, aspect_mask),
|
||||||
) catch return SpvRuntimeError.Unknown, image_view.interface.components);
|
) catch return SpvRuntimeError.Unknown, image_view.interface.components);
|
||||||
}
|
}
|
||||||
return .{
|
return .{
|
||||||
@@ -372,14 +381,15 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l
|
|||||||
.cube => cube_face,
|
.cube => cube_face,
|
||||||
else => 0,
|
else => 0,
|
||||||
};
|
};
|
||||||
|
const aspect_mask = imageReadAspect(image_view, true);
|
||||||
pixel = SoftSampler.swizzleInt4(image.readInt4(
|
pixel = SoftSampler.swizzleInt4(image.readInt4(
|
||||||
image_coord,
|
image_coord,
|
||||||
.{
|
.{
|
||||||
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
|
.aspect_mask = aspect_mask,
|
||||||
.mip_level = mip_level,
|
.mip_level = mip_level,
|
||||||
.array_layer = array_layer,
|
.array_layer = array_layer,
|
||||||
},
|
},
|
||||||
image_view.interface.format,
|
base.format.fromAspect(image_view.interface.format, aspect_mask),
|
||||||
) catch return SpvRuntimeError.Unknown, image_view.interface.components);
|
) catch return SpvRuntimeError.Unknown, image_view.interface.components);
|
||||||
}
|
}
|
||||||
return .{
|
return .{
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ state: *PipelineState,
|
|||||||
|
|
||||||
render_pass: ?*SoftRenderPass,
|
render_pass: ?*SoftRenderPass,
|
||||||
framebuffer: ?*SoftFramebuffer,
|
framebuffer: ?*SoftFramebuffer,
|
||||||
|
render_area: ?vk.Rect2D,
|
||||||
dynamic_state: DynamicState,
|
dynamic_state: DynamicState,
|
||||||
|
|
||||||
subpass_index: usize,
|
subpass_index: usize,
|
||||||
@@ -146,6 +147,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries
|
|||||||
.state = state,
|
.state = state,
|
||||||
.render_pass = null,
|
.render_pass = null,
|
||||||
.framebuffer = null,
|
.framebuffer = null,
|
||||||
|
.render_area = null,
|
||||||
.dynamic_state = .{
|
.dynamic_state = .{
|
||||||
.viewports = null,
|
.viewports = null,
|
||||||
.scissor = null,
|
.scissor = null,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const INTERFACE_BLOB_PADDING = @sizeOf(zm.F32x4);
|
|||||||
pub const InvocationResult = struct {
|
pub const InvocationResult = struct {
|
||||||
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8,
|
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8,
|
||||||
depth: ?f32,
|
depth: ?f32,
|
||||||
|
sample_mask: ?vk.SampleMask,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DerivativeInputs = struct {
|
pub const DerivativeInputs = struct {
|
||||||
@@ -76,7 +77,7 @@ pub fn shaderInvocation(
|
|||||||
SoftPipeline.current_fragment_coord = .{
|
SoftPipeline.current_fragment_coord = .{
|
||||||
.x = @intFromFloat(position[0]),
|
.x = @intFromFloat(position[0]),
|
||||||
.y = @intFromFloat(position[1]),
|
.y = @intFromFloat(position[1]),
|
||||||
.z = @intFromFloat(position[2]),
|
.z = 0,
|
||||||
};
|
};
|
||||||
defer SoftPipeline.current_fragment_coord = previous_fragment_coord;
|
defer SoftPipeline.current_fragment_coord = previous_fragment_coord;
|
||||||
|
|
||||||
@@ -165,6 +166,15 @@ pub fn shaderInvocation(
|
|||||||
else => return err,
|
else => return err,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sample_mask: ?vk.SampleMask = null;
|
||||||
|
var frag_sample_mask: [1]vk.SampleMask = undefined;
|
||||||
|
if (rt.readBuiltIn(std.mem.asBytes(&frag_sample_mask), .SampleMask)) {
|
||||||
|
sample_mask = frag_sample_mask[0];
|
||||||
|
} else |err| switch (err) {
|
||||||
|
SpvRuntimeError.InvalidSpirV, SpvRuntimeError.NotFound => {},
|
||||||
|
else => return err,
|
||||||
|
}
|
||||||
|
|
||||||
try rt.flushDescriptorSets(allocator);
|
try rt.flushDescriptorSets(allocator);
|
||||||
freeOwnedInputs(allocator, fragment_inputs);
|
freeOwnedInputs(allocator, fragment_inputs);
|
||||||
if (derivatives) |owned_derivatives| {
|
if (derivatives) |owned_derivatives| {
|
||||||
@@ -175,6 +185,7 @@ pub fn shaderInvocation(
|
|||||||
return .{
|
return .{
|
||||||
.outputs = outputs,
|
.outputs = outputs,
|
||||||
.depth = depth,
|
.depth = depth,
|
||||||
|
.sample_mask = sample_mask,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
.base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size),
|
.base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size),
|
||||||
.row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format),
|
.row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format),
|
||||||
.texel_size = base.format.texelSize(color_format),
|
.texel_size = base.format.texelSize(color_format),
|
||||||
|
.sample_count = render_target.interface.samples.toInt(),
|
||||||
|
.sample_stride = render_target.getMipLevelSize(color_range.aspect_mask, color_range.base_mip_level),
|
||||||
.width = color_extent.width,
|
.width = color_extent.width,
|
||||||
.height = color_extent.height,
|
.height = color_extent.height,
|
||||||
.format = color_format,
|
.format = color_format,
|
||||||
@@ -96,6 +98,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
||||||
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_aspect, depth_range.base_mip_level, depth_format),
|
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_aspect, depth_range.base_mip_level, depth_format),
|
||||||
.texel_size = base.format.texelSize(depth_aspect_format),
|
.texel_size = base.format.texelSize(depth_aspect_format),
|
||||||
|
.sample_count = depth_attachment.?.interface.samples.toInt(),
|
||||||
|
.sample_stride = depth_attachment.?.getMipLevelSize(depth_aspect, depth_range.base_mip_level),
|
||||||
.width = depth_extent.width,
|
.width = depth_extent.width,
|
||||||
.height = depth_extent.height,
|
.height = depth_extent.height,
|
||||||
.format = depth_aspect_format,
|
.format = depth_aspect_format,
|
||||||
@@ -126,6 +130,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
||||||
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(stencil_aspect, stencil_range.base_mip_level, stencil_format),
|
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(stencil_aspect, stencil_range.base_mip_level, stencil_format),
|
||||||
.texel_size = base.format.texelSize(stencil_aspect_format),
|
.texel_size = base.format.texelSize(stencil_aspect_format),
|
||||||
|
.sample_count = depth_attachment.?.interface.samples.toInt(),
|
||||||
|
.sample_stride = depth_attachment.?.getMipLevelSize(stencil_aspect, stencil_range.base_mip_level),
|
||||||
.width = stencil_extent.width,
|
.width = stencil_extent.width,
|
||||||
.height = stencil_extent.height,
|
.height = stencil_extent.height,
|
||||||
.format = stencil_aspect_format,
|
.format = stencil_aspect_format,
|
||||||
@@ -355,6 +361,7 @@ fn clipTransformAndRasterizePoint(
|
|||||||
var fragment_result: fragment.InvocationResult = .{
|
var fragment_result: fragment.InvocationResult = .{
|
||||||
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8),
|
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8),
|
||||||
.depth = null,
|
.depth = null,
|
||||||
|
.sample_mask = null,
|
||||||
};
|
};
|
||||||
if (has_fragment_shader) {
|
if (has_fragment_shader) {
|
||||||
const frag_x = @as(f32, @floatFromInt(px)) + 0.5;
|
const frag_x = @as(f32, @floatFromInt(px)) + 0.5;
|
||||||
@@ -387,7 +394,7 @@ fn clipTransformAndRasterizePoint(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try common.writeToTargets(fragment_result.outputs, draw_call, color_attachment_access, depth_attachment_access, stencil_attachment_access, true, @intCast(px), @intCast(py), fragment_result.depth orelse transformed.position[2]);
|
try common.writeToTargets(fragment_result.outputs, draw_call, color_attachment_access, depth_attachment_access, stencil_attachment_access, true, @intCast(px), @intCast(py), fragment_result.depth orelse transformed.position[2], fragment_result.sample_mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ inline fn run(data: RunData) !void {
|
|||||||
var fragment_result: fragment.InvocationResult = .{
|
var fragment_result: fragment.InvocationResult = .{
|
||||||
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
|
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
|
||||||
.depth = null,
|
.depth = null,
|
||||||
|
.sample_mask = null,
|
||||||
};
|
};
|
||||||
if (data.has_fragment_shader) {
|
if (data.has_fragment_shader) {
|
||||||
fragment_result = fragment.shaderInvocation(
|
fragment_result = fragment.shaderInvocation(
|
||||||
@@ -192,6 +193,7 @@ inline fn run(data: RunData) !void {
|
|||||||
@intCast(pixel_x),
|
@intCast(pixel_x),
|
||||||
@intCast(pixel_y),
|
@intCast(pixel_y),
|
||||||
fragment_result.depth orelse z,
|
fragment_result.depth orelse z,
|
||||||
|
fragment_result.sample_mask,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub const RenderTargetAccess = struct {
|
|||||||
base: []u8,
|
base: []u8,
|
||||||
row_pitch: usize,
|
row_pitch: usize,
|
||||||
texel_size: usize,
|
texel_size: usize,
|
||||||
|
sample_count: usize,
|
||||||
|
sample_stride: usize,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
format: vk.Format,
|
format: vk.Format,
|
||||||
@@ -45,6 +47,22 @@ pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
|
|||||||
pixel_y < max_y;
|
pixel_y < max_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rectContainsPixel(rect: vk.Rect2D, x: usize, y: usize) bool {
|
||||||
|
const min_x: i64 = @as(i64, rect.offset.x);
|
||||||
|
const min_y: i64 = @as(i64, rect.offset.y);
|
||||||
|
|
||||||
|
const max_x: i64 = min_x + @as(i64, @intCast(rect.extent.width));
|
||||||
|
const max_y: i64 = min_y + @as(i64, @intCast(rect.extent.height));
|
||||||
|
|
||||||
|
const pixel_x: i64 = @intCast(x);
|
||||||
|
const pixel_y: i64 = @intCast(y);
|
||||||
|
|
||||||
|
return pixel_x >= min_x and
|
||||||
|
pixel_x < max_x and
|
||||||
|
pixel_y >= min_y and
|
||||||
|
pixel_y < max_y;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn targetContainsPixel(target: RenderTargetAccess, x: i32, y: i32) bool {
|
pub fn targetContainsPixel(target: RenderTargetAccess, x: i32, y: i32) bool {
|
||||||
if (x < 0 or y < 0)
|
if (x < 0 or y < 0)
|
||||||
return false;
|
return false;
|
||||||
@@ -62,6 +80,14 @@ pub fn targetOffset(target: RenderTargetAccess, x: usize, y: usize) ?usize {
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn targetSampleOffset(target: RenderTargetAccess, x: usize, y: usize, sample_index: usize) ?usize {
|
||||||
|
const base_offset = targetOffset(target, x, y) orelse return null;
|
||||||
|
const offset = base_offset + sample_index * target.sample_stride;
|
||||||
|
if (offset > target.base.len or target.texel_size > target.base.len - offset)
|
||||||
|
return null;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compare(comptime T: type, op: vk.CompareOp, reference: T, value: T) bool {
|
pub fn compare(comptime T: type, op: vk.CompareOp, reference: T, value: T) bool {
|
||||||
return switch (op) {
|
return switch (op) {
|
||||||
.never => false,
|
.never => false,
|
||||||
@@ -100,6 +126,10 @@ fn updateStencilValue(stencil: *RenderTargetAccess, offset: usize, state: vk.Ste
|
|||||||
|
|
||||||
pub fn stencilTestAndUpdate(stencil: *RenderTargetAccess, x: usize, y: usize, state: vk.StencilOpState, depth_passed: ?bool) bool {
|
pub fn stencilTestAndUpdate(stencil: *RenderTargetAccess, x: usize, y: usize, state: vk.StencilOpState, depth_passed: ?bool) bool {
|
||||||
const offset = targetOffset(stencil.*, x, y) orelse return false;
|
const offset = targetOffset(stencil.*, x, y) orelse return false;
|
||||||
|
return stencilTestAndUpdateAtOffset(stencil, offset, state, depth_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stencilTestAndUpdateAtOffset(stencil: *RenderTargetAccess, offset: usize, state: vk.StencilOpState, depth_passed: ?bool) bool {
|
||||||
const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8);
|
const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8);
|
||||||
const reference = state.reference & std.math.maxInt(u8);
|
const reference = state.reference & std.math.maxInt(u8);
|
||||||
const compare_mask = state.compare_mask & std.math.maxInt(u8);
|
const compare_mask = state.compare_mask & std.math.maxInt(u8);
|
||||||
@@ -138,10 +168,14 @@ fn quantizeDepthForFormat(format: vk.Format, z: f32) f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
|
pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
|
||||||
|
const offset = targetOffset(depth.*, x, y) orelse return false;
|
||||||
|
return depthTestAndUpdateAtOffset(depth, offset, z, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn depthTestAndUpdateAtOffset(depth: *RenderTargetAccess, offset: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
|
||||||
if (state.depth_test_enable == .false)
|
if (state.depth_test_enable == .false)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const offset = targetOffset(depth.*, x, y) orelse return false;
|
|
||||||
const reference = quantizeDepthForFormat(depth.format, z);
|
const reference = quantizeDepthForFormat(depth.format, z);
|
||||||
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
||||||
const passed = compare(f32, state.depth_compare_op, reference, depth_value[0]);
|
const passed = compare(f32, state.depth_compare_op, reference, depth_value[0]);
|
||||||
@@ -373,13 +407,28 @@ pub fn writeToTargets(
|
|||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
z: f32,
|
z: f32,
|
||||||
|
fragment_sample_mask: ?vk.SampleMask,
|
||||||
) VkError!void {
|
) VkError!void {
|
||||||
const io = draw_call.renderer.device.interface.io();
|
const io = draw_call.renderer.device.interface.io();
|
||||||
const depth_stencil_state = draw_call.renderer.state.pipeline.?.interface.mode.graphics.depth_stencil;
|
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
|
||||||
|
const depth_stencil_state = pipeline_data.depth_stencil;
|
||||||
|
|
||||||
|
if (!sampleMaskEnablesAnySample(pipeline_data.multisample, fragment_sample_mask))
|
||||||
|
return;
|
||||||
|
|
||||||
if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height)
|
if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (draw_call.renderer.render_area) |render_area| {
|
||||||
|
if (!rectContainsPixel(render_area, x, y))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sample_count = pipeline_data.multisample.rasterization_samples.toInt();
|
||||||
|
for (0..sample_count) |sample_index| {
|
||||||
|
if (!sampleMaskEnablesSample(pipeline_data.multisample, fragment_sample_mask, sample_index))
|
||||||
|
continue;
|
||||||
|
|
||||||
var stencil_state: ?vk.StencilOpState = null;
|
var stencil_state: ?vk.StencilOpState = null;
|
||||||
var stencil_offset: ?usize = null;
|
var stencil_offset: ?usize = null;
|
||||||
if (stencil_attachment_access) |stencil| {
|
if (stencil_attachment_access) |stencil| {
|
||||||
@@ -389,10 +438,10 @@ pub fn writeToTargets(
|
|||||||
resolveStencilState(draw_call, state.front, true)
|
resolveStencilState(draw_call, state.front, true)
|
||||||
else
|
else
|
||||||
resolveStencilState(draw_call, state.back, false);
|
resolveStencilState(draw_call, state.back, false);
|
||||||
stencil_offset = targetOffset(stencil.*, x, y) orelse return;
|
stencil_offset = targetSampleOffset(stencil.*, x, y, sample_index) orelse continue;
|
||||||
if (!stencilTest(stencil, stencil_offset.?, stencil_state.?)) {
|
if (!stencilTest(stencil, stencil_offset.?, stencil_state.?)) {
|
||||||
updateStencilValue(stencil, stencil_offset.?, stencil_state.?, stencil_state.?.fail_op);
|
updateStencilValue(stencil, stencil_offset.?, stencil_state.?, stencil_state.?.fail_op);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,19 +450,19 @@ pub fn writeToTargets(
|
|||||||
// After work depth test to avoid overwritten depth pixels during fragment invocations.
|
// After work depth test to avoid overwritten depth pixels during fragment invocations.
|
||||||
var depth_passed: ?bool = null;
|
var depth_passed: ?bool = null;
|
||||||
if (depth_attachment_access) |depth| {
|
if (depth_attachment_access) |depth| {
|
||||||
const depth_offset = targetOffset(depth.*, x, y) orelse return;
|
const depth_offset = targetSampleOffset(depth.*, x, y, sample_index) orelse continue;
|
||||||
|
|
||||||
depth.mutex.lock(io) catch return VkError.DeviceLost;
|
depth.mutex.lock(io) catch return VkError.DeviceLost;
|
||||||
defer depth.mutex.unlock(io);
|
defer depth.mutex.unlock(io);
|
||||||
|
|
||||||
if (depth_stencil_state) |state| {
|
if (depth_stencil_state) |state| {
|
||||||
depth_passed = depthTestAndUpdate(depth, x, y, z, state);
|
depth_passed = depthTestAndUpdateAtOffset(depth, depth_offset, z, state);
|
||||||
if (!depth_passed.? and stencil_state == null)
|
if (!depth_passed.? and stencil_state == null)
|
||||||
return;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format);
|
const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format);
|
||||||
if (z >= depth_value[0])
|
if (z >= depth_value[0])
|
||||||
return;
|
continue;
|
||||||
blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format);
|
blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format);
|
||||||
depth_passed = true;
|
depth_passed = true;
|
||||||
}
|
}
|
||||||
@@ -423,7 +472,7 @@ pub fn writeToTargets(
|
|||||||
if (stencil_state) |state| {
|
if (stencil_state) |state| {
|
||||||
if (depth_passed != null and !depth_passed.?) {
|
if (depth_passed != null and !depth_passed.?) {
|
||||||
updateStencilValue(stencil, stencil_offset.?, state, state.depth_fail_op);
|
updateStencilValue(stencil, stencil_offset.?, state, state.depth_fail_op);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
updateStencilValue(stencil, stencil_offset.?, state, state.pass_op);
|
updateStencilValue(stencil, stencil_offset.?, state, state.pass_op);
|
||||||
}
|
}
|
||||||
@@ -435,12 +484,10 @@ pub fn writeToTargets(
|
|||||||
|
|
||||||
for (color_attachment_access, 0..) |maybe_color, location| {
|
for (color_attachment_access, 0..) |maybe_color, location| {
|
||||||
const color = maybe_color orelse continue;
|
const color = maybe_color orelse continue;
|
||||||
const color_offset = targetOffset(color, x, y) orelse continue;
|
const color_offset = targetSampleOffset(color, x, y, sample_index) orelse continue;
|
||||||
|
|
||||||
if (base.format.isUnnormalizedInteger(color.format)) {
|
if (base.format.isUnnormalizedInteger(color.format)) {
|
||||||
blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format);
|
blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format);
|
||||||
} else {
|
} else {
|
||||||
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
|
|
||||||
const src = fragmentOutputFloat4(outputs[location], color.format);
|
const src = fragmentOutputFloat4(outputs[location], color.format);
|
||||||
const encoded_dst = blitter.readFloat4(color.base[color_offset..], color.format);
|
const encoded_dst = blitter.readFloat4(color.base[color_offset..], color.format);
|
||||||
const dst = if (base.format.isSrgb(color.format)) zm.srgbToRgb(encoded_dst) else encoded_dst;
|
const dst = if (base.format.isSrgb(color.format)) zm.srgbToRgb(encoded_dst) else encoded_dst;
|
||||||
@@ -456,3 +503,52 @@ pub fn writeToTargets(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sampleMaskEnablesAnySample(multisample: anytype, fragment_sample_mask: ?vk.SampleMask) bool {
|
||||||
|
const sample_count = multisample.rasterization_samples.toInt();
|
||||||
|
if (multisample.sample_mask == null and fragment_sample_mask == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (multisample.sample_mask) |pipeline_sample_mask| {
|
||||||
|
for (pipeline_sample_mask, 0..) |word, word_index| {
|
||||||
|
if (sampleMaskWordEnablesAnySample(sample_count, word_index, word, fragment_sample_mask))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sampleMaskWordEnablesAnySample(sample_count, 0, std.math.maxInt(vk.SampleMask), fragment_sample_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sampleMaskWordEnablesAnySample(sample_count: usize, word_index: usize, pipeline_word: vk.SampleMask, fragment_sample_mask: ?vk.SampleMask) bool {
|
||||||
|
const remaining_samples = sample_count -| (word_index * 32);
|
||||||
|
const active_bits = @min(remaining_samples, 32);
|
||||||
|
if (active_bits == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const active_mask: vk.SampleMask = if (active_bits == 32)
|
||||||
|
std.math.maxInt(vk.SampleMask)
|
||||||
|
else
|
||||||
|
(@as(vk.SampleMask, 1) << @intCast(active_bits)) - 1;
|
||||||
|
|
||||||
|
const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
|
||||||
|
return ((pipeline_word & fragment_word) & active_mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sampleMaskEnablesSample(multisample: anytype, fragment_sample_mask: ?vk.SampleMask, sample_index: usize) bool {
|
||||||
|
if (sample_index >= multisample.rasterization_samples.toInt())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const word_index = sample_index / 32;
|
||||||
|
const bit_index: u5 = @intCast(sample_index % 32);
|
||||||
|
const bit = @as(vk.SampleMask, 1) << bit_index;
|
||||||
|
|
||||||
|
const pipeline_word = if (multisample.sample_mask) |pipeline_sample_mask|
|
||||||
|
if (word_index < pipeline_sample_mask.len) pipeline_sample_mask[word_index] else @as(vk.SampleMask, 0)
|
||||||
|
else
|
||||||
|
std.math.maxInt(vk.SampleMask);
|
||||||
|
|
||||||
|
const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
|
||||||
|
return (pipeline_word & fragment_word & bit) != 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ inline fn run(data: RunData) !void {
|
|||||||
var fragment_result: fragment.InvocationResult = .{
|
var fragment_result: fragment.InvocationResult = .{
|
||||||
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
|
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
|
||||||
.depth = null,
|
.depth = null,
|
||||||
|
.sample_mask = null,
|
||||||
};
|
};
|
||||||
if (data.has_fragment_shader) {
|
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, b0, b1, b2);
|
||||||
@@ -251,6 +252,7 @@ inline fn run(data: RunData) !void {
|
|||||||
@intCast(x),
|
@intCast(x),
|
||||||
@intCast(y),
|
@intCast(y),
|
||||||
fragment_result.depth orelse z,
|
fragment_result.depth orelse z,
|
||||||
|
fragment_result.sample_mask,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ mode: union(enum) {
|
|||||||
front_face: vk.FrontFace,
|
front_face: vk.FrontFace,
|
||||||
line_width: f32,
|
line_width: f32,
|
||||||
},
|
},
|
||||||
|
multisample: struct {
|
||||||
|
rasterization_samples: vk.SampleCountFlags,
|
||||||
|
sample_mask: ?[]vk.SampleMask,
|
||||||
|
},
|
||||||
color_blend: struct {
|
color_blend: struct {
|
||||||
attachments: ?[]vk.PipelineColorBlendAttachmentState,
|
attachments: ?[]vk.PipelineColorBlendAttachmentState,
|
||||||
constants: [4]f32,
|
constants: [4]f32,
|
||||||
@@ -106,6 +110,9 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
|||||||
var scissors: ?[]vk.Rect2D = null;
|
var scissors: ?[]vk.Rect2D = null;
|
||||||
errdefer if (scissors) |value| allocator.free(value);
|
errdefer if (scissors) |value| allocator.free(value);
|
||||||
|
|
||||||
|
var sample_mask: ?[]vk.SampleMask = null;
|
||||||
|
errdefer if (sample_mask) |value| allocator.free(value);
|
||||||
|
|
||||||
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
|
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
|
||||||
errdefer if (color_attachments) |value| allocator.free(value);
|
errdefer if (color_attachments) |value| allocator.free(value);
|
||||||
|
|
||||||
@@ -186,6 +193,24 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
|||||||
.front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,
|
.front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,
|
||||||
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
|
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
|
||||||
},
|
},
|
||||||
|
.multisample = blk: {
|
||||||
|
if (rasterizer_discard_enable) {
|
||||||
|
break :blk .{
|
||||||
|
.rasterization_samples = .{ .@"1_bit" = true },
|
||||||
|
.sample_mask = null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = info.p_multisample_state orelse return VkError.ValidationFailed;
|
||||||
|
const mask_word_count: usize = @divTrunc(state.rasterization_samples.toInt() + 31, 32);
|
||||||
|
break :blk .{
|
||||||
|
.rasterization_samples = state.rasterization_samples,
|
||||||
|
.sample_mask = if (state.p_sample_mask) |mask| blk_mask: {
|
||||||
|
sample_mask = allocator.dupe(vk.SampleMask, mask[0..mask_word_count]) catch return VkError.OutOfHostMemory;
|
||||||
|
break :blk_mask sample_mask;
|
||||||
|
} else null,
|
||||||
|
};
|
||||||
|
},
|
||||||
.color_blend = blk: {
|
.color_blend = blk: {
|
||||||
if (rasterizer_discard_enable or !has_color_attachments) {
|
if (rasterizer_discard_enable or !has_color_attachments) {
|
||||||
break :blk .{
|
break :blk .{
|
||||||
@@ -276,6 +301,9 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
|||||||
if (graphics.viewport_state.scissor) |scissor| {
|
if (graphics.viewport_state.scissor) |scissor| {
|
||||||
allocator.free(scissor);
|
allocator.free(scissor);
|
||||||
}
|
}
|
||||||
|
if (graphics.multisample.sample_mask) |sample_mask| {
|
||||||
|
allocator.free(sample_mask);
|
||||||
|
}
|
||||||
if (graphics.color_blend.attachments) |attachments| {
|
if (graphics.color_blend.attachments) |attachments| {
|
||||||
allocator.free(attachments);
|
allocator.free(attachments);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ const device_pfn_map = block: {
|
|||||||
functionMapEntryPoint("vkAcquireNextImage2KHR"),
|
functionMapEntryPoint("vkAcquireNextImage2KHR"),
|
||||||
functionMapEntryPoint("vkAllocateCommandBuffers"),
|
functionMapEntryPoint("vkAllocateCommandBuffers"),
|
||||||
functionMapEntryPoint("vkAllocateDescriptorSets"),
|
functionMapEntryPoint("vkAllocateDescriptorSets"),
|
||||||
functionMapEntryPoint("vkAllocateDescriptorSets"),
|
|
||||||
functionMapEntryPoint("vkAllocateMemory"),
|
functionMapEntryPoint("vkAllocateMemory"),
|
||||||
functionMapEntryPoint("vkBeginCommandBuffer"),
|
functionMapEntryPoint("vkBeginCommandBuffer"),
|
||||||
functionMapEntryPoint("vkBindBufferMemory"),
|
functionMapEntryPoint("vkBindBufferMemory"),
|
||||||
|
|||||||
Reference in New Issue
Block a user