mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-03 16:39:01 -06:00 
			
		
		
		
	gl_shader_decompiler: Keep track of written images and mark them as modified
This commit is contained in:
		@@ -1098,6 +1098,9 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
 | 
			
		||||
    if (!tic.IsBuffer()) {
 | 
			
		||||
        view->ApplySwizzle(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
 | 
			
		||||
    }
 | 
			
		||||
    if (entry.IsWritten()) {
 | 
			
		||||
        view->MarkAsModified(texture_cache.Tick());
 | 
			
		||||
    }
 | 
			
		||||
    state.images[binding] = view->GetTexture();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -389,11 +389,10 @@ public:
 | 
			
		||||
        for (const auto& sampler : ir.GetSamplers()) {
 | 
			
		||||
            entries.samplers.emplace_back(sampler);
 | 
			
		||||
        }
 | 
			
		||||
        for (const auto& image : ir.GetImages()) {
 | 
			
		||||
        for (const auto& [offset, image] : ir.GetImages()) {
 | 
			
		||||
            entries.images.emplace_back(image);
 | 
			
		||||
        }
 | 
			
		||||
        for (const auto& gmem_pair : ir.GetGlobalMemory()) {
 | 
			
		||||
            const auto& [base, usage] = gmem_pair;
 | 
			
		||||
        for (const auto& [base, usage] : ir.GetGlobalMemory()) {
 | 
			
		||||
            entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset,
 | 
			
		||||
                                                       usage.is_read, usage.is_written);
 | 
			
		||||
        }
 | 
			
		||||
@@ -706,7 +705,7 @@ private:
 | 
			
		||||
 | 
			
		||||
    void DeclareImages() {
 | 
			
		||||
        const auto& images{ir.GetImages()};
 | 
			
		||||
        for (const auto& image : images) {
 | 
			
		||||
        for (const auto& [offset, image] : images) {
 | 
			
		||||
            const std::string image_type = [&]() {
 | 
			
		||||
                switch (image.GetType()) {
 | 
			
		||||
                case Tegra::Shader::ImageType::Texture1D:
 | 
			
		||||
@@ -726,9 +725,16 @@ private:
 | 
			
		||||
                    return "image1D";
 | 
			
		||||
                }
 | 
			
		||||
            }();
 | 
			
		||||
            code.AddLine("layout (binding = IMAGE_BINDING_{}) coherent volatile writeonly uniform "
 | 
			
		||||
            std::string qualifier = "coherent volatile";
 | 
			
		||||
            if (image.IsRead() && !image.IsWritten()) {
 | 
			
		||||
                qualifier += " readonly";
 | 
			
		||||
            } else if (image.IsWritten() && !image.IsRead()) {
 | 
			
		||||
                qualifier += " writeonly";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform "
 | 
			
		||||
                         "{} {};",
 | 
			
		||||
                         image.GetIndex(), image_type, GetImage(image));
 | 
			
		||||
                         image.GetIndex(), qualifier, image_type, GetImage(image));
 | 
			
		||||
        }
 | 
			
		||||
        if (!images.empty()) {
 | 
			
		||||
            code.AddNewLine();
 | 
			
		||||
 
 | 
			
		||||
@@ -341,13 +341,16 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
 | 
			
		||||
        u64 index{};
 | 
			
		||||
        u32 type{};
 | 
			
		||||
        u8 is_bindless{};
 | 
			
		||||
        u8 is_read{};
 | 
			
		||||
        u8 is_written{};
 | 
			
		||||
        if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
 | 
			
		||||
            !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless)) {
 | 
			
		||||
            !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
 | 
			
		||||
            !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) {
 | 
			
		||||
            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);
 | 
			
		||||
        entry.entries.images.emplace_back(static_cast<u64>(offset), static_cast<std::size_t>(index),
 | 
			
		||||
                                          static_cast<Tegra::Shader::ImageType>(type),
 | 
			
		||||
                                          is_bindless != 0, is_written != 0, is_read != 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 global_memory_count{};
 | 
			
		||||
@@ -429,7 +432,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
 | 
			
		||||
        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.IsBindless() ? 1 : 0)) ||
 | 
			
		||||
            !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
 | 
			
		||||
            !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0))) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -78,6 +78,17 @@ public:
 | 
			
		||||
    /// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER
 | 
			
		||||
    void Attach(GLenum attachment, GLenum target) const;
 | 
			
		||||
 | 
			
		||||
    void ApplySwizzle(Tegra::Texture::SwizzleSource x_source,
 | 
			
		||||
                      Tegra::Texture::SwizzleSource y_source,
 | 
			
		||||
                      Tegra::Texture::SwizzleSource z_source,
 | 
			
		||||
                      Tegra::Texture::SwizzleSource w_source);
 | 
			
		||||
 | 
			
		||||
    void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
 | 
			
		||||
 | 
			
		||||
    void MarkAsModified(u64 tick) {
 | 
			
		||||
        surface.MarkAsModified(true, tick);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    GLuint GetTexture() const {
 | 
			
		||||
        if (is_proxy) {
 | 
			
		||||
            return surface.GetTexture();
 | 
			
		||||
@@ -89,13 +100,6 @@ public:
 | 
			
		||||
        return surface.GetSurfaceParams();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ApplySwizzle(Tegra::Texture::SwizzleSource x_source,
 | 
			
		||||
                      Tegra::Texture::SwizzleSource y_source,
 | 
			
		||||
                      Tegra::Texture::SwizzleSource z_source,
 | 
			
		||||
                      Tegra::Texture::SwizzleSource w_source);
 | 
			
		||||
 | 
			
		||||
    void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
 | 
			
		||||
                      Tegra::Texture::SwizzleSource y_source,
 | 
			
		||||
@@ -111,8 +115,8 @@ private:
 | 
			
		||||
    GLenum target{};
 | 
			
		||||
 | 
			
		||||
    OGLTextureView texture_view;
 | 
			
		||||
    u32 swizzle;
 | 
			
		||||
    bool is_proxy;
 | 
			
		||||
    u32 swizzle{};
 | 
			
		||||
    bool is_proxy{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class TextureCacheOpenGL final : public TextureCacheBase {
 | 
			
		||||
 
 | 
			
		||||
@@ -61,56 +61,54 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const auto type{instr.sust.image_type};
 | 
			
		||||
        const auto& image{instr.sust.is_immediate ? GetImage(instr.image, type)
 | 
			
		||||
                                                  : GetBindlessImage(instr.gpr39, type)};
 | 
			
		||||
        auto& image{instr.sust.is_immediate ? GetImage(instr.image, type)
 | 
			
		||||
                                            : GetBindlessImage(instr.gpr39, type)};
 | 
			
		||||
        image.MarkWrite();
 | 
			
		||||
 | 
			
		||||
        MetaImage meta{image, values};
 | 
			
		||||
        const Node store{Operation(OperationCode::ImageStore, meta, std::move(coords))};
 | 
			
		||||
        bb.push_back(store);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName());
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unhandled image instruction: {}", opcode->get().GetName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return pc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
 | 
			
		||||
    const auto offset{static_cast<std::size_t>(image.index.Value())};
 | 
			
		||||
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
 | 
			
		||||
    const auto offset{static_cast<u64>(image.index.Value())};
 | 
			
		||||
 | 
			
		||||
    // If this image has already been used, return the existing mapping.
 | 
			
		||||
    const auto itr{std::find_if(used_images.begin(), used_images.end(),
 | 
			
		||||
                                [=](const Image& entry) { return entry.GetOffset() == offset; })};
 | 
			
		||||
    if (itr != used_images.end()) {
 | 
			
		||||
        ASSERT(itr->GetType() == type);
 | 
			
		||||
        return *itr;
 | 
			
		||||
    const auto it = used_images.find(offset);
 | 
			
		||||
    if (it != used_images.end()) {
 | 
			
		||||
        ASSERT(it->second.GetType() == type);
 | 
			
		||||
        return it->second;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Otherwise create a new mapping for this image.
 | 
			
		||||
    const std::size_t next_index{used_images.size()};
 | 
			
		||||
    const Image entry{offset, next_index, type};
 | 
			
		||||
    return *used_images.emplace(entry).first;
 | 
			
		||||
    return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg,
 | 
			
		||||
                                        Tegra::Shader::ImageType type) {
 | 
			
		||||
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 this image has already been used, return the existing mapping.
 | 
			
		||||
    const auto itr{std::find_if(used_images.begin(), used_images.end(),
 | 
			
		||||
                                [=](const Image& entry) { return entry.GetOffset() == cbuf_key; })};
 | 
			
		||||
    if (itr != used_images.end()) {
 | 
			
		||||
        ASSERT(itr->GetType() == type);
 | 
			
		||||
        return *itr;
 | 
			
		||||
    const auto it = used_images.find(cbuf_key);
 | 
			
		||||
    if (it != used_images.end()) {
 | 
			
		||||
        ASSERT(it->second.GetType() == type);
 | 
			
		||||
        return it->second;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Otherwise create a new mapping for this image.
 | 
			
		||||
    const std::size_t next_index{used_images.size()};
 | 
			
		||||
    const Image entry{cbuf_index, cbuf_offset, next_index, type};
 | 
			
		||||
    return *used_images.emplace(entry).first;
 | 
			
		||||
    return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
 | 
			
		||||
        .first->second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace VideoCommon::Shader
 | 
			
		||||
 
 | 
			
		||||
@@ -273,50 +273,64 @@ private:
 | 
			
		||||
    bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Image {
 | 
			
		||||
class Image final {
 | 
			
		||||
public:
 | 
			
		||||
    explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type)
 | 
			
		||||
    constexpr explicit Image(u64 offset, std::size_t index, Tegra::Shader::ImageType type)
 | 
			
		||||
        : offset{offset}, index{index}, type{type}, is_bindless{false} {}
 | 
			
		||||
 | 
			
		||||
    explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
 | 
			
		||||
                   Tegra::Shader::ImageType type)
 | 
			
		||||
    constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
 | 
			
		||||
                             Tegra::Shader::ImageType type)
 | 
			
		||||
        : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
 | 
			
		||||
          is_bindless{true} {}
 | 
			
		||||
 | 
			
		||||
    explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
 | 
			
		||||
                   bool is_bindless)
 | 
			
		||||
        : offset{offset}, index{index}, type{type}, is_bindless{is_bindless} {}
 | 
			
		||||
    constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
 | 
			
		||||
                             bool is_bindless, bool is_written, bool is_read)
 | 
			
		||||
        : offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
 | 
			
		||||
          is_written{is_written}, is_read{is_read} {}
 | 
			
		||||
 | 
			
		||||
    std::size_t GetOffset() const {
 | 
			
		||||
    void MarkRead() {
 | 
			
		||||
        is_read = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void MarkWrite() {
 | 
			
		||||
        is_written = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr std::size_t GetOffset() const {
 | 
			
		||||
        return offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::size_t GetIndex() const {
 | 
			
		||||
    constexpr std::size_t GetIndex() const {
 | 
			
		||||
        return index;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Tegra::Shader::ImageType GetType() const {
 | 
			
		||||
    constexpr Tegra::Shader::ImageType GetType() const {
 | 
			
		||||
        return type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsBindless() const {
 | 
			
		||||
    constexpr bool IsBindless() const {
 | 
			
		||||
        return is_bindless;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::pair<u32, u32> GetBindlessCBuf() const {
 | 
			
		||||
    constexpr bool IsRead() const {
 | 
			
		||||
        return is_read;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr bool IsWritten() const {
 | 
			
		||||
        return is_written;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr std::pair<u32, u32> GetBindlessCBuf() const {
 | 
			
		||||
        return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool operator<(const Image& rhs) const {
 | 
			
		||||
        return std::tie(offset, index, type, is_bindless) <
 | 
			
		||||
               std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::size_t offset{};
 | 
			
		||||
    u64 offset{};
 | 
			
		||||
    std::size_t index{};
 | 
			
		||||
    Tegra::Shader::ImageType type{};
 | 
			
		||||
    bool is_bindless{};
 | 
			
		||||
    bool is_read{};
 | 
			
		||||
    bool is_written{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GlobalMemoryBase {
 | 
			
		||||
 
 | 
			
		||||
@@ -95,7 +95,7 @@ public:
 | 
			
		||||
        return used_samplers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::set<Image>& GetImages() const {
 | 
			
		||||
    const std::map<u64, Image>& GetImages() const {
 | 
			
		||||
        return used_images;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -272,10 +272,10 @@ private:
 | 
			
		||||
                                      bool is_shadow);
 | 
			
		||||
 | 
			
		||||
    /// Accesses an image.
 | 
			
		||||
    const Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
 | 
			
		||||
    Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
 | 
			
		||||
 | 
			
		||||
    /// Access a bindless image sampler.
 | 
			
		||||
    const Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
 | 
			
		||||
    Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
 | 
			
		||||
 | 
			
		||||
    /// Extracts a sequence of bits from a node
 | 
			
		||||
    Node BitfieldExtract(Node value, u32 offset, u32 bits);
 | 
			
		||||
@@ -356,7 +356,7 @@ private:
 | 
			
		||||
    std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
 | 
			
		||||
    std::map<u32, ConstBuffer> used_cbufs;
 | 
			
		||||
    std::set<Sampler> used_samplers;
 | 
			
		||||
    std::set<Image> used_images;
 | 
			
		||||
    std::map<u64, Image> used_images;
 | 
			
		||||
    std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
 | 
			
		||||
    std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
 | 
			
		||||
    bool uses_layer{};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user