summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/environment.h11
-rw-r--r--src/shader_recompiler/file_environment.cpp4
-rw-r--r--src/shader_recompiler/file_environment.h4
-rw-r--r--src/shader_recompiler/stage.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp391
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h34
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.h4
8 files changed, 347 insertions, 106 deletions
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 1fcaa56dd..6dec4b255 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -3,8 +3,8 @@
3#include <array> 3#include <array>
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "shader_recompiler/stage.h"
7#include "shader_recompiler/program_header.h" 6#include "shader_recompiler/program_header.h"
7#include "shader_recompiler/stage.h"
8 8
9namespace Shader { 9namespace Shader {
10 10
@@ -14,9 +14,9 @@ public:
14 14
15 [[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0; 15 [[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0;
16 16
17 [[nodiscard]] virtual u32 TextureBoundBuffer() = 0; 17 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
18 18
19 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() = 0; 19 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
20 20
21 [[nodiscard]] const ProgramHeader& SPH() const noexcept { 21 [[nodiscard]] const ProgramHeader& SPH() const noexcept {
22 return sph; 22 return sph;
@@ -26,9 +26,14 @@ public:
26 return stage; 26 return stage;
27 } 27 }
28 28
29 [[nodiscard]] u32 StartAddress() const noexcept {
30 return start_address;
31 }
32
29protected: 33protected:
30 ProgramHeader sph{}; 34 ProgramHeader sph{};
31 Stage stage{}; 35 Stage stage{};
36 u32 start_address{};
32}; 37};
33 38
34} // namespace Shader 39} // namespace Shader
diff --git a/src/shader_recompiler/file_environment.cpp b/src/shader_recompiler/file_environment.cpp
index 21700c72b..f2104f444 100644
--- a/src/shader_recompiler/file_environment.cpp
+++ b/src/shader_recompiler/file_environment.cpp
@@ -39,11 +39,11 @@ u64 FileEnvironment::ReadInstruction(u32 offset) {
39 return data[offset / 8]; 39 return data[offset / 8];
40} 40}
41 41
42u32 FileEnvironment::TextureBoundBuffer() { 42u32 FileEnvironment::TextureBoundBuffer() const {
43 throw NotImplementedException("Texture bound buffer serialization"); 43 throw NotImplementedException("Texture bound buffer serialization");
44} 44}
45 45
46std::array<u32, 3> FileEnvironment::WorkgroupSize() { 46std::array<u32, 3> FileEnvironment::WorkgroupSize() const {
47 return {1, 1, 1}; 47 return {1, 1, 1};
48} 48}
49 49
diff --git a/src/shader_recompiler/file_environment.h b/src/shader_recompiler/file_environment.h
index 62302bc8e..17640a622 100644
--- a/src/shader_recompiler/file_environment.h
+++ b/src/shader_recompiler/file_environment.h
@@ -14,9 +14,9 @@ public:
14 14
15 u64 ReadInstruction(u32 offset) override; 15 u64 ReadInstruction(u32 offset) override;
16 16
17 u32 TextureBoundBuffer() override; 17 u32 TextureBoundBuffer() const override;
18 18
19 std::array<u32, 3> WorkgroupSize() override; 19 std::array<u32, 3> WorkgroupSize() const override;
20 20
21private: 21private:
22 std::vector<u64> data; 22 std::vector<u64> data;
diff --git a/src/shader_recompiler/stage.h b/src/shader_recompiler/stage.h
index fc6ce6043..7d4f2c0bb 100644
--- a/src/shader_recompiler/stage.h
+++ b/src/shader_recompiler/stage.h
@@ -4,9 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
8
7namespace Shader { 9namespace Shader {
8 10
9enum class Stage { 11enum class Stage : u32 {
10 Compute, 12 Compute,
11 VertexA, 13 VertexA,
12 VertexB, 14 VertexB,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 75f7c1e61..41fc9588f 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -4,12 +4,15 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstddef> 6#include <cstddef>
7#include <fstream>
7#include <memory> 8#include <memory>
8#include <vector> 9#include <vector>
9 10
10#include "common/bit_cast.h" 11#include "common/bit_cast.h"
11#include "common/cityhash.h" 12#include "common/cityhash.h"
13#include "common/file_util.h"
12#include "common/microprofile.h" 14#include "common/microprofile.h"
15#include "common/thread_worker.h"
13#include "core/core.h" 16#include "core/core.h"
14#include "core/memory.h" 17#include "core/memory.h"
15#include "shader_recompiler/backend/spirv/emit_spirv.h" 18#include "shader_recompiler/backend/spirv/emit_spirv.h"
@@ -37,18 +40,23 @@
37namespace Vulkan { 40namespace Vulkan {
38MICROPROFILE_DECLARE(Vulkan_PipelineCache); 41MICROPROFILE_DECLARE(Vulkan_PipelineCache);
39 42
40namespace { 43template <typename Container>
41using Shader::Backend::SPIRV::EmitSPIRV; 44auto MakeSpan(Container& container) {
45 return std::span(container.data(), container.size());
46}
42 47
43class GenericEnvironment : public Shader::Environment { 48class GenericEnvironment : public Shader::Environment {
44public: 49public:
45 explicit GenericEnvironment() = default; 50 explicit GenericEnvironment() = default;
46 explicit GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_) 51 explicit GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
47 : gpu_memory{&gpu_memory_}, program_base{program_base_} {} 52 u32 start_address_)
53 : gpu_memory{&gpu_memory_}, program_base{program_base_} {
54 start_address = start_address_;
55 }
48 56
49 ~GenericEnvironment() override = default; 57 ~GenericEnvironment() override = default;
50 58
51 std::optional<u128> Analyze(u32 start_address) { 59 std::optional<u128> Analyze() {
52 const std::optional<u64> size{TryFindSize(start_address)}; 60 const std::optional<u64> size{TryFindSize(start_address)};
53 if (!size) { 61 if (!size) {
54 return std::nullopt; 62 return std::nullopt;
@@ -66,11 +74,15 @@ public:
66 return read_highest - read_lowest + INST_SIZE; 74 return read_highest - read_lowest + INST_SIZE;
67 } 75 }
68 76
77 [[nodiscard]] bool CanBeSerialized() const noexcept {
78 return has_unbound_instructions;
79 }
80
69 [[nodiscard]] u128 CalculateHash() const { 81 [[nodiscard]] u128 CalculateHash() const {
70 const size_t size{ReadSize()}; 82 const size_t size{ReadSize()};
71 auto data = std::make_unique<u64[]>(size); 83 const auto data{std::make_unique<char[]>(size)};
72 gpu_memory->ReadBlock(program_base + read_lowest, data.get(), size); 84 gpu_memory->ReadBlock(program_base + read_lowest, data.get(), size);
73 return Common::CityHash128(reinterpret_cast<const char*>(data.get()), size); 85 return Common::CityHash128(data.get(), size);
74 } 86 }
75 87
76 u64 ReadInstruction(u32 address) final { 88 u64 ReadInstruction(u32 address) final {
@@ -80,9 +92,32 @@ public:
80 if (address >= cached_lowest && address < cached_highest) { 92 if (address >= cached_lowest && address < cached_highest) {
81 return code[address / INST_SIZE]; 93 return code[address / INST_SIZE];
82 } 94 }
95 has_unbound_instructions = true;
83 return gpu_memory->Read<u64>(program_base + address); 96 return gpu_memory->Read<u64>(program_base + address);
84 } 97 }
85 98
99 void Serialize(std::ofstream& file) const {
100 const u64 code_size{static_cast<u64>(ReadSize())};
101 const auto data{std::make_unique<char[]>(code_size)};
102 gpu_memory->ReadBlock(program_base + read_lowest, data.get(), code_size);
103
104 const u32 texture_bound{TextureBoundBuffer()};
105
106 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size))
107 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound))
108 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
109 .write(reinterpret_cast<const char*>(&read_lowest), sizeof(read_lowest))
110 .write(reinterpret_cast<const char*>(&read_highest), sizeof(read_highest))
111 .write(reinterpret_cast<const char*>(&stage), sizeof(stage))
112 .write(data.get(), code_size);
113 if (stage == Shader::Stage::Compute) {
114 const std::array<u32, 3> workgroup_size{WorkgroupSize()};
115 file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size));
116 } else {
117 file.write(reinterpret_cast<const char*>(&sph), sizeof(sph));
118 }
119 }
120
86protected: 121protected:
87 static constexpr size_t INST_SIZE = sizeof(u64); 122 static constexpr size_t INST_SIZE = sizeof(u64);
88 123
@@ -122,16 +157,22 @@ protected:
122 157
123 u32 cached_lowest = std::numeric_limits<u32>::max(); 158 u32 cached_lowest = std::numeric_limits<u32>::max();
124 u32 cached_highest = 0; 159 u32 cached_highest = 0;
160
161 bool has_unbound_instructions = false;
125}; 162};
126 163
164namespace {
165using Shader::Backend::SPIRV::EmitSPIRV;
166using Shader::Maxwell::TranslateProgram;
167
127class GraphicsEnvironment final : public GenericEnvironment { 168class GraphicsEnvironment final : public GenericEnvironment {
128public: 169public:
129 explicit GraphicsEnvironment() = default; 170 explicit GraphicsEnvironment() = default;
130 explicit GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, 171 explicit GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
131 Tegra::MemoryManager& gpu_memory_, Maxwell::ShaderProgram program, 172 Tegra::MemoryManager& gpu_memory_, Maxwell::ShaderProgram program,
132 GPUVAddr program_base_, u32 start_offset) 173 GPUVAddr program_base_, u32 start_address_)
133 : GenericEnvironment{gpu_memory_, program_base_}, maxwell3d{&maxwell3d_} { 174 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
134 gpu_memory->ReadBlock(program_base + start_offset, &sph, sizeof(sph)); 175 gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
135 switch (program) { 176 switch (program) {
136 case Maxwell::ShaderProgram::VertexA: 177 case Maxwell::ShaderProgram::VertexA:
137 stage = Shader::Stage::VertexA; 178 stage = Shader::Stage::VertexA;
@@ -158,11 +199,11 @@ public:
158 199
159 ~GraphicsEnvironment() override = default; 200 ~GraphicsEnvironment() override = default;
160 201
161 u32 TextureBoundBuffer() override { 202 u32 TextureBoundBuffer() const override {
162 return maxwell3d->regs.tex_cb_index; 203 return maxwell3d->regs.tex_cb_index;
163 } 204 }
164 205
165 std::array<u32, 3> WorkgroupSize() override { 206 std::array<u32, 3> WorkgroupSize() const override {
166 throw Shader::LogicError("Requesting workgroup size in a graphics stage"); 207 throw Shader::LogicError("Requesting workgroup size in a graphics stage");
167 } 208 }
168 209
@@ -174,18 +215,20 @@ class ComputeEnvironment final : public GenericEnvironment {
174public: 215public:
175 explicit ComputeEnvironment() = default; 216 explicit ComputeEnvironment() = default;
176 explicit ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, 217 explicit ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_,
177 Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_) 218 Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
178 : GenericEnvironment{gpu_memory_, program_base_}, kepler_compute{&kepler_compute_} { 219 u32 start_address_)
220 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, kepler_compute{
221 &kepler_compute_} {
179 stage = Shader::Stage::Compute; 222 stage = Shader::Stage::Compute;
180 } 223 }
181 224
182 ~ComputeEnvironment() override = default; 225 ~ComputeEnvironment() override = default;
183 226
184 u32 TextureBoundBuffer() override { 227 u32 TextureBoundBuffer() const override {
185 return kepler_compute->regs.tex_cb_index; 228 return kepler_compute->regs.tex_cb_index;
186 } 229 }
187 230
188 std::array<u32, 3> WorkgroupSize() override { 231 std::array<u32, 3> WorkgroupSize() const override {
189 const auto& qmd{kepler_compute->launch_description}; 232 const auto& qmd{kepler_compute->launch_description};
190 return {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z}; 233 return {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z};
191 } 234 }
@@ -193,8 +236,174 @@ public:
193private: 236private:
194 Tegra::Engines::KeplerCompute* kepler_compute{}; 237 Tegra::Engines::KeplerCompute* kepler_compute{};
195}; 238};
239
240void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
241 std::ofstream& file) {
242 if (!std::ranges::all_of(envs, &GenericEnvironment::CanBeSerialized)) {
243 return;
244 }
245 const u32 num_envs{static_cast<u32>(envs.size())};
246 file.write(reinterpret_cast<const char*>(&num_envs), sizeof(num_envs));
247 for (const GenericEnvironment* const env : envs) {
248 env->Serialize(file);
249 }
250 file.write(key.data(), key.size_bytes());
251}
252
253template <typename Key, typename Envs>
254void SerializePipeline(const Key& key, const Envs& envs, const std::string& filename) {
255 try {
256 std::ofstream file;
257 file.exceptions(std::ifstream::failbit);
258 Common::FS::OpenFStream(file, filename, std::ios::binary | std::ios::app);
259 if (!file.is_open()) {
260 LOG_ERROR(Common_Filesystem, "Failed to open pipeline cache file {}", filename);
261 return;
262 }
263 if (file.tellp() == 0) {
264 // Write header...
265 }
266 const std::span key_span(reinterpret_cast<const char*>(&key), sizeof(key));
267 SerializePipeline(key_span, MakeSpan(envs), file);
268
269 } catch (const std::ios_base::failure& e) {
270 LOG_ERROR(Common_Filesystem, "{}", e.what());
271 if (!Common::FS::Delete(filename)) {
272 LOG_ERROR(Common_Filesystem, "Failed to delete pipeline cache file {}", filename);
273 }
274 }
275}
276
277class FileEnvironment final : public Shader::Environment {
278public:
279 void Deserialize(std::ifstream& file) {
280 u64 code_size{};
281 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
282 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound))
283 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
284 .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest))
285 .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest))
286 .read(reinterpret_cast<char*>(&stage), sizeof(stage));
287 code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64)));
288 file.read(reinterpret_cast<char*>(code.get()), code_size);
289 if (stage == Shader::Stage::Compute) {
290 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size));
291 } else {
292 file.read(reinterpret_cast<char*>(&sph), sizeof(sph));
293 }
294 }
295
296 u64 ReadInstruction(u32 address) override {
297 if (address < read_lowest || address > read_highest) {
298 throw Shader::LogicError("Out of bounds address {}", address);
299 }
300 return code[(address - read_lowest) / sizeof(u64)];
301 }
302
303 u32 TextureBoundBuffer() const override {
304 return texture_bound;
305 }
306
307 std::array<u32, 3> WorkgroupSize() const override {
308 return workgroup_size;
309 }
310
311private:
312 std::unique_ptr<u64[]> code;
313 std::array<u32, 3> workgroup_size{};
314 u32 texture_bound{};
315 u32 read_lowest{};
316 u32 read_highest{};
317};
196} // Anonymous namespace 318} // Anonymous namespace
197 319
320void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
321 const VideoCore::DiskResourceLoadCallback& callback) {
322 if (title_id == 0) {
323 return;
324 }
325 std::string shader_dir{Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)};
326 std::string base_dir{shader_dir + "/vulkan"};
327 std::string transferable_dir{base_dir + "/transferable"};
328 std::string precompiled_dir{base_dir + "/precompiled"};
329 if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir) ||
330 !Common::FS::CreateDir(transferable_dir) || !Common::FS::CreateDir(precompiled_dir)) {
331 LOG_ERROR(Common_Filesystem, "Failed to create pipeline cache directories");
332 return;
333 }
334 pipeline_cache_filename = fmt::format("{}/{:016x}.bin", transferable_dir, title_id);
335
336 Common::ThreadWorker worker(11, "PipelineBuilder");
337 std::mutex cache_mutex;
338 struct {
339 size_t total{0};
340 size_t built{0};
341 bool has_loaded{false};
342 } state;
343
344 std::ifstream file;
345 Common::FS::OpenFStream(file, pipeline_cache_filename, std::ios::binary | std::ios::ate);
346 if (!file.is_open()) {
347 return;
348 }
349 file.exceptions(std::ifstream::failbit);
350 const auto end{file.tellg()};
351 file.seekg(0, std::ios::beg);
352 // Read header...
353
354 while (file.tellg() != end) {
355 if (stop_loading) {
356 return;
357 }
358 u32 num_envs{};
359 file.read(reinterpret_cast<char*>(&num_envs), sizeof(num_envs));
360 auto envs{std::make_shared<std::vector<FileEnvironment>>(num_envs)};
361 for (FileEnvironment& env : *envs) {
362 env.Deserialize(file);
363 }
364 if (envs->front().ShaderStage() == Shader::Stage::Compute) {
365 ComputePipelineCacheKey key;
366 file.read(reinterpret_cast<char*>(&key), sizeof(key));
367
368 worker.QueueWork([this, key, envs, &cache_mutex, &state, &callback] {
369 ShaderPools pools;
370 ComputePipeline pipeline{CreateComputePipeline(pools, key, envs->front())};
371
372 std::lock_guard lock{cache_mutex};
373 compute_cache.emplace(key, std::move(pipeline));
374 if (state.has_loaded) {
375 callback(VideoCore::LoadCallbackStage::Build, ++state.built, state.total);
376 }
377 });
378 } else {
379 GraphicsPipelineCacheKey key;
380 file.read(reinterpret_cast<char*>(&key), sizeof(key));
381
382 worker.QueueWork([this, key, envs, &cache_mutex, &state, &callback] {
383 ShaderPools pools;
384 boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
385 for (auto& env : *envs) {
386 env_ptrs.push_back(&env);
387 }
388 GraphicsPipeline pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs))};
389
390 std::lock_guard lock{cache_mutex};
391 graphics_cache.emplace(key, std::move(pipeline));
392 if (state.has_loaded) {
393 callback(VideoCore::LoadCallbackStage::Build, ++state.built, state.total);
394 }
395 });
396 }
397 ++state.total;
398 }
399 {
400 std::lock_guard lock{cache_mutex};
401 callback(VideoCore::LoadCallbackStage::Build, 0, state.total);
402 state.has_loaded = true;
403 }
404 worker.WaitForRequests();
405}
406
198size_t ComputePipelineCacheKey::Hash() const noexcept { 407size_t ComputePipelineCacheKey::Hash() const noexcept {
199 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); 408 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
200 return static_cast<size_t>(hash); 409 return static_cast<size_t>(hash);
@@ -279,17 +488,22 @@ ComputePipeline* PipelineCache::CurrentComputePipeline() {
279 if (!cpu_shader_addr) { 488 if (!cpu_shader_addr) {
280 return nullptr; 489 return nullptr;
281 } 490 }
282 ShaderInfo* const shader{TryGet(*cpu_shader_addr)}; 491 const ShaderInfo* shader{TryGet(*cpu_shader_addr)};
283 if (!shader) { 492 if (!shader) {
284 return CreateComputePipelineWithoutShader(*cpu_shader_addr); 493 ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start};
494 shader = MakeShaderInfo(env, *cpu_shader_addr);
285 } 495 }
286 const ComputePipelineCacheKey key{MakeComputePipelineKey(shader->unique_hash)}; 496 const ComputePipelineCacheKey key{
497 .unique_hash = shader->unique_hash,
498 .shared_memory_size = qmd.shared_alloc,
499 .workgroup_size{qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z},
500 };
287 const auto [pair, is_new]{compute_cache.try_emplace(key)}; 501 const auto [pair, is_new]{compute_cache.try_emplace(key)};
288 auto& pipeline{pair->second}; 502 auto& pipeline{pair->second};
289 if (!is_new) { 503 if (!is_new) {
290 return &pipeline; 504 return &pipeline;
291 } 505 }
292 pipeline = CreateComputePipeline(shader); 506 pipeline = CreateComputePipeline(key, shader);
293 return &pipeline; 507 return &pipeline;
294} 508}
295 509
@@ -310,26 +524,25 @@ bool PipelineCache::RefreshStages() {
310 } 524 }
311 const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)}; 525 const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)};
312 if (!shader_info) { 526 if (!shader_info) {
313 const u32 offset{shader_config.offset}; 527 const u32 start_address{shader_config.offset};
314 shader_info = MakeShaderInfo(program, base_addr, offset, *cpu_shader_addr); 528 GraphicsEnvironment env{maxwell3d, gpu_memory, program, base_addr, start_address};
529 shader_info = MakeShaderInfo(env, *cpu_shader_addr);
315 } 530 }
316 graphics_key.unique_hashes[index] = shader_info->unique_hash; 531 graphics_key.unique_hashes[index] = shader_info->unique_hash;
317 } 532 }
318 return true; 533 return true;
319} 534}
320 535
321const ShaderInfo* PipelineCache::MakeShaderInfo(Maxwell::ShaderProgram program, GPUVAddr base_addr, 536const ShaderInfo* PipelineCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu_addr) {
322 u32 start_address, VAddr cpu_addr) {
323 GraphicsEnvironment env{maxwell3d, gpu_memory, program, base_addr, start_address};
324 auto info = std::make_unique<ShaderInfo>(); 537 auto info = std::make_unique<ShaderInfo>();
325 if (const std::optional<u128> cached_hash{env.Analyze(start_address)}) { 538 if (const std::optional<u128> cached_hash{env.Analyze()}) {
326 info->unique_hash = *cached_hash; 539 info->unique_hash = *cached_hash;
327 info->size_bytes = env.CachedSize(); 540 info->size_bytes = env.CachedSize();
328 } else { 541 } else {
329 // Slow path, not really hit on commercial games 542 // Slow path, not really hit on commercial games
330 // Build a control flow graph to get the real shader size 543 // Build a control flow graph to get the real shader size
331 flow_block_pool.ReleaseContents(); 544 main_pools.flow_block.ReleaseContents();
332 Shader::Maxwell::Flow::CFG cfg{env, flow_block_pool, start_address}; 545 Shader::Maxwell::Flow::CFG cfg{env, main_pools.flow_block, env.StartAddress()};
333 info->unique_hash = env.CalculateHash(); 546 info->unique_hash = env.CalculateHash();
334 info->size_bytes = env.ReadSize(); 547 info->size_bytes = env.ReadSize();
335 } 548 }
@@ -339,100 +552,100 @@ const ShaderInfo* PipelineCache::MakeShaderInfo(Maxwell::ShaderProgram program,
339 return result; 552 return result;
340} 553}
341 554
342GraphicsPipeline PipelineCache::CreateGraphicsPipeline() { 555GraphicsPipeline PipelineCache::CreateGraphicsPipeline(ShaderPools& pools,
343 flow_block_pool.ReleaseContents(); 556 const GraphicsPipelineCacheKey& key,
344 inst_pool.ReleaseContents(); 557 std::span<Shader::Environment* const> envs) {
345 block_pool.ReleaseContents(); 558 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
346 559 size_t env_index{0};
347 std::array<GraphicsEnvironment, Maxwell::MaxShaderProgram> envs;
348 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 560 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
349
350 const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()};
351 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 561 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
352 if (graphics_key.unique_hashes[index] == u128{}) { 562 if (key.unique_hashes[index] == u128{}) {
353 continue; 563 continue;
354 } 564 }
355 const auto program{static_cast<Maxwell::ShaderProgram>(index)}; 565 Shader::Environment& env{*envs[env_index]};
356 GraphicsEnvironment& env{envs[index]}; 566 ++env_index;
357 const u32 start_address{maxwell3d.regs.shader_config[index].offset};
358 env = GraphicsEnvironment{maxwell3d, gpu_memory, program, base_addr, start_address};
359 567
360 const u32 cfg_offset = start_address + sizeof(Shader::ProgramHeader); 568 const u32 cfg_offset{env.StartAddress() + sizeof(Shader::ProgramHeader)};
361 Shader::Maxwell::Flow::CFG cfg(env, flow_block_pool, cfg_offset); 569 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset);
362 programs[index] = Shader::Maxwell::TranslateProgram(inst_pool, block_pool, env, cfg); 570 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg);
363 } 571 }
364 std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{}; 572 std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{};
365 std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules; 573 std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules;
366 574
367 u32 binding{0}; 575 u32 binding{0};
576 env_index = 0;
368 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 577 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
369 if (graphics_key.unique_hashes[index] == u128{}) { 578 if (key.unique_hashes[index] == u128{}) {
370 continue; 579 continue;
371 } 580 }
372 UNIMPLEMENTED_IF(index == 0); 581 UNIMPLEMENTED_IF(index == 0);
373 582
374 GraphicsEnvironment& env{envs[index]};
375 Shader::IR::Program& program{programs[index]}; 583 Shader::IR::Program& program{programs[index]};
376
377 const size_t stage_index{index - 1}; 584 const size_t stage_index{index - 1};
378 infos[stage_index] = &program.info; 585 infos[stage_index] = &program.info;
379 std::vector<u32> code{EmitSPIRV(profile, env, program, binding)};
380 586
381 FILE* file = fopen("D:\\shader.spv", "wb"); 587 Shader::Environment& env{*envs[env_index]};
382 fwrite(code.data(), 4, code.size(), file); 588 ++env_index;
383 fclose(file);
384 std::system("spirv-cross --vulkan-semantics D:\\shader.spv");
385 589
590 const std::vector<u32> code{EmitSPIRV(profile, env, program, binding)};
386 modules[stage_index] = BuildShader(device, code); 591 modules[stage_index] = BuildShader(device, code);
387 } 592 }
388 return GraphicsPipeline(maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, device, 593 return GraphicsPipeline(maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, device,
389 descriptor_pool, update_descriptor_queue, render_pass_cache, 594 descriptor_pool, update_descriptor_queue, render_pass_cache, key.state,
390 graphics_key.state, std::move(modules), infos); 595 std::move(modules), infos);
391} 596}
392 597
393ComputePipeline PipelineCache::CreateComputePipeline(ShaderInfo* shader_info) { 598GraphicsPipeline PipelineCache::CreateGraphicsPipeline() {
599 main_pools.ReleaseContents();
600
601 std::array<GraphicsEnvironment, Maxwell::MaxShaderProgram> graphics_envs;
602 boost::container::static_vector<GenericEnvironment*, Maxwell::MaxShaderProgram> generic_envs;
603 boost::container::static_vector<Shader::Environment*, Maxwell::MaxShaderProgram> envs;
604
605 const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()};
606 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
607 if (graphics_key.unique_hashes[index] == u128{}) {
608 continue;
609 }
610 const auto program{static_cast<Maxwell::ShaderProgram>(index)};
611 GraphicsEnvironment& env{graphics_envs[index]};
612 const u32 start_address{maxwell3d.regs.shader_config[index].offset};
613 env = GraphicsEnvironment{maxwell3d, gpu_memory, program, base_addr, start_address};
614 generic_envs.push_back(&env);
615 envs.push_back(&env);
616 }
617 GraphicsPipeline pipeline{CreateGraphicsPipeline(main_pools, graphics_key, MakeSpan(envs))};
618 if (!pipeline_cache_filename.empty()) {
619 SerializePipeline(graphics_key, generic_envs, pipeline_cache_filename);
620 }
621 return pipeline;
622}
623
624ComputePipeline PipelineCache::CreateComputePipeline(const ComputePipelineCacheKey& key,
625 const ShaderInfo* shader) {
394 const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; 626 const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()};
395 const auto& qmd{kepler_compute.launch_description}; 627 const auto& qmd{kepler_compute.launch_description};
396 ComputeEnvironment env{kepler_compute, gpu_memory, program_base}; 628 ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start};
397 if (const std::optional<u128> cached_hash{env.Analyze(qmd.program_start)}) { 629 main_pools.ReleaseContents();
398 // TODO: Load from cache 630 ComputePipeline pipeline{CreateComputePipeline(main_pools, key, env)};
631 if (!pipeline_cache_filename.empty()) {
632 SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env},
633 pipeline_cache_filename);
399 } 634 }
400 flow_block_pool.ReleaseContents(); 635 return pipeline;
401 inst_pool.ReleaseContents(); 636}
402 block_pool.ReleaseContents(); 637
638ComputePipeline PipelineCache::CreateComputePipeline(ShaderPools& pools,
639 const ComputePipelineCacheKey& key,
640 Shader::Environment& env) const {
641 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
403 642
404 Shader::Maxwell::Flow::CFG cfg{env, flow_block_pool, qmd.program_start}; 643 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
405 Shader::IR::Program program{Shader::Maxwell::TranslateProgram(inst_pool, block_pool, env, cfg)}; 644 Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)};
406 u32 binding{0}; 645 u32 binding{0};
407 std::vector<u32> code{EmitSPIRV(profile, env, program, binding)}; 646 std::vector<u32> code{EmitSPIRV(profile, env, program, binding)};
408 /*
409 FILE* file = fopen("D:\\shader.spv", "wb");
410 fwrite(code.data(), 4, code.size(), file);
411 fclose(file);
412 std::system("spirv-dis D:\\shader.spv");
413 */
414 shader_info->unique_hash = env.CalculateHash();
415 shader_info->size_bytes = env.ReadSize();
416 return ComputePipeline{device, descriptor_pool, update_descriptor_queue, program.info, 647 return ComputePipeline{device, descriptor_pool, update_descriptor_queue, program.info,
417 BuildShader(device, code)}; 648 BuildShader(device, code)};
418} 649}
419 650
420ComputePipeline* PipelineCache::CreateComputePipelineWithoutShader(VAddr shader_cpu_addr) {
421 ShaderInfo shader;
422 ComputePipeline pipeline{CreateComputePipeline(&shader)};
423 const ComputePipelineCacheKey key{MakeComputePipelineKey(shader.unique_hash)};
424 const size_t size_bytes{shader.size_bytes};
425 Register(std::make_unique<ShaderInfo>(std::move(shader)), shader_cpu_addr, size_bytes);
426 return &compute_cache.emplace(key, std::move(pipeline)).first->second;
427}
428
429ComputePipelineCacheKey PipelineCache::MakeComputePipelineKey(u128 unique_hash) const {
430 const auto& qmd{kepler_compute.launch_description};
431 return {
432 .unique_hash = unique_hash,
433 .shared_memory_size = qmd.shared_alloc,
434 .workgroup_size{qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z},
435 };
436}
437
438} // namespace Vulkan 651} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 60fb976df..2ecb68bdc 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <iosfwd>
9#include <memory> 10#include <memory>
10#include <type_traits> 11#include <type_traits>
11#include <unordered_map> 12#include <unordered_map>
@@ -96,6 +97,7 @@ namespace Vulkan {
96 97
97class ComputePipeline; 98class ComputePipeline;
98class Device; 99class Device;
100class GenericEnvironment;
99class RasterizerVulkan; 101class RasterizerVulkan;
100class RenderPassCache; 102class RenderPassCache;
101class VKDescriptorPool; 103class VKDescriptorPool;
@@ -107,6 +109,18 @@ struct ShaderInfo {
107 size_t size_bytes{}; 109 size_t size_bytes{};
108}; 110};
109 111
112struct ShaderPools {
113 void ReleaseContents() {
114 inst.ReleaseContents();
115 block.ReleaseContents();
116 flow_block.ReleaseContents();
117 }
118
119 Shader::ObjectPool<Shader::IR::Inst> inst;
120 Shader::ObjectPool<Shader::IR::Block> block;
121 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
122};
123
110class PipelineCache final : public VideoCommon::ShaderCache<ShaderInfo> { 124class PipelineCache final : public VideoCommon::ShaderCache<ShaderInfo> {
111public: 125public:
112 explicit PipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu, 126 explicit PipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu,
@@ -123,19 +137,24 @@ public:
123 137
124 [[nodiscard]] ComputePipeline* CurrentComputePipeline(); 138 [[nodiscard]] ComputePipeline* CurrentComputePipeline();
125 139
140 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
141 const VideoCore::DiskResourceLoadCallback& callback);
142
126private: 143private:
127 bool RefreshStages(); 144 bool RefreshStages();
128 145
129 const ShaderInfo* MakeShaderInfo(Maxwell::ShaderProgram program, GPUVAddr base_addr, 146 const ShaderInfo* MakeShaderInfo(GenericEnvironment& env, VAddr cpu_addr);
130 u32 start_address, VAddr cpu_addr);
131 147
132 GraphicsPipeline CreateGraphicsPipeline(); 148 GraphicsPipeline CreateGraphicsPipeline();
133 149
134 ComputePipeline CreateComputePipeline(ShaderInfo* shader); 150 GraphicsPipeline CreateGraphicsPipeline(ShaderPools& pools, const GraphicsPipelineCacheKey& key,
151 std::span<Shader::Environment* const> envs);
135 152
136 ComputePipeline* CreateComputePipelineWithoutShader(VAddr shader_cpu_addr); 153 ComputePipeline CreateComputePipeline(const ComputePipelineCacheKey& key,
154 const ShaderInfo* shader);
137 155
138 ComputePipelineCacheKey MakeComputePipelineKey(u128 unique_hash) const; 156 ComputePipeline CreateComputePipeline(ShaderPools& pools, const ComputePipelineCacheKey& key,
157 Shader::Environment& env) const;
139 158
140 Tegra::GPU& gpu; 159 Tegra::GPU& gpu;
141 Tegra::Engines::Maxwell3D& maxwell3d; 160 Tegra::Engines::Maxwell3D& maxwell3d;
@@ -155,11 +174,10 @@ private:
155 std::unordered_map<ComputePipelineCacheKey, ComputePipeline> compute_cache; 174 std::unordered_map<ComputePipelineCacheKey, ComputePipeline> compute_cache;
156 std::unordered_map<GraphicsPipelineCacheKey, GraphicsPipeline> graphics_cache; 175 std::unordered_map<GraphicsPipelineCacheKey, GraphicsPipeline> graphics_cache;
157 176
158 Shader::ObjectPool<Shader::IR::Inst> inst_pool; 177 ShaderPools main_pools;
159 Shader::ObjectPool<Shader::IR::Block> block_pool;
160 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block_pool;
161 178
162 Shader::Profile profile; 179 Shader::Profile profile;
180 std::string pipeline_cache_filename;
163}; 181};
164 182
165} // namespace Vulkan 183} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
index 7e5ae43ea..1c6ba7289 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
@@ -50,6 +50,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat
50RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} 50RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
51 51
52VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { 52VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
53 std::lock_guard lock{mutex};
53 const auto [pair, is_new] = cache.try_emplace(key); 54 const auto [pair, is_new] = cache.try_emplace(key);
54 if (!is_new) { 55 if (!is_new) {
55 return *pair->second; 56 return *pair->second;
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
index db8e83f1a..eaa0ed775 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <mutex>
7#include <unordered_map> 8#include <unordered_map>
8 9
9#include "video_core/surface.h" 10#include "video_core/surface.h"
@@ -37,7 +38,7 @@ struct hash<Vulkan::RenderPassKey> {
37 38
38namespace Vulkan { 39namespace Vulkan {
39 40
40 class Device; 41class Device;
41 42
42class RenderPassCache { 43class RenderPassCache {
43public: 44public:
@@ -48,6 +49,7 @@ public:
48private: 49private:
49 const Device* device{}; 50 const Device* device{};
50 std::unordered_map<RenderPassKey, vk::RenderPass> cache; 51 std::unordered_map<RenderPassKey, vk::RenderPass> cache;
52 std::mutex mutex;
51}; 53};
52 54
53} // namespace Vulkan 55} // namespace Vulkan