Merge branch '12.0-development' into metal

This commit is contained in:
Alex Szpakowski
2021-12-24 15:34:05 -04:00
511 changed files with 5066 additions and 2506 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+38 -14
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -206,7 +206,7 @@ void Font::unloadVolatile()
textures.clear();
}
love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph)
love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph, float &dpiscale)
{
// Use spaces for the tab 'glyph'.
if (glyph == 9 && useSpacesAsTab)
@@ -221,21 +221,27 @@ love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph)
spacegd->release();
dpiscale = rasterizers[0]->getDPIScale();
return new love::font::GlyphData(glyph, gm, fmt);
}
for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
{
if (r->hasGlyph(glyph))
{
dpiscale = r->getDPIScale();
return r->getGlyphData(glyph);
}
}
dpiscale = rasterizers[0]->getDPIScale();
return rasterizers[0]->getGlyphData(glyph);
}
const Font::Glyph &Font::addGlyph(uint32 glyph)
{
StrongRef<love::font::GlyphData> gd(getRasterizerGlyphData(glyph), Acquire::NORETAIN);
float glyphdpiscale = getDPIScale();
StrongRef<love::font::GlyphData> gd(getRasterizerGlyphData(glyph, glyphdpiscale), Acquire::NORETAIN);
int w = gd->getWidth();
int h = gd->getHeight();
@@ -264,7 +270,7 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
Glyph g;
g.texture = 0;
g.spacing = floorf(gd->getAdvance() / dpiScale + 0.5f);
g.spacing = floorf(gd->getAdvance() / glyphdpiscale + 0.5f);
memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
@@ -292,18 +298,18 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
// 1---3
const GlyphVertex verts[4] =
{
{float(-o), float(-o), normToUint16((tX-o)/tWidth), normToUint16((tY-o)/tHeight), c},
{float(-o), (h+o)/dpiScale, normToUint16((tX-o)/tWidth), normToUint16((tY+h+o)/tHeight), c},
{(w+o)/dpiScale, float(-o), normToUint16((tX+w+o)/tWidth), normToUint16((tY-o)/tHeight), c},
{(w+o)/dpiScale, (h+o)/dpiScale, normToUint16((tX+w+o)/tWidth), normToUint16((tY+h+o)/tHeight), c}
{float(-o), float(-o), normToUint16((tX-o)/tWidth), normToUint16((tY-o)/tHeight), c},
{float(-o), (h+o)/glyphdpiscale, normToUint16((tX-o)/tWidth), normToUint16((tY+h+o)/tHeight), c},
{(w+o)/glyphdpiscale, float(-o), normToUint16((tX+w+o)/tWidth), normToUint16((tY-o)/tHeight), c},
{(w+o)/glyphdpiscale, (h+o)/glyphdpiscale, normToUint16((tX+w+o)/tWidth), normToUint16((tY+h+o)/tHeight), c}
};
// Copy vertex data to the glyph and set proper bearing.
for (int i = 0; i < 4; i++)
{
g.vertices[i] = verts[i];
g.vertices[i].x += gd->getBearingX() / dpiScale;
g.vertices[i].y -= gd->getBearingY() / dpiScale;
g.vertices[i].x += gd->getBearingX() / glyphdpiscale;
g.vertices[i].y -= gd->getBearingY() / glyphdpiscale;
}
textureX += w + TEXTURE_PADDING;
@@ -332,13 +338,13 @@ float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
if (it != kerning.end())
return it->second;
float k = rasterizers[0]->getKerning(leftglyph, rightglyph);
float k = rasterizers[0]->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f;
for (const auto &r : rasterizers)
{
if (r->hasGlyph(leftglyph) && r->hasGlyph(rightglyph))
{
k = floorf(r->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f);
k = floorf(r->getKerning(leftglyph, rightglyph) / r->getDPIScale() + 0.5f);
break;
}
}
@@ -347,6 +353,24 @@ float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
return k;
}
float Font::getKerning(const std::string &leftchar, const std::string &rightchar)
{
uint32 left = 0;
uint32 right = 0;
try
{
left = utf8::peek_next(leftchar.begin(), leftchar.end());
right = utf8::peek_next(rightchar.begin(), rightchar.end());
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
return getKerning(left, right);
}
void Font::getCodepointsFromString(const std::string &text, Codepoints &codepoints)
{
codepoints.reserve(text.size());
@@ -723,9 +747,9 @@ int Font::getWidth(const std::string &str)
return max_width;
}
int Font::getWidth(char character)
int Font::getWidth(uint32 glyph)
{
const Glyph &g = findGlyph(character);
const Glyph &g = findGlyph(glyph);
return g.spacing;
}
+7 -7
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -128,11 +128,9 @@ public:
int getWidth(const std::string &str);
/**
* Returns the width of the passed character.
*
* @param character A character.
* Returns the width of the passed glyph.
**/
int getWidth(char character);
int getWidth(uint32 glyph);
/**
* Returns the maximal width of a wrapped string
@@ -169,6 +167,9 @@ public:
bool hasGlyph(uint32 glyph) const;
bool hasGlyphs(const std::string &text) const;
float getKerning(uint32 leftglyph, uint32 rightglyph);
float getKerning(const std::string &leftchar, const std::string &rightchar);
void setFallbacks(const std::vector<Font *> &fallbacks);
float getDPIScale() const;
@@ -203,10 +204,9 @@ private:
void createTexture();
TextureSize getNextTextureSize() const;
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph);
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph, float &dpiscale);
const Glyph &addGlyph(uint32 glyph);
const Glyph &findGlyph(uint32 glyph);
float getKerning(uint32 leftglyph, uint32 rightglyph);
void printv(Graphics *gfx, const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
+70 -24
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -197,7 +197,7 @@ Graphics::~Graphics()
if (batchedDrawState.indexBuffer)
batchedDrawState.indexBuffer->release();
for (int i = 0; i < (int) ShaderStage::STAGE_MAX_ENUM; i++)
for (int i = 0; i < (int) SHADERSTAGE_MAX_ENUM; i++)
cachedShaderStages[i].clear();
Shader::deinitialize();
@@ -252,7 +252,7 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in
return new ParticleSystem(texture, size);
}
ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info)
ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string &source, const Shader::SourceInfo &info)
{
ShaderStage *s = nullptr;
std::string cachekey;
@@ -274,8 +274,9 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
if (s == nullptr)
{
std::string glsl = Shader::createShaderStageCode(this, stage, source, info);
s = newShaderStageInternal(stage, cachekey, glsl, usesGLSLES());
bool glsles = usesGLSLES();
std::string glsl = Shader::createShaderStageCode(this, stage, source, info, glsles, true);
s = newShaderStageInternal(stage, cachekey, glsl, glsles);
if (!cachekey.empty())
cachedShaderStages[stage][cachekey] = s;
}
@@ -285,18 +286,18 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
{
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM] = {};
bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
validstages[ShaderStage::STAGE_VERTEX] = true;
validstages[ShaderStage::STAGE_PIXEL] = true;
bool validstages[SHADERSTAGE_MAX_ENUM] = {};
validstages[SHADERSTAGE_VERTEX] = true;
validstages[SHADERSTAGE_PIXEL] = true;
for (const std::string &source : stagessource)
{
Shader::SourceInfo info = Shader::getSourceInfo(source);
bool isanystage = false;
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
{
if (!validstages[i])
continue;
@@ -304,7 +305,7 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
if (info.stages[i] != Shader::ENTRYPOINT_NONE)
{
isanystage = true;
stages[i].set(newShaderStage((ShaderStage::StageType) i, source, info), Acquire::NORETAIN);
stages[i].set(newShaderStage((ShaderStageType) i, source, info), Acquire::NORETAIN);
}
}
@@ -312,9 +313,9 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
throw love::Exception("Could not parse shader code (missing 'position' or 'effect' function?)");
}
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
{
auto stype = (ShaderStage::StageType) i;
auto stype = (ShaderStageType) i;
if (validstages[i] && stages[i].get() == nullptr)
{
const std::string &source = Shader::getDefaultCode(Shader::STANDARD_DEFAULT, stype);
@@ -324,7 +325,19 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
}
return newShaderInternal(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL]);
return newShaderInternal(stages);
}
Shader *Graphics::newComputeShader(const std::string &source)
{
Shader::SourceInfo info = Shader::getSourceInfo(source);
if (info.stages[SHADERSTAGE_COMPUTE] == Shader::ENTRYPOINT_NONE)
throw love::Exception("Could not parse compute shader code (missing 'computemain' function?)");
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM];
stages[SHADERSTAGE_COMPUTE].set(newShaderStage(SHADERSTAGE_COMPUTE, source, info));
return newShaderInternal(stages);
}
Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength)
@@ -353,18 +366,19 @@ love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector<
return new Text(font, text);
}
void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &hashkey)
void Graphics::cleanupCachedShaderStage(ShaderStageType type, const std::string &hashkey)
{
cachedShaderStages[type].erase(hashkey);
}
bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, std::string &err)
{
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM] = {};
bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
validstages[ShaderStage::STAGE_VERTEX] = true;
validstages[ShaderStage::STAGE_PIXEL] = true;
bool validstages[SHADERSTAGE_MAX_ENUM] = {};
validstages[SHADERSTAGE_VERTEX] = true;
validstages[SHADERSTAGE_PIXEL] = true;
validstages[SHADERSTAGE_COMPUTE] = true;
// Don't use cached shader stages, since the gles flag may not match the
// current renderer.
@@ -373,9 +387,9 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
Shader::SourceInfo info = Shader::getSourceInfo(source);
bool isanystage = false;
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
{
auto stype = (ShaderStage::StageType) i;
auto stype = (ShaderStageType) i;
if (!validstages[i])
continue;
@@ -383,7 +397,7 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
if (info.stages[i] != Shader::ENTRYPOINT_NONE)
{
isanystage = true;
std::string glsl = Shader::createShaderStageCode(this, stype, source, info);
std::string glsl = Shader::createShaderStageCode(this, stype, source, info, gles, false);
stages[i].set(new ShaderStageForValidation(this, stype, glsl, gles), Acquire::NORETAIN);
}
}
@@ -395,7 +409,7 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
}
}
return Shader::validate(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL], err);
return Shader::validate(stages, err);
}
int Graphics::getWidth() const
@@ -800,7 +814,7 @@ void Graphics::setRenderTargets(const RenderTargets &rts)
PixelFormat dsformat = PIXELFORMAT_STENCIL8;
if (wantsdepth && wantsstencil)
dsformat = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
else if (wantsdepth && isPixelFormatSupported(PIXELFORMAT_DEPTH24_UNORM, true, false, false))
else if (wantsdepth && isPixelFormatSupported(PIXELFORMAT_DEPTH24_UNORM, PIXELFORMATUSAGEFLAGS_RENDERTARGET, false))
dsformat = PIXELFORMAT_DEPTH24_UNORM;
else if (wantsdepth)
dsformat = PIXELFORMAT_DEPTH16_UNORM;
@@ -1111,6 +1125,35 @@ void Graphics::copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, siz
source->copyTo(dest, sourceoffset, destoffset, size);
}
void Graphics::dispatchThreadgroups(Shader* shader, int x, int y, int z)
{
if (!shader->hasStage(SHADERSTAGE_COMPUTE))
throw love::Exception("Only compute shaders can have threads dispatched.");
if (x <= 0 || y <= 0 || z <= 0)
throw love::Exception("Threadgroup dispatch size must be positive.");
if (x > capabilities.limits[LIMIT_THREADGROUPS_X]
|| y > capabilities.limits[LIMIT_THREADGROUPS_Y]
|| z > capabilities.limits[LIMIT_THREADGROUPS_Z])
{
throw love::Exception("Too many threadgroups dispatched.");
}
flushBatchedDraws();
auto prevshader = Shader::current;
shader->attach();
bool success = dispatch(x, y, z);
if (prevshader != nullptr)
prevshader->attach();
if (!success)
throw love::Exception("Compute shader must have resources bound to all writable texture and buffer variables.");
}
Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawCommand &cmd)
{
BatchedDrawState &state = batchedDrawState;
@@ -1924,6 +1967,9 @@ STRINGMAP_CLASS_BEGIN(Graphics, Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM,
{ "cubetexturesize", Graphics::LIMIT_CUBE_TEXTURE_SIZE },
{ "texelbuffersize", Graphics::LIMIT_TEXEL_BUFFER_SIZE },
{ "shaderstoragebuffersize", Graphics::LIMIT_SHADER_STORAGE_BUFFER_SIZE },
{ "threadgroupsx", Graphics::LIMIT_THREADGROUPS_X },
{ "threadgroupsy", Graphics::LIMIT_THREADGROUPS_Y },
{ "threadgroupsz", Graphics::LIMIT_THREADGROUPS_Z },
{ "rendertargets", Graphics::LIMIT_RENDER_TARGETS },
{ "texturemsaa", Graphics::LIMIT_TEXTURE_MSAA },
{ "anisotropy", Graphics::LIMIT_ANISOTROPY },
+19 -11
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -61,7 +61,7 @@ class Text;
class Video;
class Buffer;
typedef Optional<Colorf> OptionalColorf;
typedef Optional<ColorD> OptionalColorD;
const int MAX_COLOR_RENDER_TARGETS = 8;
@@ -165,6 +165,9 @@ public:
LIMIT_TEXTURE_LAYERS,
LIMIT_TEXEL_BUFFER_SIZE,
LIMIT_SHADER_STORAGE_BUFFER_SIZE,
LIMIT_THREADGROUPS_X,
LIMIT_THREADGROUPS_Y,
LIMIT_THREADGROUPS_Z,
LIMIT_RENDER_TARGETS,
LIMIT_TEXTURE_MSAA,
LIMIT_ANISOTROPY,
@@ -437,6 +440,7 @@ public:
ParticleSystem *newParticleSystem(Texture *texture, int size);
Shader *newShader(const std::vector<std::string> &stagessource);
Shader *newComputeShader(const std::string &source);
virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) = 0;
virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength);
@@ -454,8 +458,8 @@ public:
**/
void reset();
virtual void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) = 0;
virtual void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) = 0;
virtual void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) = 0;
virtual void clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth) = 0;
virtual void discard(const std::vector<bool> &colorbuffers, bool depthstencil) = 0;
@@ -672,6 +676,8 @@ public:
void copyBuffer(Buffer *source, Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size);
void dispatchThreadgroups(Shader* shader, int x, int y, int z);
void draw(Drawable *drawable, const Matrix4 &m);
void draw(Texture *texture, Quad *quad, const Matrix4 &m);
void drawLayer(Texture *texture, int layer, const Matrix4 &m);
@@ -782,9 +788,9 @@ public:
virtual PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const = 0;
/**
* Gets whether the specified pixel format is supported.
* Gets whether the specified pixel format usage is supported.
**/
virtual bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) = 0;
virtual bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) = 0;
/**
* Gets the renderer used by love.graphics.
@@ -836,7 +842,7 @@ public:
static void flushBatchedDrawsGlobal();
void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
void cleanupCachedShaderStage(ShaderStageType type, const std::string &cachekey);
template <typename T>
T *getScratchBuffer(size_t count)
@@ -934,11 +940,13 @@ protected:
{}
};
ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info);
virtual ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0;
ShaderStage *newShaderStage(ShaderStageType stage, const std::string &source, const Shader::SourceInfo &info);
virtual ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
virtual Shader *newShaderInternal(StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) = 0;
virtual StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) = 0;
virtual bool dispatch(int x, int y, int z) = 0;
virtual void setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBtexture) = 0;
virtual void initCapabilities() = 0;
@@ -999,7 +1007,7 @@ private:
std::vector<uint8> scratchBuffer;
std::unordered_map<std::string, ShaderStage *> cachedShaderStages[ShaderStage::STAGE_MAX_ENUM];
std::unordered_map<std::string, ShaderStage *> cachedShaderStages[SHADERSTAGE_MAX_ENUM];
}; // Graphics
+39 -18
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -324,18 +324,29 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
// Calculate the size in bytes of the index buffer data.
size_t size = map.size() * getIndexDataSize(datatype);
if (indexBuffer.get() == nullptr || size > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat)
bool recreate = indexData == nullptr || indexBuffer.get() == nullptr
|| size > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat;
if (recreate)
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
auto usage = vertexBuffer.get() ? vertexBuffer->getDataUsage() : BUFFERDATAUSAGE_DYNAMIC;
Buffer::Settings settings(BUFFERUSAGEFLAG_INDEX, usage);
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN);
auto buffer = StrongRef<Buffer>(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN);
auto data = (uint8 *) realloc(indexData, size);
if (data == nullptr)
throw love::Exception("Out of memory.");
indexData = data;
indexBuffer = buffer;
}
useIndexBuffer = true;
indexCount = map.size();
useIndexBuffer = true;
indexDataType = datatype;
if (!indexBuffer || indexCount == 0)
if (indexCount == 0)
return;
// Fill the buffer with the index values from the vector.
@@ -350,31 +361,40 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
break;
}
indexDataType = datatype;
indexDataModified = true;
}
void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize)
{
DataFormat dataformat = getIndexDataFormat(datatype);
if (indexBuffer.get() == nullptr || datasize > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat)
bool recreate = indexData == nullptr || indexBuffer.get() == nullptr
|| datasize > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat;
if (recreate)
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
auto usage = vertexBuffer.get() ? vertexBuffer->getDataUsage() : BUFFERDATAUSAGE_DYNAMIC;
Buffer::Settings settings(BUFFERUSAGEFLAG_INDEX, usage);
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN);
auto buffer = StrongRef<Buffer>(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN);
auto data = (uint8 *) realloc(indexData, datasize);
if (data == nullptr)
throw love::Exception("Out of memory.");
indexData = data;
indexBuffer = buffer;
}
indexCount = datasize / getIndexDataSize(datatype);
if (!indexBuffer || indexCount == 0)
return;
Buffer::Mapper ibomap(*indexBuffer);
memcpy(ibomap.data, data, datasize);
useIndexBuffer = true;
indexDataType = datatype;
if (indexCount == 0)
return;
memcpy(indexData, data, datasize);
indexDataModified = true;
}
void Mesh::setVertexMap()
@@ -386,9 +406,9 @@ void Mesh::setVertexMap()
* Copies index data from a mapped buffer to a vector.
**/
template <typename T>
static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32> &indices)
static void copyFromIndexBuffer(const void *buffer, size_t count, std::vector<uint32> &indices)
{
T *elems = (T *) buffer;
const T *elems = (const T *) buffer;
for (size_t i = 0; i < count; i++)
indices.push_back((uint32) elems[i]);
}
@@ -399,11 +419,12 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
return false;
map.clear();
map.reserve(indexCount);
if (indexData == nullptr || indexCount == 0)
return true;
map.reserve(indexCount);
// Fill the vector from the buffer.
switch (indexDataType)
{
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+268 -37
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -85,7 +85,7 @@ static const char global_syntax[] = R"(
#endif
)";
static const char global_uniforms[] = R"(
static const char render_uniforms[] = R"(
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
// 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.
@@ -148,7 +148,7 @@ static const char global_functions[] = R"(
#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
precision lowp sampler3D;
#endif
#if __VERSION__ >= 300
#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
precision lowp sampler2DShadow;
precision lowp samplerCubeShadow;
precision lowp sampler2DArrayShadow;
@@ -394,10 +394,35 @@ void main() {
}
)";
static const char compute_header[] = R"(
#define love_ThreadGroupCount gl_NumWorkGroups
#define love_ThreadGroupID gl_WorkGroupID
#define love_LocalThreadID gl_LocalInvocationID
#define love_GlobalThreadID gl_GlobalInvocationID
#define love_LocalThreadIndex gl_LocalInvocationIndex
#define love_ThreadGroupSize gl_WorkGroupSize
)";
static const char compute_uniforms[] = R"(
void love_initializeBuiltinUniforms() {}
)";
static const char compute_functions[] = R"()";
static const char compute_main[] = R"(
void computemain();
void main() {
love_initializeBuiltinUniforms();
computemain();
}
)";
struct StageInfo
{
const char *name;
const char *header;
const char *uniforms;
const char *functions;
const char *main;
const char *main_custom;
@@ -406,11 +431,12 @@ struct StageInfo
static const StageInfo stageInfo[] =
{
{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main, vertex_main_raw },
{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom, pixel_main_raw },
{ "VERTEX", vertex_header, render_uniforms, vertex_functions, vertex_main, vertex_main, vertex_main_raw },
{ "PIXEL", pixel_header, render_uniforms, pixel_functions, pixel_main, pixel_main_custom, pixel_main_raw },
{ "COMPUTE", compute_header, compute_uniforms, compute_functions, compute_main, compute_main, compute_main },
};
static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == ShaderStage::STAGE_MAX_ENUM, "Stages array size must match ShaderStage enum.");
static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == SHADERSTAGE_MAX_ENUM, "Stages array size must match ShaderStage enum.");
struct Version
{
@@ -470,6 +496,15 @@ static Shader::EntryPoint getPixelEntryPoint(const std::string &src, bool &mrt)
return Shader::ENTRYPOINT_NONE;
}
static Shader::EntryPoint getComputeEntryPoint(const std::string &src) {
std::smatch m;
if (std::regex_search(src, m, std::regex("void\\s+computemain\\s*\\(")))
return Shader::ENTRYPOINT_RAW;
return Shader::ENTRYPOINT_NONE;
}
} // glsl
static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes.");
@@ -483,12 +518,15 @@ Shader::SourceInfo Shader::getSourceInfo(const std::string &src)
{
SourceInfo info = {};
info.language = glsl::getTargetLanguage(src);
info.stages[ShaderStage::STAGE_VERTEX] = glsl::getVertexEntryPoint(src);
info.stages[ShaderStage::STAGE_PIXEL] = glsl::getPixelEntryPoint(src, info.usesMRT);
info.stages[SHADERSTAGE_VERTEX] = glsl::getVertexEntryPoint(src);
info.stages[SHADERSTAGE_PIXEL] = glsl::getPixelEntryPoint(src, info.usesMRT);
info.stages[SHADERSTAGE_COMPUTE] = glsl::getComputeEntryPoint(src);
if (info.stages[SHADERSTAGE_COMPUTE])
info.language = LANGUAGE_GLSL4;
return info;
}
std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const Shader::SourceInfo &info)
std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const Shader::SourceInfo &info, bool gles, bool checksystemfeatures)
{
if (info.language == Shader::LANGUAGE_MAX_ENUM)
throw love::Exception("Invalid shader language");
@@ -499,16 +537,26 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType
if (info.stages[stage] == ENTRYPOINT_RAW && info.language == LANGUAGE_GLSL1)
throw love::Exception("Shaders using a raw entry point (vertexmain or pixelmain) must use GLSL 3 or greater.");
const auto &features = gfx->getCapabilities().features;
if (stage == SHADERSTAGE_COMPUTE && info.language != LANGUAGE_GLSL4)
throw love::Exception("Compute shaders must use GLSL 4.");
if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3])
throw love::Exception("GLSL 3 shaders are not supported on this system.");
bool glsl1on3 = info.language == LANGUAGE_GLSL1;
if (info.language == LANGUAGE_GLSL4 && !features[Graphics::FEATURE_GLSL4])
throw love::Exception("GLSL 4 shaders are not supported on this system.");
if (checksystemfeatures)
{
const auto &features = gfx->getCapabilities().features;
bool gles = gfx->usesGLSLES();
bool glsl1on3 = info.language == LANGUAGE_GLSL1 && features[Graphics::FEATURE_GLSL3];
if (stage == SHADERSTAGE_COMPUTE && !features[Graphics::FEATURE_GLSL4])
throw love::Exception("Compute shaders require GLSL 4 which is not supported on this system.");
if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3])
throw love::Exception("GLSL 3 shaders are not supported on this system.");
if (info.language == LANGUAGE_GLSL4 && !features[Graphics::FEATURE_GLSL4])
throw love::Exception("GLSL 4 shaders are not supported on this system.");
glsl1on3 = info.language == LANGUAGE_GLSL1 && features[Graphics::FEATURE_GLSL3];
}
Language lang = info.language;
if (glsl1on3)
@@ -528,7 +576,7 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType
ss << "#define LOVE_MULTI_RENDER_TARGETS 1\n";
ss << glsl::global_syntax;
ss << stageinfo.header;
ss << glsl::global_uniforms;
ss << stageinfo.uniforms;
ss << glsl::global_functions;
ss << stageinfo.functions;
@@ -546,15 +594,15 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType
return ss.str();
}
Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
Shader::Shader(StrongRef<ShaderStage> _stages[])
: stages()
{
std::string err;
if (!validateInternal(vertex, pixel, err, validationReflection))
if (!validateInternal(_stages, err, validationReflection))
throw love::Exception("%s", err.c_str());
stages[ShaderStage::STAGE_VERTEX] = vertex;
stages[ShaderStage::STAGE_PIXEL] = pixel;
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
stages[i] = _stages[i];
}
Shader::~Shader()
@@ -569,6 +617,11 @@ Shader::~Shader()
attachDefault(STANDARD_DEFAULT);
}
bool Shader::hasStage(ShaderStageType stage)
{
return stages[stage] != nullptr;
}
void Shader::attachDefault(StandardShader defaultType)
{
Shader *defaultshader = standardShaders[defaultType];
@@ -594,10 +647,43 @@ bool Shader::isDefaultActive()
return false;
}
TextureType Shader::getMainTextureType() const
const Shader::UniformInfo *Shader::getMainTextureInfo() const
{
const UniformInfo *info = getUniformInfo(BUILTIN_TEXTURE_MAIN);
return info != nullptr ? info->textureType : TEXTURE_MAX_ENUM;
return getUniformInfo(BUILTIN_TEXTURE_MAIN);
}
DataBaseType Shader::getDataBaseType(PixelFormat format)
{
switch (getPixelFormatInfo(format).dataType)
{
case PIXELFORMATTYPE_UNORM:
return DATA_BASETYPE_UNORM;
case PIXELFORMATTYPE_SNORM:
return DATA_BASETYPE_SNORM;
case PIXELFORMATTYPE_UFLOAT:
case PIXELFORMATTYPE_SFLOAT:
return DATA_BASETYPE_FLOAT;
case PIXELFORMATTYPE_SINT:
return DATA_BASETYPE_INT;
case PIXELFORMATTYPE_UINT:
return DATA_BASETYPE_UINT;
default:
return DATA_BASETYPE_FLOAT;
}
}
bool Shader::isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b)
{
if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
return true;
if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
return true;
return false;
}
void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const
@@ -632,6 +718,9 @@ void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const
throw love::Exception("Texture's type (%s) must match the type of the shader's main texture type (%s).", textypestr, shadertextypestr);
}
if (!isResourceBaseTypeCompatible(info->dataBaseType, getDataBaseType(maintex->getPixelFormat())))
throw love::Exception("Texture's data format base type must match the uniform variable declared in the shader (float, int, or uint).");
if (info->isDepthSampler != maintex->getSamplerState().depthSampleMode.hasValue)
{
if (info->isDepthSampler)
@@ -641,21 +730,77 @@ void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const
}
}
bool Shader::validate(ShaderStage* vertex, ShaderStage* pixel, std::string& err)
void Shader::getLocalThreadgroupSize(int *x, int *y, int *z)
{
ValidationReflection reflection;
return validateInternal(vertex, pixel, err, reflection);
*x = validationReflection.localThreadgroupSize[0];
*y = validationReflection.localThreadgroupSize[1];
*z = validationReflection.localThreadgroupSize[2];
}
bool Shader::validateInternal(ShaderStage *vertex, ShaderStage *pixel, std::string &err, ValidationReflection &reflection)
bool Shader::validate(StrongRef<ShaderStage> stages[], std::string& err)
{
ValidationReflection reflection;
return validateInternal(stages, err, reflection);
}
static PixelFormat getPixelFormat(glslang::TLayoutFormat format)
{
using namespace glslang;
switch (format)
{
case ElfNone: return PIXELFORMAT_UNKNOWN;
case ElfRgba32f: return PIXELFORMAT_RGBA32_FLOAT;
case ElfRgba16f: return PIXELFORMAT_RGBA16_FLOAT;
case ElfR32f: return PIXELFORMAT_R32_FLOAT;
case ElfRgba8: return PIXELFORMAT_RGBA8_UNORM;
case ElfRgba8Snorm: return PIXELFORMAT_UNKNOWN; // no snorm yet
case ElfRg32f: return PIXELFORMAT_RG32_FLOAT;
case ElfRg16f: return PIXELFORMAT_RG16_FLOAT;
case ElfR11fG11fB10f: return PIXELFORMAT_RG11B10_FLOAT;
case ElfR16f: return PIXELFORMAT_R16_FLOAT;
case ElfRgba16: return PIXELFORMAT_RGBA16_UNORM;
case ElfRgb10A2: return PIXELFORMAT_RGB10A2_UNORM;
case ElfRg16: return PIXELFORMAT_RG16_UNORM;
case ElfRg8: return PIXELFORMAT_RG8_UNORM;
case ElfR8: return PIXELFORMAT_R8_UNORM;
case ElfRgba16Snorm: return PIXELFORMAT_UNKNOWN;
case ElfRg16Snorm: return PIXELFORMAT_UNKNOWN;
case ElfRg8Snorm: return PIXELFORMAT_UNKNOWN;
case ElfR16Snorm: return PIXELFORMAT_UNKNOWN;
case ElfR8Snorm: return PIXELFORMAT_UNKNOWN;
case ElfRgba32i: return PIXELFORMAT_RGBA32_INT;
case ElfRgba16i: return PIXELFORMAT_RGBA16_INT;
case ElfRgba8i: return PIXELFORMAT_RGBA8_INT;
case ElfR32i: return PIXELFORMAT_R32_INT;
case ElfRg32i: return PIXELFORMAT_RG32_INT;
case ElfRg16i: return PIXELFORMAT_RG16_INT;
case ElfRg8i: return PIXELFORMAT_RG8_INT;
case ElfR16i: return PIXELFORMAT_R16_INT;
case ElfR8i: return PIXELFORMAT_R8_INT;
case ElfRgba32ui: return PIXELFORMAT_RGBA32_UINT;
case ElfRgba16ui: return PIXELFORMAT_RGBA16_UINT;
case ElfRgba8ui: return PIXELFORMAT_RGBA8_UINT;
case ElfR32ui: return PIXELFORMAT_R32_UINT;
case ElfRg32ui: return PIXELFORMAT_RG32_UINT;
case ElfRg16ui: return PIXELFORMAT_RG16_UINT;
case ElfRgb10a2ui: return PIXELFORMAT_UNKNOWN;
case ElfRg8ui: return PIXELFORMAT_RG8_UINT;
case ElfR16ui: return PIXELFORMAT_R16_UINT;
case ElfR8ui: return PIXELFORMAT_R8_UINT;
default: return PIXELFORMAT_UNKNOWN;
}
}
bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, ValidationReflection &reflection)
{
glslang::TProgram program;
if (vertex != nullptr)
program.addShader(vertex->getGLSLangValidationShader());
if (pixel != nullptr)
program.addShader(pixel->getGLSLangValidationShader());
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
{
if (stages[i] != nullptr)
program.addShader(stages[i]->getGLSLangValidationShader());
}
if (!program.link(EShMsgDefault))
{
@@ -676,6 +821,58 @@ bool Shader::validateInternal(ShaderStage *vertex, ShaderStage *pixel, std::stri
reflection.usesPointSize = vertintermediate->inIoAccessed("gl_PointSize");
}
if (stages[SHADERSTAGE_COMPUTE] != nullptr)
{
for (int i = 0; i < 3; i++)
{
reflection.localThreadgroupSize[i] = program.getLocalSize(i);
if (reflection.localThreadgroupSize[i] <= 0)
{
err = "Shader validation error:\nNegative local threadgroup size.";
return false;
}
}
}
for (int i = 0; i < program.getNumUniformVariables(); i++)
{
const glslang::TObjectReflection &info = program.getUniform(i);
const glslang::TType *type = info.getType();
if (type == nullptr)
continue;
const glslang::TQualifier &qualifiers = type->getQualifier();
if (type->isImage())
{
if ((info.stages & EShLangComputeMask) == 0)
{
err = "Shader validation error:\nStorage Texture uniform variables (image2D, etc) are only allowed in compute shaders.";
return false;
}
if (!qualifiers.hasFormat())
{
err = "Shader validation error:\nStorage Texture '" + info.name + "' must have an explicit format set in its layout declaration.";
return false;
}
StorageTextureReflection texreflection = {};
texreflection.format = getPixelFormat(qualifiers.getFormat());
if (qualifiers.isReadOnly())
texreflection.access = ACCESS_READ;
else if (qualifiers.isWriteOnly())
texreflection.access = ACCESS_WRITE;
else
texreflection.access = (Access)(ACCESS_READ | ACCESS_WRITE);
reflection.storageTextures[info.name] = texreflection;
}
}
for (int i = 0; i < program.getNumBufferBlocks(); i++)
{
const glslang::TObjectReflection &info = program.getBufferBlock(i);
@@ -684,7 +881,7 @@ bool Shader::validateInternal(ShaderStage *vertex, ShaderStage *pixel, std::stri
{
const glslang::TQualifier &qualifiers = type->getQualifier();
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && (info.stages & (EShLangVertexMask | EShLangFragmentMask)))
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && (info.stages & EShLangComputeMask) == 0)
{
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must be marked as readonly in vertex and pixel shaders.";
return false;
@@ -714,6 +911,13 @@ bool Shader::validateInternal(ShaderStage *vertex, ShaderStage *pixel, std::stri
bufferReflection.stride = (size_t) info.size;
bufferReflection.memberCount = (size_t) info.numMembers;
if (qualifiers.isReadOnly())
bufferReflection.access = ACCESS_READ;
else if (qualifiers.isWriteOnly())
bufferReflection.access = ACCESS_WRITE;
else
bufferReflection.access = (Access)(ACCESS_READ | ACCESS_WRITE);
reflection.storageBuffers[info.name] = bufferReflection;
}
else
@@ -758,6 +962,33 @@ bool Shader::validateTexture(const UniformInfo *info, Texture *tex, bool interna
throw love::Exception("Texture's type (%s) must match the type of %s (%s).", textypestr, info->name.c_str(), shadertextypestr);
}
}
else if (!isResourceBaseTypeCompatible(info->dataBaseType, getDataBaseType(tex->getPixelFormat())))
{
if (internalUpdate)
return false;
else
throw love::Exception("Texture's data format base type must match the uniform variable declared in the shader (float, int, or uint).");
}
else if (isstoragetex && !tex->isComputeWritable())
{
if (internalUpdate)
return false;
else
throw love::Exception("Texture must be created with the computewrite flag set to true in order to be used with a storage texture (image2D etc) shader uniform variable.");
}
else if (isstoragetex && info->storageTextureFormat != getLinearPixelFormat(tex->getPixelFormat()))
{
if (internalUpdate)
return false;
else
{
const char *texpfstr = "unknown";
const char *shaderpfstr = "unknown";
love::getConstant(getLinearPixelFormat(tex->getPixelFormat()), texpfstr);
love::getConstant(info->storageTextureFormat, shaderpfstr);
throw love::Exception("Texture's pixel format (%s) must match the shader uniform variable %s's pixel format (%s)", texpfstr, info->name.c_str(), shaderpfstr);
}
}
return true;
}
@@ -803,7 +1034,7 @@ bool Shader::validateBuffer(const UniformInfo *info, Buffer *buffer, bool intern
if (texelbinding)
{
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
if (!isTexelBaseTypeCompatible(basetype, info->dataBaseType))
if (!isResourceBaseTypeCompatible(basetype, info->dataBaseType))
{
if (internalUpdate)
return false;
@@ -881,9 +1112,9 @@ void effect()
}
)";
const std::string &Shader::getDefaultCode(StandardShader shader, ShaderStage::StageType stage)
const std::string &Shader::getDefaultCode(StandardShader shader, ShaderStageType stage)
{
if (stage == ShaderStage::STAGE_VERTEX)
if (stage == SHADERSTAGE_VERTEX)
{
if (shader == STANDARD_POINTS)
return defaultPointsVertex;
+38 -10
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -76,6 +76,7 @@ public:
UNIFORM_UINT,
UNIFORM_BOOL,
UNIFORM_SAMPLER,
UNIFORM_STORAGETEXTURE,
UNIFORM_TEXELBUFFER,
UNIFORM_STORAGEBUFFER,
UNIFORM_UNKNOWN,
@@ -99,10 +100,17 @@ public:
ENTRYPOINT_RAW,
};
enum Access
{
ACCESS_NONE = 0,
ACCESS_READ = (1 << 0),
ACCESS_WRITE = (1 << 1),
};
struct SourceInfo
{
Language language;
EntryPoint stages[ShaderStage::STAGE_MAX_ENUM];
EntryPoint stages[SHADERSTAGE_MAX_ENUM];
bool usesMRT;
};
@@ -126,7 +134,9 @@ public:
UniformType baseType;
DataBaseType dataBaseType;
TextureType textureType;
Access access;
bool isDepthSampler;
PixelFormat storageTextureFormat;
size_t bufferStride;
size_t bufferMemberCount;
std::string name;
@@ -164,9 +174,14 @@ public:
// Pointer to the default Shader.
static Shader *standardShaders[STANDARD_MAX_ENUM];
Shader(ShaderStage *vertex, ShaderStage *pixel);
Shader(StrongRef<ShaderStage> stages[]);
virtual ~Shader();
/**
* Check whether a Shader has a stage.
**/
bool hasStage(ShaderStageType stage);
/**
* Binds this Shader's program to be used when rendering.
**/
@@ -208,18 +223,20 @@ public:
**/
virtual void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) = 0;
TextureType getMainTextureType() const;
const UniformInfo *getMainTextureInfo() const;
void validateDrawState(PrimitiveType primtype, Texture *maintexture) const;
static SourceInfo getSourceInfo(const std::string &src);
static std::string createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const SourceInfo &info);
void getLocalThreadgroupSize(int *x, int *y, int *z);
static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
static SourceInfo getSourceInfo(const std::string &src);
static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const SourceInfo &info, bool gles, bool checksystemfeatures);
static bool validate(StrongRef<ShaderStage> stages[], std::string &err);
static bool initialize();
static void deinitialize();
static const std::string &getDefaultCode(StandardShader shader, ShaderStage::StageType stage);
static const std::string &getDefaultCode(StandardShader shader, ShaderStageType stage);
static bool getConstant(const char *in, Language &out);
static bool getConstant(Language in, const char *&out);
@@ -233,20 +250,31 @@ protected:
{
size_t stride;
size_t memberCount;
Access access;
};
struct StorageTextureReflection
{
PixelFormat format;
Access access;
};
struct ValidationReflection
{
std::map<std::string, BufferReflection> storageBuffers;
std::map<std::string, StorageTextureReflection> storageTextures;
int localThreadgroupSize[3];
bool usesPointSize;
};
static bool validateInternal(ShaderStage* vertex, ShaderStage* pixel, std::string& err, ValidationReflection &reflection);
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, ValidationReflection &reflection);
static DataBaseType getDataBaseType(PixelFormat format);
static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b);
static bool validateTexture(const UniformInfo *info, Texture *tex, bool internalUpdate);
static bool validateBuffer(const UniformInfo *info, Buffer *buffer, bool internalUpdate);
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM];
ValidationReflection validationReflection;
+14 -11
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -137,17 +137,19 @@ namespace love
namespace graphics
{
ShaderStage::ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl, bool gles, const std::string &cachekey)
ShaderStage::ShaderStage(Graphics *gfx, ShaderStageType stage, const std::string &glsl, bool gles, const std::string &cachekey)
: stageType(stage)
, source(glsl)
, cacheKey(cachekey)
, glslangValidationShader(nullptr)
{
EShLanguage glslangStage = EShLangCount;
if (stage == STAGE_VERTEX)
if (stage == SHADERSTAGE_VERTEX)
glslangStage = EShLangVertex;
else if (stage == STAGE_PIXEL)
else if (stage == SHADERSTAGE_PIXEL)
glslangStage = EShLangFragment;
else if (stage == SHADERSTAGE_COMPUTE)
glslangStage = EShLangCompute;
else
throw love::Exception("Cannot compile shader stage: unknown stage type.");
@@ -195,30 +197,31 @@ ShaderStage::~ShaderStage()
delete glslangValidationShader;
}
bool ShaderStage::getConstant(const char *in, StageType &out)
bool ShaderStage::getConstant(const char *in, ShaderStageType &out)
{
return stageNames.find(in, out);
}
bool ShaderStage::getConstant(StageType in, const char *&out)
bool ShaderStage::getConstant(ShaderStageType in, const char *&out)
{
return stageNames.find(in, out);
}
const char *ShaderStage::getConstant(StageType in)
const char *ShaderStage::getConstant(ShaderStageType in)
{
const char *name = nullptr;
getConstant(in, name);
return name;
}
StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
StringMap<ShaderStageType, SHADERSTAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
{
{ "vertex", STAGE_VERTEX },
{ "pixel", STAGE_PIXEL },
{ "vertex", SHADERSTAGE_VERTEX },
{ "pixel", SHADERSTAGE_PIXEL },
{ "compute", SHADERSTAGE_COMPUTE },
};
StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM> ShaderStage::stageNames(ShaderStage::stageNameEntries, sizeof(ShaderStage::stageNameEntries));
StringMap<ShaderStageType, SHADERSTAGE_MAX_ENUM> ShaderStage::stageNames(ShaderStage::stageNameEntries, sizeof(ShaderStage::stageNameEntries));
} // graphics
} // love
+19 -18
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -39,31 +39,32 @@ namespace graphics
class Graphics;
// Order is used for stages array in ShaderStage.cpp
enum ShaderStageType
{
SHADERSTAGE_VERTEX,
SHADERSTAGE_PIXEL,
SHADERSTAGE_COMPUTE,
SHADERSTAGE_MAX_ENUM
};
class ShaderStage : public love::Object
{
public:
// Order is used for stages array in ShaderStage.cpp
enum StageType
{
STAGE_VERTEX,
STAGE_PIXEL,
STAGE_MAX_ENUM
};
ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl, bool gles, const std::string &cachekey);
ShaderStage(Graphics *gfx, ShaderStageType stage, const std::string &glsl, bool gles, const std::string &cachekey);
virtual ~ShaderStage();
virtual ptrdiff_t getHandle() const = 0;
StageType getStageType() const { return stageType; }
ShaderStageType getStageType() const { return stageType; }
const std::string &getSource() const { return source; }
const std::string &getWarnings() const { return warnings; }
glslang::TShader *getGLSLangValidationShader() const { return glslangValidationShader; }
static bool getConstant(const char *in, StageType &out);
static bool getConstant(StageType in, const char *&out);
static const char *getConstant(StageType in);
static bool getConstant(const char *in, ShaderStageType &out);
static bool getConstant(ShaderStageType in, const char *&out);
static const char *getConstant(ShaderStageType in);
protected:
@@ -71,13 +72,13 @@ protected:
private:
StageType stageType;
ShaderStageType stageType;
std::string source;
std::string cacheKey;
glslang::TShader *glslangValidationShader;
static StringMap<StageType, STAGE_MAX_ENUM>::Entry stageNameEntries[];
static StringMap<StageType, STAGE_MAX_ENUM> stageNames;
static StringMap<ShaderStageType, SHADERSTAGE_MAX_ENUM>::Entry stageNameEntries[];
static StringMap<ShaderStageType, SHADERSTAGE_MAX_ENUM> stageNames;
}; // ShaderStage
@@ -85,7 +86,7 @@ class ShaderStageForValidation final : public ShaderStage
{
public:
ShaderStageForValidation(Graphics *gfx, StageType stage, const std::string &glsl, bool gles)
ShaderStageForValidation(Graphics *gfx, ShaderStageType stage, const std::string &glsl, bool gles)
: ShaderStage(gfx, stage, glsl, gles, "")
{}
virtual ~ShaderStageForValidation() {}
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -4
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -133,9 +133,6 @@ void Text::addTextData(const TextData &t)
textData.clear();
}
if (vertices.empty())
return;
if (t.useMatrix)
t.matrix.transformXY(&vertices[0], &vertices[0], (int) vertices.size());
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+23 -3
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -163,6 +163,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
: texType(settings.type)
, format(settings.format)
, renderTarget(settings.renderTarget)
, computeWrite(settings.computeWrite)
, readable(true)
, mipmapsMode(settings.mipmaps)
, sRGB(isGammaCorrect() && !settings.linear)
@@ -255,7 +256,15 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
if (isCompressed() && renderTarget)
throw love::Exception("Compressed textures cannot be render targets.");
if (!gfx->isPixelFormatSupported(format, renderTarget, readable, sRGB))
uint32 usage = PIXELFORMATUSAGEFLAGS_NONE;
if (renderTarget)
usage |= PIXELFORMATUSAGEFLAGS_RENDERTARGET;
if (readable)
usage |= PIXELFORMATUSAGEFLAGS_SAMPLE;
if (computeWrite)
usage |= PIXELFORMATUSAGEFLAGS_COMPUTEWRITE;
if (!gfx->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, sRGB))
{
const char *fstr = "unknown";
love::getConstant(format, fstr);
@@ -265,7 +274,9 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
readablestr = readable ? " readable" : " non-readable";
const char *rtstr = "";
if (renderTarget)
if (computeWrite)
rtstr = " as a compute shader-writable texture";
else if (renderTarget)
rtstr = " as a render target";
throw love::Exception("The %s%s pixel format is not supported%s on this system.", fstr, readablestr, rtstr);
@@ -502,6 +513,9 @@ void Texture::generateMipmaps()
if (isPixelFormatDepthStencil(format))
throw love::Exception("generateMipmaps cannot be called on a depth/stencil Texture.");
if (isPixelFormatInteger(format))
throw love::Exception("generateMipmaps cannot be called on an integer Texture.");
generateMipmapsInternal();
}
@@ -566,6 +580,11 @@ bool Texture::isRenderTarget() const
return renderTarget;
}
bool Texture::isComputeWritable() const
{
return computeWrite;
}
bool Texture::isReadable() const
{
return readable;
@@ -929,6 +948,7 @@ static StringMap<Texture::SettingType, Texture::SETTING_MAX_ENUM>::Entry setting
{ "dpiscale", Texture::SETTING_DPI_SCALE },
{ "msaa", Texture::SETTING_MSAA },
{ "canvas", Texture::SETTING_RENDER_TARGET },
{ "computewrite", Texture::SETTING_COMPUTE_WRITE },
{ "readable", Texture::SETTING_READABLE },
};
+7 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -63,6 +63,7 @@ enum PixelFormatUsage
PIXELFORMATUSAGE_RENDERTARGET,
PIXELFORMATUSAGE_BLEND,
PIXELFORMATUSAGE_MSAA,
PIXELFORMATUSAGE_COMPUTEWRITE,
PIXELFORMATUSAGE_MAX_ENUM
};
@@ -74,6 +75,7 @@ enum PixelFormatUsageFlags
PIXELFORMATUSAGEFLAGS_RENDERTARGET = (1 << PIXELFORMATUSAGE_RENDERTARGET),
PIXELFORMATUSAGEFLAGS_BLEND = (1 << PIXELFORMATUSAGE_BLEND),
PIXELFORMATUSAGEFLAGS_MSAA = (1 << PIXELFORMATUSAGE_MSAA),
PIXELFORMATUSAGEFLAGS_COMPUTEWRITE = (1 << PIXELFORMATUSAGE_COMPUTEWRITE),
};
struct SamplerState
@@ -169,6 +171,7 @@ public:
SETTING_DPI_SCALE,
SETTING_MSAA,
SETTING_RENDER_TARGET,
SETTING_COMPUTE_WRITE,
SETTING_READABLE,
SETTING_MAX_ENUM
};
@@ -186,6 +189,7 @@ public:
float dpiScale = 1.0f;
int msaa = 1;
bool renderTarget = false;
bool computeWrite = false;
OptionalBool readable;
};
@@ -250,6 +254,7 @@ public:
MipmapsMode getMipmapsMode() const;
bool isRenderTarget() const;
bool isComputeWritable() const;
bool isReadable() const;
bool isCompressed() const;
@@ -308,6 +313,7 @@ protected:
PixelFormat format;
bool renderTarget;
bool computeWrite;
bool readable;
MipmapsMode mipmapsMode;
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+138 -30
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -167,14 +167,14 @@ love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings,
return new Texture(this, settings, data);
}
love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles)
love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles)
{
return new ShaderStage(this, stage, source, gles, cachekey);
}
love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
love::graphics::Shader *Graphics::newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM])
{
return new Shader(vertex, pixel);
return new Shader(stages);
}
love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
@@ -358,6 +358,7 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
}
// TODO: one buffer each for float, int, uint
if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERUSAGE_TEXEL].get() == nullptr)
{
Buffer::Settings settings(BUFFERUSAGEFLAG_TEXEL, BUFFERDATAUSAGE_STATIC);
@@ -378,7 +379,7 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
data.resize(Buffer::SHADER_STORAGE_BUFFER_MAX_STRIDE / 4);
auto buffer = newBuffer(settings, format, data.data(), data.size() * sizeof(float), data.size());
defaultBuffers[BUFFERUSAGE_TEXEL].set(buffer, Acquire::NORETAIN);
defaultBuffers[BUFFERUSAGE_SHADER_STORAGE].set(buffer, Acquire::NORETAIN);
}
// Load default resources before other Volatile.
@@ -418,8 +419,8 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
if (!Shader::standardShaders[i])
{
std::vector<std::string> stages;
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL));
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL));
Shader::standardShaders[i] = newShader(stages);
}
}
@@ -486,6 +487,88 @@ void Graphics::setActive(bool enable)
active = enable;
}
static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarriers, GLbitfield &postDispatchBarriers)
{
// TODO: handle indirect argument buffer types, when those are added.
for (auto buffer : shader->getActiveWritableStorageBuffers())
{
if (buffer == nullptr)
return false;
auto usage = buffer->getUsageFlags();
postDispatchBarriers |= GL_BUFFER_UPDATE_BARRIER_BIT;
if (usage & BUFFERUSAGEFLAG_SHADER_STORAGE)
{
preDispatchBarriers |= GL_SHADER_STORAGE_BARRIER_BIT;
postDispatchBarriers |= GL_SHADER_STORAGE_BARRIER_BIT;
}
if (usage & BUFFERUSAGEFLAG_TEXEL)
postDispatchBarriers |= GL_TEXTURE_FETCH_BARRIER_BIT;
if (usage & BUFFERUSAGEFLAG_INDEX)
postDispatchBarriers |= GL_ELEMENT_ARRAY_BARRIER_BIT;
if (usage & BUFFERUSAGEFLAG_VERTEX)
postDispatchBarriers |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
if (usage & (BUFFERUSAGEFLAG_COPY_SOURCE | BUFFERUSAGEFLAG_COPY_DEST))
postDispatchBarriers |= GL_PIXEL_BUFFER_BARRIER_BIT;
}
for (const auto &binding : shader->getStorageTextureBindings())
{
if (binding.texture == nullptr)
return false;
if (binding.access == GL_READ_ONLY)
continue;
preDispatchBarriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
postDispatchBarriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
| GL_TEXTURE_UPDATE_BARRIER_BIT
| GL_TEXTURE_FETCH_BARRIER_BIT;
if (binding.texture->isRenderTarget())
postDispatchBarriers |= GL_FRAMEBUFFER_BARRIER_BIT;
}
return true;
}
bool Graphics::dispatch(int x, int y, int z)
{
// Set by higher level code before calling dispatch(x, y, z).
auto shader = (Shader *) Shader::current;
GLbitfield preDispatchBarriers = 0;
GLbitfield postDispatchBarriers = 0;
if (!computeDispatchBarriers(shader, preDispatchBarriers, postDispatchBarriers))
return false;
// glMemoryBarrier before dispatch to make sure non-compute-read ->
// compute-write is synced.
// TODO: is this needed? spec language around GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
// makes me think so.
// This is overly conservative (dispatch -> dispatch will have redundant
// barriers).
if (preDispatchBarriers != 0)
glMemoryBarrier(preDispatchBarriers);
glDispatchCompute(x, y, z);
// Not as (theoretically) efficient as issuing the barrier right before
// they're used later, but much less complicated.
if (postDispatchBarriers != 0)
glMemoryBarrier(postDispatchBarriers);
return true;
}
void Graphics::draw(const DrawCommand &cmd)
{
gl.prepareDraw(this);
@@ -771,7 +854,7 @@ void Graphics::endPass()
}
}
void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
void Graphics::clear(OptionalColorD c, OptionalInt stencil, OptionalDouble depth)
{
if (c.hasValue || stencil.hasValue || depth.hasValue)
flushBatchedDraws();
@@ -780,8 +863,9 @@ void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth
if (c.hasValue)
{
gammaCorrectColor(c.value);
glClearColor(c.value.r, c.value.g, c.value.b, c.value.a);
Colorf cf((float)c.value.r, (float)c.value.g, (float)c.value.b, (float)c.value.b);
gammaCorrectColor(cf);
glClearColor(cf.r, cf.g, cf.b, cf.a);
flags |= GL_COLOR_BUFFER_BIT;
}
@@ -817,17 +901,19 @@ void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth
}
}
void Graphics::clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth)
void Graphics::clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth)
{
if (colors.size() == 0 && !stencil.hasValue && !depth.hasValue)
return;
int ncolorRTs = (int) states.back().renderTargets.colors.size();
const auto &rts = states.back().renderTargets.colors;
int ncolorRTs = (int) rts.size();
int ncolors = (int) colors.size();
if (ncolors <= 1 && ncolorRTs <= 1)
if (ncolors <= 1 && (ncolorRTs == 0 || (ncolorRTs == 1 && rts[0].texture != nullptr && !isPixelFormatInteger(rts[0].texture->getPixelFormat()))))
{
clear(ncolors > 0 ? colors[0] : OptionalColorf(), stencil, depth);
clear(ncolors > 0 ? colors[0] : OptionalColorD(), stencil, depth);
return;
}
@@ -841,18 +927,39 @@ void Graphics::clear(const std::vector<OptionalColorf> &colors, OptionalInt sten
if (!colors[i].hasValue)
continue;
Colorf c = colors[i].value;
gammaCorrectColor(c);
PixelFormatType datatype = PIXELFORMATTYPE_UNORM;
if (rts[i].texture != nullptr)
datatype = getPixelFormatInfo(rts[i].texture->getPixelFormat()).dataType;
ColorD c = colors[i].value;
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0)
{
const GLfloat carray[] = {c.r, c.g, c.b, c.a};
glClearBufferfv(GL_COLOR, i, carray);
if (datatype == PIXELFORMATTYPE_SINT)
{
const GLint carray[] = {(GLint)c.r, (GLint)c.g, (GLint)c.b, (GLint)c.a};
glClearBufferiv(GL_COLOR, i, carray);
}
else if (datatype == PIXELFORMATTYPE_UINT)
{
const GLuint carray[] = {(GLuint)c.r, (GLuint)c.g, (GLuint)c.b, (GLuint)c.a};
glClearBufferuiv(GL_COLOR, i, carray);
}
else
{
Colorf cf((float)c.r, (float)c.g, (float)c.b, (float)c.a);
gammaCorrectColor(cf);
const GLfloat carray[] = {cf.r, cf.g, cf.b, cf.a};
glClearBufferfv(GL_COLOR, i, carray);
}
}
else
{
Colorf cf((float)c.r, (float)c.g, (float)c.b, (float)c.a);
gammaCorrectColor(cf);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + i);
glClearColor(c.r, c.g, c.b, c.a);
glClearColor(cf.r, cf.g, cf.b, cf.a);
glClear(GL_COLOR_BUFFER_BIT);
drawbuffersmodified = true;
@@ -1566,10 +1673,13 @@ void Graphics::initCapabilities()
capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize();
capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = gl.getMaxTexelBufferSize();
capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = gl.getMaxShaderStorageBufferSize();
capabilities.limits[LIMIT_THREADGROUPS_X] = gl.getMaxComputeWorkGroupsX();
capabilities.limits[LIMIT_THREADGROUPS_Y] = gl.getMaxComputeWorkGroupsY();
capabilities.limits[LIMIT_THREADGROUPS_Z] = gl.getMaxComputeWorkGroupsZ();
capabilities.limits[LIMIT_RENDER_TARGETS] = gl.getMaxRenderTargets();
capabilities.limits[LIMIT_TEXTURE_MSAA] = gl.getMaxSamples();
capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy();
static_assert(LIMIT_MAX_ENUM == 10, "Graphics::initCapabilities must be updated when adding a new system limit!");
static_assert(LIMIT_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new system limit!");
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
@@ -1600,7 +1710,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool
}
}
bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB)
bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB)
{
if (sRGB && format == PIXELFORMAT_RGBA8_UNORM)
{
@@ -1608,22 +1718,20 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
sRGB = false;
}
uint32 requiredflags = 0;
if (rendertarget)
requiredflags |= PIXELFORMATUSAGEFLAGS_RENDERTARGET;
if (readable)
requiredflags |= PIXELFORMATUSAGEFLAGS_SAMPLE;
bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0;
bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0;
bool computewrite = (usage & PIXELFORMATUSAGEFLAGS_COMPUTEWRITE) != 0;
format = getSizedFormat(format, rendertarget, readable);
OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][sRGB ? 1 : 0];
OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][computewrite ? 1 : 0][sRGB ? 1 : 0];
if (supported.hasValue)
return supported.value;
auto supportedflags = OpenGL::getPixelFormatUsageFlags(format);
uint32 supportedflags = OpenGL::getPixelFormatUsageFlags(format);
if ((requiredflags & supportedflags) != requiredflags)
if ((usage & supportedflags) != usage)
{
supported.set(false);
return supported.value;
@@ -1664,7 +1772,7 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
// Make sure at least something is bound to a color attachment. I believe
// this is required on ES2 but I'm not positive.
if (isPixelFormatDepthStencil(format))
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0);
if (readable)
{
+10 -8
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -68,12 +68,14 @@ public:
void setActive(bool active) override;
bool dispatch(int x, int y, int z) override;
void draw(const DrawCommand &cmd) override;
void draw(const DrawIndexedCommand &cmd) override;
void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture) override;
void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override;
void clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth) override;
void discard(const std::vector<bool> &colorbuffers, bool depthstencil) override;
@@ -105,7 +107,7 @@ public:
void setWireframe(bool enable) override;
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override;
bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) override;
bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
@@ -137,8 +139,8 @@ private:
}
};
love::graphics::ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override;
love::graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
love::graphics::Shader *newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override;
love::graphics::StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) override;
void setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBtexture) override;
void initCapabilities() override;
@@ -169,8 +171,8 @@ private:
// Only needed for buffer types that can be bound to shaders.
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERUSAGE_MAX_ENUM];
// [rendertarget][readable][srgb]
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2];
// [rendertarget][readable][computewrite][srgb]
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2][2];
}; // Graphics
+271 -46
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -103,6 +103,9 @@ OpenGL::OpenGL()
, maxTextureArrayLayers(0)
, maxTexelBufferSize(0)
, maxShaderStorageBufferSize(0)
, maxComputeWorkGroupsX(0)
, maxComputeWorkGroupsY(0)
, maxComputeWorkGroupsZ(0)
, maxRenderTargets(1)
, maxSamples(1)
, maxTextureUnits(1)
@@ -122,6 +125,20 @@ bool OpenGL::initContext()
if (!gladLoadGLLoader(LOVEGetProcAddress))
return false;
initVendor();
bugs = {};
if (GLAD_ES_VERSION_3_0 && !GLAD_ES_VERSION_3_1)
{
const char *device = (const char *) glGetString(GL_RENDERER);
if (getVendor() == VENDOR_VIVANTE && strstr(device, "Vivante GC7000UL"))
bugs.brokenGLES3 = true;
}
if (bugs.brokenGLES3)
GLAD_ES_VERSION_3_0 = false;
if (GLAD_VERSION_3_2)
{
GLint profileMask = 0;
@@ -132,9 +149,6 @@ bool OpenGL::initContext()
coreProfile = false;
initOpenGLFunctions();
initVendor();
bugs = {};
#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX)
// See the comments in OpenGL.h.
@@ -278,7 +292,7 @@ void OpenGL::setupContext()
// This can't be done in initContext with the rest of the bug checks because
// isPixelFormatSupported relies on state initialized here / after init.
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (GLAD_ES_VERSION_3_0 && gfx != nullptr && !gfx->isPixelFormatSupported(PIXELFORMAT_R8_UNORM, true, true))
if (GLAD_ES_VERSION_3_0 && gfx != nullptr && !gfx->isPixelFormatSupported(PIXELFORMAT_R8_UNORM, PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET))
bugs.brokenR8PixelFormat = true;
#endif
}
@@ -290,10 +304,13 @@ void OpenGL::deInitContext()
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
{
if (state.defaultTexture[i] != 0)
for (int datatype = DATA_BASETYPE_FLOAT; datatype <= DATA_BASETYPE_UINT; datatype++)
{
gl.deleteTexture(state.defaultTexture[i]);
state.defaultTexture[i] = 0;
if (state.defaultTexture[i][datatype] != 0)
{
gl.deleteTexture(state.defaultTexture[i][datatype]);
state.defaultTexture[i][datatype] = 0;
}
}
}
@@ -500,6 +517,19 @@ void OpenGL::initMaxValues()
maxShaderStorageBufferBindings = 0;
}
if (GLAD_ES_VERSION_3_1 || GLAD_VERSION_4_3)
{
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &maxComputeWorkGroupsX);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &maxComputeWorkGroupsY);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &maxComputeWorkGroupsZ);
}
else
{
maxComputeWorkGroupsX = 0;
maxComputeWorkGroupsY = 0;
maxComputeWorkGroupsZ = 0;
}
int maxattachments = 1;
int maxdrawbuffers = 1;
@@ -542,6 +572,7 @@ void OpenGL::createDefaultTexture()
// which would create the need to use different "passthrough" shaders for
// untextured primitives vs images.
const GLubyte pix[] = {255, 255, 255, 255};
const GLubyte intpix[] = {1, 1, 1, 1};
SamplerState s;
s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
@@ -549,41 +580,55 @@ void OpenGL::createDefaultTexture()
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
{
state.defaultTexture[i] = 0;
TextureType type = (TextureType) i;
if (!isTextureTypeSupported(type))
continue;
GLuint curtexture = state.boundTextures[type][0];
glGenTextures(1, &state.defaultTexture[type]);
bindTextureToUnit(type, state.defaultTexture[type], 0, false);
setSamplerState(type, s);
bool isSRGB = false;
rawTexStorage(type, 1, PIXELFORMAT_RGBA8_UNORM, isSRGB, 1, 1);
TextureFormat fmt = convertPixelFormat(PIXELFORMAT_RGBA8_UNORM, false, isSRGB);
int slices = type == TEXTURE_CUBE ? 6 : 1;
for (int slice = 0; slice < slices; slice++)
for (int datatype = (int)DATA_BASETYPE_FLOAT; datatype <= (int)DATA_BASETYPE_UINT; datatype++)
{
GLenum gltarget = getGLTextureType(type);
state.defaultTexture[i][datatype] = 0;
if (type == TEXTURE_CUBE)
gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
TextureType type = (TextureType) i;
if (type == TEXTURE_2D || type == TEXTURE_CUBE)
glTexSubImage2D(gltarget, 0, 0, 0, 1, 1, fmt.externalformat, fmt.type, pix);
else if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
glTexSubImage3D(gltarget, 0, 0, 0, slice, 1, 1, 1, fmt.externalformat, fmt.type, pix);
if (!isTextureTypeSupported(type))
continue;
if (datatype != DATA_BASETYPE_FLOAT && !(GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0))
continue;
GLuint curtexture = state.boundTextures[type][0];
glGenTextures(1, &state.defaultTexture[type][datatype]);
bindTextureToUnit(type, state.defaultTexture[type][datatype], 0, false);
setSamplerState(type, s);
PixelFormat format = PIXELFORMAT_RGBA8_UNORM;
if (datatype == DATA_BASETYPE_INT)
format = PIXELFORMAT_RGBA8_INT;
else if (datatype == DATA_BASETYPE_UINT)
format = PIXELFORMAT_RGBA8_UINT;
const GLubyte *p = datatype == DATA_BASETYPE_FLOAT ? pix : intpix;
bool isSRGB = false;
rawTexStorage(type, 1, format, isSRGB, 1, 1);
TextureFormat fmt = convertPixelFormat(format, false, isSRGB);
int slices = type == TEXTURE_CUBE ? 6 : 1;
for (int slice = 0; slice < slices; slice++)
{
GLenum gltarget = getGLTextureType(type);
if (type == TEXTURE_CUBE)
gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
if (type == TEXTURE_2D || type == TEXTURE_CUBE)
glTexSubImage2D(gltarget, 0, 0, 0, 1, 1, fmt.externalformat, fmt.type, p);
else if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
glTexSubImage3D(gltarget, 0, 0, 0, slice, 1, 1, 1, fmt.externalformat, fmt.type, p);
}
bindTextureToUnit(type, curtexture, 0, false);
}
bindTextureToUnit(type, curtexture, 0, false);
}
}
@@ -1086,9 +1131,9 @@ GLuint OpenGL::getDefaultFBO() const
#endif
}
GLuint OpenGL::getDefaultTexture(TextureType type) const
GLuint OpenGL::getDefaultTexture(TextureType type, DataBaseType datatype) const
{
return state.defaultTexture[type];
return state.defaultTexture[type][datatype];
}
void OpenGL::setTextureUnit(int textureunit)
@@ -1139,14 +1184,19 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
}
else
{
DataBaseType datatype = DATA_BASETYPE_FLOAT;
if (textureunit == 0 && Shader::current != nullptr)
{
TextureType shadertex = Shader::current->getMainTextureType();
if (shadertex != TEXTURE_MAX_ENUM)
textype = shadertex;
const Shader::UniformInfo *info = Shader::current->getMainTextureInfo();
if (info != nullptr)
{
textype = info->textureType;
datatype = info->dataBaseType;
}
}
handle = getDefaultTexture(textype);
handle = getDefaultTexture(textype, datatype);
}
bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
@@ -1510,6 +1560,21 @@ int OpenGL::getMaxShaderStorageBufferSize() const
return maxShaderStorageBufferSize;
}
int OpenGL::getMaxComputeWorkGroupsX() const
{
return maxComputeWorkGroupsX;
}
int OpenGL::getMaxComputeWorkGroupsY() const
{
return maxComputeWorkGroupsY;
}
int OpenGL::getMaxComputeWorkGroupsZ() const
{
return maxComputeWorkGroupsZ;
}
int OpenGL::getMaxRenderTargets() const
{
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
@@ -1620,6 +1685,7 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
f.externalformat = GL_RGBA;
f.type = GL_UNSIGNED_SHORT;
break;
case PIXELFORMAT_R16_FLOAT:
f.internalformat = GL_R16F;
f.externalformat = GL_RED;
@@ -1660,6 +1726,97 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
f.type = GL_FLOAT;
break;
case PIXELFORMAT_R8_INT:
f.internalformat = GL_R8I;
f.externalformat = GL_RED_INTEGER;
f.type = GL_BYTE;
break;
case PIXELFORMAT_R8_UINT:
f.internalformat = GL_R8UI;
f.externalformat = GL_RED_INTEGER;
f.type = GL_UNSIGNED_BYTE;
break;
case PIXELFORMAT_RG8_INT:
f.internalformat = GL_RG8I;
f.externalformat = GL_RG_INTEGER;
f.type = GL_BYTE;
break;
case PIXELFORMAT_RG8_UINT:
f.internalformat = GL_RG8UI;
f.externalformat = GL_RG_INTEGER;
f.type = GL_UNSIGNED_BYTE;
break;
case PIXELFORMAT_RGBA8_INT:
f.internalformat = GL_RGBA8I;
f.externalformat = GL_RGBA_INTEGER;
f.type = GL_BYTE;
break;
case PIXELFORMAT_RGBA8_UINT:
f.internalformat = GL_RGBA8UI;
f.externalformat = GL_RGBA_INTEGER;
f.type = GL_UNSIGNED_BYTE;
break;
case PIXELFORMAT_R16_INT:
f.internalformat = GL_R16I;
f.externalformat = GL_RED_INTEGER;
f.type = GL_SHORT;
break;
case PIXELFORMAT_R16_UINT:
f.internalformat = GL_R16UI;
f.externalformat = GL_RED_INTEGER;
f.type = GL_UNSIGNED_SHORT;
break;
case PIXELFORMAT_RG16_INT:
f.internalformat = GL_RG16I;
f.externalformat = GL_RG_INTEGER;
f.type = GL_SHORT;
break;
case PIXELFORMAT_RG16_UINT:
f.internalformat = GL_RG16UI;
f.externalformat = GL_RG_INTEGER;
f.type = GL_UNSIGNED_SHORT;
break;
case PIXELFORMAT_RGBA16_INT:
f.internalformat = GL_RGBA16I;
f.externalformat = GL_RGBA_INTEGER;
f.type = GL_SHORT;
break;
case PIXELFORMAT_RGBA16_UINT:
f.internalformat = GL_RGBA16UI;
f.externalformat = GL_RGBA_INTEGER;
f.type = GL_UNSIGNED_SHORT;
break;
case PIXELFORMAT_R32_INT:
f.internalformat = GL_R32I;
f.externalformat = GL_RED_INTEGER;
f.type = GL_INT;
break;
case PIXELFORMAT_R32_UINT:
f.internalformat = GL_R32UI;
f.externalformat = GL_RED_INTEGER;
f.type = GL_UNSIGNED_INT;
break;
case PIXELFORMAT_RG32_INT:
f.internalformat = GL_RG32I;
f.externalformat = GL_RG_INTEGER;
f.type = GL_INT;
break;
case PIXELFORMAT_RG32_UINT:
f.internalformat = GL_RG32UI;
f.externalformat = GL_RG_INTEGER;
f.type = GL_UNSIGNED_INT;
break;
case PIXELFORMAT_RGBA32_INT:
f.internalformat = GL_RGBA32I;
f.externalformat = GL_RGBA_INTEGER;
f.type = GL_INT;
break;
case PIXELFORMAT_RGBA32_UINT:
f.internalformat = GL_RGBA32UI;
f.externalformat = GL_RGBA_INTEGER;
f.type = GL_UNSIGNED_INT;
break;
case PIXELFORMAT_LA8_UNORM:
if (gl.isCoreProfile() || GLAD_ES_VERSION_3_0)
{
@@ -1785,7 +1942,7 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
break;
case PIXELFORMAT_DXT1_UNORM:
f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case PIXELFORMAT_DXT3_UNORM:
f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
@@ -1928,6 +2085,7 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
{
const uint32 commonsample = PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_LINEAR;
const uint32 commonrender = PIXELFORMATUSAGEFLAGS_RENDERTARGET | PIXELFORMATUSAGEFLAGS_BLEND | PIXELFORMATUSAGEFLAGS_MSAA;
const uint32 computewrite = PIXELFORMATUSAGEFLAGS_COMPUTEWRITE;
uint32 flags = PIXELFORMATUSAGEFLAGS_NONE;
@@ -1939,11 +2097,15 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
flags |= commonsample | commonrender;
else if (pixelformat == PIXELFORMAT_R8_UNORM && (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_1))
flags |= commonsample; // We'll use OpenGL's luminance format internally.
if (GLAD_VERSION_4_3)
flags |= computewrite;
break;
case PIXELFORMAT_RGBA8_UNORM:
flags |= commonsample;
if (GLAD_VERSION_1_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8)
flags |= commonrender;
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
flags |= computewrite;
break;
case PIXELFORMAT_RGBA8_UNORM_sRGB:
if (gl.bugs.brokenSRGB)
@@ -1953,6 +2115,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0
|| ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB)))
flags |= commonrender;
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
flags |= computewrite;
break;
case PIXELFORMAT_R16_UNORM:
case PIXELFORMAT_RG16_UNORM:
@@ -1960,10 +2124,14 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|| (GLAD_VERSION_1_1 && GLAD_ARB_texture_rg)
|| (GLAD_EXT_texture_norm16 && (GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_rg)))
flags |= commonsample | commonrender;
if (GLAD_VERSION_4_3)
flags |= computewrite;
break;
case PIXELFORMAT_RGBA16_UNORM:
if (GLAD_VERSION_1_1 || GLAD_EXT_texture_norm16)
flags |= commonsample | commonrender;
if (GLAD_VERSION_4_3)
flags |= computewrite;
break;
case PIXELFORMAT_R16_FLOAT:
case PIXELFORMAT_RG16_FLOAT:
@@ -1975,6 +2143,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
flags |= commonrender;
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
if (GLAD_VERSION_4_3)
flags |= computewrite;
break;
case PIXELFORMAT_RGBA16_FLOAT:
if (GLAD_VERSION_3_0 || (GLAD_VERSION_1_0 && GLAD_ARB_texture_float && GLAD_ARB_half_float_pixel))
@@ -1985,8 +2155,13 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
flags |= commonrender;
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
flags |= computewrite;
break;
case PIXELFORMAT_R32_FLOAT:
if (GLAD_ES_VERSION_3_1)
flags |= computewrite;
// Fallthrough.
case PIXELFORMAT_RG32_FLOAT:
if (GLAD_VERSION_3_0 || (GLAD_VERSION_1_0 && GLAD_ARB_texture_float && GLAD_ARB_texture_rg))
flags |= commonsample | commonrender;
@@ -1994,6 +2169,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
flags |= commonsample;
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
if (GLAD_VERSION_4_3)
flags |= computewrite;
break;
case PIXELFORMAT_RGBA32_FLOAT:
if (GLAD_VERSION_3_0 || (GLAD_VERSION_1_0 && GLAD_ARB_texture_float))
@@ -2002,8 +2179,52 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
flags |= commonsample;
if (!(GLAD_VERSION_1_1 || GLAD_OES_texture_float_linear))
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
flags |= computewrite;
break;
case PIXELFORMAT_R8_INT:
case PIXELFORMAT_R8_UINT:
case PIXELFORMAT_RG8_INT:
case PIXELFORMAT_RG8_UINT:
case PIXELFORMAT_RGBA8_INT:
case PIXELFORMAT_RGBA8_UINT:
case PIXELFORMAT_R16_INT:
case PIXELFORMAT_R16_UINT:
case PIXELFORMAT_RG16_INT:
case PIXELFORMAT_RG16_UINT:
case PIXELFORMAT_RGBA16_INT:
case PIXELFORMAT_RGBA16_UINT:
case PIXELFORMAT_R32_INT:
case PIXELFORMAT_R32_UINT:
case PIXELFORMAT_RG32_INT:
case PIXELFORMAT_RG32_UINT:
case PIXELFORMAT_RGBA32_INT:
case PIXELFORMAT_RGBA32_UINT:
if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0)
flags |= PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET;
if (GLAD_VERSION_4_3)
flags |= computewrite;
if (GLAD_ES_VERSION_3_1)
{
switch (pixelformat)
{
case PIXELFORMAT_RGBA8_INT:
case PIXELFORMAT_RGBA8_UINT:
case PIXELFORMAT_RGBA16_INT:
case PIXELFORMAT_RGBA16_UINT:
case PIXELFORMAT_R32_INT:
case PIXELFORMAT_R32_UINT:
case PIXELFORMAT_RGBA32_INT:
case PIXELFORMAT_RGBA32_UINT:
flags |= computewrite;
break;
default:
break;
}
}
break;
case PIXELFORMAT_LA8_UNORM:
flags |= commonsample;
break;
@@ -2019,12 +2240,16 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
case PIXELFORMAT_RGB10A2_UNORM:
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0)
flags |= commonsample | commonrender;
if (GLAD_VERSION_4_3)
flags |= computewrite;
break;
case PIXELFORMAT_RG11B10_FLOAT:
if (GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_texture_packed_float)
flags |= commonsample;
if (GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_color_buffer_packed_float)
flags |= commonrender;
if (GLAD_VERSION_4_3)
flags |= computewrite;
break;
case PIXELFORMAT_STENCIL8:
+21 -3
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -185,6 +185,13 @@ public:
**/
bool brokenSRGB;
/**
* Some Android graphics drivers claim to support GLES3.0 but have bugs
* with certain aspects that users expect to work. For example:
* https://github.com/love2d/love-android/issues/204
**/
bool brokenGLES3;
/**
* Other bugs which have workarounds that don't use conditional code at
* the moment:
@@ -305,7 +312,7 @@ public:
/**
* Gets the ID for love's default texture (used for "untextured" primitives.)
**/
GLuint getDefaultTexture(TextureType type) const;
GLuint getDefaultTexture(TextureType type, DataBaseType datatype) const;
/**
* Gets the texture ID for love's default texel buffer.
@@ -383,6 +390,14 @@ public:
**/
int getMaxShaderStorageBufferSize() const;
/**
* Returns the maximum number of compute work groups that can be
* dispatched in a given dimension.
*/
int getMaxComputeWorkGroupsX() const;
int getMaxComputeWorkGroupsY() const;
int getMaxComputeWorkGroupsZ() const;
/**
* Returns the maximum supported number of simultaneous render targets.
**/
@@ -467,6 +482,9 @@ private:
int maxTextureArrayLayers;
int maxTexelBufferSize;
int maxShaderStorageBufferSize;
int maxComputeWorkGroupsX;
int maxComputeWorkGroupsY;
int maxComputeWorkGroupsZ;
int maxRenderTargets;
int maxSamples;
int maxTextureUnits;
@@ -505,7 +523,7 @@ private:
GLuint boundFramebuffers[2];
GLuint defaultTexture[TEXTURE_MAX_ENUM];
GLuint defaultTexture[TEXTURE_MAX_ENUM][DATA_BASETYPE_MAX_ENUM];
GLuint defaultTexelBuffer;
GLuint defaultStorageBuffer;
+278 -128
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -43,8 +43,8 @@ static bool isBuffer(Shader::UniformType utype)
return utype == Shader::UNIFORM_TEXELBUFFER || utype == Shader::UNIFORM_STORAGEBUFFER;
}
Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
: love::graphics::Shader(vertex, pixel)
Shader::Shader(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM])
: love::graphics::Shader(stages)
, program(0)
, builtinUniforms()
, builtinUniformInfo()
@@ -63,7 +63,7 @@ Shader::~Shader()
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
{
for (int i = 0; i < p.second.count; i++)
{
@@ -119,15 +119,8 @@ void Shader::mapActiveUniforms()
u.name = std::string(cname, (size_t) namelen);
u.location = glGetUniformLocation(program, u.name.c_str());
u.baseType = getUniformBaseType(gltype);
u.textureType = getUniformTextureType(gltype);
u.dataBaseType = getUniformTexelBaseType(gltype);
u.isDepthSampler = isDepthTextureType(gltype);
if (u.baseType == UNIFORM_MATRIX)
u.matrix = getMatrixSize(gltype);
else
u.components = getUniformTypeComponents(gltype);
u.access = ACCESS_READ;
computeUniformTypeInfo(gltype, u);
// glGetActiveUniform appends "[0]" to the end of array uniform names...
if (u.name.length() > 3)
@@ -159,12 +152,39 @@ void Shader::mapActiveUniforms()
else
{
unit.isTexelBuffer = false;
unit.texture = gl.getDefaultTexture(u.textureType);
unit.texture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
}
for (int i = 0; i < u.count; i++)
textureUnits.push_back(unit);
}
else if (u.baseType == UNIFORM_STORAGETEXTURE)
{
const auto reflectionit = validationReflection.storageTextures.find(u.name);
if (reflectionit != validationReflection.storageTextures.end())
{
u.storageTextureFormat = reflectionit->second.format;
u.access = reflectionit->second.access;
}
StorageTextureBinding binding = {};
binding.gltexture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
binding.type = u.textureType;
if ((u.access & (ACCESS_READ | ACCESS_WRITE)) != 0)
binding.access = GL_READ_WRITE;
else if ((u.access & ACCESS_WRITE) != 0)
binding.access = GL_WRITE_ONLY;
else if ((u.access & ACCESS_READ) != 0)
binding.access = GL_READ_ONLY;
bool sRGB = false;
auto fmt = OpenGL::convertPixelFormat(u.storageTextureFormat, false, sRGB);
binding.internalFormat = fmt.internalformat;
for (int i = 0; i < u.count; i++)
storageTextureBindings.push_back(binding);
}
// Make sure previously set uniform data is preserved, and shader-
// initialized values are retrieved.
@@ -190,6 +210,7 @@ void Shader::mapActiveUniforms()
case UNIFORM_INT:
case UNIFORM_BOOL:
case UNIFORM_SAMPLER:
case UNIFORM_STORAGETEXTURE:
case UNIFORM_TEXELBUFFER:
u.dataSize = sizeof(int) * u.components * u.count;
u.data = malloc(u.dataSize);
@@ -233,6 +254,17 @@ void Shader::mapActiveUniforms()
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
else if (u.baseType == UNIFORM_STORAGETEXTURE)
{
int startbinding = (int) storageTextureBindings.size() - u.count;
for (int i = 0; i < u.count; i++)
u.ints[i] = startbinding + i;
glUniform1iv(u.location, u.count, u.ints);
u.textures = new love::graphics::Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
size_t offset = 0;
@@ -284,7 +316,7 @@ void Shader::mapActiveUniforms()
if (builtin != BUILTIN_MAX_ENUM)
builtinUniformInfo[(int)builtin] = &uniforms[u.name];
if (u.baseType == UNIFORM_SAMPLER)
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE)
{
// Make sure all stored textures have their Volatiles loaded before
// the sendTextures call, since it calls getHandle().
@@ -325,6 +357,7 @@ void Shader::mapActiveUniforms()
{
UniformInfo u = {};
u.baseType = UNIFORM_STORAGEBUFFER;
u.access = ACCESS_READ;
GLsizei namelength = 0;
glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, sindex, 2048, &namelength, namebuffer);
@@ -337,6 +370,7 @@ void Shader::mapActiveUniforms()
{
u.bufferStride = reflectionit->second.stride;
u.bufferMemberCount = reflectionit->second.memberCount;
u.access = reflectionit->second.access;
}
// Make sure previously set uniform data is preserved, and shader-
@@ -369,10 +403,17 @@ void Shader::mapActiveUniforms()
if (binding.bindingindex >= 0)
{
int activeindex = (int)activeStorageBufferBindings.size();
storageBufferBindingIndexToActiveBinding[binding.bindingindex] = activeindex;
activeStorageBufferBindings.push_back(binding);
auto p = std::make_pair(activeindex, -1);
if (u.access & ACCESS_WRITE)
{
p.second = (int)activeWritableStorageBuffers.size();
activeWritableStorageBuffers.push_back(u.buffers[0]);
}
storageBufferBindingIndexToActiveBinding[binding.bindingindex] = p;
}
uniforms[u.name] = u;
@@ -399,7 +440,7 @@ void Shader::mapActiveUniforms()
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
{
for (int i = 0; i < p.second.count; i++)
{
@@ -433,9 +474,12 @@ bool Shader::loadVolatile()
textureUnits.clear();
textureUnits.push_back(TextureUnit());
storageBufferBindingIndexToActiveBinding.resize(gl.getMaxShaderStorageBufferBindings(), -1);
activeStorageBufferBindings.clear();
storageBufferBindingIndexToActiveBinding.resize(gl.getMaxShaderStorageBufferBindings(), std::make_pair(-1, -1));
activeStorageBufferBindings.clear();
activeWritableStorageBuffers.clear();
for (const auto &stage : stages)
{
if (stage.get() != nullptr)
@@ -561,7 +605,7 @@ void Shader::attach()
// retain/release happens in Graphics::setShader.
// Make sure all textures are bound to their respective texture units.
for (int i = 0; i < (int) textureUnits.size(); ++i)
for (int i = 0; i < (int) textureUnits.size(); i++)
{
const TextureUnit &unit = textureUnits[i];
if (unit.active)
@@ -573,6 +617,12 @@ void Shader::attach()
}
}
for (size_t i = 0; i < storageTextureBindings.size(); i++)
{
const auto &binding = storageTextureBindings[i];
glBindImageTexture((GLuint) i, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
}
for (auto bufferbinding : activeStorageBufferBindings)
gl.bindIndexedBuffer(bufferbinding.buffer, BUFFERUSAGE_SHADER_STORAGE, bufferbinding.bindingindex);
@@ -636,7 +686,7 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
break;
}
}
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_TEXELBUFFER)
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_STORAGETEXTURE || type == UNIFORM_TEXELBUFFER)
{
switch (info->components)
{
@@ -705,7 +755,10 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_SAMPLER)
bool issampler = info->baseType == UNIFORM_SAMPLER;
bool isstoragetex = info->baseType == UNIFORM_STORAGETEXTURE;
if (!issampler && !isstoragetex)
return;
bool shaderactive = current == this;
@@ -732,19 +785,39 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
info->textures[i] = tex;
GLuint gltex = 0;
if (textures[i] != nullptr)
gltex = (GLuint) tex->getHandle();
if (isstoragetex)
{
GLuint gltex = 0;
if (tex != nullptr)
gltex = (GLuint) tex->getHandle();
else
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
int bindingindex = info->ints[i];
auto &binding = storageTextureBindings[bindingindex];
binding.texture = tex;
binding.gltexture = gltex;
if (shaderactive)
glBindImageTexture(bindingindex, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
}
else
gltex = gl.getDefaultTexture(info->textureType);
{
GLuint gltex = 0;
if (textures[i] != nullptr)
gltex = (GLuint) tex->getHandle();
else
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
int texunit = info->ints[i];
int texunit = info->ints[i];
if (shaderactive)
gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
if (shaderactive)
gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
}
}
}
@@ -788,7 +861,7 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
if (texelbinding)
{
GLuint gltex = 0;
if (buffers[i] != nullptr)
if (buffer != nullptr)
gltex = (GLuint) buffer->getTexelBufferHandle();
else
gltex = gl.getDefaultTexelBuffer();
@@ -806,7 +879,7 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
int bindingindex = info->ints[i];
GLuint glbuffer = 0;
if (buffers[i] != nullptr)
if (buffer != nullptr)
glbuffer = (GLuint) buffer->getHandle();
else
glbuffer = gl.getDefaultStorageBuffer();
@@ -814,9 +887,13 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
if (shaderactive)
gl.bindIndexedBuffer(glbuffer, BUFFERUSAGE_SHADER_STORAGE, bindingindex);
int activeindex = storageBufferBindingIndexToActiveBinding[bindingindex];
if (activeindex >= 0)
activeStorageBufferBindings[activeindex].buffer = glbuffer;
auto activeindex = storageBufferBindingIndexToActiveBinding[bindingindex];
if (activeindex.first >= 0)
activeStorageBufferBindings[activeindex.first].buffer = glbuffer;
if (activeindex.second >= 0)
activeWritableStorageBuffers[activeindex.second] = buffer;
}
}
}
@@ -928,11 +1005,6 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
int Shader::getUniformTypeComponents(GLenum type) const
{
UniformType basetype = getUniformBaseType(type);
if (basetype == UNIFORM_SAMPLER || basetype == UNIFORM_TEXELBUFFER)
return 1;
switch (type)
{
case GL_INT:
@@ -1007,25 +1079,35 @@ Shader::MatrixSize Shader::getMatrixSize(GLenum type) const
return m;
}
Shader::UniformType Shader::getUniformBaseType(GLenum type) const
void Shader::computeUniformTypeInfo(GLenum type, UniformInfo &u)
{
u.isDepthSampler = false;
u.components = getUniformTypeComponents(type);
u.baseType = UNIFORM_UNKNOWN;
switch (type)
{
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
return UNIFORM_INT;
u.baseType = UNIFORM_INT;
u.dataBaseType = DATA_BASETYPE_INT;
break;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
case GL_UNSIGNED_INT_VEC3:
case GL_UNSIGNED_INT_VEC4:
return UNIFORM_UINT;
u.baseType = UNIFORM_UINT;
u.dataBaseType = DATA_BASETYPE_UINT;
break;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
return UNIFORM_FLOAT;
u.baseType = UNIFORM_FLOAT;
u.dataBaseType = DATA_BASETYPE_FLOAT;
break;
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
@@ -1035,105 +1117,173 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
return UNIFORM_MATRIX;
u.baseType = UNIFORM_MATRIX;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.matrix = getMatrixSize(type);
break;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
return UNIFORM_BOOL;
case GL_SAMPLER_1D:
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
u.baseType = UNIFORM_BOOL;
u.dataBaseType = DATA_BASETYPE_BOOL;
break;
case GL_SAMPLER_2D:
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_SAMPLER_2D_RECT:
case GL_SAMPLER_2D_RECT_SHADOW:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D;
break;
case GL_SAMPLER_2D_SHADOW:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D;
u.isDepthSampler = true;
break;
case GL_INT_SAMPLER_2D:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_2D;
break;
case GL_UNSIGNED_INT_SAMPLER_2D:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_2D;
break;
case GL_SAMPLER_2D_ARRAY:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_SAMPLER_2D_ARRAY_SHADOW:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D_ARRAY;
u.isDepthSampler = true;
break;
case GL_INT_SAMPLER_2D_ARRAY:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_SAMPLER_3D:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_INT_SAMPLER_3D:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_UNSIGNED_INT_SAMPLER_3D:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_SAMPLER_CUBE:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_CUBE;
break;
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_CUBE_MAP_ARRAY:
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
return UNIFORM_SAMPLER;
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_CUBE;
u.isDepthSampler = true;
break;
case GL_INT_SAMPLER_CUBE:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_CUBE;
break;
case GL_UNSIGNED_INT_SAMPLER_CUBE:
u.baseType = UNIFORM_SAMPLER;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_CUBE;
break;
case GL_SAMPLER_BUFFER:
u.baseType = UNIFORM_TEXELBUFFER;
u.dataBaseType = DATA_BASETYPE_FLOAT;
break;
case GL_INT_SAMPLER_BUFFER:
u.baseType = UNIFORM_TEXELBUFFER;
u.dataBaseType = DATA_BASETYPE_INT;
break;
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return UNIFORM_TEXELBUFFER;
default:
return UNIFORM_UNKNOWN;
}
}
u.baseType = UNIFORM_TEXELBUFFER;
u.dataBaseType = DATA_BASETYPE_UINT;
break;
TextureType Shader::getUniformTextureType(GLenum type) const
{
switch (type)
{
case GL_SAMPLER_1D:
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
// 1D-typed textures are not supported.
return TEXTURE_MAX_ENUM;
case GL_SAMPLER_2D:
case GL_SAMPLER_2D_SHADOW:
return TEXTURE_2D;
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
// Multisample textures are not supported.
return TEXTURE_MAX_ENUM;
case GL_SAMPLER_2D_RECT:
case GL_SAMPLER_2D_RECT_SHADOW:
// Rectangle textures are not supported.
return TEXTURE_MAX_ENUM;
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_2D_ARRAY_SHADOW:
return TEXTURE_2D_ARRAY;
case GL_SAMPLER_3D:
return TEXTURE_VOLUME;
case GL_SAMPLER_CUBE:
case GL_SAMPLER_CUBE_SHADOW:
return TEXTURE_CUBE;
case GL_SAMPLER_CUBE_MAP_ARRAY:
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
// Cubemap array textures are not supported.
return TEXTURE_MAX_ENUM;
default:
return TEXTURE_MAX_ENUM;
}
}
case GL_IMAGE_2D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D;
break;
case GL_INT_IMAGE_2D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_2D;
break;
case GL_UNSIGNED_INT_IMAGE_2D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_2D;
break;
case GL_IMAGE_2D_ARRAY:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_INT_IMAGE_2D_ARRAY:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_2D_ARRAY;
break;
case GL_IMAGE_3D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_INT_IMAGE_3D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_UNSIGNED_INT_IMAGE_3D:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_VOLUME;
break;
case GL_IMAGE_CUBE:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_FLOAT;
u.textureType = TEXTURE_CUBE;
break;
case GL_INT_IMAGE_CUBE:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_INT;
u.textureType = TEXTURE_CUBE;
break;
case GL_UNSIGNED_INT_IMAGE_CUBE:
u.baseType = UNIFORM_STORAGETEXTURE;
u.dataBaseType = DATA_BASETYPE_UINT;
u.textureType = TEXTURE_CUBE;
break;
DataBaseType Shader::getUniformTexelBaseType(GLenum type) const
{
switch (type)
{
case GL_SAMPLER_BUFFER:
return DATA_BASETYPE_FLOAT;
case GL_INT_SAMPLER_BUFFER:
return DATA_BASETYPE_INT;
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return DATA_BASETYPE_UINT;
default:
return DATA_BASETYPE_MAX_ENUM;
}
}
bool Shader::isDepthTextureType(GLenum type) const
{
switch (type)
{
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
return true;
default:
return false;
break;
}
}
+20 -7
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -43,7 +43,16 @@ class Shader final : public love::graphics::Shader, public Volatile
{
public:
Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel);
struct StorageTextureBinding
{
Texture *texture = nullptr;
GLuint gltexture = 0;
TextureType type = TEXTURE_2D;
GLenum access = GL_READ_ONLY;
GLenum internalFormat;
};
Shader(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]);
virtual ~Shader();
// Implements Volatile
@@ -65,6 +74,9 @@ public:
void updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, int viewportH);
const std::vector<Buffer *> &getActiveWritableStorageBuffers() const { return activeWritableStorageBuffers; }
const std::vector<StorageTextureBinding> &getStorageTextureBindings() const { return storageTextureBindings; }
private:
struct TextureUnit
@@ -89,11 +101,8 @@ private:
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate);
int getUniformTypeComponents(GLenum type) const;
void computeUniformTypeInfo(GLenum type, UniformInfo &u);
MatrixSize getMatrixSize(GLenum type) const;
UniformType getUniformBaseType(GLenum type) const;
TextureType getUniformTextureType(GLenum type) const;
DataBaseType getUniformTexelBaseType(GLenum type) const;
bool isDepthTextureType(GLenum type) const;
void flushBatchedDraws() const;
@@ -115,9 +124,13 @@ private:
// Texture unit pool for setting textures
std::vector<TextureUnit> textureUnits;
std::vector<int> storageBufferBindingIndexToActiveBinding;
std::vector<StorageTextureBinding> storageTextureBindings;
std::vector<std::pair<int, int>> storageBufferBindingIndexToActiveBinding;
std::vector<BufferBinding> activeStorageBufferBindings;
std::vector<Buffer *> activeWritableStorageBuffers;
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
}; // Shader
+7 -5
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -27,7 +27,7 @@ namespace graphics
namespace opengl
{
ShaderStage::ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey)
ShaderStage::ShaderStage(love::graphics::Graphics *gfx, ShaderStageType stage, const std::string &source, bool gles, const std::string &cachekey)
: love::graphics::ShaderStage(gfx, stage, source, gles, cachekey)
, glShader(0)
{
@@ -44,15 +44,17 @@ bool ShaderStage::loadVolatile()
if (glShader != 0)
return true;
StageType stage = getStageType();
ShaderStageType stage = getStageType();
const char *typestr = "unknown";
getConstant(stage, typestr);
GLenum glstage = 0;
if (stage == STAGE_VERTEX)
if (stage == SHADERSTAGE_VERTEX)
glstage = GL_VERTEX_SHADER;
else if (stage == STAGE_PIXEL)
else if (stage == SHADERSTAGE_PIXEL)
glstage = GL_FRAGMENT_SHADER;
else if (stage == SHADERSTAGE_COMPUTE)
glstage = GL_COMPUTE_SHADER;
else
throw love::Exception("%s shader stage is not handled in OpenGL backend code.", typestr);
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -35,7 +35,7 @@ class ShaderStage final : public love::graphics::ShaderStage, public Volatile
{
public:
ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey);
ShaderStage(love::graphics::Graphics *gfx, ShaderStageType stage, const std::string &source, bool gles, const std::string &cachekey);
virtual ~ShaderStage();
ptrdiff_t getHandle() const override { return glShader; }
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+36 -4
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -86,9 +86,16 @@ int w_Font_getHeight(lua_State *L)
int w_Font_getWidth(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const char *str = luaL_checkstring(L, 2);
luax_catchexcept(L, [&](){ lua_pushinteger(L, t->getWidth(str)); });
if (lua_type(L, 2) == LUA_TSTRING)
{
const char *str = luaL_checkstring(L, 2);
luax_catchexcept(L, [&](){ lua_pushinteger(L, t->getWidth(str)); });
}
else
{
uint32 glyph = (uint32) luaL_checknumber(L, 2);
luax_catchexcept(L, [&](){ lua_pushinteger(L, t->getWidth(glyph)); });
}
return 1;
}
@@ -214,6 +221,30 @@ int w_Font_hasGlyphs(lua_State *L)
return 1;
}
int w_Font_getKerning(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
float kerning = 0.0f;
luax_catchexcept(L, [&]() {
if (lua_type(L, 2) == LUA_TSTRING)
{
std::string left = luax_checkstring(L, 2);
std::string right = luax_checkstring(L, 3);
kerning = t->getKerning(left, right);
}
else
{
uint32 left = (uint32) luaL_checknumber(L, 2);
uint32 right = (uint32) luaL_checknumber(L, 3);
kerning = t->getKerning(left, right);
}
});
lua_pushnumber(L, kerning);
return 1;
}
int w_Font_setFallbacks(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
@@ -246,6 +277,7 @@ static const luaL_Reg w_Font_functions[] =
{ "getDescent", w_Font_getDescent },
{ "getBaseline", w_Font_getBaseline },
{ "hasGlyphs", w_Font_hasGlyphs },
{ "getKerning", w_Font_getKerning },
{ "setFallbacks", w_Font_setFallbacks },
{ "getDPIScale", w_Font_getDPIScale },
{ 0, 0 }
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+147 -67
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -68,8 +68,8 @@ int w_reset(lua_State *)
int w_clear(lua_State *L)
{
OptionalColorf color(Colorf(0.0f, 0.0f, 0.0f, 0.0f));
std::vector<OptionalColorf> colors;
OptionalColorD color(ColorD(0.0, 0.0, 0.0, 0.0));
std::vector<OptionalColorD> colors;
OptionalInt stencil(0);
OptionalDouble depth(1.0);
@@ -93,19 +93,19 @@ int w_clear(lua_State *L)
}
else if (argtype == LUA_TNIL || argtype == LUA_TNONE || luax_objlen(L, i + 1) == 0)
{
colors.push_back(OptionalColorf());
colors.push_back(OptionalColorD());
continue;
}
for (int j = 1; j <= 4; j++)
lua_rawgeti(L, i + 1, j);
OptionalColorf c;
OptionalColorD c;
c.hasValue = true;
c.value.r = (float) luaL_checknumber(L, -4);
c.value.g = (float) luaL_checknumber(L, -3);
c.value.b = (float) luaL_checknumber(L, -2);
c.value.a = (float) luaL_optnumber(L, -1, 1.0);
c.value.r = luaL_checknumber(L, -4);
c.value.g = luaL_checknumber(L, -3);
c.value.b = luaL_checknumber(L, -2);
c.value.a = luaL_optnumber(L, -1, 1.0);
colors.push_back(c);
lua_pop(L, 4);
@@ -627,7 +627,7 @@ int w_stencil(lua_State *L)
luaL_checktype(L, 4, LUA_TBOOLEAN);
if (stencilclear.hasValue)
instance()->clear(OptionalColorf(), stencilclear, OptionalDouble());
instance()->clear(OptionalColorD(), stencilclear, OptionalDouble());
luax_catchexcept(L, [&](){ instance()->drawToStencilBuffer(action, stencilvalue); });
@@ -801,6 +801,8 @@ static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool chec
s.linear = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_LINEAR), s.linear);
s.msaa = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_MSAA), s.msaa);
s.computeWrite = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_COMPUTE_WRITE), s.computeWrite);
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_READABLE));
if (!lua_isnoneornil(L, -1))
s.readable.set(luax_checkboolean(L, -1));
@@ -1195,25 +1197,25 @@ int w_newVolumeTexture(lua_State *L)
int w_newImage(lua_State *L)
{
//luax_markdeprecated(L, "love.graphics.newImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newTexture");
//luax_markdeprecated(L, 1, "love.graphics.newImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newTexture");
return w_newTexture(L);
}
int w_newCubeImage(lua_State *L)
{
//luax_markdeprecated(L, "love.graphics.newCubeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newCubeTexture");
//luax_markdeprecated(L, 1, "love.graphics.newCubeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newCubeTexture");
return w_newCubeTexture(L);
}
int w_newArrayImage(lua_State *L)
{
//luax_markdeprecated(L, "love.graphics.newArrayImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newArrayTexture");
//luax_markdeprecated(L, 1, "love.graphics.newArrayImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newArrayTexture");
return w_newArrayTexture(L);
}
int w_newVolumeImage(lua_State *L)
{
//luax_markdeprecated(L, "love.graphics.newVolumeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newVolumeTexture");
//luax_markdeprecated(L, 1, "love.graphics.newVolumeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newVolumeTexture");
return w_newVolumeTexture(L);
}
@@ -1458,6 +1460,34 @@ int w_newShader(lua_State *L)
return 1;
}
int w_newComputeShader(lua_State* L)
{
std::vector<std::string> stages;
w_getShaderSource(L, 1, stages);
bool should_error = false;
try
{
Shader *shader = instance()->newComputeShader(stages[0]);
luax_pushtype(L, shader);
shader->release();
}
catch (love::Exception &e)
{
luax_getfunction(L, "graphics", "_transformGLSLErrorMessages");
lua_pushstring(L, e.what());
// Function pushes the new error string onto the stack.
lua_pcall(L, 1, 1, 0);
should_error = true;
}
if (should_error)
return lua_error(L);
return 1;
}
int w_validateShader(lua_State *L)
{
bool gles = luax_checkboolean(L, 1);
@@ -1510,6 +1540,44 @@ static void luax_optbuffersettings(lua_State *L, int idx, Buffer::Settings &sett
lua_pop(L, 1);
}
static Buffer::DataDeclaration luax_checkdatadeclaration(lua_State* L, int formattableidx, int arrayindex, int declindex, bool requirename)
{
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
lua_getfield(L, declindex, "name");
if (requirename && lua_type(L, -1) != LUA_TSTRING)
{
std::ostringstream ss;
ss << "'name' field expected in array element #";
ss << arrayindex;
ss << " of format table";
std::string str = ss.str();
luaL_argerror(L, formattableidx, str.c_str());
}
else if (!lua_isnoneornil(L, -1))
decl.name = luax_checkstring(L, -1);
lua_pop(L, 1);
lua_getfield(L, declindex, "format");
if (lua_type(L, -1) != LUA_TSTRING)
{
std::ostringstream ss;
ss << "'format' field expected in array element #";
ss << arrayindex;
ss << " of format table";
std::string str = ss.str();
luaL_argerror(L, formattableidx, str.c_str());
}
const char* formatstr = luaL_checkstring(L, -1);
if (!getConstant(formatstr, decl.format))
luax_enumerror(L, "data format", getConstants(decl.format), formatstr);
lua_pop(L, 1);
decl.arrayLength = luax_intflag(L, declindex, "arraylength", 0);
return decl;
}
static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::DataDeclaration> &format)
{
if (lua_type(L, idx) == LUA_TSTRING)
@@ -1530,29 +1598,7 @@ static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::Da
lua_rawgeti(L, idx, i);
luaL_checktype(L, -1, LUA_TTABLE);
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
lua_getfield(L, -1, "name");
if (!lua_isnoneornil(L, -1))
decl.name = luax_checkstring(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "format");
if (lua_type(L, -1) != LUA_TSTRING)
{
std::ostringstream ss;
ss << "'format' field expected in array element #";
ss << i;
ss << " of format table";
std::string str = ss.str();
luaL_argerror(L, idx, str.c_str());
}
const char *formatstr = luaL_checkstring(L, -1);
if (!getConstant(formatstr, decl.format))
luax_enumerror(L, "data format", getConstants(decl.format), formatstr);
lua_pop(L, 1);
decl.arrayLength = luax_intflag(L, -1, "arraylength", 0);
Buffer::DataDeclaration decl = luax_checkdatadeclaration(L, idx, i, -1, false);
format.push_back(decl);
lua_pop(L, 1);
@@ -1871,38 +1917,43 @@ static Mesh *newCustomMesh(lua_State *L)
}
lua_pop(L, 1);
// Per-vertex attribute formats.
// Per-vertex attribute data formats.
for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
{
lua_rawgeti(L, 1, i);
// {name, datatype, components}
for (int j = 1; j <= 3; j++)
lua_rawgeti(L, -j, j);
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
const char *name = luaL_checkstring(L, -3);
lua_getfield(L, -1, "format");
bool hasformatfield = !lua_isnoneornil(L, -1);
lua_pop(L, 1);
DataFormat format = DATAFORMAT_MAX_ENUM;
const char *tname = luaL_checkstring(L, -2);
if (!lua_isnoneornil(L, -1))
if (hasformatfield || luax_objlen(L, -1) == 0)
decl = luax_checkdatadeclaration(L, 1, i, -1, true);
else
{
int components = (int) luaL_checkinteger(L, -1);
// Legacy format arguments: {name, datatype, components}
for (int j = 1; j <= 3; j++)
lua_rawgeti(L, -j, j);
decl.name = luaL_checkstring(L, -3);
const char* tname = luaL_checkstring(L, -2);
int components = (int)luaL_checkinteger(L, -1);
// Check deprecated format names.
if (strcmp(tname, "byte") == 0 || strcmp(tname, "unorm8") == 0)
{
if (components == 4)
format = DATAFORMAT_UNORM8_VEC4;
decl.format = DATAFORMAT_UNORM8_VEC4;
else
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
}
else if (strcmp(tname, "unorm16") == 0)
{
if (components == 2)
format = DATAFORMAT_UNORM16_VEC2;
decl.format = DATAFORMAT_UNORM16_VEC2;
else if (components == 4)
format = DATAFORMAT_UNORM16_VEC4;
decl.format = DATAFORMAT_UNORM16_VEC4;
else
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
@@ -1910,23 +1961,27 @@ static Mesh *newCustomMesh(lua_State *L)
else if (strcmp(tname, "float") == 0)
{
if (components == 1)
format = DATAFORMAT_FLOAT;
decl.format = DATAFORMAT_FLOAT;
else if (components == 2)
format = DATAFORMAT_FLOAT_VEC2;
decl.format = DATAFORMAT_FLOAT_VEC2;
else if (components == 3)
format = DATAFORMAT_FLOAT_VEC3;
decl.format = DATAFORMAT_FLOAT_VEC3;
else if (components == 4)
format = DATAFORMAT_FLOAT_VEC4;
decl.format = DATAFORMAT_FLOAT_VEC4;
else
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
}
if (decl.format == DATAFORMAT_MAX_ENUM)
luax_enumerror(L, "vertex data format", getConstants(decl.format), tname);
lua_pop(L, 3);
luax_markdeprecated(L, 1, "vertex format array values in love.graphics.newMesh", API_CUSTOM, DEPRECATED_REPLACED, "named table fields 'format' and 'name'");
}
if (format == DATAFORMAT_MAX_ENUM && !getConstant(tname, format))
luax_enumerror(L, "vertex data format", getConstants(format), tname);
lua_pop(L, 4);
vertexformat.emplace_back(name, format);
lua_pop(L, 1);
vertexformat.push_back(decl);
}
if (lua_isnumber(L, 2))
@@ -2587,6 +2642,7 @@ int w_getTextureFormats(lua_State *L)
bool rt = luax_checkboolflag(L, 1, Texture::getConstant(Texture::SETTING_RENDER_TARGET));
bool linear = luax_boolflag(L, 1, Texture::getConstant(Texture::SETTING_LINEAR), false);
bool computewrite = luax_boolflag(L, 1, Texture::getConstant(Texture::SETTING_COMPUTE_WRITE), false);
OptionalBool readable;
lua_getfield(L, 1, Texture::getConstant(Texture::SETTING_READABLE));
@@ -2610,10 +2666,17 @@ int w_getTextureFormats(lua_State *L)
if (rt && isPixelFormatDepth(format))
continue;
bool formatReadable = readable.get(!isPixelFormatDepthStencil(format));
bool sRGB = isGammaCorrect() && !linear;
luax_pushboolean(L, instance()->isPixelFormatSupported(format, rt, formatReadable, sRGB));
uint32 usage = PIXELFORMATUSAGEFLAGS_NONE;
if (rt)
usage |= PIXELFORMATUSAGEFLAGS_RENDERTARGET;
if (readable.get(!isPixelFormatDepthStencil(format)))
usage |= PIXELFORMATUSAGEFLAGS_SAMPLE;
if (computewrite)
usage |= PIXELFORMATUSAGEFLAGS_COMPUTEWRITE;
luax_pushboolean(L, instance()->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, sRGB));
lua_setfield(L, -2, name);
}
@@ -2644,7 +2707,7 @@ static int w__getFormats(lua_State *L, int idx, bool (*isFormatSupported)(PixelF
int w_getCanvasFormats(lua_State *L)
{
luax_markdeprecated(L, "love.graphics.getCanvasFormats", API_FUNCTION, DEPRECATED_REPLACED, "love.graphics.getTextureFormats");
luax_markdeprecated(L, 1, "love.graphics.getCanvasFormats", API_FUNCTION, DEPRECATED_REPLACED, "love.graphics.getTextureFormats");
bool (*supported)(PixelFormat);
@@ -2656,14 +2719,15 @@ int w_getCanvasFormats(lua_State *L)
{
supported = [](PixelFormat format) -> bool
{
return instance()->isPixelFormatSupported(format, true, true, false);
const uint32 usage = PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET;
return instance()->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, false);
};
}
else
{
supported = [](PixelFormat format) -> bool
{
return instance()->isPixelFormatSupported(format, true, false, false);
return instance()->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_RENDERTARGET, false);
};
}
}
@@ -2672,7 +2736,10 @@ int w_getCanvasFormats(lua_State *L)
supported = [](PixelFormat format) -> bool
{
bool readable = !isPixelFormatDepthStencil(format);
return instance()->isPixelFormatSupported(format, true, readable, false);
uint32 usage = PIXELFORMATUSAGEFLAGS_RENDERTARGET;
if (readable)
usage |= PIXELFORMATUSAGEFLAGS_SAMPLE;
return instance()->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, false);
};
}
@@ -2681,11 +2748,11 @@ int w_getCanvasFormats(lua_State *L)
int w_getImageFormats(lua_State *L)
{
luax_markdeprecated(L, "love.graphics.getImageFormats", API_FUNCTION, DEPRECATED_REPLACED, "love.graphics.getTextureFormats");
luax_markdeprecated(L, 1, "love.graphics.getImageFormats", API_FUNCTION, DEPRECATED_REPLACED, "love.graphics.getTextureFormats");
const auto supported = [](PixelFormat format) -> bool
{
return instance()->isPixelFormatSupported(format, false, true, false);
return instance()->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_SAMPLE, false);
};
const auto ignore = [](PixelFormat format) -> bool
@@ -3255,6 +3322,16 @@ int w_polygon(lua_State *L)
return 0;
}
int w_dispatchThreadgroups(lua_State* L)
{
Shader *shader = luax_checkshader(L, 1);
int x = (int) luaL_checkinteger(L, 2);
int y = (int) luaL_optinteger(L, 3, 1);
int z = (int) luaL_optinteger(L, 4, 1);
luax_catchexcept(L, [&](){ instance()->dispatchThreadgroups(shader, x, y, z); });
return 0;
}
int w_copyBuffer(lua_State *L)
{
Buffer *source = luax_checkbuffer(L, 1);
@@ -3405,6 +3482,7 @@ static const luaL_Reg functions[] =
{ "newSpriteBatch", w_newSpriteBatch },
{ "newParticleSystem", w_newParticleSystem },
{ "newShader", w_newShader },
{ "newComputeShader", w_newComputeShader },
{ "newBuffer", w_newBuffer },
{ "newVertexBuffer", w_newVertexBuffer },
{ "newIndexBuffer", w_newIndexBuffer },
@@ -3472,6 +3550,8 @@ static const luaL_Reg functions[] =
{ "print", w_print },
{ "printf", w_printf },
{ "dispatchThreadgroups", w_dispatchThreadgroups },
{ "copyBuffer", w_copyBuffer },
{ "isCreated", w_isCreated },
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -3,7 +3,7 @@ R"luastring"--(
-- There is a matching delimiter at the bottom of the file.
--[[
Copyright (c) 2006-2020 LOVE Development Team
Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+40 -6
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -317,6 +317,7 @@ static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, co
case Shader::UNIFORM_BOOL:
return w_Shader_sendBooleans(L, startidx, shader, info);
case Shader::UNIFORM_SAMPLER:
case Shader::UNIFORM_STORAGETEXTURE:
return w_Shader_sendTextures(L, startidx, shader, info);
case Shader::UNIFORM_TEXELBUFFER:
case Shader::UNIFORM_STORAGEBUFFER:
@@ -328,7 +329,8 @@ static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, co
static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors)
{
if (info->baseType == Shader::UNIFORM_SAMPLER || info->baseType == Shader::UNIFORM_TEXELBUFFER || info->baseType == Shader::UNIFORM_STORAGEBUFFER)
if (info->baseType == Shader::UNIFORM_SAMPLER || info->baseType == Shader::UNIFORM_STORAGETEXTURE
|| info->baseType == Shader::UNIFORM_TEXELBUFFER || info->baseType == Shader::UNIFORM_STORAGEBUFFER)
return luaL_error(L, "Only value types (floats, ints, vectors, matrices, etc) be sent to Shaders via Data objects.");
math::Transform::MatrixLayout layout = math::Transform::MATRIX_ROW_MAJOR;
@@ -491,12 +493,44 @@ int w_Shader_hasUniform(lua_State *L)
return 1;
}
int w_Shader_hasStage(lua_State* L)
{
Shader *shader = luax_checkshader(L, 1);
ShaderStageType stage;
const char *str = luaL_checkstring(L, 2);
if (!ShaderStage::getConstant(str, stage))
return luax_enumerror(L, "shader stage", str);
luax_pushboolean(L, shader->hasStage(stage));
return 1;
}
int w_Shader_getLocalThreadgroupSize(lua_State* L)
{
Shader *shader = luax_checkshader(L, 1);
if (!shader->hasStage(SHADERSTAGE_COMPUTE))
{
lua_pushnil(L);
return 1;
}
int x, y, z;
shader->getLocalThreadgroupSize(&x, &y, &z);
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushinteger(L, z);
return 3;
}
static const luaL_Reg w_Shader_functions[] =
{
{ "getWarnings", w_Shader_getWarnings },
{ "send", w_Shader_send },
{ "sendColor", w_Shader_sendColors },
{ "hasUniform", w_Shader_hasUniform },
{ "getWarnings", w_Shader_getWarnings },
{ "send", w_Shader_send },
{ "sendColor", w_Shader_sendColors },
{ "hasUniform", w_Shader_hasUniform },
{ "hasStage", w_Shader_hasStage },
{ "getLocalThreadgroupSize", w_Shader_getLocalThreadgroupSize },
{ 0, 0 }
};
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+17 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
@@ -279,6 +279,20 @@ int w_Texture_getFormat(lua_State *L)
return 1;
}
int w_Texture_isRenderTarget(lua_State *L)
{
Texture *t = luax_checktexture(L, 1);
luax_pushboolean(L, t->isRenderTarget());
return 1;
}
int w_Texture_isComputeWritable(lua_State *L)
{
Texture *t = luax_checktexture(L, 1);
luax_pushboolean(L, t->isComputeWritable());
return 1;
}
int w_Texture_isReadable(lua_State *L)
{
Texture *t = luax_checktexture(L, 1);
@@ -479,6 +493,8 @@ const luaL_Reg w_Texture_functions[] =
{ "setWrap", w_Texture_setWrap },
{ "getWrap", w_Texture_getWrap },
{ "getFormat", w_Texture_getFormat },
{ "isRenderTarget", w_Texture_isRenderTarget },
{ "isComputeWritable", w_Texture_isComputeWritable },
{ "isReadable", w_Texture_isReadable },
{ "getMipmapMode", w_Texture_getMipmapMode },
{ "getDepthSampleMode", w_Texture_getDepthSampleMode },
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
* Copyright (c) 2006-2021 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
+1 -1
View File
@@ -3,7 +3,7 @@ R"luastring"--(
-- There is a matching delimiter at the bottom of the file.
--[[
Copyright (c) 2006-2020 LOVE Development Team
Copyright (c) 2006-2021 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