2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP 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
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// 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 git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2015-10-14 09:18:45 +00:00
# if defined(_WIN32) && defined(SHADERLOG)
2013-07-29 03:43:25 +00:00
# include "Common/CommonWindows.h"
2013-01-22 20:57:47 +00:00
# endif
2012-11-01 15:19:01 +00:00
# include <map>
2015-10-14 09:18:45 +00:00
# include <cstdio>
2012-11-01 15:19:01 +00:00
2017-08-20 11:53:39 +00:00
# include "math/dataconv.h"
2013-08-26 17:00:16 +00:00
# include "base/logging.h"
2016-01-17 19:53:58 +00:00
# include "base/timeutil.h"
2017-03-03 13:15:27 +00:00
# include "gfx/gl_debug_log.h"
2016-05-28 05:00:14 +00:00
# include "i18n/i18n.h"
2013-12-09 13:55:51 +00:00
# include "math/math_util.h"
2012-11-01 15:19:01 +00:00
# include "math/lin/matrix4x4.h"
2015-05-25 22:39:27 +00:00
# include "profiler/profiler.h"
2012-11-01 15:19:01 +00:00
2016-01-17 16:11:51 +00:00
# include "Common/FileUtil.h"
2013-10-01 16:33:32 +00:00
# include "Core/Config.h"
2016-05-28 05:00:14 +00:00
# include "Core/Host.h"
2013-03-24 16:42:49 +00:00
# include "Core/Reporting.h"
2013-12-29 21:32:55 +00:00
# include "GPU/Math3D.h"
2013-06-06 08:05:31 +00:00
# include "GPU/GPUState.h"
# include "GPU/ge_constants.h"
2017-02-04 17:46:12 +00:00
# include "ext/native/gfx/GLStateCache.h"
2017-01-21 21:16:30 +00:00
# include "GPU/GLES/ShaderManagerGLES.h"
2016-04-10 08:27:28 +00:00
# include "GPU/GLES/DrawEngineGLES.h"
2017-01-21 21:16:30 +00:00
# include "FramebufferManagerGLES.h"
2012-11-01 15:19:01 +00:00
2017-11-30 23:34:29 +00:00
Shader : : Shader ( const ShaderID & id , const char * code , uint32_t glShaderType , bool useHWTransform , uint32_t attrMask , uint64_t uniformMask )
2017-11-19 10:23:16 +00:00
: failed_ ( false ) , useHWTransform_ ( useHWTransform ) , attrMask_ ( attrMask ) , uniformMask_ ( uniformMask ) {
2015-05-25 22:39:27 +00:00
PROFILE_THIS_SCOPE ( " shadercomp " ) ;
2015-10-14 15:45:21 +00:00
isFragment_ = glShaderType = = GL_FRAGMENT_SHADER ;
2012-11-18 12:04:49 +00:00
source_ = code ;
2013-01-22 20:57:47 +00:00
# ifdef SHADERLOG
2015-10-14 09:18:45 +00:00
# ifdef _WIN32
2013-08-26 17:00:16 +00:00
OutputDebugStringUTF8 ( code ) ;
2015-10-14 09:18:45 +00:00
# else
printf ( " %s \n " , code ) ;
# endif
2012-11-01 15:19:01 +00:00
# endif
2015-10-14 15:45:21 +00:00
shader = glCreateShader ( glShaderType ) ;
2013-10-08 15:18:59 +00:00
glShaderSource ( shader , 1 , & code , 0 ) ;
2012-11-01 15:19:01 +00:00
glCompileShader ( shader ) ;
2015-09-19 14:19:03 +00:00
GLint success = 0 ;
2012-11-01 15:19:01 +00:00
glGetShaderiv ( shader , GL_COMPILE_STATUS , & success ) ;
if ( ! success ) {
# define MAX_INFO_LOG_SIZE 2048
GLchar infoLog [ MAX_INFO_LOG_SIZE ] ;
GLsizei len ;
glGetShaderInfoLog ( shader , MAX_INFO_LOG_SIZE , & len , infoLog ) ;
infoLog [ len ] = ' \0 ' ;
2016-10-12 09:13:16 +00:00
# ifdef __ANDROID__
2013-10-28 14:22:33 +00:00
ELOG ( " Error in shader compilation! %s \n " , infoLog ) ;
ELOG ( " Shader source: \n %s \n " , ( const char * ) code ) ;
# endif
2017-11-30 23:34:29 +00:00
std : : string desc = GetShaderString ( SHADER_STRING_SHORT_DESC , id ) ;
ERROR_LOG ( G3D , " Error in shader compilation for: %s " , desc . c_str ( ) ) ;
ERROR_LOG ( G3D , " Info log: %s " , infoLog ) ;
2012-11-01 15:19:01 +00:00
ERROR_LOG ( G3D , " Shader source: \n %s \n " , ( const char * ) code ) ;
2017-11-30 23:34:29 +00:00
Reporting : : ReportMessage ( " Error in shader compilation: info: %s \n %s \n %s " , infoLog , desc . c_str ( ) , ( const char * ) code ) ;
2013-05-18 18:44:25 +00:00
# ifdef SHADERLOG
2013-08-26 17:00:16 +00:00
OutputDebugStringUTF8 ( infoLog ) ;
2013-05-18 18:44:25 +00:00
# endif
2013-06-06 08:05:31 +00:00
failed_ = true ;
shader = 0 ;
2012-11-01 15:19:01 +00:00
} else {
2012-12-05 03:45:28 +00:00
DEBUG_LOG ( G3D , " Compiled shader: \n %s \n " , ( const char * ) code ) ;
2012-11-01 15:19:01 +00:00
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2012-11-01 15:19:01 +00:00
}
2013-06-06 08:05:31 +00:00
Shader : : ~ Shader ( ) {
if ( shader )
glDeleteShader ( shader ) ;
}
2017-12-02 17:07:27 +00:00
LinkedShader : : LinkedShader ( VShaderID VSID , Shader * vs , FShaderID FSID , Shader * fs , bool useHWTransform , bool preloading )
2017-12-01 19:17:51 +00:00
: useHWTransform_ ( useHWTransform ) {
2015-05-25 22:39:27 +00:00
PROFILE_THIS_SCOPE ( " shaderlink " ) ;
2012-11-01 15:19:01 +00:00
program = glCreateProgram ( ) ;
2014-03-24 09:55:07 +00:00
vs_ = vs ;
2012-11-01 15:19:01 +00:00
glAttachShader ( program , vs - > shader ) ;
glAttachShader ( program , fs - > shader ) ;
2013-10-08 15:18:59 +00:00
// Bind attribute locations to fixed locations so that they're
2013-10-08 19:50:43 +00:00
// the same in all shaders. We use this later to minimize the calls to
2013-10-08 15:18:59 +00:00
// glEnableVertexAttribArray and glDisableVertexAttribArray.
2013-10-08 15:26:33 +00:00
glBindAttribLocation ( program , ATTR_POSITION , " position " ) ;
glBindAttribLocation ( program , ATTR_TEXCOORD , " texcoord " ) ;
glBindAttribLocation ( program , ATTR_NORMAL , " normal " ) ;
glBindAttribLocation ( program , ATTR_W1 , " w1 " ) ;
glBindAttribLocation ( program , ATTR_W2 , " w2 " ) ;
glBindAttribLocation ( program , ATTR_COLOR0 , " color0 " ) ;
glBindAttribLocation ( program , ATTR_COLOR1 , " color1 " ) ;
2013-10-08 15:18:59 +00:00
2015-11-28 00:01:25 +00:00
# if !defined(USING_GLES2)
2015-09-05 17:58:47 +00:00
if ( gstate_c . featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND ) {
2013-12-15 11:49:13 +00:00
// Dual source alpha
glBindFragDataLocationIndexed ( program , 0 , 0 , " fragColor0 " ) ;
glBindFragDataLocationIndexed ( program , 0 , 1 , " fragColor1 " ) ;
2013-12-18 11:00:23 +00:00
} else if ( gl_extensions . VersionGEThan ( 3 , 3 , 0 ) ) {
2013-12-16 22:18:13 +00:00
glBindFragDataLocation ( program , 0 , " fragColor0 " ) ;
2013-12-15 11:49:13 +00:00
}
2015-11-28 00:01:25 +00:00
# elif !defined(IOS)
if ( gl_extensions . GLES3 ) {
if ( gstate_c . featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND ) {
glBindFragDataLocationIndexedEXT ( program , 0 , 0 , " fragColor0 " ) ;
glBindFragDataLocationIndexedEXT ( program , 0 , 1 , " fragColor1 " ) ;
}
}
2013-12-15 11:49:13 +00:00
# endif
2012-11-01 15:19:01 +00:00
glLinkProgram ( program ) ;
2013-09-17 15:39:04 +00:00
GLint linkStatus = GL_FALSE ;
2012-11-01 15:19:01 +00:00
glGetProgramiv ( program , GL_LINK_STATUS , & linkStatus ) ;
if ( linkStatus ! = GL_TRUE ) {
GLint bufLength = 0 ;
glGetProgramiv ( program , GL_INFO_LOG_LENGTH , & bufLength ) ;
if ( bufLength ) {
char * buf = new char [ bufLength ] ;
glGetProgramInfoLog ( program , bufLength , NULL , buf ) ;
2016-10-12 09:13:16 +00:00
# ifdef __ANDROID__
2013-10-28 14:22:33 +00:00
ELOG ( " Could not link program: \n %s " , buf ) ;
# endif
2012-11-01 15:19:01 +00:00
ERROR_LOG ( G3D , " Could not link program: \n %s " , buf ) ;
2017-11-30 23:34:29 +00:00
std : : string vs_desc = vs - > GetShaderString ( SHADER_STRING_SHORT_DESC , VSID ) ;
std : : string fs_desc = fs - > GetShaderString ( SHADER_STRING_SHORT_DESC , FSID ) ;
2016-01-17 19:29:34 +00:00
std : : string vs_source = vs - > GetShaderString ( SHADER_STRING_SOURCE_CODE , VSID ) ;
std : : string fs_source = fs - > GetShaderString ( SHADER_STRING_SOURCE_CODE , FSID ) ;
2017-11-30 23:34:29 +00:00
ERROR_LOG ( G3D , " VS desc: \n %s " , vs_desc . c_str ( ) ) ;
ERROR_LOG ( G3D , " FS desc: \n %s " , fs_desc . c_str ( ) ) ;
2015-10-14 15:45:21 +00:00
ERROR_LOG ( G3D , " VS: \n %s \n " , vs_source . c_str ( ) ) ;
ERROR_LOG ( G3D , " FS: \n %s \n " , fs_source . c_str ( ) ) ;
2017-12-01 19:17:51 +00:00
if ( preloading ) {
Reporting : : ReportMessage ( " Error in shader program link during preload: info: %s \n fs: %s \n %s \n vs: %s \n %s " , buf , fs_desc . c_str ( ) , fs_source . c_str ( ) , vs_desc . c_str ( ) , vs_source . c_str ( ) ) ;
} else {
Reporting : : ReportMessage ( " Error in shader program link: info: %s \n fs: %s \n %s \n vs: %s \n %s " , buf , fs_desc . c_str ( ) , fs_source . c_str ( ) , vs_desc . c_str ( ) , vs_source . c_str ( ) ) ;
}
2013-04-17 19:41:47 +00:00
# ifdef SHADERLOG
2013-08-26 17:00:16 +00:00
OutputDebugStringUTF8 ( buf ) ;
2015-10-14 15:45:21 +00:00
OutputDebugStringUTF8 ( vs_source . c_str ( ) ) ;
OutputDebugStringUTF8 ( fs_source . c_str ( ) ) ;
2013-04-17 19:41:47 +00:00
# endif
2012-11-01 15:19:01 +00:00
delete [ ] buf ; // we're dead!
}
2013-09-17 15:39:04 +00:00
// Prevent a buffer overflow.
numBones = 0 ;
2016-03-05 21:12:24 +00:00
// Avoid weird attribute enables.
attrMask = 0 ;
availableUniforms = 0 ;
2012-11-01 15:19:01 +00:00
return ;
}
2012-12-05 03:45:28 +00:00
INFO_LOG ( G3D , " Linked shader: vs %i fs %i " , ( int ) vs - > shader , ( int ) fs - > shader ) ;
2012-11-01 15:19:01 +00:00
2012-12-05 03:45:28 +00:00
u_tex = glGetUniformLocation ( program , " tex " ) ;
u_proj = glGetUniformLocation ( program , " u_proj " ) ;
2012-11-28 12:45:22 +00:00
u_proj_through = glGetUniformLocation ( program , " u_proj_through " ) ;
2012-11-01 15:19:01 +00:00
u_texenv = glGetUniformLocation ( program , " u_texenv " ) ;
u_fogcolor = glGetUniformLocation ( program , " u_fogcolor " ) ;
2012-11-28 12:45:22 +00:00
u_fogcoef = glGetUniformLocation ( program , " u_fogcoef " ) ;
2012-12-19 17:35:37 +00:00
u_alphacolorref = glGetUniformLocation ( program , " u_alphacolorref " ) ;
2014-06-16 07:33:48 +00:00
u_alphacolormask = glGetUniformLocation ( program , " u_alphacolormask " ) ;
2013-12-02 23:09:48 +00:00
u_stencilReplaceValue = glGetUniformLocation ( program , " u_stencilReplaceValue " ) ;
2014-08-24 22:26:38 +00:00
u_testtex = glGetUniformLocation ( program , " testtex " ) ;
2012-11-01 15:19:01 +00:00
2014-05-11 16:51:35 +00:00
u_fbotex = glGetUniformLocation ( program , " fbotex " ) ;
u_blendFixA = glGetUniformLocation ( program , " u_blendFixA " ) ;
u_blendFixB = glGetUniformLocation ( program , " u_blendFixB " ) ;
2014-06-14 05:49:28 +00:00
u_fbotexSize = glGetUniformLocation ( program , " u_fbotexSize " ) ;
2014-05-11 16:51:35 +00:00
2012-12-20 13:10:42 +00:00
// Transform
u_view = glGetUniformLocation ( program , " u_view " ) ;
u_world = glGetUniformLocation ( program , " u_world " ) ;
u_texmtx = glGetUniformLocation ( program , " u_texmtx " ) ;
2016-01-17 19:29:34 +00:00
if ( VSID . Bit ( VS_BIT_ENABLE_BONES ) )
numBones = TranslateNumBones ( VSID . Bits ( VS_BIT_BONES , 3 ) + 1 ) ;
2013-07-27 15:32:24 +00:00
else
numBones = 0 ;
2015-08-26 11:26:08 +00:00
u_depthRange = glGetUniformLocation ( program , " u_depthRange " ) ;
2013-07-27 15:32:24 +00:00
2013-05-26 12:03:02 +00:00
# ifdef USE_BONE_ARRAY
u_bone = glGetUniformLocation ( program , " u_bone " ) ;
# else
2013-07-27 18:09:22 +00:00
for ( int i = 0 ; i < 8 ; i + + ) {
2013-05-26 12:03:02 +00:00
char name [ 10 ] ;
sprintf ( name , " u_bone%i " , i ) ;
u_bone [ i ] = glGetUniformLocation ( program , name ) ;
}
# endif
2012-12-20 13:10:42 +00:00
// Lighting, texturing
2012-12-20 17:31:21 +00:00
u_ambient = glGetUniformLocation ( program , " u_ambient " ) ;
2012-12-20 13:10:42 +00:00
u_matambientalpha = glGetUniformLocation ( program , " u_matambientalpha " ) ;
2012-12-20 17:31:21 +00:00
u_matdiffuse = glGetUniformLocation ( program , " u_matdiffuse " ) ;
u_matspecular = glGetUniformLocation ( program , " u_matspecular " ) ;
u_matemissive = glGetUniformLocation ( program , " u_matemissive " ) ;
2012-12-20 13:10:42 +00:00
u_uvscaleoffset = glGetUniformLocation ( program , " u_uvscaleoffset " ) ;
2014-06-07 19:21:52 +00:00
u_texclamp = glGetUniformLocation ( program , " u_texclamp " ) ;
2014-06-08 20:52:05 +00:00
u_texclampoff = glGetUniformLocation ( program , " u_texclampoff " ) ;
2012-12-20 13:10:42 +00:00
2012-12-20 17:31:21 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
char temp [ 64 ] ;
sprintf ( temp , " u_lightpos%i " , i ) ;
u_lightpos [ i ] = glGetUniformLocation ( program , temp ) ;
sprintf ( temp , " u_lightdir%i " , i ) ;
u_lightdir [ i ] = glGetUniformLocation ( program , temp ) ;
sprintf ( temp , " u_lightatt%i " , i ) ;
u_lightatt [ i ] = glGetUniformLocation ( program , temp ) ;
2013-04-09 16:25:22 +00:00
sprintf ( temp , " u_lightangle%i " , i ) ;
u_lightangle [ i ] = glGetUniformLocation ( program , temp ) ;
sprintf ( temp , " u_lightspotCoef%i " , i ) ;
u_lightspotCoef [ i ] = glGetUniformLocation ( program , temp ) ;
2012-12-20 17:31:21 +00:00
sprintf ( temp , " u_lightambient%i " , i ) ;
u_lightambient [ i ] = glGetUniformLocation ( program , temp ) ;
sprintf ( temp , " u_lightdiffuse%i " , i ) ;
u_lightdiffuse [ i ] = glGetUniformLocation ( program , temp ) ;
sprintf ( temp , " u_lightspecular%i " , i ) ;
u_lightspecular [ i ] = glGetUniformLocation ( program , temp ) ;
}
2017-01-28 21:33:31 +00:00
// We need to fetch these unconditionally, gstate_c.spline or bezier will not be set if we
// create this shader at load time from the shader cache.
u_tess_pos_tex = glGetUniformLocation ( program , " u_tess_pos_tex " ) ;
u_tess_tex_tex = glGetUniformLocation ( program , " u_tess_tex_tex " ) ;
u_tess_col_tex = glGetUniformLocation ( program , " u_tess_col_tex " ) ;
u_spline_count_u = glGetUniformLocation ( program , " u_spline_count_u " ) ;
u_spline_count_v = glGetUniformLocation ( program , " u_spline_count_v " ) ;
u_spline_type_u = glGetUniformLocation ( program , " u_spline_type_u " ) ;
u_spline_type_v = glGetUniformLocation ( program , " u_spline_type_v " ) ;
2012-12-20 17:31:21 +00:00
2017-11-19 10:08:34 +00:00
attrMask = vs - > GetAttrMask ( ) ;
2017-11-19 10:23:16 +00:00
availableUniforms = vs - > GetUniformMask ( ) | fs - > GetUniformMask ( ) ;
2013-11-12 15:39:35 +00:00
2012-11-01 15:19:01 +00:00
glUseProgram ( program ) ;
2013-01-06 22:50:05 +00:00
2012-11-01 15:19:01 +00:00
// Default uniform values
glUniform1i ( u_tex , 0 ) ;
2014-05-11 16:51:35 +00:00
glUniform1i ( u_fbotex , 1 ) ;
2014-08-24 22:26:38 +00:00
glUniform1i ( u_testtex , 2 ) ;
2017-01-28 21:33:31 +00:00
if ( u_tess_pos_tex ! = - 1 )
2017-02-25 08:22:54 +00:00
glUniform1i ( u_tess_pos_tex , 4 ) ; // Texture unit 4
2017-01-28 21:33:31 +00:00
if ( u_tess_tex_tex ! = - 1 )
2017-02-25 08:22:54 +00:00
glUniform1i ( u_tess_tex_tex , 5 ) ; // Texture unit 5
2017-01-28 21:33:31 +00:00
if ( u_tess_col_tex ! = - 1 )
2017-02-25 08:22:54 +00:00
glUniform1i ( u_tess_col_tex , 6 ) ; // Texture unit 6
2017-01-28 21:33:31 +00:00
2012-11-01 15:19:01 +00:00
// The rest, use the "dirty" mechanism.
2017-01-23 22:15:54 +00:00
dirtyUniforms = DIRTY_ALL_UNIFORMS ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2012-11-01 15:19:01 +00:00
}
LinkedShader : : ~ LinkedShader ( ) {
2013-10-08 20:59:40 +00:00
// Shaders are automatically detached by glDeleteProgram.
2012-11-01 15:19:01 +00:00
glDeleteProgram ( program ) ;
}
2012-12-19 14:14:41 +00:00
// Utility
2013-06-06 08:05:31 +00:00
static void SetColorUniform3 ( int uniform , u32 color ) {
2017-08-20 11:53:39 +00:00
float f [ 4 ] ;
Uint8x4ToFloat4 ( f , color ) ;
glUniform3fv ( uniform , 1 , f ) ;
2012-12-19 14:14:41 +00:00
}
2013-06-06 08:05:31 +00:00
static void SetColorUniform3Alpha ( int uniform , u32 color , u8 alpha ) {
2017-08-20 11:53:39 +00:00
float f [ 4 ] ;
Uint8x3ToFloat4_AlphaUint8 ( f , color , alpha ) ;
glUniform4fv ( uniform , 1 , f ) ;
2012-12-20 13:10:42 +00:00
}
2013-05-12 20:09:02 +00:00
// This passes colors unscaled (e.g. 0 - 255 not 0 - 1.)
2013-06-06 08:05:31 +00:00
static void SetColorUniform3Alpha255 ( int uniform , u32 color , u8 alpha ) {
2017-11-21 14:42:01 +00:00
if ( gl_extensions . gpuVendor = = GPU_VENDOR_IMGTEC ) {
2013-07-20 17:14:36 +00:00
const float col [ 4 ] = {
2014-06-16 07:33:48 +00:00
( float ) ( ( color & 0xFF ) > > 0 ) * ( 1.0f / 255.0f ) ,
2013-07-20 17:14:36 +00:00
( float ) ( ( color & 0xFF00 ) > > 8 ) * ( 1.0f / 255.0f ) ,
( float ) ( ( color & 0xFF0000 ) > > 16 ) * ( 1.0f / 255.0f ) ,
( float ) alpha * ( 1.0f / 255.0f )
} ;
glUniform4fv ( uniform , 1 , col ) ;
} else {
const float col [ 4 ] = {
2014-06-16 07:33:48 +00:00
( float ) ( ( color & 0xFF ) > > 0 ) ,
( float ) ( ( color & 0xFF00 ) > > 8 ) ,
( float ) ( ( color & 0xFF0000 ) > > 16 ) ,
2014-11-09 12:34:23 +00:00
( float ) alpha
2013-07-20 17:14:36 +00:00
} ;
glUniform4fv ( uniform , 1 , col ) ;
}
2013-05-07 14:52:49 +00:00
}
2014-06-16 07:33:48 +00:00
static void SetColorUniform3iAlpha ( int uniform , u32 color , u8 alpha ) {
const int col [ 4 ] = {
2014-06-16 08:02:49 +00:00
( int ) ( ( color & 0xFF ) > > 0 ) ,
( int ) ( ( color & 0xFF00 ) > > 8 ) ,
( int ) ( ( color & 0xFF0000 ) > > 16 ) ,
( int ) alpha ,
2014-06-16 07:33:48 +00:00
} ;
glUniform4iv ( uniform , 1 , col ) ;
}
2013-06-06 08:05:31 +00:00
static void SetColorUniform3ExtraFloat ( int uniform , u32 color , float extra ) {
2013-01-14 19:43:18 +00:00
const float col [ 4 ] = {
( ( color & 0xFF ) ) / 255.0f ,
( ( color & 0xFF00 ) > > 8 ) / 255.0f ,
( ( color & 0xFF0000 ) > > 16 ) / 255.0f ,
extra
} ;
2012-12-20 17:31:21 +00:00
glUniform4fv ( uniform , 1 , col ) ;
}
2017-08-20 11:53:39 +00:00
static void SetFloat24Uniform3 ( int uniform , const uint32_t data [ 3 ] ) {
float f [ 4 ] ;
ExpandFloat24x3ToFloat4 ( f , data ) ;
glUniform3fv ( uniform , 1 , f ) ;
2014-04-18 12:30:18 +00:00
}
2015-08-26 11:26:08 +00:00
static void SetFloatUniform4 ( int uniform , float data [ 4 ] ) {
glUniform4fv ( uniform , 1 , data ) ;
}
2013-05-18 19:18:04 +00:00
static void SetMatrix4x3 ( int uniform , const float * m4x3 ) {
float m4x4 [ 16 ] ;
2013-12-29 21:32:55 +00:00
ConvertMatrix4x3To4x4 ( m4x4 , m4x3 ) ;
2012-12-21 10:08:54 +00:00
glUniformMatrix4fv ( uniform , 1 , GL_FALSE , m4x4 ) ;
}
2015-03-23 06:35:24 +00:00
static inline void ScaleProjMatrix ( Matrix4x4 & in ) {
2015-11-12 14:22:20 +00:00
float yOffset = gstate_c . vpYOffset ;
if ( g_Config . iRenderingMode = = FB_NON_BUFFERED_MODE ) {
// GL upside down is a pain as usual.
yOffset = - yOffset ;
}
2016-01-16 08:17:27 +00:00
const Vec3 trans ( gstate_c . vpXOffset , yOffset , gstate_c . vpZOffset ) ;
2016-01-03 20:11:05 +00:00
const Vec3 scale ( gstate_c . vpWidthScale , gstate_c . vpHeightScale , gstate_c . vpDepthScale ) ;
2015-03-23 06:35:24 +00:00
in . translateAndScale ( trans , scale ) ;
}
2017-12-02 17:07:27 +00:00
void LinkedShader : : use ( const VShaderID & VSID , LinkedShader * previous ) {
2012-12-05 03:45:28 +00:00
glUseProgram ( program ) ;
2013-10-08 15:47:05 +00:00
int enable , disable ;
if ( previous ) {
enable = attrMask & ~ previous - > attrMask ;
disable = ( ~ attrMask ) & previous - > attrMask ;
} else {
enable = attrMask ;
disable = ~ attrMask ;
}
2013-10-08 19:50:43 +00:00
for ( int i = 0 ; i < ATTR_COUNT ; i + + ) {
2013-10-08 15:47:05 +00:00
if ( enable & ( 1 < < i ) )
2013-10-08 15:18:59 +00:00
glEnableVertexAttribArray ( i ) ;
2013-10-08 15:47:05 +00:00
else if ( disable & ( 1 < < i ) )
glDisableVertexAttribArray ( i ) ;
2013-10-08 15:18:59 +00:00
}
2013-01-06 22:50:05 +00:00
}
void LinkedShader : : stop ( ) {
2013-10-08 19:50:43 +00:00
for ( int i = 0 ; i < ATTR_COUNT ; i + + ) {
2013-10-08 15:18:59 +00:00
if ( attrMask & ( 1 < < i ) )
glDisableVertexAttribArray ( i ) ;
}
2012-12-21 20:49:09 +00:00
}
2012-12-19 14:14:41 +00:00
2017-12-02 17:07:27 +00:00
void LinkedShader : : UpdateUniforms ( u32 vertType , const VShaderID & vsid ) {
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-01-08 14:01:01 +00:00
u64 dirty = dirtyUniforms & availableUniforms ;
2013-11-13 09:02:58 +00:00
dirtyUniforms = 0 ;
2013-11-12 15:39:35 +00:00
if ( ! dirty )
2012-12-19 14:14:41 +00:00
return ;
2012-11-01 15:19:01 +00:00
// Update any dirty uniforms before we draw
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_PROJMATRIX ) {
2015-03-23 06:35:24 +00:00
Matrix4x4 flippedMatrix ;
memcpy ( & flippedMatrix , gstate . projMatrix , 16 * sizeof ( float ) ) ;
2015-10-31 23:23:24 +00:00
bool useBufferedRendering = g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ;
2015-11-04 23:08:48 +00:00
const bool invertedY = useBufferedRendering ? ( gstate_c . vpHeight < 0 ) : ( gstate_c . vpHeight > 0 ) ;
2015-03-23 06:35:24 +00:00
if ( invertedY ) {
2015-11-01 12:08:52 +00:00
flippedMatrix [ 1 ] = - flippedMatrix [ 1 ] ;
2012-11-28 12:45:22 +00:00
flippedMatrix [ 5 ] = - flippedMatrix [ 5 ] ;
2015-11-01 12:08:52 +00:00
flippedMatrix [ 9 ] = - flippedMatrix [ 9 ] ;
2012-11-28 12:45:22 +00:00
flippedMatrix [ 13 ] = - flippedMatrix [ 13 ] ;
2012-11-01 15:19:01 +00:00
}
2015-03-23 06:35:24 +00:00
const bool invertedX = gstate_c . vpWidth < 0 ;
if ( invertedX ) {
2012-11-28 12:45:22 +00:00
flippedMatrix [ 0 ] = - flippedMatrix [ 0 ] ;
2015-11-01 12:08:52 +00:00
flippedMatrix [ 4 ] = - flippedMatrix [ 4 ] ;
flippedMatrix [ 8 ] = - flippedMatrix [ 8 ] ;
2012-11-28 12:45:22 +00:00
flippedMatrix [ 12 ] = - flippedMatrix [ 12 ] ;
2015-11-01 12:08:52 +00:00
}
2014-11-08 17:26:24 +00:00
2016-02-07 04:28:45 +00:00
// In Phantasy Star Portable 2, depth range sometimes goes negative and is clamped by glDepthRange to 0,
// causing graphics clipping glitch (issue #1788). This hack modifies the projection matrix to work around it.
if ( gstate_c . Supports ( GPU_USE_DEPTH_RANGE_HACK ) ) {
float zScale = gstate . getViewportZScale ( ) / 65535.0f ;
float zCenter = gstate . getViewportZCenter ( ) / 65535.0f ;
// if far depth range < 0
if ( zCenter + zScale < 0.0f ) {
// if perspective projection
if ( flippedMatrix [ 11 ] < 0.0f ) {
float depthMax = gstate . getDepthRangeMax ( ) / 65535.0f ;
float depthMin = gstate . getDepthRangeMin ( ) / 65535.0f ;
float a = flippedMatrix [ 10 ] ;
float b = flippedMatrix [ 14 ] ;
float n = b / ( a - 1.0f ) ;
float f = b / ( a + 1.0f ) ;
f = ( n * f ) / ( n + ( ( zCenter + zScale ) * ( n - f ) / ( depthMax - depthMin ) ) ) ;
a = ( n + f ) / ( n - f ) ;
b = ( 2.0f * n * f ) / ( n - f ) ;
if ( ! my_isnan ( a ) & & ! my_isnan ( b ) ) {
flippedMatrix [ 10 ] = a ;
flippedMatrix [ 14 ] = b ;
}
}
}
}
2015-03-23 06:35:24 +00:00
ScaleProjMatrix ( flippedMatrix ) ;
glUniformMatrix4fv ( u_proj , 1 , GL_FALSE , flippedMatrix . m ) ;
2012-11-01 15:19:01 +00:00
}
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_PROJTHROUGHMATRIX )
2012-11-28 12:45:22 +00:00
{
Matrix4x4 proj_through ;
2015-10-31 23:23:24 +00:00
bool useBufferedRendering = g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ;
if ( useBufferedRendering ) {
proj_through . setOrtho ( 0.0f , gstate_c . curRTWidth , 0.0f , gstate_c . curRTHeight , 0.0f , 1.0f ) ;
} else {
proj_through . setOrtho ( 0.0f , gstate_c . curRTWidth , gstate_c . curRTHeight , 0.0f , 0.0f , 1.0f ) ;
}
2012-11-28 12:45:22 +00:00
glUniformMatrix4fv ( u_proj_through , 1 , GL_FALSE , proj_through . getReadPtr ( ) ) ;
}
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_TEXENV ) {
2012-12-19 14:14:41 +00:00
SetColorUniform3 ( u_texenv , gstate . texenvcolor ) ;
2012-11-01 15:19:01 +00:00
}
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_ALPHACOLORREF ) {
2014-06-16 07:35:30 +00:00
SetColorUniform3Alpha255 ( u_alphacolorref , gstate . getColorTestRef ( ) , gstate . getAlphaTestRef ( ) & gstate . getAlphaTestMask ( ) ) ;
2012-11-01 15:19:01 +00:00
}
2014-06-16 07:33:48 +00:00
if ( dirty & DIRTY_ALPHACOLORMASK ) {
SetColorUniform3iAlpha ( u_alphacolormask , gstate . colortestmask , gstate . getAlphaTestMask ( ) ) ;
2013-02-24 21:42:23 +00:00
}
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_FOGCOLOR ) {
2012-12-19 14:14:41 +00:00
SetColorUniform3 ( u_fogcolor , gstate . fogcolor ) ;
2012-11-28 12:45:22 +00:00
}
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_FOGCOEF ) {
2013-12-09 13:55:51 +00:00
float fogcoef [ 2 ] = {
2013-01-14 19:43:18 +00:00
getFloat24 ( gstate . fog1 ) ,
getFloat24 ( gstate . fog2 ) ,
} ;
2013-12-09 13:55:51 +00:00
if ( my_isinf ( fogcoef [ 1 ] ) ) {
// not really sure what a sensible value might be.
2013-12-16 14:45:21 +00:00
fogcoef [ 1 ] = fogcoef [ 1 ] < 0.0f ? - 10000.0f : 10000.0f ;
2014-09-21 17:41:44 +00:00
} else if ( my_isnan ( fogcoef [ 1 ] ) ) {
2014-03-22 22:49:14 +00:00
// Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988
// Just put the fog far away at a large finite distance.
// Infinities and NaNs are rather unpredictable in shaders on many GPUs
// so it's best to just make it a sane calculation.
fogcoef [ 0 ] = 100000.0f ;
fogcoef [ 1 ] = 1.0f ;
}
2014-06-07 22:22:46 +00:00
# ifndef MOBILE_DEVICE
else if ( my_isnanorinf ( fogcoef [ 1 ] ) | | my_isnanorinf ( fogcoef [ 0 ] ) ) {
ERROR_LOG_REPORT_ONCE ( fognan , G3D , " Unhandled fog NaN/INF combo: %f %f " , fogcoef [ 0 ] , fogcoef [ 1 ] ) ;
}
# endif
2012-11-28 12:45:22 +00:00
glUniform2fv ( u_fogcoef , 1 , fogcoef ) ;
}
2012-11-01 15:19:01 +00:00
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_UVSCALEOFFSET ) {
2013-11-30 14:54:20 +00:00
const float invW = 1.0f / ( float ) gstate_c . curTextureWidth ;
const float invH = 1.0f / ( float ) gstate_c . curTextureHeight ;
const int w = gstate . getTextureWidth ( 0 ) ;
const int h = gstate . getTextureHeight ( 0 ) ;
const float widthFactor = ( float ) w * invW ;
const float heightFactor = ( float ) h * invH ;
2013-07-09 23:22:15 +00:00
float uvscaleoff [ 4 ] ;
2017-01-08 18:37:21 +00:00
if ( gstate_c . bezier | | gstate_c . spline ) {
2017-01-23 16:44:13 +00:00
// When we are generating UV coordinates through the bezier/spline, we need to apply the scaling.
// However, this is missing a check that we're not getting our UV:s supplied for us in the vertices.
uvscaleoff [ 0 ] = gstate_c . uv . uScale * widthFactor ;
uvscaleoff [ 1 ] = gstate_c . uv . vScale * heightFactor ;
2017-01-08 18:37:21 +00:00
uvscaleoff [ 2 ] = gstate_c . uv . uOff * widthFactor ;
uvscaleoff [ 3 ] = gstate_c . uv . vOff * heightFactor ;
} else {
uvscaleoff [ 0 ] = widthFactor ;
uvscaleoff [ 1 ] = heightFactor ;
uvscaleoff [ 2 ] = 0.0f ;
uvscaleoff [ 3 ] = 0.0f ;
}
2013-11-30 14:54:20 +00:00
glUniform4fv ( u_uvscaleoffset , 1 , uvscaleoff ) ;
2012-12-20 13:10:42 +00:00
}
2015-08-26 11:26:08 +00:00
if ( ( dirty & DIRTY_TEXCLAMP ) & & u_texclamp ! = - 1 ) {
2014-06-07 19:21:52 +00:00
const float invW = 1.0f / ( float ) gstate_c . curTextureWidth ;
const float invH = 1.0f / ( float ) gstate_c . curTextureHeight ;
const int w = gstate . getTextureWidth ( 0 ) ;
const int h = gstate . getTextureHeight ( 0 ) ;
const float widthFactor = ( float ) w * invW ;
const float heightFactor = ( float ) h * invH ;
2014-06-08 02:30:46 +00:00
// First wrap xy, then half texel xy (for clamp.)
const float texclamp [ 4 ] = {
2014-06-07 19:21:52 +00:00
widthFactor ,
heightFactor ,
2014-06-08 02:30:46 +00:00
invW * 0.5f ,
invH * 0.5f ,
2014-06-07 19:21:52 +00:00
} ;
2014-06-08 20:52:05 +00:00
const float texclampoff [ 2 ] = {
gstate_c . curTextureXOffset * invW ,
gstate_c . curTextureYOffset * invH ,
} ;
2014-06-08 02:30:46 +00:00
glUniform4fv ( u_texclamp , 1 , texclamp ) ;
2014-06-08 21:24:38 +00:00
if ( u_texclampoff ! = - 1 ) {
glUniform2fv ( u_texclampoff , 1 , texclampoff ) ;
}
2014-06-07 19:21:52 +00:00
}
2012-12-20 13:10:42 +00:00
// Transform
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_WORLDMATRIX ) {
2012-12-21 10:08:54 +00:00
SetMatrix4x3 ( u_world , gstate . worldMatrix ) ;
2012-12-20 13:10:42 +00:00
}
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_VIEWMATRIX ) {
2012-12-21 10:08:54 +00:00
SetMatrix4x3 ( u_view , gstate . viewMatrix ) ;
2012-12-20 13:10:42 +00:00
}
2013-11-12 15:39:35 +00:00
if ( dirty & DIRTY_TEXMATRIX ) {
2012-12-21 10:08:54 +00:00
SetMatrix4x3 ( u_texmtx , gstate . tgenMatrix ) ;
2012-12-20 13:10:42 +00:00
}
2015-08-26 11:26:08 +00:00
if ( ( dirty & DIRTY_DEPTHRANGE ) & & u_depthRange ! = - 1 ) {
2016-01-19 04:53:38 +00:00
// Since depth is [-1, 1] mapping to [minz, maxz], this is easyish.
float vpZScale = gstate . getViewportZScale ( ) ;
float vpZCenter = gstate . getViewportZCenter ( ) ;
2016-01-03 20:11:05 +00:00
2016-01-19 04:53:38 +00:00
// These are just the reverse of the formulas in GPUStateUtils.
float halfActualZRange = vpZScale / gstate_c . vpDepthScale ;
float minz = - ( ( gstate_c . vpZOffset * halfActualZRange ) - vpZCenter ) - halfActualZRange ;
2016-01-19 04:26:31 +00:00
float viewZScale = halfActualZRange ;
float viewZCenter = minz + halfActualZRange ;
2016-01-03 20:11:05 +00:00
2016-02-07 04:18:50 +00:00
if ( ! gstate_c . Supports ( GPU_SUPPORTS_ACCURATE_DEPTH ) ) {
viewZScale = vpZScale ;
viewZCenter = vpZCenter ;
}
float viewZInvScale ;
2015-08-27 15:58:41 +00:00
if ( viewZScale ! = 0.0 ) {
2015-08-29 15:43:09 +00:00
viewZInvScale = 1.0f / viewZScale ;
2015-08-27 15:58:41 +00:00
} else {
viewZInvScale = 0.0 ;
}
2016-01-03 20:11:05 +00:00
2015-08-29 15:43:09 +00:00
float data [ 4 ] = { viewZScale , viewZCenter , viewZCenter , viewZInvScale } ;
2015-08-26 11:26:08 +00:00
SetFloatUniform4 ( u_depthRange , data ) ;
}
2013-05-18 18:44:25 +00:00
2013-12-02 23:09:48 +00:00
if ( dirty & DIRTY_STENCILREPLACEVALUE ) {
2013-12-15 11:49:13 +00:00
glUniform1f ( u_stencilReplaceValue , ( float ) gstate . getStencilTestRef ( ) * ( 1.0f / 255.0f ) ) ;
2013-12-02 23:09:48 +00:00
}
2013-05-18 18:44:25 +00:00
// TODO: Could even set all bones in one go if they're all dirty.
2013-05-26 12:03:02 +00:00
# ifdef USE_BONE_ARRAY
2013-05-18 18:44:25 +00:00
if ( u_bone ! = - 1 ) {
2013-05-18 19:18:04 +00:00
float allBones [ 8 * 16 ] ;
bool allDirty = true ;
2013-05-18 18:44:25 +00:00
for ( int i = 0 ; i < numBones ; i + + ) {
2013-11-13 09:02:58 +00:00
if ( dirty & ( DIRTY_BONEMATRIX0 < < i ) ) {
2013-12-29 21:32:55 +00:00
ConvertMatrix4x3To4x4 ( allBones + 16 * i , gstate . boneMatrix + 12 * i ) ;
2013-05-18 19:18:04 +00:00
} else {
allDirty = false ;
}
}
if ( allDirty ) {
// Set them all with one call
glUniformMatrix4fv ( u_bone , numBones , GL_FALSE , allBones ) ;
} else {
// Set them one by one. Could try to coalesce two in a row etc but too lazy.
for ( int i = 0 ; i < numBones ; i + + ) {
2013-11-13 09:02:58 +00:00
if ( dirty & ( DIRTY_BONEMATRIX0 < < i ) ) {
2013-05-18 19:18:04 +00:00
glUniformMatrix4fv ( u_bone + i , 1 , GL_FALSE , allBones + 16 * i ) ;
}
2013-05-18 18:44:25 +00:00
}
2012-12-20 13:10:42 +00:00
}
}
2013-05-26 12:03:02 +00:00
# else
float bonetemp [ 16 ] ;
for ( int i = 0 ; i < numBones ; i + + ) {
2013-11-12 15:39:35 +00:00
if ( dirty & ( DIRTY_BONEMATRIX0 < < i ) ) {
2013-12-29 21:32:55 +00:00
ConvertMatrix4x3To4x4 ( bonetemp , gstate . boneMatrix + 12 * i ) ;
2013-05-26 12:03:02 +00:00
glUniformMatrix4fv ( u_bone [ i ] , 1 , GL_FALSE , bonetemp ) ;
}
}
# endif
2012-12-20 13:10:42 +00:00
2014-06-14 05:49:28 +00:00
if ( dirty & DIRTY_SHADERBLEND ) {
2014-06-17 06:25:56 +00:00
if ( u_blendFixA ! = - 1 ) {
SetColorUniform3 ( u_blendFixA , gstate . getFixA ( ) ) ;
}
if ( u_blendFixB ! = - 1 ) {
SetColorUniform3 ( u_blendFixB , gstate . getFixB ( ) ) ;
}
2014-06-14 05:49:28 +00:00
const float fbotexSize [ 2 ] = {
1.0f / ( float ) gstate_c . curRTRenderWidth ,
1.0f / ( float ) gstate_c . curRTRenderHeight ,
} ;
if ( u_fbotexSize ! = - 1 ) {
glUniform2fv ( u_fbotexSize , 1 , fbotexSize ) ;
}
2014-05-11 16:51:35 +00:00
}
2012-12-20 13:10:42 +00:00
// Lighting
2013-11-13 09:02:58 +00:00
if ( dirty & DIRTY_AMBIENT ) {
2013-07-22 00:55:26 +00:00
SetColorUniform3Alpha ( u_ambient , gstate . ambientcolor , gstate . getAmbientA ( ) ) ;
2012-12-20 17:31:21 +00:00
}
2013-11-13 09:02:58 +00:00
if ( dirty & DIRTY_MATAMBIENTALPHA ) {
2013-07-22 00:55:26 +00:00
SetColorUniform3Alpha ( u_matambientalpha , gstate . materialambient , gstate . getMaterialAmbientA ( ) ) ;
2012-12-20 13:10:42 +00:00
}
2013-11-13 09:02:58 +00:00
if ( dirty & DIRTY_MATDIFFUSE ) {
2012-12-20 17:31:21 +00:00
SetColorUniform3 ( u_matdiffuse , gstate . materialdiffuse ) ;
}
2013-11-13 09:02:58 +00:00
if ( dirty & DIRTY_MATEMISSIVE ) {
2012-12-20 17:31:21 +00:00
SetColorUniform3 ( u_matemissive , gstate . materialemissive ) ;
}
2013-11-13 09:02:58 +00:00
if ( dirty & DIRTY_MATSPECULAR ) {
2012-12-20 22:28:58 +00:00
SetColorUniform3ExtraFloat ( u_matspecular , gstate . materialspecular , getFloat24 ( gstate . materialspecularcoef ) ) ;
2012-12-20 17:31:21 +00:00
}
2012-12-20 13:10:42 +00:00
2012-12-20 17:31:21 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2013-11-12 15:39:35 +00:00
if ( dirty & ( DIRTY_LIGHT0 < < i ) ) {
2014-11-05 08:57:22 +00:00
if ( gstate . isDirectionalLight ( i ) ) {
// Prenormalize
float x = getFloat24 ( gstate . lpos [ i * 3 + 0 ] ) ;
float y = getFloat24 ( gstate . lpos [ i * 3 + 1 ] ) ;
float z = getFloat24 ( gstate . lpos [ i * 3 + 2 ] ) ;
float len = sqrtf ( x * x + y * y + z * z ) ;
if ( len = = 0.0f )
len = 1.0f ;
else
len = 1.0f / len ;
float vec [ 3 ] = { x * len , y * len , z * len } ;
glUniform3fv ( u_lightpos [ i ] , 1 , vec ) ;
} else {
SetFloat24Uniform3 ( u_lightpos [ i ] , & gstate . lpos [ i * 3 ] ) ;
2013-06-20 21:30:21 +00:00
}
2014-04-18 12:30:18 +00:00
if ( u_lightdir [ i ] ! = - 1 ) SetFloat24Uniform3 ( u_lightdir [ i ] , & gstate . ldir [ i * 3 ] ) ;
if ( u_lightatt [ i ] ! = - 1 ) SetFloat24Uniform3 ( u_lightatt [ i ] , & gstate . latt [ i * 3 ] ) ;
if ( u_lightangle [ i ] ! = - 1 ) glUniform1f ( u_lightangle [ i ] , getFloat24 ( gstate . lcutoff [ i ] ) ) ;
if ( u_lightspotCoef [ i ] ! = - 1 ) glUniform1f ( u_lightspotCoef [ i ] , getFloat24 ( gstate . lconv [ i ] ) ) ;
if ( u_lightambient [ i ] ! = - 1 ) SetColorUniform3 ( u_lightambient [ i ] , gstate . lcolor [ i * 3 ] ) ;
if ( u_lightdiffuse [ i ] ! = - 1 ) SetColorUniform3 ( u_lightdiffuse [ i ] , gstate . lcolor [ i * 3 + 1 ] ) ;
if ( u_lightspecular [ i ] ! = - 1 ) SetColorUniform3 ( u_lightspecular [ i ] , gstate . lcolor [ i * 3 + 2 ] ) ;
2012-12-20 17:31:21 +00:00
}
}
2017-01-08 18:37:21 +00:00
2017-03-20 02:51:44 +00:00
if ( dirty & DIRTY_BEZIERSPLINE ) {
glUniform1i ( u_spline_count_u , gstate_c . spline_count_u ) ;
if ( u_spline_count_v ! = - 1 )
2017-01-08 18:37:21 +00:00
glUniform1i ( u_spline_count_v , gstate_c . spline_count_v ) ;
2017-03-20 02:51:44 +00:00
if ( u_spline_type_u ! = - 1 )
2017-01-08 18:37:21 +00:00
glUniform1i ( u_spline_type_u , gstate_c . spline_type_u ) ;
2017-03-20 02:51:44 +00:00
if ( u_spline_type_v ! = - 1 )
2017-01-08 18:37:21 +00:00
glUniform1i ( u_spline_type_v , gstate_c . spline_type_v ) ;
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2012-11-01 15:19:01 +00:00
}
2017-01-21 21:16:30 +00:00
ShaderManagerGLES : : ShaderManagerGLES ( )
2017-08-20 17:29:43 +00:00
: lastShader_ ( nullptr ) , shaderSwitchDirtyUniforms_ ( 0 ) , diskCacheDirty_ ( false ) , fsCache_ ( 16 ) , vsCache_ ( 16 ) {
2013-02-04 22:09:01 +00:00
codeBuffer_ = new char [ 16384 ] ;
2015-10-14 09:18:45 +00:00
lastFSID_ . set_invalid ( ) ;
lastVSID_ . set_invalid ( ) ;
2013-02-04 22:09:01 +00:00
}
2017-01-21 21:16:30 +00:00
ShaderManagerGLES : : ~ ShaderManagerGLES ( ) {
2013-02-04 22:09:01 +00:00
delete [ ] codeBuffer_ ;
}
2017-01-21 21:16:30 +00:00
void ShaderManagerGLES : : Clear ( ) {
2013-10-12 08:39:50 +00:00
DirtyLastShader ( ) ;
2013-08-10 16:33:11 +00:00
for ( auto iter = linkedShaderCache_ . begin ( ) ; iter ! = linkedShaderCache_ . end ( ) ; + + iter ) {
2013-06-29 20:49:38 +00:00
delete iter - > ls ;
2012-11-01 15:19:01 +00:00
}
2017-12-02 17:07:27 +00:00
fsCache_ . Iterate ( [ & ] ( const FShaderID & key , Shader * shader ) {
2017-08-20 17:29:43 +00:00
delete shader ;
} ) ;
2017-12-02 17:07:27 +00:00
vsCache_ . Iterate ( [ & ] ( const VShaderID & key , Shader * shader ) {
2017-08-20 17:29:43 +00:00
delete shader ;
} ) ;
2013-08-10 16:33:11 +00:00
linkedShaderCache_ . clear ( ) ;
2017-08-20 17:29:43 +00:00
fsCache_ . Clear ( ) ;
vsCache_ . Clear ( ) ;
2013-02-13 10:10:51 +00:00
DirtyShader ( ) ;
2012-11-01 15:19:01 +00:00
}
2017-01-21 21:16:30 +00:00
void ShaderManagerGLES : : ClearCache ( bool deleteThem ) {
2016-01-17 19:29:34 +00:00
// TODO: Recreate all from the diskcache when we come back.
2012-11-01 15:19:01 +00:00
Clear ( ) ;
}
2017-01-21 21:16:30 +00:00
void ShaderManagerGLES : : DirtyShader ( ) {
2012-11-01 15:19:01 +00:00
// Forget the last shader ID
2015-10-14 09:18:45 +00:00
lastFSID_ . set_invalid ( ) ;
lastVSID_ . set_invalid ( ) ;
2014-06-23 04:42:29 +00:00
DirtyLastShader ( ) ;
2017-09-20 15:30:34 +00:00
gstate_c . Dirty ( DIRTY_ALL_UNIFORMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE ) ;
2017-01-30 13:04:20 +00:00
shaderSwitchDirtyUniforms_ = 0 ;
2012-11-01 15:19:01 +00:00
}
2017-01-21 21:16:30 +00:00
void ShaderManagerGLES : : DirtyLastShader ( ) { // disables vertex arrays
2013-08-10 16:33:11 +00:00
if ( lastShader_ )
lastShader_ - > stop ( ) ;
2015-10-14 09:18:45 +00:00
lastShader_ = nullptr ;
2015-03-16 02:10:33 +00:00
lastVShaderSame_ = false ;
2013-04-07 21:31:04 +00:00
}
2017-12-02 17:07:27 +00:00
Shader * ShaderManagerGLES : : CompileFragmentShader ( FShaderID FSID ) {
2017-11-19 10:23:16 +00:00
uint64_t uniformMask ;
if ( ! GenerateFragmentShader ( FSID , codeBuffer_ , & uniformMask ) ) {
2016-01-17 19:29:34 +00:00
return nullptr ;
}
2017-11-30 23:34:29 +00:00
return new Shader ( FSID , codeBuffer_ , GL_FRAGMENT_SHADER , false , 0 , uniformMask ) ;
2016-01-17 19:29:34 +00:00
}
2017-12-02 17:07:27 +00:00
Shader * ShaderManagerGLES : : CompileVertexShader ( VShaderID VSID ) {
2016-01-17 19:29:34 +00:00
bool useHWTransform = VSID . Bit ( VS_BIT_USE_HW_TRANSFORM ) ;
2017-11-19 10:08:34 +00:00
uint32_t attrMask ;
2017-11-19 10:23:16 +00:00
uint64_t uniformMask ;
GenerateVertexShader ( VSID , codeBuffer_ , & attrMask , & uniformMask ) ;
2017-11-30 23:34:29 +00:00
return new Shader ( VSID , codeBuffer_ , GL_VERTEX_SHADER , useHWTransform , attrMask , uniformMask ) ;
2014-12-14 19:16:46 +00:00
}
2017-12-02 17:07:27 +00:00
Shader * ShaderManagerGLES : : ApplyVertexShader ( int prim , u32 vertType , VShaderID * VSID ) {
2017-01-23 22:15:54 +00:00
uint64_t dirty = gstate_c . GetDirtyUniforms ( ) ;
if ( dirty ) {
2013-08-10 16:33:11 +00:00
if ( lastShader_ )
2017-01-23 22:15:54 +00:00
lastShader_ - > dirtyUniforms | = dirty ;
2017-01-30 13:04:20 +00:00
shaderSwitchDirtyUniforms_ | = dirty ;
2017-01-23 22:15:54 +00:00
gstate_c . CleanUniforms ( ) ;
2012-11-01 15:19:01 +00:00
}
2017-01-30 13:04:20 +00:00
if ( gstate_c . IsDirty ( DIRTY_VERTEXSHADER_STATE ) ) {
gstate_c . Clean ( DIRTY_VERTEXSHADER_STATE ) ;
2017-12-01 10:32:16 +00:00
bool useHWTransform = CanUseHardwareTransform ( prim ) ;
2017-01-30 13:04:20 +00:00
ComputeVertexShaderID ( VSID , vertType , useHWTransform ) ;
} else {
* VSID = lastVSID_ ;
}
2012-11-01 15:19:01 +00:00
2016-01-17 19:29:34 +00:00
if ( lastShader_ ! = 0 & & * VSID = = lastVSID_ ) {
2014-05-04 23:31:57 +00:00
lastVShaderSame_ = true ;
2014-12-14 19:16:46 +00:00
return lastShader_ - > vs_ ; // Already all set.
2014-03-24 09:55:07 +00:00
} else {
2014-05-04 23:31:57 +00:00
lastVShaderSame_ = false ;
2012-12-21 20:49:09 +00:00
}
2016-01-17 19:29:34 +00:00
lastVSID_ = * VSID ;
2012-11-01 15:19:01 +00:00
2017-08-20 17:29:43 +00:00
Shader * vs = vsCache_ . Get ( * VSID ) ;
if ( ! vs ) {
2012-11-01 15:19:01 +00:00
// Vertex shader not in cache. Let's compile it.
2016-01-17 19:29:34 +00:00
vs = CompileVertexShader ( * VSID ) ;
2013-06-06 08:05:31 +00:00
if ( vs - > Failed ( ) ) {
2015-07-01 21:50:16 +00:00
I18NCategory * gr = GetI18NCategory ( " Graphics " ) ;
2013-09-07 20:02:55 +00:00
ERROR_LOG ( G3D , " Shader compilation failed, falling back to software transform " ) ;
2017-03-12 03:31:00 +00:00
if ( ! g_Config . bHideSlowWarnings ) {
host - > NotifyUserMessage ( gr - > T ( " hardware transform error - falling back to software " ) , 2.5f , 0xFF3030FF ) ;
}
2013-06-06 08:05:31 +00:00
delete vs ;
// TODO: Look for existing shader with the appropriate ID, use that instead of generating a new one - however, need to make sure
// that that shader ID is not used when computing the linked shader ID below, because then IDs won't match
// next time and we'll do this over and over...
// Can still work with software transform.
2017-12-02 17:07:27 +00:00
VShaderID vsidTemp ;
2015-10-14 08:04:27 +00:00
ComputeVertexShaderID ( & vsidTemp , vertType , false ) ;
2017-11-19 10:08:34 +00:00
uint32_t attrMask ;
2017-11-19 10:23:16 +00:00
uint64_t uniformMask ;
GenerateVertexShader ( vsidTemp , codeBuffer_ , & attrMask , & uniformMask ) ;
2017-11-30 23:34:29 +00:00
vs = new Shader ( vsidTemp , codeBuffer_ , GL_VERTEX_SHADER , false , attrMask , uniformMask ) ;
2013-06-06 08:05:31 +00:00
}
2017-08-20 17:29:43 +00:00
vsCache_ . Insert ( * VSID , vs ) ;
2016-01-17 19:29:34 +00:00
diskCacheDirty_ = true ;
2012-11-01 15:19:01 +00:00
}
2014-03-24 09:55:07 +00:00
return vs ;
}
2017-12-02 17:07:27 +00:00
LinkedShader * ShaderManagerGLES : : ApplyFragmentShader ( VShaderID VSID , Shader * vs , u32 vertType , int prim ) {
FShaderID FSID ;
2017-03-19 10:32:29 +00:00
if ( gstate_c . IsDirty ( DIRTY_FRAGMENTSHADER_STATE ) ) {
gstate_c . Clean ( DIRTY_FRAGMENTSHADER_STATE ) ;
ComputeFragmentShaderID ( & FSID ) ;
} else {
FSID = lastFSID_ ;
}
2014-06-01 01:55:00 +00:00
if ( lastVShaderSame_ & & FSID = = lastFSID_ ) {
2016-01-17 19:29:34 +00:00
lastShader_ - > UpdateUniforms ( vertType , VSID ) ;
2014-03-24 09:55:07 +00:00
return lastShader_ ;
2014-05-04 23:31:57 +00:00
}
lastFSID_ = FSID ;
2012-12-05 03:45:28 +00:00
2017-08-20 17:29:43 +00:00
Shader * fs = fsCache_ . Get ( FSID ) ;
if ( ! fs ) {
2012-11-01 15:19:01 +00:00
// Fragment shader not in cache. Let's compile it.
2016-01-17 19:29:34 +00:00
fs = CompileFragmentShader ( FSID ) ;
2017-08-20 17:29:43 +00:00
fsCache_ . Insert ( FSID , fs ) ;
2016-01-17 19:29:34 +00:00
diskCacheDirty_ = true ;
2012-11-01 15:19:01 +00:00
}
2013-06-06 08:05:31 +00:00
2012-11-01 15:19:01 +00:00
// Okay, we have both shaders. Let's see if there's a linked one.
2015-10-14 09:18:45 +00:00
LinkedShader * ls = nullptr ;
2013-06-29 20:49:38 +00:00
2017-01-30 13:04:20 +00:00
u64 switchDirty = shaderSwitchDirtyUniforms_ ;
2013-08-10 16:33:11 +00:00
for ( auto iter = linkedShaderCache_ . begin ( ) ; iter ! = linkedShaderCache_ . end ( ) ; + + iter ) {
2013-06-29 20:49:38 +00:00
// Deferred dirtying! Let's see if we can make this even more clever later.
2015-07-26 20:38:40 +00:00
iter - > ls - > dirtyUniforms | = switchDirty ;
2013-06-29 20:49:38 +00:00
if ( iter - > vs = = vs & & iter - > fs = = fs ) {
ls = iter - > ls ;
}
}
2017-01-30 13:04:20 +00:00
shaderSwitchDirtyUniforms_ = 0 ;
2012-11-01 15:19:01 +00:00
2015-10-14 09:18:45 +00:00
if ( ls = = nullptr ) {
2017-11-30 23:07:25 +00:00
_dbg_assert_ ( G3D , FSID . Bit ( FS_BIT_LMODE ) = = VSID . Bit ( VS_BIT_LMODE ) ) ;
_dbg_assert_ ( G3D , FSID . Bit ( FS_BIT_DO_TEXTURE ) = = VSID . Bit ( VS_BIT_DO_TEXTURE ) ) ;
_dbg_assert_ ( G3D , FSID . Bit ( FS_BIT_ENABLE_FOG ) = = VSID . Bit ( VS_BIT_ENABLE_FOG ) ) ;
_dbg_assert_ ( G3D , FSID . Bit ( FS_BIT_FLATSHADE ) = = VSID . Bit ( VS_BIT_FLATSHADE ) ) ;
2014-12-14 19:16:46 +00:00
// Check if we can link these.
2016-01-17 19:12:54 +00:00
ls = new LinkedShader ( VSID , vs , FSID , fs , vs - > UseHWTransform ( ) ) ;
2016-01-17 19:29:34 +00:00
ls - > use ( VSID , lastShader_ ) ;
2013-06-29 20:49:38 +00:00
const LinkedShaderCacheEntry entry ( vs , fs , ls ) ;
2013-08-10 16:33:11 +00:00
linkedShaderCache_ . push_back ( entry ) ;
2012-11-01 15:19:01 +00:00
} else {
2016-01-17 19:29:34 +00:00
ls - > use ( VSID , lastShader_ ) ;
2012-11-01 15:19:01 +00:00
}
2016-01-17 19:29:34 +00:00
ls - > UpdateUniforms ( vertType , VSID ) ;
2012-11-01 15:19:01 +00:00
2013-08-10 16:33:11 +00:00
lastShader_ = ls ;
2012-11-01 15:19:01 +00:00
return ls ;
}
2015-10-14 15:45:21 +00:00
2016-01-17 19:29:34 +00:00
std : : string Shader : : GetShaderString ( DebugShaderStringType type , ShaderID id ) const {
2015-10-14 15:45:21 +00:00
switch ( type ) {
case SHADER_STRING_SOURCE_CODE :
return source_ ;
case SHADER_STRING_SHORT_DESC :
2016-01-17 19:29:34 +00:00
return isFragment_ ? FragmentShaderDesc ( id ) : VertexShaderDesc ( id ) ;
2015-10-14 15:45:21 +00:00
default :
return " N/A " ;
}
}
2017-01-21 21:16:30 +00:00
std : : vector < std : : string > ShaderManagerGLES : : DebugGetShaderIDs ( DebugShaderType type ) {
2015-10-14 15:45:21 +00:00
std : : string id ;
std : : vector < std : : string > ids ;
switch ( type ) {
case SHADER_TYPE_VERTEX :
{
2017-12-02 17:07:27 +00:00
vsCache_ . Iterate ( [ & ] ( const VShaderID & id , Shader * shader ) {
2017-08-20 17:29:43 +00:00
std : : string idstr ;
id . ToString ( & idstr ) ;
ids . push_back ( idstr ) ;
} ) ;
2015-10-14 15:45:21 +00:00
}
break ;
case SHADER_TYPE_FRAGMENT :
{
2017-12-02 17:07:27 +00:00
fsCache_ . Iterate ( [ & ] ( const FShaderID & id , Shader * shader ) {
2017-08-20 17:29:43 +00:00
std : : string idstr ;
id . ToString ( & idstr ) ;
ids . push_back ( idstr ) ;
} ) ;
2015-10-14 15:45:21 +00:00
}
break ;
2015-11-04 11:46:38 +00:00
default :
break ;
2015-10-14 15:45:21 +00:00
}
return ids ;
}
2017-01-21 21:16:30 +00:00
std : : string ShaderManagerGLES : : DebugGetShaderString ( std : : string id , DebugShaderType type , DebugShaderStringType stringType ) {
2015-10-14 15:45:21 +00:00
ShaderID shaderId ;
shaderId . FromString ( id ) ;
switch ( type ) {
case SHADER_TYPE_VERTEX :
{
2017-12-02 17:07:27 +00:00
Shader * vs = vsCache_ . Get ( VShaderID ( shaderId ) ) ;
2017-08-20 17:29:43 +00:00
return vs ? vs - > GetShaderString ( stringType , shaderId ) : " " ;
2015-10-14 15:45:21 +00:00
}
case SHADER_TYPE_FRAGMENT :
{
2017-12-02 17:07:27 +00:00
Shader * fs = fsCache_ . Get ( FShaderID ( shaderId ) ) ;
2017-08-20 17:29:43 +00:00
return fs - > GetShaderString ( stringType , shaderId ) ;
2015-10-14 15:45:21 +00:00
}
default :
return " N/A " ;
}
}
2016-01-17 16:11:51 +00:00
// Shader pseudo-cache.
//
// We simply store the IDs of the shaders used during gameplay. On next startup of
// the same game, we simply compile all the shaders from the start, so we don't have to
// compile them on the fly later. Ideally we would store the actual compiled shaders
// rather than just their IDs, but OpenGL does not support this, except for a few obscure
// vendor-specific extensions.
//
// If things like GPU supported features have changed since the last time, we discard the cache
// as sometimes these features might have an effect on the ID bits.
# define CACHE_HEADER_MAGIC 0x83277592
2017-11-30 21:44:39 +00:00
# define CACHE_VERSION 5
2016-01-17 16:11:51 +00:00
struct CacheHeader {
uint32_t magic ;
uint32_t version ;
uint32_t featureFlags ;
uint32_t reserved ;
int numVertexShaders ;
int numFragmentShaders ;
int numLinkedPrograms ;
} ;
2017-01-21 21:16:30 +00:00
void ShaderManagerGLES : : LoadAndPrecompile ( const std : : string & filename ) {
2016-01-21 07:36:06 +00:00
File : : IOFile f ( filename , " rb " ) ;
2017-12-02 17:14:19 +00:00
u64 sz = f . GetSize ( ) ;
2016-01-21 07:36:06 +00:00
if ( ! f . IsOpen ( ) ) {
2016-01-17 16:11:51 +00:00
return ;
}
CacheHeader header ;
2016-01-21 07:36:06 +00:00
if ( ! f . ReadArray ( & header , 1 ) ) {
2016-01-17 16:11:51 +00:00
return ;
}
if ( header . magic ! = CACHE_HEADER_MAGIC | | header . version ! = CACHE_VERSION | | header . featureFlags ! = gstate_c . featureFlags ) {
return ;
}
2016-01-17 19:53:58 +00:00
time_update ( ) ;
double start = time_now_d ( ) ;
2017-08-28 13:22:18 +00:00
// Sanity check the file contents
2017-12-02 17:14:19 +00:00
if ( header . numFragmentShaders > 1000 | | header . numVertexShaders > 1000 | | header . numLinkedPrograms > 1000 ) {
ERROR_LOG ( G3D , " Corrupt shader cache file header, aborting. " ) ;
2017-08-28 13:22:18 +00:00
return ;
2017-12-02 17:14:19 +00:00
}
// Also make sure the size makes sense, in case there's corruption.
u64 expectedSize = sizeof ( header ) ;
expectedSize + = header . numVertexShaders * sizeof ( VShaderID ) ;
expectedSize + = header . numFragmentShaders * sizeof ( FShaderID ) ;
expectedSize + = header . numLinkedPrograms * ( sizeof ( VShaderID ) + sizeof ( FShaderID ) ) ;
if ( sz ! = expectedSize ) {
2017-12-02 18:20:44 +00:00
ERROR_LOG ( G3D , " Shader cache file is wrong size: %lld instead of %lld " , sz , expectedSize ) ;
2017-12-02 17:14:19 +00:00
return ;
}
2017-08-28 13:22:18 +00:00
2016-01-17 16:11:51 +00:00
for ( int i = 0 ; i < header . numVertexShaders ; i + + ) {
2017-12-02 17:07:27 +00:00
VShaderID id ;
2016-01-21 07:36:06 +00:00
if ( ! f . ReadArray ( & id , 1 ) ) {
return ;
}
2017-08-25 20:49:51 +00:00
if ( ! vsCache_ . Get ( id ) ) {
2017-12-01 10:32:16 +00:00
if ( id . Bit ( VS_BIT_IS_THROUGH ) & & id . Bit ( VS_BIT_USE_HW_TRANSFORM ) ) {
// Clearly corrupt, bailing.
ERROR_LOG_REPORT ( G3D , " Corrupt shader cache: Both IS_THROUGH and USE_HW_TRANSFORM set. " ) ;
return ;
}
2017-08-28 13:22:18 +00:00
Shader * vs = CompileVertexShader ( id ) ;
if ( vs - > Failed ( ) ) {
// Give up on using the cache, just bail. We can't safely create the fallback shaders here
// without trying to deduce the vertType from the VSID.
ERROR_LOG ( G3D , " Failed to compile a vertex shader loading from cache. Skipping rest of shader cache. " ) ;
delete vs ;
return ;
}
2017-08-25 20:49:51 +00:00
vsCache_ . Insert ( id , vs ) ;
} else {
WARN_LOG ( G3D , " Duplicate vertex shader found in GL shader cache, ignoring " ) ;
}
2016-01-17 16:11:51 +00:00
}
for ( int i = 0 ; i < header . numFragmentShaders ; i + + ) {
2017-12-02 17:07:27 +00:00
FShaderID id ;
2016-01-21 07:36:06 +00:00
if ( ! f . ReadArray ( & id , 1 ) ) {
return ;
}
2017-08-25 20:49:51 +00:00
if ( ! fsCache_ . Get ( id ) ) {
fsCache_ . Insert ( id , CompileFragmentShader ( id ) ) ;
} else {
WARN_LOG ( G3D , " Duplicate fragment shader found in GL shader cache, ignoring " ) ;
}
2016-01-17 16:11:51 +00:00
}
for ( int i = 0 ; i < header . numLinkedPrograms ; i + + ) {
2017-12-02 17:07:27 +00:00
VShaderID vsid ;
FShaderID fsid ;
2016-01-21 07:36:06 +00:00
if ( ! f . ReadArray ( & vsid , 1 ) ) {
return ;
}
if ( ! f . ReadArray ( & fsid , 1 ) ) {
return ;
}
2017-08-20 17:29:43 +00:00
Shader * vs = vsCache_ . Get ( vsid ) ;
Shader * fs = fsCache_ . Get ( fsid ) ;
if ( vs & & fs ) {
2017-12-01 19:17:51 +00:00
LinkedShader * ls = new LinkedShader ( vsid , vs , fsid , fs , vs - > UseHWTransform ( ) , true ) ;
2017-08-20 17:29:43 +00:00
LinkedShaderCacheEntry entry ( vs , fs , ls ) ;
2016-01-21 07:36:06 +00:00
linkedShaderCache_ . push_back ( entry ) ;
}
2016-01-17 16:11:51 +00:00
}
2016-01-17 19:53:58 +00:00
time_update ( ) ;
double end = time_now_d ( ) ;
NOTICE_LOG ( G3D , " Compiled and linked %d programs (%d vertex, %d fragment) in %0.1f milliseconds " , header . numLinkedPrograms , header . numVertexShaders , header . numFragmentShaders , 1000 * ( end - start ) ) ;
2016-01-17 21:11:28 +00:00
NOTICE_LOG ( G3D , " Loaded the shader cache from '%s' " , filename . c_str ( ) ) ;
2016-01-17 16:11:51 +00:00
diskCacheDirty_ = false ;
}
2017-01-21 21:16:30 +00:00
void ShaderManagerGLES : : Save ( const std : : string & filename ) {
2016-01-17 16:11:51 +00:00
if ( ! diskCacheDirty_ ) {
return ;
}
2016-01-17 21:53:06 +00:00
if ( linkedShaderCache_ . empty ( ) ) {
2016-01-17 21:11:28 +00:00
return ;
}
INFO_LOG ( G3D , " Saving the shader cache to '%s' " , filename . c_str ( ) ) ;
2016-01-17 16:11:51 +00:00
FILE * f = File : : OpenCFile ( filename , " wb " ) ;
if ( ! f ) {
// Can't save, give up for now.
diskCacheDirty_ = false ;
return ;
}
CacheHeader header ;
header . magic = CACHE_HEADER_MAGIC ;
header . version = CACHE_VERSION ;
header . reserved = 0 ;
header . featureFlags = gstate_c . featureFlags ;
2017-02-08 17:07:34 +00:00
header . numVertexShaders = GetNumVertexShaders ( ) ;
header . numFragmentShaders = GetNumFragmentShaders ( ) ;
header . numLinkedPrograms = GetNumPrograms ( ) ;
2016-01-17 16:11:51 +00:00
fwrite ( & header , 1 , sizeof ( header ) , f ) ;
2017-08-20 17:29:43 +00:00
vsCache_ . Iterate ( [ & ] ( const ShaderID & id , Shader * shader ) {
2016-01-17 16:11:51 +00:00
fwrite ( & id , 1 , sizeof ( id ) , f ) ;
2017-08-20 17:29:43 +00:00
} ) ;
fsCache_ . Iterate ( [ & ] ( const ShaderID & id , Shader * shader ) {
2016-01-17 16:11:51 +00:00
fwrite ( & id , 1 , sizeof ( id ) , f ) ;
2017-08-20 17:29:43 +00:00
} ) ;
2016-01-17 16:11:51 +00:00
for ( auto iter : linkedShaderCache_ ) {
ShaderID vsid , fsid ;
2017-08-20 17:29:43 +00:00
vsCache_ . Iterate ( [ & ] ( const ShaderID & id , Shader * shader ) {
if ( iter . vs = = shader )
vsid = id ;
} ) ;
fsCache_ . Iterate ( [ & ] ( const ShaderID & id , Shader * shader ) {
if ( iter . fs = = shader )
fsid = id ;
} ) ;
2016-01-17 16:11:51 +00:00
fwrite ( & vsid , 1 , sizeof ( vsid ) , f ) ;
fwrite ( & fsid , 1 , sizeof ( fsid ) , f ) ;
}
fclose ( f ) ;
diskCacheDirty_ = false ;
}