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:
Alex Szpakowski
2017-01-22 18:54:04 -04:00
parent a6669a8bb2
commit 731b5d5cf0
85 changed files with 50255 additions and 299 deletions
+74 -45
View File
@@ -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));
+20 -2
View File
@@ -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
View File
@@ -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},
+28 -6
View File
@@ -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;
+2 -46
View File
@@ -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 -26
View File
@@ -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
+80
View File
@@ -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
+63 -23
View File
@@ -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
+5
View File
@@ -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);
+5 -10
View File
@@ -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
{
-3
View File
@@ -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
+64 -23
View File
@@ -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;
}
+129 -71
View File
@@ -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
+22 -21
View File
@@ -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;
}