mirror of
https://github.com/love2d/love.git
synced 2026-08-18 03:34:23 +02:00
use location numbers for vertex input attributes, instead of name-based binding.
- add 'location' named field to buffer and mesh vertex format tables, replaces 'name' field. - location numbers are expected to match 'layout (location = #)' qualifiers in vertex inputs in shader code. - deprecate vertex inputs in shader code that don't have layout location qualifiers. - love's default vertex attributes use location 0 for position, 1 for texcoord, and 2 for color. - add new variants of Mesh:attachAttribute, setAttributeEnabled, and isAttributeEnabled which take location numbers instead of names. Improves draw performance in vulkan and metal backends.
This commit is contained in:
@@ -106,8 +106,13 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
|
||||
if (info.baseType == DATA_BASETYPE_BOOL)
|
||||
throw love::Exception("Bool types are not supported in vertex buffers.");
|
||||
|
||||
if (decl.name.empty())
|
||||
throw love::Exception("Vertex buffer attributes must have a name.");
|
||||
if (decl.bindingLocation < 0 || decl.bindingLocation >= VertexAttributes::MAX)
|
||||
{
|
||||
if (decl.bindingLocation == -1 && !decl.name.empty())
|
||||
legacyVertexBindings = true;
|
||||
else
|
||||
throw love::Exception("Vertex buffer attributes must have a valid binding location value within [0, %d).", VertexAttributes::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
if (texelbuffer)
|
||||
@@ -258,6 +263,17 @@ int Buffer::getDataMemberIndex(const std::string &name) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Buffer::getDataMemberIndex(int bindingLocation) const
|
||||
{
|
||||
for (size_t i = 0; i < dataMembers.size(); i++)
|
||||
{
|
||||
if (dataMembers[i].decl.bindingLocation == bindingLocation)
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Buffer::clear(size_t offset, size_t size)
|
||||
{
|
||||
if (isImmutable())
|
||||
@@ -280,53 +296,53 @@ std::vector<Buffer::DataDeclaration> Buffer::getCommonFormatDeclaration(CommonFo
|
||||
return {};
|
||||
case CommonFormat::XYf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_POS }
|
||||
};
|
||||
case CommonFormat::XYZf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC3 }
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC3, 0, ATTRIB_POS }
|
||||
};
|
||||
case CommonFormat::RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4, 0, ATTRIB_COLOR }
|
||||
};
|
||||
case CommonFormat::STf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_TEXCOORD },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4, 0, ATTRIB_COLOR },
|
||||
};
|
||||
case CommonFormat::STPf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3, 0, ATTRIB_TEXCOORD },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4, 0, ATTRIB_COLOR },
|
||||
};
|
||||
case CommonFormat::XYf_STf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_POS },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_TEXCOORD },
|
||||
};
|
||||
case CommonFormat::XYf_STPf:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 },
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_POS },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3, 0, ATTRIB_TEXCOORD },
|
||||
};
|
||||
case CommonFormat::XYf_STf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_POS },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_TEXCOORD },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4, 0, ATTRIB_COLOR },
|
||||
};
|
||||
case CommonFormat::XYf_STus_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_UNORM16_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_POS },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_UNORM16_VEC2, 0, ATTRIB_TEXCOORD },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4, 0, ATTRIB_COLOR },
|
||||
};
|
||||
case CommonFormat::XYf_STPf_RGBAub:
|
||||
return {
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
|
||||
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_POS },
|
||||
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2, 0, ATTRIB_TEXCOORD },
|
||||
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4, 0, ATTRIB_COLOR },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -65,11 +65,13 @@ public:
|
||||
std::string name;
|
||||
DataFormat format;
|
||||
int arrayLength;
|
||||
int bindingLocation;
|
||||
|
||||
DataDeclaration(const std::string &name, DataFormat format, int arrayLength = 0)
|
||||
DataDeclaration(const std::string &name, DataFormat format, int arrayLength = 0, int bindingLocation = -1)
|
||||
: name(name)
|
||||
, format(format)
|
||||
, arrayLength(arrayLength)
|
||||
, bindingLocation(bindingLocation)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -117,6 +119,7 @@ public:
|
||||
const DataMember &getDataMember(int index) const { return dataMembers[index]; }
|
||||
size_t getMemberOffset(int index) const { return dataMembers[index].offset; }
|
||||
int getDataMemberIndex(const std::string &name) const;
|
||||
int getDataMemberIndex(int bindingLocation) const;
|
||||
const std::string &getDebugName() const { return debugName; }
|
||||
|
||||
void setImmutable(bool immutable) { this->immutable = immutable; };
|
||||
@@ -154,6 +157,8 @@ public:
|
||||
**/
|
||||
virtual ptrdiff_t getTexelBufferHandle() const = 0;
|
||||
|
||||
bool hasLegacyVertexBindings() const { return legacyVertexBindings; }
|
||||
|
||||
static std::vector<DataDeclaration> getCommonFormatDeclaration(CommonFormat format);
|
||||
|
||||
class Mapper
|
||||
@@ -198,6 +203,8 @@ protected:
|
||||
bool mapped;
|
||||
MapType mappedType;
|
||||
bool immutable;
|
||||
|
||||
bool legacyVertexBindings = false;
|
||||
|
||||
}; // Buffer
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
||||
if (pixelFormat == PIXELFORMAT_LA8_UNORM && !gfx->isPixelFormatSupported(pixelFormat, PIXELFORMATUSAGEFLAGS_SAMPLE))
|
||||
pixelFormat = PIXELFORMAT_RGBA8_UNORM;
|
||||
|
||||
vertexAttributesID = gfx->registerVertexAttributes(VertexAttributes(vertexFormat, 0));
|
||||
|
||||
loadVolatile();
|
||||
++fontCount;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,8 @@ public:
|
||||
|
||||
uint32 getTextureCacheID() const;
|
||||
|
||||
VertexAttributesID getVertexAttributesID() const { return vertexAttributesID; }
|
||||
|
||||
// Implements Volatile.
|
||||
bool loadVolatile() override;
|
||||
void unloadVolatile() override;
|
||||
@@ -204,6 +206,8 @@ private:
|
||||
// ID which is incremented when the texture cache is invalidated.
|
||||
uint32 textureCacheID;
|
||||
|
||||
VertexAttributesID vertexAttributesID;
|
||||
|
||||
// 1 pixel of transparent padding between glyphs (so quads won't pick up
|
||||
// other glyphs), plus one pixel of transparent padding that the quads will
|
||||
// use, for edge antialiasing.
|
||||
|
||||
@@ -212,8 +212,10 @@ Graphics::Graphics(const char *name)
|
||||
states.reserve(10);
|
||||
states.push_back(DisplayState());
|
||||
|
||||
noAttributesID = registerVertexAttributes(VertexAttributes());
|
||||
|
||||
if (!Shader::initialize())
|
||||
throw love::Exception("Shader support failed to initialize!");
|
||||
throw love::Exception("Shader support failed to initialize.");
|
||||
}
|
||||
|
||||
Graphics::~Graphics()
|
||||
@@ -1413,6 +1415,29 @@ void Graphics::updatePendingReadbacks()
|
||||
}
|
||||
}
|
||||
|
||||
VertexAttributesID Graphics::registerVertexAttributes(const VertexAttributes &attributes)
|
||||
{
|
||||
for (size_t i = 0; i < vertexAttributesDatabase.size(); i++)
|
||||
{
|
||||
if (attributes == vertexAttributesDatabase[i])
|
||||
return { (int)i + 1 };
|
||||
}
|
||||
|
||||
vertexAttributesDatabase.push_back(attributes);
|
||||
return { (int)vertexAttributesDatabase.size() };
|
||||
}
|
||||
|
||||
bool Graphics::findVertexAttributes(VertexAttributesID id, VertexAttributes &attributes)
|
||||
{
|
||||
int index = id.id - 1;
|
||||
|
||||
if (index < 0 || index >= (int)vertexAttributesDatabase.size())
|
||||
return false;
|
||||
|
||||
attributes = vertexAttributesDatabase[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
void Graphics::intersectScissor(const Rect &rect)
|
||||
{
|
||||
Rect currect = states.back().scissorRect;
|
||||
@@ -2022,6 +2047,17 @@ void Graphics::flushBatchedDraws()
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
VertexAttributesID attributesID = sbstate.attributesIDs[(int)sbstate.formats[0]][(int)sbstate.formats[1]];
|
||||
|
||||
if (!findVertexAttributes(attributesID, attributes))
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
attributes.setCommonFormat(sbstate.formats[i], (uint8)i);
|
||||
|
||||
attributesID = registerVertexAttributes(attributes);
|
||||
sbstate.attributesIDs[(int)sbstate.formats[0]][(int)sbstate.formats[1]] = attributesID;
|
||||
}
|
||||
|
||||
size_t usedsizes[3] = {0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
@@ -2029,8 +2065,6 @@ void Graphics::flushBatchedDraws()
|
||||
if (sbstate.formats[i] == CommonFormat::NONE)
|
||||
continue;
|
||||
|
||||
attributes.setCommonFormat(sbstate.formats[i], (uint8) i);
|
||||
|
||||
usedsizes[i] = getFormatStride(sbstate.formats[i]) * sbstate.vertexCount;
|
||||
|
||||
size_t offset = sbstate.vb[i]->unmap(usedsizes[i]);
|
||||
@@ -2053,7 +2087,7 @@ void Graphics::flushBatchedDraws()
|
||||
{
|
||||
usedsizes[2] = sizeof(uint16) * sbstate.indexCount;
|
||||
|
||||
DrawIndexedCommand cmd(&attributes, &buffers, sbstate.indexBuffer);
|
||||
DrawIndexedCommand cmd(attributesID, &buffers, sbstate.indexBuffer);
|
||||
cmd.primitiveType = sbstate.primitiveMode;
|
||||
cmd.indexCount = sbstate.indexCount;
|
||||
cmd.indexType = INDEX_UINT16;
|
||||
@@ -2065,7 +2099,7 @@ void Graphics::flushBatchedDraws()
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawCommand cmd(&attributes, &buffers);
|
||||
DrawCommand cmd(attributesID, &buffers);
|
||||
cmd.primitiveType = sbstate.primitiveMode;
|
||||
cmd.vertexStart = 0;
|
||||
cmd.vertexCount = sbstate.vertexCount;
|
||||
@@ -2156,10 +2190,8 @@ void Graphics::drawFromShader(PrimitiveType primtype, int vertexcount, int insta
|
||||
|
||||
Shader::current->validateDrawState(primtype, maintexture);
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
DrawCommand cmd(&attributes, &buffers);
|
||||
DrawCommand cmd(noAttributesID, &buffers);
|
||||
|
||||
cmd.primitiveType = primtype;
|
||||
cmd.vertexCount = vertexcount;
|
||||
@@ -2190,10 +2222,8 @@ void Graphics::drawFromShader(Buffer *indexbuffer, int indexcount, int instancec
|
||||
|
||||
Shader::current->validateDrawState(PRIMITIVE_TRIANGLES, maintexture);
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
DrawIndexedCommand cmd(&attributes, &buffers, indexbuffer);
|
||||
DrawIndexedCommand cmd(noAttributesID, &buffers, indexbuffer);
|
||||
|
||||
cmd.primitiveType = PRIMITIVE_TRIANGLES;
|
||||
cmd.indexCount = indexcount;
|
||||
@@ -2221,10 +2251,8 @@ void Graphics::drawFromShaderIndirect(PrimitiveType primtype, Buffer *indirectar
|
||||
|
||||
Shader::current->validateDrawState(primtype, maintexture);
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
DrawCommand cmd(&attributes, &buffers);
|
||||
DrawCommand cmd(noAttributesID, &buffers);
|
||||
|
||||
cmd.primitiveType = primtype;
|
||||
cmd.indirectBuffer = indirectargs;
|
||||
@@ -2248,10 +2276,8 @@ void Graphics::drawFromShaderIndirect(Buffer *indexbuffer, Buffer *indirectargs,
|
||||
|
||||
Shader::current->validateDrawState(PRIMITIVE_TRIANGLES, maintexture);
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
DrawIndexedCommand cmd(&attributes, &buffers, indexbuffer);
|
||||
DrawIndexedCommand cmd(noAttributesID, &buffers, indexbuffer);
|
||||
|
||||
cmd.primitiveType = PRIMITIVE_TRIANGLES;
|
||||
cmd.indexType = getIndexDataType(indexbuffer->getDataMember(0).decl.format);
|
||||
|
||||
@@ -233,7 +233,7 @@ public:
|
||||
{
|
||||
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
|
||||
|
||||
const VertexAttributes *attributes;
|
||||
VertexAttributesID attributesID;
|
||||
const BufferBindings *buffers;
|
||||
|
||||
int vertexStart = 0;
|
||||
@@ -248,8 +248,8 @@ public:
|
||||
// TODO: This should be moved out to a state transition API?
|
||||
CullMode cullMode = CULL_NONE;
|
||||
|
||||
DrawCommand(const VertexAttributes *attribs, const BufferBindings *buffers)
|
||||
: attributes(attribs)
|
||||
DrawCommand(VertexAttributesID attributesID, const BufferBindings *buffers)
|
||||
: attributesID(attributesID)
|
||||
, buffers(buffers)
|
||||
{}
|
||||
};
|
||||
@@ -258,7 +258,7 @@ public:
|
||||
{
|
||||
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
|
||||
|
||||
const VertexAttributes *attributes;
|
||||
VertexAttributesID attributesID;
|
||||
const BufferBindings *buffers;
|
||||
|
||||
int indexCount = 0;
|
||||
@@ -276,8 +276,8 @@ public:
|
||||
// TODO: This should be moved out to a state transition API?
|
||||
CullMode cullMode = CULL_NONE;
|
||||
|
||||
DrawIndexedCommand(const VertexAttributes *attribs, const BufferBindings *buffers, Resource *indexbuffer)
|
||||
: attributes(attribs)
|
||||
DrawIndexedCommand(VertexAttributesID attributesID, const BufferBindings *buffers, Resource *indexbuffer)
|
||||
: attributesID(attributesID)
|
||||
, buffers(buffers)
|
||||
, indexBuffer(indexbuffer)
|
||||
{}
|
||||
@@ -877,7 +877,7 @@ public:
|
||||
|
||||
virtual void draw(const DrawCommand &cmd) = 0;
|
||||
virtual void draw(const DrawIndexedCommand &cmd) = 0;
|
||||
virtual void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, Texture *texture) = 0;
|
||||
virtual void drawQuads(int start, int count, VertexAttributesID attributesID, const BufferBindings &buffers, Texture *texture) = 0;
|
||||
|
||||
void flushBatchedDraws();
|
||||
BatchedVertexData requestBatchedDraw(const BatchedDrawCommand &command);
|
||||
@@ -894,6 +894,9 @@ public:
|
||||
|
||||
void validateIndirectArgsBuffer(IndirectArgsType argstype, Buffer *indirectargs, int argsindex);
|
||||
|
||||
VertexAttributesID registerVertexAttributes(const VertexAttributes &attributes);
|
||||
bool findVertexAttributes(VertexAttributesID id, VertexAttributes &attributes);
|
||||
|
||||
template <typename T>
|
||||
T *getScratchBuffer(size_t count)
|
||||
{
|
||||
@@ -964,27 +967,22 @@ protected:
|
||||
|
||||
struct BatchedDrawState
|
||||
{
|
||||
StreamBuffer *vb[2];
|
||||
StreamBuffer *vb[2] = {};
|
||||
StreamBuffer *indexBuffer = nullptr;
|
||||
|
||||
PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
|
||||
CommonFormat formats[2];
|
||||
CommonFormat formats[2] = {};
|
||||
StrongRef<Texture> texture;
|
||||
Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
|
||||
int vertexCount = 0;
|
||||
int indexCount = 0;
|
||||
|
||||
StreamBuffer::MapInfo vbMap[2];
|
||||
VertexAttributesID attributesIDs[(int)CommonFormat::COUNT][(int)CommonFormat::COUNT] = {};
|
||||
|
||||
StreamBuffer::MapInfo vbMap[2] = {};
|
||||
StreamBuffer::MapInfo indexBufferMap = StreamBuffer::MapInfo();
|
||||
|
||||
bool flushing = false;
|
||||
|
||||
BatchedDrawState()
|
||||
{
|
||||
vb[0] = vb[1] = nullptr;
|
||||
formats[0] = formats[1] = CommonFormat::NONE;
|
||||
vbMap[0] = vbMap[1] = StreamBuffer::MapInfo();
|
||||
}
|
||||
};
|
||||
|
||||
struct TemporaryBuffer
|
||||
@@ -1107,6 +1105,10 @@ private:
|
||||
|
||||
std::unordered_map<std::string, ShaderStage *> cachedShaderStages[SHADERSTAGE_MAX_ENUM];
|
||||
|
||||
std::vector<VertexAttributes> vertexAttributesDatabase;
|
||||
|
||||
VertexAttributesID noAttributesID;
|
||||
|
||||
}; // Graphics
|
||||
|
||||
STRINGMAP_DECLARE(Renderer);
|
||||
|
||||
+154
-32
@@ -115,9 +115,19 @@ Mesh::Mesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType d
|
||||
|
||||
finalizeAttribute(attrib);
|
||||
|
||||
int attributeIndex = getAttachedAttributeIndex(attrib.name);
|
||||
if (attributeIndex != i && attributeIndex != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", attrib.name.c_str());
|
||||
if (attrib.bindingLocation >= 0)
|
||||
{
|
||||
int attributeIndex = getAttachedAttributeIndex(attrib.bindingLocation);
|
||||
if (attributeIndex != i && attributeIndex != -1)
|
||||
throw love::Exception("Duplicate vertex attribute binding location: %d", attrib.bindingLocation);
|
||||
}
|
||||
|
||||
if (!attrib.name.empty())
|
||||
{
|
||||
int attributeIndex = getAttachedAttributeIndex(attrib.name);
|
||||
if (attributeIndex != i && attributeIndex != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", attrib.name.c_str());
|
||||
}
|
||||
|
||||
vertexCount = std::min(vertexCount, attrib.buffer->getArrayLength());
|
||||
}
|
||||
@@ -137,16 +147,28 @@ void Mesh::setupAttachedAttributes()
|
||||
for (size_t i = 0; i < vertexFormat.size(); i++)
|
||||
{
|
||||
const std::string &name = vertexFormat[i].decl.name;
|
||||
int bindingLocation = vertexFormat[i].decl.bindingLocation;
|
||||
|
||||
if (getAttachedAttributeIndex(name) != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||
if (bindingLocation >= 0)
|
||||
{
|
||||
if (getAttachedAttributeIndex(bindingLocation) != -1)
|
||||
throw love::Exception("Duplicate vertex attribute binding location: %d", bindingLocation);
|
||||
}
|
||||
|
||||
BuiltinVertexAttribute builtinattrib;
|
||||
int builtinAttribIndex = -1;
|
||||
if (getConstant(name.c_str(), builtinattrib))
|
||||
builtinAttribIndex = (int)builtinattrib;
|
||||
if (!name.empty())
|
||||
{
|
||||
if (getAttachedAttributeIndex(name) != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||
}
|
||||
|
||||
attachedAttributes.push_back({name, vertexBuffer, nullptr, name, (int) i, 0, STEP_PER_VERTEX, builtinAttribIndex, true});
|
||||
if (bindingLocation < 0)
|
||||
{
|
||||
BuiltinVertexAttribute builtinattrib;
|
||||
if (getConstant(name.c_str(), builtinattrib))
|
||||
bindingLocation = (int)builtinattrib;
|
||||
}
|
||||
|
||||
attachedAttributes.push_back({name, vertexBuffer, nullptr, name, bindingLocation, (int) i, 0, STEP_PER_VERTEX, bindingLocation, true});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +183,17 @@ int Mesh::getAttachedAttributeIndex(const std::string &name) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Mesh::getAttachedAttributeIndex(int bindingLocation) const
|
||||
{
|
||||
for (int i = 0; i < (int)attachedAttributes.size(); i++)
|
||||
{
|
||||
if (attachedAttributes[i].bindingLocation == bindingLocation)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Mesh::finalizeAttribute(BufferAttribute &attrib) const
|
||||
{
|
||||
if ((attrib.buffer->getUsageFlags() & BUFFERUSAGEFLAG_VERTEX) == 0)
|
||||
@@ -169,17 +202,33 @@ void Mesh::finalizeAttribute(BufferAttribute &attrib) const
|
||||
if (attrib.startArrayIndex < 0 || attrib.startArrayIndex >= (int)attrib.buffer->getArrayLength())
|
||||
throw love::Exception("Invalid start array index %d.", attrib.startArrayIndex + 1);
|
||||
|
||||
int indexInBuffer = attrib.buffer->getDataMemberIndex(attrib.nameInBuffer);
|
||||
if (indexInBuffer < 0)
|
||||
throw love::Exception("Buffer does not have a vertex attribute with name '%s'.", attrib.nameInBuffer.c_str());
|
||||
|
||||
BuiltinVertexAttribute builtinattrib;
|
||||
if (getConstant(attrib.name.c_str(), builtinattrib))
|
||||
attrib.builtinAttributeIndex = (int)builtinattrib;
|
||||
if (attrib.bindingLocationInBuffer >= 0)
|
||||
{
|
||||
int indexInBuffer = attrib.buffer->getDataMemberIndex(attrib.bindingLocationInBuffer);
|
||||
if (indexInBuffer < 0)
|
||||
throw love::Exception("Buffer does not have a vertex attribute with binding location %d.", attrib.bindingLocationInBuffer);
|
||||
attrib.indexInBuffer = indexInBuffer;
|
||||
}
|
||||
else
|
||||
attrib.builtinAttributeIndex = -1;
|
||||
{
|
||||
int indexInBuffer = attrib.buffer->getDataMemberIndex(attrib.nameInBuffer);
|
||||
if (indexInBuffer < 0)
|
||||
throw love::Exception("Buffer does not have a vertex attribute with name '%s'.", attrib.nameInBuffer.c_str());
|
||||
attrib.indexInBuffer = indexInBuffer;
|
||||
}
|
||||
|
||||
attrib.indexInBuffer = indexInBuffer;
|
||||
if (attrib.bindingLocation < 0)
|
||||
attrib.bindingLocation = attrib.buffer->getDataMember(attrib.indexInBuffer).decl.bindingLocation;
|
||||
|
||||
if (attrib.bindingLocation < 0)
|
||||
{
|
||||
BuiltinVertexAttribute builtinattrib;
|
||||
if (getConstant(attrib.name.c_str(), builtinattrib))
|
||||
attrib.bindingLocation = (int)builtinattrib;
|
||||
}
|
||||
|
||||
if (attrib.bindingLocation >= VertexAttributes::MAX || (attrib.bindingLocation < 0 && attrib.name.empty()))
|
||||
throw love::Exception("Vertex attributes must have a valid binding location value within [0, %d).", VertexAttributes::MAX);
|
||||
}
|
||||
|
||||
void *Mesh::checkVertexDataOffset(size_t vertindex, size_t *byteoffset)
|
||||
@@ -224,6 +273,7 @@ void Mesh::setAttributeEnabled(const std::string &name, bool enable)
|
||||
throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
|
||||
|
||||
attachedAttributes[index].enabled = enable;
|
||||
attributesID.invalidate();
|
||||
}
|
||||
|
||||
bool Mesh::isAttributeEnabled(const std::string &name) const
|
||||
@@ -235,10 +285,29 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
|
||||
return attachedAttributes[index].enabled;
|
||||
}
|
||||
|
||||
void Mesh::setAttributeEnabled(int bindingLocation, bool enable)
|
||||
{
|
||||
int index = getAttachedAttributeIndex(bindingLocation);
|
||||
if (index == -1)
|
||||
throw love::Exception("Mesh does not have an attached vertex attribute with binding location %d", bindingLocation);
|
||||
|
||||
attachedAttributes[index].enabled = enable;
|
||||
attributesID.invalidate();
|
||||
}
|
||||
|
||||
bool Mesh::isAttributeEnabled(int bindingLocation) const
|
||||
{
|
||||
int index = getAttachedAttributeIndex(bindingLocation);
|
||||
if (index == -1)
|
||||
throw love::Exception("Mesh does not have an attached vertex attribute with binding location %d", bindingLocation);
|
||||
|
||||
return attachedAttributes[index].enabled;
|
||||
}
|
||||
|
||||
void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, int startindex, AttributeStep step)
|
||||
{
|
||||
BufferAttribute oldattrib = {};
|
||||
BufferAttribute newattrib = {};
|
||||
BufferAttribute oldattrib;
|
||||
BufferAttribute newattrib;
|
||||
|
||||
int oldindex = getAttachedAttributeIndex(name);
|
||||
if (oldindex != -1)
|
||||
@@ -257,13 +326,42 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh,
|
||||
|
||||
finalizeAttribute(newattrib);
|
||||
|
||||
if (newattrib.indexInBuffer < 0)
|
||||
throw love::Exception("The specified vertex buffer does not have a vertex attribute named '%s'", attachname.c_str());
|
||||
if (oldindex != -1)
|
||||
attachedAttributes[oldindex] = newattrib;
|
||||
else
|
||||
attachedAttributes.push_back(newattrib);
|
||||
|
||||
attributesID.invalidate();
|
||||
}
|
||||
|
||||
void Mesh::attachAttribute(int bindingLocation, Buffer *buffer, Mesh *mesh, int attachBindingLocation, int startindex, AttributeStep step)
|
||||
{
|
||||
BufferAttribute oldattrib;
|
||||
BufferAttribute newattrib;
|
||||
|
||||
int oldindex = getAttachedAttributeIndex(bindingLocation);
|
||||
if (oldindex != -1)
|
||||
oldattrib = attachedAttributes[oldindex];
|
||||
else if (attachedAttributes.size() + 1 > VertexAttributes::MAX)
|
||||
throw love::Exception("A maximum of %d attributes can be attached at once.", VertexAttributes::MAX);
|
||||
|
||||
newattrib.bindingLocation = bindingLocation;
|
||||
newattrib.buffer = buffer;
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
|
||||
newattrib.bindingLocationInBuffer = attachBindingLocation;
|
||||
newattrib.indexInBuffer = -1;
|
||||
newattrib.startArrayIndex = startindex;
|
||||
newattrib.step = step;
|
||||
|
||||
finalizeAttribute(newattrib);
|
||||
|
||||
if (oldindex != -1)
|
||||
attachedAttributes[oldindex] = newattrib;
|
||||
else
|
||||
attachedAttributes.push_back(newattrib);
|
||||
|
||||
attributesID.invalidate();
|
||||
}
|
||||
|
||||
bool Mesh::detachAttribute(const std::string &name)
|
||||
@@ -277,6 +375,22 @@ bool Mesh::detachAttribute(const std::string &name)
|
||||
if (vertexBuffer.get() && vertexBuffer->getDataMemberIndex(name) != -1)
|
||||
attachAttribute(name, vertexBuffer, nullptr, name);
|
||||
|
||||
attributesID.invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mesh::detachAttribute(int bindingLocation)
|
||||
{
|
||||
int index = getAttachedAttributeIndex(bindingLocation);
|
||||
if (index == -1)
|
||||
return false;
|
||||
|
||||
attachedAttributes.erase(attachedAttributes.begin() + index);
|
||||
|
||||
if (vertexBuffer.get() && vertexBuffer->getDataMemberIndex(bindingLocation) != -1)
|
||||
attachAttribute(bindingLocation, vertexBuffer, nullptr, bindingLocation);
|
||||
|
||||
attributesID.invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -593,6 +707,8 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
|
||||
if (Shader::current)
|
||||
Shader::current->validateDrawState(primitiveType, texture);
|
||||
|
||||
bool attributesIDneedsupdate = !attributesID.isValid();
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
@@ -604,14 +720,17 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
|
||||
continue;
|
||||
|
||||
Buffer *buffer = attrib.buffer.get();
|
||||
int attributeindex = attrib.builtinAttributeIndex;
|
||||
int bindinglocation = attrib.bindingLocation;
|
||||
|
||||
// If the attribute is one of the LOVE-defined ones, use the constant
|
||||
// attribute index for it, otherwise query the index from the shader.
|
||||
if (attributeindex < 0 && Shader::current)
|
||||
attributeindex = Shader::current->getVertexAttributeIndex(attrib.name);
|
||||
// Query the index from the shader as a fallback to support old code that
|
||||
// hasn't set a binding location.
|
||||
if (bindinglocation < 0 && Shader::current)
|
||||
{
|
||||
bindinglocation = Shader::current->getVertexAttributeIndex(attrib.name);
|
||||
attributesIDneedsupdate = true;
|
||||
}
|
||||
|
||||
if (attributeindex >= 0)
|
||||
if (bindinglocation >= 0)
|
||||
{
|
||||
if (attrib.mesh.get())
|
||||
attrib.mesh->flush();
|
||||
@@ -622,7 +741,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
|
||||
uint16 stride = (uint16) buffer->getArrayStride();
|
||||
size_t bufferoffset = (size_t) stride * attrib.startArrayIndex;
|
||||
|
||||
attributes.set(attributeindex, member.decl.format, offset, activebuffers);
|
||||
attributes.set(bindinglocation, member.decl.format, offset, activebuffers);
|
||||
attributes.setBufferLayout(activebuffers, stride, attrib.step);
|
||||
|
||||
// TODO: Ideally we want to reuse buffers with the same stride+step.
|
||||
@@ -635,6 +754,9 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
|
||||
if ((attributes.enableBits & ~(ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR)) == 0)
|
||||
throw love::Exception("Mesh must have an enabled VertexPosition or custom attribute to be drawn.");
|
||||
|
||||
if (attributesIDneedsupdate)
|
||||
attributesID = gfx->registerVertexAttributes(attributes);
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
Buffer *indexbuffer = useIndexBuffer ? indexBuffer : nullptr;
|
||||
@@ -660,7 +782,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
|
||||
if (range.isValid())
|
||||
r.intersect(range);
|
||||
|
||||
Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexbuffer);
|
||||
Graphics::DrawIndexedCommand cmd(attributesID, &buffers, indexbuffer);
|
||||
|
||||
cmd.primitiveType = primitiveType;
|
||||
cmd.indexType = indexDataType;
|
||||
@@ -683,7 +805,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
|
||||
if (range.isValid())
|
||||
r.intersect(range);
|
||||
|
||||
Graphics::DrawCommand cmd(&attributes, &buffers);
|
||||
Graphics::DrawCommand cmd(attributesID, &buffers);
|
||||
|
||||
cmd.primitiveType = primitiveType;
|
||||
cmd.vertexStart = (int) r.getOffset();
|
||||
|
||||
@@ -57,11 +57,12 @@ public:
|
||||
StrongRef<Buffer> buffer;
|
||||
StrongRef<Mesh> mesh;
|
||||
std::string nameInBuffer;
|
||||
int indexInBuffer;
|
||||
int startArrayIndex;
|
||||
AttributeStep step;
|
||||
int builtinAttributeIndex;
|
||||
bool enabled;
|
||||
int bindingLocationInBuffer = -1;
|
||||
int indexInBuffer = 0;
|
||||
int startArrayIndex = 0;
|
||||
AttributeStep step = STEP_PER_VERTEX;
|
||||
int bindingLocation = -1;
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
static love::Type type;
|
||||
@@ -104,6 +105,8 @@ public:
|
||||
**/
|
||||
void setAttributeEnabled(const std::string &name, bool enable);
|
||||
bool isAttributeEnabled(const std::string &name) const;
|
||||
void setAttributeEnabled(int bindingLocation, bool enable);
|
||||
bool isAttributeEnabled(int bindingLocation) const;
|
||||
|
||||
/**
|
||||
* Attaches a vertex attribute from another vertex buffer to this Mesh. The
|
||||
@@ -114,6 +117,8 @@ public:
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, int startindex = 0, AttributeStep step = STEP_PER_VERTEX);
|
||||
bool detachAttribute(const std::string &name);
|
||||
void attachAttribute(int bindingLocation, Buffer *buffer, Mesh *mesh, int attachBindingLocation, int startindex = 0, AttributeStep step = STEP_PER_VERTEX);
|
||||
bool detachAttribute(int bindingLocation);
|
||||
const std::vector<BufferAttribute> &getAttachedAttributes() const;
|
||||
|
||||
void *getVertexData() const;
|
||||
@@ -188,6 +193,7 @@ private:
|
||||
|
||||
void setupAttachedAttributes();
|
||||
int getAttachedAttributeIndex(const std::string &name) const;
|
||||
int getAttachedAttributeIndex(int bindingLocation) const;
|
||||
void finalizeAttribute(BufferAttribute &attrib) const;
|
||||
|
||||
void drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buffer *indirectargs, int argsindex);
|
||||
@@ -196,6 +202,8 @@ private:
|
||||
|
||||
std::vector<BufferAttribute> attachedAttributes;
|
||||
|
||||
VertexAttributesID attributesID;
|
||||
|
||||
// Vertex buffer, for the vertex data.
|
||||
StrongRef<Buffer> vertexBuffer;
|
||||
uint8 *vertexData = nullptr;
|
||||
|
||||
@@ -93,7 +93,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
|
||||
, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
|
||||
, defaultOffset(true)
|
||||
, relativeRotation(false)
|
||||
, vertexAttributes(CommonFormat::XYf_STf_RGBAub, 0)
|
||||
, vertexAttributesID(Module::getInstance<Graphics>(Module::M_GRAPHICS)->registerVertexAttributes(VertexAttributes(CommonFormat::XYf_STf_RGBAub, 0)))
|
||||
, buffer(nullptr)
|
||||
{
|
||||
if (size == 0 || size > MAX_PARTICLES)
|
||||
@@ -154,7 +154,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
|
||||
, colors(p.colors)
|
||||
, quads(p.quads)
|
||||
, relativeRotation(p.relativeRotation)
|
||||
, vertexAttributes(p.vertexAttributes)
|
||||
, vertexAttributesID(p.vertexAttributesID)
|
||||
, buffer(nullptr)
|
||||
{
|
||||
setBufferSize(maxParticles);
|
||||
@@ -1087,7 +1087,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
vertexbuffers.set(0, buffer, 0);
|
||||
|
||||
Texture *tex = gfx->getTextureOrDefaultForActiveShader(texture);
|
||||
gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, tex);
|
||||
gfx->drawQuads(0, pCount, vertexAttributesID, vertexbuffers, tex);
|
||||
}
|
||||
|
||||
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
|
||||
|
||||
@@ -673,7 +673,7 @@ private:
|
||||
|
||||
bool relativeRotation;
|
||||
|
||||
const VertexAttributes vertexAttributes;
|
||||
VertexAttributesID vertexAttributesID;
|
||||
Buffer *buffer;
|
||||
|
||||
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
|
||||
|
||||
@@ -650,6 +650,22 @@ Shader::Shader(StrongRef<ShaderStage> _stages[], const CompileOptions &options)
|
||||
if (!validateInternal(_stages, err, reflection))
|
||||
throw love::Exception("%s", err.c_str());
|
||||
|
||||
std::vector<std::string> unsetVertexInputLocations;
|
||||
|
||||
for (const auto &kvp : reflection.vertexInputs)
|
||||
{
|
||||
if (kvp.second < 0)
|
||||
unsetVertexInputLocations.push_back(kvp.first);
|
||||
}
|
||||
|
||||
if (!unsetVertexInputLocations.empty())
|
||||
{
|
||||
std::string str = unsetVertexInputLocations[0];
|
||||
for (size_t i = 1; i < unsetVertexInputLocations.size(); i++)
|
||||
str += ", " + unsetVertexInputLocations[i];
|
||||
unsetVertexInputLocationsString = str;
|
||||
}
|
||||
|
||||
activeTextures.resize(reflection.textureCount);
|
||||
activeBuffers.resize(reflection.bufferCount);
|
||||
|
||||
@@ -1150,6 +1166,17 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < program.getNumPipeInputs(); i++)
|
||||
{
|
||||
const glslang::TObjectReflection &info = program.getPipeInput(i);
|
||||
|
||||
int location = info.layoutLocation();
|
||||
if (location == glslang::TQualifier::layoutLocationEnd)
|
||||
location = -1;
|
||||
|
||||
reflection.vertexInputs[info.name] = location;
|
||||
}
|
||||
|
||||
reflection.textureCount = 0;
|
||||
reflection.bufferCount = 0;
|
||||
|
||||
|
||||
@@ -269,6 +269,8 @@ public:
|
||||
bool isUsingDeprecatedTextureFunctions() const;
|
||||
bool isUsingDeprecatedTextureUniform() const;
|
||||
|
||||
const std::string& getUnsetVertexInputLocationsString() const { return unsetVertexInputLocationsString; }
|
||||
|
||||
static SourceInfo getSourceInfo(const std::string &src);
|
||||
static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const CompileOptions &options, const SourceInfo &info, bool gles, bool checksystemfeatures);
|
||||
|
||||
@@ -289,6 +291,8 @@ protected:
|
||||
|
||||
struct Reflection
|
||||
{
|
||||
std::map<std::string, int> vertexInputs;
|
||||
|
||||
std::map<std::string, UniformInfo> texelBuffers;
|
||||
std::map<std::string, UniformInfo> storageBuffers;
|
||||
std::map<std::string, UniformInfo> sampledTextures;
|
||||
@@ -332,6 +336,8 @@ protected:
|
||||
|
||||
std::string debugName;
|
||||
|
||||
std::string unsetVertexInputLocationsString;
|
||||
|
||||
}; // Shader
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -46,6 +46,7 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferDataUs
|
||||
, next(0)
|
||||
, color(255, 255, 255, 255)
|
||||
, colorf(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
, attributesID()
|
||||
, array_buf(nullptr)
|
||||
, vertex_data(nullptr)
|
||||
, modified_sprites()
|
||||
@@ -134,7 +135,7 @@ int SpriteBatch::addLayer(int layer, const Matrix4 &m, int index)
|
||||
int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
|
||||
{
|
||||
if (vertex_format != CommonFormat::XYf_STPf_RGBAub)
|
||||
throw love::Exception("addLayer can only be called on a SpriteBatch that uses an Array Texture!");
|
||||
throw love::Exception("addLayer can only be called on a SpriteBatch that uses an Array Texture.");
|
||||
|
||||
if (index < -1 || index >= size)
|
||||
throw love::Exception("Invalid sprite index: %d", index + 1);
|
||||
@@ -284,14 +285,16 @@ void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer, Mesh
|
||||
|
||||
newattrib.buffer = buffer;
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.bindingIndex = buffer->getDataMember(newattrib.index).decl.bindingLocation;
|
||||
|
||||
BuiltinVertexAttribute builtinattrib;
|
||||
if (getConstant(name.c_str(), builtinattrib))
|
||||
newattrib.builtinAttributeIndex = (int)builtinattrib;
|
||||
else
|
||||
newattrib.builtinAttributeIndex = -1;
|
||||
if (newattrib.bindingIndex < 0 && getConstant(name.c_str(), builtinattrib))
|
||||
newattrib.bindingIndex = (int)builtinattrib;
|
||||
|
||||
attached_attributes[name] = newattrib;
|
||||
|
||||
// Invalidate attributes ID.
|
||||
attributesID = VertexAttributesID();
|
||||
}
|
||||
|
||||
void SpriteBatch::setDrawRange(int start, int count)
|
||||
@@ -342,6 +345,8 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
|
||||
flush(); // Upload any modified sprite data to the GPU.
|
||||
|
||||
bool attributesIDneedsupdate = !attributesID.isValid();
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
@@ -361,14 +366,17 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
if (buffer->getArrayLength() < (size_t) next * 4)
|
||||
throw love::Exception("Buffer with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
|
||||
|
||||
int attributeindex = it.second.builtinAttributeIndex;
|
||||
int bindingindex = it.second.bindingIndex;
|
||||
|
||||
// If the attribute is one of the LOVE-defined ones, use the constant
|
||||
// attribute index for it, otherwise query the index from the shader.
|
||||
if (attributeindex < 0 && Shader::current)
|
||||
attributeindex = Shader::current->getVertexAttributeIndex(it.first);
|
||||
if (bindingindex < 0 && Shader::current)
|
||||
{
|
||||
bindingindex = Shader::current->getVertexAttributeIndex(it.first);
|
||||
attributesIDneedsupdate = true;
|
||||
}
|
||||
|
||||
if (attributeindex >= 0)
|
||||
if (bindingindex >= 0)
|
||||
{
|
||||
if (it.second.mesh.get())
|
||||
it.second.mesh->flush();
|
||||
@@ -378,7 +386,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
|
||||
uint16 stride = (uint16) buffer->getArrayStride();
|
||||
|
||||
attributes.set(attributeindex, member.decl.format, offset, activebuffers);
|
||||
attributes.set(bindingindex, member.decl.format, offset, activebuffers);
|
||||
attributes.setBufferLayout(activebuffers, stride);
|
||||
|
||||
// TODO: We should reuse buffer bindings with the same buffer+stride+step.
|
||||
@@ -387,6 +395,9 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
}
|
||||
}
|
||||
|
||||
if (attributesIDneedsupdate)
|
||||
attributesID = gfx->registerVertexAttributes(attributes);
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
int start = std::min(std::max(0, range_start), next - 1);
|
||||
@@ -400,7 +411,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
if (count > 0)
|
||||
{
|
||||
Texture *tex = gfx->getTextureOrDefaultForActiveShader(texture);
|
||||
gfx->drawQuads(start, count, attributes, buffers, tex);
|
||||
gfx->drawQuads(start, count, attributesID, buffers, tex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ private:
|
||||
StrongRef<Buffer> buffer;
|
||||
StrongRef<Mesh> mesh;
|
||||
int index;
|
||||
int builtinAttributeIndex;
|
||||
int bindingIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -137,6 +137,8 @@ private:
|
||||
CommonFormat vertex_format;
|
||||
size_t vertex_stride;
|
||||
|
||||
VertexAttributesID attributesID;
|
||||
|
||||
StrongRef<love::graphics::Buffer> array_buf;
|
||||
uint8 *vertex_data;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ love::Type TextBatch::type("TextBatch", &Drawable::type);
|
||||
|
||||
TextBatch::TextBatch(Font *font, const std::vector<love::font::ColoredString> &text)
|
||||
: font(font)
|
||||
, vertexAttributes(Font::vertexFormat, 0)
|
||||
, vertexAttributesID(font->getVertexAttributesID())
|
||||
, vertexData(nullptr)
|
||||
, modifiedVertices()
|
||||
, vertOffset(0)
|
||||
@@ -218,6 +218,7 @@ void TextBatch::setFont(Font *f)
|
||||
// Invalidate the texture cache ID since the font is different. We also have
|
||||
// to re-upload all the vertices based on the new font's textures.
|
||||
textureCacheID = (uint32) -1;
|
||||
vertexAttributesID = font->getVertexAttributesID();
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
@@ -292,7 +293,7 @@ void TextBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
for (const Font::DrawCommand &cmd : drawCommands)
|
||||
{
|
||||
Texture *tex = gfx->getTextureOrDefaultForActiveShader(cmd.texture);
|
||||
gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, tex);
|
||||
gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributesID, vertexBuffers, tex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
|
||||
StrongRef<Font> font;
|
||||
|
||||
VertexAttributes vertexAttributes;
|
||||
VertexAttributesID vertexAttributesID;
|
||||
BufferBindings vertexBuffers;
|
||||
|
||||
StrongRef<Buffer> vertexBuffer;
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
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 drawQuads(int start, int count, VertexAttributesID attributesID, const BufferBindings &buffers, love::graphics::Texture *texture) override;
|
||||
|
||||
void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override;
|
||||
void clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth) override;
|
||||
@@ -209,7 +209,7 @@ private:
|
||||
void endPass(bool presenting);
|
||||
|
||||
id<MTLDepthStencilState> getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil);
|
||||
void applyRenderState(id<MTLRenderCommandEncoder> renderEncoder, const VertexAttributes &attributes);
|
||||
void applyRenderState(id<MTLRenderCommandEncoder> renderEncoder, VertexAttributesID attributesID);
|
||||
bool applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::graphics::Shader *shader);
|
||||
void applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, love::graphics::Shader *shader, Texture *maintex);
|
||||
|
||||
|
||||
@@ -910,7 +910,7 @@ id<MTLDepthStencilState> Graphics::getCachedDepthStencilState(const DepthState &
|
||||
return mtlstate;
|
||||
}
|
||||
|
||||
void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const VertexAttributes &attributes)
|
||||
void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, VertexAttributesID attributesID)
|
||||
{
|
||||
const uint32 pipelineStateBits = STATEBIT_SHADER | STATEBIT_BLEND | STATEBIT_COLORMASK;
|
||||
|
||||
@@ -987,18 +987,18 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const Verte
|
||||
[encoder setCullMode:mode];
|
||||
}
|
||||
|
||||
if ((dirtyState & pipelineStateBits) != 0 || !(attributes == lastRenderPipelineKey.vertexAttributes))
|
||||
if ((dirtyState & pipelineStateBits) != 0 || attributesID != lastRenderPipelineKey.vertexAttributesID)
|
||||
{
|
||||
auto &key = lastRenderPipelineKey;
|
||||
|
||||
key.vertexAttributes = attributes;
|
||||
key.vertexAttributesID = attributesID;
|
||||
|
||||
Shader *shader = (Shader *) Shader::current;
|
||||
id<MTLRenderPipelineState> pipeline = nil;
|
||||
|
||||
if (shader)
|
||||
{
|
||||
key.blend = state.blend;
|
||||
key.blendStateKey = state.blend.toKey();
|
||||
key.colorChannelMask = state.colorMask;
|
||||
|
||||
pipeline = shader->getCachedRenderPipeline(this, key);
|
||||
@@ -1233,7 +1233,7 @@ void Graphics::draw(const DrawCommand &cmd)
|
||||
dirtyRenderState |= STATEBIT_CULLMODE;
|
||||
}
|
||||
|
||||
applyRenderState(encoder, *cmd.attributes);
|
||||
applyRenderState(encoder, cmd.attributesID);
|
||||
applyShaderUniforms(encoder, Shader::current, cmd.texture);
|
||||
|
||||
setVertexBuffers(encoder, Shader::current, cmd.buffers, renderBindings);
|
||||
@@ -1265,7 +1265,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
dirtyRenderState |= STATEBIT_CULLMODE;
|
||||
}
|
||||
|
||||
applyRenderState(encoder, *cmd.attributes);
|
||||
applyRenderState(encoder, cmd.attributesID);
|
||||
applyShaderUniforms(encoder, Shader::current, cmd.texture);
|
||||
|
||||
setVertexBuffers(encoder, Shader::current, cmd.buffers, renderBindings);
|
||||
@@ -1317,7 +1317,7 @@ static inline void advanceVertexOffsets(const VertexAttributes &attributes, Buff
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture)
|
||||
void Graphics::drawQuads(int start, int count, VertexAttributesID attributesID, const BufferBindings &buffers, love::graphics::Texture *texture)
|
||||
{ @autoreleasepool {
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
@@ -1330,7 +1330,7 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
|
||||
dirtyRenderState |= STATEBIT_CULLMODE;
|
||||
}
|
||||
|
||||
applyRenderState(encoder, attributes);
|
||||
applyRenderState(encoder, attributesID);
|
||||
applyShaderUniforms(encoder, Shader::current, texture);
|
||||
|
||||
id<MTLBuffer> ib = getMTLBuffer(quadIndexBuffer);
|
||||
@@ -1361,6 +1361,9 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
|
||||
}
|
||||
else
|
||||
{
|
||||
VertexAttributes attributes;
|
||||
findVertexAttributes(attributesID, attributes);
|
||||
|
||||
BufferBindings bufferscopy = buffers;
|
||||
if (start > 0)
|
||||
advanceVertexOffsets(attributes, bufferscopy, start * 4);
|
||||
@@ -1490,7 +1493,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int /*pixelw*/
|
||||
}
|
||||
|
||||
lastRenderPipelineKey.depthStencilFormat = dsformat;
|
||||
lastRenderPipelineKey.vertexAttributes = VertexAttributes();
|
||||
lastRenderPipelineKey.vertexAttributesID = VertexAttributesID();
|
||||
|
||||
dirtyRenderState = STATEBIT_ALL;
|
||||
}}
|
||||
|
||||
@@ -61,8 +61,8 @@ public:
|
||||
|
||||
struct RenderPipelineKey
|
||||
{
|
||||
VertexAttributes vertexAttributes;
|
||||
BlendState blend;
|
||||
VertexAttributesID vertexAttributesID;
|
||||
uint32 blendStateKey;
|
||||
uint64 colorRenderTargetFormats;
|
||||
uint32 depthStencilFormat;
|
||||
ColorChannelMask colorChannelMask;
|
||||
|
||||
@@ -882,15 +882,17 @@ id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(graphics::Graphics *g
|
||||
auto formatdesc = Metal::convertPixelFormat(device, format);
|
||||
attachment.pixelFormat = formatdesc.format;
|
||||
|
||||
if (key.blend.enable && gfx->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_BLEND))
|
||||
BlendState blend = BlendState::fromKey(key.blendStateKey);
|
||||
|
||||
if (blend.enable && gfx->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_BLEND))
|
||||
{
|
||||
attachment.blendingEnabled = YES;
|
||||
attachment.sourceRGBBlendFactor = getMTLBlendFactor(key.blend.srcFactorRGB);
|
||||
attachment.destinationRGBBlendFactor = getMTLBlendFactor(key.blend.dstFactorRGB);
|
||||
attachment.rgbBlendOperation = getMTLBlendOperation(key.blend.operationRGB);
|
||||
attachment.sourceAlphaBlendFactor = getMTLBlendFactor(key.blend.srcFactorA);
|
||||
attachment.destinationAlphaBlendFactor = getMTLBlendFactor(key.blend.dstFactorA);
|
||||
attachment.alphaBlendOperation = getMTLBlendOperation(key.blend.operationA);
|
||||
attachment.sourceRGBBlendFactor = getMTLBlendFactor(blend.srcFactorRGB);
|
||||
attachment.destinationRGBBlendFactor = getMTLBlendFactor(blend.dstFactorRGB);
|
||||
attachment.rgbBlendOperation = getMTLBlendOperation(blend.operationRGB);
|
||||
attachment.sourceAlphaBlendFactor = getMTLBlendFactor(blend.srcFactorA);
|
||||
attachment.destinationAlphaBlendFactor = getMTLBlendFactor(blend.dstFactorA);
|
||||
attachment.alphaBlendOperation = getMTLBlendOperation(blend.operationA);
|
||||
}
|
||||
|
||||
MTLColorWriteMask writeMask = MTLColorWriteMaskNone;
|
||||
@@ -923,8 +925,10 @@ id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(graphics::Graphics *g
|
||||
}
|
||||
}
|
||||
|
||||
VertexAttributes attributes;
|
||||
gfx->findVertexAttributes(key.vertexAttributesID, attributes);
|
||||
|
||||
MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor];
|
||||
const auto &attributes = key.vertexAttributes;
|
||||
|
||||
for (const auto &pair : this->attributes)
|
||||
{
|
||||
|
||||
@@ -569,8 +569,11 @@ bool Graphics::dispatch(love::graphics::Shader *s, love::graphics::Buffer *indir
|
||||
|
||||
void Graphics::draw(const DrawCommand &cmd)
|
||||
{
|
||||
VertexAttributes attributes;
|
||||
findVertexAttributes(cmd.attributesID, attributes);
|
||||
|
||||
gl.prepareDraw(this);
|
||||
gl.setVertexAttributes(*cmd.attributes, *cmd.buffers);
|
||||
gl.setVertexAttributes(attributes, *cmd.buffers);
|
||||
gl.bindTextureToUnit(cmd.texture, 0, false);
|
||||
gl.setCullMode(cmd.cullMode);
|
||||
|
||||
@@ -591,8 +594,11 @@ void Graphics::draw(const DrawCommand &cmd)
|
||||
|
||||
void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
{
|
||||
VertexAttributes attributes;
|
||||
findVertexAttributes(cmd.attributesID, attributes);
|
||||
|
||||
gl.prepareDraw(this);
|
||||
gl.setVertexAttributes(*cmd.attributes, *cmd.buffers);
|
||||
gl.setVertexAttributes(attributes, *cmd.buffers);
|
||||
gl.bindTextureToUnit(cmd.texture, 0, false);
|
||||
gl.setCullMode(cmd.cullMode);
|
||||
|
||||
@@ -640,11 +646,14 @@ static inline void advanceVertexOffsets(const VertexAttributes &attributes, Buff
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture)
|
||||
void Graphics::drawQuads(int start, int count, VertexAttributesID attributesID, const BufferBindings &buffers, love::graphics::Texture *texture)
|
||||
{
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
|
||||
VertexAttributes attributes;
|
||||
findVertexAttributes(attributesID, attributes);
|
||||
|
||||
gl.prepareDraw(this);
|
||||
gl.bindTextureToUnit(texture, 0, false);
|
||||
gl.setCullMode(CULL_NONE);
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
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 drawQuads(int start, int count, VertexAttributesID attributesID, const BufferBindings &buffers, love::graphics::Texture *texture) override;
|
||||
|
||||
void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override;
|
||||
void clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth) override;
|
||||
|
||||
@@ -214,6 +214,7 @@ enum class CommonFormat
|
||||
XYf_STf_RGBAub,
|
||||
XYf_STus_RGBAub,
|
||||
XYf_STPf_RGBAub,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
struct DataFormatInfo
|
||||
@@ -379,6 +380,17 @@ struct VertexAttributes
|
||||
bool operator == (const VertexAttributes &other) const;
|
||||
};
|
||||
|
||||
struct VertexAttributesID
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
bool isValid() const { return id > 0; }
|
||||
void invalidate() { id = 0; }
|
||||
|
||||
bool operator == (VertexAttributesID other) const { return other.id == id; }
|
||||
bool operator != (VertexAttributesID other) const { return other.id != id; }
|
||||
};
|
||||
|
||||
size_t getFormatStride(CommonFormat format);
|
||||
|
||||
uint32 getFormatFlags(CommonFormat format);
|
||||
|
||||
@@ -867,7 +867,7 @@ Graphics::RendererInfo Graphics::getRendererInfo() const
|
||||
|
||||
void Graphics::draw(const DrawCommand &cmd)
|
||||
{
|
||||
prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
|
||||
prepareDraw(cmd.attributesID, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
|
||||
|
||||
if (cmd.indirectBuffer != nullptr)
|
||||
{
|
||||
@@ -893,7 +893,7 @@ void Graphics::draw(const DrawCommand &cmd)
|
||||
|
||||
void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
{
|
||||
prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
|
||||
prepareDraw(cmd.attributesID, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode);
|
||||
|
||||
vkCmdBindIndexBuffer(
|
||||
commandBuffers.at(currentFrame),
|
||||
@@ -924,12 +924,12 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
drawCalls++;
|
||||
}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, graphics::Texture *texture)
|
||||
void Graphics::drawQuads(int start, int count, VertexAttributesID attributesID, const BufferBindings &buffers, graphics::Texture *texture)
|
||||
{
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
|
||||
prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_NONE);
|
||||
prepareDraw(attributesID, buffers, texture, PRIMITIVE_TRIANGLES, CULL_NONE);
|
||||
|
||||
vkCmdBindIndexBuffer(
|
||||
commandBuffers.at(currentFrame),
|
||||
@@ -2328,7 +2328,7 @@ void Graphics::createVulkanVertexFormat(
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindings &buffers, graphics::Texture *texture, PrimitiveType primitiveType, CullMode cullmode)
|
||||
void Graphics::prepareDraw(VertexAttributesID attributesID, const BufferBindings &buffers, graphics::Texture *texture, PrimitiveType primitiveType, CullMode cullmode)
|
||||
{
|
||||
if (!renderPassState.active)
|
||||
startRenderPass();
|
||||
@@ -2337,31 +2337,37 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
|
||||
|
||||
usedShadersInFrame.insert(s);
|
||||
|
||||
GraphicsPipelineConfiguration configuration{};
|
||||
GraphicsPipelineConfigurationFull configuration{};
|
||||
|
||||
configuration.renderPass = renderPassState.beginInfo.renderPass;
|
||||
configuration.vertexAttributes = attributes;
|
||||
configuration.wireFrame = states.back().wireframe;
|
||||
configuration.blendState = states.back().blend;
|
||||
configuration.colorChannelMask = states.back().colorMask;
|
||||
configuration.msaaSamples = renderPassState.msaa;
|
||||
configuration.numColorAttachments = renderPassState.numColorAttachments;
|
||||
configuration.packedColorAttachmentFormats = renderPassState.packedColorAttachmentFormats;
|
||||
configuration.primitiveType = primitiveType;
|
||||
configuration.core.renderPass = renderPassState.beginInfo.renderPass;
|
||||
configuration.core.attributesID = attributesID;
|
||||
configuration.core.wireFrame = states.back().wireframe;
|
||||
configuration.core.blendStateKey = states.back().blend.toKey();
|
||||
configuration.core.colorChannelMask = states.back().colorMask;
|
||||
configuration.core.msaaSamples = renderPassState.msaa;
|
||||
configuration.core.numColorAttachments = renderPassState.numColorAttachments;
|
||||
configuration.core.packedColorAttachmentFormats = renderPassState.packedColorAttachmentFormats;
|
||||
configuration.core.primitiveType = primitiveType;
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
|
||||
if (optionalDeviceExtensions.extendedDynamicState)
|
||||
{
|
||||
vkCmdSetCullModeEXT(commandBuffers.at(currentFrame), Vulkan::getCullMode(cullmode));
|
||||
pipeline = s->getCachedGraphicsPipeline(this, configuration.core);
|
||||
}
|
||||
else
|
||||
{
|
||||
configuration.dynamicState.winding = states.back().winding;
|
||||
configuration.dynamicState.depthState.compare = states.back().depthTest;
|
||||
configuration.dynamicState.depthState.write = states.back().depthWrite;
|
||||
configuration.dynamicState.stencilAction = states.back().stencil.action;
|
||||
configuration.dynamicState.stencilCompare = states.back().stencil.compare;
|
||||
configuration.dynamicState.cullmode = cullmode;
|
||||
configuration.noDynamicState.winding = states.back().winding;
|
||||
configuration.noDynamicState.depthState.compare = states.back().depthTest;
|
||||
configuration.noDynamicState.depthState.write = states.back().depthWrite;
|
||||
configuration.noDynamicState.stencilAction = states.back().stencil.action;
|
||||
configuration.noDynamicState.stencilCompare = states.back().stencil.compare;
|
||||
configuration.noDynamicState.cullmode = cullmode;
|
||||
|
||||
pipeline = s->getCachedGraphicsPipeline(this, configuration);
|
||||
}
|
||||
|
||||
VkPipeline pipeline = s->getCachedGraphicsPipeline(this, configuration);
|
||||
if (pipeline != renderPassState.pipeline)
|
||||
{
|
||||
vkCmdBindPipeline(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
@@ -2696,7 +2702,7 @@ VkSampler Graphics::getCachedSampler(const SamplerState &samplerState)
|
||||
}
|
||||
}
|
||||
|
||||
VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipelineConfiguration &configuration)
|
||||
VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipelineConfigurationCore &configuration, const GraphicsPipelineConfigurationNoDynamicState *noDynamicStateConfiguration)
|
||||
{
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
@@ -2706,7 +2712,10 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli
|
||||
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
|
||||
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
|
||||
|
||||
createVulkanVertexFormat(shader, configuration.vertexAttributes, bindingDescriptions, attributeDescriptions);
|
||||
VertexAttributes vertexAttributes;
|
||||
findVertexAttributes(configuration.attributesID, vertexAttributes);
|
||||
|
||||
createVulkanVertexFormat(shader, vertexAttributes, bindingDescriptions, attributeDescriptions);
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
|
||||
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
@@ -2733,8 +2742,8 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli
|
||||
rasterizer.lineWidth = 1.0f;
|
||||
if (!optionalDeviceExtensions.extendedDynamicState)
|
||||
{
|
||||
rasterizer.cullMode = Vulkan::getCullMode(configuration.dynamicState.cullmode);
|
||||
rasterizer.frontFace = Vulkan::getFrontFace(configuration.dynamicState.winding);
|
||||
rasterizer.cullMode = Vulkan::getCullMode(noDynamicStateConfiguration->cullmode);
|
||||
rasterizer.frontFace = Vulkan::getFrontFace(noDynamicStateConfiguration->winding);
|
||||
}
|
||||
|
||||
rasterizer.depthBiasEnable = VK_FALSE;
|
||||
@@ -2752,8 +2761,8 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli
|
||||
depthStencil.depthTestEnable = VK_TRUE;
|
||||
if (!optionalDeviceExtensions.extendedDynamicState)
|
||||
{
|
||||
depthStencil.depthWriteEnable = Vulkan::getBool(configuration.dynamicState.depthState.write);
|
||||
depthStencil.depthCompareOp = Vulkan::getCompareOp(configuration.dynamicState.depthState.compare);
|
||||
depthStencil.depthWriteEnable = Vulkan::getBool(noDynamicStateConfiguration->depthState.write);
|
||||
depthStencil.depthCompareOp = Vulkan::getCompareOp(noDynamicStateConfiguration->depthState.compare);
|
||||
}
|
||||
depthStencil.depthBoundsTestEnable = VK_FALSE;
|
||||
depthStencil.minDepthBounds = 0.0f;
|
||||
@@ -2764,31 +2773,33 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli
|
||||
if (!optionalDeviceExtensions.extendedDynamicState)
|
||||
{
|
||||
depthStencil.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
depthStencil.front.passOp = Vulkan::getStencilOp(configuration.dynamicState.stencilAction);
|
||||
depthStencil.front.passOp = Vulkan::getStencilOp(noDynamicStateConfiguration->stencilAction);
|
||||
depthStencil.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
depthStencil.front.compareOp = Vulkan::getCompareOp(getReversedCompareMode(configuration.dynamicState.stencilCompare));
|
||||
depthStencil.front.compareOp = Vulkan::getCompareOp(getReversedCompareMode(noDynamicStateConfiguration->stencilCompare));
|
||||
|
||||
depthStencil.back.failOp = VK_STENCIL_OP_KEEP;
|
||||
depthStencil.back.passOp = Vulkan::getStencilOp(configuration.dynamicState.stencilAction);
|
||||
depthStencil.back.passOp = Vulkan::getStencilOp(noDynamicStateConfiguration->stencilAction);
|
||||
depthStencil.back.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
depthStencil.back.compareOp = Vulkan::getCompareOp(getReversedCompareMode(configuration.dynamicState.stencilCompare));
|
||||
depthStencil.back.compareOp = Vulkan::getCompareOp(getReversedCompareMode(noDynamicStateConfiguration->stencilCompare));
|
||||
}
|
||||
|
||||
pipelineInfo.pDepthStencilState = &depthStencil;
|
||||
|
||||
BlendState blendState = BlendState::fromKey(configuration.blendStateKey);
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
|
||||
colorBlendAttachment.colorWriteMask = Vulkan::getColorMask(configuration.colorChannelMask);
|
||||
colorBlendAttachment.blendEnable = Vulkan::getBool(configuration.blendState.enable);
|
||||
colorBlendAttachment.srcColorBlendFactor = Vulkan::getBlendFactor(configuration.blendState.srcFactorRGB);
|
||||
colorBlendAttachment.dstColorBlendFactor = Vulkan::getBlendFactor(configuration.blendState.dstFactorRGB);
|
||||
colorBlendAttachment.colorBlendOp = Vulkan::getBlendOp(configuration.blendState.operationRGB);
|
||||
colorBlendAttachment.srcAlphaBlendFactor = Vulkan::getBlendFactor(configuration.blendState.srcFactorA);
|
||||
colorBlendAttachment.dstAlphaBlendFactor = Vulkan::getBlendFactor(configuration.blendState.dstFactorA);
|
||||
colorBlendAttachment.alphaBlendOp = Vulkan::getBlendOp(configuration.blendState.operationA);
|
||||
colorBlendAttachment.blendEnable = Vulkan::getBool(blendState.enable);
|
||||
colorBlendAttachment.srcColorBlendFactor = Vulkan::getBlendFactor(blendState.srcFactorRGB);
|
||||
colorBlendAttachment.dstColorBlendFactor = Vulkan::getBlendFactor(blendState.dstFactorRGB);
|
||||
colorBlendAttachment.colorBlendOp = Vulkan::getBlendOp(blendState.operationRGB);
|
||||
colorBlendAttachment.srcAlphaBlendFactor = Vulkan::getBlendFactor(blendState.srcFactorA);
|
||||
colorBlendAttachment.dstAlphaBlendFactor = Vulkan::getBlendFactor(blendState.dstFactorA);
|
||||
colorBlendAttachment.alphaBlendOp = Vulkan::getBlendOp(blendState.operationA);
|
||||
|
||||
std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachments(configuration.numColorAttachments, colorBlendAttachment);
|
||||
|
||||
if (configuration.blendState.enable)
|
||||
if (blendState.enable)
|
||||
{
|
||||
for (uint32 i = 0; i < configuration.numColorAttachments; i++)
|
||||
{
|
||||
|
||||
@@ -256,7 +256,7 @@ public:
|
||||
RendererInfo getRendererInfo() const 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, graphics::Texture *texture) override;
|
||||
void drawQuads(int start, int count, VertexAttributesID attributesID, const BufferBindings &buffers, graphics::Texture *texture) override;
|
||||
|
||||
// internal functions.
|
||||
|
||||
@@ -276,7 +276,7 @@ public:
|
||||
int getVsync() const;
|
||||
void mapLocalUniformData(void *data, size_t size, VkDescriptorBufferInfo &bufferInfo);
|
||||
|
||||
VkPipeline createGraphicsPipeline(Shader *shader, const GraphicsPipelineConfiguration &configuration);
|
||||
VkPipeline createGraphicsPipeline(Shader *shader, const GraphicsPipelineConfigurationCore &configuration, const GraphicsPipelineConfigurationNoDynamicState *noDynamicStateConfiguration);
|
||||
|
||||
uint32 getDeviceApiVersion() const { return deviceApiVersion; }
|
||||
|
||||
@@ -332,7 +332,7 @@ private:
|
||||
std::vector<VkVertexInputBindingDescription> &bindingDescriptions,
|
||||
std::vector<VkVertexInputAttributeDescription> &attributeDescriptions);
|
||||
void prepareDraw(
|
||||
const VertexAttributes &attributes,
|
||||
VertexAttributesID attributesID,
|
||||
const BufferBindings &buffers, graphics::Texture *texture,
|
||||
PrimitiveType, CullMode);
|
||||
void setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture);
|
||||
|
||||
@@ -201,7 +201,8 @@ void Shader::unloadVolatile()
|
||||
return;
|
||||
|
||||
vgfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout,
|
||||
descriptorPools = descriptorPools, computePipeline = computePipeline, graphicsPipelines = std::move(graphicsPipelines)](){
|
||||
descriptorPools = descriptorPools, computePipeline = computePipeline,
|
||||
graphicsPipelinesCore = std::move(graphicsPipelinesDynamicState), graphicsPipelinesFull = std::move(graphicsPipelinesNoDynamicState)]() {
|
||||
for (const auto &pools : descriptorPools)
|
||||
{
|
||||
for (const auto pool : pools)
|
||||
@@ -213,7 +214,9 @@ void Shader::unloadVolatile()
|
||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||
if (computePipeline != VK_NULL_HANDLE)
|
||||
vkDestroyPipeline(device, computePipeline, nullptr);
|
||||
for (const auto& kvp : graphicsPipelines)
|
||||
for (const auto &kvp : graphicsPipelinesCore)
|
||||
vkDestroyPipeline(device, kvp.second, nullptr);
|
||||
for (const auto &kvp : graphicsPipelinesFull)
|
||||
vkDestroyPipeline(device, kvp.second, nullptr);
|
||||
});
|
||||
|
||||
@@ -594,6 +597,7 @@ void Shader::compileShaders()
|
||||
|
||||
BindingMapper bindingMapper(spv::DecorationBinding);
|
||||
BindingMapper ioLocationMapper(spv::DecorationLocation);
|
||||
BindingMapper vertexInputLocationMapper(spv::DecorationLocation);
|
||||
|
||||
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
|
||||
{
|
||||
@@ -712,25 +716,21 @@ void Shader::compileShaders()
|
||||
|
||||
if (shaderStage == SHADERSTAGE_VERTEX)
|
||||
{
|
||||
int nextAttributeIndex = ATTRIB_MAX_ENUM;
|
||||
|
||||
// Don't skip unused inputs, vulkan still needs to have valid
|
||||
// bindings for them.
|
||||
// Use the mapper on known used inputs first, so their bindings get
|
||||
// put into the map without being changed.
|
||||
for (const auto &r : shaderResources.stage_inputs)
|
||||
{
|
||||
int index;
|
||||
auto it = reflection.vertexInputs.find(r.name);
|
||||
if (it != reflection.vertexInputs.end() && it->second >= 0)
|
||||
vertexInputLocationMapper(comp, spirv, r.name, 1, r.id);
|
||||
}
|
||||
|
||||
BuiltinVertexAttribute builtinAttribute;
|
||||
if (graphics::getConstant(r.name.c_str(), builtinAttribute))
|
||||
index = (int)builtinAttribute;
|
||||
else
|
||||
index = nextAttributeIndex++;
|
||||
|
||||
uint32_t locationOffset;
|
||||
if (!comp.get_binary_offset_for_decoration(r.id, spv::DecorationLocation, locationOffset))
|
||||
throw love::Exception("could not get binary offset for vertex attribute %s location", r.name.c_str());
|
||||
|
||||
spirv[locationOffset] = (uint32_t)index;
|
||||
for (const auto &r : shaderResources.stage_inputs)
|
||||
{
|
||||
// Don't skip unused inputs, vulkan still needs to have valid
|
||||
// bindings for them. This will also avoid shuffling intentional
|
||||
// used bindings because of the earlier loop.
|
||||
int index = (int)vertexInputLocationMapper(comp, spirv, r.name, 1, r.id);
|
||||
|
||||
DataBaseType basetype = DATA_BASETYPE_FLOAT;
|
||||
|
||||
@@ -1203,14 +1203,26 @@ VkDescriptorSet Shader::allocateDescriptorSet()
|
||||
}
|
||||
}
|
||||
|
||||
VkPipeline Shader::getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfiguration &configuration)
|
||||
VkPipeline Shader::getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfigurationCore &configuration)
|
||||
{
|
||||
auto it = graphicsPipelines.find(configuration);
|
||||
if (it != graphicsPipelines.end())
|
||||
auto it = graphicsPipelinesDynamicState.find(configuration);
|
||||
if (it != graphicsPipelinesDynamicState.end())
|
||||
return it->second;
|
||||
|
||||
VkPipeline pipeline = vgfx->createGraphicsPipeline(this, configuration);
|
||||
graphicsPipelines.insert({ configuration, pipeline });
|
||||
VkPipeline pipeline = vgfx->createGraphicsPipeline(this, configuration, nullptr);
|
||||
graphicsPipelinesDynamicState.insert({ configuration, pipeline });
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VkPipeline Shader::getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfigurationFull &configuration)
|
||||
{
|
||||
auto it = graphicsPipelinesNoDynamicState.find(configuration);
|
||||
if (it != graphicsPipelinesNoDynamicState.end())
|
||||
return it->second;
|
||||
|
||||
VkPipeline pipeline = vgfx->createGraphicsPipeline(this, configuration.core, &configuration.noDynamicState);
|
||||
graphicsPipelinesNoDynamicState.insert({ configuration, pipeline });
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@@ -46,43 +46,67 @@ namespace graphics
|
||||
namespace vulkan
|
||||
{
|
||||
|
||||
struct GraphicsPipelineConfiguration
|
||||
struct GraphicsPipelineConfigurationCore
|
||||
{
|
||||
VkRenderPass renderPass;
|
||||
VertexAttributes vertexAttributes;
|
||||
VertexAttributesID attributesID;
|
||||
bool wireFrame;
|
||||
BlendState blendState;
|
||||
uint32 blendStateKey;
|
||||
ColorChannelMask colorChannelMask;
|
||||
VkSampleCountFlagBits msaaSamples;
|
||||
uint32_t numColorAttachments;
|
||||
PrimitiveType primitiveType;
|
||||
uint64 packedColorAttachmentFormats;
|
||||
|
||||
struct DynamicState
|
||||
GraphicsPipelineConfigurationCore()
|
||||
{
|
||||
CullMode cullmode = CULL_NONE;
|
||||
Winding winding = WINDING_MAX_ENUM;
|
||||
StencilAction stencilAction = STENCIL_MAX_ENUM;
|
||||
CompareMode stencilCompare = COMPARE_MAX_ENUM;
|
||||
DepthState depthState{};
|
||||
} dynamicState;
|
||||
|
||||
GraphicsPipelineConfiguration()
|
||||
{
|
||||
memset(this, 0, sizeof(GraphicsPipelineConfiguration));
|
||||
memset(this, 0, sizeof(GraphicsPipelineConfigurationCore));
|
||||
}
|
||||
|
||||
bool operator==(const GraphicsPipelineConfiguration &other) const
|
||||
bool operator==(const GraphicsPipelineConfigurationCore &other) const
|
||||
{
|
||||
return memcmp(this, &other, sizeof(GraphicsPipelineConfiguration)) == 0;
|
||||
return memcmp(this, &other, sizeof(GraphicsPipelineConfigurationCore)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct GraphicsPipelineConfigurationHasher
|
||||
struct GraphicsPipelineConfigurationCoreHasher
|
||||
{
|
||||
size_t operator() (const GraphicsPipelineConfiguration &configuration) const
|
||||
size_t operator() (const GraphicsPipelineConfigurationCore &configuration) const
|
||||
{
|
||||
return XXH32(&configuration, sizeof(GraphicsPipelineConfiguration), 0);
|
||||
return XXH32(&configuration, sizeof(GraphicsPipelineConfigurationCore), 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct GraphicsPipelineConfigurationNoDynamicState
|
||||
{
|
||||
CullMode cullmode = CULL_NONE;
|
||||
Winding winding = WINDING_MAX_ENUM;
|
||||
StencilAction stencilAction = STENCIL_MAX_ENUM;
|
||||
CompareMode stencilCompare = COMPARE_MAX_ENUM;
|
||||
DepthState depthState{};
|
||||
};
|
||||
|
||||
struct GraphicsPipelineConfigurationFull
|
||||
{
|
||||
GraphicsPipelineConfigurationCore core;
|
||||
GraphicsPipelineConfigurationNoDynamicState noDynamicState;
|
||||
|
||||
GraphicsPipelineConfigurationFull()
|
||||
{
|
||||
memset(this, 0, sizeof(GraphicsPipelineConfigurationFull));
|
||||
}
|
||||
|
||||
bool operator==(const GraphicsPipelineConfigurationFull &other) const
|
||||
{
|
||||
return memcmp(this, &other, sizeof(GraphicsPipelineConfigurationFull)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct GraphicsPipelineConfigurationFullHasher
|
||||
{
|
||||
size_t operator() (const GraphicsPipelineConfigurationFull &configuration) const
|
||||
{
|
||||
return XXH32(&configuration, sizeof(GraphicsPipelineConfigurationFull), 0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -136,7 +160,8 @@ public:
|
||||
|
||||
void setMainTex(graphics::Texture *texture);
|
||||
|
||||
VkPipeline getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfiguration &configuration);
|
||||
VkPipeline getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfigurationCore &configuration);
|
||||
VkPipeline getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfigurationFull &configuration);
|
||||
|
||||
private:
|
||||
void compileShaders();
|
||||
@@ -183,7 +208,8 @@ private:
|
||||
|
||||
std::unordered_map<std::string, AttributeInfo> attributes;
|
||||
|
||||
std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
|
||||
std::unordered_map<GraphicsPipelineConfigurationCore, VkPipeline, GraphicsPipelineConfigurationCoreHasher> graphicsPipelinesDynamicState;
|
||||
std::unordered_map<GraphicsPipelineConfigurationFull, VkPipeline, GraphicsPipelineConfigurationFullHasher> graphicsPipelinesNoDynamicState;
|
||||
|
||||
uint32_t currentFrame = 0;
|
||||
uint32_t currentDescriptorPool = 0;
|
||||
|
||||
@@ -404,9 +404,15 @@ static int w_Buffer_getFormat(lua_State *L)
|
||||
lua_pushinteger(L, member.decl.arrayLength);
|
||||
lua_setfield(L, -2, "arraylength");
|
||||
|
||||
lua_pushinteger(L, member.decl.bindingLocation);
|
||||
lua_setfield(L, -2, "location");
|
||||
|
||||
lua_pushinteger(L, member.offset);
|
||||
lua_setfield(L, -2, "offset");
|
||||
|
||||
lua_pushinteger(L, member.size);
|
||||
lua_setfield(L, -2, "size");
|
||||
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace graphics
|
||||
static int luax_checkgraphicscreated(lua_State *L)
|
||||
{
|
||||
if (!instance()->isCreated())
|
||||
return luaL_error(L, "love.graphics cannot function without a window!");
|
||||
return luaL_error(L, "love.graphics cannot function without a window.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1599,6 +1599,11 @@ int w_newShader(lua_State *L)
|
||||
luax_markdeprecated(L, 1, "texture2D() or textureCube() function calls in shader code", API_CUSTOM, DEPRECATED_REPLACED, "texture() function calls");
|
||||
if (shader->isUsingDeprecatedTextureUniform())
|
||||
luax_markdeprecated(L, 1, "'texture' uniform variable name in shader code", API_CUSTOM, DEPRECATED_NO_REPLACEMENT, "");
|
||||
if (!shader->getUnsetVertexInputLocationsString().empty())
|
||||
{
|
||||
std::string str = "vertex input attribute(s) " + shader->getUnsetVertexInputLocationsString() + " without a 'location' layout qualifier in shader code";
|
||||
luax_markdeprecated(L, 1, str.c_str(), API_CUSTOM, DEPRECATED_REPLACED, "layout(location = #) qualifier for vertex inputs");
|
||||
}
|
||||
luax_pushtype(L, shader);
|
||||
shader->release();
|
||||
}
|
||||
@@ -1632,6 +1637,11 @@ int w_newComputeShader(lua_State* L)
|
||||
luax_markdeprecated(L, 1, "texture2D() or textureCube() function calls in shader code", API_CUSTOM, DEPRECATED_REPLACED, "texture() function calls");
|
||||
if (shader->isUsingDeprecatedTextureUniform())
|
||||
luax_markdeprecated(L, 1, "'texture' uniform variable name in shader code", API_CUSTOM, DEPRECATED_NO_REPLACEMENT, "");
|
||||
if (!shader->getUnsetVertexInputLocationsString().empty())
|
||||
{
|
||||
std::string str = "vertex input attribute(s) " + shader->getUnsetVertexInputLocationsString() + " without a 'location' layout qualifier in shader code";
|
||||
luax_markdeprecated(L, 1, str.c_str(), API_CUSTOM, DEPRECATED_REPLACED, "layout(location = #) qualifier for vertex inputs");
|
||||
}
|
||||
luax_pushtype(L, shader);
|
||||
shader->release();
|
||||
}
|
||||
@@ -1709,7 +1719,7 @@ 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)
|
||||
static Buffer::DataDeclaration luax_checkdatadeclaration(lua_State* L, int formattableidx, int arrayindex, int declindex, bool requirename, bool requirelocation)
|
||||
{
|
||||
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
|
||||
|
||||
@@ -1744,10 +1754,24 @@ static Buffer::DataDeclaration luax_checkdatadeclaration(lua_State* L, int forma
|
||||
|
||||
decl.arrayLength = luax_intflag(L, declindex, "arraylength", 0);
|
||||
|
||||
lua_getfield(L, declindex, "location");
|
||||
if (requirelocation && lua_type(L, -1) != LUA_TNUMBER)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "'location' 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.bindingLocation = luaL_checkint(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::DataDeclaration> &format)
|
||||
static void luax_checkbufferformat(lua_State *L, int idx, const Buffer::Settings &settings, std::vector<Buffer::DataDeclaration> &format)
|
||||
{
|
||||
if (lua_type(L, idx) == LUA_TSTRING)
|
||||
{
|
||||
@@ -1759,6 +1783,8 @@ static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::Da
|
||||
return;
|
||||
}
|
||||
|
||||
bool requirelocation = (settings.usageFlags & BUFFERUSAGE_VERTEX) != 0;
|
||||
|
||||
luaL_checktype(L, idx, LUA_TTABLE);
|
||||
int tablelen = luax_objlen(L, idx);
|
||||
|
||||
@@ -1767,13 +1793,34 @@ 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 = luax_checkdatadeclaration(L, idx, i, -1, false);
|
||||
Buffer::DataDeclaration decl = luax_checkdatadeclaration(L, idx, i, -1, false, requirelocation);
|
||||
|
||||
format.push_back(decl);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void luax_validatebuffervertexbindings(lua_State *L, Buffer *buffer)
|
||||
{
|
||||
if (buffer->hasLegacyVertexBindings())
|
||||
{
|
||||
std::string names;
|
||||
|
||||
for (const auto &member : buffer->getDataMembers())
|
||||
{
|
||||
if (member.decl.bindingLocation < 0)
|
||||
{
|
||||
if (names.empty())
|
||||
names = member.decl.name;
|
||||
else
|
||||
names += ", " + member.decl.name;
|
||||
}
|
||||
}
|
||||
|
||||
luax_markdeprecated(L, 1, "vertex format 'name' fields in Meshes and Buffers", API_CUSTOM, DEPRECATED_REPLACED, "'location' field containing a binding location number value.");
|
||||
}
|
||||
}
|
||||
|
||||
static Buffer *luax_newbuffer(lua_State *L, int idx, Buffer::Settings settings, const std::vector<Buffer::DataDeclaration> &format)
|
||||
{
|
||||
size_t arraylength = 0;
|
||||
@@ -1821,6 +1868,8 @@ static Buffer *luax_newbuffer(lua_State *L, int idx, Buffer::Settings settings,
|
||||
Buffer *b = nullptr;
|
||||
luax_catchexcept(L, [&] { b = instance()->newBuffer(settings, format, initialdata, bytesize, arraylength); });
|
||||
|
||||
luax_validatebuffervertexbindings(L, b);
|
||||
|
||||
if (lua_istable(L, idx))
|
||||
{
|
||||
Buffer::Mapper mapper(*b);
|
||||
@@ -1896,7 +1945,7 @@ int w_newBuffer(lua_State *L)
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
std::vector<Buffer::DataDeclaration> format;
|
||||
luax_checkbufferformat(L, 1, format);
|
||||
luax_checkbufferformat(L, 1, settings, format);
|
||||
|
||||
Buffer *b = luax_newbuffer(L, 2, settings, format);
|
||||
|
||||
@@ -2005,7 +2054,7 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (hasformatfield || luax_objlen(L, -1) == 0)
|
||||
decl = luax_checkdatadeclaration(L, 1, i, -1, true);
|
||||
decl = luax_checkdatadeclaration(L, 1, i, -1, false, true);
|
||||
else
|
||||
{
|
||||
// Legacy format arguments: {name, datatype, components}
|
||||
@@ -2013,7 +2062,7 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
lua_rawgeti(L, -j, j);
|
||||
|
||||
decl.name = luaL_checkstring(L, -3);
|
||||
const char* tname = luaL_checkstring(L, -2);
|
||||
const char *tname = luaL_checkstring(L, -2);
|
||||
int components = (int)luaL_checkinteger(L, -1);
|
||||
|
||||
// Check deprecated format names.
|
||||
@@ -2053,7 +2102,7 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
|
||||
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'");
|
||||
luax_markdeprecated(L, 1, "vertex format array values in love.graphics.newMesh", API_CUSTOM, DEPRECATED_REPLACED, "named table fields 'format' and 'location'");
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
@@ -2124,6 +2173,9 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
t->flush();
|
||||
}
|
||||
|
||||
if (t->getVertexBuffer() != nullptr)
|
||||
luax_validatebuffervertexbindings(L, t->getVertexBuffer());
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -2147,7 +2199,7 @@ static bool luax_isbufferattributetable(lua_State* L, int idx)
|
||||
|
||||
static Mesh::BufferAttribute luax_checkbufferattributetable(lua_State *L, int idx)
|
||||
{
|
||||
Mesh::BufferAttribute attrib = {};
|
||||
Mesh::BufferAttribute attrib;
|
||||
|
||||
attrib.step = STEP_PER_VERTEX;
|
||||
attrib.enabled = true;
|
||||
@@ -2156,8 +2208,13 @@ static Mesh::BufferAttribute luax_checkbufferattributetable(lua_State *L, int id
|
||||
attrib.buffer = luax_checkbuffer(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, idx, "location");
|
||||
attrib.bindingLocation = luaL_checkint(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, idx, "name");
|
||||
attrib.name = luax_checkstring(L, -1);
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
attrib.name = luax_checkstring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, idx, "step");
|
||||
@@ -2169,6 +2226,13 @@ static Mesh::BufferAttribute luax_checkbufferattributetable(lua_State *L, int id
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, idx, "locationinbuffer");
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
attrib.bindingLocationInBuffer = luaL_checkint(L, -1);
|
||||
else
|
||||
attrib.bindingLocationInBuffer = attrib.bindingLocation;
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, idx, "nameinbuffer");
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
attrib.nameInBuffer = luax_checkstring(L, -1);
|
||||
@@ -2197,6 +2261,10 @@ static Mesh* newMeshFromBuffers(lua_State *L)
|
||||
|
||||
Mesh *t = nullptr;
|
||||
luax_catchexcept(L, [&]() { t = instance()->newMesh(attributes, drawmode); });
|
||||
|
||||
if (t->getVertexBuffer() != nullptr)
|
||||
luax_validatebuffervertexbindings(L, t->getVertexBuffer());
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
@@ -260,6 +260,9 @@ int w_Mesh_getVertexFormat(lua_State *L)
|
||||
lua_pushstring(L, member.decl.name.c_str());
|
||||
lua_setfield(L, -2, "name");
|
||||
|
||||
lua_pushnumber(L, member.decl.bindingLocation);
|
||||
lua_setfield(L, -2, "location");
|
||||
|
||||
const char *formatstr = "unknown";
|
||||
getConstant(member.decl.format, formatstr);
|
||||
lua_pushstring(L, formatstr);
|
||||
@@ -280,18 +283,34 @@ int w_Mesh_getVertexFormat(lua_State *L)
|
||||
int w_Mesh_setAttributeEnabled(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
bool enable = luax_checkboolean(L, 3);
|
||||
luax_catchexcept(L, [&](){ t->setAttributeEnabled(name, enable); });
|
||||
if (lua_type(L, 2) == LUA_TSTRING)
|
||||
{
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
luax_catchexcept(L, [&](){ t->setAttributeEnabled(name, enable); });
|
||||
}
|
||||
else
|
||||
{
|
||||
int location = luaL_checkint(L, 2);
|
||||
luax_catchexcept(L, [&](){ t->setAttributeEnabled(location, enable); });
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_isAttributeEnabled(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
bool enabled = false;
|
||||
luax_catchexcept(L, [&](){ enabled = t->isAttributeEnabled(name); });
|
||||
if (lua_type(L, 2) == LUA_TSTRING)
|
||||
{
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
luax_catchexcept(L, [&](){ enabled = t->isAttributeEnabled(name); });
|
||||
}
|
||||
else
|
||||
{
|
||||
int location = luaL_checkint(L, 2);
|
||||
luax_catchexcept(L, [&](){ enabled = t->isAttributeEnabled(location); });
|
||||
}
|
||||
lua_pushboolean(L, enabled);
|
||||
return 1;
|
||||
}
|
||||
@@ -299,7 +318,13 @@ int w_Mesh_isAttributeEnabled(lua_State *L)
|
||||
int w_Mesh_attachAttribute(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
|
||||
const char *name = nullptr;
|
||||
int location = -1;
|
||||
if (lua_type(L, 2) == LUA_TSTRING)
|
||||
name = luaL_checkstring(L, 2);
|
||||
else
|
||||
location = luaL_checkint(L, 2);
|
||||
|
||||
Buffer *buffer = nullptr;
|
||||
Mesh *mesh = nullptr;
|
||||
@@ -320,10 +345,19 @@ int w_Mesh_attachAttribute(lua_State *L)
|
||||
if (stepstr != nullptr && !getConstant(stepstr, step))
|
||||
return luax_enumerror(L, "vertex attribute step", getConstants(step), stepstr);
|
||||
|
||||
const char *attachname = luaL_optstring(L, 5, name);
|
||||
const char *attachname = name;
|
||||
int attachlocation = location;
|
||||
if (name != nullptr)
|
||||
attachname = luaL_optstring(L, 5, name);
|
||||
else
|
||||
attachlocation = luaL_optint(L, 5, location);
|
||||
|
||||
int startindex = (int) luaL_optinteger(L, 6, 1) - 1;
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh, attachname, startindex, step); });
|
||||
if (name != nullptr)
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh, attachname, startindex, step); });
|
||||
else
|
||||
luax_catchexcept(L, [&]() { t->attachAttribute(location, buffer, mesh, attachlocation, startindex, step); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -348,26 +382,32 @@ int w_Mesh_getAttachedAttributes(lua_State *L)
|
||||
{
|
||||
const auto &attrib = attributes[i];
|
||||
|
||||
lua_createtable(L, 5, 0);
|
||||
lua_createtable(L, 0, 7);
|
||||
|
||||
luax_pushstring(L, attrib.name);
|
||||
lua_rawseti(L, -1, 1);
|
||||
lua_setfield(L, -2, "name");
|
||||
|
||||
lua_pushnumber(L, attrib.bindingLocation);
|
||||
lua_setfield(L, -2, "location");
|
||||
|
||||
luax_pushtype(L, attrib.buffer.get());
|
||||
lua_rawseti(L, -1, 2);
|
||||
lua_setfield(L, -2, "buffer");
|
||||
|
||||
const char *stepstr = nullptr;
|
||||
if (!getConstant(attrib.step, stepstr))
|
||||
return luaL_error(L, "Invalid vertex attribute step.");
|
||||
lua_pushstring(L, stepstr);
|
||||
lua_rawseti(L, -1, 3);
|
||||
lua_setfield(L, -2, "step");
|
||||
|
||||
const Buffer::DataMember &member = attrib.buffer->getDataMember(attrib.indexInBuffer);
|
||||
luax_pushstring(L, member.decl.name);
|
||||
lua_rawseti(L, -1, 4);
|
||||
lua_setfield(L, -2, "nameinbuffer");
|
||||
|
||||
lua_pushnumber(L, member.decl.bindingLocation);
|
||||
lua_setfield(L, -2, "locationinbuffer");
|
||||
|
||||
lua_pushinteger(L, attrib.startArrayIndex + 1);
|
||||
lua_rawseti(L, -1, 5);
|
||||
lua_setfield(L, -2, "startindex");
|
||||
|
||||
lua_rawseti(L, -1, i + 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user