summaryrefslogtreecommitdiffstats
path: root/src/video_core/shader_cache.h
blob: a76896620a624670669edd366aee2fda5f5c9e0c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <memory>
#include <mutex>
#include <span>
#include <unordered_map>
#include <utility>
#include <vector>

#include "common/common_types.h"
#include "common/polyfill_ranges.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/shader_environment.h"

namespace Tegra {
class MemoryManager;
}

namespace Tegra::Control {
struct ChannelState;
}

namespace VideoCommon {

class GenericEnvironment;

struct ShaderInfo {
    u64 unique_hash{};
    size_t size_bytes{};
};

class ShaderCache : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
    static constexpr u64 YUZU_PAGEBITS = 14;
    static constexpr u64 YUZU_PAGESIZE = u64(1) << YUZU_PAGEBITS;

    static constexpr size_t NUM_PROGRAMS = 6;

    struct Entry {
        VAddr addr_start;
        VAddr addr_end;
        ShaderInfo* data;

        bool is_memory_marked = true;

        bool Overlaps(VAddr start, VAddr end) const noexcept {
            return start < addr_end && addr_start < end;
        }
    };

public:
    /// @brief Removes shaders inside a given region
    /// @note Checks for ranges
    /// @param addr Start address of the invalidation
    /// @param size Number of bytes of the invalidation
    void InvalidateRegion(VAddr addr, size_t size);

    /// @brief Unmarks a memory region as cached and marks it for removal
    /// @param addr Start address of the CPU write operation
    /// @param size Number of bytes of the CPU write operation
    void OnCacheInvalidation(VAddr addr, size_t size);

    /// @brief Flushes delayed removal operations
    void SyncGuestHost();

protected:
    struct GraphicsEnvironments {
        std::array<GraphicsEnvironment, NUM_PROGRAMS> envs;
        std::array<Shader::Environment*, NUM_PROGRAMS> env_ptrs{};

        std::span<Shader::Environment* const> Span() const noexcept {
            return std::span(env_ptrs.begin(), std::ranges::find(env_ptrs, nullptr));
        }
    };

    explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_);

    /// @brief Update the hashes and information of shader stages
    /// @param unique_hashes Shader hashes to store into when a stage is enabled
    /// @return True no success, false on error
    bool RefreshStages(std::array<u64, NUM_PROGRAMS>& unique_hashes);

    /// @brief Returns information about the current compute shader
    /// @return Pointer to a valid shader, nullptr on error
    const ShaderInfo* ComputeShader();

    /// @brief Collect the current graphics environments
    void GetGraphicsEnvironments(GraphicsEnvironments& result,
                                 const std::array<u64, NUM_PROGRAMS>& unique_hashes);

    std::array<const ShaderInfo*, NUM_PROGRAMS> shader_infos{};
    bool last_shaders_valid = false;

private:
    /// @brief Tries to obtain a cached shader starting in a given address
    /// @note Doesn't check for ranges, the given address has to be the start of the shader
    /// @param addr Start address of the shader, this doesn't cache for region
    /// @return Pointer to a valid shader, nullptr when nothing is found
    ShaderInfo* TryGet(VAddr addr) const;

    /// @brief Register in the cache a given entry
    /// @param data Shader to store in the cache
    /// @param addr Start address of the shader that will be registered
    /// @param size Size in bytes of the shader
    void Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t size);

    /// @brief Invalidate pages in a given region
    /// @pre invalidation_mutex is locked
    void InvalidatePagesInRegion(VAddr addr, size_t size);

    /// @brief Remove shaders marked for deletion
    /// @pre invalidation_mutex is locked
    void RemovePendingShaders();

    /// @brief Invalidates entries in a given range for the passed page
    /// @param entries         Vector of entries in the page, it will be modified on overlaps
    /// @param addr            Start address of the invalidation
    /// @param addr_end        Non-inclusive end address of the invalidation
    /// @pre invalidation_mutex is locked
    void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end);

    /// @brief Removes all references to an entry in the invalidation cache
    /// @param entry Entry to remove from the invalidation cache
    /// @pre invalidation_mutex is locked
    void RemoveEntryFromInvalidationCache(const Entry* entry);

    /// @brief Unmarks an entry from the rasterizer cache
    /// @param entry Entry to unmark from memory
    void UnmarkMemory(Entry* entry);

    /// @brief Removes a vector of shaders from a list
    /// @param removed_shaders Shaders to be removed from the storage
    /// @pre invalidation_mutex is locked
    /// @pre lookup_mutex is locked
    void RemoveShadersFromStorage(std::span<ShaderInfo*> removed_shaders);

    /// @brief Creates a new entry in the lookup cache and returns its pointer
    /// @pre lookup_mutex is locked
    Entry* NewEntry(VAddr addr, VAddr addr_end, ShaderInfo* data);

    /// @brief Create a new shader entry and register it
    const ShaderInfo* MakeShaderInfo(GenericEnvironment& env, VAddr cpu_addr);

    VideoCore::RasterizerInterface& rasterizer;

    mutable std::mutex lookup_mutex;
    std::mutex invalidation_mutex;

    std::unordered_map<u64, std::unique_ptr<Entry>> lookup_cache;
    std::unordered_map<u64, std::vector<Entry*>> invalidation_cache;
    std::vector<std::unique_ptr<ShaderInfo>> storage;
    std::vector<Entry*> marked_for_removal;
};

} // namespace VideoCommon