2021-09-18 10:20:28 +00:00
/*
* * Postprocessing framework
* * Copyright ( c ) 2016 - 2020 Magnus Norddahl
* *
* * This software is provided ' as - is ' , without any express or implied
* * warranty . In no event will the authors be held liable for any damages
* * arising from the use of this software .
* *
* * Permission is granted to anyone to use this software for any purpose ,
* * including commercial applications , and to alter it and redistribute it
* * freely , subject to the following restrictions :
* *
* * 1. The origin of this software must not be misrepresented ; you must not
* * claim that you wrote the original software . If you use this software
* * in a product , an acknowledgment in the product documentation would be
* * appreciated but is not required .
* * 2. Altered source versions must be plainly marked as such , and must not be
* * misrepresented as being the original software .
* * 3. This notice may not be removed or altered from any source distribution .
*/
# include "gles_system.h"
# include "v_video.h"
# include "hw_cvars.h"
# include "gles_shaderprogram.h"
# include "hw_shaderpatcher.h"
# include "filesystem.h"
# include "printf.h"
namespace OpenGLESRenderer
{
FString GetGLSLPrecision ( ) ;
bool IsShaderCacheActive ( ) ;
TArray < uint8_t > LoadCachedProgramBinary ( const FString & vertex , const FString & fragment , uint32_t & binaryFormat ) ;
void SaveCachedProgramBinary ( const FString & vertex , const FString & fragment , const TArray < uint8_t > & binary , uint32_t binaryFormat ) ;
FShaderProgram : : FShaderProgram ( )
{
for ( int i = 0 ; i < NumShaderTypes ; i + + )
mShaders [ i ] = 0 ;
}
//==========================================================================
//
// Free shader program resources
//
//==========================================================================
FShaderProgram : : ~ FShaderProgram ( )
{
if ( mProgram ! = 0 )
glDeleteProgram ( mProgram ) ;
for ( int i = 0 ; i < NumShaderTypes ; i + + )
{
if ( mShaders [ i ] ! = 0 )
glDeleteShader ( mShaders [ i ] ) ;
}
}
//==========================================================================
//
// Creates an OpenGL shader object for the specified type of shader
//
//==========================================================================
void FShaderProgram : : CreateShader ( ShaderType type )
{
GLenum gltype = 0 ;
switch ( type )
{
default :
case Vertex : gltype = GL_VERTEX_SHADER ; break ;
case Fragment : gltype = GL_FRAGMENT_SHADER ; break ;
}
mShaders [ type ] = glCreateShader ( gltype ) ;
}
//==========================================================================
//
// Compiles a shader and attaches it the program object
//
//==========================================================================
void FShaderProgram : : Compile ( ShaderType type , const char * lumpName , const char * defines , int maxGlslVersion )
{
int lump = fileSystem . CheckNumForFullName ( lumpName ) ;
if ( lump = = - 1 ) I_FatalError ( " Unable to load '%s' " , lumpName ) ;
FString code = fileSystem . ReadFile ( lump ) . GetString ( ) . GetChars ( ) ;
Compile ( type , lumpName , code , defines , maxGlslVersion ) ;
}
void FShaderProgram : : Compile ( ShaderType type , const char * name , const FString & code , const char * defines , int maxGlslVersion )
{
mShaderNames [ type ] = name ;
mShaderSources [ type ] = PatchShader ( type , code , defines , maxGlslVersion ) ;
}
void FShaderProgram : : CompileShader ( ShaderType type )
{
CreateShader ( type ) ;
const auto & handle = mShaders [ type ] ;
2021-12-30 09:30:21 +00:00
2021-09-18 10:20:28 +00:00
const FString & patchedCode = mShaderSources [ type ] ;
int lengths [ 1 ] = { ( int ) patchedCode . Len ( ) } ;
const char * sources [ 1 ] = { patchedCode . GetChars ( ) } ;
glShaderSource ( handle , 1 , sources , lengths ) ;
glCompileShader ( handle ) ;
GLint status = 0 ;
glGetShaderiv ( handle , GL_COMPILE_STATUS , & status ) ;
if ( status = = GL_FALSE )
{
I_FatalError ( " Compile Shader '%s': \n %s \n " , mShaderNames [ type ] . GetChars ( ) , GetShaderInfoLog ( handle ) . GetChars ( ) ) ;
}
else
{
if ( mProgram = = 0 )
mProgram = glCreateProgram ( ) ;
glAttachShader ( mProgram , handle ) ;
}
}
//==========================================================================
//
// Links a program with the compiled shaders
//
//==========================================================================
void FShaderProgram : : Link ( const char * name )
{
uint32_t binaryFormat = 0 ;
TArray < uint8_t > binary ;
if ( IsShaderCacheActive ( ) )
binary = LoadCachedProgramBinary ( mShaderSources [ Vertex ] , mShaderSources [ Fragment ] , binaryFormat ) ;
bool loadedFromBinary = false ;
if ( ! loadedFromBinary )
{
CompileShader ( Vertex ) ;
CompileShader ( Fragment ) ;
glLinkProgram ( mProgram ) ;
GLint status = 0 ;
glGetProgramiv ( mProgram , GL_LINK_STATUS , & status ) ;
if ( status = = GL_FALSE )
{
I_FatalError ( " Link Shader '%s': \n %s \n " , name , GetProgramInfoLog ( mProgram ) . GetChars ( ) ) ;
}
}
// This is only for old OpenGL which didn't allow to set the binding from within the shader.
if ( screen - > glslversion < 4.20 )
{
glUseProgram ( mProgram ) ;
for ( auto & uni : samplerstobind )
{
auto index = glGetUniformLocation ( mProgram , uni . first ) ;
if ( index > = 0 )
{
glUniform1i ( index , uni . second ) ;
}
}
}
samplerstobind . Clear ( ) ;
samplerstobind . ShrinkToFit ( ) ;
}
//==========================================================================
//
// Set uniform buffer location (only useful for GL 3.3)
//
//==========================================================================
void FShaderProgram : : SetUniformBufferLocation ( int index , const char * name )
{
}
//==========================================================================
//
// Makes the shader the active program
//
//==========================================================================
void FShaderProgram : : Bind ( )
{
glUseProgram ( mProgram ) ;
}
//==========================================================================
//
// Returns the shader info log (warnings and compile errors)
//
//==========================================================================
FString FShaderProgram : : GetShaderInfoLog ( GLuint handle )
{
static char buffer [ 10000 ] ;
GLsizei length = 0 ;
buffer [ 0 ] = 0 ;
glGetShaderInfoLog ( handle , 10000 , & length , buffer ) ;
return FString ( buffer ) ;
}
//==========================================================================
//
// Returns the program info log (warnings and compile errors)
//
//==========================================================================
FString FShaderProgram : : GetProgramInfoLog ( GLuint handle )
{
static char buffer [ 10000 ] ;
GLsizei length = 0 ;
buffer [ 0 ] = 0 ;
glGetProgramInfoLog ( handle , 10000 , & length , buffer ) ;
return FString ( buffer ) ;
}
//==========================================================================
//
// Patches a shader to be compatible with the version of OpenGL in use
//
//==========================================================================
FString FShaderProgram : : PatchShader ( ShaderType type , const FString & code , const char * defines , int maxGlslVersion )
{
FString patchedCode ;
patchedCode . AppendFormat ( " #version %d \n " , 100 ) ; // Set to GLES2
patchedCode + = GetGLSLPrecision ( ) ;
if ( defines )
patchedCode < < defines ;
patchedCode < < " #line 1 \n " ;
patchedCode < < RemoveLayoutLocationDecl ( code , type = = Vertex ? " out " : " in " ) ;
if ( maxGlslVersion < 420 )
{
// Here we must strip out all layout(binding) declarations for sampler uniforms and store them in 'samplerstobind' which can then be processed by the link function.
patchedCode = RemoveSamplerBindings ( patchedCode , samplerstobind ) ;
}
return patchedCode ;
}
/////////////////////////////////////////////////////////////////////////////
void FPresentShaderBase : : Init ( const char * vtx_shader_name , const char * program_name )
{
FString prolog = Uniforms . CreateDeclaration ( " Uniforms " , PresentUniforms : : Desc ( ) ) ;
mShader . reset ( new FShaderProgram ( ) ) ;
mShader - > Compile ( FShaderProgram : : Vertex , " shaders_gles/pp/screenquad.vp " , prolog , 330 ) ;
mShader - > Compile ( FShaderProgram : : Fragment , vtx_shader_name , prolog , 330 ) ;
mShader - > Link ( program_name ) ;
mShader - > Bind ( ) ;
Uniforms . Init ( ) ;
Uniforms . UniformLocation . resize ( Uniforms . mFields . size ( ) ) ;
2021-10-08 17:07:56 +00:00
for ( size_t n = 0 ; n < Uniforms . mFields . size ( ) ; n + + )
2021-09-18 10:20:28 +00:00
{
int index = - 1 ;
UniformFieldDesc desc = Uniforms . mFields [ n ] ;
index = glGetUniformLocation ( mShader - > mProgram , desc . Name ) ;
Uniforms . UniformLocation [ n ] = index ;
}
}
void FPresentShader : : Bind ( )
{
if ( ! mShader )
{
Init ( " shaders_gles/pp/present.fp " , " shaders_gles/pp/present " ) ;
}
mShader - > Bind ( ) ;
}
2021-10-08 17:07:56 +00:00
}