mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Merge branch '12.0-development' into metal
This commit is contained in:
@@ -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,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,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,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,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,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,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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,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,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,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,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,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,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,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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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,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,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,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,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,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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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,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,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,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,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,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,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,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,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,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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,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,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,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,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,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,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,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,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,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,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,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,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,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,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
|
||||
|
||||
@@ -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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user