Restructured some Shader code. Added runtime temporary caching and sharing of shader stages. Resolves issue #1235.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2017-10-21 03:08:18 -03:00
parent f4e515edac
commit e5b0d96b27
16 changed files with 683 additions and 371 deletions
+12 -3
View File
@@ -30,6 +30,7 @@
#include "window/Window.h"
#include "Buffer.h"
#include "Text.h"
#include "ShaderStage.h"
#include "libraries/xxHash/xxhash.h"
@@ -126,9 +127,14 @@ love::graphics::Canvas *Graphics::newCanvas(const Canvas::Settings &settings)
return new Canvas(settings);
}
love::graphics::Shader *Graphics::newShader(const Shader::ShaderSource &source)
love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles)
{
return new Shader(source);
return new ShaderStage(this, stage, source, gles, cachekey);
}
love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
{
return new Shader(vertex, pixel);
}
love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
@@ -281,7 +287,10 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
try
{
if (!Shader::standardShaders[i])
Shader::standardShaders[i] = newShader(defaultShaderCode[i][target][gammacorrect]);
{
const auto &code = defaultShaderCode[i][target][gammacorrect];
Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]);
}
}
catch (love::Exception &)
{
+2 -1
View File
@@ -68,7 +68,6 @@ public:
love::graphics::ParticleSystem *newParticleSystem(Texture *texture, int size) override;
love::graphics::Canvas *newCanvas(const Canvas::Settings &settings) override;
love::graphics::Shader *newShader(const Shader::ShaderSource &source) override;
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
@@ -126,6 +125,8 @@ public:
private:
love::graphics::ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override;
love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) override;
void initCapabilities() override;
+18 -101
View File
@@ -36,8 +36,8 @@ namespace graphics
namespace opengl
{
Shader::Shader(const ShaderSource &source)
: love::graphics::Shader(source)
Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
: love::graphics::Shader(vertex, pixel)
, program(0)
, builtinUniforms()
, builtinUniformInfo()
@@ -73,71 +73,6 @@ Shader::~Shader()
}
}
GLuint Shader::compileCode(ShaderStage stage, const std::string &code)
{
GLenum glstage;
const char *typestr;
if (!getConstant(stage, typestr))
typestr = "";
switch (stage)
{
case STAGE_VERTEX:
glstage = GL_VERTEX_SHADER;
break;
case STAGE_PIXEL:
glstage = GL_FRAGMENT_SHADER;
break;
default:
throw love::Exception("Cannot create shader object: unknown shader type.");
break;
}
GLuint shaderid = glCreateShader(glstage);
if (shaderid == 0)
{
if (glGetError() == GL_INVALID_ENUM)
throw love::Exception("Cannot create %s shader object: %s shaders not supported.", typestr, typestr);
else
throw love::Exception("Cannot create %s shader object.", typestr);
}
const char *src = code.c_str();
GLint srclen = (GLint) code.length();
glShaderSource(shaderid, 1, (const GLchar **)&src, &srclen);
glCompileShader(shaderid);
GLint infologlen;
glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
// Get any warnings the shader compiler may have produced.
if (infologlen > 0)
{
GLchar *infolog = new GLchar[infologlen];
glGetShaderInfoLog(shaderid, infologlen, nullptr, infolog);
// Save any warnings for later querying.
shaderWarnings[stage] = infolog;
delete[] infolog;
}
GLint status;
glGetShaderiv(shaderid, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
glDeleteShader(shaderid);
throw love::Exception("Cannot compile %s shader code:\n%s",
typestr, shaderWarnings[stage].c_str());
}
return shaderid;
}
void Shader::mapActiveUniforms()
{
// Built-in uniform locations default to -1 (nonexistent.)
@@ -376,38 +311,22 @@ bool Shader::loadVolatile()
textureUnits.clear();
textureUnits.push_back(TextureUnit());
std::vector<GLuint> shaderids;
auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
const ShaderSource &defaults = gfx->getCurrentDefaultShaderCode();
// The shader program must have both vertex and pixel shader stages.
const std::string &vertexcode = shaderSource.vertex.empty() ? defaults.vertex : shaderSource.vertex;
const std::string &pixelcode = shaderSource.pixel.empty() ? defaults.pixel : shaderSource.pixel;
try
for (const auto &stage : stages)
{
shaderids.push_back(compileCode(STAGE_VERTEX, vertexcode));
shaderids.push_back(compileCode(STAGE_PIXEL, pixelcode));
}
catch (love::Exception &)
{
for (GLuint id : shaderids)
glDeleteShader(id);
throw;
if (stage.get() != nullptr)
stage->loadVolatile();
}
program = glCreateProgram();
if (program == 0)
{
for (GLuint id : shaderids)
glDeleteShader(id);
throw love::Exception("Cannot create shader program object.");
}
for (GLuint id : shaderids)
glAttachShader(program, id);
for (const auto &stage : stages)
{
if (stage.get() != nullptr)
glAttachShader(program, (GLuint) stage->getHandle());
}
// Bind generic vertex attribute indices to names in the shader.
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
@@ -419,10 +338,6 @@ bool Shader::loadVolatile()
glLinkProgram(program);
// Flag shaders for auto-deletion when the program object is deleted.
for (GLuint id : shaderids)
glDeleteShader(id);
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
@@ -477,8 +392,6 @@ void Shader::unloadVolatile()
// And the locations of any built-in uniform variables.
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
shaderWarnings.clear();
}
std::string Shader::getProgramWarnings() const
@@ -506,11 +419,15 @@ std::string Shader::getWarnings() const
std::string warnings;
const char *stagestr;
// Get the individual shader stage warnings
for (const auto &warning : shaderWarnings)
for (const auto &stage : stages)
{
if (getConstant(warning.first, stagestr))
warnings += std::string(stagestr) + std::string(" shader:\n") + warning.second;
if (stage.get() == nullptr)
continue;
const std::string &stagewarnings = stage->getWarnings();
if (ShaderStage::getConstant(stage->getStageType(), stagestr))
warnings += std::string(stagestr) + std::string(" shader:\n") + stagewarnings;
}
warnings += getProgramWarnings();
+1 -7
View File
@@ -47,8 +47,7 @@ public:
* Creates a new Shader using a list of source codes.
* Source must contain either vertex or pixel shader code, or both.
**/
Shader(const ShaderSource &source);
Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel);
virtual ~Shader();
// Implements Volatile
@@ -96,16 +95,11 @@ private:
TextureType getUniformTextureType(GLenum type) const;
bool isDepthTextureType(GLenum type) const;
GLuint compileCode(ShaderStage stage, const std::string &code);
void flushStreamDraws() const;
// Get any warnings or errors generated only by the shader program object.
std::string getProgramWarnings() const;
// Shader compiler warning strings for individual shader stages.
std::map<ShaderStage, std::string> shaderWarnings;
// volatile
GLuint program;
+105
View File
@@ -0,0 +1,105 @@
/**
* Copyright (c) 2006-2017 LOVE Development Team
*
* 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 "ShaderStage.h"
namespace love
{
namespace graphics
{
namespace opengl
{
ShaderStage::ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey)
: love::graphics::ShaderStage(gfx, stage, source, gles, cachekey)
, glShader(0)
{
loadVolatile();
}
ShaderStage::~ShaderStage()
{
unloadVolatile();
}
bool ShaderStage::loadVolatile()
{
if (glShader != 0)
return true;
StageType stage = getStageType();
const char *typestr = "unknown";
getConstant(stage, typestr);
GLenum glstage = 0;
if (stage == STAGE_VERTEX)
glstage = GL_VERTEX_SHADER;
else if (stage == STAGE_PIXEL)
glstage = GL_FRAGMENT_SHADER;
else
throw love::Exception("%s shader stage is not handled in OpenGL backend code.", typestr);
glShader = glCreateShader(glstage);
if (glShader == 0)
throw love::Exception("Cannot create OpenGL %s shader object.", typestr);
const std::string &sourcestring = getSource();
const char *src = sourcestring.c_str();
GLint srclen = (GLint) sourcestring.length();
glShaderSource(glShader, 1, (const GLchar **)&src, &srclen);
glCompileShader(glShader);
GLint infologlen;
glGetShaderiv(glShader, GL_INFO_LOG_LENGTH, &infologlen);
if (infologlen > 0)
{
GLchar *infolog = new GLchar[infologlen];
glGetShaderInfoLog(glShader, infologlen, nullptr, infolog);
warnings = infolog;
delete[] infolog;
}
GLint status = GL_FALSE;
glGetShaderiv(glShader, GL_COMPILE_STATUS, &status);
if (glShader == GL_FALSE)
{
glDeleteShader(glShader);
throw love::Exception("Cannot compile %s shader code:\n%s", typestr, warnings.c_str());
}
return true;
}
void ShaderStage::unloadVolatile()
{
if (glShader != 0)
glDeleteShader(glShader);
glShader = 0;
}
} // opengl
} // graphics
} // love
+55
View File
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2006-2017 LOVE Development Team
*
* 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.
**/
#pragma once
#include "graphics/ShaderStage.h"
#include "graphics/Volatile.h"
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
class ShaderStage final : public love::graphics::ShaderStage
{
public:
ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey);
virtual ~ShaderStage();
ptrdiff_t getHandle() const override { return glShader; }
// Implements Volatile.
bool loadVolatile() override;
void unloadVolatile() override;
private:
GLuint glShader;
}; // ShaderStage
} // opengl
} // graphics
} // love