Skip to content
Open
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
8 changes: 8 additions & 0 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,14 @@ target_include_directories(agc_driver_prepared_shaders_tests PRIVATE

if(BUILD_TESTING)
set_target_properties(agc_shader_mode_register_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
foreach(packedStorageFormat IN ITEMS 133 134 136)
set(packedStorageTest agc_driver_image_packed16_storage_${packedStorageFormat})
add_test(NAME ${packedStorageTest} COMMAND agc_driver_image_converted_unorm_tests --packed16 ${packedStorageFormat})
set_tests_properties(${packedStorageTest} PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 30)
if(WIN32)
set_tests_properties(${packedStorageTest} PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endforeach()
foreach(prebuildPolicyMode IN ITEMS unset 0 1)
set(prebuildPolicyTest agc_driver_prepared_graphics_prebuild_${prebuildPolicyMode})
add_test(NAME ${prebuildPolicyTest} COMMAND agc_driver_prepared_graphics_tests --prebuild-${prebuildPolicyMode})
Expand Down
27 changes: 23 additions & 4 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,9 @@ Texture::Texture(const Context& context, const std::shared_ptr<StorageTexture>&
}
VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
viewInfo.image = source->Image();
VkImageViewUsageCreateInfo usage{VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO};
usage.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
viewInfo.pNext = &usage;
viewInfo.viewType = ViewTypeFor(descriptor.dimension, viewLayerCount);
viewInfo.format = vkFormat;
viewFormat = vkFormat;
Expand Down Expand Up @@ -1166,6 +1169,17 @@ VkBufferMemoryBarrier WholeBufferBarrier(VkBuffer buffer, VkAccessFlags from, Vk
return barrier;
}

VkFormat PackedStorageViewFormat(VkFormat format) {
switch (format) {
case VK_FORMAT_R5G6B5_UNORM_PACK16:
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
return VK_FORMAT_R16_UINT;
default:
return VK_FORMAT_UNDEFINED;
}
}

// Storage images cannot use sRGB formats; the shader works on the raw encoded values either way.
// The answer is a property of the physical device, so it is computed once per format (every storage
// image lookup asks: a live vkGetPhysicalDeviceFormatProperties call each time was measurable) and
Expand All @@ -1189,7 +1203,10 @@ VkFormat StorageFormatOrUndefined(const Context& context, VkFormat format) {
return (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
};
VkFormat storage = VK_FORMAT_UNDEFINED;
if (supports(format)) {
const auto packed = PackedStorageViewFormat(format);
if (packed != VK_FORMAT_UNDEFINED) {
if (supports(packed)) storage = format;
} else if (supports(format)) {
storage = format;
} else {
VkFormat linear = VK_FORMAT_UNDEFINED;
Expand Down Expand Up @@ -1740,9 +1757,10 @@ VkImageView StorageTexture::createView(std::uint32_t mip, bool firstLayer, VkFor
Require(!firstLayer || descriptor.dimension == TextureDimension::k2DArray, "a first-layer storage view needs a 2D array surface");
const auto viewLayerCount = firstLayer ? 1u : geometry.imageLayers - descriptor.baseArray;
VkImageViewUsageCreateInfo usage{VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO};
usage.usage = VK_IMAGE_USAGE_STORAGE_BIT;
const bool packed = PackedStorageViewFormat(storageFormat) != VK_FORMAT_UNDEFINED;
usage.usage = packed && format == storageFormat ? VK_IMAGE_USAGE_SAMPLED_BIT | (attachable ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0u) : VK_IMAGE_USAGE_STORAGE_BIT;
VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
viewInfo.pNext = format == storageFormat ? nullptr : &usage;
viewInfo.pNext = format == storageFormat && !packed ? nullptr : &usage;
viewInfo.image = image;
// Storage views address one mip; cube faces are written as array layers.
viewInfo.viewType = firstLayer ? VK_IMAGE_VIEW_TYPE_2D : descriptor.dimension == TextureDimension::k1D ? VK_IMAGE_VIEW_TYPE_1D : descriptor.dimension == TextureDimension::k2D ? VK_IMAGE_VIEW_TYPE_2D : descriptor.dimension == TextureDimension::k3D ? VK_IMAGE_VIEW_TYPE_3D : descriptor.dimension == TextureDimension::k1DArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_2D_ARRAY;
Expand Down Expand Up @@ -1917,7 +1935,8 @@ VkImageView StorageTexture::FirstLayerView(std::uint32_t mip) {
}

VkImageView StorageTexture::StorageView(std::uint32_t mip, bool firstLayer) {
const auto format = UintFormatOfSint(storageFormat);
const auto packed = PackedStorageViewFormat(storageFormat);
const auto format = packed != VK_FORMAT_UNDEFINED ? packed : UintFormatOfSint(storageFormat);
if (format == VK_FORMAT_UNDEFINED) return firstLayer ? FirstLayerView(mip) : View(mip);
if (format == VK_FORMAT_R32_UINT) return AtomicView(mip, firstLayer);
const auto found = uintViews.find({mip, firstLayer});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,14 +442,92 @@ void RequireRefused(AgcDriver::VulkanDevice& device, std::span<const std::uint32
Require(refusal.find(reason) != std::string::npos, what + " of a converted image was not refused: " + refusal);
}

void CheckPacked16(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t format) {
const std::array<std::uint32_t, 4> widths = format == 133u ? std::array{5u, 6u, 5u, 0u} : format == 134u ? std::array{5u, 5u, 5u, 1u} : std::array{4u, 4u, 4u, 4u};
const std::array<std::uint32_t, 4> offsets = format == 133u ? std::array{0u, 5u, 11u, 0u} : format == 134u ? std::array{0u, 5u, 10u, 15u} : std::array{0u, 4u, 8u, 12u};
for (std::uint32_t index = 0; index < LoadWidth; ++index) {
const auto value = static_cast<std::uint16_t>(index * 4051u + 0x39d7u);
std::memcpy(texels + index * 2u, &value, 2u);
}
Run(device, LoadXyzw, TextureDescriptor(texels, format, SwizzleXYZW, LoadWidth), {}, LoadGroups);
for (std::uint32_t index = 0; index < LoadWidth; ++index) {
const auto value = static_cast<std::uint16_t>(index * 4051u + 0x39d7u);
for (std::uint32_t component = 0; component < 4u; ++component) {
const auto bits = widths[component] == 0u ? widths[0] : widths[component];
const auto maximum = (1u << bits) - 1u;
const auto actual = Output[index * 4u + component];
Require(std::isfinite(std::bit_cast<float>(actual)) && StoreField(actual, maximum) == ((value >> offsets[component]) & maximum), "packed image_load has the wrong channel for format " + std::to_string(format));
}
}
auto* target = texels + RoundTripOffset;
alignas(256) static constexpr std::array<std::uint32_t, 10> roundTrip{
0x7e080218u, 0x343c0885u, 0x4a3c3d00u, 0x7e3e0280u, 0xf0001f08u, 0x00010a1eu, 0xbf8c3f70u, 0xf0201f08u,
0x00040a1eu, 0xbf810000u,
};
Run(device, roundTrip, TextureDescriptor(texels, format, SwizzleXYZW, LoadWidth), {}, LoadGroups, TextureDescriptor(target, format, SwizzleXYZW, LoadWidth));
AgcDriver::Graphics::StorageTexture::FlushPending(reinterpret_cast<std::uintptr_t>(target), LoadWidth * 2u, nullptr, "test");
device.WaitIdle();
Require(std::memcmp(texels, target, LoadWidth * 2u) == 0, "packed image load/store changed guest bytes for format " + std::to_string(format));
alignas(256) static constexpr auto store = StoreCode(0xfu, 0u);
auto* written = texels + StoreOffset;
alignas(256) static constexpr auto storeHalf = StoreCode(0xfu, D16);
constexpr std::array halfValues{0x0000u, 0x3c00u, 0x3800u, 0x3400u, 0xbc00u, 0x4000u, 0x7c00u, 0x7e00u};
constexpr std::array halfFloats{0.0f, 1.0f, 0.5f, 0.25f, -1.0f, 2.0f, INFINITY, NAN};
for (std::uint32_t pass = 0; pass < 5u; ++pass) {
const auto swizzle = pass == 3u ? SwizzleZYX1 : SwizzleXYZW;
const auto dmask = pass == 4u ? 0x5u : 0xfu;
std::array<std::uint16_t, Threads> expected{};
Input.fill(0u);
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
std::uint32_t slot = 0;
for (std::uint32_t component = 0; component < 4u; ++component) {
if (((dmask >> component) & 1u) == 0u) continue;
auto value = StoreValues[(tid + component * 3u + pass * 4u) % StoreValues.size()];
if (tid == 0u) value = std::bit_cast<std::uint32_t>(pass == 0u ? std::array{1.0f, 0.5f, 0.25f, 1.0f}[component] : std::array{0.25f, 1.0f, 0.5f, 0.0f}[component]);
if (pass == 2u) {
const auto index = (tid + component * 3u) % halfValues.size();
value = std::bit_cast<std::uint32_t>(halfFloats[index]);
Input[tid * 4u + slot / 2u] |= halfValues[index] << ((slot % 2u) * 16u);
} else {
Input[tid * 4u + slot] = value;
}
++slot;
const auto selected = (swizzle >> (component * 3u)) & 7u;
if (selected >= 4u && widths[selected - 4u] != 0u) expected[tid] |= static_cast<std::uint16_t>(StoreField(value, (1u << widths[selected - 4u]) - 1u) << offsets[selected - 4u]);
}
}
auto* reference = texels + FloatOffset;
std::memcpy(reference, expected.data(), Threads * 2u);
Run(device, SampleLz, TextureDescriptor(reference, format, SwizzleXYZW, Threads), PointSampler, 1);
std::array<std::uint32_t, 4> sampled{};
std::copy_n(Output.begin(), 4u, sampled.begin());
const auto descriptor = TextureDescriptor(written, format, SwizzleXYZW, Threads);
Run(device, pass == 2u ? storeHalf : pass == 4u ? StoreXz : store, TextureDescriptor(written, format, swizzle, Threads), BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size() * 4u)), 1);
Run(device, SampleLz, descriptor, PointSampler, 1);
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
for (std::uint32_t component = 0; component < 4u; ++component) Require(Output[tid * 4u + component] == sampled[component], "sample after packed store differs for format " + std::to_string(format));
}
AgcDriver::Graphics::StorageTexture::FlushPending(reinterpret_cast<std::uintptr_t>(written), Threads * 2u, nullptr, "test");
device.WaitIdle();
Require(std::memcmp(written, expected.data(), Threads * 2u) == 0, "packed image_store write-back differs for format " + std::to_string(format));
}
}

}

int main() {
int main(int argc, char** argv) {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
GuestBlock block;
auto* texels = block.Data();
if (argc == 3 && std::string(argv[1]) == "--packed16") {
const auto format = static_cast<std::uint32_t>(std::stoul(argv[2]));
Require(format == 133u || format == 134u || format == 136u, "unsupported packed storage test format");
CheckPacked16(*device, texels, format);
std::cout << "Packed 16-bit storage image tests passed\n";
return 0;
}
for (std::uint32_t index = 0; index < LoadWidth; ++index) {
const auto texel = LoadTexel(index);
std::memcpy(texels + index * 4u, &texel, 4u);
Expand Down
10 changes: 8 additions & 2 deletions core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ ImageDecode inspectImageDescriptor(const DescriptorValue& descriptor, const Imag
}
}
const bool nativeRepacked = format == IrBufferFormat::Format10_11_11Float && !storage && !base.packed;
decoded.conversionFormat = RemapTextureFormat(format) != format && !nativeRepacked ? format : IrBufferFormat::Invalid;
const auto remappedFormat = storage ? RemapStorageTextureFormat(format) : RemapTextureFormat(format);
decoded.conversionFormat = remappedFormat != format && !nativeRepacked ? format : IrBufferFormat::Invalid;
if (format == IrBufferFormat::Format11_11_10UNorm || (format == IrBufferFormat::Format10_11_11Float && !nativeRepacked)) {
const bool floating = format == IrBufferFormat::Format10_11_11Float;
if (!base.srgbDecodeCompatible) {
Expand All @@ -250,7 +251,7 @@ ImageDecode inspectImageDescriptor(const DescriptorValue& descriptor, const Imag
const bool wideSint = format == IrBufferFormat::Format32SInt || format == IrBufferFormat::Format32_32SInt || format == IrBufferFormat::Format32_32_32_32SInt;
const bool narrowSint = format == IrBufferFormat::Format16SInt || format == IrBufferFormat::Format8_8SInt || format == IrBufferFormat::Format16_16SInt || format == IrBufferFormat::Format8_8_8_8SInt || format == IrBufferFormat::Format16_16_16_16SInt;
const bool rawSintStorage = storage && (wideSint || (narrowSint && !base.packed)) && base.written && !base.read && !base.atomic;
decoded.numericClass = base.atomic ? IrTextureNumericClass::Uint : nativeRepacked ? IrTextureNumericClass::Float : SampledTextureNumericClass(format);
decoded.numericClass = base.atomic ? IrTextureNumericClass::Uint : nativeRepacked ? IrTextureNumericClass::Float : SampledTextureNumericClass(remappedFormat);
if (!storage && !base.depthCompare && IsDepthBitsTexture(descriptor.dwords[1], descriptor.dwords[3])) {
decoded.depthBits = true;
if (!base.depthBitsCompatible) return rejectImage(PoisonReason::DepthBits16, "runtime image reads depth bits as unsupported 16-bit results");
Expand Down Expand Up @@ -1611,6 +1612,11 @@ std::vector<ImageResource> ResourceMaterializer::RuntimeImageModes(const ImageRe
append(IrTextureNumericClass::Uint, IrBufferFormat::Format11_11_10UInt, IrBufferFormat::Invalid, false, false);
if ((image.srgbDecodeCompatible || (image.convertedSamples && convertedSampleCompatible(image))) && image.depthBitsCompatible) append(IrTextureNumericClass::Uint, IrBufferFormat::Format11_11_10UNorm, IrBufferFormat::Invalid, false, false);
if (image.srgbDecodeCompatible && image.depthBitsCompatible) append(IrTextureNumericClass::Uint, IrBufferFormat::Format10_11_11Float, IrBufferFormat::Invalid, false, false);
if (storage) {
for (const auto format : {IrBufferFormat::Format5_6_5UNorm, IrBufferFormat::Format5_5_5_1UNorm, IrBufferFormat::Format4_4_4_4UNorm}) {
append(IrTextureNumericClass::Uint, format, IrBufferFormat::Invalid, false, false);
}
}
if (!storage) {
if (image.depthBitsCompatible) {
append(IrTextureNumericClass::Float, IrBufferFormat::Invalid, IrBufferFormat::Invalid, true, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum class ImageType : std::uint32_t {
[[nodiscard]] bool IsFmaskTextureFormat(IrBufferFormat format);
[[nodiscard]] IrTextureNumericClass SampledTextureNumericClass(IrBufferFormat format);
[[nodiscard]] IrBufferFormat RemapTextureFormat(IrBufferFormat format);
[[nodiscard]] IrBufferFormat RemapStorageTextureFormat(IrBufferFormat format);
[[nodiscard]] std::uint32_t DepthBitsTextureWidth(std::uint32_t word1, std::uint32_t word3);
[[nodiscard]] bool IsDepthBitsTexture(std::uint32_t word1, std::uint32_t word3);

Expand Down
11 changes: 11 additions & 0 deletions core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ IrBufferFormat RemapTextureFormat(IrBufferFormat format) {
return format == IrBufferFormat::Format11_11_10UNorm || format == IrBufferFormat::Format11_11_10UInt || format == IrBufferFormat::Format10_11_11Float ? IrBufferFormat::Format32UInt : format;
}

IrBufferFormat RemapStorageTextureFormat(IrBufferFormat format) {
switch (format) {
case IrBufferFormat::Format5_6_5UNorm:
case IrBufferFormat::Format5_5_5_1UNorm:
case IrBufferFormat::Format4_4_4_4UNorm:
return IrBufferFormat::Format16UInt;
default:
return RemapTextureFormat(format);
}
}

std::uint32_t DepthBitsTextureWidth(std::uint32_t word1, std::uint32_t word3) {
const auto format = static_cast<IrBufferFormat>((word1 >> 20u) & 0x1ffu);
const auto swizzle = (word3 >> 20u) & 0x1fu;
Expand Down
9 changes: 9 additions & 0 deletions core/shader/recompiler/SpirvBackend/src/SpirvBufferFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ SpirvFormatComponentType GetFormatComponentType(IrBufferFormat format) {
case IrBufferFormat::Format10_10_10_2UNorm:
case IrBufferFormat::Format8_8_8_8UNorm:
case IrBufferFormat::Format16_16_16_16UNorm:
case IrBufferFormat::Format5_6_5UNorm:
case IrBufferFormat::Format5_5_5_1UNorm:
case IrBufferFormat::Format4_4_4_4UNorm:
return SpirvFormatComponentType::Unorm;
case IrBufferFormat::Format8SNorm:
case IrBufferFormat::Format16SNorm:
Expand Down Expand Up @@ -110,6 +113,12 @@ SpirvBufferFormatInfo GetFormatInfo(IrBufferFormat format) {
}
const auto type = GetFormatComponentType(format);
switch (format) {
case IrBufferFormat::Format5_6_5UNorm:
return {format, type, 3u, 2u, {5u, 6u, 5u, 0u}, {0u, 5u, 11u, 0u}, true};
case IrBufferFormat::Format5_5_5_1UNorm:
return {format, type, 4u, 2u, {5u, 5u, 5u, 1u}, {0u, 5u, 10u, 15u}, true};
case IrBufferFormat::Format4_4_4_4UNorm:
return {format, type, 4u, 2u, {4u, 4u, 4u, 4u}, {0u, 4u, 8u, 12u}, true};
case IrBufferFormat::Format8UNorm:
case IrBufferFormat::Format8SNorm:
case IrBufferFormat::Format8UScaled:
Expand Down
Loading
Loading