2018-08-23 23:30:27 +02:00
|
|
|
// Copyright 2018 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
#include <mutex>
|
2019-09-25 04:34:18 +02:00
|
|
|
#include <optional>
|
|
|
|
#include <string>
|
2019-04-06 22:59:56 +02:00
|
|
|
#include <thread>
|
2019-09-25 04:34:18 +02:00
|
|
|
#include <unordered_set>
|
2018-12-09 22:33:10 +01:00
|
|
|
#include <boost/functional/hash.hpp>
|
2018-08-23 23:30:27 +02:00
|
|
|
#include "common/assert.h"
|
2019-04-06 22:59:56 +02:00
|
|
|
#include "common/scope_exit.h"
|
2018-08-23 23:30:27 +02:00
|
|
|
#include "core/core.h"
|
2019-04-06 22:59:56 +02:00
|
|
|
#include "core/frontend/emu_window.h"
|
2019-09-23 21:40:58 +02:00
|
|
|
#include "video_core/engines/kepler_compute.h"
|
2018-08-23 23:30:27 +02:00
|
|
|
#include "video_core/engines/maxwell_3d.h"
|
2019-04-06 05:59:54 +02:00
|
|
|
#include "video_core/memory_manager.h"
|
2018-11-08 12:08:00 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
2018-08-23 23:30:27 +02:00
|
|
|
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
2018-12-26 05:57:14 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
2019-01-14 04:58:15 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
2018-10-29 01:54:08 +01:00
|
|
|
#include "video_core/renderer_opengl/utils.h"
|
2018-12-21 02:29:15 +01:00
|
|
|
#include "video_core/shader/shader_ir.h"
|
2018-08-23 23:30:27 +02:00
|
|
|
|
|
|
|
namespace OpenGL {
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
using Tegra::Engines::ShaderType;
|
|
|
|
using VideoCommon::Shader::ConstBufferLocker;
|
2018-12-21 02:29:15 +01:00
|
|
|
using VideoCommon::Shader::ProgramCode;
|
2019-09-25 04:34:18 +02:00
|
|
|
using VideoCommon::Shader::ShaderIR;
|
|
|
|
|
|
|
|
namespace {
|
2018-12-21 02:29:15 +01:00
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
// One UBO is always reserved for emulation values on staged shaders
|
|
|
|
constexpr u32 STAGE_RESERVED_UBOS = 1;
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
constexpr u32 STAGE_MAIN_OFFSET = 10;
|
|
|
|
constexpr u32 KERNEL_MAIN_OFFSET = 0;
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
constexpr VideoCommon::Shader::CompilerSettings COMPILER_SETTINGS{};
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2018-08-23 23:30:27 +02:00
|
|
|
/// Gets the address for the specified shader stage program
|
2019-05-30 19:01:40 +02:00
|
|
|
GPUVAddr GetShaderAddress(Core::System& system, Maxwell::ShaderProgram program) {
|
|
|
|
const auto& gpu{system.GPU().Maxwell3D()};
|
2019-02-24 06:15:35 +01:00
|
|
|
const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
|
|
|
|
return gpu.regs.code_address.CodeAddress() + shader_config.offset;
|
2018-08-23 23:30:27 +02:00
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
/// Gets if the current instruction offset is a scheduler instruction
|
|
|
|
constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
|
|
|
|
// Sched instructions appear once every 4 instructions.
|
|
|
|
constexpr std::size_t SchedPeriod = 4;
|
|
|
|
const std::size_t absolute_offset = offset - main_offset;
|
|
|
|
return (absolute_offset % SchedPeriod) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Calculates the size of a program stream
|
|
|
|
std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) {
|
|
|
|
constexpr std::size_t start_offset = 10;
|
|
|
|
// This is the encoded version of BRA that jumps to itself. All Nvidia
|
|
|
|
// shaders end with one.
|
|
|
|
constexpr u64 self_jumping_branch = 0xE2400FFFFF07000FULL;
|
|
|
|
constexpr u64 mask = 0xFFFFFFFFFF7FFFFFULL;
|
|
|
|
std::size_t offset = start_offset;
|
|
|
|
while (offset < program.size()) {
|
|
|
|
const u64 instruction = program[offset];
|
|
|
|
if (!IsSchedInstruction(offset, start_offset)) {
|
|
|
|
if ((instruction & mask) == self_jumping_branch) {
|
|
|
|
// End on Maxwell's "nop" instruction
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (instruction == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
// The last instruction is included in the program size
|
|
|
|
return std::min(offset + 1, program.size());
|
|
|
|
}
|
|
|
|
|
2018-08-23 23:30:27 +02:00
|
|
|
/// Gets the shader program code from memory for the specified address
|
2019-04-16 16:11:35 +02:00
|
|
|
ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, const GPUVAddr gpu_addr,
|
|
|
|
const u8* host_ptr) {
|
2018-12-21 02:29:15 +01:00
|
|
|
ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
|
2019-03-27 19:59:00 +01:00
|
|
|
ASSERT_OR_EXECUTE(host_ptr != nullptr, {
|
|
|
|
std::fill(program_code.begin(), program_code.end(), 0);
|
|
|
|
return program_code;
|
|
|
|
});
|
2019-04-16 16:11:35 +02:00
|
|
|
memory_manager.ReadBlockUnsafe(gpu_addr, program_code.data(),
|
|
|
|
program_code.size() * sizeof(u64));
|
2019-09-25 04:34:18 +02:00
|
|
|
program_code.resize(CalculateProgramSize(program_code));
|
2018-08-23 23:30:27 +02:00
|
|
|
return program_code;
|
|
|
|
}
|
|
|
|
|
2019-01-05 05:00:06 +01:00
|
|
|
/// Gets the shader type from a Maxwell program type
|
2019-07-15 03:25:13 +02:00
|
|
|
constexpr GLenum GetShaderType(ProgramType program_type) {
|
2019-01-05 05:00:06 +01:00
|
|
|
switch (program_type) {
|
2019-07-15 03:25:13 +02:00
|
|
|
case ProgramType::VertexA:
|
|
|
|
case ProgramType::VertexB:
|
2019-01-05 05:00:06 +01:00
|
|
|
return GL_VERTEX_SHADER;
|
2019-07-15 03:25:13 +02:00
|
|
|
case ProgramType::Geometry:
|
2019-01-05 05:00:06 +01:00
|
|
|
return GL_GEOMETRY_SHADER;
|
2019-07-15 03:25:13 +02:00
|
|
|
case ProgramType::Fragment:
|
2019-01-05 05:00:06 +01:00
|
|
|
return GL_FRAGMENT_SHADER;
|
2019-07-15 03:25:13 +02:00
|
|
|
case ProgramType::Compute:
|
|
|
|
return GL_COMPUTE_SHADER;
|
2019-01-05 05:00:06 +01:00
|
|
|
default:
|
|
|
|
return GL_NONE;
|
2018-08-23 23:30:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 04:58:15 +01:00
|
|
|
/// Describes primitive behavior on geometry shaders
|
|
|
|
constexpr std::tuple<const char*, const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) {
|
|
|
|
switch (primitive_mode) {
|
|
|
|
case GL_POINTS:
|
|
|
|
return {"points", "Points", 1};
|
|
|
|
case GL_LINES:
|
|
|
|
case GL_LINE_STRIP:
|
|
|
|
return {"lines", "Lines", 2};
|
|
|
|
case GL_LINES_ADJACENCY:
|
|
|
|
case GL_LINE_STRIP_ADJACENCY:
|
|
|
|
return {"lines_adjacency", "LinesAdj", 4};
|
|
|
|
case GL_TRIANGLES:
|
|
|
|
case GL_TRIANGLE_STRIP:
|
|
|
|
case GL_TRIANGLE_FAN:
|
|
|
|
return {"triangles", "Triangles", 3};
|
|
|
|
case GL_TRIANGLES_ADJACENCY:
|
|
|
|
case GL_TRIANGLE_STRIP_ADJACENCY:
|
|
|
|
return {"triangles_adjacency", "TrianglesAdj", 6};
|
|
|
|
default:
|
|
|
|
return {"points", "Invalid", 1};
|
|
|
|
}
|
|
|
|
}
|
2018-08-23 23:30:27 +02:00
|
|
|
|
2019-07-15 07:27:18 +02:00
|
|
|
ProgramType GetProgramType(Maxwell::ShaderProgram program) {
|
2019-07-15 03:25:13 +02:00
|
|
|
switch (program) {
|
|
|
|
case Maxwell::ShaderProgram::VertexA:
|
|
|
|
return ProgramType::VertexA;
|
|
|
|
case Maxwell::ShaderProgram::VertexB:
|
|
|
|
return ProgramType::VertexB;
|
|
|
|
case Maxwell::ShaderProgram::TesselationControl:
|
|
|
|
return ProgramType::TessellationControl;
|
|
|
|
case Maxwell::ShaderProgram::TesselationEval:
|
|
|
|
return ProgramType::TessellationEval;
|
|
|
|
case Maxwell::ShaderProgram::Geometry:
|
|
|
|
return ProgramType::Geometry;
|
|
|
|
case Maxwell::ShaderProgram::Fragment:
|
|
|
|
return ProgramType::Fragment;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
2019-07-15 07:27:18 +02:00
|
|
|
return {};
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
|
2019-01-14 04:58:15 +01:00
|
|
|
/// Hashes one (or two) program streams
|
2019-07-15 03:25:13 +02:00
|
|
|
u64 GetUniqueIdentifier(ProgramType program_type, const ProgramCode& code,
|
2019-09-25 04:34:18 +02:00
|
|
|
const ProgramCode& code_b) {
|
|
|
|
u64 unique_identifier = boost::hash_value(code);
|
|
|
|
if (program_type == ProgramType::VertexA) {
|
|
|
|
// VertexA programs include two programs
|
|
|
|
boost::hash_combine(unique_identifier, boost::hash_value(code_b));
|
2019-06-25 13:57:32 +02:00
|
|
|
}
|
2019-09-25 04:34:18 +02:00
|
|
|
return unique_identifier;
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates an unspecialized program from code streams
|
2019-09-25 04:34:18 +02:00
|
|
|
std::string GenerateGLSL(const Device& device, ProgramType program_type, const ShaderIR& ir,
|
|
|
|
const std::optional<ShaderIR>& ir_b) {
|
2019-01-14 04:58:15 +01:00
|
|
|
switch (program_type) {
|
2019-07-15 03:25:13 +02:00
|
|
|
case ProgramType::VertexA:
|
2019-09-25 04:34:18 +02:00
|
|
|
case ProgramType::VertexB:
|
|
|
|
return GLShader::GenerateVertexShader(device, ir, ir_b ? &*ir_b : nullptr);
|
|
|
|
case ProgramType::Geometry:
|
|
|
|
return GLShader::GenerateGeometryShader(device, ir);
|
|
|
|
case ProgramType::Fragment:
|
|
|
|
return GLShader::GenerateFragmentShader(device, ir);
|
|
|
|
case ProgramType::Compute:
|
|
|
|
return GLShader::GenerateComputeShader(device, ir);
|
2018-08-23 23:30:27 +02:00
|
|
|
default:
|
2019-07-15 03:25:13 +02:00
|
|
|
UNIMPLEMENTED_MSG("Unimplemented program_type={}", static_cast<u32>(program_type));
|
2019-01-14 04:58:15 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
constexpr const char* GetProgramTypeName(ProgramType program_type) {
|
|
|
|
switch (program_type) {
|
|
|
|
case ProgramType::VertexA:
|
|
|
|
case ProgramType::VertexB:
|
|
|
|
return "VS";
|
|
|
|
case ProgramType::TessellationControl:
|
|
|
|
return "TCS";
|
|
|
|
case ProgramType::TessellationEval:
|
|
|
|
return "TES";
|
|
|
|
case ProgramType::Geometry:
|
|
|
|
return "GS";
|
|
|
|
case ProgramType::Fragment:
|
|
|
|
return "FS";
|
|
|
|
case ProgramType::Compute:
|
|
|
|
return "CS";
|
|
|
|
}
|
|
|
|
return "UNK";
|
|
|
|
}
|
|
|
|
|
|
|
|
Tegra::Engines::ShaderType GetEnginesShaderType(ProgramType program_type) {
|
|
|
|
switch (program_type) {
|
|
|
|
case ProgramType::VertexA:
|
|
|
|
case ProgramType::VertexB:
|
|
|
|
return Tegra::Engines::ShaderType::Vertex;
|
|
|
|
case ProgramType::TessellationControl:
|
|
|
|
return Tegra::Engines::ShaderType::TesselationControl;
|
|
|
|
case ProgramType::TessellationEval:
|
|
|
|
return Tegra::Engines::ShaderType::TesselationEval;
|
|
|
|
case ProgramType::Geometry:
|
|
|
|
return Tegra::Engines::ShaderType::Geometry;
|
|
|
|
case ProgramType::Fragment:
|
|
|
|
return Tegra::Engines::ShaderType::Fragment;
|
|
|
|
case ProgramType::Compute:
|
|
|
|
return Tegra::Engines::ShaderType::Compute;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetShaderId(u64 unique_identifier, ProgramType program_type) {
|
|
|
|
return fmt::format("{}{:016X}", GetProgramTypeName(program_type), unique_identifier);
|
|
|
|
}
|
|
|
|
|
2019-09-26 05:23:08 +02:00
|
|
|
Tegra::Engines::ConstBufferEngineInterface& GetConstBufferEngineInterface(
|
|
|
|
Core::System& system, ProgramType program_type) {
|
|
|
|
if (program_type == ProgramType::Compute) {
|
|
|
|
return system.GPU().KeplerCompute();
|
|
|
|
} else {
|
|
|
|
return system.GPU().Maxwell3D();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ProgramType program_type) {
|
|
|
|
return std::make_unique<ConstBufferLocker>(GetEnginesShaderType(program_type),
|
|
|
|
GetConstBufferEngineInterface(system, program_type));
|
|
|
|
}
|
|
|
|
|
|
|
|
void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) {
|
|
|
|
for (const auto& key : usage.keys) {
|
|
|
|
const auto [buffer, offset] = key.first;
|
|
|
|
locker.InsertKey(buffer, offset, key.second);
|
|
|
|
}
|
|
|
|
for (const auto& [offset, sampler] : usage.bound_samplers) {
|
|
|
|
locker.InsertBoundSampler(offset, sampler);
|
|
|
|
}
|
|
|
|
for (const auto& [key, sampler] : usage.bindless_samplers) {
|
|
|
|
const auto [buffer, offset] = key;
|
|
|
|
locker.InsertBindlessSampler(buffer, offset, sampler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramType program_type,
|
|
|
|
const ProgramCode& program_code, const ProgramCode& program_code_b,
|
2019-11-13 03:26:56 +01:00
|
|
|
ConstBufferLocker& locker, const ProgramVariant& variant,
|
2019-09-25 04:34:18 +02:00
|
|
|
bool hint_retrievable = false) {
|
|
|
|
LOG_INFO(Render_OpenGL, "called. {}", GetShaderId(unique_identifier, program_type));
|
|
|
|
|
|
|
|
const bool is_compute = program_type == ProgramType::Compute;
|
|
|
|
const u32 main_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
|
|
|
|
const ShaderIR ir(program_code, main_offset, COMPILER_SETTINGS, locker);
|
|
|
|
std::optional<ShaderIR> ir_b;
|
|
|
|
if (!program_code_b.empty()) {
|
|
|
|
ir_b.emplace(program_code_b, main_offset, COMPILER_SETTINGS, locker);
|
|
|
|
}
|
|
|
|
const auto entries = GLShader::GetEntries(ir);
|
|
|
|
|
|
|
|
std::string source = fmt::format(R"(// {}
|
|
|
|
#version 430 core
|
2019-09-18 06:07:01 +02:00
|
|
|
#extension GL_ARB_separate_shader_objects : enable
|
2019-09-25 04:34:18 +02:00
|
|
|
)",
|
|
|
|
GetShaderId(unique_identifier, program_type));
|
2019-11-08 00:07:45 +01:00
|
|
|
if (device.HasShaderBallot()) {
|
|
|
|
source += "#extension GL_ARB_shader_ballot : require\n";
|
|
|
|
}
|
|
|
|
if (device.HasVertexViewportLayer()) {
|
|
|
|
source += "#extension GL_ARB_shader_viewport_layer_array : require\n";
|
|
|
|
}
|
|
|
|
if (device.HasImageLoadFormatted()) {
|
|
|
|
source += "#extension GL_EXT_shader_image_load_formatted : require\n";
|
|
|
|
}
|
|
|
|
if (device.HasWarpIntrinsics()) {
|
|
|
|
source += "#extension GL_NV_gpu_shader5 : require\n"
|
|
|
|
"#extension GL_NV_shader_thread_group : require\n"
|
|
|
|
"#extension GL_NV_shader_thread_shuffle : require\n";
|
|
|
|
}
|
2019-07-15 03:25:13 +02:00
|
|
|
source += '\n';
|
|
|
|
|
2019-11-13 03:26:56 +01:00
|
|
|
auto base_bindings = variant.base_bindings;
|
2019-09-25 04:34:18 +02:00
|
|
|
if (!is_compute) {
|
2019-07-15 03:25:13 +02:00
|
|
|
source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++);
|
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
|
|
|
|
for (const auto& cbuf : entries.const_buffers) {
|
|
|
|
source +=
|
|
|
|
fmt::format("#define CBUF_BINDING_{} {}\n", cbuf.GetIndex(), base_bindings.cbuf++);
|
|
|
|
}
|
|
|
|
for (const auto& gmem : entries.global_memory_entries) {
|
|
|
|
source += fmt::format("#define GMEM_BINDING_{}_{} {}\n", gmem.GetCbufIndex(),
|
|
|
|
gmem.GetCbufOffset(), base_bindings.gmem++);
|
|
|
|
}
|
|
|
|
for (const auto& sampler : entries.samplers) {
|
|
|
|
source += fmt::format("#define SAMPLER_BINDING_{} {}\n", sampler.GetIndex(),
|
|
|
|
base_bindings.sampler++);
|
|
|
|
}
|
2019-04-27 07:37:15 +02:00
|
|
|
for (const auto& image : entries.images) {
|
|
|
|
source +=
|
|
|
|
fmt::format("#define IMAGE_BINDING_{} {}\n", image.GetIndex(), base_bindings.image++);
|
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
if (program_type == ProgramType::Geometry) {
|
2019-01-15 06:42:25 +01:00
|
|
|
const auto [glsl_topology, debug_name, max_vertices] =
|
2019-11-13 03:26:56 +01:00
|
|
|
GetPrimitiveDescription(variant.primitive_mode);
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-11-13 03:26:56 +01:00
|
|
|
source += fmt::format("layout ({}) in;\n\n", glsl_topology);
|
|
|
|
source += fmt::format("#define MAX_VERTEX_INPUT {}\n", max_vertices);
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
2019-07-15 03:25:13 +02:00
|
|
|
if (program_type == ProgramType::Compute) {
|
2019-11-13 03:26:56 +01:00
|
|
|
source +=
|
|
|
|
fmt::format("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n",
|
|
|
|
variant.block_x, variant.block_y, variant.block_z);
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-07-18 06:16:15 +02:00
|
|
|
source += '\n';
|
2019-09-25 04:34:18 +02:00
|
|
|
source += GenerateGLSL(device, program_type, ir, ir_b);
|
2019-01-14 04:58:15 +01:00
|
|
|
|
|
|
|
OGLShader shader;
|
|
|
|
shader.Create(source.c_str(), GetShaderType(program_type));
|
|
|
|
|
|
|
|
auto program = std::make_shared<OGLProgram>();
|
|
|
|
program->Create(true, hint_retrievable, shader.handle);
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
std::unordered_set<GLenum> GetSupportedFormats() {
|
2019-01-14 04:58:15 +01:00
|
|
|
GLint num_formats{};
|
|
|
|
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
|
|
|
|
|
|
|
|
std::vector<GLint> formats(num_formats);
|
|
|
|
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
std::unordered_set<GLenum> supported_formats;
|
|
|
|
for (const GLint format : formats) {
|
2019-01-14 04:58:15 +01:00
|
|
|
supported_formats.insert(static_cast<GLenum>(format));
|
2019-09-25 04:34:18 +02:00
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
return supported_formats;
|
|
|
|
}
|
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
} // Anonymous namespace
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
CachedShader::CachedShader(const ShaderParameters& params, ProgramType program_type,
|
2019-09-25 04:34:18 +02:00
|
|
|
GLShader::ShaderEntries entries, ProgramCode program_code,
|
|
|
|
ProgramCode program_code_b)
|
|
|
|
: RasterizerCacheObject{params.host_ptr}, system{params.system},
|
|
|
|
disk_cache{params.disk_cache}, device{params.device}, cpu_addr{params.cpu_addr},
|
|
|
|
unique_identifier{params.unique_identifier}, program_type{program_type}, entries{entries},
|
|
|
|
program_code{std::move(program_code)}, program_code_b{std::move(program_code_b)} {
|
2019-09-26 05:23:08 +02:00
|
|
|
if (!params.precompiled_variants) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (const auto& pair : *params.precompiled_variants) {
|
|
|
|
auto locker = MakeLocker(system, program_type);
|
|
|
|
const auto& usage = pair->first;
|
|
|
|
FillLocker(*locker, usage);
|
|
|
|
|
|
|
|
std::unique_ptr<LockerVariant>* locker_variant = nullptr;
|
|
|
|
const auto it =
|
|
|
|
std::find_if(locker_variants.begin(), locker_variants.end(), [&](const auto& variant) {
|
|
|
|
return variant->locker->HasEqualKeys(*locker);
|
|
|
|
});
|
|
|
|
if (it == locker_variants.end()) {
|
|
|
|
locker_variant = &locker_variants.emplace_back();
|
|
|
|
*locker_variant = std::make_unique<LockerVariant>();
|
|
|
|
locker_variant->get()->locker = std::move(locker);
|
|
|
|
} else {
|
|
|
|
locker_variant = &*it;
|
2019-09-25 04:34:18 +02:00
|
|
|
}
|
2019-09-26 05:23:08 +02:00
|
|
|
locker_variant->get()->programs.emplace(usage.variant, pair->second);
|
2019-09-25 04:34:18 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-31 02:56:37 +02:00
|
|
|
|
|
|
|
Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
|
|
|
|
Maxwell::ShaderProgram program_type,
|
2019-09-25 04:34:18 +02:00
|
|
|
ProgramCode program_code, ProgramCode program_code_b) {
|
2019-05-31 02:56:37 +02:00
|
|
|
params.disk_cache.SaveRaw(ShaderDiskCacheRaw(
|
2019-09-25 04:34:18 +02:00
|
|
|
params.unique_identifier, GetProgramType(program_type), program_code, program_code_b));
|
|
|
|
|
2019-11-07 03:41:32 +01:00
|
|
|
ConstBufferLocker locker(GetEnginesShaderType(GetProgramType(program_type)),
|
|
|
|
params.system.GPU().Maxwell3D());
|
2019-09-25 04:34:18 +02:00
|
|
|
const ShaderIR ir(program_code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, locker);
|
|
|
|
// TODO(Rodrigo): Handle VertexA shaders
|
|
|
|
// std::optional<ShaderIR> ir_b;
|
|
|
|
// if (!program_code_b.empty()) {
|
|
|
|
// ir_b.emplace(program_code_b, STAGE_MAIN_OFFSET);
|
|
|
|
// }
|
2019-07-15 03:25:13 +02:00
|
|
|
return std::shared_ptr<CachedShader>(
|
2019-09-25 04:34:18 +02:00
|
|
|
new CachedShader(params, GetProgramType(program_type), GLShader::GetEntries(ir),
|
|
|
|
std::move(program_code), std::move(program_code_b)));
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) {
|
|
|
|
params.disk_cache.SaveRaw(
|
|
|
|
ShaderDiskCacheRaw(params.unique_identifier, ProgramType::Compute, code));
|
2019-07-15 03:25:13 +02:00
|
|
|
|
2019-11-07 03:41:32 +01:00
|
|
|
ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute,
|
|
|
|
params.system.GPU().KeplerCompute());
|
2019-09-25 04:34:18 +02:00
|
|
|
const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, locker);
|
|
|
|
return std::shared_ptr<CachedShader>(new CachedShader(
|
|
|
|
params, ProgramType::Compute, GLShader::GetEntries(ir), std::move(code), {}));
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
Shader CachedShader::CreateFromCache(const ShaderParameters& params,
|
|
|
|
const UnspecializedShader& unspecialized) {
|
|
|
|
return std::shared_ptr<CachedShader>(new CachedShader(params, unspecialized.program_type,
|
|
|
|
unspecialized.entries, unspecialized.code,
|
|
|
|
unspecialized.code_b));
|
2019-01-05 05:00:06 +01:00
|
|
|
}
|
2018-08-23 23:30:27 +02:00
|
|
|
|
2019-11-13 03:26:56 +01:00
|
|
|
std::tuple<GLuint, BaseBindings> CachedShader::GetHandle(const ProgramVariant& variant) {
|
|
|
|
EnsureValidLockerVariant();
|
2019-09-26 05:23:08 +02:00
|
|
|
|
2019-11-13 03:26:56 +01:00
|
|
|
const auto [entry, is_cache_miss] = curr_locker_variant->programs.try_emplace(variant);
|
2019-08-25 06:28:26 +02:00
|
|
|
auto& program = entry->second;
|
|
|
|
if (is_cache_miss) {
|
2019-09-25 04:34:18 +02:00
|
|
|
program = BuildShader(device, unique_identifier, program_type, program_code, program_code_b,
|
2019-11-13 03:26:56 +01:00
|
|
|
*curr_locker_variant->locker, variant);
|
|
|
|
disk_cache.SaveUsage(GetUsage(variant, *curr_locker_variant->locker));
|
2019-01-05 05:00:06 +01:00
|
|
|
|
2019-08-25 06:28:26 +02:00
|
|
|
LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
|
2018-10-07 04:17:31 +02:00
|
|
|
}
|
2019-01-05 05:00:06 +01:00
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
auto base_bindings = variant.base_bindings;
|
|
|
|
base_bindings.cbuf += static_cast<u32>(entries.const_buffers.size());
|
2019-11-13 03:26:56 +01:00
|
|
|
base_bindings.cbuf += STAGE_RESERVED_UBOS;
|
2019-01-05 05:00:06 +01:00
|
|
|
base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size());
|
|
|
|
base_bindings.sampler += static_cast<u32>(entries.samplers.size());
|
|
|
|
|
2019-08-25 06:28:26 +02:00
|
|
|
return {program->handle, base_bindings};
|
2018-08-23 23:30:27 +02:00
|
|
|
}
|
|
|
|
|
2019-11-13 03:26:56 +01:00
|
|
|
bool CachedShader::EnsureValidLockerVariant() {
|
|
|
|
const auto previous_variant = curr_locker_variant;
|
|
|
|
if (curr_locker_variant && !curr_locker_variant->locker->IsConsistent()) {
|
|
|
|
curr_locker_variant = nullptr;
|
2019-09-26 05:23:08 +02:00
|
|
|
}
|
2019-11-13 03:26:56 +01:00
|
|
|
if (!curr_locker_variant) {
|
2019-09-26 05:23:08 +02:00
|
|
|
for (auto& variant : locker_variants) {
|
|
|
|
if (variant->locker->IsConsistent()) {
|
2019-11-13 03:26:56 +01:00
|
|
|
curr_locker_variant = variant.get();
|
2019-09-26 05:23:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-13 03:26:56 +01:00
|
|
|
if (!curr_locker_variant) {
|
2019-09-26 05:23:08 +02:00
|
|
|
auto& new_variant = locker_variants.emplace_back();
|
|
|
|
new_variant = std::make_unique<LockerVariant>();
|
|
|
|
new_variant->locker = MakeLocker(system, program_type);
|
2019-11-13 03:26:56 +01:00
|
|
|
curr_locker_variant = new_variant.get();
|
2019-09-26 05:23:08 +02:00
|
|
|
}
|
2019-11-13 03:26:56 +01:00
|
|
|
return previous_variant == curr_locker_variant;
|
2019-09-26 05:23:08 +02:00
|
|
|
}
|
|
|
|
|
2019-09-26 02:46:34 +02:00
|
|
|
ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
|
|
|
|
const ConstBufferLocker& locker) const {
|
2019-11-13 03:26:56 +01:00
|
|
|
return ShaderDiskCacheUsage{unique_identifier, variant, locker.GetKeys(),
|
|
|
|
locker.GetBoundSamplers(), locker.GetBindlessSamplers()};
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
|
2019-04-06 22:59:56 +02:00
|
|
|
Core::Frontend::EmuWindow& emu_window, const Device& device)
|
2019-05-30 19:01:40 +02:00
|
|
|
: RasterizerCache{rasterizer}, system{system}, emu_window{emu_window}, device{device},
|
|
|
|
disk_cache{system} {}
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-01-21 20:38:23 +01:00
|
|
|
void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|
|
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
2019-01-15 06:17:38 +01:00
|
|
|
const auto transferable = disk_cache.LoadTransferable();
|
|
|
|
if (!transferable) {
|
2019-01-14 04:58:15 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-04-06 22:59:56 +02:00
|
|
|
const auto [raws, shader_usages] = *transferable;
|
2019-09-25 04:34:18 +02:00
|
|
|
if (!GenerateUnspecializedShaders(stop_loading, callback, raws) || stop_loading) {
|
2019-01-21 20:38:23 +01:00
|
|
|
return;
|
2019-04-06 22:59:56 +02:00
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
const auto dumps = disk_cache.LoadPrecompiled();
|
|
|
|
const auto supported_formats = GetSupportedFormats();
|
|
|
|
|
|
|
|
// Track if precompiled cache was altered during loading to know if we have to
|
|
|
|
// serialize the virtual precompiled cache file back to the hard drive
|
2019-02-25 11:47:23 +01:00
|
|
|
bool precompiled_cache_altered = false;
|
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
// Inform the frontend about shader build initialization
|
|
|
|
if (callback) {
|
|
|
|
callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size());
|
|
|
|
}
|
2019-01-21 20:38:23 +01:00
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
std::mutex mutex;
|
|
|
|
std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
|
|
|
|
std::atomic_bool compilation_failed = false;
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
|
2019-04-26 01:10:20 +02:00
|
|
|
std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages,
|
|
|
|
const ShaderDumpsMap& dumps) {
|
2019-04-06 22:59:56 +02:00
|
|
|
context->MakeCurrent();
|
|
|
|
SCOPE_EXIT({ return context->DoneCurrent(); });
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
for (std::size_t i = begin; i < end; ++i) {
|
|
|
|
if (stop_loading || compilation_failed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto& usage{shader_usages[i]};
|
|
|
|
const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)};
|
|
|
|
const auto dump{dumps.find(usage)};
|
|
|
|
|
|
|
|
CachedProgram shader;
|
|
|
|
if (dump != dumps.end()) {
|
|
|
|
// If the shader is dumped, attempt to load it with
|
|
|
|
shader = GeneratePrecompiledProgram(dump->second, supported_formats);
|
|
|
|
if (!shader) {
|
|
|
|
compilation_failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-01-15 05:07:57 +01:00
|
|
|
if (!shader) {
|
2019-09-26 05:23:08 +02:00
|
|
|
auto locker{MakeLocker(system, unspecialized.program_type)};
|
|
|
|
FillLocker(*locker, usage);
|
2019-11-13 03:26:56 +01:00
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
shader = BuildShader(device, usage.unique_identifier, unspecialized.program_type,
|
2019-11-13 03:26:56 +01:00
|
|
|
unspecialized.code, unspecialized.code_b, *locker,
|
|
|
|
usage.variant, true);
|
2019-01-15 05:07:57 +01:00
|
|
|
}
|
2019-04-06 22:59:56 +02:00
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
std::scoped_lock lock{mutex};
|
2019-04-06 22:59:56 +02:00
|
|
|
if (callback) {
|
|
|
|
callback(VideoCore::LoadCallbackStage::Build, ++built_shaders,
|
|
|
|
shader_usages.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
precompiled_programs.emplace(usage, std::move(shader));
|
2019-09-25 04:34:18 +02:00
|
|
|
|
|
|
|
// TODO(Rodrigo): Is there a better way to do this?
|
|
|
|
precompiled_variants[usage.unique_identifier].push_back(
|
|
|
|
precompiled_programs.find(usage));
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
2019-04-06 22:59:56 +02:00
|
|
|
};
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)};
|
2019-04-06 22:59:56 +02:00
|
|
|
const std::size_t bucket_size{shader_usages.size() / num_workers};
|
|
|
|
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
|
|
|
|
std::vector<std::thread> threads(num_workers);
|
|
|
|
for (std::size_t i = 0; i < num_workers; ++i) {
|
|
|
|
const bool is_last_worker = i + 1 == num_workers;
|
|
|
|
const std::size_t start{bucket_size * i};
|
|
|
|
const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size};
|
|
|
|
|
|
|
|
// On some platforms the shared context has to be created from the GUI thread
|
|
|
|
contexts[i] = emu_window.CreateSharedContext();
|
2019-04-26 01:10:20 +02:00
|
|
|
threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps);
|
2019-04-06 22:59:56 +02:00
|
|
|
}
|
|
|
|
for (auto& thread : threads) {
|
|
|
|
thread.join();
|
|
|
|
}
|
2019-01-21 20:38:23 +01:00
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
if (compilation_failed) {
|
|
|
|
// Invalidate the precompiled cache if a shader dumped shader was rejected
|
|
|
|
disk_cache.InvalidatePrecompiled();
|
|
|
|
precompiled_cache_altered = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (stop_loading) {
|
|
|
|
return;
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
// TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw
|
|
|
|
// before precompiling them
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
for (std::size_t i = 0; i < shader_usages.size(); ++i) {
|
|
|
|
const auto& usage{shader_usages[i]};
|
2019-01-15 05:07:57 +01:00
|
|
|
if (dumps.find(usage) == dumps.end()) {
|
2019-04-06 22:59:56 +02:00
|
|
|
const auto& program{precompiled_programs.at(usage)};
|
2019-01-15 05:07:57 +01:00
|
|
|
disk_cache.SaveDump(usage, program->handle);
|
2019-02-25 11:47:23 +01:00
|
|
|
precompiled_cache_altered = true;
|
2018-12-09 22:33:10 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-25 11:47:23 +01:00
|
|
|
|
|
|
|
if (precompiled_cache_altered) {
|
|
|
|
disk_cache.SaveVirtualPrecompiledFile();
|
|
|
|
}
|
2018-12-09 22:33:10 +01:00
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
const PrecompiledVariants* ShaderCacheOpenGL::GetPrecompiledVariants(u64 unique_identifier) const {
|
|
|
|
const auto it = precompiled_variants.find(unique_identifier);
|
|
|
|
return it == precompiled_variants.end() ? nullptr : &it->second;
|
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram(
|
|
|
|
const ShaderDiskCacheDump& dump, const std::unordered_set<GLenum>& supported_formats) {
|
2019-01-15 05:07:57 +01:00
|
|
|
if (supported_formats.find(dump.binary_format) == supported_formats.end()) {
|
2019-01-14 04:58:15 +01:00
|
|
|
LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing");
|
|
|
|
return {};
|
2018-12-09 22:33:10 +01:00
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
|
|
|
|
CachedProgram shader = std::make_shared<OGLProgram>();
|
|
|
|
shader->handle = glCreateProgram();
|
2019-01-16 08:37:35 +01:00
|
|
|
glProgramParameteri(shader->handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
2019-01-15 05:07:57 +01:00
|
|
|
glProgramBinary(shader->handle, dump.binary_format, dump.binary.data(),
|
|
|
|
static_cast<GLsizei>(dump.binary.size()));
|
2019-01-14 04:58:15 +01:00
|
|
|
|
|
|
|
GLint link_status{};
|
|
|
|
glGetProgramiv(shader->handle, GL_LINK_STATUS, &link_status);
|
|
|
|
if (link_status == GL_FALSE) {
|
|
|
|
LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver - removing");
|
2019-01-15 05:07:57 +01:00
|
|
|
return {};
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return shader;
|
2018-12-09 22:33:10 +01:00
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
|
2019-01-21 20:38:23 +01:00
|
|
|
const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback,
|
2019-09-25 04:34:18 +02:00
|
|
|
const std::vector<ShaderDiskCacheRaw>& raws) {
|
2019-04-10 23:03:52 +02:00
|
|
|
if (callback) {
|
2019-01-21 20:38:23 +01:00
|
|
|
callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size());
|
2019-04-10 23:03:52 +02:00
|
|
|
}
|
2019-01-21 20:38:23 +01:00
|
|
|
|
|
|
|
for (std::size_t i = 0; i < raws.size(); ++i) {
|
2019-04-10 23:03:52 +02:00
|
|
|
if (stop_loading) {
|
2019-09-25 04:34:18 +02:00
|
|
|
return false;
|
2019-04-10 23:03:52 +02:00
|
|
|
}
|
2019-01-21 20:38:23 +01:00
|
|
|
const auto& raw{raws[i]};
|
2019-04-10 23:03:52 +02:00
|
|
|
const u64 unique_identifier{raw.GetUniqueIdentifier()};
|
|
|
|
const u64 calculated_hash{
|
|
|
|
GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB())};
|
2019-01-15 05:07:57 +01:00
|
|
|
if (unique_identifier != calculated_hash) {
|
2019-09-25 04:34:18 +02:00
|
|
|
LOG_ERROR(Render_OpenGL,
|
|
|
|
"Invalid hash in entry={:016x} (obtained hash={:016x}) - "
|
|
|
|
"removing shader cache",
|
|
|
|
raw.GetUniqueIdentifier(), calculated_hash);
|
2019-01-14 04:58:15 +01:00
|
|
|
disk_cache.InvalidateTransferable();
|
2019-09-25 04:34:18 +02:00
|
|
|
return false;
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
const u32 main_offset =
|
|
|
|
raw.GetProgramType() == ProgramType::Compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
|
|
|
|
ConstBufferLocker locker(GetEnginesShaderType(raw.GetProgramType()));
|
|
|
|
const ShaderIR ir(raw.GetProgramCode(), main_offset, COMPILER_SETTINGS, locker);
|
|
|
|
// TODO(Rodrigo): Handle VertexA shaders
|
|
|
|
// std::optional<ShaderIR> ir_b;
|
|
|
|
// if (raw.HasProgramA()) {
|
|
|
|
// ir_b.emplace(raw.GetProgramCodeB(), main_offset);
|
|
|
|
// }
|
|
|
|
|
|
|
|
UnspecializedShader unspecialized;
|
|
|
|
unspecialized.entries = GLShader::GetEntries(ir);
|
|
|
|
unspecialized.program_type = raw.GetProgramType();
|
|
|
|
unspecialized.code = raw.GetProgramCode();
|
|
|
|
unspecialized.code_b = raw.GetProgramCodeB();
|
|
|
|
unspecialized_shaders.emplace(raw.GetUniqueIdentifier(), unspecialized);
|
2019-01-21 20:38:23 +01:00
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
if (callback) {
|
2019-01-21 20:38:23 +01:00
|
|
|
callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size());
|
2019-04-10 23:03:52 +02:00
|
|
|
}
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
2019-09-25 04:34:18 +02:00
|
|
|
return true;
|
2019-01-14 04:58:15 +01:00
|
|
|
}
|
2019-01-14 02:05:53 +01:00
|
|
|
|
2018-08-23 23:30:27 +02:00
|
|
|
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
|
2019-07-10 21:38:31 +02:00
|
|
|
if (!system.GPU().Maxwell3D().dirty.shaders) {
|
2019-05-30 18:55:11 +02:00
|
|
|
return last_shaders[static_cast<std::size_t>(program)];
|
2019-01-06 07:58:43 +01:00
|
|
|
}
|
|
|
|
|
2019-05-30 19:01:40 +02:00
|
|
|
auto& memory_manager{system.GPU().MemoryManager()};
|
2019-09-25 04:34:18 +02:00
|
|
|
const GPUVAddr address{GetShaderAddress(system, program)};
|
2018-08-23 23:30:27 +02:00
|
|
|
|
|
|
|
// Look up shader in the cache based on address
|
2019-09-25 04:34:18 +02:00
|
|
|
const auto host_ptr{memory_manager.GetPointer(address)};
|
2019-02-19 02:58:32 +01:00
|
|
|
Shader shader{TryGet(host_ptr)};
|
2019-05-30 18:55:11 +02:00
|
|
|
if (shader) {
|
|
|
|
return last_shaders[static_cast<std::size_t>(program)] = shader;
|
|
|
|
}
|
2018-08-23 23:30:27 +02:00
|
|
|
|
2019-05-30 18:55:11 +02:00
|
|
|
// No shader found - create a new one
|
2019-09-25 04:34:18 +02:00
|
|
|
ProgramCode code{GetShaderCode(memory_manager, address, host_ptr)};
|
|
|
|
ProgramCode code_b;
|
|
|
|
if (program == Maxwell::ShaderProgram::VertexA) {
|
|
|
|
const GPUVAddr address_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)};
|
|
|
|
code_b = GetShaderCode(memory_manager, address_b, memory_manager.GetPointer(address_b));
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto unique_identifier = GetUniqueIdentifier(GetProgramType(program), code, code_b);
|
|
|
|
const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
|
|
|
|
const auto cpu_addr{*memory_manager.GpuToCpuAddress(address)};
|
|
|
|
const ShaderParameters params{system, disk_cache, precompiled_variants, device,
|
|
|
|
cpu_addr, host_ptr, unique_identifier};
|
|
|
|
|
|
|
|
const auto found = unspecialized_shaders.find(unique_identifier);
|
|
|
|
if (found == unspecialized_shaders.end()) {
|
|
|
|
shader = CachedShader::CreateStageFromMemory(params, program, std::move(code),
|
|
|
|
std::move(code_b));
|
2019-05-30 18:55:11 +02:00
|
|
|
} else {
|
2019-09-25 04:34:18 +02:00
|
|
|
shader = CachedShader::CreateFromCache(params, found->second);
|
2018-08-23 23:30:27 +02:00
|
|
|
}
|
2019-05-30 18:55:11 +02:00
|
|
|
Register(shader);
|
2018-08-23 23:30:27 +02:00
|
|
|
|
2019-05-30 18:55:11 +02:00
|
|
|
return last_shaders[static_cast<std::size_t>(program)] = shader;
|
2018-08-23 23:30:27 +02:00
|
|
|
}
|
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
|
|
|
|
auto& memory_manager{system.GPU().MemoryManager()};
|
|
|
|
const auto host_ptr{memory_manager.GetPointer(code_addr)};
|
|
|
|
auto kernel = TryGet(host_ptr);
|
|
|
|
if (kernel) {
|
|
|
|
return kernel;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No kernel found - create a new one
|
2019-07-15 21:29:25 +02:00
|
|
|
auto code{GetShaderCode(memory_manager, code_addr, host_ptr)};
|
2019-07-15 03:25:13 +02:00
|
|
|
const auto unique_identifier{GetUniqueIdentifier(ProgramType::Compute, code, {})};
|
2019-09-25 04:34:18 +02:00
|
|
|
const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
|
2019-07-15 03:25:13 +02:00
|
|
|
const auto cpu_addr{*memory_manager.GpuToCpuAddress(code_addr)};
|
2019-09-25 04:34:18 +02:00
|
|
|
const ShaderParameters params{system, disk_cache, precompiled_variants, device,
|
|
|
|
cpu_addr, host_ptr, unique_identifier};
|
2019-07-15 03:25:13 +02:00
|
|
|
|
2019-09-25 04:34:18 +02:00
|
|
|
const auto found = unspecialized_shaders.find(unique_identifier);
|
|
|
|
if (found == unspecialized_shaders.end()) {
|
2019-07-15 03:25:13 +02:00
|
|
|
kernel = CachedShader::CreateKernelFromMemory(params, std::move(code));
|
|
|
|
} else {
|
2019-09-25 04:34:18 +02:00
|
|
|
kernel = CachedShader::CreateFromCache(params, found->second);
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Register(kernel);
|
|
|
|
return kernel;
|
|
|
|
}
|
|
|
|
|
2019-04-23 23:19:28 +02:00
|
|
|
} // namespace OpenGL
|