mirror of
https://github.com/love2d/love.git
synced 2026-08-17 11:00:31 +02:00
love.graphics: move more platform-independent code out of the opengl backend
--HG-- branch : minor
This commit is contained in:
@@ -151,7 +151,7 @@ IndexDataType QuadIndices::getType(size_t s) const
|
||||
return vertex::getIndexDataTypeFromMax(getIndexCount(s));
|
||||
}
|
||||
|
||||
size_t QuadIndices::getElementSize()
|
||||
size_t QuadIndices::getElementSize() const
|
||||
{
|
||||
return elementSize;
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ public:
|
||||
* Can be used with getPointer to calculate an offset into the array based
|
||||
* on a number of elements.
|
||||
**/
|
||||
size_t getElementSize();
|
||||
size_t getElementSize() const;
|
||||
|
||||
/**
|
||||
* Returns the pointer to the Buffer.
|
||||
|
||||
@@ -430,6 +430,128 @@ void Graphics::setCanvas(const RenderTargetsStrongRef &rts)
|
||||
return setCanvas(targets);
|
||||
}
|
||||
|
||||
void Graphics::setCanvas(const RenderTargets &rts)
|
||||
{
|
||||
DisplayState &state = states.back();
|
||||
int ncanvases = (int) rts.colors.size();
|
||||
|
||||
if (ncanvases == 0 && rts.depthStencil.canvas == nullptr)
|
||||
return setCanvas();
|
||||
else if (ncanvases == 0)
|
||||
throw love::Exception("At least one color render target is required when using a custom depth/stencil buffer.");
|
||||
|
||||
const auto &prevRTs = state.renderTargets;
|
||||
|
||||
if (ncanvases == (int) prevRTs.colors.size())
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
for (int i = 0; i < ncanvases; i++)
|
||||
{
|
||||
if (rts.colors[i].canvas != prevRTs.colors[i].canvas.get()
|
||||
|| rts.colors[i].slice != prevRTs.colors[i].slice
|
||||
|| rts.colors[i].mipmap != prevRTs.colors[i].mipmap)
|
||||
{
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modified && (rts.depthStencil.canvas != prevRTs.depthStencil.canvas
|
||||
|| rts.depthStencil.slice != prevRTs.depthStencil.slice
|
||||
|| rts.depthStencil.mipmap != prevRTs.depthStencil.mipmap))
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (rts.temporaryRTFlags != prevRTs.temporaryRTFlags)
|
||||
modified = true;
|
||||
|
||||
if (!modified)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ncanvases > capabilities.limits[LIMIT_MULTI_CANVAS])
|
||||
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
|
||||
|
||||
love::graphics::Canvas *firstcanvas = rts.colors[0].canvas;
|
||||
|
||||
bool multiformatsupported = capabilities.features[FEATURE_MULTI_CANVAS_FORMATS];
|
||||
PixelFormat firstformat = firstcanvas->getPixelFormat();
|
||||
|
||||
if (isPixelFormatDepthStencil(firstformat))
|
||||
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (rts.colors[0].mipmap < 0 || rts.colors[0].mipmap >= firstcanvas->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", rts.colors[0].mipmap + 1);
|
||||
|
||||
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
|
||||
int pixelw = firstcanvas->getPixelWidth(rts.colors[0].mipmap);
|
||||
int pixelh = firstcanvas->getPixelHeight(rts.colors[0].mipmap);
|
||||
|
||||
for (int i = 1; i < ncanvases; i++)
|
||||
{
|
||||
love::graphics::Canvas *c = rts.colors[i].canvas;
|
||||
PixelFormat format = c->getPixelFormat();
|
||||
int mip = rts.colors[i].mipmap;
|
||||
|
||||
if (mip < 0 || mip >= c->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", mip + 1);
|
||||
|
||||
if (c->getPixelWidth(mip) != pixelw || c->getPixelHeight(mip) != pixelh)
|
||||
throw love::Exception("All canvases must have the same pixel dimensions.");
|
||||
|
||||
if (!multiformatsupported && format != firstformat)
|
||||
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
|
||||
|
||||
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
||||
throw love::Exception("All Canvases must have the same MSAA value.");
|
||||
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (format == PIXELFORMAT_sRGBA8)
|
||||
hasSRGBcanvas = true;
|
||||
}
|
||||
|
||||
if (rts.depthStencil.canvas != nullptr)
|
||||
{
|
||||
love::graphics::Canvas *c = rts.depthStencil.canvas;
|
||||
int mip = rts.depthStencil.mipmap;
|
||||
|
||||
if (!isPixelFormatDepthStencil(c->getPixelFormat()))
|
||||
throw love::Exception("Only depth/stencil format Canvases can be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (c->getPixelWidth(mip) != pixelw || c->getPixelHeight(mip) != pixelh)
|
||||
throw love::Exception("All canvases must have the same pixel dimensions.");
|
||||
|
||||
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
||||
throw love::Exception("All Canvases must have the same MSAA value.");
|
||||
|
||||
if (mip < 0 || mip >= c->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", mip + 1);
|
||||
}
|
||||
|
||||
int w = firstcanvas->getWidth(rts.colors[0].mipmap);
|
||||
int h = firstcanvas->getHeight(rts.colors[0].mipmap);
|
||||
|
||||
flushStreamDraws();
|
||||
setCanvasInternal(rts, w, h, pixelw, pixelh, hasSRGBcanvas);
|
||||
|
||||
RenderTargetsStrongRef refs;
|
||||
refs.colors.reserve(rts.colors.size());
|
||||
|
||||
for (auto c : rts.colors)
|
||||
refs.colors.emplace_back(c.canvas, c.slice);
|
||||
|
||||
refs.depthStencil = RenderTargetStrongRef(rts.depthStencil.canvas, rts.depthStencil.slice);
|
||||
refs.temporaryRTFlags = rts.temporaryRTFlags;
|
||||
|
||||
std::swap(state.renderTargets, refs);
|
||||
|
||||
canvasSwitchCount++;
|
||||
}
|
||||
|
||||
Graphics::RenderTargets Graphics::getCanvas() const
|
||||
{
|
||||
const auto &curRTs = states.back().renderTargets;
|
||||
|
||||
@@ -478,7 +478,7 @@ public:
|
||||
Shader *getShader() const;
|
||||
|
||||
void setCanvas(RenderTarget rt, uint32 temporaryRTFlags);
|
||||
virtual void setCanvas(const RenderTargets &rts) = 0;
|
||||
void setCanvas(const RenderTargets &rts);
|
||||
void setCanvas(const RenderTargetsStrongRef &rts);
|
||||
virtual void setCanvas() = 0;
|
||||
|
||||
@@ -861,6 +861,8 @@ protected:
|
||||
|
||||
virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;
|
||||
|
||||
virtual void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) = 0;
|
||||
|
||||
virtual void initCapabilities() = 0;
|
||||
virtual void getAPIStats(int &drawcalls, int &shaderswitches) const = 0;
|
||||
|
||||
|
||||
@@ -564,11 +564,88 @@ bool Mesh::getDrawRange(int &start, int &count) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mesh::draw(love::graphics::Graphics *gfx, const love::Matrix4 &m)
|
||||
void Mesh::draw(Graphics *gfx, const love::Matrix4 &m)
|
||||
{
|
||||
drawInstanced(gfx, m, 1);
|
||||
}
|
||||
|
||||
void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
{
|
||||
if (vertexCount <= 0 || instancecount <= 0)
|
||||
return;
|
||||
|
||||
if (instancecount > 1 && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
|
||||
throw love::Exception("Instancing is not supported on this system.");
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
if (Shader::current && texture.get())
|
||||
Shader::current->checkMainTexture(texture);
|
||||
|
||||
uint32 enabledattribs = 0;
|
||||
uint32 instancedattribs = 0;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (!attrib.second.enabled)
|
||||
continue;
|
||||
|
||||
love::graphics::Mesh *mesh = attrib.second.mesh;
|
||||
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
|
||||
|
||||
if (location >= 0)
|
||||
{
|
||||
uint32 bit = 1u << (uint32) location;
|
||||
|
||||
enabledattribs |= bit;
|
||||
|
||||
if (attrib.second.step == STEP_PER_INSTANCE)
|
||||
instancedattribs |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
// Not supported on all platforms or GL versions, I believe.
|
||||
if (!(enabledattribs & ATTRIBFLAG_POS))
|
||||
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
||||
|
||||
bool useindexbuffer = useIndexBuffer && ibo != nullptr && elementCount > 0;
|
||||
|
||||
int start = 0;
|
||||
int count = 0;
|
||||
|
||||
if (useindexbuffer)
|
||||
{
|
||||
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
|
||||
ibo->unmap();
|
||||
|
||||
start = std::min(std::max(0, rangeStart), (int) elementCount - 1);
|
||||
|
||||
count = (int) elementCount;
|
||||
if (rangeCount > 0)
|
||||
count = std::min(count, rangeCount);
|
||||
|
||||
count = std::min(count, (int) elementCount - start);
|
||||
}
|
||||
else
|
||||
{
|
||||
start = std::min(std::max(0, rangeStart), (int) vertexCount - 1);
|
||||
|
||||
count = (int) vertexCount;
|
||||
if (rangeCount > 0)
|
||||
count = std::min(count, rangeCount);
|
||||
|
||||
count = std::min(count, (int) vertexCount - start);
|
||||
}
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
if (count > 0)
|
||||
drawInternal(start, count, instancecount, useindexbuffer, enabledattribs, instancedattribs);
|
||||
}
|
||||
|
||||
size_t Mesh::getAttribFormatSize(const AttribFormat &format)
|
||||
{
|
||||
switch (format.type)
|
||||
|
||||
@@ -195,11 +195,11 @@ public:
|
||||
|
||||
virtual int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) = 0;
|
||||
|
||||
virtual void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) = 0;
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
|
||||
|
||||
static bool getConstant(const char *in, DrawMode &out);
|
||||
static bool getConstant(DrawMode in, const char *&out);
|
||||
|
||||
@@ -223,6 +223,8 @@ protected:
|
||||
void calculateAttributeSizes();
|
||||
size_t getAttributeOffset(size_t attribindex) const;
|
||||
|
||||
virtual void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const = 0;
|
||||
|
||||
static size_t getAttribFormatSize(const AttribFormat &format);
|
||||
static std::vector<AttribFormat> getDefaultVertexFormat();
|
||||
|
||||
|
||||
@@ -1048,15 +1048,21 @@ void ParticleSystem::update(float dt)
|
||||
prevPosition = position;
|
||||
}
|
||||
|
||||
bool ParticleSystem::prepareDraw(Graphics *gfx)
|
||||
void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
{
|
||||
uint32 pCount = getCount();
|
||||
|
||||
if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || buffer == nullptr)
|
||||
return false;
|
||||
return;
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
if (Shader::current && texture.get())
|
||||
Shader::current->checkMainTexture(texture);
|
||||
|
||||
const Vector2 *positions = texture->getQuad()->getVertexPositions();
|
||||
const Vector2 *texcoords = texture->getQuad()->getVertexTexCoords();
|
||||
|
||||
@@ -1098,7 +1104,8 @@ bool ParticleSystem::prepareDraw(Graphics *gfx)
|
||||
|
||||
buffer->unmap();
|
||||
|
||||
return true;
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
drawInternal();
|
||||
}
|
||||
|
||||
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
|
||||
|
||||
@@ -569,6 +569,9 @@ public:
|
||||
**/
|
||||
void update(float dt);
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
static bool getConstant(const char *in, AreaSpreadDistribution &out);
|
||||
static bool getConstant(AreaSpreadDistribution in, const char *&out);
|
||||
|
||||
@@ -612,7 +615,7 @@ protected:
|
||||
int quadIndex;
|
||||
};
|
||||
|
||||
bool prepareDraw(Graphics *gfx);
|
||||
virtual void drawInternal() const = 0;
|
||||
|
||||
// Pointer to the beginning of the allocated memory.
|
||||
Particle *pMem;
|
||||
|
||||
@@ -62,9 +62,9 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag
|
||||
else
|
||||
vertex_format = vertex::CommonFormat::XYf_STf_RGBAub;
|
||||
|
||||
format_stride = vertex::getFormatStride(vertex_format);
|
||||
vertex_stride = vertex::getFormatStride(vertex_format);
|
||||
|
||||
size_t vertex_size = format_stride * 4 * size;
|
||||
size_t vertex_size = vertex_stride * 4 * size;
|
||||
array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
|
||||
|
||||
// Always keep the VBO mapped when adding data (it'll be unmapped on draw.)
|
||||
size_t offset = (index == -1 ? next : index) * format_stride * 4;
|
||||
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
|
||||
auto verts = (XYf_STf_RGBAub *) ((uint8 *) array_buf->map() + offset);
|
||||
|
||||
m.transformXY(verts, quadpositions, 4);
|
||||
@@ -107,7 +107,7 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
verts[i].color = color;
|
||||
}
|
||||
|
||||
array_buf->setMappedRangeModified(offset, format_stride * 4);
|
||||
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
@@ -141,7 +141,7 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
|
||||
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
|
||||
|
||||
// Always keep the VBO mapped when adding data (it'll be unmapped on draw.)
|
||||
size_t offset = (index == -1 ? next : index) * format_stride * 4;
|
||||
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
|
||||
auto verts = (XYf_STPf_RGBAub *) ((uint8 *) array_buf->map() + offset);
|
||||
|
||||
m.transformXY(verts, quadpositions, 4);
|
||||
@@ -154,7 +154,7 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
|
||||
verts[i].color = color;
|
||||
}
|
||||
|
||||
array_buf->setMappedRangeModified(offset, format_stride * 4);
|
||||
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
@@ -225,7 +225,7 @@ void SpriteBatch::setBufferSize(int newsize)
|
||||
if (newsize == size)
|
||||
return;
|
||||
|
||||
size_t vertex_size = format_stride * 4 * newsize;
|
||||
size_t vertex_size = vertex_stride * 4 * newsize;
|
||||
love::graphics::Buffer *new_array_buf = nullptr;
|
||||
|
||||
int new_next = std::min(next, newsize);
|
||||
@@ -236,7 +236,7 @@ void SpriteBatch::setBufferSize(int newsize)
|
||||
new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
|
||||
|
||||
// Copy as much of the old data into the new GLBuffer as can fit.
|
||||
size_t copy_size = format_stride * 4 * new_next;
|
||||
size_t copy_size = vertex_stride * 4 * new_next;
|
||||
array_buf->copyTo(0, copy_size, new_array_buf, 0);
|
||||
|
||||
quad_indices = QuadIndices(gfx, newsize);
|
||||
@@ -307,5 +307,59 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (next == 0)
|
||||
return;
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (texture.get())
|
||||
{
|
||||
if (Shader::isDefaultActive())
|
||||
{
|
||||
Shader::StandardShader defaultshader = Shader::STANDARD_DEFAULT;
|
||||
if (texture->getTextureType() == TEXTURE_2D_ARRAY)
|
||||
defaultshader = Shader::STANDARD_ARRAY;
|
||||
|
||||
Shader::attachDefault(defaultshader);
|
||||
}
|
||||
|
||||
if (Shader::current)
|
||||
Shader::current->checkMainTexture(texture);
|
||||
}
|
||||
|
||||
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
||||
array_buf->unmap();
|
||||
|
||||
CommonFormat format = vertex_format;
|
||||
|
||||
if (!color_active)
|
||||
{
|
||||
if (format == CommonFormat::XYf_STPf_RGBAub)
|
||||
format = CommonFormat::XYf_STPf;
|
||||
else
|
||||
format = CommonFormat::XYf_STf;
|
||||
}
|
||||
|
||||
int start = std::min(std::max(0, range_start), next - 1);
|
||||
|
||||
int count = next;
|
||||
if (range_count > 0)
|
||||
count = std::min(count, range_count);
|
||||
|
||||
count = std::min(count, next - start);
|
||||
|
||||
size_t indexbytestart = quad_indices.getIndexCount(start) * quad_indices.getElementSize();
|
||||
size_t indexcount = quad_indices.getIndexCount(count);
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
if (count > 0)
|
||||
drawInternal(format, indexbytestart, indexcount);
|
||||
}
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "Drawable.h"
|
||||
#include "Color.h"
|
||||
#include "Mesh.h"
|
||||
#include "vertex.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -105,6 +106,9 @@ public:
|
||||
void setDrawRange();
|
||||
bool getDrawRange(int &start, int &count) const;
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
protected:
|
||||
|
||||
struct AttachedAttribute
|
||||
@@ -119,6 +123,8 @@ protected:
|
||||
**/
|
||||
void setBufferSize(int newsize);
|
||||
|
||||
virtual void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) = 0;
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
// Max number of sprites in the batch.
|
||||
@@ -133,7 +139,7 @@ protected:
|
||||
bool color_active;
|
||||
|
||||
vertex::CommonFormat vertex_format;
|
||||
size_t format_stride;
|
||||
size_t vertex_stride;
|
||||
|
||||
love::graphics::Buffer *array_buf;
|
||||
QuadIndices quad_indices;
|
||||
|
||||
@@ -235,5 +235,36 @@ int Text::getHeight(int index) const
|
||||
return text_data[index].text_info.height;
|
||||
}
|
||||
|
||||
void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
{
|
||||
if (vbo == nullptr || draw_commands.empty())
|
||||
return;
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
if (Shader::current)
|
||||
Shader::current->checkMainTextureType(TEXTURE_2D, false);
|
||||
|
||||
// Re-generate the text if the Font's texture cache was invalidated.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
regenerateVertices();
|
||||
|
||||
int totalverts = 0;
|
||||
for (const Font::DrawCommand &cmd : draw_commands)
|
||||
totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
|
||||
|
||||
if ((size_t) totalverts / 4 > quadIndices.getSize())
|
||||
quadIndices = QuadIndices(gfx, (size_t) totalverts / 4);
|
||||
|
||||
vbo->unmap(); // Make sure all pending data is flushed to the GPU.
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
drawInternal(draw_commands);
|
||||
}
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -63,6 +63,9 @@ public:
|
||||
**/
|
||||
int getHeight(int index = 0) const;
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(love::graphics::Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
protected:
|
||||
|
||||
struct TextData
|
||||
@@ -80,6 +83,8 @@ protected:
|
||||
void regenerateVertices();
|
||||
void addTextData(const TextData &s);
|
||||
|
||||
virtual void drawInternal(const std::vector<Font::DrawCommand> &commands) const = 0;
|
||||
|
||||
StrongRef<Font> font;
|
||||
Buffer *vbo;
|
||||
QuadIndices quadIndices;
|
||||
|
||||
@@ -492,107 +492,9 @@ void Graphics::setDebug(bool enable)
|
||||
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
|
||||
}
|
||||
|
||||
void Graphics::setCanvas(const RenderTargets &rts)
|
||||
void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
|
||||
{
|
||||
DisplayState &state = states.back();
|
||||
int ncanvases = (int) rts.colors.size();
|
||||
|
||||
if (ncanvases == 0 && rts.depthStencil.canvas == nullptr)
|
||||
return setCanvas();
|
||||
else if (ncanvases == 0)
|
||||
throw love::Exception("At least one color render target is required when using a custom depth/stencil buffer.");
|
||||
|
||||
const auto &prevRTs = state.renderTargets;
|
||||
|
||||
if (ncanvases == (int) prevRTs.colors.size())
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
for (int i = 0; i < ncanvases; i++)
|
||||
{
|
||||
if (rts.colors[i].canvas != prevRTs.colors[i].canvas.get()
|
||||
|| rts.colors[i].slice != prevRTs.colors[i].slice
|
||||
|| rts.colors[i].mipmap != prevRTs.colors[i].mipmap)
|
||||
{
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modified && (rts.depthStencil.canvas != prevRTs.depthStencil.canvas
|
||||
|| rts.depthStencil.slice != prevRTs.depthStencil.slice
|
||||
|| rts.depthStencil.mipmap != prevRTs.depthStencil.mipmap))
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (rts.temporaryRTFlags != prevRTs.temporaryRTFlags)
|
||||
modified = true;
|
||||
|
||||
if (!modified)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ncanvases > gl.getMaxRenderTargets())
|
||||
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
|
||||
|
||||
love::graphics::Canvas *firstcanvas = rts.colors[0].canvas;
|
||||
|
||||
bool multiformatsupported = Canvas::isMultiFormatMultiCanvasSupported();
|
||||
PixelFormat firstformat = firstcanvas->getPixelFormat();
|
||||
|
||||
if (isPixelFormatDepthStencil(firstformat))
|
||||
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (rts.colors[0].mipmap < 0 || rts.colors[0].mipmap >= firstcanvas->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", rts.colors[0].mipmap + 1);
|
||||
|
||||
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
|
||||
int pixelwidth = firstcanvas->getPixelWidth(rts.colors[0].mipmap);
|
||||
int pixelheight = firstcanvas->getPixelHeight(rts.colors[0].mipmap);
|
||||
|
||||
for (int i = 1; i < ncanvases; i++)
|
||||
{
|
||||
love::graphics::Canvas *c = rts.colors[i].canvas;
|
||||
PixelFormat format = c->getPixelFormat();
|
||||
int mip = rts.colors[i].mipmap;
|
||||
|
||||
if (mip < 0 || mip >= c->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", mip + 1);
|
||||
|
||||
if (c->getPixelWidth(mip) != pixelwidth || c->getPixelHeight(mip) != pixelheight)
|
||||
throw love::Exception("All canvases must have the same pixel dimensions.");
|
||||
|
||||
if (!multiformatsupported && format != firstformat)
|
||||
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
|
||||
|
||||
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
||||
throw love::Exception("All Canvases must have the same MSAA value.");
|
||||
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (format == PIXELFORMAT_sRGBA8)
|
||||
hasSRGBcanvas = true;
|
||||
}
|
||||
|
||||
if (rts.depthStencil.canvas != nullptr)
|
||||
{
|
||||
love::graphics::Canvas *c = rts.depthStencil.canvas;
|
||||
int mip = rts.depthStencil.mipmap;
|
||||
|
||||
if (!isPixelFormatDepthStencil(c->getPixelFormat()))
|
||||
throw love::Exception("Only depth/stencil format Canvases can be used with the 'depthstencil' field of the table passed into setCanvas.");
|
||||
|
||||
if (c->getPixelWidth(mip) != pixelwidth || c->getPixelHeight(mip) != pixelheight)
|
||||
throw love::Exception("All canvases must have the same pixel dimensions.");
|
||||
|
||||
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
|
||||
throw love::Exception("All Canvases must have the same MSAA value.");
|
||||
|
||||
if (mip < 0 || mip >= c->getMipmapCount())
|
||||
throw love::Exception("Invalid mipmap level %d.", mip + 1);
|
||||
}
|
||||
const DisplayState &state = states.back();
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("setCanvas(...)");
|
||||
|
||||
@@ -600,15 +502,13 @@ void Graphics::setCanvas(const RenderTargets &rts)
|
||||
|
||||
bindCachedFBO(rts);
|
||||
|
||||
gl.setViewport({0, 0, pixelwidth, pixelheight});
|
||||
gl.setViewport({0, 0, pixelw, pixelh});
|
||||
|
||||
// Re-apply the scissor if it was active, since the rectangle passed to
|
||||
// glScissor is affected by the viewport dimensions.
|
||||
if (state.scissor)
|
||||
setScissor(state.scissorRect);
|
||||
|
||||
int w = firstcanvas->getWidth(rts.colors[0].mipmap);
|
||||
int h = firstcanvas->getHeight(rts.colors[0].mipmap);
|
||||
projectionMatrix = Matrix4::ortho(0.0, (float) w, 0.0, (float) h);
|
||||
|
||||
// Make sure the correct sRGB setting is used when drawing to the canvases.
|
||||
@@ -619,19 +519,6 @@ void Graphics::setCanvas(const RenderTargets &rts)
|
||||
else if (!hasSRGBcanvas && gl.hasFramebufferSRGB())
|
||||
gl.setFramebufferSRGB(false);
|
||||
}
|
||||
|
||||
RenderTargetsStrongRef refs;
|
||||
refs.colors.reserve(rts.colors.size());
|
||||
|
||||
for (auto c : rts.colors)
|
||||
refs.colors.emplace_back(c.canvas, c.slice);
|
||||
|
||||
refs.depthStencil = RenderTargetStrongRef(rts.depthStencil.canvas, rts.depthStencil.slice);
|
||||
refs.temporaryRTFlags = rts.temporaryRTFlags;
|
||||
|
||||
std::swap(state.renderTargets, refs);
|
||||
|
||||
canvasSwitchCount++;
|
||||
}
|
||||
|
||||
void Graphics::setCanvas()
|
||||
@@ -643,6 +530,7 @@ void Graphics::setCanvas()
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("setCanvas()");
|
||||
|
||||
flushStreamDraws();
|
||||
endPass();
|
||||
|
||||
state.renderTargets = RenderTargetsStrongRef();
|
||||
@@ -673,8 +561,6 @@ void Graphics::setCanvas()
|
||||
|
||||
void Graphics::endPass()
|
||||
{
|
||||
flushStreamDraws();
|
||||
|
||||
auto &rts = states.back().renderTargets;
|
||||
love::graphics::Canvas *depthstencil = rts.depthStencil.canvas.get();
|
||||
|
||||
@@ -1013,6 +899,7 @@ void Graphics::present(void *screenshotCallbackData)
|
||||
if (isCanvasActive())
|
||||
throw love::Exception("present cannot be called while a Canvas is active.");
|
||||
|
||||
flushStreamDraws();
|
||||
endPass();
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
||||
|
||||
@@ -97,7 +97,6 @@ public:
|
||||
|
||||
void setColor(Colorf c) override;
|
||||
|
||||
void setCanvas(const RenderTargets &rts) override;
|
||||
void setCanvas() override;
|
||||
|
||||
void setScissor(const Rect &rect) override;
|
||||
@@ -128,6 +127,7 @@ public:
|
||||
private:
|
||||
|
||||
love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
|
||||
void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) override;
|
||||
void initCapabilities() override;
|
||||
void getAPIStats(int &drawcalls, int &shaderswitches) const override;
|
||||
|
||||
|
||||
@@ -91,94 +91,28 @@ int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inpu
|
||||
return attriblocation;
|
||||
}
|
||||
|
||||
void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m, int instancecount)
|
||||
void Mesh::drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const
|
||||
{
|
||||
if (vertexCount <= 0 || instancecount <= 0)
|
||||
return;
|
||||
|
||||
if (instancecount > 1 && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
|
||||
throw love::Exception("Instancing is not supported on this system.");
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
if (Shader::current && texture.get())
|
||||
Shader::current->checkMainTexture(texture);
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
||||
|
||||
uint32 enabledattribs = 0;
|
||||
uint32 instancedattribs = 0;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (!attrib.second.enabled)
|
||||
continue;
|
||||
|
||||
love::graphics::Mesh *mesh = attrib.second.mesh;
|
||||
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
|
||||
|
||||
if (location >= 0)
|
||||
{
|
||||
uint32 bit = 1u << (uint32) location;
|
||||
|
||||
enabledattribs |= bit;
|
||||
|
||||
if (attrib.second.step == STEP_PER_INSTANCE)
|
||||
instancedattribs |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
// Not supported on all platforms or GL versions, I believe.
|
||||
if (!(enabledattribs & ATTRIBFLAG_POS))
|
||||
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
||||
|
||||
gl.useVertexAttribArrays(enabledattribs, instancedattribs);
|
||||
|
||||
gl.useVertexAttribArrays(attribflags, instancedattribflags);
|
||||
gl.bindTextureToUnit(texture, 0, false);
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
gl.prepareDraw();
|
||||
|
||||
if (useIndexBuffer && ibo && elementCount > 0)
|
||||
GLenum gldrawmode = getGLDrawMode(drawMode);
|
||||
|
||||
if (useindexbuffer)
|
||||
{
|
||||
// Use the custom vertex map (index buffer) to draw the vertices.
|
||||
gl.bindBuffer(BUFFER_INDEX, (GLuint) ibo->getHandle());
|
||||
|
||||
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
|
||||
ibo->unmap();
|
||||
|
||||
int start = std::min(std::max(0, rangeStart), (int) elementCount - 1);
|
||||
|
||||
int count = (int) elementCount;
|
||||
if (rangeCount > 0)
|
||||
count = std::min(count, rangeCount);
|
||||
|
||||
count = std::min(count, (int) elementCount - start);
|
||||
|
||||
size_t elementsize = vertex::getIndexDataSize(elementDataType);
|
||||
const void *indices = BUFFER_OFFSET(start * elementsize);
|
||||
GLenum type = OpenGL::getGLIndexDataType(elementDataType);
|
||||
|
||||
if (count > 0)
|
||||
gl.drawElements(getGLDrawMode(drawMode), count, type, indices, instancecount);
|
||||
gl.bindBuffer(BUFFER_INDEX, (GLuint) ibo->getHandle());
|
||||
gl.drawElements(gldrawmode, count, type, indices, instancecount);
|
||||
}
|
||||
else
|
||||
{
|
||||
int start = std::min(std::max(0, rangeStart), (int) vertexCount - 1);
|
||||
|
||||
int count = (int) vertexCount;
|
||||
if (rangeCount > 0)
|
||||
count = std::min(count, rangeCount);
|
||||
|
||||
count = std::min(count, (int) vertexCount - start);
|
||||
|
||||
// Normal non-indexed drawing (no custom vertex map.)
|
||||
if (count > 0)
|
||||
gl.drawArrays(getGLDrawMode(drawMode), start, count, instancecount);
|
||||
gl.drawArrays(gldrawmode, start, count, instancecount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ public:
|
||||
virtual ~Mesh();
|
||||
|
||||
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) override;
|
||||
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) override;
|
||||
|
||||
protected:
|
||||
|
||||
void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -50,21 +50,10 @@ ParticleSystem *ParticleSystem::clone()
|
||||
return new ParticleSystem(*this);
|
||||
}
|
||||
|
||||
void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
void ParticleSystem::drawInternal() const
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (!prepareDraw(gfx))
|
||||
return;
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
if (Shader::current && texture.get())
|
||||
Shader::current->checkMainTexture(texture);
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
|
||||
|
||||
gl.bindTextureToUnit(texture, 0, false);
|
||||
|
||||
@@ -44,7 +44,10 @@ public:
|
||||
virtual ~ParticleSystem();
|
||||
|
||||
ParticleSystem *clone() override;
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
private:
|
||||
|
||||
void drawInternal() const override;
|
||||
|
||||
}; // ParticleSystem
|
||||
|
||||
|
||||
@@ -51,52 +51,15 @@ SpriteBatch::~SpriteBatch()
|
||||
{
|
||||
}
|
||||
|
||||
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
void SpriteBatch::drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount)
|
||||
{
|
||||
using namespace vertex;
|
||||
|
||||
if (next == 0)
|
||||
return;
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (texture.get())
|
||||
{
|
||||
if (Shader::isDefaultActive())
|
||||
{
|
||||
Shader::StandardShader defaultshader = Shader::STANDARD_DEFAULT;
|
||||
if (texture->getTextureType() == TEXTURE_2D_ARRAY)
|
||||
defaultshader = Shader::STANDARD_ARRAY;
|
||||
|
||||
Shader::attachDefault(defaultshader);
|
||||
}
|
||||
|
||||
if (Shader::current)
|
||||
Shader::current->checkMainTexture(texture);
|
||||
}
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
gl.bindTextureToUnit(texture, 0, false);
|
||||
|
||||
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
||||
array_buf->unmap();
|
||||
|
||||
CommonFormat format = vertex_format;
|
||||
|
||||
if (!color_active)
|
||||
{
|
||||
if (format == CommonFormat::XYf_STPf_RGBAub)
|
||||
format = CommonFormat::XYf_STPf;
|
||||
else
|
||||
format = CommonFormat::XYf_STf;
|
||||
}
|
||||
|
||||
uint32 enabledattribs = getFormatFlags(format);
|
||||
|
||||
gl.setVertexPointers(format, array_buf, format_stride, 0);
|
||||
// We want attached attributes to override local attributes, so we should
|
||||
// call this before binding attached attributes.
|
||||
gl.setVertexPointers(format, array_buf, vertex_stride, 0);
|
||||
|
||||
for (const auto &it : attached_attributes)
|
||||
{
|
||||
@@ -114,26 +77,16 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
}
|
||||
|
||||
gl.useVertexAttribArrays(enabledattribs);
|
||||
gl.bindTextureToUnit(texture, 0, false);
|
||||
|
||||
gl.prepareDraw();
|
||||
|
||||
int start = std::min(std::max(0, range_start), next - 1);
|
||||
gl.bindBuffer(BUFFER_INDEX, (GLuint) quad_indices.getBuffer()->getHandle());
|
||||
|
||||
int count = next;
|
||||
if (range_count > 0)
|
||||
count = std::min(count, range_count);
|
||||
const void *indices = BUFFER_OFFSET(indexbytestart);
|
||||
GLenum gltype = OpenGL::getGLIndexDataType(quad_indices.getType());
|
||||
|
||||
count = std::min(count, next - start);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
gl.bindBuffer(BUFFER_INDEX, (GLuint) quad_indices.getBuffer()->getHandle());
|
||||
|
||||
const void *indices = BUFFER_OFFSET(start * quad_indices.getElementSize());
|
||||
GLenum gltype = OpenGL::getGLIndexDataType(quad_indices.getType());
|
||||
|
||||
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(count), gltype, indices);
|
||||
}
|
||||
gl.drawElements(GL_TRIANGLES, (GLsizei) indexcount, gltype, indices);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -37,8 +37,9 @@ public:
|
||||
SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
|
||||
virtual ~SpriteBatch();
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
protected:
|
||||
|
||||
void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) override;
|
||||
|
||||
}; // SpriteBatch
|
||||
|
||||
|
||||
@@ -40,36 +40,10 @@ Text::~Text()
|
||||
{
|
||||
}
|
||||
|
||||
void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
void Text::drawInternal(const std::vector<Font::DrawCommand> &commands) const
|
||||
{
|
||||
if (vbo == nullptr || draw_commands.empty())
|
||||
return;
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
if (Shader::current)
|
||||
Shader::current->checkMainTextureType(TEXTURE_2D, false);
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("Text object draw");
|
||||
|
||||
// Re-generate the text if the Font's texture cache was invalidated.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
regenerateVertices();
|
||||
|
||||
int totalverts = 0;
|
||||
for (const Font::DrawCommand &cmd : draw_commands)
|
||||
totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
|
||||
|
||||
if ((size_t) totalverts / 4 > quadIndices.getSize())
|
||||
quadIndices = QuadIndices(gfx, (size_t) totalverts / 4);
|
||||
|
||||
vbo->unmap(); // Make sure all pending data is flushed to the GPU.
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
gl.prepareDraw();
|
||||
|
||||
gl.setVertexPointers(Font::vertexFormat, vbo, 0);
|
||||
@@ -82,14 +56,12 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
|
||||
// We need a separate draw call for every section of the text which uses a
|
||||
// different texture than the previous section.
|
||||
for (const Font::DrawCommand &cmd : draw_commands)
|
||||
for (const Font::DrawCommand &cmd : commands)
|
||||
{
|
||||
GLsizei count = (cmd.vertexcount / 4) * 6;
|
||||
size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
|
||||
|
||||
// TODO: Use glDrawElementsBaseVertex when supported?
|
||||
gl.bindTextureToUnit(cmd.texture, 0, false);
|
||||
|
||||
gl.drawElements(GL_TRIANGLES, count, gltype, BUFFER_OFFSET(offset));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,9 @@ public:
|
||||
Text(love::graphics::Graphics *gfx, love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||
virtual ~Text();
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(love::graphics::Graphics *gfx, const Matrix4 &m) override;
|
||||
protected:
|
||||
|
||||
void drawInternal(const std::vector<Font::DrawCommand> &commands) const override;
|
||||
|
||||
}; // Text
|
||||
|
||||
|
||||
Reference in New Issue
Block a user