mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: remove unreachable mixed image specialization paths
This commit is contained in:
@@ -61,10 +61,10 @@ uint32_t AddressF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::
|
||||
: Unary(ctx.state, spv::OpBitcast, TypeF32(ctx.state), value);
|
||||
}
|
||||
|
||||
ImageSampleLayout Layout(const IR::MemoryInfo& mem, ImageDimension dimension) {
|
||||
ImageSampleLayout Layout(const IR::MemoryInfo& mem) {
|
||||
ImageSampleLayout layout;
|
||||
uint32_t cursor = 0;
|
||||
const auto& info = ImageDimensionInfoFor(dimension);
|
||||
const auto& info = ImageDimensionInfoFor(mem.image_dimension);
|
||||
if (HasFlag(mem, Decoder::ImageSampleFlagOffset)) layout.offset = cursor++;
|
||||
if (HasFlag(mem, Decoder::ImageSampleFlagBias)) layout.bias = cursor++;
|
||||
if (HasFlag(mem, Decoder::ImageSampleFlagCompare)) layout.dref = cursor++;
|
||||
@@ -80,48 +80,6 @@ ImageSampleLayout Layout(const IR::MemoryInfo& mem, ImageDimension dimension) {
|
||||
return layout;
|
||||
}
|
||||
|
||||
void ValidateIndirectImageOperands(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
const auto op = inst.GetOpcode();
|
||||
const auto& memory = ctx.Memory(inst);
|
||||
const auto& images = ctx.state.program.info.images;
|
||||
const auto& root = images.at(memory.resource);
|
||||
if (root.indirect_root != memory.resource) return;
|
||||
if (op != IR::ValueOpcode::ImageSampleRaw &&
|
||||
op != IR::ValueOpcode::ImageGatherRaw && op != IR::ValueOpcode::ImageRead) {
|
||||
ctx.Fail(inst, "requires an unsupported indirect image operation");
|
||||
}
|
||||
const auto& native = ImageDimensionInfoFor(memory.image_dimension);
|
||||
const auto native_layout = Layout(memory, memory.image_dimension);
|
||||
const bool sampling = op == IR::ValueOpcode::ImageSampleRaw ||
|
||||
op == IR::ValueOpcode::ImageGatherRaw;
|
||||
const auto validate = [&](const IR::ImageResource& image) {
|
||||
const auto& candidate = ImageDimensionInfoFor(image.dimension);
|
||||
// Sample/fetch ignore the native array slice for a non-array T# while
|
||||
// keeping the original positions of later operands.
|
||||
const bool projected_2d = memory.image_dimension == ImageDimension::Dim2DArray &&
|
||||
image.dimension == ImageDimension::Dim2D;
|
||||
bool compatible = projected_2d ||
|
||||
(candidate.coordinate_components == native.coordinate_components &&
|
||||
candidate.multisampled == native.multisampled);
|
||||
if (sampling) {
|
||||
compatible &= candidate.multisampled == 0u && native.multisampled == 0u;
|
||||
compatible &= projected_2d || Layout(memory, image.dimension) == native_layout;
|
||||
if (HasFlag(memory, Decoder::ImageSampleFlagDerivative)) {
|
||||
compatible &= candidate.spatial_components == native.spatial_components;
|
||||
}
|
||||
if (op == IR::ValueOpcode::ImageGatherRaw ||
|
||||
HasFlag(memory, Decoder::ImageSampleFlagCompare)) {
|
||||
compatible &= image.dimension != ImageDimension::Dim3D;
|
||||
}
|
||||
}
|
||||
if (!compatible) ctx.Fail(inst, "has incompatible image operand layout");
|
||||
};
|
||||
validate(root);
|
||||
for (const auto resource: root.indirect_resources) {
|
||||
if (resource != memory.resource) validate(images.at(resource));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ZeroF32(EmitterState& state) {
|
||||
return ConstantF32(state, 0);
|
||||
}
|
||||
@@ -646,45 +604,6 @@ uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t d
|
||||
return PackImageTexel(ctx, mem, texel);
|
||||
}
|
||||
|
||||
struct ImageSampler {
|
||||
uint32_t id = 0;
|
||||
bool dynamic = false;
|
||||
};
|
||||
|
||||
auto EmitImageSamplers(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
auto& state = ctx.state;
|
||||
const auto& mem = ctx.Memory(inst);
|
||||
const auto& images = state.program.info.images;
|
||||
const auto& samplers = state.program.info.samplers;
|
||||
std::array<ImageSampler, static_cast<size_t>(Prospero::TextureNumericClass::Sint) + 1u> result {};
|
||||
const auto add = [&](uint32_t image, uint32_t sampler) {
|
||||
auto& entry = result[static_cast<size_t>(images[image].numeric_class)];
|
||||
if (entry.id != 0u) return;
|
||||
const auto& choices = samplers.at(sampler).indirect_resources;
|
||||
uint32_t index = 0;
|
||||
if (!choices.empty()) {
|
||||
const auto* handle = inst.Arg(1).ResolveInstruction();
|
||||
if (handle == nullptr || handle->GetOpcode() != IR::ValueOpcode::GetSamplerResource)
|
||||
ctx.Fail(inst, "has invalid finite sampler key provenance");
|
||||
index = EmitIndexSwitch(
|
||||
state, ctx.Def(handle->Arg(0)), static_cast<uint32_t>(choices.size()),
|
||||
TypeU32(state), [&](uint32_t ordinal) { return ConstantU32(state, choices[ordinal]); });
|
||||
}
|
||||
entry = {LoadSamplerDescriptor(state, sampler, index), index != 0u};
|
||||
};
|
||||
add(mem.resource, mem.sampler);
|
||||
if (images[mem.resource].indirect_root == mem.resource) {
|
||||
for (const auto& pair: state.program.info.sampled_pairs)
|
||||
if (images[pair.image].indirect_root == mem.resource &&
|
||||
samplers[pair.sampler].snapshot_index == samplers[mem.sampler].snapshot_index)
|
||||
add(pair.image, pair.sampler);
|
||||
for (const auto candidate: images[mem.resource].indirect_resources)
|
||||
if (result[static_cast<size_t>(images[candidate].numeric_class)].id == 0u)
|
||||
ctx.Fail(inst, "has no sampler variant for an indirect image candidate");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Emit>
|
||||
uint32_t EmitImageAccess(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t result_type,
|
||||
const Emit& emit) {
|
||||
@@ -778,7 +697,6 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
const auto& mem = ctx.Memory(inst);
|
||||
const auto image_arg = inst.Arg(0);
|
||||
ctx.ResourceIndex(image_arg, IR::ValueOpcode::GetImageResource);
|
||||
ValidateIndirectImageOperands(ctx, inst);
|
||||
const auto& image = state.program.info.images.at(mem.resource);
|
||||
const auto* address = ctx.ImageAddress(inst.Arg(image_info.needs_sampler ? 2 : 1));
|
||||
if (op == IR::ValueOpcode::ImageQueryDimensions) {
|
||||
@@ -811,14 +729,15 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return;
|
||||
}
|
||||
if (op == IR::ValueOpcode::ImageRead) {
|
||||
const auto numeric_class = image.numeric_class;
|
||||
const auto condition = ctx.Arg(inst, 2);
|
||||
ctx.Define(
|
||||
inst,
|
||||
EmitValueOrDefaultIfCondition(
|
||||
state, condition, TypeU32Vector(state, 4), ConstantU32CompositeZero(state, 4),
|
||||
[&]() {
|
||||
return EmitImageAccess(
|
||||
ctx, inst, TypeU32Vector(state, 4),
|
||||
const auto color = EmitImageAccess(
|
||||
ctx, inst, ImageVectorType(state, numeric_class, 4),
|
||||
[&](uint32_t resource, uint32_t array_index) {
|
||||
const auto& candidate = state.program.info.images[resource];
|
||||
const auto dimension = candidate.dimension;
|
||||
@@ -837,9 +756,10 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
descriptor, coord, spv::ImageOperandsLodMask,
|
||||
LodU32(ctx, mem, *address, mem.image_dimension));
|
||||
}
|
||||
return ResultVector(ctx, UnpackImageTexel(ctx, mem, fetched),
|
||||
candidate.numeric_class, false, mem);
|
||||
return fetched;
|
||||
});
|
||||
return ResultVector(ctx, UnpackImageTexel(ctx, mem, color), numeric_class,
|
||||
false, mem);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
@@ -860,7 +780,7 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
if (op == IR::ValueOpcode::ImageSampleRaw || op == IR::ValueOpcode::ImageGatherRaw) {
|
||||
const auto dimension = image.dimension;
|
||||
const auto& dimension_info = ImageDimensionInfoFor(dimension);
|
||||
const auto layout = Layout(mem, mem.image_dimension);
|
||||
const auto layout = Layout(mem);
|
||||
const auto numeric_class = image.numeric_class;
|
||||
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
|
||||
IR::Value offset_value;
|
||||
@@ -894,6 +814,9 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
ctx.Fail(inst, "has an unsupported 1D-array gather");
|
||||
return;
|
||||
}
|
||||
const auto result_numeric_class = dref ? Prospero::TextureNumericClass::Float
|
||||
: numeric_class;
|
||||
const auto result_type = ImageVectorType(state, result_numeric_class, 4);
|
||||
uint32_t component_or_dref;
|
||||
if (dref) {
|
||||
component_or_dref = layout.dref != NoImageComponent
|
||||
@@ -902,13 +825,9 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
} else {
|
||||
component_or_dref = ConstantU32(state, ImageGatherSource(state, mem));
|
||||
}
|
||||
const auto samplers = EmitImageSamplers(ctx, inst);
|
||||
const auto sampler_id = LoadSamplerDescriptor(state, mem.sampler);
|
||||
const auto EmitGather = [&](uint32_t resource, uint32_t array_index) {
|
||||
const auto& candidate = state.program.info.images[resource];
|
||||
const auto numeric_class = dref ? Prospero::TextureNumericClass::Float
|
||||
: candidate.numeric_class;
|
||||
const auto result_type = ImageVectorType(state, numeric_class, 4);
|
||||
const auto sampler = samplers[static_cast<size_t>(candidate.numeric_class)];
|
||||
const auto dimension = candidate.dimension;
|
||||
const auto coord = CoordF32(
|
||||
ctx, mem, *address, layout.coord,
|
||||
@@ -922,8 +841,7 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
offset = PackedOffset(ctx, offset_value, dimension, operand_mask);
|
||||
}
|
||||
const auto EmitMip = [&](uint32_t mip) {
|
||||
const auto sampled = MakeSampledImage(state, resource, sampler.id, mip, array_index,
|
||||
sampler.dynamic);
|
||||
const auto sampled = MakeSampledImage(state, resource, sampler_id, mip, array_index);
|
||||
const auto sample = state.builder.AllocateId();
|
||||
const std::array operands {operand_mask, offset};
|
||||
state.builder.AddFunction(
|
||||
@@ -932,15 +850,15 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
std::span(operands).first(operand_mask != 0u ? 2u : 0u));
|
||||
return sample;
|
||||
};
|
||||
const auto gathered = candidate.mip_mode == IR::ImageMipMode::Dynamic && candidate.mip_count > 1u
|
||||
return candidate.mip_mode == IR::ImageMipMode::Dynamic && candidate.mip_count > 1u
|
||||
? EmitIndexSwitch(
|
||||
state, GatherMip(ctx, inst, mem, *address, layout, candidate.mip_count),
|
||||
candidate.mip_count, result_type, EmitMip)
|
||||
: EmitMip(0u);
|
||||
return ResultVector(ctx, UnpackImageGather(ctx, mem, gathered), numeric_class,
|
||||
false, mem, true);
|
||||
};
|
||||
ctx.Define(inst, EmitImageAccess(ctx, inst, TypeU32Vector(state, 4), EmitGather));
|
||||
const auto sample = EmitImageAccess(ctx, inst, result_type, EmitGather);
|
||||
ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample),
|
||||
result_numeric_class, false, mem, true));
|
||||
return;
|
||||
}
|
||||
const bool explicit_lod = HasFlag(mem, Decoder::ImageSampleFlagDerivative) ||
|
||||
@@ -953,8 +871,10 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
} else if (dref) {
|
||||
opcode = spv::OpImageSampleDrefImplicitLod;
|
||||
}
|
||||
uint32_t result_type = ImageVectorType(state, numeric_class, 4);
|
||||
uint32_t dref_value = 0;
|
||||
if (dref) {
|
||||
result_type = TypeF32(state);
|
||||
dref_value = ZeroF32(state);
|
||||
if (layout.dref != NoImageComponent) {
|
||||
dref_value = AddressF32(ctx, mem, *address, layout.dref);
|
||||
@@ -980,16 +900,23 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
operands[1] = AddressF32(ctx, mem, *address, layout.bias);
|
||||
operand_count = 2;
|
||||
}
|
||||
const auto& samplers = state.program.info.samplers.at(mem.sampler).indirect_resources;
|
||||
uint32_t sampler_index = 0;
|
||||
if (!samplers.empty()) {
|
||||
const auto* handle = inst.Arg(1).ResolveInstruction();
|
||||
if (handle == nullptr || handle->GetOpcode() != IR::ValueOpcode::GetSamplerResource)
|
||||
ctx.Fail(inst, "has invalid finite sampler key provenance");
|
||||
sampler_index = EmitIndexSwitch(
|
||||
state, ctx.Def(handle->Arg(0)), static_cast<uint32_t>(samplers.size()),
|
||||
TypeU32(state), [&](uint32_t ordinal) { return ConstantU32(state, samplers[ordinal]); });
|
||||
}
|
||||
if (layout.offset != NoImageComponent &&
|
||||
!offset_value.IsImmediate() && !state.maintenance8_enabled) {
|
||||
ctx.Fail(inst, "requires maintenance8 for a dynamic sample texel offset");
|
||||
}
|
||||
const auto samplers = EmitImageSamplers(ctx, inst);
|
||||
const auto sampler_id = LoadSamplerDescriptor(state, mem.sampler, sampler_index);
|
||||
const auto EmitSample = [&](uint32_t resource, uint32_t array_index) {
|
||||
const auto& candidate = state.program.info.images[resource];
|
||||
const auto result_type = dref ? TypeF32(state)
|
||||
: ImageVectorType(state, candidate.numeric_class, 4);
|
||||
const auto sampler = samplers[static_cast<size_t>(candidate.numeric_class)];
|
||||
auto sample_operands = operands;
|
||||
auto sample_operand_count = operand_count;
|
||||
if (layout.offset != NoImageComponent) {
|
||||
@@ -1001,16 +928,19 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
CoordF32(ctx, mem, *address, layout.coord,
|
||||
ImageDimensionInfoFor(candidate.dimension).coordinate_components,
|
||||
candidate.cube);
|
||||
const auto sampled = MakeSampledImage(state, resource, sampler.id, 0u, array_index,
|
||||
sampler.dynamic);
|
||||
const auto sampled = MakeSampledImage(state, resource, sampler_id, 0u, array_index,
|
||||
sampler_index != 0u);
|
||||
const auto sample = state.builder.AllocateId();
|
||||
state.builder.AddFunction(opcode, result_type, sample, sampled, coord,
|
||||
std::span(&dref_value, dref ? 1u : 0u),
|
||||
std::span<const uint32_t>(sample_operands).first(sample_operand_count));
|
||||
return ResultVector(ctx, dref ? sample : UnpackImageTexel(ctx, mem, sample),
|
||||
candidate.numeric_class, dref, mem);
|
||||
return sample;
|
||||
};
|
||||
ctx.Define(inst, EmitImageAccess(ctx, inst, TypeU32Vector(state, 4), EmitSample));
|
||||
auto result = EmitImageAccess(ctx, inst, result_type, EmitSample);
|
||||
if (!dref) {
|
||||
result = UnpackImageTexel(ctx, mem, result);
|
||||
}
|
||||
ctx.Define(inst, ResultVector(ctx, result, numeric_class, dref, mem));
|
||||
return;
|
||||
}
|
||||
if (image_info.access == IR::ImageAccess::Atomic) {
|
||||
|
||||
@@ -272,8 +272,6 @@ struct ImageSampleLayout {
|
||||
uint32_t lod = NoImageComponent;
|
||||
uint32_t grad_x = NoImageComponent;
|
||||
uint32_t grad_y = NoImageComponent;
|
||||
|
||||
bool operator==(const ImageSampleLayout&) const = default;
|
||||
};
|
||||
|
||||
struct F32Class {
|
||||
|
||||
@@ -648,6 +648,10 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
|
||||
return SpecializationFail("indirect image specialization has no typed candidate");
|
||||
}
|
||||
const auto& image_class = specialization.images[exemplar];
|
||||
const auto is_2d = [](Decoder::ImageDimension dimension) {
|
||||
return dimension == Decoder::ImageDimension::Dim2D ||
|
||||
dimension == Decoder::ImageDimension::Dim2DArray;
|
||||
};
|
||||
for (uint32_t candidate = 0; candidate < specialization.images.size(); candidate++) {
|
||||
auto& image = specialization.images[candidate];
|
||||
if (image.indirect_root != root_index) {
|
||||
@@ -661,7 +665,11 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
|
||||
image.shader_swizzle = image_class.shader_swizzle;
|
||||
image.cube = image_class.cube;
|
||||
}
|
||||
if (image.mip_count != image_class.mip_count ||
|
||||
const bool same_coordinates = image.dimension == image_class.dimension &&
|
||||
image.cube == image_class.cube;
|
||||
if (image.numeric_class != image_class.numeric_class ||
|
||||
(!same_coordinates && !(is_2d(image.dimension) && is_2d(image_class.dimension))) ||
|
||||
image.mip_count != image_class.mip_count ||
|
||||
image.conversion_format != image_class.conversion_format ||
|
||||
image.shader_swizzle != image_class.shader_swizzle) {
|
||||
return SpecializationFail(
|
||||
@@ -1309,20 +1317,9 @@ void ApplyResourceSpecialization(Program& program, const ResourceSpecialization&
|
||||
}
|
||||
|
||||
SamplerPlan sampler_plan;
|
||||
EXIT_IF(!BuildSamplerPlan(program.info, sampler_plan));
|
||||
auto& samplers = program.info.samplers;
|
||||
auto& sampled_pairs = program.info.sampled_pairs;
|
||||
const auto original_pair_count = sampled_pairs.size();
|
||||
for (size_t index = 0; index < original_pair_count; ++index) {
|
||||
const auto pair = sampled_pairs[index];
|
||||
uint32_t classes = 1u << static_cast<uint32_t>(images[pair.image].numeric_class);
|
||||
for (const auto candidate: images[pair.image].indirect_resources) {
|
||||
const auto bit = 1u << static_cast<uint32_t>(images[candidate].numeric_class);
|
||||
if ((classes & bit) != 0u) continue;
|
||||
classes |= bit;
|
||||
sampled_pairs.push_back({candidate, pair.sampler, pair.first_use_pc});
|
||||
}
|
||||
}
|
||||
EXIT_IF(!BuildSamplerPlan(program.info, sampler_plan));
|
||||
const auto original_sampler_count = samplers.size();
|
||||
samplers.reserve(sampler_plan.sampler_count);
|
||||
for (uint32_t index = 0; index < sampler_plan.sampler_count; index++) {
|
||||
@@ -1434,6 +1431,10 @@ void ApplyResourceSpecialization(Program& program, const ResourceSpecialization&
|
||||
memory.sampler = sampler_plan.mapping[memory.sampler][type];
|
||||
EXIT_IF(memory.sampler == UINT32_MAX);
|
||||
}
|
||||
EXIT_IF(image.indirect_root == memory.resource &&
|
||||
inst.GetOpcode() != ValueOpcode::ImageSampleRaw &&
|
||||
inst.GetOpcode() != ValueOpcode::ImageGatherRaw &&
|
||||
inst.GetOpcode() != ValueOpcode::ImageRead);
|
||||
}
|
||||
}
|
||||
for (auto& memory: memory_info) {
|
||||
|
||||
Reference in New Issue
Block a user