summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/engines/shader_bytecode.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp132
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp12
-rw-r--r--src/video_core/shader/decode/image.cpp66
-rw-r--r--src/video_core/shader/node.h46
-rw-r--r--src/video_core/shader/shader_ir.h9
7 files changed, 93 insertions, 188 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 81dfe33a5..b46fcf03d 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -544,7 +544,7 @@ enum class VoteOperation : u64 {
544 Eq = 2, // allThreadsEqualNV 544 Eq = 2, // allThreadsEqualNV
545}; 545};
546 546
547enum class ImageAtomicSize : u64 { 547enum class ImageAtomicOperationType : u64 {
548 U32 = 0, 548 U32 = 0,
549 S32 = 1, 549 S32 = 1,
550 U64 = 2, 550 U64 = 2,
@@ -1431,7 +1431,7 @@ union Instruction {
1431 1431
1432 union { 1432 union {
1433 BitField<28, 1, u64> is_ba; 1433 BitField<28, 1, u64> is_ba;
1434 BitField<51, 3, ImageAtomicSize> size; 1434 BitField<51, 3, ImageAtomicOperationType> operation_type;
1435 BitField<33, 3, ImageType> image_type; 1435 BitField<33, 3, ImageType> image_type;
1436 BitField<29, 4, ImageAtomicOperation> operation; 1436 BitField<29, 4, ImageAtomicOperation> operation;
1437 BitField<49, 2, OutOfBoundsStore> out_of_bounds_store; 1437 BitField<49, 2, OutOfBoundsStore> out_of_bounds_store;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 70ce6572b..0f3a35f74 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -716,36 +716,20 @@ private:
716 const char* image_type = [&] { 716 const char* image_type = [&] {
717 switch (image.GetType()) { 717 switch (image.GetType()) {
718 case Tegra::Shader::ImageType::Texture1D: 718 case Tegra::Shader::ImageType::Texture1D:
719 return "image1D"; 719 return "1D";
720 case Tegra::Shader::ImageType::TextureBuffer: 720 case Tegra::Shader::ImageType::TextureBuffer:
721 return "imageBuffer"; 721 return "Buffer";
722 case Tegra::Shader::ImageType::Texture1DArray: 722 case Tegra::Shader::ImageType::Texture1DArray:
723 return "image1DArray"; 723 return "1DArray";
724 case Tegra::Shader::ImageType::Texture2D: 724 case Tegra::Shader::ImageType::Texture2D:
725 return "image2D"; 725 return "2D";
726 case Tegra::Shader::ImageType::Texture2DArray: 726 case Tegra::Shader::ImageType::Texture2DArray:
727 return "image2DArray"; 727 return "2DArray";
728 case Tegra::Shader::ImageType::Texture3D: 728 case Tegra::Shader::ImageType::Texture3D:
729 return "image3D"; 729 return "3D";
730 default: 730 default:
731 UNREACHABLE(); 731 UNREACHABLE();
732 return "image1D"; 732 return "1D";
733 }
734 }();
735
736 const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
737 if (!image.IsSizeKnown()) {
738 return {"", ""};
739 }
740 switch (image.GetSize()) {
741 case Tegra::Shader::ImageAtomicSize::U32:
742 return {"u", "r32ui, "};
743 case Tegra::Shader::ImageAtomicSize::S32:
744 return {"i", "r32i, "};
745 default:
746 UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
747 static_cast<u32>(image.GetSize()));
748 return {"", ""};
749 } 733 }
750 }(); 734 }();
751 735
@@ -756,8 +740,12 @@ private:
756 qualifier += " writeonly"; 740 qualifier += " writeonly";
757 } 741 }
758 742
759 code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform " 743 std::string format;
760 "{} {};", 744 if (image.IsAtomic()) {
745 format = "r32ui, ";
746 }
747
748 code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format,
761 image.GetIndex(), qualifier, image_type, GetImage(image)); 749 image.GetIndex(), qualifier, image_type, GetImage(image));
762 } 750 }
763 if (!images.empty()) { 751 if (!images.empty()) {
@@ -1225,28 +1213,13 @@ private:
1225 } 1213 }
1226 1214
1227 std::string BuildImageValues(Operation operation) { 1215 std::string BuildImageValues(Operation operation) {
1216 constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"};
1228 const auto meta{std::get<MetaImage>(operation.GetMeta())}; 1217 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1229 const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
1230 constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
1231 if (!meta.image.IsSizeKnown()) {
1232 return {float_constructors, Type::Float};
1233 }
1234 switch (meta.image.GetSize()) {
1235 case Tegra::Shader::ImageAtomicSize::U32:
1236 return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
1237 case Tegra::Shader::ImageAtomicSize::S32:
1238 return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
1239 default:
1240 UNIMPLEMENTED_MSG("Unimplemented image size={}",
1241 static_cast<u32>(meta.image.GetSize()));
1242 return {float_constructors, Type::Float};
1243 }
1244 }();
1245 1218
1246 const std::size_t values_count{meta.values.size()}; 1219 const std::size_t values_count{meta.values.size()};
1247 std::string expr = fmt::format("{}(", constructors.at(values_count - 1)); 1220 std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
1248 for (std::size_t i = 0; i < values_count; ++i) { 1221 for (std::size_t i = 0; i < values_count; ++i) {
1249 expr += Visit(meta.values.at(i)).As(type); 1222 expr += Visit(meta.values.at(i)).AsUint();
1250 if (i + 1 < values_count) { 1223 if (i + 1 < values_count) {
1251 expr += ", "; 1224 expr += ", ";
1252 } 1225 }
@@ -1255,29 +1228,6 @@ private:
1255 return expr; 1228 return expr;
1256 } 1229 }
1257 1230
1258 Expression AtomicImage(Operation operation, const char* opname) {
1259 constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
1260 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1261 ASSERT(meta.values.size() == 1);
1262 ASSERT(meta.image.IsSizeKnown());
1263
1264 const auto type = [&]() {
1265 switch (const auto size = meta.image.GetSize()) {
1266 case Tegra::Shader::ImageAtomicSize::U32:
1267 return Type::Uint;
1268 case Tegra::Shader::ImageAtomicSize::S32:
1269 return Type::Int;
1270 default:
1271 UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
1272 return Type::Uint;
1273 }
1274 }();
1275
1276 return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
1277 BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
1278 type};
1279 }
1280
1281 Expression Assign(Operation operation) { 1231 Expression Assign(Operation operation) {
1282 const Node& dest = operation[0]; 1232 const Node& dest = operation[0];
1283 const Node& src = operation[1]; 1233 const Node& src = operation[1];
@@ -1810,7 +1760,7 @@ private:
1810 const auto meta{std::get<MetaImage>(operation.GetMeta())}; 1760 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1811 return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image), 1761 return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image),
1812 BuildIntegerCoordinates(operation), GetSwizzle(meta.element)), 1762 BuildIntegerCoordinates(operation), GetSwizzle(meta.element)),
1813 Type::Float}; 1763 Type::Uint};
1814 } 1764 }
1815 1765
1816 Expression ImageStore(Operation operation) { 1766 Expression ImageStore(Operation operation) {
@@ -1820,31 +1770,14 @@ private:
1820 return {}; 1770 return {};
1821 } 1771 }
1822 1772
1823 Expression AtomicImageAdd(Operation operation) { 1773 template <const std::string_view& opname>
1824 return AtomicImage(operation, "imageAtomicAdd"); 1774 Expression AtomicImage(Operation operation) {
1825 } 1775 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1826 1776 ASSERT(meta.values.size() == 1);
1827 Expression AtomicImageMin(Operation operation) {
1828 return AtomicImage(operation, "imageAtomicMin");
1829 }
1830
1831 Expression AtomicImageMax(Operation operation) {
1832 return AtomicImage(operation, "imageAtomicMax");
1833 }
1834 Expression AtomicImageAnd(Operation operation) {
1835 return AtomicImage(operation, "imageAtomicAnd");
1836 }
1837
1838 Expression AtomicImageOr(Operation operation) {
1839 return AtomicImage(operation, "imageAtomicOr");
1840 }
1841
1842 Expression AtomicImageXor(Operation operation) {
1843 return AtomicImage(operation, "imageAtomicXor");
1844 }
1845 1777
1846 Expression AtomicImageExchange(Operation operation) { 1778 return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image),
1847 return AtomicImage(operation, "imageAtomicExchange"); 1779 BuildIntegerCoordinates(operation), Visit(meta.values[0]).AsUint()),
1780 Type::Uint};
1848 } 1781 }
1849 1782
1850 Expression Branch(Operation operation) { 1783 Expression Branch(Operation operation) {
@@ -2039,6 +1972,12 @@ private:
2039 Func() = delete; 1972 Func() = delete;
2040 ~Func() = delete; 1973 ~Func() = delete;
2041 1974
1975 static constexpr std::string_view Add = "Add";
1976 static constexpr std::string_view And = "And";
1977 static constexpr std::string_view Or = "Or";
1978 static constexpr std::string_view Xor = "Xor";
1979 static constexpr std::string_view Exchange = "Exchange";
1980
2042 static constexpr std::string_view ShuffleIndexed = "shuffleNV"; 1981 static constexpr std::string_view ShuffleIndexed = "shuffleNV";
2043 static constexpr std::string_view ShuffleUp = "shuffleUpNV"; 1982 static constexpr std::string_view ShuffleUp = "shuffleUpNV";
2044 static constexpr std::string_view ShuffleDown = "shuffleDownNV"; 1983 static constexpr std::string_view ShuffleDown = "shuffleDownNV";
@@ -2178,13 +2117,12 @@ private:
2178 2117
2179 &GLSLDecompiler::ImageLoad, 2118 &GLSLDecompiler::ImageLoad,
2180 &GLSLDecompiler::ImageStore, 2119 &GLSLDecompiler::ImageStore,
2181 &GLSLDecompiler::AtomicImageAdd, 2120
2182 &GLSLDecompiler::AtomicImageMin, 2121 &GLSLDecompiler::AtomicImage<Func::Add>,
2183 &GLSLDecompiler::AtomicImageMax, 2122 &GLSLDecompiler::AtomicImage<Func::And>,
2184 &GLSLDecompiler::AtomicImageAnd, 2123 &GLSLDecompiler::AtomicImage<Func::Or>,
2185 &GLSLDecompiler::AtomicImageOr, 2124 &GLSLDecompiler::AtomicImage<Func::Xor>,
2186 &GLSLDecompiler::AtomicImageXor, 2125 &GLSLDecompiler::AtomicImage<Func::Exchange>,
2187 &GLSLDecompiler::AtomicImageExchange,
2188 2126
2189 &GLSLDecompiler::Branch, 2127 &GLSLDecompiler::Branch,
2190 &GLSLDecompiler::BranchIndirect, 2128 &GLSLDecompiler::BranchIndirect,
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 02b4dd234..6a7012b54 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -343,20 +343,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
343 u8 is_bindless{}; 343 u8 is_bindless{};
344 u8 is_written{}; 344 u8 is_written{};
345 u8 is_read{}; 345 u8 is_read{};
346 u8 is_size_known{}; 346 u8 is_atomic{};
347 u32 size{};
348 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || 347 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
349 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) || 348 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
350 !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) || 349 !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) ||
351 !LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) { 350 !LoadObjectFromPrecompiled(is_atomic)) {
352 return {}; 351 return {};
353 } 352 }
354 entry.entries.images.emplace_back( 353 entry.entries.images.emplace_back(
355 static_cast<std::size_t>(offset), static_cast<std::size_t>(index), 354 static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
356 static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0, 355 static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0,
357 is_read != 0, 356 is_read != 0, is_atomic != 0);
358 is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size))
359 : std::nullopt);
360 } 357 }
361 358
362 u32 global_memory_count{}; 359 u32 global_memory_count{};
@@ -429,14 +426,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
429 return false; 426 return false;
430 } 427 }
431 for (const auto& image : entries.images) { 428 for (const auto& image : entries.images) {
432 const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U;
433 if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) || 429 if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) ||
434 !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) || 430 !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) ||
435 !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) || 431 !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) ||
436 !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) || 432 !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) ||
437 !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) || 433 !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) ||
438 !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) || 434 !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
439 !SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) { 435 !SaveObjectToPrecompiled(static_cast<u8>(image.IsAtomic() ? 1 : 0))) {
440 return false; 436 return false;
441 } 437 }
442 } 438 }
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 9d31bff43..77fc58f25 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -955,16 +955,6 @@ private:
955 return {}; 955 return {};
956 } 956 }
957 957
958 Id AtomicImageMin(Operation operation) {
959 UNIMPLEMENTED();
960 return {};
961 }
962
963 Id AtomicImageMax(Operation operation) {
964 UNIMPLEMENTED();
965 return {};
966 }
967
968 Id AtomicImageAnd(Operation operation) { 958 Id AtomicImageAnd(Operation operation) {
969 UNIMPLEMENTED(); 959 UNIMPLEMENTED();
970 return {}; 960 return {};
@@ -1449,8 +1439,6 @@ private:
1449 &SPIRVDecompiler::ImageLoad, 1439 &SPIRVDecompiler::ImageLoad,
1450 &SPIRVDecompiler::ImageStore, 1440 &SPIRVDecompiler::ImageStore,
1451 &SPIRVDecompiler::AtomicImageAdd, 1441 &SPIRVDecompiler::AtomicImageAdd,
1452 &SPIRVDecompiler::AtomicImageMin,
1453 &SPIRVDecompiler::AtomicImageMax,
1454 &SPIRVDecompiler::AtomicImageAnd, 1442 &SPIRVDecompiler::AtomicImageAnd,
1455 &SPIRVDecompiler::AtomicImageOr, 1443 &SPIRVDecompiler::AtomicImageOr,
1456 &SPIRVDecompiler::AtomicImageXor, 1444 &SPIRVDecompiler::AtomicImageXor,
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index e611f9f3b..95ec1cdd9 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -101,32 +101,35 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
101 UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0); 101 UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0);
102 102
103 const OperationCode operation_code = [instr] { 103 const OperationCode operation_code = [instr] {
104 switch (instr.suatom_d.operation) { 104 switch (instr.suatom_d.operation_type) {
105 case Tegra::Shader::ImageAtomicOperation::Add: 105 case Tegra::Shader::ImageAtomicOperationType::S32:
106 return OperationCode::AtomicImageAdd; 106 case Tegra::Shader::ImageAtomicOperationType::U32:
107 case Tegra::Shader::ImageAtomicOperation::Min: 107 switch (instr.suatom_d.operation) {
108 return OperationCode::AtomicImageMin; 108 case Tegra::Shader::ImageAtomicOperation::Add:
109 case Tegra::Shader::ImageAtomicOperation::Max: 109 return OperationCode::AtomicImageAdd;
110 return OperationCode::AtomicImageMax; 110 case Tegra::Shader::ImageAtomicOperation::And:
111 case Tegra::Shader::ImageAtomicOperation::And: 111 return OperationCode::AtomicImageAnd;
112 return OperationCode::AtomicImageAnd; 112 case Tegra::Shader::ImageAtomicOperation::Or:
113 case Tegra::Shader::ImageAtomicOperation::Or: 113 return OperationCode::AtomicImageOr;
114 return OperationCode::AtomicImageOr; 114 case Tegra::Shader::ImageAtomicOperation::Xor:
115 case Tegra::Shader::ImageAtomicOperation::Xor: 115 return OperationCode::AtomicImageXor;
116 return OperationCode::AtomicImageXor; 116 case Tegra::Shader::ImageAtomicOperation::Exch:
117 case Tegra::Shader::ImageAtomicOperation::Exch: 117 return OperationCode::AtomicImageExchange;
118 return OperationCode::AtomicImageExchange; 118 }
119 default: 119 default:
120 UNIMPLEMENTED_MSG("Unimplemented operation={}", 120 break;
121 static_cast<u32>(instr.suatom_d.operation.Value()));
122 return OperationCode::AtomicImageAdd;
123 } 121 }
122 UNIMPLEMENTED_MSG("Unimplemented operation={} type={}",
123 static_cast<u64>(instr.suatom_d.operation.Value()),
124 static_cast<u64>(instr.suatom_d.operation_type.Value()));
125 return OperationCode::AtomicImageAdd;
124 }(); 126 }();
125 127
126 Node value = GetRegister(instr.gpr0); 128 Node value = GetRegister(instr.gpr0);
127 129
128 const auto type = instr.suatom_d.image_type; 130 const auto type = instr.suatom_d.image_type;
129 const auto& image{GetImage(instr.image, type, instr.suatom_d.size)}; 131 auto& image = GetImage(instr.image, type);
132 image.MarkAtomic();
130 133
131 MetaImage meta{image, {std::move(value)}}; 134 MetaImage meta{image, {std::move(value)}};
132 SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type))); 135 SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type)));
@@ -139,35 +142,32 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
139 return pc; 142 return pc;
140} 143}
141 144
142Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, 145Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
143 std::optional<Tegra::Shader::ImageAtomicSize> size) {
144 const auto offset{static_cast<std::size_t>(image.index.Value())}; 146 const auto offset{static_cast<std::size_t>(image.index.Value())};
145 if (const auto image = TryUseExistingImage(offset, type, size)) { 147 if (const auto image = TryUseExistingImage(offset, type)) {
146 return *image; 148 return *image;
147 } 149 }
148 150
149 const std::size_t next_index{used_images.size()}; 151 const std::size_t next_index{used_images.size()};
150 return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second; 152 return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
151} 153}
152 154
153Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, 155Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
154 std::optional<Tegra::Shader::ImageAtomicSize> size) {
155 const Node image_register{GetRegister(reg)}; 156 const Node image_register{GetRegister(reg)};
156 const auto [base_image, cbuf_index, cbuf_offset]{ 157 const auto [base_image, cbuf_index, cbuf_offset]{
157 TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; 158 TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
158 const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)}; 159 const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
159 160
160 if (const auto image = TryUseExistingImage(cbuf_key, type, size)) { 161 if (const auto image = TryUseExistingImage(cbuf_key, type)) {
161 return *image; 162 return *image;
162 } 163 }
163 164
164 const std::size_t next_index{used_images.size()}; 165 const std::size_t next_index{used_images.size()};
165 return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size}) 166 return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
166 .first->second; 167 .first->second;
167} 168}
168 169
169Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, 170Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) {
170 std::optional<Tegra::Shader::ImageAtomicSize> size) {
171 auto it = used_images.find(offset); 171 auto it = used_images.find(offset);
172 if (it == used_images.end()) { 172 if (it == used_images.end()) {
173 return nullptr; 173 return nullptr;
@@ -175,14 +175,6 @@ Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
175 auto& image = it->second; 175 auto& image = it->second;
176 ASSERT(image.GetType() == type); 176 ASSERT(image.GetType() == type);
177 177
178 if (size) {
179 // We know the size, if it's known it has to be the same as before, otherwise we can set it.
180 if (image.IsSizeKnown()) {
181 ASSERT(image.GetSize() == size);
182 } else {
183 image.SetSize(*size);
184 }
185 }
186 return &image; 178 return &image;
187} 179}
188 180
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index e5b75783d..338bab17c 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -149,11 +149,10 @@ enum class OperationCode {
149 TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 149 TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
150 TexelFetch, /// (MetaTexture, int[N], int) -> float4 150 TexelFetch, /// (MetaTexture, int[N], int) -> float4
151 151
152 ImageLoad, /// (MetaImage, int[N] coords) -> void 152 ImageLoad, /// (MetaImage, int[N] coords) -> void
153 ImageStore, /// (MetaImage, int[N] coords) -> void 153 ImageStore, /// (MetaImage, int[N] coords) -> void
154
154 AtomicImageAdd, /// (MetaImage, int[N] coords) -> void 155 AtomicImageAdd, /// (MetaImage, int[N] coords) -> void
155 AtomicImageMin, /// (MetaImage, int[N] coords) -> void
156 AtomicImageMax, /// (MetaImage, int[N] coords) -> void
157 AtomicImageAnd, /// (MetaImage, int[N] coords) -> void 156 AtomicImageAnd, /// (MetaImage, int[N] coords) -> void
158 AtomicImageOr, /// (MetaImage, int[N] coords) -> void 157 AtomicImageOr, /// (MetaImage, int[N] coords) -> void
159 AtomicImageXor, /// (MetaImage, int[N] coords) -> void 158 AtomicImageXor, /// (MetaImage, int[N] coords) -> void
@@ -295,21 +294,18 @@ private:
295 294
296class Image final { 295class Image final {
297public: 296public:
298 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, 297 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type)
299 std::optional<Tegra::Shader::ImageAtomicSize> size) 298 : offset{offset}, index{index}, type{type}, is_bindless{false} {}
300 : offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {}
301 299
302 constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, 300 constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
303 Tegra::Shader::ImageType type, 301 Tegra::Shader::ImageType type)
304 std::optional<Tegra::Shader::ImageAtomicSize> size)
305 : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, 302 : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
306 is_bindless{true}, size{size} {} 303 is_bindless{true} {}
307 304
308 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, 305 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
309 bool is_bindless, bool is_written, bool is_read, 306 bool is_bindless, bool is_written, bool is_read, bool is_atomic)
310 std::optional<Tegra::Shader::ImageAtomicSize> size)
311 : offset{offset}, index{index}, type{type}, is_bindless{is_bindless}, 307 : offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
312 is_written{is_written}, is_read{is_read}, size{size} {} 308 is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {}
313 309
314 void MarkWrite() { 310 void MarkWrite() {
315 is_written = true; 311 is_written = true;
@@ -319,8 +315,10 @@ public:
319 is_read = true; 315 is_read = true;
320 } 316 }
321 317
322 void SetSize(Tegra::Shader::ImageAtomicSize size_) { 318 void MarkAtomic() {
323 size = size_; 319 MarkWrite();
320 MarkRead();
321 is_atomic = true;
324 } 322 }
325 323
326 constexpr std::size_t GetOffset() const { 324 constexpr std::size_t GetOffset() const {
@@ -347,21 +345,17 @@ public:
347 return is_read; 345 return is_read;
348 } 346 }
349 347
350 constexpr std::pair<u32, u32> GetBindlessCBuf() const { 348 constexpr bool IsAtomic() const {
351 return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)}; 349 return is_atomic;
352 }
353
354 constexpr bool IsSizeKnown() const {
355 return size.has_value();
356 } 350 }
357 351
358 constexpr Tegra::Shader::ImageAtomicSize GetSize() const { 352 constexpr std::pair<u32, u32> GetBindlessCBuf() const {
359 return size.value(); 353 return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
360 } 354 }
361 355
362 constexpr bool operator<(const Image& rhs) const { 356 constexpr bool operator<(const Image& rhs) const {
363 return std::tie(offset, index, type, size, is_bindless) < 357 return std::tie(offset, index, type, is_bindless) <
364 std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless); 358 std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless);
365 } 359 }
366 360
367private: 361private:
@@ -371,7 +365,7 @@ private:
371 bool is_bindless{}; 365 bool is_bindless{};
372 bool is_written{}; 366 bool is_written{};
373 bool is_read{}; 367 bool is_read{};
374 std::optional<Tegra::Shader::ImageAtomicSize> size{}; 368 bool is_atomic{};
375}; 369};
376 370
377struct GlobalMemoryBase { 371struct GlobalMemoryBase {
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 6aed9bb84..c3e147eea 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -276,16 +276,13 @@ private:
276 bool is_shadow); 276 bool is_shadow);
277 277
278 /// Accesses an image. 278 /// Accesses an image.
279 Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, 279 Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
280 std::optional<Tegra::Shader::ImageAtomicSize> size = {});
281 280
282 /// Access a bindless image sampler. 281 /// Access a bindless image sampler.
283 Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, 282 Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
284 std::optional<Tegra::Shader::ImageAtomicSize> size = {});
285 283
286 /// Tries to access an existing image, updating it's state as needed 284 /// Tries to access an existing image, updating it's state as needed
287 Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, 285 Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type);
288 std::optional<Tegra::Shader::ImageAtomicSize> size);
289 286
290 /// Extracts a sequence of bits from a node 287 /// Extracts a sequence of bits from a node
291 Node BitfieldExtract(Node value, u32 offset, u32 bits); 288 Node BitfieldExtract(Node value, u32 offset, u32 bits);