mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
shader: merge typed image candidates as raw vectors with shared sampler variants
This commit is contained in:
@@ -1210,6 +1210,52 @@ void TestDescendingImageRange() {
|
||||
}
|
||||
}
|
||||
|
||||
void TestMixedIndirectImageSamplers() {
|
||||
auto fixture = MakeIndirectImageFixture(false, 32u, 0u, 0u);
|
||||
fixture->PlanAndTrack();
|
||||
auto& samplers = fixture->program.info.samplers;
|
||||
samplers.resize(3u, samplers[0]);
|
||||
samplers[0].indirect_resources = {1u, 2u};
|
||||
const auto plan = ExtractResourcePlan(fixture->program);
|
||||
std::array<uint32_t, 8> data {0x3000u, 16u << 16u, 1u, 0u,
|
||||
0x2000u, 32u << 16u, 3u, 0u};
|
||||
LinearTestMemory memory;
|
||||
using F = Libs::Graphics::Prospero::BufferFormat;
|
||||
for (uint32_t record = 0; record < 3u; ++record) {
|
||||
const auto offset = (0x1000u + record * 32u) / 4u;
|
||||
memory.words[offset] = 0x100u + record;
|
||||
memory.words[offset + 1u] = static_cast<uint32_t>(
|
||||
record == 0u ? F::k32_32_32_32Float : F::k32_32_32_32UInt) << 20u;
|
||||
memory.words[offset + 3u] = Libs::Graphics::DstSel(4, 5, 6, 7) |
|
||||
(uint32_t(Libs::Graphics::Prospero::ImageType::kColor2D) << 28u);
|
||||
}
|
||||
const SrtRuntime runtime {.user_data = data, .userdata = &memory,
|
||||
.read_specialization_memory = ReadLinearTestMemory};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) && snapshot.images.size() == 4u,
|
||||
"mixed float/uint image table failed materialization");
|
||||
ApplyResourceSpecialization(fixture->program, specialization);
|
||||
const auto& info = fixture->program.info;
|
||||
Check(info.sampled_pairs.size() == 2u && info.samplers.size() == 6u,
|
||||
"indirect image candidates duplicated sampler pairs or lost finite class variants");
|
||||
for (const auto& pair : info.sampled_pairs) {
|
||||
const auto integer = info.images[pair.image].numeric_class ==
|
||||
Libs::Graphics::Prospero::TextureNumericClass::Uint;
|
||||
const auto& sampler = info.samplers[pair.sampler];
|
||||
Check(sampler.snapshot_index == 0u && sampler.integer_border == integer &&
|
||||
sampler.force_point_filtering == integer && sampler.indirect_resources.size() == 2u &&
|
||||
pair.first_use_pc == 0x10f0u,
|
||||
"mixed image class changed native sampler identity or integer-border semantics");
|
||||
for (uint32_t ordinal = 0; ordinal < sampler.indirect_resources.size(); ++ordinal) {
|
||||
const auto& candidate = info.samplers[sampler.indirect_resources[ordinal]];
|
||||
Check(candidate.snapshot_index == ordinal + 1u &&
|
||||
candidate.integer_border == integer && candidate.force_point_filtering == integer,
|
||||
"mixed image class mapped a finite sampler choice to the wrong variant");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestUniformizedMaterialImageKeys() {
|
||||
namespace CFG = Libs::Graphics::ShaderRecompiler::CFG;
|
||||
enum class Variant {
|
||||
@@ -4086,6 +4132,7 @@ int main() {
|
||||
Run("draw-uniform scalar image", TestUniformScalarBufferImage);
|
||||
Run("scalar buffer selector domain", TestScalarBufferSelectorDomain);
|
||||
Run("descending image range", TestDescendingImageRange);
|
||||
Run("mixed indirect image samplers", TestMixedIndirectImageSamplers);
|
||||
Run("SRT runtime", TestSrtFlatteningAndRuntimeMemoization);
|
||||
Run("dynamic SRT", TestDynamicSrtReadRemainsExplicit);
|
||||
Run("writable descriptor phi", TestWritableDescriptorPhi);
|
||||
|
||||
@@ -34419,7 +34419,7 @@ TestCase ImageLoadR32SintUsesSignedSampledImage() {
|
||||
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32SInt);
|
||||
test.has_user_data = true;
|
||||
test.required_spirv = {"image_17", "OpTypeImage %int", "OpImageFetch %v4int",
|
||||
"OpBitcast %uint"};
|
||||
"OpBitcast %v4uint"};
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -36113,49 +36113,102 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
resource == 4u ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D, layers));
|
||||
}
|
||||
const auto native_sampler = vulkan.CreateSampler(test.name);
|
||||
enum class Access { Sample, Gather, GatherHomogeneous, Fetch };
|
||||
constexpr u32 uint_texel = 0x80000106u;
|
||||
auto uint_texture = vulkan.CreateImageMips(
|
||||
test.name, 2, 2, vk::Format::eR32G32B32A32Uint, vk::ImageUsageFlagBits::eSampled,
|
||||
{std::vector<u32>(16u, uint_texel), std::vector<u32>(4u, uint_texel + 10u)}, 4u,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageType::e2D, vk::ImageViewType::e2D, 1u);
|
||||
vk::SamplerCreateInfo integer_sampler_info{};
|
||||
integer_sampler_info.addressModeU = integer_sampler_info.addressModeV =
|
||||
integer_sampler_info.addressModeW = vk::SamplerAddressMode::eClampToEdge;
|
||||
integer_sampler_info.borderColor = vk::BorderColor::eIntOpaqueWhite;
|
||||
vk::Sampler integer_sampler;
|
||||
RequireVk(test.name, "integer sampler", vulkan.Device().createSampler(
|
||||
&integer_sampler_info, nullptr, &integer_sampler), "vkCreateSampler");
|
||||
integer_sampler_info.addressModeU = integer_sampler_info.addressModeV =
|
||||
integer_sampler_info.addressModeW = vk::SamplerAddressMode::eClampToBorder;
|
||||
vk::Sampler integer_border_sampler;
|
||||
RequireVk(test.name, "integer border sampler", vulkan.Device().createSampler(
|
||||
&integer_sampler_info, nullptr, &integer_border_sampler), "vkCreateSampler");
|
||||
const std::array native_samplers{native_sampler, native_sampler, native_sampler,
|
||||
integer_sampler, integer_sampler, integer_border_sampler};
|
||||
enum class Access { Sample, Gather, GatherHomogeneous, MixedSample, MixedGather, MixedFetch, Fetch };
|
||||
auto *access = &sample;
|
||||
bool uint_bound = false;
|
||||
for (const auto mode : {Access::Sample, Access::Gather,
|
||||
Access::GatherHomogeneous, Access::Fetch}) {
|
||||
const bool fetch = mode == Access::Fetch;
|
||||
const bool gather = mode == Access::Gather || mode == Access::GatherHomogeneous;
|
||||
Access::GatherHomogeneous, Access::MixedSample,
|
||||
Access::MixedGather, Access::MixedFetch, Access::Fetch}) {
|
||||
const bool mixed = mode == Access::MixedSample || mode == Access::MixedGather || mode == Access::MixedFetch;
|
||||
const bool fetch = mode == Access::Fetch || mode == Access::MixedFetch;
|
||||
const bool gather = mode == Access::Gather || mode == Access::GatherHomogeneous || mode == Access::MixedGather;
|
||||
const bool homogeneous = mode == Access::GatherHomogeneous;
|
||||
if (mode == Access::Gather) {
|
||||
test.name = "IndirectImageGatherContiguousRuns";
|
||||
test.name = mixed ? (fetch ? "IndirectImageMixedFetch" : gather ? "IndirectImageMixedGather"
|
||||
: "IndirectImageMixedSample")
|
||||
: fetch ? "IndirectImageFetchContiguousRuns"
|
||||
: homogeneous ? "IndirectImageGatherHomogeneous"
|
||||
: gather ? "IndirectImageGatherContiguousRuns" : "IndirectImageContiguousRuns";
|
||||
if (uint_bound != mixed) {
|
||||
std::swap(textures[6], uint_texture);
|
||||
compiled.program.info.images[6].numeric_class = mixed ? Prospero::TextureNumericClass::Uint
|
||||
: Prospero::TextureNumericClass::Float;
|
||||
uint_bound = mixed;
|
||||
}
|
||||
if (mode == Access::MixedSample) {
|
||||
const auto position = std::ranges::find_if(*block, [&](const Inst &inst) { return &inst == &sampler; });
|
||||
const auto sampler_key = block->PrependNewInst(position, ValueOpcode::BitwiseAnd32,
|
||||
{Value(&key), Value(1u)});
|
||||
sampler.SetArg(0, Value(&*sampler_key));
|
||||
compiled.program.info.samplers.resize(6);
|
||||
for (u32 index = 0; index < 6u; ++index) {
|
||||
auto &resource = compiled.program.info.samplers[index];
|
||||
resource.snapshot_index = index % 3u;
|
||||
resource.force_point_filtering = index >= 3u;
|
||||
resource.integer_border = index >= 3u;
|
||||
}
|
||||
compiled.program.info.samplers[0].indirect_resources = {1u, 2u};
|
||||
compiled.program.info.samplers[3].indirect_resources = {4u, 5u};
|
||||
compiled.program.info.sampled_pairs.push_back({6u, 3u, 0x10f0u});
|
||||
}
|
||||
const auto opcode = fetch ? ValueOpcode::ImageRead : gather ? ValueOpcode::ImageGatherRaw
|
||||
: ValueOpcode::ImageSampleRaw;
|
||||
if (access->GetOpcode() != opcode) {
|
||||
const auto position = std::ranges::find_if(*block, [&](const Inst &inst) {
|
||||
return &inst == access;
|
||||
});
|
||||
auto &read = *block->PrependNewInst(
|
||||
position, ValueOpcode::ImageGatherRaw,
|
||||
{Value(&image), Value(&sampler), Value(&address), Value(0u), Value(0u),
|
||||
Value(0u), Value(0u), Value(true)}, memory_flag_bits);
|
||||
const auto read_at = fetch ? block->PrependNewInst(position, opcode,
|
||||
{Value(&image), Value(&address), Value(true)}, memory_flag_bits)
|
||||
: gather ? block->PrependNewInst(position, opcode,
|
||||
{Value(&image), Value(&sampler), Value(&address), Value(0u), Value(0u),
|
||||
Value(0u), Value(0u), Value(true)}, memory_flag_bits)
|
||||
: block->PrependNewInst(position, opcode,
|
||||
{Value(&image), Value(&sampler), Value(&address), Value(true)}, memory_flag_bits);
|
||||
auto &read = *read_at;
|
||||
access->ReplaceUsesWith(Value(&read), false);
|
||||
access = &read;
|
||||
}
|
||||
if (gather) {
|
||||
compiled.program.info.images[4].cube = false;
|
||||
auto &gather_memory = compiled.program.memory_info[0];
|
||||
gather_memory.dmask = 1;
|
||||
gather_memory.image_sample_flags = ShaderRecompiler::Decoder::ImageSampleFlagLevelZero |
|
||||
ShaderRecompiler::Decoder::ImageSampleFlagOffset;
|
||||
// GATHER4_LZ_O places its packed signed offset before the coordinates.
|
||||
address.SetArg(0, Value(0x3fu));
|
||||
address.SetArg(1, Value(std::bit_cast<u32>(0.75f)));
|
||||
address.SetArg(2, Value(std::bit_cast<u32>(0.25f)));
|
||||
(mixed ? 0u : ShaderRecompiler::Decoder::ImageSampleFlagOffset);
|
||||
// Nonmixed GATHER4_LZ_O places its packed signed offset before the coordinates.
|
||||
address.SetArg(0, Value(mixed ? std::bit_cast<u32>(1.5f) : 0x3fu));
|
||||
address.SetArg(1, Value(std::bit_cast<u32>(mixed ? 1.5f : 0.75f)));
|
||||
address.SetArg(2, Value(mixed ? 0u : std::bit_cast<u32>(0.25f)));
|
||||
address.SetArg(3, Value(0u));
|
||||
} else if (mode == Access::MixedSample) {
|
||||
compiled.program.memory_info[0].dmask = 0xf;
|
||||
compiled.program.memory_info[0].image_sample_flags = ShaderRecompiler::Decoder::ImageSampleFlagLod;
|
||||
for (u32 component = 0; component < 4u; ++component)
|
||||
address.SetArg(component, Value(component < 2u ? std::bit_cast<u32>(1.5f) : 0u));
|
||||
}
|
||||
auto &selected_resources = compiled.program.info.images[0].indirect_resources;
|
||||
selected_resources = homogeneous ? std::vector<u32>{0u, 2u, 3u, 5u, 6u}
|
||||
: root.indirect_resources;
|
||||
if (homogeneous) test.name = "IndirectImageGatherHomogeneous";
|
||||
compiled.resources.flattened_srt[6] = homogeneous ? 4u : ordinals[5];
|
||||
compiled.resources.flattened_srt[7] = mixed ? 5u : ordinals[6];
|
||||
if (fetch) {
|
||||
test.name = "IndirectImageFetchContiguousRuns";
|
||||
const auto position = std::ranges::find_if(*block, [&](const Inst &inst) {
|
||||
return &inst == access;
|
||||
});
|
||||
auto &read = *block->PrependNewInst(
|
||||
position, ValueOpcode::ImageRead,
|
||||
{Value(&image), Value(&address), Value(true)}, memory_flag_bits);
|
||||
access->ReplaceUsesWith(Value(&read), false);
|
||||
sampler.Invalidate();
|
||||
compiled.program.info.samplers.clear();
|
||||
compiled.program.info.sampled_pairs.clear();
|
||||
@@ -36183,7 +36236,7 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
const auto image_opcode = fetch ? "OpImageFetch" : gather ? "OpImageGather"
|
||||
: "OpImageSampleExplicitLod";
|
||||
Require(test.name, "native array image runs", tools.Disassemble(compiled.spirv, &text) &&
|
||||
CountText(text, image_opcode) == (homogeneous ? 1u : 3u) * halves,
|
||||
CountText(text, image_opcode) == (homogeneous ? 1u : mixed ? 4u : 3u) * halves,
|
||||
"mixed runs expanded into per-image accesses");
|
||||
if (homogeneous) {
|
||||
Require(test.name, "homogeneous gather array", text.find("OpSwitch") == std::string::npos,
|
||||
@@ -36205,25 +36258,31 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
||||
Require(test.name, "nonuniform image operands",
|
||||
std::ranges::count_if(image_operands, [&](u32 operand) {
|
||||
return std::ranges::find(decorated, operand) != decorated.end();
|
||||
}) == (homogeneous ? 1u : 2u) * halves,
|
||||
}) == (homogeneous ? 1u : mixed ? (fetch ? 1u : 4u) : 2u) * halves,
|
||||
"a dynamically selected image operand lacks its nonuniform decoration");
|
||||
}
|
||||
test.initial.assign(wave_size, 0xdeadbeefu);
|
||||
test.expected.assign(wave_size, 0u);
|
||||
for (u32 lane = 0; lane < ordinals.size(); ++lane) {
|
||||
const auto resource = selected_resources[compiled.resources.flattened_srt[1u + lane]];
|
||||
test.expected[lane] = std::bit_cast<u32>(float(resource + (fetch && resource != 0u ? 10u : 0u)));
|
||||
test.expected[lane] = mixed && resource == 6u
|
||||
? (fetch ? uint_texel + 10u : (lane & 1u) ? 1u : uint_texel)
|
||||
: std::bit_cast<u32>(float(resource + (fetch && resource != 0u ? 10u : 0u)));
|
||||
}
|
||||
auto buffer = vulkan.CreateStorageBuffer(test.name, test.initial, test.initial.size());
|
||||
vulkan.Dispatch(test, compiled, buffer, nullptr, nullptr, nullptr, nullptr,
|
||||
fetch ? vk::Sampler{} : native_sampler, textures);
|
||||
fetch ? vk::Sampler{} : native_sampler, textures,
|
||||
mixed && !fetch ? std::span<const vk::Sampler>(native_samplers) : std::span<const vk::Sampler>{});
|
||||
const auto actual = vulkan.ReadBuffer(test.name, buffer, test.expected.size());
|
||||
vulkan.DestroyBuffer(&buffer);
|
||||
CompareWords(test, "root, native slots, array runs and mapping bounds", test.expected, actual);
|
||||
}
|
||||
std::printf("[compute] %-32s ok\n", test.name);
|
||||
}
|
||||
vulkan.Device().destroySampler(integer_sampler);
|
||||
vulkan.Device().destroySampler(integer_border_sampler);
|
||||
vulkan.Device().destroySampler(native_sampler);
|
||||
vulkan.DestroyImage(&uint_texture);
|
||||
for (auto &texture : textures) vulkan.DestroyImage(&texture);
|
||||
}
|
||||
|
||||
@@ -40533,18 +40592,16 @@ void CheckImageSamplerSpecialization() {
|
||||
ShaderRecompiler::IR::ApplyResourceSpecialization(
|
||||
mixed_sampler_program, mixed_sampler_specialization);
|
||||
Require("ImageSamplerSpecialization", "mixed sampler variant",
|
||||
mixed_sampler_program.info.samplers.size() == 3u &&
|
||||
mixed_sampler_program.info.samplers.size() == 2u &&
|
||||
!mixed_sampler_program.info.samplers[0].force_point_filtering &&
|
||||
!mixed_sampler_program.info.samplers[0].integer_border &&
|
||||
!mixed_sampler_program.info.samplers[1].force_point_filtering &&
|
||||
mixed_sampler_program.info.samplers[1].force_point_filtering &&
|
||||
mixed_sampler_program.info.samplers[1].integer_border &&
|
||||
mixed_sampler_program.info.samplers[2].force_point_filtering &&
|
||||
mixed_sampler_program.info.samplers[2].integer_border &&
|
||||
mixed_sampler_program.info.sampled_pairs[0].sampler == 0u &&
|
||||
mixed_sampler_program.info.sampled_pairs[1].sampler == 2u &&
|
||||
mixed_sampler_program.info.sampled_pairs[2].sampler == 2u &&
|
||||
mixed_sampler_program.info.sampled_pairs[1].sampler == 1u &&
|
||||
mixed_sampler_program.info.sampled_pairs[2].sampler == 1u &&
|
||||
mixed_sampler_program.info.sampled_pairs[3].sampler == 1u &&
|
||||
mixed_sampler_program.memory_info[0].sampler == 2u &&
|
||||
mixed_sampler_program.memory_info[0].sampler == 1u &&
|
||||
mixed_sampler_snapshot.samplers.size() == 1u &&
|
||||
std::ranges::all_of(mixed_sampler_program.info.samplers,
|
||||
[](const auto& sampler) {
|
||||
|
||||
@@ -14257,6 +14257,7 @@ void TestBoundedScalarMaterialImageKeys() {
|
||||
uint64_t unavailable = 0;
|
||||
bool clean = true;
|
||||
bool mixed = false;
|
||||
bool mixed_dimension = false;
|
||||
bool same_key = false;
|
||||
uint32_t keys = 0;
|
||||
uint32_t images = 0;
|
||||
@@ -14288,7 +14289,8 @@ void TestBoundedScalarMaterialImageKeys() {
|
||||
words[1] = static_cast<uint32_t>(integer && !(m.mixed && key == 5573u)
|
||||
? Prospero::BufferFormat::k32UInt : Prospero::BufferFormat::k32Float) << 20u;
|
||||
words[3] = DstSel(4, 5, 6, 7) |
|
||||
(static_cast<uint32_t>(Prospero::ImageType::kColor3D) << 28u);
|
||||
(static_cast<uint32_t>(m.mixed_dimension && key == 5573u
|
||||
? Prospero::ImageType::kColor2D : Prospero::ImageType::kColor3D) << 28u);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -14335,8 +14337,15 @@ void TestBoundedScalarMaterialImageKeys() {
|
||||
Check(!MaterializeResources(plan, runtime, snapshot, specialization) && memory.keys == 0,
|
||||
"scalar material byte range escaped V# bounds");
|
||||
memory = {.mixed = true};
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
||||
snapshot.images.size() == 4u &&
|
||||
std::ranges::count_if(specialization.images, [](const auto& image) {
|
||||
return image.numeric_class == Prospero::TextureNumericClass::Uint;
|
||||
}) == 1,
|
||||
"selected material fields lost their mixed float/uint image classes");
|
||||
memory = {.mixed_dimension = true};
|
||||
Check(!MaterializeResources(plan, runtime, snapshot, specialization),
|
||||
"selected material fields bypassed image compatibility");
|
||||
"selected material fields bypassed image coordinate compatibility");
|
||||
memory = {};
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization),
|
||||
"native scalar material image selection did not recover after rejected candidates");
|
||||
|
||||
Reference in New Issue
Block a user