shithub: cstory

Download patch

ref: eb62a80956b485c780f9028cd08440b4971aecba
parent: 4c024b540f755ca4679f826026e4843328cccb9e
author: Clownacy <Clownacy@users.noreply.github.com>
date: Wed Jul 31 18:38:33 EDT 2019

Change references to OpenGL 2.1 to 3.2

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,7 +11,7 @@
 option(FIX_BUGS "Fix certain bugs (see src/Bug Fixes.txt)" OFF)
 option(NONPORTABLE "Enable bits of code that aren't portable, but are what the original game used" OFF)
 option(FORCE_LOCAL_LIBS "Compile the built-in versions of SDL2, FreeType, and FLTK instead of using the system-provided ones" OFF)
-set(RENDERER "Texture" CACHE STRING "Which renderer the game should use: 'OpenGL2' for an OpenGL 2.1 renderer, 'Texture' for SDL2's hardware-accelerated Texture API, 'Surface' for SDL2's software-rendered Surface API, and 'Software' for a handwritten software renderer")
+set(RENDERER "Texture" CACHE STRING "Which renderer the game should use: 'OpenGL3' for an OpenGL 3.2 renderer, 'Texture' for SDL2's hardware-accelerated Texture API, 'Surface' for SDL2's software-rendered Surface API, and 'Software' for a handwritten software renderer")
 
 project(CSE2 LANGUAGES C CXX)
 
@@ -246,8 +246,8 @@
 	target_compile_definitions(CSE2 PRIVATE NONPORTABLE)
 endif()
 
-if(RENDERER MATCHES "OpenGL2")
-	target_sources(CSE2 PRIVATE "src/Backends/Rendering/OpenGL2.cpp")
+if(RENDERER MATCHES "OpenGL3")
+	target_sources(CSE2 PRIVATE "src/Backends/Rendering/OpenGL3.cpp")
 	find_package(GLEW REQUIRED)
 	target_link_libraries(CSE2 GLEW::GLEW)
 	find_package(OpenGL REQUIRED)
--- a/Makefile
+++ b/Makefile
@@ -208,8 +208,8 @@
 	RESOURCES += ICON/ICON_MINI.bmp
 endif
 
-ifeq ($(RENDERER), OpenGL2)
-	SOURCES += Backends/Rendering/OpenGL2
+ifeq ($(RENDERER), OpenGL3)
+	SOURCES += Backends/Rendering/OpenGL3
 	CXXFLAGS += `pkg-config glew --cflags`
 
 	ifeq ($(STATIC), 1)
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@
 * `-DFIX_BUGS=ON` - Fix bugs in the game (see [src/Bug Fixes.txt](src/Bug%20Fixes.txt))
 * `-DNONPORTABLE=ON` - Enable bits of code that aren't portable, but are what the original game used
 * `-DFORCE_LOCAL_LIBS=ON` - Compile the built-in versions of SDL2, FreeType, and FLTK instead of using the system-provided ones
-* `-DRENDERER=OpenGL2` - Use the hardware-accelerated OpenGL 2.1 renderer
+* `-DRENDERER=OpenGL3` - Use the hardware-accelerated OpenGL 3.2 renderer
 * `-DRENDERER=Texture` - Use the hardware-accelerated SDL2 Texture API renderer (default)
 * `-DRENDERER=Surface` - Use the software-rendered SDL2 Surface API renderer
 * `-DRENDERER=Software` - Use a handwritten software renderer
@@ -58,7 +58,7 @@
 * `WINDOWS=1` - Enable Windows-only features like a unique file/taskbar icon, and system font loading (needed for the font setting in Config.dat to do anything)
 * `RASPBERRY_PI=1` - Enable tweaks to improve performance on Raspberry Pis
 * `NONPORTABLE=1` - Enable bits of code that aren't portable, but are what the original game used
-* `RENDERER=OpenGL2` - Use the hardware-accelerated OpenGL 2.1 renderer
+* `RENDERER=OpenGL3` - Use the hardware-accelerated OpenGL 3.2 renderer
 * `RENDERER=Texture` - Use the hardware-accelerated SDL2 Texture API renderer (default)
 * `RENDERER=Surface` - Use the software-rendered SDL2 Surface API renderer
 * `RENDERER=Software` - Use a hand-written software renderer
--- a/src/Backends/Rendering/OpenGL2.cpp
+++ /dev/null
@@ -1,637 +1,0 @@
-#include "../Rendering.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#include <GL/glew.h>
-#include "SDL.h"
-
-#include "../../WindowsWrapper.h"
-
-#include "../../Font.h"
-
-typedef struct Backend_Surface
-{
-	GLuint texture_id;
-	unsigned int width;
-	unsigned int height;
-	unsigned char *pixels;
-} Backend_Surface;
-
-typedef struct Backend_Glyph
-{
-	GLuint texture_id;
-	unsigned int width;
-	unsigned int height;
-} Backend_Glyph;
-
-static SDL_Window *window;
-static SDL_GLContext context;
-
-static GLuint program_texture;
-static GLuint program_texture_colour_key;
-static GLuint program_colour_fill;
-static GLuint program_glyph;
-
-static GLint program_colour_fill_uniform_colour;
-static GLint program_glyph_uniform_colour;
-
-static GLuint framebuffer_id;
-static GLuint vertex_buffer_id;
-
-static struct
-{
-	GLfloat vertexes[4][2];
-	GLfloat texture_coordinates[4][2];
-} vertex_buffer;
-
-static Backend_Surface framebuffer_surface;
-
-static const GLchar *vertex_shader_plain = " \
-#version 150 core\n \
-in vec2 input_vertex_coordinates; \
-void main() \
-{ \
-	gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
-} \
-";
-
-static const GLchar *vertex_shader_texture = " \
-#version 150 core\n \
-in vec2 input_vertex_coordinates; \
-in vec2 input_texture_coordinates; \
-out vec2 texture_coordinates; \
-void main() \
-{ \
-	texture_coordinates = input_texture_coordinates; \
-	gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
-} \
-";
-
-static const GLchar *fragment_shader_texture = " \
-#version 150 core\n \
-uniform sampler2D tex; \
-in vec2 texture_coordinates; \
-out vec4 fragment; \
-void main() \
-{ \
-	fragment = texture2D(tex, texture_coordinates); \
-} \
-";
-
-static const GLchar *fragment_shader_texture_colour_key = " \
-#version 150 core\n \
-uniform sampler2D tex; \
-in vec2 texture_coordinates; \
-out vec4 fragment; \
-void main() \
-{ \
-	vec4 colour = texture2D(tex, texture_coordinates); \
-\
-	if (colour.xyz == vec3(0.0f, 0.0f, 0.0f)) \
-		discard; \
-\
-	fragment = colour; \
-} \
-";
-
-static const GLchar *fragment_shader_colour_fill = " \
-#version 150 core\n \
-uniform vec4 colour; \
-out vec4 fragment; \
-void main() \
-{ \
-	fragment = colour; \
-} \
-";
-
-static const GLchar *fragment_shader_glyph = " \
-#version 150 core\n \
-uniform sampler2D tex; \
-uniform vec4 colour; \
-in vec2 texture_coordinates; \
-out vec4 fragment; \
-void main() \
-{ \
-	fragment = colour * vec4(1.0, 1.0, 1.0, texture2D(tex, texture_coordinates).r); \
-} \
-";
-
-static void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void* userParam)
-{
-	if (type == GL_DEBUG_TYPE_ERROR)
-	printf("GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-           ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
-            type, severity, message);
-}
-
-static GLuint CompileShader(const char *vertex_shader_source, const char *fragment_shader_source)
-{
-	GLint shader_status;
-
-	GLuint program_id = glCreateProgram();
-
-	GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
-	glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
-	glCompileShader(vertex_shader);
-
-	glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &shader_status);
-	if (shader_status != GL_TRUE)
-		return 0;
-
-	glAttachShader(program_id, vertex_shader);
-
-	GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
-	glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
-	glCompileShader(fragment_shader);
-
-	glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &shader_status);
-	if (shader_status != GL_TRUE)
-		return 0;
-
-	glAttachShader(program_id, fragment_shader);
-
-	glBindAttribLocation(program_id, 1, "input_vertex_coordinates");
-	glBindAttribLocation(program_id, 2, "input_texture_coordinates");
-
-	glLinkProgram(program_id);
-
-	glGetProgramiv(program_id, GL_LINK_STATUS, &shader_status);
-	if (shader_status != GL_TRUE)
-		return 0;
-
-	return program_id;
-}
-
-SDL_Window* Backend_CreateWindow(const char *title, int width, int height)
-{
-	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
-	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
-	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
-
-	return SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
-}
-
-BOOL Backend_Init(SDL_Window *p_window)
-{
-	window = p_window;
-
-	int window_width, window_height;
-	SDL_GetWindowSize(window, &window_width, &window_height);
-
-	context = SDL_GL_CreateContext(window);
-
-	if (glewInit() != GLEW_OK)
-		return FALSE;
-
-	// Check if the platform supports OpenGL 3.2
-	if (!GLEW_VERSION_3_2)
-		return FALSE;
-
-	// During init, enable debug output
-	glEnable(GL_DEBUG_OUTPUT);
-	glDebugMessageCallback(MessageCallback, 0);
-
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-	glClear(GL_COLOR_BUFFER_BIT);
-
-	// Set up VAO
-	GLuint vertex_array_id;
-	glGenVertexArrays(1, &vertex_array_id);
-	glBindVertexArray(vertex_array_id);
-
-	// Set up VBO
-	glGenBuffers(1, &vertex_buffer_id);
-	glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
-	glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer), NULL, GL_DYNAMIC_DRAW);
-
-	// Set up IBO
-	const GLuint indices[4] = {0, 1, 2, 3};
-	GLuint indices_buffer_id;
-	glGenBuffers(1, &indices_buffer_id);
-	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer_id);
-	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
-
-	// Set up our shaders
-	program_texture = CompileShader(vertex_shader_texture, fragment_shader_texture);
-	program_texture_colour_key = CompileShader(vertex_shader_texture, fragment_shader_texture_colour_key);
-	program_colour_fill = CompileShader(vertex_shader_plain, fragment_shader_colour_fill);
-	program_glyph = CompileShader(vertex_shader_texture, fragment_shader_glyph);
-
-	if (program_texture == 0 || program_texture_colour_key == 0 || program_colour_fill == 0 || program_glyph == 0)
-		printf("Failed to compile shaders\n");
-
-	// Get shader uniforms
-	program_colour_fill_uniform_colour = glGetUniformLocation(program_colour_fill, "colour");
-	program_glyph_uniform_colour = glGetUniformLocation(program_glyph, "colour");
-
-	glEnableVertexAttribArray(1);
-	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
-	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(GLfloat) * 4 * 2));
-
-	// Set up framebuffer (used for surface-to-surface blitting)
-	glGenFramebuffers(1, &framebuffer_id);
-	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
-
-	// Set up framebuffer screen texture (used for screen-to-surface blitting)
-	glGenTextures(1, &framebuffer_surface.texture_id);
-	glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-	framebuffer_surface.width = window_width;
-	framebuffer_surface.height = window_height;
-
-	return TRUE;
-}
-
-void Backend_Deinit(void)
-{
-	glDeleteTextures(1, &framebuffer_surface.texture_id);
-	glDeleteFramebuffers(1, &framebuffer_id);
-	glDeleteProgram(program_glyph);
-	glDeleteProgram(program_colour_fill);
-	glDeleteProgram(program_texture_colour_key);
-	glDeleteProgram(program_texture);
-	SDL_GL_DeleteContext(context);
-}
-
-void Backend_DrawScreen(void)
-{
-	glUseProgram(program_texture);
-
-	// Enable texture coordinates, since this uses textures
-	glEnableVertexAttribArray(2);
-
-	// Target actual screen, and not our framebuffer
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
-	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
-	// Draw framebuffer to screen
-	glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
-
-	vertex_buffer.texture_coordinates[0][0] = 0.0f;
-	vertex_buffer.texture_coordinates[0][1] = 1.0f;
-	vertex_buffer.texture_coordinates[1][0] = 1.0f;
-	vertex_buffer.texture_coordinates[1][1] = 1.0f;
-	vertex_buffer.texture_coordinates[2][0] = 1.0f;
-	vertex_buffer.texture_coordinates[2][1] = 0.0f;
-	vertex_buffer.texture_coordinates[3][0] = 0.0f;
-	vertex_buffer.texture_coordinates[3][1] = 0.0f;
-
-	vertex_buffer.vertexes[0][0] = -1.0f;
-	vertex_buffer.vertexes[0][1] = -1.0f;
-	vertex_buffer.vertexes[1][0] = 1.0f;
-	vertex_buffer.vertexes[1][1] = -1.0f;
-	vertex_buffer.vertexes[2][0] = 1.0f;
-	vertex_buffer.vertexes[2][1] = 1.0f;
-	vertex_buffer.vertexes[3][0] = -1.0f;
-	vertex_buffer.vertexes[3][1] = 1.0f;
-
-	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
-	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-
-	SDL_GL_SwapWindow(window);
-
-	// According to https://www.khronos.org/opengl/wiki/Common_Mistakes#Swap_Buffers
-	// the buffer should always be cleared
-	glClear(GL_COLOR_BUFFER_BIT);
-
-	// Switch back to our framebuffer
-	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
-}
-
-Backend_Surface* Backend_CreateSurface(unsigned int width, unsigned int height)
-{
-	Backend_Surface *surface = (Backend_Surface*)malloc(sizeof(Backend_Surface));
-
-	if (surface == NULL)
-		return NULL;
-
-	glGenTextures(1, &surface->texture_id);
-	glBindTexture(GL_TEXTURE_2D, surface->texture_id);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-	surface->width = width;
-	surface->height = height;
-
-	return surface;
-}
-
-void Backend_FreeSurface(Backend_Surface *surface)
-{
-	if (surface == NULL)
-		return;
-
-	glDeleteTextures(1, &surface->texture_id);
-	free(surface);
-}
-
-unsigned char* Backend_Lock(Backend_Surface *surface, unsigned int *pitch)
-{
-	if (surface == NULL)
-		return NULL;
-
-	surface->pixels = (unsigned char*)malloc(surface->width * surface->height * 3);
-	*pitch = surface->width * 3;
-	return surface->pixels;
-}
-
-void Backend_Unlock(Backend_Surface *surface)
-{
-	if (surface == NULL)
-		return;
-
-	glBindTexture(GL_TEXTURE_2D, surface->texture_id);
-	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surface->width, surface->height, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
-	free(surface->pixels);
-}
-
-static void BlitCommon(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
-{
-	if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
-		return;
-
-	// Switch to colour-key shader if we have to
-	glUseProgram(colour_key ? program_texture_colour_key : program_texture);
-
-	// Enable texture coordinates, since this uses textures
-	glEnableVertexAttribArray(2);
-
-	glBindTexture(GL_TEXTURE_2D, source_surface->texture_id);
-
-	const GLfloat texture_left = (GLfloat)rect->left / (GLfloat)source_surface->width;
-	const GLfloat texture_right = (GLfloat)rect->right / (GLfloat)source_surface->width;
-	const GLfloat texture_top = (GLfloat)rect->top / (GLfloat)source_surface->height;
-	const GLfloat texture_bottom = (GLfloat)rect->bottom / (GLfloat)source_surface->height;
-
-	const GLfloat vertex_left = (x * (2.0f / destination_surface->width)) - 1.0f;
-	const GLfloat vertex_right = ((x + (rect->right - rect->left)) * (2.0f / destination_surface->width)) - 1.0f;
-	const GLfloat vertex_top = (y * (2.0f / destination_surface->height)) - 1.0f;
-	const GLfloat vertex_bottom = ((y + (rect->bottom - rect->top)) * (2.0f / destination_surface->height)) - 1.0f;
-
-	vertex_buffer.texture_coordinates[0][0] = texture_left;
-	vertex_buffer.texture_coordinates[0][1] = texture_top;
-	vertex_buffer.texture_coordinates[1][0] = texture_right;
-	vertex_buffer.texture_coordinates[1][1] = texture_top;
-	vertex_buffer.texture_coordinates[2][0] = texture_right;
-	vertex_buffer.texture_coordinates[2][1] = texture_bottom;
-	vertex_buffer.texture_coordinates[3][0] = texture_left;
-	vertex_buffer.texture_coordinates[3][1] = texture_bottom;
-
-	vertex_buffer.vertexes[0][0] = vertex_left;
-	vertex_buffer.vertexes[0][1] = vertex_top;
-	vertex_buffer.vertexes[1][0] = vertex_right;
-	vertex_buffer.vertexes[1][1] = vertex_top;
-	vertex_buffer.vertexes[2][0] = vertex_right;
-	vertex_buffer.vertexes[2][1] = vertex_bottom;
-	vertex_buffer.vertexes[3][0] = vertex_left;
-	vertex_buffer.vertexes[3][1] = vertex_bottom;
-
-	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
-	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-}
-
-void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
-{
-	if (source_surface == NULL || destination_surface == NULL)
-		return;
-
-	// Point our framebuffer to the destination texture
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destination_surface->texture_id, 0);
-	glViewport(0, 0, destination_surface->width, destination_surface->height);
-
-	BlitCommon(source_surface, rect, destination_surface, x, y, colour_key);
-}
-
-void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key)
-{
-	if (source_surface == NULL)
-		return;
-
-	// Point our framebuffer to the screen texture
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
-	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
-	BlitCommon(source_surface, rect, &framebuffer_surface, x, y, colour_key);
-}
-
-static void ColourFillCommon(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
-{
-	if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
-		return;
-
-	glUseProgram(program_colour_fill);
-
-	// Disable texture coordinate array, since this doesn't use textures
-	glDisableVertexAttribArray(2);
-
-	glUniform4f(program_colour_fill_uniform_colour, red / 255.0f, green / 255.0f, blue / 255.0f, 1.0f);
-
-	const GLfloat vertex_left = (rect->left * (2.0f / surface->width)) - 1.0f;
-	const GLfloat vertex_right = (rect->right * (2.0f / surface->width)) - 1.0f;
-	const GLfloat vertex_top = (rect->top * (2.0f / surface->height)) - 1.0f;
-	const GLfloat vertex_bottom = (rect->bottom * (2.0f / surface->height)) - 1.0f;
-
-	vertex_buffer.vertexes[0][0] = vertex_left;
-	vertex_buffer.vertexes[0][1] = vertex_top;
-	vertex_buffer.vertexes[1][0] = vertex_right;
-	vertex_buffer.vertexes[1][1] = vertex_top;
-	vertex_buffer.vertexes[2][0] = vertex_right;
-	vertex_buffer.vertexes[2][1] = vertex_bottom;
-	vertex_buffer.vertexes[3][0] = vertex_left;
-	vertex_buffer.vertexes[3][1] = vertex_bottom;
-
-	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
-	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-}
-
-void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
-{
-	if (surface == NULL)
-		return;
-
-	// Point our framebuffer to the destination texture
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
-	glViewport(0, 0, surface->width, surface->height);
-
-	ColourFillCommon(surface, rect, red, green, blue);
-}
-
-void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
-{
-	// Point our framebuffer to the screen texture
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
-	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
-	ColourFillCommon(&framebuffer_surface, rect, red, green, blue);
-}
-
-void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect)
-{
-	if (surface == NULL)
-		return;
-
-	// Point our framebuffer to the destination texture
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
-	glViewport(0, 0, surface->width, surface->height);
-
-	BlitCommon(&framebuffer_surface, rect, surface, rect->left, rect->top, FALSE);
-}
-
-BOOL Backend_SupportsSubpixelGlyph(void)
-{
-	return FALSE;	// Per-component alpha is available as an extension, but I haven't looked into it yet
-}
-
-Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, unsigned short total_greys, unsigned char pixel_mode)
-{
-	Backend_Glyph *glyph = (Backend_Glyph*)malloc(sizeof(Backend_Glyph));
-
-	if (glyph == NULL)
-		return NULL;
-
-	const int destination_pitch = (width + 3) & ~3;	// Round up to the nearest 4 (OpenGL needs this)
-
-	unsigned char *buffer = (unsigned char*)malloc(destination_pitch * height);
-
-	if (buffer == NULL)
-	{
-		free(glyph);
-		return NULL;
-	}
-
-	switch (pixel_mode)
-	{
-		// FONT_PIXEL_MODE_LCD is unsupported
-
-		case FONT_PIXEL_MODE_GRAY:
-			for (unsigned int y = 0; y < height; ++y)
-			{
-				const unsigned char *source_pointer = pixels + y * pitch;
-				unsigned char *destination_pointer = buffer + y * destination_pitch;
-
-				for (unsigned int x = 0; x < width; ++x)
-				{
-					*destination_pointer++ = (unsigned char)(pow((double)*source_pointer++ / (total_greys - 1), 1.0 / 1.8) * 255.0);
-				}
-			}
-
-			break;
-
-		case FONT_PIXEL_MODE_MONO:
-			for (unsigned int y = 0; y < height; ++y)
-			{
-				const unsigned char *source_pointer = pixels + y * pitch;
-				unsigned char *destination_pointer = buffer + y * destination_pitch;
-
-				for (unsigned int x = 0; x < width; ++x)
-				{
-					*destination_pointer++ = *source_pointer++ ? 0xFF : 0;
-				}
-			}
-
-			break;
-	}
-
-	glGenTextures(1, &glyph->texture_id);
-	glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0,  GL_RED, GL_UNSIGNED_BYTE, buffer);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-	glyph->width = width;
-	glyph->height = height;
-
-	free(buffer);
-
-	return glyph;
-}
-
-void Backend_UnloadGlyph(Backend_Glyph *glyph)
-{
-	if (glyph == NULL)
-		return;
-
-	glDeleteTextures(1, &glyph->texture_id);
-	free(glyph);
-}
-
-static void DrawGlyphCommon(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
-{
-	glUseProgram(program_glyph);
-
-	// Enable texture coordinates, since this uses textures
-	glEnableVertexAttribArray(2);
-
-	glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
-
-	const GLfloat vertex_left = (x * (2.0f / surface->width)) - 1.0f;
-	const GLfloat vertex_right = ((x + glyph->width) * (2.0f / surface->width)) - 1.0f;
-	const GLfloat vertex_top = (y * (2.0f / surface->height)) - 1.0f;
-	const GLfloat vertex_bottom = ((y + glyph->height) * (2.0f / surface->height)) - 1.0f;
-
-	glUniform4f(program_glyph_uniform_colour, colours[0] / 255.0f, colours[1] / 255.0f, colours[2] / 255.0f, 1.0f);
-
-	vertex_buffer.texture_coordinates[0][0] = 0.0f;
-	vertex_buffer.texture_coordinates[0][1] = 0.0f;
-	vertex_buffer.texture_coordinates[1][0] = 1.0f;
-	vertex_buffer.texture_coordinates[1][1] = 0.0f;
-	vertex_buffer.texture_coordinates[2][0] = 1.0f;
-	vertex_buffer.texture_coordinates[2][1] = 1.0f;
-	vertex_buffer.texture_coordinates[3][0] = 0.0f;
-	vertex_buffer.texture_coordinates[3][1] = 1.0f;
-
-	vertex_buffer.vertexes[0][0] = vertex_left;
-	vertex_buffer.vertexes[0][1] = vertex_top;
-	vertex_buffer.vertexes[1][0] = vertex_right;
-	vertex_buffer.vertexes[1][1] = vertex_top;
-	vertex_buffer.vertexes[2][0] = vertex_right;
-	vertex_buffer.vertexes[2][1] = vertex_bottom;
-	vertex_buffer.vertexes[3][0] = vertex_left;
-	vertex_buffer.vertexes[3][1] = vertex_bottom;
-
-	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
-	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-}
-
-void Backend_DrawGlyph(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
-{
-	if (glyph == NULL || surface == NULL)
-		return;
-
-	// Point our framebuffer to the destination texture
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
-	glViewport(0, 0, surface->width, surface->height);
-
-	DrawGlyphCommon(surface, glyph, x, y, colours);
-}
-
-void Backend_DrawGlyphToScreen(Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
-{
-	if (glyph == NULL)
-		return;
-
-	// Point our framebuffer to the screen texture
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
-	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
-	DrawGlyphCommon(&framebuffer_surface, glyph, x, y, colours);
-}
-
-void Backend_HandleDeviceLoss(void)
-{
-	// No problem for us
-}
-
-void Backend_HandleWindowResize(void)
-{
-	// No problem for us
-}
--- /dev/null
+++ b/src/Backends/Rendering/OpenGL3.cpp
@@ -1,0 +1,637 @@
+#include "../Rendering.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <GL/glew.h>
+#include "SDL.h"
+
+#include "../../WindowsWrapper.h"
+
+#include "../../Font.h"
+
+typedef struct Backend_Surface
+{
+	GLuint texture_id;
+	unsigned int width;
+	unsigned int height;
+	unsigned char *pixels;
+} Backend_Surface;
+
+typedef struct Backend_Glyph
+{
+	GLuint texture_id;
+	unsigned int width;
+	unsigned int height;
+} Backend_Glyph;
+
+static SDL_Window *window;
+static SDL_GLContext context;
+
+static GLuint program_texture;
+static GLuint program_texture_colour_key;
+static GLuint program_colour_fill;
+static GLuint program_glyph;
+
+static GLint program_colour_fill_uniform_colour;
+static GLint program_glyph_uniform_colour;
+
+static GLuint framebuffer_id;
+static GLuint vertex_buffer_id;
+
+static struct
+{
+	GLfloat vertexes[4][2];
+	GLfloat texture_coordinates[4][2];
+} vertex_buffer;
+
+static Backend_Surface framebuffer_surface;
+
+static const GLchar *vertex_shader_plain = " \
+#version 150 core\n \
+in vec2 input_vertex_coordinates; \
+void main() \
+{ \
+	gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
+} \
+";
+
+static const GLchar *vertex_shader_texture = " \
+#version 150 core\n \
+in vec2 input_vertex_coordinates; \
+in vec2 input_texture_coordinates; \
+out vec2 texture_coordinates; \
+void main() \
+{ \
+	texture_coordinates = input_texture_coordinates; \
+	gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
+} \
+";
+
+static const GLchar *fragment_shader_texture = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+	fragment = texture2D(tex, texture_coordinates); \
+} \
+";
+
+static const GLchar *fragment_shader_texture_colour_key = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+	vec4 colour = texture2D(tex, texture_coordinates); \
+\
+	if (colour.xyz == vec3(0.0f, 0.0f, 0.0f)) \
+		discard; \
+\
+	fragment = colour; \
+} \
+";
+
+static const GLchar *fragment_shader_colour_fill = " \
+#version 150 core\n \
+uniform vec4 colour; \
+out vec4 fragment; \
+void main() \
+{ \
+	fragment = colour; \
+} \
+";
+
+static const GLchar *fragment_shader_glyph = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+uniform vec4 colour; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+	fragment = colour * vec4(1.0, 1.0, 1.0, texture2D(tex, texture_coordinates).r); \
+} \
+";
+
+static void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void* userParam)
+{
+	if (type == GL_DEBUG_TYPE_ERROR)
+	printf("GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+           ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message);
+}
+
+static GLuint CompileShader(const char *vertex_shader_source, const char *fragment_shader_source)
+{
+	GLint shader_status;
+
+	GLuint program_id = glCreateProgram();
+
+	GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+	glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
+	glCompileShader(vertex_shader);
+
+	glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &shader_status);
+	if (shader_status != GL_TRUE)
+		return 0;
+
+	glAttachShader(program_id, vertex_shader);
+
+	GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+	glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
+	glCompileShader(fragment_shader);
+
+	glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &shader_status);
+	if (shader_status != GL_TRUE)
+		return 0;
+
+	glAttachShader(program_id, fragment_shader);
+
+	glBindAttribLocation(program_id, 1, "input_vertex_coordinates");
+	glBindAttribLocation(program_id, 2, "input_texture_coordinates");
+
+	glLinkProgram(program_id);
+
+	glGetProgramiv(program_id, GL_LINK_STATUS, &shader_status);
+	if (shader_status != GL_TRUE)
+		return 0;
+
+	return program_id;
+}
+
+SDL_Window* Backend_CreateWindow(const char *title, int width, int height)
+{
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+
+	return SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
+}
+
+BOOL Backend_Init(SDL_Window *p_window)
+{
+	window = p_window;
+
+	int window_width, window_height;
+	SDL_GetWindowSize(window, &window_width, &window_height);
+
+	context = SDL_GL_CreateContext(window);
+
+	if (glewInit() != GLEW_OK)
+		return FALSE;
+
+	// Check if the platform supports OpenGL 3.2
+	if (!GLEW_VERSION_3_2)
+		return FALSE;
+
+	// During init, enable debug output
+	glEnable(GL_DEBUG_OUTPUT);
+	glDebugMessageCallback(MessageCallback, 0);
+
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	// Set up VAO
+	GLuint vertex_array_id;
+	glGenVertexArrays(1, &vertex_array_id);
+	glBindVertexArray(vertex_array_id);
+
+	// Set up VBO
+	glGenBuffers(1, &vertex_buffer_id);
+	glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
+	glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer), NULL, GL_DYNAMIC_DRAW);
+
+	// Set up IBO
+	const GLuint indices[4] = {0, 1, 2, 3};
+	GLuint indices_buffer_id;
+	glGenBuffers(1, &indices_buffer_id);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer_id);
+	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
+
+	// Set up our shaders
+	program_texture = CompileShader(vertex_shader_texture, fragment_shader_texture);
+	program_texture_colour_key = CompileShader(vertex_shader_texture, fragment_shader_texture_colour_key);
+	program_colour_fill = CompileShader(vertex_shader_plain, fragment_shader_colour_fill);
+	program_glyph = CompileShader(vertex_shader_texture, fragment_shader_glyph);
+
+	if (program_texture == 0 || program_texture_colour_key == 0 || program_colour_fill == 0 || program_glyph == 0)
+		printf("Failed to compile shaders\n");
+
+	// Get shader uniforms
+	program_colour_fill_uniform_colour = glGetUniformLocation(program_colour_fill, "colour");
+	program_glyph_uniform_colour = glGetUniformLocation(program_glyph, "colour");
+
+	glEnableVertexAttribArray(1);
+	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
+	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(GLfloat) * 4 * 2));
+
+	// Set up framebuffer (used for surface-to-surface blitting)
+	glGenFramebuffers(1, &framebuffer_id);
+	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
+
+	// Set up framebuffer screen texture (used for screen-to-surface blitting)
+	glGenTextures(1, &framebuffer_surface.texture_id);
+	glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+	framebuffer_surface.width = window_width;
+	framebuffer_surface.height = window_height;
+
+	return TRUE;
+}
+
+void Backend_Deinit(void)
+{
+	glDeleteTextures(1, &framebuffer_surface.texture_id);
+	glDeleteFramebuffers(1, &framebuffer_id);
+	glDeleteProgram(program_glyph);
+	glDeleteProgram(program_colour_fill);
+	glDeleteProgram(program_texture_colour_key);
+	glDeleteProgram(program_texture);
+	SDL_GL_DeleteContext(context);
+}
+
+void Backend_DrawScreen(void)
+{
+	glUseProgram(program_texture);
+
+	// Enable texture coordinates, since this uses textures
+	glEnableVertexAttribArray(2);
+
+	// Target actual screen, and not our framebuffer
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+	// Draw framebuffer to screen
+	glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
+
+	vertex_buffer.texture_coordinates[0][0] = 0.0f;
+	vertex_buffer.texture_coordinates[0][1] = 1.0f;
+	vertex_buffer.texture_coordinates[1][0] = 1.0f;
+	vertex_buffer.texture_coordinates[1][1] = 1.0f;
+	vertex_buffer.texture_coordinates[2][0] = 1.0f;
+	vertex_buffer.texture_coordinates[2][1] = 0.0f;
+	vertex_buffer.texture_coordinates[3][0] = 0.0f;
+	vertex_buffer.texture_coordinates[3][1] = 0.0f;
+
+	vertex_buffer.vertexes[0][0] = -1.0f;
+	vertex_buffer.vertexes[0][1] = -1.0f;
+	vertex_buffer.vertexes[1][0] = 1.0f;
+	vertex_buffer.vertexes[1][1] = -1.0f;
+	vertex_buffer.vertexes[2][0] = 1.0f;
+	vertex_buffer.vertexes[2][1] = 1.0f;
+	vertex_buffer.vertexes[3][0] = -1.0f;
+	vertex_buffer.vertexes[3][1] = 1.0f;
+
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+
+	SDL_GL_SwapWindow(window);
+
+	// According to https://www.khronos.org/opengl/wiki/Common_Mistakes#Swap_Buffers
+	// the buffer should always be cleared
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	// Switch back to our framebuffer
+	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
+}
+
+Backend_Surface* Backend_CreateSurface(unsigned int width, unsigned int height)
+{
+	Backend_Surface *surface = (Backend_Surface*)malloc(sizeof(Backend_Surface));
+
+	if (surface == NULL)
+		return NULL;
+
+	glGenTextures(1, &surface->texture_id);
+	glBindTexture(GL_TEXTURE_2D, surface->texture_id);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+	surface->width = width;
+	surface->height = height;
+
+	return surface;
+}
+
+void Backend_FreeSurface(Backend_Surface *surface)
+{
+	if (surface == NULL)
+		return;
+
+	glDeleteTextures(1, &surface->texture_id);
+	free(surface);
+}
+
+unsigned char* Backend_Lock(Backend_Surface *surface, unsigned int *pitch)
+{
+	if (surface == NULL)
+		return NULL;
+
+	surface->pixels = (unsigned char*)malloc(surface->width * surface->height * 3);
+	*pitch = surface->width * 3;
+	return surface->pixels;
+}
+
+void Backend_Unlock(Backend_Surface *surface)
+{
+	if (surface == NULL)
+		return;
+
+	glBindTexture(GL_TEXTURE_2D, surface->texture_id);
+	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surface->width, surface->height, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
+	free(surface->pixels);
+}
+
+static void BlitCommon(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
+{
+	if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
+		return;
+
+	// Switch to colour-key shader if we have to
+	glUseProgram(colour_key ? program_texture_colour_key : program_texture);
+
+	// Enable texture coordinates, since this uses textures
+	glEnableVertexAttribArray(2);
+
+	glBindTexture(GL_TEXTURE_2D, source_surface->texture_id);
+
+	const GLfloat texture_left = (GLfloat)rect->left / (GLfloat)source_surface->width;
+	const GLfloat texture_right = (GLfloat)rect->right / (GLfloat)source_surface->width;
+	const GLfloat texture_top = (GLfloat)rect->top / (GLfloat)source_surface->height;
+	const GLfloat texture_bottom = (GLfloat)rect->bottom / (GLfloat)source_surface->height;
+
+	const GLfloat vertex_left = (x * (2.0f / destination_surface->width)) - 1.0f;
+	const GLfloat vertex_right = ((x + (rect->right - rect->left)) * (2.0f / destination_surface->width)) - 1.0f;
+	const GLfloat vertex_top = (y * (2.0f / destination_surface->height)) - 1.0f;
+	const GLfloat vertex_bottom = ((y + (rect->bottom - rect->top)) * (2.0f / destination_surface->height)) - 1.0f;
+
+	vertex_buffer.texture_coordinates[0][0] = texture_left;
+	vertex_buffer.texture_coordinates[0][1] = texture_top;
+	vertex_buffer.texture_coordinates[1][0] = texture_right;
+	vertex_buffer.texture_coordinates[1][1] = texture_top;
+	vertex_buffer.texture_coordinates[2][0] = texture_right;
+	vertex_buffer.texture_coordinates[2][1] = texture_bottom;
+	vertex_buffer.texture_coordinates[3][0] = texture_left;
+	vertex_buffer.texture_coordinates[3][1] = texture_bottom;
+
+	vertex_buffer.vertexes[0][0] = vertex_left;
+	vertex_buffer.vertexes[0][1] = vertex_top;
+	vertex_buffer.vertexes[1][0] = vertex_right;
+	vertex_buffer.vertexes[1][1] = vertex_top;
+	vertex_buffer.vertexes[2][0] = vertex_right;
+	vertex_buffer.vertexes[2][1] = vertex_bottom;
+	vertex_buffer.vertexes[3][0] = vertex_left;
+	vertex_buffer.vertexes[3][1] = vertex_bottom;
+
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+}
+
+void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
+{
+	if (source_surface == NULL || destination_surface == NULL)
+		return;
+
+	// Point our framebuffer to the destination texture
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destination_surface->texture_id, 0);
+	glViewport(0, 0, destination_surface->width, destination_surface->height);
+
+	BlitCommon(source_surface, rect, destination_surface, x, y, colour_key);
+}
+
+void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key)
+{
+	if (source_surface == NULL)
+		return;
+
+	// Point our framebuffer to the screen texture
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
+	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+	BlitCommon(source_surface, rect, &framebuffer_surface, x, y, colour_key);
+}
+
+static void ColourFillCommon(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
+{
+	if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
+		return;
+
+	glUseProgram(program_colour_fill);
+
+	// Disable texture coordinate array, since this doesn't use textures
+	glDisableVertexAttribArray(2);
+
+	glUniform4f(program_colour_fill_uniform_colour, red / 255.0f, green / 255.0f, blue / 255.0f, 1.0f);
+
+	const GLfloat vertex_left = (rect->left * (2.0f / surface->width)) - 1.0f;
+	const GLfloat vertex_right = (rect->right * (2.0f / surface->width)) - 1.0f;
+	const GLfloat vertex_top = (rect->top * (2.0f / surface->height)) - 1.0f;
+	const GLfloat vertex_bottom = (rect->bottom * (2.0f / surface->height)) - 1.0f;
+
+	vertex_buffer.vertexes[0][0] = vertex_left;
+	vertex_buffer.vertexes[0][1] = vertex_top;
+	vertex_buffer.vertexes[1][0] = vertex_right;
+	vertex_buffer.vertexes[1][1] = vertex_top;
+	vertex_buffer.vertexes[2][0] = vertex_right;
+	vertex_buffer.vertexes[2][1] = vertex_bottom;
+	vertex_buffer.vertexes[3][0] = vertex_left;
+	vertex_buffer.vertexes[3][1] = vertex_bottom;
+
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+}
+
+void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
+{
+	if (surface == NULL)
+		return;
+
+	// Point our framebuffer to the destination texture
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
+	glViewport(0, 0, surface->width, surface->height);
+
+	ColourFillCommon(surface, rect, red, green, blue);
+}
+
+void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
+{
+	// Point our framebuffer to the screen texture
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
+	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+	ColourFillCommon(&framebuffer_surface, rect, red, green, blue);
+}
+
+void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect)
+{
+	if (surface == NULL)
+		return;
+
+	// Point our framebuffer to the destination texture
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
+	glViewport(0, 0, surface->width, surface->height);
+
+	BlitCommon(&framebuffer_surface, rect, surface, rect->left, rect->top, FALSE);
+}
+
+BOOL Backend_SupportsSubpixelGlyph(void)
+{
+	return FALSE;	// Per-component alpha is available as an extension, but I haven't looked into it yet
+}
+
+Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, unsigned short total_greys, unsigned char pixel_mode)
+{
+	Backend_Glyph *glyph = (Backend_Glyph*)malloc(sizeof(Backend_Glyph));
+
+	if (glyph == NULL)
+		return NULL;
+
+	const int destination_pitch = (width + 3) & ~3;	// Round up to the nearest 4 (OpenGL needs this)
+
+	unsigned char *buffer = (unsigned char*)malloc(destination_pitch * height);
+
+	if (buffer == NULL)
+	{
+		free(glyph);
+		return NULL;
+	}
+
+	switch (pixel_mode)
+	{
+		// FONT_PIXEL_MODE_LCD is unsupported
+
+		case FONT_PIXEL_MODE_GRAY:
+			for (unsigned int y = 0; y < height; ++y)
+			{
+				const unsigned char *source_pointer = pixels + y * pitch;
+				unsigned char *destination_pointer = buffer + y * destination_pitch;
+
+				for (unsigned int x = 0; x < width; ++x)
+				{
+					*destination_pointer++ = (unsigned char)(pow((double)*source_pointer++ / (total_greys - 1), 1.0 / 1.8) * 255.0);
+				}
+			}
+
+			break;
+
+		case FONT_PIXEL_MODE_MONO:
+			for (unsigned int y = 0; y < height; ++y)
+			{
+				const unsigned char *source_pointer = pixels + y * pitch;
+				unsigned char *destination_pointer = buffer + y * destination_pitch;
+
+				for (unsigned int x = 0; x < width; ++x)
+				{
+					*destination_pointer++ = *source_pointer++ ? 0xFF : 0;
+				}
+			}
+
+			break;
+	}
+
+	glGenTextures(1, &glyph->texture_id);
+	glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0,  GL_RED, GL_UNSIGNED_BYTE, buffer);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+	glyph->width = width;
+	glyph->height = height;
+
+	free(buffer);
+
+	return glyph;
+}
+
+void Backend_UnloadGlyph(Backend_Glyph *glyph)
+{
+	if (glyph == NULL)
+		return;
+
+	glDeleteTextures(1, &glyph->texture_id);
+	free(glyph);
+}
+
+static void DrawGlyphCommon(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
+{
+	glUseProgram(program_glyph);
+
+	// Enable texture coordinates, since this uses textures
+	glEnableVertexAttribArray(2);
+
+	glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
+
+	const GLfloat vertex_left = (x * (2.0f / surface->width)) - 1.0f;
+	const GLfloat vertex_right = ((x + glyph->width) * (2.0f / surface->width)) - 1.0f;
+	const GLfloat vertex_top = (y * (2.0f / surface->height)) - 1.0f;
+	const GLfloat vertex_bottom = ((y + glyph->height) * (2.0f / surface->height)) - 1.0f;
+
+	glUniform4f(program_glyph_uniform_colour, colours[0] / 255.0f, colours[1] / 255.0f, colours[2] / 255.0f, 1.0f);
+
+	vertex_buffer.texture_coordinates[0][0] = 0.0f;
+	vertex_buffer.texture_coordinates[0][1] = 0.0f;
+	vertex_buffer.texture_coordinates[1][0] = 1.0f;
+	vertex_buffer.texture_coordinates[1][1] = 0.0f;
+	vertex_buffer.texture_coordinates[2][0] = 1.0f;
+	vertex_buffer.texture_coordinates[2][1] = 1.0f;
+	vertex_buffer.texture_coordinates[3][0] = 0.0f;
+	vertex_buffer.texture_coordinates[3][1] = 1.0f;
+
+	vertex_buffer.vertexes[0][0] = vertex_left;
+	vertex_buffer.vertexes[0][1] = vertex_top;
+	vertex_buffer.vertexes[1][0] = vertex_right;
+	vertex_buffer.vertexes[1][1] = vertex_top;
+	vertex_buffer.vertexes[2][0] = vertex_right;
+	vertex_buffer.vertexes[2][1] = vertex_bottom;
+	vertex_buffer.vertexes[3][0] = vertex_left;
+	vertex_buffer.vertexes[3][1] = vertex_bottom;
+
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+}
+
+void Backend_DrawGlyph(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
+{
+	if (glyph == NULL || surface == NULL)
+		return;
+
+	// Point our framebuffer to the destination texture
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
+	glViewport(0, 0, surface->width, surface->height);
+
+	DrawGlyphCommon(surface, glyph, x, y, colours);
+}
+
+void Backend_DrawGlyphToScreen(Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
+{
+	if (glyph == NULL)
+		return;
+
+	// Point our framebuffer to the screen texture
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
+	glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+	DrawGlyphCommon(&framebuffer_surface, glyph, x, y, colours);
+}
+
+void Backend_HandleDeviceLoss(void)
+{
+	// No problem for us
+}
+
+void Backend_HandleWindowResize(void)
+{
+	// No problem for us
+}