mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-22 13:37:06 +00:00
289 lines
8 KiB
C++
289 lines
8 KiB
C++
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
|
#include "Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h"
|
|
|
|
#include "Cemu/FileCache/FileCache.h"
|
|
|
|
#include "config/ActiveSettings.h"
|
|
#include "config/LaunchSettings.h"
|
|
|
|
extern std::atomic_int g_compiled_shaders_total;
|
|
extern std::atomic_int g_compiled_shaders_async;
|
|
|
|
bool s_isLoadingShaders{false};
|
|
|
|
bool RendererShaderGL::loadBinary()
|
|
{
|
|
if (!s_programBinaryCache)
|
|
return false;
|
|
if (m_isGameShader == false || m_isGfxPackShader)
|
|
return false; // only non-custom
|
|
if (!glProgramBinary)
|
|
return false; // OpenGL program binaries not supported
|
|
|
|
cemu_assert_debug(m_baseHash != 0);
|
|
uint64 h1, h2;
|
|
GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2);
|
|
std::vector<uint8> cacheFileData;
|
|
if (!s_programBinaryCache->GetFile({h1, h2 }, cacheFileData))
|
|
return false;
|
|
if (cacheFileData.size() <= sizeof(uint32))
|
|
return false;
|
|
|
|
uint32 shaderBinFormat = *(uint32*)(cacheFileData.data());
|
|
|
|
m_program = glCreateProgram();
|
|
glProgramBinary(m_program, shaderBinFormat, cacheFileData.data()+4, cacheFileData.size()-4);
|
|
|
|
int status = -1;
|
|
glGetProgramiv(m_program, GL_LINK_STATUS, &status);
|
|
if (status != GL_TRUE)
|
|
{
|
|
glDeleteProgram(m_program);
|
|
m_program = 0;
|
|
return false;
|
|
}
|
|
m_isCompiled = true;
|
|
return true;
|
|
}
|
|
|
|
void RendererShaderGL::storeBinary()
|
|
{
|
|
if (!s_programBinaryCache)
|
|
return;
|
|
if (!glGetProgramBinary)
|
|
return;
|
|
if (m_program == 0)
|
|
return;
|
|
if (!m_isGameShader || m_isGfxPackShader)
|
|
return;
|
|
|
|
GLint binaryLength = 0;
|
|
glGetProgramiv(m_program, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
|
if (binaryLength > 0)
|
|
{
|
|
uint64 h1, h2;
|
|
GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2);
|
|
// build stored shader data (4 byte format + binary data)
|
|
std::vector<uint8> storedBinary(binaryLength+sizeof(uint32), 0);
|
|
GLenum binaryFormat = 0;
|
|
glGetProgramBinary(m_program, binaryLength, NULL, &binaryFormat, storedBinary.data()+sizeof(uint32));
|
|
*(uint32*)(storedBinary.data() + 0) = binaryFormat;
|
|
// store
|
|
s_programBinaryCache->AddFileAsync({h1, h2 }, storedBinary.data(), storedBinary.size());
|
|
}
|
|
}
|
|
|
|
RendererShaderGL::RendererShaderGL(ShaderType type, uint64 baseHash, uint64 auxHash, bool isGameShader, bool isGfxPackShader, const std::string& glslSource)
|
|
: RendererShader(type, baseHash, auxHash, isGameShader, isGfxPackShader), m_glslSource(glslSource)
|
|
{
|
|
GLenum glShaderType;
|
|
switch (type)
|
|
{
|
|
case ShaderType::kVertex:
|
|
glShaderType = GL_VERTEX_SHADER;
|
|
break;
|
|
case ShaderType::kFragment:
|
|
glShaderType = GL_FRAGMENT_SHADER;
|
|
break;
|
|
case ShaderType::kGeometry:
|
|
glShaderType = GL_GEOMETRY_SHADER;
|
|
break;
|
|
default:
|
|
cemu_assert_debug(false);
|
|
}
|
|
|
|
if (s_isLoadingShaders)
|
|
{
|
|
if (loadBinary())
|
|
{
|
|
m_glslSource.clear();
|
|
m_glslSource.shrink_to_fit();
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_shader_object = glCreateShader(glShaderType);
|
|
|
|
const char *c_str = m_glslSource.c_str();
|
|
const GLint size = (GLint)m_glslSource.size();
|
|
glShaderSource(m_shader_object, 1, &c_str, &size);
|
|
glCompileShader(m_shader_object);
|
|
|
|
GLint log_length;
|
|
glGetShaderiv(m_shader_object, GL_INFO_LOG_LENGTH, &log_length);
|
|
if (log_length > 0)
|
|
{
|
|
char log[2048]{};
|
|
GLsizei log_size;
|
|
glGetShaderInfoLog(m_shader_object, std::min<uint32>(log_length, sizeof(log) - 1), &log_size, log);
|
|
cemuLog_log(LogType::Force, "Error/Warning in shader:");
|
|
cemuLog_log(LogType::Force, log);
|
|
}
|
|
|
|
// set debug name
|
|
if (LaunchSettings::NSightModeEnabled())
|
|
{
|
|
auto objNameStr = fmt::format("shader_{:016x}_{:016x}", m_baseHash, m_auxHash);
|
|
glObjectLabel(GL_SHADER, m_shader_object, objNameStr.size(), objNameStr.c_str());
|
|
}
|
|
|
|
m_program = glCreateProgram();
|
|
glProgramParameteri(m_program, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
|
glProgramParameteri(m_program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
|
|
glAttachShader(m_program, m_shader_object);
|
|
m_shader_attached = true;
|
|
glLinkProgram(m_program);
|
|
|
|
storeBinary();
|
|
|
|
// count shader compilation
|
|
if (!s_isLoadingShaders)
|
|
++g_compiled_shaders_total;
|
|
|
|
// we can throw away the GLSL code to conserve RAM
|
|
m_glslSource.clear();
|
|
m_glslSource.shrink_to_fit();
|
|
}
|
|
|
|
RendererShaderGL::~RendererShaderGL()
|
|
{
|
|
if (m_shader_object != 0 && m_shader_attached)
|
|
glDetachShader(m_program, m_shader_object);
|
|
|
|
if (m_shader_object != 0)
|
|
glDeleteShader(m_shader_object);
|
|
|
|
if (m_program != 0)
|
|
glDeleteProgram(m_program);
|
|
}
|
|
|
|
void RendererShaderGL::PreponeCompilation()
|
|
{
|
|
// the logic for initiating compilation is currently in the constructor
|
|
// here we only guarantee that it is finished before we return
|
|
if (m_isCompiled)
|
|
return;
|
|
WaitForCompiled();
|
|
}
|
|
|
|
bool RendererShaderGL::IsCompiled()
|
|
{
|
|
cemu_assert_debug(false);
|
|
return true;
|
|
}
|
|
|
|
bool RendererShaderGL::WaitForCompiled()
|
|
{
|
|
char infoLog[8 * 1024];
|
|
if (m_isCompiled)
|
|
return true;
|
|
// check if compilation was successful
|
|
GLint compileStatus = GL_FALSE;
|
|
glGetShaderiv(m_shader_object, GL_COMPILE_STATUS, &compileStatus);
|
|
if (compileStatus == 0)
|
|
{
|
|
uint32 infoLogLength, tempLength;
|
|
glGetShaderiv(m_shader_object, GL_INFO_LOG_LENGTH, (GLint *)&infoLogLength);
|
|
if (infoLogLength != 0)
|
|
{
|
|
tempLength = sizeof(infoLog) - 1;
|
|
glGetShaderInfoLog(m_shader_object, std::min(infoLogLength, tempLength), (GLsizei*)&tempLength, (GLcharARB*)infoLog);
|
|
infoLog[tempLength] = '\0';
|
|
cemuLog_log(LogType::Force, "Compile error in shader. Log:");
|
|
cemuLog_log(LogType::Force, infoLog);
|
|
}
|
|
if (m_shader_object != 0)
|
|
glDeleteShader(m_shader_object);
|
|
m_isCompiled = true;
|
|
return false;
|
|
}
|
|
// get shader binary
|
|
GLint linkStatus = GL_FALSE;
|
|
glGetProgramiv(m_program, GL_LINK_STATUS, &linkStatus);
|
|
if (linkStatus == 0)
|
|
{
|
|
uint32 infoLogLength, tempLength;
|
|
glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, (GLint *)&infoLogLength);
|
|
if (infoLogLength != 0)
|
|
{
|
|
tempLength = sizeof(infoLog) - 1;
|
|
glGetProgramInfoLog(m_program, std::min(infoLogLength, tempLength), (GLsizei*)&tempLength, (GLcharARB*)infoLog);
|
|
infoLog[tempLength] = '\0';
|
|
cemuLog_log(LogType::Force, "Link error in shader. Log:");
|
|
cemuLog_log(LogType::Force, infoLog);
|
|
}
|
|
m_isCompiled = true;
|
|
return false;
|
|
}
|
|
|
|
/*glDetachShader(m_program, m_shader_object);
|
|
m_shader_attached = false;*/
|
|
m_isCompiled = true;
|
|
return true;
|
|
}
|
|
|
|
sint32 RendererShaderGL::GetUniformLocation(const char* name)
|
|
{
|
|
return glGetUniformLocation(m_program, name);
|
|
}
|
|
|
|
void RendererShaderGL::SetUniform2fv(sint32 location, void* data, sint32 count)
|
|
{
|
|
glProgramUniform2fv(m_program, location, count, (const GLfloat*)data);
|
|
}
|
|
|
|
void RendererShaderGL::SetUniform4iv(sint32 location, void* data, sint32 count)
|
|
{
|
|
glProgramUniform4iv(m_program, location, count, (const GLint*)data);
|
|
}
|
|
|
|
void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId)
|
|
{
|
|
cemu_assert_debug(!s_programBinaryCache); // should not be set, ShaderCacheLoading_Close() not called?
|
|
// determine if cache is enabled
|
|
bool usePrecompiled = false;
|
|
switch (ActiveSettings::GetPrecompiledShadersOption())
|
|
{
|
|
case PrecompiledShaderOption::Auto:
|
|
if (g_renderer->GetVendor() == GfxVendor::Nvidia)
|
|
usePrecompiled = false;
|
|
else
|
|
usePrecompiled = true;
|
|
break;
|
|
case PrecompiledShaderOption::Enable:
|
|
usePrecompiled = true;
|
|
break;
|
|
case PrecompiledShaderOption::Disable:
|
|
usePrecompiled = false;
|
|
break;
|
|
default:
|
|
UNREACHABLE;
|
|
}
|
|
|
|
cemuLog_log(LogType::Force, "Using precompiled shaders: {}", usePrecompiled ? "true" : "false");
|
|
|
|
if (usePrecompiled)
|
|
{
|
|
const uint32 cacheMagic = GeneratePrecompiledCacheId();
|
|
const std::string cacheFilename = fmt::format("{:016x}_gl.bin", cacheTitleId);
|
|
s_programBinaryCache.reset(FileCache::Open(ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename), true, cacheMagic));
|
|
if (!s_programBinaryCache)
|
|
cemuLog_log(LogType::Force, "Unable to open OpenGL precompiled cache {}", cacheFilename);
|
|
}
|
|
s_isLoadingShaders = true;
|
|
}
|
|
|
|
void RendererShaderGL::ShaderCacheLoading_end()
|
|
{
|
|
s_isLoadingShaders = false;
|
|
}
|
|
|
|
void RendererShaderGL::ShaderCacheLoading_Close()
|
|
{
|
|
s_programBinaryCache.reset();
|
|
g_compiled_shaders_total = 0;
|
|
g_compiled_shaders_async = 0;
|
|
}
|
|
|
|
|
|
std::unique_ptr<class FileCache> RendererShaderGL::s_programBinaryCache{};
|