shithub: cstory

Download patch

ref: e93ee47728f4cfb7888ecf4fd8634d18bc27222b
parent: dacd34072ad8bda281cc55136484e1962a3daa3c
author: Clownacy <Clownacy@users.noreply.github.com>
date: Wed May 6 19:02:08 EDT 2020

More backend rearranging

The Window stuff isn't meant for the user, so hide it a bit more

--- /dev/null
+++ b/src/Backends/Rendering/Window/OpenGL.h
@@ -1,0 +1,5 @@
+#pragma once
+
+bool WindowBackend_OpenGL_CreateWindow(const char *window_title, int *screen_width, int *screen_height, bool fullscreen);
+void WindowBackend_OpenGL_DestroyWindow(void);
+void WindowBackend_OpenGL_Display(void);
--- /dev/null
+++ b/src/Backends/Rendering/Window/OpenGL3/GLFW3.cpp
@@ -1,0 +1,94 @@
+#include "../Window-OpenGL.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#ifdef USE_OPENGLES2
+#include <GLES2/gl2.h>
+#else
+#include <glad/glad.h>
+#endif
+#include <GLFW/glfw3.h>
+
+#include "../Misc.h"
+#include "../Shared/GLFW3.h"
+
+GLFWwindow *window;
+
+bool WindowBackend_OpenGL_CreateWindow(const char *window_title, int *screen_width, int *screen_height, bool fullscreen)
+{
+#ifdef USE_OPENGLES2
+	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+#else
+	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
+	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
+	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+#endif
+
+	GLFWmonitor *monitor = NULL;
+
+	if (fullscreen)
+	{
+		monitor = glfwGetPrimaryMonitor();
+
+		if (monitor != NULL)
+		{
+			const GLFWvidmode *mode = glfwGetVideoMode(monitor);
+
+			*screen_width = mode->width;
+			*screen_height = mode->height;
+		}
+	}
+
+	window = glfwCreateWindow(*screen_width, *screen_height, window_title, monitor, NULL);
+
+	if (window != NULL)
+	{
+		glfwMakeContextCurrent(window);
+
+			#ifndef USE_OPENGLES2
+				if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
+				{
+					// Check if the platform supports OpenGL 3.2
+					if (GLAD_GL_VERSION_3_2)
+					{
+			#endif
+						Backend_PostWindowCreation();
+
+						return true;
+			#ifndef USE_OPENGLES2
+					}
+					else
+					{
+						Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Your system does not support OpenGL 3.2");
+					}
+				}
+				else
+				{
+					Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not initialize OpenGL context");
+				}
+			#endif
+
+		glfwDestroyWindow(window);
+	}
+	else
+	{
+		Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not create window");
+	}
+
+	return false;
+}
+
+void WindowBackend_OpenGL_DestroyWindow(void)
+{
+	glfwDestroyWindow(window);
+}
+
+void WindowBackend_OpenGL_Display(void)
+{
+	glfwSwapBuffers(window);
+}
--- /dev/null
+++ b/src/Backends/Rendering/Window/OpenGL3/SDL2.cpp
@@ -1,0 +1,116 @@
+#include "../Window-OpenGL.h"
+
+#include <stddef.h>
+#include <string>
+
+#ifdef USE_OPENGLES2
+#include <GLES2/gl2.h>
+#else
+#include <glad/glad.h>
+#endif
+#include "SDL.h"
+
+#include "../Misc.h"
+#include "../Shared/SDL2.h"
+#include "../../Resource.h"
+
+SDL_Window *window;
+
+static SDL_GLContext context;
+
+bool WindowBackend_OpenGL_CreateWindow(const char *window_title, int *screen_width, int *screen_height, bool fullscreen)
+{
+#ifdef USE_OPENGLES2
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES) < 0)
+		Backend_PrintError("Couldn't set OpenGL context type to ES: %s", SDL_GetError());
+
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0) < 0)
+		Backend_PrintError("Couldn't set OpenGL context flags to 0: %s", SDL_GetError());
+
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2) < 0)
+		Backend_PrintError("Couldn't set OpenGL major version to 2: %s", SDL_GetError());
+
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0) < 0)
+		Backend_PrintError("Couldn't set OpenGL minor version to 0: %s", SDL_GetError());
+#else
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) < 0)
+		Backend_PrintError("Couldn't set OpenGL context type to core: %s", SDL_GetError());
+
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG) < 0)
+		Backend_PrintError("Couldn't set OpenGL forward compatibility: %s", SDL_GetError());
+
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0)
+		Backend_PrintError("Couldn't set OpenGL major version to 3: %s", SDL_GetError());
+
+	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2) < 0)
+		Backend_PrintError("Couldn't set OpenGL minor verison to 2: %s", SDL_GetError());
+#endif
+
+	window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, *screen_width, *screen_height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | (fullscreen ? SDL_WINDOW_FULLSCREEN : 0));
+
+	if (window != NULL)
+	{
+		context = SDL_GL_CreateContext(window);
+
+		if (context != NULL)
+		{
+			if (SDL_GL_MakeCurrent(window, context) == 0)
+			{
+			#ifndef USE_OPENGLES2
+				if (gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
+				{
+					// Check if the platform supports OpenGL 3.2
+					if (GLAD_GL_VERSION_3_2)
+					{
+			#endif
+						Backend_PostWindowCreation();
+
+						return true;
+			#ifndef USE_OPENGLES2
+					}
+					else
+					{
+						Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Your system does not support OpenGL 3.2");
+					}
+				}
+				else
+				{
+					Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Couldn't load OpenGL functions");
+				}
+			#endif
+			}
+			else
+			{
+				std::string error_message = std::string("Couldn't setup OpenGL context for rendering: ") + SDL_GetError();
+				Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "SDL_GL_MakeCurrent failed");
+			}
+
+			SDL_GL_DeleteContext(context);
+		}
+		else
+		{
+			std::string error_message = std::string("Couldn't create OpenGL context: %s", SDL_GetError());
+			Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not create OpenGL context");
+		}
+
+		SDL_DestroyWindow(window);
+	}
+	else
+	{
+		std::string error_message = std::string("Could not create window: ") + SDL_GetError();
+		Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", error_message.c_str());
+	}
+
+	return false;
+}
+
+void WindowBackend_OpenGL_DestroyWindow(void)
+{
+	SDL_GL_DeleteContext(context);
+	SDL_DestroyWindow(window);
+}
+
+void WindowBackend_OpenGL_Display(void)
+{
+	SDL_GL_SwapWindow(window);
+}
--- /dev/null
+++ b/src/Backends/Rendering/Window/OpenGLES2/GLFW3.cpp
@@ -1,0 +1,2 @@
+#define USE_OPENGLES2
+#include "Window-OpenGL3.cpp"
--- /dev/null
+++ b/src/Backends/Rendering/Window/OpenGLES2/SDL2.cpp
@@ -1,0 +1,2 @@
+#define USE_OPENGLES2
+#include "Window-OpenGL3.cpp"
--- /dev/null
+++ b/src/Backends/Rendering/Window/Software.h
@@ -1,0 +1,9 @@
+#pragma once
+
+#include <stddef.h>
+
+bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen);
+void WindowBackend_Software_DestroyWindow(void);
+unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch);
+void WindowBackend_Software_Display(void);
+void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height);
--- /dev/null
+++ b/src/Backends/Rendering/Window/Software/GLFW3.cpp
@@ -1,0 +1,156 @@
+#include "../Window-Software.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#if defined(__APPLE__)
+ #include <OpenGL/gl.h>
+#else
+ #include <GL/gl.h>
+#endif
+#include <GLFW/glfw3.h>
+
+#include "../Misc.h"
+#include "../Shared/GLFW3.h"
+
+GLFWwindow *window;
+
+static unsigned char *framebuffer;
+static int framebuffer_width;
+static int framebuffer_height;
+
+static float framebuffer_x_ratio;
+static float framebuffer_y_ratio;
+
+static GLuint screen_texture_id;
+
+bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen)
+{
+	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
+	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
+	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+	framebuffer_width = screen_width;
+	framebuffer_height = screen_height;
+
+	GLFWmonitor *monitor = NULL;
+
+	if (fullscreen)
+	{
+		monitor = glfwGetPrimaryMonitor();
+
+		if (monitor != NULL)
+		{
+			const GLFWvidmode *mode = glfwGetVideoMode(monitor);
+
+			screen_width = mode->width;
+			screen_height = mode->height;
+		}
+	}
+
+	window = glfwCreateWindow(screen_width, screen_height, window_title, monitor, NULL);
+
+	if (window != NULL)
+	{
+		glfwMakeContextCurrent(window);
+
+		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+		glEnable(GL_TEXTURE_2D);
+
+		WindowBackend_Software_HandleWindowResize(screen_width, screen_height);
+
+		// Create screen texture
+		glGenTextures(1, &screen_texture_id);
+		glBindTexture(GL_TEXTURE_2D, screen_texture_id);
+
+		int framebuffer_texture_width = 1;
+		while (framebuffer_texture_width < framebuffer_width)
+			framebuffer_texture_width <<= 1;
+
+		int framebuffer_texture_height = 1;
+		while (framebuffer_texture_height < framebuffer_height)
+			framebuffer_texture_height <<= 1;
+
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, framebuffer_texture_width, framebuffer_texture_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+		framebuffer_x_ratio = (float)framebuffer_width / framebuffer_texture_width;
+		framebuffer_y_ratio = (float)framebuffer_height / framebuffer_texture_height;
+
+		framebuffer = (unsigned char*)malloc(framebuffer_width * framebuffer_height * 3);
+
+		Backend_PostWindowCreation();
+
+		return true;
+	}
+	else
+	{
+		Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not create window");
+	}
+
+	return false;
+}
+
+void WindowBackend_Software_DestroyWindow(void)
+{
+	free(framebuffer);
+	glDeleteTextures(1, &screen_texture_id);
+	glfwDestroyWindow(window);
+}
+
+unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
+{
+	*pitch = framebuffer_width * 3;
+
+	return framebuffer;
+}
+
+void WindowBackend_Software_Display(void)
+{
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer_width, framebuffer_height, GL_RGB, GL_UNSIGNED_BYTE, framebuffer);
+
+	glBegin(GL_TRIANGLE_STRIP);
+		glTexCoord2f(0.0f, framebuffer_y_ratio);
+		glVertex2f(-1.0f, -1.0f);
+		glTexCoord2f(framebuffer_x_ratio, framebuffer_y_ratio);
+		glVertex2f(1.0f, -1.0f);
+		glTexCoord2f(0.0f, 0.0f);
+		glVertex2f(-1.0f, 1.0f);
+		glTexCoord2f(framebuffer_x_ratio, 0.0f);
+		glVertex2f(1.0f, 1.0f);
+	glEnd();
+
+	glfwSwapBuffers(window);
+}
+
+void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
+{
+	// Do some viewport trickery, to fit the framebuffer in the center of the screen
+	GLint viewport_x;
+	GLint viewport_y;
+	GLsizei viewport_width;
+	GLsizei viewport_height;
+
+	if ((float)width / (float)height > (float)framebuffer_width / (float)framebuffer_height)
+	{
+		viewport_y = 0;
+		viewport_height = height;
+
+		viewport_width = framebuffer_width * ((float)height / (float)framebuffer_height);
+		viewport_x = (width - viewport_width) / 2;
+	}
+	else
+	{
+		viewport_x = 0;
+		viewport_width = width;
+
+		viewport_height = framebuffer_height * ((float)width / (float)framebuffer_width);
+		viewport_y = (height - viewport_height) / 2;
+	}
+
+	glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
+}
--- /dev/null
+++ b/src/Backends/Rendering/Window/Software/Null.cpp
@@ -1,0 +1,39 @@
+#include "../Window-Software.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+static unsigned char *framebuffer;
+
+unsigned char* WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen, size_t *pitch)
+{
+	(void)window_title;
+	(void)fullscreen;
+
+	framebuffer = (unsigned char*)malloc(screen_width * screen_height * 3);
+
+	if (framebuffer != NULL)
+	{
+		*pitch = screen_width * 3;
+
+		return framebuffer;
+	}
+
+	return NULL;
+}
+
+void WindowBackend_Software_DestroyWindow(void)
+{
+	free(framebuffer);
+}
+
+void WindowBackend_Software_Display(void)
+{
+	
+}
+
+void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
+{
+	(void)width;
+	(void)height;
+}
--- /dev/null
+++ b/src/Backends/Rendering/Window/Software/SDL2.cpp
@@ -1,0 +1,95 @@
+#include "../Window-Software.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string>
+
+#include "SDL.h"
+
+#include "../Misc.h"
+#include "../Shared/SDL2.h"
+
+SDL_Window *window;
+
+static SDL_Surface *window_sdlsurface;
+static SDL_Surface *framebuffer_sdlsurface;
+
+bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen)
+{
+	window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, screen_width, screen_height, 0);
+
+	if (window != NULL)
+	{
+		if (fullscreen)
+			if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) < 0)
+				Backend_PrintError("Couldn't set window to fullscree: %s", SDL_GetError());
+
+		window_sdlsurface = SDL_GetWindowSurface(window);
+
+		if (window_sdlsurface != NULL)
+		{
+
+			framebuffer_sdlsurface = SDL_CreateRGBSurfaceWithFormat(0, window_sdlsurface->w, window_sdlsurface->h, 0, SDL_PIXELFORMAT_RGB24);
+
+			if (framebuffer_sdlsurface != NULL)
+			{
+				Backend_PostWindowCreation();
+
+				return true;
+			}
+			else
+			{
+				std::string error_message = std::string("Couldn't create framebuffer surface: ") + SDL_GetError();
+				Backend_ShowMessageBox("Fatal error (software rendering backend)", error_message.c_str());
+				SDL_DestroyWindow(window);
+			}
+		}
+		else
+		{
+			std::string error_message = std::string("Couldn't get SDL surface associated with window: ") + SDL_GetError();
+			Backend_ShowMessageBox("Fatal error (software rendering backend)", error_message.c_str());
+		}
+	}
+	else
+	{
+		std::string error_message = std::string("Couldn't create window: ") + SDL_GetError();
+		Backend_ShowMessageBox("Fatal error (software rendering backend)", error_message.c_str());
+	}
+
+	return false;
+}
+
+void WindowBackend_Software_DestroyWindow(void)
+{
+	SDL_FreeSurface(framebuffer_sdlsurface);
+	SDL_DestroyWindow(window);
+}
+
+unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
+{
+	*pitch = framebuffer_sdlsurface->pitch;
+
+	return (unsigned char*)framebuffer_sdlsurface->pixels;
+}
+
+void WindowBackend_Software_Display(void)
+{
+	if (SDL_BlitSurface(framebuffer_sdlsurface, NULL, window_sdlsurface, NULL) < 0)
+		Backend_PrintError("Couldn't blit framebuffer surface to window surface: %s", SDL_GetError());
+
+	if (SDL_UpdateWindowSurface(window) < 0)
+		Backend_PrintError("Couldn't copy window surface to the screen: %s", SDL_GetError());
+}
+
+void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
+{
+	(void)width;
+	(void)height;
+
+	// https://wiki.libsdl.org/SDL_GetWindowSurface
+	// We need to fetch a new surface pointer
+	window_sdlsurface = SDL_GetWindowSurface(window);
+
+	if (window_sdlsurface == NULL)
+		Backend_PrintError("Couldn't get SDL surface associated with window: %s", SDL_GetError());
+}
--- /dev/null
+++ b/src/Backends/Rendering/Window/Software/WiiU.cpp
@@ -1,0 +1,269 @@
+// Sexy new backend that bounces the software-rendered frame to the GPU,
+// eliminating V-tearing, and gaining support for rendering to the TV for
+// free!
+
+#include "../Window-Software.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gx2/display.h>
+#include <gx2/draw.h>
+#include <gx2/registers.h>
+#include <gx2/sampler.h>
+#include <gx2/texture.h>
+#include <gx2r/buffer.h>
+#include <gx2r/draw.h>
+#include <gx2r/resource.h>
+#include <gx2r/surface.h>
+#include <whb/gfx.h>
+
+#include "../../Attributes.h"
+
+#include "WiiUShaders/texture.gsh.h"
+
+typedef struct Viewport
+{
+	float x;
+	float y;
+	float width;
+	float height;
+} Viewport;
+
+static unsigned char *fake_framebuffer;
+
+static size_t fake_framebuffer_width;
+static size_t fake_framebuffer_height;
+
+static WHBGfxShaderGroup shader_group;
+
+static GX2RBuffer vertex_position_buffer;
+static GX2RBuffer texture_coordinate_buffer;
+
+static GX2Sampler sampler;
+
+static GX2Texture screen_texture;
+
+static Viewport tv_viewport;
+static Viewport drc_viewport;
+
+static void CalculateViewport(unsigned int actual_screen_width, unsigned int actual_screen_height, Viewport *viewport)
+{
+	if ((float)actual_screen_width / (float)actual_screen_height > (float)fake_framebuffer_width / (float)fake_framebuffer_height)
+	{
+		viewport->y = 0.0f;
+		viewport->height = actual_screen_height;
+
+		viewport->width = fake_framebuffer_width * ((float)actual_screen_height / (float)fake_framebuffer_height);
+		viewport->x = (actual_screen_width - viewport->width) / 2;
+	}
+	else
+	{
+		viewport->x = 0.0f;
+		viewport->width = actual_screen_width;
+
+		viewport->height = fake_framebuffer_height * ((float)actual_screen_width / (float)fake_framebuffer_width);
+		viewport->y = (actual_screen_height - viewport->height) / 2;
+	}
+}
+
+bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen)
+{
+	(void)window_title;
+	(void)fullscreen;
+
+	fake_framebuffer_width = screen_width;
+	fake_framebuffer_height = screen_height;
+
+	fake_framebuffer = (unsigned char*)malloc(fake_framebuffer_width * fake_framebuffer_height * 3);
+
+	if (fake_framebuffer != NULL)
+	{
+		WHBGfxInit();
+
+		if (WHBGfxLoadGFDShaderGroup(&shader_group, 0, rtexture))
+		{
+			WHBGfxInitShaderAttribute(&shader_group, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
+			WHBGfxInitShaderAttribute(&shader_group, "input_texture_coordinates", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
+			WHBGfxInitFetchShader(&shader_group);
+
+			// Initialise vertex position buffer
+			const float vertex_positions[4][2] = {
+				{-1.0f,  1.0f},
+				{ 1.0f,  1.0f},
+				{ 1.0f, -1.0f},
+				{-1.0f, -1.0f}
+			};
+
+			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 = sizeof(vertex_positions[0]);
+			vertex_position_buffer.elemCount = sizeof(vertex_positions) / sizeof(vertex_positions[0]);
+			GX2RCreateBuffer(&vertex_position_buffer);
+			memcpy(GX2RLockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0), vertex_positions, sizeof(vertex_positions));
+			GX2RUnlockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
+
+			// Initialise texture coordinate buffer
+			const float texture_coordinates[4][2] = {
+				{0.0f, 0.0f},
+				{1.0f, 0.0f},
+				{1.0f, 1.0f},
+				{0.0f, 1.0f}
+			};
+
+			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 = sizeof(texture_coordinates[0]);
+			texture_coordinate_buffer.elemCount = sizeof(texture_coordinates) / sizeof(texture_coordinates[0]);
+			GX2RCreateBuffer(&texture_coordinate_buffer);
+			memcpy(GX2RLockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0), texture_coordinates, sizeof(texture_coordinates));
+			GX2RUnlockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
+
+			// Initialise sampler
+			GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_POINT);
+
+			// Initialise screen texture
+			screen_texture.surface.width = fake_framebuffer_width;
+			screen_texture.surface.height = fake_framebuffer_height;
+			screen_texture.surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
+			screen_texture.surface.depth = 1;
+			screen_texture.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
+			screen_texture.surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
+			screen_texture.surface.mipLevels = 1;
+			screen_texture.viewNumMips = 1;
+			screen_texture.viewNumSlices = 1;
+			screen_texture.compMap = 0x00010203;
+			GX2CalcSurfaceSizeAndAlignment(&screen_texture.surface);
+			GX2InitTextureRegs(&screen_texture);
+
+			if (GX2RCreateSurface(&screen_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)))
+			{
+				// Do some binding
+				GX2SetPixelSampler(&sampler, shader_group.pixelShader->samplerVars[0].location);
+				GX2SetPixelTexture(&screen_texture, shader_group.pixelShader->samplerVars[0].location);
+				GX2RSetAttributeBuffer(&vertex_position_buffer, 0, vertex_position_buffer.elemSize, 0);
+				GX2RSetAttributeBuffer(&texture_coordinate_buffer, 1, texture_coordinate_buffer.elemSize, 0);
+
+				// Calculate centred viewports
+				switch (GX2GetSystemTVScanMode())
+				{
+					// For now, we have to match WUT's broken behaviour (its `GX2TVScanMode`
+					// enum is missing values, and the rest are off-by-one)
+					//case GX2_TV_SCAN_MODE_576I:
+					case GX2_TV_SCAN_MODE_480I:	// Actually 576i
+					case GX2_TV_SCAN_MODE_480P:	// Actually 480i
+						CalculateViewport(854, 480, &tv_viewport);
+						break;
+
+					case GX2_TV_SCAN_MODE_720P:	// Actually 480p
+					default:	// Funnel the *real* 1080p into this
+						CalculateViewport(1280, 720, &tv_viewport);
+						break;
+
+					case GX2_TV_SCAN_MODE_1080I:	// Actually invalid
+					case GX2_TV_SCAN_MODE_1080P:	// Actually 1080i
+						CalculateViewport(1920, 1080, &tv_viewport);
+						break;
+				}
+
+				CalculateViewport(854, 480, &drc_viewport);
+
+				return true;
+			}
+
+			GX2RDestroyBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
+			GX2RDestroyBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
+
+			WHBGfxFreeShaderGroup(&shader_group);
+		}
+
+		WHBGfxShutdown();
+
+		free(fake_framebuffer);
+	}
+
+	return false;
+}
+
+void WindowBackend_Software_DestroyWindow(void)
+{
+	GX2RDestroySurfaceEx(&screen_texture.surface, (GX2RResourceFlags)0);
+
+	GX2RDestroyBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
+	GX2RDestroyBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
+
+	WHBGfxFreeShaderGroup(&shader_group);
+
+	WHBGfxShutdown();
+
+	free(fake_framebuffer);
+}
+
+unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
+{
+	*pitch = fake_framebuffer_width * 3;
+
+	return fake_framebuffer;
+}
+
+ATTRIBUTE_HOT void WindowBackend_Software_Display(void)
+{
+	// Convert frame from RGB24 to RGBA32, and upload it to the GPU texture
+	unsigned char *framebuffer = (unsigned char*)GX2RLockSurfaceEx(&screen_texture.surface, 0, (GX2RResourceFlags)0);
+
+	const unsigned char *in_pointer = fake_framebuffer;
+
+	for (size_t y = 0; y < fake_framebuffer_height; ++y)
+	{
+		unsigned char *out_pointer = &framebuffer[screen_texture.surface.pitch * 4 * y];
+
+		for (size_t x = 0; x < fake_framebuffer_width; ++x)
+		{
+			*out_pointer++ = *in_pointer++;
+			*out_pointer++ = *in_pointer++;
+			*out_pointer++ = *in_pointer++;
+			*out_pointer++ = 0;
+		}
+	}
+
+	GX2RUnlockSurfaceEx(&screen_texture.surface, 0, (GX2RResourceFlags)0);
+
+	WHBGfxBeginRender();
+
+	// Draw to the TV
+	WHBGfxBeginRenderTV();
+	GX2SetViewport(tv_viewport.x, tv_viewport.y, tv_viewport.width, tv_viewport.height, 0.0f, 1.0f);
+	WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+	GX2SetFetchShader(&shader_group.fetchShader);
+	GX2SetVertexShader(shader_group.vertexShader);
+	GX2SetPixelShader(shader_group.pixelShader);
+	GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
+	WHBGfxFinishRenderTV();
+
+	// Draw to the gamepad
+	WHBGfxBeginRenderDRC();
+	GX2SetViewport(drc_viewport.x, drc_viewport.y, drc_viewport.width, drc_viewport.height, 0.0f, 1.0f);
+	WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+	GX2SetFetchShader(&shader_group.fetchShader);
+	GX2SetVertexShader(shader_group.vertexShader);
+	GX2SetPixelShader(shader_group.pixelShader);
+	GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
+	WHBGfxFinishRenderDRC();
+
+	WHBGfxFinishRender();
+}
+
+void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
+{
+	(void)width;
+	(void)height;
+
+	// The window doesn't resize on the Wii U
+}
--- a/src/Backends/Window-OpenGL.h
+++ /dev/null
@@ -1,5 +1,0 @@
-#pragma once
-
-bool WindowBackend_OpenGL_CreateWindow(const char *window_title, int *screen_width, int *screen_height, bool fullscreen);
-void WindowBackend_OpenGL_DestroyWindow(void);
-void WindowBackend_OpenGL_Display(void);
--- a/src/Backends/Window-Software.h
+++ /dev/null
@@ -1,9 +1,0 @@
-#pragma once
-
-#include <stddef.h>
-
-bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen);
-void WindowBackend_Software_DestroyWindow(void);
-unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch);
-void WindowBackend_Software_Display(void);
-void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height);
--- a/src/Backends/Window/GLFW3-OpenGL3.cpp
+++ /dev/null
@@ -1,94 +1,0 @@
-#include "../Window-OpenGL.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#ifdef USE_OPENGLES2
-#include <GLES2/gl2.h>
-#else
-#include <glad/glad.h>
-#endif
-#include <GLFW/glfw3.h>
-
-#include "../Misc.h"
-#include "../Shared/GLFW3.h"
-
-GLFWwindow *window;
-
-bool WindowBackend_OpenGL_CreateWindow(const char *window_title, int *screen_width, int *screen_height, bool fullscreen)
-{
-#ifdef USE_OPENGLES2
-	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
-#else
-	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
-	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
-#endif
-
-	GLFWmonitor *monitor = NULL;
-
-	if (fullscreen)
-	{
-		monitor = glfwGetPrimaryMonitor();
-
-		if (monitor != NULL)
-		{
-			const GLFWvidmode *mode = glfwGetVideoMode(monitor);
-
-			*screen_width = mode->width;
-			*screen_height = mode->height;
-		}
-	}
-
-	window = glfwCreateWindow(*screen_width, *screen_height, window_title, monitor, NULL);
-
-	if (window != NULL)
-	{
-		glfwMakeContextCurrent(window);
-
-			#ifndef USE_OPENGLES2
-				if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
-				{
-					// Check if the platform supports OpenGL 3.2
-					if (GLAD_GL_VERSION_3_2)
-					{
-			#endif
-						Backend_PostWindowCreation();
-
-						return true;
-			#ifndef USE_OPENGLES2
-					}
-					else
-					{
-						Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Your system does not support OpenGL 3.2");
-					}
-				}
-				else
-				{
-					Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not initialize OpenGL context");
-				}
-			#endif
-
-		glfwDestroyWindow(window);
-	}
-	else
-	{
-		Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not create window");
-	}
-
-	return false;
-}
-
-void WindowBackend_OpenGL_DestroyWindow(void)
-{
-	glfwDestroyWindow(window);
-}
-
-void WindowBackend_OpenGL_Display(void)
-{
-	glfwSwapBuffers(window);
-}
--- a/src/Backends/Window/GLFW3-OpenGLES2.cpp
+++ /dev/null
@@ -1,2 +1,0 @@
-#define USE_OPENGLES2
-#include "Window-OpenGL3.cpp"
--- a/src/Backends/Window/GLFW3-Software.cpp
+++ /dev/null
@@ -1,156 +1,0 @@
-#include "../Window-Software.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#if defined(__APPLE__)
- #include <OpenGL/gl.h>
-#else
- #include <GL/gl.h>
-#endif
-#include <GLFW/glfw3.h>
-
-#include "../Misc.h"
-#include "../Shared/GLFW3.h"
-
-GLFWwindow *window;
-
-static unsigned char *framebuffer;
-static int framebuffer_width;
-static int framebuffer_height;
-
-static float framebuffer_x_ratio;
-static float framebuffer_y_ratio;
-
-static GLuint screen_texture_id;
-
-bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen)
-{
-	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
-
-	framebuffer_width = screen_width;
-	framebuffer_height = screen_height;
-
-	GLFWmonitor *monitor = NULL;
-
-	if (fullscreen)
-	{
-		monitor = glfwGetPrimaryMonitor();
-
-		if (monitor != NULL)
-		{
-			const GLFWvidmode *mode = glfwGetVideoMode(monitor);
-
-			screen_width = mode->width;
-			screen_height = mode->height;
-		}
-	}
-
-	window = glfwCreateWindow(screen_width, screen_height, window_title, monitor, NULL);
-
-	if (window != NULL)
-	{
-		glfwMakeContextCurrent(window);
-
-		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-
-		glEnable(GL_TEXTURE_2D);
-
-		WindowBackend_Software_HandleWindowResize(screen_width, screen_height);
-
-		// Create screen texture
-		glGenTextures(1, &screen_texture_id);
-		glBindTexture(GL_TEXTURE_2D, screen_texture_id);
-
-		int framebuffer_texture_width = 1;
-		while (framebuffer_texture_width < framebuffer_width)
-			framebuffer_texture_width <<= 1;
-
-		int framebuffer_texture_height = 1;
-		while (framebuffer_texture_height < framebuffer_height)
-			framebuffer_texture_height <<= 1;
-
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, framebuffer_texture_width, framebuffer_texture_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-		framebuffer_x_ratio = (float)framebuffer_width / framebuffer_texture_width;
-		framebuffer_y_ratio = (float)framebuffer_height / framebuffer_texture_height;
-
-		framebuffer = (unsigned char*)malloc(framebuffer_width * framebuffer_height * 3);
-
-		Backend_PostWindowCreation();
-
-		return true;
-	}
-	else
-	{
-		Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not create window");
-	}
-
-	return false;
-}
-
-void WindowBackend_Software_DestroyWindow(void)
-{
-	free(framebuffer);
-	glDeleteTextures(1, &screen_texture_id);
-	glfwDestroyWindow(window);
-}
-
-unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
-{
-	*pitch = framebuffer_width * 3;
-
-	return framebuffer;
-}
-
-void WindowBackend_Software_Display(void)
-{
-	glClear(GL_COLOR_BUFFER_BIT);
-
-	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer_width, framebuffer_height, GL_RGB, GL_UNSIGNED_BYTE, framebuffer);
-
-	glBegin(GL_TRIANGLE_STRIP);
-		glTexCoord2f(0.0f, framebuffer_y_ratio);
-		glVertex2f(-1.0f, -1.0f);
-		glTexCoord2f(framebuffer_x_ratio, framebuffer_y_ratio);
-		glVertex2f(1.0f, -1.0f);
-		glTexCoord2f(0.0f, 0.0f);
-		glVertex2f(-1.0f, 1.0f);
-		glTexCoord2f(framebuffer_x_ratio, 0.0f);
-		glVertex2f(1.0f, 1.0f);
-	glEnd();
-
-	glfwSwapBuffers(window);
-}
-
-void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
-{
-	// Do some viewport trickery, to fit the framebuffer in the center of the screen
-	GLint viewport_x;
-	GLint viewport_y;
-	GLsizei viewport_width;
-	GLsizei viewport_height;
-
-	if ((float)width / (float)height > (float)framebuffer_width / (float)framebuffer_height)
-	{
-		viewport_y = 0;
-		viewport_height = height;
-
-		viewport_width = framebuffer_width * ((float)height / (float)framebuffer_height);
-		viewport_x = (width - viewport_width) / 2;
-	}
-	else
-	{
-		viewport_x = 0;
-		viewport_width = width;
-
-		viewport_height = framebuffer_height * ((float)width / (float)framebuffer_width);
-		viewport_y = (height - viewport_height) / 2;
-	}
-
-	glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
-}
--- a/src/Backends/Window/Null-Software.cpp
+++ /dev/null
@@ -1,39 +1,0 @@
-#include "../Window-Software.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-static unsigned char *framebuffer;
-
-unsigned char* WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen, size_t *pitch)
-{
-	(void)window_title;
-	(void)fullscreen;
-
-	framebuffer = (unsigned char*)malloc(screen_width * screen_height * 3);
-
-	if (framebuffer != NULL)
-	{
-		*pitch = screen_width * 3;
-
-		return framebuffer;
-	}
-
-	return NULL;
-}
-
-void WindowBackend_Software_DestroyWindow(void)
-{
-	free(framebuffer);
-}
-
-void WindowBackend_Software_Display(void)
-{
-	
-}
-
-void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
-{
-	(void)width;
-	(void)height;
-}
--- a/src/Backends/Window/SDL2-OpenGL3.cpp
+++ /dev/null
@@ -1,116 +1,0 @@
-#include "../Window-OpenGL.h"
-
-#include <stddef.h>
-#include <string>
-
-#ifdef USE_OPENGLES2
-#include <GLES2/gl2.h>
-#else
-#include <glad/glad.h>
-#endif
-#include "SDL.h"
-
-#include "../Misc.h"
-#include "../Shared/SDL2.h"
-#include "../../Resource.h"
-
-SDL_Window *window;
-
-static SDL_GLContext context;
-
-bool WindowBackend_OpenGL_CreateWindow(const char *window_title, int *screen_width, int *screen_height, bool fullscreen)
-{
-#ifdef USE_OPENGLES2
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES) < 0)
-		Backend_PrintError("Couldn't set OpenGL context type to ES: %s", SDL_GetError());
-
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0) < 0)
-		Backend_PrintError("Couldn't set OpenGL context flags to 0: %s", SDL_GetError());
-
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2) < 0)
-		Backend_PrintError("Couldn't set OpenGL major version to 2: %s", SDL_GetError());
-
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0) < 0)
-		Backend_PrintError("Couldn't set OpenGL minor version to 0: %s", SDL_GetError());
-#else
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) < 0)
-		Backend_PrintError("Couldn't set OpenGL context type to core: %s", SDL_GetError());
-
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG) < 0)
-		Backend_PrintError("Couldn't set OpenGL forward compatibility: %s", SDL_GetError());
-
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0)
-		Backend_PrintError("Couldn't set OpenGL major version to 3: %s", SDL_GetError());
-
-	if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2) < 0)
-		Backend_PrintError("Couldn't set OpenGL minor verison to 2: %s", SDL_GetError());
-#endif
-
-	window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, *screen_width, *screen_height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | (fullscreen ? SDL_WINDOW_FULLSCREEN : 0));
-
-	if (window != NULL)
-	{
-		context = SDL_GL_CreateContext(window);
-
-		if (context != NULL)
-		{
-			if (SDL_GL_MakeCurrent(window, context) == 0)
-			{
-			#ifndef USE_OPENGLES2
-				if (gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
-				{
-					// Check if the platform supports OpenGL 3.2
-					if (GLAD_GL_VERSION_3_2)
-					{
-			#endif
-						Backend_PostWindowCreation();
-
-						return true;
-			#ifndef USE_OPENGLES2
-					}
-					else
-					{
-						Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Your system does not support OpenGL 3.2");
-					}
-				}
-				else
-				{
-					Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Couldn't load OpenGL functions");
-				}
-			#endif
-			}
-			else
-			{
-				std::string error_message = std::string("Couldn't setup OpenGL context for rendering: ") + SDL_GetError();
-				Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "SDL_GL_MakeCurrent failed");
-			}
-
-			SDL_GL_DeleteContext(context);
-		}
-		else
-		{
-			std::string error_message = std::string("Couldn't create OpenGL context: %s", SDL_GetError());
-			Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", "Could not create OpenGL context");
-		}
-
-		SDL_DestroyWindow(window);
-	}
-	else
-	{
-		std::string error_message = std::string("Could not create window: ") + SDL_GetError();
-		Backend_ShowMessageBox("Fatal error (OpenGL rendering backend)", error_message.c_str());
-	}
-
-	return false;
-}
-
-void WindowBackend_OpenGL_DestroyWindow(void)
-{
-	SDL_GL_DeleteContext(context);
-	SDL_DestroyWindow(window);
-}
-
-void WindowBackend_OpenGL_Display(void)
-{
-	SDL_GL_SwapWindow(window);
-}
--- a/src/Backends/Window/SDL2-OpenGLES2.cpp
+++ /dev/null
@@ -1,2 +1,0 @@
-#define USE_OPENGLES2
-#include "Window-OpenGL3.cpp"
--- a/src/Backends/Window/SDL2-Software.cpp
+++ /dev/null
@@ -1,95 +1,0 @@
-#include "../Window-Software.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string>
-
-#include "SDL.h"
-
-#include "../Misc.h"
-#include "../Shared/SDL2.h"
-
-SDL_Window *window;
-
-static SDL_Surface *window_sdlsurface;
-static SDL_Surface *framebuffer_sdlsurface;
-
-bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen)
-{
-	window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, screen_width, screen_height, 0);
-
-	if (window != NULL)
-	{
-		if (fullscreen)
-			if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) < 0)
-				Backend_PrintError("Couldn't set window to fullscree: %s", SDL_GetError());
-
-		window_sdlsurface = SDL_GetWindowSurface(window);
-
-		if (window_sdlsurface != NULL)
-		{
-
-			framebuffer_sdlsurface = SDL_CreateRGBSurfaceWithFormat(0, window_sdlsurface->w, window_sdlsurface->h, 0, SDL_PIXELFORMAT_RGB24);
-
-			if (framebuffer_sdlsurface != NULL)
-			{
-				Backend_PostWindowCreation();
-
-				return true;
-			}
-			else
-			{
-				std::string error_message = std::string("Couldn't create framebuffer surface: ") + SDL_GetError();
-				Backend_ShowMessageBox("Fatal error (software rendering backend)", error_message.c_str());
-				SDL_DestroyWindow(window);
-			}
-		}
-		else
-		{
-			std::string error_message = std::string("Couldn't get SDL surface associated with window: ") + SDL_GetError();
-			Backend_ShowMessageBox("Fatal error (software rendering backend)", error_message.c_str());
-		}
-	}
-	else
-	{
-		std::string error_message = std::string("Couldn't create window: ") + SDL_GetError();
-		Backend_ShowMessageBox("Fatal error (software rendering backend)", error_message.c_str());
-	}
-
-	return false;
-}
-
-void WindowBackend_Software_DestroyWindow(void)
-{
-	SDL_FreeSurface(framebuffer_sdlsurface);
-	SDL_DestroyWindow(window);
-}
-
-unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
-{
-	*pitch = framebuffer_sdlsurface->pitch;
-
-	return (unsigned char*)framebuffer_sdlsurface->pixels;
-}
-
-void WindowBackend_Software_Display(void)
-{
-	if (SDL_BlitSurface(framebuffer_sdlsurface, NULL, window_sdlsurface, NULL) < 0)
-		Backend_PrintError("Couldn't blit framebuffer surface to window surface: %s", SDL_GetError());
-
-	if (SDL_UpdateWindowSurface(window) < 0)
-		Backend_PrintError("Couldn't copy window surface to the screen: %s", SDL_GetError());
-}
-
-void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
-{
-	(void)width;
-	(void)height;
-
-	// https://wiki.libsdl.org/SDL_GetWindowSurface
-	// We need to fetch a new surface pointer
-	window_sdlsurface = SDL_GetWindowSurface(window);
-
-	if (window_sdlsurface == NULL)
-		Backend_PrintError("Couldn't get SDL surface associated with window: %s", SDL_GetError());
-}
--- a/src/Backends/Window/WiiU-Software.cpp
+++ /dev/null
@@ -1,269 +1,0 @@
-// Sexy new backend that bounces the software-rendered frame to the GPU,
-// eliminating V-tearing, and gaining support for rendering to the TV for
-// free!
-
-#include "../Window-Software.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <gx2/display.h>
-#include <gx2/draw.h>
-#include <gx2/registers.h>
-#include <gx2/sampler.h>
-#include <gx2/texture.h>
-#include <gx2r/buffer.h>
-#include <gx2r/draw.h>
-#include <gx2r/resource.h>
-#include <gx2r/surface.h>
-#include <whb/gfx.h>
-
-#include "../../Attributes.h"
-
-#include "WiiUShaders/texture.gsh.h"
-
-typedef struct Viewport
-{
-	float x;
-	float y;
-	float width;
-	float height;
-} Viewport;
-
-static unsigned char *fake_framebuffer;
-
-static size_t fake_framebuffer_width;
-static size_t fake_framebuffer_height;
-
-static WHBGfxShaderGroup shader_group;
-
-static GX2RBuffer vertex_position_buffer;
-static GX2RBuffer texture_coordinate_buffer;
-
-static GX2Sampler sampler;
-
-static GX2Texture screen_texture;
-
-static Viewport tv_viewport;
-static Viewport drc_viewport;
-
-static void CalculateViewport(unsigned int actual_screen_width, unsigned int actual_screen_height, Viewport *viewport)
-{
-	if ((float)actual_screen_width / (float)actual_screen_height > (float)fake_framebuffer_width / (float)fake_framebuffer_height)
-	{
-		viewport->y = 0.0f;
-		viewport->height = actual_screen_height;
-
-		viewport->width = fake_framebuffer_width * ((float)actual_screen_height / (float)fake_framebuffer_height);
-		viewport->x = (actual_screen_width - viewport->width) / 2;
-	}
-	else
-	{
-		viewport->x = 0.0f;
-		viewport->width = actual_screen_width;
-
-		viewport->height = fake_framebuffer_height * ((float)actual_screen_width / (float)fake_framebuffer_width);
-		viewport->y = (actual_screen_height - viewport->height) / 2;
-	}
-}
-
-bool WindowBackend_Software_CreateWindow(const char *window_title, int screen_width, int screen_height, bool fullscreen)
-{
-	(void)window_title;
-	(void)fullscreen;
-
-	fake_framebuffer_width = screen_width;
-	fake_framebuffer_height = screen_height;
-
-	fake_framebuffer = (unsigned char*)malloc(fake_framebuffer_width * fake_framebuffer_height * 3);
-
-	if (fake_framebuffer != NULL)
-	{
-		WHBGfxInit();
-
-		if (WHBGfxLoadGFDShaderGroup(&shader_group, 0, rtexture))
-		{
-			WHBGfxInitShaderAttribute(&shader_group, "input_vertex_coordinates", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
-			WHBGfxInitShaderAttribute(&shader_group, "input_texture_coordinates", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
-			WHBGfxInitFetchShader(&shader_group);
-
-			// Initialise vertex position buffer
-			const float vertex_positions[4][2] = {
-				{-1.0f,  1.0f},
-				{ 1.0f,  1.0f},
-				{ 1.0f, -1.0f},
-				{-1.0f, -1.0f}
-			};
-
-			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 = sizeof(vertex_positions[0]);
-			vertex_position_buffer.elemCount = sizeof(vertex_positions) / sizeof(vertex_positions[0]);
-			GX2RCreateBuffer(&vertex_position_buffer);
-			memcpy(GX2RLockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0), vertex_positions, sizeof(vertex_positions));
-			GX2RUnlockBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
-
-			// Initialise texture coordinate buffer
-			const float texture_coordinates[4][2] = {
-				{0.0f, 0.0f},
-				{1.0f, 0.0f},
-				{1.0f, 1.0f},
-				{0.0f, 1.0f}
-			};
-
-			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 = sizeof(texture_coordinates[0]);
-			texture_coordinate_buffer.elemCount = sizeof(texture_coordinates) / sizeof(texture_coordinates[0]);
-			GX2RCreateBuffer(&texture_coordinate_buffer);
-			memcpy(GX2RLockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0), texture_coordinates, sizeof(texture_coordinates));
-			GX2RUnlockBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
-
-			// Initialise sampler
-			GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_POINT);
-
-			// Initialise screen texture
-			screen_texture.surface.width = fake_framebuffer_width;
-			screen_texture.surface.height = fake_framebuffer_height;
-			screen_texture.surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
-			screen_texture.surface.depth = 1;
-			screen_texture.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
-			screen_texture.surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
-			screen_texture.surface.mipLevels = 1;
-			screen_texture.viewNumMips = 1;
-			screen_texture.viewNumSlices = 1;
-			screen_texture.compMap = 0x00010203;
-			GX2CalcSurfaceSizeAndAlignment(&screen_texture.surface);
-			GX2InitTextureRegs(&screen_texture);
-
-			if (GX2RCreateSurface(&screen_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)))
-			{
-				// Do some binding
-				GX2SetPixelSampler(&sampler, shader_group.pixelShader->samplerVars[0].location);
-				GX2SetPixelTexture(&screen_texture, shader_group.pixelShader->samplerVars[0].location);
-				GX2RSetAttributeBuffer(&vertex_position_buffer, 0, vertex_position_buffer.elemSize, 0);
-				GX2RSetAttributeBuffer(&texture_coordinate_buffer, 1, texture_coordinate_buffer.elemSize, 0);
-
-				// Calculate centred viewports
-				switch (GX2GetSystemTVScanMode())
-				{
-					// For now, we have to match WUT's broken behaviour (its `GX2TVScanMode`
-					// enum is missing values, and the rest are off-by-one)
-					//case GX2_TV_SCAN_MODE_576I:
-					case GX2_TV_SCAN_MODE_480I:	// Actually 576i
-					case GX2_TV_SCAN_MODE_480P:	// Actually 480i
-						CalculateViewport(854, 480, &tv_viewport);
-						break;
-
-					case GX2_TV_SCAN_MODE_720P:	// Actually 480p
-					default:	// Funnel the *real* 1080p into this
-						CalculateViewport(1280, 720, &tv_viewport);
-						break;
-
-					case GX2_TV_SCAN_MODE_1080I:	// Actually invalid
-					case GX2_TV_SCAN_MODE_1080P:	// Actually 1080i
-						CalculateViewport(1920, 1080, &tv_viewport);
-						break;
-				}
-
-				CalculateViewport(854, 480, &drc_viewport);
-
-				return true;
-			}
-
-			GX2RDestroyBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
-			GX2RDestroyBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
-
-			WHBGfxFreeShaderGroup(&shader_group);
-		}
-
-		WHBGfxShutdown();
-
-		free(fake_framebuffer);
-	}
-
-	return false;
-}
-
-void WindowBackend_Software_DestroyWindow(void)
-{
-	GX2RDestroySurfaceEx(&screen_texture.surface, (GX2RResourceFlags)0);
-
-	GX2RDestroyBufferEx(&texture_coordinate_buffer, (GX2RResourceFlags)0);
-	GX2RDestroyBufferEx(&vertex_position_buffer, (GX2RResourceFlags)0);
-
-	WHBGfxFreeShaderGroup(&shader_group);
-
-	WHBGfxShutdown();
-
-	free(fake_framebuffer);
-}
-
-unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
-{
-	*pitch = fake_framebuffer_width * 3;
-
-	return fake_framebuffer;
-}
-
-ATTRIBUTE_HOT void WindowBackend_Software_Display(void)
-{
-	// Convert frame from RGB24 to RGBA32, and upload it to the GPU texture
-	unsigned char *framebuffer = (unsigned char*)GX2RLockSurfaceEx(&screen_texture.surface, 0, (GX2RResourceFlags)0);
-
-	const unsigned char *in_pointer = fake_framebuffer;
-
-	for (size_t y = 0; y < fake_framebuffer_height; ++y)
-	{
-		unsigned char *out_pointer = &framebuffer[screen_texture.surface.pitch * 4 * y];
-
-		for (size_t x = 0; x < fake_framebuffer_width; ++x)
-		{
-			*out_pointer++ = *in_pointer++;
-			*out_pointer++ = *in_pointer++;
-			*out_pointer++ = *in_pointer++;
-			*out_pointer++ = 0;
-		}
-	}
-
-	GX2RUnlockSurfaceEx(&screen_texture.surface, 0, (GX2RResourceFlags)0);
-
-	WHBGfxBeginRender();
-
-	// Draw to the TV
-	WHBGfxBeginRenderTV();
-	GX2SetViewport(tv_viewport.x, tv_viewport.y, tv_viewport.width, tv_viewport.height, 0.0f, 1.0f);
-	WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-	GX2SetFetchShader(&shader_group.fetchShader);
-	GX2SetVertexShader(shader_group.vertexShader);
-	GX2SetPixelShader(shader_group.pixelShader);
-	GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
-	WHBGfxFinishRenderTV();
-
-	// Draw to the gamepad
-	WHBGfxBeginRenderDRC();
-	GX2SetViewport(drc_viewport.x, drc_viewport.y, drc_viewport.width, drc_viewport.height, 0.0f, 1.0f);
-	WHBGfxClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-	GX2SetFetchShader(&shader_group.fetchShader);
-	GX2SetVertexShader(shader_group.vertexShader);
-	GX2SetPixelShader(shader_group.pixelShader);
-	GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
-	WHBGfxFinishRenderDRC();
-
-	WHBGfxFinishRender();
-}
-
-void WindowBackend_Software_HandleWindowResize(unsigned int width, unsigned int height)
-{
-	(void)width;
-	(void)height;
-
-	// The window doesn't resize on the Wii U
-}