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:
Sasha Szpakowski
2024-09-07 18:06:03 -03:00
parent c7b4ebbc55
commit 50ff1e4d2b
31 changed files with 683 additions and 258 deletions
+38 -22
View File
@@ -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 },
};
}
+8 -1
View File
@@ -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
+2
View File
@@ -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;
}
+4
View File
@@ -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.
+43 -17
View File
@@ -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);
+19 -17
View File
@@ -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
View File
@@ -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();
+13 -5
View File
@@ -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;
+3 -3
View File
@@ -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)
+1 -1
View File
@@ -673,7 +673,7 @@ private:
bool relativeRotation;
const VertexAttributes vertexAttributes;
VertexAttributesID vertexAttributesID;
Buffer *buffer;
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
+27
View File
@@ -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;
+6
View File
@@ -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
+22 -11
View File
@@ -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);
}
}
+3 -1
View File
@@ -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;
+3 -2
View File
@@ -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);
}
}
+1 -1
View File
@@ -86,7 +86,7 @@ private:
StrongRef<Font> font;
VertexAttributes vertexAttributes;
VertexAttributesID vertexAttributesID;
BufferBindings vertexBuffers;
StrongRef<Buffer> vertexBuffer;
+2 -2
View File
@@ -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);
+12 -9
View File
@@ -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;
}}
+2 -2
View File
@@ -61,8 +61,8 @@ public:
struct RenderPipelineKey
{
VertexAttributes vertexAttributes;
BlendState blend;
VertexAttributesID vertexAttributesID;
uint32 blendStateKey;
uint64 colorRenderTargetFormats;
uint32 depthStencilFormat;
ColorChannelMask colorChannelMask;
+12 -8
View File
@@ -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)
{
+12 -3
View File
@@ -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);
+1 -1
View File
@@ -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;
+12
View File
@@ -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);
+51 -40
View File
@@ -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++)
{
+3 -3
View File
@@ -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);
+35 -23
View File
@@ -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;
}
+47 -21
View File
@@ -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;
+6
View File
@@ -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);
}
+78 -10
View File
@@ -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;
}
+53 -13
View File
@@ -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);
}