mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
Implement RDNA2 64-bit image atomic maximum
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
@@ -29,6 +29,7 @@ struct GraphicContext {
|
||||
bool memory_budget_ext_enabled = false;
|
||||
bool compute_subgroup_size_control_enabled = false;
|
||||
bool sample_rate_shading_enabled = false;
|
||||
bool shader_image_int64_atomics_enabled = false;
|
||||
// bool fp64_denorm_preserve = false; // Temporarily disabled.
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool provoking_vertex_last_enabled = false;
|
||||
|
||||
@@ -358,10 +358,8 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
}
|
||||
|
||||
vk::ImageViewUsageCreateInfo usage {};
|
||||
usage.usage = image.usage;
|
||||
if (!is_storage) {
|
||||
usage.usage &= ~vk::ImageUsageFlagBits::eStorage;
|
||||
}
|
||||
usage.usage = is_storage ? vk::ImageUsageFlagBits::eStorage
|
||||
: image.usage & ~vk::ImageUsageFlagBits::eStorage;
|
||||
vk::ImageViewMinLodCreateInfoEXT min_lod {};
|
||||
if (normalized.min_lod != 0) {
|
||||
min_lod.minLod = static_cast<float>(normalized.base_level) +
|
||||
|
||||
@@ -312,13 +312,14 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
resource.written && !resource.read && !resource.atomic;
|
||||
const auto numeric_class = Prospero::SampledTextureNumericClass(format);
|
||||
const bool raw_float_atomic = format == Prospero::BufferFormat::k32Float && uint_resource &&
|
||||
resource.atomic;
|
||||
resource.atomic && !resource.atomic64;
|
||||
const bool format_ok =
|
||||
raw_sint_storage || raw_float_atomic ||
|
||||
(numeric_class != Prospero::TextureNumericClass::Unsupported &&
|
||||
numeric_class != Prospero::TextureNumericClass::Sint &&
|
||||
uint_resource == (numeric_class == Prospero::TextureNumericClass::Uint) &&
|
||||
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
|
||||
(!resource.atomic || format == (resource.atomic64 ? Prospero::BufferFormat::k32_32UInt
|
||||
: Prospero::BufferFormat::k32UInt)));
|
||||
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
|
||||
return;
|
||||
}
|
||||
@@ -354,7 +355,8 @@ static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::Image
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32Float;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Uint:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32UInt;
|
||||
desc.info.guest_format = resource.atomic64 ? Prospero::BufferFormat::k32_32UInt
|
||||
: Prospero::BufferFormat::k32UInt;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Sint:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32SInt;
|
||||
@@ -365,10 +367,10 @@ static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::Image
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {1, 1, 1};
|
||||
desc.info.resources = {1, 1};
|
||||
desc.info.bytes_per_block = 4;
|
||||
desc.info.bytes_per_block = Prospero::NumBytesPerElement(desc.info.guest_format);
|
||||
desc.info.samples = 1;
|
||||
desc.info.mip_layout[0] = {0, 0, 1, 1};
|
||||
desc.view_info.format = desc.info.pixel_format;
|
||||
desc.view_info.format = resource.atomic64 ? vk::Format::eR64Uint : desc.info.pixel_format;
|
||||
desc.view_info.type = vk::ImageViewType::e2D;
|
||||
desc.view_info.aspect = vk::ImageAspectFlagBits::eColor;
|
||||
desc.view_info.usage = binding == TextureCache::BindingType::Storage
|
||||
@@ -538,6 +540,9 @@ static bool ResolveTextureMipView(const TileSurfaceDescription& description, boo
|
||||
|
||||
TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||
if (resource.atomic64 && !m_context.GetGraphics().shader_image_int64_atomics_enabled) {
|
||||
EXIT("64-bit image atomics require shaderImageInt64Atomics\n");
|
||||
}
|
||||
auto descriptor = DecodeNativeDescriptor<ShaderTextureResource>(value);
|
||||
const bool storage = resource.written;
|
||||
if (storage) {
|
||||
@@ -650,8 +655,9 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
pixel_format = depth_format->depth_attachment_format;
|
||||
}
|
||||
}
|
||||
const auto storage_view_format = storage && (resource.atomic ||
|
||||
format == Prospero::BufferFormat::k32SInt)
|
||||
const auto storage_view_format = resource.atomic64 ? vk::Format::eR64Uint
|
||||
: storage && (resource.atomic ||
|
||||
format == Prospero::BufferFormat::k32SInt)
|
||||
? vk::Format::eR32Uint
|
||||
: SrgbStorageViewFormat(pixel_format);
|
||||
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
|
||||
|
||||
@@ -460,7 +460,15 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||
provoking_vertex.pNext = supported_features2.pNext;
|
||||
supported_features2.pNext = &provoking_vertex;
|
||||
}
|
||||
const bool image_atomic_int64_extension =
|
||||
HasExtension(device_extensions, VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME);
|
||||
vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT image_atomic_int64 {};
|
||||
if (image_atomic_int64_extension) {
|
||||
image_atomic_int64.pNext = supported_features2.pNext;
|
||||
supported_features2.pNext = &image_atomic_int64;
|
||||
}
|
||||
physical_device.getFeatures2(&supported_features2);
|
||||
graphics.shader_image_int64_atomics_enabled = image_atomic_int64.shaderImageInt64Atomics;
|
||||
|
||||
auto features12 = WindowContext::RequiredVulkan12Features();
|
||||
features12.shaderSharedInt64Atomics = supported_features12.shaderSharedInt64Atomics;
|
||||
@@ -605,6 +613,11 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||
provoking_vertex.transformFeedbackPreservesProvokingVertex = VK_FALSE;
|
||||
create_info.pNext = &provoking_vertex;
|
||||
}
|
||||
if (graphics.shader_image_int64_atomics_enabled) {
|
||||
image_atomic_int64.pNext = const_cast<void*>(create_info.pNext);
|
||||
image_atomic_int64.sparseImageInt64Atomics = VK_FALSE;
|
||||
create_info.pNext = &image_atomic_int64;
|
||||
}
|
||||
create_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_info.queueCreateInfoCount = 1;
|
||||
create_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
|
||||
@@ -969,6 +982,7 @@ void WindowContext::CreateVulkan() {
|
||||
graphic_ctx.memory_budget_ext_enabled = true;
|
||||
}
|
||||
for (const auto* extension: {VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
|
||||
VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME,
|
||||
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME,
|
||||
VK_EXT_MESH_SHADER_EXTENSION_NAME,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME,
|
||||
|
||||
@@ -124,13 +124,21 @@ uint32_t ImageType(EmitterState& state, const IR::ImageResource& image) {
|
||||
sampled = 2;
|
||||
if (image.atomic) {
|
||||
EXIT_IF(image.numeric_class != Prospero::TextureNumericClass::Uint);
|
||||
format = spv::ImageFormatR32ui;
|
||||
format = image.atomic64 ? spv::ImageFormatR64ui : spv::ImageFormatR32ui;
|
||||
if (image.atomic64) {
|
||||
state.builder.RequireExtension("SPV_EXT_shader_image_int64");
|
||||
state.builder.RequireCapability(spv::CapabilityInt64);
|
||||
state.builder.RequireCapability(spv::CapabilityInt64Atomics);
|
||||
state.builder.RequireCapability(spv::CapabilityInt64ImageEXT);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
EXIT("invalid image resource class");
|
||||
}
|
||||
const auto& info = ImageDimensionInfoFor(image.dimension);
|
||||
return state.builder.Type(spv::OpTypeImage, ImageScalarType(state, image.numeric_class),
|
||||
const auto scalar_type = image.atomic64 ? TypeScalarU64(state)
|
||||
: ImageScalarType(state, image.numeric_class);
|
||||
return state.builder.Type(spv::OpTypeImage, scalar_type,
|
||||
info.spirv_dimension, image.depth_compare ? 1u : 0u, info.arrayed,
|
||||
info.multisampled, sampled, format);
|
||||
}
|
||||
|
||||
@@ -865,10 +865,14 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
}
|
||||
if (image_info.access == IR::ImageAccess::Atomic) {
|
||||
const auto dimension = image.dimension;
|
||||
ctx.Define(inst, EmitValueOrZeroIfCondition(state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
||||
const auto result_type = TypeId(state, inst.GetType());
|
||||
const auto zero = image.atomic64 ? ConstantU64(state, 0) : ConstantU32(state, 0);
|
||||
ctx.Define(inst, EmitValueOrDefaultIfCondition(
|
||||
state, ctx.Arg(inst, inst.NumArgs() - 1), result_type, zero, [&]() {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto pointer_type = state.builder.Type(
|
||||
spv::OpTypePointer, spv::StorageClassImage, TypeU32(state));
|
||||
spv::OpTypePointer, spv::StorageClassImage,
|
||||
image.atomic64 ? TypeScalarU64(state) : TypeU32(state));
|
||||
state.builder.AddFunction(spv::OpImageTexelPointer, pointer_type, pointer,
|
||||
StorageImageDescriptorPointer(state, mem.resource),
|
||||
CoordU32(ctx, mem, *address, dimension),
|
||||
|
||||
@@ -317,6 +317,7 @@ inline constexpr auto EmitImageAtomicSMin32 = EmitImage;
|
||||
inline constexpr auto EmitImageAtomicUMin32 = EmitImage;
|
||||
inline constexpr auto EmitImageAtomicSMax32 = EmitImage;
|
||||
inline constexpr auto EmitImageAtomicUMax32 = EmitImage;
|
||||
inline constexpr auto EmitImageAtomicUMax64 = EmitImage;
|
||||
inline constexpr auto EmitImageAtomicAnd32 = EmitImage;
|
||||
inline constexpr auto EmitImageAtomicOr32 = EmitImage;
|
||||
inline constexpr auto EmitImageAtomicXor32 = EmitImage;
|
||||
|
||||
@@ -1044,17 +1044,11 @@ uint32_t EmitBufferAtomic64(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return EmitValueOrDefaultIfCondition(
|
||||
state, EmitMemoryElementInBounds(state, resource, index), TypeU64(state),
|
||||
ConstantU64(state, 0), [&]() {
|
||||
const auto value = Unary(state, spv::OpBitcast, TypeScalarU64(state),
|
||||
ctx.Arg(inst, inst.NumArgs() - 2));
|
||||
const auto old = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
SpirvAtomicOpcode(inst.GetOpcode()), TypeScalarU64(state), old,
|
||||
EmitStorageBufferElementPointer(state, resource, index,
|
||||
TypeStorageBufferU64ElementPointer(state)),
|
||||
ConstantU32(state, spv::ScopeDevice),
|
||||
ConstantU32(state, spv::MemorySemanticsMaskNone), value);
|
||||
const auto pointer = EmitStorageBufferElementPointer(
|
||||
state, resource, index, TypeStorageBufferU64ElementPointer(state));
|
||||
const auto old = EmitAtomicOperation(ctx, inst, pointer, spv::ScopeDevice);
|
||||
EmitAtomicMemoryBarrier(state, mem.kind);
|
||||
return Unary(state, spv::OpBitcast, TypeU64(state), old);
|
||||
return old;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -330,6 +330,7 @@ spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
||||
case IR::ValueOpcode::BufferAtomicSMax32:
|
||||
case IR::ValueOpcode::SharedAtomicSMax32: return spv::OpAtomicSMax;
|
||||
case IR::ValueOpcode::ImageAtomicUMax32:
|
||||
case IR::ValueOpcode::ImageAtomicUMax64:
|
||||
case IR::ValueOpcode::BufferAtomicUMax32:
|
||||
case IR::ValueOpcode::SharedAtomicUMax32: return spv::OpAtomicUMax;
|
||||
case IR::ValueOpcode::ImageAtomicAnd32:
|
||||
@@ -352,6 +353,8 @@ uint32_t EmitAtomicOperation(ValueEmitContext& ctx, const IR::Inst& inst, uint32
|
||||
uint32_t scope) {
|
||||
const auto opcode = SpirvAtomicOpcode(inst.GetOpcode());
|
||||
const auto old = ctx.state.builder.AllocateId();
|
||||
const bool wide = inst.GetType() == IR::Type::U64;
|
||||
const auto type = wide ? TypeScalarU64(ctx.state) : TypeU32(ctx.state);
|
||||
if (opcode == spv::OpAtomicCompareExchange) {
|
||||
const auto desired = ctx.Arg(inst, inst.NumArgs() - 3);
|
||||
const auto comparator = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
@@ -360,12 +363,13 @@ uint32_t EmitAtomicOperation(ValueEmitContext& ctx, const IR::Inst& inst, uint32
|
||||
ConstantU32(ctx.state, scope), ConstantU32(ctx.state, spv::MemorySemanticsMaskNone),
|
||||
ConstantU32(ctx.state, spv::MemorySemanticsMaskNone), desired, comparator);
|
||||
} else {
|
||||
const auto value = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
ctx.state.builder.AddFunction(opcode, TypeU32(ctx.state), old, pointer,
|
||||
auto value = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
if (wide) value = Unary(ctx.state, spv::OpBitcast, type, value);
|
||||
ctx.state.builder.AddFunction(opcode, type, old, pointer,
|
||||
ConstantU32(ctx.state, scope),
|
||||
ConstantU32(ctx.state, spv::MemorySemanticsMaskNone), value);
|
||||
}
|
||||
return old;
|
||||
return wide ? Unary(ctx.state, spv::OpBitcast, TypeU64(ctx.state), old) : old;
|
||||
}
|
||||
|
||||
void EmitAtomicMemoryBarrier(EmitterState& state, IR::ResourceKind kind) {
|
||||
|
||||
@@ -360,9 +360,19 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
SetUnsupported(inst, Family::MIMG, opcode,
|
||||
"MIMG image gather requires exactly one dmask bit");
|
||||
}
|
||||
if (inst.opcode == Opcode::IMAGE_ATOMIC_CMPSWAP && inst.dmask != 0x3u) {
|
||||
SetUnsupported(inst, Family::MIMG, opcode,
|
||||
"MIMG image compare-and-swap requires 32-bit DMASK 0x3");
|
||||
if (atomic != nullptr) {
|
||||
const bool compare_swap = inst.opcode == Opcode::IMAGE_ATOMIC_CMPSWAP;
|
||||
const auto mask32 = compare_swap ? 0x3u : 0x1u;
|
||||
const auto mask64 = compare_swap ? 0xfu : 0x3u;
|
||||
if (inst.dmask != mask32 && inst.dmask != mask64) {
|
||||
SetUnsupported(inst, Family::MIMG, opcode, "MIMG image atomic has invalid DMASK");
|
||||
} else if (inst.dmask == mask64) {
|
||||
inst.data_bits = 64u;
|
||||
if (inst.opcode != Opcode::IMAGE_ATOMIC_UMAX) {
|
||||
SetUnsupported(inst, Family::MIMG, opcode,
|
||||
"MIMG 64-bit image atomic opcode is not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool supports_d16 = sample != nullptr || gather != nullptr || opcode == 0x00u ||
|
||||
opcode == 0x01u || opcode == 0x08u || opcode == 0x09u;
|
||||
|
||||
@@ -568,7 +568,9 @@ void Translator::IMAGE_ATOMIC(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
ir.GetExec()},
|
||||
flags);
|
||||
} else {
|
||||
result = ir.Emit(opcode, {resource, address, ReadU32(data), ir.GetExec()}, flags);
|
||||
const IR::Value value =
|
||||
memory.data_bits == 64u ? IR::Value(ReadU64(data)) : IR::Value(ReadU32(data));
|
||||
result = ir.Emit(opcode, {resource, address, value, ir.GetExec()}, flags);
|
||||
}
|
||||
if (inst.glc) {
|
||||
WriteOperand(inst.dst, result);
|
||||
@@ -1052,7 +1054,8 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
case Decoder::Opcode::IMAGE_ATOMIC_SMAX:
|
||||
return IMAGE_ATOMIC(inst, IR::ValueOpcode::ImageAtomicSMax32);
|
||||
case Decoder::Opcode::IMAGE_ATOMIC_UMAX:
|
||||
return IMAGE_ATOMIC(inst, IR::ValueOpcode::ImageAtomicUMax32);
|
||||
return IMAGE_ATOMIC(inst, inst.data_bits == 64u ? IR::ValueOpcode::ImageAtomicUMax64
|
||||
: IR::ValueOpcode::ImageAtomicUMax32);
|
||||
case Decoder::Opcode::IMAGE_ATOMIC_AND:
|
||||
return IMAGE_ATOMIC(inst, IR::ValueOpcode::ImageAtomicAnd32);
|
||||
case Decoder::Opcode::IMAGE_ATOMIC_OR:
|
||||
|
||||
@@ -133,6 +133,7 @@ struct ImageResource {
|
||||
bool read = false;
|
||||
bool written = false;
|
||||
bool atomic = false;
|
||||
bool atomic64 = false;
|
||||
bool depth_compare = false;
|
||||
bool cube = false;
|
||||
bool r128 = false;
|
||||
@@ -278,7 +279,7 @@ struct StageOutput {
|
||||
inline constexpr uint32_t FirstImageBinding = 1u;
|
||||
inline constexpr uint32_t FirstComparisonImageBinding = 22u;
|
||||
inline constexpr uint32_t FirstStorageImageBinding = 29u;
|
||||
inline constexpr uint32_t ImageBindingCount = 43u;
|
||||
inline constexpr uint32_t ImageBindingCount = 48u;
|
||||
|
||||
enum class DescriptorBindingKind : uint32_t {
|
||||
Buffers = 0u,
|
||||
@@ -291,8 +292,8 @@ enum class DescriptorBindingKind : uint32_t {
|
||||
Count,
|
||||
};
|
||||
|
||||
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Samplers) == 44u);
|
||||
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Count) == 50u);
|
||||
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Samplers) == 49u);
|
||||
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Count) == 55u);
|
||||
|
||||
struct PushData {
|
||||
static constexpr uint32_t DwordCount = 32;
|
||||
@@ -369,7 +370,7 @@ DescriptorBindingForImage(const ImageResource& image) {
|
||||
if (image.numeric_class != Prospero::TextureNumericClass::Uint) {
|
||||
return std::nullopt;
|
||||
}
|
||||
base = AtomicUintBinding;
|
||||
base = AtomicUintBinding + (image.atomic64 ? 5u : 0u);
|
||||
} else {
|
||||
switch (image.numeric_class) {
|
||||
case Prospero::TextureNumericClass::Float: base = StorageFloatBinding; break;
|
||||
|
||||
@@ -227,6 +227,7 @@ ImageOpcodeInfo ImageOpcodeInfoOf(ValueOpcode opcode) {
|
||||
case ValueOpcode::ImageAtomicUMin32:
|
||||
case ValueOpcode::ImageAtomicSMax32:
|
||||
case ValueOpcode::ImageAtomicUMax32:
|
||||
case ValueOpcode::ImageAtomicUMax64:
|
||||
case ValueOpcode::ImageAtomicAnd32:
|
||||
case ValueOpcode::ImageAtomicOr32:
|
||||
case ValueOpcode::ImageAtomicXor32:
|
||||
|
||||
@@ -305,6 +305,7 @@ VALUE_OPCODE(ImageAtomicSMin32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(ImageAtomicUMin32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(ImageAtomicSMax32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(ImageAtomicUMax32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(ImageAtomicUMax64, U64, ImageResource, ImageAddress, U64, U1)
|
||||
VALUE_OPCODE(ImageAtomicAnd32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(ImageAtomicOr32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(ImageAtomicXor32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
|
||||
@@ -460,8 +460,10 @@ static bool BuildResourceSpecialization(const ResourcePlan& program, ResourceSna
|
||||
image.cube = DescriptorIsCube(descriptor);
|
||||
const auto format =
|
||||
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||
if (base.atomic && format != Prospero::BufferFormat::k32UInt &&
|
||||
format != Prospero::BufferFormat::k32Float) {
|
||||
if (base.atomic &&
|
||||
(base.atomic64 ? format != Prospero::BufferFormat::k32_32UInt
|
||||
: format != Prospero::BufferFormat::k32UInt &&
|
||||
format != Prospero::BufferFormat::k32Float)) {
|
||||
return SpecializationFail(
|
||||
fmt::format("atomic image descriptor {} uses unsupported format {}", i,
|
||||
static_cast<uint32_t>(format)));
|
||||
|
||||
@@ -1725,6 +1725,8 @@ private:
|
||||
|
||||
uint32_t AddImage(uint32_t source, const MemoryInfo& memory, ValueOpcode op, uint32_t pc) {
|
||||
const auto resource_class = ImageOpcodeInfoOf(op).resource_class;
|
||||
const bool atomic64 = ImageOpcodeInfoOf(op).access == ImageAccess::Atomic &&
|
||||
memory.data_bits == 64u;
|
||||
const auto mip = resource_class == ImageResourceClass::Storage && memory.image_has_mip
|
||||
? ImageMipMode::DynamicStorage
|
||||
: ImageMipMode::None;
|
||||
@@ -1733,7 +1735,8 @@ private:
|
||||
auto& image = m_info.images[i];
|
||||
if (image.source == source && image.resource_class == resource_class &&
|
||||
image.dimension == memory.image_dimension && image.mip_mode == mip &&
|
||||
image.depth_compare == depth && image.r128 == memory.image_r128) {
|
||||
image.depth_compare == depth && image.r128 == memory.image_r128 &&
|
||||
image.atomic64 == atomic64) {
|
||||
Merge(image, op, pc);
|
||||
return i;
|
||||
}
|
||||
@@ -1749,6 +1752,7 @@ private:
|
||||
image.mip_mode = mip;
|
||||
image.depth_compare = depth;
|
||||
image.r128 = memory.image_r128;
|
||||
image.atomic64 = atomic64;
|
||||
Merge(image, op, pc);
|
||||
m_info.images.push_back(image);
|
||||
return static_cast<uint32_t>(m_info.images.size() - 1);
|
||||
|
||||
Reference in New Issue
Block a user