diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index f37f20b50..61495c535 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -308,9 +308,8 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource, const bool descriptor_ok = IsSupportedStorageTextureDescriptor(resource, descriptor); const bool encoding_ok = IsSupportedStorageTextureEncoding(resource, descriptor); const bool uint_resource = resource.numeric_class == Prospero::TextureNumericClass::Uint; - const bool raw_sint_storage = format == Prospero::BufferFormat::k32SInt && uint_resource && - resource.written && - (resource.atomic ? !resource.atomic64 : !resource.read); + const bool raw_sint_storage = uint_resource && + ShaderRecompiler::IR::RawSignedStorageFormat(resource, format) != Prospero::BufferFormat::kInvalid; const auto numeric_class = Prospero::SampledTextureNumericClass(Prospero::RemapTextureFormat(format)); const bool raw_float_atomic = format == Prospero::BufferFormat::k32Float && uint_resource && @@ -588,10 +587,11 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR pixel_format = depth_format->depth_attachment_format; } } + const auto raw_sint_format = ShaderRecompiler::IR::RawSignedStorageFormat(resource, format); const auto storage_view_format = resource.atomic64 ? vk::Format::eR64Uint - : storage && (resource.atomic || - format == Prospero::BufferFormat::k32SInt) - ? vk::Format::eR32Uint + : storage && resource.atomic ? vk::Format::eR32Uint + : raw_sint_format != Prospero::BufferFormat::kInvalid + ? VulkanFormat(raw_sint_format) : SrgbStorageViewFormat(pixel_format); const auto view_format = storage && storage_view_format != vk::Format::eUndefined ? storage_view_format diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp index c4c68bb3d..d715aeb56 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp @@ -385,9 +385,10 @@ Format::BufferFormatInfo ImageConversionFormat(const EmitterState& state, const auto format = image.conversion_format; if (format == Prospero::BufferFormat::kInvalid) return {}; const auto info = Format::GetFormatInfo(format); - EXIT_IF(Prospero::RemapTextureFormat(format) == format || info.component_count == 0u || + const bool raw_signed = IR::RawSignedStorageFormat(image, format) != Prospero::BufferFormat::kInvalid; + EXIT_IF((Prospero::RemapTextureFormat(format) == format && !raw_signed) || info.component_count == 0u || info.component_count > 4u); - EXIT_IF(info.type != Format::ComponentType::Uscaled && + EXIT_IF(!raw_signed && info.type != Format::ComponentType::Uscaled && ((info.type != Format::ComponentType::Uint && info.type != Format::ComponentType::Unorm) || !info.packed_bitfield || info.byte_size != sizeof(uint32_t))); @@ -610,15 +611,16 @@ uint32_t EmitOneDimensionalGatherLz(ValueEmitContext& ctx, const IR::MemoryInfo& uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t data, bool integer) { const auto swizzle = ctx.state.program.info.images[mem.resource].shader_swizzle; const auto info = ImageConversionFormat(ctx.state, mem); - const bool packed = info.format != Prospero::BufferFormat::kInvalid; - EXIT_IF(packed && (!info.packed_bitfield || - (info.type != Format::ComponentType::Uint && - info.type != Format::ComponentType::Unorm))); + const bool converted = info.format != Prospero::BufferFormat::kInvalid; + const bool packed = info.packed_bitfield; + EXIT_IF(converted && info.type != Format::ComponentType::Sint && + (!packed || (info.type != Format::ComponentType::Uint && + info.type != Format::ComponentType::Unorm))); const auto zero = ConstantU32(ctx.state, 0); uint32_t values[4] {zero, zero, zero, zero}; auto packed_value = zero; const auto dmask = mem.dmask != 0u ? mem.dmask : 1u; - for (uint32_t component = 0; component < (packed ? info.component_count : 4u); component++) { + for (uint32_t component = 0; component < (converted ? info.component_count : 4u); component++) { const auto source = InverseSwizzle(swizzle, component); const bool selected = source < 4u && ((dmask >> source) & 1u) != 0u; uint32_t raw = zero; @@ -638,9 +640,13 @@ uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t d } } } - if (packed) { + if (converted) { if (!selected) continue; auto value = EncodeFormattedStoreComponent(ctx.state, info, component, raw, mem.data_bits); + if (!packed) { + values[component] = value; + continue; + } if (info.type == Format::ComponentType::Uint) { value = EmitUMin32(ctx.state, value, ConstantU32(ctx.state, (1u << info.component_bits[component]) - 1u)); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp index cae819067..67201cb71 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp @@ -435,6 +435,13 @@ uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::Buffer data = EmitBitCastU32F32(state, EmitF16BitsToF32(state, data)); } } + if (info.type == Format::ComponentType::Sint && bits < 32u) { + const auto maximum = (1u << (bits - 1u)) - 1u; + data = EmitSMax32(state, data, ConstantU32(state, ~maximum)); + data = EmitSMin32(state, data, ConstantU32(state, maximum)); + return Binary(state, spv::OpBitwiseAnd, TypeU32(state), data, + ConstantU32(state, (1u << bits) - 1u)); + } const bool normalized = info.type == Format::ComponentType::Unorm || info.type == Format::ComponentType::Snorm; const bool scaled = info.type == Format::ComponentType::Uscaled || diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index 9feb74586..e79883eed 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -169,6 +169,17 @@ struct ImageResource { bool operator==(const ImageResource& other) const = default; }; +[[nodiscard]] constexpr Prospero::BufferFormat +RawSignedStorageFormat(const ImageResource& image, Prospero::BufferFormat format) { + if (image.resource_class != ImageResourceClass::Storage || !image.written) + return Prospero::BufferFormat::kInvalid; + if (format == Prospero::BufferFormat::k32SInt && (image.atomic ? !image.atomic64 : !image.read)) + return Prospero::BufferFormat::k32UInt; + if (format == Prospero::BufferFormat::k16SInt && !image.read && !image.atomic) + return Prospero::BufferFormat::k16UInt; + return Prospero::BufferFormat::kInvalid; +} + struct SamplerResource { uint32_t source = 0; uint32_t first_use_pc = 0; diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp index c180dcf88..8b87bb2bb 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp @@ -755,8 +755,9 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna if (storage || image.conversion_format != Prospero::BufferFormat::kInvalid) { image.shader_swizzle = DescriptorImageSwizzle(descriptor); } - const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt && - base.written && (base.atomic ? !base.atomic64 : !base.read); + const auto raw_sint_format = RawSignedStorageFormat(base, format); + const bool raw_sint_storage = raw_sint_format != Prospero::BufferFormat::kInvalid; + if (raw_sint_format == Prospero::BufferFormat::k16UInt) image.conversion_format = format; image.numeric_class = Prospero::SampledTextureNumericClass( Prospero::RemapTextureFormat(format)); if (!storage && format == Prospero::BufferFormat::k11_11_10UNorm) { diff --git a/tests/ResourceMaterializationTests.cpp b/tests/ResourceMaterializationTests.cpp index 271bb7907..38287b704 100644 --- a/tests/ResourceMaterializationTests.cpp +++ b/tests/ResourceMaterializationTests.cpp @@ -1164,6 +1164,82 @@ void TestMixedSamplerVariantsShareRuntimeDescriptor() { "sampler variants retained stale or duplicated descriptors after refresh"); } +void TestSignedStorageSpecialization() { + using namespace Libs::Graphics::ShaderRecompiler::IR; + using F = Libs::Graphics::Prospero::BufferFormat; + using N = Libs::Graphics::Prospero::TextureNumericClass; + Program program; + program.stage = Libs::Graphics::ShaderType::Compute; + program.srt_plan_complete = program.resource_tracking_complete = true; + program.user_data_count = 1u; + auto &block = AddValueBlock(program); + DescriptorSource source; + source.dword_count = 8u; + source.dwords.fill(Value(0u)); + source.dwords[0] = Value(0x100u); + source.dwords[1] = Value(&block.AppendNewInst(ValueOpcode::GetUserData, {Value(ScalarReg(0))})); + constexpr auto swizzle = Libs::Graphics::DstSel(5, 4, 1, 0); + source.dwords[3] = Value(swizzle | + (uint32_t(Libs::Graphics::Prospero::ImageType::kColor2D) << 28u)); + program.descriptor_sources.push_back(source); + program.info.images.push_back({.source = 0u, .resource_class = ImageResourceClass::Storage, + .dimension = Libs::Graphics::ShaderRecompiler::Decoder::ImageDimension::Dim2D, .written = true}); + auto plan = ExtractResourcePlan(program); + std::array data{}; + const SrtRuntime runtime{.user_data = data}; + ResourceSnapshot snapshot; + ResourceSpecialization specialization, signed16; + for (const auto format : {F::k16SInt, F::k32SInt, F::k16SInt}) { + data[0] = uint32_t(format) << 20u; + Check(MaterializeResources(plan, runtime, snapshot, specialization), + "write-only signed storage image failed materialization"); + const auto conversion = format == F::k16SInt ? format : F::kInvalid; + const auto view = format == F::k16SInt ? F::k16UInt : F::k32UInt; + Check(specialization.images[0].numeric_class == N::Uint && + specialization.images[0].conversion_format == conversion && + specialization.images[0].shader_swizzle == swizzle && + snapshot.images[0].dwords[1] == data[0] && + RawSignedStorageFormat(plan.info.images[0], format) == view, + "signed storage specialization lost guest format, conversion, swizzle or raw view width"); + if (format == F::k16SInt) { + if (signed16.images.empty()) signed16 = specialization; + else Check(specialization == signed16, "signed storage format refresh retained a stale specialization"); + } else { + Check(specialization != signed16, "signed 16/32-bit stores shared incompatible conversion state"); + } + ApplyResourceSpecialization(program, specialization); + const auto binding = DescriptorBindingForImage(program.info.images[0]); + Check(binding && ImageBindingResourceClass(*binding) == ImageResourceClass::Storage, + "signed storage conversion did not reuse the unsigned storage binding"); + } + for (const auto format : {F::k16SInt, F::k32SInt}) { + data[0] = uint32_t(format) << 20u; + auto &image = plan.info.images[0]; + image.read = true; + Check(RawSignedStorageFormat(image, format) == F::kInvalid && + !MaterializeResources(plan, runtime, snapshot, specialization), + "signed raw storage view accepted a non-atomic read/write image"); + image.atomic = true; + Check((RawSignedStorageFormat(image, format) != F::kInvalid) == (format == F::k32SInt) && + MaterializeResources(plan, runtime, snapshot, specialization) == (format == F::k32SInt), + "signed storage conversion changed atomic format eligibility"); + image.atomic64 = true; + Check(RawSignedStorageFormat(image, format) == F::kInvalid && + !MaterializeResources(plan, runtime, snapshot, specialization), + "signed 16/32-bit image acquired a 64-bit atomic raw view"); + image.resource_class = ImageResourceClass::Sampled; + image.written = image.atomic = image.atomic64 = false; + Check(MaterializeResources(plan, runtime, snapshot, specialization) && + specialization.images[0].numeric_class == N::Sint && + specialization.images[0].conversion_format == F::kInvalid && + RawSignedStorageFormat(image, format) == F::kInvalid, + "storage conversion changed ordinary signed image reads"); + image.resource_class = ImageResourceClass::Storage; + image.written = true; + image.read = false; + } +} + void TestIndirectImageSamplerClasses(bool mixed) { using namespace Libs::Graphics::ShaderRecompiler::IR; using Libs::Graphics::Prospero::BufferFormat; @@ -1256,6 +1332,7 @@ int main() { TestCyclicControlPredicatesRemainUnknown(); TestEmptyScalarBufferSpecialization(); TestMixedSamplerVariantsShareRuntimeDescriptor(); + TestSignedStorageSpecialization(); for (const bool mixed : {false, true}) TestIndirectImageSamplerClasses(mixed); std::puts("ResourceMaterializationTests: all cases passed"); return 0; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index ced337eef..6962d5c2c 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -37,6 +37,7 @@ #include "graphics/host_gpu/renderer/renderTarget.h" #include "graphics/host_gpu/renderer/sync.h" #include "graphics/host_gpu/vulkanCommon.h" +#include #include "graphics/presentation/window/windowInternal.h" #include "graphics/shader/recompiler/BufferFormat.h" #include "graphics/shader/recompiler/ShaderRecompiler.h" @@ -2009,7 +2010,6 @@ public: u32 height = 0; u32 layers = 1; u32 mip_levels = 1; - u32 dwords_per_pixel = 0; }; [[nodiscard]] vk::Device Device() const { return m_device; } @@ -15749,11 +15749,12 @@ public: return std::max(value, 1u); } - static size_t ImageMipDwordCount(u32 width, u32 height, u32 dwords_per_pixel, + static size_t ImageMipDwordCount(u32 width, u32 height, vk::Format format, u32 level, u32 layers = 1) { - return static_cast(MipExtent(width, level)) * - static_cast(MipExtent(height, level)) * layers * - dwords_per_pixel; + const auto block = vk::blockExtent(format); + const auto blocks_x = (MipExtent(width, level) + block[0] - 1u) / block[0]; + const auto blocks_y = (MipExtent(height, level) + block[1] - 1u) / block[1]; + return (size_t{blocks_x} * blocks_y * layers * vk::blockSize(format) + 3u) / 4u; } Image CreateImageMips(const char *shader_name, u32 width, u32 height, @@ -15772,7 +15773,8 @@ public: ret.height = height; ret.layers = layers; ret.mip_levels = std::max(static_cast(initial_mips.size()), 1u); - ret.dwords_per_pixel = dwords_per_pixel; + Require(shader_name, "image format size", dwords_per_pixel == (vk::blockSize(format) + 3u) / 4u, + "image fixture word size disagrees with its native format"); vk::ImageCreateInfo image_info{}; image_info.sType = vk::StructureType::eImageCreateInfo; @@ -15845,13 +15847,13 @@ public: size_t total_dwords = 0; for (u32 level = 0; level < ret.mip_levels; level++) { total_dwords += - ImageMipDwordCount(width, height, dwords_per_pixel, level, layers); + ImageMipDwordCount(width, height, format, level, layers); } std::vector contents(total_dwords, 0); size_t offset = 0; for (u32 level = 0; level < ret.mip_levels; level++) { const auto level_dwords = - ImageMipDwordCount(width, height, dwords_per_pixel, level, layers); + ImageMipDwordCount(width, height, format, level, layers); const auto &src = initial_mips[level]; for (size_t i = 0; i < src.size() && i < level_dwords; i++) { contents[offset + i] = src[i]; @@ -15891,9 +15893,8 @@ public: } std::vector ReadImage(const char *shader_name, Image *image) { - const auto dword_count = static_cast(image->width) * - static_cast(image->height) * - image->layers * image->dwords_per_pixel; + const auto dword_count = ImageMipDwordCount(image->width, image->height, + image->format, 0, image->layers); auto staging = CreateHostBuffer(shader_name, dword_count * sizeof(u32), vk::BufferUsageFlagBits::eTransferDst, {}); @@ -19549,7 +19550,7 @@ private: copies.push_back(copy); offset += static_cast( ImageMipDwordCount(image->width, image->height, - image->dwords_per_pixel, level, image->layers) * + image->format, level, image->layers) * sizeof(u32)); } cmd.copyBufferToImage(staging, image->image, @@ -31882,23 +31883,23 @@ TestCase TBufferLoadFormatXSintSignExtendsSubDword() { TestCase TBufferStoreFormatXSintWritesSubDword() { using O = ShaderOpcode; - - std::vector code; - AppendVMovU32(&code, 20, 0); - AppendVMovLiteral(&code, 0, 0xffffff80u); - code.push_back(EncodeMtbuf0(0x04, 6, 0)); - code.push_back(EncodeMtbuf1(0x04, 0, 0, 20)); - AppendVMovU32(&code, 20, 4); - AppendVMovLiteral(&code, 1, 0xffff8001u); - code.push_back(EncodeMtbuf0(0x04, 12, 0)); - code.push_back(EncodeMtbuf1(0x04, 1, 0, 20)); - AppendEnd(&code); - - return {"TBufferStoreFormatXSintWritesSubDword", - code, - {0x11223344u, 0x55667788u}, - {0x11223380u, 0x55668001u}, - {O::V_MOV_B32, O::TBUFFER_STORE_FORMAT_X, O::S_ENDPGM}}; + TestCase test; + test.name = "TBufferStoreFormatXSintWritesSubDword"; + constexpr std::array inputs{0xffffff80u, 0xffffff7fu, 128u, + 0xffff8001u, 0xffff7fffu, 32768u}; + constexpr std::array encoded{0x80u, 0x80u, 0x7fu, 0x8001u, 0x8000u, 0x7fffu}; + test.initial.assign(inputs.size(), 0x11223344u); + test.expected = test.initial; + for (u32 index = 0; index < inputs.size(); ++index) { + AppendVMovU32(&test.code, 20, index * 4u); + AppendVMovLiteral(&test.code, 0, inputs[index]); + test.code.push_back(EncodeMtbuf0(0x04, index < 3u ? 6u : 12u, 0)); + test.code.push_back(EncodeMtbuf1(0x04, 0, 0, 20)); + test.expected[index] = (test.expected[index] & (index < 3u ? 0xffffff00u : 0xffff0000u)) | encoded[index]; + } + AppendEnd(&test.code); + test.opcodes = {O::V_MOV_B32, O::TBUFFER_STORE_FORMAT_X, O::S_ENDPGM}; + return test; } TestCase TBufferLoadFormatXy88IntegerComponents() { @@ -38292,6 +38293,82 @@ TestCase ImageStoreR32FloatUsesFormatlessStorageImage() { return test; } +void CheckSigned16ImageStore(VulkanHarness &vulkan) { + constexpr std::array values{INT32_MIN, -32769, -32768, -1, 0, 32767, 32768, INT32_MAX}; + const u32 alignment = vulkan.RuntimeContext().GetPhysicalDeviceProperties() + .limits.minStorageBufferOffsetAlignment; + for (const u32 wave : {32u, 64u}) { + for (const bool d16 : {false, true}) { + for (const u32 component : {0u, 2u}) { + TestCase test; + test.name = "Signed16ImageAndBufferStore"; + const u32 width = wave - 2u; + const u32 output = (wave * 4u + alignment - 1u) / alignment * alignment; + test.user_data = MakeStorageTextureData(Prospero::BufferFormat::k16SInt); + test.user_data[1] |= ((width - 1u) & 3u) << 30u; + test.user_data[2] = ((width - 1u) >> 2u) | (1u << 14u); + test.has_user_data = test.has_compute_info = true; + test.image_descriptor_swizzle = component == 0u ? DstSel(4, 0, 0, 1) : DstSel(0, 0, 4, 1); + const std::array descriptors{0x1000u, 4u << 16u, wave, 0x5204u, + 0x2000u, 2u << 16u, wave, (u32(Prospero::BufferFormat::k16SInt) << 12u) | 4u}; + std::copy(descriptors.begin(), descriptors.end(), test.user_data.begin() + 8u); + test.storage_buffer_ranges = {{0u, wave * 4u}, {output, wave * 2u}}; + test.compute_info.wave_size = wave; + test.compute_info.threads_num[0] = wave; + test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1u; + test.compute_info.thread_ids_num = 1u; + test.compute_info.lds_size_dwords = 0u; + test.initial.assign(output / 4u + wave / 2u, 0xdeadbeefu); + for (u32 lane = 0; lane < wave; ++lane) + test.initial[lane] = d16 ? 0x7a5b0000u | (u32(values[lane % values.size()]) & 0xffffu) + : u32(values[lane % values.size()]); + test.expected = test.initial; + std::vector pixels(width, 0x13571357u), expected = pixels; + for (u32 lane = 0; lane < wave; ++lane) { + const auto input = values[lane % values.size()]; + const auto value = d16 ? int32_t(std::bit_cast(uint16_t(input))) : input; + const u32 encoded = uint16_t(std::clamp(value, -32768, 32767)); + const auto set_half = [&](std::vector &words, u32 index) { + const auto shift = (index & 1u) * 16u; + words[index / 2u] = (words[index / 2u] & ~(0xffffu << shift)) | (encoded << shift); + }; + set_half(test.expected, output / 2u + lane); + if (lane < width) { + set_half(expected, lane); + if (lane & 1u) set_half(expected, width + lane); + } + } + auto &code = test.code; + code = {EncodeMubuf0(0x0c, 0, true, false), EncodeMubuf1(4, 2, 0), + EncodeSopp(0x0c, 0), EncodeMubuf0(d16 ? 0x84 : 0x04, 0, true, false), + EncodeMubuf1(4, 3, 0), EncodeVop1(0x01, 20, Vgpr(0))}; + AppendVMovU32(&code, 21, 0u); + code.insert(code.end(), {EncodeMimg0(0x08, 1u << component), + EncodeMimg1(4, 20, 0, 0, false, d16)}); + AppendVMovU32(&code, 21, 1u); + AppendSMovLiteral(&code, 126, 0xaaaaaaaau); + AppendSMovLiteral(&code, 127, wave == 64u ? 0xaaaaaaaau : 0u); + code.insert(code.end(), {EncodeMimg0(0x08, 1u << component), + EncodeMimg1(4, 20, 0, 0, false, d16)}); + AppendEnd(&code); + test.opcodes = {ShaderOpcode::IMAGE_STORE}; + const auto compiled = CompileCase(test, vulkan.SubgroupSize()); + auto buffer = vulkan.CreateStorageBuffer(test.name, test.initial, test.initial.size()); + auto image = vulkan.CreateImage2D(test.name, width, 2u, vk::Format::eR16Sint, + vk::ImageUsageFlagBits::eStorage, pixels, 1u, vk::ImageLayout::eGeneral, vk::Format::eR16Uint); + vulkan.Dispatch(test, compiled, buffer, nullptr, nullptr, nullptr, &image); + CompareWords(test, "signed image saturation, sparse channels, masked lanes and OOB", expected, + vulkan.ReadImage(test.name, &image)); + CompareWords(test, "shared buffer conversion and adjacent halfwords", test.expected, + vulkan.ReadBuffer(test.name, buffer, test.expected.size())); + vulkan.DestroyImage(&image); + vulkan.DestroyBuffer(&buffer); + } + } + } + std::printf("[compute] %-32s ok\n", "Signed16ImageAndBufferStore"); +} + TestCase ImageStoreR32SintUsesRawUintView() { using O = ShaderOpcode; @@ -45517,6 +45594,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, BufferStoreFormatXRejectsPartialRecord()); RunCase(&vulkan, BufferStoreFormatXyRejectsPartialRecord()); RunCase(&vulkan, BufferFormatStoreVariants()); + RunCase(&vulkan, TBufferStoreFormatXSintWritesSubDword()); RunCase(&vulkan, BufferStoreByteD16Hi(false)); RunCase(&vulkan, BufferStoreByteD16Hi(true)); return 0; @@ -45634,6 +45712,7 @@ int main(int argc, char **argv) { if (argc == 2 && std::strcmp(argv[1], "--packed-texture-only") == 0) { VulkanHarness vulkan; vulkan.CheckPackedTextureComponents(); + CheckSigned16ImageStore(vulkan); vulkan.CheckBgra16Readback(); RunCase(&vulkan, ImageLoadPackedUintUnpacksAndSwizzles()); RunCase(&vulkan, ImageLoadPackedUnorm()); @@ -46414,6 +46493,7 @@ int main(int argc, char **argv) { CheckTessellationPrograms(); CheckPixelParameterAliases(); CheckRectListShaders(); + CheckSigned16ImageStore(vulkan); CheckBoundedFormattedBufferCopy(vulkan); CheckIndirectBufferStore(vulkan); CheckBoundedRecordBufferAccess(vulkan);