浏览代码

Merge pull request #2869 from ReinUsesLisp/suld

shader/image: Implement SULD and fix SUATOM
bunnei 6 年之前
父节点
当前提交
376f1a4432

+ 5 - 3
src/video_core/engines/shader_bytecode.h

@@ -544,7 +544,7 @@ enum class VoteOperation : u64 {
     Eq = 2,  // allThreadsEqualNV
 };
 
-enum class ImageAtomicSize : u64 {
+enum class ImageAtomicOperationType : u64 {
     U32 = 0,
     S32 = 1,
     U64 = 2,
@@ -1432,11 +1432,11 @@ union Instruction {
             ASSERT(mode == SurfaceDataMode::D_BA);
             return store_data_layout;
         }
-    } sust;
+    } suldst;
 
     union {
         BitField<28, 1, u64> is_ba;
-        BitField<51, 3, ImageAtomicSize> size;
+        BitField<51, 3, ImageAtomicOperationType> operation_type;
         BitField<33, 3, ImageType> image_type;
         BitField<29, 4, ImageAtomicOperation> operation;
         BitField<49, 2, OutOfBoundsStore> out_of_bounds_store;
@@ -1595,6 +1595,7 @@ public:
         TMML_B, // Texture Mip Map Level
         TMML,   // Texture Mip Map Level
         SUST,   // Surface Store
+        SULD,   // Surface Load
         SUATOM, // Surface Atomic Operation
         EXIT,
         NOP,
@@ -1884,6 +1885,7 @@ private:
             INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
             INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
             INST("11101011001-----", Id::SUST, Type::Image, "SUST"),
+            INST("11101011000-----", Id::SULD, Type::Image, "SULD"),
             INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"),
             INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"),
             INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),

+ 22 - 0
src/video_core/renderer_opengl/gl_device.cpp

@@ -2,8 +2,10 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <algorithm>
 #include <array>
 #include <cstddef>
+#include <vector>
 #include <glad/glad.h>
 
 #include "common/logging/log.h"
@@ -30,9 +32,27 @@ bool TestProgram(const GLchar* glsl) {
     return link_status == GL_TRUE;
 }
 
+std::vector<std::string_view> GetExtensions() {
+    GLint num_extensions;
+    glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
+    std::vector<std::string_view> extensions;
+    extensions.reserve(num_extensions);
+    for (GLint index = 0; index < num_extensions; ++index) {
+        extensions.push_back(
+            reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(index))));
+    }
+    return extensions;
+}
+
+bool HasExtension(const std::vector<std::string_view>& images, std::string_view extension) {
+    return std::find(images.begin(), images.end(), extension) != images.end();
+}
+
 } // Anonymous namespace
 
 Device::Device() {
+    const std::vector extensions = GetExtensions();
+
     uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
     shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
     max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
@@ -40,6 +60,7 @@ Device::Device() {
     has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group &&
                           GLAD_GL_NV_shader_thread_shuffle;
     has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
+    has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted");
     has_variable_aoffi = TestVariableAoffi();
     has_component_indexing_bug = TestComponentIndexingBug();
     has_precise_bug = TestPreciseBug();
@@ -55,6 +76,7 @@ Device::Device(std::nullptr_t) {
     max_varyings = 15;
     has_warp_intrinsics = true;
     has_vertex_viewport_layer = true;
+    has_image_load_formatted = true;
     has_variable_aoffi = true;
     has_component_indexing_bug = false;
     has_precise_bug = false;

+ 5 - 0
src/video_core/renderer_opengl/gl_device.h

@@ -38,6 +38,10 @@ public:
         return has_vertex_viewport_layer;
     }
 
+    bool HasImageLoadFormatted() const {
+        return has_image_load_formatted;
+    }
+
     bool HasVariableAoffi() const {
         return has_variable_aoffi;
     }
@@ -61,6 +65,7 @@ private:
     u32 max_varyings{};
     bool has_warp_intrinsics{};
     bool has_vertex_viewport_layer{};
+    bool has_image_load_formatted{};
     bool has_variable_aoffi{};
     bool has_component_indexing_bug{};
     bool has_precise_bug{};

+ 8 - 8
src/video_core/renderer_opengl/gl_shader_cache.cpp

@@ -211,14 +211,14 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn
     const auto primitive_mode{variant.primitive_mode};
     const auto texture_buffer_usage{variant.texture_buffer_usage};
 
-    std::string source = "#version 430 core\n"
-                         "#extension GL_ARB_separate_shader_objects : enable\n"
-                         "#extension GL_NV_gpu_shader5 : enable\n"
-                         "#extension GL_NV_shader_thread_group : enable\n"
-                         "#extension GL_NV_shader_thread_shuffle : enable\n";
-    if (entries.shader_viewport_layer_array) {
-        source += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
-    }
+    std::string source = R"(#version 430 core
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shader_viewport_layer_array : enable
+#extension GL_EXT_shader_image_load_formatted : enable
+#extension GL_NV_gpu_shader5 : enable
+#extension GL_NV_shader_thread_group : enable
+#extension GL_NV_shader_thread_shuffle : enable
+)";
     if (program_type == ProgramType::Compute) {
         source += "#extension GL_ARB_compute_variable_group_size : require\n";
     }

+ 49 - 98
src/video_core/renderer_opengl/gl_shader_decompiler.cpp

@@ -19,6 +19,7 @@
 #include "video_core/renderer_opengl/gl_device.h"
 #include "video_core/renderer_opengl/gl_rasterizer.h"
 #include "video_core/renderer_opengl/gl_shader_decompiler.h"
+#include "video_core/shader/node.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace OpenGL::GLShader {
@@ -398,8 +399,6 @@ public:
                                                        usage.is_read, usage.is_written);
         }
         entries.clip_distances = ir.GetClipDistances();
-        entries.shader_viewport_layer_array =
-            IsVertexShader(stage) && (ir.UsesLayer() || ir.UsesViewportIndex());
         entries.shader_length = ir.GetLength();
         return entries;
     }
@@ -725,36 +724,20 @@ private:
             const char* image_type = [&] {
                 switch (image.GetType()) {
                 case Tegra::Shader::ImageType::Texture1D:
-                    return "image1D";
+                    return "1D";
                 case Tegra::Shader::ImageType::TextureBuffer:
-                    return "imageBuffer";
+                    return "Buffer";
                 case Tegra::Shader::ImageType::Texture1DArray:
-                    return "image1DArray";
+                    return "1DArray";
                 case Tegra::Shader::ImageType::Texture2D:
-                    return "image2D";
+                    return "2D";
                 case Tegra::Shader::ImageType::Texture2DArray:
-                    return "image2DArray";
+                    return "2DArray";
                 case Tegra::Shader::ImageType::Texture3D:
-                    return "image3D";
+                    return "3D";
                 default:
                     UNREACHABLE();
-                    return "image1D";
-                }
-            }();
-
-            const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
-                if (!image.IsSizeKnown()) {
-                    return {"", ""};
-                }
-                switch (image.GetSize()) {
-                case Tegra::Shader::ImageAtomicSize::U32:
-                    return {"u", "r32ui, "};
-                case Tegra::Shader::ImageAtomicSize::S32:
-                    return {"i", "r32i, "};
-                default:
-                    UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
-                                      static_cast<u32>(image.GetSize()));
-                    return {"", ""};
+                    return "1D";
                 }
             }();
 
@@ -765,8 +748,12 @@ private:
                 qualifier += " writeonly";
             }
 
-            code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform "
-                         "{} {};",
+            std::string format;
+            if (image.IsAtomic()) {
+                format = "r32ui, ";
+            }
+
+            code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format,
                          image.GetIndex(), qualifier, image_type, GetImage(image));
         }
         if (!images.empty()) {
@@ -1234,28 +1221,13 @@ private:
     }
 
     std::string BuildImageValues(Operation operation) {
+        constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"};
         const auto meta{std::get<MetaImage>(operation.GetMeta())};
-        const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
-            constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
-            if (!meta.image.IsSizeKnown()) {
-                return {float_constructors, Type::Float};
-            }
-            switch (meta.image.GetSize()) {
-            case Tegra::Shader::ImageAtomicSize::U32:
-                return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
-            case Tegra::Shader::ImageAtomicSize::S32:
-                return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
-            default:
-                UNIMPLEMENTED_MSG("Unimplemented image size={}",
-                                  static_cast<u32>(meta.image.GetSize()));
-                return {float_constructors, Type::Float};
-            }
-        }();
 
         const std::size_t values_count{meta.values.size()};
         std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
         for (std::size_t i = 0; i < values_count; ++i) {
-            expr += Visit(meta.values.at(i)).As(type);
+            expr += Visit(meta.values.at(i)).AsUint();
             if (i + 1 < values_count) {
                 expr += ", ";
             }
@@ -1264,29 +1236,6 @@ private:
         return expr;
     }
 
-    Expression AtomicImage(Operation operation, const char* opname) {
-        constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
-        const auto meta{std::get<MetaImage>(operation.GetMeta())};
-        ASSERT(meta.values.size() == 1);
-        ASSERT(meta.image.IsSizeKnown());
-
-        const auto type = [&]() {
-            switch (const auto size = meta.image.GetSize()) {
-            case Tegra::Shader::ImageAtomicSize::U32:
-                return Type::Uint;
-            case Tegra::Shader::ImageAtomicSize::S32:
-                return Type::Int;
-            default:
-                UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
-                return Type::Uint;
-            }
-        }();
-
-        return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
-                            BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
-                type};
-    }
-
     Expression Assign(Operation operation) {
         const Node& dest = operation[0];
         const Node& src = operation[1];
@@ -1809,6 +1758,19 @@ private:
         return {tmp, Type::Float};
     }
 
+    Expression ImageLoad(Operation operation) {
+        if (!device.HasImageLoadFormatted()) {
+            LOG_ERROR(Render_OpenGL,
+                      "Device lacks GL_EXT_shader_image_load_formatted, stubbing image load");
+            return {"0", Type::Int};
+        }
+
+        const auto meta{std::get<MetaImage>(operation.GetMeta())};
+        return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image),
+                            BuildIntegerCoordinates(operation), GetSwizzle(meta.element)),
+                Type::Uint};
+    }
+
     Expression ImageStore(Operation operation) {
         const auto meta{std::get<MetaImage>(operation.GetMeta())};
         code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
@@ -1816,31 +1778,14 @@ private:
         return {};
     }
 
-    Expression AtomicImageAdd(Operation operation) {
-        return AtomicImage(operation, "imageAtomicAdd");
-    }
-
-    Expression AtomicImageMin(Operation operation) {
-        return AtomicImage(operation, "imageAtomicMin");
-    }
-
-    Expression AtomicImageMax(Operation operation) {
-        return AtomicImage(operation, "imageAtomicMax");
-    }
-    Expression AtomicImageAnd(Operation operation) {
-        return AtomicImage(operation, "imageAtomicAnd");
-    }
-
-    Expression AtomicImageOr(Operation operation) {
-        return AtomicImage(operation, "imageAtomicOr");
-    }
-
-    Expression AtomicImageXor(Operation operation) {
-        return AtomicImage(operation, "imageAtomicXor");
-    }
+    template <const std::string_view& opname>
+    Expression AtomicImage(Operation operation) {
+        const auto meta{std::get<MetaImage>(operation.GetMeta())};
+        ASSERT(meta.values.size() == 1);
 
-    Expression AtomicImageExchange(Operation operation) {
-        return AtomicImage(operation, "imageAtomicExchange");
+        return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image),
+                            BuildIntegerCoordinates(operation), Visit(meta.values[0]).AsUint()),
+                Type::Uint};
     }
 
     Expression Branch(Operation operation) {
@@ -2035,6 +1980,12 @@ private:
         Func() = delete;
         ~Func() = delete;
 
+        static constexpr std::string_view Add = "Add";
+        static constexpr std::string_view And = "And";
+        static constexpr std::string_view Or = "Or";
+        static constexpr std::string_view Xor = "Xor";
+        static constexpr std::string_view Exchange = "Exchange";
+
         static constexpr std::string_view ShuffleIndexed = "shuffleNV";
         static constexpr std::string_view ShuffleUp = "shuffleUpNV";
         static constexpr std::string_view ShuffleDown = "shuffleDownNV";
@@ -2172,14 +2123,14 @@ private:
         &GLSLDecompiler::TextureQueryLod,
         &GLSLDecompiler::TexelFetch,
 
+        &GLSLDecompiler::ImageLoad,
         &GLSLDecompiler::ImageStore,
-        &GLSLDecompiler::AtomicImageAdd,
-        &GLSLDecompiler::AtomicImageMin,
-        &GLSLDecompiler::AtomicImageMax,
-        &GLSLDecompiler::AtomicImageAnd,
-        &GLSLDecompiler::AtomicImageOr,
-        &GLSLDecompiler::AtomicImageXor,
-        &GLSLDecompiler::AtomicImageExchange,
+
+        &GLSLDecompiler::AtomicImage<Func::Add>,
+        &GLSLDecompiler::AtomicImage<Func::And>,
+        &GLSLDecompiler::AtomicImage<Func::Or>,
+        &GLSLDecompiler::AtomicImage<Func::Xor>,
+        &GLSLDecompiler::AtomicImage<Func::Exchange>,
 
         &GLSLDecompiler::Branch,
         &GLSLDecompiler::BranchIndirect,

+ 0 - 1
src/video_core/renderer_opengl/gl_shader_decompiler.h

@@ -90,7 +90,6 @@ struct ShaderEntries {
     std::vector<ImageEntry> images;
     std::vector<GlobalMemoryEntry> global_memory_entries;
     std::array<bool, Maxwell::NumClipDistances> clip_distances{};
-    bool shader_viewport_layer_array{};
     std::size_t shader_length{};
 };
 

+ 4 - 18
src/video_core/renderer_opengl/gl_shader_disk_cache.cpp

@@ -343,20 +343,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
         u8 is_bindless{};
         u8 is_written{};
         u8 is_read{};
-        u8 is_size_known{};
-        u32 size{};
+        u8 is_atomic{};
         if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
             !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
             !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) ||
-            !LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) {
+            !LoadObjectFromPrecompiled(is_atomic)) {
             return {};
         }
         entry.entries.images.emplace_back(
             static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
             static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0,
-            is_read != 0,
-            is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size))
-                          : std::nullopt);
+            is_read != 0, is_atomic != 0);
     }
 
     u32 global_memory_count{};
@@ -382,12 +379,6 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
         }
     }
 
-    bool shader_viewport_layer_array{};
-    if (!LoadObjectFromPrecompiled(shader_viewport_layer_array)) {
-        return {};
-    }
-    entry.entries.shader_viewport_layer_array = shader_viewport_layer_array;
-
     u64 shader_length{};
     if (!LoadObjectFromPrecompiled(shader_length)) {
         return {};
@@ -435,14 +426,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
         return false;
     }
     for (const auto& image : entries.images) {
-        const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U;
         if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) ||
             !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) ||
             !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) ||
             !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) ||
             !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) ||
             !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
-            !SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) {
+            !SaveObjectToPrecompiled(static_cast<u8>(image.IsAtomic() ? 1 : 0))) {
             return false;
         }
     }
@@ -464,10 +454,6 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
         }
     }
 
-    if (!SaveObjectToPrecompiled(entries.shader_viewport_layer_array)) {
-        return false;
-    }
-
     if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) {
         return false;
     }

+ 5 - 10
src/video_core/renderer_vulkan/vk_shader_decompiler.cpp

@@ -19,6 +19,7 @@
 #include "video_core/engines/shader_header.h"
 #include "video_core/renderer_vulkan/vk_device.h"
 #include "video_core/renderer_vulkan/vk_shader_decompiler.h"
+#include "video_core/shader/node.h"
 #include "video_core/shader/shader_ir.h"
 
 namespace Vulkan::VKShader {
@@ -939,22 +940,17 @@ private:
         return {};
     }
 
-    Id ImageStore(Operation operation) {
-        UNIMPLEMENTED();
-        return {};
-    }
-
-    Id AtomicImageAdd(Operation operation) {
+    Id ImageLoad(Operation operation) {
         UNIMPLEMENTED();
         return {};
     }
 
-    Id AtomicImageMin(Operation operation) {
+    Id ImageStore(Operation operation) {
         UNIMPLEMENTED();
         return {};
     }
 
-    Id AtomicImageMax(Operation operation) {
+    Id AtomicImageAdd(Operation operation) {
         UNIMPLEMENTED();
         return {};
     }
@@ -1440,10 +1436,9 @@ private:
         &SPIRVDecompiler::TextureQueryLod,
         &SPIRVDecompiler::TexelFetch,
 
+        &SPIRVDecompiler::ImageLoad,
         &SPIRVDecompiler::ImageStore,
         &SPIRVDecompiler::AtomicImageAdd,
-        &SPIRVDecompiler::AtomicImageMin,
-        &SPIRVDecompiler::AtomicImageMax,
         &SPIRVDecompiler::AtomicImageAnd,
         &SPIRVDecompiler::AtomicImageOr,
         &SPIRVDecompiler::AtomicImageXor,

+ 77 - 60
src/video_core/shader/decode/image.cpp

@@ -41,11 +41,46 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
     const Instruction instr = {program_code[pc]};
     const auto opcode = OpCode::Decode(instr);
 
+    const auto GetCoordinates = [this, instr](Tegra::Shader::ImageType image_type) {
+        std::vector<Node> coords;
+        const std::size_t num_coords{GetImageTypeNumCoordinates(image_type)};
+        coords.reserve(num_coords);
+        for (std::size_t i = 0; i < num_coords; ++i) {
+            coords.push_back(GetRegister(instr.gpr8.Value() + i));
+        }
+        return coords;
+    };
+
     switch (opcode->get().GetId()) {
+    case OpCode::Id::SULD: {
+        UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P);
+        UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store !=
+                         Tegra::Shader::OutOfBoundsStore::Ignore);
+
+        const auto type{instr.suldst.image_type};
+        auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type)
+                                              : GetBindlessImage(instr.gpr39, type)};
+        image.MarkRead();
+
+        u32 indexer = 0;
+        for (u32 element = 0; element < 4; ++element) {
+            if (!instr.suldst.IsComponentEnabled(element)) {
+                continue;
+            }
+            MetaImage meta{image, {}, element};
+            Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type));
+            SetTemporary(bb, indexer++, std::move(value));
+        }
+        for (u32 i = 0; i < indexer; ++i) {
+            SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
+        }
+        break;
+    }
     case OpCode::Id::SUST: {
-        UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P);
-        UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore);
-        UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store
+        UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P);
+        UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store !=
+                         Tegra::Shader::OutOfBoundsStore::Ignore);
+        UNIMPLEMENTED_IF(instr.suldst.component_mask_selector != 0xf); // Ensure we have RGBA
 
         std::vector<Node> values;
         constexpr std::size_t hardcoded_size{4};
@@ -53,58 +88,51 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
             values.push_back(GetRegister(instr.gpr0.Value() + i));
         }
 
-        std::vector<Node> coords;
-        const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)};
-        for (std::size_t i = 0; i < num_coords; ++i) {
-            coords.push_back(GetRegister(instr.gpr8.Value() + i));
-        }
-
-        const auto type{instr.sust.image_type};
-        auto& image{instr.sust.is_immediate ? GetImage(instr.image, type)
-                                            : GetBindlessImage(instr.gpr39, type)};
+        const auto type{instr.suldst.image_type};
+        auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type)
+                                              : GetBindlessImage(instr.gpr39, type)};
         image.MarkWrite();
 
-        MetaImage meta{image, values};
-        bb.push_back(Operation(OperationCode::ImageStore, meta, std::move(coords)));
+        MetaImage meta{image, std::move(values)};
+        bb.push_back(Operation(OperationCode::ImageStore, meta, GetCoordinates(type)));
         break;
     }
     case OpCode::Id::SUATOM: {
         UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0);
 
-        Node value = GetRegister(instr.gpr0);
-
-        std::vector<Node> coords;
-        const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)};
-        for (std::size_t i = 0; i < num_coords; ++i) {
-            coords.push_back(GetRegister(instr.gpr8.Value() + i));
-        }
-
         const OperationCode operation_code = [instr] {
-            switch (instr.suatom_d.operation) {
-            case Tegra::Shader::ImageAtomicOperation::Add:
-                return OperationCode::AtomicImageAdd;
-            case Tegra::Shader::ImageAtomicOperation::Min:
-                return OperationCode::AtomicImageMin;
-            case Tegra::Shader::ImageAtomicOperation::Max:
-                return OperationCode::AtomicImageMax;
-            case Tegra::Shader::ImageAtomicOperation::And:
-                return OperationCode::AtomicImageAnd;
-            case Tegra::Shader::ImageAtomicOperation::Or:
-                return OperationCode::AtomicImageOr;
-            case Tegra::Shader::ImageAtomicOperation::Xor:
-                return OperationCode::AtomicImageXor;
-            case Tegra::Shader::ImageAtomicOperation::Exch:
-                return OperationCode::AtomicImageExchange;
+            switch (instr.suatom_d.operation_type) {
+            case Tegra::Shader::ImageAtomicOperationType::S32:
+            case Tegra::Shader::ImageAtomicOperationType::U32:
+                switch (instr.suatom_d.operation) {
+                case Tegra::Shader::ImageAtomicOperation::Add:
+                    return OperationCode::AtomicImageAdd;
+                case Tegra::Shader::ImageAtomicOperation::And:
+                    return OperationCode::AtomicImageAnd;
+                case Tegra::Shader::ImageAtomicOperation::Or:
+                    return OperationCode::AtomicImageOr;
+                case Tegra::Shader::ImageAtomicOperation::Xor:
+                    return OperationCode::AtomicImageXor;
+                case Tegra::Shader::ImageAtomicOperation::Exch:
+                    return OperationCode::AtomicImageExchange;
+                }
             default:
-                UNIMPLEMENTED_MSG("Unimplemented operation={}",
-                                  static_cast<u32>(instr.suatom_d.operation.Value()));
-                return OperationCode::AtomicImageAdd;
+                break;
             }
+            UNIMPLEMENTED_MSG("Unimplemented operation={} type={}",
+                              static_cast<u64>(instr.suatom_d.operation.Value()),
+                              static_cast<u64>(instr.suatom_d.operation_type.Value()));
+            return OperationCode::AtomicImageAdd;
         }();
 
-        const auto& image{GetImage(instr.image, instr.suatom_d.image_type, instr.suatom_d.size)};
+        Node value = GetRegister(instr.gpr0);
+
+        const auto type = instr.suatom_d.image_type;
+        auto& image = GetImage(instr.image, type);
+        image.MarkAtomic();
+
         MetaImage meta{image, {std::move(value)}};
-        SetRegister(bb, instr.gpr0, Operation(operation_code, meta, std::move(coords)));
+        SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type)));
         break;
     }
     default:
@@ -114,35 +142,32 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
     return pc;
 }
 
-Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
-                          std::optional<Tegra::Shader::ImageAtomicSize> size) {
+Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
     const auto offset{static_cast<std::size_t>(image.index.Value())};
-    if (const auto image = TryUseExistingImage(offset, type, size)) {
+    if (const auto image = TryUseExistingImage(offset, type)) {
         return *image;
     }
 
     const std::size_t next_index{used_images.size()};
-    return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second;
+    return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
 }
 
-Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
-                                  std::optional<Tegra::Shader::ImageAtomicSize> size) {
+Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
     const Node image_register{GetRegister(reg)};
     const auto [base_image, cbuf_index, cbuf_offset]{
         TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
     const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
 
-    if (const auto image = TryUseExistingImage(cbuf_key, type, size)) {
+    if (const auto image = TryUseExistingImage(cbuf_key, type)) {
         return *image;
     }
 
     const std::size_t next_index{used_images.size()};
-    return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size})
+    return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
         .first->second;
 }
 
-Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
-                                     std::optional<Tegra::Shader::ImageAtomicSize> size) {
+Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) {
     auto it = used_images.find(offset);
     if (it == used_images.end()) {
         return nullptr;
@@ -150,14 +175,6 @@ Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
     auto& image = it->second;
     ASSERT(image.GetType() == type);
 
-    if (size) {
-        // We know the size, if it's known it has to be the same as before, otherwise we can set it.
-        if (image.IsSizeKnown()) {
-            ASSERT(image.GetSize() == size);
-        } else {
-            image.SetSize(*size);
-        }
-    }
     return &image;
 }
 

+ 21 - 25
src/video_core/shader/node.h

@@ -149,10 +149,10 @@ enum class OperationCode {
     TextureQueryLod,        /// (MetaTexture, float[N] coords) -> float4
     TexelFetch,             /// (MetaTexture, int[N], int) -> float4
 
-    ImageStore,          /// (MetaImage, int[N] values) -> void
+    ImageLoad,  /// (MetaImage, int[N] coords) -> void
+    ImageStore, /// (MetaImage, int[N] coords) -> void
+
     AtomicImageAdd,      /// (MetaImage, int[N] coords) -> void
-    AtomicImageMin,      /// (MetaImage, int[N] coords) -> void
-    AtomicImageMax,      /// (MetaImage, int[N] coords) -> void
     AtomicImageAnd,      /// (MetaImage, int[N] coords) -> void
     AtomicImageOr,       /// (MetaImage, int[N] coords) -> void
     AtomicImageXor,      /// (MetaImage, int[N] coords) -> void
@@ -294,21 +294,18 @@ private:
 
 class Image final {
 public:
-    constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
-                             std::optional<Tegra::Shader::ImageAtomicSize> size)
-        : offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {}
+    constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type)
+        : offset{offset}, index{index}, type{type}, is_bindless{false} {}
 
     constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
-                             Tegra::Shader::ImageType type,
-                             std::optional<Tegra::Shader::ImageAtomicSize> size)
+                             Tegra::Shader::ImageType type)
         : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
-          is_bindless{true}, size{size} {}
+          is_bindless{true} {}
 
     constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
-                             bool is_bindless, bool is_written, bool is_read,
-                             std::optional<Tegra::Shader::ImageAtomicSize> size)
+                             bool is_bindless, bool is_written, bool is_read, bool is_atomic)
         : offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
-          is_written{is_written}, is_read{is_read}, size{size} {}
+          is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {}
 
     void MarkWrite() {
         is_written = true;
@@ -318,8 +315,10 @@ public:
         is_read = true;
     }
 
-    void SetSize(Tegra::Shader::ImageAtomicSize size_) {
-        size = size_;
+    void MarkAtomic() {
+        MarkWrite();
+        MarkRead();
+        is_atomic = true;
     }
 
     constexpr std::size_t GetOffset() const {
@@ -346,21 +345,17 @@ public:
         return is_read;
     }
 
-    constexpr std::pair<u32, u32> GetBindlessCBuf() const {
-        return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
+    constexpr bool IsAtomic() const {
+        return is_atomic;
     }
 
-    constexpr bool IsSizeKnown() const {
-        return size.has_value();
-    }
-
-    constexpr Tegra::Shader::ImageAtomicSize GetSize() const {
-        return size.value();
+    constexpr std::pair<u32, u32> GetBindlessCBuf() const {
+        return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
     }
 
     constexpr bool operator<(const Image& rhs) const {
-        return std::tie(offset, index, type, size, is_bindless) <
-               std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless);
+        return std::tie(offset, index, type, is_bindless) <
+               std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless);
     }
 
 private:
@@ -370,7 +365,7 @@ private:
     bool is_bindless{};
     bool is_written{};
     bool is_read{};
-    std::optional<Tegra::Shader::ImageAtomicSize> size{};
+    bool is_atomic{};
 };
 
 struct GlobalMemoryBase {
@@ -402,6 +397,7 @@ struct MetaTexture {
 struct MetaImage {
     const Image& image;
     std::vector<Node> values;
+    u32 element{};
 };
 
 /// Parameters that modify an operation but are not part of any particular operand

+ 3 - 6
src/video_core/shader/shader_ir.h

@@ -284,16 +284,13 @@ private:
                                       bool is_shadow);
 
     /// Accesses an image.
-    Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
-                    std::optional<Tegra::Shader::ImageAtomicSize> size = {});
+    Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
 
     /// Access a bindless image sampler.
-    Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
-                            std::optional<Tegra::Shader::ImageAtomicSize> size = {});
+    Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
 
     /// Tries to access an existing image, updating it's state as needed
-    Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
-                               std::optional<Tegra::Shader::ImageAtomicSize> size);
+    Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type);
 
     /// Extracts a sequence of bits from a node
     Node BitfieldExtract(Node value, u32 offset, u32 bits);