3DS: Implement adjustable filtering and sprite rotation

This commit is contained in:
Jeffrey Pfau 2016-08-21 03:16:00 -07:00
parent 39230ca9ac
commit 526f86d085
8 changed files with 235 additions and 133 deletions

View File

@ -66,6 +66,7 @@ Misc:
- Util: Add Vector GetConstPointer - Util: Add Vector GetConstPointer
- Util: Add rtrim - Util: Add rtrim
- GBA Memory: Optimize Load-/StoreMultiple - GBA Memory: Optimize Load-/StoreMultiple
- 3DS: Adjustable filering
0.4.1: (2016-07-11) 0.4.1: (2016-07-11)
Bugfixes: Bugfixes:

View File

@ -30,12 +30,9 @@ set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)
list(APPEND GUI_SRC list(APPEND GUI_SRC
${CMAKE_CURRENT_BINARY_DIR}/icons.c ${CMAKE_CURRENT_BINARY_DIR}/icons.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c ${CMAKE_CURRENT_BINARY_DIR}/uishader.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h ${CMAKE_CURRENT_BINARY_DIR}/uishader.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h ${CMAKE_CURRENT_BINARY_DIR}/uishader.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_SOURCE_DIR}/gui-font.c ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c
${CMAKE_CURRENT_SOURCE_DIR}/ctr-gpu.c ${CMAKE_CURRENT_SOURCE_DIR}/ctr-gpu.c
@ -43,12 +40,9 @@ list(APPEND GUI_SRC
set_source_files_properties( set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/icons.c ${CMAKE_CURRENT_BINARY_DIR}/icons.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c ${CMAKE_CURRENT_BINARY_DIR}/uishader.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h ${CMAKE_CURRENT_BINARY_DIR}/uishader.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h
PROPERTIES GENERATED ON) PROPERTIES GENERATED ON)
add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c) add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c)
@ -68,36 +62,21 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.c
DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw) DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica ${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica
COMMAND ${PICASSO} COMMAND ${PICASSO}
-o ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin -o ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin
-h ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h -h ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
${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 ${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica
COMMENT "picasso uishader.v.pica") ${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica
COMMENT "picasso uishader.shbin")
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.c ${CMAKE_CURRENT_BINARY_DIR}/uishader.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "raw2c uishader.v.shbin") COMMENT "raw2c uishader.shbin")
add_custom_command(OUTPUT ${BINARY_NAME}.3dsx add_custom_command(OUTPUT ${BINARY_NAME}.3dsx
COMMAND ${3DSXTOOL} ${BINARY_NAME}.elf ${BINARY_NAME}.3dsx --smdh=${BINARY_NAME}.smdh COMMAND ${3DSXTOOL} ${BINARY_NAME}.elf ${BINARY_NAME}.3dsx --smdh=${BINARY_NAME}.smdh

View File

@ -14,10 +14,8 @@
#include "ctr-gpu.h" #include "ctr-gpu.h"
#include "uishader_v.h" #include "uishader.h"
#include "uishader_v.shbin.h" #include "uishader.shbin.h"
#include "uishader_g.h"
#include "uishader_g.shbin.h"
struct ctrUIVertex { struct ctrUIVertex {
short x, y; short x, y;
@ -25,6 +23,7 @@ struct ctrUIVertex {
short u, v; short u, v;
short uw, vh; short uw, vh;
u32 abgr; u32 abgr;
float rotate[2];
}; };
#define MAX_NUM_QUADS 256 #define MAX_NUM_QUADS 256
@ -36,34 +35,31 @@ static int ctrVertStart = 0;
static C3D_Tex* activeTexture = NULL; static C3D_Tex* activeTexture = NULL;
static shaderProgram_s gpuShader; static shaderProgram_s uiProgram;
static DVLB_s* vertexShader = NULL; static DVLB_s* uiShader = NULL;
static DVLB_s* geometryShader = NULL; static int GSH_FVEC_projectionMtx;
static int GSH_FVEC_textureMtx;
bool ctrInitGpu() { bool ctrInitGpu() {
// Load vertex shader binary // Load vertex shader binary
vertexShader = DVLB_ParseFile((u32*) uishader_v, uishader_v_size); uiShader = DVLB_ParseFile((u32*) uishader, uishader_size);
if (vertexShader == NULL) { if (uiShader == NULL) {
return false;
}
// Load geometry shader binary
geometryShader = DVLB_ParseFile((u32*) uishader_g, uishader_g_size);
if (geometryShader == NULL) {
return false; return false;
} }
// Create shader // Create shader
shaderProgramInit(&gpuShader); shaderProgramInit(&uiProgram);
Result res = shaderProgramSetVsh(&gpuShader, &vertexShader->DVLE[0]); Result res = shaderProgramSetVsh(&uiProgram, &uiShader->DVLE[0]);
if (res < 0) { if (res < 0) {
return false; return false;
} }
res = shaderProgramSetGsh(&gpuShader, &geometryShader->DVLE[0], 3); res = shaderProgramSetGsh(&uiProgram, &uiShader->DVLE[1], 4);
if (res < 0) { if (res < 0) {
return false; return false;
} }
C3D_BindProgram(&gpuShader); C3D_BindProgram(&uiProgram);
GSH_FVEC_projectionMtx = shaderInstanceGetUniformLocation(uiProgram.geometryShader, "projectionMtx");
GSH_FVEC_textureMtx = shaderInstanceGetUniformLocation(uiProgram.geometryShader, "textureMtx");
// Allocate buffers // Allocate buffers
ctrVertexBuffer = linearAlloc(VERTEX_BUFFER_SIZE); ctrVertexBuffer = linearAlloc(VERTEX_BUFFER_SIZE);
@ -82,6 +78,7 @@ bool ctrInitGpu() {
AttrInfo_AddLoader(attrInfo, 0, GPU_SHORT, 4); // in_pos AttrInfo_AddLoader(attrInfo, 0, GPU_SHORT, 4); // in_pos
AttrInfo_AddLoader(attrInfo, 1, GPU_SHORT, 4); // in_tc0 AttrInfo_AddLoader(attrInfo, 1, GPU_SHORT, 4); // in_tc0
AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col
AttrInfo_AddLoader(attrInfo, 3, GPU_FLOAT, 2); // in_rot
return true; return true;
} }
@ -92,23 +89,22 @@ void ctrDeinitGpu() {
ctrVertexBuffer = NULL; ctrVertexBuffer = NULL;
} }
shaderProgramFree(&gpuShader); shaderProgramFree(&uiProgram);
if (vertexShader) { if (uiShader) {
DVLB_Free(vertexShader); DVLB_Free(uiShader);
vertexShader = NULL; uiShader = NULL;
}
if (geometryShader) {
DVLB_Free(geometryShader);
geometryShader = NULL;
} }
} }
void ctrSetViewportSize(s16 w, s16 h) { void ctrSetViewportSize(s16 w, s16 h, bool tilt) {
C3D_SetViewport(0, 0, h, w); C3D_SetViewport(0, 0, h, w);
C3D_Mtx projectionMtx; 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); C3D_FVUnifMtx4x4(GPU_GEOMETRY_SHADER, GSH_FVEC_projectionMtx, &projectionMtx);
} }
@ -145,7 +141,7 @@ void ctrActivateTexture(C3D_Tex* texture) {
C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx); 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) { if (x >= 400 && w >= 0) {
return; return;
} }
@ -170,12 +166,14 @@ void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 u
vtx->uw = uw; vtx->uw = uw;
vtx->vh = vh; vtx->vh = vh;
vtx->abgr = color; vtx->abgr = color;
vtx->rotate[0] = cosf(rotate);
vtx->rotate[1] = sinf(rotate);
++ctrNumVerts; ++ctrNumVerts;
} }
void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) { 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) { void ctrFlushBatch(void) {
@ -185,7 +183,7 @@ void ctrFlushBatch(void) {
C3D_BufInfo* bufInfo = C3D_GetBufInfo(); C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo); 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); GSPGPU_FlushDataCache(&ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex) * ctrNumVerts);
C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts); C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts);

View File

@ -14,10 +14,10 @@
bool ctrInitGpu(void); bool ctrInitGpu(void);
void ctrDeinitGpu(void); void ctrDeinitGpu(void);
void ctrSetViewportSize(s16 w, s16 h); void ctrSetViewportSize(s16 w, s16 h, bool tilt);
void ctrActivateTexture(C3D_Tex* texture); 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 ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h);
void ctrFlushBatch(void); void ctrFlushBatch(void);
void ctrFinalize(void); void ctrFinalize(void);

View File

@ -107,7 +107,9 @@ void GUIFontDrawGlyph(const struct GUIFont* font, int glyph_x, int glyph_y, uint
u16 u = tex->width * data.texcoord.left; u16 u = tex->width * data.texcoord.left;
u16 v = tex->height * data.texcoord.bottom; 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) { 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 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
} }
switch (orient) { switch (orient) {
case GUI_ORIENT_HMIRROR: 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; break;
case GUI_ORIENT_VMIRROR: 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; break;
case GUI_ORIENT_0: case GUI_ORIENT_0:
default: default:
@ -157,5 +165,9 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h,
} }
struct GUIIconMetric metric = defaultIconMetrics[icon]; 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);
} }

View File

@ -35,6 +35,13 @@ static enum ScreenMode {
SM_MAX SM_MAX
} screenMode = SM_PA_TOP; } 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 _3DS_INPUT 0x3344534B
#define AUDIO_SAMPLES 384 #define AUDIO_SAMPLES 384
@ -68,6 +75,7 @@ static bool frameLimiter = true;
static C3D_RenderBuf bottomScreen; static C3D_RenderBuf bottomScreen;
static C3D_RenderBuf topScreen; static C3D_RenderBuf topScreen;
static C3D_RenderBuf upscaleBuffer;
static aptHookCookie cookie; static aptHookCookie cookie;
@ -78,7 +86,7 @@ static bool _initGpu(void) {
return false; 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; return false;
} }
@ -94,6 +102,7 @@ static void _cleanup(void) {
C3D_RenderBufDelete(&topScreen); C3D_RenderBufDelete(&topScreen);
C3D_RenderBufDelete(&bottomScreen); C3D_RenderBufDelete(&bottomScreen);
C3D_RenderBufDelete(&upscaleBuffer);
C3D_Fini(); C3D_Fini();
gfxExit(); gfxExit();
@ -210,7 +219,7 @@ static void _guiPrepare(void) {
} }
C3D_RenderBufBind(&bottomScreen); C3D_RenderBufBind(&bottomScreen);
ctrSetViewportSize(320, 240); ctrSetViewportSize(320, 240, true);
} }
static void _guiFinish(void) { static void _guiFinish(void) {
@ -248,6 +257,14 @@ static void _setup(struct mGUIRunner* runner) {
if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) { if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
screenMode = mode; 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; frameLimiter = true;
runner->core->setAudioBufferSize(runner->core, AUDIO_SAMPLES); runner->core->setAudioBufferSize(runner->core, AUDIO_SAMPLES);
@ -292,9 +309,17 @@ static void _gameLoaded(struct mGUIRunner* runner) {
memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t)); memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t));
} }
unsigned mode; unsigned mode;
if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode != screenMode) { if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
screenMode = mode; 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) { static void _gameUnloaded(struct mGUIRunner* runner) {
@ -330,20 +355,27 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
} }
static void _drawTex(struct mCore* core, bool faded) { 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); C3D_RenderBufBind(&bottomScreen);
ctrSetViewportSize(320, 240); screen_w = 320;
} else { screen_h = 240;
break;
case SM_PA_BOTTOM:
C3D_RenderBufBind(&topScreen); 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; u32 color = faded ? 0x3FFFFFFF : 0xFFFFFFFF;
int screen_w = screenMode < SM_PA_TOP ? 320 : 400;
int screen_h = 240;
unsigned corew, coreh; unsigned corew, coreh;
core->desiredVideoDimensions(core, &corew, &coreh); core->desiredVideoDimensions(core, &corew, &coreh);
@ -356,17 +388,57 @@ static void _drawTex(struct mCore* core, bool faded) {
w = temp; w = temp;
} }
int gcd = h; int gcd = h;
int aspectw = corew / gcd; unsigned aspectw = corew / gcd;
int aspecth = coreh / gcd; unsigned aspecth = coreh / gcd;
int x = 0;
int y = 0;
switch (screenMode) { switch (screenMode) {
case SM_PA_TOP: case SM_PA_TOP:
case SM_PA_BOTTOM: case SM_PA_BOTTOM:
default:
w = corew; w = corew;
h = coreh; h = coreh;
x = (screen_w - w) / 2;
y = (screen_h - h) / 2;
ctrSetViewportSize(screen_w, screen_h, true);
ctrActivateTexture(&outputTexture);
break; break;
case SM_AF_TOP: 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: case SM_AF_BOTTOM:
w = screen_w / aspectw; w = screen_w / aspectw;
h = screen_h / aspecth; h = screen_h / aspecth;
@ -385,10 +457,10 @@ static void _drawTex(struct mCore* core, bool faded) {
break; break;
} }
int x = (screen_w - w) / 2; x = (screen_w - w) / 2;
int y = (screen_h - h) / 2; y = (screen_h - h) / 2;
ctrActivateTexture(&upscaleBuffer.colorBuf);
ctrAddRectScaled(color, x, y, w, h, 0, 0, corew, coreh); ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0);
ctrFlushBatch(); ctrFlushBatch();
} }
@ -604,7 +676,8 @@ int main() {
return 1; return 1;
} }
C3D_TexSetWrap(&outputTexture, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE); 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; void* outputTextureEnd = (u8*)outputTexture.data + 256 * 256 * 2;
// Zero texture data to make sure no garbage around the border interferes with filtering // Zero texture data to make sure no garbage around the border interferes with filtering
@ -686,9 +759,21 @@ int main() {
"Stretched/Top", "Stretched/Top",
}, },
.nStates = 6 .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, .setup = _setup,
.teardown = 0, .teardown = 0,
.gameLoaded = _gameLoaded, .gameLoaded = _gameLoaded,

View File

@ -5,6 +5,14 @@
; License, v. 2.0. If a copy of the MPL was not distributed with this ; 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/. ; 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 ; Uniforms
.fvec projectionMtx[4] .fvec projectionMtx[4]
.fvec textureMtx[2] .fvec textureMtx[2]
@ -12,59 +20,77 @@
; Constants ; Constants
.constf consts1(0.0, 1.0, -0.5, -1.0) .constf consts1(0.0, 1.0, -0.5, -1.0)
; Outputs : here only position and color ; Outputs
.out out_pos position .out out_pos position
.out out_tc0 texcoord0 .out out_tc0 texcoord0
.out out_col color .out out_col color
; Inputs : here we have only vertices .entry gshMain
.alias in_pos v0 .proc gshMain
.alias in_tc0 v1
.alias in_col v2
.gsh
.proc main
; Set up the vertex endpoints ; Set up the vertex endpoints
mov r0.xy, in_pos.xy mov r0.xy, in_pos.zw
mov r0.zw, consts1.zy mov r0.zw, consts1.xx
add r1.xy, r0.xy, in_pos.zw mov r4.xy, in_pos.xy
add r5, r4.xyzw, r0.xwww
add r6, r4.xyzw, r0.xyww
add r7, r4.xyzw, r0.wyww
dp4 r2.x, projectionMtx[0], r0 ; Create rotation matrix
dp4 r2.y, projectionMtx[1], r0 mov r8, in_rot.xzww
dp4 r2.z, projectionMtx[2], r0 mov r9, in_rot.yxww
dp4 r2.w, projectionMtx[3], r0 mov r10.zw, consts1.zy
dp4 r3.x, projectionMtx[0], r1 ; Transform coordinates
dp4 r3.y, projectionMtx[1], r1 dp4 r10.x, r8, r4
dp4 r3.z, projectionMtx[2], r1 dp4 r10.y, r9, r4
dp4 r3.w, projectionMtx[3], r1 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 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 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 ; Set up the texture endpoints
mov r0.xy, in_tc0.xy mov r6.xy, in_tc0.xy
mov r0.zw, consts1.xy add r7.xy, r6.xy, in_tc0.zw
add r1.xy, r0.xy, in_tc0.zw
dp4 r4.x, textureMtx[0], r0 dp4 r4.x, textureMtx[0], r6
dp4 r4.y, textureMtx[1], r0 dp4 r4.y, textureMtx[1], r6
mov r4.zw, consts1.xy mov r4.zw, consts1.xy
dp4 r5.x, textureMtx[0], r1 dp4 r5.x, textureMtx[0], r7
dp4 r5.y, textureMtx[1], r1 dp4 r5.y, textureMtx[1], r7
mov r5.zw, consts1.xy mov r5.zw, consts1.xy
; Emit top-left ; Emit top-left
setemit 0 setemit 0
mov out_pos.xyzw, r2.xyzw mov out_pos, r0
mov out_tc0.xyzw, r4.xyzw mov out_tc0.xyzw, r4.xyzw
mov out_col, in_col mov out_col, in_col
emit emit
; Emit bottom-left ; Emit bottom-left
setemit 1 setemit 1
mov out_pos.x, r2.x mov out_pos, r1
mov out_pos.y, r3.y
mov out_pos.z, consts1.z
mov out_pos.w, consts1.y
mov out_tc0.x, r5.x mov out_tc0.x, r5.x
mov out_tc0.y, r4.y mov out_tc0.y, r4.y
mov out_tc0.z, consts1.x mov out_tc0.z, consts1.x
@ -74,17 +100,14 @@
; Emit bottom-right ; Emit bottom-right
setemit 2, prim setemit 2, prim
mov out_pos.xyzw, r3.xyzw mov out_pos, r2
mov out_tc0.xyzw, r5.xyzw mov out_tc0.xyzw, r5.xyzw
mov out_col, in_col mov out_col, in_col
emit emit
; Emit top-right ; Emit top-right
setemit 1, prim inv setemit 1, prim inv
mov out_pos.x, r3.x mov out_pos, r3
mov out_pos.y, r2.y
mov out_pos.z, consts1.z
mov out_pos.w, consts1.y
mov out_tc0.x, r4.x mov out_tc0.x, r4.x
mov out_tc0.y, r5.y mov out_tc0.y, r5.y
mov out_tc0.z, consts1.x mov out_tc0.z, consts1.x

View File

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