mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
Clean up some common Image and Canvas code.
This commit is contained in:
@@ -72,7 +72,7 @@ Canvas::Canvas(const Settings &settings)
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
const Graphics::Capabilities &caps = gfx->getCapabilities();
|
||||
|
||||
if (!gfx->isCanvasFormatSupported(format, readable))
|
||||
if (!gfx->isPixelFormatSupported(format, true, readable, false))
|
||||
{
|
||||
const char *fstr = "rgba8";
|
||||
const char *readablestr = "";
|
||||
|
||||
@@ -694,7 +694,7 @@ void Graphics::setCanvas(const RenderTargets &rts)
|
||||
PixelFormat dsformat = PIXELFORMAT_STENCIL8;
|
||||
if (wantsdepth && wantsstencil)
|
||||
dsformat = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
|
||||
else if (wantsdepth && isCanvasFormatSupported(PIXELFORMAT_DEPTH24_UNORM, false))
|
||||
else if (wantsdepth && isPixelFormatSupported(PIXELFORMAT_DEPTH24_UNORM, true, false, false))
|
||||
dsformat = PIXELFORMAT_DEPTH24_UNORM;
|
||||
else if (wantsdepth)
|
||||
dsformat = PIXELFORMAT_DEPTH16_UNORM;
|
||||
|
||||
@@ -785,12 +785,14 @@ public:
|
||||
const Capabilities &getCapabilities() const;
|
||||
|
||||
/**
|
||||
* Gets whether the specified pixel format is supported by Canvases or
|
||||
* Images.
|
||||
* Converts PIXELFORMAT_NORMAL and PIXELFORMAT_HDR into a real format.
|
||||
**/
|
||||
virtual bool isCanvasFormatSupported(PixelFormat format) const = 0;
|
||||
virtual bool isCanvasFormatSupported(PixelFormat format, bool readable) const = 0;
|
||||
virtual bool isImageFormatSupported(PixelFormat format, bool sRGB = false) const = 0;
|
||||
virtual PixelFormat getSizedFormat(PixelFormat format) const = 0;
|
||||
|
||||
/**
|
||||
* Gets whether the specified pixel format is supported.
|
||||
**/
|
||||
virtual bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) = 0;
|
||||
|
||||
/**
|
||||
* Gets the renderer used by love.graphics.
|
||||
|
||||
@@ -79,7 +79,7 @@ Image::~Image()
|
||||
void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
|
||||
{
|
||||
Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
if (gfx != nullptr && !gfx->isImageFormatSupported(fmt, sRGB))
|
||||
if (gfx != nullptr && !gfx->isPixelFormatSupported(fmt, false, true, sRGB))
|
||||
{
|
||||
const char *str;
|
||||
if (love::getConstant(fmt, str))
|
||||
|
||||
@@ -196,7 +196,9 @@ Canvas::Canvas(const Settings &settings)
|
||||
, renderbuffer(0)
|
||||
, actualSamples(0)
|
||||
{
|
||||
format = getSizedFormat(format);
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
if (gfx != nullptr)
|
||||
format = gfx->getSizedFormat(format);
|
||||
|
||||
initQuad();
|
||||
loadVolatile();
|
||||
@@ -474,153 +476,11 @@ void Canvas::generateMipmaps()
|
||||
glGenerateMipmap(gltextype);
|
||||
}
|
||||
|
||||
PixelFormat Canvas::getSizedFormat(PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case PIXELFORMAT_NORMAL:
|
||||
if (isGammaCorrect())
|
||||
return PIXELFORMAT_sRGBA8_UNORM;
|
||||
else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8_UNORM, true, true, false))
|
||||
// 32-bit render targets don't have guaranteed support on GLES2.
|
||||
return PIXELFORMAT_RGBA4_UNORM;
|
||||
else
|
||||
return PIXELFORMAT_RGBA8_UNORM;
|
||||
case PIXELFORMAT_HDR:
|
||||
return PIXELFORMAT_RGBA16_FLOAT;
|
||||
default:
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
bool Canvas::isSupported()
|
||||
{
|
||||
return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
|
||||
}
|
||||
|
||||
bool Canvas::isMultiFormatMultiCanvasSupported()
|
||||
{
|
||||
return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
|
||||
}
|
||||
|
||||
Canvas::SupportedFormat Canvas::supportedFormats[] = {};
|
||||
Canvas::SupportedFormat Canvas::checkedFormats[] = {};
|
||||
|
||||
bool Canvas::isFormatSupported(PixelFormat format)
|
||||
{
|
||||
return isFormatSupported(format, !isPixelFormatDepthStencil(format));
|
||||
}
|
||||
|
||||
bool Canvas::isFormatSupported(PixelFormat format, bool readable)
|
||||
{
|
||||
if (!isSupported())
|
||||
return false;
|
||||
|
||||
const char *fstr = "?";
|
||||
love::getConstant(format, fstr);
|
||||
|
||||
bool supported = true;
|
||||
format = getSizedFormat(format);
|
||||
|
||||
if (!OpenGL::isPixelFormatSupported(format, true, readable, false))
|
||||
return false;
|
||||
|
||||
if (checkedFormats[format].get(readable))
|
||||
return supportedFormats[format].get(readable);
|
||||
|
||||
// Even though we might have the necessary OpenGL version or extension,
|
||||
// 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.
|
||||
GLuint texture = 0;
|
||||
GLuint renderbuffer = 0;
|
||||
|
||||
// Avoid the test for depth/stencil formats - not every GL version
|
||||
// guarantees support for depth/stencil-only render targets (which we would
|
||||
// need for the test below to work), and we already do some finagling in
|
||||
// convertPixelFormat to try to use the best-supported internal
|
||||
// depth/stencil format for a particular driver.
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
{
|
||||
checkedFormats[format].set(readable, true);
|
||||
supportedFormats[format].set(readable, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unusedSRGB = false;
|
||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, readable, unusedSRGB);
|
||||
|
||||
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||
|
||||
GLuint fbo = 0;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
|
||||
|
||||
// Make sure at least something is bound to a color attachment. I believe
|
||||
// this is required on ES2 but I'm not positive.
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
|
||||
|
||||
if (readable)
|
||||
{
|
||||
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::Wrap w;
|
||||
gl.setTextureWrap(TEXTURE_2D, w);
|
||||
|
||||
unusedSRGB = false;
|
||||
gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
glGenRenderbuffers(1, &renderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
|
||||
}
|
||||
|
||||
for (GLenum attachment : fmt.framebufferAttachments)
|
||||
{
|
||||
if (attachment == GL_NONE)
|
||||
continue;
|
||||
|
||||
if (readable)
|
||||
gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
|
||||
else
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
|
||||
}
|
||||
|
||||
supported = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||
gl.deleteFramebuffer(fbo);
|
||||
|
||||
if (texture != 0)
|
||||
gl.deleteTexture(texture);
|
||||
|
||||
if (renderbuffer != 0)
|
||||
glDeleteRenderbuffers(1, &renderbuffer);
|
||||
|
||||
// Cache the result so we don't do this for every isFormatSupported call.
|
||||
checkedFormats[format].set(readable, true);
|
||||
supportedFormats[format].set(readable, supported);
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
void Canvas::resetFormatSupport()
|
||||
{
|
||||
for (int i = 0; i < (int)PIXELFORMAT_MAX_ENUM; i++)
|
||||
{
|
||||
checkedFormats[i].readable = false;
|
||||
checkedFormats[i].nonreadable = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -71,34 +71,10 @@ public:
|
||||
return fbo;
|
||||
}
|
||||
|
||||
static PixelFormat getSizedFormat(PixelFormat format);
|
||||
static bool isSupported();
|
||||
static bool isMultiFormatMultiCanvasSupported();
|
||||
static bool isFormatSupported(PixelFormat format, bool readable);
|
||||
static bool isFormatSupported(PixelFormat format);
|
||||
static void resetFormatSupport();
|
||||
|
||||
private:
|
||||
|
||||
struct SupportedFormat
|
||||
{
|
||||
bool readable = false;
|
||||
bool nonreadable = false;
|
||||
|
||||
bool get(bool getreadable)
|
||||
{
|
||||
return getreadable ? readable : nonreadable;
|
||||
}
|
||||
|
||||
void set(bool setreadable, bool val)
|
||||
{
|
||||
if (setreadable)
|
||||
readable = val;
|
||||
else
|
||||
nonreadable = val;
|
||||
}
|
||||
};
|
||||
|
||||
GLuint fbo;
|
||||
|
||||
GLuint texture;
|
||||
@@ -108,9 +84,6 @@ private:
|
||||
|
||||
int actualSamples;
|
||||
|
||||
static SupportedFormat supportedFormats[PIXELFORMAT_MAX_ENUM];
|
||||
static SupportedFormat checkedFormats[PIXELFORMAT_MAX_ENUM];
|
||||
|
||||
}; // Canvas
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -91,9 +91,9 @@ static GLenum getGLBlendFactor(BlendFactor factor)
|
||||
Graphics::Graphics()
|
||||
: windowHasStencil(false)
|
||||
, mainVAO(0)
|
||||
, supportedFormats()
|
||||
{
|
||||
gl = OpenGL();
|
||||
Canvas::resetFormatSupport();
|
||||
|
||||
auto window = getInstance<love::window::Window>(M_WINDOW);
|
||||
|
||||
@@ -1388,19 +1388,127 @@ void Graphics::initCapabilities()
|
||||
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
|
||||
}
|
||||
|
||||
bool Graphics::isCanvasFormatSupported(PixelFormat format) const
|
||||
PixelFormat Graphics::getSizedFormat(PixelFormat format) const
|
||||
{
|
||||
return Canvas::isFormatSupported(format);
|
||||
switch (format)
|
||||
{
|
||||
case PIXELFORMAT_NORMAL:
|
||||
if (isGammaCorrect())
|
||||
return PIXELFORMAT_sRGBA8_UNORM;
|
||||
else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8_UNORM, true, true, false))
|
||||
// 32-bit render targets don't have guaranteed support on GLES2.
|
||||
return PIXELFORMAT_RGBA4_UNORM;
|
||||
else
|
||||
return PIXELFORMAT_RGBA8_UNORM;
|
||||
case PIXELFORMAT_HDR:
|
||||
return PIXELFORMAT_RGBA16_FLOAT;
|
||||
default:
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
bool Graphics::isCanvasFormatSupported(PixelFormat format, bool readable) const
|
||||
bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB)
|
||||
{
|
||||
return Canvas::isFormatSupported(format, readable);
|
||||
}
|
||||
format = getSizedFormat(format);
|
||||
|
||||
bool Graphics::isImageFormatSupported(PixelFormat format, bool sRGB) const
|
||||
{
|
||||
return Image::isFormatSupported(format, sRGB);
|
||||
if (sRGB && format == PIXELFORMAT_RGBA8_UNORM)
|
||||
{
|
||||
format = PIXELFORMAT_sRGBA8_UNORM;
|
||||
sRGB = false;
|
||||
}
|
||||
|
||||
OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][sRGB ? 1 : 0];
|
||||
|
||||
if (supported.hasValue)
|
||||
return supported.value;
|
||||
|
||||
if (!OpenGL::isPixelFormatSupported(format, rendertarget, readable, sRGB))
|
||||
{
|
||||
supported.set(false);
|
||||
return supported.value;
|
||||
}
|
||||
|
||||
if (!rendertarget)
|
||||
{
|
||||
supported.set(true);
|
||||
return supported.value;
|
||||
}
|
||||
|
||||
// Even though we might have the necessary OpenGL version or extension,
|
||||
// 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.
|
||||
GLuint texture = 0;
|
||||
GLuint renderbuffer = 0;
|
||||
|
||||
// Avoid the test for depth/stencil formats - not every GL version
|
||||
// guarantees support for depth/stencil-only render targets (which we would
|
||||
// need for the test below to work), and we already do some finagling in
|
||||
// convertPixelFormat to try to use the best-supported internal
|
||||
// depth/stencil format for a particular driver.
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
{
|
||||
supported.set(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, readable, sRGB);
|
||||
|
||||
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||
|
||||
GLuint fbo = 0;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
|
||||
|
||||
// Make sure at least something is bound to a color attachment. I believe
|
||||
// this is required on ES2 but I'm not positive.
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
|
||||
|
||||
if (readable)
|
||||
{
|
||||
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::Wrap w;
|
||||
gl.setTextureWrap(TEXTURE_2D, w);
|
||||
|
||||
gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
glGenRenderbuffers(1, &renderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
|
||||
}
|
||||
|
||||
for (GLenum attachment : fmt.framebufferAttachments)
|
||||
{
|
||||
if (attachment == GL_NONE)
|
||||
continue;
|
||||
|
||||
if (readable)
|
||||
gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
|
||||
else
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
|
||||
}
|
||||
|
||||
supported.set(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||
gl.deleteFramebuffer(fbo);
|
||||
|
||||
if (texture != 0)
|
||||
gl.deleteTexture(texture);
|
||||
|
||||
if (renderbuffer != 0)
|
||||
glDeleteRenderbuffers(1, &renderbuffer);
|
||||
|
||||
return supported.value;
|
||||
}
|
||||
|
||||
Shader::Language Graphics::getShaderLanguageTarget() const
|
||||
|
||||
@@ -104,9 +104,8 @@ public:
|
||||
|
||||
void setWireframe(bool enable) override;
|
||||
|
||||
bool isCanvasFormatSupported(PixelFormat format) const override;
|
||||
bool isCanvasFormatSupported(PixelFormat format, bool readable) const override;
|
||||
bool isImageFormatSupported(PixelFormat format, bool sRGB) const override;
|
||||
PixelFormat getSizedFormat(PixelFormat format) const override;
|
||||
bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) override;
|
||||
Renderer getRenderer() const override;
|
||||
RendererInfo getRendererInfo() const override;
|
||||
|
||||
@@ -153,6 +152,9 @@ private:
|
||||
bool windowHasStencil;
|
||||
GLuint mainVAO;
|
||||
|
||||
// [rendertarget][readable][srgb]
|
||||
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2];
|
||||
|
||||
}; // Graphics
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -351,11 +351,6 @@ bool Image::setMipmapSharpness(float sharpness)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::isFormatSupported(PixelFormat pixelformat, bool sRGB)
|
||||
{
|
||||
return OpenGL::isPixelFormatSupported(pixelformat, false, true, sRGB);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -54,8 +54,6 @@ public:
|
||||
|
||||
bool setMipmapSharpness(float sharpness) override;
|
||||
|
||||
static bool isFormatSupported(PixelFormat pixelformat, bool sRGB);
|
||||
|
||||
private:
|
||||
|
||||
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
|
||||
|
||||
@@ -2282,14 +2282,14 @@ int w_getCanvasFormats(lua_State *L)
|
||||
{
|
||||
supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
return instance()->isCanvasFormatSupported(format, true);
|
||||
return instance()->isPixelFormatSupported(format, true, true, false);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
return instance()->isCanvasFormatSupported(format, false);
|
||||
return instance()->isPixelFormatSupported(format, true, false, false);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2297,7 +2297,8 @@ int w_getCanvasFormats(lua_State *L)
|
||||
{
|
||||
supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
return instance()->isCanvasFormatSupported(format);
|
||||
bool readable = !isPixelFormatDepthStencil(format);
|
||||
return instance()->isPixelFormatSupported(format, true, readable, false);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2308,7 +2309,7 @@ int w_getImageFormats(lua_State *L)
|
||||
{
|
||||
const auto supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
return instance()->isImageFormatSupported(format);
|
||||
return instance()->isPixelFormatSupported(format, false, true, false);
|
||||
};
|
||||
|
||||
const auto ignore = [](PixelFormat format) -> bool
|
||||
|
||||
Reference in New Issue
Block a user