Move some Canvas code to Texture, devirtualize Texture::draw.

This commit is contained in:
Alex Szpakowski
2020-02-03 19:02:58 -04:00
parent f9248d478c
commit b46b3ed552
19 changed files with 136 additions and 128 deletions
+6 -25
View File
@@ -27,13 +27,15 @@ namespace graphics
{
love::Type Canvas::type("Canvas", &Texture::type);
int Canvas::canvasCount = 0;
Canvas::Canvas(const Settings &settings)
: Texture(settings.type)
{
this->settings = settings;
renderTarget = true;
sRGB = false;
width = settings.width;
height = settings.height;
pixelWidth = (int) ((width * settings.dpiScale) + 0.5);
@@ -69,7 +71,7 @@ Canvas::Canvas(const Settings &settings)
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
const Graphics::Capabilities &caps = gfx->getCapabilities();
if (!gfx->isPixelFormatSupported(format, true, readable, false))
if (!gfx->isPixelFormatSupported(format, renderTarget, readable, sRGB))
{
const char *fstr = "rgba8";
const char *readablestr = "";
@@ -93,13 +95,10 @@ Canvas::Canvas(const Settings &settings)
}
validateDimensions(true);
canvasCount++;
}
Canvas::~Canvas()
{
canvasCount--;
}
Canvas::MipmapMode Canvas::getMipmapMode() const
@@ -131,12 +130,10 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
}
Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr && gfx->isCanvasActive(this))
if (gfx != nullptr && gfx->isRenderTargetActive(this))
throw love::Exception("Canvas:newImageData cannot be called while that Canvas is currently active.");
PixelFormat dataformat = getPixelFormat();
if (dataformat == PIXELFORMAT_sRGBA8_UNORM)
dataformat = PIXELFORMAT_RGBA8_UNORM;
PixelFormat dataformat = getLinearPixelFormat(getPixelFormat());
if (!image::ImageData::validPixelFormat(dataformat))
{
@@ -148,22 +145,6 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
return module->newImageData(r.w, r.h, dataformat);
}
void Canvas::draw(Graphics *gfx, Quad *q, const Matrix4 &t)
{
if (gfx->isCanvasActive(this))
throw love::Exception("Cannot render a Canvas to itself!");
Texture::draw(gfx, q, t);
}
void Canvas::drawLayer(Graphics *gfx, int layer, Quad *quad, const Matrix4 &m)
{
if (gfx->isCanvasActive(this, layer))
throw love::Exception("Cannot render a Canvas to itself!");
Texture::drawLayer(gfx, layer, quad, m);
}
bool Canvas::getConstant(const char *in, MipmapMode &out)
{
return mipmapModes.find(in, out);
-6
View File
@@ -81,16 +81,10 @@ public:
int getRequestedMSAA() const;
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
virtual void generateMipmaps() = 0;
virtual int getMSAA() 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;
static int canvasCount;
static bool getConstant(const char *in, MipmapMode &out);
static bool getConstant(MipmapMode in, const char *&out);
static std::vector<std::string> getConstants(MipmapMode);
+7 -8
View File
@@ -761,33 +761,33 @@ bool Graphics::isCanvasActive() const
return !rts.colors.empty() || rts.depthStencil.canvas != nullptr;
}
bool Graphics::isCanvasActive(love::graphics::Canvas *canvas) const
bool Graphics::isRenderTargetActive(Texture *texture) const
{
const auto &rts = states.back().renderTargets;
for (const auto &rt : rts.colors)
{
if (rt.canvas.get() == canvas)
if (rt.canvas.get() == texture)
return true;
}
if (rts.depthStencil.canvas.get() == canvas)
if (rts.depthStencil.canvas.get() == texture)
return true;
return false;
}
bool Graphics::isCanvasActive(Canvas *canvas, int slice) const
bool Graphics::isRenderTargetActive(Texture *texture, int slice) const
{
const auto &rts = states.back().renderTargets;
for (const auto &rt : rts.colors)
{
if (rt.canvas.get() == canvas && rt.slice == slice)
if (rt.canvas.get() == texture && rt.slice == slice)
return true;
}
if (rts.depthStencil.canvas.get() == canvas && rts.depthStencil.slice == slice)
if (rts.depthStencil.canvas.get() == texture && rts.depthStencil.slice == slice)
return true;
return false;
@@ -1593,8 +1593,7 @@ Graphics::Stats Graphics::getStats() const
stats.canvasSwitches = canvasSwitchCount;
stats.drawCallsBatched = drawCallsBatched;
stats.canvases = Canvas::canvasCount;
stats.images = Image::imageCount;
stats.textures = Texture::textureCount;
stats.fonts = Font::fontCount;
stats.textureMemory = Texture::totalGraphicsMemory;
+4 -5
View File
@@ -202,8 +202,7 @@ public:
int drawCallsBatched;
int canvasSwitches;
int shaderSwitches;
int canvases;
int images;
int textures;
int fonts;
int64 textureMemory;
};
@@ -551,8 +550,8 @@ public:
RenderTargets getCanvas() const;
bool isCanvasActive() const;
bool isCanvasActive(Canvas *canvas) const;
bool isCanvasActive(Canvas *canvas, int slice) const;
bool isRenderTargetActive(Texture *texture) const;
bool isRenderTargetActive(Texture *texture, int slice) const;
/**
* Scissor defines a box such that everything outside that box is discarded
@@ -781,7 +780,7 @@ public:
/**
* Converts PIXELFORMAT_NORMAL and PIXELFORMAT_HDR into a real format.
**/
virtual PixelFormat getSizedFormat(PixelFormat format) const = 0;
virtual PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable, bool sRGB) const = 0;
/**
* Gets whether the specified pixel format is supported.
+3 -17
View File
@@ -31,15 +31,14 @@ namespace graphics
love::Type Image::type("Image", &Texture::type);
int Image::imageCount = 0;
Image::Image(const Slices &data, const Settings &settings, bool validatedata)
: Texture(data.getTextureType())
, settings(settings)
, mipmapsType(settings.mipmaps ? MIPMAPS_GENERATED : MIPMAPS_NONE)
, sRGB(isGammaCorrect() && !settings.linear)
, usingDefaultTexture(false)
{
renderTarget = false;
sRGB = isGammaCorrect() && !settings.linear;
if (validatedata && data.validate() && data.getMipmapCount() > 1)
mipmapsType = MIPMAPS_DATA;
}
@@ -72,13 +71,12 @@ Image::Image(const Slices &slices, const Settings &settings)
Image::~Image()
{
--imageCount;
}
void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
{
Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr && !gfx->isPixelFormatSupported(fmt, false, true, sRGB))
if (gfx != nullptr && !gfx->isPixelFormatSupported(fmt, renderTarget, readable, sRGB))
{
const char *str;
if (love::getConstant(fmt, str))
@@ -104,8 +102,6 @@ void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
mipmapCount = mipmapsType == MIPMAPS_NONE ? 1 : getTotalMipmapCount(w, h, depth);
initQuad();
++imageCount;
}
void Image::uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y)
@@ -172,16 +168,6 @@ void Image::replacePixels(const void *data, size_t size, int slice, int mipmap,
generateMipmaps();
}
bool Image::isCompressed() const
{
return isPixelFormatCompressed(format);
}
bool Image::isFormatLinear() const
{
return isGammaCorrect() && !sRGB && format != PIXELFORMAT_sRGBA8_UNORM;
}
Image::MipmapsType Image::getMipmapsType() const
{
return mipmapsType;
-6
View File
@@ -58,11 +58,8 @@ public:
void replacePixels(const void *data, size_t size, int slice, int mipmap, const Rect &rect, bool reloadmipmaps);
bool isFormatLinear() const;
bool isCompressed() const;
MipmapsType getMipmapsType() const;
static int imageCount;
static bool getConstant(const char *in, SettingType &out);
static bool getConstant(SettingType in, const char *&out);
static const char *getConstant(SettingType in);
@@ -76,13 +73,10 @@ protected:
void uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y);
virtual void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) = 0;
virtual void generateMipmaps() = 0;
// The settings used to initialize this Image.
Settings settings;
MipmapsType mipmapsType;
bool sRGB;
// True if the image wasn't able to be properly created and it had to fall
// back to a default texture.
+26
View File
@@ -156,12 +156,15 @@ std::vector<std::string> SamplerState::getConstants(WrapMode)
}
love::Type Texture::type("Texture", &Drawable::type);
int Texture::textureCount = 0;
int64 Texture::totalGraphicsMemory = 0;
Texture::Texture(TextureType texType)
: texType(texType)
, format(PIXELFORMAT_UNKNOWN)
, renderTarget(false)
, readable(true)
, sRGB(false)
, width(0)
, height(0)
, depth(1)
@@ -175,10 +178,12 @@ Texture::Texture(TextureType texType)
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
samplerState = gfx->getDefaultSamplerState();
++textureCount;
}
Texture::~Texture()
{
--textureCount;
setGraphicsMemorySize(0);
}
@@ -207,11 +212,26 @@ PixelFormat Texture::getPixelFormat() const
return format;
}
bool Texture::isRenderTarget() const
{
return renderTarget;
}
bool Texture::isReadable() const
{
return readable;
}
bool Texture::isCompressed() const
{
return isPixelFormatCompressed(format);
}
bool Texture::isFormatLinear() const
{
return isGammaCorrect() && !sRGB && format != PIXELFORMAT_sRGBA8_UNORM;
}
bool Texture::isValidSlice(int slice) const
{
if (slice < 0)
@@ -241,6 +261,9 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
if (!readable)
throw love::Exception("Textures with non-readable formats cannot be drawn.");
if (renderTarget && gfx->isRenderTargetActive(this))
throw love::Exception("Cannot render a Texture to itself.");
if (texType == TEXTURE_2D_ARRAY)
{
drawLayer(gfx, q->getLayer(), q, localTransform);
@@ -291,6 +314,9 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
if (!readable)
throw love::Exception("Textures with non-readable formats cannot be drawn.");
if (renderTarget && gfx->isRenderTargetActive(this, layer))
throw love::Exception("Cannot render a Texture to itself.");
if (texType != TEXTURE_2D_ARRAY)
throw love::Exception("drawLayer can only be used with Array Textures!");
+12 -2
View File
@@ -126,6 +126,7 @@ class Texture : public Drawable, public Resource
public:
static love::Type type;
static int textureCount;
enum MipmapsType
{
@@ -175,16 +176,20 @@ public:
/**
* Draws the texture using the specified transformation with a Quad applied.
**/
virtual void draw(Graphics *gfx, Quad *quad, const Matrix4 &m);
void draw(Graphics *gfx, Quad *quad, const Matrix4 &m);
void drawLayer(Graphics *gfx, int layer, const Matrix4 &m);
virtual void drawLayer(Graphics *gfx, int layer, Quad *quad, const Matrix4 &m);
void drawLayer(Graphics *gfx, int layer, Quad *quad, const Matrix4 &m);
TextureType getTextureType() const;
PixelFormat getPixelFormat() const;
bool isRenderTarget() const;
bool isReadable() const;
bool isCompressed() const;
bool isFormatLinear() const;
bool isValidSlice(int slice) const;
int getWidth(int mip = 0) const;
@@ -201,6 +206,8 @@ public:
virtual void setSamplerState(const SamplerState &s);
const SamplerState &getSamplerState() const;
virtual void generateMipmaps() = 0;
Quad *getQuad() const;
static int getTotalMipmapCount(int w, int h);
@@ -220,8 +227,11 @@ protected:
TextureType texType;
PixelFormat format;
bool renderTarget;
bool readable;
bool sRGB;
int width;
int height;
+29 -38
View File
@@ -31,7 +31,7 @@ namespace graphics
namespace opengl
{
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers, int nb_mips)
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers)
{
// get currently bound fbo to reset to it later
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
@@ -60,42 +60,35 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
// Make sure all faces and layers of the texture are initialized to
// transparent black. This is unfortunately probably pretty slow for
// 2D-array and 3D textures with a lot of layers...
for (int mip = nb_mips - 1; mip >= 0; mip--)
for (int layer = layers - 1; layer >= 0; layer--)
{
int nlayers = layers;
if (texType == TEXTURE_VOLUME)
nlayers = std::max(layers >> mip, 1);
for (int layer = nlayers - 1; layer >= 0; layer--)
for (int face = faces - 1; face >= 0; face--)
{
for (int face = faces - 1; face >= 0; face--)
for (GLenum attachment : fmt.framebufferAttachments)
{
for (GLenum attachment : fmt.framebufferAttachments)
{
if (attachment == GL_NONE)
continue;
if (attachment == GL_NONE)
continue;
gl.framebufferTexture(attachment, texType, texture, mip, layer, face);
}
gl.framebufferTexture(attachment, texType, texture, 0, layer, face);
}
if (isPixelFormatDepthStencil(format))
{
bool hadDepthWrites = gl.hasDepthWrites();
if (!hadDepthWrites) // glDepthMask also affects glClear.
gl.setDepthWrites(true);
if (isPixelFormatDepthStencil(format))
{
bool hadDepthWrites = gl.hasDepthWrites();
if (!hadDepthWrites) // glDepthMask also affects glClear.
gl.setDepthWrites(true);
gl.clearDepth(1.0);
glClearStencil(0);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
gl.clearDepth(1.0);
glClearStencil(0);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (!hadDepthWrites)
gl.setDepthWrites(hadDepthWrites);
}
else
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
if (!hadDepthWrites)
gl.setDepthWrites(hadDepthWrites);
}
else
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
}
}
@@ -198,7 +191,7 @@ Canvas::Canvas(const Settings &settings)
{
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr)
format = gfx->getSizedFormat(format);
format = gfx->getSizedFormat(format, renderTarget, readable, sRGB);
initQuad();
loadVolatile();
@@ -259,8 +252,8 @@ bool Canvas::loadVolatile()
return false;
}
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers, mipmapCount);
// Create a local FBO used for glReadPixels as well as MSAA blitting.
status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
@@ -287,6 +280,9 @@ bool Canvas::loadVolatile()
setGraphicsMemorySize(memsize);
if (getMipmapCount() > 1)
generateMipmaps();
return true;
}
@@ -390,11 +386,6 @@ void Canvas::generateMipmaps()
glGenerateMipmap(gltextype);
}
bool Canvas::isMultiFormatMultiCanvasSupported()
{
return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
}
} // opengl
} // graphics
} // love
-2
View File
@@ -68,8 +68,6 @@ public:
return fbo;
}
static bool isMultiFormatMultiCanvasSupported();
private:
GLuint fbo;
+5 -5
View File
@@ -1362,7 +1362,7 @@ void Graphics::getAPIStats(int &shaderswitches) const
void Graphics::initCapabilities()
{
capabilities.features[FEATURE_MULTI_CANVAS_FORMATS] = Canvas::isMultiFormatMultiCanvasSupported();
capabilities.features[FEATURE_MULTI_CANVAS_FORMATS] = gl.isMultiFormatMRTSupported();
capabilities.features[FEATURE_CLAMP_ZERO] = gl.isClampZeroOneTextureWrapSupported();
capabilities.features[FEATURE_BLENDMINMAX] = GLAD_VERSION_1_4 || GLAD_ES_VERSION_3_0 || GLAD_EXT_blend_minmax;
capabilities.features[FEATURE_LIGHTEN] = capabilities.features[FEATURE_BLENDMINMAX];
@@ -1388,14 +1388,14 @@ void Graphics::initCapabilities()
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
}
PixelFormat Graphics::getSizedFormat(PixelFormat format) const
PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable, bool sRGB) const
{
switch (format)
{
case PIXELFORMAT_NORMAL:
if (isGammaCorrect())
return PIXELFORMAT_sRGBA8_UNORM;
else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8_UNORM, true, true, false))
else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8_UNORM, rendertarget, readable, sRGB))
// 32-bit render targets don't have guaranteed support on GLES2.
return PIXELFORMAT_RGBA4_UNORM;
else
@@ -1409,14 +1409,14 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format) const
bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB)
{
format = getSizedFormat(format);
if (sRGB && format == PIXELFORMAT_RGBA8_UNORM)
{
format = PIXELFORMAT_sRGBA8_UNORM;
sRGB = false;
}
format = getSizedFormat(format, rendertarget, readable, sRGB);
OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][sRGB ? 1 : 0];
if (supported.hasValue)
+1 -1
View File
@@ -104,7 +104,7 @@ public:
void setWireframe(bool enable) override;
PixelFormat getSizedFormat(PixelFormat format) const override;
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable, bool sRGB) const override;
bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) override;
Renderer getRenderer() const override;
RendererInfo getRendererInfo() const override;
+10 -7
View File
@@ -105,9 +105,6 @@ void Image::loadData()
if (!isCompressed())
gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers);
if (mipmapsType == MIPMAPS_GENERATED)
mipcount = 1;
int w = pixelWidth;
int h = pixelHeight;
int d = depth;
@@ -123,17 +120,23 @@ void Image::loadData()
if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)
{
for (int slice = 0; slice < slices.getSliceCount(mip); slice++)
mipsize += slices.get(slice, mip)->getSize();
{
auto id = slices.get(slice, mip);
if (id != nullptr)
mipsize += id->getSize();
}
}
GLenum gltarget = OpenGL::getGLTextureType(texType);
glCompressedTexImage3D(gltarget, mip, fmt.internalformat, w, h, d, 0, mipsize, nullptr);
if (mipsize > 0)
{
GLenum gltarget = OpenGL::getGLTextureType(texType);
glCompressedTexImage3D(gltarget, mip, fmt.internalformat, w, h, d, 0, mipsize, nullptr);
}
}
for (int slice = 0; slice < slicecount; slice++)
{
love::image::ImageDataBase *id = slices.get(slice, mip);
if (id != nullptr)
uploadImageData(id, mip, slice, 0, 0);
}
+1 -1
View File
@@ -50,11 +50,11 @@ public:
ptrdiff_t getHandle() const override;
void setSamplerState(const SamplerState &s) override;
void generateMipmaps() override;
private:
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
void generateMipmaps() override;
void loadDefaultTexture();
void loadData();
+5
View File
@@ -1328,6 +1328,11 @@ bool OpenGL::isBaseVertexSupported() const
return baseVertexSupported;
}
bool OpenGL::isMultiFormatMRTSupported() const
{
return getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
}
int OpenGL::getMax2DTextureSize() const
{
return std::max(max2DTextureSize, 1);
+1
View File
@@ -347,6 +347,7 @@ public:
bool isDepthCompareSampleSupported() const;
bool isSamplerLODBiasSupported() const;
bool isBaseVertexSupported() const;
bool isMultiFormatMRTSupported() const;
/**
* Returns the maximum supported width or height of a texture.
+2 -5
View File
@@ -2394,11 +2394,8 @@ int w_getStats(lua_State *L)
lua_pushinteger(L, stats.shaderSwitches);
lua_setfield(L, -2, "shaderswitches");
lua_pushinteger(L, stats.canvases);
lua_setfield(L, -2, "canvases");
lua_pushinteger(L, stats.images);
lua_setfield(L, -2, "images");
lua_pushinteger(L, stats.textures);
lua_setfield(L, -2, "textures");
lua_pushinteger(L, stats.fonts);
lua_setfield(L, -2, "fonts");