shithub: cstory

Download patch

ref: 4d2a2270689a8cc836d0297535b3cd91c986ffe1
parent: 8377f011cf71743289f8b1b17b685154fb477a85
author: Clownacy <Clownacy@users.noreply.github.com>
date: Mon Apr 20 23:16:37 EDT 2020

Add render-to-texture support to Wii U renderer

--- a/src/Backends/Rendering/WiiU.cpp
+++ b/src/Backends/Rendering/WiiU.cpp
@@ -5,6 +5,7 @@
 #include <string.h>
 #include <string>
 
+#include <gx2/context.h>
 #include <gx2/draw.h>
 #include <gx2/event.h>
 #include <gx2/registers.h>
@@ -24,8 +25,8 @@
 
 typedef struct RenderBackend_Surface
 {
-	bool is_screen;
 	GX2Texture texture;
+	GX2ColorBuffer colour_buffer;
 	unsigned int width;
 	unsigned int height;
 	unsigned char *lock_buffer;	// TODO - Dumb
@@ -44,128 +45,124 @@
 
 static GX2Sampler sampler;
 
-RenderBackend_Surface *framebuffer;
-static unsigned int framebuffer_width;
-static unsigned int framebuffer_height;
+static RenderBackend_Surface *framebuffer_surface;
 
-static void StartFrameDraw(void)
-{
-	WHBGfxBeginRender();
+static GX2ContextState *gx2_context;
 
-	// Draw to the gamepad
-	WHBGfxBeginRenderDRC();
-	WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-}
-
-static void EndFrameDraw(void)
-{
-	WHBGfxFinishRenderDRC();
-
-	WHBGfxFinishRender();
-}
-
 RenderBackend_Surface* RenderBackend_Init(const char *window_title, int screen_width, int screen_height, bool fullscreen)
 {
 	(void)window_title;
 	(void)fullscreen;
 
-	framebuffer_width = screen_width;
-	framebuffer_height = screen_height;
+	WHBGfxInit();
 
-	framebuffer = (RenderBackend_Surface*)malloc(sizeof(RenderBackend_Surface));
+	// Initialise the shaders
 
-	if (framebuffer != NULL)
+	// Texture shader
+	if (WHBGfxLoadGFDShaderGroup(&texture_shader, 0, rtexture))
 	{
-		framebuffer->is_screen = true;
+		WHBGfxInitShaderAttribute(&texture_shader, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
+		WHBGfxInitShaderAttribute(&texture_shader, "input_texture_coordinates", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
+		WHBGfxInitFetchShader(&texture_shader);
 
-		WHBGfxInit();
-
-		if (WHBGfxLoadGFDShaderGroup(&texture_shader, 0, rtexture))
+		// Texture shader (with colour-key)
+		if (WHBGfxLoadGFDShaderGroup(&texture_colour_key_shader, 0, rtexture_colour_key))
 		{
-			WHBGfxInitShaderAttribute(&texture_shader, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
-			WHBGfxInitShaderAttribute(&texture_shader, "input_texture_coordinates", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
-			WHBGfxInitFetchShader(&texture_shader);
+			WHBGfxInitShaderAttribute(&texture_colour_key_shader, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
+			WHBGfxInitShaderAttribute(&texture_colour_key_shader, "input_texture_coordinates", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
+			WHBGfxInitFetchShader(&texture_colour_key_shader);
 
-			if (WHBGfxLoadGFDShaderGroup(&texture_colour_key_shader, 0, rtexture_colour_key))
+			// Colour-fill shader
+			if (WHBGfxLoadGFDShaderGroup(&colour_fill_shader, 0, rcolour_fill))
 			{
-				WHBGfxInitShaderAttribute(&texture_colour_key_shader, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
-				WHBGfxInitShaderAttribute(&texture_colour_key_shader, "input_texture_coordinates", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
-				WHBGfxInitFetchShader(&texture_colour_key_shader);
+				WHBGfxInitShaderAttribute(&colour_fill_shader, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
+				WHBGfxInitFetchShader(&colour_fill_shader);
 
-				if (WHBGfxLoadGFDShaderGroup(&colour_fill_shader, 0, rcolour_fill))
-				{
-					WHBGfxInitShaderAttribute(&colour_fill_shader, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
-					WHBGfxInitFetchShader(&colour_fill_shader);
+				// Initialise vertex position buffer
+				vertex_position_buffer.flags = (GX2RResourceFlags)(GX2R_RESOURCE_BIND_VERTEX_BUFFER |
+																   GX2R_RESOURCE_USAGE_CPU_READ |
+																   GX2R_RESOURCE_USAGE_CPU_WRITE |
+																   GX2R_RESOURCE_USAGE_GPU_READ);
+				vertex_position_buffer.elemSize = 2 * sizeof(float);
+				vertex_position_buffer.elemCount = 4;
+				GX2RCreateBuffer(&vertex_position_buffer);
 
-					// Initialise vertex position buffer
-					vertex_position_buffer.flags = (GX2RResourceFlags)(GX2R_RESOURCE_BIND_VERTEX_BUFFER |
-																	   GX2R_RESOURCE_USAGE_CPU_READ |
-																	   GX2R_RESOURCE_USAGE_CPU_WRITE |
-																	   GX2R_RESOURCE_USAGE_GPU_READ);
-					vertex_position_buffer.elemSize = 2 * sizeof(float);
-					vertex_position_buffer.elemCount = 4;
-					GX2RCreateBuffer(&vertex_position_buffer);
+				// Initialise texture coordinate buffer
+				texture_coordinate_buffer.flags = (GX2RResourceFlags)(GX2R_RESOURCE_BIND_VERTEX_BUFFER |
+																	  GX2R_RESOURCE_USAGE_CPU_READ |
+																	  GX2R_RESOURCE_USAGE_CPU_WRITE |
+																	  GX2R_RESOURCE_USAGE_GPU_READ);
+				texture_coordinate_buffer.elemSize = 2 * sizeof(float);
+				texture_coordinate_buffer.elemCount = 4;
+				GX2RCreateBuffer(&texture_coordinate_buffer);
 
-					// Initialise texture coordinate buffer
-					texture_coordinate_buffer.flags = (GX2RResourceFlags)(GX2R_RESOURCE_BIND_VERTEX_BUFFER |
-																		  GX2R_RESOURCE_USAGE_CPU_READ |
-																		  GX2R_RESOURCE_USAGE_CPU_WRITE |
-																		  GX2R_RESOURCE_USAGE_GPU_READ);
-					texture_coordinate_buffer.elemSize = 2 * sizeof(float);
-					texture_coordinate_buffer.elemCount = 4;
-					GX2RCreateBuffer(&texture_coordinate_buffer);
+				// Initialise sampler
+				GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_POINT);
 
-					// Initialise sampler
-					GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_POINT);
+				// Create framebuffer surface
+				framebuffer_surface = RenderBackend_CreateSurface(screen_width, screen_height);
 
-					{
-						// Disable depth-test (enabled by default for some reason)
-						GX2SetDepthOnlyControl(FALSE, FALSE, GX2_COMPARE_FUNC_ALWAYS);
+				if (framebuffer_surface != NULL)
+				{
+					// Create a 'context' (this voodoo magic can be used to undo `GX2SetColorBuffer`,
+					// allowing us to draw to the screen once again).
+					gx2_context = (GX2ContextState*)aligned_alloc(GX2_CONTEXT_STATE_ALIGNMENT, sizeof(GX2ContextState));
+					memset(gx2_context, 0, sizeof(GX2ContextState));
+					GX2SetupContextStateEx(gx2_context, TRUE);
+					GX2SetContextState(gx2_context);
 
+					// Disable depth-test (enabled by default for some reason)
+					GX2SetDepthOnlyControl(FALSE, FALSE, GX2_COMPARE_FUNC_ALWAYS);
 
 
-						// Enable blending
-				//		GX2SetColorControl(GX2_LOGIC_OP_COPY, 0xFF, FALSE, TRUE);
 
-						// Set custom blending mode for pre-multiplied alpha
-		/*				GX2SetBlendControl(GX2_RENDER_TARGET_0,
-										   GX2_BLEND_MODE_ZERO,
-										   GX2_BLEND_MODE_ONE,
-										   GX2_BLEND_COMBINE_MODE_ADD,
-										   TRUE,
-										   GX2_BLEND_MODE_ZERO,
-										   GX2_BLEND_MODE_ONE,
-										   GX2_BLEND_COMBINE_MODE_ADD);
-		*/
-						// Do some binding
+					// Enable blending
+			//		GX2SetColorControl(GX2_LOGIC_OP_COPY, 0xFF, FALSE, TRUE);
 
-						StartFrameDraw();
+					// Set custom blending mode for pre-multiplied alpha
+	/*				GX2SetBlendControl(GX2_RENDER_TARGET_0,
+									   GX2_BLEND_MODE_ZERO,
+									   GX2_BLEND_MODE_ONE,
+									   GX2_BLEND_COMBINE_MODE_ADD,
+									   TRUE,
+									   GX2_BLEND_MODE_ZERO,
+									   GX2_BLEND_MODE_ONE,
+									   GX2_BLEND_COMBINE_MODE_ADD);
+	*/
+					// Do some binding
+/*
+					WHBGfxBeginRender();
 
-						return framebuffer;
-					}
-
-					GX2RDestroyBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
-					GX2RDestroyBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
-
-					WHBGfxFreeShaderGroup(&colour_fill_shader);
+					// Draw to the gamepad
+					WHBGfxBeginRenderDRC();
+					WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+*/
+					return framebuffer_surface;
 				}
 
-				WHBGfxFreeShaderGroup(&texture_colour_key_shader);
+				GX2RDestroyBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
+				GX2RDestroyBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
+
+				WHBGfxFreeShaderGroup(&colour_fill_shader);
 			}
 
-			WHBGfxFreeShaderGroup(&texture_shader);
+			WHBGfxFreeShaderGroup(&texture_colour_key_shader);
 		}
 
-		WHBGfxShutdown();
-
-		free(framebuffer);
+		WHBGfxFreeShaderGroup(&texture_shader);
 	}
 
+	WHBGfxShutdown();
+
 	return NULL;
 }
 
 void RenderBackend_Deinit(void)
 {
+	free(gx2_context);
+
+	RenderBackend_FreeSurface(framebuffer_surface);
+
 	GX2RDestroyBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
 	GX2RDestroyBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
 
@@ -174,15 +171,84 @@
 	WHBGfxFreeShaderGroup(&texture_shader);
 
 	WHBGfxShutdown();
-
-	free(framebuffer);
 }
 
 void RenderBackend_DrawScreen(void)
 {
-	EndFrameDraw();
+	// Make sure the buffers aren't currently being used before we modify them
+	GX2DrawDone();
 
-	StartFrameDraw();
+	// Set buffer to full-screen
+	float *position_pointer = (float*)GX2RLockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
+	position_pointer[0] = -1.0f;
+	position_pointer[1] = -1.0f;
+	position_pointer[2] = 1.0f;
+	position_pointer[3] = -1.0f;
+	position_pointer[4] = 1.0f;
+	position_pointer[5] = 1.0f;
+	position_pointer[6] = -1.0f;
+	position_pointer[7] = 1.0f;
+	GX2RUnlockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
+
+	// Set buffer to full-texture
+	float *texture_coordinate_pointer = (float*)GX2RLockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
+	texture_coordinate_pointer[0] = 0.0f;
+	texture_coordinate_pointer[1] = 1.0f;
+	texture_coordinate_pointer[2] = 1.0f;
+	texture_coordinate_pointer[3] = 1.0f;
+	texture_coordinate_pointer[4] = 1.0f;
+	texture_coordinate_pointer[5] = 0.0f;
+	texture_coordinate_pointer[6] = 0.0f;
+	texture_coordinate_pointer[7] = 0.0f;
+	GX2RUnlockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
+
+
+	WHBGfxBeginRender();
+
+	// Draw to the TV
+	WHBGfxBeginRenderTV();
+	WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+	// This might be needed? Not sure.
+//	GX2RInvalidateSurface(&framebuffer_surface->texture.surface, 0, (GX2RResourceFlags)0);
+
+	GX2SetFetchShader(&texture_shader.fetchShader);
+	GX2SetVertexShader(texture_shader.vertexShader);
+	GX2SetPixelShader(texture_shader.pixelShader);
+
+	GX2SetPixelSampler(&sampler, texture_shader.pixelShader->samplerVars[0].location);
+	GX2SetPixelTexture(&framebuffer_surface->texture, texture_shader.pixelShader->samplerVars[0].location);
+	GX2RSetAttributeBuffer(&vertex_position_buffer, 0, vertex_position_buffer.elemSize, 0);
+	GX2RSetAttributeBuffer(&texture_coordinate_buffer, 1, texture_coordinate_buffer.elemSize, 0);
+
+	GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
+
+	WHBGfxFinishRenderTV();
+
+	// Draw to the gamepad
+	WHBGfxBeginRenderDRC();
+	WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+	// This might be needed? Not sure.
+//	GX2RInvalidateSurface(&framebuffer_surface->texture.surface, 0, (GX2RResourceFlags)0);
+
+	GX2SetFetchShader(&texture_shader.fetchShader);
+	GX2SetVertexShader(texture_shader.vertexShader);
+	GX2SetPixelShader(texture_shader.pixelShader);
+
+	GX2SetPixelSampler(&sampler, texture_shader.pixelShader->samplerVars[0].location);
+	GX2SetPixelTexture(&framebuffer_surface->texture, texture_shader.pixelShader->samplerVars[0].location);
+	GX2RSetAttributeBuffer(&vertex_position_buffer, 0, vertex_position_buffer.elemSize, 0);
+	GX2RSetAttributeBuffer(&texture_coordinate_buffer, 1, texture_coordinate_buffer.elemSize, 0);
+
+	GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
+
+	WHBGfxFinishRenderDRC();
+
+	WHBGfxFinishRender();
+
+	// Do this or else the screen will never update. I wish I understood why.
+	GX2SetContextState(gx2_context);
 }
 
 RenderBackend_Surface* RenderBackend_CreateSurface(unsigned int width, unsigned int height)
@@ -191,7 +257,6 @@
 
 	if (surface != NULL)
 	{
-		surface->is_screen = false;
 		surface->width = width;
 		surface->height = height;
 
@@ -210,11 +275,21 @@
 		GX2CalcSurfaceSizeAndAlignment(&surface->texture.surface);
 		GX2InitTextureRegs(&surface->texture);
 
+			// Initialise colour buffer (needed so the texture can be drawn to)
+			memset(&surface->colour_buffer, 0, sizeof(surface->colour_buffer));
+			surface->colour_buffer.surface = surface->texture.surface;
+			surface->colour_buffer.viewNumSlices = 1;
+			GX2InitColorBufferRegs(&surface->colour_buffer);
+
 		if (GX2RCreateSurface(&surface->texture.surface, (GX2RResourceFlags)(GX2R_RESOURCE_BIND_TEXTURE | GX2R_RESOURCE_BIND_COLOR_BUFFER |
 		                                                                     GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_CPU_READ |
 		                                                                     GX2R_RESOURCE_USAGE_GPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ)))
 		{
-			return surface;
+
+			if (GX2RCreateSurfaceUserMemory(&surface->colour_buffer.surface, (uint8_t*)surface->texture.surface.image, (uint8_t*)surface->texture.surface.mipmaps, surface->texture.surface.resourceFlags))
+				return surface;
+
+			GX2RDestroySurfaceEx(&surface->texture.surface, (GX2RResourceFlags)0);
 		}
 
 		free(surface);
@@ -227,6 +302,7 @@
 {
 	if (surface != NULL)
 	{
+		GX2RDestroySurfaceEx(&surface->colour_buffer.surface, (GX2RResourceFlags)0);
 		GX2RDestroySurfaceEx(&surface->texture.surface, (GX2RResourceFlags)0);
 		free(surface);
 	}
@@ -293,11 +369,10 @@
 	if (source_surface == NULL || destination_surface == NULL)
 		return;
 
-	if (source_surface->is_screen || !destination_surface->is_screen)
-		return;
-
+	// Make sure the buffers aren't currently being used before we modify them
 	GX2DrawDone();
 
+	// Set vertex position buffer
 	const float destination_left = x;
 	const float destination_top = y;
 	const float destination_right = x + (rect->right - rect->left);
@@ -312,18 +387,21 @@
 	position_pointer[5] = destination_bottom;
 	position_pointer[6] = destination_left;
 	position_pointer[7] = destination_bottom;
+
 	for (unsigned int i = 0; i < 8; i += 2)
 	{
-		position_pointer[i + 0] /= framebuffer_width;
+		position_pointer[i + 0] /= destination_surface->width;
 		position_pointer[i + 0] *= 2.0f;
 		position_pointer[i + 0] -= 1.0f;
 
-		position_pointer[i + 1] /= framebuffer_height;
+		position_pointer[i + 1] /= destination_surface->height;
 		position_pointer[i + 1] *= -2.0f;
 		position_pointer[i + 1] += 1.0f;
 	}
+
 	GX2RUnlockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
 
+	// Set texture coordinate buffer
 	float *texture_coordinate_pointer = (float*)GX2RLockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
 	texture_coordinate_pointer[0] = rect->left / (float)source_surface->width;
 	texture_coordinate_pointer[1] = rect->top / (float)source_surface->height;
@@ -335,15 +413,26 @@
 	texture_coordinate_pointer[7] = rect->bottom / (float)source_surface->height;
 	GX2RUnlockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
 
-	GX2SetFetchShader(&texture_colour_key_shader.fetchShader);
-	GX2SetVertexShader(texture_colour_key_shader.vertexShader);
-	GX2SetPixelShader(texture_colour_key_shader.pixelShader);
+	// Draw to the selected texture, instead of the screen
+	GX2SetColorBuffer(&destination_surface->colour_buffer, GX2_RENDER_TARGET_0);
+	GX2SetViewport(0.0f, 0.0f, (float)destination_surface->colour_buffer.surface.width, (float)destination_surface->colour_buffer.surface.height, 0.0f, 1.0f);
+	GX2SetScissor(0, 0, destination_surface->colour_buffer.surface.width, destination_surface->colour_buffer.surface.height);
 
-	GX2SetPixelSampler(&sampler, texture_colour_key_shader.pixelShader->samplerVars[0].location);
-	GX2SetPixelTexture(&source_surface->texture, texture_colour_key_shader.pixelShader->samplerVars[0].location);
+	// Select shader
+	WHBGfxShaderGroup *shader = colour_key ? &texture_colour_key_shader : &texture_shader;
+
+	// Bind it
+	GX2SetFetchShader(&shader->fetchShader);
+	GX2SetVertexShader(shader->vertexShader);
+	GX2SetPixelShader(shader->pixelShader);
+
+	// Bind misc. data
+	GX2SetPixelSampler(&sampler, shader->pixelShader->samplerVars[0].location);
+	GX2SetPixelTexture(&source_surface->texture, shader->pixelShader->samplerVars[0].location);
 	GX2RSetAttributeBuffer(&vertex_position_buffer, 0, vertex_position_buffer.elemSize, 0);
 	GX2RSetAttributeBuffer(&texture_coordinate_buffer, 1, texture_coordinate_buffer.elemSize, 0);
 
+	// Draw
 	GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
 }
 
@@ -352,9 +441,7 @@
 	if (surface == NULL)
 		return;
 
-	if (!surface->is_screen)
-		return;
-
+	// Make sure the buffers aren't currently being used before we modify them
 	GX2DrawDone();
 
 	float *position_pointer = (float*)GX2RLockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
@@ -368,15 +455,19 @@
 	position_pointer[7] = rect->bottom;
 	for (unsigned int i = 0; i < 8; i += 2)
 	{
-		position_pointer[i + 0] /= framebuffer_width;
+		position_pointer[i + 0] /= surface->width;
 		position_pointer[i + 0] *= 2.0f;
 		position_pointer[i + 0] -= 1.0f;
 
-		position_pointer[i + 1] /= framebuffer_height;
+		position_pointer[i + 1] /= surface->height;
 		position_pointer[i + 1] *= -2.0f;
 		position_pointer[i + 1] += 1.0f;
 	}
 	GX2RUnlockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
+
+	GX2SetColorBuffer(&surface->colour_buffer, GX2_RENDER_TARGET_0);
+	GX2SetViewport(0, 0, (float)surface->colour_buffer.surface.width, (float)surface->colour_buffer.surface.height, 0.0f, 1.0f);
+	GX2SetScissor(0, 0, (float)surface->colour_buffer.surface.width, (float)surface->colour_buffer.surface.height);
 
 	float uniform_colours[4] = {red / 255.0f, green / 255.0f, blue / 255.0f, 1.0f};