summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2020-04-24 01:44:14 -0300
committerGravatar ReinUsesLisp2020-04-26 01:38:51 -0300
commitddd82ef42b7bb7bea4c80edeb96bca8512580df3 (patch)
treeb2b09f597fe519128d065f034d51103f478e6776
parentMerge pull request #3768 from H27CK/cmd-title-fmt (diff)
downloadyuzu-ddd82ef42b7bb7bea4c80edeb96bca8512580df3.tar.gz
yuzu-ddd82ef42b7bb7bea4c80edeb96bca8512580df3.tar.xz
yuzu-ddd82ef42b7bb7bea4c80edeb96bca8512580df3.zip
shader/memory_util: Deduplicate code
Deduplicate code shared between vk_pipeline_cache and gl_shader_cache as well as shader decoder code. While we are at it, fix a bug in gl_shader_cache where compute shaders had an start offset of a stage shader.
Diffstat (limited to '')
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp82
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp69
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h8
-rw-r--r--src/video_core/shader/control_flow.cpp12
-rw-r--r--src/video_core/shader/decode.cpp12
-rw-r--r--src/video_core/shader/memory_util.cpp77
-rw-r--r--src/video_core/shader/memory_util.h47
-rw-r--r--src/video_core/shader/shader_ir.h3
9 files changed, 153 insertions, 159 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 8ede4ba9b..ff53282c9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -124,6 +124,8 @@ add_library(video_core STATIC
124 shader/decode.cpp 124 shader/decode.cpp
125 shader/expr.cpp 125 shader/expr.cpp
126 shader/expr.h 126 shader/expr.h
127 shader/memory_util.cpp
128 shader/memory_util.h
127 shader/node_helper.cpp 129 shader/node_helper.cpp
128 shader/node_helper.h 130 shader/node_helper.h
129 shader/node.h 131 shader/node.h
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index f63156b8d..9759a7078 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -10,8 +10,6 @@
10#include <thread> 10#include <thread>
11#include <unordered_set> 11#include <unordered_set>
12 12
13#include <boost/functional/hash.hpp>
14
15#include "common/alignment.h" 13#include "common/alignment.h"
16#include "common/assert.h" 14#include "common/assert.h"
17#include "common/logging/log.h" 15#include "common/logging/log.h"
@@ -28,76 +26,26 @@
28#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 26#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
29#include "video_core/renderer_opengl/gl_state_tracker.h" 27#include "video_core/renderer_opengl/gl_state_tracker.h"
30#include "video_core/renderer_opengl/utils.h" 28#include "video_core/renderer_opengl/utils.h"
29#include "video_core/shader/memory_util.h"
31#include "video_core/shader/registry.h" 30#include "video_core/shader/registry.h"
32#include "video_core/shader/shader_ir.h" 31#include "video_core/shader/shader_ir.h"
33 32
34namespace OpenGL { 33namespace OpenGL {
35 34
36using Tegra::Engines::ShaderType; 35using Tegra::Engines::ShaderType;
36using VideoCommon::Shader::GetShaderAddress;
37using VideoCommon::Shader::GetShaderCode;
38using VideoCommon::Shader::GetUniqueIdentifier;
39using VideoCommon::Shader::KERNEL_MAIN_OFFSET;
37using VideoCommon::Shader::ProgramCode; 40using VideoCommon::Shader::ProgramCode;
38using VideoCommon::Shader::Registry; 41using VideoCommon::Shader::Registry;
39using VideoCommon::Shader::ShaderIR; 42using VideoCommon::Shader::ShaderIR;
43using VideoCommon::Shader::STAGE_MAIN_OFFSET;
40 44
41namespace { 45namespace {
42 46
43constexpr u32 STAGE_MAIN_OFFSET = 10;
44constexpr u32 KERNEL_MAIN_OFFSET = 0;
45
46constexpr VideoCommon::Shader::CompilerSettings COMPILER_SETTINGS{}; 47constexpr VideoCommon::Shader::CompilerSettings COMPILER_SETTINGS{};
47 48
48/// Gets the address for the specified shader stage program
49GPUVAddr GetShaderAddress(Core::System& system, Maxwell::ShaderProgram program) {
50 const auto& gpu{system.GPU().Maxwell3D()};
51 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
52 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
53}
54
55/// Gets if the current instruction offset is a scheduler instruction
56constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
57 // Sched instructions appear once every 4 instructions.
58 constexpr std::size_t SchedPeriod = 4;
59 const std::size_t absolute_offset = offset - main_offset;
60 return (absolute_offset % SchedPeriod) == 0;
61}
62
63/// Calculates the size of a program stream
64std::size_t CalculateProgramSize(const ProgramCode& program) {
65 constexpr std::size_t start_offset = 10;
66 // This is the encoded version of BRA that jumps to itself. All Nvidia
67 // shaders end with one.
68 constexpr u64 self_jumping_branch = 0xE2400FFFFF07000FULL;
69 constexpr u64 mask = 0xFFFFFFFFFF7FFFFFULL;
70 std::size_t offset = start_offset;
71 while (offset < program.size()) {
72 const u64 instruction = program[offset];
73 if (!IsSchedInstruction(offset, start_offset)) {
74 if ((instruction & mask) == self_jumping_branch) {
75 // End on Maxwell's "nop" instruction
76 break;
77 }
78 if (instruction == 0) {
79 break;
80 }
81 }
82 offset++;
83 }
84 // The last instruction is included in the program size
85 return std::min(offset + 1, program.size());
86}
87
88/// Gets the shader program code from memory for the specified address
89ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, const GPUVAddr gpu_addr,
90 const u8* host_ptr) {
91 ProgramCode code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
92 ASSERT_OR_EXECUTE(host_ptr != nullptr, {
93 std::fill(code.begin(), code.end(), 0);
94 return code;
95 });
96 memory_manager.ReadBlockUnsafe(gpu_addr, code.data(), code.size() * sizeof(u64));
97 code.resize(CalculateProgramSize(code));
98 return code;
99}
100
101/// Gets the shader type from a Maxwell program type 49/// Gets the shader type from a Maxwell program type
102constexpr GLenum GetGLShaderType(ShaderType shader_type) { 50constexpr GLenum GetGLShaderType(ShaderType shader_type) {
103 switch (shader_type) { 51 switch (shader_type) {
@@ -114,17 +62,6 @@ constexpr GLenum GetGLShaderType(ShaderType shader_type) {
114 } 62 }
115} 63}
116 64
117/// Hashes one (or two) program streams
118u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& code,
119 const ProgramCode& code_b = {}) {
120 u64 unique_identifier = boost::hash_value(code);
121 if (is_a) {
122 // VertexA programs include two programs
123 boost::hash_combine(unique_identifier, boost::hash_value(code_b));
124 }
125 return unique_identifier;
126}
127
128constexpr const char* GetShaderTypeName(ShaderType shader_type) { 65constexpr const char* GetShaderTypeName(ShaderType shader_type) {
129 switch (shader_type) { 66 switch (shader_type) {
130 case ShaderType::Vertex: 67 case ShaderType::Vertex:
@@ -456,11 +393,12 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
456 const auto host_ptr{memory_manager.GetPointer(address)}; 393 const auto host_ptr{memory_manager.GetPointer(address)};
457 394
458 // No shader found - create a new one 395 // No shader found - create a new one
459 ProgramCode code{GetShaderCode(memory_manager, address, host_ptr)}; 396 ProgramCode code{GetShaderCode(memory_manager, address, host_ptr, false)};
460 ProgramCode code_b; 397 ProgramCode code_b;
461 if (program == Maxwell::ShaderProgram::VertexA) { 398 if (program == Maxwell::ShaderProgram::VertexA) {
462 const GPUVAddr address_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)}; 399 const GPUVAddr address_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)};
463 code_b = GetShaderCode(memory_manager, address_b, memory_manager.GetPointer(address_b)); 400 const u8* host_ptr_b = memory_manager.GetPointer(address_b);
401 code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false);
464 } 402 }
465 403
466 const auto unique_identifier = GetUniqueIdentifier( 404 const auto unique_identifier = GetUniqueIdentifier(
@@ -498,7 +436,7 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
498 436
499 const auto host_ptr{memory_manager.GetPointer(code_addr)}; 437 const auto host_ptr{memory_manager.GetPointer(code_addr)};
500 // No kernel found, create a new one 438 // No kernel found, create a new one
501 auto code{GetShaderCode(memory_manager, code_addr, host_ptr)}; 439 auto code{GetShaderCode(memory_manager, code_addr, host_ptr, true)};
502 const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; 440 const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)};
503 441
504 const ShaderParameters params{system, disk_cache, device, 442 const ShaderParameters params{system, disk_cache, device,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 91b1b16a5..8cedeaeba 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -27,12 +27,18 @@
27#include "video_core/renderer_vulkan/vk_update_descriptor.h" 27#include "video_core/renderer_vulkan/vk_update_descriptor.h"
28#include "video_core/renderer_vulkan/wrapper.h" 28#include "video_core/renderer_vulkan/wrapper.h"
29#include "video_core/shader/compiler_settings.h" 29#include "video_core/shader/compiler_settings.h"
30#include "video_core/shader/memory_util.h"
30 31
31namespace Vulkan { 32namespace Vulkan {
32 33
33MICROPROFILE_DECLARE(Vulkan_PipelineCache); 34MICROPROFILE_DECLARE(Vulkan_PipelineCache);
34 35
35using Tegra::Engines::ShaderType; 36using Tegra::Engines::ShaderType;
37using VideoCommon::Shader::GetShaderAddress;
38using VideoCommon::Shader::GetShaderCode;
39using VideoCommon::Shader::KERNEL_MAIN_OFFSET;
40using VideoCommon::Shader::ProgramCode;
41using VideoCommon::Shader::STAGE_MAIN_OFFSET;
36 42
37namespace { 43namespace {
38 44
@@ -45,60 +51,6 @@ constexpr VkDescriptorType STORAGE_IMAGE = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
45constexpr VideoCommon::Shader::CompilerSettings compiler_settings{ 51constexpr VideoCommon::Shader::CompilerSettings compiler_settings{
46 VideoCommon::Shader::CompileDepth::FullDecompile}; 52 VideoCommon::Shader::CompileDepth::FullDecompile};
47 53
48/// Gets the address for the specified shader stage program
49GPUVAddr GetShaderAddress(Core::System& system, Maxwell::ShaderProgram program) {
50 const auto& gpu{system.GPU().Maxwell3D()};
51 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
52 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
53}
54
55/// Gets if the current instruction offset is a scheduler instruction
56constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
57 // Sched instructions appear once every 4 instructions.
58 constexpr std::size_t SchedPeriod = 4;
59 const std::size_t absolute_offset = offset - main_offset;
60 return (absolute_offset % SchedPeriod) == 0;
61}
62
63/// Calculates the size of a program stream
64std::size_t CalculateProgramSize(const ProgramCode& program, bool is_compute) {
65 const std::size_t start_offset = is_compute ? 0 : 10;
66 // This is the encoded version of BRA that jumps to itself. All Nvidia
67 // shaders end with one.
68 constexpr u64 self_jumping_branch = 0xE2400FFFFF07000FULL;
69 constexpr u64 mask = 0xFFFFFFFFFF7FFFFFULL;
70 std::size_t offset = start_offset;
71 while (offset < program.size()) {
72 const u64 instruction = program[offset];
73 if (!IsSchedInstruction(offset, start_offset)) {
74 if ((instruction & mask) == self_jumping_branch) {
75 // End on Maxwell's "nop" instruction
76 break;
77 }
78 if (instruction == 0) {
79 break;
80 }
81 }
82 ++offset;
83 }
84 // The last instruction is included in the program size
85 return std::min(offset + 1, program.size());
86}
87
88/// Gets the shader program code from memory for the specified address
89ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, const GPUVAddr gpu_addr,
90 const u8* host_ptr, bool is_compute) {
91 ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
92 ASSERT_OR_EXECUTE(host_ptr != nullptr, {
93 std::fill(program_code.begin(), program_code.end(), 0);
94 return program_code;
95 });
96 memory_manager.ReadBlockUnsafe(gpu_addr, program_code.data(),
97 program_code.size() * sizeof(u64));
98 program_code.resize(CalculateProgramSize(program_code, is_compute));
99 return program_code;
100}
101
102constexpr std::size_t GetStageFromProgram(std::size_t program) { 54constexpr std::size_t GetStageFromProgram(std::size_t program) {
103 return program == 0 ? 0 : program - 1; 55 return program == 0 ? 0 : program - 1;
104} 56}
@@ -212,9 +164,9 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
212 const auto host_ptr{memory_manager.GetPointer(program_addr)}; 164 const auto host_ptr{memory_manager.GetPointer(program_addr)};
213 165
214 // No shader found - create a new one 166 // No shader found - create a new one
215 constexpr u32 stage_offset = 10; 167 constexpr u32 stage_offset = STAGE_MAIN_OFFSET;
216 const auto stage = static_cast<Tegra::Engines::ShaderType>(index == 0 ? 0 : index - 1); 168 const auto stage = static_cast<Tegra::Engines::ShaderType>(index == 0 ? 0 : index - 1);
217 auto code = GetShaderCode(memory_manager, program_addr, host_ptr, false); 169 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, false);
218 170
219 shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr, 171 shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr,
220 std::move(code), stage_offset); 172 std::move(code), stage_offset);
@@ -270,11 +222,10 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
270 // No shader found - create a new one 222 // No shader found - create a new one
271 const auto host_ptr = memory_manager.GetPointer(program_addr); 223 const auto host_ptr = memory_manager.GetPointer(program_addr);
272 224
273 auto code = GetShaderCode(memory_manager, program_addr, host_ptr, true); 225 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, true);
274 constexpr u32 kernel_main_offset = 0;
275 shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, 226 shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute,
276 program_addr, *cpu_addr, std::move(code), 227 program_addr, *cpu_addr, std::move(code),
277 kernel_main_offset); 228 KERNEL_MAIN_OFFSET);
278 if (cpu_addr) { 229 if (cpu_addr) {
279 Register(shader); 230 Register(shader);
280 } else { 231 } else {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 602a0a340..2cce53162 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -25,6 +25,7 @@
25#include "video_core/renderer_vulkan/vk_resource_manager.h" 25#include "video_core/renderer_vulkan/vk_resource_manager.h"
26#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 26#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
27#include "video_core/renderer_vulkan/wrapper.h" 27#include "video_core/renderer_vulkan/wrapper.h"
28#include "video_core/shader/memory_util.h"
28#include "video_core/shader/registry.h" 29#include "video_core/shader/registry.h"
29#include "video_core/shader/shader_ir.h" 30#include "video_core/shader/shader_ir.h"
30#include "video_core/surface.h" 31#include "video_core/surface.h"
@@ -47,8 +48,6 @@ class CachedShader;
47using Shader = std::shared_ptr<CachedShader>; 48using Shader = std::shared_ptr<CachedShader>;
48using Maxwell = Tegra::Engines::Maxwell3D::Regs; 49using Maxwell = Tegra::Engines::Maxwell3D::Regs;
49 50
50using ProgramCode = std::vector<u64>;
51
52struct GraphicsPipelineCacheKey { 51struct GraphicsPipelineCacheKey {
53 FixedPipelineState fixed_state; 52 FixedPipelineState fixed_state;
54 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; 53 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
@@ -113,7 +112,8 @@ namespace Vulkan {
113class CachedShader final : public RasterizerCacheObject { 112class CachedShader final : public RasterizerCacheObject {
114public: 113public:
115 explicit CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, 114 explicit CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr,
116 VAddr cpu_addr, ProgramCode program_code, u32 main_offset); 115 VAddr cpu_addr, VideoCommon::Shader::ProgramCode program_code,
116 u32 main_offset);
117 ~CachedShader(); 117 ~CachedShader();
118 118
119 GPUVAddr GetGpuAddr() const { 119 GPUVAddr GetGpuAddr() const {
@@ -145,7 +145,7 @@ private:
145 Tegra::Engines::ShaderType stage); 145 Tegra::Engines::ShaderType stage);
146 146
147 GPUVAddr gpu_addr{}; 147 GPUVAddr gpu_addr{};
148 ProgramCode program_code; 148 VideoCommon::Shader::ProgramCode program_code;
149 VideoCommon::Shader::Registry registry; 149 VideoCommon::Shader::Registry registry;
150 VideoCommon::Shader::ShaderIR shader_ir; 150 VideoCommon::Shader::ShaderIR shader_ir;
151 ShaderEntries entries; 151 ShaderEntries entries;
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index e00a3fb70..8d86020f6 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -13,6 +13,7 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "video_core/shader/ast.h" 14#include "video_core/shader/ast.h"
15#include "video_core/shader/control_flow.h" 15#include "video_core/shader/control_flow.h"
16#include "video_core/shader/memory_util.h"
16#include "video_core/shader/registry.h" 17#include "video_core/shader/registry.h"
17#include "video_core/shader/shader_ir.h" 18#include "video_core/shader/shader_ir.h"
18 19
@@ -115,17 +116,6 @@ Pred GetPredicate(u32 index, bool negated) {
115 return static_cast<Pred>(static_cast<u64>(index) + (negated ? 8ULL : 0ULL)); 116 return static_cast<Pred>(static_cast<u64>(index) + (negated ? 8ULL : 0ULL));
116} 117}
117 118
118/**
119 * Returns whether the instruction at the specified offset is a 'sched' instruction.
120 * Sched instructions always appear before a sequence of 3 instructions.
121 */
122constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
123 constexpr u32 SchedPeriod = 4;
124 u32 absolute_offset = offset - main_offset;
125
126 return (absolute_offset % SchedPeriod) == 0;
127}
128
129enum class ParseResult : u32 { 119enum class ParseResult : u32 {
130 ControlCaught, 120 ControlCaught,
131 BlockEnd, 121 BlockEnd,
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index 87ac9ac6c..1167ff4ec 100644
--- a/src/video_core/shader/decode.cpp
+++ b/src/video_core/shader/decode.cpp
@@ -13,6 +13,7 @@
13#include "video_core/engines/shader_bytecode.h" 13#include "video_core/engines/shader_bytecode.h"
14#include "video_core/engines/shader_header.h" 14#include "video_core/engines/shader_header.h"
15#include "video_core/shader/control_flow.h" 15#include "video_core/shader/control_flow.h"
16#include "video_core/shader/memory_util.h"
16#include "video_core/shader/node_helper.h" 17#include "video_core/shader/node_helper.h"
17#include "video_core/shader/shader_ir.h" 18#include "video_core/shader/shader_ir.h"
18 19
@@ -23,17 +24,6 @@ using Tegra::Shader::OpCode;
23 24
24namespace { 25namespace {
25 26
26/**
27 * Returns whether the instruction at the specified offset is a 'sched' instruction.
28 * Sched instructions always appear before a sequence of 3 instructions.
29 */
30constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
31 constexpr u32 SchedPeriod = 4;
32 u32 absolute_offset = offset - main_offset;
33
34 return (absolute_offset % SchedPeriod) == 0;
35}
36
37void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile& gpu_driver, 27void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile& gpu_driver,
38 const std::list<Sampler>& used_samplers) { 28 const std::list<Sampler>& used_samplers) {
39 if (gpu_driver.IsTextureHandlerSizeKnown() || used_samplers.size() <= 1) { 29 if (gpu_driver.IsTextureHandlerSizeKnown() || used_samplers.size() <= 1) {
diff --git a/src/video_core/shader/memory_util.cpp b/src/video_core/shader/memory_util.cpp
new file mode 100644
index 000000000..074f21691
--- /dev/null
+++ b/src/video_core/shader/memory_util.cpp
@@ -0,0 +1,77 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstddef>
7
8#include <boost/container_hash/hash.hpp>
9
10#include "common/common_types.h"
11#include "core/core.h"
12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/memory_manager.h"
14#include "video_core/shader/memory_util.h"
15#include "video_core/shader/shader_ir.h"
16
17namespace VideoCommon::Shader {
18
19GPUVAddr GetShaderAddress(Core::System& system,
20 Tegra::Engines::Maxwell3D::Regs::ShaderProgram program) {
21 const auto& gpu{system.GPU().Maxwell3D()};
22 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
23 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
24}
25
26bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
27 // Sched instructions appear once every 4 instructions.
28 constexpr std::size_t SchedPeriod = 4;
29 const std::size_t absolute_offset = offset - main_offset;
30 return (absolute_offset % SchedPeriod) == 0;
31}
32
33std::size_t CalculateProgramSize(const ProgramCode& program, bool is_compute) {
34 // This is the encoded version of BRA that jumps to itself. All Nvidia
35 // shaders end with one.
36 static constexpr u64 SELF_JUMPING_BRANCH = 0xE2400FFFFF07000FULL;
37 static constexpr u64 MASK = 0xFFFFFFFFFF7FFFFFULL;
38
39 const std::size_t start_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
40 std::size_t offset = start_offset;
41 while (offset < program.size()) {
42 const u64 instruction = program[offset];
43 if (!IsSchedInstruction(offset, start_offset)) {
44 if ((instruction & MASK) == SELF_JUMPING_BRANCH) {
45 // End on Maxwell's "nop" instruction
46 break;
47 }
48 if (instruction == 0) {
49 break;
50 }
51 }
52 ++offset;
53 }
54 // The last instruction is included in the program size
55 return std::min(offset + 1, program.size());
56}
57
58ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, GPUVAddr gpu_addr,
59 const u8* host_ptr, bool is_compute) {
60 ProgramCode code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
61 ASSERT_OR_EXECUTE(host_ptr != nullptr, { return code; });
62 memory_manager.ReadBlockUnsafe(gpu_addr, code.data(), code.size() * sizeof(u64));
63 code.resize(CalculateProgramSize(code, is_compute));
64 return code;
65}
66
67u64 GetUniqueIdentifier(Tegra::Engines::ShaderType shader_type, bool is_a, const ProgramCode& code,
68 const ProgramCode& code_b) {
69 u64 unique_identifier = boost::hash_value(code);
70 if (is_a) {
71 // VertexA programs include two programs
72 boost::hash_combine(unique_identifier, boost::hash_value(code_b));
73 }
74 return unique_identifier;
75}
76
77} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/memory_util.h b/src/video_core/shader/memory_util.h
new file mode 100644
index 000000000..be90d24fd
--- /dev/null
+++ b/src/video_core/shader/memory_util.h
@@ -0,0 +1,47 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <vector>
9
10#include "common/common_types.h"
11#include "video_core/engines/maxwell_3d.h"
12#include "video_core/engines/shader_type.h"
13
14namespace Core {
15class System;
16}
17
18namespace Tegra {
19class MemoryManager;
20}
21
22namespace VideoCommon::Shader {
23
24using ProgramCode = std::vector<u64>;
25
26constexpr u32 STAGE_MAIN_OFFSET = 10;
27constexpr u32 KERNEL_MAIN_OFFSET = 0;
28
29/// Gets the address for the specified shader stage program
30GPUVAddr GetShaderAddress(Core::System& system,
31 Tegra::Engines::Maxwell3D::Regs::ShaderProgram program);
32
33/// Gets if the current instruction offset is a scheduler instruction
34bool IsSchedInstruction(std::size_t offset, std::size_t main_offset);
35
36/// Calculates the size of a program stream
37std::size_t CalculateProgramSize(const ProgramCode& program, bool is_compute);
38
39/// Gets the shader program code from memory for the specified address
40ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, GPUVAddr gpu_addr,
41 const u8* host_ptr, bool is_compute);
42
43/// Hashes one (or two) program streams
44u64 GetUniqueIdentifier(Tegra::Engines::ShaderType shader_type, bool is_a, const ProgramCode& code,
45 const ProgramCode& code_b = {});
46
47} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index c6e7bdf50..69de5e68b 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -18,6 +18,7 @@
18#include "video_core/engines/shader_header.h" 18#include "video_core/engines/shader_header.h"
19#include "video_core/shader/ast.h" 19#include "video_core/shader/ast.h"
20#include "video_core/shader/compiler_settings.h" 20#include "video_core/shader/compiler_settings.h"
21#include "video_core/shader/memory_util.h"
21#include "video_core/shader/node.h" 22#include "video_core/shader/node.h"
22#include "video_core/shader/registry.h" 23#include "video_core/shader/registry.h"
23 24
@@ -25,8 +26,6 @@ namespace VideoCommon::Shader {
25 26
26struct ShaderBlock; 27struct ShaderBlock;
27 28
28using ProgramCode = std::vector<u64>;
29
30constexpr u32 MAX_PROGRAM_LENGTH = 0x1000; 29constexpr u32 MAX_PROGRAM_LENGTH = 0x1000;
31 30
32class ConstBuffer { 31class ConstBuffer {