2013-07-06 10:08:52 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011-2013 Gregory hainaut
|
|
|
|
* Copyright (C) 2007-2009 Gabest
|
|
|
|
*
|
|
|
|
* This Program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* 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 for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with GNU Make; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "GSShaderOGL.h"
|
2013-08-05 20:25:25 +00:00
|
|
|
#include "GLState.h"
|
2013-07-06 10:08:52 +00:00
|
|
|
|
2016-04-24 08:54:39 +00:00
|
|
|
#include "res/glsl_source.h"
|
|
|
|
|
2014-02-01 11:11:14 +00:00
|
|
|
GSShaderOGL::GSShaderOGL(bool debug) :
|
2015-09-11 10:19:49 +00:00
|
|
|
m_pipeline(0),
|
2015-07-17 16:16:35 +00:00
|
|
|
m_debug_shader(debug)
|
2013-07-06 10:08:52 +00:00
|
|
|
{
|
2016-04-10 15:05:33 +00:00
|
|
|
// Create a default pipeline
|
|
|
|
m_pipeline = LinkPipeline(0, 0, 0);
|
|
|
|
BindPipeline(m_pipeline);
|
2013-07-06 10:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GSShaderOGL::~GSShaderOGL()
|
|
|
|
{
|
2016-04-10 15:05:33 +00:00
|
|
|
for (auto p : m_prog_to_delete) glDeleteProgram(p);
|
|
|
|
glDeleteProgramPipelines(m_pipe_to_delete.size(), &m_pipe_to_delete[0]);
|
2013-07-06 10:08:52 +00:00
|
|
|
}
|
|
|
|
|
2016-04-10 15:05:33 +00:00
|
|
|
GLuint GSShaderOGL::LinkPipeline(GLuint vs, GLuint gs, GLuint ps)
|
|
|
|
{
|
|
|
|
GLuint p;
|
|
|
|
glCreateProgramPipelines(1, &p);
|
|
|
|
glUseProgramStages(p, GL_VERTEX_SHADER_BIT, vs);
|
|
|
|
glUseProgramStages(p, GL_GEOMETRY_SHADER_BIT, gs);
|
|
|
|
glUseProgramStages(p, GL_FRAGMENT_SHADER_BIT, ps);
|
|
|
|
|
|
|
|
m_pipe_to_delete.push_back(p);
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GSShaderOGL::BindPipeline(GLuint vs, GLuint gs, GLuint ps)
|
2013-07-06 10:08:52 +00:00
|
|
|
{
|
2016-04-10 15:28:05 +00:00
|
|
|
BindPipeline(m_pipeline);
|
2016-04-10 12:14:30 +00:00
|
|
|
if (GLState::vs != vs)
|
2013-07-06 10:08:52 +00:00
|
|
|
{
|
2016-04-10 12:14:30 +00:00
|
|
|
GLState::vs = vs;
|
|
|
|
glUseProgramStages(m_pipeline, GL_VERTEX_SHADER_BIT, vs);
|
|
|
|
}
|
|
|
|
if (GLState::gs != gs)
|
|
|
|
{
|
|
|
|
GLState::gs = gs;
|
|
|
|
glUseProgramStages(m_pipeline, GL_GEOMETRY_SHADER_BIT, gs);
|
2013-07-06 10:08:52 +00:00
|
|
|
}
|
2015-05-06 17:08:40 +00:00
|
|
|
#ifdef _DEBUG
|
|
|
|
if (true)
|
|
|
|
#else
|
2016-04-10 12:14:30 +00:00
|
|
|
if (GLState::ps != ps)
|
2015-05-06 17:08:40 +00:00
|
|
|
#endif
|
2013-07-06 10:08:52 +00:00
|
|
|
{
|
2015-04-03 18:28:44 +00:00
|
|
|
// In debug always sets the program. It allow to replace the program in apitrace easily.
|
2016-04-10 12:14:30 +00:00
|
|
|
GLState::ps = ps;
|
|
|
|
glUseProgramStages(m_pipeline, GL_FRAGMENT_SHADER_BIT, ps);
|
2013-07-06 10:08:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-10 15:05:33 +00:00
|
|
|
void GSShaderOGL::BindPipeline(GLuint pipe)
|
|
|
|
{
|
|
|
|
if (GLState::pipeline != pipe) {
|
|
|
|
GLState::pipeline = pipe;
|
|
|
|
glBindProgramPipeline(pipe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-06 10:08:52 +00:00
|
|
|
bool GSShaderOGL::ValidateProgram(GLuint p)
|
|
|
|
{
|
|
|
|
if (!m_debug_shader) return true;
|
|
|
|
|
2014-11-09 14:27:24 +00:00
|
|
|
GLint status = 0;
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramiv(p, GL_LINK_STATUS, &status);
|
2013-07-06 10:08:52 +00:00
|
|
|
if (status) return true;
|
|
|
|
|
|
|
|
GLint log_length = 0;
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramiv(p, GL_INFO_LOG_LENGTH, &log_length);
|
2013-07-06 10:08:52 +00:00
|
|
|
if (log_length > 0) {
|
|
|
|
char* log = new char[log_length];
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramInfoLog(p, log_length, NULL, log);
|
2013-07-06 10:08:52 +00:00
|
|
|
fprintf(stderr, "%s", log);
|
|
|
|
delete[] log;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GSShaderOGL::ValidatePipeline(GLuint p)
|
|
|
|
{
|
|
|
|
if (!m_debug_shader) return true;
|
|
|
|
|
|
|
|
// FIXME: might be mandatory to validate the pipeline
|
2015-10-17 15:05:15 +00:00
|
|
|
glValidateProgramPipeline(p);
|
2013-07-06 10:08:52 +00:00
|
|
|
|
2014-11-09 14:27:24 +00:00
|
|
|
GLint status = 0;
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramPipelineiv(p, GL_VALIDATE_STATUS, &status);
|
2013-07-06 10:08:52 +00:00
|
|
|
if (status) return true;
|
|
|
|
|
|
|
|
GLint log_length = 0;
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramPipelineiv(p, GL_INFO_LOG_LENGTH, &log_length);
|
2013-07-06 10:08:52 +00:00
|
|
|
if (log_length > 0) {
|
|
|
|
char* log = new char[log_length];
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramPipelineInfoLog(p, log_length, NULL, log);
|
2013-07-06 10:08:52 +00:00
|
|
|
fprintf(stderr, "%s", log);
|
|
|
|
delete[] log;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-07-07 16:13:11 +00:00
|
|
|
std::string GSShaderOGL::GenGlslHeader(const std::string& entry, GLenum type, const std::string& macro)
|
2013-07-06 10:08:52 +00:00
|
|
|
{
|
2013-07-07 16:13:11 +00:00
|
|
|
std::string header;
|
2014-09-30 20:04:42 +00:00
|
|
|
header = "#version 330 core\n";
|
2014-10-01 07:50:59 +00:00
|
|
|
// Need GL version 420
|
|
|
|
header += "#extension GL_ARB_shading_language_420pack: require\n";
|
2016-04-07 20:11:35 +00:00
|
|
|
// Need GL version 410
|
|
|
|
header += "#extension GL_ARB_separate_shader_objects: require\n";
|
2013-10-24 20:54:27 +00:00
|
|
|
if (GLLoader::found_GL_ARB_shader_image_load_store) {
|
2013-07-28 14:40:43 +00:00
|
|
|
// Need GL version 420
|
|
|
|
header += "#extension GL_ARB_shader_image_load_store: require\n";
|
2013-10-24 20:54:27 +00:00
|
|
|
} else {
|
2013-07-28 14:40:43 +00:00
|
|
|
header += "#define DISABLE_GL42_image\n";
|
2013-10-24 20:54:27 +00:00
|
|
|
}
|
2013-07-12 21:12:34 +00:00
|
|
|
|
2014-01-11 15:01:13 +00:00
|
|
|
// Stupid GL implementation (can't use GL_ES)
|
|
|
|
// AMD/nvidia define it to 0
|
|
|
|
// intel window don't define it
|
|
|
|
// intel linux refuse to define it
|
|
|
|
header += "#define pGL_ES 0\n";
|
2013-12-30 14:48:26 +00:00
|
|
|
|
2013-07-06 10:08:52 +00:00
|
|
|
// Allow to puts several shader in 1 files
|
|
|
|
switch (type) {
|
|
|
|
case GL_VERTEX_SHADER:
|
2013-07-07 16:13:11 +00:00
|
|
|
header += "#define VERTEX_SHADER 1\n";
|
2013-07-06 10:08:52 +00:00
|
|
|
break;
|
|
|
|
case GL_GEOMETRY_SHADER:
|
2013-07-07 16:13:11 +00:00
|
|
|
header += "#define GEOMETRY_SHADER 1\n";
|
2013-07-06 10:08:52 +00:00
|
|
|
break;
|
|
|
|
case GL_FRAGMENT_SHADER:
|
2013-07-07 16:13:11 +00:00
|
|
|
header += "#define FRAGMENT_SHADER 1\n";
|
2013-07-06 10:08:52 +00:00
|
|
|
break;
|
|
|
|
default: ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select the entry point ie the main function
|
2013-07-07 16:13:11 +00:00
|
|
|
header += format("#define %s main\n", entry.c_str());
|
|
|
|
|
|
|
|
header += macro;
|
|
|
|
|
|
|
|
return header;
|
|
|
|
}
|
2013-07-06 10:08:52 +00:00
|
|
|
|
2013-07-07 16:13:11 +00:00
|
|
|
GLuint GSShaderOGL::Compile(const std::string& glsl_file, const std::string& entry, GLenum type, const char* glsl_h_code, const std::string& macro_sel)
|
|
|
|
{
|
2013-07-19 19:25:50 +00:00
|
|
|
ASSERT(glsl_h_code != NULL);
|
|
|
|
|
2013-07-07 16:13:11 +00:00
|
|
|
GLuint program = 0;
|
|
|
|
|
|
|
|
if (type == GL_GEOMETRY_SHADER && !GLLoader::found_geometry_shader) {
|
|
|
|
return program;
|
|
|
|
}
|
2013-07-06 10:08:52 +00:00
|
|
|
|
|
|
|
// Note it is better to separate header and source file to have the good line number
|
|
|
|
// in the glsl compiler report
|
2016-04-24 08:54:39 +00:00
|
|
|
const int shader_nb = 3;
|
|
|
|
const char* sources[shader_nb];
|
2013-07-06 10:08:52 +00:00
|
|
|
|
2013-07-07 16:13:11 +00:00
|
|
|
std::string header = GenGlslHeader(entry, type, macro_sel);
|
2016-04-24 08:54:39 +00:00
|
|
|
|
2013-07-07 16:13:11 +00:00
|
|
|
sources[0] = header.c_str();
|
2016-04-24 08:54:39 +00:00
|
|
|
sources[1] = common_header_glsl;
|
|
|
|
sources[2] = glsl_h_code;
|
2013-07-06 10:08:52 +00:00
|
|
|
|
2016-04-07 20:11:35 +00:00
|
|
|
program = glCreateShaderProgramv(type, shader_nb, sources);
|
2013-07-06 10:08:52 +00:00
|
|
|
|
2016-04-07 20:11:35 +00:00
|
|
|
bool status = ValidateProgram(program);
|
2013-07-06 10:08:52 +00:00
|
|
|
|
|
|
|
if (!status) {
|
|
|
|
// print extra info
|
|
|
|
fprintf(stderr, "%s (entry %s, prog %d) :", glsl_file.c_str(), entry.c_str(), program);
|
|
|
|
fprintf(stderr, "\n%s", macro_sel.c_str());
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
2016-04-10 15:05:33 +00:00
|
|
|
|
|
|
|
m_prog_to_delete.push_back(program);
|
|
|
|
|
2013-07-06 10:08:52 +00:00
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
2015-07-15 06:59:16 +00:00
|
|
|
// This function will get the binary program. Normally it must be used a caching
|
|
|
|
// solution but Nvidia also incorporates the ASM dump. Asm is nice because it allow
|
|
|
|
// to have an overview of the program performance based on the instruction number
|
|
|
|
// Note: initially I was using cg offline compiler but it doesn't support latest
|
|
|
|
// GLSL improvement (unfortunately).
|
|
|
|
int GSShaderOGL::DumpAsm(const std::string& file, GLuint p)
|
|
|
|
{
|
|
|
|
if (!GLLoader::nvidia_buggy_driver) return 0;
|
|
|
|
|
|
|
|
GLint binaryLength;
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramiv(p, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
2015-07-15 06:59:16 +00:00
|
|
|
|
|
|
|
char* binary = new char[binaryLength+4];
|
|
|
|
GLenum binaryFormat;
|
2015-10-17 15:05:15 +00:00
|
|
|
glGetProgramBinary(p, binaryLength, NULL, &binaryFormat, binary);
|
2015-07-15 06:59:16 +00:00
|
|
|
|
|
|
|
FILE* outfile = fopen(file.c_str(), "w");
|
|
|
|
ASSERT(outfile);
|
|
|
|
|
|
|
|
// Search the magic number "!!"
|
|
|
|
int asm_ = 0;
|
|
|
|
while (asm_ < binaryLength && (binary[asm_] != '!' || binary[asm_+1] != '!')) {
|
|
|
|
asm_ += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int instructions = -1;
|
|
|
|
if (asm_ < binaryLength) {
|
|
|
|
// Now print asm as text
|
|
|
|
char* asm_txt = strtok(&binary[asm_], "\n");
|
|
|
|
while (asm_txt != NULL && (strncmp(asm_txt, "END", 3) || !strncmp(asm_txt, "ENDIF", 5))) {
|
2015-07-18 09:21:26 +00:00
|
|
|
if (!strncmp(asm_txt, "OUT", 3) || !strncmp(asm_txt, "TEMP", 4) || !strncmp(asm_txt, "LONG", 4)) {
|
2015-07-15 06:59:16 +00:00
|
|
|
instructions = 0;
|
|
|
|
} else if (instructions >= 0) {
|
|
|
|
if (instructions == 0)
|
|
|
|
fprintf(outfile, "\n");
|
|
|
|
instructions++;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(outfile, "%s\n", asm_txt);
|
|
|
|
asm_txt = strtok(NULL, "\n");
|
|
|
|
}
|
|
|
|
fprintf(outfile, "\nFound %d instructions\n", instructions);
|
|
|
|
}
|
|
|
|
fclose(outfile);
|
|
|
|
|
|
|
|
if (instructions < 0) {
|
|
|
|
// RAW dump in case of error
|
|
|
|
fprintf(stderr, "Error: failed to find the number of instructions!\n");
|
|
|
|
outfile = fopen(file.c_str(), "wb");
|
|
|
|
fwrite(binary, binaryLength, 1, outfile);
|
|
|
|
fclose(outfile);
|
|
|
|
ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] binary;
|
|
|
|
|
|
|
|
return instructions;
|
|
|
|
}
|