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
+81 -5
View File
@@ -22,6 +22,7 @@
#include "Graphics.h"
#include "Buffer.h"
#include "math/MathModule.h"
#include "data/DataModule.h"
#include "Polyline.h"
#include "font/Font.h"
#include "window/Window.h"
@@ -100,7 +101,7 @@ bool isDebugEnabled()
love::Type Graphics::type("graphics", &Module::type);
Shader::ShaderSource Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
Graphics::Graphics()
: width(0)
@@ -115,6 +116,7 @@ Graphics::Graphics()
, canvasSwitchCount(0)
, drawCallsBatched(0)
, capabilities()
, cachedShaderStages()
{
transformStack.reserve(16);
transformStack.push_back(Matrix4());
@@ -149,6 +151,9 @@ Graphics::~Graphics()
delete streamBufferState.vb[1];
delete streamBufferState.indexBuffer;
for (int i = 0; i < (int) ShaderStage::STAGE_MAX_ENUM; i++)
cachedShaderStages[i].clear();
Shader::deinitialize();
}
@@ -177,9 +182,77 @@ Video *Graphics::newVideo(love::video::VideoStream *stream, float dpiscale)
return new Video(this, stream, dpiscale);
}
bool Graphics::validateShader(bool gles, const Shader::ShaderSource &source, std::string &err)
ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &optsource)
{
return Shader::validate(this, gles, source, true, err);
if (stage == ShaderStage::STAGE_MAX_ENUM)
throw love::Exception("Invalid shader stage.");
const std::string &source = optsource.empty() ? getCurrentDefaultShaderCode().source[stage] : optsource;
ShaderStage *s = nullptr;
std::string cachekey;
if (!source.empty())
{
data::HashFunction::Value hashvalue;
data::hash(data::HashFunction::FUNCTION_SHA1, source.c_str(), source.size(), hashvalue);
cachekey = std::string(hashvalue.data, hashvalue.size);
auto it = cachedShaderStages[stage].find(cachekey);
if (it != cachedShaderStages[stage].end())
{
s = it->second;
s->retain();
}
}
if (s == nullptr)
{
s = newShaderStageInternal(stage, cachekey, source, getRenderer() == RENDERER_OPENGLES);
if (!cachekey.empty())
cachedShaderStages[stage][cachekey] = s;
}
return s;
}
void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &hashkey)
{
cachedShaderStages[type].erase(hashkey);
}
Shader *Graphics::newShader(const std::string &vertex, const std::string &pixel)
{
if (vertex.empty() && pixel.empty())
throw love::Exception("Error creating shader: no source code!");
StrongRef<ShaderStage> vertexstage(newShaderStage(ShaderStage::STAGE_VERTEX, vertex), Acquire::NORETAIN);
StrongRef<ShaderStage> pixelstage(newShaderStage(ShaderStage::STAGE_PIXEL, pixel), Acquire::NORETAIN);
return newShaderInternal(vertexstage.get(), pixelstage.get());
}
bool Graphics::validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err)
{
if (vertex.empty() && pixel.empty())
{
err = "Error validating shader: no source code!";
return false;
}
StrongRef<ShaderStage> vertexstage;
StrongRef<ShaderStage> pixelstage;
// Don't use cached shader stages, since the gles flag may not match the
// current renderer.
if (!vertex.empty())
vertexstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_VERTEX, vertex, gles), Acquire::NORETAIN);
if (!pixel.empty())
pixelstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_PIXEL, pixel, gles), Acquire::NORETAIN);
return Shader::validate(vertexstage.get(), pixelstage.get(), err);
}
int Graphics::getWidth() const
@@ -1425,9 +1498,12 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
return p;
}
const Shader::ShaderSource &Graphics::getCurrentDefaultShaderCode() const
const Graphics::DefaultShaderCode &Graphics::getCurrentDefaultShaderCode() const
{
return defaultShaderCode[Shader::STANDARD_DEFAULT][getShaderLanguageTarget()][isGammaCorrect() ? 1 : 0];
int languageindex = (int) getShaderLanguageTarget();
int gammaindex = isGammaCorrect() ? 1 : 0;
return defaultShaderCode[Shader::STANDARD_DEFAULT][languageindex][gammaindex];
}
/**
+19 -4
View File
@@ -34,6 +34,7 @@
#include "Texture.h"
#include "Canvas.h"
#include "Font.h"
#include "ShaderStage.h"
#include "Shader.h"
#include "Quad.h"
#include "Mesh.h"
@@ -44,6 +45,7 @@
#include "font/Rasterizer.h"
#include "font/Font.h"
#include "video/VideoStream.h"
#include "data/HashFunction.h"
// C++
#include <string>
@@ -359,6 +361,11 @@ public:
{}
};
struct DefaultShaderCode
{
std::string source[ShaderStage::STAGE_MAX_ENUM];
};
Graphics();
virtual ~Graphics();
@@ -378,7 +385,9 @@ public:
virtual ParticleSystem *newParticleSystem(Texture *texture, int size) = 0;
virtual Canvas *newCanvas(const Canvas::Settings &settings) = 0;
virtual Shader *newShader(const Shader::ShaderSource &source) = 0;
ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
Shader *newShader(const std::string &vertex, const std::string &pixel);
virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
@@ -390,7 +399,7 @@ public:
virtual Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {}) = 0;
bool validateShader(bool gles, const Shader::ShaderSource &source, std::string &err);
bool validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err);
/**
* Resets the current color, background color, line style, and so forth.
@@ -758,7 +767,9 @@ public:
static void flushStreamDrawsGlobal();
virtual Shader::Language getShaderLanguageTarget() const = 0;
const Shader::ShaderSource &getCurrentDefaultShaderCode() const;
const DefaultShaderCode &getCurrentDefaultShaderCode() const;
void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
template <typename T>
T *getScratchBuffer(size_t count)
@@ -806,7 +817,7 @@ public:
static std::vector<std::string> getConstants(StackType);
// Default shader code (a shader is always required internally.)
static Shader::ShaderSource defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
static DefaultShaderCode defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
protected:
@@ -868,6 +879,8 @@ protected:
}
};
virtual ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0;
virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;
virtual void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) = 0;
@@ -926,6 +939,8 @@ private:
std::vector<uint8> scratchBuffer;
std::unordered_map<std::string, ShaderStage *> cachedShaderStages[ShaderStage::STAGE_MAX_ENUM];
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
static StringMap<DrawMode, DRAW_MAX_ENUM> drawModes;
+11 -175
View File
@@ -29,103 +29,6 @@
// C++
#include <string>
// TODO: Use love.graphics to determine actual limits?
static const TBuiltInResource defaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,
/* .MaxTextureUnits = */ 32,
/* .MaxTextureCoords = */ 32,
/* .MaxVertexAttribs = */ 64,
/* .MaxVertexUniformComponents = */ 16384,
/* .MaxVaryingFloats = */ 128,
/* .MaxVertexTextureImageUnits = */ 32,
/* .MaxCombinedTextureImageUnits = */ 80,
/* .MaxTextureImageUnits = */ 32,
/* .MaxFragmentUniformComponents = */ 16384,
/* .MaxDrawBuffers = */ 8,
/* .MaxVertexUniformVectors = */ 4096,
/* .MaxVaryingVectors = */ 32,
/* .MaxFragmentUniformVectors = */ 4096,
/* .MaxVertexOutputVectors = */ 32,
/* .MaxFragmentInputVectors = */ 31,
/* .MinProgramTexelOffset = */ -8,
/* .MaxProgramTexelOffset = */ 7,
/* .MaxClipDistances = */ 8,
/* .MaxComputeWorkGroupCountX = */ 65535,
/* .MaxComputeWorkGroupCountY = */ 65535,
/* .MaxComputeWorkGroupCountZ = */ 65535,
/* .MaxComputeWorkGroupSizeX = */ 1024,
/* .MaxComputeWorkGroupSizeY = */ 1024,
/* .MaxComputeWorkGroupSizeZ = */ 64,
/* .MaxComputeUniformComponents = */ 1024,
/* .MaxComputeTextureImageUnits = */ 32,
/* .MaxComputeImageUniforms = */ 16,
/* .MaxComputeAtomicCounters = */ 4096,
/* .MaxComputeAtomicCounterBuffers = */ 8,
/* .MaxVaryingComponents = */ 128,
/* .MaxVertexOutputComponents = */ 128,
/* .MaxGeometryInputComponents = */ 128,
/* .MaxGeometryOutputComponents = */ 128,
/* .MaxFragmentInputComponents = */ 128,
/* .MaxImageUnits = */ 192,
/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
/* .MaxCombinedShaderOutputResources = */ 144,
/* .MaxImageSamples = */ 32,
/* .MaxVertexImageUniforms = */ 16,
/* .MaxTessControlImageUniforms = */ 16,
/* .MaxTessEvaluationImageUniforms = */ 16,
/* .MaxGeometryImageUniforms = */ 16,
/* .MaxFragmentImageUniforms = */ 16,
/* .MaxCombinedImageUniforms = */ 80,
/* .MaxGeometryTextureImageUnits = */ 16,
/* .MaxGeometryOutputVertices = */ 256,
/* .MaxGeometryTotalOutputComponents = */ 1024,
/* .MaxGeometryUniformComponents = */ 1024,
/* .MaxGeometryVaryingComponents = */ 64,
/* .MaxTessControlInputComponents = */ 128,
/* .MaxTessControlOutputComponents = */ 128,
/* .MaxTessControlTextureImageUnits = */ 16,
/* .MaxTessControlUniformComponents = */ 1024,
/* .MaxTessControlTotalOutputComponents = */ 4096,
/* .MaxTessEvaluationInputComponents = */ 128,
/* .MaxTessEvaluationOutputComponents = */ 128,
/* .MaxTessEvaluationTextureImageUnits = */ 16,
/* .MaxTessEvaluationUniformComponents = */ 1024,
/* .MaxTessPatchComponents = */ 120,
/* .MaxPatchVertices = */ 32,
/* .MaxTessGenLevel = */ 64,
/* .MaxViewports = */ 16,
/* .MaxVertexAtomicCounters = */ 4096,
/* .MaxTessControlAtomicCounters = */ 4096,
/* .MaxTessEvaluationAtomicCounters = */ 4096,
/* .MaxGeometryAtomicCounters = */ 4096,
/* .MaxFragmentAtomicCounters = */ 4096,
/* .MaxCombinedAtomicCounters = */ 4096,
/* .MaxAtomicCounterBindings = */ 8,
/* .MaxVertexAtomicCounterBuffers = */ 8,
/* .MaxTessControlAtomicCounterBuffers = */ 8,
/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
/* .MaxGeometryAtomicCounterBuffers = */ 8,
/* .MaxFragmentAtomicCounterBuffers = */ 8,
/* .MaxCombinedAtomicCounterBuffers = */ 8,
/* .MaxAtomicCounterBufferSize = */ 16384,
/* .MaxTransformFeedbackBuffers = */ 4,
/* .MaxTransformFeedbackInterleavedComponents = */ 64,
/* .MaxCullDistances = */ 8,
/* .MaxCombinedClipAndCullDistances = */ 8,
/* .MaxSamples = */ 32,
/* .limits = */ {
/* .nonInductiveForLoops = */ 1,
/* .whileLoops = */ 1,
/* .doWhileLoops = */ 1,
/* .generalUniformIndexing = */ 1,
/* .generalAttributeMatrixVectorIndexing = */ 1,
/* .generalVaryingIndexing = */ 1,
/* .generalSamplerIndexing = */ 1,
/* .generalVariableIndexing = */ 1,
/* .generalConstantMatrixVectorIndexing = */ 1,
}};
namespace love
{
namespace graphics
@@ -136,17 +39,15 @@ love::Type Shader::type("Shader", &Object::type);
Shader *Shader::current = nullptr;
Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr};
Shader::Shader(const ShaderSource &source)
: shaderSource(source)
Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
: stages()
{
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx == nullptr)
throw love::Exception("love.graphics must be initialized to create a Shader.");
bool gles = gfx->getRenderer() == Graphics::RENDERER_OPENGLES;
std::string err;
if (!validate(gfx, gles, source, false, err))
if (!validate(vertex, pixel, err))
throw love::Exception("%s", err.c_str());
stages[ShaderStage::STAGE_VERTEX] = vertex;
stages[ShaderStage::STAGE_PIXEL] = pixel;
}
Shader::~Shader()
@@ -225,62 +126,15 @@ void Shader::checkMainTexture(Texture *tex) const
checkMainTextureType(tex->getTextureType(), tex->getDepthSampleMode().hasValue);
}
bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err)
bool Shader::validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err)
{
if (source.vertex.empty() && source.pixel.empty())
{
err = "Error validating shader: no source code!";
return false;
}
bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
int defaultversion = gles ? 100 : 120;
EProfile defaultprofile = ENoProfile;
glslang::TShader vshader(EShLangVertex);
glslang::TShader pshader(EShLangFragment);
// TProgram must be destroyed before TShader.
glslang::TProgram program;
auto addshader = [&](glslang::TShader &s, const std::string &src, ShaderStage stage) -> bool
{
if (src.empty())
return true;
if (vertex != nullptr)
program.addShader(vertex->getGLSLangShader());
const char *csrc = src.c_str();
int srclen = (int) src.length();
s.setStringsWithLengths(&csrc, &srclen, 1);
bool forcedefault = false;
if (src.find("#define LOVE_GLSL1_ON_GLSL3") != std::string::npos)
forcedefault = true;
bool forwardcompat = supportsGLSL3 && !forcedefault;
if (!s.parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
{
const char *stagename = "unknown";
getConstant(stage, stagename);
err = "Error validating " + std::string(stagename) + " shader:\n\n"
+ std::string(s.getInfoLog()) + "\n" + std::string(s.getInfoDebugLog());
return false;
}
program.addShader(&s);
return true;
};
const ShaderSource &defaults = gfx->getCurrentDefaultShaderCode();
const std::string &vertcode = (checkWithDefaults && source.vertex.empty()) ? defaults.vertex : source.vertex;
const std::string &pixcode = (checkWithDefaults && source.pixel.empty()) ? defaults.pixel : source.pixel;
if (!addshader(vshader, vertcode, STAGE_VERTEX))
return false;
if (!addshader(pshader, pixcode, STAGE_PIXEL))
return false;
if (pixel != nullptr)
program.addShader(pixel->getGLSLangShader());
if (!program.link(EShMsgDefault))
{
@@ -311,16 +165,6 @@ bool Shader::getConstant(Language in, const char *&out)
return languages.find(in, out);
}
bool Shader::getConstant(const char *in, ShaderStage &out)
{
return stageNames.find(in, out);
}
bool Shader::getConstant(ShaderStage in, const char *&out)
{
return stageNames.find(in, out);
}
bool Shader::getConstant(const char *in, BuiltinUniform &out)
{
return builtinNames.find(in, out);
@@ -341,14 +185,6 @@ StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry Shader::languageEn
StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> Shader::languages(Shader::languageEntries, sizeof(Shader::languageEntries));
StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM>::Entry Shader::stageNameEntries[] =
{
{ "vertex", STAGE_VERTEX },
{ "pixel", STAGE_PIXEL },
};
StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM> Shader::stageNames(Shader::stageNameEntries, sizeof(Shader::stageNameEntries));
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{ "MainTex", BUILTIN_TEXTURE_MAIN },
+4 -33
View File
@@ -24,6 +24,7 @@
#include "common/Object.h"
#include "common/StringMap.h"
#include "Texture.h"
#include "ShaderStage.h"
// STL
#include <string>
@@ -59,13 +60,6 @@ public:
LANGUAGE_MAX_ENUM
};
enum ShaderStage
{
STAGE_VERTEX,
STAGE_PIXEL,
STAGE_MAX_ENUM
};
// Built-in uniform variables.
enum BuiltinUniform
{
@@ -103,12 +97,6 @@ public:
STANDARD_MAX_ENUM
};
struct ShaderSource
{
std::string vertex;
std::string pixel;
};
struct MatrixSize
{
short columns;
@@ -150,7 +138,7 @@ public:
// Pointer to the default Shader.
static Shader *standardShaders[STANDARD_MAX_ENUM];
Shader(const ShaderSource &source);
Shader(ShaderStage *vertex, ShaderStage *pixel);
virtual ~Shader();
/**
@@ -197,7 +185,7 @@ public:
virtual ptrdiff_t getHandle() const = 0;
static bool validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err);
static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
static bool initialize();
static void deinitialize();
@@ -205,34 +193,17 @@ public:
static bool getConstant(const char *in, Language &out);
static bool getConstant(Language in, const char *&out);
static bool getConstant(const char *in, ShaderStage &out);
static bool getConstant(ShaderStage in, const char *&out);
static bool getConstant(const char *in, BuiltinUniform &out);
static bool getConstant(BuiltinUniform in, const char *&out);
protected:
struct CachedShaderStage
{
int referenceCount;
ShaderStage stage;
glslang::TShader *glslangShader;
ptrdiff_t handle;
};
// Source code used for this Shader.
ShaderSource shaderSource;
static std::map<std::string, CachedShaderStage> cachedShaders;
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
private:
static StringMap<Language, LANGUAGE_MAX_ENUM>::Entry languageEntries[];
static StringMap<Language, LANGUAGE_MAX_ENUM> languages;
static StringMap<ShaderStage, STAGE_MAX_ENUM>::Entry stageNameEntries[];
static StringMap<ShaderStage, STAGE_MAX_ENUM> stageNames;
// Names for the built-in uniform variables.
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
+205
View File
@@ -0,0 +1,205 @@
/**
* 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"
#include "common/Exception.h"
#include "Graphics.h"
#include "libraries/glslang/glslang/Public/ShaderLang.h"
// TODO: Use love.graphics to determine actual limits?
static const TBuiltInResource defaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,
/* .MaxTextureUnits = */ 32,
/* .MaxTextureCoords = */ 32,
/* .MaxVertexAttribs = */ 64,
/* .MaxVertexUniformComponents = */ 16384,
/* .MaxVaryingFloats = */ 128,
/* .MaxVertexTextureImageUnits = */ 32,
/* .MaxCombinedTextureImageUnits = */ 80,
/* .MaxTextureImageUnits = */ 32,
/* .MaxFragmentUniformComponents = */ 16384,
/* .MaxDrawBuffers = */ 8,
/* .MaxVertexUniformVectors = */ 4096,
/* .MaxVaryingVectors = */ 32,
/* .MaxFragmentUniformVectors = */ 4096,
/* .MaxVertexOutputVectors = */ 32,
/* .MaxFragmentInputVectors = */ 31,
/* .MinProgramTexelOffset = */ -8,
/* .MaxProgramTexelOffset = */ 7,
/* .MaxClipDistances = */ 8,
/* .MaxComputeWorkGroupCountX = */ 65535,
/* .MaxComputeWorkGroupCountY = */ 65535,
/* .MaxComputeWorkGroupCountZ = */ 65535,
/* .MaxComputeWorkGroupSizeX = */ 1024,
/* .MaxComputeWorkGroupSizeY = */ 1024,
/* .MaxComputeWorkGroupSizeZ = */ 64,
/* .MaxComputeUniformComponents = */ 1024,
/* .MaxComputeTextureImageUnits = */ 32,
/* .MaxComputeImageUniforms = */ 16,
/* .MaxComputeAtomicCounters = */ 4096,
/* .MaxComputeAtomicCounterBuffers = */ 8,
/* .MaxVaryingComponents = */ 128,
/* .MaxVertexOutputComponents = */ 128,
/* .MaxGeometryInputComponents = */ 128,
/* .MaxGeometryOutputComponents = */ 128,
/* .MaxFragmentInputComponents = */ 128,
/* .MaxImageUnits = */ 192,
/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
/* .MaxCombinedShaderOutputResources = */ 144,
/* .MaxImageSamples = */ 32,
/* .MaxVertexImageUniforms = */ 16,
/* .MaxTessControlImageUniforms = */ 16,
/* .MaxTessEvaluationImageUniforms = */ 16,
/* .MaxGeometryImageUniforms = */ 16,
/* .MaxFragmentImageUniforms = */ 16,
/* .MaxCombinedImageUniforms = */ 80,
/* .MaxGeometryTextureImageUnits = */ 16,
/* .MaxGeometryOutputVertices = */ 256,
/* .MaxGeometryTotalOutputComponents = */ 1024,
/* .MaxGeometryUniformComponents = */ 1024,
/* .MaxGeometryVaryingComponents = */ 64,
/* .MaxTessControlInputComponents = */ 128,
/* .MaxTessControlOutputComponents = */ 128,
/* .MaxTessControlTextureImageUnits = */ 16,
/* .MaxTessControlUniformComponents = */ 1024,
/* .MaxTessControlTotalOutputComponents = */ 4096,
/* .MaxTessEvaluationInputComponents = */ 128,
/* .MaxTessEvaluationOutputComponents = */ 128,
/* .MaxTessEvaluationTextureImageUnits = */ 16,
/* .MaxTessEvaluationUniformComponents = */ 1024,
/* .MaxTessPatchComponents = */ 120,
/* .MaxPatchVertices = */ 32,
/* .MaxTessGenLevel = */ 64,
/* .MaxViewports = */ 16,
/* .MaxVertexAtomicCounters = */ 4096,
/* .MaxTessControlAtomicCounters = */ 4096,
/* .MaxTessEvaluationAtomicCounters = */ 4096,
/* .MaxGeometryAtomicCounters = */ 4096,
/* .MaxFragmentAtomicCounters = */ 4096,
/* .MaxCombinedAtomicCounters = */ 4096,
/* .MaxAtomicCounterBindings = */ 8,
/* .MaxVertexAtomicCounterBuffers = */ 8,
/* .MaxTessControlAtomicCounterBuffers = */ 8,
/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
/* .MaxGeometryAtomicCounterBuffers = */ 8,
/* .MaxFragmentAtomicCounterBuffers = */ 8,
/* .MaxCombinedAtomicCounterBuffers = */ 8,
/* .MaxAtomicCounterBufferSize = */ 16384,
/* .MaxTransformFeedbackBuffers = */ 4,
/* .MaxTransformFeedbackInterleavedComponents = */ 64,
/* .MaxCullDistances = */ 8,
/* .MaxCombinedClipAndCullDistances = */ 8,
/* .MaxSamples = */ 32,
/* .limits = */ {
/* .nonInductiveForLoops = */ 1,
/* .whileLoops = */ 1,
/* .doWhileLoops = */ 1,
/* .generalUniformIndexing = */ 1,
/* .generalAttributeMatrixVectorIndexing = */ 1,
/* .generalVaryingIndexing = */ 1,
/* .generalSamplerIndexing = */ 1,
/* .generalVariableIndexing = */ 1,
/* .generalConstantMatrixVectorIndexing = */ 1,
}
};
namespace love
{
namespace graphics
{
ShaderStage::ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl, bool gles, const std::string &cachekey)
: stageType(stage)
, source(glsl)
, cacheKey(cachekey)
, glslangShader(nullptr)
{
EShLanguage glslangStage = EShLangCount;
if (stage == STAGE_VERTEX)
glslangStage = EShLangVertex;
else if (stage == STAGE_PIXEL)
glslangStage = EShLangFragment;
else
throw love::Exception("Cannot compile shader stage: unknown stage type.");
glslangShader = new glslang::TShader(glslangStage);
bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
int defaultversion = gles ? 100 : 120;
EProfile defaultprofile = ENoProfile;
const char *csrc = glsl.c_str();
int srclen = (int) glsl.length();
glslangShader->setStringsWithLengths(&csrc, &srclen, 1);
bool forcedefault = false;
if (source.find("#define LOVE_GLSL1_ON_GLSL3") != std::string::npos)
forcedefault = true;
bool forwardcompat = supportsGLSL3 && !forcedefault;
if (!glslangShader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
{
const char *stagename = "unknown";
getConstant(stage, stagename);
std::string err = "Error validating " + std::string(stagename) + " shader:\n\n"
+ std::string(glslangShader->getInfoLog()) + "\n"
+ std::string(glslangShader->getInfoDebugLog());
delete glslangShader;
throw love::Exception("%s", err.c_str());
}
}
ShaderStage::~ShaderStage()
{
if (!cacheKey.empty())
{
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
gfx->cleanupCachedShaderStage(stageType, cacheKey);
}
delete glslangShader;
}
bool ShaderStage::getConstant(const char *in, StageType &out)
{
return stageNames.find(in, out);
}
bool ShaderStage::getConstant(StageType in, const char *&out)
{
return stageNames.find(in, out);
}
StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
{
{ "vertex", STAGE_VERTEX },
{ "pixel", STAGE_PIXEL },
};
StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM> ShaderStage::stageNames(ShaderStage::stageNameEntries, sizeof(ShaderStage::stageNameEntries));
} // graphics
} // love
+99
View File
@@ -0,0 +1,99 @@
/**
* 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 "common/Object.h"
#include "common/StringMap.h"
#include "Volatile.h"
#include <stddef.h>
#include <string>
namespace glslang
{
class TShader;
}
namespace love
{
namespace graphics
{
class Graphics;
class ShaderStage : public love::Object, public Volatile
{
public:
enum StageType
{
STAGE_VERTEX,
STAGE_PIXEL,
STAGE_MAX_ENUM
};
ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl, bool gles, const std::string &cachekey);
virtual ~ShaderStage();
StageType getStageType() const { return stageType; }
const std::string &getSource() const { return source; }
const std::string &getWarnings() const { return warnings; }
glslang::TShader *getGLSLangShader() const { return glslangShader; }
virtual ptrdiff_t getHandle() const = 0;
static bool getConstant(const char *in, StageType &out);
static bool getConstant(StageType in, const char *&out);
protected:
std::string warnings;
private:
StageType stageType;
std::string source;
std::string cacheKey;
glslang::TShader *glslangShader;
static StringMap<StageType, STAGE_MAX_ENUM>::Entry stageNameEntries[];
static StringMap<StageType, STAGE_MAX_ENUM> stageNames;
}; // ShaderStage
class ShaderStageForValidation final : public ShaderStage
{
public:
ShaderStageForValidation(Graphics *gfx, StageType stage, const std::string &glsl, bool gles)
: ShaderStage(gfx, stage, glsl, gles, "")
{}
virtual ~ShaderStageForValidation() {}
ptrdiff_t getHandle() const override { return 0; }
bool loadVolatile() override { return true; }
void unloadVolatile() override { }
}; // ShaderStageForValidation
} // graphics
} // love
-8
View File
@@ -52,14 +52,6 @@ enum TextureType
TEXTURE_MAX_ENUM
};
class TextureTooLargeException : public love::Exception
{
public:
TextureTooLargeException(const char *dimname, int pix)
: Exception("Cannot create texture: %s of %d pixels is too large for this system.", dimname, pix)
{}
};
/**
* Base class for 2D textures. All textures can be drawn with Quads, have a
* width and height, and have filter and wrap modes.
+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
+31 -24
View File
@@ -1234,7 +1234,7 @@ int w_newCanvas(lua_State *L)
return 1;
}
static int w_getShaderSource(lua_State *L, int startidx, bool gles, Shader::ShaderSource &source)
static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string &vertexsource, std::string &pixelsource)
{
using namespace love::filesystem;
@@ -1315,17 +1315,17 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, Shader::Shad
// vertex shader code
if (lua_isstring(L, -2))
source.vertex = luax_checkstring(L, -2);
vertexsource = luax_checkstring(L, -2);
else if (has_arg1 && has_arg2)
return luaL_error(L, "Could not parse vertex shader code (missing 'position' function?)");
// pixel shader code
if (lua_isstring(L, -1))
source.pixel = luax_checkstring(L, -1);
pixelsource = luax_checkstring(L, -1);
else if (has_arg1 && has_arg2)
return luaL_error(L, "Could not parse pixel shader code (missing 'effect' function?)");
if (source.vertex.empty() && source.pixel.empty())
if (vertexsource.empty() && pixelsource.empty())
{
// Original args had source code, but effectCodeToGLSL couldn't translate it
for (int i = startidx; i < startidx + 2; i++)
@@ -1342,13 +1342,13 @@ int w_newShader(lua_State *L)
{
bool gles = instance()->getRenderer() == Graphics::RENDERER_OPENGLES;
Shader::ShaderSource source;
w_getShaderSource(L, 1, gles, source);
std::string vertexsource, pixelsource;
w_getShaderSource(L, 1, gles, vertexsource, pixelsource);
bool should_error = false;
try
{
Shader *shader = instance()->newShader(source);
Shader *shader = instance()->newShader(vertexsource, pixelsource);
luax_pushtype(L, shader);
shader->release();
}
@@ -1372,11 +1372,20 @@ int w_validateShader(lua_State *L)
{
bool gles = luax_checkboolean(L, 1);
Shader::ShaderSource source;
w_getShaderSource(L, 2, gles, source);
std::string vertexsource, pixelsource;
w_getShaderSource(L, 2, gles, vertexsource, pixelsource);
bool success = true;
std::string err;
bool success = instance()->validateShader(gles, source, err);
try
{
success = instance()->validateShader(gles, vertexsource, pixelsource, err);
}
catch (love::Exception &e)
{
success = false;
err = e.what();
}
luax_pushboolean(L, success);
@@ -2007,23 +2016,21 @@ int w_setDefaultShaderCode(lua_State *L)
lua_getfield(L, -3, "videopixel");
lua_getfield(L, -4, "arraypixel");
Shader::ShaderSource code;
code.vertex = luax_checkstring(L, -4);
code.pixel = luax_checkstring(L, -3);
Shader::ShaderSource videocode;
videocode.vertex = luax_checkstring(L, -4);
videocode.pixel = luax_checkstring(L, -2);
Shader::ShaderSource arraycode;
arraycode.vertex = luax_checkstring(L, -4);
arraycode.pixel = luax_checkstring(L, -1);
std::string vertex = luax_checkstring(L, -4);
std::string pixel = luax_checkstring(L, -3);
std::string videopixel = luax_checkstring(L, -2);
std::string arraypixel = luax_checkstring(L, -1);
lua_pop(L, 5);
Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i] = code;
Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i] = videocode;
Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i] = arraycode;
Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_PIXEL] = pixel;
Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_PIXEL] = videopixel;
Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_PIXEL] = arraypixel;
}
}