diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp index 3e4ca1aac..4b45f5dc4 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp @@ -47,7 +47,8 @@ constexpr std::array Red{0, 64, 128, 255}; alignas(256) std::array Input{}; alignas(256) std::array CubeInput{}; -alignas(256) std::array Output{}; +alignas(256) std::array Output{}; +std::uint32_t Swizzle = 0xfacu; alignas(256) std::array Extra{}; alignas(4096) std::array Texels{}; @@ -71,6 +72,14 @@ alignas(256) constexpr std::array OffsetCode{ 0x7e0c0308, 0xf0f80108, 0x00820901, 0xbf8c3f70, 0x34160082, 0xe0701000, 0x8001090b, 0xbf810000, }; +alignas(256) constexpr std::array QuadCode{ + 0x1614008c, 0xe03c1000, 0x8000010a, 0x7e0802f3, 0x7e0a02f3, 0x7e0c02f3, 0x7e0e02f3, 0xbf8c3f70, 0xf0bc0f08, 0x00820401, 0xbf8c3f70, 0x34160084, 0xe0781000, 0x8001040b, 0xbf810000, 0xbf810000, 0xbf810000, +}; + +alignas(256) constexpr std::array PairCode{ + 0x1614008c, 0xe03c1000, 0x8000010a, 0x7e0802f3, 0x7e0a02f3, 0x7e0c02f3, 0x7e0e02f3, 0xbf8c3f70, 0xf0bc0608, 0x00820401, 0xbf8c3f70, 0x34160084, 0xe0781000, 0x8001040b, 0xbf810000, 0xbf810000, 0xbf810000, +}; + struct Sampler { std::uint32_t clamp; std::uint32_t filter; @@ -91,7 +100,7 @@ std::array TextureDescriptor(std::uint32_t format, bool cube = static_cast(address >> 8u), static_cast((address >> 40u) & 0xffu) | (format << 20u) | (((Side - 1u) & 3u) << 30u), ((Side - 1u) >> 2u) | ((Side - 1u) << 14u), - 0xfacu | ((cube ? TypeCube : Type2D) << 28u), + Swizzle | ((cube ? TypeCube : Type2D) << 28u), cube ? 5u : 0u, 0u, 0u, 0u, }; } @@ -240,6 +249,32 @@ void Run(AgcDriver::VulkanDevice& device, const Sampler& sampler, const char* na } } +constexpr std::uint32_t Select(std::uint32_t x, std::uint32_t y, std::uint32_t z, std::uint32_t w) { + return x | (y << 3u) | (z << 6u) | (w << 9u); +} + +void RunChannels(AgcDriver::VulkanDevice& device, std::span code, std::array channels, std::uint32_t registers, std::uint32_t firstChannel, const char* name) { + const Sampler sampler{ClampEdge, FilterBilinear}; + Swizzle = Select(channels[0], channels[1], channels[2], channels[3]); + Output.fill(-2.0f); + const auto result = Compile(device, Format8888UNorm, sampler, code); + Swizzle = 0xfacu; + device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); + device.WaitIdle(); + for (std::uint32_t tid = 0; tid < Threads; ++tid) { + const float compared = Expected(tid, sampler, false); + for (std::uint32_t reg = 0; reg < 4u; ++reg) { + float expected = -1.0f; + if (reg < registers) { + const auto select = channels[firstChannel + reg]; + expected = select == 0u ? 0.0f : select == 1u ? 1.0f : compared; + } + const float actual = Output[tid * 4u + reg]; + Require(std::fabs(actual - expected) <= 1e-4f, std::string(name) + ": thread " + std::to_string(tid) + " register " + std::to_string(reg) + " returned " + std::to_string(actual) + ", expected " + std::to_string(expected)); + } + } +} + void RunCube(AgcDriver::VulkanDevice& device, const Sampler& sampler, const char* name) { FillCubeTexels(); for (std::uint32_t tid = 0; tid < Threads; ++tid) { @@ -313,6 +348,19 @@ int main() { Run(*device, {ClampWrap, FilterBilinear}, "cached bilinear comparison with offsets", true, true); } Reject(*device, Format32Float, {ClampEdge, FilterPoint}, "native comparison with a nonconstant texel offset requires", OffsetCode, false); + RunChannels(*device, QuadCode, {4, 5, 6, 7}, 4, 0, "view R,G,B,A"); + RunChannels(*device, QuadCode, {0, 0, 0, 0}, 4, 0, "view 0,0,0,0"); + RunChannels(*device, QuadCode, {1, 1, 1, 1}, 4, 0, "view 1,1,1,1"); + RunChannels(*device, QuadCode, {0, 4, 1, 5}, 4, 0, "view 0,R,1,G"); + RunChannels(*device, QuadCode, {4, 0, 0, 1}, 4, 0, "view R,0,0,1"); + RunChannels(*device, QuadCode, {5, 4, 5, 6}, 4, 0, "view G,R,B,A"); + RunChannels(*device, QuadCode, {7, 4, 4, 4}, 4, 0, "view A,R,R,R"); + RunChannels(*device, PairCode, {0, 4, 4, 4}, 2, 1, "view 0,R,R,R, dmask 6"); + Swizzle = Select(4, 2, 4, 4); + Reject(*device, Format8888UNorm, {ClampEdge, FilterPoint}, "every view channel is a constant or a color channel"); + Swizzle = Select(5, 5, 5, 5); + Reject(*device, Format8888UNorm, {ClampEdge, FilterPoint}, "a view channel selects red"); + Swizzle = 0xfacu; Reject(*device, Format8888UInt, {ClampEdge, FilterPoint}, "unsupported format"); Reject(*device, Format8888UNorm, {ClampMirror, FilterPoint}, "wrap, clamp-to-edge or clamp-to-border"); Reject(*device, Format8888UNorm, {ClampHalfBorder, FilterPoint}, "wrap, clamp-to-edge or clamp-to-border"); diff --git a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp index 5ca52cecf..4b5c84f79 100644 --- a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp @@ -528,7 +528,15 @@ std::uint32_t emulatedCompareState(const ShaderInfo& info, const ResourceSnapsho const auto reference = colorCompareReference(format); const auto type = rawImageType(descriptor); if (type != ImageType::Color2D && type != ImageType::Color2DArray && type != ImageType::Cube) throw std::runtime_error("comparison sampling of a color texture is implemented only for 2D, 2D array and cube views"); - if ((descriptorImageSwizzle(descriptor) & 0x7u) != 4u) throw std::runtime_error("comparison sampling of a color texture is implemented only when the view's X channel is red"); + bool redChannel = false; + bool compareChannel = false; + for (std::uint32_t channel = 0u; channel < 4u; ++channel) { + const auto select = (descriptorImageSwizzle(descriptor) >> (channel * 3u)) & 0x7u; + if (select == 2u || select == 3u) throw std::runtime_error("comparison sampling of a color texture is implemented only when every view channel is a constant or a color channel"); + redChannel = redChannel || select == 4u; + compareChannel = compareChannel || select >= 4u; + } + if (compareChannel && !redChannel) throw std::runtime_error("comparison sampling of a color texture is implemented only when a view channel selects red"); std::optional samplerState; for (const auto& pair : info.sampledPairs) { if (pair.image != index) continue; diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp index 57dd83fd7..94480ef85 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp @@ -333,7 +333,7 @@ std::uint32_t ResultVector(SpirvValueEmitContext& ctx, const ImageEmitAccess& ac std::uint32_t component[4] = {}; for (std::uint32_t index = 0; index < 4u; index++) { if (dref) { - component[index] = index == 0u ? F32BitsToU32(ctx, value) : ConstantU32(state, 0); + component[index] = F32BitsToU32(ctx, value); continue; } const auto scalar = state.module.AllocateId(); @@ -1336,7 +1336,8 @@ void EmitEmulatedCompareSample(SpirvValueEmitContext& ctx, const ImageEmitAccess else state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 2), coord, ux, uy); const auto texel = state.module.AllocateId(); state.module.AddFunction(spv::OpImageFetch, ImageVectorType(state, IrTextureNumericClass::Float, 4), texel, descriptor, coord, spv::ImageOperandsLodMask, ConstantU32(state, 0u)); - auto red = extract(f32, texel, 0u); + auto red = extract(f32, texel, 3u); + for (std::uint32_t channel = 3u; channel-- > 0u;) red = Select(state, f32, equal(RuntimeImageSwizzle(state, mem.resource, channel), 4u), extract(f32, texel, channel), red); red = Select(state, f32, equal(modeX, EmulatedCompare::AddressBorder), Select(state, f32, inside(x, width), red, borderRed), red); red = Select(state, f32, equal(modeY, EmulatedCompare::AddressBorder), Select(state, f32, inside(y, height), red, borderRed), red); constexpr std::array operations{spv::OpFOrdLessThan, spv::OpFOrdEqual, spv::OpFOrdLessThanEqual, spv::OpFOrdGreaterThan, spv::OpFOrdNotEqual, spv::OpFOrdGreaterThanEqual}; @@ -1377,7 +1378,15 @@ void EmitEmulatedCompareSample(SpirvValueEmitContext& ctx, const ImageEmitAccess const auto bottom = ext(f32, GLSLstd450FMix, {compareTexel(x0, y1), compareTexel(x1, y1), weightU}); return ext(f32, GLSLstd450FMix, {top, bottom, weightV}); }); - ctx.Define(access.inst, TableResult(ctx, access, ResultVector(ctx, access, result, setup.numericClass, true, false))); + const auto channelResult = [&](std::uint32_t channel) { + const auto selector = RuntimeImageSwizzle(state, mem.resource, channel); + return Select(state, f32, equal(selector, 0u), zero, Select(state, f32, equal(selector, 1u), one, result)); + }; + std::uint32_t channels[4] = {}; + for (std::uint32_t channel = 0; channel < 4u; ++channel) channels[channel] = channelResult(channel); + const auto value = state.module.AllocateId(); + state.module.AddFunction(spv::OpCompositeConstruct, TypeF32Vector(state, 4), value, channels[0], channels[1], channels[2], channels[3]); + ctx.Define(access.inst, TableResult(ctx, access, ResultVector(ctx, access, value, IrTextureNumericClass::Float, false, false))); } void EmitSampleOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access, const SampleSetup& setup) { diff --git a/core/shader/recompiler/Translation/src/MemoryInstructions/ImageInstructions.cpp b/core/shader/recompiler/Translation/src/MemoryInstructions/ImageInstructions.cpp index 4b5b9a0fa..f29c3029b 100644 --- a/core/shader/recompiler/Translation/src/MemoryInstructions/ImageInstructions.cpp +++ b/core/shader/recompiler/Translation/src/MemoryInstructions/ImageInstructions.cpp @@ -204,15 +204,7 @@ bool TranslationContext::imageSample(const RdnaInstruction& inst) { IrValue* sampler = getSamplerResource(memory); IrValue* address = makeImageAddress(inst, inst.source0); IrValue& result = ir.Emit(IrOpcode::ImageSampleRaw, IrOpcodeType(IrOpcode::ImageSampleRaw), {resource, sampler, address}, addMemoryInfo(memory, inst.programCounter)); - const bool dref = (memory.imageSampleFlags & RdnaImageSampleFlagCompare) != 0u; - if (dref && memory.dataBits != 16u) { - IrValue& component = ir.CompositeExtract(result, 0u); - for (std::uint32_t index = 0; index < memory.dataDwords; ++index) { - writeOperand(offsetOperand(inst.destination, index), &component); - } - } else { - writeImageComponents(inst.destination, &result, memory, 4u); - } + writeImageComponents(inst.destination, &result, memory, 4u); return true; }