Add new non-readable Canvas formats: depth16, depth24, depth32f, depth24stencil8, and depth32fstencil8.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2017-04-11 18:34:04 -03:00
parent 018a3831cb
commit c36a53fdcb
5 changed files with 206 additions and 56 deletions
+13 -2
View File
@@ -53,7 +53,12 @@ static StringMap<PixelFormat, PIXELFORMAT_MAX_ENUM>::Entry formatEntries[] =
{ "rgb10a2", PIXELFORMAT_RGB10A2 }, { "rgb10a2", PIXELFORMAT_RGB10A2 },
{ "rg11b10f", PIXELFORMAT_RG11B10F }, { "rg11b10f", PIXELFORMAT_RG11B10F },
{ "stencil8", PIXELFORMAT_STENCIL8 }, { "stencil8", PIXELFORMAT_STENCIL8 },
{ "depth16", PIXELFORMAT_DEPTH16 },
{ "depth24", PIXELFORMAT_DEPTH24 },
{ "depth32f", PIXELFORMAT_DEPTH32F },
{ "depth24stencil8", PIXELFORMAT_DEPTH24_STENCIL8 },
{ "depth32fstencil8", PIXELFORMAT_DEPTH32F_STENCIL8 },
{ "DXT1", PIXELFORMAT_DXT1 }, { "DXT1", PIXELFORMAT_DXT1 },
{ "DXT3", PIXELFORMAT_DXT3 }, { "DXT3", PIXELFORMAT_DXT3 },
@@ -116,7 +121,8 @@ bool isPixelFormatCompressed(PixelFormat format)
bool isPixelFormatDepthStencil(PixelFormat format) bool isPixelFormatDepthStencil(PixelFormat format)
{ {
return format == PIXELFORMAT_STENCIL8; int iformat = (int) format;
return iformat >= (int) PIXELFORMAT_STENCIL8 && iformat <= (int) PIXELFORMAT_DEPTH32F_STENCIL8;
} }
size_t getPixelFormatSize(PixelFormat format) size_t getPixelFormatSize(PixelFormat format)
@@ -133,6 +139,7 @@ size_t getPixelFormatSize(PixelFormat format)
case PIXELFORMAT_RGBA4: case PIXELFORMAT_RGBA4:
case PIXELFORMAT_RGB5A1: case PIXELFORMAT_RGB5A1:
case PIXELFORMAT_RGB565: case PIXELFORMAT_RGB565:
case PIXELFORMAT_DEPTH16:
return 2; return 2;
case PIXELFORMAT_RGBA8: case PIXELFORMAT_RGBA8:
case PIXELFORMAT_sRGBA8: case PIXELFORMAT_sRGBA8:
@@ -141,10 +148,14 @@ size_t getPixelFormatSize(PixelFormat format)
case PIXELFORMAT_R32F: case PIXELFORMAT_R32F:
case PIXELFORMAT_RGB10A2: case PIXELFORMAT_RGB10A2:
case PIXELFORMAT_RG11B10F: case PIXELFORMAT_RG11B10F:
case PIXELFORMAT_DEPTH24:
case PIXELFORMAT_DEPTH32F:
case PIXELFORMAT_DEPTH24_STENCIL8:
return 4; return 4;
case PIXELFORMAT_RGBA16: case PIXELFORMAT_RGBA16:
case PIXELFORMAT_RGBA16F: case PIXELFORMAT_RGBA16F:
case PIXELFORMAT_RG32F: case PIXELFORMAT_RG32F:
case PIXELFORMAT_DEPTH32F_STENCIL8:
return 8; return 8;
case PIXELFORMAT_RGBA32F: case PIXELFORMAT_RGBA32F:
return 16; return 16;
+5
View File
@@ -59,6 +59,11 @@ enum PixelFormat
// depth/stencil formats // depth/stencil formats
PIXELFORMAT_STENCIL8, PIXELFORMAT_STENCIL8,
PIXELFORMAT_DEPTH16,
PIXELFORMAT_DEPTH24,
PIXELFORMAT_DEPTH32F,
PIXELFORMAT_DEPTH24_STENCIL8,
PIXELFORMAT_DEPTH32F_STENCIL8,
// compressed formats // compressed formats
PIXELFORMAT_DXT1, PIXELFORMAT_DXT1,
+100 -52
View File
@@ -30,7 +30,7 @@ namespace graphics
namespace opengl namespace opengl
{ {
static GLenum createFBO(GLuint &framebuffer, TextureType texType, GLuint texture, int layers, bool initialize) static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers)
{ {
// get currently bound fbo to reset to it later // get currently bound fbo to reset to it later
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
@@ -40,27 +40,35 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, GLuint texture
if (texture != 0) if (texture != 0)
{ {
if (initialize) bool unusedSRGB = false;
{ OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, unusedSRGB);
int faces = texType == TEXTURE_CUBE ? 6 : 1;
// Make sure all faces and layers of the texture are initialized to int faces = texType == TEXTURE_CUBE ? 6 : 1;
// transparent black. This is unfortunately probably pretty slow for
// 2D-array and 3D textures with a lot of layers... // Make sure all faces and layers of the texture are initialized to
for (int layer = layers - 1; layer >= 0; layer--) // transparent black. This is unfortunately probably pretty slow for
// 2D-array and 3D textures with a lot of layers...
for (int layer = layers - 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)
{ {
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, layer, face); if (attachment == GL_NONE)
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); continue;
glClear(GL_COLOR_BUFFER_BIT);
gl.framebufferTexture(attachment, texType, texture, 0, layer, face);
if (isPixelFormatDepthStencil(format))
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
else
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
} }
} }
} }
else
{
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
}
} }
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -90,7 +98,11 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat
else else
glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, width, height); glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, fmt.framebufferAttachments[0], GL_RENDERBUFFER, buffer); for (GLenum attachment : fmt.framebufferAttachments)
{
if (attachment != GL_NONE)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, buffer);
}
if (samples > 1) if (samples > 1)
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
@@ -103,9 +115,14 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat
if (status == GL_FRAMEBUFFER_COMPLETE && (reqsamples <= 1 || samples > 1)) if (status == GL_FRAMEBUFFER_COMPLETE && (reqsamples <= 1 || samples > 1))
{ {
// Initialize the buffer to transparent black. if (isPixelFormatDepthStencil(pixelformat))
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT); else
{
// Initialize the buffer to transparent black.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
} }
else else
{ {
@@ -129,17 +146,17 @@ Canvas::Canvas(const Settings &settings)
, actualSamples(0) , actualSamples(0)
, textureMemory(0) , textureMemory(0)
{ {
this->width = settings.width; width = settings.width;
this->height = settings.height; height = settings.height;
this->pixelWidth = (int) ((width * settings.pixeldensity) + 0.5); pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
this->pixelHeight = (int) ((height * settings.pixeldensity) + 0.5); pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
if (texType == TEXTURE_VOLUME) if (texType == TEXTURE_VOLUME)
this->depth = settings.layers; depth = settings.layers;
else if (texType == TEXTURE_2D_ARRAY) else if (texType == TEXTURE_2D_ARRAY)
this->layers = settings.layers; layers = settings.layers;
else else
this->layers = 1; layers = 1;
if (width <= 0 || height <= 0 || layers <= 0) if (width <= 0 || height <= 0 || layers <= 0)
throw love::Exception("Canvas dimensions must be greater than 0."); throw love::Exception("Canvas dimensions must be greater than 0.");
@@ -147,9 +164,8 @@ Canvas::Canvas(const Settings &settings)
if (texType != TEXTURE_2D && settings.msaa > 1) if (texType != TEXTURE_2D && settings.msaa > 1)
throw love::Exception("MSAA is only supported for Canvases with the 2D texture type."); throw love::Exception("MSAA is only supported for Canvases with the 2D texture type.");
this->format = getSizedFormat(settings.format); format = getSizedFormat(settings.format);
readable = !isPixelFormatDepthStencil(format);
readable = this->format != PIXELFORMAT_STENCIL8;
initQuad(); initQuad();
loadVolatile(); loadVolatile();
@@ -263,7 +279,7 @@ bool Canvas::loadVolatile()
} }
// Create a canvas-local FBO used for glReadPixels as well as MSAA blitting. // Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
status = createFBO(fbo, texType, texture, texType == TEXTURE_VOLUME ? depth : layers, true); status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers);
if (status != GL_FRAMEBUFFER_COMPLETE) if (status != GL_FRAMEBUFFER_COMPLETE)
{ {
@@ -468,14 +484,17 @@ bool Canvas::isMultiFormatMultiCanvasSupported()
return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object); return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
} }
bool Canvas::supportedFormats[] = {false}; Canvas::SupportedFormat Canvas::supportedFormats[] = {};
bool Canvas::checkedFormats[] = {false}; Canvas::SupportedFormat Canvas::checkedFormats[] = {};
bool Canvas::isFormatSupported(PixelFormat format) bool Canvas::isFormatSupported(PixelFormat format)
{ {
if (!isSupported()) if (!isSupported())
return false; return false;
const char *fstr = "?";
love::getConstant(format, fstr);
bool supported = true; bool supported = true;
format = getSizedFormat(format); format = getSizedFormat(format);
@@ -485,16 +504,32 @@ bool Canvas::isFormatSupported(PixelFormat format)
if (!OpenGL::isPixelFormatSupported(format, true, readable, false)) if (!OpenGL::isPixelFormatSupported(format, true, readable, false))
return false; return false;
if (checkedFormats[format]) if (checkedFormats[format].get(readable))
return supportedFormats[format]; return supportedFormats[format].get(readable);
// Even though we might have the necessary OpenGL version or extension, // Even though we might have the necessary OpenGL version or extension,
// drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching // 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 // a texture to a FBO whose format the driver doesn't like. So we should
// test with an actual FBO. // test with an actual FBO.
GLuint texture = 0;
GLuint renderbuffer = 0;
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 (depthstencil)
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
if (readable) if (readable)
{ {
GLuint texture = 0;
glGenTextures(1, &texture); glGenTextures(1, &texture);
gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false); gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
@@ -505,28 +540,41 @@ bool Canvas::isFormatSupported(PixelFormat format)
Texture::Wrap w; Texture::Wrap w;
gl.setTextureWrap(TEXTURE_2D, w); gl.setTextureWrap(TEXTURE_2D, w);
bool unusedSRGB = false; unusedSRGB = false;
gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 2, 2); gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 1, 1);
GLuint fbo = 0;
supported = (createFBO(fbo, TEXTURE_2D, texture, 1, false) == GL_FRAMEBUFFER_COMPLETE);
gl.deleteFramebuffer(fbo);
gl.deleteTexture(texture);
} }
else else
{ {
int samples = 0; glGenRenderbuffers(1, &renderbuffer);
GLuint renderbuffer = 0; glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
supported = createRenderbuffer(2, 2, samples, format, renderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
if (supported)
glDeleteRenderbuffers(1, &renderbuffer);
} }
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. // Cache the result so we don't do this for every isFormatSupported call.
checkedFormats[format] = true; checkedFormats[format].set(readable, true);
supportedFormats[format] = supported; supportedFormats[format].set(readable, supported);
return supported; return supported;
} }
+21 -2
View File
@@ -86,6 +86,25 @@ public:
private: 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 fbo;
GLuint texture; GLuint texture;
@@ -98,8 +117,8 @@ private:
size_t textureMemory; size_t textureMemory;
static bool supportedFormats[PIXELFORMAT_MAX_ENUM]; static SupportedFormat supportedFormats[PIXELFORMAT_MAX_ENUM];
static bool checkedFormats[PIXELFORMAT_MAX_ENUM]; static SupportedFormat checkedFormats[PIXELFORMAT_MAX_ENUM];
}; // Canvas }; // Canvas
+67
View File
@@ -1343,6 +1343,60 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
} }
break; break;
case PIXELFORMAT_DEPTH16:
f.internalformat = GL_DEPTH_COMPONENT16;
f.externalformat = GL_DEPTH_COMPONENT;
f.type = GL_UNSIGNED_SHORT;
f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
break;
case PIXELFORMAT_DEPTH24:
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0 && !GLAD_OES_depth24 && 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_DEPTH_COMPONENT24;
f.externalformat = GL_DEPTH_COMPONENT;
f.type = GL_UNSIGNED_INT;
f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
}
break;
case PIXELFORMAT_DEPTH32F:
f.internalformat = GL_DEPTH_COMPONENT32F;
f.externalformat = GL_DEPTH_COMPONENT;
f.type = GL_FLOAT;
f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
break;
case PIXELFORMAT_DEPTH24_STENCIL8:
f.internalformat = GL_DEPTH24_STENCIL8;
f.externalformat = GL_DEPTH_STENCIL;
f.type = GL_UNSIGNED_INT_24_8;
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
{
f.framebufferAttachments[0] = GL_DEPTH_STENCIL_ATTACHMENT;
}
else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
{
f.framebufferAttachments[0] = GL_DEPTH_ATTACHMENT;
f.framebufferAttachments[1] = GL_STENCIL_ATTACHMENT;
}
break;
case PIXELFORMAT_DEPTH32F_STENCIL8:
f.internalformat = GL_DEPTH32F_STENCIL8;
f.externalformat = GL_DEPTH_STENCIL;
f.type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
f.framebufferAttachments[0] = GL_DEPTH_STENCIL_ATTACHMENT;
break;
case PIXELFORMAT_DXT1: case PIXELFORMAT_DXT1:
f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT; f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
break; break;
@@ -1580,6 +1634,19 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget,
case PIXELFORMAT_STENCIL8: case PIXELFORMAT_STENCIL8:
return rendertarget && !readable; return rendertarget && !readable;
case PIXELFORMAT_DEPTH16:
return rendertarget && !readable;
case PIXELFORMAT_DEPTH24:
return rendertarget && !readable && (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_depth24 || GLAD_OES_packed_depth_stencil);
case PIXELFORMAT_DEPTH24_STENCIL8:
return rendertarget && !readable && (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil);
case PIXELFORMAT_DEPTH32F:
case PIXELFORMAT_DEPTH32F_STENCIL8:
return rendertarget && !readable && (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_ARB_depth_buffer_float);
case PIXELFORMAT_DXT1: case PIXELFORMAT_DXT1:
return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1; return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
case PIXELFORMAT_DXT3: case PIXELFORMAT_DXT3: