mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
shader: accept indirect image rows whose type reads the leading sample coordinates
This commit is contained in:
1 parent
dfcf1c0b03
commit
d3768c00e9
2 files changed
+130
-4
No files matched your search
@@ -1047,6 +1047,26 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
|
|||||||
return dimension == Decoder::ImageDimension::Dim1D ||
|
return dimension == Decoder::ImageDimension::Dim1D ||
|
||||||
dimension == Decoder::ImageDimension::Dim2D;
|
dimension == Decoder::ImageDimension::Dim2D;
|
||||||
};
|
};
|
||||||
|
// The descriptor's type selects the address layout. A 2D or 3D candidate reads the leading
|
||||||
|
// coordinates of an equal or larger dimension, but an array with more than one slice reads
|
||||||
|
// the next as its slice. Derivative, LOD, clamp and mip operands shift with the dimension.
|
||||||
|
const auto leading = [&](Decoder::ImageDimension dimension, const DescriptorValue& value) {
|
||||||
|
const bool sliced =
|
||||||
|
static_cast<Prospero::ImageType>((value.dwords[3] >> 28u) & 0xfu) ==
|
||||||
|
Prospero::ImageType::kColor2DArray &&
|
||||||
|
(value.dwords[4] & 0x1fffu) != ((value.dwords[4] >> 16u) & 0x1fffu);
|
||||||
|
return !sliced &&
|
||||||
|
(dimension == Decoder::ImageDimension::Dim2D || dimension == base.dimension);
|
||||||
|
};
|
||||||
|
const bool leading_coordinates = (base.dimension == Decoder::ImageDimension::Dim2D ||
|
||||||
|
base.dimension == Decoder::ImageDimension::Dim3D) &&
|
||||||
|
leading(image_class.dimension, snapshot.images[exemplar]) &&
|
||||||
|
std::ranges::none_of(program.memory_info, [&](const MemoryInfo& memory) {
|
||||||
|
return memory.kind == ResourceKind::Image && memory.resource == root_index &&
|
||||||
|
(memory.image_has_mip || (memory.image_sample_flags &
|
||||||
|
(Decoder::ImageSampleFlagLod | Decoder::ImageSampleFlagDerivative |
|
||||||
|
Decoder::ImageSampleFlagLodClamp)) != 0u);
|
||||||
|
});
|
||||||
for (uint32_t candidate = 0; candidate < specialization.images.size(); candidate++) {
|
for (uint32_t candidate = 0; candidate < specialization.images.size(); candidate++) {
|
||||||
auto& image = specialization.images[candidate];
|
auto& image = specialization.images[candidate];
|
||||||
if (image.indirect_root != root_index) {
|
if (image.indirect_root != root_index) {
|
||||||
@@ -1066,8 +1086,10 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
|
|||||||
base.dimension == Decoder::ImageDimension::Dim2D &&
|
base.dimension == Decoder::ImageDimension::Dim2D &&
|
||||||
!image.cube && !image_class.cube && is_1d_or_2d(image.dimension) &&
|
!image.cube && !image_class.cube && is_1d_or_2d(image.dimension) &&
|
||||||
is_1d_or_2d(image_class.dimension);
|
is_1d_or_2d(image_class.dimension);
|
||||||
|
const bool prefix_coordinates =
|
||||||
|
leading_coordinates && leading(image.dimension, snapshot.images[candidate]);
|
||||||
if ((!same_coordinates && !(is_2d(image.dimension) && is_2d(image_class.dimension)) &&
|
if ((!same_coordinates && !(is_2d(image.dimension) && is_2d(image_class.dimension)) &&
|
||||||
!unfiltered_coordinates) ||
|
!unfiltered_coordinates && !prefix_coordinates) ||
|
||||||
image.conversion_format != image_class.conversion_format ||
|
image.conversion_format != image_class.conversion_format ||
|
||||||
(!storage_swizzle && image.shader_swizzle != image_class.shader_swizzle)) {
|
(!storage_swizzle && image.shader_swizzle != image_class.shader_swizzle)) {
|
||||||
return SpecializationFail(
|
return SpecializationFail(
|
||||||
@@ -1717,8 +1739,9 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ImageHeapKind(const ImageResource& root, DescriptorValue& value) {
|
uint32_t ImageHeapKind(const ImageResource& root, DescriptorValue& value) {
|
||||||
// Mirrors BuildResourceSpecialization for sampled roots: entries it would reject, and
|
// Mirrors BuildResourceSpecialization for sampled roots: entries it would reject, entries of
|
||||||
// entries that need shader-side conversion, read as null instead of failing the draw.
|
// another dimension and entries that need shader-side conversion read as null instead of
|
||||||
|
// failing the draw.
|
||||||
const auto format = static_cast<Prospero::BufferFormat>((value.dwords[1] >> 20u) & 0x1ffu);
|
const auto format = static_cast<Prospero::BufferFormat>((value.dwords[1] >> 20u) & 0x1ffu);
|
||||||
const auto tile = static_cast<Prospero::TileMode>((value.dwords[3] >> 20u) & 0x1fu);
|
const auto tile = static_cast<Prospero::TileMode>((value.dwords[3] >> 20u) & 0x1fu);
|
||||||
const auto numeric_class =
|
const auto numeric_class =
|
||||||
|
|||||||
@@ -18053,7 +18053,8 @@ public:
|
|||||||
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
|
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
|
||||||
if (image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
if (image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||||
supported = supported ||
|
supported = supported ||
|
||||||
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray;
|
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray ||
|
||||||
|
image.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D;
|
||||||
}
|
}
|
||||||
Require(test.name, "dispatch", supported,
|
Require(test.name, "dispatch", supported,
|
||||||
"unsupported image dimension needs a matching Vulkan test view");
|
"unsupported image dimension needs a matching Vulkan test view");
|
||||||
@@ -41990,6 +41991,106 @@ void CheckWaterfallFloorImageTable(VulkanHarness &vulkan) {
|
|||||||
std::printf("[compute] %s ok\n", test.name);
|
std::printf("[compute] %s ok\n", test.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckMixedDimensionImageTable(VulkanHarness &vulkan) {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
constexpr u32 base = 256u, table_offset = 0x100u, rows = 4u;
|
||||||
|
// Row 0 is a one-slice 2D array under a 3D sample: it reads the leading two coordinates and
|
||||||
|
// clamps the third to its slice. A second slice or a LOD operand would read the third instead,
|
||||||
|
// and a 3D row 0 under a 2D sample, or 2D rows under a 1D sample, would read a coordinate the
|
||||||
|
// sample does not supply.
|
||||||
|
constexpr std::array names{"MixedDimensionImageTable", "MixedDimensionImageTableLod",
|
||||||
|
"MixedDimensionImageTableSlices", "MixedDimensionImageTableFlat",
|
||||||
|
"MixedDimensionImageTableLine"};
|
||||||
|
for (u32 variant = 0; variant < names.size(); ++variant) {
|
||||||
|
const bool lod = variant == 1u, slices = variant == 2u, flat = variant == 3u,
|
||||||
|
line = variant == 4u;
|
||||||
|
TestCase test;
|
||||||
|
test.name = names[variant];
|
||||||
|
test.dispatch_x = rows;
|
||||||
|
test.bda_mappings = {{0u, 0u}};
|
||||||
|
test.initial.resize((base + table_offset + rows * 32u) / 4u);
|
||||||
|
for (u32 row = 0; row < rows; ++row) {
|
||||||
|
auto image = MakeSampledTextureData(Prospero::BufferFormat::k32Float);
|
||||||
|
image[0] = row + 1u;
|
||||||
|
auto type = row == 0u ? Prospero::ImageType::kColor2DArray : Prospero::ImageType::kColor3D;
|
||||||
|
if (flat) type = row == 0u ? Prospero::ImageType::kColor3D : Prospero::ImageType::kColor2D;
|
||||||
|
if (line) type = row == 0u ? Prospero::ImageType::kColor1D : Prospero::ImageType::kColor2D;
|
||||||
|
image[3] = DstSel(4, 0, 0, 1) | (static_cast<u32>(type) << 28u);
|
||||||
|
image[4] = row == 0u && slices ? 1u : 0u;
|
||||||
|
std::copy_n(image.begin(), 8, test.initial.begin() + (base + table_offset + row * 32u) / 4u);
|
||||||
|
}
|
||||||
|
test.user_data[0] = base;
|
||||||
|
const auto output = MakeStructuredStorageBufferData(0u, rows * 4u);
|
||||||
|
std::copy_n(output.begin(), 4, test.user_data.begin() + 48);
|
||||||
|
test.has_user_data = test.has_compute_info = true;
|
||||||
|
test.compute_info.wave_size = 64u;
|
||||||
|
test.compute_info.threads_num[0] = test.compute_info.threads_num[1] =
|
||||||
|
test.compute_info.threads_num[2] = 1u;
|
||||||
|
test.compute_info.group_id[0] = true;
|
||||||
|
test.compute_info.workgroup_register = 44u;
|
||||||
|
u32 dim = 2u;
|
||||||
|
if (flat) dim = 1u;
|
||||||
|
if (line) dim = 0u;
|
||||||
|
auto &code = test.code;
|
||||||
|
AppendVMovLiteral(&code, 10, std::bit_cast<u32>(0.5f));
|
||||||
|
AppendVMovLiteral(&code, 11, std::bit_cast<u32>(0.5f));
|
||||||
|
AppendVMovLiteral(&code, 12, std::bit_cast<u32>(0.5f));
|
||||||
|
AppendVMovLiteral(&code, 13, 0u);
|
||||||
|
code.insert(code.end(), {EncodeSop2(0x1e, 23, 44, InlineU32(5)),
|
||||||
|
EncodeSop2(0x02, 23, 23, 255), table_offset, EncodeSmem0(0x03, 8), EncodeSmem1(0, 23),
|
||||||
|
EncodeMimg0(lod ? 0x24 : 0x27, 1, 0, false, dim), EncodeMimg1(5, 10, 2, 13),
|
||||||
|
EncodeSop2(0x1e, 25, 44, InlineU32(2)), EncodeVop1(0x01, 30, 25)});
|
||||||
|
AppendBufferStoreDword(&code, 5, 30);
|
||||||
|
AppendEnd(&code);
|
||||||
|
if (variant != 0u) {
|
||||||
|
ShaderRecompiler::CompileOptions options;
|
||||||
|
options.user_data = test.user_data;
|
||||||
|
options.input_info.compute = &test.compute_info;
|
||||||
|
auto translated = ShaderRecompiler::TranslateProgram(test.code, options);
|
||||||
|
const auto plan = LoadedResourcePlan(test.name, translated.program);
|
||||||
|
ShaderRecompiler::IR::ResourceSnapshot snapshot;
|
||||||
|
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||||
|
const std::array workgroup_counts{rows, 1u, 1u};
|
||||||
|
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||||
|
.user_data = test.user_data,
|
||||||
|
.userdata = &test.initial,
|
||||||
|
.read_specialization_memory = ReadTestMemory,
|
||||||
|
.workgroup_counts = workgroup_counts,
|
||||||
|
};
|
||||||
|
FakeGuestMemory fake(ReadTestMemory, runtime.userdata);
|
||||||
|
Require(test.name, "coordinate layout",
|
||||||
|
!ShaderRecompiler::IR::MaterializeResources(plan, runtime, snapshot, specialization),
|
||||||
|
"a candidate read a coordinate outside its layout");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::S_LSHL_B32, O::S_ADD_I32, O::S_LOAD_DWORDX8,
|
||||||
|
O::IMAGE_SAMPLE, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
const auto compiled = CompileCase(test, vulkan.SubgroupSize());
|
||||||
|
std::vector<VulkanHarness::Image> images;
|
||||||
|
for (const auto &descriptor : compiled.resources.images) {
|
||||||
|
const bool volume =
|
||||||
|
(descriptor.dwords[3] >> 28u) == static_cast<u32>(Prospero::ImageType::kColor3D);
|
||||||
|
images.push_back(vulkan.CreateImageMips(test.name, 1, 1, vk::Format::eR32Sfloat,
|
||||||
|
vk::ImageUsageFlagBits::eSampled,
|
||||||
|
{{std::bit_cast<u32>(static_cast<float>(descriptor.dwords[0]))}}, 1,
|
||||||
|
vk::ImageLayout::eShaderReadOnlyOptimal, volume ? vk::ImageType::e3D : vk::ImageType::e2D,
|
||||||
|
volume ? vk::ImageViewType::e3D : vk::ImageViewType::e2D, 1));
|
||||||
|
}
|
||||||
|
const auto sampler = vulkan.CreateSampler(test.name, 1);
|
||||||
|
auto buffer = vulkan.CreateStorageBuffer(test.name, test.initial, test.initial.size(), true);
|
||||||
|
vulkan.Dispatch(test, compiled, buffer, nullptr, nullptr, nullptr, nullptr, sampler, images);
|
||||||
|
std::vector<u32> expected;
|
||||||
|
for (u32 row = 0; row < rows; ++row)
|
||||||
|
expected.push_back(std::bit_cast<u32>(static_cast<float>(row + 1u)));
|
||||||
|
CompareWords(test, "mixed dimension rows", expected,
|
||||||
|
vulkan.ReadBuffer(test.name, buffer, expected.size()));
|
||||||
|
vulkan.DestroyBuffer(&buffer);
|
||||||
|
vulkan.Device().destroySampler(sampler);
|
||||||
|
for (auto &image : images) vulkan.DestroyImage(&image);
|
||||||
|
std::printf("[compute] %s ok\n", test.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
|
void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
constexpr u32 list_address = 160;
|
constexpr u32 list_address = 160;
|
||||||
@@ -52455,6 +52556,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckPointerImageTable(vulkan);
|
CheckPointerImageTable(vulkan);
|
||||||
CheckWorkgroupDividedImageTable(vulkan);
|
CheckWorkgroupDividedImageTable(vulkan);
|
||||||
CheckWaterfallFloorImageTable(vulkan);
|
CheckWaterfallFloorImageTable(vulkan);
|
||||||
|
CheckMixedDimensionImageTable(vulkan);
|
||||||
CheckNestedPointerImageTable(vulkan);
|
CheckNestedPointerImageTable(vulkan);
|
||||||
CheckGuardedRecordImageTable(vulkan);
|
CheckGuardedRecordImageTable(vulkan);
|
||||||
CheckIndirectImageKeySwitch(vulkan);
|
CheckIndirectImageKeySwitch(vulkan);
|
||||||
@@ -52635,6 +52737,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckPointerImageTable(vulkan);
|
CheckPointerImageTable(vulkan);
|
||||||
CheckWorkgroupDividedImageTable(vulkan);
|
CheckWorkgroupDividedImageTable(vulkan);
|
||||||
CheckWaterfallFloorImageTable(vulkan);
|
CheckWaterfallFloorImageTable(vulkan);
|
||||||
|
CheckMixedDimensionImageTable(vulkan);
|
||||||
CheckNestedPointerImageTable(vulkan);
|
CheckNestedPointerImageTable(vulkan);
|
||||||
CheckGuardedRecordImageTable(vulkan);
|
CheckGuardedRecordImageTable(vulkan);
|
||||||
CheckIndirectImageKeySwitch(vulkan);
|
CheckIndirectImageKeySwitch(vulkan);
|
||||||
|
|||||||
Reference in new issue
Block a user