mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Refactor Buffer internals.
Allows buffers created with love.graphics.newBuffer to only allocate VRAM data. Previously they had a copy of their contents in RAM. Also makes it easier to implement Buffers in other graphics APIs and to implement more types of Buffers in the future.
This commit is contained in:
@@ -34,7 +34,6 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
|
||||
, size(size)
|
||||
, typeFlags(settings.typeFlags)
|
||||
, usage(settings.usage)
|
||||
, mapFlags(settings.mapFlags)
|
||||
, mapped(false)
|
||||
{
|
||||
if (size == 0 && arraylength == 0)
|
||||
|
||||
@@ -48,11 +48,9 @@ public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
enum MapFlags
|
||||
enum MapType
|
||||
{
|
||||
MAP_NONE = 0,
|
||||
MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified.
|
||||
MAP_READ = (1 << 1),
|
||||
MAP_WRITE_INVALIDATE,
|
||||
};
|
||||
|
||||
enum TypeFlags
|
||||
@@ -94,13 +92,13 @@ public:
|
||||
struct Settings
|
||||
{
|
||||
TypeFlags typeFlags;
|
||||
MapFlags mapFlags;
|
||||
BufferUsage usage;
|
||||
bool zeroInitialize;
|
||||
|
||||
Settings(uint32 typeflags, uint32 mapflags, BufferUsage usage)
|
||||
Settings(uint32 typeflags, BufferUsage usage)
|
||||
: typeFlags((TypeFlags)typeflags)
|
||||
, mapFlags((MapFlags)mapflags)
|
||||
, usage(usage)
|
||||
, zeroInitialize(false)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -111,7 +109,6 @@ public:
|
||||
TypeFlags getTypeFlags() const { return typeFlags; }
|
||||
BufferUsage getUsage() const { return usage; }
|
||||
bool isMapped() const { return mapped; }
|
||||
uint32 getMapFlags() const { return mapFlags; }
|
||||
|
||||
size_t getArrayLength() const { return arrayLength; }
|
||||
size_t getArrayStride() const { return arrayStride; }
|
||||
@@ -121,35 +118,21 @@ public:
|
||||
int getDataMemberIndex(const std::string &name) const;
|
||||
|
||||
/**
|
||||
* Map the Buffer to client memory.
|
||||
*
|
||||
* This can be faster for large changes to the buffer. For smaller
|
||||
* changes, see fill().
|
||||
* Map a portion of the Buffer to client memory.
|
||||
*/
|
||||
virtual void *map() = 0;
|
||||
virtual void *map(MapType map, size_t offset, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* Unmap a previously mapped Buffer. The buffer must be unmapped when used
|
||||
* to draw.
|
||||
*/
|
||||
virtual void unmap() = 0;
|
||||
virtual void unmap(size_t usedoffset, size_t usedsize) = 0;
|
||||
|
||||
/**
|
||||
* Marks a range of mapped data as modified.
|
||||
* NOTE: Buffer::fill calls this internally for you.
|
||||
**/
|
||||
virtual void setMappedRangeModified(size_t offset, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* Fill a portion of the buffer with data and marks the range as modified.
|
||||
* Fill a portion of the buffer with data.
|
||||
*/
|
||||
virtual void fill(size_t offset, size_t size, const void *data) = 0;
|
||||
|
||||
/**
|
||||
* Copy the contents of this Buffer to another Buffer object.
|
||||
**/
|
||||
virtual void copyTo(size_t offset, size_t size, Buffer *other, size_t otheroffset) = 0;
|
||||
|
||||
/**
|
||||
* Texel buffers may use an additional texture handle as well as a buffer
|
||||
* handle.
|
||||
@@ -165,14 +148,12 @@ public:
|
||||
Mapper(Buffer &buffer)
|
||||
: buffer(buffer)
|
||||
{
|
||||
data = buffer.map();
|
||||
data = buffer.map(MAP_WRITE_INVALIDATE, 0, buffer.getSize());
|
||||
}
|
||||
|
||||
~Mapper()
|
||||
{
|
||||
if (buffer.getMapFlags() & MAP_EXPLICIT_RANGE_MODIFY)
|
||||
buffer.setMappedRangeModified(0, buffer.getSize());
|
||||
buffer.unmap();
|
||||
buffer.unmap(0, buffer.getSize());
|
||||
}
|
||||
|
||||
Buffer &buffer;
|
||||
@@ -194,8 +175,6 @@ protected:
|
||||
|
||||
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
|
||||
BufferUsage usage;
|
||||
|
||||
uint32 mapFlags;
|
||||
|
||||
bool mapped;
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ void Graphics::createQuadIndexBuffer()
|
||||
|
||||
size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, 0, BUFFERUSAGE_STATIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, BUFFERUSAGE_STATIC);
|
||||
quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0);
|
||||
|
||||
Buffer::Mapper map(*quadIndexBuffer);
|
||||
|
||||
+84
-154
@@ -46,20 +46,21 @@ std::vector<Buffer::DataDeclaration> Mesh::getDefaultVertexFormat()
|
||||
love::Type Mesh::type("Mesh", &Drawable::type);
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
, indexDataType(INDEX_UINT16)
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
: primitiveType(drawmode)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, data, datasize, 0), Acquire::NORETAIN);
|
||||
try
|
||||
{
|
||||
vertexData = new uint8[datasize];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory");
|
||||
}
|
||||
|
||||
memcpy(vertexData, data, datasize);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, vertexData, datasize, 0), Acquire::NORETAIN);
|
||||
|
||||
vertexCount = vertexBuffer->getArrayLength();
|
||||
vertexStride = vertexBuffer->getArrayStride();
|
||||
@@ -68,27 +69,17 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &
|
||||
setupAttachedAttributes();
|
||||
|
||||
indexDataType = getIndexDataTypeFromMax(vertexCount);
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount((size_t) vertexcount)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
: vertexCount((size_t) vertexcount)
|
||||
, indexDataType(getIndexDataTypeFromMax(vertexcount))
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
{
|
||||
if (vertexcount <= 0)
|
||||
throw love::Exception("Invalid number of vertices (%d).", vertexcount);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, nullptr, 0, vertexcount), Acquire::NORETAIN);
|
||||
|
||||
vertexStride = vertexBuffer->getArrayStride();
|
||||
@@ -96,25 +87,21 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &
|
||||
|
||||
setupAttachedAttributes();
|
||||
|
||||
memset(vertexBuffer->map(), 0, vertexBuffer->getSize());
|
||||
vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize());
|
||||
vertexBuffer->unmap();
|
||||
try
|
||||
{
|
||||
vertexData = new uint8[vertexBuffer->getSize()];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory");
|
||||
}
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
memset(vertexData, 0, vertexBuffer->getSize());
|
||||
vertexBuffer->fill(0, vertexBuffer->getSize(), vertexData);
|
||||
}
|
||||
|
||||
Mesh::Mesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
, indexDataType(INDEX_UINT16)
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
: primitiveType(drawmode)
|
||||
{
|
||||
if (attributes.size() == 0)
|
||||
throw love::Exception("At least one buffer attribute must be specified in this constructor.");
|
||||
@@ -139,7 +126,9 @@ Mesh::Mesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType d
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete vertexScratchBuffer;
|
||||
delete vertexData;
|
||||
if (indexData != nullptr)
|
||||
free(indexData);
|
||||
}
|
||||
|
||||
void Mesh::setupAttachedAttributes()
|
||||
@@ -151,7 +140,7 @@ void Mesh::setupAttachedAttributes()
|
||||
if (getAttachedAttributeIndex(name) != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||
|
||||
attachedAttributes.push_back({name, vertexBuffer, (int) i, STEP_PER_VERTEX, true});
|
||||
attachedAttributes.push_back({name, vertexBuffer, nullptr, (int) i, STEP_PER_VERTEX, true});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,89 +155,19 @@ int Mesh::getAttachedAttributeIndex(const std::string &name) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
|
||||
void *Mesh::checkVertexDataOffset(size_t vertindex, size_t *byteoffset)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("setVertex can only be called on a Mesh which owns its own vertex buffer.");
|
||||
if (vertexData == nullptr)
|
||||
throw love::Exception("Mesh must own its own vertex buffer.");
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vertexBuffer->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vertexBuffer->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("getVertex can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
// We're relying on map() returning read/write data... ew.
|
||||
const uint8 *bufferdata = (const uint8 *) vertexBuffer->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void *Mesh::getVertexScratchBuffer()
|
||||
{
|
||||
return vertexScratchBuffer;
|
||||
}
|
||||
|
||||
void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("setVertexAttribute can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
const auto &member = vertexFormat[attribindex];
|
||||
|
||||
size_t offset = vertindex * vertexStride + member.offset;
|
||||
size_t size = std::min(datasize, member.info.size);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vertexBuffer->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vertexBuffer->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("getVertexAttribute can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
const auto &member = vertexFormat[attribindex];
|
||||
|
||||
size_t offset = vertindex * vertexStride + member.offset;
|
||||
size_t size = std::min(datasize, member.info.size);
|
||||
|
||||
// We're relying on map() returning read/write data... ew.
|
||||
const uint8 *bufferdata = (const uint8 *) vertexBuffer->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
if (byteoffset != nullptr)
|
||||
*byteoffset = offset;
|
||||
return vertexData + offset;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexCount() const
|
||||
@@ -289,7 +208,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
|
||||
return attachedAttributes[index].enabled;
|
||||
}
|
||||
|
||||
void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step)
|
||||
void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, AttributeStep step)
|
||||
{
|
||||
if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
|
||||
throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
|
||||
@@ -309,6 +228,7 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::s
|
||||
|
||||
newattrib.name = name;
|
||||
newattrib.buffer = buffer;
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
|
||||
newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname);
|
||||
newattrib.step = step;
|
||||
@@ -331,7 +251,7 @@ bool Mesh::detachAttribute(const std::string &name)
|
||||
attachedAttributes.erase(attachedAttributes.begin() + index);
|
||||
|
||||
if (vertexBuffer.get() && vertexBuffer->getDataMemberIndex(name) != -1)
|
||||
attachAttribute(name, vertexBuffer, name);
|
||||
attachAttribute(name, vertexBuffer, nullptr, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -341,36 +261,49 @@ const std::vector<Mesh::BufferAttribute> &Mesh::getAttachedAttributes() const
|
||||
return attachedAttributes;
|
||||
}
|
||||
|
||||
void *Mesh::mapVertexData()
|
||||
void *Mesh::getVertexData() const
|
||||
{
|
||||
return vertexBuffer.get() != nullptr ? vertexBuffer->map() : nullptr;
|
||||
return vertexData;
|
||||
}
|
||||
|
||||
void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
|
||||
void Mesh::setVertexDataModified(size_t offset, size_t size)
|
||||
{
|
||||
if (!vertexBuffer.get())
|
||||
return;
|
||||
|
||||
vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize);
|
||||
vertexBuffer->unmap();
|
||||
if (vertexData != nullptr)
|
||||
modifiedVertexData.encapsulate(offset, size);
|
||||
}
|
||||
|
||||
void Mesh::flush()
|
||||
{
|
||||
if (vertexBuffer.get())
|
||||
vertexBuffer->unmap();
|
||||
if (vertexBuffer.get() && vertexData != nullptr && modifiedVertexData.isValid())
|
||||
{
|
||||
if (vertexBuffer->getUsage() == BUFFERUSAGE_STREAM)
|
||||
{
|
||||
vertexBuffer->fill(0, vertexBuffer->getSize(), vertexData);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t offset = modifiedVertexData.getOffset();
|
||||
size_t size = modifiedVertexData.getSize();
|
||||
vertexBuffer->fill(offset, size, vertexData + offset);
|
||||
}
|
||||
|
||||
if (indexBuffer.get())
|
||||
indexBuffer->unmap();
|
||||
modifiedVertexData.invalidate();
|
||||
}
|
||||
|
||||
if (indexDataModified && indexData != nullptr && indexBuffer != nullptr)
|
||||
{
|
||||
indexBuffer->fill(0, indexBuffer->getSize(), indexData);
|
||||
indexDataModified = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a vector to a mapped index buffer.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, void *data, size_t maxval)
|
||||
{
|
||||
T *elems = (T *) buffermap.data;
|
||||
T *elems = (T *) data;
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
@@ -395,7 +328,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, usage);
|
||||
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
@@ -405,17 +338,15 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
if (!indexBuffer || indexCount == 0)
|
||||
return;
|
||||
|
||||
Buffer::Mapper ibomap(*indexBuffer);
|
||||
|
||||
// Fill the buffer with the index values from the vector.
|
||||
switch (datatype)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyToIndexBuffer<uint16>(map, ibomap, maxval);
|
||||
copyToIndexBuffer<uint16>(map, indexData, maxval);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyToIndexBuffer<uint32>(map, ibomap, maxval);
|
||||
copyToIndexBuffer<uint32>(map, indexData, maxval);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -430,7 +361,7 @@ void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasiz
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, usage);
|
||||
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
@@ -470,24 +401,18 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
map.clear();
|
||||
map.reserve(indexCount);
|
||||
|
||||
if (!indexBuffer || indexCount == 0)
|
||||
if (indexData == nullptr || indexCount == 0)
|
||||
return true;
|
||||
|
||||
if ((indexBuffer->getMapFlags() & Buffer::MAP_READ) == 0)
|
||||
return false;
|
||||
|
||||
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
||||
void *buffer = indexBuffer->map();
|
||||
|
||||
// Fill the vector from the buffer.
|
||||
switch (indexDataType)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyFromIndexBuffer<uint16>(buffer, indexCount, map);
|
||||
copyFromIndexBuffer<uint16>(indexData, indexCount, map);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyFromIndexBuffer<uint32>(buffer, indexCount, map);
|
||||
copyFromIndexBuffer<uint32>(indexData, indexCount, map);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -507,6 +432,12 @@ void Mesh::setIndexBuffer(Buffer *buffer)
|
||||
|
||||
if (buffer != nullptr)
|
||||
indexDataType = getIndexDataType(buffer->getDataMember(0).decl.format);
|
||||
|
||||
if (indexData != nullptr)
|
||||
{
|
||||
free(indexData);
|
||||
indexData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer *Mesh::getIndexBuffer() const
|
||||
@@ -583,6 +514,8 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
|
||||
gfx->flushBatchedDraws();
|
||||
|
||||
flush();
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
@@ -612,8 +545,8 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
|
||||
if (attributeindex >= 0)
|
||||
{
|
||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||
buffer->unmap();
|
||||
if (attrib.mesh.get())
|
||||
attrib.mesh->flush();
|
||||
|
||||
const auto &member = buffer->getDataMember(attrib.indexInBuffer);
|
||||
|
||||
@@ -637,9 +570,6 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
|
||||
if (useIndexBuffer && indexBuffer != nullptr && indexCount > 0)
|
||||
{
|
||||
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
|
||||
indexBuffer->unmap();
|
||||
|
||||
Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer);
|
||||
|
||||
cmd.primitiveType = primitiveType;
|
||||
|
||||
+23
-28
@@ -25,6 +25,7 @@
|
||||
#include "common/int.h"
|
||||
#include "common/math.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "common/Range.h"
|
||||
#include "Drawable.h"
|
||||
#include "Texture.h"
|
||||
#include "vertex.h"
|
||||
@@ -54,6 +55,7 @@ public:
|
||||
{
|
||||
std::string name;
|
||||
StrongRef<Buffer> buffer;
|
||||
StrongRef<Mesh> mesh;
|
||||
int indexInBuffer;
|
||||
AttributeStep step;
|
||||
bool enabled;
|
||||
@@ -68,21 +70,10 @@ public:
|
||||
virtual ~Mesh();
|
||||
|
||||
/**
|
||||
* Sets the values of all attributes at a specific vertex index in the Mesh.
|
||||
* The size of the data must be less than or equal to the total size of all
|
||||
* vertex attributes.
|
||||
* Validates a vertex index and whether the Mesh has its own vertex buffer,
|
||||
* and returns a pointer to the vertex data at the given vertex index.
|
||||
**/
|
||||
void setVertex(size_t vertindex, const void *data, size_t datasize);
|
||||
size_t getVertex(size_t vertindex, void *data, size_t datasize);
|
||||
void *getVertexScratchBuffer();
|
||||
|
||||
/**
|
||||
* Sets the values for a single attribute at a specific vertex index in the
|
||||
* Mesh. The size of the data must be less than or equal to the size of the
|
||||
* attribute.
|
||||
**/
|
||||
void setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize);
|
||||
size_t getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize);
|
||||
void *checkVertexDataOffset(size_t vertindex, size_t *byteoffset);
|
||||
|
||||
/**
|
||||
* Gets the total number of vertices that can be used when drawing the Mesh.
|
||||
@@ -114,13 +105,16 @@ public:
|
||||
/**
|
||||
* Attaches a vertex attribute from another vertex buffer to this Mesh. The
|
||||
* attribute will be used when drawing this Mesh.
|
||||
* Attributes from other Meshes should also pass in the Mesh as an argument,
|
||||
* to make sure this Mesh knows to flush the passed in Mesh's data to its
|
||||
* buffer when drawing.
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
bool detachAttribute(const std::string &name);
|
||||
const std::vector<BufferAttribute> &getAttachedAttributes() const;
|
||||
|
||||
void *mapVertexData();
|
||||
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
|
||||
void *getVertexData() const;
|
||||
void setVertexDataModified(size_t offset, size_t size);
|
||||
|
||||
/**
|
||||
* Flushes all modified data to the GPU.
|
||||
@@ -197,23 +191,24 @@ private:
|
||||
|
||||
// Vertex buffer, for the vertex data.
|
||||
StrongRef<Buffer> vertexBuffer;
|
||||
size_t vertexCount;
|
||||
size_t vertexStride;
|
||||
uint8 *vertexData = nullptr;
|
||||
Range modifiedVertexData = Range();
|
||||
|
||||
// Block of memory whose size is at least as large as a single vertex. Helps
|
||||
// avoid memory allocations when using Mesh::setVertex etc.
|
||||
char *vertexScratchBuffer;
|
||||
size_t vertexCount = 0;
|
||||
size_t vertexStride = 0;
|
||||
|
||||
// Index buffer, for the vertex map.
|
||||
StrongRef<Buffer> indexBuffer;
|
||||
bool useIndexBuffer;
|
||||
size_t indexCount;
|
||||
IndexDataType indexDataType;
|
||||
uint8 *indexData = nullptr;
|
||||
bool indexDataModified = false;
|
||||
bool useIndexBuffer = false;
|
||||
size_t indexCount = 0;
|
||||
IndexDataType indexDataType = INDEX_UINT16;
|
||||
|
||||
PrimitiveType primitiveType;
|
||||
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
|
||||
|
||||
int rangeStart;
|
||||
int rangeCount;
|
||||
int rangeStart = -1;
|
||||
int rangeCount = -1;
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ void ParticleSystem::createBuffers(size_t size)
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
size_t bytes = sizeof(Vertex) * size * 4;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STREAM);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_STREAM);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub);
|
||||
buffer = gfx->newBuffer(settings, decl, nullptr, bytes, 0);
|
||||
}
|
||||
@@ -1043,7 +1043,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
const Vector2 *positions = texture->getQuad()->getVertexPositions();
|
||||
const Vector2 *texcoords = texture->getQuad()->getVertexTexCoords();
|
||||
|
||||
Vertex *pVerts = (Vertex *) buffer->map();
|
||||
Vertex *pVerts = (Vertex *) buffer->map(Buffer::MAP_WRITE_INVALIDATE, 0, buffer->getSize());
|
||||
Particle *p = pHead;
|
||||
|
||||
bool useQuads = !quads.empty();
|
||||
@@ -1079,7 +1079,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
buffer->unmap();
|
||||
buffer->unmap(0, pCount * sizeof(Vertex) * 4);
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
|
||||
@@ -48,8 +48,7 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage
|
||||
, colorf(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
, array_buf(nullptr)
|
||||
, vertex_data(nullptr)
|
||||
, modified_sprite_first(LOVE_INT32_MAX)
|
||||
, modified_sprite_last(0)
|
||||
, modified_sprites()
|
||||
, range_start(-1)
|
||||
, range_count(-1)
|
||||
{
|
||||
@@ -74,7 +73,7 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage
|
||||
|
||||
memset(vertex_data, 0, vertex_size);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, usage);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, usage);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
|
||||
|
||||
array_buf.set(gfx->newBuffer(settings, decl, nullptr, vertex_size, 0), Acquire::NORETAIN);
|
||||
@@ -118,8 +117,7 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
verts[i].color = color;
|
||||
}
|
||||
|
||||
modified_sprite_first = std::min(modified_sprite_first, spriteindex);
|
||||
modified_sprite_last = std::max(modified_sprite_last, spriteindex);
|
||||
modified_sprites.encapsulate(spriteindex);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
@@ -165,8 +163,7 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
|
||||
verts[i].color = color;
|
||||
}
|
||||
|
||||
modified_sprite_first = std::min(modified_sprite_first, spriteindex);
|
||||
modified_sprite_last = std::max(modified_sprite_last, spriteindex);
|
||||
modified_sprites.encapsulate(spriteindex);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
@@ -183,19 +180,17 @@ void SpriteBatch::clear()
|
||||
|
||||
void SpriteBatch::flush()
|
||||
{
|
||||
if (modified_sprite_first <= modified_sprite_last)
|
||||
if (modified_sprites.isValid())
|
||||
{
|
||||
size_t offset = modified_sprite_first * vertex_stride * 4;
|
||||
size_t size = (modified_sprite_last - modified_sprite_first) * vertex_stride * 4;
|
||||
size_t offset = modified_sprites.getOffset() * vertex_stride * 4;
|
||||
size_t size = modified_sprites.getSize() * vertex_stride * 4;
|
||||
|
||||
// TODO: switch this to fill() once it gets cleaned up.
|
||||
memcpy(((uint8 *)array_buf->map() + offset), vertex_data + offset, size);
|
||||
if (array_buf->getUsage() == BUFFERUSAGE_STREAM)
|
||||
array_buf->fill(0, array_buf->getSize(), vertex_data);
|
||||
else
|
||||
array_buf->fill(offset, size, vertex_data + offset);
|
||||
|
||||
array_buf->setMappedRangeModified(offset, size);
|
||||
array_buf->unmap();
|
||||
|
||||
modified_sprite_first = LOVE_INT32_MAX;
|
||||
modified_sprite_last = 0;
|
||||
modified_sprites.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +244,7 @@ void SpriteBatch::setBufferSize(int newsize)
|
||||
throw love::Exception("Out of memory.");
|
||||
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getMapFlags(), array_buf->getUsage());
|
||||
Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getUsage());
|
||||
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
|
||||
|
||||
array_buf.set(gfx->newBuffer(settings, decl, nullptr, vertex_size, 0), Acquire::NORETAIN);
|
||||
@@ -267,7 +262,7 @@ int SpriteBatch::getBufferSize() const
|
||||
return size;
|
||||
}
|
||||
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh)
|
||||
{
|
||||
if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
|
||||
throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a SpriteBatch vertex attribute.");
|
||||
@@ -288,6 +283,7 @@ void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
|
||||
throw love::Exception("The specified Buffer does not have a vertex attribute named '%s'", name.c_str());
|
||||
|
||||
newattrib.buffer = buffer;
|
||||
newattrib.mesh = mesh;
|
||||
|
||||
attached_attributes[name] = newattrib;
|
||||
}
|
||||
@@ -371,8 +367,8 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
|
||||
if (attributeindex >= 0)
|
||||
{
|
||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||
buffer->unmap();
|
||||
if (it.second.mesh.get())
|
||||
it.second.mesh->flush();
|
||||
|
||||
const auto &member = buffer->getDataMember(it.second.index);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "common/math.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/Color.h"
|
||||
#include "common/Range.h"
|
||||
#include "Drawable.h"
|
||||
#include "Mesh.h"
|
||||
#include "vertex.h"
|
||||
@@ -90,10 +91,13 @@ public:
|
||||
int getBufferSize() const;
|
||||
|
||||
/**
|
||||
* Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
|
||||
* Attaches a specific vertex attribute from a Buffer to this SpriteBatch.
|
||||
* The vertex attribute will be used when drawing the SpriteBatch.
|
||||
* If the attribute comes from a Mesh, it should be given as an argument as
|
||||
* well, to make sure the SpriteBatch flushes its data to its Buffer when
|
||||
* the SpriteBatch is drawn.
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Buffer *buffer);
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh);
|
||||
|
||||
void setDrawRange(int start, int count);
|
||||
void setDrawRange();
|
||||
@@ -107,6 +111,7 @@ private:
|
||||
struct AttachedAttribute
|
||||
{
|
||||
StrongRef<Buffer> buffer;
|
||||
StrongRef<Mesh> mesh;
|
||||
int index;
|
||||
};
|
||||
|
||||
@@ -134,8 +139,7 @@ private:
|
||||
StrongRef<love::graphics::Buffer> array_buf;
|
||||
uint8 *vertex_data;
|
||||
|
||||
int modified_sprite_first;
|
||||
int modified_sprite_last;
|
||||
Range modified_sprites;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
|
||||
|
||||
|
||||
@@ -33,14 +33,18 @@ love::Type Text::type("Text", &Drawable::type);
|
||||
Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
|
||||
: font(font)
|
||||
, vertexAttributes(Font::vertexFormat, 0)
|
||||
, vert_offset(0)
|
||||
, texture_cache_id((uint32) -1)
|
||||
, vertexData(nullptr)
|
||||
, modifiedVertices()
|
||||
, vertOffset(0)
|
||||
, textureCacheID((uint32) -1)
|
||||
{
|
||||
set(text);
|
||||
}
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
if (vertexData != nullptr)
|
||||
free(vertexData);
|
||||
}
|
||||
|
||||
void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
|
||||
@@ -49,32 +53,40 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
|
||||
size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
|
||||
|
||||
// If we haven't created a VBO or the vertices are too big, make a new one.
|
||||
if (datasize > 0 && (!vertex_buffer || (offset + datasize) > vertex_buffer->getSize()))
|
||||
if (datasize > 0 && (!vertexBuffer || (offset + datasize) > vertexBuffer->getSize()))
|
||||
{
|
||||
// Make it bigger than necessary to reduce potential future allocations.
|
||||
size_t newsize = size_t((offset + datasize) * 1.5);
|
||||
|
||||
if (vertex_buffer != nullptr)
|
||||
newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize);
|
||||
if (vertexBuffer != nullptr)
|
||||
newsize = std::max(size_t(vertexBuffer->getSize() * 1.5), newsize);
|
||||
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_DYNAMIC);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_DYNAMIC);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(Font::vertexFormat);
|
||||
Buffer *new_buffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0);
|
||||
Buffer *newbuffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0);
|
||||
|
||||
if (vertex_buffer != nullptr)
|
||||
vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0);
|
||||
void *newdata = nullptr;
|
||||
if (vertexData != nullptr)
|
||||
newdata = realloc(vertexData, newsize);
|
||||
else
|
||||
newdata = malloc(newsize);
|
||||
|
||||
vertex_buffer = new_buffer;
|
||||
if (newdata == nullptr)
|
||||
throw love::Exception("Out of memory.");
|
||||
else
|
||||
vertexData = (uint8 *) newdata;
|
||||
|
||||
vertexBuffers.set(0, vertex_buffer, 0);
|
||||
vertexBuffer = newbuffer;
|
||||
|
||||
vertexBuffers.set(0, vertexBuffer, 0);
|
||||
}
|
||||
|
||||
if (vertex_buffer != nullptr && datasize > 0)
|
||||
if (vertexData != nullptr && datasize > 0)
|
||||
{
|
||||
uint8 *bufferdata = (uint8 *) vertex_buffer->map();
|
||||
memcpy(bufferdata + offset, &vertices[0], datasize);
|
||||
// We unmap when we draw, to avoid unnecessary full map()/unmap() calls.
|
||||
memcpy(vertexData + offset, &vertices[0], datasize);
|
||||
modifiedVertices.encapsulate(offset, datasize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,85 +94,85 @@ void Text::regenerateVertices()
|
||||
{
|
||||
// If the font's texture cache was invalidated then we need to recreate the
|
||||
// text's vertices, since glyph texcoords might have changed.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
if (font->getTextureCacheID() != textureCacheID)
|
||||
{
|
||||
std::vector<TextData> textdata = text_data;
|
||||
std::vector<TextData> textdata = textData;
|
||||
|
||||
clear();
|
||||
|
||||
for (const TextData &t : textdata)
|
||||
addTextData(t);
|
||||
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
textureCacheID = font->getTextureCacheID();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::addTextData(const TextData &t)
|
||||
{
|
||||
std::vector<Font::GlyphVertex> vertices;
|
||||
std::vector<Font::DrawCommand> new_commands;
|
||||
std::vector<Font::DrawCommand> newcommands;
|
||||
|
||||
Font::TextInfo text_info;
|
||||
Font::TextInfo textinfo;
|
||||
|
||||
Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// We only have formatted text if the align mode is valid.
|
||||
if (t.align == Font::ALIGN_MAX_ENUM)
|
||||
new_commands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &text_info);
|
||||
newcommands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &textinfo);
|
||||
else
|
||||
new_commands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &text_info);
|
||||
newcommands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &textinfo);
|
||||
|
||||
size_t voffset = vert_offset;
|
||||
size_t voffset = vertOffset;
|
||||
|
||||
// Must be before the early exit below.
|
||||
if (!t.append_vertices)
|
||||
if (!t.appendVertices)
|
||||
{
|
||||
voffset = 0;
|
||||
vert_offset = 0;
|
||||
draw_commands.clear();
|
||||
text_data.clear();
|
||||
vertOffset = 0;
|
||||
drawCommands.clear();
|
||||
textData.clear();
|
||||
}
|
||||
|
||||
if (vertices.empty())
|
||||
return;
|
||||
|
||||
if (t.use_matrix)
|
||||
if (t.useMatrix)
|
||||
t.matrix.transformXY(&vertices[0], &vertices[0], (int) vertices.size());
|
||||
|
||||
uploadVertices(vertices, voffset);
|
||||
|
||||
if (!new_commands.empty())
|
||||
if (!newcommands.empty())
|
||||
{
|
||||
// The start vertex should be adjusted to account for the vertex offset.
|
||||
for (Font::DrawCommand &cmd : new_commands)
|
||||
for (Font::DrawCommand &cmd : newcommands)
|
||||
cmd.startvertex += (int) voffset;
|
||||
|
||||
auto firstcmd = new_commands.begin();
|
||||
auto firstcmd = newcommands.begin();
|
||||
|
||||
// If the first draw command in the new list has the same texture as the
|
||||
// last one in the existing list we're building and its vertices are
|
||||
// in-order, we can combine them (saving a draw call.)
|
||||
if (!draw_commands.empty())
|
||||
if (!drawCommands.empty())
|
||||
{
|
||||
auto prevcmd = draw_commands.back();
|
||||
auto prevcmd = drawCommands.back();
|
||||
if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
|
||||
{
|
||||
draw_commands.back().vertexcount += firstcmd->vertexcount;
|
||||
drawCommands.back().vertexcount += firstcmd->vertexcount;
|
||||
++firstcmd;
|
||||
}
|
||||
}
|
||||
|
||||
// Append the new draw commands to the list we're building.
|
||||
draw_commands.insert(draw_commands.end(), firstcmd, new_commands.end());
|
||||
drawCommands.insert(drawCommands.end(), firstcmd, newcommands.end());
|
||||
}
|
||||
|
||||
vert_offset = voffset + vertices.size();
|
||||
vertOffset = voffset + vertices.size();
|
||||
|
||||
text_data.push_back(t);
|
||||
text_data.back().text_info = text_info;
|
||||
textData.push_back(t);
|
||||
textData.back().textInfo = textinfo;
|
||||
|
||||
// Font::generateVertices can invalidate the font's texture cache.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
if (font->getTextureCacheID() != textureCacheID)
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
@@ -192,15 +204,15 @@ int Text::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::A
|
||||
|
||||
addTextData({codepoints, wrap, align, {}, true, true, m});
|
||||
|
||||
return (int) text_data.size() - 1;
|
||||
return (int) textData.size() - 1;
|
||||
}
|
||||
|
||||
void Text::clear()
|
||||
{
|
||||
text_data.clear();
|
||||
draw_commands.clear();
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
vert_offset = 0;
|
||||
textData.clear();
|
||||
drawCommands.clear();
|
||||
textureCacheID = font->getTextureCacheID();
|
||||
vertOffset = 0;
|
||||
}
|
||||
|
||||
void Text::setFont(Font *f)
|
||||
@@ -209,7 +221,7 @@ void Text::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.
|
||||
texture_cache_id = (uint32) -1;
|
||||
textureCacheID = (uint32) -1;
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
@@ -221,28 +233,28 @@ Font *Text::getFont() const
|
||||
int Text::getWidth(int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
index = std::max((int) text_data.size() - 1, 0);
|
||||
index = std::max((int) textData.size() - 1, 0);
|
||||
|
||||
if (index >= (int) text_data.size())
|
||||
if (index >= (int) textData.size())
|
||||
return 0;
|
||||
|
||||
return text_data[index].text_info.width;
|
||||
return textData[index].textInfo.width;
|
||||
}
|
||||
|
||||
int Text::getHeight(int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
index = std::max((int) text_data.size() - 1, 0);
|
||||
index = std::max((int) textData.size() - 1, 0);
|
||||
|
||||
if (index >= (int) text_data.size())
|
||||
if (index >= (int) textData.size())
|
||||
return 0;
|
||||
|
||||
return text_data[index].text_info.height;
|
||||
return textData[index].textInfo.height;
|
||||
}
|
||||
|
||||
void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
{
|
||||
if (vertex_buffer == nullptr || draw_commands.empty())
|
||||
if (vertexBuffer == nullptr || vertexData == nullptr || drawCommands.empty())
|
||||
return;
|
||||
|
||||
gfx->flushBatchedDraws();
|
||||
@@ -254,18 +266,30 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
Shader::current->checkMainTextureType(TEXTURE_2D, false);
|
||||
|
||||
// Re-generate the text if the Font's texture cache was invalidated.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
if (font->getTextureCacheID() != textureCacheID)
|
||||
regenerateVertices();
|
||||
|
||||
int totalverts = 0;
|
||||
for (const Font::DrawCommand &cmd : draw_commands)
|
||||
for (const Font::DrawCommand &cmd : drawCommands)
|
||||
totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
|
||||
|
||||
vertex_buffer->unmap(); // Make sure all pending data is flushed to the GPU.
|
||||
// Make sure all pending data is uploaded to the GPU.
|
||||
if (modifiedVertices.isValid())
|
||||
{
|
||||
size_t offset = modifiedVertices.getOffset();
|
||||
size_t size = modifiedVertices.getSize();
|
||||
|
||||
if (vertexBuffer->getUsage() == BUFFERUSAGE_STREAM)
|
||||
vertexBuffer->fill(0, vertexBuffer->getSize(), vertexData);
|
||||
else
|
||||
vertexBuffer->fill(offset, size, vertexData + offset);
|
||||
|
||||
modifiedVertices.invalidate();
|
||||
}
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
for (const Font::DrawCommand &cmd : draw_commands)
|
||||
for (const Font::DrawCommand &cmd : drawCommands)
|
||||
gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/Range.h"
|
||||
#include "Drawable.h"
|
||||
#include "Font.h"
|
||||
#include "Buffer.h"
|
||||
@@ -73,9 +74,9 @@ private:
|
||||
Font::ColoredCodepoints codepoints;
|
||||
float wrap;
|
||||
Font::AlignMode align;
|
||||
Font::TextInfo text_info;
|
||||
bool use_matrix;
|
||||
bool append_vertices;
|
||||
Font::TextInfo textInfo;
|
||||
bool useMatrix;
|
||||
bool appendVertices;
|
||||
Matrix4 matrix;
|
||||
};
|
||||
|
||||
@@ -88,16 +89,18 @@ private:
|
||||
VertexAttributes vertexAttributes;
|
||||
BufferBindings vertexBuffers;
|
||||
|
||||
StrongRef<Buffer> vertex_buffer;
|
||||
StrongRef<Buffer> vertexBuffer;
|
||||
uint8 *vertexData;
|
||||
Range modifiedVertices;
|
||||
|
||||
std::vector<Font::DrawCommand> draw_commands;
|
||||
std::vector<Font::DrawCommand> drawCommands;
|
||||
|
||||
std::vector<TextData> text_data;
|
||||
std::vector<TextData> textData;
|
||||
|
||||
size_t vert_offset;
|
||||
size_t vertOffset;
|
||||
|
||||
// Used so we know when the font's texture cache is invalidated.
|
||||
uint32 texture_cache_id;
|
||||
uint32 textureCacheID;
|
||||
|
||||
}; // Text
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "common/Exception.h"
|
||||
#include "graphics/vertex.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -80,22 +81,26 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
|
||||
|
||||
target = OpenGL::getGLBufferType(mapType);
|
||||
|
||||
try
|
||||
if (usage == BUFFERUSAGE_STREAM)
|
||||
ownsMemoryMap = true;
|
||||
|
||||
std::vector<uint8> emptydata;
|
||||
if (settings.zeroInitialize && data == nullptr)
|
||||
{
|
||||
memoryMap = new char[size];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
try
|
||||
{
|
||||
emptydata.resize(getSize());
|
||||
data = emptydata.data();
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
memcpy(memoryMap, data, size);
|
||||
|
||||
if (!load(data != nullptr))
|
||||
if (!load(data))
|
||||
{
|
||||
unloadVolatile();
|
||||
delete[] memoryMap;
|
||||
throw love::Exception("Could not create buffer (out of VRAM?)");
|
||||
}
|
||||
}
|
||||
@@ -103,7 +108,8 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
unloadVolatile();
|
||||
delete[] memoryMap;
|
||||
if (memoryMap != nullptr && ownsMemoryMap)
|
||||
free(memoryMap);
|
||||
}
|
||||
|
||||
bool Buffer::loadVolatile()
|
||||
@@ -111,7 +117,7 @@ bool Buffer::loadVolatile()
|
||||
if (buffer != 0)
|
||||
return true;
|
||||
|
||||
return load(true);
|
||||
return load(nullptr);
|
||||
}
|
||||
|
||||
void Buffer::unloadVolatile()
|
||||
@@ -125,7 +131,7 @@ void Buffer::unloadVolatile()
|
||||
texture = 0;
|
||||
}
|
||||
|
||||
bool Buffer::load(bool restore)
|
||||
bool Buffer::load(const void *initialdata)
|
||||
{
|
||||
while (glGetError() != GL_NO_ERROR)
|
||||
/* Clear the error buffer. */;
|
||||
@@ -133,11 +139,8 @@ bool Buffer::load(bool restore)
|
||||
glGenBuffers(1, &buffer);
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
|
||||
// Copy the old buffer only if 'restore' was requested.
|
||||
const GLvoid *src = restore ? memoryMap : nullptr;
|
||||
|
||||
// Note that if 'src' is '0', no data will be copied.
|
||||
glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
|
||||
// initialdata can be null.
|
||||
glBufferData(target, (GLsizeiptr) getSize(), initialdata, OpenGL::getGLBufferUsage(getUsage()));
|
||||
|
||||
if (getTypeFlags() & TYPEFLAG_TEXEL)
|
||||
{
|
||||
@@ -150,137 +153,104 @@ bool Buffer::load(bool restore)
|
||||
return (glGetError() == GL_NO_ERROR);
|
||||
}
|
||||
|
||||
void *Buffer::map()
|
||||
{
|
||||
if (mapped)
|
||||
return memoryMap;
|
||||
|
||||
mapped = true;
|
||||
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = 0;
|
||||
isMappedDataModified = false;
|
||||
|
||||
return memoryMap;
|
||||
}
|
||||
|
||||
void Buffer::unmapStatic(size_t offset, size_t size)
|
||||
void *Buffer::map(MapType /*map*/, size_t offset, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
return nullptr;
|
||||
|
||||
// Upload the mapped data to the buffer.
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset);
|
||||
}
|
||||
Range r(offset, size);
|
||||
|
||||
void Buffer::unmapStream()
|
||||
{
|
||||
GLenum glusage = OpenGL::getGLBufferUsage(getUsage());
|
||||
if (!Range(0, getSize()).contains(r))
|
||||
return nullptr;
|
||||
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage);
|
||||
char *data = nullptr;
|
||||
|
||||
#if LOVE_WINDOWS
|
||||
// TODO: Verify that this codepath is a useful optimization.
|
||||
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
|
||||
glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage);
|
||||
if (ownsMemoryMap)
|
||||
{
|
||||
if (memoryMap == nullptr)
|
||||
memoryMap = (char *) malloc(getSize());
|
||||
data = memoryMap;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap);
|
||||
{
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
data = (char *) gfx->getBufferMapMemory(size);
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
{
|
||||
mapped = true;
|
||||
mappedRange = r;
|
||||
if (!ownsMemoryMap)
|
||||
memoryMap = data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Buffer::unmap()
|
||||
void Buffer::unmap(size_t usedoffset, size_t usedsize)
|
||||
{
|
||||
if (!mapped)
|
||||
Range r(usedoffset, usedsize);
|
||||
|
||||
if (!mapped || !mappedRange.contains(r))
|
||||
return;
|
||||
|
||||
mapped = false;
|
||||
|
||||
if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
|
||||
// Orphan optimization - see fill().
|
||||
if (usage != BUFFERUSAGE_STATIC && mappedRange.first == 0 && mappedRange.getSize() == getSize())
|
||||
{
|
||||
if (!isMappedDataModified)
|
||||
return;
|
||||
|
||||
modifiedOffset = std::min(modifiedOffset, getSize() - 1);
|
||||
modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = getSize();
|
||||
usedoffset = 0;
|
||||
usedsize = getSize();
|
||||
}
|
||||
|
||||
if (modifiedSize > 0)
|
||||
char *data = memoryMap + (usedoffset - mappedRange.getOffset());
|
||||
|
||||
fill(usedoffset, usedsize, data);
|
||||
|
||||
if (!ownsMemoryMap)
|
||||
{
|
||||
switch (getUsage())
|
||||
{
|
||||
case BUFFERUSAGE_STATIC:
|
||||
unmapStatic(modifiedOffset, modifiedSize);
|
||||
break;
|
||||
case BUFFERUSAGE_STREAM:
|
||||
unmapStream();
|
||||
break;
|
||||
case BUFFERUSAGE_DYNAMIC:
|
||||
default:
|
||||
// It's probably more efficient to treat it like a streaming buffer if
|
||||
// at least a third of its contents have been modified during the map().
|
||||
if (modifiedSize >= getSize() / 3)
|
||||
unmapStream();
|
||||
else
|
||||
unmapStatic(modifiedOffset, modifiedSize);
|
||||
break;
|
||||
}
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
gfx->releaseBufferMapMemory(memoryMap);
|
||||
memoryMap = nullptr;
|
||||
}
|
||||
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = 0;
|
||||
}
|
||||
|
||||
void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
|
||||
{
|
||||
if (!mapped || !(mapFlags & MAP_EXPLICIT_RANGE_MODIFY))
|
||||
return;
|
||||
|
||||
if (!isMappedDataModified)
|
||||
{
|
||||
modifiedOffset = offset;
|
||||
modifiedSize = modifiedsize;
|
||||
isMappedDataModified = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// We're being conservative right now by internally marking the whole range
|
||||
// from the start of section a to the end of section b as modified if both
|
||||
// a and b are marked as modified.
|
||||
|
||||
size_t oldrangeend = modifiedOffset + modifiedSize;
|
||||
modifiedOffset = std::min(modifiedOffset, offset);
|
||||
|
||||
size_t newrangeend = std::max(offset + modifiedsize, oldrangeend);
|
||||
modifiedSize = newrangeend - modifiedOffset;
|
||||
}
|
||||
|
||||
void Buffer::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
memcpy(memoryMap + offset, data, size);
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
if (mapped)
|
||||
setMappedRangeModified(offset, size);
|
||||
size_t buffersize = getSize();
|
||||
|
||||
if (!Range(0, buffersize).contains(Range(offset, size)))
|
||||
return;
|
||||
|
||||
GLenum glusage = OpenGL::getGLBufferUsage(usage);
|
||||
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
|
||||
if (usage != BUFFERUSAGE_STATIC && size == buffersize)
|
||||
{
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferData(target, (GLsizeiptr) buffersize, nullptr, glusage);
|
||||
|
||||
#if LOVE_WINDOWS
|
||||
// TODO: Verify that this codepath is a useful optimization.
|
||||
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
|
||||
glBufferData(target, (GLsizeiptr) buffersize, data, glusage);
|
||||
else
|
||||
#endif
|
||||
glBufferSubData(target, 0, (GLsizeiptr) buffersize, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
|
||||
{
|
||||
other->fill(otheroffset, size, memoryMap + offset);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/Range.h"
|
||||
#include "graphics/Buffer.h"
|
||||
#include "graphics/Volatile.h"
|
||||
|
||||
@@ -49,19 +50,16 @@ public:
|
||||
bool loadVolatile() override;
|
||||
void unloadVolatile() override;
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
void setMappedRangeModified(size_t offset, size_t size) override;
|
||||
void *map(MapType map, size_t offset, size_t size) override;
|
||||
void unmap(size_t usedoffset, size_t usedsize) override;
|
||||
void fill(size_t offset, size_t size, const void *data) override;
|
||||
|
||||
ptrdiff_t getHandle() const override { return buffer; };
|
||||
ptrdiff_t getTexelBufferHandle() const override { return texture; };
|
||||
|
||||
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
|
||||
|
||||
private:
|
||||
|
||||
bool load(bool restore);
|
||||
bool load(const void *initialdata);
|
||||
|
||||
void unmapStatic(size_t offset, size_t size);
|
||||
void unmapStream();
|
||||
@@ -77,10 +75,9 @@ private:
|
||||
|
||||
// A pointer to mapped memory.
|
||||
char *memoryMap = nullptr;
|
||||
bool ownsMemoryMap = false;
|
||||
|
||||
size_t modifiedOffset = 0;
|
||||
size_t modifiedSize = 0;
|
||||
bool isMappedDataModified = false;
|
||||
Range mappedRange;
|
||||
|
||||
}; // Buffer
|
||||
|
||||
|
||||
@@ -93,11 +93,22 @@ Graphics::Graphics()
|
||||
, mainVAO(0)
|
||||
, internalBackbufferFBO(0)
|
||||
, requestedBackbufferMSAA(0)
|
||||
, bufferMapMemory(nullptr)
|
||||
, bufferMapMemorySize(2 * 1024 * 1024)
|
||||
, defaultBuffers()
|
||||
, supportedFormats()
|
||||
{
|
||||
gl = OpenGL();
|
||||
|
||||
try
|
||||
{
|
||||
bufferMapMemory = new char[bufferMapMemorySize];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
// Handled in getBufferMapMemory.
|
||||
}
|
||||
|
||||
auto window = getInstance<love::window::Window>(M_WINDOW);
|
||||
|
||||
if (window != nullptr)
|
||||
@@ -121,6 +132,7 @@ Graphics::Graphics()
|
||||
|
||||
Graphics::~Graphics()
|
||||
{
|
||||
delete[] bufferMapMemory;
|
||||
}
|
||||
|
||||
const char *Graphics::getName() const
|
||||
@@ -328,7 +340,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
|
||||
|
||||
if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERTYPE_TEXEL].get() == nullptr)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, 0, BUFFERUSAGE_STATIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, BUFFERUSAGE_STATIC);
|
||||
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT_VEC4, 0}};
|
||||
|
||||
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
@@ -1436,6 +1448,21 @@ void Graphics::setWireframe(bool enable)
|
||||
states.back().wireframe = enable;
|
||||
}
|
||||
|
||||
void *Graphics::getBufferMapMemory(size_t size)
|
||||
{
|
||||
// We don't need anything more complicated because get/release calls are
|
||||
// never interleaved (as of when this comment was written.)
|
||||
if (bufferMapMemory == nullptr || size > bufferMapMemorySize)
|
||||
return malloc(size);
|
||||
return bufferMapMemory;
|
||||
}
|
||||
|
||||
void Graphics::releaseBufferMapMemory(void *mem)
|
||||
{
|
||||
if (mem != bufferMapMemory)
|
||||
free(mem);
|
||||
}
|
||||
|
||||
Graphics::Renderer Graphics::getRenderer() const
|
||||
{
|
||||
return GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
|
||||
|
||||
@@ -112,6 +112,9 @@ public:
|
||||
// Internal use.
|
||||
void cleanupRenderTexture(love::graphics::Texture *texture);
|
||||
|
||||
void *getBufferMapMemory(size_t size);
|
||||
void releaseBufferMapMemory(void *mem);
|
||||
|
||||
private:
|
||||
|
||||
struct CachedFBOHasher
|
||||
@@ -159,6 +162,9 @@ private:
|
||||
GLuint internalBackbufferFBO;
|
||||
int requestedBackbufferMSAA;
|
||||
|
||||
char *bufferMapMemory;
|
||||
size_t bufferMapMemorySize;
|
||||
|
||||
// Only needed for buffer types that can be bound to shaders.
|
||||
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERTYPE_MAX_ENUM];
|
||||
|
||||
|
||||
@@ -761,30 +761,8 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
||||
buffer->retain();
|
||||
}
|
||||
|
||||
bool addbuffertoarray = true;
|
||||
|
||||
if (info->buffers[i] != nullptr)
|
||||
{
|
||||
Buffer *oldbuffer = info->buffers[i];
|
||||
auto it = std::find(buffersToUnmap.begin(), buffersToUnmap.end(), oldbuffer);
|
||||
if (it != buffersToUnmap.end())
|
||||
{
|
||||
addbuffertoarray = false;
|
||||
if (buffer != nullptr)
|
||||
*it = buffer;
|
||||
else
|
||||
{
|
||||
auto last = buffersToUnmap.end() - 1;
|
||||
*it = *last;
|
||||
buffersToUnmap.erase(last);
|
||||
}
|
||||
}
|
||||
|
||||
oldbuffer->release();
|
||||
}
|
||||
|
||||
if (addbuffertoarray && buffer != nullptr)
|
||||
buffersToUnmap.push_back(buffer);
|
||||
info->buffers[i]->release();
|
||||
|
||||
info->buffers[i] = buffer;
|
||||
|
||||
@@ -916,13 +894,6 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
|
||||
GLint location = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW];
|
||||
if (location >= 0)
|
||||
glUniform4fv(location, 13, (const GLfloat *) &data);
|
||||
|
||||
// TODO: Find a better place to put this.
|
||||
// Buffers used in this shader can be mapped by external code without
|
||||
// unmapping. We need to make sure the data on the GPU is up to date,
|
||||
// otherwise the shader can read from old data.
|
||||
for (Buffer *buffer : buffersToUnmap)
|
||||
buffer->unmap();
|
||||
}
|
||||
|
||||
int Shader::getUniformTypeComponents(GLenum type) const
|
||||
|
||||
@@ -119,8 +119,6 @@ private:
|
||||
|
||||
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
|
||||
|
||||
std::vector<Buffer *> buffersToUnmap;
|
||||
|
||||
float lastPointSize;
|
||||
|
||||
}; // Shader
|
||||
|
||||
@@ -186,13 +186,6 @@ Buffer *luax_checkbuffer(lua_State *L, int idx)
|
||||
return luax_checktype<Buffer>(L, idx);
|
||||
}
|
||||
|
||||
static int w_Buffer_flush(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
t->unmap();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w_Buffer_setArrayData(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
@@ -223,12 +216,8 @@ static int w_Buffer_setArrayData(lua_State *L)
|
||||
return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count);
|
||||
|
||||
size_t datasize = std::min(d->getSize(), count * stride);
|
||||
char *bytedata = (char *) t->map() + offset;
|
||||
|
||||
memcpy(bytedata, d->getData(), datasize);
|
||||
|
||||
t->setMappedRangeModified(offset, datasize);
|
||||
t->unmap();
|
||||
t->fill(offset, datasize, d->getData());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -256,7 +245,7 @@ static int w_Buffer_setArrayData(lua_State *L)
|
||||
if (startindex + count > arraylength)
|
||||
return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count);
|
||||
|
||||
char *data = (char *) t->map() + offset;
|
||||
char *data = (char *) t->map(Buffer::MAP_WRITE_INVALIDATE, offset, count * stride);
|
||||
|
||||
if (tableoftables)
|
||||
{
|
||||
@@ -303,8 +292,7 @@ static int w_Buffer_setArrayData(lua_State *L)
|
||||
}
|
||||
}
|
||||
|
||||
t->setMappedRangeModified(offset, count * stride);
|
||||
t->unmap();
|
||||
t->unmap(offset, count * stride);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -376,7 +364,6 @@ static int w_Buffer_isBufferType(lua_State *L)
|
||||
|
||||
static const luaL_Reg w_Buffer_functions[] =
|
||||
{
|
||||
{ "flush", w_Buffer_flush },
|
||||
{ "setArrayData", w_Buffer_setArrayData },
|
||||
{ "getElementCount", w_Buffer_getElementCount },
|
||||
{ "getElementStride", w_Buffer_getElementStride },
|
||||
|
||||
@@ -1559,7 +1559,7 @@ static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::Da
|
||||
}
|
||||
}
|
||||
|
||||
static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format)
|
||||
static Buffer *luax_newbuffer(lua_State *L, int idx, Buffer::Settings settings, const std::vector<Buffer::DataDeclaration> &format)
|
||||
{
|
||||
size_t arraylength = 0;
|
||||
size_t bytesize = 0;
|
||||
@@ -1600,6 +1600,7 @@ static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &set
|
||||
if (len <= 0)
|
||||
luaL_argerror(L, idx, "number of elements must be greater than 0");
|
||||
arraylength = (size_t) len;
|
||||
settings.zeroInitialize = true;
|
||||
}
|
||||
|
||||
Buffer *b = nullptr;
|
||||
@@ -1663,7 +1664,7 @@ static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &set
|
||||
|
||||
int w_newBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(0, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
Buffer::Settings settings(0, BUFFERUSAGE_DYNAMIC);
|
||||
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
|
||||
@@ -1691,7 +1692,7 @@ int w_newBuffer(lua_State *L)
|
||||
|
||||
int w_newVertexBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_DYNAMIC);
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
std::vector<Buffer::DataDeclaration> format;
|
||||
@@ -1706,7 +1707,7 @@ int w_newVertexBuffer(lua_State *L)
|
||||
|
||||
int w_newIndexBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, BUFFERUSAGE_DYNAMIC);
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
size_t arraylength = 0;
|
||||
@@ -1747,6 +1748,7 @@ int w_newIndexBuffer(lua_State *L)
|
||||
if (len <= 0)
|
||||
return luaL_argerror(L, 1, "number of elements must be greater than 0");
|
||||
arraylength = (size_t) len;
|
||||
settings.zeroInitialize = true;
|
||||
}
|
||||
|
||||
if (data != nullptr || !lua_isnoneornil(L, 2))
|
||||
@@ -1953,8 +1955,9 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
|
||||
luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); });
|
||||
|
||||
// Maximum possible data size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
char *data = (char *) t->getVertexData();
|
||||
size_t stride = t->getVertexStride();
|
||||
const auto &members = t->getVertexFormat();
|
||||
|
||||
for (size_t vertindex = 0; vertindex < numvertices; vertindex++)
|
||||
{
|
||||
@@ -1965,7 +1968,8 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
int n = 0;
|
||||
for (size_t i = 0; i < vertexformat.size(); i++)
|
||||
{
|
||||
const auto &info = getDataFormatInfo(vertexformat[i].format);
|
||||
const auto &member = members[i];
|
||||
const auto &info = getDataFormatInfo(member.decl.format);
|
||||
|
||||
// get vertices[vertindex][n]
|
||||
for (int c = 0; c < info.components; c++)
|
||||
@@ -1974,20 +1978,18 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
lua_rawgeti(L, -(c + 1), n);
|
||||
}
|
||||
|
||||
size_t offset = vertindex * stride + member.offset;
|
||||
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
luax_writebufferdata(L, -info.components, vertexformat[i].format, data);
|
||||
luax_writebufferdata(L, -info.components, member.decl.format, data + offset);
|
||||
|
||||
lua_pop(L, info.components);
|
||||
|
||||
luax_catchexcept(L,
|
||||
[&](){ t->setVertexAttribute(vertindex, i, data, sizeof(float) * 4); },
|
||||
[&](bool diderror){ if (diderror) t->release(); }
|
||||
);
|
||||
}
|
||||
|
||||
lua_pop(L, 1); // pop vertices[vertindex]
|
||||
}
|
||||
|
||||
t->setVertexDataModified(0, stride * numvertices);
|
||||
t->flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -67,11 +67,13 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
|
||||
|
||||
size_t datasize = std::min(d->getSize(), vertcount * stride);
|
||||
char *bytedata = (char *) t->mapVertexData() + byteoffset;
|
||||
char *bytedata = (char *) t->getVertexData() + byteoffset;
|
||||
|
||||
memcpy(bytedata, d->getData(), datasize);
|
||||
|
||||
t->unmapVertexData(byteoffset, datasize);
|
||||
t->setVertexDataModified(byteoffset, datasize);
|
||||
t->flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -88,7 +90,7 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
for (const Buffer::DataMember &member : vertexformat)
|
||||
ncomponents += member.info.components;
|
||||
|
||||
char *data = (char *) t->mapVertexData() + byteoffset;
|
||||
char *data = (char *) t->getVertexData() + byteoffset;
|
||||
|
||||
for (int i = 0; i < vertcount; i++)
|
||||
{
|
||||
@@ -114,7 +116,9 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
data += stride;
|
||||
}
|
||||
|
||||
t->unmapVertexData(byteoffset, vertcount * stride);
|
||||
t->setVertexDataModified(byteoffset, vertcount * stride);
|
||||
t->flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -126,7 +130,10 @@ int w_Mesh_setVertex(lua_State *L)
|
||||
bool istable = lua_istable(L, 3);
|
||||
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
|
||||
char *data = nullptr;
|
||||
size_t offset = 0;
|
||||
luax_catchexcept(L, [&](){ data = (char *) t->checkVertexDataOffset(index, &offset); });
|
||||
|
||||
int idx = istable ? 1 : 3;
|
||||
|
||||
@@ -156,7 +163,7 @@ int w_Mesh_setVertex(lua_State *L)
|
||||
}
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertex(index, data, t->getVertexStride()); });
|
||||
t->setVertexDataModified(offset, t->getVertexStride());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -167,9 +174,8 @@ int w_Mesh_getVertex(lua_State *L)
|
||||
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
|
||||
luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); });
|
||||
const char *data = nullptr;
|
||||
luax_catchexcept(L, [&](){ data = (const char *) t->checkVertexDataOffset(index, nullptr); });
|
||||
|
||||
int n = 0;
|
||||
|
||||
@@ -195,13 +201,14 @@ int w_Mesh_setVertexAttribute(lua_State *L)
|
||||
|
||||
const Buffer::DataMember &member = vertexformat[attribindex];
|
||||
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
char *data = nullptr;
|
||||
size_t offset = 0;
|
||||
luax_catchexcept(L, [&](){ data = (char *) t->checkVertexDataOffset(vertindex, &offset); });
|
||||
|
||||
// Fetch the values from Lua and store them in the data buffer.
|
||||
luax_writebufferdata(L, 4, member.decl.format, data);
|
||||
luax_writebufferdata(L, 4, member.decl.format, data + member.offset);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
t->setVertexDataModified(offset + member.offset, member.size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -218,12 +225,10 @@ int w_Mesh_getVertexAttribute(lua_State *L)
|
||||
|
||||
const Buffer::DataMember &member = vertexformat[attribindex];
|
||||
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
const char *data = nullptr;
|
||||
luax_catchexcept(L, [&](){ data = (const char *) t->checkVertexDataOffset(vertindex, nullptr); });
|
||||
|
||||
luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
|
||||
luax_readbufferdata(L, member.decl.format, data);
|
||||
luax_readbufferdata(L, member.decl.format, data + member.offset);
|
||||
return member.info.components;
|
||||
}
|
||||
|
||||
@@ -290,17 +295,17 @@ int w_Mesh_attachAttribute(lua_State *L)
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
|
||||
Buffer *buffer = nullptr;
|
||||
Mesh *mesh = nullptr;
|
||||
if (luax_istype(L, 3, Buffer::type))
|
||||
{
|
||||
buffer = luax_checktype<Buffer>(L, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh *mesh = luax_checkmesh(L, 3);
|
||||
mesh = luax_checkmesh(L, 3);
|
||||
buffer = mesh->getVertexBuffer();
|
||||
if (buffer == nullptr)
|
||||
return luaL_error(L, "Mesh does not have its own vertex buffer.");
|
||||
luax_markdeprecated(L, "Mesh:attachAttribute(name, mesh, ...)", API_METHOD, DEPRECATED_REPLACED, "Mesh:attachAttribute(name, buffer, ...)");
|
||||
}
|
||||
|
||||
AttributeStep step = STEP_PER_VERTEX;
|
||||
@@ -310,7 +315,7 @@ int w_Mesh_attachAttribute(lua_State *L)
|
||||
|
||||
const char *attachname = luaL_optstring(L, 5, name);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, attachname, step); });
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh, attachname, step); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -218,20 +218,20 @@ int w_SpriteBatch_attachAttribute(lua_State *L)
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
|
||||
Buffer *buffer = nullptr;
|
||||
Mesh *mesh = nullptr;
|
||||
if (luax_istype(L, 3, Buffer::type))
|
||||
{
|
||||
buffer = luax_checktype<Buffer>(L, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh *mesh = luax_checktype<Mesh>(L, 3);
|
||||
mesh = luax_checktype<Mesh>(L, 3);
|
||||
buffer = mesh->getVertexBuffer();
|
||||
if (buffer == nullptr)
|
||||
return luaL_error(L, "Mesh does not have its own vertex buffer.");
|
||||
luax_markdeprecated(L, "SpriteBatch:attachAttribute(name, mesh)", API_METHOD, DEPRECATED_REPLACED, "SpriteBatch:attachAttribute(name, buffer)");
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer); });
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user