love.graphics: move more platform-independent code out of the opengl backend

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2017-07-22 23:34:39 -03:00
parent b568e267eb
commit 864147c38c
22 changed files with 368 additions and 316 deletions
+5 -118
View File
@@ -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());
+1 -1
View File
@@ -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;
+8 -74
View File
@@ -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);
}
}
+4 -1
View File
@@ -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:
+1 -12
View File
@@ -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);
+4 -1
View File
@@ -44,7 +44,10 @@ public:
virtual ~ParticleSystem();
ParticleSystem *clone() override;
void draw(Graphics *gfx, const Matrix4 &m) override;
private:
void drawInternal() const override;
}; // ParticleSystem
+9 -56
View File
@@ -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
+3 -2
View File
@@ -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
+2 -30
View File
@@ -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));
}
}
+3 -2
View File
@@ -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