mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
fix(shader): let image writes, explicit-LOD gathers and size queries pass the masked-lane walk
Following the ImageRead case, every image operation was checked against
the two lists the masked-lane walk uses: accesses guarded by a trailing exec
operand, and lane-local values.
- ImageWrite carries its exec last like StoreBuffer, and EmitWriteOp stores
only inside an if on it (bindless tables and FMASK refuse the store). It
joins the exec-guarded list.
- A gather with an LOD of its own (gather4_l, gather4_lz) or outside a pixel
shader reads only its lane's address, like the explicit-LOD samples the
walk already follows. A pixel gather without one still keeps the select,
since on the GPU it takes its LOD from the quad.
- ImageQueryDimensions returns the size of its lane's level of the image and
is lane-local.
Left out on purpose:
- ImageQueryLod and implicit-LOD samples read their quad.
- ImageBvhIntersectRay has a trailing exec operand, but its emitter ignores
it and records faults in every lane.
- Image, buffer, shared and address atomics are guarded by their exec in the
emitter too, but the test from 60c65521 pins them as keeping the select,
and no refused shader has needed them so far.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
83f8c193c9
commit
fda4aa90c9
2 files changed
+35
-3
No files matched your search
@@ -592,7 +592,7 @@ int main() {
|
||||
return b.eliminate() == 0u && !removed(written);
|
||||
});
|
||||
|
||||
const auto sample = [](IrShaderStage stage, std::uint32_t sampleFlags) {
|
||||
const auto sample = [](IrShaderStage stage, std::uint32_t sampleFlags, IrOpcode opcode = IrOpcode::ImageSampleRaw) {
|
||||
Builder b;
|
||||
b.program.Resources().stage = stage;
|
||||
MemoryInfo memory;
|
||||
@@ -608,7 +608,7 @@ int main() {
|
||||
MemoryFlags flags{0u, 0u};
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &flags, sizeof(flags));
|
||||
auto& sampled = b.emit(IrOpcode::ImageSampleRaw, IrType::U32x4, {&image, &sampler, &address}, bits);
|
||||
auto& sampled = b.emit(opcode, IrType::U32x4, {&image, &sampler, &address}, bits);
|
||||
b.keep(b.select(exec, b.emit(IrOpcode::CompositeExtractU32x4, IrType::U32, {&sampled, &b.constant(0u)}), old));
|
||||
b.eliminate();
|
||||
return removed(written);
|
||||
@@ -622,6 +622,26 @@ int main() {
|
||||
return sample(IrShaderStage::Pixel, RdnaImageSampleFlagLevelZero) && sample(IrShaderStage::Mesh, 0u);
|
||||
});
|
||||
|
||||
passed &= run("a pixel gather without an LOD of its own takes it from the quad and keeps the select", [&] {
|
||||
return !sample(IrShaderStage::Pixel, 0u, IrOpcode::ImageGatherRaw) && !sample(IrShaderStage::Pixel, RdnaImageSampleFlagBias, IrOpcode::ImageGatherRaw);
|
||||
});
|
||||
|
||||
passed &= run("a gather with an explicit LOD reads only its lane and drops the select", [&] {
|
||||
return sample(IrShaderStage::Pixel, RdnaImageSampleFlagLevelZero, IrOpcode::ImageGatherRaw) && sample(IrShaderStage::Pixel, RdnaImageSampleFlagLod, IrOpcode::ImageGatherRaw) &&
|
||||
sample(IrShaderStage::Mesh, 0u, IrOpcode::ImageGatherRaw);
|
||||
});
|
||||
|
||||
passed &= run("an image size query reads only its lane's level and drops the select", [] {
|
||||
Builder b;
|
||||
auto& exec = b.mask(16u);
|
||||
auto& old = b.lane();
|
||||
auto& written = b.select(exec, b.add(old, 1u), old);
|
||||
auto& address = b.emit(IrOpcode::MakeImageAddress, IrType::ImageAddress, {&written});
|
||||
auto& size = b.emit(IrOpcode::ImageQueryDimensions, IrType::U32x4, {&b.emit(IrOpcode::GetImageResource, IrType::ImageResource, {}), &address});
|
||||
b.keep(b.select(exec, b.emit(IrOpcode::CompositeExtractU32x4, IrType::U32, {&size, &b.constant(0u)}), old));
|
||||
return b.eliminate() == 1u && removed(written);
|
||||
});
|
||||
|
||||
const auto guarded = [](IrOpcode opcode, bool sameExec, bool asActive) {
|
||||
Builder b;
|
||||
auto& wide = b.mask(48u);
|
||||
@@ -643,6 +663,12 @@ int main() {
|
||||
b.keep(b.select(exec, b.emit(IrOpcode::CompositeExtractU32x4, IrType::U32, {&read, &b.constant(0u)}), old));
|
||||
break;
|
||||
}
|
||||
case IrOpcode::ImageWrite: {
|
||||
auto& address = b.emit(IrOpcode::MakeImageAddress, IrType::ImageAddress, {&old, &old});
|
||||
auto& texel = b.emit(IrOpcode::CompositeConstructU32x4, IrType::U32x4, {&written, &old, &old, &old});
|
||||
b.emit(opcode, IrType::Void, {&b.emit(IrOpcode::GetImageResource, IrType::ImageResource, {}), &address, &texel, &active});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
b.emit(opcode, IrType::Void, {&b.emit(IrOpcode::CompositeConstructU32x4, IrType::U32x4, {&written, &old, &old, &old}), &active});
|
||||
break;
|
||||
@@ -659,6 +685,10 @@ int main() {
|
||||
return !guarded(IrOpcode::WriteSharedU32, false, false) && !guarded(IrOpcode::LoadBufferU32, false, false) && !guarded(IrOpcode::ImageRead, false, false) && !guarded(IrOpcode::SetAttribute, false, false);
|
||||
});
|
||||
|
||||
passed &= run("an image write under the write's exec drops the select, under a wider exec or as its exec keeps it", [&] {
|
||||
return guarded(IrOpcode::ImageWrite, true, false) && !guarded(IrOpcode::ImageWrite, false, false) && !guarded(IrOpcode::ImageWrite, true, true);
|
||||
});
|
||||
|
||||
passed &= run("a value used as a store's exec keeps the select", [&] {
|
||||
return !guarded(IrOpcode::WriteSharedU32, true, true);
|
||||
});
|
||||
|
||||
@@ -171,6 +171,7 @@ bool isLaneLocal(IrOpcode opcode) {
|
||||
case IrOpcode::FPFrexpMant64:
|
||||
case IrOpcode::FPFrexpExp64:
|
||||
case IrOpcode::MakeImageAddress:
|
||||
case IrOpcode::ImageQueryDimensions:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -205,6 +206,7 @@ bool readsOnlyWhereActive(IrOpcode opcode) {
|
||||
case IrOpcode::WriteSharedU32x4:
|
||||
case IrOpcode::SetAttribute:
|
||||
case IrOpcode::ImageRead:
|
||||
case IrOpcode::ImageWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -212,7 +214,7 @@ bool readsOnlyWhereActive(IrOpcode opcode) {
|
||||
}
|
||||
|
||||
bool isExplicitLodSample(const IrProgram& program, const IrValue& value) {
|
||||
if (value.Opcode() != IrOpcode::ImageSampleRaw) return false;
|
||||
if (value.Opcode() != IrOpcode::ImageSampleRaw && value.Opcode() != IrOpcode::ImageGatherRaw) return false;
|
||||
if (program.Resources().stage != IrShaderStage::Pixel) return true;
|
||||
const auto index = value.Flags<MemoryFlags>().index;
|
||||
if (index >= program.Resources().memoryInfo.size()) return false;
|
||||
|
||||
Reference in new issue
Block a user