12#include <coreinit/memdefaultheap.h>
13#include <coreinit/alarm.h>
14#include <coreinit/time.h>
16#include <gx2/context.h>
17#include <gx2/display.h>
22#include <gx2/registers.h>
23#include <gx2/sampler.h>
24#include <gx2/surface.h>
26#include <gx2/texture.h>
28#include <proc_ui/procui.h>
30#include "../Platform.h"
31#include "WutVideoDriver.h"
32#include "shaders/Texture2DShader.h"
33#include "shaders/ColorShader.h"
37 inline float DegToRad(
float degrees)
39 return degrees * (3.14159265358979323846f / 180.0f);
42 void PixelRectToNdc(
float x,
float y,
float w,
float h,
float scaleX,
float scaleY,
int designWidth,
int designHeight,
float offset[3],
float scale[3])
44 float centerPxX = x + w * 0.5f;
45 float centerPxY = y + h * 0.5f;
47 offset[0] = (centerPxX / designWidth) * 2.0f - 1.0f;
48 offset[1] = 1.0f - (centerPxY / designHeight) * 2.0f;
51 scale[0] = (w * scaleX) / designWidth;
52 scale[1] = (h * scaleY) / designHeight;
60 void RotatedQuadToNdc(
float x,
float y,
float w,
float h,
float scaleX,
float scaleY,
float degrees,
int designWidth,
int designHeight,
float corners[8])
62 const float cx = x + w * 0.5f;
63 const float cy = y + h * 0.5f;
64 const float hw = w * scaleX * 0.5f;
65 const float hh = h * scaleY * 0.5f;
66 const float rad = DegToRad(degrees);
67 const float c = cosf(rad);
68 const float s = sinf(rad);
70 static const float lx[4] = { -1.0f, 1.0f, 1.0f, -1.0f };
71 static const float ly[4] = { -1.0f, -1.0f, 1.0f, 1.0f };
73 for(
int i = 0; i < 4; i++)
75 float dx = lx[i] * hw;
76 float dy = -ly[i] * hh;
77 float px = cx + dx * c - dy * s;
78 float py = cy + dx * s + dy * c;
80 corners[i * 2 + 0] = (px / designWidth) * 2.0f - 1.0f;
81 corners[i * 2 + 1] = 1.0f - (py / designHeight) * 2.0f;
88 const uint8_t * WhiteVtxs()
91 static bool vtxsInit =
false;
94 memset(whiteVtxs, 0xFF,
sizeof(whiteVtxs));
95 GX2Invalidate(GX2_INVALIDATE_MODE_CPU, whiteVtxs,
sizeof(whiteVtxs));
101 volatile uint32_t systemFrameTimer = 0;
102 OSAlarm frameTimerAlarm;
104 void FrameTimerAlarmHandler(OSAlarm *, OSContext *)
106 __atomic_fetch_add(&systemFrameTimer, 1, __ATOMIC_RELAXED);
109 OSTime FrameTimerInterval()
111 if(GX2GetSystemTVScanMode() == GX2_TV_SCAN_MODE_576I)
112 return OSSecondsToTicks(1) / 50;
114 return (OSSecondsToTicks(1) * 1001) / 60000;
122WutVideoDriver::WutVideoDriver()
123 : screenWidth(0), screenHeight(0), clearColor{0, 0, 0, 255}
124 , imageRenderer(nullptr), glyphRenderer(nullptr)
128WutVideoDriver::~WutVideoDriver()
130 delete imageRenderer;
131 imageRenderer =
nullptr;
132 delete glyphRenderer;
133 glyphRenderer =
nullptr;
139 retireLock =
nullptr;
147 screenHeight = height;
150 const GX2ColorBuffer * tvBuffer = WHBGfxGetTVColourBuffer();
151 const GX2ColorBuffer * drcBuffer = WHBGfxGetDRCColourBuffer();
152 targetWidth[(int)OutputTarget::TV] = tvBuffer->surface.width ? (
int)tvBuffer->surface.width : width;
153 targetHeight[(int)OutputTarget::TV] = tvBuffer->surface.height ? (
int)tvBuffer->surface.height : height;
154 targetWidth[(int)OutputTarget::DRC] = drcBuffer->surface.width ? (
int)drcBuffer->surface.width : width;
155 targetHeight[(int)OutputTarget::DRC] = drcBuffer->surface.height ? (
int)drcBuffer->surface.height : height;
159 drawQueue.reserve(1024);
160 retireLock =
new Mutex();
162 meshPositions =
static_cast<float *
>(memalign(GX2_VERTEX_BUFFER_ALIGNMENT, cuMeshArenaVertices * 3 *
sizeof(
float)));
163 meshColors =
static_cast<uint8_t *
>(memalign(GX2_VERTEX_BUFFER_ALIGNMENT, cuMeshArenaVertices * 4 *
sizeof(uint8_t)));
168 OSCreateAlarm(&frameTimerAlarm);
169 const OSTime interval = FrameTimerInterval();
170 OSSetPeriodicAlarm(&frameTimerAlarm, OSGetTime() + interval, interval, FrameTimerAlarmHandler);
175void WutVideoDriver::computeUIScale()
177 int tvWidth, tvHeight;
178 switch(GX2GetSystemTVScanMode())
180 case GX2_TV_SCAN_MODE_480I:
181 case GX2_TV_SCAN_MODE_480P:
182 if(GX2GetSystemTVAspectRatio() == GX2_ASPECT_RATIO_16_9)
193 case GX2_TV_SCAN_MODE_1080I:
194 case GX2_TV_SCAN_MODE_1080P:
198 case GX2_TV_SCAN_MODE_720P:
206 const int drcWidth = 854;
207 const int drcHeight = 480;
213 (float)tvWidth / screenWidth, (
float)tvHeight / screenHeight,
214 (float)drcWidth / screenWidth, (
float)drcHeight / screenHeight
220 OSCancelAlarm(&frameTimerAlarm);
226 return platform->getStatus() == Status::Running;
229void WutVideoDriver::prepareFrame(
unsigned targets)
239 gpuFramesInFlight =
false;
249 auto drawPass = [&]() {
250 WHBGfxClearColor(clearColor.r / 255.0f, clearColor.g / 255.0f, clearColor.b / 255.0f, clearColor.a / 255.0f);
251 GX2SetDepthOnlyControl(GX2_DISABLE, GX2_DISABLE, GX2_COMPARE_FUNC_ALWAYS);
252 GX2SetColorControl(GX2_LOGIC_OP_COPY, 0xFF, GX2_DISABLE, GX2_ENABLE);
253 GX2SetCullOnlyControl(GX2_FRONT_FACE_CCW, GX2_DISABLE, GX2_DISABLE);
254 GX2SetBlendControl(GX2_RENDER_TARGET_0, GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD, GX2_DISABLE, GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD);
257 if(targets & (1u << (
unsigned)OutputTarget::TV))
259 WHBGfxBeginRenderTV();
262 if(targets & (1u << (
unsigned)OutputTarget::DRC))
264 WHBGfxBeginRenderDRC();
277 uint32_t swapCount, flipCount;
278 OSTime lastFlip, lastVsync;
279 GX2GetSwapStatus(&swapCount, &flipCount, &lastFlip, &lastVsync);
281 if(flipCount < swapCount)
284 return gpuFramesInFlight && GX2GetRetiredTimeStamp() < lastSubmitTimeStamp;
291 gpuFramesInFlight =
false;
295 if(gpuFramesInFlight)
297 if(GX2GetRetiredTimeStamp() < lastSubmitTimeStamp)
298 GX2WaitTimeStamp(lastSubmitTimeStamp);
299 gpuFramesInFlight =
false;
311 if(drawQueue.size() >= cuMaxQueuedDraws)
314 drawQueue.push_back(cmd);
320 if(!vertices || count == 0 || !meshPositions || !meshColors || count > cuMeshArenaVertices - meshUsed)
323 const float invW = 2.0f / screenWidth;
324 const float invH = 2.0f / screenHeight;
325 float * pos = meshPositions + (size_t)meshUsed * 3;
326 uint8_t * col = meshColors + (size_t)meshUsed * 4;
328 for(
size_t i = 0; i < count; i++)
331 pos[i * 3 + 0] = v.
x * invW - 1.0f;
332 pos[i * 3 + 1] = 1.0f - v.
y * invH;
333 pos[i * 3 + 2] = 0.0f;
334 col[i * 4 + 0] = v.
c.
r;
335 col[i * 4 + 1] = v.
c.
g;
336 col[i * 4 + 2] = v.
c.
b;
337 col[i * 4 + 3] = v.
c.
a;
346 cmd.
first = meshUsed;
347 cmd.
count = (uint32_t)count;
348 meshUsed += (uint32_t)count;
354 if(!mesh || mesh->
count == 0)
359 const float invW = 2.0f / screenWidth;
360 const float invH = 2.0f / screenHeight;
368 cmd.
offset[0] = x * invW - 1.0f;
369 cmd.
offset[1] = 1.0f - y * invH;
371 cmd.
scale[0] = scaleX;
372 cmd.
scale[1] = scaleY;
374 cmd.colorIntensity[0] = cmd.colorIntensity[1] = cmd.colorIntensity[2] = cmd.colorIntensity[3] = 1.0f;
388 mesh->
next = retiredMeshes;
389 retiredMeshes = mesh;
391 retireLock->unlock();
394void WutVideoDriver::freeRetiredMeshes()
398 WutMesh * list = retiredMeshes;
399 retiredMeshes =
nullptr;
413void WutVideoDriver::replayDrawQueue()
const
418 static const float identityOffset[3] = { 0.0f, 0.0f, 0.0f };
419 static const float identityScale[3] = { 1.0f, 1.0f, 1.0f };
421 enum class BoundShader { None, Texture, Color };
422 BoundShader bound = BoundShader::None;
423 bool defaultQuadBound =
false;
424 bool meshBound =
false;
425 const GX2Texture * lastTexture =
nullptr;
426 const GX2Sampler * lastSampler =
nullptr;
428 for(
const WutDrawCmd& cmd : drawQueue)
432 if(bound != BoundShader::Color)
435 bound = BoundShader::Color;
438 colorShader->
bindMesh(cmd.mesh->positions, cmd.mesh->colors, cmd.mesh->count, cmd.offset, cmd.scale);
446 if(bound != BoundShader::Color)
449 bound = BoundShader::Color;
454 colorShader->
bindMesh(meshPositions, meshColors, cuMeshArenaVertices);
463 if(bound != BoundShader::Color)
466 bound = BoundShader::Color;
475 colorShader->
draw(GX2_PRIMITIVE_MODE_QUADS, 4);
479 if(bound != BoundShader::Texture)
483 bound = BoundShader::Texture;
485 defaultQuadBound =
false;
486 lastTexture =
nullptr;
487 lastSampler =
nullptr;
493 defaultQuadBound =
false;
495 textureShader->
setOffset(identityOffset);
496 textureShader->
setScale(identityScale);
500 if(!defaultQuadBound)
503 defaultQuadBound =
true;
512 if(cmd.texture != lastTexture || cmd.sampler != lastSampler)
515 lastTexture = cmd.texture;
516 lastSampler = cmd.sampler;
519 textureShader->
draw(GX2_PRIMITIVE_MODE_QUADS, 4);
525 if(drawQueue.empty())
535 if(meshUsed > meshFlushed)
537 GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, meshPositions + (
size_t)meshFlushed * 3, (meshUsed - meshFlushed) * 3 *
sizeof(
float));
538 GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, meshColors + (
size_t)meshFlushed * 4, (meshUsed - meshFlushed) * 4 *
sizeof(uint8_t));
539 meshFlushed = meshUsed;
542 if(uiTargets & (1u << (
unsigned)OutputTarget::TV))
544 WHBGfxBeginRenderTV();
547 if(uiTargets & (1u << (
unsigned)OutputTarget::DRC))
549 WHBGfxBeginRenderDRC();
556void WutVideoDriver::presentBuffer()
567 WHBGfxFinishRenderTV();
568 WHBGfxFinishRenderDRC();
570 GX2SwapScanBuffers();
572 GX2SetTVEnable(TRUE);
573 GX2SetDRCEnable(TRUE);
574 lastSubmitTimeStamp = GX2GetLastSubmittedTimeStamp();
575 gpuFramesInFlight =
true;
582 return __atomic_load_n(&systemFrameTimer, __ATOMIC_RELAXED);
592 return GX2GetSystemTVScanMode() == GX2_TV_SCAN_MODE_576I ? 50 : 60;
597 return GX2GetSystemTVScanMode() == GX2_TV_SCAN_MODE_576I ? (1.0f / 50.0f) : (1.0f / 60.0f);
607 GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_LINEAR);
612 if(width <= 0 || height <= 0)
615 GX2Texture * texture =
new GX2Texture();
616 GX2InitTexture(texture, width, height, 1, 0, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, GX2_SURFACE_DIM_TEXTURE_2D, GX2_TILE_MODE_LINEAR_ALIGNED);
618 GX2CalcSurfaceSizeAndAlignment(&texture->surface);
619 GX2InitTextureRegs(texture);
621 texture->surface.image = MEMAllocFromDefaultHeapEx(texture->surface.imageSize, texture->surface.alignment);
622 if(!texture->surface.image)
633 if(!texture || !rgba || width <= 0 || height <= 0)
636 GX2Texture * tex =
static_cast<GX2Texture *
>(texture);
637 if(!tex->surface.image)
640 uint8_t * dst =
static_cast<uint8_t *
>(tex->surface.image);
641 const uint32_t dstStride = tex->surface.pitch * 4;
642 const uint32_t srcStride =
static_cast<uint32_t
>(width) * 4;
643 for(
int y = 0; y < height; y++)
644 memcpy(dst + y * dstStride, rgba + y * srcStride, srcStride);
646 GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, tex->surface.image, tex->surface.imageSize);
651 return width == allocWidth && height == allocHeight;
659 driver->flushDrawQueue();
661 GX2Texture * tex =
static_cast<GX2Texture *
>(texture);
662 if(tex->surface.image)
663 MEMFreeToDefaultHeap(tex->surface.image);
667void WutImageRenderer::drawTexture(
void * texture,
float xpos,
float ypos, uint16_t width, uint16_t height,
float degrees,
float scaleX,
float scaleY, uint8_t alpha)
669 if(!texture || !driver->isForeground())
675 cmd.
texture =
static_cast<GX2Texture *
>(texture);
678 PixelRectToNdc(xpos, ypos, width, height, scaleX, scaleY, driver->getScreenWidth(), driver->getScreenHeight(), cmd.
offset, cmd.
scale);
679 cmd.colorIntensity[0] = 1.0f;
680 cmd.colorIntensity[1] = 1.0f;
681 cmd.colorIntensity[2] = 1.0f;
682 cmd.colorIntensity[3] = alpha / 255.0f;
690 RotatedQuadToNdc(xpos, ypos, width, height, scaleX, scaleY, degrees, driver->getScreenWidth(), driver->getScreenHeight(), corners);
692 uint32_t rotatedSlot = 0;
696 cmd.
slot = rotatedSlot;
700 cmd.
angle = DegToRad(degrees);
704 driver->queueDraw(cmd);
709 if(!driver->isForeground())
712 driver->queueMesh(vertices, count);
718 if(!vertices || count == 0)
724 m->
positions =
static_cast<float *
>(memalign(GX2_VERTEX_BUFFER_ALIGNMENT, count * 3 *
sizeof(
float)));
725 m->
colors =
static_cast<uint8_t *
>(memalign(GX2_VERTEX_BUFFER_ALIGNMENT, count * 4 *
sizeof(uint8_t)));
726 m->
count = (uint32_t)count;
738 const float invW = 2.0f / driver->getScreenWidth();
739 const float invH = 2.0f / driver->getScreenHeight();
740 for(
size_t i = 0; i < count; i++)
753 GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, m->
positions, count * 3 *
sizeof(
float));
754 GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, m->
colors, count * 4 *
sizeof(uint8_t));
761 driver->retireMesh(
static_cast<WutMesh *
>(mesh));
766 if(!mesh || !driver->isForeground())
769 driver->queueMeshInstance(
static_cast<const WutMesh *
>(mesh), x, y, scaleX, scaleY);
774 if(!driver->isForeground())
783 PixelRectToNdc(x, y, width, height, 1.0f, 1.0f, driver->getScreenWidth(), driver->getScreenHeight(), cmd.
offset, cmd.
scale);
784 cmd.colorIntensity[0] = color.r / 255.0f;
785 cmd.colorIntensity[1] = color.g / 255.0f;
786 cmd.colorIntensity[2] = color.b / 255.0f;
787 cmd.colorIntensity[3] = color.a / 255.0f;
789 driver->queueDraw(cmd);
799 GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_LINEAR);
804 GX2Texture * texture =
new GX2Texture();
805 GX2InitTexture(texture, width == 0 ? 1 : width, height == 0 ? 1 : height, 1, 0, GX2_SURFACE_FORMAT_UNORM_R8, GX2_SURFACE_DIM_TEXTURE_2D, GX2_TILE_MODE_LINEAR_ALIGNED);
807 GX2CalcSurfaceSizeAndAlignment(&texture->surface);
809 texture->surface.image = MEMAllocFromDefaultHeapEx(texture->surface.imageSize, texture->surface.alignment);
810 if(!texture->surface.image)
815 memset(texture->surface.image, 0x00, texture->surface.imageSize);
822 texture->compMap = 0x00000000;
823 GX2InitTextureRegs(texture);
830 if(!texturePtr || !bitmap || !bitmap->buffer)
833 GX2Texture * texture =
static_cast<GX2Texture *
>(texturePtr);
834 if(!texture->surface.image)
837 uint8_t * dst =
static_cast<uint8_t *
>(texture->surface.image);
838 uint8_t * src = bitmap->buffer;
840 uint32_t copyWidth = (bitmap->width < texture->surface.width) ? bitmap->width : texture->surface.width;
841 uint32_t copyHeight = (bitmap->rows < texture->surface.height) ? bitmap->rows : texture->surface.height;
843 uint32_t srcPitch = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;
845 srcPitch = bitmap->width;
847 for(uint32_t y = 0; y < copyHeight; y++)
848 memcpy(dst + y * texture->surface.pitch, src + y * srcPitch, copyWidth);
850 GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, texture->surface.image, texture->surface.imageSize);
858 driver->flushDrawQueue();
860 GX2Texture * texture =
static_cast<GX2Texture *
>(texturePtr);
861 if(texture->surface.image)
862 MEMFreeToDefaultHeap(texture->surface.image);
868 if(!texturePtr || !driver->isForeground())
874 cmd.
texture =
static_cast<GX2Texture *
>(texturePtr);
877 PixelRectToNdc(screenX, screenY, width, height, 1.0f, 1.0f, driver->getScreenWidth(), driver->getScreenHeight(), cmd.
offset, cmd.
scale);
878 cmd.colorIntensity[0] = color.r / 255.0f;
879 cmd.colorIntensity[1] = color.g / 255.0f;
880 cmd.colorIntensity[2] = color.b / 255.0f;
881 cmd.colorIntensity[3] = color.a / 255.0f;
883 driver->queueDraw(cmd);
888 if(!driver->isForeground())
897 PixelRectToNdc(screenX, screenY, width, height, 1.0f, 1.0f, driver->getScreenWidth(), driver->getScreenHeight(), cmd.
offset, cmd.
scale);
898 cmd.colorIntensity[0] = color.r / 255.0f;
899 cmd.colorIntensity[1] = color.g / 255.0f;
900 cmd.colorIntensity[2] = color.b / 255.0f;
901 cmd.colorIntensity[3] = color.a / 255.0f;
903 driver->queueDraw(cmd);
void setOffset(const float offset[3])
Sets the NDC position of the quad's center.
void resetFrame()
Call once per frame, before any draws - rewinds the per-draw color slot counter so this frame starts ...
void bindMesh(const float *positions, const uint8_t *colors, uint32_t vertexCapacity, const float *offset=nullptr, const float *scale=nullptr)
Binds caller-owned vertex streams for mesh draws and sets the uniforms so vertices pass through uncha...
void setColorIntensity(const float colorIntensity[4])
Sets the RGBA color the quad is filled with.
void setAttributeBuffer(const uint8_t *colorAttr)
static void drawMesh(uint32_t firstVertex, uint32_t vertexCount)
Draws triangles from the streams bound with bindMesh().
void setShaders() const
Binds the fetch, vertex and pixel shaders for subsequent draws.
void setAngle(float angleRadians)
Sets the quad rotation in radians.
static const uint32_t cuColorVtxsSize
Size in bytes of the four per-vertex colors of one quad.
void setScale(const float scale[3])
Sets the NDC half-extents of the quad.
static ColorShader * instance()
A simple mutual-exclusion lock.
void unlock()
Releases the mutex. Must be called by the thread that locked it.
void lock()
Blocks until the mutex is acquired. Not recursive: locking twice from one thread deadlocks.
static void draw(int32_t primitive=GX2_PRIMITIVE_MODE_QUADS, uint32_t vtxCount=4)
Issues the draw call for the currently bound shaders and attribute buffers.
void setOffset(const float offset[3])
void setRotatedAttributeBuffer(uint32_t slot) const
Binds a slot from uploadRotatedQuad() as the position stream (plus the shared texcoords).
void setAttributeBuffer() const
Binds the quad position and texture-coordinate buffers.
void resetFrame()
Call once per frame, before any draws - rewinds the rotated-quad slot counter (see WutVideoDriver::pr...
static Texture2DShader * instance()
void clearBlur() const
The compiled pixel shader reads a per-draw blur-direction uniform that this driver doesn't expose as ...
void setTextureAndSampler(const GX2Texture *texture, const GX2Sampler *sampler) const
Binds the texture to sample and the sampler describing its filtering.
void setColorIntensity(const float colorIntensity[4])
void setScale(const float scale[3])
void setAngle(float angleRadians)
void setShaders() const
Binds the fetch, vertex and pixel shaders for subsequent draws.
GX2-backed GlyphRenderer for GuiTextRenderer, using Texture2DShader for glyph quads and ColorShader f...
void drawQuad(void *texture, int16_t screenX, int16_t screenY, uint16_t width, uint16_t height, const PixelColor &color) override
Draws a glyph texture as a quad tinted with color.
void drawFeature(int16_t screenX, int16_t screenY, uint16_t width, uint16_t height, const PixelColor &color) override
Draws a solid-color rectangle used for text decoration (underline/strike-through).
void destroyTexture(void *texture) override
Destroys a texture created by createTexture().
WutGlyphRenderer(WutVideoDriver *driver)
void * createTexture(uint16_t width, uint16_t height) override
Allocates an empty single-channel glyph texture. Returns an opaque handle.
void loadTextureData(void *texture, FT_Bitmap *bitmap) override
Uploads a rasterized FreeType glyph bitmap into a texture from createTexture().
GX2-backed ImageRenderer for GuiImage/GuiImageData, using Texture2DShader.
void drawMeshInstance(void *mesh, float x, float y, float scaleX, float scaleY) override
Draws a mesh from createMesh() fully opaque, as its vertex colors say.
void destroyTexture(void *texture) override
Destroys a texture created by createTexture.
void destroyMesh(void *mesh) override
Releases a mesh from createMesh().
void loadTextureData(void *texture, const uint8_t *rgba, int width, int height) override
Loads raw RGBA8 pixels into the pre-allocated texture.
void * createTexture(int width, int height) override
Allocates an empty hardware-aligned texture buffer.
void drawRectangle(float x, float y, float width, float height, PixelColor color) override
Draws a solid-color rectangle (alpha-blended using color.a).
void drawMesh(const MeshVertex *vertices, size_t count) override
Draws a triangle list: three vertices per triangle, each with its own color.
void * createMesh(const MeshVertex *vertices, size_t count) override
Hands a triangle list to the backend to keep in its own ready-to-draw form (a GX display list,...
void drawTexture(void *texture, float xpos, float ypos, uint16_t width, uint16_t height, float degrees, float scaleX, float scaleY, uint8_t alpha) override
Draws a texture created by createTexture.
bool canReuseTexture(int allocWidth, int allocHeight, int width, int height) const override
Whether a texture allocated for allocWidth x allocHeight can be reloaded in place.
WutImageRenderer(WutVideoDriver *driver)
Wii U VideoDriver: GX2 + libwhb's WHBGfx* helpers.
uint32_t getFrameTimer() override
void clearScreen(const PixelColor &color) override
Clears the current frame buffer.
void queueDraw(const WutDrawCmd &cmd)
Records a UI draw for the next flushDrawQueue().
bool isPresentPending() const
True while the last presented frame has not finished: not yet on screen (the swap is waiting for the ...
void retireMesh(WutMesh *mesh)
Sets a mesh aside to be freed once the GPU is done with every frame that might draw it.
void queueMesh(const MeshVertex *vertices, size_t count)
Copies a triangle list into this frame's vertex arena (converting positions to NDC) and records a Mes...
float getDeltaTime() const override
void render() override
Flushes the current frame to the screen and swaps buffers.
int getRefreshRate() const override
void init(int width, int height) override
void queueMeshInstance(const WutMesh *mesh, float x, float y, float scaleX, float scaleY)
Records a draw of a mesh made by WutImageRenderer::createMesh().
void shutdown() override
Releases the video backend. Call after every other user of the renderers has finished.
void flushDrawQueue()
Replays every recorded UI draw into the TV context, then the same list into the GamePad context,...
void waitForIdle()
Waits until the GPU has retired the last submitted frame.
bool isForeground() const
False once the OS has taken away the foreground (HOME menu overlay, forced exit, etc....
A single 2D vertex with its own color, in design-canvas coordinates.
float y
Y position (design-canvas pixels).
float x
X position (design-canvas pixels).
PixelColor c
Straight (non-premultiplied) RGBA color.
8-bit-per-channel RGBA color used by the video interfaces.
uint8_t g
Green color component.
uint8_t r
Red color component.
uint8_t b
Blue color component.
uint8_t a
Alpha component.
One recorded UI draw (image, glyph quad or flat rectangle).
@ Mesh
triangle list from the mesh arena through ColorShader (see WutVideoDriver::queueMesh())
@ TextureRotated
textured quad whose corners were pre-rotated into a Texture2DShader rotated slot
@ Texture
textured quad through Texture2DShader, shader-side angle/offset/scale
@ Color
flat-color quad through ColorShader
@ MeshInstance
a WutMesh through ColorShader, placed by the shader-side offset/scale (see WutVideoDriver::queueMeshI...
float offset[3]
NDC position (unused by TextureRotated)
const GX2Sampler * sampler
Texture/TextureRotated only.
float angle
Texture only, radians.
float scale[3]
NDC half-extents (unused by TextureRotated)
uint32_t slot
TextureRotated only: slot from Texture2DShader::uploadRotatedQuad()
uint32_t first
Mesh only: index of the first vertex in the arena.
uint32_t count
Mesh only: number of vertices.
const GX2Texture * texture
Texture/TextureRotated only.
const WutMesh * mesh
MeshInstance only.
A mesh kept in GPU-readable vertex buffers across frames (ImageRenderer::createMesh()).
WutMesh * next
Link in the retired list.
uint32_t count
Number of vertices.
uint8_t * colors
RGBA8 per vertex.
float * positions
3 floats per vertex (x, y, 0)