Browse Source

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

--HG--
branch : minor
Alex Szpakowski 8 years ago
parent
commit
c36a53fdcb

+ 13 - 2
src/common/pixelformat.cpp

@@ -53,7 +53,12 @@ static StringMap<PixelFormat, PIXELFORMAT_MAX_ENUM>::Entry formatEntries[] =
 	{ "rgb10a2",  PIXELFORMAT_RGB10A2  },
 	{ "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       },
 	{ "DXT3",      PIXELFORMAT_DXT3       },
@@ -116,7 +121,8 @@ bool isPixelFormatCompressed(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)
@@ -133,6 +139,7 @@ size_t getPixelFormatSize(PixelFormat format)
 	case PIXELFORMAT_RGBA4:
 	case PIXELFORMAT_RGB5A1:
 	case PIXELFORMAT_RGB565:
+	case PIXELFORMAT_DEPTH16:
 		return 2;
 	case PIXELFORMAT_RGBA8:
 	case PIXELFORMAT_sRGBA8:
@@ -141,10 +148,14 @@ size_t getPixelFormatSize(PixelFormat format)
 	case PIXELFORMAT_R32F:
 	case PIXELFORMAT_RGB10A2:
 	case PIXELFORMAT_RG11B10F:
+	case PIXELFORMAT_DEPTH24:
+	case PIXELFORMAT_DEPTH32F:
+	case PIXELFORMAT_DEPTH24_STENCIL8:
 		return 4;
 	case PIXELFORMAT_RGBA16:
 	case PIXELFORMAT_RGBA16F:
 	case PIXELFORMAT_RG32F:
+	case PIXELFORMAT_DEPTH32F_STENCIL8:
 		return 8;
 	case PIXELFORMAT_RGBA32F:
 		return 16;

+ 5 - 0
src/common/pixelformat.h

@@ -59,6 +59,11 @@ enum PixelFormat
 
 	// depth/stencil formats
 	PIXELFORMAT_STENCIL8,
+	PIXELFORMAT_DEPTH16,
+	PIXELFORMAT_DEPTH24,
+	PIXELFORMAT_DEPTH32F,
+	PIXELFORMAT_DEPTH24_STENCIL8,
+	PIXELFORMAT_DEPTH32F_STENCIL8,
 
 	// compressed formats
 	PIXELFORMAT_DXT1,

+ 99 - 51
src/modules/graphics/opengl/Canvas.cpp

@@ -30,7 +30,7 @@ namespace graphics
 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
 	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 (initialize)
-		{
-			int faces = texType == TEXTURE_CUBE ? 6 : 1;
+		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
-			// 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--)
+		// 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 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);
-					glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-					glClear(GL_COLOR_BUFFER_BIT);
+					if (attachment == GL_NONE)
+						continue;
+
+					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);
@@ -90,7 +98,11 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat
 	else
 		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)
 		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))
 	{
-		// Initialize the buffer to transparent black.
-		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-		glClear(GL_COLOR_BUFFER_BIT);
+		if (isPixelFormatDepthStencil(pixelformat))
+			glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+		else
+		{
+			// Initialize the buffer to transparent black.
+			glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+			glClear(GL_COLOR_BUFFER_BIT);
+		}
 	}
 	else
 	{
@@ -129,17 +146,17 @@ Canvas::Canvas(const Settings &settings)
 	, actualSamples(0)
 	, textureMemory(0)
 {
-	this->width = settings.width;
-	this->height = settings.height;
-	this->pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
-	this->pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
+	width = settings.width;
+	height = settings.height;
+	pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
+	pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
 
 	if (texType == TEXTURE_VOLUME)
-		this->depth = settings.layers;
+		depth = settings.layers;
 	else if (texType == TEXTURE_2D_ARRAY)
-		this->layers = settings.layers;
+		layers = settings.layers;
 	else
-		this->layers = 1;
+		layers = 1;
 
 	if (width <= 0 || height <= 0 || layers <= 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)
 		throw love::Exception("MSAA is only supported for Canvases with the 2D texture type.");
 
-	this->format = getSizedFormat(settings.format);
-
-	readable = this->format != PIXELFORMAT_STENCIL8;
+	format = getSizedFormat(settings.format);
+	readable = !isPixelFormatDepthStencil(format);
 
 	initQuad();
 	loadVolatile();
@@ -263,7 +279,7 @@ bool Canvas::loadVolatile()
 		}
 
 		// 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)
 		{
@@ -468,14 +484,17 @@ bool Canvas::isMultiFormatMultiCanvasSupported()
 	return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
 }
 
-bool Canvas::supportedFormats[] = {false};
-bool Canvas::checkedFormats[] = {false};
+Canvas::SupportedFormat Canvas::supportedFormats[] = {};
+Canvas::SupportedFormat Canvas::checkedFormats[] = {};
 
 bool Canvas::isFormatSupported(PixelFormat format)
 {
 	if (!isSupported())
 		return false;
 
+	const char *fstr = "?";
+	love::getConstant(format, fstr);
+
 	bool supported = true;
 	format = getSizedFormat(format);
 
@@ -485,16 +504,32 @@ bool Canvas::isFormatSupported(PixelFormat format)
 	if (!OpenGL::isPixelFormatSupported(format, true, readable, false))
 		return false;
 
-	if (checkedFormats[format])
-		return supportedFormats[format];
+	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;
+
+	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)
 	{
-		GLuint texture = 0;
 		glGenTextures(1, &texture);
 		gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
 
@@ -505,28 +540,41 @@ bool Canvas::isFormatSupported(PixelFormat format)
 		Texture::Wrap w;
 		gl.setTextureWrap(TEXTURE_2D, w);
 
-		bool unusedSRGB = false;
-		gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 2, 2);
-
-		GLuint fbo = 0;
-		supported = (createFBO(fbo, TEXTURE_2D, texture, 1, false) == GL_FRAMEBUFFER_COMPLETE);
-		gl.deleteFramebuffer(fbo);
-
-		gl.deleteTexture(texture);
+		unusedSRGB = false;
+		gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 1, 1);
 	}
 	else
 	{
-		int samples = 0;
-		GLuint renderbuffer = 0;
-		supported = createRenderbuffer(2, 2, samples, format, renderbuffer);
+		glGenRenderbuffers(1, &renderbuffer);
+		glBindRenderbuffer(GL_RENDERBUFFER, 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.
-	checkedFormats[format] = true;
-	supportedFormats[format] = supported;
+	checkedFormats[format].set(readable, true);
+	supportedFormats[format].set(readable, supported);
 
 	return supported;
 }

+ 21 - 2
src/modules/graphics/opengl/Canvas.h

@@ -86,6 +86,25 @@ public:
 
 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;
@@ -98,8 +117,8 @@ private:
 
 	size_t textureMemory;
 
-	static bool supportedFormats[PIXELFORMAT_MAX_ENUM];
-	static bool checkedFormats[PIXELFORMAT_MAX_ENUM];
+	static SupportedFormat supportedFormats[PIXELFORMAT_MAX_ENUM];
+	static SupportedFormat checkedFormats[PIXELFORMAT_MAX_ENUM];
 
 }; // Canvas
 

+ 67 - 0
src/modules/graphics/opengl/OpenGL.cpp

@@ -1343,6 +1343,60 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
 		}
 		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:
 		f.internalformat = isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
 		break;
@@ -1580,6 +1634,19 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget,
 	case PIXELFORMAT_STENCIL8:
 		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:
 		return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
 	case PIXELFORMAT_DXT3: