mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
graphics: share signed storage conversion and support R16 image writes
Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<uint32_t, 1> 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;
|
||||
|
||||
@@ -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 <vulkan/vulkan_format_traits.hpp>
|
||||
#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<size_t>(MipExtent(width, level)) *
|
||||
static_cast<size_t>(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<u32>(static_cast<u32>(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<u32> 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<u32> ReadImage(const char *shader_name, Image *image) {
|
||||
const auto dword_count = static_cast<size_t>(image->width) *
|
||||
static_cast<size_t>(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<vk::DeviceSize>(
|
||||
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<u32> 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<u32, 6> inputs{0xffffff80u, 0xffffff7fu, 128u,
|
||||
0xffff8001u, 0xffff7fffu, 32768u};
|
||||
constexpr std::array<u32, 6> 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<int32_t, 8> 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<u32, 8> 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<u32> 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<int16_t>(uint16_t(input))) : input;
|
||||
const u32 encoded = uint16_t(std::clamp(value, -32768, 32767));
|
||||
const auto set_half = [&](std::vector<u32> &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<false, false>());
|
||||
@@ -46414,6 +46493,7 @@ int main(int argc, char **argv) {
|
||||
CheckTessellationPrograms();
|
||||
CheckPixelParameterAliases();
|
||||
CheckRectListShaders();
|
||||
CheckSigned16ImageStore(vulkan);
|
||||
CheckBoundedFormattedBufferCopy(vulkan);
|
||||
CheckIndirectBufferStore(vulkan);
|
||||
CheckBoundedRecordBufferAccess(vulkan);
|
||||
|
||||
Reference in New Issue
Block a user