From 4de0f1e1c8ba29c354a54464851d859a7e9cb7d7 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 17 Sep 2019 22:38:01 -0300 Subject: [PATCH 1/3] shader_bytecode: Add SULD encoding --- src/video_core/engines/shader_bytecode.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index a6110bd86f..12fb8abb7e 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1590,6 +1590,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, @@ -1875,6 +1876,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"), From 675f23aedc9a3a99925068e952cbcb3faf88296a Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 18 Sep 2019 01:07:01 -0300 Subject: [PATCH 2/3] shader/image: Implement SULD and remove irrelevant code * Implement SULD as float. * Remove conditional declaration of GL_ARB_shader_viewport_layer_array. --- src/video_core/engines/shader_bytecode.h | 2 +- src/video_core/renderer_opengl/gl_device.cpp | 22 ++++++ src/video_core/renderer_opengl/gl_device.h | 5 ++ .../renderer_opengl/gl_shader_cache.cpp | 16 ++-- .../renderer_opengl/gl_shader_decompiler.cpp | 17 ++++- .../renderer_opengl/gl_shader_decompiler.h | 1 - .../renderer_opengl/gl_shader_disk_cache.cpp | 10 --- .../renderer_vulkan/vk_shader_decompiler.cpp | 7 ++ src/video_core/shader/decode/image.cpp | 73 +++++++++++++------ src/video_core/shader/node.h | 4 +- 10 files changed, 110 insertions(+), 47 deletions(-) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 12fb8abb7e..81dfe33a56 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1427,7 +1427,7 @@ union Instruction { ASSERT(mode == SurfaceDataMode::D_BA); return store_data_layout; } - } sust; + } suldst; union { BitField<28, 1, u64> is_ba; diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 4f59a87b40..64de7e4254 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/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 #include #include +#include #include #include "common/logging/log.h" @@ -30,9 +32,27 @@ bool TestProgram(const GLchar* glsl) { return link_status == GL_TRUE; } +std::vector GetExtensions() { + GLint num_extensions; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + std::vector extensions; + extensions.reserve(num_extensions); + for (GLint index = 0; index < num_extensions; ++index) { + extensions.push_back( + reinterpret_cast(glGetStringi(GL_EXTENSIONS, static_cast(index)))); + } + return extensions; +} + +bool HasExtension(const std::vector& 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(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); shader_storage_alignment = GetInteger(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); max_vertex_attributes = GetInteger(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; diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index ba6dcd3bef..bb273c3d6c 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/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{}; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 0dbc4c02f0..42ca3b1bd2 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/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"; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 76439e7abc..70ce6572bc 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/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; } @@ -1801,6 +1800,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(operation.GetMeta())}; + return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image), + BuildIntegerCoordinates(operation), GetSwizzle(meta.element)), + Type::Float}; + } + Expression ImageStore(Operation operation) { const auto meta{std::get(operation.GetMeta())}; code.AddLine("imageStore({}, {}, {});", GetImage(meta.image), @@ -2164,6 +2176,7 @@ private: &GLSLDecompiler::TextureQueryLod, &GLSLDecompiler::TexelFetch, + &GLSLDecompiler::ImageLoad, &GLSLDecompiler::ImageStore, &GLSLDecompiler::AtomicImageAdd, &GLSLDecompiler::AtomicImageMin, diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 2ea02f5bf3..e538dc001b 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -90,7 +90,6 @@ struct ShaderEntries { std::vector images; std::vector global_memory_entries; std::array clip_distances{}; - bool shader_viewport_layer_array{}; std::size_t shader_length{}; }; 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 f141c4e3b1..02b4dd234b 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -382,12 +382,6 @@ std::optional 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 {}; @@ -464,10 +458,6 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std: } } - if (!SaveObjectToPrecompiled(entries.shader_viewport_layer_array)) { - return false; - } - if (!SaveObjectToPrecompiled(static_cast(entries.shader_length))) { return false; } diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index f7fbbb6e4d..9d31bff438 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/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,6 +940,11 @@ private: return {}; } + Id ImageLoad(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + Id ImageStore(Operation operation) { UNIMPLEMENTED(); return {}; @@ -1440,6 +1446,7 @@ private: &SPIRVDecompiler::TextureQueryLod, &SPIRVDecompiler::TexelFetch, + &SPIRVDecompiler::ImageLoad, &SPIRVDecompiler::ImageStore, &SPIRVDecompiler::AtomicImageAdd, &SPIRVDecompiler::AtomicImageMin, diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index d54fb88c99..e611f9f3ba 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/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 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 values; constexpr std::size_t hardcoded_size{4}; @@ -53,32 +88,18 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { values.push_back(GetRegister(instr.gpr0.Value() + i)); } - std::vector 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 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: @@ -102,9 +123,13 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { } }(); - 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; + const auto& image{GetImage(instr.image, type, instr.suatom_d.size)}; + 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: diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index abf2cb1ab5..e5b75783d5 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -149,7 +149,8 @@ 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 @@ -402,6 +403,7 @@ struct MetaTexture { struct MetaImage { const Image& image; std::vector values; + u32 element{}; }; /// Parameters that modify an operation but are not part of any particular operand From 44000971e271e350638611b0265a3fed7bcced2a Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 18 Sep 2019 01:50:40 -0300 Subject: [PATCH 3/3] gl_shader_decompiler: Use uint for images and fix SUATOM In the process remove implementation of SUATOM.MIN and SUATOM.MAX as these require a distinction between U32 and S32. These have to be implemented with imageCompSwap loop. --- src/video_core/engines/shader_bytecode.h | 4 +- .../renderer_opengl/gl_shader_decompiler.cpp | 132 +++++------------- .../renderer_opengl/gl_shader_disk_cache.cpp | 12 +- .../renderer_vulkan/vk_shader_decompiler.cpp | 12 -- src/video_core/shader/decode/image.cpp | 66 ++++----- src/video_core/shader/node.h | 46 +++--- src/video_core/shader/shader_ir.h | 9 +- 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 81dfe33a56..b46fcf03d5 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 { Eq = 2, // allThreadsEqualNV }; -enum class ImageAtomicSize : u64 { +enum class ImageAtomicOperationType : u64 { U32 = 0, S32 = 1, U64 = 2, @@ -1431,7 +1431,7 @@ union Instruction { 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; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 70ce6572bc..0f3a35f741 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: 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 { - 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(image.GetSize())); - return {"", ""}; + return "1D"; } }(); @@ -756,8 +740,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()) { @@ -1225,28 +1213,13 @@ private: } std::string BuildImageValues(Operation operation) { + constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"}; const auto meta{std::get(operation.GetMeta())}; - const auto [constructors, type] = [&]() -> std::pair, 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(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 += ", "; } @@ -1255,29 +1228,6 @@ private: return expr; } - Expression AtomicImage(Operation operation, const char* opname) { - constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("}; - const auto meta{std::get(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(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]; @@ -1810,7 +1760,7 @@ private: const auto meta{std::get(operation.GetMeta())}; return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image), BuildIntegerCoordinates(operation), GetSwizzle(meta.element)), - Type::Float}; + Type::Uint}; } Expression ImageStore(Operation operation) { @@ -1820,31 +1770,14 @@ private: return {}; } - Expression AtomicImageAdd(Operation operation) { - return AtomicImage(operation, "imageAtomicAdd"); - } + template + Expression AtomicImage(Operation operation) { + const auto meta{std::get(operation.GetMeta())}; + ASSERT(meta.values.size() == 1); - 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"); - } - - 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) { @@ -2039,6 +1972,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"; @@ -2178,13 +2117,12 @@ private: &GLSLDecompiler::ImageLoad, &GLSLDecompiler::ImageStore, - &GLSLDecompiler::AtomicImageAdd, - &GLSLDecompiler::AtomicImageMin, - &GLSLDecompiler::AtomicImageMax, - &GLSLDecompiler::AtomicImageAnd, - &GLSLDecompiler::AtomicImageOr, - &GLSLDecompiler::AtomicImageXor, - &GLSLDecompiler::AtomicImageExchange, + + &GLSLDecompiler::AtomicImage, + &GLSLDecompiler::AtomicImage, + &GLSLDecompiler::AtomicImage, + &GLSLDecompiler::AtomicImage, + &GLSLDecompiler::AtomicImage, &GLSLDecompiler::Branch, &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 02b4dd234b..6a7012b544 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 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(offset), static_cast(index), static_cast(type), is_bindless != 0, is_written != 0, - is_read != 0, - is_size_known ? std::make_optional(static_cast(size)) - : std::nullopt); + is_read != 0, is_atomic != 0); } u32 global_memory_count{}; @@ -429,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(image.GetSize()) : 0U; if (!SaveObjectToPrecompiled(static_cast(image.GetOffset())) || !SaveObjectToPrecompiled(static_cast(image.GetIndex())) || !SaveObjectToPrecompiled(static_cast(image.GetType())) || !SaveObjectToPrecompiled(static_cast(image.IsBindless() ? 1 : 0)) || !SaveObjectToPrecompiled(static_cast(image.IsWritten() ? 1 : 0)) || !SaveObjectToPrecompiled(static_cast(image.IsRead() ? 1 : 0)) || - !SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) { + !SaveObjectToPrecompiled(static_cast(image.IsAtomic() ? 1 : 0))) { return false; } } diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 9d31bff438..77fc58f25a 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: return {}; } - Id AtomicImageMin(Operation operation) { - UNIMPLEMENTED(); - return {}; - } - - Id AtomicImageMax(Operation operation) { - UNIMPLEMENTED(); - return {}; - } - Id AtomicImageAnd(Operation operation) { UNIMPLEMENTED(); return {}; @@ -1449,8 +1439,6 @@ private: &SPIRVDecompiler::ImageLoad, &SPIRVDecompiler::ImageStore, &SPIRVDecompiler::AtomicImageAdd, - &SPIRVDecompiler::AtomicImageMin, - &SPIRVDecompiler::AtomicImageMax, &SPIRVDecompiler::AtomicImageAnd, &SPIRVDecompiler::AtomicImageOr, &SPIRVDecompiler::AtomicImageXor, diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index e611f9f3ba..95ec1cdd90 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) { UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0); 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(instr.suatom_d.operation.Value())); - return OperationCode::AtomicImageAdd; + break; } + UNIMPLEMENTED_MSG("Unimplemented operation={} type={}", + static_cast(instr.suatom_d.operation.Value()), + static_cast(instr.suatom_d.operation_type.Value())); + return OperationCode::AtomicImageAdd; }(); Node value = GetRegister(instr.gpr0); const auto type = instr.suatom_d.image_type; - const auto& image{GetImage(instr.image, type, instr.suatom_d.size)}; + auto& image = GetImage(instr.image, type); + image.MarkAtomic(); MetaImage meta{image, {std::move(value)}}; SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type))); @@ -139,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 size) { +Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) { const auto offset{static_cast(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 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(global_code.size()))}; const auto cbuf_key{(static_cast(cbuf_index) << 32) | static_cast(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 size) { +Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) { auto it = used_images.find(offset); if (it == used_images.end()) { return nullptr; @@ -175,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 ℑ } diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index e5b75783d5..338bab17ca 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -149,11 +149,10 @@ enum class OperationCode { TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 TexelFetch, /// (MetaTexture, int[N], int) -> float4 - ImageLoad, /// (MetaImage, int[N] coords) -> void - ImageStore, /// (MetaImage, int[N] coords) -> 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 @@ -295,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 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 size) + Tegra::Shader::ImageType type) : offset{(static_cast(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 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; @@ -319,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 { @@ -347,21 +345,17 @@ public: return is_read; } + constexpr bool IsAtomic() const { + return is_atomic; + } + constexpr std::pair GetBindlessCBuf() const { return {static_cast(offset >> 32), static_cast(offset)}; } - constexpr bool IsSizeKnown() const { - return size.has_value(); - } - - constexpr Tegra::Shader::ImageAtomicSize GetSize() const { - return size.value(); - } - 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: @@ -371,7 +365,7 @@ private: bool is_bindless{}; bool is_written{}; bool is_read{}; - std::optional size{}; + bool is_atomic{}; }; struct GlobalMemoryBase { diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 6aed9bb841..c3e147eeaf 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -276,16 +276,13 @@ private: bool is_shadow); /// Accesses an image. - Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, - std::optional 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 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 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);