summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/dirty_flags.cpp6
-rw-r--r--src/video_core/dirty_flags.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp46
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h54
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp31
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h30
9 files changed, 114 insertions, 64 deletions
diff --git a/src/video_core/dirty_flags.cpp b/src/video_core/dirty_flags.cpp
index 7149af290..b1be065c3 100644
--- a/src/video_core/dirty_flags.cpp
+++ b/src/video_core/dirty_flags.cpp
@@ -58,6 +58,11 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
58 FillBlock(table, OFF(zeta), NUM(zeta), flag); 58 FillBlock(table, OFF(zeta), NUM(zeta), flag);
59 } 59 }
60} 60}
61
62void SetupDirtyShaders(Maxwell3D::DirtyState::Tables& tables) {
63 FillBlock(tables[0], OFF(shader_config[0]),
64 NUM(shader_config[0]) * Maxwell3D::Regs::MaxShaderProgram, Shaders);
65}
61} // Anonymous namespace 66} // Anonymous namespace
62 67
63void SetupDirtyFlags(Maxwell3D::DirtyState::Tables& tables) { 68void SetupDirtyFlags(Maxwell3D::DirtyState::Tables& tables) {
@@ -65,6 +70,7 @@ void SetupDirtyFlags(Maxwell3D::DirtyState::Tables& tables) {
65 SetupIndexBuffer(tables); 70 SetupIndexBuffer(tables);
66 SetupDirtyDescriptors(tables); 71 SetupDirtyDescriptors(tables);
67 SetupDirtyRenderTargets(tables); 72 SetupDirtyRenderTargets(tables);
73 SetupDirtyShaders(tables);
68} 74}
69 75
70} // namespace VideoCommon::Dirty 76} // namespace VideoCommon::Dirty
diff --git a/src/video_core/dirty_flags.h b/src/video_core/dirty_flags.h
index 702688ace..504465d3f 100644
--- a/src/video_core/dirty_flags.h
+++ b/src/video_core/dirty_flags.h
@@ -36,6 +36,8 @@ enum : u8 {
36 36
37 IndexBuffer, 37 IndexBuffer,
38 38
39 Shaders,
40
39 LastCommonEntry, 41 LastCommonEntry,
40}; 42};
41 43
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3551dbdcc..dd1937863 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -635,7 +635,7 @@ void RasterizerOpenGL::SyncDepthClamp() {
635 635
636void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { 636void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
637 auto& flags = maxwell3d.dirty.flags; 637 auto& flags = maxwell3d.dirty.flags;
638 if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) { 638 if (!flags[Dirty::ClipDistances] && !flags[VideoCommon::Dirty::Shaders]) {
639 return; 639 return;
640 } 640 }
641 flags[Dirty::ClipDistances] = false; 641 flags[Dirty::ClipDistances] = false;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index dbdf5230f..586da84e3 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -83,11 +83,6 @@ void SetupDirtyScissors(Tables& tables) {
83 FillBlock(tables[1], OFF(scissor_test), NUM(scissor_test), Scissors); 83 FillBlock(tables[1], OFF(scissor_test), NUM(scissor_test), Scissors);
84} 84}
85 85
86void SetupDirtyShaders(Tables& tables) {
87 FillBlock(tables[0], OFF(shader_config[0]), NUM(shader_config[0]) * Regs::MaxShaderProgram,
88 Shaders);
89}
90
91void SetupDirtyPolygonModes(Tables& tables) { 86void SetupDirtyPolygonModes(Tables& tables) {
92 tables[0][OFF(polygon_mode_front)] = PolygonModeFront; 87 tables[0][OFF(polygon_mode_front)] = PolygonModeFront;
93 tables[0][OFF(polygon_mode_back)] = PolygonModeBack; 88 tables[0][OFF(polygon_mode_back)] = PolygonModeBack;
@@ -217,7 +212,6 @@ StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags}
217 SetupDirtyScissors(tables); 212 SetupDirtyScissors(tables);
218 SetupDirtyVertexInstances(tables); 213 SetupDirtyVertexInstances(tables);
219 SetupDirtyVertexFormat(tables); 214 SetupDirtyVertexFormat(tables);
220 SetupDirtyShaders(tables);
221 SetupDirtyPolygonModes(tables); 215 SetupDirtyPolygonModes(tables);
222 SetupDirtyDepthTest(tables); 216 SetupDirtyDepthTest(tables);
223 SetupDirtyStencilTest(tables); 217 SetupDirtyStencilTest(tables);
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 94c905116..5864c7c07 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -52,7 +52,6 @@ enum : u8 {
52 BlendState0, 52 BlendState0,
53 BlendState7 = BlendState0 + 7, 53 BlendState7 = BlendState0 + 7,
54 54
55 Shaders,
56 ClipDistances, 55 ClipDistances,
57 56
58 PolygonModes, 57 PolygonModes,
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 2bc1f67ae..100a5e07a 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -125,13 +125,12 @@ GraphicsPipeline::GraphicsPipeline(Tegra::Engines::Maxwell3D& maxwell3d_,
125 VKUpdateDescriptorQueue& update_descriptor_queue_, 125 VKUpdateDescriptorQueue& update_descriptor_queue_,
126 Common::ThreadWorker* worker_thread, 126 Common::ThreadWorker* worker_thread,
127 RenderPassCache& render_pass_cache, 127 RenderPassCache& render_pass_cache,
128 const FixedPipelineState& state_, 128 const GraphicsPipelineCacheKey& key_,
129 std::array<vk::ShaderModule, NUM_STAGES> stages, 129 std::array<vk::ShaderModule, NUM_STAGES> stages,
130 const std::array<const Shader::Info*, NUM_STAGES>& infos) 130 const std::array<const Shader::Info*, NUM_STAGES>& infos)
131 : maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, texture_cache{texture_cache_}, 131 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, texture_cache{texture_cache_},
132 buffer_cache{buffer_cache_}, scheduler{scheduler_}, 132 buffer_cache{buffer_cache_}, scheduler{scheduler_},
133 update_descriptor_queue{update_descriptor_queue_}, state{state_}, spv_modules{ 133 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} {
134 std::move(stages)} {
135 std::ranges::transform(infos, stage_infos.begin(), 134 std::ranges::transform(infos, stage_infos.begin(),
136 [](const Shader::Info* info) { return info ? *info : Shader::Info{}; }); 135 [](const Shader::Info* info) { return info ? *info : Shader::Info{}; });
137 136
@@ -144,7 +143,7 @@ GraphicsPipeline::GraphicsPipeline(Tegra::Engines::Maxwell3D& maxwell3d_,
144 pipeline_layout = builder.CreatePipelineLayout(set_layout); 143 pipeline_layout = builder.CreatePipelineLayout(set_layout);
145 descriptor_update_template = builder.CreateTemplate(set_layout, *pipeline_layout); 144 descriptor_update_template = builder.CreateTemplate(set_layout, *pipeline_layout);
146 145
147 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(state))}; 146 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
148 MakePipeline(device, render_pass); 147 MakePipeline(device, render_pass);
149 148
150 std::lock_guard lock{build_mutex}; 149 std::lock_guard lock{build_mutex};
@@ -158,6 +157,11 @@ GraphicsPipeline::GraphicsPipeline(Tegra::Engines::Maxwell3D& maxwell3d_,
158 } 157 }
159} 158}
160 159
160void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
161 transition_keys.push_back(transition->key);
162 transitions.push_back(transition);
163}
164
161void GraphicsPipeline::Configure(bool is_indexed) { 165void GraphicsPipeline::Configure(bool is_indexed) {
162 static constexpr size_t max_images_elements = 64; 166 static constexpr size_t max_images_elements = 64;
163 std::array<ImageId, max_images_elements> image_view_ids; 167 std::array<ImageId, max_images_elements> image_view_ids;
@@ -294,12 +298,12 @@ void GraphicsPipeline::Configure(bool is_indexed) {
294void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pass) { 298void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pass) {
295 FixedPipelineState::DynamicState dynamic{}; 299 FixedPipelineState::DynamicState dynamic{};
296 if (!device.IsExtExtendedDynamicStateSupported()) { 300 if (!device.IsExtExtendedDynamicStateSupported()) {
297 dynamic = state.dynamic_state; 301 dynamic = key.state.dynamic_state;
298 } 302 }
299 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings; 303 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
300 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; 304 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
301 for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 305 for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
302 const bool instanced = state.binding_divisors[index] != 0; 306 const bool instanced = key.state.binding_divisors[index] != 0;
303 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 307 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
304 vertex_bindings.push_back({ 308 vertex_bindings.push_back({
305 .binding = static_cast<u32>(index), 309 .binding = static_cast<u32>(index),
@@ -309,14 +313,14 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
309 if (instanced) { 313 if (instanced) {
310 vertex_binding_divisors.push_back({ 314 vertex_binding_divisors.push_back({
311 .binding = static_cast<u32>(index), 315 .binding = static_cast<u32>(index),
312 .divisor = state.binding_divisors[index], 316 .divisor = key.state.binding_divisors[index],
313 }); 317 });
314 } 318 }
315 } 319 }
316 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; 320 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
317 const auto& input_attributes = stage_infos[0].input_generics; 321 const auto& input_attributes = stage_infos[0].input_generics;
318 for (size_t index = 0; index < state.attributes.size(); ++index) { 322 for (size_t index = 0; index < key.state.attributes.size(); ++index) {
319 const auto& attribute = state.attributes[index]; 323 const auto& attribute = key.state.attributes[index];
320 if (!attribute.enabled || !input_attributes[index].used) { 324 if (!attribute.enabled || !input_attributes[index].used) {
321 continue; 325 continue;
322 } 326 }
@@ -345,7 +349,7 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
345 if (!vertex_binding_divisors.empty()) { 349 if (!vertex_binding_divisors.empty()) {
346 vertex_input_ci.pNext = &input_divisor_ci; 350 vertex_input_ci.pNext = &input_divisor_ci;
347 } 351 }
348 auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, state.topology); 352 auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology);
349 if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { 353 if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
350 if (!spv_modules[1] && !spv_modules[2]) { 354 if (!spv_modules[1] && !spv_modules[2]) {
351 LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points"); 355 LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points");
@@ -357,14 +361,14 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
357 .pNext = nullptr, 361 .pNext = nullptr,
358 .flags = 0, 362 .flags = 0,
359 .topology = input_assembly_topology, 363 .topology = input_assembly_topology,
360 .primitiveRestartEnable = state.primitive_restart_enable != 0 && 364 .primitiveRestartEnable = key.state.primitive_restart_enable != 0 &&
361 SupportsPrimitiveRestart(input_assembly_topology), 365 SupportsPrimitiveRestart(input_assembly_topology),
362 }; 366 };
363 const VkPipelineTessellationStateCreateInfo tessellation_ci{ 367 const VkPipelineTessellationStateCreateInfo tessellation_ci{
364 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, 368 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
365 .pNext = nullptr, 369 .pNext = nullptr,
366 .flags = 0, 370 .flags = 0,
367 .patchControlPoints = state.patch_control_points_minus_one.Value() + 1, 371 .patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1,
368 }; 372 };
369 VkPipelineViewportStateCreateInfo viewport_ci{ 373 VkPipelineViewportStateCreateInfo viewport_ci{
370 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 374 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
@@ -376,7 +380,7 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
376 .pScissors = nullptr, 380 .pScissors = nullptr,
377 }; 381 };
378 std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles; 382 std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
379 std::ranges::transform(state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); 383 std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
380 VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{ 384 VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
381 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV, 385 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
382 .pNext = nullptr, 386 .pNext = nullptr,
@@ -393,15 +397,15 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
393 .pNext = nullptr, 397 .pNext = nullptr,
394 .flags = 0, 398 .flags = 0,
395 .depthClampEnable = 399 .depthClampEnable =
396 static_cast<VkBool32>(state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), 400 static_cast<VkBool32>(key.state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
397 .rasterizerDiscardEnable = 401 .rasterizerDiscardEnable =
398 static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), 402 static_cast<VkBool32>(key.state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
399 .polygonMode = 403 .polygonMode =
400 MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(state.polygon_mode)), 404 MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)),
401 .cullMode = static_cast<VkCullModeFlags>( 405 .cullMode = static_cast<VkCullModeFlags>(
402 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), 406 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
403 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), 407 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
404 .depthBiasEnable = state.depth_bias_enable, 408 .depthBiasEnable = key.state.depth_bias_enable,
405 .depthBiasConstantFactor = 0.0f, 409 .depthBiasConstantFactor = 0.0f,
406 .depthBiasClamp = 0.0f, 410 .depthBiasClamp = 0.0f,
407 .depthBiasSlopeFactor = 0.0f, 411 .depthBiasSlopeFactor = 0.0f,
@@ -411,7 +415,7 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
411 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 415 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
412 .pNext = nullptr, 416 .pNext = nullptr,
413 .flags = 0, 417 .flags = 0,
414 .rasterizationSamples = MaxwellToVK::MsaaMode(state.msaa_mode), 418 .rasterizationSamples = MaxwellToVK::MsaaMode(key.state.msaa_mode),
415 .sampleShadingEnable = VK_FALSE, 419 .sampleShadingEnable = VK_FALSE,
416 .minSampleShading = 0.0f, 420 .minSampleShading = 0.0f,
417 .pSampleMask = nullptr, 421 .pSampleMask = nullptr,
@@ -435,7 +439,7 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
435 .maxDepthBounds = 0.0f, 439 .maxDepthBounds = 0.0f,
436 }; 440 };
437 static_vector<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments; 441 static_vector<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
438 const size_t num_attachments{NumAttachments(state)}; 442 const size_t num_attachments{NumAttachments(key.state)};
439 for (size_t index = 0; index < num_attachments; ++index) { 443 for (size_t index = 0; index < num_attachments; ++index) {
440 static constexpr std::array mask_table{ 444 static constexpr std::array mask_table{
441 VK_COLOR_COMPONENT_R_BIT, 445 VK_COLOR_COMPONENT_R_BIT,
@@ -443,7 +447,7 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
443 VK_COLOR_COMPONENT_B_BIT, 447 VK_COLOR_COMPONENT_B_BIT,
444 VK_COLOR_COMPONENT_A_BIT, 448 VK_COLOR_COMPONENT_A_BIT,
445 }; 449 };
446 const auto& blend{state.attachments[index]}; 450 const auto& blend{key.state.attachments[index]};
447 const std::array mask{blend.Mask()}; 451 const std::array mask{blend.Mask()};
448 VkColorComponentFlags write_mask{}; 452 VkColorComponentFlags write_mask{};
449 for (size_t i = 0; i < mask_table.size(); ++i) { 453 for (size_t i = 0; i < mask_table.size(); ++i) {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 7d14d2378..fd787840b 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -4,10 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <array> 8#include <array>
8#include <atomic> 9#include <atomic>
9#include <condition_variable> 10#include <condition_variable>
10#include <mutex> 11#include <mutex>
12#include <type_traits>
11 13
12#include "common/thread_worker.h" 14#include "common/thread_worker.h"
13#include "shader_recompiler/shader_info.h" 15#include "shader_recompiler/shader_info.h"
@@ -20,6 +22,39 @@
20 22
21namespace Vulkan { 23namespace Vulkan {
22 24
25struct GraphicsPipelineCacheKey {
26 std::array<u128, 6> unique_hashes;
27 FixedPipelineState state;
28
29 size_t Hash() const noexcept;
30
31 bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
32
33 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
34 return !operator==(rhs);
35 }
36
37 size_t Size() const noexcept {
38 return sizeof(unique_hashes) + state.Size();
39 }
40};
41static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
42static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
43static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
44
45} // namespace Vulkan
46
47namespace std {
48template <>
49struct hash<Vulkan::GraphicsPipelineCacheKey> {
50 size_t operator()(const Vulkan::GraphicsPipelineCacheKey& k) const noexcept {
51 return k.Hash();
52 }
53};
54} // namespace std
55
56namespace Vulkan {
57
23class Device; 58class Device;
24class RenderPassCache; 59class RenderPassCache;
25class VKScheduler; 60class VKScheduler;
@@ -35,7 +70,8 @@ public:
35 const Device& device, VKDescriptorPool& descriptor_pool, 70 const Device& device, VKDescriptorPool& descriptor_pool,
36 VKUpdateDescriptorQueue& update_descriptor_queue, 71 VKUpdateDescriptorQueue& update_descriptor_queue,
37 Common::ThreadWorker* worker_thread, 72 Common::ThreadWorker* worker_thread,
38 RenderPassCache& render_pass_cache, const FixedPipelineState& state, 73 RenderPassCache& render_pass_cache,
74 const GraphicsPipelineCacheKey& key,
39 std::array<vk::ShaderModule, NUM_STAGES> stages, 75 std::array<vk::ShaderModule, NUM_STAGES> stages,
40 const std::array<const Shader::Info*, NUM_STAGES>& infos); 76 const std::array<const Shader::Info*, NUM_STAGES>& infos);
41 77
@@ -47,16 +83,30 @@ public:
47 GraphicsPipeline& operator=(const GraphicsPipeline&) = delete; 83 GraphicsPipeline& operator=(const GraphicsPipeline&) = delete;
48 GraphicsPipeline(const GraphicsPipeline&) = delete; 84 GraphicsPipeline(const GraphicsPipeline&) = delete;
49 85
86 void AddTransition(GraphicsPipeline* transition);
87
88 GraphicsPipeline* Next(const GraphicsPipelineCacheKey& current_key) noexcept {
89 if (key == current_key) {
90 return this;
91 }
92 const auto it{std::find(transition_keys.begin(), transition_keys.end(), current_key)};
93 return it != transition_keys.end() ? transitions[std::distance(transition_keys.begin(), it)]
94 : nullptr;
95 }
96
50private: 97private:
51 void MakePipeline(const Device& device, VkRenderPass render_pass); 98 void MakePipeline(const Device& device, VkRenderPass render_pass);
52 99
100 const GraphicsPipelineCacheKey key;
53 Tegra::Engines::Maxwell3D& maxwell3d; 101 Tegra::Engines::Maxwell3D& maxwell3d;
54 Tegra::MemoryManager& gpu_memory; 102 Tegra::MemoryManager& gpu_memory;
55 TextureCache& texture_cache; 103 TextureCache& texture_cache;
56 BufferCache& buffer_cache; 104 BufferCache& buffer_cache;
57 VKScheduler& scheduler; 105 VKScheduler& scheduler;
58 VKUpdateDescriptorQueue& update_descriptor_queue; 106 VKUpdateDescriptorQueue& update_descriptor_queue;
59 const FixedPipelineState state; 107
108 std::vector<GraphicsPipelineCacheKey> transition_keys;
109 std::vector<GraphicsPipeline*> transitions;
60 110
61 std::array<vk::ShaderModule, NUM_STAGES> spv_modules; 111 std::array<vk::ShaderModule, NUM_STAGES> spv_modules;
62 std::array<Shader::Info, NUM_STAGES> stage_infos; 112 std::array<Shader::Info, NUM_STAGES> stage_infos;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 4317b2ac7..2bd870060 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -21,6 +21,7 @@
21#include "shader_recompiler/frontend/maxwell/control_flow.h" 21#include "shader_recompiler/frontend/maxwell/control_flow.h"
22#include "shader_recompiler/frontend/maxwell/program.h" 22#include "shader_recompiler/frontend/maxwell/program.h"
23#include "shader_recompiler/program_header.h" 23#include "shader_recompiler/program_header.h"
24#include "video_core/dirty_flags.h"
24#include "video_core/engines/kepler_compute.h" 25#include "video_core/engines/kepler_compute.h"
25#include "video_core/engines/maxwell_3d.h" 26#include "video_core/engines/maxwell_3d.h"
26#include "video_core/memory_manager.h" 27#include "video_core/memory_manager.h"
@@ -700,17 +701,28 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
700 MICROPROFILE_SCOPE(Vulkan_PipelineCache); 701 MICROPROFILE_SCOPE(Vulkan_PipelineCache);
701 702
702 if (!RefreshStages()) { 703 if (!RefreshStages()) {
704 current_pipeline = nullptr;
703 return nullptr; 705 return nullptr;
704 } 706 }
705 graphics_key.state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported()); 707 graphics_key.state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported());
706 708
709 if (current_pipeline) {
710 GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
711 if (next) {
712 current_pipeline = next;
713 return current_pipeline;
714 }
715 }
707 const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)}; 716 const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
708 auto& pipeline{pair->second}; 717 auto& pipeline{pair->second};
709 if (!is_new) { 718 if (is_new) {
710 return pipeline.get(); 719 pipeline = CreateGraphicsPipeline();
711 } 720 }
712 pipeline = CreateGraphicsPipeline(); 721 if (current_pipeline) {
713 return pipeline.get(); 722 current_pipeline->AddTransition(pipeline.get());
723 }
724 current_pipeline = pipeline.get();
725 return current_pipeline;
714} 726}
715 727
716ComputePipeline* PipelineCache::CurrentComputePipeline() { 728ComputePipeline* PipelineCache::CurrentComputePipeline() {
@@ -743,6 +755,12 @@ ComputePipeline* PipelineCache::CurrentComputePipeline() {
743} 755}
744 756
745bool PipelineCache::RefreshStages() { 757bool PipelineCache::RefreshStages() {
758 auto& dirty{maxwell3d.dirty.flags};
759 if (!dirty[VideoCommon::Dirty::Shaders]) {
760 return last_valid_shaders;
761 }
762 dirty[VideoCommon::Dirty::Shaders] = false;
763
746 const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()}; 764 const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()};
747 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 765 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
748 if (!maxwell3d.regs.IsShaderConfigEnabled(index)) { 766 if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
@@ -755,6 +773,7 @@ bool PipelineCache::RefreshStages() {
755 const std::optional<VAddr> cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)}; 773 const std::optional<VAddr> cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)};
756 if (!cpu_shader_addr) { 774 if (!cpu_shader_addr) {
757 LOG_ERROR(Render_Vulkan, "Invalid GPU address for shader 0x{:016x}", shader_addr); 775 LOG_ERROR(Render_Vulkan, "Invalid GPU address for shader 0x{:016x}", shader_addr);
776 last_valid_shaders = false;
758 return false; 777 return false;
759 } 778 }
760 const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)}; 779 const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)};
@@ -766,6 +785,7 @@ bool PipelineCache::RefreshStages() {
766 shader_infos[index] = shader_info; 785 shader_infos[index] = shader_info;
767 graphics_key.unique_hashes[index] = shader_info->unique_hash; 786 graphics_key.unique_hashes[index] = shader_info->unique_hash;
768 } 787 }
788 last_valid_shaders = true;
769 return true; 789 return true;
770} 790}
771 791
@@ -832,8 +852,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
832 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 852 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
833 return std::make_unique<GraphicsPipeline>( 853 return std::make_unique<GraphicsPipeline>(
834 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, device, descriptor_pool, 854 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, device, descriptor_pool,
835 update_descriptor_queue, thread_worker, render_pass_cache, key.state, std::move(modules), 855 update_descriptor_queue, thread_worker, render_pass_cache, key, std::move(modules), infos);
836 infos);
837} 856}
838 857
839std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() { 858std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index e12e4422f..ad569acc4 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -58,26 +58,6 @@ static_assert(std::has_unique_object_representations_v<ComputePipelineCacheKey>)
58static_assert(std::is_trivially_copyable_v<ComputePipelineCacheKey>); 58static_assert(std::is_trivially_copyable_v<ComputePipelineCacheKey>);
59static_assert(std::is_trivially_constructible_v<ComputePipelineCacheKey>); 59static_assert(std::is_trivially_constructible_v<ComputePipelineCacheKey>);
60 60
61struct GraphicsPipelineCacheKey {
62 std::array<u128, 6> unique_hashes;
63 FixedPipelineState state;
64
65 size_t Hash() const noexcept;
66
67 bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept;
68
69 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
70 return !operator==(rhs);
71 }
72
73 size_t Size() const noexcept {
74 return sizeof(unique_hashes) + state.Size();
75 }
76};
77static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
78static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
79static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
80
81} // namespace Vulkan 61} // namespace Vulkan
82 62
83namespace std { 63namespace std {
@@ -89,13 +69,6 @@ struct hash<Vulkan::ComputePipelineCacheKey> {
89 } 69 }
90}; 70};
91 71
92template <>
93struct hash<Vulkan::GraphicsPipelineCacheKey> {
94 size_t operator()(const Vulkan::GraphicsPipelineCacheKey& k) const noexcept {
95 return k.Hash();
96 }
97};
98
99} // namespace std 72} // namespace std
100 73
101namespace Vulkan { 74namespace Vulkan {
@@ -181,7 +154,10 @@ private:
181 TextureCache& texture_cache; 154 TextureCache& texture_cache;
182 155
183 GraphicsPipelineCacheKey graphics_key{}; 156 GraphicsPipelineCacheKey graphics_key{};
157 GraphicsPipeline* current_pipeline{};
158
184 std::array<const ShaderInfo*, 6> shader_infos{}; 159 std::array<const ShaderInfo*, 6> shader_infos{};
160 bool last_valid_shaders{};
185 161
186 std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<ComputePipeline>> compute_cache; 162 std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<ComputePipeline>> compute_cache;
187 std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<GraphicsPipeline>> graphics_cache; 163 std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;