cleanup and refactoring
This commit is contained in:
parent
2302158928
commit
76f6bf62a4
1285 changed files with 757994 additions and 8 deletions
463
raytracer/nvpro_core/nvgl/programmanager_gl.cpp
Normal file
463
raytracer/nvpro_core/nvgl/programmanager_gl.cpp
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#include "programmanager_gl.hpp"
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <nvh/fileoperations.hpp>
|
||||
#include <nvh/nvprint.hpp>
|
||||
|
||||
namespace nvgl {
|
||||
|
||||
static bool checkProgram(GLuint program)
|
||||
{
|
||||
if(!program)
|
||||
return false;
|
||||
|
||||
GLint result = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &result);
|
||||
|
||||
int infoLogLength;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
|
||||
if(infoLogLength > 1
|
||||
#ifdef NDEBUG
|
||||
&& result == GL_FALSE
|
||||
#endif
|
||||
)
|
||||
{
|
||||
std::vector<char> buffer(infoLogLength);
|
||||
glGetProgramInfoLog(program, infoLogLength, NULL, &buffer[0]);
|
||||
LOGW("%s\n", &buffer[0]);
|
||||
}
|
||||
|
||||
return result == GL_TRUE;
|
||||
}
|
||||
|
||||
static bool checkShader(GLuint shader, std::string const& filename)
|
||||
{
|
||||
if(!shader)
|
||||
return false;
|
||||
|
||||
GLint result = GL_FALSE;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
|
||||
|
||||
LOGI("%s ...\n", filename.c_str());
|
||||
int infoLogLength;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
|
||||
if(infoLogLength > 1
|
||||
#ifdef NDEBUG
|
||||
&& result == GL_FALSE
|
||||
#endif
|
||||
)
|
||||
{
|
||||
std::vector<char> buffer(infoLogLength);
|
||||
glGetShaderInfoLog(shader, infoLogLength, NULL, &buffer[0]);
|
||||
LOGW("%s\n", &buffer[0]);
|
||||
}
|
||||
|
||||
return result == GL_TRUE;
|
||||
}
|
||||
|
||||
bool ProgramManager::setupProgram(Program& prog)
|
||||
{
|
||||
prog.program = 0;
|
||||
|
||||
if(prog.definitions.empty())
|
||||
return false;
|
||||
|
||||
m_supportsExtendedInclude = has_GL_ARB_shading_language_include != 0;
|
||||
|
||||
std::string combinedPrepend = m_prepend;
|
||||
std::string combinedFilenames;
|
||||
for(size_t i = 0; i < prog.definitions.size(); i++)
|
||||
{
|
||||
combinedPrepend += prog.definitions[i].prepend;
|
||||
combinedFilenames += prog.definitions[i].filename;
|
||||
}
|
||||
|
||||
bool allFound = true;
|
||||
for(size_t i = 0; i < prog.definitions.size(); i++)
|
||||
{
|
||||
Definition& definition = prog.definitions[i];
|
||||
if(definition.filetype == FILETYPE_DEFAULT)
|
||||
{
|
||||
definition.filetype = m_filetype;
|
||||
}
|
||||
|
||||
if(m_rawOnly)
|
||||
{
|
||||
definition.content = getContent(definition.filename, definition.filenameFound);
|
||||
}
|
||||
else
|
||||
{
|
||||
char const* strDefine = "";
|
||||
|
||||
switch(definition.type)
|
||||
{
|
||||
case GL_VERTEX_SHADER:
|
||||
strDefine = "#define _VERTEX_SHADER_ 1\n";
|
||||
break;
|
||||
case GL_FRAGMENT_SHADER:
|
||||
strDefine = "#define _FRAGMENT_SHADER_ 1\n";
|
||||
break;
|
||||
case GL_COMPUTE_SHADER:
|
||||
strDefine = "#define _COMPUTE_SHADER_ 1\n";
|
||||
break;
|
||||
case GL_GEOMETRY_SHADER:
|
||||
strDefine = "#define _GEOMETRY_SHADER_ 1\n";
|
||||
break;
|
||||
case GL_TESS_CONTROL_SHADER:
|
||||
strDefine = "#define _TESS_CONTROL_SHADER_ 1\n";
|
||||
break;
|
||||
case GL_TESS_EVALUATION_SHADER:
|
||||
strDefine = "#define _TESS_EVALUATION_SHADER_ 1\n";
|
||||
break;
|
||||
#if GL_NV_mesh_shader
|
||||
case GL_MESH_SHADER_NV:
|
||||
strDefine = "#define _MESH_SHADER_ 1\n";
|
||||
break;
|
||||
case GL_TASK_SHADER_NV:
|
||||
strDefine = "#define _TASK_SHADER_ 1\n";
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
definition.content = manualInclude(definition.filename, definition.filenameFound,
|
||||
m_prepend + definition.prepend + std::string(strDefine), false);
|
||||
}
|
||||
allFound = allFound && !definition.content.empty();
|
||||
}
|
||||
|
||||
if(m_preprocessOnly)
|
||||
{
|
||||
prog.program = PREPROCESS_ONLY_PROGRAM;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
prog.program = glCreateProgram();
|
||||
if(!m_useCacheFile.empty() && has_GL_VERSION_4_1)
|
||||
{
|
||||
glProgramParameteri(prog.program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
bool loadedCache = false;
|
||||
if(!m_useCacheFile.empty() && (!allFound || m_preferCache) && has_GL_VERSION_4_1)
|
||||
{
|
||||
// try cache
|
||||
loadedCache = loadBinary(prog.program, combinedPrepend, combinedFilenames);
|
||||
}
|
||||
if(!loadedCache)
|
||||
{
|
||||
for(size_t i = 0; i < prog.definitions.size(); i++)
|
||||
{
|
||||
Definition& definition = prog.definitions[i];
|
||||
GLuint shader = 0;
|
||||
if(!definition.content.empty())
|
||||
{
|
||||
char const* sourcePointer = definition.content.c_str();
|
||||
shader = glCreateShader(definition.type);
|
||||
glShaderSource(shader, 1, &sourcePointer, NULL);
|
||||
glCompileShader(shader);
|
||||
}
|
||||
if(!shader || !checkShader(shader, definition.filename))
|
||||
{
|
||||
glDeleteShader(shader);
|
||||
glDeleteProgram(prog.program);
|
||||
prog.program = 0;
|
||||
return false;
|
||||
}
|
||||
glAttachShader(prog.program, shader);
|
||||
glDeleteShader(shader);
|
||||
}
|
||||
glLinkProgram(prog.program);
|
||||
}
|
||||
|
||||
if(checkProgram(prog.program))
|
||||
{
|
||||
if(!m_useCacheFile.empty() && !loadedCache && has_GL_VERSION_4_1)
|
||||
{
|
||||
saveBinary(prog.program, combinedPrepend, combinedFilenames);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
glDeleteProgram(prog.program);
|
||||
prog.program = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
ProgramID ProgramManager::createProgram(const Definition& def0,
|
||||
const Definition& def1 /*= ShaderDefinition()*/,
|
||||
const Definition& def2 /*= ShaderDefinition()*/,
|
||||
const Definition& def3 /*= ShaderDefinition()*/,
|
||||
const Definition& def4 /*= ShaderDefinition()*/)
|
||||
{
|
||||
std::vector<ProgramManager::Definition> defs;
|
||||
defs.push_back(def0);
|
||||
if(def1.type)
|
||||
defs.push_back(def1);
|
||||
if(def2.type)
|
||||
defs.push_back(def2);
|
||||
if(def3.type)
|
||||
defs.push_back(def3);
|
||||
if(def4.type)
|
||||
defs.push_back(def4);
|
||||
|
||||
return createProgram(defs);
|
||||
}
|
||||
|
||||
ProgramID ProgramManager::createProgram(const std::vector<ProgramManager::Definition>& definitions)
|
||||
{
|
||||
Program prog;
|
||||
prog.definitions = definitions;
|
||||
|
||||
setupProgram(prog);
|
||||
|
||||
for(size_t i = 0; i < m_programs.size(); i++)
|
||||
{
|
||||
if(m_programs[i].definitions.empty())
|
||||
{
|
||||
m_programs[i] = prog;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
m_programs.push_back(prog);
|
||||
return m_programs.size() - 1;
|
||||
}
|
||||
|
||||
bool ProgramManager::areProgramsValid()
|
||||
{
|
||||
bool valid = true;
|
||||
for(size_t i = 0; i < m_programs.size(); i++)
|
||||
{
|
||||
valid = valid && isValid((ProgramID)i);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
void ProgramManager::deletePrograms()
|
||||
{
|
||||
for(size_t i = 0; i < m_programs.size(); i++)
|
||||
{
|
||||
if(m_programs[i].program && m_programs[i].program != PREPROCESS_ONLY_PROGRAM)
|
||||
{
|
||||
glDeleteProgram(m_programs[i].program);
|
||||
}
|
||||
m_programs[i].program = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramManager::reloadProgram(ProgramID i)
|
||||
{
|
||||
if(!isValid(i))
|
||||
return;
|
||||
|
||||
bool old = m_preprocessOnly;
|
||||
|
||||
if(m_programs[i].program && m_programs[i].program != PREPROCESS_ONLY_PROGRAM)
|
||||
{
|
||||
glDeleteProgram(m_programs[i].program);
|
||||
}
|
||||
|
||||
m_preprocessOnly = m_programs[i].program == PREPROCESS_ONLY_PROGRAM;
|
||||
m_programs[i].program = 0;
|
||||
if(!m_programs[i].definitions.empty())
|
||||
{
|
||||
setupProgram(m_programs[i]);
|
||||
}
|
||||
m_preprocessOnly = old;
|
||||
}
|
||||
|
||||
void ProgramManager::reloadPrograms()
|
||||
{
|
||||
LOGI("Reloading programs...\n");
|
||||
|
||||
for(size_t i = 0; i < m_programs.size(); i++)
|
||||
{
|
||||
reloadProgram((ProgramID)i);
|
||||
}
|
||||
|
||||
LOGI("done\n");
|
||||
}
|
||||
|
||||
bool ProgramManager::isValid(ProgramID idx) const
|
||||
{
|
||||
return idx.isValid() && (m_programs[idx].definitions.empty() || m_programs[idx].program != 0);
|
||||
}
|
||||
|
||||
unsigned int ProgramManager::get(ProgramID idx) const
|
||||
{
|
||||
assert(m_programs[idx].program != PREPROCESS_ONLY_PROGRAM);
|
||||
return m_programs[idx].program;
|
||||
}
|
||||
|
||||
void ProgramManager::destroyProgram(ProgramID idx)
|
||||
{
|
||||
if(m_programs[idx].program && m_programs[idx].program != PREPROCESS_ONLY_PROGRAM)
|
||||
{
|
||||
glDeleteProgram(m_programs[idx].program);
|
||||
}
|
||||
m_programs[idx].program = 0;
|
||||
m_programs[idx].definitions.clear();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash2A, by Austin Appleby
|
||||
|
||||
// This is a variant of MurmurHash2 modified to use the Merkle-Damgard
|
||||
// construction. Bulk speed should be identical to Murmur2, small-key speed
|
||||
// will be 10%-20% slower due to the added overhead at the end of the hash.
|
||||
|
||||
// This variant fixes a minor issue where null keys were more likely to
|
||||
// collide with each other than expected, and also makes the algorithm
|
||||
// more amenable to incremental implementations. All other caveats from
|
||||
// MurmurHash2 still apply.
|
||||
|
||||
#define mmix(h, k) \
|
||||
{ \
|
||||
k *= m; \
|
||||
k ^= k >> r; \
|
||||
k *= m; \
|
||||
h *= m; \
|
||||
h ^= k; \
|
||||
}
|
||||
|
||||
static unsigned int strMurmurHash2A(const void* key, size_t len, unsigned int seed)
|
||||
{
|
||||
const unsigned int m = 0x5bd1e995;
|
||||
const int r = 24;
|
||||
unsigned int l = (unsigned int)len;
|
||||
|
||||
const unsigned char* data = (const unsigned char*)key;
|
||||
|
||||
unsigned int h = seed;
|
||||
unsigned int t = 0;
|
||||
|
||||
while(len >= 4)
|
||||
{
|
||||
unsigned int k = *(unsigned int*)data;
|
||||
|
||||
mmix(h, k);
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
|
||||
switch(len)
|
||||
{
|
||||
case 3:
|
||||
t ^= data[2] << 16;
|
||||
case 2:
|
||||
t ^= data[1] << 8;
|
||||
case 1:
|
||||
t ^= data[0];
|
||||
};
|
||||
|
||||
mmix(h, t);
|
||||
mmix(h, l);
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
}
|
||||
#undef mmix
|
||||
|
||||
|
||||
static size_t strHexFromByte(char* buffer, size_t bufferlen, const void* data, size_t len)
|
||||
{
|
||||
const char tostr[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
const unsigned char* d = (const unsigned char*)data;
|
||||
char* out = buffer;
|
||||
size_t i = 0;
|
||||
for(; i < len && (i * 2) + 1 < bufferlen; i++, d++, out += 2)
|
||||
{
|
||||
unsigned int val = *d;
|
||||
unsigned int hi = val / 16;
|
||||
unsigned int lo = val % 16;
|
||||
out[0] = tostr[hi];
|
||||
out[1] = tostr[lo];
|
||||
}
|
||||
|
||||
return i * 2;
|
||||
}
|
||||
|
||||
std::string ProgramManager::binaryName(const std::string& combinedPrepend, const std::string& combinedFilenames)
|
||||
{
|
||||
unsigned int hashCombine = combinedPrepend.empty() ? 0 : strMurmurHash2A(&combinedPrepend[0], combinedPrepend.size(), 127);
|
||||
unsigned int hashFilenames = strMurmurHash2A(&combinedFilenames[0], combinedFilenames.size(), 129);
|
||||
|
||||
std::string hexCombine;
|
||||
std::string hexFilenames;
|
||||
hexCombine.resize(8);
|
||||
hexFilenames.resize(8);
|
||||
strHexFromByte(&hexCombine[0], 8, &hashCombine, 4);
|
||||
strHexFromByte(&hexFilenames[0], 8, &hashFilenames, 4);
|
||||
|
||||
return m_useCacheFile + "_" + hexCombine + "_" + hexFilenames + ".glp";
|
||||
}
|
||||
|
||||
bool ProgramManager::loadBinary(GLuint program, const std::string& combinedPrepend, const std::string& combinedFilenames)
|
||||
{
|
||||
std::string filename = binaryName(combinedPrepend, combinedFilenames);
|
||||
std::string filenameFound;
|
||||
std::string binraw = nvh::loadFile(filename, true, m_directories, filenameFound);
|
||||
if(!binraw.empty())
|
||||
{
|
||||
const char* bindata = &binraw[0];
|
||||
glProgramBinary(program, *(GLenum*)bindata, bindata + 4, GLsizei(binraw.size() - 4));
|
||||
return checkProgram(program);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProgramManager::saveBinary(GLuint program, const std::string& combinedPrepend, const std::string& combinedFilenames)
|
||||
{
|
||||
std::string filename = binaryName(combinedPrepend, combinedFilenames);
|
||||
|
||||
GLint datasize;
|
||||
GLint datasize2;
|
||||
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &datasize);
|
||||
|
||||
std::string binraw;
|
||||
binraw.resize(datasize + 4ULL);
|
||||
char* bindata = &binraw[0];
|
||||
glGetProgramBinary(program, datasize, &datasize2, (GLenum*)bindata, bindata + 4);
|
||||
|
||||
std::ofstream binfile;
|
||||
binfile.open(filename.c_str(), std::ios::binary | std::ios::out);
|
||||
if(binfile.is_open())
|
||||
{
|
||||
binfile.write(bindata, datasize + 4ULL);
|
||||
}
|
||||
}
|
||||
} // namespace nvgl
|
||||
Loading…
Add table
Add a link
Reference in a new issue