Browse Source
Merge pull request #2976 from FernandoS27/cache-fast-brx-rebased
Merge pull request #2976 from FernandoS27/cache-fast-brx-rebased
Implement Fast BRX, fix TXQ and addapt the Shader Cache for itpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1486 additions and 866 deletions
-
6CMakeModules/GenerateSCMRev.cmake
-
6src/common/CMakeLists.txt
-
11src/common/hash.h
-
7src/video_core/CMakeLists.txt
-
119src/video_core/engines/const_buffer_engine_interface.h
-
20src/video_core/engines/kepler_compute.cpp
-
14src/video_core/engines/kepler_compute.h
-
21src/video_core/engines/maxwell_3d.cpp
-
14src/video_core/engines/maxwell_3d.h
-
9src/video_core/renderer_opengl/gl_rasterizer.cpp
-
533src/video_core/renderer_opengl/gl_shader_cache.cpp
-
77src/video_core/renderer_opengl/gl_shader_cache.h
-
75src/video_core/renderer_opengl/gl_shader_decompiler.cpp
-
9src/video_core/renderer_opengl/gl_shader_decompiler.h
-
449src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
-
44src/video_core/renderer_opengl/gl_shader_disk_cache.h
-
86src/video_core/renderer_opengl/gl_shader_gen.cpp
-
36src/video_core/renderer_opengl/gl_shader_gen.h
-
7src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
-
4src/video_core/shader/ast.cpp
-
110src/video_core/shader/const_buffer_locker.cpp
-
80src/video_core/shader/const_buffer_locker.h
-
383src/video_core/shader/control_flow.cpp
-
69src/video_core/shader/control_flow.h
-
41src/video_core/shader/decode.cpp
-
70src/video_core/shader/decode/texture.cpp
-
21src/video_core/shader/expr.h
-
7src/video_core/shader/shader_ir.cpp
-
24src/video_core/shader/shader_ir.h
@ -0,0 +1,119 @@ |
|||||
|
// Copyright 2019 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <type_traits> |
||||
|
#include "common/bit_field.h" |
||||
|
#include "common/common_types.h" |
||||
|
#include "video_core/engines/shader_bytecode.h" |
||||
|
#include "video_core/textures/texture.h" |
||||
|
|
||||
|
namespace Tegra::Engines { |
||||
|
|
||||
|
enum class ShaderType : u32 { |
||||
|
Vertex = 0, |
||||
|
TesselationControl = 1, |
||||
|
TesselationEval = 2, |
||||
|
Geometry = 3, |
||||
|
Fragment = 4, |
||||
|
Compute = 5, |
||||
|
}; |
||||
|
|
||||
|
struct SamplerDescriptor { |
||||
|
union { |
||||
|
BitField<0, 20, Tegra::Shader::TextureType> texture_type; |
||||
|
BitField<20, 1, u32> is_array; |
||||
|
BitField<21, 1, u32> is_buffer; |
||||
|
BitField<22, 1, u32> is_shadow; |
||||
|
u32 raw{}; |
||||
|
}; |
||||
|
|
||||
|
bool operator==(const SamplerDescriptor& rhs) const noexcept { |
||||
|
return raw == rhs.raw; |
||||
|
} |
||||
|
|
||||
|
bool operator!=(const SamplerDescriptor& rhs) const noexcept { |
||||
|
return !operator==(rhs); |
||||
|
} |
||||
|
|
||||
|
static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) { |
||||
|
SamplerDescriptor result; |
||||
|
switch (tic_texture_type) { |
||||
|
case Tegra::Texture::TextureType::Texture1D: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); |
||||
|
result.is_array.Assign(0); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::Texture2D: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); |
||||
|
result.is_array.Assign(0); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::Texture3D: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture3D); |
||||
|
result.is_array.Assign(0); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::TextureCubemap: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube); |
||||
|
result.is_array.Assign(0); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::Texture1DArray: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); |
||||
|
result.is_array.Assign(1); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::Texture2DArray: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); |
||||
|
result.is_array.Assign(1); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::Texture1DBuffer: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D); |
||||
|
result.is_array.Assign(0); |
||||
|
result.is_buffer.Assign(1); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::Texture2DNoMipmap: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); |
||||
|
result.is_array.Assign(0); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
case Tegra::Texture::TextureType::TextureCubeArray: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube); |
||||
|
result.is_array.Assign(1); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
default: |
||||
|
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D); |
||||
|
result.is_array.Assign(0); |
||||
|
result.is_buffer.Assign(0); |
||||
|
result.is_shadow.Assign(0); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
static_assert(std::is_trivially_copyable_v<SamplerDescriptor>); |
||||
|
|
||||
|
class ConstBufferEngineInterface { |
||||
|
public: |
||||
|
virtual ~ConstBufferEngineInterface() = default; |
||||
|
virtual u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const = 0; |
||||
|
virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0; |
||||
|
virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, |
||||
|
u64 offset) const = 0; |
||||
|
virtual u32 GetBoundBuffer() const = 0; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Tegra::Engines |
||||
@ -0,0 +1,110 @@ |
|||||
|
// Copyright 2019 yuzu Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#pragma once
|
||||
|
|
||||
|
#include <algorithm>
|
||||
|
#include <memory>
|
||||
|
#include "common/assert.h"
|
||||
|
#include "common/common_types.h"
|
||||
|
#include "video_core/engines/maxwell_3d.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; |
||||
|
} |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
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 keys == rhs.keys && bound_samplers == rhs.bound_samplers && |
||||
|
bindless_samplers == rhs.bindless_samplers; |
||||
|
} |
||||
|
|
||||
|
} // namespace VideoCommon::Shader
|
||||
@ -0,0 +1,80 @@ |
|||||
|
// Copyright 2019 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <unordered_map> |
||||
|
#include "common/common_types.h" |
||||
|
#include "common/hash.h" |
||||
|
#include "video_core/engines/const_buffer_engine_interface.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); |
||||
|
|
||||
|
/// 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 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; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
const Tegra::Engines::ShaderType stage; |
||||
|
Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; |
||||
|
KeyMap keys; |
||||
|
BoundSamplerMap bound_samplers; |
||||
|
BindlessSamplerMap bindless_samplers; |
||||
|
}; |
||||
|
|
||||
|
} // namespace VideoCommon::Shader |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue