mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-05-14 07:02:41 +00:00
Move shader caches to VideoCommon
This commit is contained in:
parent
24df896eb8
commit
dec0c3bce8
48 changed files with 1448 additions and 3346 deletions
Source/Core/VideoBackends/OGL
|
@ -33,8 +33,6 @@
|
|||
#include "VideoCommon/ImageWrite.h"
|
||||
#include "VideoCommon/PixelShaderManager.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
@ -43,8 +41,6 @@ namespace OGL
|
|||
{
|
||||
static constexpr u32 UBO_LENGTH = 32 * 1024 * 1024;
|
||||
|
||||
std::unique_ptr<ProgramShaderCache::SharedContextAsyncShaderCompiler>
|
||||
ProgramShaderCache::s_async_compiler;
|
||||
u32 ProgramShaderCache::s_ubo_buffer_size;
|
||||
s32 ProgramShaderCache::s_ubo_align;
|
||||
GLuint ProgramShaderCache::s_attributeless_VBO = 0;
|
||||
|
@ -54,17 +50,9 @@ GLuint ProgramShaderCache::s_last_VAO = 0;
|
|||
static std::unique_ptr<StreamBuffer> s_buffer;
|
||||
static int num_failures = 0;
|
||||
|
||||
static LinearDiskCache<SHADERUID, u8> s_program_disk_cache;
|
||||
static LinearDiskCache<UBERSHADERUID, u8> s_uber_program_disk_cache;
|
||||
static GLuint CurrentProgram = 0;
|
||||
ProgramShaderCache::PCache ProgramShaderCache::pshaders;
|
||||
ProgramShaderCache::UberPCache ProgramShaderCache::ubershaders;
|
||||
ProgramShaderCache::PipelineProgramMap ProgramShaderCache::pipelineprograms;
|
||||
std::mutex ProgramShaderCache::pipelineprogramlock;
|
||||
ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry;
|
||||
ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_uber_entry;
|
||||
SHADERUID ProgramShaderCache::last_uid;
|
||||
UBERSHADERUID ProgramShaderCache::last_uber_uid;
|
||||
static std::string s_glsl_header = "";
|
||||
|
||||
static std::string GetGLSLVersionString()
|
||||
|
@ -270,143 +258,6 @@ void ProgramShaderCache::UploadConstants()
|
|||
}
|
||||
}
|
||||
|
||||
SHADER* ProgramShaderCache::SetShader(PrimitiveType primitive_type,
|
||||
const GLVertexFormat* vertex_format)
|
||||
{
|
||||
if (g_ActiveConfig.bDisableSpecializedShaders)
|
||||
return SetUberShader(primitive_type, vertex_format);
|
||||
|
||||
SHADERUID uid;
|
||||
std::memset(&uid, 0, sizeof(uid));
|
||||
uid.puid = GetPixelShaderUid();
|
||||
uid.vuid = GetVertexShaderUid();
|
||||
uid.guid = GetGeometryShaderUid(primitive_type);
|
||||
ClearUnusedPixelShaderUidBits(APIType::OpenGL, &uid.puid);
|
||||
|
||||
// Check if the shader is already set
|
||||
if (last_entry && uid == last_uid)
|
||||
{
|
||||
last_entry->shader.Bind();
|
||||
BindVertexFormat(vertex_format);
|
||||
return &last_entry->shader;
|
||||
}
|
||||
|
||||
// Check if shader is already in cache
|
||||
auto iter = pshaders.find(uid);
|
||||
if (iter != pshaders.end())
|
||||
{
|
||||
PCacheEntry* entry = &iter->second;
|
||||
if (entry->pending)
|
||||
return SetUberShader(primitive_type, vertex_format);
|
||||
|
||||
last_uid = uid;
|
||||
last_entry = entry;
|
||||
BindVertexFormat(vertex_format);
|
||||
last_entry->shader.Bind();
|
||||
return &last_entry->shader;
|
||||
}
|
||||
|
||||
// Compile the new shader program.
|
||||
PCacheEntry& newentry = pshaders[uid];
|
||||
newentry.in_cache = false;
|
||||
newentry.pending = false;
|
||||
|
||||
// Can we background compile this shader? Requires background shader compiling to be enabled,
|
||||
// and all ubershaders to have been successfully compiled.
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders() && !ubershaders.empty() && s_async_compiler)
|
||||
{
|
||||
newentry.pending = true;
|
||||
s_async_compiler->QueueWorkItem(s_async_compiler->CreateWorkItem<ShaderCompileWorkItem>(uid));
|
||||
return SetUberShader(primitive_type, vertex_format);
|
||||
}
|
||||
|
||||
// Synchronous shader compiling.
|
||||
ShaderHostConfig host_config = ShaderHostConfig::GetCurrent();
|
||||
ShaderCode vcode = GenerateVertexShaderCode(APIType::OpenGL, host_config, uid.vuid.GetUidData());
|
||||
ShaderCode pcode = GeneratePixelShaderCode(APIType::OpenGL, host_config, uid.puid.GetUidData());
|
||||
ShaderCode gcode;
|
||||
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders &&
|
||||
!uid.guid.GetUidData()->IsPassthrough())
|
||||
gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData());
|
||||
|
||||
if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()))
|
||||
return nullptr;
|
||||
|
||||
INCSTAT(stats.numPixelShadersCreated);
|
||||
SETSTAT(stats.numPixelShadersAlive, pshaders.size());
|
||||
|
||||
last_uid = uid;
|
||||
last_entry = &newentry;
|
||||
BindVertexFormat(vertex_format);
|
||||
last_entry->shader.Bind();
|
||||
return &last_entry->shader;
|
||||
}
|
||||
|
||||
SHADER* ProgramShaderCache::SetUberShader(PrimitiveType primitive_type,
|
||||
const GLVertexFormat* vertex_format)
|
||||
{
|
||||
UBERSHADERUID uid;
|
||||
std::memset(&uid, 0, sizeof(uid));
|
||||
uid.puid = UberShader::GetPixelShaderUid();
|
||||
uid.vuid = UberShader::GetVertexShaderUid();
|
||||
uid.guid = GetGeometryShaderUid(primitive_type);
|
||||
UberShader::ClearUnusedPixelShaderUidBits(APIType::OpenGL, &uid.puid);
|
||||
|
||||
// We need to use the ubershader vertex format with all attributes enabled.
|
||||
// Otherwise, the NV driver can generate variants for the vertex shaders.
|
||||
const GLVertexFormat* uber_vertex_format = static_cast<const GLVertexFormat*>(
|
||||
VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration()));
|
||||
|
||||
// Check if the shader is already set
|
||||
if (last_uber_entry && last_uber_uid == uid)
|
||||
{
|
||||
BindVertexFormat(uber_vertex_format);
|
||||
last_uber_entry->shader.Bind();
|
||||
return &last_uber_entry->shader;
|
||||
}
|
||||
|
||||
// Check if shader is already in cache
|
||||
auto iter = ubershaders.find(uid);
|
||||
if (iter != ubershaders.end())
|
||||
{
|
||||
PCacheEntry* entry = &iter->second;
|
||||
last_uber_uid = uid;
|
||||
last_uber_entry = entry;
|
||||
BindVertexFormat(uber_vertex_format);
|
||||
last_uber_entry->shader.Bind();
|
||||
return &last_uber_entry->shader;
|
||||
}
|
||||
|
||||
// Make an entry in the table
|
||||
PCacheEntry& newentry = ubershaders[uid];
|
||||
newentry.in_cache = false;
|
||||
newentry.pending = false;
|
||||
|
||||
ShaderHostConfig host_config = ShaderHostConfig::GetCurrent();
|
||||
ShaderCode vcode =
|
||||
UberShader::GenVertexShader(APIType::OpenGL, host_config, uid.vuid.GetUidData());
|
||||
ShaderCode pcode =
|
||||
UberShader::GenPixelShader(APIType::OpenGL, host_config, uid.puid.GetUidData());
|
||||
ShaderCode gcode;
|
||||
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders &&
|
||||
!uid.guid.GetUidData()->IsPassthrough())
|
||||
{
|
||||
gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData());
|
||||
}
|
||||
|
||||
if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()))
|
||||
{
|
||||
GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
last_uber_uid = uid;
|
||||
last_uber_entry = &newentry;
|
||||
BindVertexFormat(uber_vertex_format);
|
||||
last_uber_entry->shader.Bind();
|
||||
return &last_uber_entry->shader;
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::CompileShader(SHADER& shader, const std::string& vcode,
|
||||
const std::string& pcode, const std::string& gcode)
|
||||
{
|
||||
|
@ -620,11 +471,6 @@ bool ProgramShaderCache::CheckProgramLinkResult(GLuint id, const std::string& vc
|
|||
return true;
|
||||
}
|
||||
|
||||
ProgramShaderCache::PCacheEntry ProgramShaderCache::GetShaderProgram()
|
||||
{
|
||||
return *last_entry;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::Init()
|
||||
{
|
||||
// We have to get the UBO alignment here because
|
||||
|
@ -642,93 +488,14 @@ void ProgramShaderCache::Init()
|
|||
// Then once more to get bytes
|
||||
s_buffer = StreamBuffer::Create(GL_UNIFORM_BUFFER, UBO_LENGTH);
|
||||
|
||||
// The GPU shader code appears to be context-specific on Mesa/i965.
|
||||
// This means that if we compiled the ubershaders asynchronously, they will be recompiled
|
||||
// on the main thread the first time they are used, causing stutter. Nouveau has been
|
||||
// reported to crash if draw calls are invoked on the shared context threads. For now,
|
||||
// disable asynchronous compilation on Mesa.
|
||||
if (!DriverDetails::HasBug(DriverDetails::BUG_SHARED_CONTEXT_SHADER_COMPILATION))
|
||||
s_async_compiler = std::make_unique<SharedContextAsyncShaderCompiler>();
|
||||
|
||||
// Read our shader cache, only if supported and enabled
|
||||
if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache)
|
||||
LoadProgramBinaries();
|
||||
|
||||
CreateHeader();
|
||||
CreateAttributelessVAO();
|
||||
|
||||
CurrentProgram = 0;
|
||||
last_entry = nullptr;
|
||||
last_uber_entry = nullptr;
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
{
|
||||
if (s_async_compiler)
|
||||
s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads());
|
||||
PrecompileUberShaders();
|
||||
}
|
||||
|
||||
if (s_async_compiler)
|
||||
{
|
||||
// No point using the async compiler without workers.
|
||||
s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
|
||||
if (!s_async_compiler->HasWorkerThreads())
|
||||
s_async_compiler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramShaderCache::RetrieveAsyncShaders()
|
||||
{
|
||||
if (s_async_compiler)
|
||||
s_async_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
void ProgramShaderCache::Reload()
|
||||
{
|
||||
if (s_async_compiler)
|
||||
{
|
||||
s_async_compiler->WaitUntilCompletion();
|
||||
s_async_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
const bool use_cache = g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache;
|
||||
if (use_cache)
|
||||
SaveProgramBinaries();
|
||||
|
||||
s_program_disk_cache.Close();
|
||||
s_uber_program_disk_cache.Close();
|
||||
DestroyShaders();
|
||||
|
||||
if (use_cache)
|
||||
LoadProgramBinaries();
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
PrecompileUberShaders();
|
||||
|
||||
CurrentProgram = 0;
|
||||
last_entry = nullptr;
|
||||
last_uber_entry = nullptr;
|
||||
last_uid = {};
|
||||
last_uber_uid = {};
|
||||
}
|
||||
|
||||
void ProgramShaderCache::Shutdown()
|
||||
{
|
||||
if (s_async_compiler)
|
||||
{
|
||||
s_async_compiler->WaitUntilCompletion();
|
||||
s_async_compiler->StopWorkerThreads();
|
||||
s_async_compiler->RetrieveWorkItems();
|
||||
s_async_compiler.reset();
|
||||
}
|
||||
|
||||
// store all shaders in cache on disk
|
||||
if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache)
|
||||
SaveProgramBinaries();
|
||||
s_program_disk_cache.Close();
|
||||
s_uber_program_disk_cache.Close();
|
||||
|
||||
DestroyShaders();
|
||||
s_buffer.reset();
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
@ -781,134 +548,6 @@ void ProgramShaderCache::InvalidateLastProgram()
|
|||
CurrentProgram = 0;
|
||||
}
|
||||
|
||||
GLuint ProgramShaderCache::CreateProgramFromBinary(const u8* value, u32 value_size)
|
||||
{
|
||||
const u8* binary = value + sizeof(GLenum);
|
||||
GLint binary_size = value_size - sizeof(GLenum);
|
||||
GLenum prog_format;
|
||||
std::memcpy(&prog_format, value, sizeof(GLenum));
|
||||
|
||||
GLuint progid = glCreateProgram();
|
||||
glProgramBinary(progid, prog_format, binary, binary_size);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(progid, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glDeleteProgram(progid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return progid;
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::CreateCacheEntryFromBinary(PCacheEntry* entry, const u8* value,
|
||||
u32 value_size)
|
||||
{
|
||||
entry->in_cache = true;
|
||||
entry->pending = false;
|
||||
entry->shader.glprogid = CreateProgramFromBinary(value, value_size);
|
||||
if (entry->shader.glprogid == 0)
|
||||
return false;
|
||||
|
||||
entry->shader.SetProgramVariables();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::LoadProgramBinaries()
|
||||
{
|
||||
GLint Supported;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &Supported);
|
||||
if (!Supported)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "GL_ARB_get_program_binary is supported, but no binary format is known. So "
|
||||
"disable shader cache.");
|
||||
g_ogl_config.bSupportsGLSLCache = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load game-specific shaders.
|
||||
std::string cache_filename =
|
||||
GetDiskShaderCacheFileName(APIType::OpenGL, "ProgramBinaries", true, true);
|
||||
ProgramShaderCacheInserter<SHADERUID> inserter(pshaders);
|
||||
s_program_disk_cache.OpenAndRead(cache_filename, inserter);
|
||||
|
||||
// Load global ubershaders.
|
||||
cache_filename =
|
||||
GetDiskShaderCacheFileName(APIType::OpenGL, "UberProgramBinaries", false, true);
|
||||
ProgramShaderCacheInserter<UBERSHADERUID> uber_inserter(ubershaders);
|
||||
s_uber_program_disk_cache.OpenAndRead(cache_filename, uber_inserter);
|
||||
}
|
||||
SETSTAT(stats.numPixelShadersAlive, pshaders.size());
|
||||
}
|
||||
|
||||
static bool GetProgramBinary(const ProgramShaderCache::PCacheEntry& entry, std::vector<u8>& data)
|
||||
{
|
||||
// Clear any prior error code
|
||||
glGetError();
|
||||
|
||||
GLint link_status = GL_FALSE, delete_status = GL_TRUE, binary_size = 0;
|
||||
glGetProgramiv(entry.shader.glprogid, GL_LINK_STATUS, &link_status);
|
||||
glGetProgramiv(entry.shader.glprogid, GL_DELETE_STATUS, &delete_status);
|
||||
glGetProgramiv(entry.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size);
|
||||
if (glGetError() != GL_NO_ERROR || link_status == GL_FALSE || delete_status == GL_TRUE ||
|
||||
binary_size == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.resize(binary_size + sizeof(GLenum));
|
||||
|
||||
GLsizei length = binary_size;
|
||||
GLenum prog_format;
|
||||
glGetProgramBinary(entry.shader.glprogid, binary_size, &length, &prog_format,
|
||||
&data[sizeof(GLenum)]);
|
||||
if (glGetError() != GL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
std::memcpy(&data[0], &prog_format, sizeof(prog_format));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename CacheMapType, typename DiskCacheType>
|
||||
static void SaveProgramBinaryMap(CacheMapType& program_map, DiskCacheType& disk_cache)
|
||||
{
|
||||
std::vector<u8> binary_data;
|
||||
for (auto& entry : program_map)
|
||||
{
|
||||
if (entry.second.in_cache || entry.second.pending)
|
||||
continue;
|
||||
|
||||
// Entry is now in cache (even if it fails, we don't want to try to save it again).
|
||||
entry.second.in_cache = true;
|
||||
if (!GetProgramBinary(entry.second, binary_data))
|
||||
continue;
|
||||
|
||||
disk_cache.Append(entry.first, &binary_data[0], static_cast<u32>(binary_data.size()));
|
||||
}
|
||||
|
||||
disk_cache.Sync();
|
||||
}
|
||||
|
||||
void ProgramShaderCache::SaveProgramBinaries()
|
||||
{
|
||||
SaveProgramBinaryMap(pshaders, s_program_disk_cache);
|
||||
SaveProgramBinaryMap(ubershaders, s_uber_program_disk_cache);
|
||||
}
|
||||
|
||||
void ProgramShaderCache::DestroyShaders()
|
||||
{
|
||||
glUseProgram(0);
|
||||
|
||||
for (auto& entry : pshaders)
|
||||
entry.second.Destroy();
|
||||
pshaders.clear();
|
||||
|
||||
for (auto& entry : ubershaders)
|
||||
entry.second.Destroy();
|
||||
ubershaders.clear();
|
||||
}
|
||||
|
||||
const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* vertex_shader,
|
||||
const OGLShader* geometry_shader,
|
||||
const OGLShader* pixel_shader)
|
||||
|
@ -1144,346 +783,4 @@ void ProgramShaderCache::CreateHeader()
|
|||
v > GlslEs300 ? "precision highp sampler2DMS;" : "",
|
||||
v >= GlslEs310 ? "precision highp image2DArray;" : "");
|
||||
}
|
||||
|
||||
void ProgramShaderCache::PrecompileUberShaders()
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) {
|
||||
UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) {
|
||||
// UIDs must have compatible texgens, a mismatching combination will never be queried.
|
||||
if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) {
|
||||
if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
UBERSHADERUID uid;
|
||||
std::memcpy(&uid.vuid, &vuid, sizeof(uid.vuid));
|
||||
std::memcpy(&uid.puid, &puid, sizeof(uid.puid));
|
||||
std::memcpy(&uid.guid, &guid, sizeof(uid.guid));
|
||||
|
||||
// The ubershader may already exist if shader caching is enabled.
|
||||
if (!success || ubershaders.find(uid) != ubershaders.end())
|
||||
return;
|
||||
|
||||
PCacheEntry& entry = ubershaders[uid];
|
||||
entry.in_cache = false;
|
||||
entry.pending = false;
|
||||
|
||||
// Multi-context path?
|
||||
if (s_async_compiler)
|
||||
{
|
||||
entry.pending = true;
|
||||
s_async_compiler->QueueWorkItem(
|
||||
s_async_compiler->CreateWorkItem<UberShaderCompileWorkItem>(uid));
|
||||
return;
|
||||
}
|
||||
|
||||
ShaderHostConfig host_config = ShaderHostConfig::GetCurrent();
|
||||
ShaderCode vcode =
|
||||
UberShader::GenVertexShader(APIType::OpenGL, host_config, uid.vuid.GetUidData());
|
||||
ShaderCode pcode =
|
||||
UberShader::GenPixelShader(APIType::OpenGL, host_config, uid.puid.GetUidData());
|
||||
ShaderCode gcode;
|
||||
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders &&
|
||||
!uid.guid.GetUidData()->IsPassthrough())
|
||||
{
|
||||
GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData());
|
||||
}
|
||||
|
||||
// Always background compile, even when it's not supported.
|
||||
// This way hopefully the driver can still compile the shaders in parallel.
|
||||
if (!CompileShader(entry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()))
|
||||
{
|
||||
// Stop compiling shaders if any of them fail, no point continuing.
|
||||
success = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (s_async_compiler)
|
||||
{
|
||||
s_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) {
|
||||
Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(),
|
||||
static_cast<int>(completed), static_cast<int>(total));
|
||||
});
|
||||
s_async_compiler->RetrieveWorkItems();
|
||||
Host_UpdateProgressDialog("", -1, -1);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
PanicAlert("One or more ubershaders failed to compile. Disabling ubershaders.");
|
||||
for (auto& it : ubershaders)
|
||||
it.second.Destroy();
|
||||
ubershaders.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param)
|
||||
{
|
||||
SharedContextData* ctx_data = new SharedContextData();
|
||||
ctx_data->context = GLInterface->CreateSharedContext();
|
||||
if (!ctx_data->context)
|
||||
{
|
||||
PanicAlert("Failed to create shared context for shader compiling.");
|
||||
delete ctx_data;
|
||||
return false;
|
||||
}
|
||||
|
||||
*param = ctx_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param)
|
||||
{
|
||||
SharedContextData* ctx_data = reinterpret_cast<SharedContextData*>(param);
|
||||
if (!ctx_data->context->MakeCurrent())
|
||||
{
|
||||
PanicAlert("Failed to make shared context current.");
|
||||
ctx_data->context->Shutdown();
|
||||
delete ctx_data;
|
||||
return false;
|
||||
}
|
||||
|
||||
CreatePrerenderArrays(ctx_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param)
|
||||
{
|
||||
SharedContextData* ctx_data = reinterpret_cast<SharedContextData*>(param);
|
||||
DestroyPrerenderArrays(ctx_data);
|
||||
ctx_data->context->Shutdown();
|
||||
delete ctx_data;
|
||||
}
|
||||
|
||||
ProgramShaderCache::ShaderCompileWorkItem::ShaderCompileWorkItem(const SHADERUID& uid)
|
||||
{
|
||||
std::memcpy(&m_uid, &uid, sizeof(m_uid));
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::ShaderCompileWorkItem::Compile()
|
||||
{
|
||||
ShaderHostConfig host_config = ShaderHostConfig::GetCurrent();
|
||||
ShaderCode vcode =
|
||||
GenerateVertexShaderCode(APIType::OpenGL, host_config, m_uid.vuid.GetUidData());
|
||||
ShaderCode pcode = GeneratePixelShaderCode(APIType::OpenGL, host_config, m_uid.puid.GetUidData());
|
||||
ShaderCode gcode;
|
||||
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders &&
|
||||
!m_uid.guid.GetUidData()->IsPassthrough())
|
||||
gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData());
|
||||
|
||||
CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer());
|
||||
DrawPrerenderArray(m_program,
|
||||
static_cast<PrimitiveType>(m_uid.guid.GetUidData()->primitive_type));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::ShaderCompileWorkItem::Retrieve()
|
||||
{
|
||||
auto iter = pshaders.find(m_uid);
|
||||
if (iter != pshaders.end() && !iter->second.pending)
|
||||
{
|
||||
// Main thread already compiled this shader.
|
||||
m_program.Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
PCacheEntry& entry = pshaders[m_uid];
|
||||
entry.shader = m_program;
|
||||
entry.in_cache = false;
|
||||
entry.pending = false;
|
||||
}
|
||||
|
||||
ProgramShaderCache::UberShaderCompileWorkItem::UberShaderCompileWorkItem(const UBERSHADERUID& uid)
|
||||
{
|
||||
std::memcpy(&m_uid, &uid, sizeof(m_uid));
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::UberShaderCompileWorkItem::Compile()
|
||||
{
|
||||
ShaderHostConfig host_config = ShaderHostConfig::GetCurrent();
|
||||
ShaderCode vcode =
|
||||
UberShader::GenVertexShader(APIType::OpenGL, host_config, m_uid.vuid.GetUidData());
|
||||
ShaderCode pcode =
|
||||
UberShader::GenPixelShader(APIType::OpenGL, host_config, m_uid.puid.GetUidData());
|
||||
ShaderCode gcode;
|
||||
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders &&
|
||||
!m_uid.guid.GetUidData()->IsPassthrough())
|
||||
gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData());
|
||||
|
||||
CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer());
|
||||
DrawPrerenderArray(m_program,
|
||||
static_cast<PrimitiveType>(m_uid.guid.GetUidData()->primitive_type));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::UberShaderCompileWorkItem::Retrieve()
|
||||
{
|
||||
auto iter = ubershaders.find(m_uid);
|
||||
if (iter != ubershaders.end() && !iter->second.pending)
|
||||
{
|
||||
// Main thread already compiled this shader.
|
||||
m_program.Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
PCacheEntry& entry = ubershaders[m_uid];
|
||||
entry.shader = m_program;
|
||||
entry.in_cache = false;
|
||||
entry.pending = false;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::CreatePrerenderArrays(SharedContextData* data)
|
||||
{
|
||||
// Create a framebuffer object to render into.
|
||||
// This is because in EGL, and potentially GLX, we have a surfaceless context.
|
||||
glGenTextures(1, &data->prerender_FBO_tex);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, data->prerender_FBO_tex);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glGenTextures(1, &data->prerender_FBO_depth);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, data->prerender_FBO_depth);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, 1, 1, 1, 0, GL_DEPTH_COMPONENT,
|
||||
GL_FLOAT, nullptr);
|
||||
glGenFramebuffers(1, &data->prerender_FBO);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, data->prerender_FBO);
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, data->prerender_FBO_tex, 0, 0);
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, data->prerender_FBO_depth, 0, 0);
|
||||
|
||||
// Create VAO for the prerender vertices.
|
||||
// We don't use the normal VAO map, since we need to change the VBO pointer.
|
||||
glGenVertexArrays(1, &data->prerender_VAO);
|
||||
glBindVertexArray(data->prerender_VAO);
|
||||
|
||||
// Create and populate the prerender VBO. We need enough space to draw 3 triangles.
|
||||
static constexpr float vbo_data[] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
|
||||
constexpr u32 vbo_stride = sizeof(float) * 3;
|
||||
glGenBuffers(1, &data->prerender_VBO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, data->prerender_VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW);
|
||||
|
||||
// We only need a position in our prerender vertex.
|
||||
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
|
||||
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr);
|
||||
|
||||
// The other attributes have to be active to avoid variant generation.
|
||||
glEnableVertexAttribArray(SHADER_POSMTX_ATTRIB);
|
||||
glVertexAttribIPointer(SHADER_POSMTX_ATTRIB, 1, GL_UNSIGNED_BYTE, vbo_stride, nullptr);
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
glEnableVertexAttribArray(SHADER_NORM0_ATTRIB + i);
|
||||
glVertexAttribPointer(SHADER_NORM0_ATTRIB + i, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr);
|
||||
}
|
||||
for (u32 i = 0; i < 2; i++)
|
||||
{
|
||||
glEnableVertexAttribArray(SHADER_COLOR0_ATTRIB + i);
|
||||
glVertexAttribPointer(SHADER_COLOR0_ATTRIB + i, 4, GL_UNSIGNED_BYTE, GL_TRUE, vbo_stride,
|
||||
nullptr);
|
||||
}
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB + i);
|
||||
glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB + i, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr);
|
||||
}
|
||||
|
||||
// We need an index buffer to set up the same drawing state on Mesa.
|
||||
static constexpr u16 ibo_data[] = {0, 1, 2};
|
||||
glGenBuffers(1, &data->prerender_IBO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data->prerender_IBO);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ibo_data), ibo_data, GL_STATIC_DRAW);
|
||||
|
||||
// Mesa also requires the primitive restart state matches?
|
||||
if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart)
|
||||
{
|
||||
if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3)
|
||||
{
|
||||
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GLExtensions::Version() >= 310)
|
||||
{
|
||||
glEnable(GL_PRIMITIVE_RESTART);
|
||||
glPrimitiveRestartIndex(65535);
|
||||
}
|
||||
else
|
||||
{
|
||||
glEnableClientState(GL_PRIMITIVE_RESTART_NV);
|
||||
glPrimitiveRestartIndexNV(65535);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramShaderCache::DestroyPrerenderArrays(SharedContextData* data)
|
||||
{
|
||||
if (data->prerender_VAO)
|
||||
{
|
||||
glDeleteVertexArrays(1, &data->prerender_VAO);
|
||||
data->prerender_VAO = 0;
|
||||
}
|
||||
if (data->prerender_VBO)
|
||||
{
|
||||
glDeleteBuffers(1, &data->prerender_VBO);
|
||||
data->prerender_VBO = 0;
|
||||
}
|
||||
if (data->prerender_IBO)
|
||||
{
|
||||
glDeleteBuffers(1, &data->prerender_IBO);
|
||||
data->prerender_IBO = 0;
|
||||
}
|
||||
if (data->prerender_FBO)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &data->prerender_FBO);
|
||||
data->prerender_FBO = 0;
|
||||
}
|
||||
if (data->prerender_FBO_tex)
|
||||
{
|
||||
glDeleteTextures(1, &data->prerender_FBO_tex);
|
||||
data->prerender_FBO_tex = 0;
|
||||
}
|
||||
if (data->prerender_FBO_depth)
|
||||
{
|
||||
glDeleteTextures(1, &data->prerender_FBO_depth);
|
||||
data->prerender_FBO_depth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramShaderCache::DrawPrerenderArray(const SHADER& shader, PrimitiveType primitive_type)
|
||||
{
|
||||
// This is called on a worker thread, so we don't want to use the normal binding process.
|
||||
glUseProgram(shader.glprogid);
|
||||
|
||||
// The number of primitives drawn depends on the type.
|
||||
switch (primitive_type)
|
||||
{
|
||||
case PrimitiveType::Points:
|
||||
glDrawElements(GL_POINTS, 1, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
case PrimitiveType::Lines:
|
||||
glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
case PrimitiveType::Triangles:
|
||||
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
case PrimitiveType::TriangleStrip:
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 3, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
// Has to be finished by the time the main thread picks it up.
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(sync);
|
||||
}
|
||||
} // namespace OGL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue