mirror of
https://github.com/yuzu-emu/yuzu-android
synced 2024-12-23 11:11:22 -08:00
Merge pull request #2869 from ReinUsesLisp/suld
shader/image: Implement SULD and fix SUATOM
This commit is contained in:
commit
376f1a4432
@ -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"),
|
||||
|
@ -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;
|
||||
|
@ -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{};
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
template <const std::string_view& opname>
|
||||
Expression AtomicImage(Operation operation) {
|
||||
const auto meta{std::get<MetaImage>(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) {
|
||||
@ -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,
|
||||
|
@ -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{};
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {};
|
||||
@ -949,16 +955,6 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Id AtomicImageMin(Operation operation) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
Id AtomicImageMax(Operation operation) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
Id AtomicImageAnd(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,
|
||||
|
@ -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,40 +88,25 @@ 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)
|
||||
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_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::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:
|
||||
@ -95,16 +115,24 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
|
||||
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;
|
||||
}
|
||||
default:
|
||||
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 ℑ
|
||||
}
|
||||
|
||||
|
@ -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 bool IsAtomic() const {
|
||||
return is_atomic;
|
||||
}
|
||||
|
||||
constexpr std::pair<u32, u32> GetBindlessCBuf() const {
|
||||
return {static_cast<u32>(offset >> 32), static_cast<u32>(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:
|
||||
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user