Parcourir la source

Add basic support for render targets

Daniele Bartolini il y a 11 ans
Parent
commit
0e2882bac1

+ 51 - 21
engine/renderers/backend/RenderContext.h

@@ -100,30 +100,35 @@ namespace crown
 
 #define STATE_BLEND_FUNC(src, dst) (uint64_t(src << 4) | uint64_t(dst))
 
-#define CLEAR_COLOR					0x1
-#define CLEAR_DEPTH					0x2
+#define CLEAR_COLOR								0x1
+#define CLEAR_DEPTH								0x2
 
 // Texture flags
-#define TEXTURE_FILTER_NEAREST		0x00000001
-#define TEXTURE_FILTER_LINEAR		0x00000002
-#define TEXTURE_FILTER_BILINEAR		0x00000003
-#define TEXTURE_FILTER_TRILINEAR	0x00000004
-#define TEXTURE_FILTER_MASK			0x0000000F
-#define TEXTURE_FILTER_SHIFT		0
-
-#define TEXTURE_WRAP_U_CLAMP_EDGE	0x00000010
-#define TEXTURE_WRAP_U_CLAMP_REPEAT	0x00000020
-#define TEXTURE_WRAP_U_MASK			0x00000030
-#define TEXTURE_WRAP_U_SHIFT		4
-#define TEXTURE_WRAP_V_CLAMP_EDGE	0x00000100
-#define TEXTURE_WRAP_V_CLAMP_REPEAT	0x00000200
-#define TEXTURE_WRAP_V_MASK			0x00000300
-#define TEXTURE_WRAP_V_SHIFT		8
+#define TEXTURE_FILTER_NEAREST					0x00000001
+#define TEXTURE_FILTER_LINEAR					0x00000002
+#define TEXTURE_FILTER_BILINEAR					0x00000003
+#define TEXTURE_FILTER_TRILINEAR				0x00000004
+#define TEXTURE_FILTER_MASK						0x0000000F
+#define TEXTURE_FILTER_SHIFT					0
+
+#define TEXTURE_WRAP_U_CLAMP_EDGE				0x00000010
+#define TEXTURE_WRAP_U_CLAMP_REPEAT				0x00000020
+#define TEXTURE_WRAP_U_MASK						0x00000030
+#define TEXTURE_WRAP_U_SHIFT					4
+#define TEXTURE_WRAP_V_CLAMP_EDGE				0x00000100
+#define TEXTURE_WRAP_V_CLAMP_REPEAT				0x00000200
+#define TEXTURE_WRAP_V_MASK						0x00000300
+#define TEXTURE_WRAP_V_SHIFT					8
+
+#define RENDER_TARGET_NO_TEXTURE				0x00000001
+#define RENDER_TARGET_MASK						0x0000000F
+#define RENDER_TARGET_SHIFT						0
 
 // Sampler flags
-#define SAMPLER_TEXTURE				0x10000000
-#define SAMPLER_MASK				0xF0000000
-#define SAMPLER_SHIFT				28
+#define SAMPLER_TEXTURE							0x10000000
+#define SAMPLER_RENDER_TARGET					0x20000000
+#define SAMPLER_MASK							0xF0000000
+#define SAMPLER_SHIFT							28
 
 struct ViewRect
 {
@@ -190,7 +195,7 @@ struct RenderState
 		for (uint32_t i = 0; i < STATE_MAX_TEXTURES; i++)
 		{
 			samplers[i].sampler_id.id = INVALID_ID;
-			samplers[i].flags = SAMPLER_TEXTURE;
+			samplers[i].flags = 0;
 		}
 	}
 
@@ -338,6 +343,7 @@ struct RenderContext
 
 	void set_texture(uint8_t unit, UniformId sampler_uniform, TextureId texture, uint32_t flags)
 	{
+		CE_ASSERT(unit < STATE_MAX_TEXTURES, "Texture unit out of bounds");
 		m_flags |= STATE_TEXTURE_0 << unit;
 
 		Sampler& sampler = m_state.samplers[unit];
@@ -348,6 +354,19 @@ struct RenderContext
 		set_uniform(sampler_uniform, UniformType::INTEGER_1, &val, 1);
 	}
 
+	void set_texture(uint8_t unit, UniformId sampler_uniform, RenderTargetId texture, uint8_t attachment, uint32_t texture_flags)
+	{
+		CE_ASSERT(unit < STATE_MAX_TEXTURES, "Texture unit out of bounds");
+		m_flags |= STATE_TEXTURE_0 << unit;
+
+		Sampler& sampler = m_state.samplers[unit];
+		sampler.sampler_id = texture;
+		sampler.flags |= SAMPLER_RENDER_TARGET | texture_flags;
+
+		const uint32_t val = unit;
+		set_uniform(sampler_uniform, UniformType::INTEGER_1, &val, 1);
+	}
+
 	void set_layer_render_target(uint8_t layer, RenderTargetId target)
 	{
 		CE_ASSERT(layer < MAX_RENDER_LAYERS, "Layer out of bounds");
@@ -423,6 +442,17 @@ struct RenderContext
 		m_num_states = 0;
 		m_state.clear();
 
+		memset(m_targets, 0xFF, sizeof(m_targets));
+		memset(m_viewports, 0, sizeof(m_viewports));
+		memset(m_scissors, 0, sizeof(m_scissors));
+		memset(m_clears, 0, sizeof(m_clears));
+
+		for (uint32_t i = 0; i < MAX_RENDER_LAYERS; i++)
+		{
+			m_projection_matrices[i] = matrix4x4::IDENTITY;
+			m_view_matrices[i] = matrix4x4::IDENTITY;
+		}
+
 		m_commands.clear();
 		m_last_uniform_offset = 0;
 		m_constants.clear();

+ 54 - 8
engine/renderers/backend/Renderer.h

@@ -82,7 +82,7 @@ public:
 	void update_uniform_impl(UniformId id, size_t size, const void* data);
 	void destroy_uniform_impl(UniformId id);
 
-	void create_render_target_impl(RenderTargetId id, uint16_t width, uint16_t height, RenderTargetFormat::Enum format);
+	void create_render_target_impl(RenderTargetId id, uint16_t width, uint16_t height, PixelFormat::Enum format, uint32_t flags);
 	void destroy_render_target_impl(RenderTargetId id);
 
 	/// Initializes the renderer.
@@ -432,15 +432,29 @@ public:
 		m_submit->m_commands.write(id);
 	}
 
-	// 
-	// RenderTargetId create_render_target(uint16_t width, uint16_t height, RenderTargetFormat::Enum format)
-	// {
+	/// Creates a new render target of size @a width and @a height with the given @a format.
+	RenderTargetId create_render_target(uint16_t width, uint16_t height, PixelFormat::Enum format, uint32_t flags = 0)
+	{
+		const RenderTargetId id = m_render_targets.create();
+
+		m_submit->m_commands.write(CommandType::CREATE_RENDER_TARGET);
+		m_submit->m_commands.write(id);
+		m_submit->m_commands.write(width);
+		m_submit->m_commands.write(height);
+		m_submit->m_commands.write(format);
+		m_submit->m_commands.write(flags);
+
+		return id;
+	}
 
-	// }
-	// void destroy_render_target(RenderTargetId id)
-	// {
+	/// Destroys the render target @a id.
+	void destroy_render_target(RenderTargetId id)
+	{
+		CE_ASSERT(m_render_targets.has(id), "Render target does not exist");
 
-	// }
+		m_submit->m_commands.write(CommandType::DESTROY_RENDER_TARGET);
+		m_submit->m_commands.write(id);
+	}
 
 	//-----------------------------------------------------------------------------
 	void execute_commands(CommandBuffer& cmds)
@@ -677,6 +691,31 @@ public:
 
 					break;
 				}
+				case CommandType::CREATE_RENDER_TARGET:
+				{
+					RenderTargetId id;
+					uint16_t width;
+					uint16_t height;
+					PixelFormat::Enum format;
+					uint32_t flags;
+
+					cmds.read(id);
+					cmds.read(width);
+					cmds.read(height);
+					cmds.read(format);
+					cmds.read(flags);
+
+					create_render_target_impl(id, width, height, format, flags);
+					break;
+				}
+				case CommandType::DESTROY_RENDER_TARGET:
+				{
+					RenderTargetId id;
+					cmds.read(id);
+
+					destroy_render_target_impl(id);
+					break;
+				}
 				case CommandType::END:
 				{
 					end = true;
@@ -768,6 +807,13 @@ public:
 		m_submit->set_texture(unit, sampler_uniform, texture, flags);
 	}
 
+	void set_texture(uint8_t unit, UniformId sampler_uniform, RenderTargetId texture, uint8_t attachment, uint32_t texture_flags)
+	{
+		CE_ASSERT(m_uniforms.has(sampler_uniform), "Uniform does not exist");
+		CE_ASSERT(m_render_targets.has(texture), "Render target does not exist");
+		m_submit->set_texture(unit, sampler_uniform, texture, attachment, texture_flags);
+	}
+
 	void set_layer_render_target(uint8_t layer, RenderTargetId id)
 	{
 		CE_ASSERT(m_render_targets.has(id), "Render target does not exist");

+ 7 - 10
engine/renderers/backend/RendererTypes.h

@@ -66,16 +66,6 @@ struct UniformType
 	};
 };
 
-struct RenderTargetFormat
-{
-	enum Enum
-	{
-		RTF_RGB_8,		///< RGB values, 8-bit each
-		RTF_RGBA_8,		///< RGBA values, 8-bit each
-		RTF_D24			///< Depth
-	};
-};
-
 struct ShaderUniform
 {
 	enum Enum
@@ -143,6 +133,10 @@ struct PixelFormat
 	{
 		RGB_8 = 0,
 		RGBA_8,
+		D16,
+		D24,
+		D32,
+		D24S8,
 		COUNT
 	};
 };
@@ -177,6 +171,9 @@ struct CommandType
 		CREATE_UNIFORM,
 		DESTROY_UNIFORM,
 
+		CREATE_RENDER_TARGET,
+		DESTROY_RENDER_TARGET,
+
 		END
 	};
 };

+ 141 - 82
engine/renderers/backend/gl/GLRenderer.cpp

@@ -30,6 +30,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 	#include <GL/glew.h>
 #elif defined(ANDROID)
 	#include <GLES2/gl2.h>
+	#include <GLES2/gl2ext.h>
 #else
 	#error "Oops, wrong platform"
 #endif
@@ -75,6 +76,16 @@ static const char* gl_error_to_string(GLenum error)
 		function;
 #endif
 
+#ifdef ANDROID
+	#define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES
+	#ifndef GL_DEPTH_STENCIL_ATTACHMENT
+		#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+	#endif // GL_DEPTH_STENCIL_ATTACHMENT
+	#define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
+	#define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES
+	#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
+#endif // ANDROID
+
 namespace crown
 {
 
@@ -115,19 +126,54 @@ const GLenum TEXTURE_WRAP_TABLE[] =
 };
 
 //-----------------------------------------------------------------------------
-struct GLTextureFormatInfo
+struct TextureFormatInfo
 {
 	GLenum internal_format;
 	GLenum format;
 };
 
 //-----------------------------------------------------------------------------
-const GLTextureFormatInfo TEXTURE_FORMAT_TABLE[PixelFormat::COUNT] =
+const TextureFormatInfo TEXTURE_FORMAT_TABLE[PixelFormat::COUNT] =
 {
 	{ GL_RGB, GL_RGB },
-	{ GL_RGBA, GL_RGBA}
+	{ GL_RGBA, GL_RGBA},
+	{ GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT},
+	{ GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT},
+	{ GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT},
+	{ GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL}
 };
 
+//-----------------------------------------------------------------------------
+static bool is_depth(const GLenum format)
+{
+	switch (format)
+	{
+		case GL_DEPTH_COMPONENT16:
+		case GL_DEPTH_COMPONENT24:
+		case GL_DEPTH_COMPONENT32:
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+static bool is_color(const GLenum format)
+{
+	switch (format)
+	{
+		case GL_RGB:
+		case GL_RGBA:
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
 //-----------------------------------------------------------------------------
 const GLenum DEPTH_FUNCTION_TABLE[] = 
 {
@@ -215,12 +261,12 @@ ShaderUniform::Enum name_to_stock_uniform(const char* uniform)
 	return ShaderUniform::COUNT;
 }
 
-UniformType::Enum gl_enum_to_uniform_type(GLenum type)
+static UniformType::Enum gl_enum_to_uniform_type(GLenum type)
 {
 	switch (type)
 	{
 		case GL_INT: return UniformType::INTEGER_1;
-		case GL_INT_VEC2: return UniformType::INTEGER_2; 
+		case GL_INT_VEC2: return UniformType::INTEGER_2;
 		case GL_INT_VEC3: return UniformType::INTEGER_3;
 		case GL_INT_VEC4: return UniformType::INTEGER_4;
 		case GL_FLOAT: return UniformType::FLOAT_1;
@@ -348,6 +394,7 @@ struct Texture
 	void create(uint32_t width, uint32_t height, PixelFormat::Enum format, const void* data)
 	{
 		GL_CHECK(glGenTextures(1, &m_id));
+		CE_ASSERT(m_id != 0, "Failed to create texture");
 		GL_CHECK(glBindTexture(GL_TEXTURE_2D, m_id));
 
 		#if defined(LINUX) || defined(WINDOWS)
@@ -606,79 +653,74 @@ public:
 //-----------------------------------------------------------------------------
 struct RenderTarget
 {
-	void create(uint16_t /*width*/, uint16_t /*height*/, RenderTargetFormat /*format*/)
+	void create(uint16_t width, uint16_t height, PixelFormat::Enum format, uint32_t flags)
 	{
-		// // Create and bind FBO
-		// GL_CHECK(glGenFramebuffersEXT(1, &m_gl_fbo));
-		// GL_CHECK(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_gl_fbo));
-
-		// GLuint renderedTexture;
-		// glGenTextures(1, &renderedTexture);
-		 
-		// // "Bind" the newly created texture : all future texture functions will modify this texture
-		// glBindTexture(GL_TEXTURE_2D, renderedTexture);
-		 
-		// // Give an empty image to OpenGL ( the last "0" )
-		// glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, 1024, 768, 0,GL_RGB, GL_UNSIGNED_BYTE, 0);
-		 
-		// // Poor filtering. Needed !
-		// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-		// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
-
-		// // Create color/depth attachments
-		// switch (format)
-		// {
-		// 	case RTF_RGBA_8:
-		// 	case RTF_D24:
-		// 	{
-		// 		if (format == RTF_RGBA_8)
-		// 		{
-		// 			GL_CHECK(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
-  //                      GL_COLOR_ATTACHMENT0_EXT,
-  //                      GL_TEXTURE_2D,
-  //                      renderedTexture,
-  //                      0));
-		// 			break;
-		// 		}
-		// 		else if (format == RTF_D24)
-		// 		{
-		// 			GL_CHECK(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height));
-		// 			GL_CHECK(glFramebufferRenderbufferEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_gl_rbo));
-		// 		}
-
-		// 		break;
-		// 	}
-		// 	default:
-		// 	{
-		// 		CE_ASSERT(false, "Oops, render target format not supported!");
-		// 		break;
-		// 	}
-		// }
-
-		// GLenum status = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
-		// CE_ASSERT(status == GL_FRAMEBUFFER_COMPLETE_EXT, "Oops, framebuffer incomplete!");
-
-		// GL_CHECK(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
-
-		// m_width = width;
-		// m_height = height;
-		// m_format = format;
+		m_width = width;
+		m_height = height;
+		m_format = format;
+		m_col_texture = 0;
+		m_fbo = 0;
+		m_rbo = 0;
+
+		GL_CHECK(glGenFramebuffers(1, &m_fbo));
+		CE_ASSERT(m_fbo != 0, "Failed to create frame buffer");
+		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_fbo));
+
+		const bool no_texture = (flags & RENDER_TARGET_NO_TEXTURE) >> RENDER_TARGET_SHIFT;
+		const TextureFormatInfo& tif = TEXTURE_FORMAT_TABLE[format];
+
+		const GLenum attachment = is_depth(tif.internal_format) ? GL_DEPTH_ATTACHMENT :
+									is_color(tif.internal_format) ? GL_COLOR_ATTACHMENT0 : GL_DEPTH_STENCIL_ATTACHMENT;
+
+		if (!no_texture)
+		{
+			GL_CHECK(glGenTextures(1, &m_col_texture));
+			CE_ASSERT(m_col_texture != 0, "Failed to create texture");
+			GL_CHECK(glBindTexture(GL_TEXTURE_2D, m_col_texture));
+			GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, tif.internal_format, width, height, 0, tif.format, GL_UNSIGNED_BYTE, 0));
+
+			GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+			GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+	
+			GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER,
+											attachment,
+											GL_TEXTURE_2D,
+											m_col_texture,
+											0));
+
+			GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
+		}
+		else
+		{
+			GL_CHECK(glGenRenderbuffers(1, &m_rbo));
+			CE_ASSERT(m_rbo != 0, "Failed to create renderbuffer");
+			GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, m_rbo));
+			GL_CHECK(glRenderbufferStorage(GL_RENDERBUFFER, tif.internal_format, width, height));
+			GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, m_rbo));
+			GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0));
+		}
+
+		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+		CE_ASSERT(status == GL_FRAMEBUFFER_COMPLETE, "Oops, framebuffer incomplete!");
+		CE_UNUSED(status);
+
+		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
 	}
 
 	void destroy()
 	{
-		// GL_CHECK(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
-		// GL_CHECK(glDeleteFramebuffersEXT(1, &m_gl_fbo));
-
-		// GL_CHECK(glDeleteRenderbuffersEXT(1, &m_gl_rbo));
+		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+		GL_CHECK(glDeleteTextures(1, &m_col_texture));
+		GL_CHECK(glDeleteFramebuffers(1, &m_fbo));
+		GL_CHECK(glDeleteRenderbuffers(1, &m_rbo));
 	}
 
 	uint16_t m_width;
 	uint16_t m_height;
-	RenderTargetFormat m_format;
-	GLuint m_gl_fbo;
-	GLuint m_gl_rbo;
+	PixelFormat::Enum m_format;
+	GLuint m_col_texture;
+	GLuint m_fbo;
+	GLuint m_rbo;
 };
 
 /// OpenGL renderer
@@ -757,8 +799,8 @@ public:
 	//-----------------------------------------------------------------------------
 	void render(RenderContext& context)
 	{
-		//RenderTargetId old_rt;
-		//old_rt.id = INVALID_ID;
+		RenderTargetId cur_rt;
+		cur_rt.id = INVALID_ID;
 		uint8_t layer = 0xFF;
 
 		// Sort render keys
@@ -774,6 +816,9 @@ public:
 			m_index_buffers[context.m_transient_ib->ib.index].update(0, context.m_tib_offset, context.m_transient_ib->data);
 		}
 
+		// Bind default framebuffer
+		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+
 		for (uint32_t s = 0; s < context.m_num_states; s++)
 		{
 			const uint64_t key_s = context.m_keys[s].key;
@@ -789,6 +834,13 @@ public:
 			{
 				layer = key.m_layer;
 
+				// Switch render target if necessary
+				if (cur_rt != context.m_targets[layer])
+				{
+					cur_rt = context.m_targets[layer];
+					glBindFramebuffer(GL_FRAMEBUFFER, m_render_targets[cur_rt.index].m_fbo);
+				}
+
 				// Viewport
 				const ViewRect& viewport = context.m_viewports[layer];
 				GL_CHECK(glViewport(viewport.m_x, viewport.m_y, viewport.m_width, viewport.m_height));
@@ -898,6 +950,13 @@ public:
 								texture.commit(unit, sampler.flags);
 								break;
 							}
+							case SAMPLER_RENDER_TARGET:
+							{
+								RenderTarget& rt = m_render_targets[sampler.sampler_id.index];
+								GL_CHECK(glActiveTexture(GL_TEXTURE0 + unit));
+								GL_CHECK(glBindTexture(GL_TEXTURE_2D, rt.m_col_texture));
+								break;
+							}
 							default:
 							{
 								CE_ASSERT(false, "Oops, sampler unknown");
@@ -1048,7 +1107,6 @@ Renderer::~Renderer()
 	CE_ASSERT(m_textures.size() == 0, "%d textures not freed", m_textures.size());
 	CE_ASSERT(m_shaders.size() == 0, "%d shaders not freed", m_shaders.size());
 	CE_ASSERT(m_gpu_programs.size() == 0, "%d GPU programs not freed", m_gpu_programs.size());
-	// CE_ASSERT(m_uniforms.size() == 0, "%d uniforms not freed", m_uniforms.size());
 	CE_ASSERT(m_render_targets.size() == 0, "%d render targets not freed", m_render_targets.size());
 
 	CE_DELETE(m_allocator, m_impl);
@@ -1193,16 +1251,17 @@ void Renderer::destroy_uniform_impl(UniformId id)
 	m_uniforms.destroy(id);
 }
 
-// //-----------------------------------------------------------------------------
-// void Renderer::create_render_target_impl(RenderTargetId id, uint16_t width, uint16_t height, RenderTargetFormat::Enum format)
-// {
-
-// }
-
-// //-----------------------------------------------------------------------------
-// void Renderer::destroy_render_target_impl(RenderTargetId id)
-// {
+//-----------------------------------------------------------------------------
+void Renderer::create_render_target_impl(RenderTargetId id, uint16_t width, uint16_t height, PixelFormat::Enum format, uint32_t flags)
+{
+	m_impl->m_render_targets[id.index].create(width, height, format, flags);
+}
 
-// }
+//-----------------------------------------------------------------------------
+void Renderer::destroy_render_target_impl(RenderTargetId id)
+{
+	m_impl->m_render_targets[id.index].destroy();
+	m_render_targets.destroy(id);
+}
 
 } // namespace crown