mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Added support for GLSL 3 shaders (GLSL 3.30 and GLSL ES 3.00).
To use GLSL 3 in a shader, the first line of the shader has to be: #pragma language glsl3 glsl1 is the default language. Added “glsl3” graphics feature as part of the table returned by love.graphics.getSupported(). Added love.graphics.validateShader(boolean gles, shadercode). It returns a boolean along with an error string if the shader code has errors for its target language and platform (gles or desktop). Implemented support for Core Profile OpenGL 3.3 in love.graphics. --HG-- branch : minor
This commit is contained in:
@@ -81,8 +81,8 @@ Colorf unGammaCorrectColor(const Colorf &c)
|
||||
|
||||
love::Type Graphics::type("graphics", &Module::type);
|
||||
|
||||
Shader::ShaderSource Graphics::defaultShaderCode[Graphics::RENDERER_MAX_ENUM][2];
|
||||
Shader::ShaderSource Graphics::defaultVideoShaderCode[Graphics::RENDERER_MAX_ENUM][2];
|
||||
Shader::ShaderSource Graphics::defaultShaderCode[Shader::LANGUAGE_MAX_ENUM][2];
|
||||
Shader::ShaderSource Graphics::defaultVideoShaderCode[Shader::LANGUAGE_MAX_ENUM][2];
|
||||
|
||||
Graphics::Graphics()
|
||||
: width(0)
|
||||
@@ -101,6 +101,9 @@ Graphics::Graphics()
|
||||
|
||||
pixelScaleStack.reserve(16);
|
||||
pixelScaleStack.push_back(1);
|
||||
|
||||
if (!Shader::initialize())
|
||||
throw love::Exception("Shader support failed to initialize!");
|
||||
}
|
||||
|
||||
Graphics::~Graphics()
|
||||
@@ -124,6 +127,8 @@ Graphics::~Graphics()
|
||||
delete streamBufferState.vb[0];
|
||||
delete streamBufferState.vb[1];
|
||||
delete streamBufferState.indexBuffer;
|
||||
|
||||
Shader::deinitialize();
|
||||
}
|
||||
|
||||
Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
|
||||
@@ -131,6 +136,11 @@ Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
|
||||
return new Quad(v, sw, sh);
|
||||
}
|
||||
|
||||
bool Graphics::validateShader(bool gles, const Shader::ShaderSource &source, std::string &err)
|
||||
{
|
||||
return Shader::validate(this, gles, source, true, err);
|
||||
}
|
||||
|
||||
int Graphics::getWidth() const
|
||||
{
|
||||
return width;
|
||||
@@ -544,6 +554,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
|
||||
|
||||
int totalvertices = state.vertexCount + req.vertexCount;
|
||||
|
||||
// We only support uint16 index buffers for now.
|
||||
if (totalvertices > LOVE_UINT16_MAX && req.indexMode != TriangleIndexMode::NONE)
|
||||
shouldflush = true;
|
||||
|
||||
@@ -555,45 +566,39 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (req.formats[i] != CommonFormat::NONE)
|
||||
{
|
||||
size_t stride = getFormatStride(req.formats[i]);
|
||||
size_t datasize = stride * totalvertices;
|
||||
if (req.formats[i] == CommonFormat::NONE)
|
||||
continue;
|
||||
|
||||
size_t cursize = state.vb[i]->getSize();
|
||||
size_t stride = getFormatStride(req.formats[i]);
|
||||
size_t datasize = stride * totalvertices;
|
||||
|
||||
if (datasize > cursize)
|
||||
{
|
||||
shouldflush = true;
|
||||
|
||||
if (stride * req.vertexCount > cursize)
|
||||
{
|
||||
buffersizes[i] = std::max(datasize, cursize * 2);
|
||||
shouldresize = true;
|
||||
}
|
||||
}
|
||||
|
||||
newdatasizes[i] = stride * req.vertexCount;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16);
|
||||
size_t cursize = state.indexBuffer->getSize();
|
||||
|
||||
if (datasize > cursize)
|
||||
{
|
||||
if (state.vbMap[i].data != nullptr && datasize > state.vbMap[i].size)
|
||||
shouldflush = true;
|
||||
|
||||
if (reqIndexSize > cursize)
|
||||
{
|
||||
buffersizes[2] = std::max(datasize, cursize * 2);
|
||||
shouldresize = true;
|
||||
}
|
||||
if (datasize > state.vb[i]->getSize())
|
||||
{
|
||||
buffersizes[i] = std::max(datasize, state.vb[i]->getSize() * 2);
|
||||
shouldresize = true;
|
||||
}
|
||||
|
||||
newdatasizes[i] = stride * req.vertexCount;
|
||||
}
|
||||
|
||||
if (req.indexMode != TriangleIndexMode::NONE)
|
||||
{
|
||||
size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16);
|
||||
|
||||
if (state.indexBufferMap.data != nullptr && datasize > state.indexBufferMap.size)
|
||||
shouldflush = true;
|
||||
|
||||
if (datasize > state.indexBuffer->getSize())
|
||||
{
|
||||
buffersizes[2] = std::max(datasize, state.indexBuffer->getSize() * 2);
|
||||
shouldresize = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldflush)
|
||||
if (shouldflush || shouldresize)
|
||||
{
|
||||
flushStreamDraws();
|
||||
|
||||
@@ -609,30 +614,48 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (state.vb[i]->getSize() < buffersizes[i])
|
||||
state.vb[i]->setSize(buffersizes[i]);
|
||||
{
|
||||
delete state.vb[i];
|
||||
state.vb[i] = newStreamBuffer(BUFFER_VERTEX, buffersizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.indexBuffer->getSize() < buffersizes[2])
|
||||
state.indexBuffer->setSize(buffersizes[2]);
|
||||
{
|
||||
delete state.indexBuffer;
|
||||
state.indexBuffer = newStreamBuffer(BUFFER_INDEX, buffersizes[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.indexMode != TriangleIndexMode::NONE)
|
||||
{
|
||||
uint16 *indices = (uint16 *) state.indexBuffer->getOffsetData();
|
||||
if (state.indexBufferMap.data == nullptr)
|
||||
state.indexBufferMap = state.indexBuffer->map(reqIndexSize);
|
||||
|
||||
uint16 *indices = (uint16 *) state.indexBufferMap.data;
|
||||
fillIndices(req.indexMode, state.vertexCount, req.vertexCount, indices);
|
||||
state.indexBuffer->incrementOffset(reqIndexSize);
|
||||
|
||||
state.indexBufferMap.data += reqIndexSize;
|
||||
}
|
||||
|
||||
StreamVertexData d;
|
||||
d.stream[0] = state.vb[0]->getOffsetData();
|
||||
d.stream[1] = state.vb[1]->getOffsetData();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (newdatasizes[i] > 0)
|
||||
{
|
||||
if (state.vbMap[i].data == nullptr)
|
||||
state.vbMap[i] = state.vb[i]->map(newdatasizes[i]);
|
||||
|
||||
d.stream[i] = state.vbMap[i].data;
|
||||
|
||||
state.vbMap[i].data += newdatasizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
state.vertexCount += req.vertexCount;
|
||||
state.indexCount += reqIndexCount;
|
||||
|
||||
state.vb[0]->incrementOffset(newdatasizes[0]);
|
||||
state.vb[1]->incrementOffset(newdatasizes[1]);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -1107,6 +1130,11 @@ Vector Graphics::inverseTransformPoint(Vector point)
|
||||
return p;
|
||||
}
|
||||
|
||||
const Shader::ShaderSource &Graphics::getCurrentDefaultShaderCode() const
|
||||
{
|
||||
return defaultShaderCode[getShaderLanguageTarget()][isGammaCorrect() ? 1 : 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
**/
|
||||
@@ -1308,8 +1336,9 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM>::Entry Graphics::featur
|
||||
{ "multicanvasformats", FEATURE_MULTI_CANVAS_FORMATS },
|
||||
{ "clampzero", FEATURE_CLAMP_ZERO },
|
||||
{ "lighten", FEATURE_LIGHTEN },
|
||||
{ "fullnpot", FEATURE_FULL_NPOT },
|
||||
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
|
||||
{ "fullnpot", FEATURE_FULL_NPOT },
|
||||
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
|
||||
{ "glsl3", FEATURE_GLSL3 },
|
||||
};
|
||||
|
||||
StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries));
|
||||
|
||||
@@ -166,6 +166,7 @@ public:
|
||||
FEATURE_LIGHTEN,
|
||||
FEATURE_FULL_NPOT,
|
||||
FEATURE_PIXEL_SHADER_HIGHP,
|
||||
FEATURE_GLSL3,
|
||||
FEATURE_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -313,6 +314,8 @@ public:
|
||||
|
||||
virtual Shader *newShader(const Shader::ShaderSource &source) = 0;
|
||||
|
||||
bool validateShader(bool gles, const Shader::ShaderSource &source, std::string &err);
|
||||
|
||||
/**
|
||||
* Resets the current color, background color, line style, and so forth.
|
||||
**/
|
||||
@@ -634,6 +637,11 @@ public:
|
||||
**/
|
||||
virtual double getSystemLimit(SystemLimit limittype) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the renderer used by love.graphics.
|
||||
**/
|
||||
virtual Renderer getRenderer() const = 0;
|
||||
|
||||
/**
|
||||
* Returns system-dependent renderer information.
|
||||
* Returned strings can vary greatly between systems! Do not rely on it for
|
||||
@@ -667,6 +675,9 @@ public:
|
||||
virtual void flushStreamDraws() = 0;
|
||||
StreamVertexData requestStreamDraw(const StreamDrawRequest &request);
|
||||
|
||||
virtual Shader::Language getShaderLanguageTarget() const = 0;
|
||||
const Shader::ShaderSource &getCurrentDefaultShaderCode() const;
|
||||
|
||||
template <typename T>
|
||||
T *getScratchBuffer(size_t count)
|
||||
{
|
||||
@@ -712,8 +723,8 @@ public:
|
||||
static bool getConstant(StackType in, const char *&out);
|
||||
|
||||
// Default shader code (a shader is always required internally.)
|
||||
static Shader::ShaderSource defaultShaderCode[RENDERER_MAX_ENUM][2];
|
||||
static Shader::ShaderSource defaultVideoShaderCode[RENDERER_MAX_ENUM][2];
|
||||
static Shader::ShaderSource defaultShaderCode[Shader::LANGUAGE_MAX_ENUM][2];
|
||||
static Shader::ShaderSource defaultVideoShaderCode[Shader::LANGUAGE_MAX_ENUM][2];
|
||||
|
||||
protected:
|
||||
|
||||
@@ -756,6 +767,7 @@ protected:
|
||||
{
|
||||
StreamBuffer *vb[2];
|
||||
StreamBuffer *indexBuffer = nullptr;
|
||||
|
||||
vertex::PrimitiveMode primitiveMode = vertex::PrimitiveMode::TRIANGLES;
|
||||
vertex::CommonFormat formats[2];
|
||||
StrongRef<Texture> texture;
|
||||
@@ -763,13 +775,19 @@ protected:
|
||||
int vertexCount = 0;
|
||||
int indexCount = 0;
|
||||
|
||||
StreamBuffer::MapInfo vbMap[2];
|
||||
StreamBuffer::MapInfo indexBufferMap = StreamBuffer::MapInfo();
|
||||
|
||||
StreamBufferState()
|
||||
{
|
||||
vb[0] = vb[1] = nullptr;
|
||||
formats[0] = formats[1] = vertex::CommonFormat::NONE;
|
||||
vbMap[0] = vbMap[1] = StreamBuffer::MapInfo();
|
||||
}
|
||||
};
|
||||
|
||||
virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;
|
||||
|
||||
void restoreState(const DisplayState &s);
|
||||
void restoreStateChecked(const DisplayState &s);
|
||||
|
||||
|
||||
+213
-23
@@ -20,6 +20,110 @@
|
||||
|
||||
// LOVE
|
||||
#include "Shader.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
// glslang
|
||||
#include "libraries/glslang/glslang/Public/ShaderLang.h"
|
||||
|
||||
// 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
|
||||
{
|
||||
@@ -31,6 +135,19 @@ Shader *Shader::current = nullptr;
|
||||
Shader *Shader::defaultShader = nullptr;
|
||||
Shader *Shader::defaultVideoShader = nullptr;
|
||||
|
||||
Shader::Shader(const ShaderSource &source)
|
||||
: shaderSource(source)
|
||||
{
|
||||
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))
|
||||
throw love::Exception("%s", err.c_str());
|
||||
}
|
||||
|
||||
Shader::~Shader()
|
||||
{
|
||||
if (defaultShader == this)
|
||||
@@ -56,6 +173,92 @@ void Shader::attachDefault()
|
||||
current = nullptr;
|
||||
}
|
||||
|
||||
bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err)
|
||||
{
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
{
|
||||
err = "Error validating shader: no source code!";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool supportsGLSL3 = gfx->isSupported(Graphics::FEATURE_GLSL3);
|
||||
|
||||
int defaultversion = gles ? 100 : 120;
|
||||
EProfile defaultprofile = gles ? EEsProfile : 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;
|
||||
|
||||
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;
|
||||
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 (!program.link(EShMsgDefault))
|
||||
{
|
||||
err = "Cannot compile shader:\n\n" + std::string(program.getInfoLog()) + "\n" + std::string(program.getInfoDebugLog());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Shader::initialize()
|
||||
{
|
||||
return glslang::InitializeProcess();
|
||||
}
|
||||
|
||||
void Shader::deinitialize()
|
||||
{
|
||||
glslang::FinalizeProcess();
|
||||
}
|
||||
|
||||
bool Shader::getConstant(const char *in, Language &out)
|
||||
{
|
||||
return languages.find(in, out);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -66,16 +269,6 @@ bool Shader::getConstant(ShaderStage in, const char *&out)
|
||||
return stageNames.find(in, out);
|
||||
}
|
||||
|
||||
bool Shader::getConstant(const char *in, UniformType &out)
|
||||
{
|
||||
return uniformTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Shader::getConstant(UniformType in, const char *&out)
|
||||
{
|
||||
return uniformTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Shader::getConstant(const char *in, VertexAttribID &out)
|
||||
{
|
||||
return attribNames.find(in, out);
|
||||
@@ -96,6 +289,16 @@ bool Shader::getConstant(BuiltinUniform in, const char *&out)
|
||||
return builtinNames.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry Shader::languageEntries[] =
|
||||
{
|
||||
{"glsl1", LANGUAGE_GLSL1 },
|
||||
{"glsles1", LANGUAGE_GLSLES1},
|
||||
{"glsl3", LANGUAGE_GLSL3 },
|
||||
{"glsles3", LANGUAGE_GLSLES3},
|
||||
};
|
||||
|
||||
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", Shader::STAGE_VERTEX},
|
||||
@@ -104,19 +307,6 @@ StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM>::Entry Shader::stageNameE
|
||||
|
||||
StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM> Shader::stageNames(Shader::stageNameEntries, sizeof(Shader::stageNameEntries));
|
||||
|
||||
StringMap<Shader::UniformType, Shader::UNIFORM_MAX_ENUM>::Entry Shader::uniformTypeEntries[] =
|
||||
{
|
||||
{"float", Shader::UNIFORM_FLOAT},
|
||||
{"matrix", Shader::UNIFORM_MATRIX},
|
||||
{"int", Shader::UNIFORM_INT},
|
||||
{"uint", Shader::UNIFORM_UINT},
|
||||
{"bool", Shader::UNIFORM_BOOL},
|
||||
{"image", Shader::UNIFORM_SAMPLER},
|
||||
{"unknown", Shader::UNIFORM_UNKNOWN},
|
||||
};
|
||||
|
||||
StringMap<Shader::UniformType, Shader::UNIFORM_MAX_ENUM> Shader::uniformTypes(Shader::uniformTypeEntries, sizeof(Shader::uniformTypeEntries));
|
||||
|
||||
StringMap<VertexAttribID, ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
|
||||
{
|
||||
{"VertexPosition", ATTRIB_POS},
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace love
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class Graphics;
|
||||
|
||||
// A GLSL shader
|
||||
class Shader : public Object
|
||||
{
|
||||
@@ -43,6 +45,15 @@ public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
enum Language
|
||||
{
|
||||
LANGUAGE_GLSL1,
|
||||
LANGUAGE_GLSLES1,
|
||||
LANGUAGE_GLSL3,
|
||||
LANGUAGE_GLSLES3,
|
||||
LANGUAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
enum ShaderStage
|
||||
{
|
||||
STAGE_VERTEX,
|
||||
@@ -122,6 +133,7 @@ public:
|
||||
static Shader *defaultShader;
|
||||
static Shader *defaultVideoShader;
|
||||
|
||||
Shader(const ShaderSource &source);
|
||||
virtual ~Shader();
|
||||
|
||||
/**
|
||||
@@ -158,26 +170,36 @@ public:
|
||||
**/
|
||||
virtual void setVideoTextures(ptrdiff_t ytexture, ptrdiff_t cbtexture, ptrdiff_t crtexture) = 0;
|
||||
|
||||
static bool validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err);
|
||||
|
||||
static bool initialize();
|
||||
static void deinitialize();
|
||||
|
||||
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, UniformType &out);
|
||||
static bool getConstant(UniformType in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, VertexAttribID &out);
|
||||
static bool getConstant(VertexAttribID in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, BuiltinUniform &out);
|
||||
static bool getConstant(BuiltinUniform in, const char *&out);
|
||||
|
||||
protected:
|
||||
|
||||
// Source code used for this Shader.
|
||||
ShaderSource shaderSource;
|
||||
|
||||
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;
|
||||
|
||||
static StringMap<UniformType, UNIFORM_MAX_ENUM>::Entry uniformTypeEntries[];
|
||||
static StringMap<UniformType, UNIFORM_MAX_ENUM> uniformTypes;
|
||||
|
||||
// Names for the generic vertex attributes used by love.
|
||||
static StringMap<VertexAttribID, ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
|
||||
static StringMap<VertexAttribID, ATTRIB_MAX_ENUM> attribNames;
|
||||
|
||||
@@ -26,54 +26,10 @@ namespace love
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
StreamBuffer::StreamBuffer(Mode mode, size_t size)
|
||||
: data(nullptr)
|
||||
, offset(0)
|
||||
, totalSize(size)
|
||||
StreamBuffer::StreamBuffer(BufferType mode, size_t size)
|
||||
: bufferSize(size)
|
||||
, mode(mode)
|
||||
{
|
||||
setSize(size);
|
||||
}
|
||||
|
||||
StreamBuffer::~StreamBuffer()
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void *StreamBuffer::getData() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
void *StreamBuffer::getOffsetData() const
|
||||
{
|
||||
return data + offset;
|
||||
}
|
||||
|
||||
void StreamBuffer::incrementOffset(size_t amount)
|
||||
{
|
||||
offset += amount;
|
||||
}
|
||||
|
||||
void StreamBuffer::resetOffset()
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
void StreamBuffer::setSize(size_t size)
|
||||
{
|
||||
delete[] data;
|
||||
|
||||
try
|
||||
{
|
||||
data = new uint8[size];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
this->totalSize = size;
|
||||
}
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/int.h"
|
||||
#include "vertex.h"
|
||||
|
||||
// C
|
||||
#include <cstddef>
|
||||
@@ -31,45 +32,40 @@ namespace love
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// TODO: This class will need to be changed significantly in the future to
|
||||
// accomodate non-client-side vertex/index data.
|
||||
class StreamBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
enum Mode
|
||||
struct MapInfo
|
||||
{
|
||||
MODE_VERTEX,
|
||||
MODE_INDEX,
|
||||
uint8 *data = nullptr;
|
||||
size_t size = 0;
|
||||
|
||||
MapInfo() {}
|
||||
|
||||
MapInfo(uint8 *data, size_t size)
|
||||
: data(data)
|
||||
, size(size)
|
||||
{}
|
||||
};
|
||||
|
||||
StreamBuffer(Mode mode, size_t size);
|
||||
~StreamBuffer();
|
||||
virtual ~StreamBuffer() {}
|
||||
|
||||
void *getData() const;
|
||||
void *getOffsetData() const;
|
||||
size_t getSize() const { return bufferSize; }
|
||||
BufferType getMode() const { return mode; }
|
||||
|
||||
void incrementOffset(size_t amount);
|
||||
void resetOffset();
|
||||
virtual MapInfo map(size_t minsize) = 0;
|
||||
virtual size_t unmap(size_t usedsize) = 0;
|
||||
virtual void markUsed(size_t usedsize) = 0;
|
||||
|
||||
void setSize(size_t size);
|
||||
virtual ptrdiff_t getHandle() const = 0;
|
||||
|
||||
size_t getSize() const
|
||||
{
|
||||
return totalSize;
|
||||
}
|
||||
protected:
|
||||
|
||||
Mode getMode() const
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
StreamBuffer(BufferType mode, size_t size);
|
||||
|
||||
private:
|
||||
|
||||
uint8 *data;
|
||||
size_t offset;
|
||||
size_t totalSize;
|
||||
Mode mode;
|
||||
size_t bufferSize;
|
||||
BufferType mode;
|
||||
|
||||
}; // StreamBuffer
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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 "BufferSync.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
BufferSync::~BufferSync()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void BufferSync::lock(size_t start, size_t length)
|
||||
{
|
||||
Range range = {start, length};
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
locks.emplace_back(range, sync);
|
||||
}
|
||||
|
||||
void BufferSync::wait(size_t start, size_t length)
|
||||
{
|
||||
Range range = {start, length};
|
||||
int lockcount = (int) locks.size();
|
||||
|
||||
for (int i = 0; i < lockcount; i++)
|
||||
{
|
||||
if (range.overlaps(locks[i].range))
|
||||
{
|
||||
syncWait(locks[i].sync);
|
||||
glDeleteSync(locks[i].sync);
|
||||
|
||||
locks[i] = locks[lockcount - 1];
|
||||
locks.pop_back();
|
||||
|
||||
--lockcount;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BufferSync::cleanup()
|
||||
{
|
||||
for (const auto &lock : locks)
|
||||
glDeleteSync(lock.sync);
|
||||
|
||||
locks.clear();
|
||||
}
|
||||
|
||||
void BufferSync::syncWait(GLsync sync)
|
||||
{
|
||||
GLbitfield flags = 0;
|
||||
GLuint64 duration = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
GLenum status = glClientWaitSync(sync, flags, duration);
|
||||
|
||||
if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED)
|
||||
return;
|
||||
|
||||
if (status == GL_WAIT_FAILED)
|
||||
return;
|
||||
|
||||
flags = GL_SYNC_FLUSH_COMMANDS_BIT;
|
||||
duration = 1000000000; // 1 second in nanoseconds.
|
||||
}
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "OpenGL.h"
|
||||
|
||||
// C
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class BufferSync
|
||||
{
|
||||
public:
|
||||
|
||||
~BufferSync();
|
||||
|
||||
void lock(size_t start, size_t length);
|
||||
void wait(size_t start, size_t length);
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
|
||||
struct Range
|
||||
{
|
||||
size_t offset;
|
||||
size_t length;
|
||||
|
||||
bool overlaps(const Range &other) const
|
||||
{
|
||||
return offset < (other.offset + other.length)
|
||||
&& other.offset < (offset + length);
|
||||
}
|
||||
};
|
||||
|
||||
struct Lock
|
||||
{
|
||||
Range range;
|
||||
GLsync sync;
|
||||
|
||||
Lock(const Range &range, GLsync sync)
|
||||
: range(range)
|
||||
, sync(sync)
|
||||
{}
|
||||
};
|
||||
|
||||
void syncWait(GLsync sync);
|
||||
|
||||
std::vector<Lock> locks;
|
||||
|
||||
}; // BufferSync
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "Graphics.h"
|
||||
#include "font/Font.h"
|
||||
#include "Font.h"
|
||||
#include "graphics/Polyline.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "math/MathModule.h"
|
||||
#include "window/Window.h"
|
||||
|
||||
@@ -95,6 +95,11 @@ const char *Graphics::getName() const
|
||||
return "love.graphics.opengl";
|
||||
}
|
||||
|
||||
love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferType type, size_t size)
|
||||
{
|
||||
return CreateStreamBuffer(type, size);
|
||||
}
|
||||
|
||||
Image *Graphics::newImage(const std::vector<love::image::ImageData *> &data, const Image::Settings &settings)
|
||||
{
|
||||
return new Image(data, settings);
|
||||
@@ -215,6 +220,13 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
|
||||
|
||||
// Okay, setup OpenGL.
|
||||
gl.initContext();
|
||||
|
||||
if (gl.isCoreProfile())
|
||||
{
|
||||
glGenVertexArrays(1, &mainVAO);
|
||||
glBindVertexArray(mainVAO);
|
||||
}
|
||||
|
||||
gl.setupContext();
|
||||
|
||||
created = true;
|
||||
@@ -237,12 +249,6 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
if (gl.isCoreProfile())
|
||||
{
|
||||
glGenVertexArrays(1, &mainVAO);
|
||||
glBindVertexArray(mainVAO);
|
||||
}
|
||||
|
||||
gl.setTextureUnit(0);
|
||||
|
||||
// Set pixel row alignment
|
||||
@@ -274,9 +280,9 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
|
||||
{
|
||||
// Initial sizes that should be good enough for most cases. It will
|
||||
// resize to fit if needed, later.
|
||||
streamBufferState.vb[0] = new StreamBuffer(StreamBuffer::MODE_VERTEX, 1024 * 1024 * 1);
|
||||
streamBufferState.vb[1] = new StreamBuffer(StreamBuffer::MODE_VERTEX, 256 * 1024 * 1);
|
||||
streamBufferState.indexBuffer = new StreamBuffer(StreamBuffer::MODE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
|
||||
streamBufferState.vb[0] = CreateStreamBuffer(BUFFER_VERTEX, 1024 * 1024 * 1);
|
||||
streamBufferState.vb[1] = CreateStreamBuffer(BUFFER_VERTEX, 256 * 1024 * 1);
|
||||
streamBufferState.indexBuffer = CreateStreamBuffer(BUFFER_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
|
||||
}
|
||||
|
||||
// Reload all volatile objects.
|
||||
@@ -299,15 +305,15 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
|
||||
// We always need a default shader.
|
||||
if (!Shader::defaultShader)
|
||||
{
|
||||
Renderer renderer = GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
|
||||
Shader::defaultShader = newShader(defaultShaderCode[renderer][gammacorrect]);
|
||||
Shader::Language target = getShaderLanguageTarget();
|
||||
Shader::defaultShader = newShader(defaultShaderCode[target][gammacorrect]);
|
||||
}
|
||||
|
||||
// and a default video shader.
|
||||
if (!Shader::defaultVideoShader)
|
||||
{
|
||||
Renderer renderer = GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
|
||||
Shader::defaultVideoShader = newShader(defaultVideoShaderCode[renderer][gammacorrect]);
|
||||
Shader::Language target = getShaderLanguageTarget();
|
||||
Shader::defaultVideoShader = newShader(defaultVideoShaderCode[target][gammacorrect]);
|
||||
}
|
||||
|
||||
// A shader should always be active, but the default shader shouldn't be
|
||||
@@ -365,7 +371,7 @@ void Graphics::flushStreamDraws()
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
const auto &sbstate = streamBufferState;
|
||||
auto &sbstate = streamBufferState;
|
||||
|
||||
if (sbstate.vertexCount == 0 && sbstate.indexCount == 0)
|
||||
return;
|
||||
@@ -373,19 +379,22 @@ void Graphics::flushStreamDraws()
|
||||
OpenGL::TempDebugGroup debuggroup("Stream vertices flush and draw");
|
||||
|
||||
uint32 attribs = 0;
|
||||
size_t usedsizes[3] = {0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (sbstate.formats[i] == CommonFormat::NONE)
|
||||
continue;
|
||||
|
||||
StreamBuffer *buffer = sbstate.vb[i];
|
||||
|
||||
buffer->resetOffset();
|
||||
ptrdiff_t offset = (ptrdiff_t) buffer->getData();
|
||||
GLsizei stride = (GLsizei) getFormatStride(sbstate.formats[i]);
|
||||
usedsizes[i] = stride * sbstate.vertexCount;
|
||||
|
||||
gl.bindBuffer(BUFFER_VERTEX, 0);
|
||||
love::graphics::StreamBuffer *buffer = sbstate.vb[i];
|
||||
|
||||
gl.bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle());
|
||||
size_t offset = buffer->unmap(usedsizes[i]);
|
||||
|
||||
sbstate.vbMap[i] = StreamBuffer::MapInfo();
|
||||
|
||||
switch (sbstate.formats[i])
|
||||
{
|
||||
@@ -451,15 +460,27 @@ void Graphics::flushStreamDraws()
|
||||
|
||||
if (sbstate.indexCount > 0)
|
||||
{
|
||||
sbstate.indexBuffer->resetOffset();
|
||||
ptrdiff_t offset = (ptrdiff_t) sbstate.indexBuffer->getData();
|
||||
usedsizes[2] = sizeof(uint16) * sbstate.indexCount;
|
||||
|
||||
gl.bindBuffer(BUFFER_INDEX, (GLuint) sbstate.indexBuffer->getHandle());
|
||||
size_t offset = sbstate.indexBuffer->unmap(usedsizes[2]);
|
||||
|
||||
sbstate.indexBufferMap = StreamBuffer::MapInfo();
|
||||
|
||||
gl.bindBuffer(BUFFER_INDEX, 0);
|
||||
gl.drawElements(glmode, sbstate.indexCount, GL_UNSIGNED_SHORT, BUFFER_OFFSET(offset));
|
||||
}
|
||||
else
|
||||
gl.drawArrays(glmode, 0, sbstate.vertexCount);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (usedsizes[i] > 0)
|
||||
sbstate.vb[i]->markUsed(usedsizes[i]);
|
||||
}
|
||||
|
||||
if (usedsizes[2] > 0)
|
||||
sbstate.indexBuffer->markUsed(usedsizes[2]);
|
||||
|
||||
popTransform();
|
||||
|
||||
if (attribs & ATTRIB_CONSTANTCOLOR)
|
||||
@@ -1376,6 +1397,11 @@ void Graphics::setWireframe(bool enable)
|
||||
states.back().wireframe = enable;
|
||||
}
|
||||
|
||||
Graphics::Renderer Graphics::getRenderer() const
|
||||
{
|
||||
return GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
|
||||
}
|
||||
|
||||
Graphics::RendererInfo Graphics::getRendererInfo() const
|
||||
{
|
||||
RendererInfo info;
|
||||
@@ -1459,11 +1485,25 @@ bool Graphics::isSupported(Feature feature) const
|
||||
return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot;
|
||||
case FEATURE_PIXEL_SHADER_HIGHP:
|
||||
return gl.isPixelShaderHighpSupported();
|
||||
case FEATURE_GLSL3:
|
||||
return GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Shader::Language Graphics::getShaderLanguageTarget() const
|
||||
{
|
||||
if (gl.isCoreProfile())
|
||||
return Shader::LANGUAGE_GLSL3;
|
||||
else if (GLAD_ES_VERSION_3_0)
|
||||
return Shader::LANGUAGE_GLSLES3;
|
||||
else if (GLAD_ES_VERSION_2_0)
|
||||
return Shader::LANGUAGE_GLSLES1;
|
||||
else
|
||||
return Shader::LANGUAGE_GLSL1;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -129,9 +129,12 @@ public:
|
||||
|
||||
bool isSupported(Feature feature) const override;
|
||||
double getSystemLimit(SystemLimit limittype) const override;
|
||||
Renderer getRenderer() const override;
|
||||
RendererInfo getRendererInfo() const override;
|
||||
Stats getStats() const override;
|
||||
|
||||
Shader::Language getShaderLanguageTarget() const override;
|
||||
|
||||
private:
|
||||
|
||||
struct CachedRenderbuffer
|
||||
@@ -143,6 +146,8 @@ private:
|
||||
GLuint renderbuffer;
|
||||
};
|
||||
|
||||
love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
|
||||
|
||||
void endPass();
|
||||
void bindCachedFBO(const std::vector<love::graphics::Canvas *> &canvases);
|
||||
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace opengl
|
||||
{
|
||||
|
||||
Shader::Shader(const ShaderSource &source)
|
||||
: shaderSource(source)
|
||||
: love::graphics::Shader(source)
|
||||
, program(0)
|
||||
, builtinUniforms()
|
||||
, builtinAttributes()
|
||||
@@ -46,9 +46,6 @@ Shader::Shader(const ShaderSource &source)
|
||||
, lastPointSize(0.0f)
|
||||
, videoTextureUnits()
|
||||
{
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
throw love::Exception("Cannot create shader: no source code!");
|
||||
|
||||
// load shader source and create program object
|
||||
loadVolatile();
|
||||
}
|
||||
@@ -360,14 +357,12 @@ bool Shader::loadVolatile()
|
||||
|
||||
std::vector<GLuint> shaderids;
|
||||
|
||||
bool gammacorrect = graphics::isGammaCorrect();
|
||||
const ShaderSource *defaults = &Graphics::defaultShaderCode[Graphics::RENDERER_OPENGL][gammacorrect ? 1 : 0];
|
||||
if (GLAD_ES_VERSION_2_0)
|
||||
defaults = &Graphics::defaultShaderCode[Graphics::RENDERER_OPENGLES][gammacorrect ? 1 : 0];
|
||||
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;
|
||||
const std::string &vertexcode = shaderSource.vertex.empty() ? defaults.vertex : shaderSource.vertex;
|
||||
const std::string &pixelcode = shaderSource.pixel.empty() ? defaults.pixel : shaderSource.pixel;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -102,9 +102,6 @@ private:
|
||||
// Get any warnings or errors generated only by the shader program object.
|
||||
std::string getProgramWarnings() const;
|
||||
|
||||
// Source code used for this Shader.
|
||||
ShaderSource shaderSource;
|
||||
|
||||
// Shader compiler warning strings for individual shader stages.
|
||||
std::map<ShaderStage, std::string> shaderWarnings;
|
||||
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* 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 "StreamBuffer.h"
|
||||
#include "OpenGL.h"
|
||||
#include "BufferSync.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "common/Exception.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
static int BUFFER_FRAMES = 3;
|
||||
|
||||
class StreamBufferClientMemory final : public love::graphics::StreamBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
StreamBufferClientMemory(BufferType mode, size_t size)
|
||||
: love::graphics::StreamBuffer(mode, size)
|
||||
, data(nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
data = new uint8[size];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~StreamBufferClientMemory()
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
MapInfo map(size_t /*minsize*/) override
|
||||
{
|
||||
return MapInfo(data, bufferSize);
|
||||
}
|
||||
|
||||
size_t unmap(size_t /*usedsize*/) override
|
||||
{
|
||||
return (size_t) data;
|
||||
}
|
||||
|
||||
void markUsed(size_t /*usedsize*/) override { }
|
||||
ptrdiff_t getHandle() const override { return 0; }
|
||||
|
||||
private:
|
||||
|
||||
uint8 *data;
|
||||
|
||||
}; // StreamBufferClientMemory
|
||||
|
||||
class StreamBufferSubDataOrphan : public love::graphics::StreamBuffer, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
StreamBufferSubDataOrphan(BufferType mode, size_t size)
|
||||
: love::graphics::StreamBuffer(mode, size)
|
||||
, vbo(0)
|
||||
, glMode(OpenGL::getGLBufferType(mode))
|
||||
, data(nullptr)
|
||||
, offset(0)
|
||||
{
|
||||
try
|
||||
{
|
||||
data = new uint8[size];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
loadVolatile();
|
||||
}
|
||||
|
||||
virtual ~StreamBufferSubDataOrphan()
|
||||
{
|
||||
unloadVolatile();
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
MapInfo map(size_t minsize) override
|
||||
{
|
||||
if (offset + minsize > bufferSize)
|
||||
{
|
||||
offset = 0;
|
||||
glBufferData(glMode, bufferSize, nullptr, GL_STREAM_DRAW);
|
||||
}
|
||||
|
||||
return MapInfo(data, bufferSize - offset);
|
||||
}
|
||||
|
||||
size_t unmap(size_t usedsize) override
|
||||
{
|
||||
gl.bindBuffer(mode, vbo);
|
||||
glBufferSubData(glMode, offset, usedsize, data);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void markUsed(size_t usedsize) override
|
||||
{
|
||||
offset += usedsize;
|
||||
}
|
||||
|
||||
ptrdiff_t getHandle() const override { return vbo; }
|
||||
|
||||
bool loadVolatile() override
|
||||
{
|
||||
if (vbo != 0)
|
||||
return true;
|
||||
|
||||
glGenBuffers(1, &vbo);
|
||||
gl.bindBuffer(mode, vbo);
|
||||
glBufferData(glMode, bufferSize, nullptr, GL_STREAM_DRAW);
|
||||
|
||||
offset = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void unloadVolatile() override
|
||||
{
|
||||
if (vbo == 0)
|
||||
return;
|
||||
|
||||
gl.deleteBuffer(vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
GLuint vbo;
|
||||
GLenum glMode;
|
||||
|
||||
uint8 *data;
|
||||
|
||||
size_t offset;
|
||||
|
||||
}; // StreamBufferSubDataOrphan
|
||||
|
||||
class StreamBufferMapSync final : public love::graphics::StreamBuffer, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
StreamBufferMapSync(BufferType type, size_t size)
|
||||
: love::graphics::StreamBuffer(type, size)
|
||||
, vbo(0)
|
||||
, gpuReadOffset(0)
|
||||
, glMode(OpenGL::getGLBufferType(mode))
|
||||
{
|
||||
loadVolatile();
|
||||
}
|
||||
|
||||
~StreamBufferMapSync()
|
||||
{
|
||||
unloadVolatile();
|
||||
}
|
||||
|
||||
MapInfo map(size_t minsize) override
|
||||
{
|
||||
gl.bindBuffer(mode, vbo);
|
||||
|
||||
if (gpuReadOffset + minsize > bufferSize * BUFFER_FRAMES)
|
||||
gpuReadOffset = 0;
|
||||
|
||||
MapInfo info;
|
||||
info.size = bufferSize - (gpuReadOffset % bufferSize);
|
||||
|
||||
sync.wait(gpuReadOffset, info.size);
|
||||
|
||||
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
||||
|
||||
info.data = (uint8 *) glMapBufferRange(glMode, gpuReadOffset, info.size, flags);
|
||||
return info;
|
||||
}
|
||||
|
||||
size_t unmap(size_t usedsize) override
|
||||
{
|
||||
gl.bindBuffer(mode, vbo);
|
||||
glFlushMappedBufferRange(glMode, 0, usedsize);
|
||||
glUnmapBuffer(glMode);
|
||||
|
||||
return gpuReadOffset;
|
||||
}
|
||||
|
||||
void markUsed(size_t usedsize) override
|
||||
{
|
||||
sync.lock(gpuReadOffset, usedsize);
|
||||
gpuReadOffset += usedsize;
|
||||
}
|
||||
|
||||
ptrdiff_t getHandle() const override { return vbo; }
|
||||
|
||||
bool loadVolatile() override
|
||||
{
|
||||
if (vbo != 0)
|
||||
return true;
|
||||
|
||||
glGenBuffers(1, &vbo);
|
||||
gl.bindBuffer(mode, vbo);
|
||||
glBufferData(glMode, bufferSize * BUFFER_FRAMES, nullptr, GL_STREAM_DRAW);
|
||||
|
||||
gpuReadOffset = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void unloadVolatile() override
|
||||
{
|
||||
if (vbo == 0)
|
||||
return;
|
||||
|
||||
gl.deleteBuffer(vbo);
|
||||
vbo = 0;
|
||||
|
||||
sync.cleanup();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
GLuint vbo;
|
||||
size_t gpuReadOffset;
|
||||
GLenum glMode;
|
||||
|
||||
BufferSync sync;
|
||||
|
||||
}; // StreamBufferMapSync
|
||||
|
||||
class StreamBufferPersistentMapSync final : public love::graphics::StreamBuffer, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
StreamBufferPersistentMapSync(BufferType type, size_t size)
|
||||
: love::graphics::StreamBuffer(type, size)
|
||||
, vbo(0)
|
||||
, gpuReadOffset(0)
|
||||
, glMode(OpenGL::getGLBufferType(mode))
|
||||
, data(nullptr)
|
||||
{
|
||||
loadVolatile();
|
||||
}
|
||||
|
||||
~StreamBufferPersistentMapSync()
|
||||
{
|
||||
unloadVolatile();
|
||||
}
|
||||
|
||||
MapInfo map(size_t minsize) override
|
||||
{
|
||||
if (gpuReadOffset + minsize > bufferSize * BUFFER_FRAMES)
|
||||
gpuReadOffset = 0;
|
||||
|
||||
MapInfo info;
|
||||
info.size = bufferSize - (gpuReadOffset % bufferSize);
|
||||
info.data = data + gpuReadOffset;
|
||||
|
||||
sync.wait(gpuReadOffset, info.size);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
size_t unmap(size_t usedsize) override
|
||||
{
|
||||
gl.bindBuffer(mode, vbo);
|
||||
glFlushMappedBufferRange(glMode, gpuReadOffset, usedsize);
|
||||
|
||||
return gpuReadOffset;
|
||||
}
|
||||
|
||||
void markUsed(size_t usedsize) override
|
||||
{
|
||||
sync.lock(gpuReadOffset, usedsize);
|
||||
gpuReadOffset += usedsize;
|
||||
}
|
||||
|
||||
ptrdiff_t getHandle() const override { return vbo; }
|
||||
|
||||
bool loadVolatile() override
|
||||
{
|
||||
if (vbo != 0)
|
||||
return true;
|
||||
|
||||
glGenBuffers(1, &vbo);
|
||||
gl.bindBuffer(mode, vbo);
|
||||
|
||||
GLbitfield storageflags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
GLbitfield mapflags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
|
||||
glBufferStorage(glMode, bufferSize * BUFFER_FRAMES, nullptr, storageflags);
|
||||
data = (uint8 *) glMapBufferRange(glMode, 0, bufferSize * BUFFER_FRAMES, mapflags);
|
||||
|
||||
gpuReadOffset = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void unloadVolatile() override
|
||||
{
|
||||
if (vbo == 0)
|
||||
return;
|
||||
|
||||
gl.bindBuffer(mode, vbo);
|
||||
glUnmapBuffer(glMode);
|
||||
gl.deleteBuffer(vbo);
|
||||
vbo = 0;
|
||||
|
||||
sync.cleanup();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
GLuint vbo;
|
||||
size_t gpuReadOffset;
|
||||
GLenum glMode;
|
||||
uint8 *data;
|
||||
|
||||
BufferSync sync;
|
||||
|
||||
}; // StreamBufferPersistentMapSync
|
||||
|
||||
love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size)
|
||||
{
|
||||
if (gl.isCoreProfile())
|
||||
{
|
||||
// FIXME: This is disabled until more efficient manual syncing can be
|
||||
// implemented.
|
||||
#if 0
|
||||
if (GLAD_VERSION_4_4 || GLAD_ARB_buffer_storage)
|
||||
return new StreamBufferPersistentMapSync(mode, size);
|
||||
else
|
||||
#endif
|
||||
return new StreamBufferSubDataOrphan(mode, size);
|
||||
}
|
||||
else
|
||||
return new StreamBufferClientMemory(mode, size);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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/StreamBuffer.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -720,15 +720,12 @@ int w_newCanvas(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newShader(lua_State *L)
|
||||
static int w_getShaderSource(lua_State *L, int startidx, bool gles, Shader::ShaderSource &source)
|
||||
{
|
||||
luax_checkgraphicscreated(L);
|
||||
|
||||
// clamp stack to 2 elements
|
||||
lua_settop(L, 2);
|
||||
|
||||
// read any filepath arguments
|
||||
for (int i = 1; i <= 2; i++)
|
||||
for (int i = startidx; i < startidx + 2; i++)
|
||||
{
|
||||
if (!lua_isstring(L, i))
|
||||
continue;
|
||||
@@ -763,25 +760,32 @@ int w_newShader(lua_State *L)
|
||||
}
|
||||
}
|
||||
|
||||
bool has_arg1 = lua_isstring(L, 1) != 0;
|
||||
bool has_arg2 = lua_isstring(L, 2) != 0;
|
||||
bool has_arg1 = lua_isstring(L, startidx + 0) != 0;
|
||||
bool has_arg2 = lua_isstring(L, startidx + 1) != 0;
|
||||
|
||||
// require at least one string argument
|
||||
if (!(has_arg1 || has_arg2))
|
||||
luaL_checkstring(L, 1);
|
||||
luaL_checkstring(L, startidx);
|
||||
|
||||
luax_getfunction(L, "graphics", "_shaderCodeToGLSL");
|
||||
|
||||
// push vertexcode and pixelcode strings to the top of the stack
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
lua_pushboolean(L, gles);
|
||||
|
||||
if (has_arg1)
|
||||
lua_pushvalue(L, startidx + 0);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
if (has_arg2)
|
||||
lua_pushvalue(L, startidx + 1);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
// call effectCodeToGLSL, returned values will be at the top of the stack
|
||||
if (lua_pcall(L, 2, 2, 0) != 0)
|
||||
if (lua_pcall(L, 3, 2, 0) != 0)
|
||||
return luaL_error(L, "%s", lua_tostring(L, -1));
|
||||
|
||||
Shader::ShaderSource source;
|
||||
|
||||
// vertex shader code
|
||||
if (lua_isstring(L, -2))
|
||||
source.vertex = luax_checkstring(L, -2);
|
||||
@@ -797,13 +801,23 @@ int w_newShader(lua_State *L)
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
{
|
||||
// Original args had source code, but effectCodeToGLSL couldn't translate it
|
||||
for (int i = 1; i <= 2; i++)
|
||||
for (int i = startidx; i < startidx + 2; i++)
|
||||
{
|
||||
if (lua_isstring(L, i))
|
||||
return luaL_argerror(L, i, "missing 'position' or 'effect' function?");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_newShader(lua_State *L)
|
||||
{
|
||||
bool gles = instance()->getRenderer() == Graphics::RENDERER_OPENGLES;
|
||||
|
||||
Shader::ShaderSource source;
|
||||
w_getShaderSource(L, 1, gles, source);
|
||||
|
||||
bool should_error = false;
|
||||
try
|
||||
{
|
||||
@@ -827,6 +841,28 @@ int w_newShader(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_validateShader(lua_State *L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TBOOLEAN);
|
||||
bool gles = luax_toboolean(L, 1);
|
||||
|
||||
Shader::ShaderSource source;
|
||||
w_getShaderSource(L, 2, gles, source);
|
||||
|
||||
std::string err;
|
||||
bool success = instance()->validateShader(gles, source, err);
|
||||
|
||||
luax_pushboolean(L, success);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
luax_pushstring(L, err);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static vertex::Usage luax_optmeshusage(lua_State *L, int idx, vertex::Usage def)
|
||||
{
|
||||
const char *usagestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
|
||||
@@ -1428,16 +1464,17 @@ int w_getShader(lua_State *L)
|
||||
|
||||
int w_setDefaultShaderCode(lua_State *L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int renderer = 0; renderer < Graphics::RENDERER_MAX_ENUM; renderer++)
|
||||
{
|
||||
const char *lang = renderer == Graphics::RENDERER_OPENGLES ? "glsles" : "glsl";
|
||||
luaL_checktype(L, i + 1, LUA_TTABLE);
|
||||
|
||||
lua_getfield(L, i + 1, lang);
|
||||
for (int lang = 0; lang < Shader::LANGUAGE_MAX_ENUM; lang++)
|
||||
{
|
||||
const char *langname;
|
||||
if (!Shader::getConstant((Shader::Language) lang, langname))
|
||||
continue;
|
||||
|
||||
lua_getfield(L, i + 1, langname);
|
||||
|
||||
lua_getfield(L, -1, "vertex");
|
||||
lua_getfield(L, -2, "pixel");
|
||||
@@ -1453,8 +1490,8 @@ int w_setDefaultShaderCode(lua_State *L)
|
||||
|
||||
lua_pop(L, 4);
|
||||
|
||||
Graphics::defaultShaderCode[renderer][i] = code;
|
||||
Graphics::defaultVideoShaderCode[renderer][i] = videocode;
|
||||
Graphics::defaultShaderCode[lang][i] = code;
|
||||
Graphics::defaultVideoShaderCode[lang][i] = videocode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2111,6 +2148,8 @@ static const luaL_Reg functions[] =
|
||||
{ "newText", w_newText },
|
||||
{ "_newVideo", w_newVideo },
|
||||
|
||||
{ "validateShader", w_validateShader },
|
||||
|
||||
{ "setCanvas", w_setCanvas },
|
||||
{ "getCanvas", w_getCanvas },
|
||||
|
||||
@@ -2248,6 +2287,8 @@ extern "C" int luaopen_love_graphics(lua_State *L)
|
||||
|
||||
if (luaL_loadbuffer(L, (const char *)graphics_lua, sizeof(graphics_lua), "wrap_Graphics.lua") == 0)
|
||||
lua_call(L, 0, 0);
|
||||
else
|
||||
lua_error(L);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -29,19 +29,20 @@ local ipairs = ipairs
|
||||
|
||||
local GLSL = {}
|
||||
|
||||
GLSL.VERSION = "#version 120"
|
||||
GLSL.VERSION_ES = "#version 100"
|
||||
GLSL.VERSION = { -- index using [target][gles]
|
||||
glsl1 = {[false]="#version 120", [true]="#version 100"},
|
||||
glsl3 = {[false]="#version 330 core", [true]="#version 300 es"},
|
||||
}
|
||||
|
||||
GLSL.SYNTAX = [[
|
||||
#ifndef GL_ES
|
||||
#define lowp
|
||||
#define mediump
|
||||
#define highp
|
||||
#if !defined(GL_ES) && __VERSION__ < 140
|
||||
#define lowp
|
||||
#define mediump
|
||||
#define highp
|
||||
#endif
|
||||
#define number float
|
||||
#define Image sampler2D
|
||||
#define extern uniform
|
||||
#define Texel texture2D
|
||||
#pragma optionNV(strict on)]]
|
||||
|
||||
-- Uniforms shared by the vertex and pixel shader stages.
|
||||
@@ -50,9 +51,9 @@ GLSL.UNIFORMS = [[
|
||||
// but we can't guarantee that highp is always supported in fragment shaders...
|
||||
// We *really* don't want to use mediump for these in vertex shaders though.
|
||||
#if defined(VERTEX) || defined(GL_FRAGMENT_PRECISION_HIGH)
|
||||
#define LOVE_UNIFORM_PRECISION highp
|
||||
#define LOVE_UNIFORM_PRECISION highp
|
||||
#else
|
||||
#define LOVE_UNIFORM_PRECISION mediump
|
||||
#define LOVE_UNIFORM_PRECISION mediump
|
||||
#endif
|
||||
uniform LOVE_UNIFORM_PRECISION mat4 TransformMatrix;
|
||||
uniform LOVE_UNIFORM_PRECISION mat4 ProjectionMatrix;
|
||||
@@ -61,6 +62,22 @@ uniform LOVE_UNIFORM_PRECISION mat3 NormalMatrix;
|
||||
uniform mediump vec4 love_ScreenSize;]]
|
||||
|
||||
GLSL.FUNCTIONS = [[
|
||||
#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3)
|
||||
#define Texel texture
|
||||
#else
|
||||
#if __VERSION__ >= 130
|
||||
#define texture2D Texel
|
||||
#define love_texture2D texture
|
||||
#else
|
||||
#define love_texture2D texture2D
|
||||
#endif
|
||||
vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); }
|
||||
#ifdef PIXEL
|
||||
vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); }
|
||||
#endif
|
||||
#define texture love_texture
|
||||
#endif
|
||||
|
||||
float gammaToLinearPrecise(float c) {
|
||||
return c <= 0.04045 ? c * 0.077399380804954 : pow((c + 0.055) * 0.9478672985782, 2.4);
|
||||
}
|
||||
@@ -93,45 +110,52 @@ mediump vec3 linearToGammaFast(mediump vec3 c) { return pow(max(c, vec3(0.0)), v
|
||||
mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
|
||||
|
||||
#ifdef LOVE_PRECISE_GAMMA
|
||||
#define gammaToLinear gammaToLinearPrecise
|
||||
#define linearToGamma linearToGammaPrecise
|
||||
#define gammaToLinear gammaToLinearPrecise
|
||||
#define linearToGamma linearToGammaPrecise
|
||||
#else
|
||||
#define gammaToLinear gammaToLinearFast
|
||||
#define linearToGamma linearToGammaFast
|
||||
#define gammaToLinear gammaToLinearFast
|
||||
#define linearToGamma linearToGammaFast
|
||||
#endif
|
||||
|
||||
#ifdef LOVE_GAMMA_CORRECT
|
||||
#define gammaCorrectColor gammaToLinear
|
||||
#define unGammaCorrectColor linearToGamma
|
||||
#define gammaCorrectColorPrecise gammaToLinearPrecise
|
||||
#define unGammaCorrectColorPrecise linearToGammaPrecise
|
||||
#define gammaCorrectColorFast gammaToLinearFast
|
||||
#define unGammaCorrectColorFast linearToGammaFast
|
||||
#define gammaCorrectColor gammaToLinear
|
||||
#define unGammaCorrectColor linearToGamma
|
||||
#define gammaCorrectColorPrecise gammaToLinearPrecise
|
||||
#define unGammaCorrectColorPrecise linearToGammaPrecise
|
||||
#define gammaCorrectColorFast gammaToLinearFast
|
||||
#define unGammaCorrectColorFast linearToGammaFast
|
||||
#else
|
||||
#define gammaCorrectColor
|
||||
#define unGammaCorrectColor
|
||||
#define gammaCorrectColorPrecise
|
||||
#define unGammaCorrectColorPrecise
|
||||
#define gammaCorrectColorFast
|
||||
#define unGammaCorrectColorFast
|
||||
#define gammaCorrectColor
|
||||
#define unGammaCorrectColor
|
||||
#define gammaCorrectColorPrecise
|
||||
#define unGammaCorrectColorPrecise
|
||||
#define gammaCorrectColorFast
|
||||
#define unGammaCorrectColorFast
|
||||
#endif]]
|
||||
|
||||
GLSL.VERTEX = {
|
||||
HEADER = [[
|
||||
#define VERTEX
|
||||
#define LOVE_PRECISE_GAMMA
|
||||
|
||||
#if __VERSION__ >= 130
|
||||
#define attribute in
|
||||
#define varying out
|
||||
#ifndef LOVE_GLSL1_ON_GLSL3
|
||||
#define love_VertexID gl_VertexID
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GL_ES
|
||||
uniform mediump float love_PointSize;
|
||||
#endif
|
||||
|
||||
attribute vec4 VertexPosition;
|
||||
attribute vec4 VertexTexCoord;
|
||||
attribute vec4 VertexColor;
|
||||
attribute vec4 ConstantColor;
|
||||
|
||||
varying vec4 VaryingTexCoord;
|
||||
varying vec4 VaryingColor;
|
||||
|
||||
#ifdef GL_ES
|
||||
uniform mediump float love_PointSize;
|
||||
#endif]],
|
||||
varying vec4 VaryingColor;]],
|
||||
|
||||
FUNCTIONS = "",
|
||||
|
||||
@@ -148,26 +172,39 @@ void main() {
|
||||
|
||||
GLSL.PIXEL = {
|
||||
HEADER = [[
|
||||
#define PIXEL
|
||||
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
#define love_MaxCanvases gl_MaxDrawBuffers
|
||||
|
||||
#if __VERSION__ >= 130
|
||||
#define varying in
|
||||
#ifdef LOVE_MULTI_CANVAS
|
||||
layout(location = 0) out vec4 love_Canvases[love_MaxCanvases];
|
||||
#else
|
||||
layout(location = 0) out vec4 love_PixelColor;
|
||||
#endif
|
||||
#else
|
||||
#ifdef LOVE_MULTI_CANVAS
|
||||
#define love_Canvases gl_FragData
|
||||
#else
|
||||
#define love_PixelColor gl_FragColor
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// See Shader::checkSetScreenParams in Shader.cpp.
|
||||
#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))
|
||||
|
||||
varying mediump vec4 VaryingTexCoord;
|
||||
varying mediump vec4 VaryingColor;
|
||||
|
||||
#define love_Canvases gl_FragData
|
||||
|
||||
uniform sampler2D _tex0_;]],
|
||||
varying mediump vec4 VaryingColor;]],
|
||||
|
||||
FUNCTIONS = [[
|
||||
uniform sampler2D love_VideoYChannel;
|
||||
uniform sampler2D love_VideoCbChannel;
|
||||
uniform sampler2D love_VideoCrChannel;
|
||||
|
||||
vec4 VideoTexel(vec2 texcoords)
|
||||
{
|
||||
vec4 VideoTexel(vec2 texcoords) {
|
||||
vec3 yuv;
|
||||
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
|
||||
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
|
||||
@@ -184,39 +221,37 @@ vec4 VideoTexel(vec2 texcoords)
|
||||
}]],
|
||||
|
||||
FOOTER = [[
|
||||
uniform sampler2D MainTexture;
|
||||
void main() {
|
||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
||||
float dummy = Texel(_tex0_, vec2(.5)).r;
|
||||
|
||||
// See Shader::checkSetScreenParams in Shader.cpp.
|
||||
vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w);
|
||||
|
||||
gl_FragColor = effect(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
|
||||
love_PixelColor = effect(VaryingColor, MainTexture, VaryingTexCoord.st, love_PixelCoord);
|
||||
}]],
|
||||
|
||||
FOOTER_MULTI_CANVAS = [[
|
||||
uniform sampler2D MainTexture;
|
||||
void main() {
|
||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
||||
float dummy = Texel(_tex0_, vec2(.5)).r;
|
||||
|
||||
// See Shader::checkSetScreenParams in Shader.cpp.
|
||||
vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w);
|
||||
|
||||
effects(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
|
||||
effects(VaryingColor, MainTexture, VaryingTexCoord.st, love_PixelCoord);
|
||||
}]],
|
||||
}
|
||||
|
||||
local function createShaderStageCode(stage, code, lang, gammacorrect, multicanvas)
|
||||
local function getLanguageTarget(code)
|
||||
if not code then return nil end
|
||||
return (code:match("^%s*#pragma language (%w+)")) or "glsl1"
|
||||
end
|
||||
|
||||
local function createShaderStageCode(stage, code, lang, gles, glsl1on3, gammacorrect, multicanvas)
|
||||
stage = stage:upper()
|
||||
local lines = {
|
||||
lang == "glsles" and GLSL.VERSION_ES or GLSL.VERSION,
|
||||
GLSL.SYNTAX,
|
||||
GLSL.VERSION[lang][gles],
|
||||
"#define "..stage,
|
||||
glsl1on3 and "#define LOVE_GLSL1_ON_GLSL3 1" or "",
|
||||
gammacorrect and "#define LOVE_GAMMA_CORRECT 1" or "",
|
||||
multicanvas and "#define LOVE_MULTI_CANVAS 1" or "",
|
||||
GLSL.SYNTAX,
|
||||
GLSL[stage].HEADER,
|
||||
GLSL.UNIFORMS,
|
||||
GLSL.FUNCTIONS,
|
||||
GLSL[stage].FUNCTIONS,
|
||||
lang == "glsles" and "#line 1" or "#line 0",
|
||||
(lang == "glsl3" or gles) and "#line 1" or "#line 0",
|
||||
code,
|
||||
multicanvas and GLSL[stage].FOOTER_MULTI_CANVAS or GLSL[stage].FOOTER,
|
||||
}
|
||||
@@ -238,7 +273,7 @@ local function isPixelCode(code)
|
||||
end
|
||||
end
|
||||
|
||||
function love.graphics._shaderCodeToGLSL(arg1, arg2)
|
||||
function love.graphics._shaderCodeToGLSL(gles, arg1, arg2)
|
||||
local vertexcode, pixelcode
|
||||
local is_multicanvas = false -- whether pixel code has "effects" function instead of "effect"
|
||||
|
||||
@@ -266,18 +301,34 @@ function love.graphics._shaderCodeToGLSL(arg1, arg2)
|
||||
end
|
||||
end
|
||||
|
||||
local lang = "glsl"
|
||||
if love.graphics.getRendererInfo() == "OpenGL ES" then
|
||||
lang = "glsles"
|
||||
end
|
||||
|
||||
local supportsGLSL3 = love.graphics.getSupported().glsl3
|
||||
local gammacorrect = love.graphics.isGammaCorrect()
|
||||
|
||||
local targetlang = getLanguageTarget(pixelcode or vertexcode)
|
||||
if getLanguageTarget(vertexcode or pixelcode) ~= targetlang then
|
||||
error("vertex and pixel shader languages must match", 2)
|
||||
end
|
||||
|
||||
if targetlang == "glsl3" and not supportsGLSL3 then
|
||||
error("GLSL 3 shaders are not supported on this system!", 2)
|
||||
end
|
||||
|
||||
if targetlang ~= nil and not GLSL.VERSION[targetlang] then
|
||||
error("Invalid shader language: " .. targetlang, 2)
|
||||
end
|
||||
|
||||
local lang = targetlang or "glsl1"
|
||||
local glsl1on3 = false
|
||||
if lang == "glsl1" and supportsGLSL3 then
|
||||
lang = "glsl3"
|
||||
glsl1on3 = true
|
||||
end
|
||||
|
||||
if vertexcode then
|
||||
vertexcode = createShaderStageCode("VERTEX", vertexcode, lang, gammacorrect)
|
||||
vertexcode = createShaderStageCode("VERTEX", vertexcode, lang, gles, glsl1on3, gammacorrect)
|
||||
end
|
||||
if pixelcode then
|
||||
pixelcode = createShaderStageCode("PIXEL", pixelcode, lang, gammacorrect, is_multicanvas)
|
||||
pixelcode = createShaderStageCode("PIXEL", pixelcode, lang, gles, glsl1on3, gammacorrect, is_multicanvas)
|
||||
end
|
||||
|
||||
return vertexcode, pixelcode
|
||||
@@ -328,13 +379,20 @@ vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
local defaults = {}
|
||||
local defaults_gammacorrect = {}
|
||||
|
||||
for _, lang in ipairs{"glsl", "glsles"} do
|
||||
local langs = {
|
||||
glsl1 = {target="glsl1", gles=false},
|
||||
glsles1 = {target="glsl1", gles=true},
|
||||
glsl3 = {target="glsl3", gles=false},
|
||||
glsles3 = {target="glsl3", gles=true},
|
||||
}
|
||||
|
||||
for lang, info in pairs(langs) do
|
||||
for _, gammacorrect in ipairs{false, true} do
|
||||
local t = gammacorrect and defaults_gammacorrect or defaults
|
||||
t[lang] = {
|
||||
vertex = createShaderStageCode("VERTEX", defaultcode.vertex, lang, gammacorrect),
|
||||
pixel = createShaderStageCode("PIXEL", defaultcode.pixel, lang, gammacorrect, false),
|
||||
videopixel = createShaderStageCode("PIXEL", defaultcode.videopixel, lang, gammacorrect, false),
|
||||
vertex = createShaderStageCode("VERTEX", defaultcode.vertex, info.target, info.gles, false, gammacorrect),
|
||||
pixel = createShaderStageCode("PIXEL", defaultcode.pixel, info.target, info.gles, false, gammacorrect, false),
|
||||
videopixel = createShaderStageCode("PIXEL", defaultcode.videopixel, info.target, info.gles, false, gammacorrect, false),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -131,6 +131,8 @@ void Window::setGLContextAttributes(const ContextAttribs &attribs)
|
||||
|
||||
if (attribs.gles)
|
||||
profilemask = SDL_GL_CONTEXT_PROFILE_ES;
|
||||
else if (attribs.versionMajor * 10 + attribs.versionMinor >= 32)
|
||||
profilemask |= SDL_GL_CONTEXT_PROFILE_CORE;
|
||||
else if (attribs.debug)
|
||||
profilemask = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
|
||||
|
||||
@@ -236,34 +238,33 @@ std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
|
||||
const char *debughint = SDL_GetHint("LOVE_GRAPHICS_DEBUG");
|
||||
bool debug = (debughint != nullptr && debughint[0] != '0');
|
||||
|
||||
// Different context attribute profiles to try.
|
||||
std::vector<ContextAttribs> attribslist = {
|
||||
{2, 1, false, debug}, // OpenGL 2.1.
|
||||
{3, 0, true, debug}, // OpenGL ES 3.
|
||||
{2, 0, true, debug}, // OpenGL ES 2.
|
||||
};
|
||||
const char *preferGL2hint = SDL_GetHint("LOVE_GRAPHICS_USE_GL2");
|
||||
bool preferGL2 = (preferGL2hint != nullptr && preferGL2hint[0] != '0');
|
||||
|
||||
// OpenGL ES 3+ contexts are only properly supported in SDL 2.0.4+.
|
||||
bool removeES3 = hasSDL203orEarlier;
|
||||
std::vector<ContextAttribs> glcontexts = {{2, 1, false, debug}};
|
||||
glcontexts.insert(preferGL2 ? glcontexts.end() : glcontexts.begin(), {3, 3, false, debug});
|
||||
|
||||
std::vector<ContextAttribs> glescontexts = {{2, 0, true, debug}};
|
||||
|
||||
// While UWP SDL is above 2.0.4, it still doesn't support OpenGL ES 3+
|
||||
#ifdef LOVE_WINDOWS_UWP
|
||||
removeES3 = true;
|
||||
#ifndef LOVE_WINDOWS_UWP
|
||||
// OpenGL ES 3+ contexts are only properly supported in SDL 2.0.4+.
|
||||
if (!hasSDL203orEarlier)
|
||||
glescontexts.insert(preferGL2 ? glescontexts.end() : glescontexts.begin(), {3, 0, true, debug});
|
||||
#endif
|
||||
|
||||
if (removeES3)
|
||||
{
|
||||
auto it = std::remove_if(attribslist.begin(), attribslist.end(), [](ContextAttribs a)
|
||||
{
|
||||
return a.gles && a.versionMajor >= 3;
|
||||
});
|
||||
std::vector<ContextAttribs> attribslist;
|
||||
|
||||
attribslist.erase(it, attribslist.end());
|
||||
}
|
||||
|
||||
// Move OpenGL ES to the front of the list if we should prefer GLES.
|
||||
if (preferGLES)
|
||||
std::rotate(attribslist.begin(), attribslist.begin() + 1, attribslist.end());
|
||||
{
|
||||
attribslist.insert(attribslist.end(), glescontexts.begin(), glescontexts.end());
|
||||
attribslist.insert(attribslist.end(), glcontexts.begin(), glcontexts.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
attribslist.insert(attribslist.end(), glcontexts.begin(), glcontexts.end());
|
||||
attribslist.insert(attribslist.end(), glescontexts.begin(), glescontexts.end());
|
||||
}
|
||||
|
||||
return attribslist;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user