Merge branch '12.0-development' into metal

This commit is contained in:
Alex Szpakowski
2020-07-29 18:21:19 -03:00
57 changed files with 6870 additions and 4915 deletions
+192 -6
View File
@@ -19,24 +19,210 @@
**/
#include "Buffer.h"
#include "Graphics.h"
namespace love
{
namespace graphics
{
Buffer::Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags)
: size(size)
, type(type)
, usage(usage)
, map_flags(mapflags)
, is_mapped(false)
love::Type Buffer::type("GraphicsBuffer", &Object::type);
Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &bufferformat, size_t size, size_t arraylength)
: arrayLength(0)
, arrayStride(0)
, size(size)
, typeFlags(settings.typeFlags)
, usage(settings.usage)
, mapFlags(settings.mapFlags)
, mapped(false)
{
if (size == 0 && arraylength == 0)
throw love::Exception("Size or array length must be specified.");
if (bufferformat.size() == 0)
throw love::Exception("Data format must contain values.");
const auto &caps = gfx->getCapabilities();
bool supportsGLSL3 = caps.features[Graphics::FEATURE_GLSL3];
bool indexbuffer = settings.typeFlags & TYPEFLAG_INDEX;
bool vertexbuffer = settings.typeFlags & TYPEFLAG_VERTEX;
bool texelbuffer = settings.typeFlags & TYPEFLAG_TEXEL;
if (!indexbuffer && !vertexbuffer && !texelbuffer)
throw love::Exception("Buffer must be created with at least one buffer type (index, vertex, or texel).");
if (texelbuffer && !caps.features[Graphics::FEATURE_TEXEL_BUFFER])
throw love::Exception("Texel buffers are not supported on this system.");
size_t offset = 0;
size_t stride = 0;
for (const DataDeclaration &decl : bufferformat)
{
DataMember member(decl);
DataFormat format = member.decl.format;
const DataFormatInfo &info = member.info;
if (indexbuffer)
{
if (format != DATAFORMAT_UINT16 && format != DATAFORMAT_UINT32)
throw love::Exception("Index buffers only support uint16 and uint32 data types.");
if (bufferformat.size() > 1)
throw love::Exception("Index buffers only support a single value per element.");
if (decl.arrayLength > 0)
throw love::Exception("Arrays are not supported in index buffers.");
}
if (vertexbuffer)
{
if (decl.arrayLength > 0)
throw love::Exception("Arrays are not supported in vertex buffers.");
if (info.isMatrix)
throw love::Exception("Matrix types are not supported in vertex buffers.");
if (info.baseType == DATA_BASETYPE_BOOL)
throw love::Exception("Bool types are not supported in vertex buffers.");
if ((info.baseType == DATA_BASETYPE_INT || info.baseType == DATA_BASETYPE_UINT) && !supportsGLSL3)
throw love::Exception("Integer vertex attribute data types require GLSL 3 support.");
if (decl.name.empty())
throw love::Exception("Vertex buffer attributes must have a name.");
}
if (texelbuffer)
{
if (format != bufferformat[0].format)
throw love::Exception("All values in a texel buffer must have the same format.");
if (decl.arrayLength > 0)
throw love::Exception("Arrays are not supported in texel buffers.");
if (info.isMatrix)
throw love::Exception("Matrix types are not supported in texel buffers.");
if (info.baseType == DATA_BASETYPE_BOOL)
throw love::Exception("Bool types are not supported in texel buffers.");
if (info.components == 3)
throw love::Exception("3-component formats are not supported in texel buffers.");
if (info.baseType == DATA_BASETYPE_SNORM)
throw love::Exception("Signed normalized formats are not supported in texel buffers.");
}
// TODO: alignment
member.offset = offset;
member.size = member.info.size;
offset += member.size;
dataMembers.push_back(member);
}
stride = offset;
if (size != 0)
{
size_t remainder = size % stride;
if (remainder > 0)
size += stride - remainder;
arraylength = size / stride;
}
else
{
size = arraylength * stride;
}
this->arrayStride = stride;
this->arrayLength = arraylength;
this->size = size;
if (texelbuffer && arraylength * dataMembers.size() > caps.limits[Graphics::LIMIT_TEXEL_BUFFER_SIZE])
throw love::Exception("Cannot create texel buffer: total number of values in the buffer (%d * %d) is too large for this system (maximum %d).", (int) dataMembers.size(), (int) arraylength, caps.limits[Graphics::LIMIT_TEXEL_BUFFER_SIZE]);
}
Buffer::~Buffer()
{
}
int Buffer::getDataMemberIndex(const std::string &name) const
{
for (size_t i = 0; i < dataMembers.size(); i++)
{
if (dataMembers[i].decl.name == name)
return (int) i;
}
return -1;
}
std::vector<Buffer::DataDeclaration> Buffer::getCommonFormatDeclaration(CommonFormat format)
{
switch (format)
{
case CommonFormat::NONE:
return {};
case CommonFormat::XYf:
return {
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }
};
case CommonFormat::XYZf:
return {
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC3 }
};
case CommonFormat::RGBAub:
return {
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }
};
case CommonFormat::STf_RGBAub:
return {
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
};
case CommonFormat::STPf_RGBAub:
return {
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 },
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
};
case CommonFormat::XYf_STf:
return {
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
};
case CommonFormat::XYf_STPf:
return {
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 },
};
case CommonFormat::XYf_STf_RGBAub:
return {
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
};
case CommonFormat::XYf_STus_RGBAub:
return {
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_UNORM16_VEC2 },
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
};
case CommonFormat::XYf_STPf_RGBAub:
return {
{ getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 },
{ getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 },
};
}
return {};
}
} // graphics
} // love
+92 -38
View File
@@ -23,40 +23,102 @@
// LOVE
#include "common/config.h"
#include "common/int.h"
#include "common/Object.h"
#include "vertex.h"
#include "Resource.h"
// C
#include <stddef.h>
#include <string>
#include <vector>
namespace love
{
namespace graphics
{
class Graphics;
/**
* A block of GPU-owned memory. Currently meant for internal use.
* A block of GPU-owned memory.
**/
class Buffer : public Resource
class Buffer : public love::Object, public Resource
{
public:
static love::Type type;
enum MapFlags
{
MAP_NONE = 0,
MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified.
MAP_READ = (1 << 1),
};
Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags);
enum TypeFlags
{
TYPEFLAG_NONE = 0,
TYPEFLAG_VERTEX = 1 << BUFFERTYPE_VERTEX,
TYPEFLAG_INDEX = 1 << BUFFERTYPE_INDEX,
TYPEFLAG_TEXEL = 1 << BUFFERTYPE_TEXEL,
};
struct DataDeclaration
{
std::string name;
DataFormat format;
int arrayLength;
DataDeclaration(const std::string &name, DataFormat format, int arrayLength = 0)
: name(name)
, format(format)
, arrayLength(arrayLength)
{}
};
struct DataMember
{
DataDeclaration decl;
DataFormatInfo info;
size_t offset;
size_t size;
DataMember(const DataDeclaration &decl)
: decl(decl)
, info(getDataFormatInfo(decl.format))
, offset(0)
, size(0)
{}
};
struct Settings
{
TypeFlags typeFlags;
MapFlags mapFlags;
BufferUsage usage;
Settings(uint32 typeflags, uint32 mapflags, BufferUsage usage)
: typeFlags((TypeFlags)typeflags)
, mapFlags((MapFlags)mapflags)
, usage(usage)
{}
};
Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, size_t size, size_t arraylength);
virtual ~Buffer();
size_t getSize() const { return size; }
TypeFlags getTypeFlags() const { return typeFlags; }
BufferUsage getUsage() const { return usage; }
bool isMapped() const { return mapped; }
uint32 getMapFlags() const { return mapFlags; }
BufferType getType() const { return type; }
vertex::Usage getUsage() const { return usage; }
bool isMapped() const { return is_mapped; }
size_t getArrayLength() const { return arrayLength; }
size_t getArrayStride() const { return arrayStride; }
const std::vector<DataMember> &getDataMembers() const { return dataMembers; }
const DataMember &getDataMember(int index) const { return dataMembers[index]; }
size_t getMemberOffset(int index) const { return dataMembers[index].offset; }
int getDataMemberIndex(const std::string &name) const;
/**
* Map the Buffer to client memory.
@@ -80,10 +142,6 @@ public:
/**
* Fill a portion of the buffer with data and marks the range as modified.
*
* @param offset The offset in the GLBuffer to store the data.
* @param size The size of the incoming data.
* @param data Pointer to memory to copy data from.
*/
virtual void fill(size_t offset, size_t size, const void *data) = 0;
@@ -92,58 +150,54 @@ public:
**/
virtual void copyTo(size_t offset, size_t size, Buffer *other, size_t otheroffset) = 0;
uint32 getMapFlags() const { return map_flags; }
/**
* Texel buffers may use an additional texture handle as well as a buffer
* handle.
**/
virtual ptrdiff_t getTexelBufferHandle() const = 0;
static std::vector<DataDeclaration> getCommonFormatDeclaration(CommonFormat format);
class Mapper
{
public:
/**
* Memory-maps a Buffer.
*/
Mapper(Buffer &buffer)
: buf(buffer)
: buffer(buffer)
{
elems = buf.map();
data = buffer.map();
}
/**
* unmaps the buffer
*/
~Mapper()
{
buf.unmap();
if (buffer.getMapFlags() & MAP_EXPLICIT_RANGE_MODIFY)
buffer.setMappedRangeModified(0, buffer.getSize());
buffer.unmap();
}
/**
* Get pointer to memory mapped region
*/
void *get()
{
return elems;
}
private:
Buffer &buf;
void *elems;
Buffer &buffer;
void *data;
}; // Mapper
protected:
std::vector<DataMember> dataMembers;
size_t arrayLength;
size_t arrayStride;
// The size of the buffer, in bytes.
size_t size;
// The type of the buffer object.
BufferType type;
TypeFlags typeFlags;
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
vertex::Usage usage;
BufferUsage usage;
uint32 map_flags;
uint32 mapFlags;
bool is_mapped;
bool mapped;
}; // Buffer
+2 -2
View File
@@ -45,7 +45,7 @@ static inline uint16 normToUint16(double n)
love::Type Font::type("Font", &Object::type);
int Font::fontCount = 0;
const vertex::CommonFormat Font::vertexFormat = vertex::CommonFormat::XYf_STus_RGBAub;
const CommonFormat Font::vertexFormat = CommonFormat::XYf_STus_RGBAub;
Font::Font(love::font::Rasterizer *r, const SamplerState &s)
: rasterizers({r})
@@ -647,7 +647,7 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<D
{
Graphics::BatchedDrawCommand streamcmd;
streamcmd.formats[0] = vertexFormat;
streamcmd.indexMode = vertex::TriangleIndexMode::QUADS;
streamcmd.indexMode = TRIANGLEINDEX_QUADS;
streamcmd.vertexCount = cmd.vertexcount;
streamcmd.texture = cmd.texture;
+2 -2
View File
@@ -51,9 +51,9 @@ public:
static love::Type type;
typedef std::vector<uint32> Codepoints;
typedef vertex::XYf_STus_RGBAub GlyphVertex;
typedef XYf_STus_RGBAub GlyphVertex;
static const vertex::CommonFormat vertexFormat;
static const CommonFormat vertexFormat;
enum AlignMode
{
+103 -65
View File
@@ -105,8 +105,6 @@ bool isDebugEnabled()
love::Type Graphics::type("graphics", &Module::type);
Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
namespace opengl { extern love::graphics::Graphics *createInstance(); }
#if defined(LOVE_MACOS) || defined(LOVE_IOS)
namespace metal { extern love::graphics::Graphics *createInstance(); }
@@ -203,10 +201,12 @@ void Graphics::createQuadIndexBuffer()
return;
size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
quadIndexBuffer = newBuffer(size, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, 0, BUFFERUSAGE_STATIC);
quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0);
Buffer::Mapper map(*quadIndexBuffer);
vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get());
fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.data);
}
Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
@@ -234,7 +234,7 @@ Video *Graphics::newVideo(love::video::VideoStream *stream, float dpiscale)
return new Video(this, stream, dpiscale);
}
love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, BufferUsage usage)
{
return new SpriteBatch(this, texture, size, usage);
}
@@ -244,13 +244,8 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in
return new ParticleSystem(texture, size);
}
ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &optsource)
ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info)
{
if (stage == ShaderStage::STAGE_MAX_ENUM)
throw love::Exception("Invalid shader stage.");
const std::string &source = optsource.empty() ? getCurrentDefaultShaderCode().source[stage] : optsource;
ShaderStage *s = nullptr;
std::string cachekey;
@@ -271,7 +266,8 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
if (s == nullptr)
{
s = newShaderStageInternal(stage, cachekey, source, usesGLSLES());
std::string glsl = Shader::createShaderStageCode(this, stage, source, info);
s = newShaderStageInternal(stage, cachekey, glsl, usesGLSLES());
if (!cachekey.empty())
cachedShaderStages[stage][cachekey] = s;
}
@@ -279,37 +275,71 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
return s;
}
Shader *Graphics::newShader(const std::string &vertex, const std::string &pixel)
Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
{
if (vertex.empty() && pixel.empty())
throw love::Exception("Error creating shader: no source code!");
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
StrongRef<ShaderStage> vertexstage(newShaderStage(ShaderStage::STAGE_VERTEX, vertex), Acquire::NORETAIN);
StrongRef<ShaderStage> pixelstage(newShaderStage(ShaderStage::STAGE_PIXEL, pixel), Acquire::NORETAIN);
bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
validstages[ShaderStage::STAGE_VERTEX] = true;
validstages[ShaderStage::STAGE_PIXEL] = true;
return newShaderInternal(vertexstage.get(), pixelstage.get());
for (const std::string &source : stagessource)
{
Shader::SourceInfo info = Shader::getSourceInfo(source);
bool isanystage = false;
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
{
if (!validstages[i])
continue;
if (info.isStage[i])
{
isanystage = true;
stages[i].set(newShaderStage((ShaderStage::StageType) i, source, info), Acquire::NORETAIN);
}
}
if (!isanystage)
throw love::Exception("Could not parse shader code (missing 'position' or 'effect' function?)");
}
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
{
auto stype = (ShaderStage::StageType) i;
if (validstages[i] && stages[i].get() == nullptr)
{
const std::string &source = Shader::getDefaultCode(Shader::STANDARD_DEFAULT, stype);
Shader::SourceInfo info = Shader::getSourceInfo(source);
stages[i].set(newShaderStage(stype, source, info), Acquire::NORETAIN);
}
}
return newShaderInternal(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL]);
}
Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, vertex::Usage usage)
Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength)
{
return newMesh(Mesh::getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage);
std::vector<Buffer::DataDeclaration> dataformat = {{"", format, 0}};
return newBuffer(settings, dataformat, data, size, arraylength);
}
Mesh *Graphics::newMesh(int vertexcount, PrimitiveType drawmode, vertex::Usage usage)
{
return newMesh(Mesh::getDefaultVertexFormat(), vertexcount, drawmode, usage);
}
love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage)
Mesh *Graphics::newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
{
return new Mesh(this, vertexformat, vertexcount, drawmode, usage);
}
love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage)
Mesh *Graphics::newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
{
return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
}
Mesh *Graphics::newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode)
{
return new Mesh(attributes, drawmode);
}
love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector<Font::ColoredString> &text)
{
return new Text(font, text);
@@ -320,26 +350,44 @@ void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std::
cachedShaderStages[type].erase(hashkey);
}
bool Graphics::validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err)
bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, std::string &err)
{
if (vertex.empty() && pixel.empty())
{
err = "Error validating shader: no source code!";
return false;
}
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM] = {};
StrongRef<ShaderStage> vertexstage;
StrongRef<ShaderStage> pixelstage;
bool validstages[ShaderStage::STAGE_MAX_ENUM] = {};
validstages[ShaderStage::STAGE_VERTEX] = true;
validstages[ShaderStage::STAGE_PIXEL] = true;
// Don't use cached shader stages, since the gles flag may not match the
// current renderer.
if (!vertex.empty())
vertexstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_VERTEX, vertex, gles), Acquire::NORETAIN);
for (const std::string &source : stagessource)
{
Shader::SourceInfo info = Shader::getSourceInfo(source);
bool isanystage = false;
if (!pixel.empty())
pixelstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_PIXEL, pixel, gles), Acquire::NORETAIN);
for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++)
{
auto stype = (ShaderStage::StageType) i;
return Shader::validate(vertexstage.get(), pixelstage.get(), err);
if (!validstages[i])
continue;
if (info.isStage[i])
{
isanystage = true;
std::string glsl = Shader::createShaderStageCode(this, stype, source, info);
stages[i].set(new ShaderStageForValidation(this, stype, glsl, gles), Acquire::NORETAIN);
}
}
if (!isanystage)
{
err = "Could not parse shader code (missing 'position' or 'effect' function?)";
return false;
}
}
return Shader::validate(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL], err);
}
int Graphics::getWidth() const
@@ -942,7 +990,7 @@ CullMode Graphics::getMeshCullMode() const
return states.back().meshCullMode;
}
vertex::Winding Graphics::getFrontFaceWinding() const
Winding Graphics::getFrontFaceWinding() const
{
return states.back().winding;
}
@@ -1031,8 +1079,6 @@ void Graphics::captureScreenshot(const ScreenshotInfo &info)
Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawCommand &cmd)
{
using namespace vertex;
BatchedDrawState &state = batchedDrawState;
bool shouldflush = false;
@@ -1040,7 +1086,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
if (cmd.primitiveMode != state.primitiveMode
|| cmd.formats[0] != state.formats[0] || cmd.formats[1] != state.formats[1]
|| ((cmd.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0))
|| ((cmd.indexMode != TRIANGLEINDEX_NONE) != (state.indexCount > 0))
|| cmd.texture != state.texture
|| cmd.standardShaderType != state.standardShaderType)
{
@@ -1050,7 +1096,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
int totalvertices = state.vertexCount + cmd.vertexCount;
// We only support uint16 index buffers for now.
if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TriangleIndexMode::NONE)
if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TRIANGLEINDEX_NONE)
shouldflush = true;
int reqIndexCount = getIndexCount(cmd.indexMode, cmd.vertexCount);
@@ -1079,7 +1125,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
newdatasizes[i] = stride * cmd.vertexCount;
}
if (cmd.indexMode != TriangleIndexMode::NONE)
if (cmd.indexMode != TRIANGLEINDEX_NONE)
{
size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16);
@@ -1117,18 +1163,18 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
if (state.vb[i]->getSize() < buffersizes[i])
{
delete state.vb[i];
state.vb[i] = newStreamBuffer(BUFFER_VERTEX, buffersizes[i]);
state.vb[i] = newStreamBuffer(BUFFERTYPE_VERTEX, buffersizes[i]);
}
}
if (state.indexBuffer->getSize() < buffersizes[2])
{
delete state.indexBuffer;
state.indexBuffer = newStreamBuffer(BUFFER_INDEX, buffersizes[2]);
state.indexBuffer = newStreamBuffer(BUFFERTYPE_INDEX, buffersizes[2]);
}
}
if (cmd.indexMode != TriangleIndexMode::NONE)
if (cmd.indexMode != TRIANGLEINDEX_NONE)
{
if (state.indexBufferMap.data == nullptr)
state.indexBufferMap = state.indexBuffer->map(reqIndexSize);
@@ -1165,14 +1211,12 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman
void Graphics::flushBatchedDraws()
{
using namespace vertex;
auto &sbstate = batchedDrawState;
if (sbstate.vertexCount == 0 && sbstate.indexCount == 0)
return;
Attributes attributes;
VertexAttributes attributes;
BufferBindings buffers;
size_t usedsizes[3] = {0, 0, 0};
@@ -1315,8 +1359,8 @@ void Graphics::points(const Vector2 *positions, const Colorf *colors, size_t num
BatchedDrawCommand cmd;
cmd.primitiveMode = PRIMITIVE_POINTS;
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
cmd.formats[1] = vertex::CommonFormat::RGBAub;
cmd.formats[0] = getSinglePositionFormat(is2D);
cmd.formats[1] = CommonFormat::RGBAub;
cmd.vertexCount = (int) numpoints;
BatchedVertexData data = requestBatchedDraw(cmd);
@@ -1610,9 +1654,9 @@ void Graphics::polygon(DrawMode mode, const Vector2 *coords, size_t count, bool
bool is2D = t.isAffine2DTransform();
BatchedDrawCommand cmd;
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
cmd.formats[1] = vertex::CommonFormat::RGBAub;
cmd.indexMode = vertex::TriangleIndexMode::FAN;
cmd.formats[0] = getSinglePositionFormat(is2D);
cmd.formats[1] = CommonFormat::RGBAub;
cmd.indexMode = TRIANGLEINDEX_FAN;
cmd.vertexCount = (int)count - (skipLastFilledVertex ? 1 : 0);
BatchedVertexData data = requestBatchedDraw(cmd);
@@ -1786,14 +1830,6 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
return p;
}
const Graphics::DefaultShaderCode &Graphics::getCurrentDefaultShaderCode() const
{
int languageindex = (int) getShaderLanguageTarget();
int gammaindex = isGammaCorrect() ? 1 : 0;
return defaultShaderCode[Shader::STANDARD_DEFAULT][languageindex][gammaindex];
}
/**
* Constants.
**/
@@ -1939,6 +1975,7 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM>::Entry Graphics::featur
{ "glsl3", FEATURE_GLSL3 },
{ "glsl4", FEATURE_GLSL4 },
{ "instancing", FEATURE_INSTANCING },
{ "texelbuffer", FEATURE_TEXEL_BUFFER },
};
StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries));
@@ -1950,6 +1987,7 @@ StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::syst
{ "texturelayers", LIMIT_TEXTURE_LAYERS },
{ "volumetexturesize", LIMIT_VOLUME_TEXTURE_SIZE },
{ "cubetexturesize", LIMIT_CUBE_TEXTURE_SIZE },
{ "texelbuffersize", LIMIT_TEXEL_BUFFER_SIZE },
{ "rendertargets", LIMIT_RENDER_TARGETS },
{ "texturemsaa", LIMIT_TEXTURE_MSAA },
{ "anisotropy", LIMIT_ANISOTROPY },
+26 -35
View File
@@ -143,6 +143,7 @@ public:
FEATURE_GLSL3,
FEATURE_GLSL4,
FEATURE_INSTANCING,
FEATURE_TEXEL_BUFFER,
FEATURE_MAX_ENUM
};
@@ -161,6 +162,7 @@ public:
LIMIT_VOLUME_TEXTURE_SIZE,
LIMIT_CUBE_TEXTURE_SIZE,
LIMIT_TEXTURE_LAYERS,
LIMIT_TEXEL_BUFFER_SIZE,
LIMIT_RENDER_TARGETS,
LIMIT_TEXTURE_MSAA,
LIMIT_ANISOTROPY,
@@ -210,8 +212,8 @@ public:
{
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
const vertex::Attributes *attributes;
const vertex::BufferBindings *buffers;
const VertexAttributes *attributes;
const BufferBindings *buffers;
int vertexStart = 0;
int vertexCount = 0;
@@ -222,7 +224,7 @@ public:
// TODO: This should be moved out to a state transition API?
CullMode cullMode = CULL_NONE;
DrawCommand(const vertex::Attributes *attribs, const vertex::BufferBindings *buffers)
DrawCommand(const VertexAttributes *attribs, const BufferBindings *buffers)
: attributes(attribs)
, buffers(buffers)
{}
@@ -232,8 +234,8 @@ public:
{
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
const vertex::Attributes *attributes;
const vertex::BufferBindings *buffers;
const VertexAttributes *attributes;
const BufferBindings *buffers;
int indexCount = 0;
int instanceCount = 1;
@@ -247,7 +249,7 @@ public:
// TODO: This should be moved out to a state transition API?
CullMode cullMode = CULL_NONE;
DrawIndexedCommand(const vertex::Attributes *attribs, const vertex::BufferBindings *buffers, Resource *indexbuffer)
DrawIndexedCommand(const VertexAttributes *attribs, const BufferBindings *buffers, Resource *indexbuffer)
: attributes(attribs)
, buffers(buffers)
, indexBuffer(indexbuffer)
@@ -257,8 +259,8 @@ public:
struct BatchedDrawCommand
{
PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
vertex::CommonFormat formats[2];
vertex::TriangleIndexMode indexMode = vertex::TriangleIndexMode::NONE;
CommonFormat formats[2];
TriangleIndexMode indexMode = TRIANGLEINDEX_NONE;
int vertexCount = 0;
Texture *texture = nullptr;
Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
@@ -266,7 +268,7 @@ public:
BatchedDrawCommand()
{
// VS2013 can't initialize arrays in the above manner...
formats[1] = formats[0] = vertex::CommonFormat::NONE;
formats[1] = formats[0] = CommonFormat::NONE;
}
};
@@ -416,11 +418,6 @@ public:
}
};
struct DefaultShaderCode
{
std::string source[ShaderStage::STAGE_MAX_ENUM];
};
Graphics();
virtual ~Graphics();
@@ -434,22 +431,21 @@ public:
Font *newDefaultFont(int size, font::TrueTypeRasterizer::Hinting hinting);
Video *newVideo(love::video::VideoStream *stream, float dpiscale);
SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage);
SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferUsage usage);
ParticleSystem *newParticleSystem(Texture *texture, int size);
ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
Shader *newShader(const std::string &vertex, const std::string &pixel);
Shader *newShader(const std::vector<std::string> &stagessource);
virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) = 0;
virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength);
Mesh *newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, vertex::Usage usage);
Mesh *newMesh(int vertexcount, PrimitiveType drawmode, vertex::Usage usage);
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage);
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage);
Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
Mesh *newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode);
Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
bool validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err);
bool validateShader(bool gles, const std::vector<std::string> &stages, std::string &err);
/**
* Resets the current color, background color, line style, and so forth.
@@ -589,8 +585,8 @@ public:
void setMeshCullMode(CullMode cull);
CullMode getMeshCullMode() const;
virtual void setFrontFaceWinding(vertex::Winding winding) = 0;
vertex::Winding getFrontFaceWinding() const;
virtual void setFrontFaceWinding(Winding winding) = 0;
Winding getFrontFaceWinding() const;
/**
* Sets the enabled color components when rendering.
@@ -826,16 +822,13 @@ public:
virtual void draw(const DrawCommand &cmd) = 0;
virtual void draw(const DrawIndexedCommand &cmd) = 0;
virtual void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture) = 0;
virtual void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, Texture *texture) = 0;
void flushBatchedDraws();
BatchedVertexData requestBatchedDraw(const BatchedDrawCommand &command);
static void flushBatchedDrawsGlobal();
virtual Shader::Language getShaderLanguageTarget() const = 0;
const DefaultShaderCode &getCurrentDefaultShaderCode() const;
void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey);
template <typename T>
@@ -877,9 +870,6 @@ public:
static bool getConstant(StackType in, const char *&out);
static std::vector<std::string> getConstants(StackType);
// Default shader code (a shader is always required internally.)
static DefaultShaderCode defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
protected:
struct DisplayState
@@ -904,7 +894,7 @@ protected:
bool depthWrite = false;
CullMode meshCullMode = CULL_NONE;
vertex::Winding winding = vertex::WINDING_CCW;
Winding winding = WINDING_CCW;
StrongRef<Font> font;
StrongRef<Shader> shader;
@@ -924,7 +914,7 @@ protected:
StreamBuffer *indexBuffer = nullptr;
PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
vertex::CommonFormat formats[2];
CommonFormat formats[2];
StrongRef<Texture> texture;
Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
int vertexCount = 0;
@@ -936,7 +926,7 @@ protected:
BatchedDrawState()
{
vb[0] = vb[1] = nullptr;
formats[0] = formats[1] = vertex::CommonFormat::NONE;
formats[0] = formats[1] = CommonFormat::NONE;
vbMap[0] = vbMap[1] = StreamBuffer::MapInfo();
}
};
@@ -952,6 +942,7 @@ protected:
{}
};
ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info);
virtual ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0;
virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;
+188 -192
View File
@@ -34,36 +34,22 @@ namespace love
namespace graphics
{
static const char *getBuiltinAttribName(BuiltinVertexAttribute attribid)
{
const char *name = "";
vertex::getConstant(attribid, name);
return name;
}
static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct");
static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
std::vector<Mesh::AttribFormat> Mesh::getDefaultVertexFormat()
std::vector<Buffer::DataDeclaration> Mesh::getDefaultVertexFormat()
{
// Corresponds to the love::Vertex struct.
std::vector<Mesh::AttribFormat> vertexformat = {
{ getBuiltinAttribName(ATTRIB_POS), vertex::DATA_FLOAT, 2 },
{ getBuiltinAttribName(ATTRIB_TEXCOORD), vertex::DATA_FLOAT, 2 },
{ getBuiltinAttribName(ATTRIB_COLOR), vertex::DATA_UNORM8, 4 },
};
return vertexformat;
return Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub);
}
love::Type Mesh::type("Mesh", &Drawable::type);
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage)
: vertexFormat(vertexformat)
, vertexBuffer(nullptr)
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)
@@ -72,29 +58,29 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
, rangeStart(-1)
, rangeCount(-1)
{
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);
vertexCount = vertexBuffer->getArrayLength();
vertexStride = vertexBuffer->getArrayStride();
vertexFormat = vertexBuffer->getDataMembers();
setupAttachedAttributes();
calculateAttributeSizes(gfx);
vertexCount = datasize / vertexStride;
indexDataType = vertex::getIndexDataTypeFromMax(vertexCount);
if (vertexCount == 0)
throw love::Exception("Data size is too small for specified vertex attribute formats.");
vertexBuffer = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
indexDataType = getIndexDataTypeFromMax(vertexCount);
vertexScratchBuffer = new char[vertexStride];
}
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage)
: vertexFormat(vertexformat)
, vertexBuffer(nullptr)
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)
, indexDataType(vertex::getIndexDataTypeFromMax(vertexcount))
, indexDataType(getIndexDataTypeFromMax(vertexcount))
, primitiveType(drawmode)
, rangeStart(-1)
, rangeCount(-1)
@@ -102,83 +88,82 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
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);
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, nullptr, 0, vertexcount), Acquire::NORETAIN);
vertexStride = vertexBuffer->getArrayStride();
vertexFormat = vertexBuffer->getDataMembers();
setupAttachedAttributes();
calculateAttributeSizes(gfx);
size_t buffersize = vertexCount * vertexStride;
vertexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
// Initialize the buffer's contents to 0.
memset(vertexBuffer->map(), 0, buffersize);
memset(vertexBuffer->map(), 0, vertexBuffer->getSize());
vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize());
vertexBuffer->unmap();
vertexScratchBuffer = new char[vertexStride];
}
Mesh::~Mesh()
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)
{
delete vertexBuffer;
delete indexBuffer;
delete vertexScratchBuffer;
if (attributes.size() == 0)
throw love::Exception("At least one buffer attribute must be specified in this constructor.");
attachedAttributes = attributes;
vertexCount = attachedAttributes.size() > 0 ? LOVE_UINT32_MAX : 0;
for (const auto &attrib : attachedAttributes)
{
if (attrib.second.mesh != this)
attrib.second.mesh->release();
if ((attrib.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.");
if (getAttachedAttributeIndex(attrib.name) != -1)
throw love::Exception("Duplicate vertex attribute name: %s", attrib.name.c_str());
vertexCount = std::min(vertexCount, attrib.buffer->getArrayLength());
}
indexDataType = getIndexDataTypeFromMax(vertexCount);
}
Mesh::~Mesh()
{
delete vertexScratchBuffer;
}
void Mesh::setupAttachedAttributes()
{
for (size_t i = 0; i < vertexFormat.size(); i++)
{
const std::string &name = vertexFormat[i].name;
const std::string &name = vertexFormat[i].decl.name;
if (attachedAttributes.find(name) != attachedAttributes.end())
if (getAttachedAttributeIndex(name) != -1)
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true};
attachedAttributes.push_back({name, vertexBuffer, (int) i, STEP_PER_VERTEX, true});
}
}
void Mesh::calculateAttributeSizes(Graphics *gfx)
int Mesh::getAttachedAttributeIndex(const std::string &name) const
{
bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
size_t stride = 0;
for (const AttribFormat &format : vertexFormat)
for (int i = 0; i < (int) attachedAttributes.size(); i++)
{
size_t size = vertex::getDataTypeSize(format.type) * format.components;
if (format.components <= 0 || format.components > 4)
throw love::Exception("Vertex attributes must have between 1 and 4 components.");
// Hardware really doesn't like attributes that aren't 32 bit-aligned.
if (size % 4 != 0)
throw love::Exception("Vertex attributes must have enough components to be a multiple of 32 bits.");
if (vertex::isDataTypeInteger(format.type) && !supportsGLSL3)
throw love::Exception("Integer vertex attribute data types require GLSL 3 support.");
// Total size in bytes of each attribute in a single vertex.
attributeSizes.push_back(size);
stride += size;
if (attachedAttributes[i].name == name)
return i;
}
vertexStride = stride;
}
size_t Mesh::getAttributeOffset(size_t attribindex) const
{
size_t offset = 0;
for (size_t i = 0; i < attribindex; i++)
offset += attributeSizes[i];
return offset;
return -1;
}
void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
@@ -186,6 +171,9 @@ void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
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.");
size_t offset = vertindex * vertexStride;
size_t size = std::min(datasize, vertexStride);
@@ -200,6 +188,9 @@ 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);
@@ -223,8 +214,13 @@ void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *dat
if (attribindex >= (int) vertexFormat.size())
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
size_t size = std::min(datasize, attributeSizes[attribindex]);
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);
@@ -240,8 +236,13 @@ size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, s
if (attribindex >= (int) vertexFormat.size())
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
size_t size = std::min(datasize, attributeSizes[attribindex]);
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();
@@ -260,128 +261,106 @@ size_t Mesh::getVertexStride() const
return vertexStride;
}
const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
Buffer *Mesh::getVertexBuffer() const
{
return vertexBuffer;
}
const std::vector<Buffer::DataMember> &Mesh::getVertexFormat() const
{
return vertexFormat;
}
vertex::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
{
if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
components = vertexFormat[attribindex].components;
return vertexFormat[attribindex].type;
}
int Mesh::getAttributeIndex(const std::string &name) const
{
for (int i = 0; i < (int) vertexFormat.size(); i++)
{
if (vertexFormat[i].name == name)
return i;
}
return -1;
}
void Mesh::setAttributeEnabled(const std::string &name, bool enable)
{
auto it = attachedAttributes.find(name);
if (it == attachedAttributes.end())
int index = getAttachedAttributeIndex(name);
if (index == -1)
throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
it->second.enabled = enable;
attachedAttributes[index].enabled = enable;
}
bool Mesh::isAttributeEnabled(const std::string &name) const
{
const auto it = attachedAttributes.find(name);
if (it == attachedAttributes.end())
int index = getAttachedAttributeIndex(name);
if (index == -1)
throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
return it->second.enabled;
return attachedAttributes[index].enabled;
}
void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step)
void Mesh::attachAttribute(const std::string &name, Buffer *buffer, 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.");
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
throw love::Exception("Vertex attribute instancing is not supported on this system.");
if (mesh != this)
{
for (const auto &it : mesh->attachedAttributes)
{
// If the supplied Mesh has attached attributes of its own, then we
// prevent it from being attached to avoid reference cycles.
if (it.second.mesh != mesh)
throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own.");
}
}
BufferAttribute oldattrib = {};
BufferAttribute newattrib = {};
AttachedAttribute oldattrib = {};
AttachedAttribute newattrib = {};
int oldindex = getAttachedAttributeIndex(name);
if (oldindex != -1)
oldattrib = attachedAttributes[oldindex];
else if (attachedAttributes.size() + 1 > VertexAttributes::MAX)
throw love::Exception("A maximum of %d attributes can be attached at once.", VertexAttributes::MAX);
auto it = attachedAttributes.find(name);
if (it != attachedAttributes.end())
oldattrib = it->second;
else if (attachedAttributes.size() + 1 > vertex::Attributes::MAX)
throw love::Exception("A maximum of %d attributes can be attached at once.", vertex::Attributes::MAX);
newattrib.mesh = mesh;
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
newattrib.index = mesh->getAttributeIndex(attachname);
newattrib.name = name;
newattrib.buffer = buffer;
newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname);
newattrib.step = step;
if (newattrib.index < 0)
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
if (newattrib.indexInBuffer < 0)
throw love::Exception("The specified vertex buffer does not have a vertex attribute named '%s'", attachname.c_str());
if (newattrib.mesh != this)
newattrib.mesh->retain();
attachedAttributes[name] = newattrib;
if (oldattrib.mesh && oldattrib.mesh != this)
oldattrib.mesh->release();
if (oldindex != -1)
attachedAttributes[oldindex] = newattrib;
else
attachedAttributes.push_back(newattrib);
}
bool Mesh::detachAttribute(const std::string &name)
{
auto it = attachedAttributes.find(name);
int index = getAttachedAttributeIndex(name);
if (index == -1)
return false;
if (it != attachedAttributes.end() && it->second.mesh != this)
{
it->second.mesh->release();
attachedAttributes.erase(it);
attachedAttributes.erase(attachedAttributes.begin() + index);
if (getAttributeIndex(name) != -1)
attachAttribute(name, this, name);
if (vertexBuffer.get() && vertexBuffer->getDataMemberIndex(name) != -1)
attachAttribute(name, vertexBuffer, name);
return true;
}
return true;
}
return false;
const std::vector<Mesh::BufferAttribute> &Mesh::getAttachedAttributes() const
{
return attachedAttributes;
}
void *Mesh::mapVertexData()
{
return vertexBuffer->map();
return vertexBuffer.get() != nullptr ? vertexBuffer->map() : nullptr;
}
void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
{
if (!vertexBuffer.get())
return;
vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize);
vertexBuffer->unmap();
}
void Mesh::flush()
{
vertexBuffer->unmap();
if (vertexBuffer.get())
vertexBuffer->unmap();
if (indexBuffer != nullptr)
if (indexBuffer.get())
indexBuffer->unmap();
}
@@ -391,7 +370,7 @@ void Mesh::flush()
template <typename T>
static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
{
T *elems = (T *) buffermap.get();
T *elems = (T *) buffermap.data;
for (size_t i = 0; i < indices.size(); i++)
{
@@ -406,21 +385,18 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
{
size_t maxval = getVertexCount();
IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval);
IndexDataType datatype = getIndexDataTypeFromMax(maxval);
DataFormat dataformat = getIndexDataFormat(datatype);
// Calculate the size in bytes of the index buffer data.
size_t size = map.size() * vertex::getIndexDataSize(datatype);
size_t size = map.size() * getIndexDataSize(datatype);
if (indexBuffer && size > indexBuffer->getSize())
{
delete indexBuffer;
indexBuffer = nullptr;
}
if (!indexBuffer && size > 0)
if (indexBuffer.get() == nullptr || size > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat)
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
indexBuffer = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN);
}
useIndexBuffer = true;
@@ -448,25 +424,23 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize)
{
if (indexBuffer && datasize > indexBuffer->getSize())
{
delete indexBuffer;
indexBuffer = nullptr;
}
DataFormat dataformat = getIndexDataFormat(datatype);
if (!indexBuffer && datasize > 0)
if (indexBuffer.get() == nullptr || datasize > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat)
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
indexBuffer = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN);
}
indexCount = datasize / vertex::getIndexDataSize(datatype);
indexCount = datasize / getIndexDataSize(datatype);
if (!indexBuffer || indexCount == 0)
return;
Buffer::Mapper ibomap(*indexBuffer);
memcpy(ibomap.get(), data, datasize);
memcpy(ibomap.data, data, datasize);
useIndexBuffer = true;
indexDataType = datatype;
@@ -499,6 +473,9 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
if (!indexBuffer || 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();
@@ -517,7 +494,27 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
return true;
}
size_t Mesh::getVertexMapCount() const
void Mesh::setIndexBuffer(Buffer *buffer)
{
// Buffer constructor does the rest of the validation for index buffers
// (data member formats, etc.)
if (buffer != nullptr && (buffer->getTypeFlags() & Buffer::TYPEFLAG_INDEX) == 0)
throw love::Exception("setIndexBuffer requires a Buffer created as an index buffer.");
indexBuffer.set(buffer);
useIndexBuffer = buffer != nullptr;
indexCount = buffer != nullptr ? buffer->getArrayLength() : 0;
if (buffer != nullptr)
indexDataType = getIndexDataType(buffer->getDataMember(0).decl.format);
}
Buffer *Mesh::getIndexBuffer() const
{
return indexBuffer;
}
size_t Mesh::getIndexCount() const
{
return indexCount;
}
@@ -592,43 +589,42 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
if (Shader::current && texture.get())
Shader::current->checkMainTexture(texture);
vertex::Attributes attributes;
vertex::BufferBindings buffers;
VertexAttributes attributes;
BufferBindings buffers;
int activebuffers = 0;
for (const auto &attrib : attachedAttributes)
{
if (!attrib.second.enabled)
if (!attrib.enabled)
continue;
Mesh *mesh = attrib.second.mesh;
Buffer *buffer = attrib.buffer.get();
int attributeindex = -1;
// If the attribute is one of the LOVE-defined ones, use the constant
// attribute index for it, otherwise query the index from the shader.
BuiltinVertexAttribute builtinattrib;
if (vertex::getConstant(attrib.first.c_str(), builtinattrib))
if (getConstant(attrib.name.c_str(), builtinattrib))
attributeindex = (int) builtinattrib;
else if (Shader::current)
attributeindex = Shader::current->getVertexAttributeIndex(attrib.first);
attributeindex = Shader::current->getVertexAttributeIndex(attrib.name);
if (attributeindex >= 0)
{
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
mesh->vertexBuffer->unmap();
buffer->unmap();
const auto &formats = mesh->getVertexFormat();
const auto &format = formats[attrib.second.index];
const auto &member = buffer->getDataMember(attrib.indexInBuffer);
uint16 offset = (uint16) mesh->getAttributeOffset(attrib.second.index);
uint16 stride = (uint16) mesh->getVertexStride();
uint16 offset = (uint16) member.offset;
uint16 stride = (uint16) buffer->getArrayStride();
attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers);
attributes.setBufferLayout(activebuffers, stride, attrib.second.step);
attributes.set(attributeindex, member.decl.format, offset, activebuffers);
attributes.setBufferLayout(activebuffers, stride, attrib.step);
// TODO: Ideally we want to reuse buffers with the same stride+step.
buffers.set(activebuffers, mesh->vertexBuffer, 0);
buffers.set(activebuffers, buffer, 0);
activebuffers++;
}
}
@@ -653,7 +649,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
cmd.cullMode = gfx->getMeshCullMode();
int start = std::min(std::max(0, rangeStart), (int) indexCount - 1);
cmd.indexBufferOffset = start * vertex::getIndexDataSize(indexDataType);
cmd.indexBufferOffset = start * getIndexDataSize(indexDataType);
cmd.indexCount = (int) indexCount;
if (rangeCount > 0)
+30 -30
View File
@@ -50,17 +50,20 @@ class Mesh : public Drawable
{
public:
static love::Type type;
struct AttribFormat
struct BufferAttribute
{
std::string name;
vertex::DataType type;
int components; // max 4
StrongRef<Buffer> buffer;
int indexInBuffer;
AttributeStep step;
bool enabled;
};
Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage);
Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage);
static love::Type type;
Mesh(Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
Mesh(Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
Mesh(const std::vector<BufferAttribute> &attributes, PrimitiveType drawmode);
virtual ~Mesh();
@@ -92,12 +95,15 @@ public:
**/
size_t getVertexStride() const;
/**
* Gets the Buffer that holds the Mesh's vertices.
**/
Buffer *getVertexBuffer() const;
/**
* Gets the format of each vertex attribute stored in the Mesh.
**/
const std::vector<AttribFormat> &getVertexFormat() const;
vertex::DataType getAttributeInfo(int attribindex, int &components) const;
int getAttributeIndex(const std::string &name) const;
const std::vector<Buffer::DataMember> &getVertexFormat() const;
/**
* Sets whether a specific vertex attribute is used when drawing the Mesh.
@@ -106,11 +112,12 @@ public:
bool isAttributeEnabled(const std::string &name) const;
/**
* Attaches a vertex attribute from another Mesh to this one. The attribute
* will be used when drawing this Mesh.
* Attaches a vertex attribute from another vertex buffer to this Mesh. The
* attribute will be used when drawing this Mesh.
**/
void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
void attachAttribute(const std::string &name, Buffer *buffer, 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);
@@ -136,10 +143,13 @@ public:
**/
bool getVertexMap(std::vector<uint32> &map) const;
void setIndexBuffer(Buffer *buffer);
Buffer *getIndexBuffer() const;
/**
* Gets the total number of elements in the vertex map array.
**/
size_t getVertexMapCount() const;
size_t getIndexCount() const;
/**
* Sets the texture used when drawing the Mesh.
@@ -172,31 +182,21 @@ public:
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
static std::vector<AttribFormat> getDefaultVertexFormat();
static std::vector<Buffer::DataDeclaration> getDefaultVertexFormat();
private:
friend class SpriteBatch;
struct AttachedAttribute
{
Mesh *mesh;
int index;
AttributeStep step;
bool enabled;
};
void setupAttachedAttributes();
void calculateAttributeSizes(Graphics *gfx);
size_t getAttributeOffset(size_t attribindex) const;
int getAttachedAttributeIndex(const std::string &name) const;
std::vector<AttribFormat> vertexFormat;
std::vector<size_t> attributeSizes;
std::vector<Buffer::DataMember> vertexFormat;
std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
std::vector<BufferAttribute> attachedAttributes;
// Vertex buffer, for the vertex data.
Buffer *vertexBuffer;
StrongRef<Buffer> vertexBuffer;
size_t vertexCount;
size_t vertexStride;
@@ -205,7 +205,7 @@ private:
char *vertexScratchBuffer;
// Index buffer, for the vertex map.
Buffer *indexBuffer;
StrongRef<Buffer> indexBuffer;
bool useIndexBuffer;
size_t indexCount;
IndexDataType indexDataType;
+7 -4
View File
@@ -93,7 +93,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
, defaultOffset(true)
, relativeRotation(false)
, vertexAttributes(vertex::CommonFormat::XYf_STf_RGBAub, 0)
, vertexAttributes(CommonFormat::XYf_STf_RGBAub, 0)
, buffer(nullptr)
{
if (size == 0 || size > MAX_PARTICLES)
@@ -191,7 +191,9 @@ void ParticleSystem::createBuffers(size_t size)
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
size_t bytes = sizeof(Vertex) * size * 4;
buffer = gfx->newBuffer(bytes, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0);
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STREAM);
auto decl = Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub);
buffer = gfx->newBuffer(settings, decl, nullptr, bytes, 0);
}
catch (std::bad_alloc &)
{
@@ -203,7 +205,8 @@ void ParticleSystem::createBuffers(size_t size)
void ParticleSystem::deleteBuffers()
{
delete[] pMem;
delete buffer;
if (buffer)
buffer->release();
pMem = nullptr;
buffer = nullptr;
@@ -1080,7 +1083,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
Graphics::TempTransform transform(gfx, m);
vertex::BufferBindings vertexbuffers;
BufferBindings vertexbuffers;
vertexbuffers.set(0, buffer, 0);
gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture);
+1 -1
View File
@@ -673,7 +673,7 @@ private:
bool relativeRotation;
const vertex::Attributes vertexAttributes;
const VertexAttributes vertexAttributes;
Buffer *buffer;
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
+21 -11
View File
@@ -82,7 +82,7 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo
// extra degenerate triangle in between the core line and the overdraw
// line in order to break up the strip into two. This will let us draw
// everything in one draw call.
if (triangle_mode == vertex::TriangleIndexMode::STRIP)
if (triangle_mode == TRIANGLEINDEX_STRIP)
extra_vertices = 2;
}
@@ -383,7 +383,7 @@ void Polyline::draw(love::graphics::Graphics *gfx)
int maxvertices = LOVE_UINT16_MAX - 3;
int advance = maxvertices;
if (triangle_mode == vertex::TriangleIndexMode::STRIP)
if (triangle_mode == TRIANGLEINDEX_STRIP)
advance -= 2;
for (int vertex_start = 0; vertex_start < total_vertex_count; vertex_start += advance)
@@ -391,8 +391,8 @@ void Polyline::draw(love::graphics::Graphics *gfx)
const Vector2 *verts = vertices + vertex_start;
Graphics::BatchedDrawCommand cmd;
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
cmd.formats[1] = vertex::CommonFormat::RGBAub;
cmd.formats[0] = getSinglePositionFormat(is2D);
cmd.formats[1] = CommonFormat::RGBAub;
cmd.indexMode = triangle_mode;
cmd.vertexCount = std::min(maxvertices, total_vertex_count - vertex_start);
@@ -405,18 +405,28 @@ void Polyline::draw(love::graphics::Graphics *gfx)
Color32 *colordata = (Color32 *) data.stream[1];
int draw_rough_count = std::min(cmd.vertexCount, (int) vertex_count - vertex_start);
// Constant vertex color up to the overdraw vertices.
for (int i = 0; i < std::min(cmd.vertexCount, (int) vertex_count - vertex_start); i++)
for (int i = 0; i < draw_rough_count; i++)
colordata[i] = curcolor;
int colorcount = 0;
if (overdraw)
colorcount = std::min(cmd.vertexCount, overdraw_count - (vertex_start - overdraw_start));
if (colorcount > 0)
{
Color32 *colors = colordata + std::max(0, (overdraw_start - vertex_start));
fill_color_array(curcolor, colors, colorcount);
int draw_remaining_count = cmd.vertexCount - draw_rough_count;
int draw_overdraw_begin = overdraw_start - vertex_start;
int draw_overdraw_end = draw_overdraw_begin + overdraw_count;
draw_overdraw_begin = std::max(0, draw_overdraw_begin);
int draw_overdraw_count = std::min(draw_remaining_count, draw_overdraw_end - draw_overdraw_begin);
if (draw_overdraw_count > 0)
{
Color32 *colors = colordata + draw_overdraw_begin;
fill_color_array(curcolor, colors, draw_overdraw_count);
}
}
}
}
+3 -3
View File
@@ -44,7 +44,7 @@ class Polyline
{
public:
Polyline(vertex::TriangleIndexMode mode = vertex::TriangleIndexMode::STRIP)
Polyline(TriangleIndexMode mode = TRIANGLEINDEX_STRIP)
: vertices(nullptr)
, overdraw(nullptr)
, vertex_count(0)
@@ -94,7 +94,7 @@ protected:
Vector2 *overdraw;
size_t vertex_count;
size_t overdraw_vertex_count;
vertex::TriangleIndexMode triangle_mode;
TriangleIndexMode triangle_mode;
size_t overdraw_vertex_start;
}; // Polyline
@@ -109,7 +109,7 @@ class NoneJoinPolyline : public Polyline
public:
NoneJoinPolyline()
: Polyline(vertex::TriangleIndexMode::QUADS)
: Polyline(TRIANGLEINDEX_QUADS)
{}
void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
+511 -22
View File
@@ -28,12 +28,402 @@
// C++
#include <string>
#include <regex>
#include <sstream>
namespace love
{
namespace graphics
{
namespace glsl
{
static const char global_syntax[] = R"(
#if !defined(GL_ES) && __VERSION__ < 140
#define lowp
#define mediump
#define highp
#endif
#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
#define LOVE_HIGHP_OR_MEDIUMP highp
#else
#define LOVE_HIGHP_OR_MEDIUMP mediump
#endif
#define number float
#define Image sampler2D
#define ArrayImage sampler2DArray
#define CubeImage samplerCube
#define VolumeImage sampler3D
#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
#define DepthImage sampler2DShadow
#define DepthArrayImage sampler2DArrayShadow
#define DepthCubeImage samplerCubeShadow
#endif
#define extern uniform
#ifdef GL_EXT_texture_array
#extension GL_EXT_texture_array : enable
#endif
#ifdef GL_OES_texture_3D
#extension GL_OES_texture_3D : enable
#endif
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
)";
static const char global_uniforms[] = R"(
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
// but we can't guarantee that highp is always supported in fragment shaders...
// We *really* don't want to use mediump for these in vertex shaders though.
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't
// do it as an initializer.
LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix;
LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix;
LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix;
LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;
LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor;
#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix)
// Alternate names
#define ViewSpaceFromLocal TransformMatrix
#define ClipSpaceFromView ProjectionMatrix
#define ClipSpaceFromLocal TransformProjectionMatrix
#define ViewNormalFromLocal NormalMatrix
void love_initializeBuiltinUniforms() {
TransformMatrix = mat4(
love_UniformsPerDraw[0],
love_UniformsPerDraw[1],
love_UniformsPerDraw[2],
love_UniformsPerDraw[3]
);
ProjectionMatrix = mat4(
love_UniformsPerDraw[4],
love_UniformsPerDraw[5],
love_UniformsPerDraw[6],
love_UniformsPerDraw[7]
);
NormalMatrix = mat3(
love_UniformsPerDraw[8].xyz,
love_UniformsPerDraw[9].xyz,
love_UniformsPerDraw[10].xyz
);
love_ScreenSize = love_UniformsPerDraw[11];
ConstantColor = love_UniformsPerDraw[12];
}
)";
static const char global_functions[] = R"(
#ifdef GL_ES
#if __VERSION__ >= 300 || defined(GL_EXT_texture_array)
precision lowp sampler2DArray;
#endif
#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
precision lowp sampler3D;
#endif
#if __VERSION__ >= 300
precision lowp sampler2DShadow;
precision lowp samplerCubeShadow;
precision lowp sampler2DArrayShadow;
#endif
#endif
#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3)
#define Texel texture
#else
#if __VERSION__ >= 130
#define texture2D Texel
#define texture3D Texel
#define textureCube Texel
#define texture2DArray Texel
#define love_texture2D texture
#define love_texture3D texture
#define love_textureCube texture
#define love_texture2DArray texture
#else
#define love_texture2D texture2D
#define love_texture3D texture3D
#define love_textureCube textureCube
#define love_texture2DArray texture2DArray
#endif
vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); }
vec4 Texel(samplerCube s, vec3 c) { return love_textureCube(s, c); }
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); }
#endif
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); }
#endif
#ifdef PIXEL
vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); }
vec4 Texel(samplerCube s, vec3 c, float b) { return love_textureCube(s, c, b); }
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); }
#endif
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); }
#endif
#endif
#define texture love_texture
#endif
float gammaToLinearPrecise(float c) {
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
}
vec3 gammaToLinearPrecise(vec3 c) {
bvec3 leq = lessThanEqual(c, vec3(0.04045));
c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4);
c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4);
c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4);
return c;
}
vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); }
float linearToGammaPrecise(float c) {
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
}
vec3 linearToGammaPrecise(vec3 c) {
bvec3 lt = lessThanEqual(c, vec3(0.0031308));
c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055;
c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055;
c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055;
return c;
}
vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); }
// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); }
mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); }
mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); }
mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
#define gammaToLinear gammaToLinearFast
#define linearToGamma linearToGammaFast
#ifdef LOVE_GAMMA_CORRECT
#define gammaCorrectColor gammaToLinear
#define unGammaCorrectColor linearToGamma
#define gammaCorrectColorPrecise gammaToLinearPrecise
#define unGammaCorrectColorPrecise linearToGammaPrecise
#define gammaCorrectColorFast gammaToLinearFast
#define unGammaCorrectColorFast linearToGammaFast
#else
#define gammaCorrectColor
#define unGammaCorrectColor
#define gammaCorrectColorPrecise
#define unGammaCorrectColorPrecise
#define gammaCorrectColorFast
#define unGammaCorrectColorFast
#endif
)";
static const char vertex_header[] = R"(
#define love_Position gl_Position
#if __VERSION__ >= 130
#define attribute in
#define varying out
#ifndef LOVE_GLSL1_ON_GLSL3
#define love_VertexID gl_VertexID
#define love_InstanceID gl_InstanceID
#endif
#endif
#ifdef GL_ES
uniform mediump float love_PointSize;
#endif
)";
static const char vertex_functions[] = R"(
void setPointSize() {
#ifdef GL_ES
gl_PointSize = love_PointSize;
#endif
}
)";
static const char vertex_main[] = R"(
attribute vec4 VertexPosition;
attribute vec4 VertexTexCoord;
attribute vec4 VertexColor;
varying vec4 VaryingTexCoord;
varying vec4 VaryingColor;
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition);
void main() {
love_initializeBuiltinUniforms();
VaryingTexCoord = VertexTexCoord;
VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
setPointSize();
love_Position = position(ClipSpaceFromLocal, VertexPosition);
}
)";
static const char pixel_header[] = R"(
#ifdef GL_ES
precision mediump float;
#endif
#define love_MaxRenderTargets gl_MaxDrawBuffers
#if __VERSION__ >= 130
#define varying in
// Some drivers seem to make the pixel shader do more work when multiple
// pixel shader outputs are defined, even when only one is actually used.
// TODO: We should use reflection or something instead of this, to determine
// how many outputs are actually used in the shader code.
#ifdef LOVE_MULTI_RENDER_TARGETS
layout(location = 0) out vec4 love_RenderTargets[love_MaxRenderTargets];
#define love_PixelColor love_RenderTargets[0]
#else
layout(location = 0) out vec4 love_PixelColor;
#endif
#else
#ifdef LOVE_MULTI_RENDER_TARGETS
#define love_RenderTargets gl_FragData
#endif
#define love_PixelColor gl_FragColor
#endif
// Legacy
#define love_MaxCanvases love_MaxRenderTargets
#define love_Canvases love_RenderTargets
#ifdef LOVE_MULTI_RENDER_TARGETS
#define LOVE_MULTI_CANVASES 1
#endif
// See Shader::updateScreenParams in Shader.cpp.
#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))
)";
static const char pixel_functions[] = R"(
uniform sampler2D love_VideoYChannel;
uniform sampler2D love_VideoCbChannel;
uniform sampler2D love_VideoCrChannel;
vec4 VideoTexel(vec2 texcoords) {
vec3 yuv;
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
vec4 color;
color.r = dot(yuv, vec3(1.164, 0.000, 1.596));
color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
color.b = dot(yuv, vec3(1.164, 2.018, 0.000));
color.a = 1.0;
return gammaCorrectColor(color);
}
)";
static const char pixel_main[] = R"(
uniform sampler2D MainTex;
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
varying mediump vec4 VaryingColor;
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord);
void main() {
love_initializeBuiltinUniforms();
love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
}
)";
static const char pixel_main_custom[] = R"(
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
varying mediump vec4 VaryingColor;
void effect();
void main() {
love_initializeBuiltinUniforms();
effect();
}
)";
struct StageInfo
{
const char *name;
const char *header;
const char *functions;
const char *main;
const char *main_custom;
};
static const StageInfo stageInfo[] =
{
{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main },
{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom },
};
static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == ShaderStage::STAGE_MAX_ENUM, "Stages array size must match ShaderStage enum.");
struct Version
{
std::string glsl;
std::string glsles;
};
// Indexed by Shader::Version
static const Version versions[] =
{
{ "#version 120", "#version 100" },
{ "#version 330 core", "#version 300 es" },
{ "#version 430 core", "#version 310 es" },
};
static Shader::Language getTargetLanguage(const std::string &src)
{
std::regex r("^\\s*#pragma language (\\w+)");
std::smatch m;
std::string langstr = std::regex_search(src, m, r) && m.size() > 1 ? m[1] : std::string("glsl1");
Shader::Language lang = Shader::LANGUAGE_MAX_ENUM;
Shader::getConstant(langstr.c_str(), lang);
return lang;
}
static bool isVertexCode(const std::string &src)
{
std::regex r("vec4\\s+position\\s*\\(");
std::smatch m;
return std::regex_search(src, m, r);
}
static bool isPixelCode(const std::string &src, bool &custompixel, bool &mrt)
{
custompixel = false;
mrt = false;
std::smatch m;
if (std::regex_search(src, m, std::regex("vec4\\s+effect\\s*\\(")))
return true;
if (std::regex_search(src, m, std::regex("void\\s+effect\\s*\\(")))
{
custompixel = true;
if (src.find("love_RenderTargets") != std::string::npos || src.find("love_Canvases") != std::string::npos)
mrt = true;
return true;
}
return false;
}
} // glsl
static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes.");
love::Type Shader::type("Shader", &Object::type);
@@ -41,6 +431,59 @@ love::Type Shader::type("Shader", &Object::type);
Shader *Shader::current = nullptr;
Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr};
Shader::SourceInfo Shader::getSourceInfo(const std::string &src)
{
SourceInfo info = {};
info.language = glsl::getTargetLanguage(src);
info.isStage[ShaderStage::STAGE_VERTEX] = glsl::isVertexCode(src);
info.isStage[ShaderStage::STAGE_PIXEL] = glsl::isPixelCode(src, info.customPixelFunction, info.usesMRT);
return info;
}
std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const Shader::SourceInfo &info)
{
if (info.language == Shader::LANGUAGE_MAX_ENUM)
throw love::Exception("Invalid shader language");
const auto &features = gfx->getCapabilities().features;
if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3])
throw love::Exception("GLSL 3 shaders are not supported on this system.");
if (info.language == LANGUAGE_GLSL4 && !features[Graphics::FEATURE_GLSL4])
throw love::Exception("GLSL 4 shaders are not supported on this system.");
bool gles = gfx->getRenderer() == Graphics::RENDERER_OPENGLES;
bool glsl1on3 = info.language == LANGUAGE_GLSL1 && features[Graphics::FEATURE_GLSL3];
Language lang = info.language;
if (glsl1on3)
lang = LANGUAGE_GLSL3;
glsl::StageInfo stageinfo = glsl::stageInfo[stage];
std::stringstream ss;
ss << (gles ? glsl::versions[lang].glsles : glsl::versions[lang].glsl) << "\n";
ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n";
if (glsl1on3)
ss << "#define LOVE_GLSL1_ON_GLSL3 1\n";
if (isGammaCorrect())
ss << "#define LOVE_GAMMA_CORRECT 1\n";
if (info.usesMRT)
ss << "#define LOVE_MULTI_RENDER_TARGETS 1";
ss << glsl::global_syntax;
ss << stageinfo.header;
ss << glsl::global_uniforms;
ss << glsl::global_functions;
ss << stageinfo.functions;
ss << (info.customPixelFunction ? stageinfo.main_custom : stageinfo.main);
ss << ((!gles && (lang == Shader::LANGUAGE_GLSL1 || glsl1on3)) ? "#line 0\n" : "#line 1\n");
ss << code;
return ss.str();
}
Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
: stages()
{
@@ -157,6 +600,74 @@ void Shader::deinitialize()
glslang::FinalizeProcess();
}
static const std::string defaultVertex = R"(
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition)
{
return clipSpaceFromLocal * localPosition;
}
)";
static const std::string defaultStandardPixel = R"(
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord)
{
return Texel(tex, texcoord) * vcolor;
}
)";
static const std::string defaultVideoPixel = R"(
void effect()
{
love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor;
}
)";
static const std::string defaultArrayPixel = R"(
uniform ArrayImage MainTex;
void effect()
{
love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor;
}
)";
const std::string &Shader::getDefaultCode(StandardShader shader, ShaderStage::StageType stage)
{
if (stage == ShaderStage::STAGE_VERTEX)
return defaultVertex;
static std::string nocode = "";
switch (shader)
{
case STANDARD_DEFAULT: return defaultStandardPixel;
case STANDARD_VIDEO: return defaultVideoPixel;
case STANDARD_ARRAY: return defaultArrayPixel;
case STANDARD_MAX_ENUM: return nocode;
}
return nocode;
}
static StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry languageEntries[] =
{
{ "glsl1", Shader::LANGUAGE_GLSL1 },
{ "glsl3", Shader::LANGUAGE_GLSL3 },
{ "glsl4", Shader::LANGUAGE_GLSL4 },
};
static StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> languages(languageEntries, sizeof(languageEntries));
static StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry builtinNameEntries[] =
{
{ "MainTex", Shader::BUILTIN_TEXTURE_MAIN },
{ "love_VideoYChannel", Shader::BUILTIN_TEXTURE_VIDEO_Y },
{ "love_VideoCbChannel", Shader::BUILTIN_TEXTURE_VIDEO_CB },
{ "love_VideoCrChannel", Shader::BUILTIN_TEXTURE_VIDEO_CR },
{ "love_UniformsPerDraw", Shader::BUILTIN_UNIFORMS_PER_DRAW },
{ "love_PointSize", Shader::BUILTIN_POINT_SIZE },
};
static StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> builtinNames(builtinNameEntries, sizeof(builtinNameEntries));
bool Shader::getConstant(const char *in, Language &out)
{
return languages.find(in, out);
@@ -177,27 +688,5 @@ bool Shader::getConstant(BuiltinUniform in, const char *&out)
return builtinNames.find(in, out);
}
StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM>::Entry Shader::languageEntries[] =
{
{ "glsl1", LANGUAGE_GLSL1 },
{ "essl1", LANGUAGE_ESSL1 },
{ "glsl3", LANGUAGE_GLSL3 },
{ "essl3", LANGUAGE_ESSL3 },
};
StringMap<Shader::Language, Shader::LANGUAGE_MAX_ENUM> Shader::languages(Shader::languageEntries, sizeof(Shader::languageEntries));
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{ "MainTex", BUILTIN_TEXTURE_MAIN },
{ "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y },
{ "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB },
{ "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR },
{ "love_UniformsPerDraw", BUILTIN_UNIFORMS_PER_DRAW },
{ "love_PointSize", BUILTIN_POINT_SIZE },
};
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
} // graphics
} // love
+23 -17
View File
@@ -33,17 +33,13 @@
#include <vector>
#include <stddef.h>
namespace glslang
{
class TShader;
}
namespace love
{
namespace graphics
{
class Graphics;
class Buffer;
// A GLSL shader
class Shader : public Object, public Resource
@@ -55,9 +51,8 @@ public:
enum Language
{
LANGUAGE_GLSL1,
LANGUAGE_ESSL1,
LANGUAGE_GLSL3,
LANGUAGE_ESSL3,
LANGUAGE_GLSL4,
LANGUAGE_MAX_ENUM
};
@@ -82,6 +77,7 @@ public:
UNIFORM_UINT,
UNIFORM_BOOL,
UNIFORM_SAMPLER,
UNIFORM_TEXELBUFFER,
UNIFORM_UNKNOWN,
UNIFORM_MAX_ENUM
};
@@ -94,6 +90,14 @@ public:
STANDARD_MAX_ENUM
};
struct SourceInfo
{
Language language;
bool isStage[ShaderStage::STAGE_MAX_ENUM];
bool customPixelFunction;
bool usesMRT;
};
struct MatrixSize
{
short columns;
@@ -113,6 +117,7 @@ public:
UniformType baseType;
TextureType textureType;
DataBaseType texelBufferType;
bool isDepthSampler;
std::string name;
@@ -126,7 +131,11 @@ public:
size_t dataSize;
Texture **textures;
union
{
Texture **textures;
Buffer **buffers;
};
};
// The members in here must respect uniform buffer alignment/padding rules.
@@ -176,6 +185,7 @@ public:
virtual void updateUniform(const UniformInfo *info, int count) = 0;
virtual void sendTextures(const UniformInfo *info, Texture **textures, int count) = 0;
virtual void sendBuffers(const UniformInfo *info, Buffer **buffers, int count) = 0;
/**
* Gets whether a uniform with the specified name exists and is actively
@@ -192,11 +202,16 @@ public:
void checkMainTextureType(TextureType textype, bool isDepthSampler) const;
void checkMainTexture(Texture *texture) const;
static SourceInfo getSourceInfo(const std::string &src);
static std::string createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const SourceInfo &info);
static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
static bool initialize();
static void deinitialize();
static const std::string &getDefaultCode(StandardShader shader, ShaderStage::StageType stage);
static bool getConstant(const char *in, Language &out);
static bool getConstant(Language in, const char *&out);
@@ -207,15 +222,6 @@ protected:
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
private:
static StringMap<Language, LANGUAGE_MAX_ENUM>::Entry languageEntries[];
static StringMap<Language, LANGUAGE_MAX_ENUM> languages;
// Names for the built-in uniform variables.
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM> builtinNames;
}; // Shader
} // graphics
+7
View File
@@ -205,6 +205,13 @@ bool ShaderStage::getConstant(StageType in, const char *&out)
return stageNames.find(in, out);
}
const char *ShaderStage::getConstant(StageType in)
{
const char *name = nullptr;
getConstant(in, name);
return name;
}
StringMap<ShaderStage::StageType, ShaderStage::STAGE_MAX_ENUM>::Entry ShaderStage::stageNameEntries[] =
{
{ "vertex", STAGE_VERTEX },
+2
View File
@@ -43,6 +43,7 @@ class ShaderStage : public love::Object
{
public:
// Order is used for stages array in ShaderStage.cpp
enum StageType
{
STAGE_VERTEX,
@@ -62,6 +63,7 @@ public:
static bool getConstant(const char *in, StageType &out);
static bool getConstant(StageType in, const char *&out);
static const char *getConstant(StageType in);
protected:
+30 -30
View File
@@ -40,7 +40,7 @@ namespace graphics
love::Type SpriteBatch::type("SpriteBatch", &Drawable::type);
SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage)
SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage usage)
: texture(texture)
, size(size)
, next(0)
@@ -57,14 +57,16 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag
throw love::Exception("A texture must be used when creating a SpriteBatch.");
if (texture->getTextureType() == TEXTURE_2D_ARRAY)
vertex_format = vertex::CommonFormat::XYf_STPf_RGBAub;
vertex_format = CommonFormat::XYf_STPf_RGBAub;
else
vertex_format = vertex::CommonFormat::XYf_STf_RGBAub;
vertex_format = CommonFormat::XYf_STf_RGBAub;
vertex_stride = vertex::getFormatStride(vertex_format);
vertex_stride = getFormatStride(vertex_format);
size_t vertex_size = vertex_stride * 4 * size;
array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, usage);
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0);
}
SpriteBatch::~SpriteBatch()
@@ -79,8 +81,6 @@ int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
{
using namespace vertex;
if (vertex_format == CommonFormat::XYf_STPf_RGBAub)
return addLayer(quad->getLayer(), quad, m, index);
@@ -122,8 +122,6 @@ int SpriteBatch::addLayer(int layer, const Matrix4 &m, int index)
int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
{
using namespace vertex;
if (vertex_format != CommonFormat::XYf_STPf_RGBAub)
throw love::Exception("addLayer can only be called on a SpriteBatch that uses an Array Texture!");
@@ -222,7 +220,9 @@ void SpriteBatch::setBufferSize(int newsize)
try
{
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
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);
// Copy as much of the old data into the new GLBuffer as can fit.
size_t copy_size = vertex_stride * 4 * new_next;
@@ -248,24 +248,27 @@ int SpriteBatch::getBufferSize() const
return size;
}
void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
{
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.");
AttachedAttribute oldattrib = {};
AttachedAttribute newattrib = {};
if (mesh->getVertexCount() < (size_t) next * 4)
throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
if (buffer->getArrayLength() < (size_t) next * 4)
throw love::Exception("Buffer has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
auto it = attached_attributes.find(name);
if (it != attached_attributes.end())
oldattrib = it->second;
newattrib.index = mesh->getAttributeIndex(name);
newattrib.index = buffer->getDataMemberIndex(name);
if (newattrib.index < 0)
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
throw love::Exception("The specified Buffer does not have a vertex attribute named '%s'", name.c_str());
newattrib.mesh = mesh;
newattrib.buffer = buffer;
attached_attributes[name] = newattrib;
}
@@ -296,8 +299,6 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
{
using namespace vertex;
if (next == 0)
return;
@@ -321,7 +322,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
// Make sure the buffer isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap();
Attributes attributes;
VertexAttributes attributes;
BufferBindings buffers;
{
@@ -333,19 +334,19 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
for (const auto &it : attached_attributes)
{
Mesh *mesh = it.second.mesh.get();
Buffer *buffer = it.second.buffer.get();
// We have to do this check here as wll because setBufferSize can be
// called after attachAttribute.
if (mesh->getVertexCount() < (size_t) next * 4)
throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
if (buffer->getArrayLength() < (size_t) next * 4)
throw love::Exception("Buffer with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
int attributeindex = -1;
// If the attribute is one of the LOVE-defined ones, use the constant
// attribute index for it, otherwise query the index from the shader.
BuiltinVertexAttribute builtinattrib;
if (vertex::getConstant(it.first.c_str(), builtinattrib))
if (getConstant(it.first.c_str(), builtinattrib))
attributeindex = (int) builtinattrib;
else if (Shader::current)
attributeindex = Shader::current->getVertexAttributeIndex(it.first);
@@ -353,19 +354,18 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
if (attributeindex >= 0)
{
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
mesh->vertexBuffer->unmap();
buffer->unmap();
const auto &formats = mesh->getVertexFormat();
const auto &format = formats[it.second.index];
const auto &member = buffer->getDataMember(it.second.index);
uint16 offset = (uint16) mesh->getAttributeOffset(it.second.index);
uint16 stride = (uint16) mesh->getVertexStride();
uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
uint16 stride = (uint16) buffer->getArrayStride();
attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers);
attributes.set(attributeindex, member.decl.format, offset, activebuffers);
attributes.setBufferLayout(activebuffers, stride);
// TODO: We should reuse buffer bindings with the same buffer+stride+step.
buffers.set(activebuffers, mesh->vertexBuffer, 0);
buffers.set(activebuffers, buffer, 0);
activebuffers++;
}
}
+4 -4
View File
@@ -51,7 +51,7 @@ public:
static love::Type type;
SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage usage);
virtual ~SpriteBatch();
int add(const Matrix4 &m, int index = -1);
@@ -93,7 +93,7 @@ public:
* Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
* The vertex attribute will be used when drawing the SpriteBatch.
**/
void attachAttribute(const std::string &name, Mesh *mesh);
void attachAttribute(const std::string &name, Buffer *buffer);
void setDrawRange(int start, int count);
void setDrawRange();
@@ -106,7 +106,7 @@ private:
struct AttachedAttribute
{
StrongRef<Mesh> mesh;
StrongRef<Buffer> buffer;
int index;
};
@@ -128,7 +128,7 @@ private:
Color32 color;
Colorf colorf;
vertex::CommonFormat vertex_format;
CommonFormat vertex_format;
size_t vertex_stride;
love::graphics::Buffer *array_buf;
+6 -3
View File
@@ -42,7 +42,8 @@ Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
Text::~Text()
{
delete vertex_buffer;
if (vertex_buffer)
vertex_buffer->release();
}
void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
@@ -60,12 +61,14 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize);
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
Buffer *new_buffer = gfx->newBuffer(newsize, nullptr, BUFFER_VERTEX, vertex::USAGE_DYNAMIC, 0);
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_DYNAMIC);
auto decl = Buffer::getCommonFormatDeclaration(Font::vertexFormat);
Buffer *new_buffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0);
if (vertex_buffer != nullptr)
vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0);
delete vertex_buffer;
vertex_buffer->release();
vertex_buffer = new_buffer;
vertexBuffers.set(0, vertex_buffer, 0);
+2 -2
View File
@@ -85,8 +85,8 @@ private:
StrongRef<Font> font;
vertex::Attributes vertexAttributes;
vertex::BufferBindings vertexBuffers;
VertexAttributes vertexAttributes;
BufferBindings vertexBuffers;
Buffer *vertex_buffer;
+8 -10
View File
@@ -192,6 +192,8 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
love::image::ImageDataBase *slice = slices->get(0, 0);
format = slice->getFormat();
if (sRGB)
format = getSRGBPixelFormat(format);
pixelWidth = slice->getWidth();
pixelHeight = slice->getHeight();
@@ -305,8 +307,6 @@ void Texture::draw(Graphics *gfx, const Matrix4 &m)
void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
{
using namespace vertex;
if (!readable)
throw love::Exception("Textures with non-readable formats cannot be drawn.");
@@ -323,9 +323,9 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
bool is2D = tm.isAffine2DTransform();
Graphics::BatchedDrawCommand cmd;
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
cmd.formats[0] = getSinglePositionFormat(is2D);
cmd.formats[1] = CommonFormat::STf_RGBAub;
cmd.indexMode = TriangleIndexMode::QUADS;
cmd.indexMode = TRIANGLEINDEX_QUADS;
cmd.vertexCount = 4;
cmd.texture = this;
@@ -339,7 +339,7 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
const Vector2 *texcoords = q->getVertexTexCoords();
vertex::STf_RGBAub *vertexdata = (vertex::STf_RGBAub *) data.stream[1];
STf_RGBAub *vertexdata = (STf_RGBAub *) data.stream[1];
Color32 c = toColor32(gfx->getColor());
@@ -358,8 +358,6 @@ void Texture::drawLayer(Graphics *gfx, int layer, const Matrix4 &m)
void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
{
using namespace vertex;
if (!readable)
throw love::Exception("Textures with non-readable formats cannot be drawn.");
@@ -380,9 +378,9 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
Matrix4 t(tm, m);
Graphics::BatchedDrawCommand cmd;
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
cmd.formats[0] = getSinglePositionFormat(is2D);
cmd.formats[1] = CommonFormat::STPf_RGBAub;
cmd.indexMode = TriangleIndexMode::QUADS;
cmd.indexMode = TRIANGLEINDEX_QUADS;
cmd.vertexCount = 4;
cmd.texture = this;
cmd.standardShaderType = Shader::STANDARD_ARRAY;
@@ -395,7 +393,7 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
const Vector2 *texcoords = q->getVertexTexCoords();
vertex::STPf_RGBAub *vertexdata = (vertex::STPf_RGBAub *) data.stream[1];
STPf_RGBAub *vertexdata = (STPf_RGBAub *) data.stream[1];
for (int i = 0; i < 4; i++)
{
+4 -4
View File
@@ -121,9 +121,9 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
Matrix4 t(tm, m);
Graphics::BatchedDrawCommand cmd;
cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
cmd.formats[1] = vertex::CommonFormat::STf_RGBAub;
cmd.indexMode = vertex::TriangleIndexMode::QUADS;
cmd.formats[0] = getSinglePositionFormat(is2D);
cmd.formats[1] = CommonFormat::STf_RGBAub;
cmd.indexMode = TRIANGLEINDEX_QUADS;
cmd.vertexCount = 4;
cmd.standardShaderType = Shader::STANDARD_VIDEO;
@@ -134,7 +134,7 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
else
t.transformXY0((Vector3 *) data.stream[0], vertices, 4);
vertex::STf_RGBAub *verts = (vertex::STf_RGBAub *) data.stream[1];
STf_RGBAub *verts = (STf_RGBAub *) data.stream[1];
Color32 c = toColor32(gfx->getColor());
+142 -90
View File
@@ -35,18 +35,54 @@ namespace graphics
namespace opengl
{
Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
: love::graphics::Buffer(size, type, usage, mapflags)
, vbo(0)
, memory_map(nullptr)
, modified_offset(0)
, modified_size(0)
static GLenum getGLFormat(DataFormat format)
{
target = OpenGL::getGLBufferType(type);
switch (format)
{
case DATAFORMAT_FLOAT: return GL_R32F;
case DATAFORMAT_FLOAT_VEC2: return GL_RG32F;
case DATAFORMAT_FLOAT_VEC3: return GL_RGB32F;
case DATAFORMAT_FLOAT_VEC4: return GL_RGBA32F;
case DATAFORMAT_INT32: return GL_R32I;
case DATAFORMAT_INT32_VEC2: return GL_RG32I;
case DATAFORMAT_INT32_VEC3: return GL_RGB32I;
case DATAFORMAT_INT32_VEC4: return GL_RGBA32I;
case DATAFORMAT_UINT32: return GL_R32UI;
case DATAFORMAT_UINT32_VEC2: return GL_RG32UI;
case DATAFORMAT_UINT32_VEC3: return GL_RGB32UI;
case DATAFORMAT_UINT32_VEC4: return GL_RGBA32UI;
case DATAFORMAT_UNORM8_VEC4: return GL_RGBA8;
case DATAFORMAT_INT8_VEC4: return GL_RGBA8I;
case DATAFORMAT_UINT8_VEC4: return GL_RGBA8UI;
case DATAFORMAT_UNORM16_VEC2: return GL_RG16;
case DATAFORMAT_UNORM16_VEC4: return GL_RGBA16;
case DATAFORMAT_INT16_VEC2: return GL_RG16I;
case DATAFORMAT_INT16_VEC4: return GL_RGBA16I;
case DATAFORMAT_UINT16: return GL_R16UI;
case DATAFORMAT_UINT16_VEC2: return GL_RG16UI;
case DATAFORMAT_UINT16_VEC4: return GL_RGBA16UI;
default: return GL_ZERO;
}
}
Buffer::Buffer(love::graphics::Graphics *gfx, 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)
{
size = getSize();
arraylength = getArrayLength();
if (typeFlags & TYPEFLAG_TEXEL)
mapType = BUFFERTYPE_TEXEL;
else if (typeFlags & TYPEFLAG_VERTEX)
mapType = BUFFERTYPE_VERTEX;
else if (typeFlags & TYPEFLAG_INDEX)
mapType = BUFFERTYPE_INDEX;
target = OpenGL::getGLBufferType(mapType);
try
{
memory_map = new char[size];
memoryMap = new char[size];
}
catch (std::bad_alloc &)
{
@@ -54,34 +90,78 @@ Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usa
}
if (data != nullptr)
memcpy(memory_map, data, size);
memcpy(memoryMap, data, size);
if (!load(data != nullptr))
{
delete[] memory_map;
throw love::Exception("Could not load vertex buffer (out of VRAM?)");
unloadVolatile();
delete[] memoryMap;
throw love::Exception("Could not create buffer (out of VRAM?)");
}
}
Buffer::~Buffer()
{
if (vbo != 0)
unload();
unloadVolatile();
delete[] memoryMap;
}
delete[] memory_map;
bool Buffer::loadVolatile()
{
if (buffer != 0)
return true;
return load(true);
}
void Buffer::unloadVolatile()
{
mapped = false;
if (buffer != 0)
gl.deleteBuffer(buffer);
buffer = 0;
if (texture != 0)
gl.deleteTexture(texture);
texture = 0;
}
bool Buffer::load(bool restore)
{
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
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()));
if (getTypeFlags() & TYPEFLAG_TEXEL)
{
glGenTextures(1, &texture);
gl.bindBufferTextureToUnit(texture, 0, false, true);
glTexBuffer(target, getGLFormat(getDataMember(0).decl.format), buffer);
}
return (glGetError() == GL_NO_ERROR);
}
void *Buffer::map()
{
if (is_mapped)
return memory_map;
if (mapped)
return memoryMap;
is_mapped = true;
mapped = true;
modified_offset = 0;
modified_size = 0;
modifiedOffset = 0;
modifiedSize = 0;
isMappedDataModified = false;
return memory_map;
return memoryMap;
}
void Buffer::unmapStatic(size_t offset, size_t size)
@@ -90,8 +170,8 @@ void Buffer::unmapStatic(size_t offset, size_t size)
return;
// Upload the mapped data to the buffer.
gl.bindBuffer(type, vbo);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
gl.bindBuffer(mapType, buffer);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset);
}
void Buffer::unmapStream()
@@ -100,133 +180,105 @@ void Buffer::unmapStream()
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
gl.bindBuffer(type, vbo);
gl.bindBuffer(mapType, buffer);
glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage);
#if LOVE_WINDOWS
// TODO: Verify that this codepath is a useful optimization.
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
glBufferData(target, (GLsizeiptr) getSize(), memory_map, glusage);
glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage);
else
#endif
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memory_map);
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap);
}
void Buffer::unmap()
{
if (!is_mapped)
if (!mapped)
return;
if ((map_flags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
mapped = false;
if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
{
modified_offset = std::min(modified_offset, getSize() - 1);
modified_size = std::min(modified_size, getSize() - modified_offset);
if (!isMappedDataModified)
return;
modifiedOffset = std::min(modifiedOffset, getSize() - 1);
modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset);
}
else
{
modified_offset = 0;
modified_size = getSize();
modifiedOffset = 0;
modifiedSize = getSize();
}
if (modified_size > 0)
if (modifiedSize > 0)
{
switch (getUsage())
{
case vertex::USAGE_STATIC:
unmapStatic(modified_offset, modified_size);
case BUFFERUSAGE_STATIC:
unmapStatic(modifiedOffset, modifiedSize);
break;
case vertex::USAGE_STREAM:
case BUFFERUSAGE_STREAM:
unmapStream();
break;
case vertex::USAGE_DYNAMIC:
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 (modified_size >= getSize() / 3)
if (modifiedSize >= getSize() / 3)
unmapStream();
else
unmapStatic(modified_offset, modified_size);
unmapStatic(modifiedOffset, modifiedSize);
break;
}
}
modified_offset = 0;
modified_size = 0;
is_mapped = false;
modifiedOffset = 0;
modifiedSize = 0;
}
void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
{
if (!is_mapped || !(map_flags & MAP_EXPLICIT_RANGE_MODIFY))
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 old_range_end = modified_offset + modified_size;
modified_offset = std::min(modified_offset, offset);
size_t oldrangeend = modifiedOffset + modifiedSize;
modifiedOffset = std::min(modifiedOffset, offset);
size_t new_range_end = std::max(offset + modifiedsize, old_range_end);
modified_size = new_range_end - modified_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(memory_map + offset, data, size);
memcpy(memoryMap + offset, data, size);
if (is_mapped)
if (mapped)
setMappedRangeModified(offset, size);
else
{
gl.bindBuffer(type, vbo);
gl.bindBuffer(mapType, buffer);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
}
}
ptrdiff_t Buffer::getHandle() const
{
return vbo;
}
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
{
other->fill(otheroffset, size, memory_map + offset);
}
bool Buffer::loadVolatile()
{
return load(true);
}
void Buffer::unloadVolatile()
{
unload();
}
bool Buffer::load(bool restore)
{
glGenBuffers(1, &vbo);
gl.bindBuffer(type, vbo);
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
// Copy the old buffer only if 'restore' was requested.
const GLvoid *src = restore ? memory_map : nullptr;
// Note that if 'src' is '0', no data will be copied.
glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
return (glGetError() == GL_NO_ERROR);
}
void Buffer::unload()
{
is_mapped = false;
gl.deleteBuffer(vbo);
vbo = 0;
other->fill(otheroffset, size, memoryMap + offset);
}
} // opengl
+25 -16
View File
@@ -32,6 +32,9 @@ namespace love
{
namespace graphics
{
class Graphics;
namespace opengl
{
@@ -39,39 +42,45 @@ class Buffer final : public love::graphics::Buffer, public Volatile
{
public:
Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags);
Buffer(love::graphics::Graphics *gfx, 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 fill(size_t offset, size_t size, const void *data) override;
ptrdiff_t getHandle() const override;
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
// Implements Volatile.
bool loadVolatile() override;
void unloadVolatile() override;
void *map() override;
void unmap() override;
void setMappedRangeModified(size_t offset, size_t size) 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);
void unload();
void unmapStatic(size_t offset, size_t size);
void unmapStream();
GLenum target;
BufferType mapType = BUFFERTYPE_VERTEX;
GLenum target = 0;
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
// The buffer object identifier. Assigned by OpenGL.
GLuint buffer = 0;
// Used for Texel Buffer types.
GLuint texture = 0;
// A pointer to mapped memory.
char *memory_map;
char *memoryMap = nullptr;
size_t modified_offset;
size_t modified_size;
size_t modifiedOffset = 0;
size_t modifiedSize = 0;
bool isMappedDataModified = false;
}; // Buffer
+49 -36
View File
@@ -107,6 +107,7 @@ love::graphics::Graphics *createInstance()
Graphics::Graphics()
: windowHasStencil(false)
, mainVAO(0)
, defaultBuffers()
, supportedFormats()
{
gl = OpenGL();
@@ -162,9 +163,9 @@ love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage
return new Shader(vertex, pixel);
}
love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
{
return new Buffer(size, data, type, usage, mapflags);
return new Buffer(this, settings, format, data, size, arraylength);
}
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
@@ -255,11 +256,32 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
{
// Initial sizes that should be good enough for most cases. It will
// resize to fit if needed, later.
batchedDrawState.vb[0] = CreateStreamBuffer(BUFFER_VERTEX, 1024 * 1024 * 1);
batchedDrawState.vb[1] = CreateStreamBuffer(BUFFER_VERTEX, 256 * 1024 * 1);
batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFER_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
batchedDrawState.vb[0] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 1024 * 1024 * 1);
batchedDrawState.vb[1] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 256 * 1024 * 1);
batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERTYPE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
}
if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERTYPE_TEXEL].get() == nullptr)
{
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, 0, BUFFERUSAGE_STATIC);
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT_VEC4, 0}};
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
love::graphics::Buffer *buffer = newBuffer(settings, format, texel, sizeof(texel), 1);
defaultBuffers[BUFFERTYPE_TEXEL].set(buffer, Acquire::NORETAIN);
}
// Load default resources before other Volatile.
for (int i = 0; i < BUFFERTYPE_MAX_ENUM; i++)
{
if (defaultBuffers[i].get())
((Buffer *) defaultBuffers[i].get())->loadVolatile();
}
if (defaultBuffers[BUFFERTYPE_TEXEL].get())
gl.setDefaultTexelBuffer((GLuint) defaultBuffers[BUFFERTYPE_TEXEL]->getTexelBufferHandle());
// Reload all volatile objects.
if (!Volatile::loadAll())
::printf("Could not reload all volatile objects.\n");
@@ -269,12 +291,11 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
// Restore the graphics state.
restoreState(states.back());
int gammacorrect = isGammaCorrect() ? 1 : 0;
Shader::Language target = getShaderLanguageTarget();
// We always need a default shader.
for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++)
{
auto stype = (Shader::StandardShader) i;
if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY])
continue;
@@ -284,8 +305,10 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
{
if (!Shader::standardShaders[i])
{
const auto &code = defaultShaderCode[i][target][gammacorrect];
Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]);
std::vector<std::string> stages;
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL));
Shader::standardShaders[i] = newShader(stages);
}
}
catch (love::Exception &)
@@ -376,7 +399,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType);
gl.bindBuffer(BUFFER_INDEX, cmd.indexBuffer->getHandle());
gl.bindBuffer(BUFFERTYPE_INDEX, cmd.indexBuffer->getHandle());
if (cmd.instanceCount > 1)
glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
@@ -386,13 +409,13 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
++drawCalls;
}
static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::BufferBindings &buffers, int vertexcount)
static inline void advanceVertexOffsets(const VertexAttributes &attributes, BufferBindings &buffers, int vertexcount)
{
// TODO: Figure out a better way to avoid touching the same buffer multiple
// times, if multiple attributes share the buffer.
uint32 touchedbuffers = 0;
for (unsigned int i = 0; i < vertex::Attributes::MAX; i++)
for (unsigned int i = 0; i < VertexAttributes::MAX; i++)
{
if (!attributes.isEnabled(i))
continue;
@@ -409,7 +432,7 @@ static inline void advanceVertexOffsets(const vertex::Attributes &attributes, ve
}
}
void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture)
void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture)
{
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
@@ -418,7 +441,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
gl.bindTextureToUnit(texture, 0, false);
gl.setCullMode(CULL_NONE);
gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle());
gl.bindBuffer(BUFFERTYPE_INDEX, quadIndexBuffer->getHandle());
if (gl.isBaseVertexSupported())
{
@@ -438,7 +461,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
}
else
{
vertex::BufferBindings bufferscopy = buffers;
BufferBindings bufferscopy = buffers;
if (start > 0)
advanceVertexOffsets(attributes, bufferscopy, start * 4);
@@ -525,7 +548,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
endPass();
bool iswindow = rts.getFirstTarget().texture == nullptr;
vertex::Winding vertexwinding = state.winding;
Winding vertexwinding = state.winding;
if (iswindow)
{
@@ -543,10 +566,10 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
// Flip front face winding when rendering to a texture, since our
// projection matrix is flipped.
vertexwinding = vertexwinding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW;
vertexwinding = vertexwinding == WINDING_CW ? WINDING_CCW : WINDING_CW;
}
glFrontFace(vertexwinding == vertex::WINDING_CW ? GL_CW : GL_CCW);
glFrontFace(vertexwinding == WINDING_CW ? GL_CW : GL_CCW);
gl.setViewport({0, 0, pixelw, pixelh});
@@ -1246,7 +1269,7 @@ void Graphics::setDepthMode(CompareMode compare, bool write)
}
}
void Graphics::setFrontFaceWinding(vertex::Winding winding)
void Graphics::setFrontFaceWinding(Winding winding)
{
DisplayState &state = states.back();
@@ -1256,9 +1279,9 @@ void Graphics::setFrontFaceWinding(vertex::Winding winding)
state.winding = winding;
if (isRenderTargetActive())
winding = winding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW;
winding = winding == WINDING_CW ? WINDING_CCW : WINDING_CW;
glFrontFace(winding == vertex::WINDING_CW ? GL_CW : GL_CCW);
glFrontFace(winding == WINDING_CW ? GL_CW : GL_CCW);
}
void Graphics::setColor(Colorf c)
@@ -1388,17 +1411,19 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_GLSL3] = GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
static_assert(FEATURE_MAX_ENUM == 10, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.areTexelBuffersSupported();
static_assert(FEATURE_MAX_ENUM == 11, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();
capabilities.limits[LIMIT_TEXTURE_LAYERS] = gl.getMaxTextureLayers();
capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = gl.getMax3DTextureSize();
capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize();
capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = gl.getMaxTexelBufferSize();
capabilities.limits[LIMIT_RENDER_TARGETS] = gl.getMaxRenderTargets();
capabilities.limits[LIMIT_TEXTURE_MSAA] = gl.getMaxSamples();
capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy();
static_assert(LIMIT_MAX_ENUM == 8, "Graphics::initCapabilities must be updated when adding a new system limit!");
static_assert(LIMIT_MAX_ENUM == 9, "Graphics::initCapabilities must be updated when adding a new system limit!");
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
@@ -1524,18 +1549,6 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
return supported.value;
}
Shader::Language Graphics::getShaderLanguageTarget() const
{
if (gl.isCoreProfile())
return Shader::LANGUAGE_GLSL3;
else if (GLAD_ES_VERSION_3_0)
return Shader::LANGUAGE_ESSL3;
else if (GLAD_ES_VERSION_2_0)
return Shader::LANGUAGE_ESSL1;
else
return Shader::LANGUAGE_GLSL1;
}
} // opengl
} // graphics
} // love
+6 -5
View File
@@ -60,7 +60,7 @@ public:
const char *getName() const override;
love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
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 windowhasstencil) override;
@@ -70,7 +70,7 @@ public:
void draw(const DrawCommand &cmd) override;
void draw(const DrawIndexedCommand &cmd) override;
void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture) override;
void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture) override;
void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
@@ -91,7 +91,7 @@ public:
void setDepthMode(CompareMode compare, bool write) override;
void setFrontFaceWinding(vertex::Winding winding) override;
void setFrontFaceWinding(Winding winding) override;
void setColorMask(ColorChannelMask mask) override;
@@ -107,8 +107,6 @@ public:
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
Shader::Language getShaderLanguageTarget() const override;
// Internal use.
void cleanupRenderTexture(love::graphics::Texture *texture);
@@ -150,6 +148,9 @@ private:
bool windowHasStencil;
GLuint mainVAO;
// Only needed for buffer types that can be bound to shaders.
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERTYPE_MAX_ENUM];
// [rendertarget][readable][srgb]
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2];
+201 -106
View File
@@ -155,11 +155,11 @@ bool OpenGL::initContext()
#ifdef LOVE_WINDOWS
if (getVendor() == VENDOR_AMD)
{
// Radeon HD drivers switched from "ATI Radeon" to "AMD Radeon" around
// Radeon drivers switched from "ATI Radeon" to "AMD Radeon" around
// the 7000 series. We'll assume this bug doesn't affect those newer
// GPUs / drivers.
const char *device = (const char *) glGetString(GL_RENDERER);
if (strstr(device, "ATI Radeon HD ") || strstr(device, "ATI Mobility Radeon HD"))
if (strstr(device, "ATI Radeon") || strstr(device, "ATI Mobility Radeon"))
bugs.texStorageBreaksSubImage = true;
}
#endif
@@ -185,7 +185,7 @@ void OpenGL::setupContext()
state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1);
state.instancedAttribArrays = 0;
setVertexAttributes(vertex::Attributes(), vertex::BufferBindings());
setVertexAttributes(VertexAttributes(), BufferBindings());
// Get the current viewport.
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
@@ -222,14 +222,14 @@ void OpenGL::setupContext()
glGetIntegerv(GL_CULL_FACE_MODE, &faceCull);
state.faceCullMode = faceCull;
for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
{
state.boundBuffers[i] = 0;
glBindBuffer(getGLBufferType((BufferType) i), 0);
}
// Initialize multiple texture unit support for shaders.
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
{
state.boundTextures[i].clear();
state.boundTextures[i].resize(maxTextureUnits, 0);
@@ -280,6 +280,10 @@ void OpenGL::deInitContext()
}
}
if (state.defaultTexelBuffer != 0)
gl.deleteTexture(state.defaultTexelBuffer);
state.defaultTexelBuffer = 0;
contextInitialized = false;
}
@@ -459,6 +463,11 @@ void OpenGL::initMaxValues()
else
maxTextureArrayLayers = 0;
if (areTexelBuffersSupported())
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTexelBufferSize);
else
maxTexelBufferSize = 0;
int maxattachments = 1;
int maxdrawbuffers = 1;
@@ -562,16 +571,11 @@ GLenum OpenGL::getGLPrimitiveType(PrimitiveType type)
{
switch (type)
{
case PRIMITIVE_TRIANGLES:
return GL_TRIANGLES;
case PRIMITIVE_TRIANGLE_STRIP:
return GL_TRIANGLE_STRIP;
case PRIMITIVE_TRIANGLE_FAN:
return GL_TRIANGLE_FAN;
case PRIMITIVE_POINTS:
return GL_POINTS;
case PRIMITIVE_MAX_ENUM:
return GL_ZERO;
case PRIMITIVE_TRIANGLES: return GL_TRIANGLES;
case PRIMITIVE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
case PRIMITIVE_TRIANGLE_FAN: return GL_TRIANGLE_FAN;
case PRIMITIVE_POINTS: return GL_POINTS;
case PRIMITIVE_MAX_ENUM: return GL_ZERO;
}
return GL_ZERO;
@@ -581,12 +585,10 @@ GLenum OpenGL::getGLBufferType(BufferType type)
{
switch (type)
{
case BUFFER_VERTEX:
return GL_ARRAY_BUFFER;
case BUFFER_INDEX:
return GL_ELEMENT_ARRAY_BUFFER;
case BUFFER_MAX_ENUM:
return GL_ZERO;
case BUFFERTYPE_VERTEX: return GL_ARRAY_BUFFER;
case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
case BUFFERTYPE_TEXEL: return GL_TEXTURE_BUFFER;
case BUFFERTYPE_MAX_ENUM: return GL_ZERO;
}
return GL_ZERO;
@@ -596,16 +598,11 @@ GLenum OpenGL::getGLTextureType(TextureType type)
{
switch (type)
{
case TEXTURE_2D:
return GL_TEXTURE_2D;
case TEXTURE_VOLUME:
return GL_TEXTURE_3D;
case TEXTURE_2D_ARRAY:
return GL_TEXTURE_2D_ARRAY;
case TEXTURE_CUBE:
return GL_TEXTURE_CUBE_MAP;
case TEXTURE_MAX_ENUM:
return GL_ZERO;
case TEXTURE_2D: return GL_TEXTURE_2D;
case TEXTURE_VOLUME: return GL_TEXTURE_3D;
case TEXTURE_2D_ARRAY: return GL_TEXTURE_2D_ARRAY;
case TEXTURE_CUBE: return GL_TEXTURE_CUBE_MAP;
case TEXTURE_MAX_ENUM: return GL_TEXTURE_BUFFER; // Hack
}
return GL_ZERO;
@@ -615,74 +612,159 @@ GLenum OpenGL::getGLIndexDataType(IndexDataType type)
{
switch (type)
{
case INDEX_UINT16:
return GL_UNSIGNED_SHORT;
case INDEX_UINT32:
return GL_UNSIGNED_INT;
default:
return GL_ZERO;
case INDEX_UINT16: return GL_UNSIGNED_SHORT;
case INDEX_UINT32: return GL_UNSIGNED_INT;
default: return GL_ZERO;
}
}
GLenum OpenGL::getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat)
GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat)
{
normalized = GL_FALSE;
intformat = false;
components = 1;
switch (type)
switch (format)
{
case vertex::DATA_SNORM8:
normalized = GL_TRUE;
return GL_BYTE;
case vertex::DATA_UNORM8:
normalized = GL_TRUE;
return GL_UNSIGNED_BYTE;
case vertex::DATA_INT8:
intformat = true;
return GL_BYTE;
case vertex::DATA_UINT8:
intformat = true;
return GL_UNSIGNED_BYTE;
case vertex::DATA_SNORM16:
normalized = GL_TRUE;
return GL_SHORT;
case vertex::DATA_UNORM16:
normalized = GL_TRUE;
return GL_UNSIGNED_SHORT;
case vertex::DATA_INT16:
intformat = true;
return GL_SHORT;
case vertex::DATA_UINT16:
intformat = true;
return GL_UNSIGNED_SHORT;
case vertex::DATA_INT32:
case DATAFORMAT_FLOAT:
components = 1;
return GL_FLOAT;
case DATAFORMAT_FLOAT_VEC2:
components = 2;
return GL_FLOAT;
case DATAFORMAT_FLOAT_VEC3:
components = 3;
return GL_FLOAT;
case DATAFORMAT_FLOAT_VEC4:
components = 4;
return GL_FLOAT;
case DATAFORMAT_FLOAT_MAT2X2:
case DATAFORMAT_FLOAT_MAT2X3:
case DATAFORMAT_FLOAT_MAT2X4:
case DATAFORMAT_FLOAT_MAT3X2:
case DATAFORMAT_FLOAT_MAT3X3:
case DATAFORMAT_FLOAT_MAT3X4:
case DATAFORMAT_FLOAT_MAT4X2:
case DATAFORMAT_FLOAT_MAT4X3:
case DATAFORMAT_FLOAT_MAT4X4:
return GL_ZERO;
case DATAFORMAT_INT32:
components = 1;
intformat = true;
return GL_INT;
case vertex::DATA_UINT32:
case DATAFORMAT_INT32_VEC2:
components = 2;
intformat = true;
return GL_INT;
case DATAFORMAT_INT32_VEC3:
components = 3;
intformat = true;
return GL_INT;
case DATAFORMAT_INT32_VEC4:
components = 4;
intformat = true;
return GL_INT;
case DATAFORMAT_UINT32:
components = 1;
intformat = true;
return GL_UNSIGNED_INT;
case vertex::DATA_FLOAT:
normalized = GL_FALSE;
return GL_FLOAT;
case vertex::DATA_MAX_ENUM:
case DATAFORMAT_UINT32_VEC2:
components = 2;
intformat = true;
return GL_UNSIGNED_INT;
case DATAFORMAT_UINT32_VEC3:
components = 3;
intformat = true;
return GL_UNSIGNED_INT;
case DATAFORMAT_UINT32_VEC4:
components = 4;
intformat = true;
return GL_UNSIGNED_INT;
case DATAFORMAT_SNORM8_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_BYTE;
case DATAFORMAT_UNORM8_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_UNSIGNED_BYTE;
case DATAFORMAT_INT8_VEC4:
components = 4;
intformat = true;
return GL_BYTE;
case DATAFORMAT_UINT8_VEC4:
components = 4;
intformat = true;
return GL_UNSIGNED_BYTE;
case DATAFORMAT_SNORM16_VEC2:
components = 2;
normalized = GL_TRUE;
return GL_BYTE;
case DATAFORMAT_SNORM16_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_BYTE;
case DATAFORMAT_UNORM16_VEC2:
components = 2;
normalized = GL_TRUE;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_UNORM16_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_INT16_VEC2:
components = 2;
intformat = true;
return GL_SHORT;
case DATAFORMAT_INT16_VEC4:
components = 4;
intformat = true;
return GL_SHORT;
case DATAFORMAT_UINT16:
components = 1;
intformat = true;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_UINT16_VEC2:
components = 2;
intformat = true;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_UINT16_VEC4:
components = 4;
intformat = true;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_BOOL:
case DATAFORMAT_BOOL_VEC2:
case DATAFORMAT_BOOL_VEC3:
case DATAFORMAT_BOOL_VEC4:
return GL_ZERO;
case DATAFORMAT_MAX_ENUM:
return GL_ZERO;
}
return GL_ZERO;
}
GLenum OpenGL::getGLBufferUsage(vertex::Usage usage)
GLenum OpenGL::getGLBufferUsage(BufferUsage usage)
{
switch (usage)
{
case vertex::USAGE_STREAM:
return GL_STREAM_DRAW;
case vertex::USAGE_DYNAMIC:
return GL_DYNAMIC_DRAW;
case vertex::USAGE_STATIC:
return GL_STATIC_DRAW;
default:
return 0;
case BUFFERUSAGE_STREAM: return GL_STREAM_DRAW;
case BUFFERUSAGE_DYNAMIC: return GL_DYNAMIC_DRAW;
case BUFFERUSAGE_STATIC: return GL_STATIC_DRAW;
default: return 0;
}
}
@@ -699,14 +781,14 @@ void OpenGL::deleteBuffer(GLuint buffer)
{
glDeleteBuffers(1, &buffer);
for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
{
if (state.boundBuffers[i] == buffer)
state.boundBuffers[i] = 0;
}
}
void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers)
void OpenGL::setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers)
{
uint32 enablediff = attributes.enableBits ^ state.enabledAttribArrays;
uint32 instanceattribbits = 0;
@@ -739,18 +821,19 @@ void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const ver
if ((state.instancedAttribArrays & bit) ^ divisorbit)
glVertexAttribDivisor(i, divisor);
int components = 0;
GLboolean normalized = GL_FALSE;
bool intformat = false;
GLenum gltype = getGLVertexDataType(attrib.type, normalized, intformat);
GLenum gltype = getGLVertexDataType(attrib.format, components, normalized, intformat);
const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
bindBuffer(BUFFERTYPE_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
if (intformat)
glVertexAttribIPointer(i, attrib.components, gltype, layout.stride, offsetpointer);
glVertexAttribIPointer(i, components, gltype, layout.stride, offsetpointer);
else
glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer);
glVertexAttribPointer(i, components, gltype, normalized, layout.stride, offsetpointer);
}
i++;
@@ -988,7 +1071,7 @@ void OpenGL::setTextureUnit(int textureunit)
state.curTextureUnit = textureunit;
}
void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev)
void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
{
if (texture != state.boundTextures[target][textureunit])
{
@@ -1004,9 +1087,19 @@ void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureun
else
state.curTextureUnit = textureunit;
}
else if (bindforedit && !restoreprev && textureunit != state.curTextureUnit)
{
glActiveTexture(GL_TEXTURE0 + textureunit);
state.curTextureUnit = textureunit;
}
}
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev)
void OpenGL::bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
{
bindTextureToUnit(TEXTURE_MAX_ENUM, texture, textureunit, restoreprev, bindforedit);
}
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit)
{
TextureType textype = TEXTURE_2D;
GLuint handle = 0;
@@ -1028,14 +1121,14 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
handle = getDefaultTexture(textype);
}
bindTextureToUnit(textype, handle, textureunit, restoreprev);
bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
}
void OpenGL::deleteTexture(GLuint texture)
{
// glDeleteTextures binds texture 0 to all texture units the deleted texture
// was bound to before deletion.
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
{
for (GLuint &texid : state.boundTextures[i])
{
@@ -1068,24 +1161,15 @@ GLint OpenGL::getGLCompareMode(CompareMode mode)
{
switch (mode)
{
case COMPARE_LESS:
return GL_LESS;
case COMPARE_LEQUAL:
return GL_LEQUAL;
case COMPARE_EQUAL:
return GL_EQUAL;
case COMPARE_GEQUAL:
return GL_GEQUAL;
case COMPARE_GREATER:
return GL_GREATER;
case COMPARE_NOTEQUAL:
return GL_NOTEQUAL;
case COMPARE_ALWAYS:
return GL_ALWAYS;
case COMPARE_NEVER:
return GL_NEVER;
default:
return GL_NEVER;
case COMPARE_LESS: return GL_LESS;
case COMPARE_LEQUAL: return GL_LEQUAL;
case COMPARE_EQUAL: return GL_EQUAL;
case COMPARE_GEQUAL: return GL_GEQUAL;
case COMPARE_GREATER: return GL_GREATER;
case COMPARE_NOTEQUAL: return GL_NOTEQUAL;
case COMPARE_ALWAYS: return GL_ALWAYS;
case COMPARE_NEVER: return GL_NEVER;
default: return GL_NEVER;
}
}
@@ -1333,6 +1417,12 @@ bool OpenGL::isMultiFormatMRTSupported() const
return getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
}
bool OpenGL::areTexelBuffersSupported() const
{
// Not supported in ES until 3.2, which we don't support shaders for...
return GLAD_VERSION_3_1;
}
int OpenGL::getMax2DTextureSize() const
{
return std::max(max2DTextureSize, 1);
@@ -1353,6 +1443,11 @@ int OpenGL::getMaxTextureLayers() const
return std::max(maxTextureArrayLayers, 1);
}
int OpenGL::getMaxTexelBufferSize() const
{
return maxTexelBufferSize;
}
int OpenGL::getMaxRenderTargets() const
{
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
+25 -8
View File
@@ -166,7 +166,7 @@ public:
* initial full-size one (determined after some investigation with an
* affected user on Discord.)
* https://bitbucket.org/rude/love/issues/1436/bug-with-lovegraphicsprint-on-older-ati
*
* https://github.com/love2d/love/issues/1563
**/
bool texStorageBreaksSubImage;
@@ -238,7 +238,7 @@ public:
/**
* Set all vertex attribute state.
**/
void setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers);
void setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers);
/**
* Wrapper for glCullFace which eliminates redundant state setting.
@@ -306,6 +306,12 @@ public:
**/
GLuint getDefaultTexture(TextureType type) const;
/**
* Gets the texture ID for love's default texel buffer.
**/
GLuint getDefaultTexelBuffer() const { return state.defaultTexelBuffer; }
void setDefaultTexelBuffer(GLuint tex) { state.defaultTexelBuffer = tex; }
/**
* Helper for setting the active texture unit.
*
@@ -318,9 +324,12 @@ public:
*
* @param textureunit Index in the range of [0, maxtextureunits-1]
* @param restoreprev Restore previously bound texture unit when done.
* @param bindforedit If false, the active texture unit may be left alone.
**/
void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev);
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev);
void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit = true);
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit = true);
void bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit);
/**
* Helper for deleting an OpenGL texture.
@@ -348,6 +357,7 @@ public:
bool isSamplerLODBiasSupported() const;
bool isBaseVertexSupported() const;
bool isMultiFormatMRTSupported() const;
bool areTexelBuffersSupported() const;
/**
* Returns the maximum supported width or height of a texture.
@@ -357,6 +367,11 @@ public:
int getMaxCubeTextureSize() const;
int getMaxTextureLayers() const;
/**
* Returns the maximum number of values in a texel buffer.
**/
int getMaxTexelBufferSize() const;
/**
* Returns the maximum supported number of simultaneous render targets.
**/
@@ -398,8 +413,8 @@ public:
static GLenum getGLPrimitiveType(PrimitiveType type);
static GLenum getGLBufferType(BufferType type);
static GLenum getGLIndexDataType(IndexDataType type);
static GLenum getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat);
static GLenum getGLBufferUsage(vertex::Usage usage);
static GLenum getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat);
static GLenum getGLBufferUsage(BufferUsage usage);
static GLenum getGLTextureType(TextureType type);
static GLint getGLWrapMode(SamplerState::WrapMode wmode);
static GLint getGLCompareMode(CompareMode mode);
@@ -435,6 +450,7 @@ private:
int max3DTextureSize;
int maxCubeTextureSize;
int maxTextureArrayLayers;
int maxTexelBufferSize;
int maxRenderTargets;
int maxSamples;
int maxTextureUnits;
@@ -447,10 +463,10 @@ private:
// Tracked OpenGL state.
struct
{
GLuint boundBuffers[BUFFER_MAX_ENUM];
GLuint boundBuffers[BUFFERTYPE_MAX_ENUM];
// Texture unit state (currently bound texture for each texture unit.)
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM];
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM + 1];
bool enableState[ENABLE_MAX_ENUM];
@@ -471,6 +487,7 @@ private:
GLuint boundFramebuffers[2];
GLuint defaultTexture[TEXTURE_MAX_ENUM];
GLuint defaultTexelBuffer;
} state;
+208 -19
View File
@@ -24,6 +24,7 @@
#include "Shader.h"
#include "ShaderStage.h"
#include "Graphics.h"
#include "graphics/vertex.h"
// C++
#include <algorithm>
@@ -68,6 +69,16 @@ Shader::~Shader()
delete[] p.second.textures;
}
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
{
for (int i = 0; i < p.second.count; i++)
{
if (p.second.buffers[i] != nullptr)
p.second.buffers[i]->release();
}
delete[] p.second.buffers;
}
}
}
@@ -106,6 +117,7 @@ void Shader::mapActiveUniforms()
u.location = glGetUniformLocation(program, u.name.c_str());
u.baseType = getUniformBaseType(gltype);
u.textureType = getUniformTextureType(gltype);
u.texelBufferType = getUniformTexelBufferType(gltype);
u.isDepthSampler = isDepthTextureType(gltype);
if (u.baseType == UNIFORM_MATRIX)
@@ -129,12 +141,22 @@ void Shader::mapActiveUniforms()
if (u.location == -1)
continue;
if (u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN)
if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER)
{
TextureUnit unit;
unit.type = u.textureType;
unit.active = true;
unit.texture = gl.getDefaultTexture(u.textureType);
if (u.baseType == UNIFORM_TEXELBUFFER)
{
unit.isTexelBuffer = true;
unit.texture = gl.getDefaultTexelBuffer();
}
else
{
unit.isTexelBuffer = false;
unit.texture = gl.getDefaultTexture(u.textureType);
}
for (int i = 0; i < u.count; i++)
textureUnits.push_back(unit);
@@ -164,6 +186,7 @@ void Shader::mapActiveUniforms()
case UNIFORM_INT:
case UNIFORM_BOOL:
case UNIFORM_SAMPLER:
case UNIFORM_TEXELBUFFER:
u.dataSize = sizeof(int) * u.components * u.count;
u.data = malloc(u.dataSize);
break;
@@ -183,7 +206,7 @@ void Shader::mapActiveUniforms()
{
memset(u.data, 0, u.dataSize);
if (u.baseType == UNIFORM_SAMPLER)
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_TEXELBUFFER)
{
int startunit = (int) textureUnits.size() - u.count;
@@ -195,8 +218,16 @@ void Shader::mapActiveUniforms()
glUniform1iv(u.location, u.count, u.ints);
u.textures = new love::graphics::Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
if (u.baseType == UNIFORM_TEXELBUFFER)
{
u.buffers = new love::graphics::Buffer*[u.count];
memset(u.buffers, 0, sizeof(Buffer *) * u.count);
}
else
{
u.textures = new love::graphics::Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
}
@@ -257,7 +288,6 @@ void Shader::mapActiveUniforms()
{
if (u.textures[i] == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
if (v != nullptr)
v->loadVolatile();
@@ -265,6 +295,19 @@ void Shader::mapActiveUniforms()
sendTextures(&u, u.textures, u.count, true);
}
else if (u.baseType == UNIFORM_TEXELBUFFER)
{
for (int i = 0; i < u.count; i++)
{
if (u.buffers[i] == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(u.buffers[i]);
if (v != nullptr)
v->loadVolatile();
}
sendBuffers(&u, u.buffers, u.count, true);
}
}
// Make sure uniforms that existed before but don't exist anymore are
@@ -275,16 +318,26 @@ void Shader::mapActiveUniforms()
{
free(p.second.data);
if (p.second.baseType != UNIFORM_SAMPLER)
continue;
for (int i = 0; i < p.second.count; i++)
if (p.second.baseType == UNIFORM_SAMPLER)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
delete[] p.second.textures;
}
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
{
for (int i = 0; i < p.second.count; i++)
{
if (p.second.buffers[i] != nullptr)
p.second.buffers[i]->release();
}
delete[] p.second.buffers;
}
}
}
@@ -322,7 +375,7 @@ bool Shader::loadVolatile()
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
{
const char *name = nullptr;
if (vertex::getConstant((BuiltinVertexAttribute) i, name))
if (graphics::getConstant((BuiltinVertexAttribute) i, name))
glBindAttribLocation(program, i, (const GLchar *) name);
}
@@ -430,7 +483,12 @@ void Shader::attach()
{
const TextureUnit &unit = textureUnits[i];
if (unit.active)
gl.bindTextureToUnit(unit.type, unit.texture, i, false);
{
if (unit.isTexelBuffer)
gl.bindBufferTextureToUnit(unit.texture, i, false, false);
else
gl.bindTextureToUnit(unit.type, unit.texture, i, false, false);
}
}
// send any pending uniforms to the shader program.
@@ -493,7 +551,7 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
break;
}
}
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER)
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_TEXELBUFFER)
{
switch (info->components)
{
@@ -627,7 +685,110 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
int texunit = info->ints[i];
if (shaderactive)
gl.bindTextureToUnit(info->textureType, gltex, texunit, false);
gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
}
}
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
{
Shader::sendBuffers(info, buffers, count, false);
}
static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
{
if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
return true;
if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
return true;
return false;
}
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_TEXELBUFFER)
return;
uint32 requiredtypeflags = Buffer::TYPEFLAG_TEXEL;
bool shaderactive = current == this;
if (!internalUpdate && shaderactive)
flushBatchedDraws();
count = std::min(count, info->count);
// Bind the textures to the texture units.
for (int i = 0; i < count; i++)
{
love::graphics::Buffer *buffer = buffers[i];
if (buffer != nullptr)
{
if ((buffer->getTypeFlags() & requiredtypeflags) == 0)
{
if (internalUpdate)
continue;
else
throw love::Exception("Shader uniform '%s' is a texel buffer, but the given Buffer was not created with texel buffer capabilities.", info->name.c_str());
}
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
{
if (internalUpdate)
continue;
else
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
}
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] = buffer;
GLuint gltex = 0;
if (buffers[i] != nullptr)
gltex = (GLuint) buffer->getTexelBufferHandle();
else
gltex = gl.getDefaultTexelBuffer();
int texunit = info->ints[i];
if (shaderactive)
gl.bindBufferTextureToUnit(gltex, texunit, false, false);
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
@@ -746,11 +907,20 @@ 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
{
if (getUniformBaseType(type) == UNIFORM_SAMPLER)
UniformType basetype = getUniformBaseType(type);
if (basetype == UNIFORM_SAMPLER || basetype == UNIFORM_TEXELBUFFER)
return 1;
switch (type)
@@ -879,6 +1049,10 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const
case GL_SAMPLER_CUBE_MAP_ARRAY:
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
return UNIFORM_SAMPLER;
case GL_SAMPLER_BUFFER:
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return UNIFORM_TEXELBUFFER;
default:
return UNIFORM_UNKNOWN;
}
@@ -922,6 +1096,21 @@ TextureType Shader::getUniformTextureType(GLenum type) const
}
}
DataBaseType Shader::getUniformTexelBufferType(GLenum type) const
{
switch (type)
{
case GL_SAMPLER_BUFFER:
return DATA_BASETYPE_FLOAT;
case GL_INT_SAMPLER_BUFFER:
return DATA_BASETYPE_INT;
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return DATA_BASETYPE_UINT;
default:
return DATA_BASETYPE_MAX_ENUM;
}
}
bool Shader::isDepthTextureType(GLenum type) const
{
switch (type)
+6
View File
@@ -62,6 +62,7 @@ public:
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
void updateUniform(const UniformInfo *info, int count) override;
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
bool hasUniform(const std::string &name) const override;
ptrdiff_t getHandle() const override;
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
@@ -75,6 +76,7 @@ private:
{
GLuint texture = 0;
TextureType type = TEXTURE_2D;
bool isTexelBuffer = false;
bool active = false;
};
@@ -83,11 +85,13 @@ private:
void updateUniform(const UniformInfo *info, int count, bool internalupdate);
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalupdate);
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate);
int getUniformTypeComponents(GLenum type) const;
MatrixSize getMatrixSize(GLenum type) const;
UniformType getUniformBaseType(GLenum type) const;
TextureType getUniformTextureType(GLenum type) const;
DataBaseType getUniformTexelBufferType(GLenum type) const;
bool isDepthTextureType(GLenum type) const;
void flushBatchedDraws() const;
@@ -112,6 +116,8 @@ private:
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
std::vector<Buffer *> buffersToUnmap;
float lastPointSize;
}; // Shader
+10
View File
@@ -42,6 +42,11 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
glGenFramebuffers(1, &framebuffer);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
// Intel driver bug: https://github.com/love2d/love/issues/1592
bool current_srgb = gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB);
if (current_srgb && isPixelFormatDepthStencil(format))
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, false);
if (texture != 0)
{
if (isPixelFormatDepthStencil(format) && (GLAD_ES_VERSION_3_0 || !GLAD_ES_VERSION_2_0))
@@ -103,6 +108,11 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
// Restore sRGB state if we turned it off above.
if (current_srgb && isPixelFormatDepthStencil(format))
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, current_srgb);
return status;
}
+206 -233
View File
@@ -25,8 +25,6 @@ namespace love
{
namespace graphics
{
namespace vertex
{
static_assert(sizeof(Color32) == 4, "sizeof(Color32) incorrect!");
static_assert(sizeof(STf_RGBAub) == sizeof(float)*2 + sizeof(Color32), "sizeof(STf_RGBAub) incorrect!");
@@ -103,6 +101,68 @@ int getFormatPositionComponents(CommonFormat format)
return 0;
}
// Order here relies on order of DataFormat enum.
static const DataFormatInfo dataFormatInfo[]
{
// baseType, isMatrix, components, rows, columns, componentSize, size
{ DATA_BASETYPE_FLOAT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_FLOAT
{ DATA_BASETYPE_FLOAT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_FLOAT_VEC2
{ DATA_BASETYPE_FLOAT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_FLOAT_VEC3
{ DATA_BASETYPE_FLOAT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_FLOAT_VEC4
{ DATA_BASETYPE_FLOAT, true, 0, 2, 2, 4, 16 }, // DATAFORMAT_FLOAT_MAT2X2
{ DATA_BASETYPE_FLOAT, true, 0, 2, 3, 4, 24 }, // DATAFORMAT_FLOAT_MAT2X3
{ DATA_BASETYPE_FLOAT, true, 0, 2, 4, 4, 32 }, // DATAFORMAT_FLOAT_MAT2X4
{ DATA_BASETYPE_FLOAT, true, 0, 3, 2, 4, 24 }, // DATAFORMAT_FLOAT_MAT3X2
{ DATA_BASETYPE_FLOAT, true, 0, 3, 3, 4, 36 }, // DATAFORMAT_FLOAT_MAT3X3
{ DATA_BASETYPE_FLOAT, true, 0, 3, 4, 4, 48 }, // DATAFORMAT_FLOAT_MAT3X4
{ DATA_BASETYPE_FLOAT, true, 0, 4, 2, 4, 32 }, // DATAFORMAT_FLOAT_MAT4X2
{ DATA_BASETYPE_FLOAT, true, 0, 4, 3, 4, 48 }, // DATAFORMAT_FLOAT_MAT4X3
{ DATA_BASETYPE_FLOAT, true, 0, 4, 4, 4, 64 }, // DATAFORMAT_FLOAT_MAT4X4
{ DATA_BASETYPE_INT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_INT32
{ DATA_BASETYPE_INT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_INT32_VEC2
{ DATA_BASETYPE_INT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_INT32_VEC3
{ DATA_BASETYPE_INT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_INT32_VEC4
{ DATA_BASETYPE_UINT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_UINT32
{ DATA_BASETYPE_UINT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_UINT32_VEC2
{ DATA_BASETYPE_UINT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_UINT32_VEC3
{ DATA_BASETYPE_UINT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_UINT32_VEC4
{ DATA_BASETYPE_SNORM, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_SNORM8_VEC4
{ DATA_BASETYPE_UNORM, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_UNORM8_VEC4
{ DATA_BASETYPE_INT, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_INT8_VEC4
{ DATA_BASETYPE_UINT, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_UINT8_VEC4
{ DATA_BASETYPE_SNORM, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_SNORM16_VEC2
{ DATA_BASETYPE_SNORM, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_SNORM16_VEC4
{ DATA_BASETYPE_UNORM, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_UNORM16_VEC2
{ DATA_BASETYPE_UNORM, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_UNORM16_VEC4
{ DATA_BASETYPE_INT, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_INT16_VEC2
{ DATA_BASETYPE_INT, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_INT16_VEC4
{ DATA_BASETYPE_UINT, false, 1, 0, 0, 2, 2 }, // DATAFORMAT_UINT16
{ DATA_BASETYPE_UINT, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_UINT16_VEC2
{ DATA_BASETYPE_UINT, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_UINT16_VEC4
{ DATA_BASETYPE_BOOL, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_BOOL
{ DATA_BASETYPE_BOOL, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_BOOL_VEC2
{ DATA_BASETYPE_BOOL, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_BOOL_VEC3
{ DATA_BASETYPE_BOOL, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_BOOL_VEC4
};
static_assert((sizeof(dataFormatInfo) / sizeof(DataFormatInfo)) == DATAFORMAT_MAX_ENUM, "dataFormatInfo array size must match number of DataFormat enum values.");
const DataFormatInfo &getDataFormatInfo(DataFormat format)
{
return dataFormatInfo[format];
}
size_t getIndexDataSize(IndexDataType type)
{
switch (type)
@@ -113,63 +173,36 @@ size_t getIndexDataSize(IndexDataType type)
}
}
size_t getDataTypeSize(DataType datatype)
{
switch (datatype)
{
case DATA_SNORM8:
case DATA_UNORM8:
case DATA_INT8:
case DATA_UINT8:
return sizeof(uint8);
case DATA_SNORM16:
case DATA_UNORM16:
case DATA_INT16:
case DATA_UINT16:
return sizeof(uint16);
case DATA_INT32:
case DATA_UINT32:
return sizeof(uint32);
case DATA_FLOAT:
return sizeof(float);
case DATA_MAX_ENUM:
return 0;
}
return 0;
}
bool isDataTypeInteger(DataType datatype)
{
switch (datatype)
{
case DATA_INT8:
case DATA_UINT8:
case DATA_INT16:
case DATA_UINT16:
case DATA_INT32:
case DATA_UINT32:
return true;
default:
return false;
}
}
IndexDataType getIndexDataTypeFromMax(size_t maxvalue)
{
IndexDataType types[] = {INDEX_UINT16, INDEX_UINT32};
return types[maxvalue > LOVE_UINT16_MAX ? 1 : 0];
return maxvalue > LOVE_UINT16_MAX ? INDEX_UINT32 : INDEX_UINT16;
}
DataFormat getIndexDataFormat(IndexDataType type)
{
return type == INDEX_UINT32 ? DATAFORMAT_UINT32 : DATAFORMAT_UINT16;
}
IndexDataType getIndexDataType(DataFormat format)
{
switch (format)
{
case DATAFORMAT_UINT16: return INDEX_UINT16;
case DATAFORMAT_UINT32: return INDEX_UINT32;
default: return INDEX_MAX_ENUM;
}
}
int getIndexCount(TriangleIndexMode mode, int vertexCount)
{
switch (mode)
{
case TriangleIndexMode::NONE:
case TRIANGLEINDEX_NONE:
return 0;
case TriangleIndexMode::STRIP:
case TriangleIndexMode::FAN:
case TRIANGLEINDEX_STRIP:
case TRIANGLEINDEX_FAN:
return 3 * (vertexCount - 2);
case TriangleIndexMode::QUADS:
case TRIANGLEINDEX_QUADS:
return vertexCount * 6 / 4;
}
return 0;
@@ -180,9 +213,9 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
{
switch (mode)
{
case TriangleIndexMode::NONE:
case TRIANGLEINDEX_NONE:
break;
case TriangleIndexMode::STRIP:
case TRIANGLEINDEX_STRIP:
{
int i = 0;
for (T index = 0; index < vertexCount - 2; index++)
@@ -193,7 +226,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
}
}
break;
case TriangleIndexMode::FAN:
case TRIANGLEINDEX_FAN:
{
int i = 0;
for (T index = 2; index < vertexCount; index++)
@@ -204,7 +237,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
}
}
break;
case TriangleIndexMode::QUADS:
case TRIANGLEINDEX_QUADS:
{
// 0---2
// | / |
@@ -238,7 +271,7 @@ void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount,
fillIndicesT(mode, vertexStart, vertexCount, indices);
}
void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
void VertexAttributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
{
setBufferLayout(bufferindex, (uint16) getFormatStride(format));
@@ -247,241 +280,181 @@ void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
case CommonFormat::NONE:
break;
case CommonFormat::XYf:
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
break;
case CommonFormat::XYZf:
set(ATTRIB_POS, DATA_FLOAT, 3, 0, bufferindex);
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC3, 0, bufferindex);
break;
case CommonFormat::RGBAub:
set(ATTRIB_COLOR, DATA_UNORM8, 4, 0, bufferindex);
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, 0, bufferindex);
break;
case CommonFormat::STf_RGBAub:
set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, 0, bufferindex);
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2), bufferindex);
break;
case CommonFormat::STPf_RGBAub:
set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, 0, bufferindex);
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 3), bufferindex);
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, 0, bufferindex);
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 3), bufferindex);
break;
case CommonFormat::XYf_STf:
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex);
break;
case CommonFormat::XYf_STPf:
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex);
break;
case CommonFormat::XYf_STf_RGBAub:
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 4), bufferindex);
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 4), bufferindex);
break;
case CommonFormat::XYf_STus_RGBAub:
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATA_UNORM16, 2, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex);
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATAFORMAT_UNORM16_VEC2, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex);
break;
case CommonFormat::XYf_STPf_RGBAub:
set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 5), bufferindex);
set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex);
set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 5), bufferindex);
break;
}
}
static StringMap<BuiltinVertexAttribute, ATTRIB_MAX_ENUM>::Entry attribNameEntries[] =
STRINGMAP_BEGIN(BuiltinVertexAttribute, ATTRIB_MAX_ENUM, attribName)
{
{ "VertexPosition", ATTRIB_POS },
{ "VertexTexCoord", ATTRIB_TEXCOORD },
{ "VertexColor", ATTRIB_COLOR },
};
}
STRINGMAP_END(BuiltinVertexAttribute, ATTRIB_MAX_ENUM, attribName)
static StringMap<BuiltinVertexAttribute, ATTRIB_MAX_ENUM> attribNames(attribNameEntries, sizeof(attribNameEntries));
const char *getConstant(BuiltinVertexAttribute attrib)
{
const char *name = nullptr;
getConstant(attrib, name);
return name;
}
static StringMap<IndexDataType, INDEX_MAX_ENUM>::Entry indexTypeEntries[] =
STRINGMAP_BEGIN(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
{
{ "vertex", BUFFERTYPE_VERTEX },
{ "index", BUFFERTYPE_INDEX },
{ "texel", BUFFERTYPE_TEXEL },
}
STRINGMAP_END(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
STRINGMAP_BEGIN(IndexDataType, INDEX_MAX_ENUM, indexType)
{
{ "uint16", INDEX_UINT16 },
{ "uint32", INDEX_UINT32 },
};
}
STRINGMAP_END(IndexDataType, INDEX_MAX_ENUM, indexType)
static StringMap<IndexDataType, INDEX_MAX_ENUM> indexTypes(indexTypeEntries, sizeof(indexTypeEntries));
static StringMap<Usage, USAGE_MAX_ENUM>::Entry usageEntries[] =
STRINGMAP_BEGIN(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsage)
{
{ "stream", USAGE_STREAM },
{ "dynamic", USAGE_DYNAMIC },
{ "static", USAGE_STATIC },
};
{ "stream", BUFFERUSAGE_STREAM },
{ "dynamic", BUFFERUSAGE_DYNAMIC },
{ "static", BUFFERUSAGE_STATIC },
}
STRINGMAP_END(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsage)
static StringMap<Usage, USAGE_MAX_ENUM> usages(usageEntries, sizeof(usageEntries));
static StringMap<PrimitiveType, PRIMITIVE_MAX_ENUM>::Entry primitiveTypeEntries[] =
STRINGMAP_BEGIN(PrimitiveType, PRIMITIVE_MAX_ENUM, primitiveType)
{
{ "fan", PRIMITIVE_TRIANGLE_FAN },
{ "strip", PRIMITIVE_TRIANGLE_STRIP },
{ "triangles", PRIMITIVE_TRIANGLES },
{ "points", PRIMITIVE_POINTS },
};
}
STRINGMAP_END(PrimitiveType, PRIMITIVE_MAX_ENUM, primitiveType)
static StringMap<PrimitiveType, PRIMITIVE_MAX_ENUM> primitiveTypes(primitiveTypeEntries, sizeof(primitiveTypeEntries));
static StringMap<AttributeStep, STEP_MAX_ENUM>::Entry attributeStepEntries[] =
STRINGMAP_BEGIN(AttributeStep, STEP_MAX_ENUM, attributeStep)
{
{ "pervertex", STEP_PER_VERTEX },
{ "perinstance", STEP_PER_INSTANCE },
};
}
STRINGMAP_END(AttributeStep, STEP_MAX_ENUM, attributeStep)
static StringMap<AttributeStep, STEP_MAX_ENUM> attributeSteps(attributeStepEntries, sizeof(attributeStepEntries));
static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[] =
STRINGMAP_BEGIN(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat)
{
{ "snorm8", DATA_SNORM8 },
{ "unorm8", DATA_UNORM8 },
{ "int8", DATA_INT8 },
{ "uint8", DATA_UINT8 },
{ "snorm16", DATA_SNORM16 },
{ "unorm16", DATA_UNORM16 },
{ "int16", DATA_INT16 },
{ "uint16", DATA_UINT16 },
{ "int32", DATA_INT32 },
{ "uint32", DATA_UINT32 },
{ "float", DATA_FLOAT },
};
{ "float", DATAFORMAT_FLOAT },
{ "floatvec2", DATAFORMAT_FLOAT_VEC2 },
{ "floatvec3", DATAFORMAT_FLOAT_VEC3 },
{ "floatvec4", DATAFORMAT_FLOAT_VEC4 },
static StringMap<DataType, DATA_MAX_ENUM> dataTypes(dataTypeEntries, sizeof(dataTypeEntries));
{ "floatmat2x2", DATAFORMAT_FLOAT_MAT2X2 },
{ "floatmat2x3", DATAFORMAT_FLOAT_MAT2X3 },
{ "floatmat2x4", DATAFORMAT_FLOAT_MAT2X4 },
static StringMap<CullMode, CULL_MAX_ENUM>::Entry cullModeEntries[] =
{ "floatmat3x2", DATAFORMAT_FLOAT_MAT3X2 },
{ "floatmat3x3", DATAFORMAT_FLOAT_MAT3X3 },
{ "floatmat3x4", DATAFORMAT_FLOAT_MAT3X4 },
{ "floatmat4x2", DATAFORMAT_FLOAT_MAT4X2 },
{ "floatmat4x3", DATAFORMAT_FLOAT_MAT4X3 },
{ "floatmat4x4", DATAFORMAT_FLOAT_MAT4X4 },
{ "int32", DATAFORMAT_INT32 },
{ "int32vec2", DATAFORMAT_INT32_VEC2 },
{ "int32vec3", DATAFORMAT_INT32_VEC3 },
{ "int32vec4", DATAFORMAT_INT32_VEC4 },
{ "uint32", DATAFORMAT_UINT32 },
{ "uint32vec2", DATAFORMAT_UINT32_VEC2 },
{ "uint32vec3", DATAFORMAT_UINT32_VEC3 },
{ "uint32vec4", DATAFORMAT_UINT32_VEC4 },
{ "snorm8vec4", DATAFORMAT_SNORM8_VEC4 },
{ "unorm8vec4", DATAFORMAT_UNORM8_VEC4 },
{ "int8vec4", DATAFORMAT_INT8_VEC4 },
{ "uint8vec4", DATAFORMAT_UINT8_VEC4 },
{ "snorm16vec2", DATAFORMAT_SNORM16_VEC2 },
{ "snorm16vec4", DATAFORMAT_SNORM16_VEC4 },
{ "unorm16vec2", DATAFORMAT_UNORM16_VEC2 },
{ "unorm16vec4", DATAFORMAT_UNORM16_VEC4 },
{ "int16vec2", DATAFORMAT_INT16_VEC2 },
{ "int16vec4", DATAFORMAT_INT16_VEC4 },
{ "uint16", DATAFORMAT_UINT16 },
{ "uint16vec2", DATAFORMAT_UINT16_VEC2 },
{ "uint16vec4", DATAFORMAT_UINT16_VEC4 },
{ "bool", DATAFORMAT_BOOL },
{ "boolvec2", DATAFORMAT_BOOL_VEC2 },
{ "boolvec3", DATAFORMAT_BOOL_VEC3 },
{ "boolvec4", DATAFORMAT_BOOL_VEC4 },
}
STRINGMAP_END(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat)
STRINGMAP_BEGIN(DataBaseType, DATA_BASETYPE_MAX_ENUM, dataBaseType)
{
{ "float", DATA_BASETYPE_FLOAT },
{ "int", DATA_BASETYPE_INT },
{ "uint", DATA_BASETYPE_UINT },
{ "snorm", DATA_BASETYPE_SNORM },
{ "unorm", DATA_BASETYPE_UNORM },
{ "bool", DATA_BASETYPE_BOOL },
}
STRINGMAP_END(DataBaseType, DATA_BASETYPE_MAX_ENUM, dataBaseType)
STRINGMAP_BEGIN(CullMode, CULL_MAX_ENUM, cullMode)
{
{ "none", CULL_NONE },
{ "back", CULL_BACK },
{ "front", CULL_FRONT },
};
}
STRINGMAP_END(CullMode, CULL_MAX_ENUM, cullMode)
static StringMap<CullMode, CULL_MAX_ENUM> cullModes(cullModeEntries, sizeof(cullModeEntries));
static StringMap<Winding, WINDING_MAX_ENUM>::Entry windingEntries[] =
STRINGMAP_BEGIN(Winding, WINDING_MAX_ENUM, winding)
{
{ "cw", WINDING_CW },
{ "ccw", WINDING_CCW },
};
static StringMap<Winding, WINDING_MAX_ENUM> windings(windingEntries, sizeof(windingEntries));
bool getConstant(const char *in, BuiltinVertexAttribute &out)
{
return attribNames.find(in, out);
}
STRINGMAP_END(Winding, WINDING_MAX_ENUM, winding)
bool getConstant(BuiltinVertexAttribute in, const char *&out)
{
return attribNames.find(in, out);
}
bool getConstant(const char *in, IndexDataType &out)
{
return indexTypes.find(in, out);
}
bool getConstant(IndexDataType in, const char *&out)
{
return indexTypes.find(in, out);
}
std::vector<std::string> getConstants(IndexDataType)
{
return indexTypes.getNames();
}
bool getConstant(const char *in, Usage &out)
{
return usages.find(in, out);
}
bool getConstant(Usage in, const char *&out)
{
return usages.find(in, out);
}
std::vector<std::string> getConstants(Usage)
{
return usages.getNames();
}
bool getConstant(const char *in, PrimitiveType &out)
{
return primitiveTypes.find(in, out);
}
bool getConstant(PrimitiveType in, const char *&out)
{
return primitiveTypes.find(in, out);
}
std::vector<std::string> getConstants(PrimitiveType)
{
return primitiveTypes.getNames();
}
bool getConstant(const char *in, AttributeStep &out)
{
return attributeSteps.find(in, out);
}
bool getConstant(AttributeStep in, const char *&out)
{
return attributeSteps.find(in, out);
}
std::vector<std::string> getConstants(AttributeStep)
{
return attributeSteps.getNames();
}
bool getConstant(const char *in, DataType &out)
{
return dataTypes.find(in, out);
}
bool getConstant(DataType in, const char *&out)
{
return dataTypes.find(in, out);
}
std::vector<std::string> getConstants(DataType)
{
return dataTypes.getNames();
}
bool getConstant(const char *in, CullMode &out)
{
return cullModes.find(in, out);
}
bool getConstant(CullMode in, const char *&out)
{
return cullModes.find(in, out);
}
std::vector<std::string> getConstants(CullMode)
{
return cullModes.getNames();
}
bool getConstant(const char *in, Winding &out)
{
return windings.find(in, out);
}
bool getConstant(Winding in, const char *&out)
{
return windings.find(in, out);
}
std::vector<std::string> getConstants(Winding)
{
return windings.getNames();
}
} // vertex
} // graphics
} // love
+117 -80
View File
@@ -23,6 +23,7 @@
// LOVE
#include "common/int.h"
#include "common/Color.h"
#include "common/StringMap.h"
// C
#include <stddef.h>
@@ -46,7 +47,7 @@ enum BuiltinVertexAttribute
ATTRIB_MAX_ENUM
};
enum BuiltinVertexAttributeFlag
enum BuiltinVertexAttributeFlags
{
ATTRIBFLAG_POS = 1 << ATTRIB_POS,
ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD,
@@ -55,10 +56,10 @@ enum BuiltinVertexAttributeFlag
enum BufferType
{
BUFFER_VERTEX = 0,
BUFFER_INDEX,
BUFFER_UNIFORM,
BUFFER_MAX_ENUM
BUFFERTYPE_VERTEX = 0,
BUFFERTYPE_INDEX,
BUFFERTYPE_TEXEL,
BUFFERTYPE_MAX_ENUM
};
enum IndexDataType
@@ -93,36 +94,81 @@ enum CullMode
CULL_MAX_ENUM
};
namespace vertex
// The expected usage pattern of buffer data.
enum BufferUsage
{
// The expected usage pattern of vertex data.
enum Usage
{
USAGE_STREAM,
USAGE_DYNAMIC,
USAGE_STATIC,
USAGE_MAX_ENUM
BUFFERUSAGE_STREAM,
BUFFERUSAGE_DYNAMIC,
BUFFERUSAGE_STATIC,
BUFFERUSAGE_MAX_ENUM
};
enum DataType
// Value types used when interfacing with the GPU (vertex and shader data).
// The order of this enum affects the dataFormatInfo array.
enum DataFormat
{
DATA_SNORM8,
DATA_UNORM8,
DATA_INT8,
DATA_UINT8,
DATAFORMAT_FLOAT,
DATAFORMAT_FLOAT_VEC2,
DATAFORMAT_FLOAT_VEC3,
DATAFORMAT_FLOAT_VEC4,
DATA_SNORM16,
DATA_UNORM16,
DATA_INT16,
DATA_UINT16,
DATAFORMAT_FLOAT_MAT2X2,
DATAFORMAT_FLOAT_MAT2X3,
DATAFORMAT_FLOAT_MAT2X4,
DATA_INT32,
DATA_UINT32,
DATAFORMAT_FLOAT_MAT3X2,
DATAFORMAT_FLOAT_MAT3X3,
DATAFORMAT_FLOAT_MAT3X4,
DATA_FLOAT,
DATAFORMAT_FLOAT_MAT4X2,
DATAFORMAT_FLOAT_MAT4X3,
DATAFORMAT_FLOAT_MAT4X4,
DATA_MAX_ENUM
DATAFORMAT_INT32,
DATAFORMAT_INT32_VEC2,
DATAFORMAT_INT32_VEC3,
DATAFORMAT_INT32_VEC4,
DATAFORMAT_UINT32,
DATAFORMAT_UINT32_VEC2,
DATAFORMAT_UINT32_VEC3,
DATAFORMAT_UINT32_VEC4,
DATAFORMAT_SNORM8_VEC4,
DATAFORMAT_UNORM8_VEC4,
DATAFORMAT_INT8_VEC4,
DATAFORMAT_UINT8_VEC4,
DATAFORMAT_SNORM16_VEC2,
DATAFORMAT_SNORM16_VEC4,
DATAFORMAT_UNORM16_VEC2,
DATAFORMAT_UNORM16_VEC4,
DATAFORMAT_INT16_VEC2,
DATAFORMAT_INT16_VEC4,
DATAFORMAT_UINT16,
DATAFORMAT_UINT16_VEC2,
DATAFORMAT_UINT16_VEC4,
DATAFORMAT_BOOL,
DATAFORMAT_BOOL_VEC2,
DATAFORMAT_BOOL_VEC3,
DATAFORMAT_BOOL_VEC4,
DATAFORMAT_MAX_ENUM
};
enum DataBaseType
{
DATA_BASETYPE_FLOAT,
DATA_BASETYPE_INT,
DATA_BASETYPE_UINT,
DATA_BASETYPE_SNORM,
DATA_BASETYPE_UNORM,
DATA_BASETYPE_BOOL,
DATA_BASETYPE_MAX_ENUM
};
enum Winding
@@ -132,12 +178,12 @@ enum Winding
WINDING_MAX_ENUM
};
enum class TriangleIndexMode
enum TriangleIndexMode
{
NONE,
STRIP,
FAN,
QUADS,
TRIANGLEINDEX_NONE,
TRIANGLEINDEX_STRIP,
TRIANGLEINDEX_FAN,
TRIANGLEINDEX_QUADS,
};
enum class CommonFormat
@@ -155,6 +201,17 @@ enum class CommonFormat
XYf_STPf_RGBAub,
};
struct DataFormatInfo
{
DataBaseType baseType;
bool isMatrix;
int components;
int matrixRows;
int matrixColumns;
size_t componentSize;
size_t size;
};
struct STf_RGBAub
{
float s, t;
@@ -186,6 +243,8 @@ struct XYf_STf_RGBAub
Color32 color;
};
typedef XYf_STf_RGBAub Vertex;
struct XYf_STus_RGBAub
{
float x, y;
@@ -222,42 +281,41 @@ struct BufferBindings
void clear() { useBits = 0; }
};
struct AttributeInfo
struct VertexAttributeInfo
{
DataType type;
uint8 components;
uint8 bufferIndex;
DataFormat format : 8;
uint16 offsetFromVertex;
};
struct BufferLayout
struct VertexBufferLayout
{
// Attribute step rate is stored outside this struct as a bitmask.
uint16 stride;
};
struct Attributes
struct VertexAttributes
{
static const uint32 MAX = 32;
uint32 enableBits = 0; // indexed by attribute
uint32 instanceBits = 0; // indexed by buffer
AttributeInfo attribs[MAX];
BufferLayout bufferLayouts[BufferBindings::MAX];
VertexAttributeInfo attribs[MAX];
VertexBufferLayout bufferLayouts[BufferBindings::MAX];
Attributes() {}
Attributes(CommonFormat format, uint8 bufferindex)
VertexAttributes() {}
VertexAttributes(CommonFormat format, uint8 bufferindex)
{
setCommonFormat(format, bufferindex);
}
void set(uint32 index, DataType type, uint8 components, uint16 offsetfromvertex, uint8 bufferindex)
void set(uint32 index, DataFormat format, uint16 offsetfromvertex, uint8 bufferindex)
{
enableBits |= (1u << index);
attribs[index].bufferIndex = bufferindex;
attribs[index].type = type;
attribs[index].components = components;
attribs[index].format = format;
attribs[index].offsetFromVertex = offsetfromvertex;
}
@@ -307,51 +365,30 @@ inline CommonFormat getSinglePositionFormat(bool is2D)
return is2D ? CommonFormat::XYf : CommonFormat::XYZf;
}
size_t getIndexDataSize(IndexDataType type);
size_t getDataTypeSize(DataType datatype);
bool isDataTypeInteger(DataType datatype);
const DataFormatInfo &getDataFormatInfo(DataFormat format);
size_t getIndexDataSize(IndexDataType type);
IndexDataType getIndexDataTypeFromMax(size_t maxvalue);
DataFormat getIndexDataFormat(IndexDataType type);
IndexDataType getIndexDataType(DataFormat format);
int getIndexCount(TriangleIndexMode mode, int vertexCount);
void fillIndices(TriangleIndexMode mode, uint16 vertexStart, uint16 vertexCount, uint16 *indices);
void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices);
bool getConstant(const char *in, BuiltinVertexAttribute &out);
bool getConstant(BuiltinVertexAttribute in, const char *&out);
STRINGMAP_DECLARE(BuiltinVertexAttribute);
STRINGMAP_DECLARE(BufferType);
STRINGMAP_DECLARE(IndexDataType);
STRINGMAP_DECLARE(BufferUsage);
STRINGMAP_DECLARE(PrimitiveType);
STRINGMAP_DECLARE(AttributeStep);
STRINGMAP_DECLARE(DataFormat);
STRINGMAP_DECLARE(DataBaseType);
STRINGMAP_DECLARE(CullMode);
STRINGMAP_DECLARE(Winding);
bool getConstant(const char *in, IndexDataType &out);
bool getConstant(IndexDataType in, const char *&out);
std::vector<std::string> getConstants(IndexDataType);
bool getConstant(const char *in, Usage &out);
bool getConstant(Usage in, const char *&out);
std::vector<std::string> getConstants(Usage);
bool getConstant(const char *in, PrimitiveType &out);
bool getConstant(PrimitiveType in, const char *&out);
std::vector<std::string> getConstants(PrimitiveType);
bool getConstant(const char *in, AttributeStep &out);
bool getConstant(AttributeStep in, const char *&out);
std::vector<std::string> getConstants(AttributeStep);
bool getConstant(const char *in, DataType &out);
bool getConstant(DataType in, const char *&out);
std::vector<std::string> getConstants(DataType);
bool getConstant(const char *in, CullMode &out);
bool getConstant(CullMode in, const char *&out);
std::vector<std::string> getConstants(CullMode);
bool getConstant(const char *in, Winding &out);
bool getConstant(Winding in, const char *&out);
std::vector<std::string> getConstants(Winding);
} // vertex
typedef vertex::XYf_STf_RGBAub Vertex;
const char *getConstant(BuiltinVertexAttribute attrib);
} // graphics
} // love
+474
View File
@@ -0,0 +1,474 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_Buffer.h"
#include "Buffer.h"
#include "common/Data.h"
namespace love
{
namespace graphics
{
static const double defaultComponents[] = {0.0, 0.0, 0.0, 1.0};
template <typename T>
static inline size_t writeData(lua_State *L, int startidx, int components, char *data)
{
auto componentdata = (T *) data;
for (int i = 0; i < components; i++)
componentdata[i] = (T) (luaL_optnumber(L, startidx + i, defaultComponents[i]));
return sizeof(T) * components;
}
template <typename T>
static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data)
{
auto componentdata = (T *) data;
const auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval);
return sizeof(T) * components;
}
template <typename T>
static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data)
{
auto componentdata = (T *) data;
const auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval);
return sizeof(T) * components;
}
void luax_writebufferdata(lua_State *L, int startidx, DataFormat format, char *data)
{
switch (format)
{
case DATAFORMAT_FLOAT: writeData<float>(L, startidx, 1, data); break;
case DATAFORMAT_FLOAT_VEC2: writeData<float>(L, startidx, 2, data); break;
case DATAFORMAT_FLOAT_VEC3: writeData<float>(L, startidx, 3, data); break;
case DATAFORMAT_FLOAT_VEC4: writeData<float>(L, startidx, 4, data); break;
case DATAFORMAT_INT32: writeData<int32>(L, startidx, 1, data); break;
case DATAFORMAT_INT32_VEC2: writeData<int32>(L, startidx, 2, data); break;
case DATAFORMAT_INT32_VEC3: writeData<int32>(L, startidx, 3, data); break;
case DATAFORMAT_INT32_VEC4: writeData<int32>(L, startidx, 4, data); break;
case DATAFORMAT_UINT32: writeData<uint32>(L, startidx, 1, data); break;
case DATAFORMAT_UINT32_VEC2: writeData<uint32>(L, startidx, 2, data); break;
case DATAFORMAT_UINT32_VEC3: writeData<uint32>(L, startidx, 3, data); break;
case DATAFORMAT_UINT32_VEC4: writeData<uint32>(L, startidx, 4, data); break;
case DATAFORMAT_SNORM8_VEC4: writeSNormData<int8>(L, startidx, 4, data); break;
case DATAFORMAT_UNORM8_VEC4: writeUNormData<uint8>(L, startidx, 4, data); break;
case DATAFORMAT_INT8_VEC4: writeData<int8>(L, startidx, 4, data); break;
case DATAFORMAT_UINT8_VEC4: writeData<uint8>(L, startidx, 4, data); break;
case DATAFORMAT_SNORM16_VEC2: writeSNormData<int16>(L, startidx, 2, data); break;
case DATAFORMAT_SNORM16_VEC4: writeSNormData<int16>(L, startidx, 4, data); break;
case DATAFORMAT_UNORM16_VEC2: writeUNormData<uint16>(L, startidx, 2, data); break;
case DATAFORMAT_UNORM16_VEC4: writeUNormData<uint16>(L, startidx, 4, data); break;
case DATAFORMAT_INT16_VEC2: writeData<int16>(L, startidx, 2, data); break;
case DATAFORMAT_INT16_VEC4: writeData<int16>(L, startidx, 4, data); break;
case DATAFORMAT_UINT16: writeData<uint16>(L, startidx, 1, data); break;
case DATAFORMAT_UINT16_VEC2: writeData<uint16>(L, startidx, 2, data); break;
case DATAFORMAT_UINT16_VEC4: writeData<uint16>(L, startidx, 4, data); break;
default: break;
}
}
template <typename T>
static inline size_t readData(lua_State *L, int components, const char *data)
{
const auto componentdata = (const T *) data;
for (int i = 0; i < components; i++)
lua_pushnumber(L, (lua_Number) componentdata[i]);
return sizeof(T) * components;
}
template <typename T>
static inline size_t readSNormData(lua_State *L, int components, const char *data)
{
const auto componentdata = (const T *) data;
const auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval));
return sizeof(T) * components;
}
template <typename T>
static inline size_t readUNormData(lua_State *L, int components, const char *data)
{
const auto componentdata = (const T *) data;
const auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval);
return sizeof(T) * components;
}
void luax_readbufferdata(lua_State *L, DataFormat format, const char *data)
{
switch (format)
{
case DATAFORMAT_FLOAT: readData<float>(L, 1, data); break;
case DATAFORMAT_FLOAT_VEC2: readData<float>(L, 2, data); break;
case DATAFORMAT_FLOAT_VEC3: readData<float>(L, 3, data); break;
case DATAFORMAT_FLOAT_VEC4: readData<float>(L, 4, data); break;
case DATAFORMAT_INT32: readData<int32>(L, 1, data); break;
case DATAFORMAT_INT32_VEC2: readData<int32>(L, 2, data); break;
case DATAFORMAT_INT32_VEC3: readData<int32>(L, 3, data); break;
case DATAFORMAT_INT32_VEC4: readData<int32>(L, 4, data); break;
case DATAFORMAT_UINT32: readData<uint32>(L, 1, data); break;
case DATAFORMAT_UINT32_VEC2: readData<uint32>(L, 2, data); break;
case DATAFORMAT_UINT32_VEC3: readData<uint32>(L, 3, data); break;
case DATAFORMAT_UINT32_VEC4: readData<uint32>(L, 4, data); break;
case DATAFORMAT_SNORM8_VEC4: readSNormData<int8>(L, 4, data); break;
case DATAFORMAT_UNORM8_VEC4: readUNormData<uint8>(L, 4, data); break;
case DATAFORMAT_INT8_VEC4: readData<int8>(L, 4, data); break;
case DATAFORMAT_UINT8_VEC4: readData<uint8>(L, 4, data); break;
case DATAFORMAT_SNORM16_VEC2: readSNormData<int16>(L, 2, data); break;
case DATAFORMAT_SNORM16_VEC4: readSNormData<int16>(L, 4, data); break;
case DATAFORMAT_UNORM16_VEC2: readUNormData<uint16>(L, 2, data); break;
case DATAFORMAT_UNORM16_VEC4: readUNormData<uint16>(L, 4, data); break;
case DATAFORMAT_INT16_VEC2: readData<int16>(L, 2, data); break;
case DATAFORMAT_INT16_VEC4: readData<int16>(L, 4, data); break;
case DATAFORMAT_UINT16: readData<uint16>(L, 1, data); break;
case DATAFORMAT_UINT16_VEC2: readData<uint16>(L, 2, data); break;
case DATAFORMAT_UINT16_VEC4: readData<uint16>(L, 4, data); break;
default: break;
}
}
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);
int startindex = (int) luaL_optnumber(L, 3, 1) - 1;
int count = -1;
if (!lua_isnoneornil(L, 4))
{
count = (int) luaL_checknumber(L, 4);
if (count <= 0)
return luaL_error(L, "Element count must be greater than 0.");
}
size_t stride = t->getArrayStride();
size_t offset = startindex * stride;
int arraylength = (int) t->getArrayLength();
if (startindex >= arraylength || startindex < 0)
return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", arraylength);
if (luax_istype(L, 2, Data::type))
{
Data *d = luax_checktype<Data>(L, 2);
count = count >= 0 ? count : (arraylength - startindex);
if (startindex + count > arraylength)
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();
return 0;
}
const std::vector<Buffer::DataMember> &members = t->getDataMembers();
int ncomponents = 0;
for (const Buffer::DataMember &member : members)
ncomponents += member.info.components;
luaL_checktype(L, 2, LUA_TTABLE);
int tablelen = (int) luax_objlen(L, 2);
lua_rawgeti(L, 2, 1);
bool tableoftables = lua_istable(L, -1);
lua_pop(L, 1);
if (!tableoftables)
{
if (tablelen % ncomponents != 0)
return luaL_error(L, "Array length in flat array variant of Buffer:setArrayData must be a multiple of the total number of components (%d)", ncomponents);
tablelen /= ncomponents;
}
count = count >= 0 ? std::min(count, tablelen) : tablelen;
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;
if (tableoftables)
{
for (int i = 0; i < count; i++)
{
// get arraydata[index]
lua_rawgeti(L, 2, i + 1);
luaL_checktype(L, -1, LUA_TTABLE);
// get arraydata[index][j]
for (int j = 1; j <= ncomponents; j++)
lua_rawgeti(L, -j, j);
int idx = -ncomponents;
for (const Buffer::DataMember &member : members)
{
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
idx += member.info.components;
}
lua_pop(L, ncomponents + 1);
data += stride;
}
}
else // Flat array
{
for (int i = 0; i < count; i++)
{
// get arraydata[arrayindex * ncomponents + componentindex]
for (int componentindex = 1; componentindex <= ncomponents; componentindex++)
lua_rawgeti(L, 2, i * ncomponents + componentindex);
int idx = -ncomponents;
for (const Buffer::DataMember &member : members)
{
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
idx += member.info.components;
}
lua_pop(L, ncomponents);
data += stride;
}
}
t->setMappedRangeModified(offset, count * stride);
t->unmap();
return 0;
}
static int w_Buffer_setElement(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
size_t index = (size_t) (luaL_checkinteger(L, 2) - 1);
if (index >= t->getArrayLength())
return luaL_error(L, "Invalid Buffer element index: %d", (int) index + 1);
size_t stride = t->getArrayStride();
size_t offset = index * stride;
char *data = (char *) t->map() + offset;
const auto &members = t->getDataMembers();
bool istable = lua_istable(L, 3);
int idx = istable ? 1 : 3;
if (istable)
{
for (const Buffer::DataMember &member : members)
{
int components = member.info.components;
for (int i = idx; i < idx + components; i++)
lua_rawgeti(L, 3, i);
luax_writebufferdata(L, -components, member.decl.format, data + member.offset);
idx += components;
lua_pop(L, components);
}
}
else
{
for (const Buffer::DataMember &member : members)
{
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
idx += member.info.components;
}
}
t->setMappedRangeModified(offset, stride);
return 0;
}
static int w_Buffer_getElement(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
if ((t->getMapFlags() & Buffer::MAP_READ) == 0)
return luaL_error(L, "Buffer:getElement requires the buffer to be created with the 'cpureadable' setting set to true.");
size_t index = (size_t) (luaL_checkinteger(L, 2) - 1);
if (index >= t->getArrayLength())
return luaL_error(L, "Invalid Buffer element index: %d", (int) index + 1);
size_t offset = index * t->getArrayStride();
const char *data = (const char *) t->map() + offset;
const auto &members = t->getDataMembers();
int n = 0;
for (const Buffer::DataMember &member : members)
{
luax_readbufferdata(L, member.decl.format, data + member.offset);
n += member.info.components;
}
return n;
}
static int w_Buffer_getElementCount(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
lua_pushinteger(L, t->getArrayLength());
return 1;
}
static int w_Buffer_getElementStride(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
lua_pushinteger(L, t->getArrayStride());
return 1;
}
static int w_Buffer_getSize(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
lua_pushinteger(L, t->getSize());
return 1;
}
static int w_Buffer_getFormat(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
const auto &members = t->getDataMembers();
lua_createtable(L, (int) members.size(), 0);
for (size_t i = 0; i < members.size(); i++)
{
const Buffer::DataMember &member = members[i];
lua_createtable(L, 0, 4);
lua_pushstring(L, member.decl.name.c_str());
lua_setfield(L, -2, "name");
const char *formatstr = "unknown";
getConstant(member.decl.format, formatstr);
lua_pushstring(L, formatstr);
lua_setfield(L, -2, "format");
lua_pushinteger(L, member.decl.arrayLength);
lua_setfield(L, -2, "arraylength");
lua_pushinteger(L, member.offset);
lua_setfield(L, -2, "offset");
lua_rawseti(L, -2, i + 1);
}
return 1;
}
static int w_Buffer_isBufferType(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
BufferType buffertype = BUFFERTYPE_MAX_ENUM;
const char *typestr = luaL_checkstring(L, 2);
if (!getConstant(typestr, buffertype))
return luax_enumerror(L, "buffer type", getConstants(buffertype), typestr);
luax_pushboolean(L, (t->getTypeFlags() & (1 << buffertype)) != 0);
return 1;
}
static int w_Buffer_isCPUReadable(lua_State *L)
{
Buffer *t = luax_checkbuffer(L, 1);
luax_pushboolean(L, (t->getMapFlags() & Buffer::MAP_READ) != 0);
return 1;
}
static const luaL_Reg w_Buffer_functions[] =
{
{ "flush", w_Buffer_flush },
{ "setArrayData", w_Buffer_setArrayData },
{ "setElement", w_Buffer_setElement },
{ "getElement", w_Buffer_getElement },
{ "getElementCount", w_Buffer_getElementCount },
{ "getElementStride", w_Buffer_getElementStride },
{ "getSize", w_Buffer_getSize },
{ "getFormat", w_Buffer_getFormat },
{ "isBufferType", w_Buffer_isBufferType },
{ "isCPUReadable", w_Buffer_isCPUReadable },
{ 0, 0 }
};
extern "C" int luaopen_graphicsbuffer(lua_State *L)
{
return luax_register_type(L, &Buffer::type, w_Buffer_functions, nullptr);
}
} // graphics
} // love
+39
View File
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2006-2020 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#pragma once
// LOVE
#include "common/runtime.h"
#include "Buffer.h"
namespace love
{
namespace graphics
{
void luax_writebufferdata(lua_State *L, int startidx, DataFormat format, char *data);
void luax_readbufferdata(lua_State *L, DataFormat format, const char *data);
Buffer *luax_checkbuffer(lua_State *L, int idx);
extern "C" int luaopen_graphicsbuffer(lua_State *L);
} // graphics
} // love
+377 -148
View File
@@ -37,6 +37,7 @@
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <algorithm>
@@ -45,11 +46,6 @@ static const char graphics_lua[] =
#include "wrap_Graphics.lua"
;
// This is in a separate file because VS2013 has a 16KB limit for raw strings..
static const char graphics_shader_lua[] =
#include "wrap_GraphicsShader.lua"
;
namespace love
{
namespace graphics
@@ -748,15 +744,15 @@ static int w__pushNewTexture(lua_State *L, Texture::Slices *slices, const Textur
static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool checkType, bool checkDimensions, OptionalBool forceRenderTarget, Texture::Settings &s, bool &setdpiscale)
{
setdpiscale = false;
if (forceRenderTarget.hasValue)
s.renderTarget = forceRenderTarget.value;
if (opt && lua_isnoneornil(L, idx))
return;
luax_checktablefields<Texture::SettingType>(L, idx, "texture setting name", Texture::getConstant);
if (forceRenderTarget.hasValue)
s.renderTarget = forceRenderTarget.value;
else
if (!forceRenderTarget.hasValue)
s.renderTarget = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_RENDER_TARGET), s.renderTarget);
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_FORMAT));
@@ -1199,25 +1195,25 @@ int w_newVolumeTexture(lua_State *L)
int w_newImage(lua_State *L)
{
luax_markdeprecated(L, "love.graphics.newImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newTexture");
//luax_markdeprecated(L, "love.graphics.newImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newTexture");
return w_newTexture(L);
}
int w_newCubeImage(lua_State *L)
{
luax_markdeprecated(L, "love.graphics.newCubeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newCubeTexture");
//luax_markdeprecated(L, "love.graphics.newCubeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newCubeTexture");
return w_newCubeTexture(L);
}
int w_newArrayImage(lua_State *L)
{
luax_markdeprecated(L, "love.graphics.newArrayImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newArrayTexture");
//luax_markdeprecated(L, "love.graphics.newArrayImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newArrayTexture");
return w_newArrayTexture(L);
}
int w_newVolumeImage(lua_State *L)
{
luax_markdeprecated(L, "love.graphics.newVolumeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newVolumeTexture");
//luax_markdeprecated(L, "love.graphics.newVolumeImage", API_FUNCTION, DEPRECATED_RENAMED, "love.graphics.newVolumeTexture");
return w_newVolumeTexture(L);
}
@@ -1329,12 +1325,12 @@ int w_newSpriteBatch(lua_State *L)
Texture *texture = luax_checktexture(L, 1);
int size = (int) luaL_optinteger(L, 2, 1000);
vertex::Usage usage = vertex::USAGE_DYNAMIC;
BufferUsage usage = BUFFERUSAGE_DYNAMIC;
if (lua_gettop(L) > 2)
{
const char *usagestr = luaL_checkstring(L, 3);
if (!vertex::getConstant(usagestr, usage))
return luax_enumerror(L, "usage hint", vertex::getConstants(usage), usagestr);
if (!getConstant(usagestr, usage))
return luax_enumerror(L, "usage hint", getConstants(usage), usagestr);
}
SpriteBatch *t = nullptr;
@@ -1366,7 +1362,7 @@ int w_newParticleSystem(lua_State *L)
return 1;
}
static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string &vertexsource, std::string &pixelsource)
static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string> &stages)
{
using namespace love::filesystem;
@@ -1426,61 +1422,23 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string
if (!(has_arg1 || has_arg2))
luaL_checkstring(L, startidx);
luax_getfunction(L, "graphics", "_shaderCodeToGLSL");
// push vertexcode and pixelcode strings to the top of the stack
lua_pushboolean(L, gles);
if (has_arg1)
lua_pushvalue(L, startidx + 0);
else
lua_pushnil(L);
stages.push_back(luax_checkstring(L, startidx + 0));
if (has_arg2)
lua_pushvalue(L, startidx + 1);
else
lua_pushnil(L);
// call effectCodeToGLSL, returned values will be at the top of the stack
if (lua_pcall(L, 3, 2, 0) != 0)
return luaL_error(L, "%s", lua_tostring(L, -1));
// vertex shader code
if (lua_isstring(L, -2))
vertexsource = luax_checkstring(L, -2);
else if (has_arg1 && has_arg2)
return luaL_error(L, "Could not parse vertex shader code (missing 'position' function?)");
// pixel shader code
if (lua_isstring(L, -1))
pixelsource = luax_checkstring(L, -1);
else if (has_arg1 && has_arg2)
return luaL_error(L, "Could not parse pixel shader code (missing 'effect' function?)");
if (vertexsource.empty() && pixelsource.empty())
{
// Original args had source code, but effectCodeToGLSL couldn't translate it
for (int i = startidx; i < startidx + 2; i++)
{
if (lua_isstring(L, i))
return luaL_argerror(L, i, "missing 'position' or 'effect' function?");
}
}
stages.push_back(luax_checkstring(L, startidx + 1));
return 0;
}
int w_newShader(lua_State *L)
{
bool gles = instance()->usesGLSLES();
std::string vertexsource, pixelsource;
w_getShaderSource(L, 1, gles, vertexsource, pixelsource);
std::vector<std::string> stages;
w_getShaderSource(L, 1, stages);
bool should_error = false;
try
{
Shader *shader = instance()->newShader(vertexsource, pixelsource);
Shader *shader = instance()->newShader(stages);
luax_pushtype(L, shader);
shader->release();
}
@@ -1504,14 +1462,14 @@ int w_validateShader(lua_State *L)
{
bool gles = luax_checkboolean(L, 1);
std::string vertexsource, pixelsource;
w_getShaderSource(L, 2, gles, vertexsource, pixelsource);
std::vector<std::string> stages;
w_getShaderSource(L, 2, stages);
bool success = true;
std::string err;
try
{
success = instance()->validateShader(gles, vertexsource, pixelsource, err);
success = instance()->validateShader(gles, stages, err);
}
catch (love::Exception &e)
{
@@ -1530,22 +1488,311 @@ int w_validateShader(lua_State *L)
return 1;
}
static vertex::Usage luax_optmeshusage(lua_State *L, int idx, vertex::Usage def)
static BufferUsage luax_optbufferusage(lua_State *L, int idx, BufferUsage def)
{
const char *usagestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
if (usagestr && !vertex::getConstant(usagestr, def))
luax_enumerror(L, "usage hint", vertex::getConstants(def), usagestr);
if (usagestr && !getConstant(usagestr, def))
luax_enumerror(L, "usage hint", getConstants(def), usagestr);
return def;
}
static void luax_optbuffersettings(lua_State *L, int idx, Buffer::Settings &settings)
{
if (lua_isnoneornil(L, idx))
return;
luaL_checktype(L, idx, LUA_TTABLE);
lua_getfield(L, idx, "usage");
settings.usage = luax_optbufferusage(L, -1, settings.usage);
lua_pop(L, 1);
if (luax_boolflag(L, idx, "cpureadable", settings.mapFlags & Buffer::MAP_READ))
settings.mapFlags = (Buffer::MapFlags)(settings.mapFlags | Buffer::MAP_READ);
else
settings.mapFlags = (Buffer::MapFlags)(settings.mapFlags & (~Buffer::MAP_READ));
}
static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::DataDeclaration> &format)
{
if (lua_type(L, idx) == LUA_TSTRING)
{
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
const char *formatstr = luaL_checkstring(L, idx);
if (!getConstant(formatstr, decl.format))
luax_enumerror(L, "data format", getConstants(decl.format), formatstr);
format.push_back(decl);
return;
}
luaL_checktype(L, idx, LUA_TTABLE);
int tablelen = luax_objlen(L, idx);
for (int i = 1; i <= tablelen; i++)
{
lua_rawgeti(L, idx, i);
luaL_checktype(L, -1, LUA_TTABLE);
Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM);
lua_getfield(L, -1, "name");
if (!lua_isnoneornil(L, -1))
decl.name = luax_checkstring(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "format");
if (lua_type(L, -1) != LUA_TSTRING)
{
std::ostringstream ss;
ss << "'format' field expected in array element #";
ss << i;
ss << " of format table";
std::string str = ss.str();
luaL_argerror(L, idx, str.c_str());
}
const char *formatstr = luaL_checkstring(L, -1);
if (!getConstant(formatstr, decl.format))
luax_enumerror(L, "data format", getConstants(decl.format), formatstr);
lua_pop(L, 1);
decl.arrayLength = luax_intflag(L, -1, "arraylength", 0);
format.push_back(decl);
lua_pop(L, 1);
}
}
static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format)
{
size_t arraylength = 0;
size_t bytesize = 0;
Data *data = nullptr;
const void *initialdata = nullptr;
int ncomponents = 0;
for (const Buffer::DataDeclaration &decl : format)
ncomponents += getDataFormatInfo(decl.format).components;
if (luax_istype(L, idx, Data::type))
{
data = luax_checktype<Data>(L, idx);
initialdata = data->getData();
bytesize = data->getSize();
}
bool tableoftables = false;
if (lua_istable(L, idx))
{
arraylength = luax_objlen(L, idx);
lua_rawgeti(L, idx, 1);
tableoftables = lua_istable(L, -1);
lua_pop(L, 1);
if (!tableoftables)
{
if (arraylength % ncomponents != 0)
luaL_error(L, "Array length in flat array variant of newBuffer must be a multiple of the total number of components (%d)", ncomponents);
arraylength /= ncomponents;
}
}
else if (data == nullptr)
{
lua_Integer len = luaL_checkinteger(L, idx);
if (len <= 0)
luaL_argerror(L, idx, "number of elements must be greater than 0");
arraylength = (size_t) len;
}
Buffer *b = nullptr;
luax_catchexcept(L, [&] { b = instance()->newBuffer(settings, format, initialdata, bytesize, arraylength); });
if (lua_istable(L, idx))
{
Buffer::Mapper mapper(*b);
char *data = (char *) mapper.data;
const auto &members = b->getDataMembers();
size_t stride = b->getArrayStride();
if (tableoftables)
{
for (size_t i = 0; i < arraylength; i++)
{
// get arraydata[index]
lua_rawgeti(L, 2, i + 1);
luaL_checktype(L, -1, LUA_TTABLE);
// get arraydata[index][j]
for (int j = 1; j <= ncomponents; j++)
lua_rawgeti(L, -j, j);
int idx = -ncomponents;
for (const Buffer::DataMember &member : members)
{
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
idx += member.info.components;
}
lua_pop(L, ncomponents + 1);
data += stride;
}
}
else // Flat array
{
for (size_t i = 0; i < arraylength; i++)
{
// get arraydata[arrayindex * ncomponents + componentindex]
for (int componentindex = 1; componentindex <= ncomponents; componentindex++)
lua_rawgeti(L, 2, i * ncomponents + componentindex);
int idx = -ncomponents;
for (const Buffer::DataMember &member : members)
{
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
idx += member.info.components;
}
lua_pop(L, ncomponents);
data += stride;
}
}
}
return b;
}
int w_newBuffer(lua_State *L)
{
Buffer::Settings settings(0, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < BUFFERTYPE_MAX_ENUM; i++)
{
BufferType buffertype = (BufferType) i;
const char *tname = nullptr;
if (!getConstant(buffertype, tname))
continue;
if (luax_boolflag(L, 3, tname, false))
settings.typeFlags = (Buffer::TypeFlags)(settings.typeFlags | (1u << i));
}
luax_optbuffersettings(L, 3, settings);
std::vector<Buffer::DataDeclaration> format;
luax_checkbufferformat(L, 1, format);
Buffer *b = luax_newbuffer(L, 2, settings, format);
luax_pushtype(L, b);
b->release();
return 1;
}
int w_newVertexBuffer(lua_State *L)
{
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
luax_optbuffersettings(L, 3, settings);
std::vector<Buffer::DataDeclaration> format;
luax_checkbufferformat(L, 1, format);
Buffer *b = luax_newbuffer(L, 2, settings, format);
luax_pushtype(L, b);
b->release();
return 1;
}
int w_newIndexBuffer(lua_State *L)
{
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
luax_optbuffersettings(L, 3, settings);
size_t arraylength = 0;
size_t bytesize = 0;
DataFormat format = DATAFORMAT_UINT16;
Data *data = nullptr;
const void *initialdata = nullptr;
if (luax_istype(L, 1, Data::type))
{
data = luax_checktype<Data>(L, 1);
initialdata = data->getData();
bytesize = data->getSize();
}
if (lua_istable(L, 1))
{
arraylength = luax_objlen(L, 1);
// Scan array for invalid types and the max value.
lua_Integer maxvalue = 0;
for (size_t i = 0; i < arraylength; i++)
{
lua_rawgeti(L, 1, i + 1);
lua_Integer v = luaL_checkinteger(L, -1);
lua_pop(L, 1);
if (v < 0)
return luaL_argerror(L, 1, "expected positive integer values in array");
else
maxvalue = std::max(maxvalue, v);
}
format = getIndexDataFormat(getIndexDataTypeFromMax(maxvalue));
}
else if (data == nullptr)
{
lua_Integer len = luaL_checkinteger(L, 1);
if (len <= 0)
return luaL_argerror(L, 1, "number of elements must be greater than 0");
arraylength = (size_t) len;
}
if (data != nullptr || !lua_isnoneornil(L, 2))
{
const char *formatstr = luaL_checkstring(L, 2);
if (!getConstant(formatstr, format))
return luax_enumerror(L, "index data format", getConstants(format), formatstr);
}
Buffer *b = nullptr;
luax_catchexcept(L, [&] { b = instance()->newBuffer(settings, format, initialdata, bytesize, arraylength); });
if (lua_istable(L, 1))
{
Buffer::Mapper mapper(*b);
uint16 *u16data = (uint16 *) mapper.data;
uint32 *u32data = (uint32 *) mapper.data;
for (size_t i = 0; i < arraylength; i++)
{
lua_rawgeti(L, 1, i + 1);
lua_Integer v = luaL_checkinteger(L, -1);
lua_pop(L, 1);
if (format == DATAFORMAT_UINT16)
u16data[i] = (uint16) v;
else
u32data[i] = (uint32) v;
}
}
luax_pushtype(L, b);
b->release();
return 1;
}
static PrimitiveType luax_optmeshdrawmode(lua_State *L, int idx, PrimitiveType def)
{
const char *modestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
if (modestr && !vertex::getConstant(modestr, def))
luax_enumerror(L, "mesh draw mode", vertex::getConstants(def), modestr);
if (modestr && !getConstant(modestr, def))
luax_enumerror(L, "mesh draw mode", getConstants(def), modestr);
return def;
}
@@ -1555,7 +1802,9 @@ static Mesh *newStandardMesh(lua_State *L)
Mesh *t = nullptr;
PrimitiveType drawmode = luax_optmeshdrawmode(L, 2, PRIMITIVE_TRIANGLE_FAN);
vertex::Usage usage = luax_optmeshusage(L, 3, vertex::USAGE_DYNAMIC);
BufferUsage usage = luax_optbufferusage(L, 3, BUFFERUSAGE_DYNAMIC);
std::vector<Buffer::DataDeclaration> format = Mesh::getDefaultVertexFormat();
// First argument is a table of standard vertices, or the number of
// standard vertices.
@@ -1595,12 +1844,12 @@ static Mesh *newStandardMesh(lua_State *L)
vertices.push_back(v);
}
luax_catchexcept(L, [&](){ t = instance()->newMesh(vertices, drawmode, usage); });
luax_catchexcept(L, [&](){ t = instance()->newMesh(format, vertices.data(), vertices.size() * sizeof(Vertex), drawmode, usage); });
}
else
{
int count = (int) luaL_checkinteger(L, 1);
luax_catchexcept(L, [&](){ t = instance()->newMesh(count, drawmode, usage); });
luax_catchexcept(L, [&](){ t = instance()->newMesh(format, count, drawmode, usage); });
}
return t;
@@ -1612,10 +1861,10 @@ static Mesh *newCustomMesh(lua_State *L)
// First argument is the vertex format, second is a table of vertices or
// the number of vertices.
std::vector<Mesh::AttribFormat> vertexformat;
std::vector<Buffer::DataDeclaration> vertexformat;
PrimitiveType drawmode = luax_optmeshdrawmode(L, 3, PRIMITIVE_TRIANGLE_FAN);
vertex::Usage usage = luax_optmeshusage(L, 4, vertex::USAGE_DYNAMIC);
BufferUsage usage = luax_optbufferusage(L, 4, BUFFERUSAGE_DYNAMIC);
lua_rawgeti(L, 1, 1);
if (!lua_istable(L, -1))
@@ -1634,27 +1883,53 @@ static Mesh *newCustomMesh(lua_State *L)
for (int j = 1; j <= 3; j++)
lua_rawgeti(L, -j, j);
Mesh::AttribFormat format;
format.name = luaL_checkstring(L, -3);
const char *name = luaL_checkstring(L, -3);
DataFormat format = DATAFORMAT_MAX_ENUM;
const char *tname = luaL_checkstring(L, -2);
if (strcmp(tname, "byte") == 0) // Legacy name.
format.type = vertex::DATA_UNORM8;
else if (!vertex::getConstant(tname, format.type))
if (!lua_isnoneornil(L, -1))
{
luax_enumerror(L, "Mesh vertex data type name", vertex::getConstants(format.type), tname);
return nullptr;
int components = (int) luaL_checkinteger(L, -1);
// Check deprecated format names.
if (strcmp(tname, "byte") == 0 || strcmp(tname, "unorm8") == 0)
{
if (components == 4)
format = DATAFORMAT_UNORM8_VEC4;
else
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
}
else if (strcmp(tname, "unorm16") == 0)
{
if (components == 2)
format = DATAFORMAT_UNORM16_VEC2;
else if (components == 4)
format = DATAFORMAT_UNORM16_VEC4;
else
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
}
else if (strcmp(tname, "float") == 0)
{
if (components == 1)
format = DATAFORMAT_FLOAT;
else if (components == 2)
format = DATAFORMAT_FLOAT_VEC2;
else if (components == 3)
format = DATAFORMAT_FLOAT_VEC3;
else if (components == 4)
format = DATAFORMAT_FLOAT_VEC4;
else
luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname);
}
}
format.components = (int) luaL_checkinteger(L, -1);
if (format.components <= 0 || format.components > 4)
{
luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", format.components);
return nullptr;
}
if (format == DATAFORMAT_MAX_ENUM && !getConstant(tname, format))
luax_enumerror(L, "vertex data format", getConstants(format), tname);
lua_pop(L, 4);
vertexformat.push_back(format);
vertexformat.emplace_back(name, format);
}
if (lua_isnumber(L, 2))
@@ -1679,10 +1954,6 @@ static Mesh *newCustomMesh(lua_State *L)
}
lua_pop(L, 1);
int vertexcomponents = 0;
for (const Mesh::AttribFormat &format : vertexformat)
vertexcomponents += format.components;
size_t numvertices = luax_objlen(L, 2);
luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); });
@@ -1699,19 +1970,19 @@ static Mesh *newCustomMesh(lua_State *L)
int n = 0;
for (size_t i = 0; i < vertexformat.size(); i++)
{
int components = vertexformat[i].components;
const auto &info = getDataFormatInfo(vertexformat[i].format);
// get vertices[vertindex][n]
for (int c = 0; c < components; c++)
for (int c = 0; c < info.components; c++)
{
n++;
lua_rawgeti(L, -(c + 1), n);
}
// Fetch the values from Lua and store them in data buffer.
luax_writeAttributeData(L, -components, vertexformat[i].type, components, data);
luax_writebufferdata(L, -info.components, vertexformat[i].format, data);
lua_pop(L, components);
lua_pop(L, info.components);
luax_catchexcept(L,
[&](){ t->setVertexAttribute(vertindex, i, data, sizeof(float) * 4); },
@@ -2214,8 +2485,8 @@ int w_setMeshCullMode(lua_State *L)
const char *str = luaL_checkstring(L, 1);
CullMode mode;
if (!vertex::getConstant(str, mode))
return luax_enumerror(L, "cull mode", vertex::getConstants(mode), str);
if (!getConstant(str, mode))
return luax_enumerror(L, "cull mode", getConstants(mode), str);
luax_catchexcept(L, [&]() { instance()->setMeshCullMode(mode); });
return 0;
@@ -2225,7 +2496,7 @@ int w_getMeshCullMode(lua_State *L)
{
CullMode mode = instance()->getMeshCullMode();
const char *str;
if (!vertex::getConstant(mode, str))
if (!getConstant(mode, str))
return luaL_error(L, "Unknown cull mode");
lua_pushstring(L, str);
return 1;
@@ -2234,10 +2505,10 @@ int w_getMeshCullMode(lua_State *L)
int w_setFrontFaceWinding(lua_State *L)
{
const char *str = luaL_checkstring(L, 1);
vertex::Winding winding;
Winding winding;
if (!vertex::getConstant(str, winding))
return luax_enumerror(L, "vertex winding", vertex::getConstants(winding), str);
if (!getConstant(str, winding))
return luax_enumerror(L, "vertex winding", getConstants(winding), str);
luax_catchexcept(L, [&]() { instance()->setFrontFaceWinding(winding); });
return 0;
@@ -2245,9 +2516,9 @@ int w_setFrontFaceWinding(lua_State *L)
int w_getFrontFaceWinding(lua_State *L)
{
vertex::Winding winding = instance()->getFrontFaceWinding();
Winding winding = instance()->getFrontFaceWinding();
const char *str;
if (!vertex::getConstant(winding, str))
if (!getConstant(winding, str))
return luaL_error(L, "Unknown vertex winding");
lua_pushstring(L, str);
return 1;
@@ -2289,46 +2560,6 @@ int w_getShader(lua_State *L)
return 1;
}
int w_setDefaultShaderCode(lua_State *L)
{
for (int i = 0; i < 2; i++)
{
luaL_checktype(L, i + 1, LUA_TTABLE);
for (int lang = 0; lang < Shader::LANGUAGE_MAX_ENUM; lang++)
{
const char *langname;
if (!Shader::getConstant((Shader::Language) lang, langname))
continue;
lua_getfield(L, i + 1, langname);
lua_getfield(L, -1, "vertex");
lua_getfield(L, -2, "pixel");
lua_getfield(L, -3, "videopixel");
lua_getfield(L, -4, "arraypixel");
std::string vertex = luax_checkstring(L, -4);
std::string pixel = luax_checkstring(L, -3);
std::string videopixel = luax_checkstring(L, -2);
std::string arraypixel = luax_checkstring(L, -1);
lua_pop(L, 5);
Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_PIXEL] = pixel;
Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_PIXEL] = videopixel;
Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_PIXEL] = arraypixel;
}
}
return 0;
}
int w_getSupported(lua_State *L)
{
const Graphics::Capabilities &caps = instance()->getCapabilities();
@@ -3156,6 +3387,9 @@ static const luaL_Reg functions[] =
{ "newSpriteBatch", w_newSpriteBatch },
{ "newParticleSystem", w_newParticleSystem },
{ "newShader", w_newShader },
{ "newBuffer", w_newBuffer },
{ "newVertexBuffer", w_newVertexBuffer },
{ "newIndexBuffer", w_newIndexBuffer },
{ "newMesh", w_newMesh },
{ "newText", w_newText },
{ "_newVideo", w_newVideo },
@@ -3203,7 +3437,6 @@ static const luaL_Reg functions[] =
{ "setShader", w_setShader },
{ "getShader", w_getShader },
{ "_setDefaultShaderCode", w_setDefaultShaderCode },
{ "getSupported", w_getSupported },
{ "getTextureFormats", w_getTextureFormats },
@@ -3286,6 +3519,7 @@ static const lua_CFunction types[] =
luaopen_texture,
luaopen_font,
luaopen_quad,
luaopen_graphicsbuffer,
luaopen_spritebatch,
luaopen_particlesystem,
luaopen_shader,
@@ -3328,11 +3562,6 @@ extern "C" int luaopen_love_graphics(lua_State *L)
else
lua_error(L);
if (luaL_loadbuffer(L, (const char *)graphics_shader_lua, sizeof(graphics_shader_lua), "wrap_GraphicsShader.lua") == 0)
lua_call(L, 0, 0);
else
lua_error(L);
return n;
}
+1
View File
@@ -31,6 +31,7 @@
#include "wrap_Mesh.h"
#include "wrap_Text.h"
#include "wrap_Video.h"
#include "wrap_Buffer.h"
#include "Graphics.h"
namespace love
@@ -1,520 +0,0 @@
R"luastring"--(
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
-- There is a matching delimiter at the bottom of the file.
--[[
Copyright (c) 2006-2020 LOVE Development Team
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--]]
local table_concat, table_insert = table.concat, table.insert
local ipairs = ipairs
local GLSL = {}
GLSL.VERSION = { -- index using [target][gles]
glsl1 = {[false]="#version 120", [true]="#version 100"},
glsl3 = {[false]="#version 330 core", [true]="#version 300 es"},
glsl4 = {[false]="#version 430 core", [true]="#version 310 es"},
}
GLSL.SYNTAX = [[
#if !defined(GL_ES) && __VERSION__ < 140
#define lowp
#define mediump
#define highp
#endif
#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
#define LOVE_HIGHP_OR_MEDIUMP highp
#else
#define LOVE_HIGHP_OR_MEDIUMP mediump
#endif
#if __VERSION__ >= 300
#define LOVE_IO_LOCATION(x) layout (location = x)
#else
#define LOVE_IO_LOCATION(x)
#endif
#define number float
#define Image sampler2D
#define ArrayImage sampler2DArray
#define CubeImage samplerCube
#define VolumeImage sampler3D
#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
#define DepthImage sampler2DShadow
#define DepthArrayImage sampler2DArrayShadow
#define DepthCubeImage samplerCubeShadow
#endif
#define extern uniform
#ifdef GL_EXT_texture_array
#extension GL_EXT_texture_array : enable
#endif
#ifdef GL_OES_texture_3D
#extension GL_OES_texture_3D : enable
#endif
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
]]
-- Uniforms shared by the vertex and pixel shader stages.
GLSL.UNIFORMS = [[
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
// but we can't guarantee that highp is always supported in fragment shaders...
// We *really* don't want to use mediump for these in vertex shaders though.
#ifdef LOVE_USE_UNIFORM_BUFFERS
layout (std140) uniform love_UniformsPerDrawBuffer {
highp vec4 love_UniformsPerDraw[13];
};
#else
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
#endif
// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't
// do it as an initializer.
LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix;
LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix;
LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix;
LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;
LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor;
#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix)
// Alternate names
#define ViewSpaceFromLocal TransformMatrix
#define ClipSpaceFromView ProjectionMatrix
#define ClipSpaceFromLocal TransformProjectionMatrix
#define ViewNormalFromLocal NormalMatrix
void love_initializeBuiltinUniforms() {
TransformMatrix = mat4(
love_UniformsPerDraw[0],
love_UniformsPerDraw[1],
love_UniformsPerDraw[2],
love_UniformsPerDraw[3]
);
ProjectionMatrix = mat4(
love_UniformsPerDraw[4],
love_UniformsPerDraw[5],
love_UniformsPerDraw[6],
love_UniformsPerDraw[7]
);
NormalMatrix = mat3(
love_UniformsPerDraw[8].xyz,
love_UniformsPerDraw[9].xyz,
love_UniformsPerDraw[10].xyz
);
love_ScreenSize = love_UniformsPerDraw[11];
ConstantColor = love_UniformsPerDraw[12];
}
]]
GLSL.FUNCTIONS = [[
#ifdef GL_ES
#if __VERSION__ >= 300 || defined(GL_EXT_texture_array)
precision lowp sampler2DArray;
#endif
#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
precision lowp sampler3D;
#endif
#if __VERSION__ >= 300
precision lowp sampler2DShadow;
precision lowp samplerCubeShadow;
precision lowp sampler2DArrayShadow;
#endif
#endif
#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3)
#define Texel texture
#else
#if __VERSION__ >= 130
#define texture2D Texel
#define texture3D Texel
#define textureCube Texel
#define texture2DArray Texel
#define love_texture2D texture
#define love_texture3D texture
#define love_textureCube texture
#define love_texture2DArray texture
#else
#define love_texture2D texture2D
#define love_texture3D texture3D
#define love_textureCube textureCube
#define love_texture2DArray texture2DArray
#endif
vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); }
vec4 Texel(samplerCube s, vec3 c) { return love_textureCube(s, c); }
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); }
#endif
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); }
#endif
#ifdef PIXEL
vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); }
vec4 Texel(samplerCube s, vec3 c, float b) { return love_textureCube(s, c, b); }
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); }
#endif
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); }
#endif
#endif
#define texture love_texture
#endif
float gammaToLinearPrecise(float c) {
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
}
vec3 gammaToLinearPrecise(vec3 c) {
bvec3 leq = lessThanEqual(c, vec3(0.04045));
c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4);
c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4);
c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4);
return c;
}
vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); }
float linearToGammaPrecise(float c) {
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
}
vec3 linearToGammaPrecise(vec3 c) {
bvec3 lt = lessThanEqual(c, vec3(0.0031308));
c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055;
c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055;
c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055;
return c;
}
vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); }
// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); }
mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); }
mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); }
mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); }
mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); }
#define gammaToLinear gammaToLinearFast
#define linearToGamma linearToGammaFast
#ifdef LOVE_GAMMA_CORRECT
#define gammaCorrectColor gammaToLinear
#define unGammaCorrectColor linearToGamma
#define gammaCorrectColorPrecise gammaToLinearPrecise
#define unGammaCorrectColorPrecise linearToGammaPrecise
#define gammaCorrectColorFast gammaToLinearFast
#define unGammaCorrectColorFast linearToGammaFast
#else
#define gammaCorrectColor
#define unGammaCorrectColor
#define gammaCorrectColorPrecise
#define unGammaCorrectColorPrecise
#define gammaCorrectColorFast
#define unGammaCorrectColorFast
#endif]]
GLSL.VERTEX = {
HEADER = [[
#define love_Position gl_Position
#if __VERSION__ >= 130
#define attribute in
#define varying out
#ifndef LOVE_GLSL1_ON_GLSL3
#define love_VertexID gl_VertexID
#define love_InstanceID gl_InstanceID
#endif
#endif
#ifdef GL_ES
uniform mediump float love_PointSize;
#endif]],
FUNCTIONS = [[
void setPointSize() {
#ifdef GL_ES
gl_PointSize = love_PointSize;
#endif
}]],
MAIN = [[
LOVE_IO_LOCATION(0) attribute vec4 VertexPosition;
LOVE_IO_LOCATION(1) attribute vec4 VertexTexCoord;
LOVE_IO_LOCATION(2) attribute vec4 VertexColor;
varying vec4 VaryingTexCoord;
varying vec4 VaryingColor;
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition);
void main() {
love_initializeBuiltinUniforms();
VaryingTexCoord = VertexTexCoord;
VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
setPointSize();
love_Position = position(ClipSpaceFromLocal, VertexPosition);
}]],
}
GLSL.PIXEL = {
HEADER = [[
#ifdef GL_ES
precision mediump float;
#endif
#define love_MaxRenderTargets gl_MaxDrawBuffers
#if __VERSION__ >= 130
#define varying in
// Some drivers seem to make the pixel shader do more work when multiple
// pixel shader outputs are defined, even when only one is actually used.
// TODO: We should use reflection or something instead of this, to determine
// how many outputs are actually used in the shader code.
#ifdef LOVE_MULTI_RENDER_TARGETS
LOVE_IO_LOCATION(0) out vec4 love_RenderTargets[love_MaxRenderTargets];
#define love_PixelColor love_RenderTargets[0]
#else
LOVE_IO_LOCATION(0) out vec4 love_PixelColor;
#endif
#else
#ifdef LOVE_MULTI_RENDER_TARGETS
#define love_RenderTargets gl_FragData
#endif
#define love_PixelColor gl_FragColor
#endif
// Legacy
#define love_MaxCanvases love_MaxRenderTargets
#define love_Canvases love_RenderTargets
#ifdef LOVE_MULTI_RENDER_TARGETS
#define LOVE_MULTI_CANVASES 1
#endif
// See Shader::updateScreenParams in Shader.cpp.
#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))]],
FUNCTIONS = [[
uniform sampler2D love_VideoYChannel;
uniform sampler2D love_VideoCbChannel;
uniform sampler2D love_VideoCrChannel;
vec4 VideoTexel(vec2 texcoords) {
vec3 yuv;
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
vec4 color;
color.r = dot(yuv, vec3(1.164, 0.000, 1.596));
color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
color.b = dot(yuv, vec3(1.164, 2.018, 0.000));
color.a = 1.0;
return gammaCorrectColor(color);
}]],
MAIN = [[
uniform sampler2D MainTex;
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
varying mediump vec4 VaryingColor;
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord);
void main() {
love_initializeBuiltinUniforms();
love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
}]],
MAIN_CUSTOM = [[
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
varying mediump vec4 VaryingColor;
void effect();
void main() {
love_initializeBuiltinUniforms();
effect();
}]],
}
local function getLanguageTarget(code)
if not code then return nil end
return (code:match("^%s*#pragma language (%w+)")) or "glsl1"
end
local function createShaderStageCode(stage, code, lang, gles, glsl1on3, gammacorrect, custom, multirendertarget, useubo)
stage = stage:upper()
local lines = {
GLSL.VERSION[lang][gles],
"#define " ..stage .. " " .. stage,
glsl1on3 and "#define LOVE_GLSL1_ON_GLSL3 1" or "",
gammacorrect and "#define LOVE_GAMMA_CORRECT 1" or "",
multirendertarget and "#define LOVE_MULTI_RENDER_TARGETS 1" or "",
useubo and "#define LOVE_USE_UNIFORM_BUFFERS 1" or "",
GLSL.SYNTAX,
GLSL[stage].HEADER,
GLSL.UNIFORMS,
GLSL.FUNCTIONS,
GLSL[stage].FUNCTIONS,
custom and GLSL[stage].MAIN_CUSTOM or GLSL[stage].MAIN,
((lang == "glsl1" or glsl1on3) and not gles) and "#line 0" or "#line 1",
code,
}
return table_concat(lines, "\n")
end
local function isVertexCode(code)
return code:match("vec4%s+position%s*%(") ~= nil
end
local function isPixelCode(code)
if code:match("vec4%s+effect%s*%(") then
return true
elseif code:match("void%s+effect%s*%(") then -- custom effect function
local mrt = (code:match("love_RenderTargets") ~= nil) or (code:match("love_Canvases") ~= nil)
return true, true, mrt
else
return false
end
end
function love.graphics._shaderCodeToGLSL(gles, arg1, arg2)
local vertexcode, pixelcode
local is_custompixel = false -- whether pixel code has "effects" function instead of "effect"
local is_multicanvas = false
if arg1 then
if isVertexCode(arg1) then
vertexcode = arg1 -- first arg contains vertex shader code
end
local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg1)
if ispixel then
pixelcode = arg1 -- first arg contains pixel shader code
is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas
end
end
if arg2 then
if isVertexCode(arg2) then
vertexcode = arg2 -- second arg contains vertex shader code
end
local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg2)
if ispixel then
pixelcode = arg2 -- second arg contains pixel shader code
is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas
end
end
local graphicsfeatures = love.graphics.getSupported()
local supportsGLSL3 = graphicsfeatures.glsl3
local supportsGLSL4 = graphicsfeatures.glsl4
local gammacorrect = love.graphics.isGammaCorrect()
local renderer = love.graphics.getRenderer()
local useubo = renderer == "Metal"
local targetlang = getLanguageTarget(pixelcode or vertexcode)
if getLanguageTarget(vertexcode or pixelcode) ~= targetlang then
error("vertex and pixel shader languages must match", 2)
end
if targetlang == "glsl3" and not supportsGLSL3 then
error("GLSL 3 shaders are not supported on this system.", 2)
end
if targetlang == "glsl4" and not supportsGLSL4 then
error("GLSL 4 shaders are not supported on this system.", 2)
end
if targetlang ~= nil and not GLSL.VERSION[targetlang] then
error("Invalid shader language: " .. targetlang, 2)
end
local lang = targetlang or "glsl1"
local glsl1on3 = false
if lang == "glsl1" and supportsGLSL3 then
lang = "glsl3"
glsl1on3 = true
end
if vertexcode then
vertexcode = createShaderStageCode("VERTEX", vertexcode, lang, gles, glsl1on3, gammacorrect, false, false, useubo)
end
if pixelcode then
pixelcode = createShaderStageCode("PIXEL", pixelcode, lang, gles, glsl1on3, gammacorrect, is_custompixel, is_multicanvas, useubo)
end
return vertexcode, pixelcode
end
local defaultcode = {
vertex = [[
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition) {
return clipSpaceFromLocal * localPosition;
}]],
pixel = [[
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
return Texel(tex, texcoord) * vcolor;
}]],
videopixel = [[
void effect() {
love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor;
}]],
arraypixel = [[
uniform ArrayImage MainTex;
void effect() {
love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor;
}]],
}
local defaults = {}
local defaults_gammacorrect = {}
local langs = {
glsl1 = {target="glsl1", gles=false},
essl1 = {target="glsl1", gles=true},
glsl3 = {target="glsl3", gles=false},
essl3 = {target="glsl3", gles=true},
}
-- FIXME: this is temporary until a glslang pull request is merged in.
local useubo = true
for lang, info in pairs(langs) do
for _, gammacorrect in ipairs{false, true} do
local t = gammacorrect and defaults_gammacorrect or defaults
t[lang] = {
vertex = createShaderStageCode("VERTEX", defaultcode.vertex, info.target, info.gles, false, gammacorrect, false, false, useubo),
pixel = createShaderStageCode("PIXEL", defaultcode.pixel, info.target, info.gles, false, gammacorrect, false, false, useubo),
videopixel = createShaderStageCode("PIXEL", defaultcode.videopixel, info.target, info.gles, false, gammacorrect, true, false, useubo),
arraypixel = createShaderStageCode("PIXEL", defaultcode.arraypixel, info.target, info.gles, false, gammacorrect, true, false, useubo),
}
end
end
love.graphics._setDefaultShaderCode(defaults, defaults_gammacorrect)
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
--)luastring"--"
+133 -184
View File
@@ -20,6 +20,7 @@
// LOVE
#include "wrap_Mesh.h"
#include "wrap_Buffer.h"
#include "Texture.h"
#include "wrap_Texture.h"
@@ -36,140 +37,6 @@ Mesh *luax_checkmesh(lua_State *L, int idx)
return luax_checktype<Mesh>(L, idx);
}
static const double defaultComponents[] = {0.0, 0.0, 0.0, 1.0};
template <typename T>
static inline size_t writeData(lua_State *L, int startidx, int components, char *data)
{
auto componentdata = (T *) data;
for (int i = 0; i < components; i++)
componentdata[i] = (T) (luaL_optnumber(L, startidx + i, defaultComponents[i]));
return sizeof(T) * components;
}
template <typename T>
static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data)
{
auto componentdata = (T *) data;
auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval);
return sizeof(T) * components;
}
template <typename T>
static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data)
{
auto componentdata = (T *) data;
auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval);
return sizeof(T) * components;
}
char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data)
{
switch (type)
{
case vertex::DATA_SNORM8:
return data + writeSNormData<int8>(L, startidx, components, data);
case vertex::DATA_UNORM8:
return data + writeUNormData<uint8>(L, startidx, components, data);
case vertex::DATA_INT8:
return data + writeData<int8>(L, startidx, components, data);
case vertex::DATA_UINT8:
return data + writeData<uint8>(L, startidx, components, data);
case vertex::DATA_SNORM16:
return data + writeSNormData<int16>(L, startidx, components, data);
case vertex::DATA_UNORM16:
return data + writeUNormData<uint16>(L, startidx, components, data);
case vertex::DATA_INT16:
return data + writeData<int16>(L, startidx, components, data);
case vertex::DATA_UINT16:
return data + writeData<uint16>(L, startidx, components, data);
case vertex::DATA_INT32:
return data + writeData<int32>(L, startidx, components, data);
case vertex::DATA_UINT32:
return data + writeData<uint32>(L, startidx, components, data);
case vertex::DATA_FLOAT:
return data + writeData<float>(L, startidx, components, data);
default:
return data;
}
}
template <typename T>
static inline size_t readData(lua_State *L, int components, const char *data)
{
auto componentdata = (const T *) data;
for (int i = 0; i < components; i++)
lua_pushnumber(L, (lua_Number) componentdata[i]);
return sizeof(T) * components;
}
template <typename T>
static inline size_t readSNormData(lua_State *L, int components, const char *data)
{
auto componentdata = (const T *) data;
auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval));
return sizeof(T) * components;
}
template <typename T>
static inline size_t readUNormData(lua_State *L, int components, const char *data)
{
auto componentdata = (const T *) data;
auto maxval = std::numeric_limits<T>::max();
for (int i = 0; i < components; i++)
lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval);
return sizeof(T) * components;
}
const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data)
{
switch (type)
{
case vertex::DATA_SNORM8:
return data + readSNormData<int8>(L, components, data);
case vertex::DATA_UNORM8:
return data + readUNormData<uint8>(L, components, data);
case vertex::DATA_INT8:
return data + readData<int8>(L, components, data);
case vertex::DATA_UINT8:
return data + readData<uint8>(L, components, data);
case vertex::DATA_SNORM16:
return data + readSNormData<int16>(L, components, data);
case vertex::DATA_UNORM16:
return data + readUNormData<uint16>(L, components, data);
case vertex::DATA_INT16:
return data + readData<int16>(L, components, data);
case vertex::DATA_UINT16:
return data + readData<uint16>(L, components, data);
case vertex::DATA_INT32:
return data + readData<int32>(L, components, data);
case vertex::DATA_UINT32:
return data + readData<uint32>(L, components, data);
case vertex::DATA_FLOAT:
return data + readData<float>(L, components, data);
default:
return data;
}
}
int w_Mesh_setVertices(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -188,7 +55,7 @@ int w_Mesh_setVertices(lua_State *L)
size_t byteoffset = vertstart * stride;
int totalverts = (int) t->getVertexCount();
if (vertstart >= totalverts)
if (vertstart >= totalverts || vertstart < 0)
return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", totalverts);
if (luax_istype(L, 2, Data::type))
@@ -215,11 +82,11 @@ int w_Mesh_setVertices(lua_State *L)
if (vertstart + vertcount > totalverts)
return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
int ncomponents = 0;
for (const Mesh::AttribFormat &format : vertexformat)
ncomponents += format.components;
for (const Buffer::DataMember &member : vertexformat)
ncomponents += member.info.components;
char *data = (char *) t->mapVertexData() + byteoffset;
@@ -235,14 +102,16 @@ int w_Mesh_setVertices(lua_State *L)
int idx = -ncomponents;
for (const Mesh::AttribFormat &format : vertexformat)
for (const Buffer::DataMember &member : vertexformat)
{
// Fetch the values from Lua and store them in data buffer.
data = luax_writeAttributeData(L, idx, format.type, format.components, data);
idx += format.components;
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
idx += member.info.components;
}
lua_pop(L, ncomponents + 1);
data += stride;
}
t->unmapVertexData(byteoffset, vertcount * stride);
@@ -256,34 +125,34 @@ int w_Mesh_setVertex(lua_State *L)
bool istable = lua_istable(L, 3);
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
char *data = (char *) t->getVertexScratchBuffer();
char *writtendata = data;
int idx = istable ? 1 : 3;
if (istable)
{
for (const Mesh::AttribFormat &format : vertexformat)
for (const Buffer::DataMember &member : vertexformat)
{
for (int i = idx; i < idx + format.components; i++)
int components = member.info.components;
for (int i = idx; i < idx + components; i++)
lua_rawgeti(L, 3, i);
// Fetch the values from Lua and store them in data buffer.
writtendata = luax_writeAttributeData(L, -format.components, format.type, format.components, writtendata);
luax_writebufferdata(L, -components, member.decl.format, data + member.offset);
idx += format.components;
lua_pop(L, format.components);
idx += components;
lua_pop(L, components);
}
}
else
{
for (const Mesh::AttribFormat &format : vertexformat)
for (const Buffer::DataMember &member : vertexformat)
{
// Fetch the values from Lua and store them in data buffer.
writtendata = luax_writeAttributeData(L, idx, format.type, format.components, writtendata);
idx += format.components;
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
idx += member.info.components;
}
}
@@ -296,19 +165,18 @@ int w_Mesh_getVertex(lua_State *L)
Mesh *t = luax_checkmesh(L, 1);
size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
char *data = (char *) t->getVertexScratchBuffer();
const char *readdata = data;
luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); });
int n = 0;
for (const Mesh::AttribFormat &format : vertexformat)
for (const Buffer::DataMember &member : vertexformat)
{
readdata = luax_readAttributeData(L, format.type, format.components, readdata);
n += format.components;
luax_readbufferdata(L, member.decl.format, data + member.offset);
n += member.info.components;
}
return n;
@@ -320,15 +188,18 @@ int w_Mesh_setVertexAttribute(lua_State *L)
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
vertex::DataType type;
int components;
luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
const auto &vertexformat = t->getVertexFormat();
if (attribindex < 0 || attribindex >= (int) vertexformat.size())
return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
const Buffer::DataMember &member = vertexformat[attribindex];
// Maximum possible size for a single vertex attribute.
char data[sizeof(float) * 4];
// Fetch the values from Lua and store them in the data buffer.
luax_writeAttributeData(L, 4, type, components, data);
luax_writebufferdata(L, 4, member.decl.format, data);
luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
return 0;
@@ -340,17 +211,20 @@ int w_Mesh_getVertexAttribute(lua_State *L)
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
vertex::DataType type;
int components;
luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
const auto &vertexformat = t->getVertexFormat();
if (attribindex < 0 || attribindex >= (int) vertexformat.size())
return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
const Buffer::DataMember &member = vertexformat[attribindex];
// Maximum possible size for a single vertex attribute.
char data[sizeof(float) * 4];
luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
luax_readAttributeData(L, type, components, data);
return components;
luax_readbufferdata(L, member.decl.format, data);
return member.info.components;
}
int w_Mesh_getVertexCount(lua_State *L)
@@ -364,28 +238,27 @@ int w_Mesh_getVertexFormat(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
lua_createtable(L, (int) vertexformat.size(), 0);
const char *tname = nullptr;
for (size_t i = 0; i < vertexformat.size(); i++)
{
if (!vertex::getConstant(vertexformat[i].type, tname))
return luax_enumerror(L, "vertex attribute data type", vertex::getConstants(vertexformat[i].type), tname);
const auto &decl = vertexformat[i].decl;
if (!getConstant(decl.format, tname))
return luax_enumerror(L, "vertex attribute data type", getConstants(decl.format), tname);
lua_createtable(L, 3, 0);
lua_pushstring(L, vertexformat[i].name.c_str());
lua_pushstring(L, decl.name.c_str());
lua_rawseti(L, -2, 1);
lua_pushstring(L, tname);
lua_rawseti(L, -2, 2);
lua_pushinteger(L, vertexformat[i].components);
lua_rawseti(L, -2, 3);
// format[i] = {name, type, components}
// format[i] = {name, type}
lua_rawseti(L, -2, (int) i + 1);
}
@@ -415,16 +288,29 @@ int w_Mesh_attachAttribute(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const char *name = luaL_checkstring(L, 2);
Mesh *mesh = luax_checkmesh(L, 3);
Buffer *buffer = nullptr;
if (luax_istype(L, 3, Buffer::type))
{
buffer = luax_checktype<Buffer>(L, 3);
}
else
{
Mesh *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;
const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
if (stepstr != nullptr && !vertex::getConstant(stepstr, step))
return luax_enumerror(L, "vertex attribute step", vertex::getConstants(step), stepstr);
if (stepstr != nullptr && !getConstant(stepstr, step))
return luax_enumerror(L, "vertex attribute step", getConstants(step), stepstr);
const char *attachname = luaL_optstring(L, 5, name);
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname, step); });
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, attachname, step); });
return 0;
}
@@ -438,6 +324,48 @@ int w_Mesh_detachAttribute(lua_State *L)
return 1;
}
int w_Mesh_getAttachedAttributes(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const auto &attributes = t->getAttachedAttributes();
lua_createtable(L, (int) attributes.size(), 0);
for (int i = 0; i < (int) attributes.size(); i++)
{
const auto &attrib = attributes[i];
lua_createtable(L, 4, 0);
luax_pushstring(L, attrib.name);
lua_rawseti(L, -1, 1);
luax_pushtype(L, attrib.buffer.get());
lua_rawseti(L, -1, 2);
const char *stepstr = nullptr;
if (!getConstant(attrib.step, stepstr))
return luaL_error(L, "Invalid vertex attribute step.");
lua_pushstring(L, stepstr);
lua_rawseti(L, -1, 3);
const Buffer::DataMember &member = attrib.buffer->getDataMember(attrib.indexInBuffer);
luax_pushstring(L, member.decl.name);
lua_rawseti(L, -1, 4);
lua_rawseti(L, -1, i + 1);
}
return 1;
}
int w_Mesh_getVertexBuffer(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
luax_pushtype(L, t->getVertexBuffer());
return 1;
}
int w_Mesh_flush(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -462,10 +390,10 @@ int w_Mesh_setVertexMap(lua_State *L)
const char *indextypestr = luaL_checkstring(L, 3);
IndexDataType indextype;
if (!vertex::getConstant(indextypestr, indextype))
return luax_enumerror(L, "index data type", vertex::getConstants(indextype), indextypestr);
if (!getConstant(indextypestr, indextype))
return luax_enumerror(L, "index data type", getConstants(indextype), indextypestr);
size_t datatypesize = vertex::getIndexDataSize(indextype);
size_t datatypesize = getIndexDataSize(indextype);
int indexcount = (int) luaL_optinteger(L, 4, d->getSize() / datatypesize);
@@ -528,6 +456,23 @@ int w_Mesh_getVertexMap(lua_State *L)
return 1;
}
int w_Mesh_setIndexBuffer(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
Buffer *b = nullptr;
if (!lua_isnoneornil(L, 2))
b = luax_checkbuffer(L, 2);
luax_catchexcept(L, [&]() { t->setIndexBuffer(b); });
return 0;
}
int w_Mesh_getIndexBuffer(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
luax_pushtype(L, t->getIndexBuffer());
return 1;
}
int w_Mesh_setTexture(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -561,8 +506,8 @@ int w_Mesh_setDrawMode(lua_State *L)
const char *str = luaL_checkstring(L, 2);
PrimitiveType mode;
if (!vertex::getConstant(str, mode))
return luax_enumerror(L, "mesh draw mode", vertex::getConstants(mode), str);
if (!getConstant(str, mode))
return luax_enumerror(L, "mesh draw mode", getConstants(mode), str);
t->setDrawMode(mode);
return 0;
@@ -574,7 +519,7 @@ int w_Mesh_getDrawMode(lua_State *L)
PrimitiveType mode = t->getDrawMode();
const char *str;
if (!vertex::getConstant(mode, str))
if (!getConstant(mode, str))
return luaL_error(L, "Unknown mesh draw mode.");
lua_pushstring(L, str);
@@ -624,9 +569,13 @@ static const luaL_Reg w_Mesh_functions[] =
{ "isAttributeEnabled", w_Mesh_isAttributeEnabled },
{ "attachAttribute", w_Mesh_attachAttribute },
{ "detachAttribute", w_Mesh_detachAttribute },
{ "getAttachedAttributes", w_Mesh_getAttachedAttributes },
{ "getVertexBuffer", w_Mesh_getVertexBuffer },
{ "flush", w_Mesh_flush },
{ "setVertexMap", w_Mesh_setVertexMap },
{ "getVertexMap", w_Mesh_getVertexMap },
{ "setIndexBuffer", w_Mesh_setIndexBuffer },
{ "getIndexBuffer", w_Mesh_getIndexBuffer },
{ "setTexture", w_Mesh_setTexture },
{ "getTexture", w_Mesh_getTexture },
{ "setDrawMode", w_Mesh_setDrawMode },
-3
View File
@@ -30,9 +30,6 @@ namespace love
namespace graphics
{
char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data);
const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data);
Mesh *luax_checkmesh(lua_State *L, int idx);
extern "C" int luaopen_mesh(lua_State *L);
+52 -17
View File
@@ -287,6 +287,23 @@ int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shad
return 0;
}
int w_Shader_sendBuffers(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
{
int count = _getCount(L, startidx, info);
std::vector<Buffer *> buffers;
buffers.reserve(count);
for (int i = 0; i < count; i++)
{
Buffer *buffer = luax_checktype<Buffer>(L, startidx + i);
buffers.push_back(buffer);
}
luax_catchexcept(L, [&]() { shader->sendBuffers(info, buffers.data(), count); });
return 0;
}
static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, const char *name)
{
switch (info->baseType)
@@ -303,6 +320,8 @@ static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, co
return w_Shader_sendBooleans(L, startidx, shader, info);
case Shader::UNIFORM_SAMPLER:
return w_Shader_sendTextures(L, startidx, shader, info);
case Shader::UNIFORM_TEXELBUFFER:
return w_Shader_sendBuffers(L, startidx, shader, info);
default:
return luaL_error(L, "Unknown variable type for shader uniform '%s", name);
}
@@ -313,20 +332,36 @@ static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const S
if (info->baseType == Shader::UNIFORM_SAMPLER)
return luaL_error(L, "Uniform sampler values (textures) cannot be sent to Shaders via Data objects.");
bool columnmajor = false;
if (info->baseType == Shader::UNIFORM_MATRIX && lua_type(L, startidx + 1) == LUA_TSTRING)
math::Transform::MatrixLayout layout = math::Transform::MATRIX_ROW_MAJOR;
int dataidx = startidx;
if (info->baseType == Shader::UNIFORM_MATRIX)
{
const char *layoutstr = lua_tostring(L, startidx + 1);
math::Transform::MatrixLayout layout;
if (!math::Transform::getConstant(layoutstr, layout))
return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
if (lua_type(L, startidx) == LUA_TSTRING)
{
// (matrixlayout, data, ...)
const char *layoutstr = lua_tostring(L, startidx);
if (!math::Transform::getConstant(layoutstr, layout))
return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
startidx++;
startidx++;
dataidx = startidx;
}
else if (lua_type(L, startidx + 1) == LUA_TSTRING)
{
// (data, matrixlayout, ...)
// Should be deprecated in the future (doesn't match the argument
// order of Shader:send(name, matrixlayout, table))
const char *layoutstr = lua_tostring(L, startidx + 1);
if (!math::Transform::getConstant(layoutstr, layout))
return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
startidx++;
}
}
Data *data = luax_checktype<Data>(L, startidx);
bool columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
Data *data = luax_checktype<Data>(L, dataidx);
size_t size = data->getSize();
ptrdiff_t offset = (ptrdiff_t) luaL_optinteger(L, startidx + 1, 0);
@@ -339,17 +374,17 @@ static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const S
if (!lua_isnoneornil(L, startidx + 2))
{
lua_Integer datasize = luaL_checkinteger(L, startidx + 2);
if (datasize <= 0)
lua_Integer sizearg = luaL_checkinteger(L, startidx + 2);
if (sizearg <= 0)
return luaL_error(L, "Size must be greater than 0.");
else if ((size_t) datasize > size - offset)
else if ((size_t) sizearg > size - offset)
return luaL_error(L, "Size and offset must fit within the Data's bounds.");
else if (size % uniformstride != 0)
return luaL_error(L, "Size must be a multiple of the uniform's size in bytes.");
else if (size > info->dataSize)
else if (sizearg % uniformstride != 0)
return luaL_error(L, "Size (%d) must be a multiple of the uniform's size in bytes (%d).", sizearg, uniformstride);
else if ((size_t) sizearg > info->dataSize)
return luaL_error(L, "Size must not be greater than the uniform's total size in bytes.");
size = (size_t) datasize;
size = (size_t) sizearg;
}
else
{
@@ -416,7 +451,7 @@ int w_Shader_send(lua_State *L)
return 1;
}
if (luax_istype(L, 3, Data::type))
if (luax_istype(L, 3, Data::type) || (info->baseType == Shader::UNIFORM_MATRIX && luax_istype(L, 4, Data::type)))
w_Shader_sendData(L, 3, shader, info, false);
else
w_Shader_sendLuaValues(L, 3, shader, info, name);
+15 -2
View File
@@ -216,9 +216,22 @@ int w_SpriteBatch_attachAttribute(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
const char *name = luaL_checkstring(L, 2);
Mesh *m = luax_checktype<Mesh>(L, 3);
luax_catchexcept(L, [&](){ t->attachAttribute(name, m); });
Buffer *buffer = nullptr;
if (luax_istype(L, 3, Buffer::type))
{
buffer = luax_checktype<Buffer>(L, 3);
}
else
{
Mesh *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); });
return 0;
}
+2 -1
View File
@@ -30,12 +30,13 @@ static void loveSTBIAssert(bool test, const char *teststr)
}
// stb_image
#define STBI_ONLY_JPEG
#define STBI_ONLY_JPEG
// #define STBI_ONLY_PNG
#define STBI_ONLY_BMP
#define STBI_ONLY_TGA
#define STBI_ONLY_HDR
#define STBI_NO_STDIO
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ASSERT(A) loveSTBIAssert((A), #A)
#include "libraries/stb/stb_image.h"
-5
View File
@@ -36,10 +36,6 @@
#ifdef LOVE_ANDROID
#include <SDL.h>
extern "C"
{
#include "luajit.h"
}
#endif // LOVE_ANDROID
#ifdef LOVE_LEGENDARY_CONSOLE_IO_HACK
@@ -383,7 +379,6 @@ int luaopen_love(lua_State *L)
lua_setfield(L, -2, "_version_codename");
#ifdef LOVE_ANDROID
luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE | LUAJIT_MODE_OFF);
lua_register(L, "print", w_print_sdl_log);
#endif
+70 -39
View File
@@ -24,6 +24,7 @@
#include "common/delay.h"
#include "Timer.h"
#include <iostream>
#if defined(LOVE_WINDOWS)
#include <windows.h>
#elif defined(LOVE_MACOS) || defined(LOVE_IOS)
@@ -35,15 +36,6 @@
#include <sys/time.h>
#endif
#if defined(LOVE_LINUX)
static inline double getTimeOfDay()
{
timeval t;
gettimeofday(&t, NULL);
return (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
}
#endif
namespace love
{
namespace timer
@@ -109,52 +101,91 @@ double Timer::getAverageDelta() const
return averageDelta;
}
double Timer::getTimerPeriod()
#if defined(LOVE_LINUX)
static inline timespec getTimeOfDay()
{
#if defined(LOVE_MACOS) || defined(LOVE_IOS)
mach_timebase_info_data_t info;
mach_timebase_info(&info);
return (double) info.numer / (double) info.denom / 1000000000.0;
#elif defined(LOVE_WINDOWS)
LARGE_INTEGER temp;
if (QueryPerformanceFrequency(&temp) != 0 && temp.QuadPart != 0)
return 1.0 / (double) temp.QuadPart;
#endif
return 0;
timeval t;
gettimeofday(&t, NULL);
return timespec { t.tv_sec, t.tv_usec * 1000 };
}
double Timer::getTime()
static timespec getTimeAbsolute()
{
// The timer period (reciprocal of the frequency.)
static const double timerPeriod = getTimerPeriod();
#if defined(LOVE_LINUX)
(void) timerPeriod; // Unused on linux
double mt;
// Check for POSIX timers and monotonic clocks. If not supported, use the gettimeofday fallback.
#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) \
&& (defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC))
timespec t;
#ifdef CLOCK_MONOTONIC_RAW
clockid_t clk_id = CLOCK_MONOTONIC_RAW;
#else
clockid_t clk_id = CLOCK_MONOTONIC;
#endif
timespec t;
if (clock_gettime(clk_id, &t) == 0)
mt = (double) t.tv_sec + (double) t.tv_nsec / 1000000000.0;
return t;
else
return getTimeOfDay();
#endif
mt = getTimeOfDay();
return mt;
#elif defined(LOVE_MACOS) || defined(LOVE_IOS)
return (double) mach_absolute_time() * timerPeriod;
#elif defined(LOVE_WINDOWS)
LARGE_INTEGER microTime;
QueryPerformanceCounter(&microTime);
return (double) microTime.QuadPart * timerPeriod;
#endif
return getTimeOfDay();
}
double Timer::getTime()
{
static const timespec start = getTimeAbsolute();
const timespec now = getTimeAbsolute();
// tv_sec and tv_nsec should be signed on POSIX, so we are fine in just subtracting here.
const long sec = now.tv_sec - start.tv_sec;
const long nsec = now.tv_nsec - start.tv_nsec;
return (double) sec + (double) nsec / 1.0e9;
}
#elif defined(LOVE_MACOS) || defined(LOVE_IOS)
static mach_timebase_info_data_t getTimebaseInfo()
{
mach_timebase_info_data_t info;
mach_timebase_info(&info);
return info;
}
double Timer::getTime()
{
static const mach_timebase_info_data_t info = getTimebaseInfo();
static const uint64_t start = mach_absolute_time();
const uint64_t rel = mach_absolute_time() - start;
return ((double) rel * 1.0e-9) * (double) info.numer / (double) info.denom;
}
#elif defined(LOVE_WINDOWS)
static LARGE_INTEGER getTimeAbsolute()
{
LARGE_INTEGER t;
QueryPerformanceCounter(&t);
return t;
}
static LARGE_INTEGER getFrequency()
{
LARGE_INTEGER freq;
// "On systems that run Windows XP or later, the function will always succeed and will thus never return zero."
QueryPerformanceFrequency(&freq);
return freq;
}
double Timer::getTime()
{
static const LARGE_INTEGER freq = getFrequency();
static const LARGE_INTEGER start = getTimeAbsolute();
const LARGE_INTEGER now = getTimeAbsolute();
LARGE_INTEGER rel;
rel.QuadPart = now.QuadPart - start.QuadPart;
return (double) rel.QuadPart / (double) freq.QuadPart;
}
#endif
} // timer
} // love
+5 -6
View File
@@ -72,9 +72,11 @@ public:
double getAverageDelta() const;
/**
* Gets the amount of time passed since an unspecified time. Useful for
* profiling code or measuring intervals. The time is microsecond-precise,
* and increases monotonically.
* Gets the amount of time in seconds passed since its first invocation
* (which happens as part of the Timer constructor,
* which is called when the module is first opened).
* Useful for profiling code or measuring intervals.
* The time is microsecond-precise, and increases monotonically.
* @return The time (in seconds)
**/
static double getTime();
@@ -99,9 +101,6 @@ private:
// The current timestep.
double dt;
// Returns the timer period on some platforms.
static double getTimerPeriod();
}; // Timer
} // timer