mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
shader: select the primary mip for linear explicit-LOD gathers
This commit is contained in:
@@ -97,10 +97,14 @@ uint32_t GatherMip(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Memory
|
|||||||
ConstantF32Value(state, 1.0f / 256.0f));
|
ConstantF32Value(state, 1.0f / 256.0f));
|
||||||
};
|
};
|
||||||
const auto sampler_control = ctx.Arg(inst, 6);
|
const auto sampler_control = ctx.Arg(inst, 6);
|
||||||
|
const auto mip_filter = Extract(sampler_control, 26, 2);
|
||||||
const auto preclamp = Binary(state, spv::OpINotEqual, TypeBool(state),
|
const auto preclamp = Binary(state, spv::OpINotEqual, TypeBool(state),
|
||||||
Extract(sampler_control, 28, 1), ConstantU32(state, 0));
|
Extract(sampler_control, 28, 1), ConstantU32(state, 0));
|
||||||
const auto half = ConstantF32Value(state, 0.5f);
|
|
||||||
const auto zero = ZeroF32(state);
|
const auto zero = ZeroF32(state);
|
||||||
|
// Point selects the nearest mip; linear gathers return the lower, primary mip only.
|
||||||
|
const auto rounding = Select(state, TypeF32(state),
|
||||||
|
Binary(state, spv::OpIEqual, TypeBool(state), mip_filter, ConstantU32(state, 1)),
|
||||||
|
ConstantF32Value(state, 0.5f), zero);
|
||||||
const auto base = Unary(state, spv::OpConvertUToF, TypeF32(state),
|
const auto base = Unary(state, spv::OpConvertUToF, TypeF32(state),
|
||||||
Extract(ctx.Arg(inst, 4), 12, 4));
|
Extract(ctx.Arg(inst, 4), 12, 4));
|
||||||
const auto minimum = Binary(state, spv::OpFSub, TypeF32(state),
|
const auto minimum = Binary(state, spv::OpFSub, TypeF32(state),
|
||||||
@@ -108,19 +112,19 @@ uint32_t GatherMip(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Memory
|
|||||||
const auto last = ConstantF32Value(state, static_cast<float>(mip_count - 1u));
|
const auto last = ConstantF32Value(state, static_cast<float>(mip_count - 1u));
|
||||||
auto lod = Binary(state, spv::OpFAdd, TypeF32(state),
|
auto lod = Binary(state, spv::OpFAdd, TypeF32(state),
|
||||||
AddressF32(ctx, mem, address, layout.lod),
|
AddressF32(ctx, mem, address, layout.lod),
|
||||||
Select(state, TypeF32(state), preclamp, half, zero));
|
Select(state, TypeF32(state), preclamp, rounding, zero));
|
||||||
lod = EmitGlsl<GLSLstd450FClamp, IR::Type::F32>(
|
lod = EmitGlsl<GLSLstd450FClamp, IR::Type::F32>(
|
||||||
state, lod, LodFixed(ctx.Arg(inst, 5), 0), LodFixed(ctx.Arg(inst, 5), 12));
|
state, lod, LodFixed(ctx.Arg(inst, 5), 0), LodFixed(ctx.Arg(inst, 5), 12));
|
||||||
// T# minimum LOD is in physical mip space and applies after the S# view-relative clamp.
|
// T# minimum LOD is in physical mip space and applies after the S# view-relative clamp.
|
||||||
lod = EmitGlsl<GLSLstd450FMax, IR::Type::F32>(state, lod, minimum);
|
lod = EmitGlsl<GLSLstd450FMax, IR::Type::F32>(state, lod, minimum);
|
||||||
lod = EmitGlsl<GLSLstd450FClamp, IR::Type::F32>(state, lod, zero, last);
|
lod = EmitGlsl<GLSLstd450FClamp, IR::Type::F32>(state, lod, zero, last);
|
||||||
lod = Binary(state, spv::OpFAdd, TypeF32(state), lod,
|
lod = Binary(state, spv::OpFAdd, TypeF32(state), lod,
|
||||||
Select(state, TypeF32(state), preclamp, zero, half));
|
Select(state, TypeF32(state), preclamp, zero, rounding));
|
||||||
// The clamped value is nonnegative, so conversion performs the integer mip rounding.
|
// The clamped value is nonnegative, so conversion performs the integer mip rounding.
|
||||||
const auto mip = EmitUMin32(state, Unary(state, spv::OpConvertFToU, TypeU32(state), lod),
|
const auto mip = EmitUMin32(state, Unary(state, spv::OpConvertFToU, TypeU32(state), lod),
|
||||||
ConstantU32(state, mip_count - 1u));
|
ConstantU32(state, mip_count - 1u));
|
||||||
const auto mip_none = Binary(state, spv::OpIEqual, TypeBool(state),
|
const auto mip_none = Binary(state, spv::OpIEqual, TypeBool(state),
|
||||||
Extract(sampler_control, 26, 2), ConstantU32(state, 0));
|
mip_filter, ConstantU32(state, 0));
|
||||||
return Select(state, TypeU32(state), mip_none, ConstantU32(state, 0), mip);
|
return Select(state, TypeU32(state), mip_none, ConstantU32(state, 0), mip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1671,11 +1671,13 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
|||||||
if (program.info.samplers[base_index].gather_lod) {
|
if (program.info.samplers[base_index].gather_lod) {
|
||||||
const auto control = snapshot.samplers[i].dwords[2];
|
const auto control = snapshot.samplers[i].dwords[2];
|
||||||
const auto filter = (control >> 26u) & 3u;
|
const auto filter = (control >> 26u) & 3u;
|
||||||
// MipNone always selects the base level. Explicit point gathers currently require
|
const auto perf_mip = (snapshot.samplers[i].dwords[1] >> 24u) & 15u;
|
||||||
// encoded-zero primary and secondary bias; linear primary-mip selection is unsupported.
|
// MipNone always selects the base level. Point/linear selection requires zero
|
||||||
if (filter > 1u || (filter == 1u && (control & 0xfffffu) != 0u)) {
|
// primary/secondary bias; linear mip snapping is not implemented.
|
||||||
|
if (filter > 2u || (filter != 0u && (control & 0xfffffu) != 0u) ||
|
||||||
|
(filter == 2u && perf_mip != 0u)) {
|
||||||
return SpecializationFail(
|
return SpecializationFail(
|
||||||
"explicit-LOD gather requires mip filtering None or Point with zero LOD biases");
|
"explicit-LOD gather requires None, unbiased Point, or unbiased Linear without mip snapping");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5177,19 +5177,24 @@ void TestGatherLodSamplerValidation() {
|
|||||||
Libs::Graphics::Prospero::BufferFormat::k32Float) << 20u;
|
Libs::Graphics::Prospero::BufferFormat::k32Float) << 20u;
|
||||||
user_data[3] = Libs::Graphics::DstSel(4, 5, 6, 7) |
|
user_data[3] = Libs::Graphics::DstSel(4, 5, 6, 7) |
|
||||||
(static_cast<uint32_t>(Libs::Graphics::Prospero::ImageType::kColor2D) << 28u);
|
(static_cast<uint32_t>(Libs::Graphics::Prospero::ImageType::kColor2D) << 28u);
|
||||||
user_data[9] = 0xfff000u;
|
|
||||||
constexpr std::array sampler_controls{
|
constexpr std::array sampler_controls{
|
||||||
0u, 0x4001u, 1u << 26u, (1u << 26u) | 256u,
|
0u, 0x4001u, 1u << 26u, (1u << 26u) | 256u,
|
||||||
(1u << 26u) | (1u << 14u), 2u << 26u, 3u << 26u};
|
(1u << 26u) | (1u << 14u), 2u << 26u, (2u << 26u) | 256u,
|
||||||
|
(2u << 26u) | (1u << 14u), 0x0a500000u, 0x1a500000u, 3u << 26u};
|
||||||
for (const auto control : sampler_controls) {
|
for (const auto control : sampler_controls) {
|
||||||
user_data[10] = control;
|
for (const uint32_t perf_mip : {0u, 1u}) {
|
||||||
ResourceSnapshot snapshot;
|
user_data[9] = 0xfff000u | (perf_mip << 24u);
|
||||||
ResourceSpecialization specialization;
|
user_data[10] = control;
|
||||||
const bool supported = !explicit_lod || (control >> 26u) == 0 ||
|
ResourceSnapshot snapshot;
|
||||||
control == (1u << 26u);
|
ResourceSpecialization specialization;
|
||||||
Check(MaterializeResources(plan, {.user_data = user_data}, snapshot,
|
const auto filter = (control >> 26u) & 3u;
|
||||||
specialization) == supported,
|
const bool supported = !explicit_lod || filter == 0 ||
|
||||||
"explicit gather accepted an unsupported sampler or rejected a valid one");
|
(filter <= 2 && (control & 0xfffffu) == 0 &&
|
||||||
|
(filter != 2 || perf_mip == 0));
|
||||||
|
Check(MaterializeResources(plan, {.user_data = user_data}, snapshot,
|
||||||
|
specialization) == supported,
|
||||||
|
"explicit gather accepted an unsupported sampler or rejected a valid one");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37524,11 +37524,12 @@ template <u32 mode> TestCase ImageGatherLodPerLane() {
|
|||||||
"ImageGatherLodSamplerBounds", "ImageGatherLodTextureMinimum",
|
"ImageGatherLodSamplerBounds", "ImageGatherLodTextureMinimum",
|
||||||
"ImageGatherLodMipNone", "ImageGatherLodPointPostclampMaximum",
|
"ImageGatherLodMipNone", "ImageGatherLodPointPostclampMaximum",
|
||||||
"ImageGatherLodPointPreclampMaximum", "ImageGatherLodPointPostclampMinimum",
|
"ImageGatherLodPointPreclampMaximum", "ImageGatherLodPointPostclampMinimum",
|
||||||
"ImageGatherLodPointPreclampMinimum"};
|
"ImageGatherLodPointPreclampMinimum", "ImageGatherLodLinearPrimary",
|
||||||
|
"ImageGatherLodLinearIgnoresPreclamp"};
|
||||||
constexpr std::array lods = mode < 5
|
constexpr std::array lods = mode < 5
|
||||||
? std::array{-1.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 9.0f}
|
? std::array{-1.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 9.0f}
|
||||||
: std::array{-1.0f, 0.0f, 0.25f, 0.75f, 1.25f, 1.75f, 2.75f, 9.0f};
|
: std::array{-1.0f, 0.0f, 0.25f, 0.75f, 1.25f, 1.75f, 2.75f, 9.0f};
|
||||||
constexpr std::array<std::array<u32, 8>, 9> selected_mips{{
|
constexpr std::array<std::array<u32, 8>, 11> selected_mips{{
|
||||||
{0, 0, 1, 2, 3, 4, 4, 4},
|
{0, 0, 1, 2, 3, 4, 4, 4},
|
||||||
{1, 1, 2, 3, 4, 4, 4, 4},
|
{1, 1, 2, 3, 4, 4, 4, 4},
|
||||||
{2, 2, 2, 3, 3, 3, 3, 3},
|
{2, 2, 2, 3, 3, 3, 3, 3},
|
||||||
@@ -37538,9 +37539,11 @@ template <u32 mode> TestCase ImageGatherLodPerLane() {
|
|||||||
{0, 0, 0, 1, 1, 1, 1, 1},
|
{0, 0, 0, 1, 1, 1, 1, 1},
|
||||||
{2, 2, 2, 2, 2, 2, 3, 4},
|
{2, 2, 2, 2, 2, 2, 3, 4},
|
||||||
{1, 1, 1, 1, 1, 2, 3, 4},
|
{1, 1, 1, 1, 1, 2, 3, 4},
|
||||||
|
{0, 0, 0, 0, 1, 1, 2, 4},
|
||||||
|
{0, 0, 0, 0, 1, 1, 2, 4},
|
||||||
}};
|
}};
|
||||||
constexpr u32 lanes = lods.size();
|
constexpr u32 lanes = lods.size();
|
||||||
constexpr u32 sampler_min = mode == 2 ? 256 : mode >= 7 ? 448 : 0;
|
constexpr u32 sampler_min = mode == 2 ? 256 : mode == 7 || mode == 8 ? 448 : 0;
|
||||||
constexpr u32 sampler_max = mode == 2 ? 512 : mode == 3 ? 256
|
constexpr u32 sampler_max = mode == 2 ? 512 : mode == 3 ? 256
|
||||||
: mode == 5 || mode == 6 ? 448 : 1024;
|
: mode == 5 || mode == 6 ? 448 : 1024;
|
||||||
TestCase test;
|
TestCase test;
|
||||||
@@ -37585,8 +37588,8 @@ template <u32 mode> TestCase ImageGatherLodPerLane() {
|
|||||||
test.user_data[9] = sampler_min | (sampler_max << 12u);
|
test.user_data[9] = sampler_min | (sampler_max << 12u);
|
||||||
// Point preclamp selects whether the half-level rounding adjustment precedes
|
// Point preclamp selects whether the half-level rounding adjustment precedes
|
||||||
// or follows the clamps. The streaming T# minimum follows the sampler bounds.
|
// or follows the clamps. The streaming T# minimum follows the sampler bounds.
|
||||||
test.user_data[10] = mode == 4 ? 0x4001u : (1u << 26u);
|
test.user_data[10] = mode == 4 ? 0x4001u : mode >= 9 ? 0x0a500000u : (1u << 26u);
|
||||||
if constexpr (mode == 6 || mode == 8) {
|
if constexpr (mode == 6 || mode == 8 || mode == 10) {
|
||||||
test.user_data[10] |= 1u << 28u;
|
test.user_data[10] |= 1u << 28u;
|
||||||
}
|
}
|
||||||
test.user_data[18] = lanes * sizeof(u32);
|
test.user_data[18] = lanes * sizeof(u32);
|
||||||
@@ -42828,6 +42831,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(ImageGatherLodPerLane<6>);
|
AddCase(ImageGatherLodPerLane<6>);
|
||||||
AddCase(ImageGatherLodPerLane<7>);
|
AddCase(ImageGatherLodPerLane<7>);
|
||||||
AddCase(ImageGatherLodPerLane<8>);
|
AddCase(ImageGatherLodPerLane<8>);
|
||||||
|
AddCase(ImageGatherLodPerLane<9>);
|
||||||
|
AddCase(ImageGatherLodPerLane<10>);
|
||||||
AddCase(ImageD16GatherPacksHalfPairs);
|
AddCase(ImageD16GatherPacksHalfPairs);
|
||||||
AddCase(ImageSampleA16SamplerCoordsOnGpu);
|
AddCase(ImageSampleA16SamplerCoordsOnGpu);
|
||||||
AddCase(ImageSampleOpcodeAliasUsesNormalCoords);
|
AddCase(ImageSampleOpcodeAliasUsesNormalCoords);
|
||||||
@@ -48658,6 +48663,8 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, ImageGatherLodPerLane<6>());
|
RunCase(&vulkan, ImageGatherLodPerLane<6>());
|
||||||
RunCase(&vulkan, ImageGatherLodPerLane<7>());
|
RunCase(&vulkan, ImageGatherLodPerLane<7>());
|
||||||
RunCase(&vulkan, ImageGatherLodPerLane<8>());
|
RunCase(&vulkan, ImageGatherLodPerLane<8>());
|
||||||
|
RunCase(&vulkan, ImageGatherLodPerLane<9>());
|
||||||
|
RunCase(&vulkan, ImageGatherLodPerLane<10>());
|
||||||
RunCase(&vulkan, ImageStoreSingleMipBounds<0>());
|
RunCase(&vulkan, ImageStoreSingleMipBounds<0>());
|
||||||
RunCase(&vulkan, ImageStoreSingleMipBounds<1>());
|
RunCase(&vulkan, ImageStoreSingleMipBounds<1>());
|
||||||
RunCase(&vulkan, ImageStoreSingleMipBounds<2>());
|
RunCase(&vulkan, ImageStoreSingleMipBounds<2>());
|
||||||
|
|||||||
Reference in New Issue
Block a user