Add a new “stencil8” pixel format for Canvases (resolves issue #1003).

Also fixed Canvas MSAA (resolves issue #1271).

stencil-formatted Canvases can’t be drawn, and can only be used as the value for a new ‘depthstencil’ field to the table-argument variant of love.graphics.setCanvas.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2017-04-11 00:00:22 -03:00
parent 08a377ff30
commit 018a3831cb
23 changed files with 518 additions and 321 deletions
+1 -1
View File
@@ -55,7 +55,7 @@ public:
virtual int getMSAA() const = 0;
virtual int getRequestedMSAA() const = 0;
virtual ptrdiff_t getMSAAHandle() const = 0;
virtual ptrdiff_t getRenderTargetHandle() const = 0;
void draw(Graphics *gfx, Quad *q, const Matrix4 &t) override;
void drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &t) override;
+42 -20
View File
@@ -187,9 +187,9 @@ int Graphics::getPixelHeight() const
double Graphics::getCurrentPixelDensity() const
{
if (states.back().renderTargets.size() > 0)
if (states.back().renderTargets.colors.size() > 0)
{
love::graphics::Canvas *c = states.back().renderTargets[0].canvas;
Canvas *c = states.back().renderTargets.colors[0].canvas;
return (double) c->getPixelHeight() / (double) c->getHeight();
}
@@ -291,19 +291,28 @@ void Graphics::restoreStateChecked(const DisplayState &s)
setFont(s.font.get());
setShader(s.shader.get());
bool canvaseschanged = s.renderTargets.size() != cur.renderTargets.size();
const auto &sRTs = s.renderTargets;
const auto &curRTs = cur.renderTargets;
bool canvaseschanged = sRTs.colors.size() != curRTs.colors.size();
if (!canvaseschanged)
{
for (size_t i = 0; i < s.renderTargets.size() && i < cur.renderTargets.size(); i++)
for (size_t i = 0; i < sRTs.colors.size() && i < curRTs.colors.size(); i++)
{
const auto &rt1 = s.renderTargets[i];
const auto &rt2 = cur.renderTargets[i];
const auto &rt1 = sRTs.colors[i];
const auto &rt2 = curRTs.colors[i];
if (rt1.canvas.get() != rt2.canvas.get() || rt1.slice != rt2.slice)
{
canvaseschanged = true;
break;
}
}
if (!canvaseschanged && (sRTs.depthStencil.canvas.get() != curRTs.depthStencil.canvas.get()
|| sRTs.depthStencil.slice != curRTs.depthStencil.slice))
{
canvaseschanged = true;
}
}
if (canvaseschanged)
@@ -401,45 +410,58 @@ void Graphics::setCanvas(RenderTarget rt)
if (rt.canvas == nullptr)
return setCanvas();
std::vector<RenderTarget> rts = {rt};
RenderTargets rts;
rts.colors.push_back(rt);
setCanvas(rts);
}
void Graphics::setCanvas(const std::vector<RenderTargetStrongRef> &rts)
void Graphics::setCanvas(const RenderTargetsStrongRef &rts)
{
std::vector<RenderTarget> canvaslist;
canvaslist.reserve(rts.size());
RenderTargets targets;
targets.colors.reserve(rts.colors.size());
for (const auto &rt : rts)
canvaslist.emplace_back(rt.canvas.get(), rt.slice);
for (const auto &rt : rts.colors)
targets.colors.emplace_back(rt.canvas.get(), rt.slice);
return setCanvas(canvaslist);
targets.depthStencil = RenderTarget(rts.depthStencil.canvas, rts.depthStencil.slice);
return setCanvas(targets);
}
std::vector<Graphics::RenderTarget> Graphics::getCanvas() const
Graphics::RenderTargets Graphics::getCanvas() const
{
std::vector<RenderTarget> rts;
rts.reserve(states.back().renderTargets.size());
const auto &curRTs = states.back().renderTargets;
for (const auto &rt : states.back().renderTargets)
rts.emplace_back(rt.canvas.get(), rt.slice);
RenderTargets rts;
rts.colors.reserve(curRTs.colors.size());
for (const auto &rt : curRTs.colors)
rts.colors.emplace_back(rt.canvas.get(), rt.slice);
rts.depthStencil = RenderTarget(curRTs.depthStencil.canvas, curRTs.depthStencil.slice);
return rts;
}
bool Graphics::isCanvasActive() const
{
return !states.back().renderTargets.empty();
return !states.back().renderTargets.colors.empty();
}
bool Graphics::isCanvasActive(love::graphics::Canvas *canvas) const
{
for (const auto &rt : states.back().renderTargets)
const auto &curRTs = states.back().renderTargets;
for (const auto &rt : curRTs.colors)
{
if (rt.canvas.get() == canvas)
return true;
}
if (curRTs.depthStencil.canvas.get() == canvas)
return true;
return false;
}
+31 -6
View File
@@ -315,13 +315,18 @@ public:
struct RenderTarget
{
Canvas *canvas = nullptr;
int slice = 0;
Canvas *canvas;
int slice;
RenderTarget(Canvas *canvas, int slice = 0)
: canvas(canvas)
, slice(slice)
{}
RenderTarget()
: canvas(nullptr)
, slice(0)
{}
};
struct RenderTargetStrongRef
@@ -335,6 +340,26 @@ public:
{}
};
struct RenderTargets
{
std::vector<RenderTarget> colors;
RenderTarget depthStencil;
RenderTargets()
: depthStencil(nullptr)
{}
};
struct RenderTargetsStrongRef
{
std::vector<RenderTargetStrongRef> colors;
RenderTargetStrongRef depthStencil;
RenderTargetsStrongRef()
: depthStencil(nullptr)
{}
};
Graphics();
virtual ~Graphics();
@@ -456,11 +481,11 @@ public:
Shader *getShader() const;
void setCanvas(RenderTarget rt);
virtual void setCanvas(const std::vector<RenderTarget> &rts) = 0;
void setCanvas(const std::vector<RenderTargetStrongRef> &rts);
virtual void setCanvas(const RenderTargets &rts) = 0;
void setCanvas(const RenderTargetsStrongRef &rts);
virtual void setCanvas() = 0;
std::vector<RenderTarget> getCanvas() const;
RenderTargets getCanvas() const;
bool isCanvasActive() const;
bool isCanvasActive(Canvas *canvas) const;
@@ -813,7 +838,7 @@ protected:
StrongRef<Font> font;
StrongRef<Shader> shader;
std::vector<RenderTargetStrongRef> renderTargets;
RenderTargetsStrongRef renderTargets;
ColorMask colorMask = ColorMask(true, true, true, true);
+9 -1
View File
@@ -206,6 +206,14 @@ void Shader::checkMainTextureType(TextureType textype) const
}
}
void Shader::checkMainTexture(Texture *texture) const
{
if (!texture->isReadable())
throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader.");
checkMainTextureType(texture->getTextureType());
}
bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err)
{
if (source.vertex.empty() && source.pixel.empty())
@@ -242,7 +250,7 @@ bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool
if (!s.parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
{
const char *stagename;
const char *stagename = "unknown";
getConstant(stage, stagename);
err = "Error validating " + std::string(stagename) + " shader:\n\n"
+ std::string(s.getInfoLog()) + "\n" + std::string(s.getInfoDebugLog());
+1
View File
@@ -190,6 +190,7 @@ public:
TextureType getMainTextureType() const;
void checkMainTextureType(TextureType textype) const;
void checkMainTexture(Texture *texture) const;
virtual ptrdiff_t getHandle() const = 0;
+12
View File
@@ -51,6 +51,7 @@ float Texture::defaultMipmapSharpness = 0.0f;
Texture::Texture(TextureType texType)
: texType(texType)
, format(PIXELFORMAT_UNKNOWN)
, readable(true)
, width(0)
, height(0)
, depth(1)
@@ -84,6 +85,11 @@ PixelFormat Texture::getPixelFormat() const
return format;
}
bool Texture::isReadable() const
{
return readable;
}
void Texture::draw(Graphics *gfx, const Matrix4 &m)
{
draw(gfx, quad, m);
@@ -93,6 +99,9 @@ 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.");
if (texType == TEXTURE_2D_ARRAY)
{
drawLayer(gfx, q->getLayer(), q, localTransform);
@@ -132,6 +141,9 @@ 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.");
if (texType != TEXTURE_2D_ARRAY)
throw love::Exception("drawLayer can only be used with Array Textures!");
+3
View File
@@ -120,6 +120,8 @@ public:
TextureType getTextureType() const;
PixelFormat getPixelFormat() const;
bool isReadable() const;
int getWidth() const;
int getHeight() const;
int getDepth() const;
@@ -166,6 +168,7 @@ protected:
TextureType texType;
PixelFormat format;
bool readable;
int width;
int height;
+121 -86
View File
@@ -69,8 +69,9 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, GLuint texture
return status;
}
static bool createMSAABuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer)
static bool createRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer)
{
int reqsamples = samples;
bool unusedSRGB = false;
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, true, unusedSRGB);
@@ -84,16 +85,23 @@ static bool createMSAABuffer(int width, int height, int &samples, PixelFormat pi
glGenRenderbuffers(1, &buffer);
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, fmt.internalformat, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, buffer);
if (samples > 1)
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, fmt.internalformat, width, height);
else
glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, width, height);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, fmt.framebufferAttachments[0], GL_RENDERBUFFER, buffer);
if (samples > 1)
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
else
samples = 0;
glBindRenderbuffer(GL_RENDERBUFFER, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE && samples > 1)
if (status == GL_FRAMEBUFFER_COMPLETE && (reqsamples <= 1 || samples > 1))
{
// Initialize the buffer to transparent black.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -109,16 +117,17 @@ static bool createMSAABuffer(int width, int height, int &samples, PixelFormat pi
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
gl.deleteFramebuffer(fbo);
return status == GL_FRAMEBUFFER_COMPLETE && samples > 1;
return status == GL_FRAMEBUFFER_COMPLETE;
}
Canvas::Canvas(const Settings &settings)
: love::graphics::Canvas(settings.type)
, fbo(0)
, texture(0)
, msaa_buffer(0)
, actual_samples(0)
, texture_memory(0)
, renderbuffer(0)
, requestedSamples(settings.msaa)
, actualSamples(0)
, textureMemory(0)
{
this->width = settings.width;
this->height = settings.height;
@@ -140,6 +149,8 @@ Canvas::Canvas(const Settings &settings)
this->format = getSizedFormat(settings.format);
readable = this->format != PIXELFORMAT_STENCIL8;
initQuad();
loadVolatile();
@@ -167,9 +178,12 @@ bool Canvas::loadVolatile()
throw love::Exception("The %s canvas format is not supported by your OpenGL drivers.", fstr);
}
if (settings.msaa > 1 && texType != TEXTURE_2D)
if (requestedSamples > 1 && texType != TEXTURE_2D)
throw love::Exception("MSAA is only supported for 2D texture types.");
if (!readable && texType != TEXTURE_2D)
throw love::Exception("Non-readable pixel formats are only supported for 2D texture types.");
if (!gl.isTextureTypeSupported(texType))
{
const char *textypestr = "unknown";
@@ -214,68 +228,74 @@ bool Canvas::loadVolatile()
OpenGL::TempDebugGroup debuggroup("Canvas load");
fbo = texture = 0;
msaa_buffer = 0;
renderbuffer = 0;
status = GL_FRAMEBUFFER_COMPLETE;
if (isReadable())
{
glGenTextures(1, &texture);
gl.bindTextureToUnit(this, 0, false);
GLenum gltype = OpenGL::getGLTextureType(texType);
if (GLAD_ANGLE_texture_usage)
glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
setFilter(filter);
setWrap(wrap);
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
bool isSRGB = format == PIXELFORMAT_sRGBA8;
if (!gl.rawTexStorage(texType, 1, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers))
{
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
return false;
}
if (glGetError() != GL_NO_ERROR)
{
gl.deleteTexture(texture);
texture = 0;
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
return false;
}
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
status = createFBO(fbo, texType, texture, texType == TEXTURE_VOLUME ? depth : layers, true);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (fbo != 0)
{
gl.deleteFramebuffer(fbo);
fbo = 0;
}
return false;
}
}
// getMaxRenderbufferSamples will be 0 on systems that don't support
// multisampled renderbuffers / don't export FBO multisample extensions.
settings.msaa = std::min(settings.msaa, gl.getMaxRenderbufferSamples());
settings.msaa = std::max(settings.msaa, 0);
actualSamples = requestedSamples;
actualSamples = std::min(actualSamples, gl.getMaxRenderbufferSamples());
actualSamples = std::max(actualSamples, 0);
actualSamples = actualSamples == 1 ? 0 : actualSamples;
glGenTextures(1, &texture);
gl.bindTextureToUnit(this, 0, false);
if (!isReadable() || actualSamples > 0)
createRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer);
GLenum gltype = OpenGL::getGLTextureType(texType);
size_t prevmemsize = textureMemory;
if (GLAD_ANGLE_texture_usage)
glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
textureMemory = getPixelFormatSize(format) * pixelWidth * pixelHeight;
setFilter(filter);
setWrap(wrap);
if (actualSamples > 0 && isReadable())
textureMemory += (textureMemory * actualSamples);
else if (actualSamples > 0)
textureMemory *= actualSamples;
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
bool isSRGB = format == PIXELFORMAT_sRGBA8;
if (!gl.rawTexStorage(texType, 1, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers))
{
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
return false;
}
if (glGetError() != GL_NO_ERROR)
{
gl.deleteTexture(texture);
texture = 0;
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
return false;
}
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
status = createFBO(fbo, texType, texture, texType == TEXTURE_VOLUME ? depth : layers, true);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (fbo != 0)
{
gl.deleteFramebuffer(fbo);
fbo = 0;
}
return false;
}
actual_samples = settings.msaa == 1 ? 0 : settings.msaa;
if (actual_samples > 0 && !createMSAABuffer(pixelWidth, pixelHeight, actual_samples, format, msaa_buffer))
actual_samples = 0;
size_t prevmemsize = texture_memory;
texture_memory = getPixelFormatSize(format) * pixelWidth * pixelHeight;
if (msaa_buffer != 0)
texture_memory += (texture_memory * actual_samples);
gl.updateTextureMemorySize(prevmemsize, texture_memory);
gl.updateTextureMemorySize(prevmemsize, textureMemory);
return true;
}
@@ -285,18 +305,18 @@ void Canvas::unloadVolatile()
if (fbo != 0)
gl.deleteFramebuffer(fbo);
if (msaa_buffer != 0)
glDeleteRenderbuffers(1, &msaa_buffer);
if (renderbuffer != 0)
glDeleteRenderbuffers(1, &renderbuffer);
if (texture != 0)
gl.deleteTexture(texture);
fbo = 0;
msaa_buffer = 0;
renderbuffer = 0;
texture = 0;
gl.updateTextureMemorySize(texture_memory, 0);
texture_memory = 0;
gl.updateTextureMemorySize(textureMemory, 0);
textureMemory = 0;
}
void Canvas::setFilter(const Texture::Filter &f)
@@ -355,6 +375,9 @@ ptrdiff_t Canvas::getHandle() const
love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int x, int y, int w, int h)
{
if (!isReadable())
throw love::Exception("Canvas:newImageData cannot be called on non-readable Canvases.");
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > getPixelWidth() || (y + h) > getPixelHeight())
throw love::Exception("Invalid rectangle dimensions.");
@@ -423,7 +446,7 @@ PixelFormat Canvas::getSizedFormat(PixelFormat format)
case PIXELFORMAT_NORMAL:
if (isGammaCorrect())
return PIXELFORMAT_sRGBA8;
else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8, true, false))
else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8, true, true, false))
// 32-bit render targets don't have guaranteed support on GLES2.
return PIXELFORMAT_RGBA4;
else
@@ -456,7 +479,10 @@ bool Canvas::isFormatSupported(PixelFormat format)
bool supported = true;
format = getSizedFormat(format);
if (!OpenGL::isPixelFormatSupported(format, true, false))
bool depthstencil = isPixelFormatDepthStencil(format);
bool readable = !depthstencil;
if (!OpenGL::isPixelFormatSupported(format, true, readable, false))
return false;
if (checkedFormats[format])
@@ -466,28 +492,37 @@ bool Canvas::isFormatSupported(PixelFormat format)
// drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
// a texture to a FBO whose format the driver doesn't like. So we should
// test with an actual FBO.
if (readable)
{
GLuint texture = 0;
glGenTextures(1, &texture);
gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
GLuint texture = 0;
glGenTextures(1, &texture);
gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
Texture::Filter f;
f.min = f.mag = Texture::FILTER_NEAREST;
gl.setTextureFilter(TEXTURE_2D, f);
Texture::Filter f;
f.min = f.mag = Texture::FILTER_NEAREST;
gl.setTextureFilter(TEXTURE_2D, f);
Texture::Wrap w;
gl.setTextureWrap(TEXTURE_2D, w);
Texture::Wrap w;
gl.setTextureWrap(TEXTURE_2D, w);
bool unusedSRGB = false;
gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 2, 2);
bool unusedSRGB = false;
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, unusedSRGB);
GLuint fbo = 0;
supported = (createFBO(fbo, TEXTURE_2D, texture, 1, false) == GL_FRAMEBUFFER_COMPLETE);
gl.deleteFramebuffer(fbo);
glTexImage2D(GL_TEXTURE_2D, 0, fmt.internalformat, 2, 2, 0, fmt.externalformat, fmt.type, nullptr);
gl.deleteTexture(texture);
}
else
{
int samples = 0;
GLuint renderbuffer = 0;
supported = createRenderbuffer(2, 2, samples, format, renderbuffer);
GLuint fbo = 0;
supported = (createFBO(fbo, TEXTURE_2D, texture, 1, false) == GL_FRAMEBUFFER_COMPLETE);
gl.deleteFramebuffer(fbo);
gl.deleteTexture(texture);
if (supported)
glDeleteRenderbuffers(1, &renderbuffer);
}
// Cache the result so we don't do this for every isFormatSupported call.
checkedFormats[format] = true;
+8 -10
View File
@@ -56,20 +56,19 @@ public:
int getMSAA() const override
{
return actual_samples;
return actualSamples;
}
int getRequestedMSAA() const override
{
return settings.msaa;
return requestedSamples;
}
ptrdiff_t getMSAAHandle() const override
ptrdiff_t getRenderTargetHandle() const override
{
return msaa_buffer;
return renderbuffer != 0 ? renderbuffer : texture;
}
inline GLenum getStatus() const
{
return status;
@@ -87,18 +86,17 @@ public:
private:
Settings settings;
GLuint fbo;
GLuint texture;
GLuint msaa_buffer;
GLuint renderbuffer;
GLenum status;
int actual_samples;
int requestedSamples;
int actualSamples;
size_t texture_memory;
size_t textureMemory;
static bool supportedFormats[PIXELFORMAT_MAX_ENUM];
static bool checkedFormats[PIXELFORMAT_MAX_ENUM];
+147 -128
View File
@@ -169,7 +169,7 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
this->pixelWidth = pixelwidth;
this->pixelHeight = pixelheight;
if (states.back().renderTargets.empty())
if (!isCanvasActive())
{
// Set the viewport to top-left corner.
gl.setViewport({0, 0, pixelwidth, pixelheight});
@@ -302,8 +302,8 @@ void Graphics::unSetMode()
for (const auto &pair : framebufferObjects)
gl.deleteFramebuffer(pair.second);
for (const CachedRenderbuffer &rb : stencilBuffers)
glDeleteRenderbuffers(1, &rb.renderbuffer);
for (love::graphics::Canvas *c : stencilBuffers)
c->release();
framebufferObjects.clear();
stencilBuffers.clear();
@@ -355,6 +355,9 @@ void Graphics::flushStreamDraws()
Shader::standardShaders[Shader::STANDARD_ARRAY]->attach();
}
if (!sbstate.texture->isReadable())
throw love::Exception("Textures with non-readable formats cannot be drawn.");
if (Shader::current)
Shader::current->checkMainTextureType(textype);
}
@@ -502,28 +505,38 @@ void Graphics::setDebug(bool enable)
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
}
void Graphics::setCanvas(const std::vector<RenderTarget> &rts)
void Graphics::setCanvas(const RenderTargets &rts)
{
DisplayState &state = states.back();
int ncanvases = (int) rts.size();
int ncanvases = (int) rts.colors.size();
if (ncanvases == 0)
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.");
if (ncanvases == (int) state.renderTargets.size())
const auto &prevRTs = state.renderTargets;
if (ncanvases == (int) prevRTs.colors.size())
{
bool modified = false;
for (int i = 0; i < ncanvases; i++)
{
if (rts[i].canvas != state.renderTargets[i].canvas.get()
|| rts[i].slice != state.renderTargets[i].slice)
if (rts.colors[i].canvas != prevRTs.colors[i].canvas.get()
|| rts.colors[i].slice != prevRTs.colors[i].slice)
{
modified = true;
break;
}
}
if (!modified && rts.depthStencil.canvas == prevRTs.depthStencil.canvas
&& rts.depthStencil.slice == prevRTs.depthStencil.slice)
{
modified = true;
}
if (!modified)
return;
}
@@ -531,32 +544,53 @@ void Graphics::setCanvas(const std::vector<RenderTarget> &rts)
if (ncanvases > gl.getMaxRenderTargets())
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
love::graphics::Canvas *firstcanvas = rts[0].canvas;
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.");
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
int pixelwidth = firstcanvas->getPixelWidth();
int pixelheight = firstcanvas->getPixelHeight();
for (int i = 1; i < ncanvases; i++)
{
love::graphics::Canvas *c = rts[i].canvas;
love::graphics::Canvas *c = rts.colors[i].canvas;
PixelFormat format = c->getPixelFormat();
if (c->getPixelWidth() != pixelwidth || c->getPixelHeight() != pixelheight)
throw love::Exception("All canvases in must have the same pixel dimensions.");
throw love::Exception("All canvases must have the same pixel dimensions.");
if (!multiformatsupported && c->getPixelFormat() != firstformat)
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 in must have the same MSAA value.");
throw love::Exception("All Canvases must have the same MSAA value.");
if (c->getPixelFormat() == PIXELFORMAT_sRGBA8)
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;
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() != pixelwidth || c->getPixelHeight() != 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.");
}
OpenGL::TempDebugGroup debuggroup("setCanvas(...)");
endPass();
@@ -583,13 +617,15 @@ void Graphics::setCanvas(const std::vector<RenderTarget> &rts)
gl.setFramebufferSRGB(false);
}
std::vector<RenderTargetStrongRef> canvasrefs;
canvasrefs.reserve(rts.size());
RenderTargetsStrongRef refs;
refs.colors.reserve(rts.colors.size());
for (auto c : rts)
canvasrefs.emplace_back(c.canvas, c.slice);
for (auto c : rts.colors)
refs.colors.emplace_back(c.canvas, c.slice);
std::swap(state.renderTargets, canvasrefs);
refs.depthStencil = RenderTargetStrongRef(rts.depthStencil.canvas, rts.depthStencil.slice);
std::swap(state.renderTargets, refs);
canvasSwitchCount++;
}
@@ -598,14 +634,14 @@ void Graphics::setCanvas()
{
DisplayState &state = states.back();
if (state.renderTargets.empty())
if (state.renderTargets.colors.empty() && state.renderTargets.depthStencil.canvas == nullptr)
return;
OpenGL::TempDebugGroup debuggroup("setCanvas()");
endPass();
state.renderTargets.clear();
state.renderTargets = RenderTargetsStrongRef();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
@@ -635,21 +671,25 @@ void Graphics::endPass()
{
flushStreamDraws();
// Discard the stencil buffer.
discard({}, true);
auto &rts = states.back().renderTargets;
auto &canvases = states.back().renderTargets;
// Discard the stencil buffer if we're using an internal cached one.
if (rts.depthStencil.canvas.get() == nullptr)
discard({}, true);
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
// don't have to worry about resolving to slices.
if (canvases.size() > 0 && canvases[0].canvas->getMSAA() > 1)
if (rts.colors.size() > 0 && rts.colors[0].canvas->getMSAA() > 1)
{
int w = canvases[0].canvas->getPixelWidth();
int h = canvases[0].canvas->getPixelHeight();
int w = rts.colors[0].canvas->getPixelWidth();
int h = rts.colors[0].canvas->getPixelHeight();
for (int i = 0; i < (int) canvases.size(); i++)
for (int i = 0; i < (int) rts.colors.size(); i++)
{
Canvas *c = (Canvas *) canvases[i].canvas.get();
Canvas *c = (Canvas *) rts.colors[i].canvas.get();
if (!c->isReadable())
continue;
glReadBuffer(GL_COLOR_ATTACHMENT0 + i);
@@ -685,7 +725,7 @@ void Graphics::clear(const std::vector<OptionalColorf> &colors)
if (colors.size() == 0)
return;
int ncanvases = (int) states.back().renderTargets.size();
int ncanvases = (int) states.back().renderTargets.colors.size();
int ncolors = std::min((int) colors.size(), ncanvases);
if (ncolors <= 1 && ncanvases <= 1)
@@ -767,7 +807,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
attachments.reserve(colorbuffers.size());
// glDiscardFramebuffer uses different attachment enums for the default FBO.
if (states.back().renderTargets.empty() && gl.getDefaultFBO() == 0)
if (!isCanvasActive() && gl.getDefaultFBO() == 0)
{
if (colorbuffers.size() > 0 && colorbuffers[0])
attachments.push_back(GL_COLOR);
@@ -780,7 +820,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
}
else
{
int rendertargetcount = std::max((int) states.back().renderTargets.size(), 1);
int rendertargetcount = std::max((int) states.back().renderTargets.colors.size(), 1);
for (int i = 0; i < (int) colorbuffers.size(); i++)
{
@@ -802,11 +842,18 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
}
void Graphics::bindCachedFBO(const std::vector<RenderTarget> &targets)
void Graphics::bindCachedFBO(const RenderTargets &targets)
{
int ntargets = (int) targets.size();
uint32 hash = XXH32(&targets[0], sizeof(RenderTarget) * ntargets, 0);
RenderTarget hashtargets[MAX_COLOR_RENDER_TARGETS + 1];
int hashcount = 0;
for (int i = 0; i < (int) targets.colors.size(); i++)
hashtargets[hashcount++] = targets.colors[i];
if (targets.depthStencil.canvas != nullptr)
hashtargets[hashcount++] = targets.depthStencil;
uint32 hash = XXH32(hashtargets, sizeof(RenderTarget) * hashcount, 0);
GLuint fbo = framebufferObjects[hash];
if (fbo != 0)
@@ -815,47 +862,66 @@ void Graphics::bindCachedFBO(const std::vector<RenderTarget> &targets)
}
else
{
int w = targets[0].canvas->getPixelWidth();
int h = targets[0].canvas->getPixelHeight();
int msaa = std::max(targets[0].canvas->getMSAA(), 1);
int w = targets.colors[0].canvas->getPixelWidth();
int h = targets.colors[0].canvas->getPixelHeight();
int msaa = targets.colors[0].canvas->getMSAA();
int reqmsaa = targets.colors[0].canvas->getRequestedMSAA();
RenderTarget depthstencil = targets.depthStencil;
if (depthstencil.canvas == nullptr)
{
depthstencil.canvas = getCachedStencilBuffer(w, h, reqmsaa);
depthstencil.slice = 0;
}
glGenFramebuffers(1, &fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
int ncolortargets = 0;
GLenum drawbuffers[MAX_COLOR_RENDER_TARGETS];
for (int i = 0; i < ntargets; i++)
auto attachCanvas = [&](const RenderTarget &rt)
{
drawbuffers[i] = GL_COLOR_ATTACHMENT0 + i;
bool renderbuffer = msaa > 1 || !rt.canvas->isReadable();
bool srgb = false;
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(rt.canvas->getPixelFormat(), renderbuffer, srgb);
if (msaa > 1)
if (fmt.framebufferAttachments[0] == GL_COLOR_ATTACHMENT0)
{
GLuint rbo = (GLuint) targets[i].canvas->getMSAAHandle();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffers[i], GL_RENDERBUFFER, rbo);
fmt.framebufferAttachments[0] = GL_COLOR_ATTACHMENT0 + ncolortargets;
drawbuffers[ncolortargets] = fmt.framebufferAttachments[0];
ncolortargets++;
}
else
GLuint handle = (GLuint) rt.canvas->getRenderTargetHandle();
for (GLenum attachment : fmt.framebufferAttachments)
{
GLuint tex = (GLuint) targets[i].canvas->getHandle();
TextureType textype = targets[i].canvas->getTextureType();
if (attachment == GL_NONE)
continue;
else if (renderbuffer)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, handle);
else
{
TextureType textype = rt.canvas->getTextureType();
int layer = textype == TEXTURE_CUBE ? 0 : targets[i].slice;
int face = textype == TEXTURE_CUBE ? targets[i].slice : 0;
int layer = textype == TEXTURE_CUBE ? 0 : rt.slice;
int face = textype == TEXTURE_CUBE ? rt.slice : 0;
gl.framebufferTexture(drawbuffers[i], textype, tex, 0, layer, face);
gl.framebufferTexture(attachment, textype, handle, 0, layer, face);
}
}
}
};
if (ntargets > 1)
glDrawBuffers(ntargets, drawbuffers);
for (const auto &rt : targets.colors)
attachCanvas(rt);
GLuint stencil = attachCachedStencilBuffer(w, h, targets[0].canvas->getRequestedMSAA());
if (depthstencil.canvas != nullptr)
attachCanvas(depthstencil);
if (stencil == 0)
{
gl.deleteFramebuffer(fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
throw love::Exception("Could not create stencil buffer!");
}
if (ncolortargets > 1)
glDrawBuffers(ncolortargets, drawbuffers);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -870,80 +936,33 @@ void Graphics::bindCachedFBO(const std::vector<RenderTarget> &targets)
}
}
GLuint Graphics::attachCachedStencilBuffer(int w, int h, int samples)
love::graphics::Canvas *Graphics::getCachedStencilBuffer(int w, int h, int samples)
{
samples = samples == 1 ? 0 : samples;
love::graphics::Canvas *canvas = nullptr;
for (const CachedRenderbuffer &rb : stencilBuffers)
for (love::graphics::Canvas *c : stencilBuffers)
{
if (rb.w == w && rb.h == h && rb.samples == samples)
if (c->getPixelWidth() == w && c->getPixelHeight() == h && c->getRequestedMSAA() == samples)
{
// Attach the buffer to the framebuffer object.
for (GLenum attachment : rb.attachments)
{
if (attachment != GL_NONE)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rb.renderbuffer);
}
return rb.renderbuffer;
canvas = c;
break;
}
}
OpenGL::TempDebugGroup debuggroup("Create cached stencil buffer");
CachedRenderbuffer rb;
rb.w = w;
rb.h = h;
rb.samples = samples;
rb.attachments[0] = GL_STENCIL_ATTACHMENT;
rb.attachments[1] = GL_NONE;
GLenum format = GL_STENCIL_INDEX8;
// Prefer a combined depth/stencil buffer.
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
if (canvas == nullptr)
{
format = GL_DEPTH24_STENCIL8;
rb.attachments[0] = GL_DEPTH_STENCIL_ATTACHMENT;
}
else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
{
format = GL_DEPTH24_STENCIL8;
rb.attachments[0] = GL_DEPTH_ATTACHMENT;
rb.attachments[1] = GL_STENCIL_ATTACHMENT;
Canvas::Settings settings;
settings.format = PIXELFORMAT_STENCIL8;
settings.width = w;
settings.height = h;
settings.msaa = samples;
canvas = newCanvas(settings);
stencilBuffers.push_back(canvas);
}
glGenRenderbuffers(1, &rb.renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, rb.renderbuffer);
if (rb.samples > 1)
glRenderbufferStorageMultisample(GL_RENDERBUFFER, rb.samples, format, rb.w, rb.h);
else
glRenderbufferStorage(GL_RENDERBUFFER, format, rb.w, rb.h);
// Attach the buffer to the framebuffer object.
for (GLenum attachment : rb.attachments)
{
if (attachment != GL_NONE)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rb.renderbuffer);
}
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteRenderbuffers(1, &rb.renderbuffer);
rb.renderbuffer = 0;
}
if (rb.renderbuffer != 0)
{
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
stencilBuffers.push_back(rb);
}
return rb.renderbuffer;
return canvas;
}
void Graphics::present(void *screenshotCallbackData)
@@ -951,7 +970,7 @@ void Graphics::present(void *screenshotCallbackData)
if (!isActive())
return;
if (!states.back().renderTargets.empty())
if (isCanvasActive())
throw love::Exception("present cannot be called while a Canvas is active.");
endPass();
@@ -1083,7 +1102,7 @@ void Graphics::setScissor(const Rect &rect)
glrect.h = (int) (rect.h * density);
// OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
gl.setScissor(glrect, !state.renderTargets.empty());
gl.setScissor(glrect, isCanvasActive());
state.scissor = true;
state.scissorRect = rect;
@@ -1100,7 +1119,7 @@ void Graphics::setScissor()
void Graphics::drawToStencilBuffer(StencilAction action, int value)
{
if (states.back().renderTargets.empty() && !windowHasStencil)
if (!isCanvasActive() && !windowHasStencil)
throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
flushStreamDraws();
@@ -1161,7 +1180,7 @@ void Graphics::stopDrawToStencilBuffer()
void Graphics::setStencilTest(CompareMode compare, int value)
{
if (compare != COMPARE_ALWAYS && states.back().renderTargets.empty() && !windowHasStencil)
if (compare != COMPARE_ALWAYS && !isCanvasActive() && !windowHasStencil)
throw love::Exception("The window must have stenciling enabled to use setStencilTest on the main screen.");
DisplayState &state = states.back();
+4 -13
View File
@@ -97,7 +97,7 @@ public:
void setColor(Colorf c) override;
void setCanvas(const std::vector<RenderTarget> &rts) override;
void setCanvas(const RenderTargets &rts) override;
void setCanvas() override;
void setScissor(const Rect &rect) override;
@@ -131,26 +131,17 @@ public:
private:
struct CachedRenderbuffer
{
int w;
int h;
int samples;
GLenum attachments[2];
GLuint renderbuffer;
};
love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
void endPass();
void bindCachedFBO(const std::vector<RenderTarget> &targets);
void bindCachedFBO(const RenderTargets &targets);
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
GLuint attachCachedStencilBuffer(int w, int h, int samples);
love::graphics::Canvas *getCachedStencilBuffer(int w, int h, int samples);
void setDebug(bool enable);
std::unordered_map<uint32, GLuint> framebufferObjects;
std::vector<CachedRenderbuffer> stencilBuffers;
std::vector<love::graphics::Canvas *> stencilBuffers;
QuadIndices *quadIndices;
+2 -2
View File
@@ -237,7 +237,7 @@ bool Image::loadVolatile()
OpenGL::TempDebugGroup debuggroup("Image load");
if (!OpenGL::isPixelFormatSupported(format, false, sRGB))
if (!OpenGL::isPixelFormatSupported(format, false, true, sRGB))
{
const char *str;
if (love::getConstant(format, str))
@@ -511,7 +511,7 @@ Image::MipmapsType Image::getMipmapsType() const
bool Image::isFormatSupported(PixelFormat pixelformat)
{
return OpenGL::isPixelFormatSupported(pixelformat, false, false);
return OpenGL::isPixelFormatSupported(pixelformat, false, true, false);
}
bool Image::hasSRGBSupport()
+1 -1
View File
@@ -100,7 +100,7 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
throw love::Exception("Instancing is not supported on this system.");
if (Shader::current && texture.get())
Shader::current->checkMainTextureType(texture->getTextureType());
Shader::current->checkMainTexture(texture);
gfx->flushStreamDraws();
+33 -1
View File
@@ -1173,6 +1173,9 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
{
TextureFormat f;
f.framebufferAttachments[0] = GL_COLOR_ATTACHMENT0;
f.framebufferAttachments[1] = GL_NONE;
if (pixelformat == PIXELFORMAT_RGBA8 && isSRGB)
pixelformat = PIXELFORMAT_sRGBA8;
else if (pixelformat == PIXELFORMAT_ETC1)
@@ -1314,6 +1317,32 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
f.type = GL_UNSIGNED_INT_10F_11F_11F_REV;
break;
case PIXELFORMAT_STENCIL8:
// Prefer a combined depth/stencil buffer due to driver issues.
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
{
f.internalformat = GL_DEPTH24_STENCIL8;
f.externalformat = GL_DEPTH_STENCIL;
f.type = GL_UNSIGNED_INT_24_8;
f.framebufferAttachments[0] = GL_DEPTH_STENCIL_ATTACHMENT;
}
else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
{
f.internalformat = GL_DEPTH24_STENCIL8;
f.externalformat = GL_DEPTH_STENCIL;
f.type = GL_UNSIGNED_INT_24_8;
f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
f.framebufferAttachments[1] = GL_STENCIL_ATTACHMENT;
}
else
{
f.internalformat = GL_STENCIL_INDEX8;
f.externalformat = GL_STENCIL;
f.type = GL_UNSIGNED_BYTE;
f.framebufferAttachments[0] = GL_STENCIL_ATTACHMENT;
}
break;
case PIXELFORMAT_DXT1:
f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
break;
@@ -1454,7 +1483,7 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
return f;
}
bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget, bool isSRGB)
bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget, bool readable, bool isSRGB)
{
if (rendertarget && isPixelFormatCompressed(pixelformat))
return false;
@@ -1548,6 +1577,9 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget,
else
return GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_texture_packed_float;
case PIXELFORMAT_STENCIL8:
return rendertarget && !readable;
case PIXELFORMAT_DXT1:
return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
case PIXELFORMAT_DXT3:
+4 -1
View File
@@ -96,6 +96,9 @@ public:
GLenum externalformat = 0;
GLenum type = 0;
// For depth/stencil formats.
GLenum framebufferAttachments[2];
bool swizzled = false;
GLint swizzle[4];
};
@@ -379,7 +382,7 @@ public:
static TextureFormat convertPixelFormat(PixelFormat pixelformat, bool renderbuffer, bool &isSRGB);
static bool isTexStorageSupported();
static bool isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget, bool isSRGB);
static bool isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget, bool readable, bool isSRGB);
static bool hasTextureFilteringSupport(PixelFormat pixelformat);
static const char *errorString(GLenum errorcode);
@@ -58,7 +58,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
return;
if (Shader::current && texture.get())
Shader::current->checkMainTextureType(texture->getTextureType());
Shader::current->checkMainTexture(texture);
OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
+1 -1
View File
@@ -677,7 +677,7 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count
{
if (textures[i] != nullptr)
{
if (textures[i]->getTextureType() != info->textureType)
if (textures[i]->getTextureType() != info->textureType || !textures[i]->isReadable())
continue;
textures[i]->retain();
+1 -1
View File
@@ -76,7 +76,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
}
if (Shader::current)
Shader::current->checkMainTextureType(texture->getTextureType());
Shader::current->checkMainTexture(texture);
}
OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
+9 -3
View File
@@ -57,11 +57,14 @@ int w_Canvas_renderTo(lua_State *L)
if (graphics)
{
// Save the current render targets so we can restore them when we're done.
std::vector<Graphics::RenderTarget> oldtargets = graphics->getCanvas();
Graphics::RenderTargets oldtargets = graphics->getCanvas();
for (auto c : oldtargets)
for (auto c : oldtargets.colors)
c.canvas->retain();
if (oldtargets.depthStencil.canvas != nullptr)
oldtargets.depthStencil.canvas->retain();
luax_catchexcept(L, [&](){ graphics->setCanvas(rt); });
lua_settop(L, 2); // make sure the function is on top of the stack
@@ -69,9 +72,12 @@ int w_Canvas_renderTo(lua_State *L)
graphics->setCanvas(oldtargets);
for (auto c : oldtargets)
for (auto c : oldtargets.colors)
c.canvas->release();
if (oldtargets.depthStencil.canvas != nullptr)
oldtargets.depthStencil.canvas->release();
if (status != 0)
return lua_error(L);
}
+63 -45
View File
@@ -124,7 +124,7 @@ int w_discard(lua_State *L)
else
{
bool discardcolor = luax_optboolean(L, 1, true);
size_t numbuffers = std::max((size_t) 1, instance()->getCanvas().size());
size_t numbuffers = std::max((size_t) 1, instance()->getCanvas().colors.size());
colorbuffers = std::vector<bool>(numbuffers, discardcolor);
}
@@ -201,6 +201,21 @@ int w_getPixelDensity(lua_State *L)
return 1;
}
static Graphics::RenderTarget checkRenderTarget(lua_State *L, int idx)
{
lua_rawgeti(L, idx, 1);
Graphics::RenderTarget target(luax_checkcanvas(L, -1), 0);
lua_pop(L, 1);
TextureType type = target.canvas->getTextureType();
if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
target.slice = luax_checkintflag(L, idx, "layer") - 1;
else if (type == TEXTURE_CUBE)
target.slice = luax_checkintflag(L, idx, "face") - 1;
return target;
}
int w_setCanvas(lua_State *L)
{
// Disable stencil writes.
@@ -214,7 +229,7 @@ int w_setCanvas(lua_State *L)
}
bool is_table = lua_istable(L, 1);
std::vector<Graphics::RenderTarget> targets;
Graphics::RenderTargets targets;
if (is_table)
{
@@ -227,29 +242,24 @@ int w_setCanvas(lua_State *L)
lua_rawgeti(L, 1, i);
if (table_of_tables)
{
lua_rawgeti(L, -1, 1);
Graphics::RenderTarget target(luax_checkcanvas(L, -1), 0);
lua_pop(L, 1);
TextureType type = target.canvas->getTextureType();
if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
target.slice = luax_checkintflag(L, -1, "layer") - 1;
else if (type == TEXTURE_CUBE)
target.slice = luax_checkintflag(L, -1, "face") - 1;
targets.push_back(target);
}
targets.colors.push_back(checkRenderTarget(L, -1));
else
{
targets.emplace_back(luax_checkcanvas(L, -1), 0);
targets.colors.emplace_back(luax_checkcanvas(L, -1), 0);
if (targets.back().canvas->getTextureType() != TEXTURE_2D)
if (targets.colors.back().canvas->getTextureType() != TEXTURE_2D)
return luaL_error(L, "The table-of-tables variant of setCanvas must be used with non-2D Canvases.");
}
lua_pop(L, 1);
}
lua_getfield(L, 1, "depthstencil");
if (lua_istable(L, -1))
targets.depthStencil = checkRenderTarget(L, -1);
else if (!lua_isnoneornil(L, -1))
targets.depthStencil.canvas = luax_checkcanvas(L, -1);
lua_pop(L, 1);
}
else
{
@@ -261,19 +271,19 @@ int w_setCanvas(lua_State *L)
if (i == 1 && type != TEXTURE_2D)
{
target.slice = (int) luaL_checknumber(L, 2) - 1;
targets.push_back(target);
targets.colors.push_back(target);
break;
}
if (i > 1 && type != TEXTURE_2D)
return luaL_error(L, "This variant of setCanvas only supports 2D texture types.");
targets.push_back(target);
targets.colors.push_back(target);
}
}
luax_catchexcept(L, [&]() {
if (targets.size() > 0)
if (targets.colors.size() > 0)
instance()->setCanvas(targets);
else
instance()->setCanvas();
@@ -282,10 +292,31 @@ int w_setCanvas(lua_State *L)
return 0;
}
static void pushRenderTarget(lua_State *L, const Graphics::RenderTarget &rt)
{
lua_createtable(L, 1, 1);
luax_pushtype(L, rt.canvas);
lua_rawseti(L, -2, 1);
TextureType type = rt.canvas->getTextureType();
if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
{
lua_pushnumber(L, rt.slice + 1);
lua_setfield(L, -2, "layer");
}
else if (type == TEXTURE_VOLUME)
{
lua_pushnumber(L, rt.slice + 1);
lua_setfield(L, -2, "face");
}
}
int w_getCanvas(lua_State *L)
{
const std::vector<Graphics::RenderTarget> targets = instance()->getCanvas();
int ntargets = (int) targets.size();
Graphics::RenderTargets targets = instance()->getCanvas();
int ntargets = (int) targets.colors.size();
if (ntargets == 0)
{
@@ -294,7 +325,7 @@ int w_getCanvas(lua_State *L)
}
bool hasNon2DTextureType = false;
for (const auto &rt : targets)
for (const auto &rt : targets.colors)
{
if (rt.canvas->getTextureType() != TEXTURE_2D)
{
@@ -303,40 +334,27 @@ int w_getCanvas(lua_State *L)
}
}
if (hasNon2DTextureType)
if (hasNon2DTextureType || targets.depthStencil.canvas != nullptr)
{
lua_createtable(L, ntargets, 0);
for (int i = 0; i < ntargets; i++)
{
const auto &rt = targets[i];
lua_createtable(L, 1, 1);
luax_pushtype(L, rt.canvas);
lua_rawseti(L, -2, 1);
TextureType type = rt.canvas->getTextureType();
if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
{
lua_pushnumber(L, rt.slice + 1);
lua_setfield(L, -2, "layer");
}
else if (type == TEXTURE_VOLUME)
{
lua_pushnumber(L, rt.slice + 1);
lua_setfield(L, -2, "face");
}
pushRenderTarget(L, targets.colors[i]);
lua_rawseti(L, -2, i + 1);
}
if (targets.depthStencil.canvas != nullptr)
{
pushRenderTarget(L, targets.depthStencil);
lua_setfield(L, -2, "depthstencil");
}
return 1;
}
else
{
for (const auto &rt : targets)
for (const auto &rt : targets.colors)
luax_pushtype(L, rt.canvas);
return ntargets;
+8
View File
@@ -237,6 +237,13 @@ int w_Texture_getFormat(lua_State *L)
return 1;
}
int w_Texture_isReadable(lua_State *L)
{
Texture *t = luax_checktexture(L, 1);
luax_pushboolean(L, t->isReadable());
return 1;
}
const luaL_Reg w_Texture_functions[] =
{
{ "getTextureType", w_Texture_getTextureType },
@@ -257,6 +264,7 @@ const luaL_Reg w_Texture_functions[] =
{ "setWrap", w_Texture_setWrap },
{ "getWrap", w_Texture_getWrap },
{ "getFormat", w_Texture_getFormat },
{ "isReadable", w_Texture_isReadable },
{ 0, 0 }
};