794 lines
27 KiB
Zig
794 lines
27 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vulkan");
|
|
const base = @import("base");
|
|
const spv = @import("spv");
|
|
const zm = base.zm;
|
|
const blitter = @import("device/blitter.zig");
|
|
|
|
const VkError = base.VkError;
|
|
const F32x4 = zm.F32x4;
|
|
const U32x4 = blitter.U32x4;
|
|
|
|
const SoftImage = @import("SoftImage.zig");
|
|
const SoftImageView = @import("SoftImageView.zig");
|
|
|
|
const Self = @This();
|
|
pub const Interface = base.Sampler;
|
|
pub const ImageOffset = spv.Runtime.ImageOffset;
|
|
|
|
const CubeCoordinate = struct {
|
|
face: u32,
|
|
u: f32,
|
|
v: f32,
|
|
w: f32 = 0.0,
|
|
};
|
|
|
|
const ImageSamplingContext = struct {
|
|
image: *SoftImage,
|
|
image_view: *SoftImageView,
|
|
sampler: *Self,
|
|
dim: spv.SpvDim,
|
|
coord: CubeCoordinate,
|
|
mip_level: u32,
|
|
};
|
|
|
|
interface: Interface,
|
|
|
|
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.SamplerCreateInfo) VkError!*Self {
|
|
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
|
|
errdefer allocator.destroy(self);
|
|
|
|
var interface = try Interface.init(device, allocator, info);
|
|
|
|
interface.vtable = &.{
|
|
.destroy = destroy,
|
|
};
|
|
|
|
self.* = .{
|
|
.interface = interface,
|
|
};
|
|
return self;
|
|
}
|
|
|
|
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
|
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
|
allocator.destroy(self);
|
|
}
|
|
|
|
fn resolveCubeCoordinate(x: f32, y: f32, z: f32) CubeCoordinate {
|
|
const ax = @abs(x);
|
|
const ay = @abs(y);
|
|
const az = @abs(z);
|
|
|
|
var face: u32 = 0;
|
|
var sc: f32 = 0.0;
|
|
var tc: f32 = 0.0;
|
|
var ma: f32 = 1.0;
|
|
|
|
if (ax >= ay and ax >= az) {
|
|
ma = ax;
|
|
if (x >= 0.0) {
|
|
face = 0;
|
|
sc = -z;
|
|
tc = -y;
|
|
} else {
|
|
face = 1;
|
|
sc = z;
|
|
tc = -y;
|
|
}
|
|
} else if (ay >= ax and ay >= az) {
|
|
ma = ay;
|
|
if (y >= 0.0) {
|
|
face = 2;
|
|
sc = x;
|
|
tc = z;
|
|
} else {
|
|
face = 3;
|
|
sc = x;
|
|
tc = -z;
|
|
}
|
|
} else {
|
|
ma = az;
|
|
if (z >= 0.0) {
|
|
face = 4;
|
|
sc = x;
|
|
tc = -y;
|
|
} else {
|
|
face = 5;
|
|
sc = -x;
|
|
tc = -y;
|
|
}
|
|
}
|
|
|
|
const inv_ma = if (ma == 0.0) 0.0 else 1.0 / ma;
|
|
return .{
|
|
.face = face,
|
|
.u = (sc * inv_ma + 1.0) * 0.5,
|
|
.v = (tc * inv_ma + 1.0) * 0.5,
|
|
};
|
|
}
|
|
|
|
fn cubeDirection(face: u32, u: f32, v: f32) struct { x: f32, y: f32, z: f32 } {
|
|
const sc = u * 2.0 - 1.0;
|
|
const tc = v * 2.0 - 1.0;
|
|
|
|
return switch (face) {
|
|
0 => .{ .x = 1.0, .y = -tc, .z = -sc },
|
|
1 => .{ .x = -1.0, .y = -tc, .z = sc },
|
|
2 => .{ .x = sc, .y = 1.0, .z = tc },
|
|
3 => .{ .x = sc, .y = -1.0, .z = -tc },
|
|
4 => .{ .x = sc, .y = -tc, .z = 1.0 },
|
|
5 => .{ .x = -sc, .y = -tc, .z = -1.0 },
|
|
else => .{ .x = 0.0, .y = 0.0, .z = 0.0 },
|
|
};
|
|
}
|
|
|
|
inline fn sampleAddress(coord: i32, extent: u32, mode: vk.SamplerAddressMode) i32 {
|
|
return sampleAddressOrBorder(coord, extent, mode).?;
|
|
}
|
|
|
|
fn sampleAddressOrBorder(coord: i32, extent: u32, mode: vk.SamplerAddressMode) ?i32 {
|
|
const extent_i: i32 = @intCast(extent);
|
|
return switch (mode) {
|
|
.repeat => @mod(coord, extent_i),
|
|
.mirrored_repeat => blk: {
|
|
const period = extent_i * 2;
|
|
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),
|
|
};
|
|
}
|
|
|
|
fn samplerBorderColor(sampler: *Self, format: vk.Format) F32x4 {
|
|
var color: F32x4 = switch (sampler.interface.border_color) {
|
|
.float_opaque_white, .int_opaque_white => .{ 1.0, 1.0, 1.0, 1.0 },
|
|
.float_opaque_black, .int_opaque_black => .{ 0.0, 0.0, 0.0, 1.0 },
|
|
else => .{ 0.0, 0.0, 0.0, 0.0 },
|
|
};
|
|
|
|
switch (base.format.componentCount(format)) {
|
|
1 => {
|
|
color[1] = 0.0;
|
|
color[2] = 0.0;
|
|
color[3] = 1.0;
|
|
},
|
|
2 => {
|
|
color[2] = 0.0;
|
|
color[3] = 1.0;
|
|
},
|
|
3 => color[3] = 1.0,
|
|
else => {},
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
fn samplerBorderColorInt(sampler: *Self, format: vk.Format) U32x4 {
|
|
var color: U32x4 = switch (sampler.interface.border_color) {
|
|
.float_opaque_white, .int_opaque_white => .{ 1, 1, 1, 1 },
|
|
.float_opaque_black, .int_opaque_black => .{ 0, 0, 0, 1 },
|
|
else => .{ 0, 0, 0, 0 },
|
|
};
|
|
|
|
switch (base.format.componentCount(format)) {
|
|
1 => {
|
|
color[1] = 0;
|
|
color[2] = 0;
|
|
color[3] = 1;
|
|
},
|
|
2 => {
|
|
color[2] = 0;
|
|
color[3] = 1;
|
|
},
|
|
3 => color[3] = 1,
|
|
else => {},
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
fn viewLayerCount(image_view: *SoftImageView) u32 {
|
|
return image_view.interface.layerCount();
|
|
}
|
|
|
|
fn viewMipCount(image_view: *SoftImageView) u32 {
|
|
return image_view.interface.levelCount();
|
|
}
|
|
|
|
fn sampleLod(image_view: *SoftImageView, sampler: *Self, lod: ?f32) f32 {
|
|
const mip_count = viewMipCount(image_view);
|
|
if (mip_count <= 1)
|
|
return 0.0;
|
|
|
|
const clamped_lod = filterLod(sampler, lod);
|
|
const max_level: f32 = @floatFromInt(mip_count - 1);
|
|
return std.math.clamp(clamped_lod, 0.0, max_level);
|
|
}
|
|
|
|
fn filterLod(sampler: *Self, lod: ?f32) f32 {
|
|
const requested_lod = if (lod) |explicit_lod|
|
|
explicit_lod + sampler.interface.mip_lod_bias
|
|
else
|
|
sampler.interface.min_lod;
|
|
return std.math.clamp(requested_lod, sampler.interface.min_lod, sampler.interface.max_lod);
|
|
}
|
|
|
|
fn sampleMipLevel(image_view: *SoftImageView, sampler: *Self, lod: ?f32) u32 {
|
|
const range = image_view.interface.subresource_range;
|
|
const mip_count = viewMipCount(image_view);
|
|
if (mip_count <= 1)
|
|
return range.base_mip_level;
|
|
|
|
const clamped_lod = sampleLod(image_view, sampler, lod);
|
|
const level_float = switch (sampler.interface.mipmap_mode) {
|
|
.nearest => @round(clamped_lod),
|
|
else => @floor(clamped_lod),
|
|
};
|
|
const level: u32 = @intFromFloat(level_float);
|
|
return range.base_mip_level + level;
|
|
}
|
|
|
|
fn sampleFilter(sampler: *Self, lod: f32) vk.Filter {
|
|
const filter = if (lod <= 0.0) sampler.interface.mag_filter else sampler.interface.min_filter;
|
|
return switch (filter) {
|
|
.linear => .linear,
|
|
else => .nearest,
|
|
};
|
|
}
|
|
|
|
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", .Rect => @sqrt(std.math.pow(f32, derivatives.dx.x * width, 2.0) + std.math.pow(f32, derivatives.dx.y * height, 2.0)),
|
|
.Cube => @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 * width, 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", .Rect => @sqrt(std.math.pow(f32, derivatives.dy.x * width, 2.0) + std.math.pow(f32, derivatives.dy.y * height, 2.0)),
|
|
.Cube => @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 * width, 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_view) - 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));
|
|
}
|
|
|
|
fn sampledFormat(image_view: *SoftImageView) vk.Format {
|
|
const range = image_view.interface.subresource_range;
|
|
return base.format.fromAspect(image_view.interface.format, range.aspect_mask);
|
|
}
|
|
|
|
fn swizzleFloatComponent(color: F32x4, swizzle: vk.ComponentSwizzle, comptime identity_index: usize) f32 {
|
|
return switch (swizzle) {
|
|
.identity => color[identity_index],
|
|
.zero => 0.0,
|
|
.one => 1.0,
|
|
.r => color[0],
|
|
.g => color[1],
|
|
.b => color[2],
|
|
.a => color[3],
|
|
else => color[identity_index],
|
|
};
|
|
}
|
|
|
|
pub fn swizzleFloat4(color: F32x4, components: vk.ComponentMapping) F32x4 {
|
|
return .{
|
|
swizzleFloatComponent(color, components.r, 0),
|
|
swizzleFloatComponent(color, components.g, 1),
|
|
swizzleFloatComponent(color, components.b, 2),
|
|
swizzleFloatComponent(color, components.a, 3),
|
|
};
|
|
}
|
|
|
|
fn swizzleIntComponent(color: U32x4, swizzle: vk.ComponentSwizzle, comptime identity_index: usize) u32 {
|
|
return switch (swizzle) {
|
|
.identity => color[identity_index],
|
|
.zero => 0,
|
|
.one => 1,
|
|
.r => color[0],
|
|
.g => color[1],
|
|
.b => color[2],
|
|
.a => color[3],
|
|
else => color[identity_index],
|
|
};
|
|
}
|
|
|
|
pub fn swizzleInt4(color: U32x4, components: vk.ComponentMapping) U32x4 {
|
|
return .{
|
|
swizzleIntComponent(color, components.r, 0),
|
|
swizzleIntComponent(color, components.g, 1),
|
|
swizzleIntComponent(color, components.b, 2),
|
|
swizzleIntComponent(color, components.a, 3),
|
|
};
|
|
}
|
|
|
|
fn compareDepth(op: vk.CompareOp, reference: f32, value: f32) bool {
|
|
return switch (op) {
|
|
.never => false,
|
|
.less => reference < value,
|
|
.equal => reference == value,
|
|
.less_or_equal => reference <= value,
|
|
.greater => reference > value,
|
|
.not_equal => reference != value,
|
|
.greater_or_equal => reference >= value,
|
|
.always => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
fn readSampledFloat4(
|
|
image: *SoftImage,
|
|
image_view: *SoftImageView,
|
|
sampler: *Self,
|
|
dim: spv.SpvDim,
|
|
coord: CubeCoordinate,
|
|
mip_level: u32,
|
|
ix: i32,
|
|
iy: i32,
|
|
iz: i32,
|
|
) VkError!F32x4 {
|
|
const range = image_view.interface.subresource_range;
|
|
const format = sampledFormat(image_view);
|
|
const extent = image.getMipLevelExtent(mip_level);
|
|
const width_f: f32 = @floatFromInt(extent.width);
|
|
const height_f: f32 = @floatFromInt(extent.height);
|
|
|
|
const texel = if (dim == .Cube) blk: {
|
|
const dir = cubeDirection(
|
|
coord.face,
|
|
(@as(f32, @floatFromInt(ix)) + 0.5) / width_f,
|
|
(@as(f32, @floatFromInt(iy)) + 0.5) / height_f,
|
|
);
|
|
break :blk resolveCubeCoordinate(dir.x, dir.y, dir.z);
|
|
} else coord;
|
|
|
|
const z: i32, const layer: u32 = switch (image_view.interface.view_type) {
|
|
.@"1d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.v, viewLayerCount(image_view)) },
|
|
.@"2d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, viewLayerCount(image_view)) },
|
|
.cube_array => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, @divTrunc(viewLayerCount(image_view), 6)) * 6 + texel.face },
|
|
.@"3d" => .{ sampleAddressOrBorder(iz, extent.depth, sampler.interface.address_mode_w) orelse return samplerBorderColor(sampler, format), range.base_array_layer },
|
|
.cube => .{ 0, range.base_array_layer + texel.face },
|
|
else => .{ 0, range.base_array_layer },
|
|
};
|
|
|
|
const sx = if (dim == .Cube)
|
|
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, @as(i32, @intCast(extent.width)) - 1)
|
|
else
|
|
sampleAddressOrBorder(ix, extent.width, sampler.interface.address_mode_u) orelse return samplerBorderColor(sampler, format);
|
|
const sy = switch (image_view.interface.view_type) {
|
|
.@"1d", .@"1d_array" => 0,
|
|
else => if (dim == .Cube)
|
|
std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, @as(i32, @intCast(extent.height)) - 1)
|
|
else
|
|
sampleAddressOrBorder(iy, extent.height, sampler.interface.address_mode_v) orelse return samplerBorderColor(sampler, format),
|
|
};
|
|
|
|
const color = try image.readFloat4(
|
|
.{
|
|
.x = sx,
|
|
.y = sy,
|
|
.z = z,
|
|
},
|
|
.{
|
|
.aspect_mask = range.aspect_mask,
|
|
.mip_level = mip_level,
|
|
.array_layer = layer,
|
|
},
|
|
format,
|
|
);
|
|
return if (base.format.isSrgb(format)) zm.srgbToRgb(color) else color;
|
|
}
|
|
|
|
fn readSampledFloat4At(context: *const ImageSamplingContext, ix: i32, iy: i32, iz: i32) VkError!F32x4 {
|
|
const color = try readSampledFloat4(
|
|
context.image,
|
|
context.image_view,
|
|
context.sampler,
|
|
context.dim,
|
|
context.coord,
|
|
context.mip_level,
|
|
ix,
|
|
iy,
|
|
iz,
|
|
);
|
|
return swizzleFloat4(color, context.image_view.interface.components);
|
|
}
|
|
|
|
const DepthCompareSamplingContext = struct {
|
|
image_context: ImageSamplingContext,
|
|
dref: f32,
|
|
};
|
|
|
|
fn readDepthCompareAt(context: *const DepthCompareSamplingContext, ix: i32, iy: i32, iz: i32) VkError!F32x4 {
|
|
const color = try readSampledFloat4At(&context.image_context, ix, iy, iz);
|
|
const result: f32 = if (compareDepth(context.image_context.sampler.interface.compare_op, context.dref, color[0])) 1.0 else 0.0;
|
|
return zm.f32x4s(result);
|
|
}
|
|
|
|
fn readSampledInt4(
|
|
image: *SoftImage,
|
|
image_view: *SoftImageView,
|
|
sampler: *Self,
|
|
dim: spv.SpvDim,
|
|
coord: CubeCoordinate,
|
|
mip_level: u32,
|
|
ix: i32,
|
|
iy: i32,
|
|
iz: i32,
|
|
) VkError!U32x4 {
|
|
const range = image_view.interface.subresource_range;
|
|
const format = sampledFormat(image_view);
|
|
const extent = image.getMipLevelExtent(mip_level);
|
|
const width_f: f32 = @floatFromInt(extent.width);
|
|
const height_f: f32 = @floatFromInt(extent.height);
|
|
|
|
const texel = if (dim == .Cube) blk: {
|
|
const dir = cubeDirection(
|
|
coord.face,
|
|
(@as(f32, @floatFromInt(ix)) + 0.5) / width_f,
|
|
(@as(f32, @floatFromInt(iy)) + 0.5) / height_f,
|
|
);
|
|
break :blk resolveCubeCoordinate(dir.x, dir.y, dir.z);
|
|
} else coord;
|
|
|
|
const z: i32, const layer: u32 = switch (image_view.interface.view_type) {
|
|
.@"1d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.v, viewLayerCount(image_view)) },
|
|
.@"2d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, viewLayerCount(image_view)) },
|
|
.cube_array => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, @divTrunc(viewLayerCount(image_view), 6)) * 6 + texel.face },
|
|
.@"3d" => .{ sampleAddressOrBorder(iz, extent.depth, sampler.interface.address_mode_w) orelse return samplerBorderColorInt(sampler, format), range.base_array_layer },
|
|
.cube => .{ 0, range.base_array_layer + texel.face },
|
|
else => .{ 0, range.base_array_layer },
|
|
};
|
|
|
|
const sx = if (dim == .Cube)
|
|
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, @as(i32, @intCast(extent.width)) - 1)
|
|
else
|
|
sampleAddressOrBorder(ix, extent.width, sampler.interface.address_mode_u) orelse return samplerBorderColorInt(sampler, format);
|
|
const sy = switch (image_view.interface.view_type) {
|
|
.@"1d", .@"1d_array" => 0,
|
|
else => if (dim == .Cube)
|
|
std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, @as(i32, @intCast(extent.height)) - 1)
|
|
else
|
|
sampleAddressOrBorder(iy, extent.height, sampler.interface.address_mode_v) orelse return samplerBorderColorInt(sampler, format),
|
|
};
|
|
|
|
return image.readInt4(
|
|
.{
|
|
.x = sx,
|
|
.y = sy,
|
|
.z = z,
|
|
},
|
|
.{
|
|
.aspect_mask = range.aspect_mask,
|
|
.mip_level = mip_level,
|
|
.array_layer = layer,
|
|
},
|
|
format,
|
|
);
|
|
}
|
|
|
|
fn sampleImageFloat4Level(
|
|
image: *SoftImage,
|
|
image_view: *SoftImageView,
|
|
sampler: *Self,
|
|
dim: spv.SpvDim,
|
|
x: f32,
|
|
y: f32,
|
|
z: f32,
|
|
mip_level: u32,
|
|
filter: vk.Filter,
|
|
offset: ImageOffset,
|
|
) VkError!F32x4 {
|
|
const extent = image.getMipLevelExtent(mip_level);
|
|
const coord: CubeCoordinate = switch (image_view.interface.view_type) {
|
|
.@"1d_array" => .{
|
|
.u = x,
|
|
.v = y,
|
|
.face = 0,
|
|
},
|
|
.@"1d" => .{
|
|
.u = x,
|
|
.v = 0.0,
|
|
.face = 0,
|
|
},
|
|
.@"2d_array" => .{
|
|
.u = x,
|
|
.v = y,
|
|
.w = z,
|
|
.face = 0,
|
|
},
|
|
.cube, .cube_array => resolveCubeCoordinate(x, y, z),
|
|
else => .{
|
|
.u = x,
|
|
.v = y,
|
|
.w = z,
|
|
.face = 0,
|
|
},
|
|
};
|
|
const scale_u: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.width);
|
|
const scale_v: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.height);
|
|
const scale_w: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.depth);
|
|
const context: ImageSamplingContext = .{
|
|
.image = image,
|
|
.image_view = image_view,
|
|
.sampler = sampler,
|
|
.dim = dim,
|
|
.coord = coord,
|
|
.mip_level = mip_level,
|
|
};
|
|
|
|
return sampleFloat4(
|
|
*const ImageSamplingContext,
|
|
&context,
|
|
zm.f32x4(
|
|
coord.u * scale_u + @as(f32, @floatFromInt(offset.x)),
|
|
coord.v * scale_v + @as(f32, @floatFromInt(offset.y)),
|
|
coord.w * scale_w + @as(f32, @floatFromInt(offset.z)),
|
|
0.0,
|
|
),
|
|
filter,
|
|
image_view.interface.view_type == .@"3d",
|
|
readSampledFloat4At,
|
|
);
|
|
}
|
|
|
|
pub fn sampleImageFloat4(
|
|
image: *SoftImage,
|
|
image_view: *SoftImageView,
|
|
sampler: *Self,
|
|
dim: spv.SpvDim,
|
|
x: f32,
|
|
y: f32,
|
|
z: f32,
|
|
lod: ?f32,
|
|
offset: ImageOffset,
|
|
) VkError!F32x4 {
|
|
const range = image_view.interface.subresource_range;
|
|
const mip_count = viewMipCount(image_view);
|
|
const clamped_lod = sampleLod(image_view, sampler, lod);
|
|
const filter = sampleFilter(sampler, filterLod(sampler, lod));
|
|
|
|
if (mip_count > 1 and sampler.interface.mipmap_mode == .linear) {
|
|
const lower_lod = @floor(clamped_lod);
|
|
const upper_lod = @min(lower_lod + 1.0, @as(f32, @floatFromInt(mip_count - 1)));
|
|
const lower_level = range.base_mip_level + @as(u32, @intFromFloat(lower_lod));
|
|
const upper_level = range.base_mip_level + @as(u32, @intFromFloat(upper_lod));
|
|
const lower = try sampleImageFloat4Level(image, image_view, sampler, dim, x, y, z, lower_level, filter, offset);
|
|
|
|
if (upper_level == lower_level)
|
|
return lower;
|
|
|
|
const upper = try sampleImageFloat4Level(image, image_view, sampler, dim, x, y, z, upper_level, filter, offset);
|
|
const weight = clamped_lod - lower_lod;
|
|
return lower * zm.f32x4s(1.0 - weight) + upper * zm.f32x4s(weight);
|
|
}
|
|
|
|
return sampleImageFloat4Level(image, image_view, sampler, dim, x, y, z, sampleMipLevel(image_view, sampler, lod), filter, offset);
|
|
}
|
|
|
|
fn sampleImageDrefLevel(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, w: f32, dref: f32, mip_level: u32, filter: vk.Filter, offset: ImageOffset) VkError!f32 {
|
|
const extent = image.getMipLevelExtent(mip_level);
|
|
const coord: CubeCoordinate = switch (image_view.interface.view_type) {
|
|
.@"1d_array" => .{
|
|
.u = x,
|
|
.v = y,
|
|
.face = 0,
|
|
},
|
|
.@"1d" => .{
|
|
.u = x,
|
|
.v = 0.0,
|
|
.face = 0,
|
|
},
|
|
.@"2d_array" => .{
|
|
.u = x,
|
|
.v = y,
|
|
.w = z,
|
|
.face = 0,
|
|
},
|
|
.cube => resolveCubeCoordinate(x, y, z),
|
|
.cube_array => blk: {
|
|
var coord = resolveCubeCoordinate(x, y, z);
|
|
coord.w = w;
|
|
break :blk coord;
|
|
},
|
|
else => .{
|
|
.u = x,
|
|
.v = y,
|
|
.w = z,
|
|
.face = 0,
|
|
},
|
|
};
|
|
const scale_u: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.width);
|
|
const scale_v: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.height);
|
|
const scale_w: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.depth);
|
|
const image_context: ImageSamplingContext = .{
|
|
.image = image,
|
|
.image_view = image_view,
|
|
.sampler = sampler,
|
|
.dim = dim,
|
|
.coord = coord,
|
|
.mip_level = mip_level,
|
|
};
|
|
const context: DepthCompareSamplingContext = .{
|
|
.image_context = image_context,
|
|
.dref = dref,
|
|
};
|
|
|
|
const result = try sampleFloat4(
|
|
*const DepthCompareSamplingContext,
|
|
&context,
|
|
zm.f32x4(
|
|
coord.u * scale_u + @as(f32, @floatFromInt(offset.x)),
|
|
coord.v * scale_v + @as(f32, @floatFromInt(offset.y)),
|
|
coord.w * scale_w + @as(f32, @floatFromInt(offset.z)),
|
|
0.0,
|
|
),
|
|
filter,
|
|
image_view.interface.view_type == .@"3d",
|
|
readDepthCompareAt,
|
|
);
|
|
return result[0];
|
|
}
|
|
|
|
pub fn sampleImageInt4(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: ImageOffset) VkError!U32x4 {
|
|
const mip_level = sampleMipLevel(image_view, sampler, lod);
|
|
const extent = image.getMipLevelExtent(mip_level);
|
|
const coord: CubeCoordinate = switch (image_view.interface.view_type) {
|
|
.@"1d_array" => .{
|
|
.u = x,
|
|
.v = y,
|
|
.face = 0,
|
|
},
|
|
.@"1d" => .{
|
|
.u = x,
|
|
.v = 0.0,
|
|
.face = 0,
|
|
},
|
|
.@"2d_array" => .{
|
|
.u = x,
|
|
.v = y,
|
|
.w = z,
|
|
.face = 0,
|
|
},
|
|
.cube, .cube_array => resolveCubeCoordinate(x, y, z),
|
|
else => .{
|
|
.u = x,
|
|
.v = y,
|
|
.w = z,
|
|
.face = 0,
|
|
},
|
|
};
|
|
const scale_u: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.width);
|
|
const scale_v: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.height);
|
|
const scale_w: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.depth);
|
|
|
|
const ix = @as(i32, @intFromFloat(@floor(coord.u * scale_u))) + offset.x;
|
|
const iy = @as(i32, @intFromFloat(@floor(coord.v * scale_v))) + offset.y;
|
|
const iz = @as(i32, @intFromFloat(@floor(coord.w * scale_w))) + offset.z;
|
|
const color = try readSampledInt4(
|
|
image,
|
|
image_view,
|
|
sampler,
|
|
dim,
|
|
coord,
|
|
mip_level,
|
|
ix,
|
|
iy,
|
|
iz,
|
|
);
|
|
return swizzleInt4(color, image_view.interface.components);
|
|
}
|
|
|
|
pub fn sampleImageDref(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, w: f32, dref: f32, lod: ?f32, offset: ImageOffset) VkError!f32 {
|
|
if (sampler.interface.compare_enable == .false) {
|
|
const color = try sampleImageFloat4(image, image_view, sampler, dim, x, y, z, lod, offset);
|
|
return color[0];
|
|
}
|
|
|
|
const range = image_view.interface.subresource_range;
|
|
const mip_count = viewMipCount(image_view);
|
|
const clamped_lod = sampleLod(image_view, sampler, lod);
|
|
const filter = sampleFilter(sampler, filterLod(sampler, lod));
|
|
|
|
if (mip_count > 1 and sampler.interface.mipmap_mode == .linear) {
|
|
const lower_lod = @floor(clamped_lod);
|
|
const upper_lod = @min(lower_lod + 1.0, @as(f32, @floatFromInt(mip_count - 1)));
|
|
const lower_level = range.base_mip_level + @as(u32, @intFromFloat(lower_lod));
|
|
const upper_level = range.base_mip_level + @as(u32, @intFromFloat(upper_lod));
|
|
const lower = try sampleImageDrefLevel(image, image_view, sampler, dim, x, y, z, w, dref, lower_level, filter, offset);
|
|
|
|
if (upper_level == lower_level)
|
|
return lower;
|
|
|
|
const upper = try sampleImageDrefLevel(image, image_view, sampler, dim, x, y, z, w, dref, upper_level, filter, offset);
|
|
const weight = clamped_lod - lower_lod;
|
|
return lower * (1.0 - weight) + upper * weight;
|
|
}
|
|
|
|
return sampleImageDrefLevel(image, image_view, sampler, dim, x, y, z, w, dref, sampleMipLevel(image_view, sampler, lod), filter, offset);
|
|
}
|
|
|
|
pub fn sampleFloat4(
|
|
comptime Context: type,
|
|
context: Context,
|
|
pos: F32x4,
|
|
filter: vk.Filter,
|
|
filter_3D: bool,
|
|
comptime read: fn (Context, i32, i32, i32) VkError!F32x4,
|
|
) VkError!F32x4 {
|
|
if (filter == .nearest) {
|
|
return read(
|
|
context,
|
|
@intFromFloat(@floor(pos[0])),
|
|
@intFromFloat(@floor(pos[1])),
|
|
@intFromFloat(@floor(pos[2])),
|
|
);
|
|
}
|
|
|
|
const x = pos[0] - 0.5;
|
|
const y = pos[1] - 0.5;
|
|
const z = pos[2] - 0.5;
|
|
const x0: i32 = @intFromFloat(@floor(x));
|
|
const y0: i32 = @intFromFloat(@floor(y));
|
|
const z0: i32 = @intFromFloat(@floor(z));
|
|
const x1 = x0 + 1;
|
|
const y1 = y0 + 1;
|
|
const z1 = z0 + 1;
|
|
const wx = x - @as(f32, @floatFromInt(x0));
|
|
const wy = y - @as(f32, @floatFromInt(y0));
|
|
const wz = z - @as(f32, @floatFromInt(z0));
|
|
|
|
const p000 = try read(context, x0, y0, z0);
|
|
const p100 = try read(context, x1, y0, z0);
|
|
const p010 = try read(context, x0, y1, z0);
|
|
const p110 = try read(context, x1, y1, z0);
|
|
|
|
const row00 = p000 * zm.f32x4s(1.0 - wx) + p100 * zm.f32x4s(wx);
|
|
const row10 = p010 * zm.f32x4s(1.0 - wx) + p110 * zm.f32x4s(wx);
|
|
const slice0 = row00 * zm.f32x4s(1.0 - wy) + row10 * zm.f32x4s(wy);
|
|
|
|
if (!filter_3D)
|
|
return slice0;
|
|
|
|
const p001 = try read(context, x0, y0, z1);
|
|
const p101 = try read(context, x1, y0, z1);
|
|
const p011 = try read(context, x0, y1, z1);
|
|
const p111 = try read(context, x1, y1, z1);
|
|
|
|
const row01 = p001 * zm.f32x4s(1.0 - wx) + p101 * zm.f32x4s(wx);
|
|
const row11 = p011 * zm.f32x4s(1.0 - wx) + p111 * zm.f32x4s(wx);
|
|
const slice1 = row01 * zm.f32x4s(1.0 - wy) + row11 * zm.f32x4s(wy);
|
|
|
|
return slice0 * zm.f32x4s(1.0 - wz) + slice1 * zm.f32x4s(wz);
|
|
}
|