Merge branch '12.0-development' into metal

This commit is contained in:
Alex Szpakowski
2020-12-22 22:37:17 -04:00
288 changed files with 15292 additions and 12550 deletions
+6
View File
@@ -291,6 +291,12 @@ public:
**/
static bool setMixWithSystem(bool mix);
/**
* Pause/resume audio context
*/
virtual void pauseContext() = 0;
virtual void resumeContext() = 0;
private:
static StringMap<DistanceModel, DISTANCE_MAX_ENUM>::Entry distanceModelEntries[];
+9
View File
@@ -203,6 +203,15 @@ bool Audio::isEFXsupported() const
return false;
}
void Audio::pauseContext()
{
}
void Audio::resumeContext()
{
}
} // null
} // audio
} // love
+3
View File
@@ -86,6 +86,9 @@ public:
int getMaxSourceEffects() const;
bool isEFXsupported() const;
void pauseContext();
void resumeContext();
private:
float volume;
DistanceModel distanceModel;
+23
View File
@@ -26,6 +26,10 @@
#include <cstdlib>
#include <iostream>
#ifdef LOVE_IOS
#include "common/ios.h"
#endif
namespace love
{
namespace audio
@@ -189,10 +193,18 @@ Audio::Audio()
poolThread = new PoolThread(pool);
poolThread->start();
#ifdef LOVE_IOS
love::ios::initAudioSessionInterruptionHandler();
#endif
}
Audio::~Audio()
{
#ifdef LOVE_IOS
love::ios::destroyAudioSessionInterruptionHandler();
#endif
poolThread->setFinish();
poolThread->wait();
@@ -293,6 +305,17 @@ std::vector<love::audio::Source*> Audio::pause()
return Source::pause(pool);
}
void Audio::pauseContext()
{
alcMakeContextCurrent(nullptr);
}
void Audio::resumeContext()
{
if (context && alcGetCurrentContext() != context)
alcMakeContextCurrent(context);
}
void Audio::setVolume(float volume)
{
alListenerf(AL_GAIN, volume);
+2
View File
@@ -95,6 +95,8 @@ public:
void pause(love::audio::Source *source);
void pause(const std::vector<love::audio::Source*> &sources);
std::vector<love::audio::Source*> pause();
void pauseContext();
void resumeContext();
void setVolume(float volume);
float getVolume() const;
-1
View File
@@ -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)
+11 -32
View File
@@ -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
@@ -95,13 +93,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)
{}
};
@@ -112,7 +110,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; }
@@ -122,35 +119,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.
@@ -166,14 +149,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;
@@ -195,8 +176,6 @@ protected:
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
BufferUsage usage;
uint32 mapFlags;
bool mapped;
+6 -1
View File
@@ -135,6 +135,11 @@ Graphics *Graphics::createInstance(const std::vector<Renderer> &renderers)
return instance;
}
Graphics::DisplayState::DisplayState()
{
defaultSamplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_LINEAR;
}
Graphics::Graphics()
: width(0)
, height(0)
@@ -202,7 +207,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);
+7 -1
View File
@@ -472,7 +472,7 @@ public:
* @param width The viewport width.
* @param height The viewport height.
**/
virtual bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) = 0;
virtual bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) = 0;
/**
* Un-sets the current graphics display mode (uninitializing objects if
@@ -507,6 +507,9 @@ public:
double getCurrentDPIScale() const;
double getScreenDPIScale() const;
virtual int getRequestedBackbufferMSAA() const = 0;
virtual int getBackbufferMSAA() const = 0;
/**
* Sets the current constant color.
**/
@@ -874,6 +877,8 @@ protected:
struct DisplayState
{
DisplayState();
Colorf color = Colorf(1.0, 1.0, 1.0, 1.0);
Colorf backgroundColor = Colorf(0.0, 0.0, 0.0, 1.0);
@@ -905,6 +910,7 @@ protected:
bool wireframe = false;
// Default mipmap filter is set in the DisplayState constructor.
SamplerState defaultSamplerState = SamplerState();
};
+84 -154
View File
@@ -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
View File
@@ -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;
+3 -3
View File
@@ -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);
+51 -37
View File
@@ -47,6 +47,8 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage
, color(255, 255, 255, 255)
, colorf(1.0f, 1.0f, 1.0f, 1.0f)
, array_buf(nullptr)
, vertex_data(nullptr)
, modified_sprites()
, range_start(-1)
, range_count(-1)
{
@@ -64,14 +66,22 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage
vertex_stride = getFormatStride(vertex_format);
size_t vertex_size = vertex_stride * 4 * size;
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, usage);
vertex_data = (uint8 *) malloc(vertex_size);
if (vertex_data == nullptr)
throw love::Exception("Out of memory.");
memset(vertex_data, 0, vertex_size);
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, usage);
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0);
array_buf.set(gfx->newBuffer(settings, decl, nullptr, vertex_size, 0), Acquire::NORETAIN);
}
SpriteBatch::~SpriteBatch()
{
delete array_buf;
free(vertex_data);
}
int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
@@ -93,9 +103,10 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
const Vector2 *quadpositions = quad->getVertexPositions();
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
// Always keep the buffer mapped when adding data (it'll be unmapped on draw.)
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
auto verts = (XYf_STf_RGBAub *) ((uint8 *) array_buf->map() + offset);
int spriteindex = (index == -1 ? next : index);
size_t offset = spriteindex * vertex_stride * 4;
auto verts = (XYf_STf_RGBAub *) (vertex_data + offset);
m.transformXY(verts, quadpositions, 4);
@@ -106,7 +117,7 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
verts[i].color = color;
}
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
modified_sprites.encapsulate(spriteindex);
// Increment counter.
if (index == -1)
@@ -137,9 +148,10 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
const Vector2 *quadpositions = quad->getVertexPositions();
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
// Always keep the buffer mapped when adding data (it'll be unmapped on draw.)
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
auto verts = (XYf_STPf_RGBAub *) ((uint8 *) array_buf->map() + offset);
int spriteindex = (index == -1 ? next : index);
size_t offset = spriteindex * vertex_stride * 4;
auto verts = (XYf_STPf_RGBAub *) (vertex_data + offset);
m.transformXY(verts, quadpositions, 4);
@@ -151,7 +163,7 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
verts[i].color = color;
}
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
modified_sprites.encapsulate(spriteindex);
// Increment counter.
if (index == -1)
@@ -168,7 +180,18 @@ void SpriteBatch::clear()
void SpriteBatch::flush()
{
array_buf->unmap();
if (modified_sprites.isValid())
{
size_t offset = modified_sprites.getOffset() * vertex_stride * 4;
size_t size = modified_sprites.getSize() * vertex_stride * 4;
if (array_buf->getUsage() == BUFFERUSAGE_STREAM)
array_buf->fill(0, array_buf->getSize(), vertex_data);
else
array_buf->fill(offset, size, vertex_data + offset);
modified_sprites.invalidate();
}
}
void SpriteBatch::setTexture(Texture *newtexture)
@@ -213,33 +236,24 @@ void SpriteBatch::setBufferSize(int newsize)
return;
size_t vertex_size = vertex_stride * 4 * newsize;
love::graphics::Buffer *new_array_buf = nullptr;
int new_next = std::min(next, newsize);
try
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getMapFlags(), array_buf->getUsage());
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
new_array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0);
void *new_vertex_data = realloc(vertex_data, vertex_size);
if (new_vertex_data == nullptr)
throw love::Exception("Out of memory.");
// Copy as much of the old data into the new GLBuffer as can fit.
size_t copy_size = vertex_stride * 4 * new_next;
array_buf->copyTo(0, copy_size, new_array_buf, 0);
}
catch (love::Exception &)
{
delete new_array_buf;
throw;
}
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getUsage());
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
// We don't need to unmap the old GLBuffer since we're deleting it.
delete array_buf;
array_buf.set(gfx->newBuffer(settings, decl, nullptr, vertex_size, 0), Acquire::NORETAIN);
array_buf->fill(0, vertex_stride * 4 * new_next, new_vertex_data);
vertex_data = (uint8 *) new_vertex_data;
array_buf = new_array_buf;
size = newsize;
next = new_next;
}
@@ -248,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.");
@@ -269,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;
}
@@ -319,8 +334,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
Shader::current->checkMainTexture(texture);
}
// Make sure the buffer isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap();
flush(); // Upload any modified sprite data to the GPU.
VertexAttributes attributes;
BufferBindings buffers;
@@ -353,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);
+12 -4
View File
@@ -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;
};
@@ -130,8 +135,11 @@ private:
CommonFormat vertex_format;
size_t vertex_stride;
love::graphics::Buffer *array_buf;
StrongRef<love::graphics::Buffer> array_buf;
uint8 *vertex_data;
Range modified_sprites;
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
+80 -60
View File
@@ -33,17 +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)
, vertex_buffer(nullptr)
, vert_offset(0)
, texture_cache_id((uint32) -1)
, vertexData(nullptr)
, modifiedVertices()
, vertOffset(0)
, textureCacheID((uint32) -1)
{
set(text);
}
Text::~Text()
{
if (vertex_buffer)
vertex_buffer->release();
if (vertexData != nullptr)
free(vertexData);
}
void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
@@ -52,33 +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->release();
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);
}
}
@@ -86,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();
}
@@ -196,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)
@@ -213,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();
}
@@ -225,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();
@@ -258,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);
}
+11 -8
View File
@@ -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;
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
+26 -2
View File
@@ -173,7 +173,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
, mipmapCount(1)
, pixelWidth(0)
, pixelHeight(0)
, requestedMSAA(settings.msaa)
, requestedMSAA(settings.msaa > 1 ? settings.msaa : 0)
, samplerState()
, graphicsMemorySize(0)
, usingDefaultTexture(false)
@@ -231,6 +231,9 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
if (mipmapsMode != MIPMAPS_NONE)
mipmapCount = getTotalMipmapCount(pixelWidth, pixelHeight, depth);
if (mipmapsMode == MIPMAPS_AUTO && isPixelFormatDepthStencil(format))
throw love::Exception("Automatic mipmap generation cannot be used for depth/stencil textures.");
if (pixelWidth <= 0 || pixelHeight <= 0 || layers <= 0 || depth <= 0)
throw love::Exception("Texture dimensions must be greater than 0.");
@@ -279,6 +282,9 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
samplerState = gfx->getDefaultSamplerState();
if (getMipmapCount() == 1)
samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
Quad::Viewport v = {0, 0, (double) width, (double) height};
quad.set(new Quad(v, width, height), Acquire::NORETAIN);
@@ -485,6 +491,20 @@ void Texture::replacePixels(const void *data, size_t size, int slice, int mipmap
generateMipmaps();
}
void Texture::generateMipmaps()
{
if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
if (isPixelFormatCompressed(format))
throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
if (isPixelFormatDepthStencil(format))
throw love::Exception("generateMipmaps cannot be called on a depth/stencil Texture.");
generateMipmapsInternal();
}
love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
{
if (!isReadable())
@@ -519,7 +539,11 @@ love::image::ImageData *Texture::newImageData(love::image::Image *module, int sl
throw love::Exception("ImageData with the '%s' pixel format is not supported.", formatname);
}
return module->newImageData(r.w, r.h, dataformat);
auto imagedata = module->newImageData(r.w, r.h, dataformat);
readbackImageData(imagedata, slice, mipmap, r);
return imagedata;
}
TextureType Texture::getTextureType() const
+5 -2
View File
@@ -218,9 +218,9 @@ public:
void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, int x, int y, bool reloadmipmaps);
void replacePixels(const void *data, size_t size, int slice, int mipmap, const Rect &rect, bool reloadmipmaps);
virtual void generateMipmaps() = 0;
void generateMipmaps();
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
virtual ptrdiff_t getRenderTargetHandle() const = 0;
@@ -278,6 +278,9 @@ protected:
void uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y);
virtual void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) = 0;
virtual void generateMipmapsInternal() = 0;
virtual void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) = 0;
bool validateDimensions(bool throwException) const;
TextureType texType;
+6 -7
View File
@@ -22,6 +22,7 @@
#include "graphics/Buffer.h"
#include "Metal.h"
#include "common/Range.h"
namespace love
{
@@ -37,23 +38,21 @@ public:
Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
virtual ~Buffer();
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 (ptrdiff_t) buffer; }
ptrdiff_t getTexelBufferHandle() const override { return (ptrdiff_t) texture; }
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
private:
id<MTLBuffer> buffer;
id<MTLTexture> texture;
char *memoryMap;
id<MTLBuffer> mapBuffer;
NSRange mappedRange;
Range mappedRange;
}; // Buffer
+78 -32
View File
@@ -19,6 +19,7 @@
**/
#import "Buffer.h"
#include "Graphics.h"
namespace love
{
@@ -58,12 +59,13 @@ static MTLPixelFormat getMTLPixelFormat(DataFormat format)
Buffer::Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
: love::graphics::Buffer(gfx, settings, format, size, arraylength)
, texture(nil)
, mapBuffer(nil)
, mappedRange()
{ @autoreleasepool {
size = getSize();
arraylength = getArrayLength();
MTLResourceOptions opts = MTLResourceStorageModeManaged;
MTLResourceOptions opts = MTLResourceStorageModePrivate;
buffer = [device newBufferWithLength:size options:opts];
if (buffer == nil)
@@ -71,25 +73,22 @@ Buffer::Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settin
if (typeFlags & TYPEFLAG_TEXEL)
{
MTLPixelFormat pixformat = getMTLPixelFormat(getDataMember(0).decl.format);
auto desc = [MTLTextureDescriptor textureBufferDescriptorWithPixelFormat:pixformat
width:size
resourceOptions:opts
usage:MTLTextureUsageShaderRead];
texture = [buffer newTextureWithDescriptor:desc offset:0 bytesPerRow:size];
if (@available(iOS 12, macOS 10.14, *))
{
MTLPixelFormat pixformat = getMTLPixelFormat(getDataMember(0).decl.format);
auto desc = [MTLTextureDescriptor textureBufferDescriptorWithPixelFormat:pixformat
width:size
resourceOptions:opts
usage:MTLTextureUsageShaderRead];
texture = [buffer newTextureWithDescriptor:desc offset:0 bytesPerRow:size];
}
if (texture == nil)
throw love::Exception("Could not create Metal texel buffer.");
}
// TODO: synchronization etc
memoryMap = (char *) buffer.contents;
if (data != nullptr)
{
memcpy(map(), data, size);
unmap();
}
fill(0, size, data);
}}
Buffer::~Buffer()
@@ -98,30 +97,77 @@ Buffer::~Buffer()
texture = nil;
}}
void *Buffer::map()
{
return memoryMap;
}
void Buffer::unmap()
void *Buffer::map(MapType /*map*/, size_t offset, size_t size)
{ @autoreleasepool {
[buffer didModifyRange:{0, size}];
if (size == 0)
return nullptr;
Range r(offset, size);
if (!Range(0, getSize()).contains(r))
return nullptr;
auto gfx = Graphics::getInstance();
// TODO: Don't create a new buffer every time, also do something for stream
// buffers.
mapBuffer = [gfx->device newBufferWithLength:size options:MTLResourceStorageModeShared];
if (mapBuffer != nil)
{
mappedRange = r;
return mapBuffer.contents;
}
return nullptr;
}}
void Buffer::setMappedRangeModified(size_t offset, size_t size)
{
mappedRange = NSIntersectionRange(mappedRange, {offset, size});
}
void Buffer::unmap(size_t usedoffset, size_t usedsize)
{ @autoreleasepool {
if (mapBuffer == nil)
return;
Range r(usedoffset, usedsize);
if (!mapped || !mappedRange.contains(r))
return;
auto gfx = Graphics::getInstance();
auto encoder = gfx->useBlitEncoder();
[encoder copyFromBuffer:mapBuffer
sourceOffset:(usedoffset - mappedRange.getOffset())
toBuffer:buffer
destinationOffset:usedoffset
size:usedsize];
mapBuffer = nil;
}}
void Buffer::fill(size_t offset, size_t size, const void *data)
{
// TODO
}
{ @autoreleasepool {
if (size == 0)
return;
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
{
// TODO
}
size_t buffersize = getSize();
if (!Range(0, buffersize).contains(Range(offset, size)))
return;
// TODO: Don't create a new buffer every time, also do something for stream
// buffers.
auto gfx = Graphics::getInstance();
auto encoder = gfx->useBlitEncoder();
auto tempbuffer = [gfx->device newBufferWithLength:size options:MTLResourceStorageModeShared];
memcpy(tempbuffer.contents, data, size);
[encoder copyFromBuffer:tempbuffer
sourceOffset:0
toBuffer:buffer
destinationOffset:offset
size:size];
}}
} // metal
} // graphics
+4 -1
View File
@@ -49,7 +49,7 @@ public:
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) override;
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
void unSetMode() override;
void setActive(bool active) override;
@@ -65,6 +65,9 @@ public:
void present(void *screenshotCallbackData) override;
int getRequestedBackbufferMSAA() const override { return 0; } // TODO
int getBackbufferMSAA() const override { return 0; } // TODO
void setColor(Colorf c) override;
void setScissor(const Rect &rect) override;
+4 -3
View File
@@ -188,7 +188,8 @@ Graphics::Graphics()
{0.0f, 0.0f, 0.0f, 1.0f},
{0, 0, 0, 1},
};
Buffer::Settings attribsettings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STATIC);
Buffer::Settings attribsettings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_STATIC);
defaultAttributesBuffer = newBuffer(attribsettings, dataformat, &defaults, sizeof(DefaultVertexAttributes), 0);
}
@@ -288,13 +289,13 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
}
}
bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth)
bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
{ @autoreleasepool {
this->width = width;
this->height = height;
this->metalLayer = (__bridge CAMetalLayer *) context;
this->windowHasStencil = backbufferstencil;
this->windowHasStencil = windowhasstencil;
metalLayer.device = device;
metalLayer.pixelFormat = isGammaCorrect() ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
+2 -2
View File
@@ -40,8 +40,6 @@ public:
Texture(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const Slices *data);
virtual ~Texture();
void generateMipmaps() override;
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
void setSamplerState(const SamplerState &s) override;
ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
@@ -54,6 +52,8 @@ public:
private:
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
void generateMipmapsInternal() override;
void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) override;
id<MTLTexture> texture;
id<MTLTexture> msaaTexture;
+2 -3
View File
@@ -197,7 +197,7 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
options:options];
}}
void Texture::generateMipmaps()
void Texture::generateMipmapsInternal()
{ @autoreleasepool {
// TODO: alternate method for non-color-renderable and non-filterable
// pixel formats.
@@ -205,10 +205,9 @@ void Texture::generateMipmaps()
[encoder generateMipmapsForTexture:texture];
}}
love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect)
void Texture::readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect)
{
// TODO
return nullptr;
}
void Texture::setSamplerState(const SamplerState &s)
+90 -120
View File
@@ -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
+6 -9
View File
@@ -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
+166 -39
View File
@@ -107,11 +107,24 @@ love::graphics::Graphics *createInstance()
Graphics::Graphics()
: windowHasStencil(false)
, 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,21 +134,22 @@ Graphics::Graphics()
if (window->isOpen())
{
int w, h;
love::window::WindowSettings settings;
window->getWindow(w, h, settings);
love::window::WindowSettings s;
window->getWindow(w, h, s);
double dpiW = w;
double dpiH = h;
window->windowToDPICoords(&dpiW, &dpiH);
void *context = nullptr; // TODO
setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil, settings.depth);
setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), s.stencil, s.msaa);
}
}
}
Graphics::~Graphics()
{
delete[] bufferMapMemory;
}
const char *Graphics::getName() const
@@ -188,14 +202,94 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
// Set up the projection matrix
projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f);
}
updateBackbuffer(width, height, pixelwidth, pixelheight, requestedBackbufferMSAA);
}
bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int /*backbufferdepth*/)
void Graphics::updateBackbuffer(int width, int height, int /*pixelwidth*/, int pixelheight, int msaa)
{
bool useinternalbackbuffer = false;
if (msaa > 1)
useinternalbackbuffer = true;
// Our internal backbuffer code needs glBlitFramebuffer.
if (!(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_ES_VERSION_3_0
|| GLAD_EXT_framebuffer_blit || GLAD_ANGLE_framebuffer_blit || GLAD_NV_framebuffer_blit))
{
if (!(msaa > 1 && GLAD_APPLE_framebuffer_multisample))
useinternalbackbuffer = false;
}
GLuint prevFBO = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
bool restoreFBO = prevFBO != getInternalBackbufferFBO();
if (useinternalbackbuffer)
{
Texture::Settings settings;
settings.width = width;
settings.height = height;
settings.dpiScale = (float)pixelheight / (float)height;
settings.msaa = msaa;
settings.renderTarget = true;
settings.readable.set(false);
settings.format = isGammaCorrect() ? PIXELFORMAT_RGBA8_UNORM_sRGB : PIXELFORMAT_RGBA8_UNORM;
internalBackbuffer.set(newTexture(settings), Acquire::NORETAIN);
settings.format = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
internalBackbufferDepthStencil.set(newTexture(settings), Acquire::NORETAIN);
RenderTargets rts;
rts.colors.push_back(internalBackbuffer.get());
rts.depthStencil.texture = internalBackbufferDepthStencil;
internalBackbufferFBO = bindCachedFBO(rts);
}
else
{
internalBackbuffer.set(nullptr);
internalBackbufferDepthStencil.set(nullptr);
internalBackbufferFBO = 0;
}
requestedBackbufferMSAA = msaa;
if (restoreFBO)
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, prevFBO);
}
GLuint Graphics::getInternalBackbufferFBO() const
{
if (internalBackbufferFBO != 0)
return internalBackbufferFBO;
else
return getSystemBackbufferFBO();
}
GLuint Graphics::getSystemBackbufferFBO() const
{
#ifdef LOVE_IOS
// Hack: iOS uses a custom FBO.
SDL_SysWMinfo info = {};
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
if (info.info.uikit.resolveFramebuffer != 0)
return info.info.uikit.resolveFramebuffer;
else
return info.info.uikit.framebuffer;
#else
return 0;
#endif
}
bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
{
this->width = width;
this->height = height;
this->windowHasStencil = backbufferstencil;
this->windowHasStencil = windowhasstencil;
this->requestedBackbufferMSAA = msaa;
// Okay, setup OpenGL.
gl.initContext();
@@ -211,8 +305,6 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
created = true;
initCapabilities();
setViewportSize(width, height, pixelwidth, pixelheight);
// Enable blending
gl.setEnableState(OpenGL::ENABLE_BLEND, true);
@@ -252,6 +344,8 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
setDebug(isDebugEnabled());
setViewportSize(width, height, pixelwidth, pixelheight);
if (batchedDrawState.vb[0] == nullptr)
{
// Initial sizes that should be good enough for most cases. It will
@@ -263,7 +357,7 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
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};
@@ -335,6 +429,9 @@ void Graphics::unSetMode()
flushBatchedDraws();
internalBackbuffer.set(nullptr);
internalBackbufferDepthStencil.set(nullptr);
// Unload all volatile objects. These must be reloaded after the display
// mode change.
Volatile::unloadAll();
@@ -552,7 +649,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
if (iswindow)
{
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
// The projection matrix is flipped compared to rendering to a texture,
// due to OpenGL considering (0,0) bottom-left instead of top-left.
@@ -594,6 +691,8 @@ void Graphics::endPass()
// Discard the depth/stencil buffer if we're using an internal cached one.
if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
discard({}, true);
else if (!rts.getFirstTarget().texture.get())
discard({}, true); // Backbuffer
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
// don't have to worry about resolving to slices.
@@ -646,15 +745,12 @@ void Graphics::endPass()
}
}
// generateMipmaps can't be used for depth/stencil textures.
for (const auto &rt : rts.colors)
{
if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0)
rt.texture->generateMipmaps();
}
int dsmipmap = rts.depthStencil.mipmap;
if (depthstencil != nullptr && depthstencil->getMipmapsMode() == Texture::MIPMAPS_AUTO && dsmipmap == 0)
depthstencil->generateMipmaps();
}
void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
@@ -812,7 +908,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
attachments.reserve(colorbuffers.size());
// glDiscardFramebuffer uses different attachment enums for the default FBO.
if (!isRenderTargetActive() && gl.getDefaultFBO() == 0)
if (!isRenderTargetActive() && getInternalBackbufferFBO() == 0)
{
if (colorbuffers.size() > 0 && colorbuffers[0])
attachments.push_back(GL_COLOR);
@@ -879,7 +975,7 @@ void Graphics::cleanupRenderTexture(love::graphics::Texture *texture)
}
}
void Graphics::bindCachedFBO(const RenderTargets &targets)
GLuint Graphics::bindCachedFBO(const RenderTargets &targets)
{
GLuint fbo = framebufferObjects[targets];
@@ -962,6 +1058,8 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
framebufferObjects[targets] = fbo;
}
return fbo;
}
void Graphics::present(void *screenshotCallbackData)
@@ -977,13 +1075,34 @@ void Graphics::present(void *screenshotCallbackData)
flushBatchedDraws();
endPass();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
int w = getPixelWidth();
int h = getPixelHeight();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
// Copy internal backbuffer to system backbuffer. When MSAA is used this
// is a direct MSAA resolve.
if (internalBackbuffer.get())
{
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, getSystemBackbufferFBO());
// Discard system backbuffer to prevent it from copying its contents
// from VRAM to chip memory.
discard(OpenGL::FRAMEBUFFER_DRAW, {true}, true);
// updateBackbuffer checks for glBlitFramebuffer support.
if (GLAD_APPLE_framebuffer_multisample && internalBackbuffer->getMSAA() > 1)
glResolveMultisampleFramebufferAPPLE();
else
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Discarding the internal backbuffer directly after resolving it should
// eliminate any copy back to vram it might need to do.
discard(OpenGL::FRAMEBUFFER_READ, {true}, false);
}
if (!pendingScreenshotCallbacks.empty())
{
int w = getPixelWidth();
int h = getPixelHeight();
size_t row = 4 * w;
size_t size = row * h;
@@ -1002,26 +1121,7 @@ void Graphics::present(void *screenshotCallbackData)
throw love::Exception("Out of memory.");
}
#ifdef LOVE_IOS
SDL_SysWMinfo info = {};
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
if (info.info.uikit.resolveFramebuffer != 0)
{
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, info.info.uikit.resolveFramebuffer);
// We need to do an explicit MSAA resolve on iOS, because it uses
// GLES FBOs rather than a system framebuffer.
if (GLAD_ES_VERSION_3_0)
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
else if (GLAD_APPLE_framebuffer_multisample)
glResolveMultisampleFramebufferAPPLE();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_READ, info.info.uikit.resolveFramebuffer);
}
#endif
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getSystemBackbufferFBO());
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Replace alpha values with full opacity.
@@ -1085,6 +1185,8 @@ void Graphics::present(void *screenshotCallbackData)
if (window != nullptr)
window->swapBuffers();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
// Reset the per-frame stat counts.
drawCalls = 0;
gl.stats.shaderSwitches = 0;
@@ -1105,6 +1207,16 @@ void Graphics::present(void *screenshotCallbackData)
}
}
int Graphics::getRequestedBackbufferMSAA() const
{
return requestedBackbufferMSAA;
}
int Graphics::getBackbufferMSAA() const
{
return internalBackbuffer.get() ? internalBackbuffer->getMSAA() : 0;
}
void Graphics::setScissor(const Rect &rect)
{
flushBatchedDraws();
@@ -1354,6 +1466,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 RENDERER_OPENGL;
+20 -2
View File
@@ -63,7 +63,7 @@ public:
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) override;
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
void unSetMode() override;
void setActive(bool active) override;
@@ -79,6 +79,9 @@ public:
void present(void *screenshotCallbackData) override;
int getRequestedBackbufferMSAA() const override;
int getBackbufferMSAA() const override;
void setColor(Colorf c) override;
void setScissor(const Rect &rect) override;
@@ -110,6 +113,9 @@ public:
// Internal use.
void cleanupRenderTexture(love::graphics::Texture *texture);
void *getBufferMapMemory(size_t size);
void releaseBufferMapMemory(void *mem);
private:
struct CachedFBOHasher
@@ -139,15 +145,27 @@ private:
void getAPIStats(int &shaderswitches) const override;
void endPass();
void bindCachedFBO(const RenderTargets &targets);
GLuint bindCachedFBO(const RenderTargets &targets);
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
void updateBackbuffer(int width, int height, int pixelwidth, int pixelheight, int msaa);
GLuint getInternalBackbufferFBO() const;
GLuint getSystemBackbufferFBO() const;
void setDebug(bool enable);
std::unordered_map<RenderTargets, GLuint, CachedFBOHasher> framebufferObjects;
bool windowHasStencil;
GLuint mainVAO;
StrongRef<love::graphics::Texture> internalBackbuffer;
StrongRef<love::graphics::Texture> internalBackbufferDepthStencil;
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];
+1 -30
View File
@@ -752,30 +752,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;
@@ -907,13 +885,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
-2
View File
@@ -116,8 +116,6 @@ private:
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
std::vector<Buffer *> buffersToUnmap;
float lastPointSize;
}; // Shader
+1 -1
View File
@@ -499,7 +499,7 @@ love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size)
{
// AMD's pinned memory seems to be faster than persistent mapping,
// on AMD GPUs.
if (GLAD_AMD_pinned_memory)
if (GLAD_AMD_pinned_memory && gl.getVendor() == OpenGL::VENDOR_AMD)
{
try
{
+7 -14
View File
@@ -218,6 +218,10 @@ Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const
if (textureGLError != GL_NO_ERROR)
throw love::Exception("Cannot create Texture (OpenGL error: %s)", OpenGL::errorString(textureGLError));
}
// ImageData is referenced by the first loadVolatile call, but we don't
// hang on to it after that so we can save memory.
slices.clear();
}
Texture::~Texture()
@@ -480,14 +484,8 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
}
}
void Texture::generateMipmaps()
void Texture::generateMipmapsInternal()
{
if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
if (isPixelFormatCompressed(format))
throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
gl.bindTextureToUnit(this, 0, false);
GLenum gltextype = OpenGL::getGLTextureType(texType);
@@ -498,13 +496,10 @@ void Texture::generateMipmaps()
glGenerateMipmap(gltextype);
}
love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
void Texture::readbackImageData(love::image::ImageData *data, int slice, int mipmap, const Rect &r)
{
// Base class does validation (only RTs allowed, etc) and creates ImageData.
love::image::ImageData *data = love::graphics::Texture::newImageData(module, slice, mipmap, r);
if (fbo == 0) // Should never be reached.
return data;
return;
bool isSRGB = false;
OpenGL::TextureFormat fmt = gl.convertPixelFormat(data->getFormat(), false, isSRGB);
@@ -525,8 +520,6 @@ love::image::ImageData *Texture::newImageData(love::image::Image *module, int sl
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
return data;
}
void Texture::setSamplerState(const SamplerState &s)
+4 -2
View File
@@ -46,8 +46,6 @@ public:
bool loadVolatile() override;
void unloadVolatile() override;
void generateMipmaps() override;
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
void setSamplerState(const SamplerState &s) override;
ptrdiff_t getHandle() const override;
@@ -62,6 +60,10 @@ private:
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
void generateMipmapsInternal() override;
void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) override;
Slices slices;
GLuint fbo;
+3 -16
View File
@@ -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 },
+15 -13
View File
@@ -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();
}
+26 -21
View File
@@ -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;
}
+3 -3
View File
@@ -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;
}
+16
View File
@@ -65,6 +65,11 @@
# include "graphics/Graphics.h"
#endif
// For love::window::setHighDPIAllowed
#ifdef LOVE_ENABLE_WINDOW
# include "window/Window.h"
#endif
// For love::audio::Audio::setMixWithSystem.
#ifdef LOVE_ENABLE_AUDIO
# include "audio/Audio.h"
@@ -318,6 +323,14 @@ static int w__setGammaCorrect(lua_State *L)
return 0;
}
static int w__setHighDPIAllowed(lua_State *L)
{
#ifdef LOVE_ENABLE_WINDOW
love::window::setHighDPIAllowed((bool) lua_toboolean(L, 1));
#endif
return 0;
}
static int w__setAudioMixWithSystem(lua_State *L)
{
bool success = false;
@@ -395,6 +408,9 @@ int luaopen_love(lua_State *L)
lua_pushcfunction(L, w__setGammaCorrect);
lua_setfield(L, -2, "_setGammaCorrect");
lua_pushcfunction(L, w__setHighDPIAllowed);
lua_setfield(L, -2, "_setHighDPIAllowed");
// Exposed here because we need to be able to call it before the audio
// module is initialized.
lua_pushcfunction(L, w__setAudioMixWithSystem);
+6 -6
View File
@@ -45,7 +45,7 @@ Body::Body(World *world, b2Vec2 p, Body::Type type)
udata->ref = nullptr;
b2BodyDef def;
def.position = Physics::scaleDown(p);
def.userData = (void *) udata;
def.userData.pointer = (uintptr_t)udata;
body = world->world->CreateBody(&def);
// Box2D body holds a reference to the love Body.
this->retain();
@@ -385,9 +385,9 @@ void Body::setBullet(bool bullet)
return body->SetBullet(bullet);
}
bool Body::isActive() const
bool Body::isEnabled() const
{
return body->IsActive();
return body->IsEnabled();
}
bool Body::isAwake() const
@@ -405,9 +405,9 @@ bool Body::isSleepingAllowed() const
return body->IsSleepingAllowed();
}
void Body::setActive(bool active)
void Body::setEnabled(bool enabled)
{
body->SetActive(active);
body->SetEnabled(enabled);
}
void Body::setAwake(bool awake)
@@ -544,7 +544,7 @@ int Body::setUserData(lua_State *L)
if (udata == nullptr)
{
udata = new bodyudata();
body->SetUserData((void *) udata);
body->GetUserData().pointer = (uintptr_t)udata;
}
if(!udata->ref)
+3 -3
View File
@@ -28,7 +28,7 @@
#include "physics/Body.h"
// Box2D
#include <Box2D/Box2D.h>
#include <box2d/Box2D.h>
namespace love
{
@@ -360,7 +360,7 @@ public:
* Checks whether a Body is active or not. An inactive body
* cannot be interacted with.
**/
bool isActive() const;
bool isEnabled() const;
/**
* Checks whether a Body is awake or not. A Body
@@ -377,7 +377,7 @@ public:
/**
* Changes the body's active state.
**/
void setActive(bool active);
void setEnabled(bool enabled);
/**
* Changes the body's sleep state.
+6 -34
View File
@@ -47,56 +47,28 @@ void ChainShape::setNextVertex(float x, float y)
{
b2Vec2 v(x, y);
b2ChainShape *c = (b2ChainShape *)shape;
c->SetNextVertex(Physics::scaleDown(v));
}
void ChainShape::setNextVertex()
{
b2ChainShape *c = (b2ChainShape *)shape;
c->m_hasNextVertex = false;
c->m_nextVertex = Physics::scaleDown(v);
}
void ChainShape::setPreviousVertex(float x, float y)
{
b2Vec2 v(x, y);
b2ChainShape *c = (b2ChainShape *)shape;
c->SetPrevVertex(Physics::scaleDown(v));
c->m_prevVertex = Physics::scaleDown(v);
}
void ChainShape::setPreviousVertex()
{
b2ChainShape *c = (b2ChainShape *)shape;
c->m_hasPrevVertex = false;
}
bool ChainShape::getNextVertex(float &x, float &y) const
b2Vec2 ChainShape::getNextVertex() const
{
b2ChainShape *c = (b2ChainShape *)shape;
if (c->m_hasNextVertex)
{
b2Vec2 v = Physics::scaleUp(c->m_nextVertex);
x = v.x;
y = v.y;
return true;
}
return false;
return Physics::scaleUp(c->m_nextVertex);
}
bool ChainShape::getPreviousVertex(float &x, float &y) const
b2Vec2 ChainShape::getPreviousVertex() const
{
b2ChainShape *c = (b2ChainShape *)shape;
if (c->m_hasPrevVertex)
{
b2Vec2 v = Physics::scaleUp(c->m_prevVertex);
x = v.x;
y = v.y;
return true;
}
return false;
return Physics::scaleUp(c->m_prevVertex);
}
EdgeShape *ChainShape::getChildEdge(int index) const
+2 -4
View File
@@ -56,7 +56,6 @@ public:
* @param y The y-coordinate of the vertex.
**/
void setNextVertex(float x, float y);
void setNextVertex();
/**
* Establish connectivity to a vertex that precedes
@@ -65,17 +64,16 @@ public:
* @param y The y-coordinate of the vertex.
**/
void setPreviousVertex(float x, float y);
void setPreviousVertex();
/**
* Gets the vertex that follows the last vertex.
**/
bool getNextVertex(float &x, float &y) const;
b2Vec2 getNextVertex() const;
/**
* Gets the vertex that precedes the first vertex.
**/
bool getPreviousVertex(float &x, float &y) const;
b2Vec2 getPreviousVertex() const;
/**
* Returns a child EdgeShape.
+1 -1
View File
@@ -27,7 +27,7 @@
#include "World.h"
// Box2D
#include <Box2D/Box2D.h>
#include <box2d/Box2D.h>
namespace love
{
+33 -5
View File
@@ -58,22 +58,50 @@ float DistanceJoint::getLength() const
void DistanceJoint::setFrequency(float hz)
{
joint->SetFrequency(hz);
float stiffness, damping;
b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
joint->SetStiffness(stiffness);
}
float DistanceJoint::getFrequency() const
{
return joint->GetFrequency();
float frequency, ratio;
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return frequency;
}
void DistanceJoint::setDampingRatio(float d)
void DistanceJoint::setDampingRatio(float ratio)
{
joint->SetDampingRatio(d);
float stiffness, damping;
b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
joint->SetDamping(damping);
}
float DistanceJoint::getDampingRatio() const
{
return joint->GetDampingRatio();
float frequency, ratio;
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return ratio;
}
void DistanceJoint::setStiffness(float k)
{
joint->SetStiffness(k);
}
float DistanceJoint::getStiffness() const
{
return joint->GetStiffness();
}
void DistanceJoint::setDamping(float d)
{
joint->SetDamping(d);
}
float DistanceJoint::getDamping() const
{
return joint->GetDamping();
}
+25 -7
View File
@@ -57,27 +57,45 @@ public:
float getLength() const;
/**
* Sets the response speed.
* Sets the response speed. Independent of mass
**/
void setFrequency(float hz);
/**
* Gets the response speed.
* Gets the response speed. Independent of mass
**/
float getFrequency() const;
/**
* Sets the damping ratio.
* 0 = no damping, 1 = critical damping.
* Set the spring damping ratio. Independent of mass
**/
void setDampingRatio(float d);
void setDampingRatio(float ratio);
/**
* Gets the damping ratio.
* 0 = no damping, 1 = critical damping.
* Get the spring damping ratio. Independent of mass
**/
float getDampingRatio() const;
/**
* Sets the response speed. Dependent of mass
**/
void setStiffness(float k);
/**
* Gets the response speed. Dependent of mass
**/
float getStiffness() const;
/**
* Set the spring damping. Dependent of mass
**/
void setDamping(float ratio);
/**
* Get the spring damping. Dependent of mass
**/
float getDamping() const;
private:
// The Box2D DistanceJoint object.
b2DistanceJoint *joint;
+4 -34
View File
@@ -48,28 +48,13 @@ void EdgeShape::setNextVertex(float x, float y)
b2EdgeShape *e = (b2EdgeShape *)shape;
b2Vec2 v(x, y);
e->m_vertex3 = Physics::scaleDown(v);
e->m_hasVertex3 = true;
}
void EdgeShape::setNextVertex()
{
b2EdgeShape *e = (b2EdgeShape *)shape;
e->m_hasVertex3 = false;
}
bool EdgeShape::getNextVertex(float &x, float &y) const
b2Vec2 EdgeShape::getNextVertex() const
{
b2EdgeShape *e = (b2EdgeShape *)shape;
if (e->m_hasVertex3)
{
b2Vec2 v = Physics::scaleUp(e->m_vertex3);
x = v.x;
y = v.y;
return true;
}
return false;
return Physics::scaleUp(e->m_vertex3);
}
void EdgeShape::setPreviousVertex(float x, float y)
@@ -77,28 +62,13 @@ void EdgeShape::setPreviousVertex(float x, float y)
b2EdgeShape *e = (b2EdgeShape *)shape;
b2Vec2 v(x, y);
e->m_vertex0 = Physics::scaleDown(v);
e->m_hasVertex0 = true;
}
void EdgeShape::setPreviousVertex()
{
b2EdgeShape *e = (b2EdgeShape *)shape;
e->m_hasVertex0 = false;
}
bool EdgeShape::getPreviousVertex(float &x, float &y) const
b2Vec2 EdgeShape::getPreviousVertex() const
{
b2EdgeShape *e = (b2EdgeShape *)shape;
if (e->m_hasVertex0)
{
b2Vec2 v = Physics::scaleUp(e->m_vertex0);
x = v.x;
y = v.y;
return true;
}
return false;
return Physics::scaleUp(e->m_vertex0);
}
int EdgeShape::getPoints(lua_State *L)
+2 -4
View File
@@ -50,12 +50,10 @@ public:
virtual ~EdgeShape();
void setNextVertex(float x, float y);
void setNextVertex();
bool getNextVertex(float &x, float &y) const;
b2Vec2 getNextVertex() const;
void setPreviousVertex(float x, float y);
void setPreviousVertex();
bool getPreviousVertex(float &x, float &y) const;
b2Vec2 getPreviousVertex() const;
/**
* Returns the transformed points of the edge shape.
+2 -2
View File
@@ -45,7 +45,7 @@ Fixture::Fixture(Body *body, Shape *shape, float density)
udata->ref = nullptr;
b2FixtureDef def;
def.shape = shape->shape;
def.userData = (void *)udata;
def.userData.pointer = (uintptr_t)udata;
def.density = density;
fixture = body->body->CreateFixture(&def);
this->retain();
@@ -262,7 +262,7 @@ int Fixture::setUserData(lua_State *L)
if (udata == nullptr)
{
udata = new fixtureudata();
fixture->SetUserData((void *) udata);
fixture->GetUserData().pointer = (uintptr_t)udata;
}
if(!udata->ref)
+1 -1
View File
@@ -29,7 +29,7 @@
#include "common/Reference.h"
// Box2D
#include <Box2D/Box2D.h>
#include <box2d/Box2D.h>
namespace love
{
+4 -4
View File
@@ -154,7 +154,7 @@ float Joint::getReactionTorque(float dt)
b2Joint *Joint::createJoint(b2JointDef *def)
{
def->userData = udata;
def->userData.pointer = (uintptr_t)udata;
joint = world->world->CreateJoint(def);
world->registerObject(joint, this);
// Box2D joint has a reference to this love Joint.
@@ -185,9 +185,9 @@ void Joint::destroyJoint(bool implicit)
this->release();
}
bool Joint::isActive() const
bool Joint::isEnabled() const
{
return joint->IsActive();
return joint->IsEnabled();
}
bool Joint::getCollideConnected() const
@@ -202,7 +202,7 @@ int Joint::setUserData(lua_State *L)
if (udata == nullptr)
{
udata = new jointudata();
joint->SetUserData((void *) udata);
joint->GetUserData().pointer = (uintptr_t)udata;
}
if(!udata->ref)
+2 -2
View File
@@ -26,7 +26,7 @@
#include "physics/Joint.h"
// Box2D
#include <Box2D/Box2D.h>
#include <box2d/Box2D.h>
namespace love
{
@@ -103,7 +103,7 @@ public:
**/
float getReactionTorque(float dt);
bool isActive() const;
bool isEnabled() const;
bool getCollideConnected() const;
+41 -7
View File
@@ -80,28 +80,62 @@ float MouseJoint::getMaxForce() const
void MouseJoint::setFrequency(float hz)
{
// This is kind of a crappy check. The frequency is used in an internal
// This is kind of a crappy check. The Stiffness is used in an internal
// box2d calculation whose result must be > FLT_EPSILON, but other variables
// go into that calculation...
if (hz <= FLT_EPSILON * 2)
throw love::Exception("MouseJoint frequency must be a positive number.");
throw love::Exception("MouseJoint Stiffness must be a positive number.");
joint->SetFrequency(hz);
float stiffness, damping;
b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
joint->SetStiffness(stiffness);
}
float MouseJoint::getFrequency() const
{
return joint->GetFrequency();
float frequency, ratio;
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return frequency;
}
void MouseJoint::setDampingRatio(float d)
void MouseJoint::setDampingRatio(float ratio)
{
joint->SetDampingRatio(d);
float stiffness, damping;
b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
joint->SetDamping(damping);
}
float MouseJoint::getDampingRatio() const
{
return joint->GetDampingRatio();
float frequency, ratio;
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return ratio;
}
void MouseJoint::setStiffness(float k)
{
// This is kind of a crappy check. The Stiffness is used in an internal
// box2d calculation whose result must be > FLT_EPSILON, but other variables
// go into that calculation...
if (k <= FLT_EPSILON * 2)
throw love::Exception("MouseJoint Stiffness must be a positive number.");
joint->SetStiffness(k);
}
float MouseJoint::getStiffness() const
{
return joint->GetStiffness();
}
void MouseJoint::setDamping(float d)
{
joint->SetDamping(d);
}
float MouseJoint::getDamping() const
{
return joint->GetDamping();
}
Body *MouseJoint::getBodyA() const
+25 -7
View File
@@ -76,27 +76,45 @@ public:
float getMaxForce() const;
/**
* Sets the response speed.
* Sets the response speed. Independent of mass
**/
void setFrequency(float hz);
/**
* Gets the response speed.
* Gets the response speed. Independent of mass
**/
float getFrequency() const;
/**
* Sets the damping ratio.
* 0 = no damping, 1 = critical damping.
* Set the spring damping ratio. Independent of mass
**/
void setDampingRatio(float d);
void setDampingRatio(float ratio);
/**
* Gets the damping ratio.
* 0 = no damping, 1 = critical damping.
* Get the spring damping ratio. Independent of mass
**/
float getDampingRatio() const;
/**
* Sets the response speed. Dependent of mass
**/
void setStiffness(float k);
/**
* Gets the response speed. Dependent of mass
**/
float getStiffness() const;
/**
* Set the spring damping. Dependent of mass
**/
void setDamping(float ratio);
/**
* Get the spring damping. Dependent of mass
**/
float getDamping() const;
virtual Body *getBodyA() const;
virtual Body *getBodyB() const;
+73 -4
View File
@@ -83,10 +83,19 @@ PolygonShape *Physics::newRectangleShape(float x, float y, float w, float h, flo
return new PolygonShape(s);
}
EdgeShape *Physics::newEdgeShape(float x1, float y1, float x2, float y2)
EdgeShape *Physics::newEdgeShape(float x1, float y1, float x2, float y2, bool oneSided)
{
b2EdgeShape *s = new b2EdgeShape();
s->Set(Physics::scaleDown(b2Vec2(x1, y1)), Physics::scaleDown(b2Vec2(x2, y2)));
if (oneSided)
{
b2Vec2 v1 = Physics::scaleDown(b2Vec2(x1, y1));
b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2));
s->SetOneSided(v1, v1, v2, v2);
}
else
{
s->SetTwoSided(Physics::scaleDown(b2Vec2(x1, y1)), Physics::scaleDown(b2Vec2(x2, y2)));
}
return new EdgeShape(s);
}
@@ -160,7 +169,7 @@ int Physics::newChainShape(lua_State *L)
if (istable)
argc = (int) luax_objlen(L, 2);
if (argc % 2 != 0)
if (argc == 0 || argc % 2 != 0)
return luaL_error(L, "Number of vertex components must be a multiple of two.");
int vcount = argc/2;
@@ -196,7 +205,7 @@ int Physics::newChainShape(lua_State *L)
if (loop)
s->CreateLoop(vecs, vcount);
else
s->CreateChain(vecs, vcount);
s->CreateChain(vecs, vcount, vecs[0], vecs[vcount-1]);
}
catch (love::Exception &)
{
@@ -386,6 +395,66 @@ b2AABB Physics::scaleUp(const b2AABB &aabb)
return t;
}
void Physics::b2LinearFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB)
{
float massA = bodyA->GetMass();
float massB = bodyB->GetMass();
float mass;
if (massA > 0.0f && massB > 0.0f)
{
mass = massA * massB / (massA + massB);
}
else if (massA > 0.0f)
{
mass = massA;
}
else
{
mass = massB;
}
if (mass == 0.0f || stiffness <= 0.0f)
{
frequency = 0.0f;
ratio = 0.0f;
return;
};
float omega = b2Sqrt(stiffness / mass);
frequency = omega / (2.0f * b2_pi);
ratio = damping / (mass * 2.0f * omega);
}
void Physics::b2AngularFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB)
{
float IA = bodyA->GetInertia();
float IB = bodyB->GetInertia();
float I;
if (IA > 0.0f && IB > 0.0f)
{
I = IA * IB / (IA + IB);
}
else if (IA > 0.0f)
{
I = IA;
}
else
{
I = IB;
}
if (I == 0.0f || stiffness <= 0.0f)
{
frequency = 0.0f;
ratio = 0.0f;
return;
};
float omega = b2Sqrt(stiffness / I);
frequency = omega / (2.0f * b2_pi);
ratio = damping / (I * 2.0f * omega);
}
} // box2d
} // physics
} // love
+24 -2
View File
@@ -140,7 +140,7 @@ public:
* @param x2 The x coordinate of the second point.
* @param y2 The y coordinate of the second point.
**/
EdgeShape *newEdgeShape(float x1, float y1, float x2, float y2);
EdgeShape *newEdgeShape(float x1, float y1, float x2, float y2, bool oneSided);
/**
* Creates a new PolygonShape from a variable number of vertices.
@@ -354,7 +354,29 @@ public:
* @param aabb The unscaled input AABB.
* @return The scaled AABB.
**/
static b2AABB scaleUp(const b2AABB &aabb);
static b2AABB scaleUp(const b2AABB& aabb);
/**
* Calculates linear frequency and damping radio from stiffness and damping
* @param frequency The output frequency
* @param ratio The output damping ratio
* @param stiffness The joint stiffness
* @param damping The joint damping
* @param bodyA The bodyA of the joint
* @param bodyB The bodyB of the joint
**/
static void b2LinearFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB);
/**
* Calculates angular frequency and damping radio from stiffness and damping
* @param frequency The output frequency
* @param ratio The output damping ratio
* @param stiffness The joint stiffness
* @param damping The joint damping
* @param bodyA The bodyA of the joint
* @param bodyB The bodyB of the joint
**/
static void b2AngularFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB);
private:
+2 -2
View File
@@ -47,10 +47,10 @@ int PolygonShape::getPoints(lua_State *L)
{
love::luax_assert_argc(L, 0);
b2PolygonShape *p = (b2PolygonShape *)shape;
int count = p->GetVertexCount();
int count = p->m_count;
for (int i = 0; i<count; i++)
{
b2Vec2 v = Physics::scaleUp(p->GetVertex(i));
b2Vec2 v = Physics::scaleUp(p->m_vertices[i]);
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
}
+2 -2
View File
@@ -38,7 +38,7 @@ RopeJoint::RopeJoint(Body *body1, Body *body2, float x1, float y1, float x2, flo
: Joint(body1, body2)
, joint(NULL)
{
b2RopeJointDef def;
b2DistanceJointDef def;
def.bodyA = body1->body;
def.bodyB = body2->body;
body1->getLocalPoint(x1, y1, x1, y1);
@@ -49,7 +49,7 @@ RopeJoint::RopeJoint(Body *body1, Body *body2, float x1, float y1, float x2, flo
def.localAnchorB.y = Physics::scaleDown(y2);
def.maxLength = Physics::scaleDown(maxLength);
def.collideConnected = collideConnected;
joint = (b2RopeJoint *)createJoint(&def);
joint = (b2DistanceJoint *)createJoint(&def);
}
RopeJoint::~RopeJoint()
+1 -1
View File
@@ -56,7 +56,7 @@ public:
private:
// The Box2D RopeJoint object.
b2RopeJoint *joint;
b2DistanceJoint *joint;
};
} // box2d
+1 -1
View File
@@ -26,7 +26,7 @@
#include "physics/box2d/Body.h"
// Box2D
#include <Box2D/Box2D.h>
#include <box2d/Box2D.h>
namespace love
{
+33 -5
View File
@@ -68,22 +68,50 @@ void WeldJoint::init(b2WeldJointDef &def, Body *body1, Body *body2, float xA, fl
void WeldJoint::setFrequency(float hz)
{
joint->SetFrequency(hz);
float stiffness, damping;
b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
joint->SetStiffness(stiffness);
}
float WeldJoint::getFrequency() const
{
return joint->GetFrequency();
float frequency, ratio;
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return frequency;
}
void WeldJoint::setDampingRatio(float d)
void WeldJoint::setDampingRatio(float ratio)
{
joint->SetDampingRatio(d);
float stiffness, damping;
b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
joint->SetDamping(damping);
}
float WeldJoint::getDampingRatio() const
{
return joint->GetDampingRatio();
float frequency, ratio;
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return ratio;
}
void WeldJoint::setStiffness(float k)
{
joint->SetStiffness(k);
}
float WeldJoint::getStiffness() const
{
return joint->GetStiffness();
}
void WeldJoint::setDamping(float d)
{
joint->SetDamping(d);
}
float WeldJoint::getDamping() const
{
return joint->GetDamping();
}
float WeldJoint::getReferenceAngle() const
+25 -7
View File
@@ -50,27 +50,45 @@ public:
virtual ~WeldJoint();
/**
* Sets the response speed.
* Sets the response speed. Independent of mass
**/
void setFrequency(float hz);
/**
* Gets the response speed.
* Gets the response speed. Independent of mass
**/
float getFrequency() const;
/**
* Sets the damping ratio.
* 0 = no damping, 1 = critical damping.
* Set the spring damping ratio. Independent of mass
**/
void setDampingRatio(float d);
void setDampingRatio(float ratio);
/**
* Gets the damping ratio.
* 0 = no damping, 1 = critical damping.
* Get the spring damping ratio. Independent of mass
**/
float getDampingRatio() const;
/**
* Sets the response speed. Dependent of mass
**/
void setStiffness(float k);
/**
* Gets the response speed. Dependent of mass
**/
float getStiffness() const;
/**
* Set the spring damping. Dependent of mass
**/
void setDamping(float ratio);
/**
* Get the spring damping. Dependent of mass
**/
float getDamping() const;
/**
* Gets the reference angle.
**/
+37 -9
View File
@@ -57,7 +57,7 @@ float WheelJoint::getJointTranslation() const
float WheelJoint::getJointSpeed() const
{
return Physics::scaleUp(joint->GetJointSpeed());
return Physics::scaleUp(joint->GetJointLinearSpeed());
}
void WheelJoint::setMotorEnabled(bool enable)
@@ -95,24 +95,52 @@ float WheelJoint::getMotorTorque(float inv_dt) const
return Physics::scaleUp(Physics::scaleUp(joint->GetMotorTorque(inv_dt)));
}
void WheelJoint::setSpringFrequency(float hz)
void WheelJoint::setFrequency(float hz)
{
joint->SetSpringFrequencyHz(hz);
float stiffness, damping;
b2AngularStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
joint->SetStiffness(stiffness);
}
float WheelJoint::getSpringFrequency() const
float WheelJoint::getFrequency() const
{
return joint->GetSpringFrequencyHz();
float frequency, ratio;
Physics::b2AngularFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return frequency;
}
void WheelJoint::setSpringDampingRatio(float ratio)
void WheelJoint::setDampingRatio(float ratio)
{
joint->SetSpringDampingRatio(ratio);
float stiffness, damping;
b2AngularStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
joint->SetDamping(damping);
}
float WheelJoint::getSpringDampingRatio() const
float WheelJoint::getDampingRatio() const
{
return joint->GetSpringDampingRatio();
float frequency, ratio;
Physics::b2AngularFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
return ratio;
}
void WheelJoint::setStiffness(float k)
{
joint->SetStiffness(k);
}
float WheelJoint::getStiffness() const
{
return joint->GetStiffness();
}
void WheelJoint::setDamping(float ratio)
{
joint->SetDamping(ratio);
}
float WheelJoint::getDamping() const
{
return joint->GetDamping();
}
int WheelJoint::getAxis(lua_State *L)
+28 -9
View File
@@ -96,25 +96,44 @@ public:
float getMotorTorque(float inv_dt) const;
/**
* Set the spring frequency, in hertz. Setting the frequency to 0
* disables the spring.
* Sets the response speed. Independent of mass
**/
void setSpringFrequency(float hz);
void setFrequency(float hz);
/**
* Get the spring frequency, in hertz.
* Gets the response speed. Independent of mass
**/
float getSpringFrequency() const;
float getFrequency() const;
/**
* Set the spring damping ratio.
* Set the spring damping ratio. Independent of mass
**/
void setSpringDampingRatio(float ratio);
void setDampingRatio(float ratio);
/**
* Get the spring damping ratio.
* Get the spring damping ratio. Independent of mass
**/
float getSpringDampingRatio() const;
float getDampingRatio() const;
/**
* Sets the response speed. Dependent of mass
**/
void setStiffness(float k);
/**
* Gets the response speed. Dependent of mass
**/
float getStiffness() const;
/**
* Set the spring damping. Dependent of mass
**/
void setDamping(float ratio);
/**
* Get the spring damping. Dependent of mass
**/
float getDamping() const;
/**
* Gets the axis unit vector, relative to body1.
+2 -2
View File
@@ -183,7 +183,7 @@ World::RayCastCallback::~RayCastCallback()
{
}
float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction)
float World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction)
{
if (L != nullptr)
{
@@ -201,7 +201,7 @@ float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &
lua_call(L, 6, 1);
if (!lua_isnumber(L, -1))
luaL_error(L, "Raycast callback didn't return a number!");
float32 fraction = (float32) lua_tonumber(L, -1);
float fraction = (float) lua_tonumber(L, -1);
lua_pop(L, 1);
return fraction;
}
+2 -2
View File
@@ -31,7 +31,7 @@
#include <unordered_map>
// Box2D
#include <Box2D/Box2D.h>
#include <box2d/Box2D.h>
namespace love
{
@@ -107,7 +107,7 @@ public:
public:
RayCastCallback(World *world, lua_State *L, int idx);
~RayCastCallback();
virtual float32 ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction);
virtual float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction);
private:
World *world;
lua_State *L;
+2 -2
View File
@@ -518,7 +518,7 @@ int w_Body_setBullet(lua_State *L)
int w_Body_isActive(lua_State *L)
{
Body *t = luax_checkbody(L, 1);
luax_pushboolean(L, t->isActive());
luax_pushboolean(L, t->isEnabled());
return 1;
}
@@ -548,7 +548,7 @@ int w_Body_setActive(lua_State *L)
{
Body *t = luax_checkbody(L, 1);
bool b = luax_checkboolean(L, 2);
luax_catchexcept(L, [&](){ t->setActive(b); });
luax_catchexcept(L, [&](){ t->setEnabled(b); });
return 0;
}
+14 -32
View File
@@ -37,28 +37,18 @@ ChainShape *luax_checkchainshape(lua_State *L, int idx)
int w_ChainShape_setNextVertex(lua_State *L)
{
ChainShape *c = luax_checkchainshape(L, 1);
if (lua_isnoneornil(L, 2))
c->setNextVertex();
else
{
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
luax_catchexcept(L, [&](){ c->setNextVertex(x, y); });
}
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
luax_catchexcept(L, [&](){ c->setNextVertex(x, y); });
return 0;
}
int w_ChainShape_setPreviousVertex(lua_State *L)
{
ChainShape *c = luax_checkchainshape(L, 1);
if (lua_isnoneornil(L, 2))
c->setPreviousVertex();
else
{
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
luax_catchexcept(L, [&](){ c->setPreviousVertex(x, y); });
}
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
luax_catchexcept(L, [&](){ c->setPreviousVertex(x, y); });
return 0;
}
@@ -95,27 +85,19 @@ int w_ChainShape_getPoint(lua_State *L)
int w_ChainShape_getNextVertex(lua_State *L)
{
ChainShape *c = luax_checkchainshape(L, 1);
float x, y;
if (c->getNextVertex(x, y))
{
lua_pushnumber(L, x);
lua_pushnumber(L, y);
return 2;
}
return 0;
b2Vec2 v = c->getNextVertex();
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
return 2;
}
int w_ChainShape_getPreviousVertex(lua_State *L)
{
ChainShape *c = luax_checkchainshape(L, 1);
float x, y;
if (c->getPreviousVertex(x, y))
{
lua_pushnumber(L, x);
lua_pushnumber(L, y);
return 2;
}
return 0;
b2Vec2 v = c->getPreviousVertex();
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
return 2;
}
int w_ChainShape_getPoints(lua_State *L)
@@ -80,6 +80,36 @@ int w_DistanceJoint_getDampingRatio(lua_State *L)
return 1;
}
int w_DistanceJoint_setStiffness(lua_State *L)
{
DistanceJoint *t = luax_checkdistancejoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setStiffness(arg1);
return 0;
}
int w_DistanceJoint_getStiffness(lua_State *L)
{
DistanceJoint *t = luax_checkdistancejoint(L, 1);
lua_pushnumber(L, t->getStiffness());
return 1;
}
int w_DistanceJoint_setDamping(lua_State *L)
{
DistanceJoint *t = luax_checkdistancejoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setDamping(arg1);
return 0;
}
int w_DistanceJoint_getDamping(lua_State *L)
{
DistanceJoint *t = luax_checkdistancejoint(L, 1);
lua_pushnumber(L, t->getDamping());
return 1;
}
static const luaL_Reg w_DistanceJoint_functions[] =
{
{ "setLength", w_DistanceJoint_setLength },
@@ -88,6 +118,10 @@ static const luaL_Reg w_DistanceJoint_functions[] =
{ "getFrequency", w_DistanceJoint_getFrequency },
{ "setDampingRatio", w_DistanceJoint_setDampingRatio },
{ "getDampingRatio", w_DistanceJoint_getDampingRatio },
{ "setStiffness", w_DistanceJoint_setStiffness },
{ "getStiffness", w_DistanceJoint_getStiffness },
{ "setDamping", w_DistanceJoint_setDamping },
{ "getDamping", w_DistanceJoint_getDamping },
{ 0, 0 }
};
+14 -32
View File
@@ -35,55 +35,37 @@ EdgeShape *luax_checkedgeshape(lua_State *L, int idx)
int w_EdgeShape_setNextVertex(lua_State *L)
{
EdgeShape *t = luax_checkedgeshape(L, 1);
if (lua_isnoneornil(L, 2))
t->setNextVertex();
else
{
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
t->setNextVertex(x, y);
}
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
t->setNextVertex(x, y);
return 0;
}
int w_EdgeShape_setPreviousVertex(lua_State *L)
{
EdgeShape *t = luax_checkedgeshape(L, 1);
if (lua_isnoneornil(L, 2))
t->setPreviousVertex();
else
{
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
t->setPreviousVertex(x, y);
}
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
t->setPreviousVertex(x, y);
return 0;
}
int w_EdgeShape_getNextVertex(lua_State *L)
{
EdgeShape *t = luax_checkedgeshape(L, 1);
float x, y;
if (t->getNextVertex(x, y))
{
lua_pushnumber(L, x);
lua_pushnumber(L, y);
return 2;
}
return 0;
b2Vec2 v = t->getNextVertex();
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
return 2;
}
int w_EdgeShape_getPreviousVertex(lua_State *L)
{
EdgeShape *t = luax_checkedgeshape(L, 1);
float x, y;
if (t->getPreviousVertex(x, y))
{
lua_pushnumber(L, x);
lua_pushnumber(L, y);
return 2;
}
return 0;
b2Vec2 v = t->getPreviousVertex();
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
return 2;
}
int w_EdgeShape_getPoints(lua_State *L)
@@ -96,6 +96,36 @@ int w_MouseJoint_getDampingRatio(lua_State *L)
return 1;
}
int w_MouseJoint_setStiffness(lua_State *L)
{
MouseJoint *t = luax_checkmousejoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
luax_catchexcept(L, [&]() { t->setStiffness(arg1); });
return 0;
}
int w_MouseJoint_getStiffness(lua_State *L)
{
MouseJoint *t = luax_checkmousejoint(L, 1);
lua_pushnumber(L, t->getStiffness());
return 1;
}
int w_MouseJoint_setDamping(lua_State *L)
{
MouseJoint *t = luax_checkmousejoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setDamping(arg1);
return 0;
}
int w_MouseJoint_getDamping(lua_State *L)
{
MouseJoint *t = luax_checkmousejoint(L, 1);
lua_pushnumber(L, t->getDamping());
return 1;
}
static const luaL_Reg w_MouseJoint_functions[] =
{
{ "setTarget", w_MouseJoint_setTarget },
@@ -106,6 +136,10 @@ static const luaL_Reg w_MouseJoint_functions[] =
{ "getFrequency", w_MouseJoint_getFrequency },
{ "setDampingRatio", w_MouseJoint_setDampingRatio },
{ "getDampingRatio", w_MouseJoint_getDampingRatio },
{ "setStiffness", w_MouseJoint_setStiffness },
{ "getStiffness", w_MouseJoint_getStiffness },
{ "setDamping", w_MouseJoint_setDamping },
{ "getDamping", w_MouseJoint_getDamping },
{ 0, 0 }
};
+2 -1
View File
@@ -160,8 +160,9 @@ int w_newEdgeShape(lua_State *L)
float y1 = (float)luaL_checknumber(L, 2);
float x2 = (float)luaL_checknumber(L, 3);
float y2 = (float)luaL_checknumber(L, 4);
bool oneSided = luax_optboolean(L, 5, false);
EdgeShape *shape;
luax_catchexcept(L, [&](){ shape = instance()->newEdgeShape(x1, y1, x2, y2); });
luax_catchexcept(L, [&](){ shape = instance()->newEdgeShape(x1, y1, x2, y2, oneSided); });
luax_pushtype(L, shape);
shape->release();
return 1;
@@ -65,6 +65,36 @@ int w_WeldJoint_getDampingRatio(lua_State *L)
return 1;
}
int w_WeldJoint_setStiffness(lua_State *L)
{
WeldJoint *t = luax_checkweldjoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setStiffness(arg1);
return 0;
}
int w_WeldJoint_getStiffness(lua_State *L)
{
WeldJoint *t = luax_checkweldjoint(L, 1);
lua_pushnumber(L, t->getStiffness());
return 1;
}
int w_WeldJoint_setDamping(lua_State *L)
{
WeldJoint *t = luax_checkweldjoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setDamping(arg1);
return 0;
}
int w_WeldJoint_getDamping(lua_State *L)
{
WeldJoint *t = luax_checkweldjoint(L, 1);
lua_pushnumber(L, t->getDamping());
return 1;
}
int w_WeldJoint_getReferenceAngle(lua_State *L)
{
WeldJoint *t = luax_checkweldjoint(L, 1);
@@ -78,6 +108,10 @@ static const luaL_Reg w_WeldJoint_functions[] =
{ "getFrequency", w_WeldJoint_getFrequency },
{ "setDampingRatio", w_WeldJoint_setDampingRatio },
{ "getDampingRatio", w_WeldJoint_getDampingRatio },
{ "setStiffness", w_WeldJoint_setStiffness },
{ "getStiffness", w_WeldJoint_getStiffness },
{ "setDamping", w_WeldJoint_setDamping },
{ "getDamping", w_WeldJoint_getDamping },
{ "getReferenceAngle", w_WeldJoint_getReferenceAngle },
{ 0, 0 }
};
+54 -12
View File
@@ -102,33 +102,63 @@ int w_WheelJoint_getMotorTorque(lua_State *L)
return 1;
}
int w_WheelJoint_setSpringFrequency(lua_State *L)
int w_WheelJoint_setFrequency(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setSpringFrequency(arg1);
t->setFrequency(arg1);
return 0;
}
int w_WheelJoint_getSpringFrequency(lua_State *L)
int w_WheelJoint_getFrequency(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
lua_pushnumber(L, t->getSpringFrequency());
lua_pushnumber(L, t->getFrequency());
return 1;
}
int w_WheelJoint_setSpringDampingRatio(lua_State *L)
int w_WheelJoint_setDampingRatio(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setSpringDampingRatio(arg1);
t->setDampingRatio(arg1);
return 0;
}
int w_WheelJoint_getSpringDampingRatio(lua_State *L)
int w_WheelJoint_getDampingRatio(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
lua_pushnumber(L, t->getSpringDampingRatio());
lua_pushnumber(L, t->getDampingRatio());
return 1;
}
int w_WheelJoint_setStiffness(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setStiffness(arg1);
return 0;
}
int w_WheelJoint_getStiffness(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
lua_pushnumber(L, t->getStiffness());
return 1;
}
int w_WheelJoint_setDamping(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setDamping(arg1);
return 0;
}
int w_WheelJoint_getDamping(lua_State *L)
{
WheelJoint *t = luax_checkwheeljoint(L, 1);
lua_pushnumber(L, t->getDamping());
return 1;
}
@@ -150,10 +180,22 @@ static const luaL_Reg w_WheelJoint_functions[] =
{ "setMaxMotorTorque", w_WheelJoint_setMaxMotorTorque },
{ "getMaxMotorTorque", w_WheelJoint_getMaxMotorTorque },
{ "getMotorTorque", w_WheelJoint_getMotorTorque },
{ "setSpringFrequency", w_WheelJoint_setSpringFrequency },
{ "getSpringFrequency", w_WheelJoint_getSpringFrequency },
{ "setSpringDampingRatio", w_WheelJoint_setSpringDampingRatio },
{ "getSpringDampingRatio", w_WheelJoint_getSpringDampingRatio },
{ "setSpringFrequency", w_WheelJoint_setFrequency },
{ "getSpringFrequency", w_WheelJoint_getFrequency },
{ "setSpringDampingRatio", w_WheelJoint_setDampingRatio },
{ "getSpringDampingRatio", w_WheelJoint_getDampingRatio },
{ "setSpringStiffness", w_WheelJoint_setStiffness },
{ "getSpringStiffness", w_WheelJoint_getStiffness },
{ "setSpringDamping", w_WheelJoint_setDamping },
{ "getSpringDamping", w_WheelJoint_getDamping },
{ "setFrequency", w_WheelJoint_setFrequency },
{ "getFrequency", w_WheelJoint_getFrequency },
{ "setDampingRatio", w_WheelJoint_setDampingRatio },
{ "getDampingRatio", w_WheelJoint_getDampingRatio },
{ "setStiffness", w_WheelJoint_setStiffness },
{ "getStiffness", w_WheelJoint_getStiffness },
{ "setDamping", w_WheelJoint_setDamping },
{ "getDamping", w_WheelJoint_getDamping },
{ "getAxis", w_WheelJoint_getAxis },
{ 0, 0 }
};
+22
View File
@@ -39,7 +39,18 @@
#if defined(LOVE_ANDROID)
#include "common/android.h"
#elif defined(LOVE_LINUX)
#ifdef __has_include
#if __has_include(<spawn.h>)
#define LOVE_HAS_POSIX_SPAWN
#endif
#endif
#ifdef LOVE_HAS_POSIX_SPAWN
#include <spawn.h>
#else
#include <unistd.h>
#endif
#endif
namespace love
@@ -70,10 +81,12 @@ std::string System::getOS() const
#endif
}
#ifdef LOVE_HAS_POSIX_SPAWN
extern "C"
{
extern char **environ; // The environment, always available
}
#endif
bool System::openURL(const std::string &url) const
{
@@ -104,10 +117,19 @@ bool System::openURL(const std::string &url) const
pid_t pid;
const char *argv[] = {"xdg-open", url.c_str(), nullptr};
#ifdef LOVE_HAS_POSIX_SPAWN
// Note: at the moment this process inherits our file descriptors.
// Note: the below const_cast is really ugly as well.
if (posix_spawnp(&pid, "xdg-open", nullptr, nullptr, const_cast<char **>(argv), environ) != 0)
return false;
#else
pid = fork();
if (pid == 0)
{
execvp("xdg-open", const_cast<char **>(argv));
return false;
}
#endif
// Check if xdg-open already completed (or failed.)
int status = 0;
+12
View File
@@ -26,6 +26,18 @@ namespace love
namespace window
{
static bool highDPIAllowed = false;
void setHighDPIAllowed(bool enable)
{
highDPIAllowed = enable;
}
bool isHighDPIAllowed()
{
return highDPIAllowed;
}
Window::~Window()
{
}
+5 -2
View File
@@ -43,6 +43,10 @@ class Graphics;
namespace window
{
// Applied when the window is first created.
void setHighDPIAllowed(bool enable);
bool isHighDPIAllowed();
// Forward-declared so it can be used in the class methods. We can't define the
// whole thing here because it uses the Window::Type enum.
struct WindowSettings;
@@ -66,7 +70,7 @@ public:
SETTING_BORDERLESS,
SETTING_CENTERED,
SETTING_DISPLAY,
SETTING_HIGHDPI,
SETTING_HIGHDPI, // Deprecated
SETTING_USE_DPISCALE,
SETTING_REFRESHRATE,
SETTING_X,
@@ -261,7 +265,6 @@ struct WindowSettings
bool borderless = false;
bool centered = true;
int display = 0;
bool highdpi = false;
bool usedpiscale = true;
double refreshrate = 0.0;
bool useposition = false;
+112 -96
View File
@@ -94,7 +94,7 @@ void Window::setGraphics(graphics::Graphics *graphics)
this->graphics.set(graphics);
}
void Window::setGLFramebufferAttributes(int msaa, bool sRGB, bool stencil, int depth)
void Window::setGLFramebufferAttributes(bool sRGB)
{
// Set GL window / framebuffer attributes.
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
@@ -102,12 +102,18 @@ void Window::setGLFramebufferAttributes(int msaa, bool sRGB, bool stencil, int d
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencil ? 8 : 0);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depth);
SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (msaa > 0) ? msaa : 0);
// Always use 24/8 depth/stencil (make sure any Graphics implementations
// that have their own backbuffer match this, too).
// Changing this after initial window creation would need the context to be
// destroyed and recreated, which we really don't want.
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
// Backbuffer MSAA is handled by the love.graphics implementation.
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
@@ -289,7 +295,7 @@ std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
return attribslist;
}
bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer, int msaa, bool stencil, int depth)
bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer)
{
bool needsglcontext = (windowflags & SDL_WINDOW_OPENGL) != 0;
#ifdef LOVE_GRAPHICS_METAL
@@ -369,10 +375,9 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
// Try each context profile in order.
for (ContextAttribs attribs : attribslist)
{
int curMSAA = msaa;
bool curSRGB = love::graphics::isGammaCorrect();
setGLFramebufferAttributes(curMSAA, curSRGB, stencil, depth);
setGLFramebufferAttributes(curSRGB);
setGLContextAttributes(attribs);
windowerror.clear();
@@ -380,33 +385,14 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
create(&attribs);
if (!window && curMSAA > 0)
{
// The MSAA setting could have caused the failure.
setGLFramebufferAttributes(0, curSRGB, stencil, depth);
if (create(&attribs))
curMSAA = 0;
}
if (!window && curSRGB)
{
// same with sRGB.
setGLFramebufferAttributes(curMSAA, false, stencil, depth);
// The sRGB setting could have caused the failure.
setGLFramebufferAttributes(false);
if (create(&attribs))
curSRGB = false;
}
if (!window && curMSAA > 0 && curSRGB)
{
// Or both!
setGLFramebufferAttributes(0, false, stencil, depth);
if (create(&attribs))
{
curMSAA = 0;
curSRGB = false;
}
}
if (window && glcontext)
{
// Store the successful context attributes so we can re-use them in
@@ -481,6 +467,9 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
auto renderer = graphics != nullptr ? graphics->getRenderer() : graphics::Graphics::RENDERER_NONE;
if (isOpen())
updateSettings(this->settings, false);
WindowSettings f;
if (settings)
@@ -500,54 +489,11 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
height = mode.h;
}
Uint32 sdlflags = 0;
if (renderer == graphics::Graphics::RENDERER_OPENGL)
sdlflags |= SDL_WINDOW_OPENGL;
#ifdef LOVE_GRAPHICS_METAL
if (renderer == graphics::Graphics::RENDERER_METAL)
sdlflags |= SDL_WINDOW_METAL;
#endif
// On Android we always must have fullscreen type FULLSCREEN_TYPE_DESKTOP
#ifdef LOVE_ANDROID
f.fstype = FULLSCREEN_DESKTOP;
#endif
if (f.fullscreen)
{
if (f.fstype == FULLSCREEN_DESKTOP)
sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
else
{
sdlflags |= SDL_WINDOW_FULLSCREEN;
SDL_DisplayMode mode = {0, width, height, 0, nullptr};
// Fullscreen window creation will bug out if no mode can be used.
if (SDL_GetClosestDisplayMode(f.display, &mode, &mode) == nullptr)
{
// GetClosestDisplayMode will fail if we request a size larger
// than the largest available display mode, so we'll try to use
// the largest (first) mode in that case.
if (SDL_GetDisplayMode(f.display, 0, &mode) < 0)
return false;
}
width = mode.w;
height = mode.h;
}
}
if (f.resizable)
sdlflags |= SDL_WINDOW_RESIZABLE;
if (f.borderless)
sdlflags |= SDL_WINDOW_BORDERLESS;
if (f.highdpi)
sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
int x = f.x;
int y = f.y;
@@ -567,10 +513,81 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display);
}
close();
SDL_DisplayMode fsmode = {0, width, height, 0, nullptr};
if (!createWindowAndContext(x, y, width, height, sdlflags, renderer, f.msaa, f.stencil, f.depth))
return false;
if (f.fullscreen && f.fstype == FULLSCREEN_EXCLUSIVE)
{
// Fullscreen window creation will bug out if no mode can be used.
if (SDL_GetClosestDisplayMode(f.display, &fsmode, &fsmode) == nullptr)
{
// GetClosestDisplayMode will fail if we request a size larger
// than the largest available display mode, so we'll try to use
// the largest (first) mode in that case.
if (SDL_GetDisplayMode(f.display, 0, &fsmode) < 0)
return false;
}
}
bool needsetmode = false;
Uint32 sdlflags = 0;
if (f.fullscreen)
{
if (f.fstype == FULLSCREEN_DESKTOP)
sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
else
{
sdlflags |= SDL_WINDOW_FULLSCREEN;
width = fsmode.w;
height = fsmode.h;
}
}
if (renderer == graphics::Graphics::RENDERER_OPENGL)
sdlflags |= SDL_WINDOW_OPENGL;
#ifdef LOVE_GRAPHICS_METAL
if (renderer == graphics::Graphics::RENDERER_METAL)
sdlflags |= SDL_WINDOW_METAL;
#endif
if (isOpen())
{
if (SDL_SetWindowFullscreen(window, sdlflags) == 0 && renderer == graphics::Graphics::RENDERER_OPENGL)
SDL_GL_MakeCurrent(window, glcontext);
if (!f.fullscreen)
SDL_SetWindowSize(window, width, height);
// On linux systems 2.0.5+ might not be available...
// TODO: require at least 2.0.5?
#if SDL_VERSION_ATLEAST(2, 0, 5)
if (this->settings.resizable != f.resizable)
SDL_SetWindowResizable(window, f.resizable ? SDL_TRUE : SDL_FALSE);
#endif
if (this->settings.borderless != f.borderless)
SDL_SetWindowBordered(window, f.borderless ? SDL_FALSE : SDL_TRUE);
}
else
{
sdlflags |= SDL_WINDOW_OPENGL;
if (f.resizable)
sdlflags |= SDL_WINDOW_RESIZABLE;
if (f.borderless)
sdlflags |= SDL_WINDOW_BORDERLESS;
if (isHighDPIAllowed())
sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
if (!createWindowAndContext(x, y, width, height, sdlflags, renderer))
return false;
needsetmode = true;
}
// Make sure the window keeps any previously set icon.
setIcon(icon.get());
@@ -581,7 +598,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
// Enforce minimum window dimensions.
SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
if ((f.useposition || f.centered) && !f.fullscreen)
if (this->settings.display != f.display || ((f.useposition || f.centered) && !f.fullscreen))
SDL_SetWindowPosition(window, x, y);
SDL_RaiseWindow(window);
@@ -595,15 +612,23 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
double scaledw, scaledh;
fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
void *context = nullptr;
if (renderer == graphics::Graphics::RENDERER_OPENGL)
context = (void *) glcontext;
if (needsetmode)
{
void *context = nullptr;
if (renderer == graphics::Graphics::RENDERER_OPENGL)
context = (void *) glcontext;
#ifdef LOVE_GRAPHICS_METAL
if (renderer == graphics::Graphics::RENDERER_METAL && metalView)
context = (void *) SDL_Metal_GetLayer(metalView);
if (renderer == graphics::Graphics::RENDERER_METAL && metalView)
context = (void *) SDL_Metal_GetLayer(metalView);
#endif
graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.depth);
graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.msaa);
this->settings.msaa = graphics->getBackbufferMSAA();
}
else
{
graphics->setViewportSize((int) scaledw, (int) scaledh, pixelWidth, pixelHeight);
}
}
#ifdef LOVE_ANDROID
@@ -680,7 +705,8 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
getPosition(settings.x, settings.y, settings.display);
settings.highdpi = (wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
setHighDPIAllowed((wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0);
settings.usedpiscale = newsettings.usedpiscale;
// Only minimize on focus loss if the window is in exclusive-fullscreen mode
@@ -689,17 +715,6 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
else
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
// Verify MSAA setting.
int buffers = 0;
int samples = 0;
if ((wflags & SDL_WINDOW_OPENGL) != 0)
{
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
}
settings.msaa = (buffers > 0 ? samples : 0);
settings.vsync = getVSync();
settings.stencil = newsettings.stencil;
@@ -773,7 +788,7 @@ void Window::close(bool allowExceptions)
open = false;
}
bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
bool Window::setFullscreen(bool fullscreen, FullscreenType fstype)
{
if (!window)
return false;
@@ -812,9 +827,10 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
{
if (glcontext)
SDL_GL_MakeCurrent(window, glcontext);
updateSettings(newsettings, true);
// Apparently this gets un-set when we exit fullscreen (at least in macOS).
// This gets un-set when we exit fullscreen (at least in macOS).
if (!fullscreen)
SDL_SetWindowMinimumSize(window, settings.minwidth, settings.minheight);
+4 -4
View File
@@ -131,8 +131,6 @@ public:
private:
void close(bool allowExceptions);
struct ContextAttribs
{
int versionMajor;
@@ -141,11 +139,13 @@ private:
bool debug;
};
void setGLFramebufferAttributes(int msaa, bool sRGB, bool stencil, int depth);
void close(bool allowExceptions);
void setGLFramebufferAttributes(bool sRGB);
void setGLContextAttributes(const ContextAttribs &attribs);
bool checkGLVersion(const ContextAttribs &attribs, std::string &outversion);
std::vector<ContextAttribs> getContextAttribsList() const;
bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer, int msaa, bool stencil, int depth);
bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer);
// Update the saved window settings based on the window's actual state.
void updateSettings(const WindowSettings &newsettings, bool updateGraphicsViewport);
+18 -4
View File
@@ -75,9 +75,19 @@ static int readWindowSettings(lua_State *L, int idx, WindowSettings &settings)
settings.borderless = luax_boolflag(L, idx, settingName(Window::SETTING_BORDERLESS), settings.borderless);
settings.centered = luax_boolflag(L, idx, settingName(Window::SETTING_CENTERED), settings.centered);
settings.display = luax_intflag(L, idx, settingName(Window::SETTING_DISPLAY), settings.display+1) - 1;
settings.highdpi = luax_boolflag(L, idx, settingName(Window::SETTING_HIGHDPI), settings.highdpi);
settings.usedpiscale = luax_boolflag(L, idx, settingName(Window::SETTING_USE_DPISCALE), settings.usedpiscale);
lua_getfield(L, idx, settingName(Window::SETTING_HIGHDPI));
if (!lua_isnoneornil(L, -1))
{
luax_markdeprecated(L, "window.highdpi", API_FIELD, DEPRECATED_REPLACED, "t.highdpi in love.conf");
bool highdpi = luax_checkboolean(L, -1);
if (!instance()->isOpen())
setHighDPIAllowed(highdpi);
}
lua_pop(L, 1);
lua_getfield(L, idx, settingName(Window::SETTING_VSYNC));
if (lua_isnumber(L, -1))
settings.vsync = (int) lua_tointeger(L, -1);
@@ -201,9 +211,6 @@ int w_getMode(lua_State *L)
lua_pushinteger(L, settings.display + 1);
lua_setfield(L, -2, settingName(Window::SETTING_DISPLAY));
luax_pushboolean(L, settings.highdpi);
lua_setfield(L, -2, settingName(Window::SETTING_HIGHDPI));
luax_pushboolean(L, settings.usedpiscale);
lua_setfield(L, -2, settingName(Window::SETTING_USE_DPISCALE));
@@ -219,6 +226,12 @@ int w_getMode(lua_State *L)
return 3;
}
int w_isHighDPIAllowed(lua_State *L)
{
luax_pushboolean(L, isHighDPIAllowed());
return 1;
}
int w_getDisplayOrientation(lua_State *L)
{
int displayindex = 0;
@@ -618,6 +631,7 @@ static const luaL_Reg functions[] =
{ "setMode", w_setMode },
{ "updateMode", w_updateMode },
{ "getMode", w_getMode },
{ "isHighDPIAllowed", w_isHighDPIAllowed },
{ "getDisplayOrientation", w_getDisplayOrientation },
{ "getFullscreenModes", w_getFullscreenModes },
{ "setFullscreen", w_setFullscreen },