summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/engines/shader_bytecode.h32
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp145
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp42
-rw-r--r--src/video_core/shader/decode/image.cpp92
-rw-r--r--src/video_core/shader/node.h57
-rw-r--r--src/video_core/shader/shader_ir.h10
7 files changed, 329 insertions, 69 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index bd8c1ada0..052e6d24e 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -544,6 +544,28 @@ enum class VoteOperation : u64 {
544 Eq = 2, // allThreadsEqualNV 544 Eq = 2, // allThreadsEqualNV
545}; 545};
546 546
547enum class ImageAtomicSize : u64 {
548 U32 = 0,
549 S32 = 1,
550 U64 = 2,
551 F32 = 3,
552 S64 = 5,
553 SD32 = 6,
554 SD64 = 7,
555};
556
557enum class ImageAtomicOperation : u64 {
558 Add = 0,
559 Min = 1,
560 Max = 2,
561 Inc = 3,
562 Dec = 4,
563 And = 5,
564 Or = 6,
565 Xor = 7,
566 Exch = 8,
567};
568
547union Instruction { 569union Instruction {
548 Instruction& operator=(const Instruction& instr) { 570 Instruction& operator=(const Instruction& instr) {
549 value = instr.value; 571 value = instr.value;
@@ -1392,6 +1414,14 @@ union Instruction {
1392 } sust; 1414 } sust;
1393 1415
1394 union { 1416 union {
1417 BitField<28, 1, u64> is_ba;
1418 BitField<51, 3, ImageAtomicSize> size;
1419 BitField<33, 3, ImageType> image_type;
1420 BitField<29, 4, ImageAtomicOperation> operation;
1421 BitField<49, 2, OutOfBoundsStore> out_of_bounds_store;
1422 } suatom_d;
1423
1424 union {
1395 BitField<20, 24, u64> target; 1425 BitField<20, 24, u64> target;
1396 BitField<5, 1, u64> constant_buffer; 1426 BitField<5, 1, u64> constant_buffer;
1397 1427
@@ -1543,6 +1573,7 @@ public:
1543 TMML_B, // Texture Mip Map Level 1573 TMML_B, // Texture Mip Map Level
1544 TMML, // Texture Mip Map Level 1574 TMML, // Texture Mip Map Level
1545 SUST, // Surface Store 1575 SUST, // Surface Store
1576 SUATOM, // Surface Atomic Operation
1546 EXIT, 1577 EXIT,
1547 NOP, 1578 NOP,
1548 IPA, 1579 IPA,
@@ -1826,6 +1857,7 @@ private:
1826 INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"), 1857 INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
1827 INST("1101111101011---", Id::TMML, Type::Texture, "TMML"), 1858 INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
1828 INST("11101011001-----", Id::SUST, Type::Image, "SUST"), 1859 INST("11101011001-----", Id::SUST, Type::Image, "SUST"),
1860 INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"),
1829 INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"), 1861 INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"),
1830 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 1862 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
1831 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), 1863 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 6edb2ca38..137b23740 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -706,7 +706,7 @@ private:
706 void DeclareImages() { 706 void DeclareImages() {
707 const auto& images{ir.GetImages()}; 707 const auto& images{ir.GetImages()};
708 for (const auto& [offset, image] : images) { 708 for (const auto& [offset, image] : images) {
709 const std::string image_type = [&]() { 709 const char* image_type = [&] {
710 switch (image.GetType()) { 710 switch (image.GetType()) {
711 case Tegra::Shader::ImageType::Texture1D: 711 case Tegra::Shader::ImageType::Texture1D:
712 return "image1D"; 712 return "image1D";
@@ -725,6 +725,23 @@ private:
725 return "image1D"; 725 return "image1D";
726 } 726 }
727 }(); 727 }();
728
729 const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
730 if (!image.IsSizeKnown()) {
731 return {"", ""};
732 }
733 switch (image.GetSize()) {
734 case Tegra::Shader::ImageAtomicSize::U32:
735 return {"u", "r32ui, "};
736 case Tegra::Shader::ImageAtomicSize::S32:
737 return {"i", "r32i, "};
738 default:
739 UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
740 static_cast<u32>(image.GetSize()));
741 return {"", ""};
742 }
743 }();
744
728 std::string qualifier = "coherent volatile"; 745 std::string qualifier = "coherent volatile";
729 if (image.IsRead() && !image.IsWritten()) { 746 if (image.IsRead() && !image.IsWritten()) {
730 qualifier += " readonly"; 747 qualifier += " readonly";
@@ -1180,6 +1197,74 @@ private:
1180 return expr; 1197 return expr;
1181 } 1198 }
1182 1199
1200 std::string BuildIntegerCoordinates(Operation operation) {
1201 constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
1202 const std::size_t coords_count{operation.GetOperandsCount()};
1203 std::string expr = constructors.at(coords_count - 1);
1204 for (std::size_t i = 0; i < coords_count; ++i) {
1205 expr += VisitOperand(operation, i).AsInt();
1206 if (i + 1 < coords_count) {
1207 expr += ", ";
1208 }
1209 }
1210 expr += ')';
1211 return expr;
1212 }
1213
1214 std::string BuildImageValues(Operation operation) {
1215 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1216 const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
1217 constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
1218 if (!meta.image.IsSizeKnown()) {
1219 return {float_constructors, Type::Float};
1220 }
1221 switch (meta.image.GetSize()) {
1222 case Tegra::Shader::ImageAtomicSize::U32:
1223 return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
1224 case Tegra::Shader::ImageAtomicSize::S32:
1225 return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
1226 default:
1227 UNIMPLEMENTED_MSG("Unimplemented image size={}",
1228 static_cast<u32>(meta.image.GetSize()));
1229 return {float_constructors, Type::Float};
1230 }
1231 }();
1232
1233 const std::size_t values_count{meta.values.size()};
1234 std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
1235 for (std::size_t i = 0; i < values_count; ++i) {
1236 expr += Visit(meta.values.at(i)).As(type);
1237 if (i + 1 < values_count) {
1238 expr += ", ";
1239 }
1240 }
1241 expr += ')';
1242 return expr;
1243 }
1244
1245 Expression AtomicImage(Operation operation, const char* opname) {
1246 constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
1247 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1248 ASSERT(meta.values.size() == 1);
1249 ASSERT(meta.image.IsSizeKnown());
1250
1251 const auto type = [&]() {
1252 switch (const auto size = meta.image.GetSize()) {
1253 case Tegra::Shader::ImageAtomicSize::U32:
1254 return Type::Uint;
1255 case Tegra::Shader::ImageAtomicSize::S32:
1256 return Type::Int;
1257 default:
1258 UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
1259 return Type::Uint;
1260 }
1261 }();
1262
1263 return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
1264 BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
1265 type};
1266 }
1267
1183 Expression Assign(Operation operation) { 1268 Expression Assign(Operation operation) {
1184 const Node& dest = operation[0]; 1269 const Node& dest = operation[0];
1185 const Node& src = operation[1]; 1270 const Node& src = operation[1];
@@ -1694,36 +1779,37 @@ private:
1694 } 1779 }
1695 1780
1696 Expression ImageStore(Operation operation) { 1781 Expression ImageStore(Operation operation) {
1697 constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
1698 const auto meta{std::get<MetaImage>(operation.GetMeta())}; 1782 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1783 code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
1784 BuildIntegerCoordinates(operation), BuildImageValues(operation));
1785 return {};
1786 }
1699 1787
1700 std::string expr = "imageStore("; 1788 Expression AtomicImageAdd(Operation operation) {
1701 expr += GetImage(meta.image); 1789 return AtomicImage(operation, "imageAtomicAdd");
1702 expr += ", "; 1790 }
1703 1791
1704 const std::size_t coords_count{operation.GetOperandsCount()}; 1792 Expression AtomicImageMin(Operation operation) {
1705 expr += constructors.at(coords_count - 1); 1793 return AtomicImage(operation, "imageAtomicMin");
1706 for (std::size_t i = 0; i < coords_count; ++i) { 1794 }
1707 expr += VisitOperand(operation, i).AsInt();
1708 if (i + 1 < coords_count) {
1709 expr += ", ";
1710 }
1711 }
1712 expr += "), ";
1713 1795
1714 const std::size_t values_count{meta.values.size()}; 1796 Expression AtomicImageMax(Operation operation) {
1715 UNIMPLEMENTED_IF(values_count != 4); 1797 return AtomicImage(operation, "imageAtomicMax");
1716 expr += "vec4("; 1798 }
1717 for (std::size_t i = 0; i < values_count; ++i) { 1799 Expression AtomicImageAnd(Operation operation) {
1718 expr += Visit(meta.values.at(i)).AsFloat(); 1800 return AtomicImage(operation, "imageAtomicAnd");
1719 if (i + 1 < values_count) { 1801 }
1720 expr += ", ";
1721 }
1722 }
1723 expr += "));";
1724 1802
1725 code.AddLine(expr); 1803 Expression AtomicImageOr(Operation operation) {
1726 return {}; 1804 return AtomicImage(operation, "imageAtomicOr");
1805 }
1806
1807 Expression AtomicImageXor(Operation operation) {
1808 return AtomicImage(operation, "imageAtomicXor");
1809 }
1810
1811 Expression AtomicImageExchange(Operation operation) {
1812 return AtomicImage(operation, "imageAtomicExchange");
1727 } 1813 }
1728 1814
1729 Expression Branch(Operation operation) { 1815 Expression Branch(Operation operation) {
@@ -2019,6 +2105,13 @@ private:
2019 &GLSLDecompiler::TexelFetch, 2105 &GLSLDecompiler::TexelFetch,
2020 2106
2021 &GLSLDecompiler::ImageStore, 2107 &GLSLDecompiler::ImageStore,
2108 &GLSLDecompiler::AtomicImageAdd,
2109 &GLSLDecompiler::AtomicImageMin,
2110 &GLSLDecompiler::AtomicImageMax,
2111 &GLSLDecompiler::AtomicImageAnd,
2112 &GLSLDecompiler::AtomicImageOr,
2113 &GLSLDecompiler::AtomicImageXor,
2114 &GLSLDecompiler::AtomicImageExchange,
2022 2115
2023 &GLSLDecompiler::Branch, 2116 &GLSLDecompiler::Branch,
2024 &GLSLDecompiler::BranchIndirect, 2117 &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 5450feedf..f141c4e3b 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -341,16 +341,22 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
341 u64 index{}; 341 u64 index{};
342 u32 type{}; 342 u32 type{};
343 u8 is_bindless{}; 343 u8 is_bindless{};
344 u8 is_read{};
345 u8 is_written{}; 344 u8 is_written{};
345 u8 is_read{};
346 u8 is_size_known{};
347 u32 size{};
346 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || 348 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
347 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) || 349 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
348 !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) { 350 !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) ||
351 !LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) {
349 return {}; 352 return {};
350 } 353 }
351 entry.entries.images.emplace_back(static_cast<u64>(offset), static_cast<std::size_t>(index), 354 entry.entries.images.emplace_back(
352 static_cast<Tegra::Shader::ImageType>(type), 355 static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
353 is_bindless != 0, is_written != 0, is_read != 0); 356 static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0,
357 is_read != 0,
358 is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size))
359 : std::nullopt);
354 } 360 }
355 361
356 u32 global_memory_count{}; 362 u32 global_memory_count{};
@@ -429,12 +435,14 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
429 return false; 435 return false;
430 } 436 }
431 for (const auto& image : entries.images) { 437 for (const auto& image : entries.images) {
438 const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U;
432 if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) || 439 if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) ||
433 !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) || 440 !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) ||
434 !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) || 441 !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) ||
435 !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) || 442 !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) ||
443 !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) ||
436 !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) || 444 !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
437 !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0))) { 445 !SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) {
438 return false; 446 return false;
439 } 447 }
440 } 448 }
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index a35b45c9c..7675fc7b3 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -944,6 +944,41 @@ private:
944 return {}; 944 return {};
945 } 945 }
946 946
947 Id AtomicImageAdd(Operation operation) {
948 UNIMPLEMENTED();
949 return {};
950 }
951
952 Id AtomicImageMin(Operation operation) {
953 UNIMPLEMENTED();
954 return {};
955 }
956
957 Id AtomicImageMax(Operation operation) {
958 UNIMPLEMENTED();
959 return {};
960 }
961
962 Id AtomicImageAnd(Operation operation) {
963 UNIMPLEMENTED();
964 return {};
965 }
966
967 Id AtomicImageOr(Operation operation) {
968 UNIMPLEMENTED();
969 return {};
970 }
971
972 Id AtomicImageXor(Operation operation) {
973 UNIMPLEMENTED();
974 return {};
975 }
976
977 Id AtomicImageExchange(Operation operation) {
978 UNIMPLEMENTED();
979 return {};
980 }
981
947 Id Branch(Operation operation) { 982 Id Branch(Operation operation) {
948 const auto target = std::get_if<ImmediateNode>(&*operation[0]); 983 const auto target = std::get_if<ImmediateNode>(&*operation[0]);
949 UNIMPLEMENTED_IF(!target); 984 UNIMPLEMENTED_IF(!target);
@@ -1366,6 +1401,13 @@ private:
1366 &SPIRVDecompiler::TexelFetch, 1401 &SPIRVDecompiler::TexelFetch,
1367 1402
1368 &SPIRVDecompiler::ImageStore, 1403 &SPIRVDecompiler::ImageStore,
1404 &SPIRVDecompiler::AtomicImageAdd,
1405 &SPIRVDecompiler::AtomicImageMin,
1406 &SPIRVDecompiler::AtomicImageMax,
1407 &SPIRVDecompiler::AtomicImageAnd,
1408 &SPIRVDecompiler::AtomicImageOr,
1409 &SPIRVDecompiler::AtomicImageXor,
1410 &SPIRVDecompiler::AtomicImageExchange,
1369 1411
1370 &SPIRVDecompiler::Branch, 1412 &SPIRVDecompiler::Branch,
1371 &SPIRVDecompiler::BranchIndirect, 1413 &SPIRVDecompiler::BranchIndirect,
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index 008109a99..d54fb88c9 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -44,7 +44,6 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
44 switch (opcode->get().GetId()) { 44 switch (opcode->get().GetId()) {
45 case OpCode::Id::SUST: { 45 case OpCode::Id::SUST: {
46 UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P); 46 UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P);
47 UNIMPLEMENTED_IF(instr.sust.image_type == Tegra::Shader::ImageType::TextureBuffer);
48 UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore); 47 UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore);
49 UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store 48 UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store
50 49
@@ -66,8 +65,46 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
66 image.MarkWrite(); 65 image.MarkWrite();
67 66
68 MetaImage meta{image, values}; 67 MetaImage meta{image, values};
69 const Node store{Operation(OperationCode::ImageStore, meta, std::move(coords))}; 68 bb.push_back(Operation(OperationCode::ImageStore, meta, std::move(coords)));
70 bb.push_back(store); 69 break;
70 }
71 case OpCode::Id::SUATOM: {
72 UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0);
73
74 Node value = GetRegister(instr.gpr0);
75
76 std::vector<Node> coords;
77 const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)};
78 for (std::size_t i = 0; i < num_coords; ++i) {
79 coords.push_back(GetRegister(instr.gpr8.Value() + i));
80 }
81
82 const OperationCode operation_code = [instr] {
83 switch (instr.suatom_d.operation) {
84 case Tegra::Shader::ImageAtomicOperation::Add:
85 return OperationCode::AtomicImageAdd;
86 case Tegra::Shader::ImageAtomicOperation::Min:
87 return OperationCode::AtomicImageMin;
88 case Tegra::Shader::ImageAtomicOperation::Max:
89 return OperationCode::AtomicImageMax;
90 case Tegra::Shader::ImageAtomicOperation::And:
91 return OperationCode::AtomicImageAnd;
92 case Tegra::Shader::ImageAtomicOperation::Or:
93 return OperationCode::AtomicImageOr;
94 case Tegra::Shader::ImageAtomicOperation::Xor:
95 return OperationCode::AtomicImageXor;
96 case Tegra::Shader::ImageAtomicOperation::Exch:
97 return OperationCode::AtomicImageExchange;
98 default:
99 UNIMPLEMENTED_MSG("Unimplemented operation={}",
100 static_cast<u32>(instr.suatom_d.operation.Value()));
101 return OperationCode::AtomicImageAdd;
102 }
103 }();
104
105 const auto& image{GetImage(instr.image, instr.suatom_d.image_type, instr.suatom_d.size)};
106 MetaImage meta{image, {std::move(value)}};
107 SetRegister(bb, instr.gpr0, Operation(operation_code, meta, std::move(coords)));
71 break; 108 break;
72 } 109 }
73 default: 110 default:
@@ -77,38 +114,51 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
77 return pc; 114 return pc;
78} 115}
79 116
80Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) { 117Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
81 const auto offset{static_cast<u64>(image.index.Value())}; 118 std::optional<Tegra::Shader::ImageAtomicSize> size) {
82 119 const auto offset{static_cast<std::size_t>(image.index.Value())};
83 // If this image has already been used, return the existing mapping. 120 if (const auto image = TryUseExistingImage(offset, type, size)) {
84 const auto it = used_images.find(offset); 121 return *image;
85 if (it != used_images.end()) {
86 ASSERT(it->second.GetType() == type);
87 return it->second;
88 } 122 }
89 123
90 // Otherwise create a new mapping for this image.
91 const std::size_t next_index{used_images.size()}; 124 const std::size_t next_index{used_images.size()};
92 return used_images.emplace(offset, Image{offset, next_index, type}).first->second; 125 return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second;
93} 126}
94 127
95Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) { 128Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
129 std::optional<Tegra::Shader::ImageAtomicSize> size) {
96 const Node image_register{GetRegister(reg)}; 130 const Node image_register{GetRegister(reg)};
97 const auto [base_image, cbuf_index, cbuf_offset]{ 131 const auto [base_image, cbuf_index, cbuf_offset]{
98 TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; 132 TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
99 const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)}; 133 const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
100 134
101 // If this image has already been used, return the existing mapping. 135 if (const auto image = TryUseExistingImage(cbuf_key, type, size)) {
102 const auto it = used_images.find(cbuf_key); 136 return *image;
103 if (it != used_images.end()) {
104 ASSERT(it->second.GetType() == type);
105 return it->second;
106 } 137 }
107 138
108 // Otherwise create a new mapping for this image.
109 const std::size_t next_index{used_images.size()}; 139 const std::size_t next_index{used_images.size()};
110 return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type}) 140 return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size})
111 .first->second; 141 .first->second;
112} 142}
113 143
144Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
145 std::optional<Tegra::Shader::ImageAtomicSize> size) {
146 auto it = used_images.find(offset);
147 if (it == used_images.end()) {
148 return nullptr;
149 }
150 auto& image = it->second;
151 ASSERT(image.GetType() == type);
152
153 if (size) {
154 // We know the size, if it's known it has to be the same as before, otherwise we can set it.
155 if (image.IsSizeKnown()) {
156 ASSERT(image.GetSize() == size);
157 } else {
158 image.SetSize(*size);
159 }
160 }
161 return &image;
162}
163
114} // namespace VideoCommon::Shader 164} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index b29aedce8..b47b201cf 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <memory> 9#include <memory>
10#include <optional>
10#include <string> 11#include <string>
11#include <tuple> 12#include <tuple>
12#include <utility> 13#include <utility>
@@ -148,7 +149,14 @@ enum class OperationCode {
148 TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 149 TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
149 TexelFetch, /// (MetaTexture, int[N], int) -> float4 150 TexelFetch, /// (MetaTexture, int[N], int) -> float4
150 151
151 ImageStore, /// (MetaImage, float[N] coords) -> void 152 ImageStore, /// (MetaImage, int[N] values) -> void
153 AtomicImageAdd, /// (MetaImage, int[N] coords) -> void
154 AtomicImageMin, /// (MetaImage, int[N] coords) -> void
155 AtomicImageMax, /// (MetaImage, int[N] coords) -> void
156 AtomicImageAnd, /// (MetaImage, int[N] coords) -> void
157 AtomicImageOr, /// (MetaImage, int[N] coords) -> void
158 AtomicImageXor, /// (MetaImage, int[N] coords) -> void
159 AtomicImageExchange, /// (MetaImage, int[N] coords) -> void
152 160
153 Branch, /// (uint branch_target) -> void 161 Branch, /// (uint branch_target) -> void
154 BranchIndirect, /// (uint branch_target) -> void 162 BranchIndirect, /// (uint branch_target) -> void
@@ -275,25 +283,32 @@ private:
275 283
276class Image final { 284class Image final {
277public: 285public:
278 constexpr explicit Image(u64 offset, std::size_t index, Tegra::Shader::ImageType type) 286 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
279 : offset{offset}, index{index}, type{type}, is_bindless{false} {} 287 std::optional<Tegra::Shader::ImageAtomicSize> size)
288 : offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {}
280 289
281 constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, 290 constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
282 Tegra::Shader::ImageType type) 291 Tegra::Shader::ImageType type,
292 std::optional<Tegra::Shader::ImageAtomicSize> size)
283 : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, 293 : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
284 is_bindless{true} {} 294 is_bindless{true}, size{size} {}
285 295
286 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, 296 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
287 bool is_bindless, bool is_written, bool is_read) 297 bool is_bindless, bool is_written, bool is_read,
298 std::optional<Tegra::Shader::ImageAtomicSize> size)
288 : offset{offset}, index{index}, type{type}, is_bindless{is_bindless}, 299 : offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
289 is_written{is_written}, is_read{is_read} {} 300 is_written{is_written}, is_read{is_read}, size{size} {}
301
302 void MarkWrite() {
303 is_written = true;
304 }
290 305
291 void MarkRead() { 306 void MarkRead() {
292 is_read = true; 307 is_read = true;
293 } 308 }
294 309
295 void MarkWrite() { 310 void SetSize(Tegra::Shader::ImageAtomicSize size_) {
296 is_written = true; 311 size = size_;
297 } 312 }
298 313
299 constexpr std::size_t GetOffset() const { 314 constexpr std::size_t GetOffset() const {
@@ -312,25 +327,39 @@ public:
312 return is_bindless; 327 return is_bindless;
313 } 328 }
314 329
315 constexpr bool IsRead() const {
316 return is_read;
317 }
318
319 constexpr bool IsWritten() const { 330 constexpr bool IsWritten() const {
320 return is_written; 331 return is_written;
321 } 332 }
322 333
334 constexpr bool IsRead() const {
335 return is_read;
336 }
337
323 constexpr std::pair<u32, u32> GetBindlessCBuf() const { 338 constexpr std::pair<u32, u32> GetBindlessCBuf() const {
324 return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)}; 339 return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
325 } 340 }
326 341
342 constexpr bool IsSizeKnown() const {
343 return size.has_value();
344 }
345
346 constexpr Tegra::Shader::ImageAtomicSize GetSize() const {
347 return size.value();
348 }
349
350 constexpr bool operator<(const Image& rhs) const {
351 return std::tie(offset, index, type, size, is_bindless) <
352 std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless);
353 }
354
327private: 355private:
328 u64 offset{}; 356 u64 offset{};
329 std::size_t index{}; 357 std::size_t index{};
330 Tegra::Shader::ImageType type{}; 358 Tegra::Shader::ImageType type{};
331 bool is_bindless{}; 359 bool is_bindless{};
332 bool is_read{};
333 bool is_written{}; 360 bool is_written{};
361 bool is_read{};
362 std::optional<Tegra::Shader::ImageAtomicSize> size{};
334}; 363};
335 364
336struct GlobalMemoryBase { 365struct GlobalMemoryBase {
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 0f891eace..62816bd56 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -272,10 +272,16 @@ private:
272 bool is_shadow); 272 bool is_shadow);
273 273
274 /// Accesses an image. 274 /// Accesses an image.
275 Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type); 275 Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
276 std::optional<Tegra::Shader::ImageAtomicSize> size = {});
276 277
277 /// Access a bindless image sampler. 278 /// Access a bindless image sampler.
278 Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type); 279 Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
280 std::optional<Tegra::Shader::ImageAtomicSize> size = {});
281
282 /// Tries to access an existing image, updating it's state as needed
283 Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
284 std::optional<Tegra::Shader::ImageAtomicSize> size);
279 285
280 /// Extracts a sequence of bits from a node 286 /// Extracts a sequence of bits from a node
281 Node BitfieldExtract(Node value, u32 offset, u32 bits); 287 Node BitfieldExtract(Node value, u32 offset, u32 bits);