From df2ba1f7afec04e5927d4e4e945eddeb5a3634e8 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Sun, 14 Jun 2026 22:54:20 +0200 Subject: [PATCH] adding more image queries --- build.zig.zon | 4 +- src/soft/SoftPipeline.zig | 107 ++++++++++++++++++++++++++++++-------- src/soft/SoftSampler.zig | 39 ++++++++++++++ 3 files changed, 127 insertions(+), 23 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 6a4f4ad..a259fb2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -26,8 +26,8 @@ .hash = "N-V-__8AAF9uOh0I4P_99za7N822J3JwsDaqONrFVrcEQo59", }, .SPIRV_Interpreter = .{ - .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#5c1eb25b4835f4161dd49cd000b19ae1cca6e80a", - .hash = "SPIRV_Interpreter-0.0.1-ajmpn8mDBwChlKPOGs3EkJl6RiTJYDcvpLbsIJaHBAOe", + .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#52ded6b490a47d843ad401cbd2c41b4dbdc7dca1", + .hash = "SPIRV_Interpreter-0.0.1-ajmpn0GXBwD01BnmV_Kf8EeNqTDoyuq7UvHVaq-WoBP0", .lazy = true, }, //.SPIRV_Interpreter = .{ diff --git a/src/soft/SoftPipeline.zig b/src/soft/SoftPipeline.zig index cdc6f58..a865932 100644 --- a/src/soft/SoftPipeline.zig +++ b/src/soft/SoftPipeline.zig @@ -288,10 +288,23 @@ fn imageApi() spv.Runtime.ImageAPI { .sampleImageInt4 = sampleImageInt4, .sampleImageDref = sampleImageDref, .queryImageSize = queryImageSize, + .queryImageLevels = queryImageLevels, + .queryImageSamples = queryImageSamples, + .queryImageLod = queryImageLod, }; } -fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) SpvRuntimeError!spv.Runtime.Vec4(f32) { +fn imageMipLevel(image: *SoftImage, image_view: *SoftImageView, lod: ?i32) u32 { + const range = image_view.interface.subresource_range; + const mip_lod: u32 = if (lod) |level| @intCast(@max(level, 0)) else 0; + const max_lod = if (range.level_count == vk.REMAINING_MIP_LEVELS) + image.interface.mip_levels - range.base_mip_level - 1 + else + range.level_count - 1; + return range.base_mip_level + @min(mip_lod, max_lod); +} + +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)); @@ -302,16 +315,24 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0; + const mip_level = imageMipLevel(image, image_view, lod); + const image_coord: vk.Offset3D = switch (image_view.interface.view_type) { + .@"1d", .@"1d_array" => .{ .x = x, .y = 0, .z = 0 }, + .@"2d", .@"2d_array", .cube, .cube_array => .{ .x = x, .y = y, .z = 0 }, + else => .{ .x = x, .y = y, .z = z }, + }; + const array_layer = image_view.interface.subresource_range.base_array_layer + switch (image_view.interface.view_type) { + .@"1d_array" => @as(u32, @intCast(y)), + .@"2d_array" => @as(u32, @intCast(z)), + .cube => cube_face, + else => 0, + }; pixel = image.readFloat4( - .{ - .x = x, - .y = y, - .z = if (dim == .Cube) 0 else z, - }, + image_coord, .{ .aspect_mask = image_view.interface.subresource_range.aspect_mask, - .mip_level = image_view.interface.subresource_range.base_mip_level, - .array_layer = image_view.interface.subresource_range.base_array_layer + cube_face, + .mip_level = mip_level, + .array_layer = array_layer, }, image_view.interface.format, ) catch return SpvRuntimeError.Unknown; @@ -324,7 +345,7 @@ 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) SpvRuntimeError!spv.Runtime.Vec4(u32) { +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)); @@ -335,16 +356,24 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) S const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0; + const mip_level = imageMipLevel(image, image_view, lod); + const image_coord: vk.Offset3D = switch (image_view.interface.view_type) { + .@"1d", .@"1d_array" => .{ .x = x, .y = 0, .z = 0 }, + .@"2d", .@"2d_array", .cube, .cube_array => .{ .x = x, .y = y, .z = 0 }, + else => .{ .x = x, .y = y, .z = z }, + }; + const array_layer = image_view.interface.subresource_range.base_array_layer + switch (image_view.interface.view_type) { + .@"1d_array" => @as(u32, @intCast(y)), + .@"2d_array" => @as(u32, @intCast(z)), + .cube => cube_face, + else => 0, + }; pixel = image.readInt4( - .{ - .x = x, - .y = y, - .z = if (dim == .Cube) 0 else z, - }, + image_coord, .{ .aspect_mask = image_view.interface.subresource_range.aspect_mask, - .mip_level = image_view.interface.subresource_range.base_mip_level, - .array_layer = image_view.interface.subresource_range.base_array_layer + cube_face, + .mip_level = mip_level, + .array_layer = array_layer, }, image_view.interface.format, ) catch return SpvRuntimeError.Unknown; @@ -471,7 +500,7 @@ fn sampleImageDref(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x return SoftSampler.sampleImageDref(image, image_view, sampler, dim, x, y, z, dref, lod, offset) catch return SpvRuntimeError.Unknown; } -fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool) SpvRuntimeError!spv.Runtime.Vec4(u32) { +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) @@ -488,11 +517,18 @@ fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool) SpvRuntim const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); - const extent = image.getMipLevelExtent(image_view.interface.subresource_range.base_mip_level); - const layers = if (image_view.interface.subresource_range.layer_count == vk.REMAINING_ARRAY_LAYERS) - image.interface.array_layers - image_view.interface.subresource_range.base_array_layer + const range = image_view.interface.subresource_range; + const mip_lod: u32 = if (lod) |level| @intCast(@max(level, 0)) else 0; + const max_lod = if (range.level_count == vk.REMAINING_MIP_LEVELS) + image.interface.mip_levels - range.base_mip_level - 1 else - image_view.interface.subresource_range.layer_count; + range.level_count - 1; + const mip_level = range.base_mip_level + @min(mip_lod, max_lod); + const extent = image.getMipLevelExtent(mip_level); + const layers = if (range.layer_count == vk.REMAINING_ARRAY_LAYERS) + image.interface.array_layers - range.base_array_layer + else + range.layer_count; return switch (dim) { .@"1D" => if (arrayed) .{ .x = extent.width, .y = layers, .z = 0, .w = 0 } @@ -506,3 +542,32 @@ fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool) SpvRuntim else => .{ .x = extent.width, .y = extent.height, .z = layers, .w = 0 }, }; } + +fn queryImageSamples(context: *anyopaque) SpvRuntimeError!u32 { + const image_view: *SoftImageView = @ptrCast(@alignCast(context)); + const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); + return @intCast(image.interface.samples.toInt()); +} + +fn queryImageLevels(context: *anyopaque) SpvRuntimeError!u32 { + const image_view: *SoftImageView = @ptrCast(@alignCast(context)); + const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); + const range = image_view.interface.subresource_range; + return if (range.level_count == vk.REMAINING_MIP_LEVELS) + image.interface.mip_levels - range.base_mip_level + else + range.level_count; +} + +fn queryImageLod(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, derivatives: spv.Runtime.ImageDerivatives) SpvRuntimeError!spv.Runtime.Vec4(f32) { + const image_view: *SoftImageView = @ptrCast(@alignCast(context)); + const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); + const sampler: *SoftSampler = @ptrCast(@alignCast(context2)); + const lod = SoftSampler.queryImageLod(image, image_view, sampler, dim, derivatives); + return .{ + .x = lod[0], + .y = lod[1], + .z = 0.0, + .w = 0.0, + }; +} diff --git a/src/soft/SoftSampler.zig b/src/soft/SoftSampler.zig index 8aabc5f..7901b8e 100644 --- a/src/soft/SoftSampler.zig +++ b/src/soft/SoftSampler.zig @@ -137,6 +137,7 @@ fn sampleAddressOrBorder(coord: i32, extent: u32, mode: vk.SamplerAddressMode) ? const mirrored = @mod(coord, period); break :blk if (mirrored < extent_i) mirrored else period - mirrored - 1; }, + .mirror_clamp_to_edge => std.math.clamp(if (coord < 0) -coord - 1 else coord, 0, extent_i - 1), .clamp_to_border => if (coord < 0 or coord >= extent_i) null else coord, else => std.math.clamp(coord, 0, extent_i - 1), }; @@ -223,6 +224,44 @@ fn sampleMipLevel(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, return range.base_mip_level + level; } +fn mipmapModeLevel(sampler: *Self, clamped_lod: f32) f32 { + return switch (sampler.interface.mipmap_mode) { + .nearest => @round(clamped_lod), + .linear => clamped_lod, + else => @floor(clamped_lod), + }; +} + +pub fn queryImageLod(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, derivatives: spv.Runtime.ImageDerivatives) F32x4 { + const range = image_view.interface.subresource_range; + const extent = image.getMipLevelExtent(range.base_mip_level); + const width: f32 = @floatFromInt(extent.width); + const height: f32 = @floatFromInt(extent.height); + const depth: f32 = @floatFromInt(extent.depth); + + const dx = switch (dim) { + .@"1D" => @abs(derivatives.dx.x) * width, + .@"2D", .Cube, .Rect => @sqrt(std.math.pow(f32, derivatives.dx.x * width, 2.0) + std.math.pow(f32, derivatives.dx.y * height, 2.0)), + .@"3D" => @sqrt(std.math.pow(f32, derivatives.dx.x * width, 2.0) + std.math.pow(f32, derivatives.dx.y * height, 2.0) + std.math.pow(f32, derivatives.dx.z * depth, 2.0)), + else => @abs(derivatives.dx.x) * width, + }; + const dy = switch (dim) { + .@"1D" => @abs(derivatives.dy.x) * width, + .@"2D", .Cube, .Rect => @sqrt(std.math.pow(f32, derivatives.dy.x * width, 2.0) + std.math.pow(f32, derivatives.dy.y * height, 2.0)), + .@"3D" => @sqrt(std.math.pow(f32, derivatives.dy.x * width, 2.0) + std.math.pow(f32, derivatives.dy.y * height, 2.0) + std.math.pow(f32, derivatives.dy.z * depth, 2.0)), + else => @abs(derivatives.dy.x) * width, + }; + + const rho = @max(dx, dy); + const lod = if (rho > 0.0) @log2(rho) else -std.math.inf(f32); + const biased_lod = lod + sampler.interface.mip_lod_bias; + const clamped_lod = std.math.clamp(biased_lod, sampler.interface.min_lod, sampler.interface.max_lod); + const max_level: f32 = @floatFromInt(viewMipCount(image, range) - 1); + const level = std.math.clamp(mipmapModeLevel(sampler, clamped_lod), 0.0, max_level); + + return .{ level, lod, 0.0, 0.0 }; +} + fn sampleArrayLayer(coord: f32, layer_count: u32) u32 { const layer_coord: i32 = @intFromFloat(@floor(coord + 0.5)); return @intCast(sampleAddress(layer_coord, layer_count, .clamp_to_edge));