shader: apply the LOD clamp of image_sample _cl variants

Co-authored-by: Senaxx <michelbeld@gmail.com>
This commit is contained in:
nmzikandSenaxx committed 2026-10-26 05:32:02 +01:00
1 parent b4209dde1b
commit 4ebae87a1b
5 files changed
+70 -3

No files matched your search

@@ -337,6 +337,7 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
check_feature(device_features2.features.shaderImageGatherExtended,
"shaderImageGatherExtended");
check_feature(device_features2.features.shaderResourceMinLod, "shaderResourceMinLod");
check_feature(device_features2.features.independentBlend, "independentBlend");
check_feature(device_features2.features.tessellationShader, "tessellationShader");
@@ -636,6 +637,7 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
device_features.shaderStorageImageMultisample =
supported_features2.features.shaderStorageImageMultisample;
device_features.shaderImageGatherExtended = VK_TRUE;
device_features.shaderResourceMinLod = VK_TRUE;
device_features.independentBlend = VK_TRUE;
device_features.dualSrcBlend = VK_TRUE;
device_features.tessellationShader = VK_TRUE;
@@ -77,6 +77,7 @@ ImageSampleLayout Layout(const IR::MemoryInfo& mem) {
layout.coord = cursor;
cursor += info.coordinate_components;
if (HasFlag(mem, Decoder::ImageSampleFlagLod)) layout.lod = cursor++;
if (HasFlag(mem, Decoder::ImageSampleFlagLodClamp)) layout.clamp = cursor++;
return layout;
}
@@ -1069,7 +1070,12 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
dref_value = AddressF32(ctx, mem, *address, layout.dref);
}
}
std::array<uint32_t, 4> operands {};
// _cl clamps the computed LOD from below.
uint32_t min_lod = 0;
if (layout.clamp != NoImageComponent) {
min_lod = AddressF32(ctx, mem, *address, layout.clamp);
}
std::array<uint32_t, 5> operands {};
size_t operand_count = 0;
if (HasFlag(mem, Decoder::ImageSampleFlagDerivative)) {
operands[0] = spv::ImageOperandsGradMask;
@@ -1081,6 +1087,11 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
if (HasFlag(mem, Decoder::ImageSampleFlagLod) && layout.lod != NoImageComponent) {
lod = AddressF32(ctx, mem, *address, layout.lod);
}
// SPIR-V takes MinLod only with implicit LOD or Grad.
if (min_lod != 0) {
lod = EmitGlsl<GLSLstd450FMax, IR::Type::F32>(state, lod, min_lod);
min_lod = 0;
}
operands[0] = spv::ImageOperandsLodMask;
operands[1] = lod;
operand_count = 2;
@@ -1104,6 +1115,13 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
sample_operands[sample_operand_count++] = PackedOffset(
ctx, offset_value, candidate.dimension, sample_operands[0]);
}
// MinLod follows the offset in operand order.
if (min_lod != 0) {
state.builder.RequireCapability(spv::CapabilityMinLod);
sample_operand_count = std::max(size_t{1}, sample_operand_count);
sample_operands[0] |= spv::ImageOperandsMinLodMask;
sample_operands[sample_operand_count++] = min_lod;
}
const auto coord =
CoordF32(ctx, mem, *address, layout.coord,
ImageDimensionInfoFor(candidate.dimension).coordinate_components,
@@ -283,6 +283,7 @@ struct ImageSampleLayout {
uint32_t bias = NoImageComponent;
uint32_t coord = 0;
uint32_t lod = NoImageComponent;
uint32_t clamp = NoImageComponent;
uint32_t grad_x = NoImageComponent;
uint32_t grad_y = NoImageComponent;
};
@@ -73,7 +73,7 @@ constexpr uint32_t ImageSampleAddressComponents(uint32_t flags, ImageDimension d
if ((flags & ImageSampleFlagBias) != 0) {
components++;
}
if ((flags & ImageSampleFlagLod) != 0) {
if ((flags & (ImageSampleFlagLod | ImageSampleFlagLodClamp)) != 0) {
components++;
}
if ((flags & ImageSampleFlagDerivative) != 0) {
+47 -1
View File
@@ -21559,8 +21559,9 @@ private:
"shaderStorageImageWriteWithoutFormat is not supported");
Require("VulkanHarness", "dispatch",
available_features.shaderImageGatherExtended == true &&
available_features.shaderResourceMinLod == true &&
available_derivatives.computeDerivativeGroupQuads == true,
"image gather or compute derivative quads are not supported");
"image gather, MinLod or compute derivative quads are not supported");
Require("VulkanHarness", "dispatch",
available_features11.storageBuffer16BitAccess == true &&
available_features12.storageBuffer8BitAccess == true,
@@ -21694,6 +21695,7 @@ private:
device_features.shaderStorageImageWriteWithoutFormat = true;
device_features.shaderStorageImageMultisample = available_features.shaderStorageImageMultisample;
device_features.shaderImageGatherExtended = true;
device_features.shaderResourceMinLod = true;
device_features.sampleRateShading = true;
device_features.shaderInt64 = true;
device_features.shaderFloat64 = available_features.shaderFloat64;
@@ -39224,6 +39226,48 @@ TestCase ImageGatherExplicitLod() {
return test;
}
TestCase ImageSampleLodClamp() {
using O = ShaderOpcode;
// _cl clamps the computed LOD from below: compute's implicit LOD and zero
// derivatives would both select mip 0, so a clamp of 1 must read mip 1.
std::vector<u32> code;
AppendVMovU32(&code, 20, 0);
AppendVMovU32(&code, 21, 0);
AppendVMovU32(&code, 22, 0);
AppendVMovU32(&code, 23, 0);
AppendVMovLiteral(&code, 24, 0x3f000000u);
AppendVMovLiteral(&code, 25, 0x3f000000u);
AppendVMovLiteral(&code, 26, 0x3f800000u);
code.push_back(EncodeMimg0(0x21, 0x1));
code.push_back(EncodeMimg1(0, 24, 0, 2));
code.push_back(EncodeMimg0(0x23, 0x1));
code.push_back(EncodeMimg1(1, 20, 0, 2));
AppendStoreVgpr(&code, 0, 0);
AppendStoreVgpr(&code, 1, 1);
AppendEnd(&code);
TestCase test;
test.name = "ImageSampleLodClamp";
test.code = std::move(code);
test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.expected = std::vector<u32>(2, 0x41000000u);
test.sampled_image_rgba_mips = {std::vector<u32>(4 * 4 * 4, 0x3f800000u),
std::vector<u32>(2 * 2 * 4, 0x41000000u)};
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32_32_32_32Float);
test.user_data[1] |= 3u << 30u;
test.user_data[2] = 3u << 14u;
test.user_data[3] |= 1u << 16u;
test.user_data[8] = 0x92u;
test.user_data[9] = 0xfff000u;
test.user_data[10] = 0x05000000u;
test.user_data[50] = test.expected.size() * sizeof(u32);
test.user_data[51] = 3u << 28u;
test.has_user_data = true;
test.required_spirv = {"MinLod"};
return test;
}
template <u32 mode> TestCase ImageGatherLodPerLane() {
using O = ShaderOpcode;
constexpr std::array names{
@@ -44788,6 +44832,7 @@ std::vector<TestCase> MakeCases() {
AddCase(ImageSampleAndGather);
AddCase(ImageCubeGradientsPreserveDerivatives);
AddCase(ImageGatherExplicitLod);
AddCase(ImageSampleLodClamp);
AddCase(ImageGatherLodPerLane<0>);
AddCase(ImageGatherLodPerLane<1>);
AddCase(ImageGatherLodPerLane<2>);
@@ -51205,6 +51250,7 @@ int main(int argc, char **argv) {
VulkanHarness vulkan;
vulkan.CheckComparisonDepthTexture();
RunCase(&vulkan, ImageGatherExplicitLod());
RunCase(&vulkan, ImageSampleLodClamp());
RunCase(&vulkan, ImageGatherLodPerLane<0>());
RunCase(&vulkan, ImageGatherLodPerLane<1>());
RunCase(&vulkan, ImageGatherLodPerLane<2>());