diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp index f55927e50d..7847b0d021 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp @@ -5773,6 +5773,49 @@ void verifyVolumeArrayLoadMode() { expectFailure([&] { static_cast(ResourceMaterializer::RuntimeImageMode(store, descriptor, ResourceMaterializer::RuntimeImageModes(store))); }, "incompatible with the static runtime image interface", "2D-array image atomic on a 3D T# was accepted"); } +void verifyVolumeBiasTableRejection() { + using namespace ShaderRecompiler; + namespace Abi = ImageTableAbi; + ImageResource image{}; + image.resourceClass = ImageResourceClass::Sampled; + image.dimension = RdnaImageDimension::Dim2D; + image.singleMipPlaneBiasCompatible = true; + const DescriptorValue volume{{0x100u, TableFormat8888UNorm << 20u, 0u, (TableType3D << 28u) | 0xfacu, 0u, 0u, 0u, 0u}, 8u, 0xffu}; + const auto directModes = ResourceMaterializer::RuntimeImageModes(image); + require(directModes[ResourceMaterializer::RuntimeImageMode(image, volume, directModes)].dimension == RdnaImageDimension::Dim3D, "volume bias: a supported direct one-slice volume was rejected"); + image.table = 0u; + image.tableOperation = TableOperation::Supported; + ShaderInfo info{}; + info.images.push_back(image); + info.runtimeImageModes.push_back(ResourceMaterializer::RuntimeImageModes(image)); + ResourceSnapshot snapshot{}; + snapshot.tables.tables.resize(1u); + auto& column = snapshot.tables.tables.front(); + column.records = 2u; + column.keys = 2u; + column.codes = {0u, 1u}; + auto plane = volume; + plane.dwords[3] = (TableType2D << 28u) | 0xfacu; + for (const auto [word, bits] : {std::pair{4u, 1u}, std::pair{3u, 1u << 12u}, std::pair{3u, 1u << 16u}, std::pair{5u, 1u << 4u}}) { + auto invalid = volume; + invalid.dwords[word] |= bits; + snapshot.tables.words = {plane, invalid}; + const auto resolved = ResourceMaterializer::ResolveImageTables(info, snapshot); + const auto mapStart = resolved.map.at(Abi::TableHeader(0u) + Abi::TableMapStart); + const auto good = resolved.map.at(mapStart); + const auto bad = resolved.map.at(mapStart + 1u); + require(resolved.elements.size() == 1u && (resolved.map.at(good + Abi::ImageRecordFlags) & Abi::PoisonFlag) == 0u, "volume bias: an unsupported table neighbour prevented the valid plane binding"); + const auto fault = resolved.map.at(bad + Abi::ImageRecordFlags); + require((fault & Abi::PoisonFlag) != 0u && resolved.faults == 1u, "volume bias: an unsupported volume was not marked to fault on selection"); + const auto& poison = resolved.poison.at(fault & ~Abi::PoisonFlag); + require(poison.reason == Abi::PoisonReason::Dimension && poison.words == invalid.dwords, "volume bias: the table fault lost its reason or descriptor"); + image.table = NoTable; + const auto match = ResourceMaterializer::TryRuntimeImageMode(image, invalid, directModes); + require(!match.mode.has_value() && match.reason == Abi::PoisonReason::Dimension, "volume bias: an unsupported direct descriptor selected a mode"); + expectFailure([&] { static_cast(ResourceMaterializer::RuntimeImageMode(image, invalid, directModes)); }, "one slice and mip level zero only", "volume bias: direct use of an unsupported descriptor was accepted"); + } +} + void verifyDynamicStorageMipChain() { using namespace ShaderRecompiler; ImageResource image{}; @@ -5921,6 +5964,7 @@ int main(int argc, char** argv) { verifyGuardedNullPointers(); verifyCubeDepthCompareMode(); verifyVolumeArrayLoadMode(); + verifyVolumeBiasTableRejection(); verifyDynamicStorageMipChain(); verifySrgbDepthCompareModes(); verifyQuadMaskNeighbourReads(); diff --git a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp index 9e48c35387..ca0eef25c8 100644 --- a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp @@ -1672,7 +1672,7 @@ RuntimeImageModeMatch ResourceMaterializer::TryRuntimeImageMode(const ImageResou } } if (image.singleMipPlaneBiasCompatible && image.dimension == RdnaImageDimension::Dim2D && decoded.dimension == RdnaImageDimension::Dim3D) { - if ((descriptor.dwords[4] & 0x1fffu) != 0u || (descriptor.dwords[3] & 0xff000u) != 0u || (descriptor.dwords[5] & 0xf0u) != 0u) throw std::runtime_error("2D biased sampling of a volume requires one slice and mip level zero only"); + if ((descriptor.dwords[4] & 0x1fffu) != 0u || (descriptor.dwords[3] & 0xff000u) != 0u || (descriptor.dwords[5] & 0xf0u) != 0u) return reject(PoisonReason::Dimension, "2D biased sampling of a volume requires one slice and mip level zero only"); } for (std::uint32_t index = 0u; index < modes.size(); ++index) { const auto& mode = modes[index];