Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,11 @@ void verifyUnusedUnnormalizedSampler() {
auto selected = info;
selected.images[0].indirectRoot = 0u;
expectFailure([&] { static_cast<void>(populate(selected)); }, "unnormalized guest sampler samples an image selected at run time, which is not implemented", "unnormalized samplers: an image table root was accepted");
snapshot.images[0].dwords[3] = 0xa0000041u;
const auto constant = populate(info);
require(constant.size() == 1u && constant[0].samplerUnnormalized == std::vector<bool>{true}, "unnormalized samplers: a 3D view whose channels select constants was bound or refused");
expectFailure([&] { static_cast<void>(populate(selected)); }, "unnormalized guest sampler samples an image selected at run time, which is not implemented", "unnormalized samplers: an image table root whose channels select constants was accepted");
snapshot.images[0].dwords[3] = 0x90000facu;
auto compared = info;
compared.samplers[0].depthCompare = true;
expectFailure([&] { static_cast<void>(populate(compared)); }, "unnormalized guest sampler is used with depth comparison, which is not implemented", "unnormalized samplers: a depth-compare S# without live uses was accepted");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ constexpr std::int32_t Width = 13;
constexpr std::int32_t Height = 7;
constexpr std::uint32_t StorageLevels = 4;
constexpr std::uint32_t Format8888UNorm = 56;
constexpr std::uint32_t Format32UInt = 20;
constexpr std::uint32_t Format32SInt = 21;
constexpr std::uint32_t Type1D = 8;
constexpr std::uint32_t Type2D = 9;
constexpr std::uint32_t Type3D = 10;
constexpr std::uint32_t OneZeroOneZero = 0x041u;
constexpr std::uint8_t OtherLevelTexel = 0x40u;
alignas(256) std::array<std::uint32_t, Threads * Words> Buffer{};
alignas(256) std::array<std::uint8_t, 16384> SingleLevel{};
Expand All @@ -44,6 +49,14 @@ alignas(256) constexpr std::array<std::uint32_t, 40> Code{

constexpr std::array<const char*, 3> Instructions{"image_sample_lz", "image_sample_l 2.7", "image_sample"};

alignas(256) constexpr std::array<std::uint32_t, 34> ConstantCode{
0x34020086, 0xe0301000, 0x80000201, 0xe0301004, 0x80000301, 0xbf8c3f70, 0xf09c0608, 0x00610802,
0xf11c0208, 0x00610a02, 0xf11c0408, 0x00610e02, 0xbf8c3f70, 0xe0701010, 0x80000801, 0xe0701014,
0x80000901, 0xe0701018, 0x80000a01, 0xe070101c, 0x80000b01, 0xe0701020, 0x80000c01, 0xe0701024,
0x80000d01, 0xe0701028, 0x80000e01, 0xe070102c, 0x80000f01, 0xe0701030, 0x80001001, 0xe0701034,
0x80001101, 0xbf810000,
};

struct SamplerCase {
const char* name;
std::array<std::uint32_t, 4> words;
Expand Down Expand Up @@ -107,6 +120,12 @@ void FillTexture(std::span<std::uint8_t> texels, std::uint32_t levels) {
}
}

std::string Hex(std::uint32_t value) {
char text[16];
std::snprintf(text, sizeof(text), "0x%08x", value);
return text;
}

std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
const auto address = reinterpret_cast<std::uintptr_t>(data);
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x31016facu};
Expand All @@ -128,7 +147,7 @@ std::array<std::uint32_t, 8> TextureDescriptor(const void* data, std::uint32_t l

using Samples = std::vector<std::array<std::uint32_t, Results>>;

Samples Run(AgcDriver::VulkanDevice& device, const std::array<std::uint32_t, 8>& texture, const std::array<std::uint32_t, 4>& sampler, const std::vector<Coordinate>& coordinates) {
Samples Run(AgcDriver::VulkanDevice& device, const std::array<std::uint32_t, 8>& texture, const std::array<std::uint32_t, 4>& sampler, const std::vector<Coordinate>& coordinates, std::span<const std::uint32_t> code = Code) {
Samples samples;
for (std::size_t first = 0; first < coordinates.size(); first += Threads) {
Buffer.fill(0xdeadbeefu);
Expand All @@ -142,7 +161,6 @@ Samples Run(AgcDriver::VulkanDevice& device, const std::array<std::uint32_t, 8>&
std::copy(buffer.begin(), buffer.end(), userData.begin());
std::copy(texture.begin(), texture.end(), userData.begin() + 4);
std::copy(sampler.begin(), sampler.end(), userData.begin() + 12);
const std::span<const std::uint32_t> code(Code);
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1};
ShaderRecompiler::RecompileRequest request{
Expand Down Expand Up @@ -231,6 +249,43 @@ int main() {
ExpectFailure(*device, TextureDescriptor(MultiLevel.data(), 2u, 2u), Samplers[0].words, "single-level", "a 3-level view");
ExpectFailure(*device, single, {0x00008092u, 0x00fff000u, 0x05100000u, 0u}, "different minification", "unequal minification and magnification filters");
ExpectFailure(*device, single, {0x00008090u, 0x00fff000u, 0x05500000u, 0u}, "clamp mode 0", "wrap on X");
const auto constantView = [&](std::uint32_t type, std::uint32_t format, std::uint32_t swizzle) {
auto descriptor = TextureDescriptor(SingleLevel.data(), 0u, 0u);
descriptor[1] = (descriptor[1] & ~(0x1ffu << 20u)) | (format << 20u);
descriptor[3] = swizzle | (type << 28u);
return descriptor;
};
for (const auto& sampler : Samplers) {
for (const auto& sample : Run(*device, constantView(Type3D, Format8888UNorm, OneZeroOneZero), sampler.words, coordinates)) {
for (std::uint32_t index = 0; index < Results; ++index) {
const std::uint32_t expected = index % 2u == 0u ? 0x3f800000u : 0u;
Require(sample[index] == expected, std::string(sampler.name) + ": a 3D view whose channels select (1, 0, 1, 0) returned " + Hex(sample[index]) + " for result " + std::to_string(index));
}
}
}
struct ConstantCase {
const char* name;
std::array<std::uint32_t, 8> texture;
std::uint32_t one;
};
const std::array<ConstantCase, 4> constants{{
{"a 3D UNORM view selecting (1, 0, 1, 0)", constantView(Type3D, Format8888UNorm, OneZeroOneZero), 0x3f800000u},
{"a 32_UINT view selecting (1, 0, 1, 0)", constantView(Type2D, Format32UInt, OneZeroOneZero), 1u},
{"a 32_SINT view selecting (1, 0, 1, 0)", constantView(Type2D, Format32SInt, OneZeroOneZero), 1u},
{"a 1D view selecting (0, 0, 0, 0)", constantView(Type1D, Format8888UNorm, 0u), 0u},
}};
const std::array<const char*, 3> constantInstructions{"image_sample_lz dmask 0x6", "image_gather4_lz dmask 0x2", "image_gather4_lz dmask 0x4"};
for (const auto& constant : constants) {
auto normalized = Samplers[0].words;
normalized[0] &= ~0x8000u;
for (const auto& sample : Run(*device, constant.texture, normalized, coordinates, ConstantCode)) {
const std::array<std::uint32_t, 10> expected{0u, constant.one, 0u, 0u, 0u, 0u, constant.one, constant.one, constant.one, constant.one};
for (std::uint32_t index = 0; index < expected.size(); ++index) {
const auto instruction = constantInstructions[index < 2u ? 0u : index < 6u ? 1u : 2u];
Require(sample[index] == expected[index], std::string(instruction) + ", " + constant.name + ": result " + std::to_string(index) + " is " + Hex(sample[index]) + ", expected " + Hex(expected[index]));
}
}
}
std::puts("image sample unnormalized tests passed");
return 0;
} catch (const std::exception& error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ struct ImageResource {
bool packed = false;
bool fmaskCompatible = true;
bool depthBitsCompatible = true;
bool constantSwizzle = false;
bool constantSwizzleCompatible = true;
std::uint32_t byElements = 0;
std::uint32_t byComponents = 0;
IrBufferFormat packedFormat = IrBufferFormat::Invalid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ UnnormalizedProof ProveUnnormalized(const ShaderInfo& info, const ResourceSnapsh
if (image.indirectRoot != ImageResource::NoIndirectImage) {
failUnnormalized("samples an image selected at run time");
}
if (image.constantSwizzle) {
continue;
}
if ((image.dimension != RdnaImageDimension::Dim1D && image.dimension != RdnaImageDimension::Dim2D) || image.cube) {
failUnnormalized("samples a 1D-array, 2D-array, 3D, cube or multisampled image");
}
Expand Down Expand Up @@ -295,7 +298,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
for (std::uint32_t component = 0; component < 4u; ++component) plan.specialization.push_back({PipelineSpecialization::ExportBase + target * 4u + component, (exportMappings[target] >> (component * 2u)) & 3u});
}
std::vector<std::uint32_t> samplerModes(info.samplers.size(), 0u);
const auto samplerMode = [](const ImageResource& image) { return image.numericClass == IrTextureNumericClass::Sint || image.conversionFormat != IrBufferFormat::Invalid || image.depthBits ? 2u : 1u; };
const auto samplerMode = [](const ImageResource& image) { return !image.constantSwizzle && (image.numericClass == IrTextureNumericClass::Sint || image.conversionFormat != IrBufferFormat::Invalid || image.depthBits) ? 2u : 1u; };
for (const auto& pair : info.sampledPairs) {
const auto& image = info.images.at(pair.image);
if (image.indirectRoot == ImageResource::NoIndirectImage) samplerModes.at(pair.sampler) |= samplerMode(info.runtimeImageModes.at(pair.image).at(imageModes[pair.image]));
Expand Down Expand Up @@ -343,7 +346,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
previous = resource;
{
const auto word = snapshot.images.at(resource).dwords[3];
active = DescriptorBindingForImage(mode) == logical.kind && mode.packedFormat != IrBufferFormat::Fmask8_S2_F1 && (image.mipMode != ImageMipMode::DynamicStorage || mip <= ((word >> 16u) & 0xfu) - ((word >> 12u) & 0xfu));
active = DescriptorBindingForImage(mode) == logical.kind && mode.packedFormat != IrBufferFormat::Fmask8_S2_F1 && !mode.constantSwizzle && (image.mipMode != ImageMipMode::DynamicStorage || mip <= ((word >> 16u) & 0xfu) - ((word >> 12u) & 0xfu));
}
} else if (samplerHeap) {
active = (samplerModes.at(resource) & (1u << (element & 1u))) != 0u;
Expand Down
20 changes: 20 additions & 0 deletions core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,21 @@ std::vector<ImageResource> ResourceMaterializer::RuntimeImageModes(const ImageRe
modes.push_back(mode);
}
}
if (image.constantSwizzleCompatible && !storage && !image.depthCompare && !image.packed && image.indirectRoot == ImageResource::NoIndirectImage) {
for (const auto numeric : {IrTextureNumericClass::Float, IrTextureNumericClass::Uint, IrTextureNumericClass::Sint}) {
auto mode = image;
mode.numericClass = numeric;
mode.conversionFormat = IrBufferFormat::Invalid;
mode.packedFormat = IrBufferFormat::Invalid;
mode.depthBits = false;
mode.depthUnorm16 = false;
mode.cube = false;
mode.mipCount = 1u;
mode.shaderSwizzle = ShaderImageIdentitySwizzle;
mode.constantSwizzle = true;
modes.push_back(mode);
}
}
if (modes.empty()) throw std::runtime_error("image instruction has no supported runtime modes");
return modes;
}
Expand All @@ -727,6 +742,11 @@ std::uint32_t ResourceMaterializer::RuntimeImageMode(const ImageResource& image,
}
}
if (decoded.mipCount > (image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u)) throw std::runtime_error("runtime storage image mip capacity exceeded");
if ((descriptorImageSwizzle(descriptor) & 06666u) == 0u && !decoded.fmask && !decoded.depthBits && decoded.conversionFormat == IrBufferFormat::Invalid && !decoded.srgbDecode) {
for (std::uint32_t index = 0u; index < modes.size(); ++index) {
if (modes[index].constantSwizzle && modes[index].numericClass == decoded.numericClass) return index;
}
}
for (std::uint32_t index = 0u; index < modes.size(); ++index) {
const auto& mode = modes[index];
if (((mode.emulatedCompare & EmulatedCompare::Enabled) != 0u) != emulated) continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,7 @@ class Tracker {
image.srgbDecodeCompatible = image.srgbDecodeCompatible && !ImageOpcodeInfoOf(op).needsSampler;
image.fmaskCompatible = image.fmaskCompatible && op == IrOpcode::ImageRead && memory.dataBits == 32u;
image.depthBitsCompatible = image.depthBitsCompatible && memory.dataBits == 32u;
image.constantSwizzleCompatible = image.constantSwizzleCompatible && (op == IrOpcode::ImageSampleRaw || op == IrOpcode::ImageGatherRaw);
if ((memory.imageSampleFlags & RdnaImageSampleFlagCompare) != 0u) {
constexpr auto unsupported = RdnaImageSampleFlagLod | RdnaImageSampleFlagDerivative;
if (op == IrOpcode::ImageGatherRaw || (memory.imageSampleFlags & unsupported) != 0u) image.emulatedCompare |= EmulatedCompare::Unsupported;
Expand Down
6 changes: 5 additions & 1 deletion core/shader/recompiler/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ void encodeInfo(Writer& writer, const CompiledShaderInfo& compiled) {
out.Value(image.packed);
out.Value(image.fmaskCompatible);
out.Value(image.depthBitsCompatible);
out.Value(image.constantSwizzle);
out.Value(image.constantSwizzleCompatible);
out.Value(image.byElements);
out.Value(image.byComponents);
out.Value(image.packedFormat);
Expand Down Expand Up @@ -550,7 +552,7 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) {
in.Value(buffer.scalar);
in.Value(buffer.typedAlignment);
});
reader.List(info.images, 72, [](Reader& in, ImageResource& image) {
reader.List(info.images, 74, [](Reader& in, ImageResource& image) {
in.Value(image.source);
in.Value(image.firstUsePc);
in.Value(image.resourceClass);
Expand All @@ -575,6 +577,8 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) {
in.Value(image.packed);
in.Value(image.fmaskCompatible);
in.Value(image.depthBitsCompatible);
in.Value(image.constantSwizzle);
in.Value(image.constantSwizzleCompatible);
in.Value(image.byElements);
in.Value(image.byComponents);
in.Value(image.packedFormat);
Expand Down
2 changes: 1 addition & 1 deletion core/shader/recompiler/ShaderDiskCache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace ShaderRecompiler::ShaderDiskCache {

inline constexpr std::uint32_t FormatVersion = 21;
inline constexpr std::uint32_t FormatVersion = 22;

enum class LoadStatus {
Loaded,
Expand Down
21 changes: 21 additions & 0 deletions core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1470,7 +1470,28 @@ TableSelection TableSlot(SpirvValueEmitContext& ctx, const IrValue& inst, const
return EmitIndirectImageSelector(ctx, image, key);
}

void EmitConstantSwizzleSample(SpirvValueEmitContext& ctx, const ImageEmitAccess& access) {
auto& state = ctx.state;
const auto numericClass = access.image.numericClass;
const bool gather = access.inst.Opcode() == IrOpcode::ImageGatherRaw;
const auto scalarType = ImageScalarType(state, numericClass);
const auto one = numericClass == IrTextureNumericClass::Float ? ConstantF32(state, 0x3f800000u) : numericClass == IrTextureNumericClass::Sint ? ConstantI32(state, 1) : ConstantU32(state, 1u);
const auto zero = SampledComponentZero(state, numericClass);
std::uint32_t channels[4] = {};
for (std::uint32_t channel = 0; channel < 4u; channel++) {
const auto selector = RuntimeImageSwizzle(state, access.mem.resource, gather ? ImageGatherComponent(EffectiveDmask(access.mem)) : channel);
channels[channel] = Select(state, scalarType, Binary(state, spv::OpIEqual, TypeBool(state), selector, ConstantU32(state, 1u)), one, zero);
}
const auto value = state.module.AllocateId();
state.module.AddFunction(spv::OpCompositeConstruct, ImageVectorType(state, numericClass, 4), value, channels[0], channels[1], channels[2], channels[3]);
ctx.Define(access.inst, ResultVector(ctx, access, value, numericClass, false, gather));
}

void EmitSamplingOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access) {
if (access.image.constantSwizzle) {
EmitConstantSwizzleSample(ctx, access);
return;
}
if (access.image.srgbDecode) {
ctx.Fail(access.inst, "samples or gathers an sRGB image the device cannot sample, which is not implemented");
}
Expand Down
Loading