mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 07:38:56 +02:00
VUID-vkCmdDrawIndexed-format-07753 (FLOAT/SINT/UINT mistmatches)
This commit is contained in:
parent
42451f0c3d
commit
3ee14ed99a
20 changed files with 332 additions and 57 deletions
|
|
@ -206,10 +206,16 @@ std::string_view FormatStorage(ImageFormat format) {
|
|||
return "S16";
|
||||
case ImageFormat::R32_UINT:
|
||||
return "U32";
|
||||
case ImageFormat::R32_SINT:
|
||||
return "S32";
|
||||
case ImageFormat::R32G32_UINT:
|
||||
return "U32X2";
|
||||
case ImageFormat::R32G32_SINT:
|
||||
return "S32X2";
|
||||
case ImageFormat::R32G32B32A32_UINT:
|
||||
return "U32X4";
|
||||
case ImageFormat::R32G32B32A32_SINT:
|
||||
return "S32X4";
|
||||
}
|
||||
throw InvalidArgument("Invalid image format {}", format);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,10 +148,16 @@ std::string_view ImageFormatString(ImageFormat format) {
|
|||
return ",r16i";
|
||||
case ImageFormat::R32_UINT:
|
||||
return ",r32ui";
|
||||
case ImageFormat::R32_SINT:
|
||||
return ",r32i";
|
||||
case ImageFormat::R32G32_UINT:
|
||||
return ",rg32ui";
|
||||
case ImageFormat::R32G32_SINT:
|
||||
return ",rg32i";
|
||||
case ImageFormat::R32G32B32A32_UINT:
|
||||
return ",rgba32ui";
|
||||
case ImageFormat::R32G32B32A32_SINT:
|
||||
return ",rgba32i";
|
||||
default:
|
||||
throw NotImplementedException("Image format: {}", format);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
|
|||
if (def.count > 1) {
|
||||
throw NotImplementedException("Indirect texture sample");
|
||||
}
|
||||
return ctx.OpLoad(ctx.image_buffer_type, def.id);
|
||||
return ctx.OpLoad(def.image_type, def.id);
|
||||
} else {
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
|
|
@ -215,16 +215,22 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<Id, bool> Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
struct ImageInfo {
|
||||
Id image;
|
||||
bool is_integer;
|
||||
bool is_signed;
|
||||
};
|
||||
|
||||
ImageInfo Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
if (!index.IsImmediate() || index.U32() != 0) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
|
||||
return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
|
||||
return {ctx.OpLoad(def.image_type, def.id), def.is_integer, def.is_signed};
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
|
||||
return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
|
||||
return {ctx.OpLoad(def.image_type, def.id), def.is_integer, def.is_signed};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -550,8 +556,25 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
|
|||
lod = Id{};
|
||||
}
|
||||
const ImageOperands operands(lod, ms);
|
||||
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
||||
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||
bool is_integer = false;
|
||||
bool is_signed = false;
|
||||
Id result_type{ctx.F32[4]};
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)};
|
||||
is_integer = def.is_integer;
|
||||
is_signed = def.is_signed;
|
||||
if (is_integer) {
|
||||
result_type = is_signed ? ctx.S32[4] : ctx.U32[4];
|
||||
}
|
||||
}
|
||||
Id fetched = Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
|
||||
result_type, TextureImage(ctx, info, index), coords,
|
||||
operands.MaskOptional(), operands.Span());
|
||||
if (is_integer) {
|
||||
// IR expects F32x4 from ImageFetch; bitcast integer results to float vector.
|
||||
fetched = ctx.OpBitcast(ctx.F32[4], fetched);
|
||||
}
|
||||
return fetched;
|
||||
}
|
||||
|
||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
||||
|
|
@ -612,21 +635,25 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
|
|||
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
|
||||
return ctx.ConstantNull(ctx.U32[4]);
|
||||
}
|
||||
const auto [image, is_integer] = Image(ctx, index, info);
|
||||
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
|
||||
const auto [image, is_integer, is_signed] = Image(ctx, index, info);
|
||||
const Id result_type{is_integer ? (is_signed ? ctx.S32[4] : ctx.U32[4]) : ctx.F32[4]};
|
||||
Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst,
|
||||
result_type, image, coords, std::nullopt, std::span<const Id>{})};
|
||||
if (!is_integer) {
|
||||
color = ctx.OpBitcast(ctx.U32[4], color);
|
||||
} else if (is_signed) {
|
||||
color = ctx.OpBitcast(ctx.U32[4], color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const auto [image, is_integer] = Image(ctx, index, info);
|
||||
const auto [image, is_integer, is_signed] = Image(ctx, index, info);
|
||||
if (!is_integer) {
|
||||
color = ctx.OpBitcast(ctx.F32[4], color);
|
||||
} else if (is_signed) {
|
||||
color = ctx.OpBitcast(ctx.S32[4], color);
|
||||
}
|
||||
ctx.OpImageWrite(image, coords, color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,18 @@
|
|||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Image(EmitContext& ctx, IR::TextureInstInfo info) {
|
||||
struct ImageInfo {
|
||||
Id id;
|
||||
bool is_signed;
|
||||
};
|
||||
|
||||
ImageInfo Image(EmitContext& ctx, IR::TextureInstInfo info) {
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
|
||||
return def.id;
|
||||
return {def.id, def.is_signed};
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
|
||||
return def.id;
|
||||
return {def.id, def.is_signed};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,42 +29,66 @@ std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
|
|||
}
|
||||
|
||||
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id), bool value_signed) {
|
||||
if (!index.IsImmediate() || index.U32() != 0) {
|
||||
// TODO: handle layers
|
||||
throw NotImplementedException("Image indexing");
|
||||
}
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const Id image{Image(ctx, info)};
|
||||
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
|
||||
const auto image_info{Image(ctx, info)};
|
||||
Id pointer_type{image_info.is_signed ? ctx.image_s32 : ctx.image_u32};
|
||||
if (!Sirit::ValidId(pointer_type)) {
|
||||
const Id element_type{image_info.is_signed ? ctx.S32[1] : ctx.U32[1]};
|
||||
pointer_type = ctx.TypePointer(spv::StorageClass::Image, element_type);
|
||||
}
|
||||
const Id image{image_info.id};
|
||||
const Id pointer{ctx.OpImageTexelPointer(pointer_type, image, coords, ctx.Const(0U))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||
const Id result_type{image_info.is_signed ? ctx.S32[1] : ctx.U32[1]};
|
||||
|
||||
// Ensure value type matches result_type's pointee type
|
||||
Id cast_value{value};
|
||||
if (image_info.is_signed) {
|
||||
// Result type is signed s32, ensure value is also s32
|
||||
cast_value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
} else {
|
||||
// Result type is unsigned u32, ensure value is also u32
|
||||
cast_value = ctx.OpBitcast(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id result{(ctx.*atomic_func)(result_type, pointer, scope, semantics, cast_value)};
|
||||
|
||||
// Convert result back to u32 for IR compatibility
|
||||
if (image_info.is_signed) {
|
||||
result = ctx.OpBitcast(ctx.U32[1], result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd, false);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin, true);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin, false);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax, true);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax, false);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
||||
|
|
@ -74,22 +103,22 @@ Id EmitImageAtomicDec32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) {
|
|||
|
||||
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd, false);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr, false);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor, false);
|
||||
}
|
||||
|
||||
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id value) {
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange);
|
||||
return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange, false);
|
||||
}
|
||||
|
||||
Id EmitBindlessImageAtomicIAdd32(EmitContext&) {
|
||||
|
|
|
|||
|
|
@ -69,10 +69,16 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
|
|||
return spv::ImageFormat::R16i;
|
||||
case ImageFormat::R32_UINT:
|
||||
return spv::ImageFormat::R32ui;
|
||||
case ImageFormat::R32_SINT:
|
||||
return spv::ImageFormat::R32i;
|
||||
case ImageFormat::R32G32_UINT:
|
||||
return spv::ImageFormat::Rg32ui;
|
||||
case ImageFormat::R32G32_SINT:
|
||||
return spv::ImageFormat::Rg32i;
|
||||
case ImageFormat::R32G32B32A32_UINT:
|
||||
return spv::ImageFormat::Rgba32ui;
|
||||
case ImageFormat::R32G32B32A32_SINT:
|
||||
return spv::ImageFormat::Rgba32i;
|
||||
}
|
||||
throw InvalidArgument("Invalid image format {}", format);
|
||||
}
|
||||
|
|
@ -1311,20 +1317,25 @@ void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) {
|
|||
return;
|
||||
}
|
||||
const spv::ImageFormat format{spv::ImageFormat::Unknown};
|
||||
image_buffer_type = TypeImage(F32[1], spv::Dim::Buffer, 0U, false, false, 1, format);
|
||||
|
||||
const Id type{TypePointer(spv::StorageClass::UniformConstant, image_buffer_type)};
|
||||
texture_buffers.reserve(info.texture_buffer_descriptors.size());
|
||||
for (const TextureBufferDescriptor& desc : info.texture_buffer_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Array of texture buffers");
|
||||
}
|
||||
// Use the correct sampled type based on the descriptor's data format
|
||||
const Id sampled_type{desc.is_integer ? (desc.is_signed ? S32[1] : U32[1]) : F32[1]};
|
||||
image_buffer_type = TypeImage(sampled_type, spv::Dim::Buffer, 0U, false, false, 1, format);
|
||||
const Id type{TypePointer(spv::StorageClass::UniformConstant, image_buffer_type)};
|
||||
const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)};
|
||||
Decorate(id, spv::Decoration::Binding, binding);
|
||||
Decorate(id, spv::Decoration::DescriptorSet, 0U);
|
||||
Name(id, NameOf(stage, desc, "texbuf"));
|
||||
texture_buffers.push_back({
|
||||
.id = id,
|
||||
.image_type = image_buffer_type,
|
||||
.is_integer = desc.is_integer,
|
||||
.is_signed = desc.is_signed,
|
||||
.count = desc.count,
|
||||
});
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
|
|
@ -1341,7 +1352,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
|
|||
throw NotImplementedException("Array of image buffers");
|
||||
}
|
||||
const spv::ImageFormat format{GetImageFormat(desc.format)};
|
||||
const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
|
||||
const Id sampled_type{desc.is_integer ? (desc.is_signed ? S32[1] : U32[1]) : F32[1]};
|
||||
const Id image_type{
|
||||
TypeImage(sampled_type, spv::Dim::Buffer, false, false, false, 2, format)};
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
|
||||
|
|
@ -1357,6 +1368,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
|
|||
.image_type = image_type,
|
||||
.count = desc.count,
|
||||
.is_integer = desc.is_integer,
|
||||
.is_signed = desc.is_signed,
|
||||
});
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(id);
|
||||
|
|
@ -1393,6 +1405,9 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
|
|||
if (info.uses_atomic_image_u32) {
|
||||
image_u32 = TypePointer(spv::StorageClass::Image, U32[1]);
|
||||
}
|
||||
if (info.uses_atomic_s32_min || info.uses_atomic_s32_max) {
|
||||
image_s32 = TypePointer(spv::StorageClass::Image, S32[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_index) {
|
||||
|
|
@ -1401,7 +1416,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
|
|||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Array of images");
|
||||
}
|
||||
const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
|
||||
const Id sampled_type{desc.is_integer ? (desc.is_signed ? S32[1] : U32[1]) : F32[1]};
|
||||
const Id image_type{ImageType(*this, desc, sampled_type)};
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
|
||||
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
|
||||
|
|
@ -1417,6 +1432,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
|
|||
.image_type = image_type,
|
||||
.count = desc.count,
|
||||
.is_integer = desc.is_integer,
|
||||
.is_signed = desc.is_signed,
|
||||
});
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(id);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ struct TextureDefinition {
|
|||
|
||||
struct TextureBufferDefinition {
|
||||
Id id;
|
||||
Id image_type; // Stores the correct buffer image type (F32 or U32 based on descriptor)
|
||||
bool is_integer;
|
||||
bool is_signed;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
|
|
@ -53,6 +56,7 @@ struct ImageBufferDefinition {
|
|||
Id image_type;
|
||||
u32 count;
|
||||
bool is_integer;
|
||||
bool is_signed;
|
||||
};
|
||||
|
||||
struct ImageDefinition {
|
||||
|
|
@ -60,6 +64,7 @@ struct ImageDefinition {
|
|||
Id image_type;
|
||||
u32 count;
|
||||
bool is_integer;
|
||||
bool is_signed;
|
||||
};
|
||||
|
||||
struct UniformDefinitions {
|
||||
|
|
@ -250,6 +255,7 @@ public:
|
|||
|
||||
Id image_buffer_type{};
|
||||
Id image_u32{};
|
||||
Id image_s32{};
|
||||
|
||||
std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
|
||||
std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
|
||||
|
|
|
|||
|
|
@ -204,6 +204,65 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
|||
return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf));
|
||||
}
|
||||
|
||||
static inline bool IsTexturePixelFormatSignedCached(Environment& env,
|
||||
const ConstBufferAddr& cbuf) {
|
||||
switch (ReadTexturePixelFormatCached(env, cbuf)) {
|
||||
case TexturePixelFormat::A8B8G8R8_SINT:
|
||||
case TexturePixelFormat::R8_SINT:
|
||||
case TexturePixelFormat::R8G8_SINT:
|
||||
case TexturePixelFormat::R16_SINT:
|
||||
case TexturePixelFormat::R16G16_SINT:
|
||||
case TexturePixelFormat::R16G16B16A16_SINT:
|
||||
case TexturePixelFormat::R32_SINT:
|
||||
case TexturePixelFormat::R32G32_SINT:
|
||||
case TexturePixelFormat::R32G32B32A32_SINT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool IsImageFormatSigned(ImageFormat format) {
|
||||
switch (format) {
|
||||
case ImageFormat::R8_SINT:
|
||||
case ImageFormat::R16_SINT:
|
||||
case ImageFormat::R32_SINT:
|
||||
case ImageFormat::R32G32_SINT:
|
||||
case ImageFormat::R32G32B32A32_SINT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline std::optional<ImageFormat> BufferImageFormatFromPixelFormat(
|
||||
TexturePixelFormat pixel_format) {
|
||||
switch (pixel_format) {
|
||||
case TexturePixelFormat::R8_UINT:
|
||||
return ImageFormat::R8_UINT;
|
||||
case TexturePixelFormat::R8_SINT:
|
||||
return ImageFormat::R8_SINT;
|
||||
case TexturePixelFormat::R16_UINT:
|
||||
return ImageFormat::R16_UINT;
|
||||
case TexturePixelFormat::R16_SINT:
|
||||
return ImageFormat::R16_SINT;
|
||||
case TexturePixelFormat::R32_UINT:
|
||||
return ImageFormat::R32_UINT;
|
||||
case TexturePixelFormat::R32_SINT:
|
||||
return ImageFormat::R32_SINT;
|
||||
case TexturePixelFormat::R32G32_UINT:
|
||||
return ImageFormat::R32G32_UINT;
|
||||
case TexturePixelFormat::R32G32_SINT:
|
||||
return ImageFormat::R32G32_SINT;
|
||||
case TexturePixelFormat::R32G32B32A32_UINT:
|
||||
return ImageFormat::R32G32B32A32_UINT;
|
||||
case TexturePixelFormat::R32G32B32A32_SINT:
|
||||
return ImageFormat::R32G32B32A32_SINT;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
|
||||
|
|
@ -652,12 +711,21 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
|||
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
|
||||
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
|
||||
const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)};
|
||||
ImageFormat image_format = flags.image_format;
|
||||
if (flags.type == TextureType::Buffer) {
|
||||
const auto pixel_format = ReadTexturePixelFormatCached(env, cbuf);
|
||||
if (const auto mapped = BufferImageFormatFromPixelFormat(pixel_format)) {
|
||||
image_format = *mapped;
|
||||
}
|
||||
}
|
||||
const bool is_signed{IsImageFormatSigned(image_format)};
|
||||
if (flags.type == TextureType::Buffer) {
|
||||
index = descriptors.Add(ImageBufferDescriptor{
|
||||
.format = flags.image_format,
|
||||
.format = image_format,
|
||||
.is_written = is_written,
|
||||
.is_read = is_read,
|
||||
.is_integer = is_integer,
|
||||
.is_signed = is_signed,
|
||||
.cbuf_index = cbuf.index,
|
||||
.cbuf_offset = cbuf.offset,
|
||||
.count = cbuf.count,
|
||||
|
|
@ -666,22 +734,26 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
|||
} else {
|
||||
index = descriptors.Add(ImageDescriptor{
|
||||
.type = flags.type,
|
||||
.format = flags.image_format,
|
||||
.format = image_format,
|
||||
.is_written = is_written,
|
||||
.is_read = is_read,
|
||||
.is_integer = is_integer,
|
||||
.is_signed = is_signed,
|
||||
.cbuf_index = cbuf.index,
|
||||
.cbuf_offset = cbuf.offset,
|
||||
.count = cbuf.count,
|
||||
.size_shift = DESCRIPTOR_SIZE_SHIFT,
|
||||
});
|
||||
}
|
||||
flags.image_format.Assign(image_format);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (flags.type == TextureType::Buffer) {
|
||||
index = descriptors.Add(TextureBufferDescriptor{
|
||||
.has_secondary = cbuf.has_secondary,
|
||||
.is_integer = IsTexturePixelFormatIntegerCached(env, cbuf),
|
||||
.is_signed = IsTexturePixelFormatSignedCached(env, cbuf),
|
||||
.cbuf_index = cbuf.index,
|
||||
.cbuf_offset = cbuf.offset,
|
||||
.shift_left = cbuf.shift_left,
|
||||
|
|
|
|||
|
|
@ -150,8 +150,11 @@ enum class ImageFormat : u32 {
|
|||
R16_UINT,
|
||||
R16_SINT,
|
||||
R32_UINT,
|
||||
R32_SINT,
|
||||
R32G32_UINT,
|
||||
R32G32_SINT,
|
||||
R32G32B32A32_UINT,
|
||||
R32G32B32A32_SINT,
|
||||
};
|
||||
|
||||
enum class Interpolation {
|
||||
|
|
@ -178,6 +181,8 @@ struct StorageBufferDescriptor {
|
|||
|
||||
struct TextureBufferDescriptor {
|
||||
bool has_secondary;
|
||||
bool is_integer; // True if data is SINT/UINT (from R_type in TIC), false if FLOAT
|
||||
bool is_signed; // True if integer data is signed
|
||||
u32 cbuf_index;
|
||||
u32 cbuf_offset;
|
||||
u32 shift_left;
|
||||
|
|
@ -196,6 +201,7 @@ struct ImageBufferDescriptor {
|
|||
bool is_written;
|
||||
bool is_read;
|
||||
bool is_integer;
|
||||
bool is_signed;
|
||||
u32 cbuf_index;
|
||||
u32 cbuf_offset;
|
||||
u32 count;
|
||||
|
|
@ -229,6 +235,7 @@ struct ImageDescriptor {
|
|||
bool is_written;
|
||||
bool is_read;
|
||||
bool is_integer;
|
||||
bool is_signed;
|
||||
u32 cbuf_index;
|
||||
u32 cbuf_offset;
|
||||
u32 count;
|
||||
|
|
|
|||
|
|
@ -458,14 +458,14 @@ void BufferCache<P>::UnbindGraphicsTextureBuffers(size_t stage) {
|
|||
template <class P>
|
||||
void BufferCache<P>::BindGraphicsTextureBuffer(size_t stage, size_t tbo_index, GPUVAddr gpu_addr,
|
||||
u32 size, PixelFormat format, bool is_written,
|
||||
bool is_image) {
|
||||
bool is_image, bool is_integer, bool is_signed) {
|
||||
channel_state->enabled_texture_buffers[stage] |= 1U << tbo_index;
|
||||
channel_state->written_texture_buffers[stage] |= (is_written ? 1U : 0U) << tbo_index;
|
||||
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
|
||||
channel_state->image_texture_buffers[stage] |= (is_image ? 1U : 0U) << tbo_index;
|
||||
}
|
||||
channel_state->texture_buffers[stage][tbo_index] =
|
||||
GetTextureBufferBinding(gpu_addr, size, format);
|
||||
GetTextureBufferBinding(gpu_addr, size, format, is_integer, is_signed);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
|
@ -532,7 +532,8 @@ void BufferCache<P>::UnbindComputeTextureBuffers() {
|
|||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size,
|
||||
PixelFormat format, bool is_written, bool is_image) {
|
||||
PixelFormat format, bool is_written, bool is_image,
|
||||
bool is_integer, bool is_signed) {
|
||||
if (tbo_index >= channel_state->compute_texture_buffers.size()) [[unlikely]] {
|
||||
LOG_ERROR(HW_GPU, "Texture buffer index {} exceeds maximum texture buffer count",
|
||||
tbo_index);
|
||||
|
|
@ -544,7 +545,7 @@ void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_add
|
|||
channel_state->image_compute_texture_buffers |= (is_image ? 1U : 0U) << tbo_index;
|
||||
}
|
||||
channel_state->compute_texture_buffers[tbo_index] =
|
||||
GetTextureBufferBinding(gpu_addr, size, format);
|
||||
GetTextureBufferBinding(gpu_addr, size, format, is_integer, is_signed);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
|
@ -955,15 +956,17 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
|
|||
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
const PixelFormat format = binding.format;
|
||||
const bool is_integer = binding.is_integer;
|
||||
const bool is_signed = binding.is_signed;
|
||||
buffer.MarkUsage(offset, size);
|
||||
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
|
||||
if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) {
|
||||
runtime.BindImageBuffer(buffer, offset, size, format);
|
||||
} else {
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format);
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format, is_integer, is_signed);
|
||||
}
|
||||
} else {
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format);
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format, is_integer, is_signed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1090,15 +1093,17 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
|
|||
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
const PixelFormat format = binding.format;
|
||||
const bool is_integer = binding.is_integer;
|
||||
const bool is_signed = binding.is_signed;
|
||||
buffer.MarkUsage(offset, size);
|
||||
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
|
||||
if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) {
|
||||
runtime.BindImageBuffer(buffer, offset, size, format);
|
||||
} else {
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format);
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format, is_integer, is_signed);
|
||||
}
|
||||
} else {
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format);
|
||||
runtime.BindTextureBuffer(buffer, offset, size, format, is_integer, is_signed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1833,7 +1838,8 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
|||
|
||||
template <class P>
|
||||
TextureBufferBinding BufferCache<P>::GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size,
|
||||
PixelFormat format) {
|
||||
PixelFormat format, bool is_integer,
|
||||
bool is_signed) {
|
||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
|
||||
TextureBufferBinding binding;
|
||||
if (!device_addr || size == 0) {
|
||||
|
|
@ -1841,11 +1847,15 @@ TextureBufferBinding BufferCache<P>::GetTextureBufferBinding(GPUVAddr gpu_addr,
|
|||
binding.size = 0;
|
||||
binding.buffer_id = NULL_BUFFER_ID;
|
||||
binding.format = PixelFormat::Invalid;
|
||||
binding.is_integer = false;
|
||||
binding.is_signed = false;
|
||||
} else {
|
||||
binding.device_addr = *device_addr;
|
||||
binding.size = size;
|
||||
binding.buffer_id = BufferId{};
|
||||
binding.format = format;
|
||||
binding.is_integer = is_integer;
|
||||
binding.is_signed = is_signed;
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ struct Binding {
|
|||
|
||||
struct TextureBufferBinding : Binding {
|
||||
PixelFormat format;
|
||||
bool is_integer{}; // True if data is SINT/UINT, false if FLOAT
|
||||
bool is_signed{}; // True if integer data is signed
|
||||
};
|
||||
|
||||
static constexpr Binding NULL_BINDING{
|
||||
|
|
@ -251,7 +253,8 @@ public:
|
|||
void UnbindGraphicsTextureBuffers(size_t stage);
|
||||
|
||||
void BindGraphicsTextureBuffer(size_t stage, size_t tbo_index, GPUVAddr gpu_addr, u32 size,
|
||||
PixelFormat format, bool is_written, bool is_image);
|
||||
PixelFormat format, bool is_written, bool is_image,
|
||||
bool is_integer, bool is_signed);
|
||||
|
||||
void UnbindComputeStorageBuffers();
|
||||
|
||||
|
|
@ -261,7 +264,7 @@ public:
|
|||
void UnbindComputeTextureBuffers();
|
||||
|
||||
void BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, PixelFormat format,
|
||||
bool is_written, bool is_image);
|
||||
bool is_written, bool is_image, bool is_integer, bool is_signed);
|
||||
|
||||
[[nodiscard]] std::pair<Buffer*, u32> ObtainBuffer(GPUVAddr gpu_addr, u32 size,
|
||||
ObtainBufferSynchronize sync_info,
|
||||
|
|
@ -445,7 +448,8 @@ private:
|
|||
bool is_written) const;
|
||||
|
||||
[[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size,
|
||||
PixelFormat format);
|
||||
PixelFormat format, bool is_integer,
|
||||
bool is_signed);
|
||||
|
||||
[[nodiscard]] std::span<const u8> ImmediateBufferWithData(DAddr device_addr, size_t size);
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ void Buffer::MakeResident(GLenum access) noexcept {
|
|||
glMakeNamedBufferResidentNV(buffer.handle, access);
|
||||
}
|
||||
|
||||
GLuint Buffer::View(u32 offset, u32 size, PixelFormat format) {
|
||||
GLuint Buffer::View(u32 offset, u32 size, PixelFormat format, bool is_integer, bool is_signed) {
|
||||
const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) {
|
||||
return offset == view.offset && size == view.size && format == view.format;
|
||||
})};
|
||||
|
|
@ -370,8 +370,8 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
|
|||
}
|
||||
|
||||
void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||
PixelFormat format) {
|
||||
*texture_handles++ = buffer.View(offset, size, format);
|
||||
PixelFormat format, bool is_integer, bool is_signed) {
|
||||
*texture_handles++ = buffer.View(offset, size, format, is_integer, is_signed);
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindImageBuffer(Buffer& buffer, u32 offset, u32 size, PixelFormat format) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ public:
|
|||
|
||||
void MarkUsage(u64 offset, u64 size) {}
|
||||
|
||||
[[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
|
||||
[[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format,
|
||||
bool is_integer = false, bool is_signed = false);
|
||||
|
||||
[[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept {
|
||||
return address;
|
||||
|
|
@ -118,7 +119,7 @@ public:
|
|||
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||
|
||||
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format);
|
||||
VideoCore::Surface::PixelFormat format, bool is_integer, bool is_signed);
|
||||
|
||||
void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format);
|
||||
|
|
|
|||
|
|
@ -177,7 +177,8 @@ void ComputePipeline::Configure() {
|
|||
ImageView& image_view{texture_cache.GetImageView(views[texbuf_index].id)};
|
||||
buffer_cache.BindComputeTextureBuffer(texbuf_index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
is_written, is_image, desc.is_integer,
|
||||
desc.is_signed);
|
||||
++texbuf_index;
|
||||
}
|
||||
}};
|
||||
|
|
|
|||
|
|
@ -397,7 +397,8 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
|||
ImageView& image_view{texture_cache.GetImageView(texture_buffer_it->id)};
|
||||
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
is_written, is_image, desc.is_integer,
|
||||
desc.is_signed);
|
||||
++index;
|
||||
++texture_buffer_it;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -433,10 +433,16 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
|
|||
return GL_R16I;
|
||||
case Shader::ImageFormat::R32_UINT:
|
||||
return GL_R32UI;
|
||||
case Shader::ImageFormat::R32_SINT:
|
||||
return GL_R32I;
|
||||
case Shader::ImageFormat::R32G32_UINT:
|
||||
return GL_RG32UI;
|
||||
case Shader::ImageFormat::R32G32_SINT:
|
||||
return GL_RG32I;
|
||||
case Shader::ImageFormat::R32G32B32A32_UINT:
|
||||
return GL_RGBA32UI;
|
||||
case Shader::ImageFormat::R32G32B32A32_SINT:
|
||||
return GL_RGBA32I;
|
||||
}
|
||||
ASSERT_MSG(false, "Invalid image format={}", format);
|
||||
return GL_R32UI;
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
|
|||
} // Anonymous namespace
|
||||
|
||||
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
|
||||
: VideoCommon::BufferBase(null_params), tracker{4096} {
|
||||
: VideoCommon::BufferBase(null_params), tracker{4096} {
|
||||
if (runtime.device.HasNullDescriptor()) {
|
||||
return;
|
||||
|
|
@ -93,6 +94,7 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_p
|
|||
}
|
||||
|
||||
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
|
||||
: VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device},
|
||||
: VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device},
|
||||
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, tracker{SizeBytes()} {
|
||||
if (runtime.device.HasDebuggingToolAttached()) {
|
||||
|
|
@ -100,7 +102,46 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
|
|||
}
|
||||
}
|
||||
|
||||
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
|
||||
VkFormat SelectTexelBufferFormat(VkFormat float_format, bool is_integer, bool is_signed) {
|
||||
// If the buffer stores integer data but Vulkan reports float format,
|
||||
// we need to map to appropriate integer formats for type compatibility
|
||||
if (!is_integer) {
|
||||
// Non-integer buffer, use the original float format
|
||||
return float_format;
|
||||
}
|
||||
|
||||
// Integer buffer: map float formats to signed/unsigned equivalents
|
||||
if (is_signed) {
|
||||
// Signed integer
|
||||
switch (float_format) {
|
||||
case VK_FORMAT_R32_SFLOAT:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case VK_FORMAT_R32G32_SFLOAT:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case VK_FORMAT_R32G32B32A32_SFLOAT:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
default:
|
||||
// For non-float formats, use as-is
|
||||
return float_format;
|
||||
}
|
||||
} else {
|
||||
// Unsigned integer
|
||||
switch (float_format) {
|
||||
case VK_FORMAT_R32_SFLOAT:
|
||||
return VK_FORMAT_R32_UINT;
|
||||
case VK_FORMAT_R32G32_SFLOAT:
|
||||
return VK_FORMAT_R32G32_UINT;
|
||||
case VK_FORMAT_R32G32B32A32_SFLOAT:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
default:
|
||||
// For non-float formats, use as-is
|
||||
return float_format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format,
|
||||
bool is_integer, bool is_signed) {
|
||||
if (!device) {
|
||||
// Null buffer supported, return a null descriptor
|
||||
return VK_NULL_HANDLE;
|
||||
|
|
@ -119,6 +160,8 @@ VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat
|
|||
.offset = offset,
|
||||
.size = size,
|
||||
.format = format,
|
||||
.is_integer = is_integer,
|
||||
.is_signed = is_signed,
|
||||
.handle = device->GetLogical().CreateBufferView({
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ public:
|
|||
explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params);
|
||||
explicit Buffer(BufferCacheRuntime& runtime, VAddr cpu_addr_, u64 size_bytes_);
|
||||
|
||||
[[nodiscard]] VkBufferView View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
|
||||
[[nodiscard]] VkBufferView View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format,
|
||||
bool is_integer = false, bool is_signed = false);
|
||||
|
||||
[[nodiscard]] VkBuffer Handle() const noexcept {
|
||||
return *buffer;
|
||||
|
|
@ -60,6 +61,8 @@ private:
|
|||
u32 offset;
|
||||
u32 size;
|
||||
VideoCore::Surface::PixelFormat format;
|
||||
bool is_integer;
|
||||
bool is_signed;
|
||||
vk::BufferView handle;
|
||||
};
|
||||
|
||||
|
|
@ -149,8 +152,8 @@ public:
|
|||
}
|
||||
|
||||
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format) {
|
||||
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format));
|
||||
VideoCore::Surface::PixelFormat format, bool is_integer, bool is_signed) {
|
||||
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format, is_integer, is_signed));
|
||||
}
|
||||
|
||||
bool ShouldLimitDynamicStorageBuffers() const {
|
||||
|
|
|
|||
|
|
@ -194,7 +194,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
|||
ImageView& image_view = texture_cache.GetImageView(views[index].id);
|
||||
buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
is_written, is_image, desc.is_integer,
|
||||
desc.is_signed);
|
||||
++index;
|
||||
}
|
||||
}};
|
||||
|
|
|
|||
|
|
@ -422,7 +422,8 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
|||
ImageView& image_view{texture_cache.GetImageView(texture_buffer_it->id)};
|
||||
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
is_written, is_image, desc.is_integer,
|
||||
desc.is_signed);
|
||||
++index;
|
||||
++texture_buffer_it;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,29 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
|||
return allocator.CreateImage(image_ci);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkFormat ConvertUintToUnormFormat(VkFormat format) {
|
||||
// Convert UINT formats to UNORM equivalents for sampling compatibility
|
||||
// Shaders expect FLOAT component types for samplers, not UINT
|
||||
switch (format) {
|
||||
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
|
||||
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
case VK_FORMAT_A2B10G10R10_UINT_PACK32:
|
||||
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
case VK_FORMAT_R8_UINT:
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
case VK_FORMAT_R16_UINT:
|
||||
return VK_FORMAT_R16_UNORM;
|
||||
case VK_FORMAT_R8G8_UINT:
|
||||
return VK_FORMAT_R8G8_UNORM;
|
||||
case VK_FORMAT_R16G16_UINT:
|
||||
return VK_FORMAT_R16G16_UNORM;
|
||||
case VK_FORMAT_R16G16B16A16_UINT:
|
||||
return VK_FORMAT_R16G16B16A16_UNORM;
|
||||
default:
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
||||
VkFormat format) {
|
||||
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
||||
|
|
@ -692,10 +715,16 @@ void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4
|
|||
return VK_FORMAT_R16_SINT;
|
||||
case Shader::ImageFormat::R32_UINT:
|
||||
return VK_FORMAT_R32_UINT;
|
||||
case Shader::ImageFormat::R32_SINT:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case Shader::ImageFormat::R32G32_UINT:
|
||||
return VK_FORMAT_R32G32_UINT;
|
||||
case Shader::ImageFormat::R32G32_SINT:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case Shader::ImageFormat::R32G32B32A32_UINT:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
case Shader::ImageFormat::R32G32B32A32_SINT:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
}
|
||||
ASSERT_MSG(false, "Invalid image format={}", format);
|
||||
return VK_FORMAT_R32_UINT;
|
||||
|
|
@ -2193,7 +2222,13 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
|
||||
}
|
||||
}
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||
auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||
|
||||
// Convert UINT formats to UNORM for sampling compatibility when not a render target
|
||||
if (!info.IsRenderTarget()) {
|
||||
format_info.format = ConvertUintToUnormFormat(format_info.format);
|
||||
}
|
||||
|
||||
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Image view format {} has different usage flags than image format {}", format,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue