1 // Copyright 2018 yuzu Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <bitset>
6 #include "common/assert.h"
7 #include "common/logging/log.h"
8 #include "core/core.h"
9 #include "video_core/engines/kepler_compute.h"
10 #include "video_core/engines/maxwell_3d.h"
11 #include "video_core/engines/shader_type.h"
12 #include "video_core/memory_manager.h"
13 #include "video_core/rasterizer_interface.h"
14 #include "video_core/renderer_base.h"
15 #include "video_core/textures/decoders.h"
16 
17 namespace Tegra::Engines {
18 
KeplerCompute(Core::System & system_,MemoryManager & memory_manager_)19 KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
20     : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {}
21 
22 KeplerCompute::~KeplerCompute() = default;
23 
BindRasterizer(VideoCore::RasterizerInterface & rasterizer_)24 void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
25     rasterizer = &rasterizer_;
26 }
27 
CallMethod(u32 method,u32 method_argument,bool is_last_call)28 void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
29     ASSERT_MSG(method < Regs::NUM_REGS,
30                "Invalid KeplerCompute register, increase the size of the Regs structure");
31 
32     regs.reg_array[method] = method_argument;
33 
34     switch (method) {
35     case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
36         upload_state.ProcessExec(regs.exec_upload.linear != 0);
37         break;
38     }
39     case KEPLER_COMPUTE_REG_INDEX(data_upload): {
40         upload_state.ProcessData(method_argument, is_last_call);
41         if (is_last_call) {
42             system.GPU().Maxwell3D().OnMemoryWrite();
43         }
44         break;
45     }
46     case KEPLER_COMPUTE_REG_INDEX(launch):
47         ProcessLaunch();
48         break;
49     default:
50         break;
51     }
52 }
53 
CallMultiMethod(u32 method,const u32 * base_start,u32 amount,u32 methods_pending)54 void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
55                                     u32 methods_pending) {
56     for (std::size_t i = 0; i < amount; i++) {
57         CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
58     }
59 }
60 
GetTexture(std::size_t offset) const61 Texture::FullTextureInfo KeplerCompute::GetTexture(std::size_t offset) const {
62     const std::bitset<8> cbuf_mask = launch_description.const_buffer_enable_mask.Value();
63     ASSERT(cbuf_mask[regs.tex_cb_index]);
64 
65     const auto& texinfo = launch_description.const_buffer_config[regs.tex_cb_index];
66     ASSERT(texinfo.Address() != 0);
67 
68     const GPUVAddr address = texinfo.Address() + offset * sizeof(Texture::TextureHandle);
69     ASSERT(address < texinfo.Address() + texinfo.size);
70 
71     const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(address)};
72     return GetTextureInfo(tex_handle);
73 }
74 
GetTextureInfo(Texture::TextureHandle tex_handle) const75 Texture::FullTextureInfo KeplerCompute::GetTextureInfo(Texture::TextureHandle tex_handle) const {
76     return Texture::FullTextureInfo{GetTICEntry(tex_handle.tic_id), GetTSCEntry(tex_handle.tsc_id)};
77 }
78 
AccessConstBuffer32(ShaderType stage,u64 const_buffer,u64 offset) const79 u32 KeplerCompute::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const {
80     ASSERT(stage == ShaderType::Compute);
81     const auto& buffer = launch_description.const_buffer_config[const_buffer];
82     u32 result;
83     std::memcpy(&result, memory_manager.GetPointer(buffer.Address() + offset), sizeof(u32));
84     return result;
85 }
86 
AccessBoundSampler(ShaderType stage,u64 offset) const87 SamplerDescriptor KeplerCompute::AccessBoundSampler(ShaderType stage, u64 offset) const {
88     return AccessBindlessSampler(stage, regs.tex_cb_index, offset * sizeof(Texture::TextureHandle));
89 }
90 
AccessBindlessSampler(ShaderType stage,u64 const_buffer,u64 offset) const91 SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 const_buffer,
92                                                        u64 offset) const {
93     ASSERT(stage == ShaderType::Compute);
94     const auto& tex_info_buffer = launch_description.const_buffer_config[const_buffer];
95     const GPUVAddr tex_info_address = tex_info_buffer.Address() + offset;
96     return AccessSampler(memory_manager.Read<u32>(tex_info_address));
97 }
98 
AccessSampler(u32 handle) const99 SamplerDescriptor KeplerCompute::AccessSampler(u32 handle) const {
100     const Texture::TextureHandle tex_handle{handle};
101     const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
102     SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
103     result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
104     return result;
105 }
106 
AccessGuestDriverProfile()107 VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() {
108     return rasterizer->AccessGuestDriverProfile();
109 }
110 
AccessGuestDriverProfile() const111 const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const {
112     return rasterizer->AccessGuestDriverProfile();
113 }
114 
ProcessLaunch()115 void KeplerCompute::ProcessLaunch() {
116     const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address();
117     memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description,
118                                    LaunchParams::NUM_LAUNCH_PARAMETERS * sizeof(u32));
119 
120     const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start;
121     LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr);
122 
123     rasterizer->DispatchCompute(code_addr);
124 }
125 
GetTICEntry(u32 tic_index) const126 Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
127     const GPUVAddr tic_address_gpu{regs.tic.Address() + tic_index * sizeof(Texture::TICEntry)};
128 
129     Texture::TICEntry tic_entry;
130     memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
131 
132     return tic_entry;
133 }
134 
GetTSCEntry(u32 tsc_index) const135 Texture::TSCEntry KeplerCompute::GetTSCEntry(u32 tsc_index) const {
136     const GPUVAddr tsc_address_gpu{regs.tsc.Address() + tsc_index * sizeof(Texture::TSCEntry)};
137 
138     Texture::TSCEntry tsc_entry;
139     memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
140     return tsc_entry;
141 }
142 
143 } // namespace Tegra::Engines
144