mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader: share indirect image accesses by type and guard emitted size
This commit is contained in:
@@ -689,68 +689,58 @@ auto EmitImageAccess(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t resul
|
||||
}
|
||||
const auto selected = EmitIndirectResourceIndex(
|
||||
state, key, image.indirect_mapping_offset, image.indirect_search_iterations, 0u);
|
||||
struct ImageRun {
|
||||
uint32_t first;
|
||||
uint32_t count;
|
||||
uint32_t resource;
|
||||
uint32_t slot_bias;
|
||||
const auto SameType = [&](uint32_t lhs, uint32_t rhs) {
|
||||
const auto& first = state.program.info.images[lhs];
|
||||
const auto& second = state.program.info.images[rhs];
|
||||
return first.dimension == second.dimension && first.cube == second.cube &&
|
||||
IR::DescriptorBindingForImage(first) == IR::DescriptorBindingForImage(second) &&
|
||||
(inst.GetOpcode() != IR::ValueOpcode::ImageWrite ||
|
||||
first.shader_swizzle == second.shader_swizzle) &&
|
||||
first.mip_count == 1u && second.mip_count == 1u;
|
||||
};
|
||||
std::vector<ImageRun> runs;
|
||||
for (uint32_t ordinal = 0; ordinal < image.indirect_resources.size(); ++ordinal) {
|
||||
const auto resource = image.indirect_resources[ordinal];
|
||||
const auto& candidate = state.program.info.images[resource];
|
||||
const auto kind = *IR::DescriptorBindingForImage(candidate);
|
||||
if (!runs.empty()) {
|
||||
auto& run = runs.back();
|
||||
const auto& first = state.program.info.images[run.resource];
|
||||
const auto next_slot = run.slot_bias + ordinal;
|
||||
if (candidate.dimension == first.dimension && candidate.cube == first.cube &&
|
||||
IR::DescriptorBindingForImage(first) == kind &&
|
||||
(inst.GetOpcode() != IR::ValueOpcode::ImageWrite ||
|
||||
candidate.shader_swizzle == first.shader_swizzle) &&
|
||||
candidate.mip_count == 1u && first.mip_count == 1u &&
|
||||
(ordinal == 1u || candidate.descriptor_index == next_slot)) {
|
||||
// Native roots precede appended children; only the root may have a slot gap.
|
||||
if (ordinal == 1u)
|
||||
run.slot_bias = candidate.descriptor_index - ordinal;
|
||||
++run.count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
runs.push_back({ordinal, 1u, resource,
|
||||
candidate.descriptor_index - ordinal});
|
||||
// Descriptor order is unrelated to image type. Share each typed access and
|
||||
// select its descriptor slot independently, including gaps between native roots.
|
||||
struct ImageGroup { uint32_t resource; uint32_t count; };
|
||||
std::vector<ImageGroup> groups;
|
||||
std::vector<uint32_t> ordinal_groups;
|
||||
for (const auto resource: image.indirect_resources) {
|
||||
const auto found = std::ranges::find_if(groups, [&](const ImageGroup& group) {
|
||||
return SameType(resource, group.resource);
|
||||
});
|
||||
ordinal_groups.push_back(static_cast<uint32_t>(found - groups.begin()));
|
||||
if (found == groups.end()) groups.push_back({resource, 1u});
|
||||
else ++found->count;
|
||||
}
|
||||
const auto EmitRun = [&](uint32_t index) {
|
||||
const auto& run = runs[index];
|
||||
if (run.count == 1u) return emit(run.resource, 0u);
|
||||
auto slot = Binary(state, spv::OpIAdd, TypeU32(state), selected,
|
||||
ConstantU32(state, run.slot_bias));
|
||||
if (run.first == 0u) {
|
||||
const auto root_slot = image.descriptor_index;
|
||||
if (root_slot != run.slot_bias) {
|
||||
const auto is_root = Binary(state, spv::OpIEqual, TypeBool(state), selected,
|
||||
ConstantU32(state, 0u));
|
||||
const auto root_or_child = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpSelect, TypeU32(state), root_or_child,
|
||||
is_root, ConstantU32(state, root_slot), slot);
|
||||
slot = root_or_child;
|
||||
}
|
||||
}
|
||||
return emit(run.resource, slot);
|
||||
};
|
||||
auto run_index = ConstantU32(state, 0u);
|
||||
for (uint32_t index = 1; index < runs.size(); ++index) {
|
||||
const bool dynamic_slot = std::ranges::any_of(groups, [](const ImageGroup& group) {
|
||||
return group.count > 1u;
|
||||
});
|
||||
auto group_index = ConstantU32(state, 0u);
|
||||
auto slot_bias = ConstantU32(state, image.descriptor_index);
|
||||
auto previous_bias = image.descriptor_index;
|
||||
for (uint32_t ordinal = 1; ordinal < image.indirect_resources.size(); ++ordinal) {
|
||||
const auto& candidate = state.program.info.images[image.indirect_resources[ordinal]];
|
||||
const auto bias = candidate.descriptor_index - ordinal;
|
||||
const bool select_group = ordinal_groups[ordinal] != ordinal_groups[ordinal - 1u];
|
||||
const bool select_slot = dynamic_slot && bias != previous_bias;
|
||||
previous_bias = bias;
|
||||
if (!select_group && !select_slot) continue;
|
||||
const auto at_or_after = Binary(state, spv::OpUGreaterThanEqual, TypeBool(state),
|
||||
selected, ConstantU32(state, runs[index].first));
|
||||
const auto next = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpSelect, TypeU32(state), next, at_or_after,
|
||||
ConstantU32(state, index), run_index);
|
||||
run_index = next;
|
||||
selected, ConstantU32(state, ordinal));
|
||||
if (select_group) group_index = Select(state, TypeU32(state), at_or_after,
|
||||
ConstantU32(state, ordinal_groups[ordinal]), group_index);
|
||||
if (select_slot) slot_bias = Select(state, TypeU32(state), at_or_after,
|
||||
ConstantU32(state, bias), slot_bias);
|
||||
}
|
||||
return runs.size() == 1u
|
||||
? EmitRun(0u)
|
||||
: EmitIndexSwitch(state, run_index, static_cast<uint32_t>(runs.size()),
|
||||
result_type, EmitRun);
|
||||
const auto slot = dynamic_slot
|
||||
? Binary(state, spv::OpIAdd, TypeU32(state), selected, slot_bias) : 0u;
|
||||
const auto EmitGroup = [&](uint32_t index) {
|
||||
const auto& group = groups[index];
|
||||
return emit(group.resource, group.count == 1u ? 0u : slot);
|
||||
};
|
||||
return groups.size() == 1u
|
||||
? EmitGroup(0u)
|
||||
: EmitIndexSwitch(state, group_index, static_cast<uint32_t>(groups.size()),
|
||||
result_type, EmitGroup);
|
||||
}
|
||||
|
||||
template <typename Emit>
|
||||
|
||||
Reference in New Issue
Block a user