summaryrefslogtreecommitdiff
path: root/Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp
diff options
context:
space:
mode:
authorNeoBrainX <NeoBrainX@gmail.com>2013-03-26 22:16:29 +0100
committerNeoBrainX <NeoBrainX@gmail.com>2013-03-26 22:21:19 +0100
commit3253603ae7a80860a294c11c95b3697634ac73d9 (patch)
tree5ef2a70521939d71fb3cacbd8a47babd9bdbdba8 /Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp
parent76148a52b82cae8e6cf11b6eeeeff55345b0f12e (diff)
parentb75a617d8a2e4307ed4af06669e92fc33f6af1df (diff)
Merge 'master' into shader-uids-awesome.
Conflicts: Source/Core/VideoCommon/Src/LightingShaderGen.cpp Source/Core/VideoCommon/Src/PixelShaderGen.cpp Source/Core/VideoCommon/Src/PixelShaderGen.h Source/Core/VideoCommon/Src/PixelShaderManager.cpp Source/Core/VideoCommon/Src/VertexShaderGen.cpp Source/Core/VideoCommon/Src/VertexShaderGen.h Source/Plugins/Plugin_VideoOGL/Src/PixelShaderCache.cpp Source/Plugins/Plugin_VideoOGL/Src/PixelShaderCache.h Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp Source/Plugins/Plugin_VideoOGL/Src/VertexShaderCache.cpp Source/Plugins/Plugin_VideoOGL/Src/VertexShaderCache.h
Diffstat (limited to 'Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp')
-rw-r--r--Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp540
1 files changed, 540 insertions, 0 deletions
diff --git a/Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp b/Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp
new file mode 100644
index 0000000000..53b0210919
--- /dev/null
+++ b/Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp
@@ -0,0 +1,540 @@
+// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#include "ProgramShaderCache.h"
+#include "MathUtil.h"
+#include "StreamBuffer.h"
+#include "Debugger.h"
+#include "Statistics.h"
+#include "ImageWrite.h"
+#include "Render.h"
+
+namespace OGL
+{
+
+static const u32 UBO_LENGTH = 32*1024*1024;
+
+GLintptr ProgramShaderCache::s_vs_data_size;
+GLintptr ProgramShaderCache::s_ps_data_size;
+GLintptr ProgramShaderCache::s_vs_data_offset;
+u8 *ProgramShaderCache::s_ubo_buffer;
+u32 ProgramShaderCache::s_ubo_buffer_size;
+bool ProgramShaderCache::s_ubo_dirty;
+
+static StreamBuffer *s_buffer;
+
+LinearDiskCache<SHADERUID, u8> g_program_disk_cache;
+static GLuint CurrentProgram = 0;
+ProgramShaderCache::PCache ProgramShaderCache::pshaders;
+ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry;
+SHADERUID ProgramShaderCache::last_uid;
+
+static char s_glsl_header[1024] = "";
+
+const char *UniformNames[NUM_UNIFORMS] =
+{
+ // PIXEL SHADER UNIFORMS
+ I_COLORS,
+ I_KCOLORS,
+ I_ALPHA,
+ I_TEXDIMS,
+ I_ZBIAS ,
+ I_INDTEXSCALE ,
+ I_INDTEXMTX,
+ I_FOG,
+ I_PLIGHTS,
+ I_PMATERIALS,
+ // VERTEX SHADER UNIFORMS
+ I_POSNORMALMATRIX,
+ I_PROJECTION ,
+ I_MATERIALS,
+ I_LIGHTS,
+ I_TEXMATRICES,
+ I_TRANSFORMMATRICES ,
+ I_NORMALMATRICES ,
+ I_POSTTRANSFORMMATRICES,
+ I_DEPTHPARAMS,
+};
+
+void SHADER::SetProgramVariables()
+{
+ // glsl shader must be bind to set samplers
+ Bind();
+
+ // Bind UBO
+ if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
+ {
+ GLint PSBlock_id = glGetUniformBlockIndex(glprogid, "PSBlock");
+ GLint VSBlock_id = glGetUniformBlockIndex(glprogid, "VSBlock");
+
+ if(PSBlock_id != -1)
+ glUniformBlockBinding(glprogid, PSBlock_id, 1);
+ if(VSBlock_id != -1)
+ glUniformBlockBinding(glprogid, VSBlock_id, 2);
+ }
+
+ // We cache our uniform locations for now
+ // Once we move up to a newer version of GLSL, ~1.30
+ // We can remove this
+
+ // (Sonicadvance): For some reason this fails on my hardware
+ //glGetUniformIndices(glprogid, NUM_UNIFORMS, UniformNames, UniformLocations);
+ // Got to do it this crappy way.
+ UniformLocations[0] = glGetUniformLocation(glprogid, UniformNames[0]);
+ if (!g_ActiveConfig.backend_info.bSupportsGLSLUBO)
+ for (int a = 1; a < NUM_UNIFORMS; ++a)
+ UniformLocations[a] = glGetUniformLocation(glprogid, UniformNames[a]);
+
+ // Bind Texture Sampler
+ for (int a = 0; a <= 9; ++a)
+ {
+ char name[8];
+ snprintf(name, 8, "samp%d", a);
+
+ // Still need to get sampler locations since we aren't binding them statically in the shaders
+ int loc = glGetUniformLocation(glprogid, name);
+ if (loc != -1)
+ glUniform1i(loc, a);
+ }
+
+}
+
+void SHADER::SetProgramBindings()
+{
+ if (g_ActiveConfig.backend_info.bSupportsDualSourceBlend)
+ {
+ // So we do support extended blending
+ // So we need to set a few more things here.
+ // Bind our out locations
+ glBindFragDataLocationIndexed(glprogid, 0, 0, "ocol0");
+ glBindFragDataLocationIndexed(glprogid, 0, 1, "ocol1");
+ }
+ else
+ glBindFragDataLocation(glprogid, 0, "ocol0");
+
+ // Need to set some attribute locations
+ glBindAttribLocation(glprogid, SHADER_POSITION_ATTRIB, "rawpos");
+
+ glBindAttribLocation(glprogid, SHADER_POSMTX_ATTRIB, "fposmtx");
+
+ glBindAttribLocation(glprogid, SHADER_COLOR0_ATTRIB, "color0");
+ glBindAttribLocation(glprogid, SHADER_COLOR1_ATTRIB, "color1");
+
+ glBindAttribLocation(glprogid, SHADER_NORM0_ATTRIB, "rawnorm0");
+ glBindAttribLocation(glprogid, SHADER_NORM1_ATTRIB, "rawnorm1");
+ glBindAttribLocation(glprogid, SHADER_NORM2_ATTRIB, "rawnorm2");
+
+ for(int i=0; i<8; i++) {
+ char attrib_name[8];
+ snprintf(attrib_name, 8, "tex%d", i);
+ glBindAttribLocation(glprogid, SHADER_TEXTURE0_ATTRIB+i, attrib_name);
+ }
+}
+
+void SHADER::Bind()
+{
+ if(CurrentProgram != glprogid)
+ {
+ glUseProgram(glprogid);
+ CurrentProgram = glprogid;
+ }
+}
+
+
+void ProgramShaderCache::SetMultiPSConstant4fv(unsigned int offset, const float *f, unsigned int count)
+{
+ s_ubo_dirty = true;
+ memcpy(s_ubo_buffer+(offset*4*sizeof(float)), f, count*4*sizeof(float));
+}
+
+void ProgramShaderCache::SetMultiVSConstant4fv(unsigned int offset, const float *f, unsigned int count)
+{
+ s_ubo_dirty = true;
+ memcpy(s_ubo_buffer+(offset*4*sizeof(float))+s_vs_data_offset, f, count*4*sizeof(float));
+}
+
+void ProgramShaderCache::UploadConstants()
+{
+ if(s_ubo_dirty) {
+ s_buffer->Alloc(s_ubo_buffer_size);
+ size_t offset = s_buffer->Upload(s_ubo_buffer, s_ubo_buffer_size);
+ glBindBufferRange(GL_UNIFORM_BUFFER, 1, s_buffer->getBuffer(), offset, s_ps_data_size);
+ glBindBufferRange(GL_UNIFORM_BUFFER, 2, s_buffer->getBuffer(), offset + s_vs_data_offset, s_vs_data_size);
+ s_ubo_dirty = false;
+ }
+}
+
+GLuint ProgramShaderCache::GetCurrentProgram(void)
+{
+ return CurrentProgram;
+}
+
+SHADER* ProgramShaderCache::SetShader ( DSTALPHA_MODE dstAlphaMode, u32 components )
+{
+ SHADERUID uid;
+ GetShaderId(&uid, dstAlphaMode, components);
+
+ // Check if the shader is already set
+ if (last_entry)
+ {
+ if (uid == last_uid)
+ {
+ GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
+ last_entry->shader.Bind();
+ return &last_entry->shader;
+ }
+ }
+
+ last_uid = uid;
+
+ // Check if shader is already in cache
+ PCache::iterator iter = pshaders.find(uid);
+ if (iter != pshaders.end())
+ {
+ PCacheEntry *entry = &iter->second;
+ last_entry = entry;
+
+ GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
+ last_entry->shader.Bind();
+ return &last_entry->shader;
+ }
+
+ // Make an entry in the table
+ PCacheEntry& newentry = pshaders[uid];
+ last_entry = &newentry;
+ newentry.in_cache = 0;
+
+ VertexShaderCode vcode;
+ PixelShaderCode pcode;
+ GenerateVertexShaderCode(vcode, components, API_OPENGL);
+ GeneratePixelShaderCode(pcode, dstAlphaMode, API_OPENGL, components);
+
+ if (g_ActiveConfig.bEnableShaderDebugging)
+ {
+ newentry.shader.strvprog = vcode.GetBuffer();
+ newentry.shader.strpprog = pcode.GetBuffer();
+ }
+
+#if defined(_DEBUG) || defined(DEBUGFAST)
+ if (g_ActiveConfig.iLog & CONF_SAVESHADERS) {
+ static int counter = 0;
+ char szTemp[MAX_PATH];
+ sprintf(szTemp, "%svs_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
+ SaveData(szTemp, vcode.GetBuffer());
+ sprintf(szTemp, "%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
+ SaveData(szTemp, pcode.GetBuffer());
+ }
+#endif
+
+ if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer())) {
+ GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
+ return NULL;
+ }
+
+ INCSTAT(stats.numPixelShadersCreated);
+ SETSTAT(stats.numPixelShadersAlive, pshaders.size());
+ GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
+
+ last_entry->shader.Bind();
+ return &last_entry->shader;
+}
+
+bool ProgramShaderCache::CompileShader ( SHADER& shader, const char* vcode, const char* pcode )
+{
+ GLuint vsid = CompileSingleShader(GL_VERTEX_SHADER, vcode);
+ GLuint psid = CompileSingleShader(GL_FRAGMENT_SHADER, pcode);
+
+ if(!vsid || !psid)
+ {
+ glDeleteShader(vsid);
+ glDeleteShader(psid);
+ return false;
+ }
+
+ GLuint pid = shader.glprogid = glCreateProgram();;
+
+ glAttachShader(pid, vsid);
+ glAttachShader(pid, psid);
+
+ if (g_ogl_config.bSupportsGLSLCache)
+ glProgramParameteri(pid, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
+
+ shader.SetProgramBindings();
+
+ glLinkProgram(pid);
+
+ // original shaders aren't needed any more
+ glDeleteShader(vsid);
+ glDeleteShader(psid);
+
+ GLint linkStatus;
+ glGetProgramiv(pid, GL_LINK_STATUS, &linkStatus);
+ GLsizei length = 0;
+ glGetProgramiv(pid, GL_INFO_LOG_LENGTH, &length);
+ if (linkStatus != GL_TRUE || (length > 1 && DEBUG_GLSL))
+ {
+ GLsizei charsWritten;
+ GLchar* infoLog = new GLchar[length];
+ glGetProgramInfoLog(pid, length, &charsWritten, infoLog);
+ ERROR_LOG(VIDEO, "Program info log:\n%s", infoLog);
+ char szTemp[MAX_PATH];
+ sprintf(szTemp, "%sp_%d.txt", File::GetUserPath(D_DUMP_IDX).c_str(), pid);
+ std::ofstream file;
+ OpenFStream(file, szTemp, std::ios_base::out);
+ file << infoLog << s_glsl_header << vcode << s_glsl_header << pcode;
+ file.close();
+ delete [] infoLog;
+ }
+ if (linkStatus != GL_TRUE)
+ {
+ // Compile failed
+ ERROR_LOG(VIDEO, "Program linking failed; see info log");
+
+ // Don't try to use this shader
+ glDeleteProgram(pid);
+ return false;
+ }
+
+ shader.SetProgramVariables();
+
+ return true;
+}
+
+GLuint ProgramShaderCache::CompileSingleShader (GLuint type, const char* code )
+{
+ GLuint result = glCreateShader(type);
+
+ const char *src[] = {s_glsl_header, code};
+
+ glShaderSource(result, 2, src, NULL);
+ glCompileShader(result);
+ GLint compileStatus;
+ glGetShaderiv(result, GL_COMPILE_STATUS, &compileStatus);
+ GLsizei length = 0;
+ glGetShaderiv(result, GL_INFO_LOG_LENGTH, &length);
+ if (compileStatus != GL_TRUE || (length > 1 && DEBUG_GLSL))
+ {
+ GLsizei charsWritten;
+ GLchar* infoLog = new GLchar[length];
+ glGetShaderInfoLog(result, length, &charsWritten, infoLog);
+ ERROR_LOG(VIDEO, "PS Shader info log:\n%s", infoLog);
+ char szTemp[MAX_PATH];
+ sprintf(szTemp, "%sps_%d.txt", File::GetUserPath(D_DUMP_IDX).c_str(), result);
+ std::ofstream file;
+ OpenFStream(file, szTemp, std::ios_base::out);
+ file << infoLog << s_glsl_header << code;
+ file.close();
+ delete[] infoLog;
+ }
+ if (compileStatus != GL_TRUE)
+ {
+ // Compile failed
+ ERROR_LOG(VIDEO, "Shader compilation failed; see info log");
+
+ // Don't try to use this shader
+ glDeleteShader(result);
+ return 0;
+ }
+ (void)GL_REPORT_ERROR();
+ return result;
+}
+
+
+
+void ProgramShaderCache::GetShaderId ( SHADERUID* uid, DSTALPHA_MODE dstAlphaMode, u32 components )
+{
+ GetPixelShaderUid(uid->puid, dstAlphaMode, API_OPENGL, components);
+ GetVertexShaderUid(uid->vuid, components, API_OPENGL);
+}
+
+
+
+ProgramShaderCache::PCacheEntry ProgramShaderCache::GetShaderProgram(void)
+{
+ return *last_entry;
+}
+
+void ProgramShaderCache::Init(void)
+{
+ // We have to get the UBO alignment here because
+ // if we generate a buffer that isn't aligned
+ // then the UBO will fail.
+ if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
+ {
+ GLint Align;
+ glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &Align);
+
+ s_ps_data_size = C_PENVCONST_END * sizeof(float) * 4;
+ s_vs_data_size = C_VENVCONST_END * sizeof(float) * 4;
+ s_vs_data_offset = ROUND_UP(s_ps_data_size, Align);
+ s_ubo_buffer_size = ROUND_UP(s_ps_data_size, Align) + ROUND_UP(s_vs_data_size, Align);
+
+ // We multiply by *4*4 because we need to get down to basic machine units.
+ // So multiply by four to get how many floats we have from vec4s
+ // Then once more to get bytes
+ s_buffer = new StreamBuffer(GL_UNIFORM_BUFFER, UBO_LENGTH);
+
+ s_ubo_buffer = new u8[s_ubo_buffer_size];
+ memset(s_ubo_buffer, 0, s_ubo_buffer_size);
+ s_ubo_dirty = true;
+ }
+
+ // Read our shader cache, only if supported
+ if (g_ogl_config.bSupportsGLSLCache)
+ {
+ 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
+ {
+ if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
+ File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX).c_str());
+
+ char cache_filename[MAX_PATH];
+ sprintf(cache_filename, "%sogl-%s-shaders.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(),
+ SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str());
+
+ ProgramShaderCacheInserter inserter;
+ g_program_disk_cache.OpenAndRead(cache_filename, inserter);
+ }
+ SETSTAT(stats.numPixelShadersAlive, pshaders.size());
+ }
+
+ CreateHeader();
+
+ CurrentProgram = 0;
+ last_entry = NULL;
+}
+
+void ProgramShaderCache::Shutdown(void)
+{
+ // store all shaders in cache on disk
+ if (g_ogl_config.bSupportsGLSLCache)
+ {
+ PCache::iterator iter = pshaders.begin();
+ for (; iter != pshaders.end(); ++iter)
+ {
+ if(iter->second.in_cache) continue;
+
+ GLint binary_size;
+ glGetProgramiv(iter->second.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size);
+ if(!binary_size) continue;
+
+ u8 *data = new u8[binary_size+sizeof(GLenum)];
+ u8 *binary = data + sizeof(GLenum);
+ GLenum *prog_format = (GLenum*)data;
+ glGetProgramBinary(iter->second.shader.glprogid, binary_size, NULL, prog_format, binary);
+
+ g_program_disk_cache.Append(iter->first, data, binary_size+sizeof(GLenum));
+ delete [] data;
+ }
+
+ g_program_disk_cache.Sync();
+ g_program_disk_cache.Close();
+ }
+
+ glUseProgram(0);
+
+ PCache::iterator iter = pshaders.begin();
+ for (; iter != pshaders.end(); ++iter)
+ iter->second.Destroy();
+ pshaders.clear();
+
+ if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
+ {
+ delete s_buffer;
+ s_buffer = 0;
+ delete [] s_ubo_buffer;
+ s_ubo_buffer = 0;
+ }
+}
+
+void ProgramShaderCache::CreateHeader ( void )
+{
+#ifdef _WIN32
+ // Intel Windows driver has a issue:
+ // their glsl doesn't know about the ubo extension, so we can't load it.
+ // but as version 140, ubo is in core and don't have to be loaded in glsl.
+ // as sandy do ogl3.1, glsl 140 is supported, so force it in this way.
+ // TODO: remove this again when the issue is fixed:
+ // see http://communities.intel.com/thread/36084
+ bool glsl140_hack = strcmp(g_ogl_config.gl_vendor, "Intel") == 0;
+#elif __APPLE__
+ // as apple doesn't support glsl130 at all, we also have to use glsl140
+ bool glsl140_hack = true;
+#else
+ bool glsl140_hack = false;
+#endif
+
+ snprintf(s_glsl_header, sizeof(s_glsl_header),
+ "#version %s\n"
+ "%s\n" // tex_rect
+ "%s\n" // ubo
+
+ "\n"// A few required defines and ones that will make our lives a lot easier
+ "#define ATTRIN in\n"
+ "#define ATTROUT out\n"
+ "#define VARYIN centroid in\n"
+ "#define VARYOUT centroid out\n"
+
+ // Silly differences
+ "#define float2 vec2\n"
+ "#define float3 vec3\n"
+ "#define float4 vec4\n"
+
+ // hlsl to glsl function translation
+ "#define frac(x) fract(x)\n"
+ "#define saturate(x) clamp(x, 0.0f, 1.0f)\n"
+ "#define lerp(x, y, z) mix(x, y, z)\n"
+
+
+ , glsl140_hack ? "140" : "130"
+ , glsl140_hack ? "#define texture2DRect texture" : "#extension GL_ARB_texture_rectangle : enable"
+ , g_ActiveConfig.backend_info.bSupportsGLSLUBO && !glsl140_hack ? "#extension GL_ARB_uniform_buffer_object : enable" : "// ubo disabled"
+ );
+}
+
+
+void ProgramShaderCache::ProgramShaderCacheInserter::Read ( const SHADERUID& key, const u8* value, u32 value_size )
+{
+ const u8 *binary = value+sizeof(GLenum);
+ GLenum *prog_format = (GLenum*)value;
+ GLint binary_size = value_size-sizeof(GLenum);
+
+ PCacheEntry entry;
+ entry.in_cache = 1;
+ entry.shader.glprogid = glCreateProgram();
+ glProgramBinary(entry.shader.glprogid, *prog_format, binary, binary_size);
+
+ GLint success;
+ glGetProgramiv(entry.shader.glprogid, GL_LINK_STATUS, &success);
+
+ if (success)
+ {
+ pshaders[key] = entry;
+ entry.shader.SetProgramVariables();
+ }
+ else
+ glDeleteProgram(entry.shader.glprogid);
+}
+
+
+} // namespace OGL