mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-27 00:00:07 -06:00
Compute shader renderer (#2041)
* nothing works yet * don't double buffer 3D framebuffers for the GL Renderer looks like leftovers from when 3D+2D composition was done in the frontend * oops * it works! * implement display capture for compute renderer it's actually just all stolen from the regular OpenGL renderer * fix bad indirect call * handle cleanup properly * add hires rendering to the compute shader renderer * fix UB also misc changes to use more unsigned multiplication also fix framebuffer resize * correct edge filling behaviour when AA is disabled * fix full color textures * fix edge marking (polygon id is 6-bit not 5) also make the code a bit nicer * take all edge cases into account for XMin/XMax calculation * use hires coordinate again * stop using fixed size buffers based on scale factor in shaders this makes shader compile times tolerable on Wintel - beginning of the shader cache - increase size of tile idx in workdesc to 20 bits * apparently & is not defined on bvec4 why does this even compile on Intel and Nvidia? * put the texture cache into it's own file * add compute shader renderer properly to the GUI also add option to toggle using high resolution vertex coordinates * unbind sampler object in compute shader renderer * fix GetRangedBitMask for 64 bit aligned 64 bits pretty embarassing * convert NonStupidBitfield.h back to LF only new lines * actually adapt to latest changes * fix stupid merge * actually make compute shader renderer work with newest changes * show progress on shader compilation * remove merge leftover
This commit is contained in:
@ -18,6 +18,14 @@
|
||||
|
||||
#include "OpenGLSupport.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define XXH_STATIC_LINKING_ONLY
|
||||
#include "xxhash/xxhash.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
@ -27,9 +35,158 @@ using Platform::LogLevel;
|
||||
namespace OpenGL
|
||||
{
|
||||
|
||||
bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name)
|
||||
struct ShaderCacheEntry
|
||||
{
|
||||
u32 Length;
|
||||
u8* Data;
|
||||
u32 BinaryFormat;
|
||||
|
||||
ShaderCacheEntry(u8* data, u32 length, u32 binaryFmt)
|
||||
: Length(length), Data(data), BinaryFormat(binaryFmt)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
}
|
||||
|
||||
ShaderCacheEntry(const ShaderCacheEntry&) = delete;
|
||||
ShaderCacheEntry(ShaderCacheEntry&& other)
|
||||
{
|
||||
Data = other.Data;
|
||||
Length = other.Length;
|
||||
BinaryFormat = other.BinaryFormat;
|
||||
|
||||
other.Data = nullptr;
|
||||
other.Length = 0;
|
||||
other.BinaryFormat = 0;
|
||||
}
|
||||
|
||||
~ShaderCacheEntry()
|
||||
{
|
||||
if (Data) // check whether it was moved
|
||||
delete[] Data;
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<u64, ShaderCacheEntry> ShaderCache;
|
||||
std::vector<u64> NewShaders;
|
||||
|
||||
constexpr u32 ShaderCacheMagic = 0x11CAC4E1;
|
||||
constexpr u32 ShaderCacheVersion = 1;
|
||||
|
||||
void LoadShaderCache()
|
||||
{
|
||||
// for now the shader cache only contains only compute shaders
|
||||
// because they take the longest to compile
|
||||
Platform::FileHandle* file = Platform::OpenLocalFile("shadercache", Platform::FileMode::Read);
|
||||
if (file == nullptr)
|
||||
{
|
||||
Log(LogLevel::Error, "Could not find shader cache\n");
|
||||
return;
|
||||
}
|
||||
|
||||
u32 magic, version, numPrograms;
|
||||
if (Platform::FileRead(&magic, 4, 1, file) != 1 || magic != ShaderCacheMagic)
|
||||
{
|
||||
Log(LogLevel::Error, "Shader cache file has invalid magic\n");
|
||||
goto fileInvalid;
|
||||
}
|
||||
|
||||
if (Platform::FileRead(&version, 4, 1, file) != 1 || version != ShaderCacheVersion)
|
||||
{
|
||||
Log(LogLevel::Error, "Shader cache file has bad version\n");
|
||||
goto fileInvalid;
|
||||
}
|
||||
|
||||
if (Platform::FileRead(&numPrograms, 4, 1, file) != 1)
|
||||
{
|
||||
Log(LogLevel::Error, "Shader cache file invalid program count\n");
|
||||
goto fileInvalid;
|
||||
}
|
||||
|
||||
// not the best approach, because once changes pile up
|
||||
// we read and overwrite the old files
|
||||
for (u32 i = 0; i < numPrograms; i++)
|
||||
{
|
||||
int error = 3;
|
||||
|
||||
u32 length, binaryFormat;
|
||||
u64 sourceHash;
|
||||
error -= Platform::FileRead(&sourceHash, 8, 1, file);
|
||||
error -= Platform::FileRead(&length, 4, 1, file);
|
||||
error -= Platform::FileRead(&binaryFormat, 4, 1, file);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
Log(LogLevel::Error, "Invalid shader cache entry\n");
|
||||
goto fileInvalid;
|
||||
}
|
||||
|
||||
u8* data = new u8[length];
|
||||
if (Platform::FileRead(data, length, 1, file) != 1)
|
||||
{
|
||||
Log(LogLevel::Error, "Could not read shader cache entry data\n");
|
||||
delete[] data;
|
||||
goto fileInvalid;
|
||||
}
|
||||
|
||||
ShaderCache.erase(sourceHash);
|
||||
ShaderCache.emplace(sourceHash, ShaderCacheEntry(data, length, binaryFormat));
|
||||
}
|
||||
|
||||
fileInvalid:
|
||||
Platform::CloseFile(file);
|
||||
}
|
||||
|
||||
void SaveShaderCache()
|
||||
{
|
||||
Platform::FileHandle* file = Platform::OpenLocalFile("shadercache", Platform::FileMode::ReadWrite);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
Log(LogLevel::Error, "Could not open or create shader cache file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int written = 3;
|
||||
u32 magic = ShaderCacheMagic, version = ShaderCacheVersion, numPrograms = ShaderCache.size();
|
||||
written -= Platform::FileWrite(&magic, 4, 1, file);
|
||||
written -= Platform::FileWrite(&version, 4, 1, file);
|
||||
written -= Platform::FileWrite(&numPrograms, 4, 1, file);
|
||||
|
||||
if (written != 0)
|
||||
{
|
||||
Log(LogLevel::Error, "Could not write shader cache header\n");
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
Platform::FileSeek(file, 0, Platform::FileSeekOrigin::End);
|
||||
|
||||
printf("new shaders %d\n", NewShaders.size());
|
||||
|
||||
for (u64 newShader : NewShaders)
|
||||
{
|
||||
int error = 4;
|
||||
auto it = ShaderCache.find(newShader);
|
||||
|
||||
error -= Platform::FileWrite(&it->first, 8, 1, file);
|
||||
error -= Platform::FileWrite(&it->second.Length, 4, 1, file);
|
||||
error -= Platform::FileWrite(&it->second.BinaryFormat, 4, 1, file);
|
||||
error -= Platform::FileWrite(it->second.Data, it->second.Length, 1, file);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
Log(LogLevel::Error, "Could not insert new shader cache entry\n");
|
||||
goto writeError;
|
||||
}
|
||||
}
|
||||
|
||||
writeError:
|
||||
Platform::CloseFile(file);
|
||||
|
||||
NewShaders.clear();
|
||||
}
|
||||
|
||||
bool CompilerShader(GLuint& id, const std::string& source, const std::string& name, const std::string& type)
|
||||
{
|
||||
int len;
|
||||
int res;
|
||||
|
||||
if (!glCreateShader)
|
||||
@ -38,61 +195,32 @@ bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char*
|
||||
return false;
|
||||
}
|
||||
|
||||
ids[0] = glCreateShader(GL_VERTEX_SHADER);
|
||||
len = strlen(vs);
|
||||
glShaderSource(ids[0], 1, &vs, &len);
|
||||
glCompileShader(ids[0]);
|
||||
const char* sourceC = source.c_str();
|
||||
int len = source.length();
|
||||
glShaderSource(id, 1, &sourceC, &len);
|
||||
|
||||
glGetShaderiv(ids[0], GL_COMPILE_STATUS, &res);
|
||||
glCompileShader(id);
|
||||
|
||||
glGetShaderiv(id, GL_COMPILE_STATUS, &res);
|
||||
if (res != GL_TRUE)
|
||||
{
|
||||
glGetShaderiv(ids[0], GL_INFO_LOG_LENGTH, &res);
|
||||
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &res);
|
||||
if (res < 1) res = 1024;
|
||||
char* log = new char[res+1];
|
||||
glGetShaderInfoLog(ids[0], res+1, NULL, log);
|
||||
Log(LogLevel::Error, "OpenGL: failed to compile vertex shader %s: %s\n", name, log);
|
||||
Log(LogLevel::Debug, "shader source:\n--\n%s\n--\n", vs);
|
||||
glGetShaderInfoLog(id, res+1, NULL, log);
|
||||
Log(LogLevel::Error, "OpenGL: failed to compile %s shader %s: %s\n", type.c_str(), name.c_str(), log);
|
||||
Log(LogLevel::Debug, "shader source:\n--\n%s\n--\n", source.c_str());
|
||||
delete[] log;
|
||||
|
||||
glDeleteShader(ids[0]);
|
||||
glDeleteShader(id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ids[1] = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
len = strlen(fs);
|
||||
glShaderSource(ids[1], 1, &fs, &len);
|
||||
glCompileShader(ids[1]);
|
||||
|
||||
glGetShaderiv(ids[1], GL_COMPILE_STATUS, &res);
|
||||
if (res != GL_TRUE)
|
||||
{
|
||||
glGetShaderiv(ids[1], GL_INFO_LOG_LENGTH, &res);
|
||||
if (res < 1) res = 1024;
|
||||
char* log = new char[res+1];
|
||||
glGetShaderInfoLog(ids[1], res+1, NULL, log);
|
||||
Log(LogLevel::Error, "OpenGL: failed to compile fragment shader %s: %s\n", name, log);
|
||||
//printf("shader source:\n--\n%s\n--\n", fs);
|
||||
delete[] log;
|
||||
|
||||
Platform::FileHandle* logf = Platform::OpenFile("shaderfail.log", Platform::FileMode::WriteText);
|
||||
Platform::FileWrite(fs, len+1, 1, logf);
|
||||
Platform::CloseFile(logf);
|
||||
|
||||
glDeleteShader(ids[0]);
|
||||
glDeleteShader(ids[1]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ids[2] = glCreateProgram();
|
||||
glAttachShader(ids[2], ids[0]);
|
||||
glAttachShader(ids[2], ids[1]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkShaderProgram(GLuint* ids)
|
||||
bool LinkProgram(GLuint& result, GLuint* ids, int numIds)
|
||||
{
|
||||
int res;
|
||||
|
||||
@ -102,46 +230,132 @@ bool LinkShaderProgram(GLuint* ids)
|
||||
return false;
|
||||
}
|
||||
|
||||
glLinkProgram(ids[2]);
|
||||
for (int i = 0; i < numIds; i++)
|
||||
{
|
||||
glAttachShader(result, ids[i]);
|
||||
}
|
||||
|
||||
glDetachShader(ids[2], ids[0]);
|
||||
glDetachShader(ids[2], ids[1]);
|
||||
glLinkProgram(result);
|
||||
|
||||
glDeleteShader(ids[0]);
|
||||
glDeleteShader(ids[1]);
|
||||
for (int i = 0; i < numIds; i++)
|
||||
glDetachShader(result, ids[i]);
|
||||
|
||||
glGetProgramiv(ids[2], GL_LINK_STATUS, &res);
|
||||
glGetProgramiv(result, GL_LINK_STATUS, &res);
|
||||
if (res != GL_TRUE)
|
||||
{
|
||||
glGetProgramiv(ids[2], GL_INFO_LOG_LENGTH, &res);
|
||||
glGetProgramiv(result, GL_INFO_LOG_LENGTH, &res);
|
||||
if (res < 1) res = 1024;
|
||||
char* log = new char[res+1];
|
||||
glGetProgramInfoLog(ids[2], res+1, NULL, log);
|
||||
glGetProgramInfoLog(result, res+1, NULL, log);
|
||||
Log(LogLevel::Error, "OpenGL: failed to link shader program: %s\n", log);
|
||||
delete[] log;
|
||||
|
||||
glDeleteProgram(ids[2]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeleteShaderProgram(GLuint* ids)
|
||||
bool CompileComputeProgram(GLuint& result, const std::string& source, const std::string& name)
|
||||
{
|
||||
if (glDeleteProgram)
|
||||
{ // If OpenGL isn't loaded, then there's no shader program to delete
|
||||
glDeleteProgram(ids[2]);
|
||||
result = glCreateProgram();
|
||||
|
||||
/*u64 sourceHash = XXH64(source.data(), source.size(), 0);
|
||||
auto it = ShaderCache.find(sourceHash);
|
||||
if (it != ShaderCache.end())
|
||||
{
|
||||
glProgramBinary(result, it->second.BinaryFormat, it->second.Data, it->second.Length);
|
||||
|
||||
GLint linkStatus;
|
||||
glGetProgramiv(result, GL_LINK_STATUS, &linkStatus);
|
||||
if (linkStatus == GL_TRUE)
|
||||
{
|
||||
Log(LogLevel::Info, "Restored shader %s from cache\n", name.c_str());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}*/
|
||||
Log(LogLevel::Error, "Shader %s from cache was rejected\n", name.c_str());
|
||||
|
||||
GLuint shader;
|
||||
bool linkingSucess = false;
|
||||
|
||||
if (!glCreateShader || !glDeleteShader)
|
||||
goto error;
|
||||
|
||||
shader = glCreateShader(GL_COMPUTE_SHADER);
|
||||
|
||||
if (!CompilerShader(shader, source, name, "compute"))
|
||||
goto error;
|
||||
|
||||
linkingSucess = LinkProgram(result, &shader, 1);
|
||||
|
||||
error:
|
||||
glDeleteShader(shader);
|
||||
|
||||
if (!linkingSucess)
|
||||
{
|
||||
glDeleteProgram(result);
|
||||
}
|
||||
/*else
|
||||
{
|
||||
GLint length;
|
||||
GLenum format;
|
||||
glGetProgramiv(result, GL_PROGRAM_BINARY_LENGTH, &length);
|
||||
|
||||
u8* buffer = new u8[length];
|
||||
glGetProgramBinary(result, length, nullptr, &format, buffer);
|
||||
|
||||
ShaderCache.emplace(sourceHash, ShaderCacheEntry(buffer, length, format));
|
||||
NewShaders.push_back(sourceHash);
|
||||
}*/
|
||||
|
||||
return linkingSucess;
|
||||
}
|
||||
|
||||
void UseShaderProgram(GLuint* ids)
|
||||
bool CompileVertexFragmentProgram(GLuint& result,
|
||||
const std::string& vs, const std::string& fs,
|
||||
const std::string& name,
|
||||
const std::initializer_list<AttributeTarget>& vertexInAttrs,
|
||||
const std::initializer_list<AttributeTarget>& fragmentOutAttrs)
|
||||
{
|
||||
if (glUseProgram)
|
||||
{ // If OpenGL isn't loaded, then there's no shader program to use
|
||||
glUseProgram(ids[2]);
|
||||
GLuint shaders[2] =
|
||||
{
|
||||
glCreateShader(GL_VERTEX_SHADER),
|
||||
glCreateShader(GL_FRAGMENT_SHADER)
|
||||
};
|
||||
result = glCreateProgram();
|
||||
|
||||
bool linkingSucess = false;
|
||||
|
||||
if (!CompilerShader(shaders[0], vs, name, "vertex"))
|
||||
goto error;
|
||||
|
||||
if (!CompilerShader(shaders[1], fs, name, "fragment"))
|
||||
goto error;
|
||||
|
||||
|
||||
for (const AttributeTarget& target : vertexInAttrs)
|
||||
{
|
||||
glBindAttribLocation(result, target.Location, target.Name);
|
||||
}
|
||||
for (const AttributeTarget& target : fragmentOutAttrs)
|
||||
{
|
||||
glBindFragDataLocation(result, target.Location, target.Name);
|
||||
}
|
||||
|
||||
linkingSucess = LinkProgram(result, shaders, 2);
|
||||
|
||||
error:
|
||||
glDeleteShader(shaders[1]);
|
||||
glDeleteShader(shaders[0]);
|
||||
|
||||
if (!linkingSucess)
|
||||
glDeleteProgram(result);
|
||||
|
||||
return linkingSucess;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user