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