all repos — mgba @ 526f86d08529aa8f501cb015c1e442aa743940da

mGBA Game Boy Advance Emulator

3DS: Implement adjustable filtering and sprite rotation
Jeffrey Pfau jeffrey@endrift.com
Sun, 21 Aug 2016 03:16:00 -0700
commit

526f86d08529aa8f501cb015c1e442aa743940da

parent

39230ca9acbf63224aadf43e030bfa7b95264181

M CHANGESCHANGES

@@ -66,6 +66,7 @@ - PSP2: Allow UTF-8 filenames

- Util: Add Vector GetConstPointer - Util: Add rtrim - GBA Memory: Optimize Load-/StoreMultiple + - 3DS: Adjustable filering 0.4.1: (2016-07-11) Bugfixes:
M src/platform/3ds/CMakeLists.txtsrc/platform/3ds/CMakeLists.txt

@@ -30,12 +30,9 @@ set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)

list(APPEND GUI_SRC ${CMAKE_CURRENT_BINARY_DIR}/icons.c - ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c - ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h - ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h - ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c - ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h - ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h + ${CMAKE_CURRENT_BINARY_DIR}/uishader.c + ${CMAKE_CURRENT_BINARY_DIR}/uishader.h + ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c ${CMAKE_CURRENT_SOURCE_DIR}/ctr-gpu.c

@@ -43,12 +40,9 @@ ${CMAKE_CURRENT_SOURCE_DIR}/ctr-gpu.h)

set_source_files_properties( ${CMAKE_CURRENT_BINARY_DIR}/icons.c - ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c - ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h - ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h - ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c - ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h - ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h + ${CMAKE_CURRENT_BINARY_DIR}/uishader.c + ${CMAKE_CURRENT_BINARY_DIR}/uishader.h + ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h PROPERTIES GENERATED ON) add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c)

@@ -68,36 +62,21 @@ COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw

DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica ${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica COMMAND ${PICASSO} - -o ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin - -h ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h + -o ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin + -h ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h + ${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica ${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica - COMMENT "picasso uishader.g.pica") - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h - MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin - COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "raw2c uishader.g.shbin") - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica - COMMAND ${PICASSO} - -o ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin - -h ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h - ${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica - COMMENT "picasso uishader.v.pica") + COMMENT "picasso uishader.shbin") add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h - MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin - COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.c ${CMAKE_CURRENT_BINARY_DIR}/uishader.h + MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin + COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "raw2c uishader.v.shbin") + COMMENT "raw2c uishader.shbin") add_custom_command(OUTPUT ${BINARY_NAME}.3dsx COMMAND ${3DSXTOOL} ${BINARY_NAME}.elf ${BINARY_NAME}.3dsx --smdh=${BINARY_NAME}.smdh
M src/platform/3ds/ctr-gpu.csrc/platform/3ds/ctr-gpu.c

@@ -14,10 +14,8 @@ #include <stdio.h>

#include "ctr-gpu.h" -#include "uishader_v.h" -#include "uishader_v.shbin.h" -#include "uishader_g.h" -#include "uishader_g.shbin.h" +#include "uishader.h" +#include "uishader.shbin.h" struct ctrUIVertex { short x, y;

@@ -25,6 +23,7 @@ short w, h;

short u, v; short uw, vh; u32 abgr; + float rotate[2]; }; #define MAX_NUM_QUADS 256

@@ -36,34 +35,31 @@ static int ctrVertStart = 0;

static C3D_Tex* activeTexture = NULL; -static shaderProgram_s gpuShader; -static DVLB_s* vertexShader = NULL; -static DVLB_s* geometryShader = NULL; +static shaderProgram_s uiProgram; +static DVLB_s* uiShader = NULL; +static int GSH_FVEC_projectionMtx; +static int GSH_FVEC_textureMtx; bool ctrInitGpu() { // Load vertex shader binary - vertexShader = DVLB_ParseFile((u32*) uishader_v, uishader_v_size); - if (vertexShader == NULL) { - return false; - } - - // Load geometry shader binary - geometryShader = DVLB_ParseFile((u32*) uishader_g, uishader_g_size); - if (geometryShader == NULL) { + uiShader = DVLB_ParseFile((u32*) uishader, uishader_size); + if (uiShader == NULL) { return false; } // Create shader - shaderProgramInit(&gpuShader); - Result res = shaderProgramSetVsh(&gpuShader, &vertexShader->DVLE[0]); + shaderProgramInit(&uiProgram); + Result res = shaderProgramSetVsh(&uiProgram, &uiShader->DVLE[0]); if (res < 0) { return false; } - res = shaderProgramSetGsh(&gpuShader, &geometryShader->DVLE[0], 3); + res = shaderProgramSetGsh(&uiProgram, &uiShader->DVLE[1], 4); if (res < 0) { return false; } - C3D_BindProgram(&gpuShader); + C3D_BindProgram(&uiProgram); + GSH_FVEC_projectionMtx = shaderInstanceGetUniformLocation(uiProgram.geometryShader, "projectionMtx"); + GSH_FVEC_textureMtx = shaderInstanceGetUniformLocation(uiProgram.geometryShader, "textureMtx"); // Allocate buffers ctrVertexBuffer = linearAlloc(VERTEX_BUFFER_SIZE);

@@ -82,6 +78,7 @@ AttrInfo_Init(attrInfo);

AttrInfo_AddLoader(attrInfo, 0, GPU_SHORT, 4); // in_pos AttrInfo_AddLoader(attrInfo, 1, GPU_SHORT, 4); // in_tc0 AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col + AttrInfo_AddLoader(attrInfo, 3, GPU_FLOAT, 2); // in_rot return true; }

@@ -92,23 +89,22 @@ linearFree(ctrVertexBuffer);

ctrVertexBuffer = NULL; } - shaderProgramFree(&gpuShader); - - if (vertexShader) { - DVLB_Free(vertexShader); - vertexShader = NULL; - } + shaderProgramFree(&uiProgram); - if (geometryShader) { - DVLB_Free(geometryShader); - geometryShader = NULL; + if (uiShader) { + DVLB_Free(uiShader); + uiShader = NULL; } } -void ctrSetViewportSize(s16 w, s16 h) { +void ctrSetViewportSize(s16 w, s16 h, bool tilt) { C3D_SetViewport(0, 0, h, w); C3D_Mtx projectionMtx; - Mtx_OrthoTilt(&projectionMtx, 0.0, w, h, 0.0, 0.0, 1.0); + if (tilt) { + Mtx_OrthoTilt(&projectionMtx, 0.0, w, h, 0.0, 0.0, 1.0, true); + } else { + Mtx_Ortho(&projectionMtx, 0.0, w, 0.0, h, 0.0, 1.0, true); + } C3D_FVUnifMtx4x4(GPU_GEOMETRY_SHADER, GSH_FVEC_projectionMtx, &projectionMtx); }

@@ -145,7 +141,7 @@ };

C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx); } -void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh) { +void ctrAddRectEx(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh, float rotate) { if (x >= 400 && w >= 0) { return; }

@@ -170,12 +166,14 @@ vtx->v = v;

vtx->uw = uw; vtx->vh = vh; vtx->abgr = color; + vtx->rotate[0] = cosf(rotate); + vtx->rotate[1] = sinf(rotate); ++ctrNumVerts; } void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) { - ctrAddRectScaled(color, x, y, w, h, u, v, w, h); + ctrAddRectEx(color, x, y, w, h, u, v, w, h, 0); } void ctrFlushBatch(void) {

@@ -185,7 +183,7 @@ }

C3D_BufInfo* bufInfo = C3D_GetBufInfo(); BufInfo_Init(bufInfo); - BufInfo_Add(bufInfo, &ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex), 3, 0x210); + BufInfo_Add(bufInfo, &ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex), 4, 0x3210); GSPGPU_FlushDataCache(&ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex) * ctrNumVerts); C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts);
M src/platform/3ds/ctr-gpu.hsrc/platform/3ds/ctr-gpu.h

@@ -14,10 +14,10 @@

bool ctrInitGpu(void); void ctrDeinitGpu(void); -void ctrSetViewportSize(s16 w, s16 h); +void ctrSetViewportSize(s16 w, s16 h, bool tilt); void ctrActivateTexture(C3D_Tex* texture); -void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh); +void ctrAddRectEx(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh, float rotate); void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h); void ctrFlushBatch(void); void ctrFinalize(void);
M src/platform/3ds/gui-font.csrc/platform/3ds/gui-font.c

@@ -107,7 +107,9 @@ u16 y = glyph_y + tex->height * height / 8;

u16 u = tex->width * data.texcoord.left; u16 v = tex->height * data.texcoord.bottom; - ctrAddRectScaled(color, x, y, tex->width * width * FONT_SIZE, tex->height * height * -FONT_SIZE, u, v, tex->width * width, tex->height * height); + ctrAddRectEx(color, x, y, + tex->width * width * FONT_SIZE, tex->height * height * -FONT_SIZE, + u, v, tex->width * width, tex->height * height, 0); } void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) {

@@ -136,10 +138,16 @@ break;

} switch (orient) { case GUI_ORIENT_HMIRROR: - ctrAddRectScaled(color, x + metric.width, y, -metric.width, metric.height, metric.x, metric.y, metric.width, metric.height); + ctrAddRectEx(color, x + metric.width, y, + -metric.width, metric.height, + metric.x, metric.y, + metric.width, metric.height, 0); break; case GUI_ORIENT_VMIRROR: - ctrAddRectScaled(color, x, y + metric.height, metric.width, -metric.height, metric.x, metric.y, metric.width, metric.height); + ctrAddRectEx(color, x, y + metric.height, + metric.width, -metric.height, + metric.x, metric.y, + metric.width, metric.height, 0); break; case GUI_ORIENT_0: default:

@@ -157,5 +165,9 @@ return;

} struct GUIIconMetric metric = defaultIconMetrics[icon]; - ctrAddRectScaled(color, x, y, w ? w : metric.width, h ? h : metric.height, metric.x, metric.y, metric.width, metric.height); + ctrAddRectEx(color, x, y, + w ? w : metric.width, + h ? h : metric.height, + metric.x, metric.y, + metric.width, metric.height, 0); }
M src/platform/3ds/main.csrc/platform/3ds/main.c

@@ -35,6 +35,13 @@ SM_SF_TOP,

SM_MAX } screenMode = SM_PA_TOP; +static enum FilterMode { + FM_NEAREST, + FM_LINEAR_1x, + FM_LINEAR_2x, + FM_MAX +} filterMode = FM_LINEAR_2x; + #define _3DS_INPUT 0x3344534B #define AUDIO_SAMPLES 384

@@ -68,6 +75,7 @@ static bool frameLimiter = true;

static C3D_RenderBuf bottomScreen; static C3D_RenderBuf topScreen; +static C3D_RenderBuf upscaleBuffer; static aptHookCookie cookie;

@@ -78,7 +86,7 @@ if (!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE)) {

return false; } - if (!C3D_RenderBufInit(&topScreen, 240, 400, GPU_RB_RGB8, 0) || !C3D_RenderBufInit(&bottomScreen, 240, 320, GPU_RB_RGB8, 0)) { + if (!C3D_RenderBufInit(&topScreen, 240, 400, GPU_RB_RGB8, 0) || !C3D_RenderBufInit(&bottomScreen, 240, 320, GPU_RB_RGB8, 0) || !C3D_RenderBufInit(&upscaleBuffer, 512, 512, GPU_RB_RGB565, 0)) { return false; }

@@ -94,6 +102,7 @@ }

C3D_RenderBufDelete(&topScreen); C3D_RenderBufDelete(&bottomScreen); + C3D_RenderBufDelete(&upscaleBuffer); C3D_Fini(); gfxExit();

@@ -210,7 +219,7 @@ return;

} C3D_RenderBufBind(&bottomScreen); - ctrSetViewportSize(320, 240); + ctrSetViewportSize(320, 240, true); } static void _guiFinish(void) {

@@ -248,6 +257,14 @@ unsigned mode;

if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) { screenMode = mode; } + if (mCoreConfigGetUIntValue(&runner->config, "filterMode", &mode) && mode < FM_MAX) { + filterMode = mode; + if (filterMode == FM_NEAREST) { + C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_NEAREST, GPU_NEAREST); + } else { + C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_LINEAR, GPU_LINEAR); + } + } frameLimiter = true; runner->core->setAudioBufferSize(runner->core, AUDIO_SAMPLES);

@@ -292,9 +309,17 @@ } else if (hasSound == DSP_SUPPORTED) {

memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t)); } unsigned mode; - if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode != screenMode) { + if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) { screenMode = mode; } + if (mCoreConfigGetUIntValue(&runner->config, "filterMode", &mode) && mode < FM_MAX) { + filterMode = mode; + if (filterMode == FM_NEAREST) { + C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_NEAREST, GPU_NEAREST); + } else { + C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_LINEAR, GPU_LINEAR); + } + } } static void _gameUnloaded(struct mGUIRunner* runner) {

@@ -330,19 +355,26 @@ }

} static void _drawTex(struct mCore* core, bool faded) { - if (screenMode < SM_PA_TOP) { + unsigned screen_w, screen_h; + switch (screenMode) { + case SM_PA_TOP: C3D_RenderBufBind(&bottomScreen); - ctrSetViewportSize(320, 240); - } else { + screen_w = 320; + screen_h = 240; + break; + case SM_PA_BOTTOM: C3D_RenderBufBind(&topScreen); - ctrSetViewportSize(400, 240); + screen_w = 400; + screen_h = 240; + break; + default: + C3D_RenderBufBind(&upscaleBuffer); + screen_w = upscaleBuffer.colorBuf.width; + screen_h = upscaleBuffer.colorBuf.height; + break; } - ctrActivateTexture(&outputTexture); u32 color = faded ? 0x3FFFFFFF : 0xFFFFFFFF; - - int screen_w = screenMode < SM_PA_TOP ? 320 : 400; - int screen_h = 240; unsigned corew, coreh; core->desiredVideoDimensions(core, &corew, &coreh);

@@ -356,18 +388,58 @@ h = w;

w = temp; } int gcd = h; - int aspectw = corew / gcd; - int aspecth = coreh / gcd; + unsigned aspectw = corew / gcd; + unsigned aspecth = coreh / gcd; + int x = 0; + int y = 0; switch (screenMode) { case SM_PA_TOP: case SM_PA_BOTTOM: - default: w = corew; h = coreh; + x = (screen_w - w) / 2; + y = (screen_h - h) / 2; + ctrSetViewportSize(screen_w, screen_h, true); + ctrActivateTexture(&outputTexture); break; case SM_AF_TOP: case SM_AF_BOTTOM: + case SM_SF_TOP: + case SM_SF_BOTTOM: + default: + if (filterMode == FM_LINEAR_1x) { + w = corew; + h = coreh; + } else { + w = corew * 2; + h = coreh * 2; + } + ctrSetViewportSize(screen_w, screen_h, false); + ctrActivateTexture(&outputTexture); + break; + } + + ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0); + ctrFlushBatch(); + + corew = w; + coreh = h; + screen_h = 240; + if (screenMode < SM_PA_TOP) { + C3D_RenderBufBind(&bottomScreen); + screen_w = 320; + } else { + C3D_RenderBufBind(&topScreen); + screen_w = 400; + } + ctrSetViewportSize(screen_w, screen_h, true); + + switch (screenMode) { + default: + return; + case SM_AF_TOP: + case SM_AF_BOTTOM: w = screen_w / aspectw; h = screen_h / aspecth; if (w * aspecth > screen_h) {

@@ -385,10 +457,10 @@ h = screen_h;

break; } - int x = (screen_w - w) / 2; - int y = (screen_h - h) / 2; - - ctrAddRectScaled(color, x, y, w, h, 0, 0, corew, coreh); + x = (screen_w - w) / 2; + y = (screen_h - h) / 2; + ctrActivateTexture(&upscaleBuffer.colorBuf); + ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0); ctrFlushBatch(); }

@@ -604,7 +676,8 @@ _cleanup();

return 1; } C3D_TexSetWrap(&outputTexture, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE); - C3D_TexSetFilter(&outputTexture, GPU_LINEAR, GPU_LINEAR); + C3D_TexSetFilter(&outputTexture, GPU_NEAREST, GPU_NEAREST); + C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_LINEAR, GPU_LINEAR); void* outputTextureEnd = (u8*)outputTexture.data + 256 * 256 * 2; // Zero texture data to make sure no garbage around the border interferes with filtering

@@ -686,9 +759,21 @@ "Aspect-Ratio Fit/Top",

"Stretched/Top", }, .nStates = 6 + }, + { + .title = "Filtering", + .data = "filterMode", + .submenu = 0, + .state = FM_LINEAR_2x, + .validStates = (const char*[]) { + NULL, // Disable choosing nearest neighbor; it always looks bad + "Bilinear (smooter)", + "Bilinear (pixelated)", + }, + .nStates = 3 } }, - .nConfigExtra = 1, + .nConfigExtra = 2, .setup = _setup, .teardown = 0, .gameLoaded = _gameLoaded,
M src/platform/3ds/uishader.g.picasrc/platform/3ds/uishader.g.pica

@@ -5,6 +5,14 @@ ; This Source Code Form is subject to the terms of the Mozilla Public

; License, v. 2.0. If a copy of the MPL was not distributed with this ; file, You can obtain one at http://mozilla.org/MPL/2.0/. +; Inputs +.alias in_pos v0 ; [x, y, w, h] +.alias in_tc0 v1 ; [u, v, uw, vh] +.alias in_col v2 +.alias in_rot v3 + +.gsh point c0 + ; Uniforms .fvec projectionMtx[4] .fvec textureMtx[2]

@@ -12,59 +20,77 @@

; Constants .constf consts1(0.0, 1.0, -0.5, -1.0) -; Outputs : here only position and color +; Outputs .out out_pos position .out out_tc0 texcoord0 .out out_col color -; Inputs : here we have only vertices -.alias in_pos v0 -.alias in_tc0 v1 -.alias in_col v2 - -.gsh -.proc main +.entry gshMain +.proc gshMain ; Set up the vertex endpoints - mov r0.xy, in_pos.xy - mov r0.zw, consts1.zy - add r1.xy, r0.xy, in_pos.zw + mov r0.xy, in_pos.zw + mov r0.zw, consts1.xx + mov r4.xy, in_pos.xy + add r5, r4.xyzw, r0.xwww + add r6, r4.xyzw, r0.xyww + add r7, r4.xyzw, r0.wyww + + ; Create rotation matrix + mov r8, in_rot.xzww + mov r9, in_rot.yxww + mov r10.zw, consts1.zy + + ; Transform coordinates + dp4 r10.x, r8, r4 + dp4 r10.y, r9, r4 + dp4 r0.x, projectionMtx[0], r10 + dp4 r0.y, projectionMtx[1], r10 + dp4 r0.z, projectionMtx[2], r10 + dp4 r0.w, projectionMtx[3], r10 + + dp4 r10.x, r8, r5 + dp4 r10.y, r9, r5 + dp4 r1.x, projectionMtx[0], r10 + dp4 r1.y, projectionMtx[1], r10 + dp4 r1.z, projectionMtx[2], r10 + dp4 r1.w, projectionMtx[3], r10 - dp4 r2.x, projectionMtx[0], r0 - dp4 r2.y, projectionMtx[1], r0 - dp4 r2.z, projectionMtx[2], r0 - dp4 r2.w, projectionMtx[3], r0 + dp4 r10.x, r8, r6 + dp4 r10.y, r9, r6 + dp4 r2.x, projectionMtx[0], r10 + dp4 r2.y, projectionMtx[1], r10 + dp4 r2.z, projectionMtx[2], r10 + dp4 r2.w, projectionMtx[3], r10 - dp4 r3.x, projectionMtx[0], r1 - dp4 r3.y, projectionMtx[1], r1 - dp4 r3.z, projectionMtx[2], r1 - dp4 r3.w, projectionMtx[3], r1 + dp4 r10.x, r8, r7 + dp4 r10.y, r9, r7 + dp4 r3.x, projectionMtx[0], r10 + dp4 r3.y, projectionMtx[1], r10 + dp4 r3.z, projectionMtx[2], r10 + dp4 r3.w, projectionMtx[3], r10 ; Set up the texture endpoints - mov r0.xy, in_tc0.xy - mov r0.zw, consts1.xy - add r1.xy, r0.xy, in_tc0.zw + mov r6.xy, in_tc0.xy + add r7.xy, r6.xy, in_tc0.zw - dp4 r4.x, textureMtx[0], r0 - dp4 r4.y, textureMtx[1], r0 + dp4 r4.x, textureMtx[0], r6 + dp4 r4.y, textureMtx[1], r6 mov r4.zw, consts1.xy - dp4 r5.x, textureMtx[0], r1 - dp4 r5.y, textureMtx[1], r1 + dp4 r5.x, textureMtx[0], r7 + dp4 r5.y, textureMtx[1], r7 mov r5.zw, consts1.xy ; Emit top-left setemit 0 - mov out_pos.xyzw, r2.xyzw + mov out_pos, r0 mov out_tc0.xyzw, r4.xyzw mov out_col, in_col emit ; Emit bottom-left setemit 1 - mov out_pos.x, r2.x - mov out_pos.y, r3.y - mov out_pos.z, consts1.z - mov out_pos.w, consts1.y + mov out_pos, r1 mov out_tc0.x, r5.x mov out_tc0.y, r4.y mov out_tc0.z, consts1.x

@@ -74,17 +100,14 @@ emit

; Emit bottom-right setemit 2, prim - mov out_pos.xyzw, r3.xyzw + mov out_pos, r2 mov out_tc0.xyzw, r5.xyzw mov out_col, in_col emit ; Emit top-right setemit 1, prim inv - mov out_pos.x, r3.x - mov out_pos.y, r2.y - mov out_pos.z, consts1.z - mov out_pos.w, consts1.y + mov out_pos, r3 mov out_tc0.x, r4.x mov out_tc0.y, r5.y mov out_tc0.z, consts1.x
M src/platform/3ds/uishader.v.picasrc/platform/3ds/uishader.v.pica

@@ -18,15 +18,19 @@ ; Outputs

.out out_pos position .out out_tc0 texcoord0 .out out_col color +.out out_rot dummy ; Inputs .alias in_pos v0 .alias in_tc0 v1 .alias in_col v2 +.alias in_rot v3 -.proc main +.entry vshMain +.proc vshMain mov out_pos, in_pos mov out_tc0, in_tc0 + mul out_rot, consts1.ywyx, in_rot.xyy ; Normalize color by multiplying by 1 / 255 mul out_col, consts1.z, in_col