Browse Source
Merge pull request #3473 from ReinUsesLisp/shader-purge
Merge pull request #3473 from ReinUsesLisp/shader-purge
gl_shader_cache: Rework shader cache and store texture arrayspull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1002 additions and 1431 deletions
-
6CMakeModules/GenerateSCMRev.cmake
-
6src/common/CMakeLists.txt
-
6src/video_core/CMakeLists.txt
-
67src/video_core/engines/const_buffer_engine_interface.h
-
2src/video_core/engines/kepler_compute.cpp
-
2src/video_core/engines/maxwell_3d.cpp
-
34src/video_core/engines/maxwell_3d.h
-
7src/video_core/guest_driver.cpp
-
21src/video_core/guest_driver.h
-
1src/video_core/rasterizer_interface.h
-
41src/video_core/renderer_opengl/gl_rasterizer.cpp
-
9src/video_core/renderer_opengl/gl_rasterizer.h
-
510src/video_core/renderer_opengl/gl_shader_cache.cpp
-
99src/video_core/renderer_opengl/gl_shader_cache.h
-
276src/video_core/renderer_opengl/gl_shader_decompiler.cpp
-
22src/video_core/renderer_opengl/gl_shader_decompiler.h
-
404src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
-
153src/video_core/renderer_opengl/gl_shader_disk_cache.h
-
109src/video_core/renderer_opengl/gl_shader_gen.cpp
-
34src/video_core/renderer_opengl/gl_shader_gen.h
-
4src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
-
4src/video_core/renderer_vulkan/vk_pipeline_cache.h
-
126src/video_core/shader/const_buffer_locker.cpp
-
103src/video_core/shader/const_buffer_locker.h
-
13src/video_core/shader/control_flow.cpp
-
3src/video_core/shader/control_flow.h
-
22src/video_core/shader/decode.cpp
-
5src/video_core/shader/decode/texture.cpp
-
161src/video_core/shader/registry.cpp
-
137src/video_core/shader/registry.h
-
5src/video_core/shader/shader_ir.cpp
-
6src/video_core/shader/shader_ir.h
-
18src/video_core/shader/track.cpp
-
17src/yuzu/loading_screen.cpp
@ -1,109 +0,0 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <string>
|
|||
|
|||
#include <fmt/format.h>
|
|||
|
|||
#include "video_core/engines/maxwell_3d.h"
|
|||
#include "video_core/engines/shader_type.h"
|
|||
#include "video_core/renderer_opengl/gl_device.h"
|
|||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
|||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
|||
#include "video_core/shader/shader_ir.h"
|
|||
|
|||
namespace OpenGL::GLShader { |
|||
|
|||
using Tegra::Engines::Maxwell3D; |
|||
using Tegra::Engines::ShaderType; |
|||
using VideoCommon::Shader::CompileDepth; |
|||
using VideoCommon::Shader::CompilerSettings; |
|||
using VideoCommon::Shader::ProgramCode; |
|||
using VideoCommon::Shader::ShaderIR; |
|||
|
|||
std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const ShaderIR* ir_b) { |
|||
std::string out = GetCommonDeclarations(); |
|||
out += fmt::format(R"( |
|||
layout (std140, binding = {}) uniform vs_config {{ |
|||
float y_direction; |
|||
}}; |
|||
|
|||
)", |
|||
EmulationUniformBlockBinding); |
|||
out += Decompile(device, ir, ShaderType::Vertex, "vertex"); |
|||
if (ir_b) { |
|||
out += Decompile(device, *ir_b, ShaderType::Vertex, "vertex_b"); |
|||
} |
|||
|
|||
out += R"( |
|||
void main() { |
|||
gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f); |
|||
execute_vertex(); |
|||
)"; |
|||
if (ir_b) { |
|||
out += " execute_vertex_b();"; |
|||
} |
|||
out += "}\n"; |
|||
return out; |
|||
} |
|||
|
|||
std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir) { |
|||
std::string out = GetCommonDeclarations(); |
|||
out += fmt::format(R"( |
|||
layout (std140, binding = {}) uniform gs_config {{ |
|||
float y_direction; |
|||
}}; |
|||
|
|||
)", |
|||
EmulationUniformBlockBinding); |
|||
out += Decompile(device, ir, ShaderType::Geometry, "geometry"); |
|||
|
|||
out += R"( |
|||
void main() { |
|||
execute_geometry(); |
|||
} |
|||
)"; |
|||
return out; |
|||
} |
|||
|
|||
std::string GenerateFragmentShader(const Device& device, const ShaderIR& ir) { |
|||
std::string out = GetCommonDeclarations(); |
|||
out += fmt::format(R"( |
|||
layout (location = 0) out vec4 FragColor0; |
|||
layout (location = 1) out vec4 FragColor1; |
|||
layout (location = 2) out vec4 FragColor2; |
|||
layout (location = 3) out vec4 FragColor3; |
|||
layout (location = 4) out vec4 FragColor4; |
|||
layout (location = 5) out vec4 FragColor5; |
|||
layout (location = 6) out vec4 FragColor6; |
|||
layout (location = 7) out vec4 FragColor7; |
|||
|
|||
layout (std140, binding = {}) uniform fs_config {{ |
|||
float y_direction; |
|||
}}; |
|||
|
|||
)", |
|||
EmulationUniformBlockBinding); |
|||
out += Decompile(device, ir, ShaderType::Fragment, "fragment"); |
|||
|
|||
out += R"( |
|||
void main() { |
|||
execute_fragment(); |
|||
} |
|||
)"; |
|||
return out; |
|||
} |
|||
|
|||
std::string GenerateComputeShader(const Device& device, const ShaderIR& ir) { |
|||
std::string out = GetCommonDeclarations(); |
|||
out += Decompile(device, ir, ShaderType::Compute, "compute"); |
|||
out += R"( |
|||
void main() { |
|||
execute_compute(); |
|||
} |
|||
)"; |
|||
return out; |
|||
} |
|||
|
|||
} // namespace OpenGL::GLShader
|
|||
@ -1,34 +0,0 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
|
|||
#include "common/common_types.h" |
|||
#include "video_core/renderer_opengl/gl_shader_decompiler.h" |
|||
#include "video_core/shader/shader_ir.h" |
|||
|
|||
namespace OpenGL { |
|||
class Device; |
|||
} |
|||
|
|||
namespace OpenGL::GLShader { |
|||
|
|||
using VideoCommon::Shader::ProgramCode; |
|||
using VideoCommon::Shader::ShaderIR; |
|||
|
|||
/// Generates the GLSL vertex shader program source code for the given VS program |
|||
std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const ShaderIR* ir_b); |
|||
|
|||
/// Generates the GLSL geometry shader program source code for the given GS program |
|||
std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir); |
|||
|
|||
/// Generates the GLSL fragment shader program source code for the given FS program |
|||
std::string GenerateFragmentShader(const Device& device, const ShaderIR& ir); |
|||
|
|||
/// Generates the GLSL compute shader program source code for the given CS program |
|||
std::string GenerateComputeShader(const Device& device, const ShaderIR& ir); |
|||
|
|||
} // namespace OpenGL::GLShader |
|||
@ -1,126 +0,0 @@ |
|||
// Copyright 2019 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <algorithm>
|
|||
#include <tuple>
|
|||
|
|||
#include "common/common_types.h"
|
|||
#include "video_core/engines/maxwell_3d.h"
|
|||
#include "video_core/engines/shader_type.h"
|
|||
#include "video_core/shader/const_buffer_locker.h"
|
|||
|
|||
namespace VideoCommon::Shader { |
|||
|
|||
using Tegra::Engines::SamplerDescriptor; |
|||
|
|||
ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage) |
|||
: stage{shader_stage} {} |
|||
|
|||
ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage, |
|||
Tegra::Engines::ConstBufferEngineInterface& engine) |
|||
: stage{shader_stage}, engine{&engine} {} |
|||
|
|||
ConstBufferLocker::~ConstBufferLocker() = default; |
|||
|
|||
std::optional<u32> ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) { |
|||
const std::pair<u32, u32> key = {buffer, offset}; |
|||
const auto iter = keys.find(key); |
|||
if (iter != keys.end()) { |
|||
return iter->second; |
|||
} |
|||
if (!engine) { |
|||
return std::nullopt; |
|||
} |
|||
const u32 value = engine->AccessConstBuffer32(stage, buffer, offset); |
|||
keys.emplace(key, value); |
|||
return value; |
|||
} |
|||
|
|||
std::optional<SamplerDescriptor> ConstBufferLocker::ObtainBoundSampler(u32 offset) { |
|||
const u32 key = offset; |
|||
const auto iter = bound_samplers.find(key); |
|||
if (iter != bound_samplers.end()) { |
|||
return iter->second; |
|||
} |
|||
if (!engine) { |
|||
return std::nullopt; |
|||
} |
|||
const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset); |
|||
bound_samplers.emplace(key, value); |
|||
return value; |
|||
} |
|||
|
|||
std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindlessSampler( |
|||
u32 buffer, u32 offset) { |
|||
const std::pair key = {buffer, offset}; |
|||
const auto iter = bindless_samplers.find(key); |
|||
if (iter != bindless_samplers.end()) { |
|||
return iter->second; |
|||
} |
|||
if (!engine) { |
|||
return std::nullopt; |
|||
} |
|||
const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset); |
|||
bindless_samplers.emplace(key, value); |
|||
return value; |
|||
} |
|||
|
|||
std::optional<u32> ConstBufferLocker::ObtainBoundBuffer() { |
|||
if (bound_buffer_saved) { |
|||
return bound_buffer; |
|||
} |
|||
if (!engine) { |
|||
return std::nullopt; |
|||
} |
|||
bound_buffer_saved = true; |
|||
bound_buffer = engine->GetBoundBuffer(); |
|||
return bound_buffer; |
|||
} |
|||
|
|||
void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) { |
|||
keys.insert_or_assign({buffer, offset}, value); |
|||
} |
|||
|
|||
void ConstBufferLocker::InsertBoundSampler(u32 offset, SamplerDescriptor sampler) { |
|||
bound_samplers.insert_or_assign(offset, sampler); |
|||
} |
|||
|
|||
void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDescriptor sampler) { |
|||
bindless_samplers.insert_or_assign({buffer, offset}, sampler); |
|||
} |
|||
|
|||
void ConstBufferLocker::SetBoundBuffer(u32 buffer) { |
|||
bound_buffer_saved = true; |
|||
bound_buffer = buffer; |
|||
} |
|||
|
|||
bool ConstBufferLocker::IsConsistent() const { |
|||
if (!engine) { |
|||
return false; |
|||
} |
|||
return std::all_of(keys.begin(), keys.end(), |
|||
[this](const auto& pair) { |
|||
const auto [cbuf, offset] = pair.first; |
|||
const auto value = pair.second; |
|||
return value == engine->AccessConstBuffer32(stage, cbuf, offset); |
|||
}) && |
|||
std::all_of(bound_samplers.begin(), bound_samplers.end(), |
|||
[this](const auto& sampler) { |
|||
const auto [key, value] = sampler; |
|||
return value == engine->AccessBoundSampler(stage, key); |
|||
}) && |
|||
std::all_of(bindless_samplers.begin(), bindless_samplers.end(), |
|||
[this](const auto& sampler) { |
|||
const auto [cbuf, offset] = sampler.first; |
|||
const auto value = sampler.second; |
|||
return value == engine->AccessBindlessSampler(stage, cbuf, offset); |
|||
}); |
|||
} |
|||
|
|||
bool ConstBufferLocker::HasEqualKeys(const ConstBufferLocker& rhs) const { |
|||
return std::tie(keys, bound_samplers, bindless_samplers) == |
|||
std::tie(rhs.keys, rhs.bound_samplers, rhs.bindless_samplers); |
|||
} |
|||
|
|||
} // namespace VideoCommon::Shader
|
|||
@ -1,103 +0,0 @@ |
|||
// Copyright 2019 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <optional> |
|||
#include <unordered_map> |
|||
#include "common/common_types.h" |
|||
#include "common/hash.h" |
|||
#include "video_core/engines/const_buffer_engine_interface.h" |
|||
#include "video_core/engines/shader_type.h" |
|||
#include "video_core/guest_driver.h" |
|||
|
|||
namespace VideoCommon::Shader { |
|||
|
|||
using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>; |
|||
using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>; |
|||
using BindlessSamplerMap = |
|||
std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>; |
|||
|
|||
/** |
|||
* The ConstBufferLocker is a class use to interface the 3D and compute engines with the shader |
|||
* compiler. with it, the shader can obtain required data from GPU state and store it for disk |
|||
* shader compilation. |
|||
*/ |
|||
class ConstBufferLocker { |
|||
public: |
|||
explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage); |
|||
|
|||
explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage, |
|||
Tegra::Engines::ConstBufferEngineInterface& engine); |
|||
|
|||
~ConstBufferLocker(); |
|||
|
|||
/// Retrieves a key from the locker, if it's registered, it will give the registered value, if |
|||
/// not it will obtain it from maxwell3d and register it. |
|||
std::optional<u32> ObtainKey(u32 buffer, u32 offset); |
|||
|
|||
std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset); |
|||
|
|||
std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); |
|||
|
|||
std::optional<u32> ObtainBoundBuffer(); |
|||
|
|||
/// Inserts a key. |
|||
void InsertKey(u32 buffer, u32 offset, u32 value); |
|||
|
|||
/// Inserts a bound sampler key. |
|||
void InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler); |
|||
|
|||
/// Inserts a bindless sampler key. |
|||
void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); |
|||
|
|||
/// Set the bound buffer for this locker. |
|||
void SetBoundBuffer(u32 buffer); |
|||
|
|||
/// Checks keys and samplers against engine's current const buffers. Returns true if they are |
|||
/// the same value, false otherwise; |
|||
bool IsConsistent() const; |
|||
|
|||
/// Returns true if the keys are equal to the other ones in the locker. |
|||
bool HasEqualKeys(const ConstBufferLocker& rhs) const; |
|||
|
|||
/// Gives an getter to the const buffer keys in the database. |
|||
const KeyMap& GetKeys() const { |
|||
return keys; |
|||
} |
|||
|
|||
/// Gets samplers database. |
|||
const BoundSamplerMap& GetBoundSamplers() const { |
|||
return bound_samplers; |
|||
} |
|||
|
|||
/// Gets bindless samplers database. |
|||
const BindlessSamplerMap& GetBindlessSamplers() const { |
|||
return bindless_samplers; |
|||
} |
|||
|
|||
/// Gets bound buffer used on this shader |
|||
u32 GetBoundBuffer() const { |
|||
return bound_buffer; |
|||
} |
|||
|
|||
/// Obtains access to the guest driver's profile. |
|||
VideoCore::GuestDriverProfile* AccessGuestDriverProfile() const { |
|||
if (engine) { |
|||
return &engine->AccessGuestDriverProfile(); |
|||
} |
|||
return nullptr; |
|||
} |
|||
|
|||
private: |
|||
const Tegra::Engines::ShaderType stage; |
|||
Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; |
|||
KeyMap keys; |
|||
BoundSamplerMap bound_samplers; |
|||
BindlessSamplerMap bindless_samplers; |
|||
bool bound_buffer_saved{}; |
|||
u32 bound_buffer{}; |
|||
}; |
|||
|
|||
} // namespace VideoCommon::Shader |
|||
@ -0,0 +1,161 @@ |
|||
// Copyright 2019 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <algorithm>
|
|||
#include <tuple>
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/common_types.h"
|
|||
#include "video_core/engines/kepler_compute.h"
|
|||
#include "video_core/engines/maxwell_3d.h"
|
|||
#include "video_core/engines/shader_type.h"
|
|||
#include "video_core/shader/registry.h"
|
|||
|
|||
namespace VideoCommon::Shader { |
|||
|
|||
using Tegra::Engines::ConstBufferEngineInterface; |
|||
using Tegra::Engines::SamplerDescriptor; |
|||
using Tegra::Engines::ShaderType; |
|||
|
|||
namespace { |
|||
|
|||
GraphicsInfo MakeGraphicsInfo(ShaderType shader_stage, ConstBufferEngineInterface& engine) { |
|||
if (shader_stage == ShaderType::Compute) { |
|||
return {}; |
|||
} |
|||
auto& graphics = static_cast<Tegra::Engines::Maxwell3D&>(engine); |
|||
|
|||
GraphicsInfo info; |
|||
info.tfb_layouts = graphics.regs.tfb_layouts; |
|||
info.tfb_varying_locs = graphics.regs.tfb_varying_locs; |
|||
info.primitive_topology = graphics.regs.draw.topology; |
|||
info.tessellation_primitive = graphics.regs.tess_mode.prim; |
|||
info.tessellation_spacing = graphics.regs.tess_mode.spacing; |
|||
info.tfb_enabled = graphics.regs.tfb_enabled; |
|||
info.tessellation_clockwise = graphics.regs.tess_mode.cw; |
|||
return info; |
|||
} |
|||
|
|||
ComputeInfo MakeComputeInfo(ShaderType shader_stage, ConstBufferEngineInterface& engine) { |
|||
if (shader_stage != ShaderType::Compute) { |
|||
return {}; |
|||
} |
|||
auto& compute = static_cast<Tegra::Engines::KeplerCompute&>(engine); |
|||
const auto& launch = compute.launch_description; |
|||
|
|||
ComputeInfo info; |
|||
info.workgroup_size = {launch.block_dim_x, launch.block_dim_y, launch.block_dim_z}; |
|||
info.local_memory_size_in_words = launch.local_pos_alloc; |
|||
info.shared_memory_size_in_words = launch.shared_alloc; |
|||
return info; |
|||
} |
|||
|
|||
} // Anonymous namespace
|
|||
|
|||
Registry::Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info) |
|||
: stage{shader_stage}, stored_guest_driver_profile{info.guest_driver_profile}, |
|||
bound_buffer{info.bound_buffer}, graphics_info{info.graphics}, compute_info{info.compute} {} |
|||
|
|||
Registry::Registry(Tegra::Engines::ShaderType shader_stage, |
|||
Tegra::Engines::ConstBufferEngineInterface& engine) |
|||
: stage{shader_stage}, engine{&engine}, bound_buffer{engine.GetBoundBuffer()}, |
|||
graphics_info{MakeGraphicsInfo(shader_stage, engine)}, compute_info{MakeComputeInfo( |
|||
shader_stage, engine)} {} |
|||
|
|||
Registry::~Registry() = default; |
|||
|
|||
std::optional<u32> Registry::ObtainKey(u32 buffer, u32 offset) { |
|||
const std::pair<u32, u32> key = {buffer, offset}; |
|||
const auto iter = keys.find(key); |
|||
if (iter != keys.end()) { |
|||
return iter->second; |
|||
} |
|||
if (!engine) { |
|||
return std::nullopt; |
|||
} |
|||
const u32 value = engine->AccessConstBuffer32(stage, buffer, offset); |
|||
keys.emplace(key, value); |
|||
return value; |
|||
} |
|||
|
|||
std::optional<SamplerDescriptor> Registry::ObtainBoundSampler(u32 offset) { |
|||
const u32 key = offset; |
|||
const auto iter = bound_samplers.find(key); |
|||
if (iter != bound_samplers.end()) { |
|||
return iter->second; |
|||
} |
|||
if (!engine) { |
|||
return std::nullopt; |
|||
} |
|||
const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset); |
|||
bound_samplers.emplace(key, value); |
|||
return value; |
|||
} |
|||
|
|||
std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer, |
|||
u32 offset) { |
|||
const std::pair key = {buffer, offset}; |
|||
const auto iter = bindless_samplers.find(key); |
|||
if (iter != bindless_samplers.end()) { |
|||
return iter->second; |
|||
} |
|||
if (!engine) { |
|||
return std::nullopt; |
|||
} |
|||
const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset); |
|||
bindless_samplers.emplace(key, value); |
|||
return value; |
|||
} |
|||
|
|||
void Registry::InsertKey(u32 buffer, u32 offset, u32 value) { |
|||
keys.insert_or_assign({buffer, offset}, value); |
|||
} |
|||
|
|||
void Registry::InsertBoundSampler(u32 offset, SamplerDescriptor sampler) { |
|||
bound_samplers.insert_or_assign(offset, sampler); |
|||
} |
|||
|
|||
void Registry::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDescriptor sampler) { |
|||
bindless_samplers.insert_or_assign({buffer, offset}, sampler); |
|||
} |
|||
|
|||
bool Registry::IsConsistent() const { |
|||
if (!engine) { |
|||
return true; |
|||
} |
|||
return std::all_of(keys.begin(), keys.end(), |
|||
[this](const auto& pair) { |
|||
const auto [cbuf, offset] = pair.first; |
|||
const auto value = pair.second; |
|||
return value == engine->AccessConstBuffer32(stage, cbuf, offset); |
|||
}) && |
|||
std::all_of(bound_samplers.begin(), bound_samplers.end(), |
|||
[this](const auto& sampler) { |
|||
const auto [key, value] = sampler; |
|||
return value == engine->AccessBoundSampler(stage, key); |
|||
}) && |
|||
std::all_of(bindless_samplers.begin(), bindless_samplers.end(), |
|||
[this](const auto& sampler) { |
|||
const auto [cbuf, offset] = sampler.first; |
|||
const auto value = sampler.second; |
|||
return value == engine->AccessBindlessSampler(stage, cbuf, offset); |
|||
}); |
|||
} |
|||
|
|||
bool Registry::HasEqualKeys(const Registry& rhs) const { |
|||
return std::tie(keys, bound_samplers, bindless_samplers) == |
|||
std::tie(rhs.keys, rhs.bound_samplers, rhs.bindless_samplers); |
|||
} |
|||
|
|||
const GraphicsInfo& Registry::GetGraphicsInfo() const { |
|||
ASSERT(stage != Tegra::Engines::ShaderType::Compute); |
|||
return graphics_info; |
|||
} |
|||
|
|||
const ComputeInfo& Registry::GetComputeInfo() const { |
|||
ASSERT(stage == Tegra::Engines::ShaderType::Compute); |
|||
return compute_info; |
|||
} |
|||
|
|||
} // namespace VideoCommon::Shader
|
|||
@ -0,0 +1,137 @@ |
|||
// Copyright 2019 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <optional> |
|||
#include <type_traits> |
|||
#include <unordered_map> |
|||
#include <utility> |
|||
|
|||
#include "common/common_types.h" |
|||
#include "common/hash.h" |
|||
#include "video_core/engines/const_buffer_engine_interface.h" |
|||
#include "video_core/engines/maxwell_3d.h" |
|||
#include "video_core/engines/shader_type.h" |
|||
#include "video_core/guest_driver.h" |
|||
|
|||
namespace VideoCommon::Shader { |
|||
|
|||
using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>; |
|||
using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>; |
|||
using BindlessSamplerMap = |
|||
std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>; |
|||
|
|||
struct GraphicsInfo { |
|||
using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
|||
|
|||
std::array<Maxwell::TransformFeedbackLayout, Maxwell::NumTransformFeedbackBuffers> |
|||
tfb_layouts{}; |
|||
std::array<std::array<u8, 128>, Maxwell::NumTransformFeedbackBuffers> tfb_varying_locs{}; |
|||
Maxwell::PrimitiveTopology primitive_topology{}; |
|||
Maxwell::TessellationPrimitive tessellation_primitive{}; |
|||
Maxwell::TessellationSpacing tessellation_spacing{}; |
|||
bool tfb_enabled = false; |
|||
bool tessellation_clockwise = false; |
|||
}; |
|||
static_assert(std::is_trivially_copyable_v<GraphicsInfo> && |
|||
std::is_standard_layout_v<GraphicsInfo>); |
|||
|
|||
struct ComputeInfo { |
|||
std::array<u32, 3> workgroup_size{}; |
|||
u32 shared_memory_size_in_words = 0; |
|||
u32 local_memory_size_in_words = 0; |
|||
}; |
|||
static_assert(std::is_trivially_copyable_v<ComputeInfo> && std::is_standard_layout_v<ComputeInfo>); |
|||
|
|||
struct SerializedRegistryInfo { |
|||
VideoCore::GuestDriverProfile guest_driver_profile; |
|||
u32 bound_buffer = 0; |
|||
GraphicsInfo graphics; |
|||
ComputeInfo compute; |
|||
}; |
|||
|
|||
/** |
|||
* The Registry is a class use to interface the 3D and compute engines with the shader compiler. |
|||
* With it, the shader can obtain required data from GPU state and store it for disk shader |
|||
* compilation. |
|||
*/ |
|||
class Registry { |
|||
public: |
|||
explicit Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info); |
|||
|
|||
explicit Registry(Tegra::Engines::ShaderType shader_stage, |
|||
Tegra::Engines::ConstBufferEngineInterface& engine); |
|||
|
|||
~Registry(); |
|||
|
|||
/// Retrieves a key from the registry, if it's registered, it will give the registered value, if |
|||
/// not it will obtain it from maxwell3d and register it. |
|||
std::optional<u32> ObtainKey(u32 buffer, u32 offset); |
|||
|
|||
std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset); |
|||
|
|||
std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); |
|||
|
|||
/// Inserts a key. |
|||
void InsertKey(u32 buffer, u32 offset, u32 value); |
|||
|
|||
/// Inserts a bound sampler key. |
|||
void InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler); |
|||
|
|||
/// Inserts a bindless sampler key. |
|||
void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); |
|||
|
|||
/// Checks keys and samplers against engine's current const buffers. |
|||
/// Returns true if they are the same value, false otherwise. |
|||
bool IsConsistent() const; |
|||
|
|||
/// Returns true if the keys are equal to the other ones in the registry. |
|||
bool HasEqualKeys(const Registry& rhs) const; |
|||
|
|||
/// Returns graphics information from this shader |
|||
const GraphicsInfo& GetGraphicsInfo() const; |
|||
|
|||
/// Returns compute information from this shader |
|||
const ComputeInfo& GetComputeInfo() const; |
|||
|
|||
/// Gives an getter to the const buffer keys in the database. |
|||
const KeyMap& GetKeys() const { |
|||
return keys; |
|||
} |
|||
|
|||
/// Gets samplers database. |
|||
const BoundSamplerMap& GetBoundSamplers() const { |
|||
return bound_samplers; |
|||
} |
|||
|
|||
/// Gets bindless samplers database. |
|||
const BindlessSamplerMap& GetBindlessSamplers() const { |
|||
return bindless_samplers; |
|||
} |
|||
|
|||
/// Gets bound buffer used on this shader |
|||
u32 GetBoundBuffer() const { |
|||
return bound_buffer; |
|||
} |
|||
|
|||
/// Obtains access to the guest driver's profile. |
|||
VideoCore::GuestDriverProfile& AccessGuestDriverProfile() { |
|||
return engine ? engine->AccessGuestDriverProfile() : stored_guest_driver_profile; |
|||
} |
|||
|
|||
private: |
|||
const Tegra::Engines::ShaderType stage; |
|||
VideoCore::GuestDriverProfile stored_guest_driver_profile; |
|||
Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; |
|||
KeyMap keys; |
|||
BoundSamplerMap bound_samplers; |
|||
BindlessSamplerMap bindless_samplers; |
|||
u32 bound_buffer; |
|||
GraphicsInfo graphics_info; |
|||
ComputeInfo compute_info; |
|||
}; |
|||
|
|||
} // namespace VideoCommon::Shader |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue