Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Graphics/shaders.cpp')
-rw-r--r--Source/Graphics/shaders.cpp1298
1 files changed, 1298 insertions, 0 deletions
diff --git a/Source/Graphics/shaders.cpp b/Source/Graphics/shaders.cpp
new file mode 100644
index 00000000..36bdf9fb
--- /dev/null
+++ b/Source/Graphics/shaders.cpp
@@ -0,0 +1,1298 @@
+//-----------------------------------------------------------------------------
+// Name: shaders.cpp
+// Developer: Wolfire Games LLC
+// Description:
+// License: Read below
+//-----------------------------------------------------------------------------
+//
+//
+// Copyright 2022 Wolfire Games LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-----------------------------------------------------------------------------
+#include "shaders.h"
+
+#include <Graphics/textures.h>
+#include <Graphics/graphics.h>
+
+#include <Internal/common.h>
+#include <Internal/textfile.h>
+#include <Internal/datemodified.h>
+#include <Internal/filesystem.h>
+#include <Internal/profiler.h>
+#include <Internal/config.h>
+
+#include <Logging/logdata.h>
+#include <Compat/fileio.h>
+#include <Utility/assert.h>
+
+#include <string>
+
+#define SHADER_WARNINGS
+
+extern bool g_single_pass_shadow_cascade;
+
+void Shaders::Dispose() {
+ CHECK_GL_ERROR();
+ for(unsigned i=0; i<programs.size(); ++i){
+ Program& program = programs[i];
+ if(program.gl_program != UNLOADED_SHADER_ID) {
+ for(int i=0; i<kMaxShaderTypes; ++i){
+ if( program.shader_ids[i] >= 0 ) {
+ glDetachShader(program.gl_program, shaders[program.shader_ids[i]].gl_shader);
+ }
+ }
+ glDeleteProgram(program.gl_program);
+ }
+ }
+ for(unsigned i=0; i<shaders.size(); ++i){
+ if(shaders[i].gl_shader != UNLOADED_SHADER_ID) {
+ glDeleteShader(shaders[i].gl_shader);
+ }
+ }
+
+ CHECK_GL_ERROR();
+
+ shaders.clear();
+ programs.clear();
+}
+
+namespace {
+static bool IsCharInString(char chr, const char* str) {
+ for(int i=0; str[i] != '\0'; ++i){
+ if(chr == str[i]){
+ return true;
+ }
+ }
+ return false;
+}
+
+static void CopySubString(const char* src, char *dst, const char *delim, int max_chars){
+ int i = 0;
+ while(i < max_chars && !IsCharInString(src[i], delim)){
+ dst[i] = src[i];
+ ++i;
+ }
+ dst[i] = '\0';
+}
+
+static int FindCharInString(char chr, const char *str, int max_chars){
+ for(int i=0; i<max_chars; ++i){
+ if(str[i] == chr){
+ return i;
+ }
+ }
+ return -1;
+}
+
+namespace PREPROC {
+ enum Type {
+ INCLUDE,
+ UNKNOWN,
+ PRAGMA,
+ VERSION
+ };
+}
+
+struct PreprocessorDirective {
+ PREPROC::Type type;
+ std::string data;
+};
+
+// Looks at a line of text and fills *pd with information about the preprocessor directive in that line
+static void ParsePreprocessorDirective(const char* text, int line_break, PreprocessorDirective *pd){
+ pd->type = PREPROC::UNKNOWN;
+ // Copy # directive into buf e.g. get 'include' from '#include "blah"'
+ const int BUF_SIZE = 256;
+ char buf[BUF_SIZE];
+ CopySubString(&text[1], buf, " \t", min(BUF_SIZE-1, line_break-1));
+ // If #include directive, read info between "" signs
+ if(strcmp(buf, "include") == 0){
+ int open_quote_sign = FindCharInString('\"', text, line_break);
+ if(open_quote_sign == -1){
+ return;
+ }
+ int close_quote_sign = FindCharInString('\"', &text[open_quote_sign+1], line_break-(open_quote_sign+1)) + open_quote_sign+1;
+ if(close_quote_sign == -1){
+ return;
+ }
+ CopySubString(&text[open_quote_sign+1],buf,"",min(BUF_SIZE-1, close_quote_sign - open_quote_sign - 1));
+ pd->data = buf;
+ pd->type = PREPROC::INCLUDE;
+ }
+ // If #pragma directive, read proceeding non-whitespace
+ if(strcmp(buf, "pragma") == 0){
+ int first_char = strlen("#pragma");
+ while(IsCharInString(text[first_char], " \t") && first_char < line_break){
+ ++first_char;
+ }
+ int second_char = first_char;
+ while(!IsCharInString(text[second_char], " \t") && second_char < line_break){
+ ++second_char;
+ }
+ CopySubString(&text[first_char],buf,"",min(BUF_SIZE-1, second_char - first_char));
+ pd->data = buf;
+ pd->type = PREPROC::PRAGMA;
+ }
+ if(strcmp(buf, "version") == 0){
+ int first_char = strlen("#version");
+ while(IsCharInString(text[first_char], " \t") && first_char < line_break){
+ ++first_char;
+ }
+ int second_char = first_char;
+ while(!IsCharInString(text[second_char], " \t") && second_char < line_break){
+ ++second_char;
+ }
+ CopySubString(&text[first_char],buf,"",min(BUF_SIZE-1, second_char - first_char));
+ pd->data = buf;
+ pd->type = PREPROC::VERSION;
+ }
+ return;
+}
+
+static void HandleInclude(std::string *text, int line_start, const char* abs_path, int *cursor){
+ text->insert(line_start, "//");
+ *cursor += 2;
+ text->insert(*cursor, "\n");
+ *cursor += 1;
+ std::string include_text;
+ textFileRead(abs_path, &include_text);
+ text->insert(*cursor, include_text);
+ --*cursor;
+}
+
+static void AddShaderDefinition(std::string *text, const std::string &def){
+ text->insert(0,"#define "+def+"\n");
+}
+
+static void Preprocess(Shader* shader, ShaderType type, const std::vector<std::string> &definitions, const std::string& shader_dir_path) {
+ shader->include_files.clear();
+ shader->include_modified.clear();
+ shader->use_tangent = false;
+ shader->transparent = false;
+ shader->bind_out_color = false;
+ shader->bind_out_vel = false;
+ shader->depth_only = false;
+ shader->particle = false;
+ BlendMode blend_mode = _BM_NORMAL;
+ std::string &text = shader->text;
+
+ bool no_velocity_buf_defined = false;
+
+ for(unsigned i=0; i<definitions.size(); ++i){
+ if(definitions[i] == "TANGENT"){
+ shader->use_tangent = true;
+ }
+ if(definitions[i] == "ALPHA"){
+ shader->transparent = true;
+ }
+ if(definitions[i] == "DEPTH_ONLY") {
+ shader->depth_only = true;
+ }
+ if(definitions[i] == "PARTICLE") {
+ shader->particle = true;
+ }
+ if(definitions[i] == "NO_VELOCITY_BUF") {
+ no_velocity_buf_defined = true;
+ }
+ }
+
+ int version = -1;
+ int line_start = 0;
+ for(int cursor=0; text[cursor] != '\0'; ++cursor){
+ // Check each line one at a time
+ if(IsCharInString(text[cursor],"\n\r")){
+ // Search line for # sign
+ int pound_sign = -1;
+ for(int j=line_start; j<cursor; ++j){
+ if(text[j] == '#'){
+ pound_sign = j;
+ break;
+ }
+ }
+ if(pound_sign != -1){
+ PreprocessorDirective pd;
+ ParsePreprocessorDirective(&text[pound_sign], cursor-pound_sign, &pd);
+ bool recognized_pragma = false;
+ switch(pd.type){
+ case PREPROC::UNKNOWN:
+ LOGD << "Unknown preprocessor directive" << std::endl;
+ break;
+ case PREPROC::INCLUDE:
+ LOGD << "Include file \"" << pd.data << "\"." << std::endl;;
+ char rel_path[kPathSize], abs_path[kPathSize];
+ FormatString(rel_path, kPathSize, "%s%s", shader_dir_path.c_str(), pd.data.c_str());
+ if(FindFilePath(rel_path, abs_path, kPathSize, kDataPaths | kModPaths) == -1){
+ FatalError("Error", "Could not find shader include file: %s", rel_path);
+ }
+ HandleInclude(&text, line_start, abs_path, &cursor);
+ shader->include_files.push_back(abs_path);
+ shader->include_modified.push_back(GetDateModifiedInt64(shader->include_files.back().c_str()));
+ break;
+ case PREPROC::PRAGMA:
+ LOGD << "Pragma \"" << pd.data << "\"" << std::endl;
+ if (pd.data == "transparent"){
+ shader->transparent = true;
+ recognized_pragma = true;
+ } else if (pd.data == "use_tangent") {
+ shader->use_tangent = true;
+ recognized_pragma = true;
+ } else if (pd.data == "blendmode_add") {
+ blend_mode = _BM_ADD;
+ recognized_pragma = true;
+ } else if (pd.data == "blendmode_multiply") {
+ blend_mode = _BM_MULTIPLY;
+ recognized_pragma = true;
+ } else if (pd.data == "bind_out_color" ) {
+ shader->bind_out_color = true;
+ recognized_pragma = true;
+ } else if (pd.data == "bind_out_vel" ) {
+ recognized_pragma = true;
+ if(no_velocity_buf_defined) {
+ shader->bind_out_vel = false;
+ } else {
+ shader->bind_out_vel = true;
+ }
+ }
+ if( recognized_pragma ) {
+ text.insert(line_start, "//");
+ }
+ break;
+ case PREPROC::VERSION:
+ // Comment out current version definition
+ // So we can add it later at the beginning
+ text.insert(line_start, "//");
+ cursor += 2;
+ text.insert(cursor, "\n");
+ cursor += 1;
+ version = atoi(pd.data.c_str());
+ break;
+ }
+ }
+ line_start = cursor+1;
+ }
+ }
+
+ AddShaderDefinition(&text, "GAMMA_CORRECT");
+ if (type == _vertex) {
+ AddShaderDefinition(&text, "VERTEX_SHADER");
+ } else if( type == _fragment ) {
+ AddShaderDefinition(&text, "FRAGMENT_SHADER");
+ } else if( type == _geom ) {
+ AddShaderDefinition(&text, "GEOMETRY_SHADER");
+ } else if( type == _tess_ctrl ) {
+ AddShaderDefinition(&text, "TESSELATION_CONTROL_SHADER");
+ } else if( type == _tess_eval ) {
+ AddShaderDefinition(&text, "TESSELATION_EVALUATOR_SHADER");
+ }
+
+ for(unsigned i=0; i<definitions.size(); ++i){
+ AddShaderDefinition(&text, definitions[i]);
+ }
+ if (GLEW_ARB_sample_shading) {
+ AddShaderDefinition(&text, "ARB_sample_shading_available");
+ }
+ if(GLEW_ARB_shader_texture_lod && type == _fragment){
+ text.insert(0,"#extension GL_ARB_shader_texture_lod : enable\n");
+ }
+
+ // This must be the last directive added to make sure that #version is at the beginning of the file
+ if(version == -1){
+ version = 130;
+ }
+ {
+ static const int kBufSize = 256;
+ char buf[kBufSize];
+ FormatString(buf, kBufSize, "#version %d\n", version);
+ text.insert(0,buf);
+ }
+
+ if(blend_mode == _BM_ADD){
+ shader->blend_src = GL_SRC_ALPHA;
+ shader->blend_dst = GL_ONE;
+ } else if(blend_mode == _BM_MULTIPLY){
+ shader->blend_src = GL_DST_COLOR;
+ shader->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
+ } else {
+ shader->blend_src = GL_SRC_ALPHA;
+ shader->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
+ }
+}
+} //namespace ""
+
+void Shaders::ReloadShader(int which, ShaderType type) {
+ if( which >= 0 )
+ {
+ Shader &shader = shaders[which];
+ if(shader.gl_shader != UNLOADED_SHADER_ID) {
+ glDeleteShader(shader.gl_shader);
+ shader.gl_shader = UNLOADED_SHADER_ID;
+ }
+ textFileRead(shader.name, &shader.text);
+ Preprocess(&shader, type, shader.definitions, shader_dir_path);
+ shader.modified = GetDateModifiedInt64(shader.name.c_str());
+ }
+}
+
+void Shaders::Reload(bool force) {
+ CHECK_GL_ERROR();
+ for(unsigned i=0; i<shaders.size(); ++i){
+ Shader &shader = shaders[i];
+ bool modified = (shader.modified != GetDateModifiedInt64(shader.name.c_str()));
+ if(!modified) { // Check included files to see if they were modified
+ for(unsigned j=0; j<shader.include_files.size(); j++){
+ modified = (shader.include_modified[j] != GetDateModifiedInt64(shader.include_files[j].c_str()));
+ if(modified) {
+ break;
+ }
+ }
+ }
+ if(modified || force) {
+ ReloadShader(i, shader.type);
+ }
+ }
+ unsigned num_programs = programs.size();
+ for(unsigned i=0; i<num_programs; ++i){
+ Program& program = programs[i];
+ if(program.gl_program != UNLOADED_SHADER_ID) {
+ bool unloaded = false;
+ for(int j=0; j<kMaxShaderTypes; ++j){
+ if(program.shader_ids[j] >= 0 && shaders[program.shader_ids[j]].gl_shader == UNLOADED_SHADER_ID){
+ unloaded = true;
+ break;
+ }
+ }
+ if(unloaded){
+ glDeleteProgram(program.gl_program);
+ program.gl_program = UNLOADED_SHADER_ID;
+ }
+ }
+ }
+ CHECK_GL_ERROR();
+}
+
+//Return the id of a shader
+int Shaders::returnShader(const char *path, ShaderType type, const std::vector<std::string> &definitions) {
+ std::string full_path;
+ full_path = path;
+ for(unsigned i=0; i<definitions.size(); ++i){
+ full_path += " #" + definitions[i];
+ }
+
+ for (unsigned i=0; i<shaders.size(); ++i){
+ if(full_path == shaders[i].full_name){
+ return i;
+ }
+ }
+
+ static const int kBufSize = 512;
+ char abs_path[kBufSize];
+ if(FindFilePath(path, abs_path, kBufSize, kDataPaths | kModPaths) == -1) {
+ FatalError("Error", "Could not find path for shader: %s", path);
+ }
+
+ shaders.push_back(Shader());
+ Shader& shader = shaders.back();
+ shader.name = abs_path;
+ shader.full_name = full_path;
+ shader.gl_shader = UNLOADED_SHADER_ID;
+ shader.definitions = definitions;
+ textFileRead(shader.name, &shader.text);
+ Preprocess(&shader, type, definitions, shader_dir_path);
+ shader.modified = GetDateModifiedInt64(shader.name.c_str());
+ return shaders.size()-1;
+}
+
+bool Shaders::createShader(int which, ShaderType type){
+ CHECK_GL_ERROR();
+ Shader &shader = shaders[which];
+ if (shader.gl_shader != UNLOADED_SHADER_ID) {
+ return true;
+ }
+
+ if (type == _vertex) {
+ shader.gl_shader = glCreateShader(GL_VERTEX_SHADER);
+ } else if (type == _fragment) {
+ shader.gl_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ } else if (type == _geom ) {
+ shader.gl_shader = glCreateShader(GL_GEOMETRY_SHADER);
+ } else if (type == _tess_ctrl ) {
+ shader.gl_shader = glCreateShader(GL_TESS_CONTROL_SHADER);
+ } else if (type == _tess_eval ) {
+ shader.gl_shader = glCreateShader(GL_TESS_EVALUATION_SHADER);
+ }
+ shader.type = type;
+
+ const char* sources[] = {shader.text.c_str()};
+ glShaderSource(shader.gl_shader, 1, sources, NULL);
+ glCompileShader(shader.gl_shader);
+
+ // Output shader source to file
+ if (config["shader_debug"].toNumber<bool>()) {
+ size_t last_slash = shader.full_name.rfind('/');
+ if (last_slash == std::string::npos) {
+ last_slash = shader.full_name.rfind('\\');
+ }
+ size_t hashpos = shader.full_name.find('#');
+ std::string shader_name;
+
+ if (hashpos != std::string::npos) {
+ // If shader string contains '#' move type identifier to end
+ int hashlen = shader.full_name.length() - hashpos;
+ shader_name = shader.full_name.substr(last_slash + 1, shader.full_name.length() - last_slash - 7 - hashlen);
+ shader_name += shader.full_name.substr(hashpos, hashlen);
+ switch (shader.type) {
+ case _vertex:
+ shader_name += ".vert";
+ break;
+ case _fragment:
+ shader_name += ".frag";
+ break;
+ case _geom:
+ shader_name += ".geom";
+ break;
+ case _tess_eval:
+ shader_name += ".tess_eval";
+ break;
+ case _tess_ctrl:
+ shader_name += ".tess_ctrl";
+ break;
+ case kMaxShaderTypes:
+ LOGE << "Invalid enum value" << std::endl;
+ break;
+ }
+ } else {
+ // Otherwise just remove directory from full name
+ shader_name = shader.full_name.substr(last_slash + 1, shader.full_name.length() - last_slash - 1);
+ }
+ char buf[kPathSize];
+ FormatString(buf, kPathSize, "%sData/CompiledShaders/%s", GetWritePath(CoreGameModID).c_str(), shader_name.c_str());
+ FILE *file = my_fopen(buf, "w");
+ if (file){
+ fwrite(shader.text.c_str(), sizeof(char), shader.text.length(), file);
+ fclose(file);
+ }
+ }
+
+ int status = 0;
+ glGetShaderiv(shader.gl_shader, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ int len = 0;
+ char buffer[1025] = {0};
+ glGetShaderInfoLog(shader.gl_shader, 1024, &len, buffer);
+ if(len != 0){
+ char errorbuffer[512];
+ int last_slash = 0;
+ for(int i=0, len=shader.full_name.size(); i<len; ++i){
+ char chr = shader.full_name[i];
+ if(chr == '/' || chr == '\\'){
+ last_slash = i+1;
+ }
+ }
+ FormatString(errorbuffer, 512, "Error(s) in %s",&(shader.full_name.c_str()[last_slash]));
+ if (status == 0 || config["shader_debug"].toNumber<bool>()) {
+ const int kBufSize = 2048;
+ char err_buf[kBufSize];
+ FormatString(err_buf, kBufSize, "%s\n%s", &(shader.full_name.c_str()[last_slash]), buffer);
+ // fatal error or shader debugging requested, show to user
+ ErrorResponse response = DisplayError(errorbuffer, err_buf, _ok_cancel_retry);
+ if( response == _retry) {
+ ReloadShader(which, type);
+ return createShader(which, type);
+ }
+ } else {
+ // only log it
+ LOGE << errorbuffer << " " << buffer << std::endl;
+ }
+ }
+
+ if (type == _geom || type == _tess_eval || type == _tess_ctrl) {
+ glDeleteShader(shader.gl_shader);
+ shader.gl_shader = UNLOADED_SHADER_ID;
+ return false;
+ }
+ }
+ CHECK_GL_ERROR();
+
+ return true;
+}
+
+//Return the id of a shader variable
+GLint Shaders::returnShaderVariable(const std::string &name, int which){
+ Program::UniformAddressCacheMap::iterator iter =
+ programs[which].uniform_address_cache.find(name);
+ if(iter == programs[which].uniform_address_cache.end()) {
+ GLint addr = glGetUniformLocation(programs[which].gl_program,name.c_str());
+ programs[which].uniform_address_cache[name] = addr;
+ return addr;
+ } else {
+ return iter->second;
+ }
+}
+
+GLint Shaders::returnShaderVariableIndex(const std::string &name, int program_id){
+ Program::UniformAddressCacheMap::iterator iter =
+ programs[program_id].uniform_address_cache.find(name);
+ if(iter == programs[program_id].uniform_address_cache.end()) {
+ GLuint index;
+ const char* name_cstr = name.c_str();
+ glGetUniformIndices(programs[program_id].gl_program, 1, &name_cstr, &index);
+ programs[program_id].uniform_address_cache[name] = index;
+ return index;
+ } else {
+ return iter->second;
+ }
+}
+
+GLint Shaders::returnShaderVariableOffset(int uniform_index, int program_id){
+ Program::UniformOffsetCacheMap::iterator iter =
+ programs[program_id].uniform_offset_cache.find(uniform_index);
+ if(iter == programs[program_id].uniform_offset_cache.end()) {
+ GLuint index = uniform_index;
+ GLint offset;
+ glGetActiveUniformsiv(programs[program_id].gl_program, 1, &index, GL_UNIFORM_OFFSET, &offset);
+ programs[program_id].uniform_offset_cache[uniform_index] = offset;
+ return offset;
+ } else {
+ return iter->second;
+ }
+}
+
+GLint Shaders::returnShaderBlockSize(int block_index, int program_id){
+ Program::UniformOffsetCacheMap::iterator iter =
+ programs[program_id].uniform_offset_cache.find(-block_index-1);
+ if(iter == programs[program_id].uniform_offset_cache.end()) {
+ GLint block_size;
+ glGetActiveUniformBlockiv(programs[program_id].gl_program, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &block_size);
+ programs[program_id].uniform_offset_cache[-block_index-1] = block_size;
+ return block_size;
+ } else {
+ return iter->second;
+ }
+}
+
+GLint Shaders::returnShaderAttrib(const std::string &name, int which){
+ Program::AttribAddressCacheMap::iterator iter =
+ programs[which].attrib_address_cache.find(name);
+ if(iter == programs[which].attrib_address_cache.end()) {
+ GLint addr = glGetAttribLocation(programs[which].gl_program,name.c_str());
+ programs[which].attrib_address_cache[name] = addr;
+ return addr;
+ } else {
+ return iter->second;
+ }
+}
+
+
+int Shaders::returnProgram(std::string name, OptionalShaders optional_shaders) {
+ std::string short_name;
+ if(name.find(' ') != std::string::npos){
+ short_name = name.substr(0, name.find(' '));
+ } else {
+ short_name = name;
+ }
+
+ unsigned num_programs = programs.size();
+ for(unsigned i=0; i<num_programs; ++i) {
+ if(name == programs[i].name){
+ return i;
+ }
+ }
+
+ PROFILER_ZONE(g_profiler_ctx, "Assemble shader program");
+
+ programs.push_back(Program());
+ Program& program = programs.back();
+
+ program.name = name;
+ program.gl_program = UNLOADED_SHADER_ID;
+
+ std::vector<std::string> definitions;
+ size_t name_len = name.length()+1;
+ int def_start = -1;
+ const int min_definition_length = 3; // Must be at least 3 letters long
+
+ for(size_t i=0; i<name_len; ++i){
+ switch(name[i]){
+ case '#':
+ def_start = i+1;
+ break;
+ case ' ':
+ case '\0':
+ if(def_start != -1 && def_start < static_cast<int>(i) - (min_definition_length - 1)) {
+ definitions.push_back(
+ std::string(&name[def_start], i-def_start));
+ }
+ def_start = -1;
+ break;
+ }
+ }
+
+ std::string vertex_path = GetShaderPath(short_name, shader_dir_path,_vertex);
+ std::string fragment_path = GetShaderPath(short_name, shader_dir_path,_fragment);
+ std::string geom_path = GetShaderPath(short_name, shader_dir_path,_geom);
+ std::string tess_eval_path = GetShaderPath(short_name, shader_dir_path,_tess_eval);
+ std::string tess_ctrl_path = GetShaderPath(short_name, shader_dir_path,_tess_ctrl);
+
+ if(optional_shaders & kGeometry && FileExists( geom_path.c_str(), kDataPaths | kModPaths) ) {
+ definitions.push_back("HAS_GEOM");
+ }
+
+ if(optional_shaders & kTesselation){
+ if( FileExists( tess_eval_path.c_str(), kDataPaths | kModPaths)) {
+ definitions.push_back("HAS_TESS_EVAL");
+ }
+
+ if( FileExists( tess_ctrl_path.c_str(), kDataPaths | kModPaths)) {
+ definitions.push_back("HAS_TESS_CTRL");
+ }
+ }
+
+ for(int i=0; i<kMaxShaderTypes; ++i){
+ program.shader_ids[i] = MISSING_GEOM_SHADER_ID;
+ }
+
+ program.shader_ids[_vertex] = returnShader(vertex_path.c_str(), _vertex, definitions);
+ program.shader_ids[_fragment] = returnShader(fragment_path.c_str(), _fragment, definitions);
+
+ if(optional_shaders & kGeometry && FileExists( geom_path.c_str(), kDataPaths | kModPaths) ) {
+ program.shader_ids[_geom] = returnShader(geom_path.c_str(), _geom, definitions);
+ }
+
+ if(optional_shaders & kTesselation){
+ if( FileExists( tess_eval_path.c_str(), kDataPaths | kModPaths)) {
+ program.shader_ids[_tess_eval] = returnShader(tess_eval_path.c_str(), _tess_eval, definitions);
+ }
+
+ if( FileExists( tess_ctrl_path.c_str(), kDataPaths | kModPaths)) {
+ program.shader_ids[_tess_ctrl] = returnShader(tess_ctrl_path.c_str(), _tess_ctrl, definitions);
+ }
+ }
+
+ return programs.size()-1;
+}
+
+bool Shaders::IsProgramTransparent (int which_program) {
+ return shaders[programs[which_program].shader_ids[_fragment]].transparent;
+}
+
+bool Shaders::DoesProgramUseTangent (int which_program) {
+ return shaders[programs[which_program].shader_ids[_fragment]].use_tangent ||
+ shaders[programs[which_program].shader_ids[_vertex]].use_tangent;
+}
+
+
+void Shaders::createProgram(int which_program) {
+ PROFILER_ZONE(g_profiler_ctx, "Shaders::createProgram");
+ CHECK_GL_ERROR();
+ Program& program = programs[which_program];
+ if(program.gl_program != UNLOADED_SHADER_ID){
+ return;
+ }
+ program.gl_program = glCreateProgram();
+ createShader(program.shader_ids[_vertex], _vertex);
+ for(int i=_geom; i<kMaxShaderTypes; ++i){
+ if( program.shader_ids[i] >= 0 ) {
+ bool success = createShader(program.shader_ids[i], (ShaderType)i );
+ // FIXME: this is a really bad hack
+ if (!success) {
+ program.shader_ids[i] = MISSING_GEOM_SHADER_ID;
+ }
+ }
+ }
+ createShader(program.shader_ids[_fragment], _fragment);
+
+ //So, the specification says that if the out refered to in the shader
+ //doesn't exist, this call should be ignored, but the driver on MacOSX
+ //for iris doesn't. Which causes a large amount of warnings to be generated
+ //So far only envobject has two frag outputs, so the quick simple solution it to
+ //check for envobject shader
+ //Is this a good argument for using .xml files to define shader programs?
+ //Or should we rather use a pragma in the shader to define this value?
+
+ Shader* frag_shader = &shaders[program.shader_ids[_fragment]];
+ if( frag_shader->depth_only == false ) {
+ if( frag_shader->bind_out_color ) {
+ glBindFragDataLocation(program.gl_program, 0, "out_color");
+ LOGI << "Binding out color for " << frag_shader->name << std::endl;
+ }
+
+ if( frag_shader->particle == false ) {
+ if( frag_shader->bind_out_vel ) {
+ glBindFragDataLocation(program.gl_program, 1, "out_vel");
+ LOGI << "Binding out vel for " << frag_shader->name << std::endl;
+ }
+ }
+ }
+
+ glAttachShader(program.gl_program,shaders[program.shader_ids[_vertex]].gl_shader);
+ for(int i=_geom; i<kMaxShaderTypes; ++i){
+ if( program.shader_ids[i] >= 0 ) {
+ glAttachShader(program.gl_program,shaders[program.shader_ids[(ShaderType)i]].gl_shader);
+ }
+ }
+ glAttachShader(program.gl_program,shaders[program.shader_ids[_fragment]].gl_shader);
+ glLinkProgram(program.gl_program);
+
+ int status = 0;
+ glGetProgramiv(program.gl_program, GL_LINK_STATUS, &status);
+#ifndef SHADER_WARNINGS
+ if (!status) {
+#endif
+ int len = 0;
+ char buffer[1025] = {0};
+ glGetProgramInfoLog(program.gl_program, 1024, &len, buffer);
+ if(len != 0){
+ char errorbuffer[512];
+ FormatString(errorbuffer, 512,"Error(s) linking program \"%s\"",program.name.c_str());
+ if (status == 0 || config["shader_debug"].toNumber<bool>()) {
+ // fatal error or shader debugging requested, show to user
+ ErrorResponse response = DisplayError(errorbuffer, buffer,_ok_cancel_retry);
+
+ if( response == _retry) {
+ ReloadShader(program.shader_ids[_vertex], _vertex);
+ ReloadShader(program.shader_ids[_geom], _geom);
+ ReloadShader(program.shader_ids[_fragment], _fragment);
+
+ glDeleteProgram(program.gl_program);
+ program.gl_program = UNLOADED_SHADER_ID;
+
+ createProgram(which_program);
+ return;
+ }
+ } else {
+ // only log it
+ LOGE << errorbuffer << " " << buffer << std::endl;
+ }
+ }
+#ifndef SHADER_WARNINGS
+ }
+#endif
+
+ if (config["shader_debug"].toNumber<bool>()) {
+ // Log some uniform data from shader
+ LOGI << "Shader: " << program.name << std::endl;
+ GLint num_raw_uniforms = 0;
+ glGetProgramiv(program.gl_program, GL_ACTIVE_UNIFORMS, &num_raw_uniforms);
+ LOGI << "Active uniforms: " << num_raw_uniforms << std::endl;
+
+ GLint max_name_uni_len = 0;
+ glGetProgramiv(program.gl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_uni_len);
+ LOGI << "Uniform name max length: " << max_name_uni_len << std::endl;
+
+ std::vector<char> name_buf(max_name_uni_len + 1, '\0');
+
+ LOGI << "Uniforms:" << std::endl;
+ for (int i = 0; i < num_raw_uniforms; i++) {
+ GLsizei real_len = 0;
+ GLint attr_size = 0;
+ GLenum gltype = GL_NONE;
+ glGetActiveUniform(program.gl_program, i, max_name_uni_len, &real_len, &attr_size, &gltype, &name_buf[0]);
+ LOGI << "Uniform #" << i << " " << &name_buf[0] << " length " << real_len << ", size " << attr_size << " type " << gltype << std::endl;
+ }
+ }
+
+ int active_attribs = 0;
+ glGetProgramiv(program.gl_program, GL_ACTIVE_ATTRIBUTES, &active_attribs);
+ LOGD << program.name << " attribs: " << active_attribs << std::endl;
+
+ CHECK_GL_ERROR();
+
+ program.tex_uniform.resize(32);
+ program.tex_uniform[0] = glGetUniformLocation(program.gl_program,"tex0");
+ program.tex_uniform[1] = glGetUniformLocation(program.gl_program,"tex1");
+ program.tex_uniform[2] = glGetUniformLocation(program.gl_program,"tex2");
+ program.tex_uniform[3] = glGetUniformLocation(program.gl_program,"tex3");
+ program.tex_uniform[4] = glGetUniformLocation(program.gl_program,"tex4");
+ program.tex_uniform[5] = glGetUniformLocation(program.gl_program,"tex5");
+ program.tex_uniform[6] = glGetUniformLocation(program.gl_program,"tex6");
+ program.tex_uniform[7] = glGetUniformLocation(program.gl_program,"tex7");
+ program.tex_uniform[8] = glGetUniformLocation(program.gl_program,"tex8");
+ program.tex_uniform[9] = glGetUniformLocation(program.gl_program,"tex9");
+ program.tex_uniform[10] = glGetUniformLocation(program.gl_program,"tex10");
+ program.tex_uniform[11] = glGetUniformLocation(program.gl_program,"tex11");
+ program.tex_uniform[12] = glGetUniformLocation(program.gl_program,"tex12");
+ program.tex_uniform[13] = glGetUniformLocation(program.gl_program,"tex13");
+ program.tex_uniform[14] = glGetUniformLocation(program.gl_program,"tex14");
+ program.tex_uniform[15] = glGetUniformLocation(program.gl_program,"tex15");
+ program.tex_uniform[16] = glGetUniformLocation(program.gl_program,"tex16");
+ program.tex_uniform[17] = glGetUniformLocation(program.gl_program,"tex17");
+ program.tex_uniform[18] = glGetUniformLocation(program.gl_program,"tex18");
+ program.tex_uniform[19] = glGetUniformLocation(program.gl_program,"tex19");
+ program.tex_uniform[20] = glGetUniformLocation(program.gl_program,"tex20");
+ program.tex_uniform[21] = glGetUniformLocation(program.gl_program,"tex21");
+ program.tex_uniform[22] = glGetUniformLocation(program.gl_program,"tex22");
+ program.tex_uniform[23] = glGetUniformLocation(program.gl_program,"tex23");
+ program.tex_uniform[24] = glGetUniformLocation(program.gl_program,"tex24");
+ program.tex_uniform[25] = glGetUniformLocation(program.gl_program,"tex25");
+ program.tex_uniform[26] = glGetUniformLocation(program.gl_program,"tex26");
+ program.tex_uniform[27] = glGetUniformLocation(program.gl_program,"tex27");
+ program.tex_uniform[28] = glGetUniformLocation(program.gl_program,"tex28");
+ program.tex_uniform[29] = glGetUniformLocation(program.gl_program,"tex29");
+ program.tex_uniform[30] = glGetUniformLocation(program.gl_program,"tex30");
+ program.tex_uniform[31] = glGetUniformLocation(program.gl_program,"tex31");
+ program.attrib_address_cache.clear();
+ program.uniform_address_cache.clear();
+ program.uniform_value_cache.clear();
+ program.uniform_offset_cache.clear();
+
+ program.commonShaderUniforms.resize(MAX_COMMON_UNIFORMS);
+ program.commonShaderUniforms[NORMAL_MATRIX] = glGetUniformLocation(program.gl_program,"normalMatrix");
+ program.commonShaderUniforms[MODEL_MATRIX] = glGetUniformLocation(program.gl_program,"modelMatrix");
+
+ for(unsigned i=0; i<32; ++i){
+ if(program.tex_uniform[i] > 1000){
+ std::ostringstream oss;
+ oss << "Program \"" << program.name <<
+ "\" tex uniform " << i << " is set to " <<
+ program.tex_uniform[i] << " which is outside" <<
+ " the normal range";
+ LOGE << oss.str().c_str() << std::endl;
+ }
+ }
+
+ GLuint shadow_cascade_idx = glGetUniformBlockIndex(program.gl_program, "ShadowCascades");
+ if (shadow_cascade_idx != GL_INVALID_INDEX) {
+ glUniformBlockBinding(program.gl_program, shadow_cascade_idx, UBO_SHADOW_CASCADES);
+ }
+}
+
+Program *Shaders::GetCurrentProgram()
+{
+ return bound_program == -1 ? 0 : &programs[bound_program];
+}
+
+GLint Shaders::GetTexUniform(int which) {
+ return programs[bound_program].tex_uniform[which];
+}
+
+//Bind a shader program
+void Shaders::setProgram(int which) {
+ if(which == -1){
+ noProgram();
+ return;
+ }
+ if(bound_program==which)return;
+ CHECK_GL_ERROR();
+ if(programs[which].gl_program == UNLOADED_SHADER_ID) {
+ if(create_program_warning) {
+ LOGW << "Loading shader which should be preloaded. " << level_path.GetOriginalPath() << ": " << programs[which].name << std::endl;
+ }
+ createProgram(which);
+ }
+ glUseProgram(programs[which].gl_program);
+ Textures::Instance()->InvalidateBindCache();
+ bound_program=which;
+ CHECK_GL_ERROR();
+}
+
+//Stop using shaders
+void Shaders::noProgram() {
+ if(bound_program == -1)return;
+ glUseProgram(0);
+ bound_program=-1;
+}
+
+void Shaders::SetUniformMat4(const std::string &var_name, const GLfloat* data) {
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformMat4(var_id, data);
+}
+
+void Shaders::SetUniformMat4(GLint var_id, const GLfloat* data) {
+ if (var_id == -1) {
+ return;
+ }
+
+ std::vector<GLfloat> &value = programs[bound_program].uniform_value_cache[var_id];
+
+ bool changed = false;
+ if(value.size()!=16){
+ value.resize(16);
+ changed = true;
+ }
+
+ for(int i=0; i<16; i++){
+ if(value[i] != data[i]){
+ changed = true;
+ value[i] = data[i];
+ }
+ }
+
+ if(changed){
+ glUniformMatrix4fv(var_id, 1, GL_FALSE, data);
+ }
+}
+
+
+void Shaders::SetUniformMat3(const std::string & var_name, const GLfloat* data) {
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformMat3(var_id, data);
+}
+
+void Shaders::SetUniformMat3(GLint var_id, const GLfloat* data) {
+ if (var_id == -1) {
+ return;
+ }
+
+ std::vector<GLfloat> &value = programs[bound_program].uniform_value_cache[var_id];
+
+ bool changed = false;
+ if(value.size()!=9){
+ value.resize(9);
+ changed = true;
+ }
+
+ for(int i=0; i<9; i++){
+ if(value[i] != data[i]){
+ changed = true;
+ value[i] = data[i];
+ }
+ }
+
+ if(changed){
+ glUniformMatrix3fv(var_id, 1, GL_FALSE, data);
+ }
+}
+
+void Shaders::SetUniformVec3( const std::string & var_name, const vec3& data_vec ) {
+ CHECK_GL_ERROR();
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformVec3(var_id, data_vec);
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformVec3( GLint var_id, const vec3& data_vec ) {
+ if (var_id == -1) {
+ return;
+ }
+
+ CHECK_GL_ERROR();
+ GLfloat *data = (GLfloat*)&data_vec;
+ SetUniformVec3(var_id, data);
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformVec3( GLint var_id, const GLfloat* data ) {
+ if (var_id == -1) {
+ return;
+ }
+
+ CHECK_GL_ERROR();
+ std::vector<GLfloat> &value = programs[bound_program].uniform_value_cache[var_id];
+
+ bool changed = false;
+ if(value.size()!=3){
+ value.resize(3);
+ changed = true;
+ }
+
+ for(int i=0; i<3; i++){
+ if(value[i] != data[i]){
+ changed = true;
+ value[i] = data[i];
+ }
+ }
+
+ if(changed){
+ glUniform3fv(var_id, 1, data);
+ }
+ CHECK_GL_ERROR();
+}
+
+
+void Shaders::SetUniformVec2( const std::string & var_name, const vec2& data_vec ) {
+ CHECK_GL_ERROR();
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformVec2(var_id, data_vec);
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformVec2( GLint var_id, const vec2& data_vec ) {
+ if (var_id == -1) {
+ return;
+ }
+
+ CHECK_GL_ERROR();
+ GLfloat *data = (GLfloat*)&data_vec;
+ SetUniformVec2(var_id, data);
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformVec2( GLint var_id, const GLfloat* data ) {
+ if (var_id == -1) {
+ return;
+ }
+
+ CHECK_GL_ERROR();
+ std::vector<GLfloat> &value = programs[bound_program].uniform_value_cache[var_id];
+
+ bool changed = false;
+ if(value.size()!=2){
+ value.resize(2);
+ changed = true;
+ }
+
+ for(int i=0; i<2; i++){
+ if(value[i] != data[i]){
+ changed = true;
+ value[i] = data[i];
+ }
+ }
+
+ if(changed){
+ glUniform2fv(var_id, 1, data);
+ }
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformFloat( const std::string &var_name, const float& data ) {
+ CHECK_GL_ERROR();
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformFloat(var_id, data);
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformFloat( GLint var_id, const float& data) {
+ if (var_id == -1) {
+ return;
+ }
+
+ std::vector<GLfloat> &value = programs[bound_program].uniform_value_cache[var_id];
+
+ bool changed = false;
+ if(value.size()!=1){
+ value.resize(1);
+ changed = true;
+ }
+
+ if(value[0] != data){
+ changed = true;
+ value[0] = data;
+ }
+
+ if(changed){
+ glUniform1f(var_id, data);
+ }
+}
+
+void Shaders::SetUniformInt( const std::string &var_name, const int& data, ForceFlag force_flag ) {
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformInt(var_id, data, force_flag);
+}
+
+void Shaders::SetUniformInt( GLint var_id, const int& data, ForceFlag force_flag) {
+ if (var_id == -1) {
+ return;
+ }
+
+ std::vector<GLfloat> &value = programs[bound_program].uniform_value_cache[var_id];
+
+ bool changed = false;
+ if(value.size()!=1){
+ value.resize(1);
+ changed = true;
+ }
+
+ if(value[0] != (GLfloat)data){
+ changed = true;
+ value[0] = (GLfloat)data;
+ }
+
+ if(changed || force_flag == kForce){
+ glUniform1i(var_id, data);
+ }
+}
+
+int Shaders::GetProgramBlendSrc( int which_program ) {
+ return shaders[programs[which_program].shader_ids[_fragment]].blend_src;
+}
+
+int Shaders::GetProgramBlendDst( int which_program ) {
+ return shaders[programs[which_program].shader_ids[_fragment]].blend_dst;
+}
+
+void Shaders::SetUniformMat4Array( const std::string &var_name, const std::vector<mat4> &transforms )
+{
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformMat4Array(var_id, transforms);
+}
+
+void Shaders::SetUniformMat4Array(GLint var_id, const std::vector<mat4> &transforms) {
+ if (var_id == -1) {
+ return;
+ }
+
+ glUniformMatrix4fv(var_id, transforms.size(), GL_FALSE, &transforms[0].entries[0]);
+}
+
+void Shaders::SetUniformVec4Array( const std::string &var_name, const std::vector<vec4> &val )
+{
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformVec4Array(var_id, val);
+}
+
+void Shaders::SetUniformVec4Array(GLint var_id, const std::vector<vec4> &val) {
+ if (var_id == -1) {
+ return;
+ }
+
+ glUniform4fv(var_id, val.size(), &val[0].entries[0]);
+}
+
+void Shaders::SetUniformVec3Array( const std::string &var_name, const std::vector<vec3> &val ) {
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformVec3Array(var_id, val);
+}
+
+void Shaders::SetUniformVec3Array( GLint var_id, const std::vector<vec3> &val )
+{
+ if (var_id == -1) {
+ return;
+ }
+
+ glUniform3fv(var_id, val.size(), &val[0].entries[0]);
+}
+
+void Shaders::SetUniformVec3Array(GLint var_id, const vec3* val, int size) {
+ if (var_id == -1) {
+ return;
+ }
+
+ glUniform3fv(var_id, size, (const GLfloat*)val);
+}
+
+void Shaders::ResetVRAM()
+{
+ for(unsigned i=0; i<shaders.size(); ++i){
+ shaders[i].gl_shader = UNLOADED_SHADER_ID;
+ }
+ for(unsigned i=0; i<programs.size(); ++i){
+ programs[i].gl_program = UNLOADED_SHADER_ID;
+ programs[i].attrib_address_cache.clear();
+ programs[i].uniform_address_cache.clear();
+ programs[i].uniform_value_cache.clear();
+ programs[i].uniform_offset_cache.clear();
+ }
+}
+
+int Shaders::GetUBOBindIndex(int shader_id, const char* name) {
+ LOG_ASSERT(shader_id >= 0 && shader_id < (int)programs.size());
+ Program* program = &programs[shader_id];
+ LOG_ASSERT(program->gl_program != UNLOADED_SHADER_ID);
+ Program::UniformAddressCacheMap::iterator iter =
+ program->uniform_address_cache.find(name);
+ if(iter == program->uniform_address_cache.end()) {
+ int programHandle = program->gl_program;
+ GLuint blockIndex = glGetUniformBlockIndex(programHandle, name);
+ program->uniform_address_cache[name] = blockIndex;
+ return blockIndex;
+ } else {
+ return iter->second;
+ }
+}
+
+void Shaders::SetUniformVec4( const std::string & var_name, const vec4& data_vec ) {
+ CHECK_GL_ERROR();
+ GLint var_id = returnShaderVariable(var_name,bound_program);
+ SetUniformVec4(var_id, data_vec);
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformVec4( GLint var_id, const vec4& data_vec ) {
+ if (var_id == -1) {
+ return;
+ }
+
+ CHECK_GL_ERROR();
+ GLfloat *data = (GLfloat*)&data_vec;
+ SetUniformVec4(var_id, data);
+ CHECK_GL_ERROR();
+}
+
+void Shaders::SetUniformVec4( GLint var_id, const GLfloat* data ) {
+ if (var_id == -1) {
+ return;
+ }
+
+ if (data == NULL) {
+ LOGE << "Data pointer is null." << std::endl;
+ return;
+ }
+
+ CHECK_GL_ERROR();
+ std::vector<GLfloat> &value = programs[bound_program].uniform_value_cache[var_id];
+
+ bool changed = false;
+ if(value.size()!=4){
+ value.resize(4);
+ changed = true;
+ }
+
+ for(int i=0; i<4; i++){
+ if(value[i] != data[i]){
+ changed = true;
+ value[i] = data[i];
+ }
+ }
+
+ if(changed){
+ glUniform4fv(var_id, 1, data);
+ }
+ CHECK_GL_ERROR();
+}
+
+std::string GetShaderPath( const std::string &shader_name, const std::string& shader_dir_path, ShaderType type ) {
+ switch(type){
+ case _vertex: return shader_dir_path+shader_name+".vert";
+ case _fragment: return shader_dir_path+shader_name+".frag";
+ case _geom: return shader_dir_path+shader_name+".geom";
+ case _tess_eval: return shader_dir_path+shader_name+".tess_eval";
+ case _tess_ctrl: return shader_dir_path+shader_name+".tess_ctrl";
+ default: return "";
+ }
+}
+
+void ShaderUniform::Submit()
+{
+ switch(type) {
+ case ShaderUniformType::UNKNOWN:
+ case ShaderUniformType::SAMPLER2D:
+ case ShaderUniformType::SAMPLER3D:
+ case ShaderUniformType::SAMPLERCUBE:
+ break;
+ case ShaderUniformType::INT:
+ glUniform1fv(address, 1, cachedValue);
+ break;
+ case ShaderUniformType::FLOAT:
+ glUniform1fv(address, 1, cachedValue);
+ break;
+ case ShaderUniformType::VEC2:
+ glUniform2fv(address, 1, cachedValue);
+ break;
+ case ShaderUniformType::VEC3:
+ glUniform3fv(address, 1, cachedValue);
+ break;
+ case ShaderUniformType::VEC4:
+ glUniform4fv(address, 1, cachedValue);
+ break;
+ case ShaderUniformType::MAT3:
+ glUniformMatrix3fv(address, 1, GL_FALSE, cachedValue);
+ break;
+ case ShaderUniformType::MAT4:
+ glUniformMatrix4fv(address, 1, GL_FALSE, cachedValue);
+ break;
+ default:
+ break;
+ }
+}