Compare commits

...

35 Commits

Author SHA1 Message Date
Dave Murphy
9f21cf7b38
libctru 1.7.1 2023-10-28 21:06:58 +01:00
fincs
00396e8a99
Fix for libctru v2.3.0 2023-10-28 21:57:00 +02:00
oreo639
66a0594e5d Add C3D_LightEnvBumpNormalZ()
Used to configure whether the Z component of the normal map is used or if
the Z component is reconstructed based on the XY components of the normal map.
2023-08-12 16:52:36 +02:00
oreo639
e8825650c6 Correct typo in tex3ds docs 2022-08-06 11:43:21 +02:00
fincs
e2992d276f
Fix #58 2022-05-28 21:14:55 +02:00
fincs
b6d0b7d876
citro3d v1.7.0 2022-01-28 23:49:36 +01:00
fincs
bbe09c4265
Implement VRAM bank awareness for rendertarget allocations 2021-08-26 23:46:07 +02:00
fincs
a491a8eb79
Use __3DS__ instead of _3DS 2021-08-07 13:11:39 +02:00
fincs
c7425b653e
Remove spurious warning 2021-04-29 17:54:15 +02:00
fincs
100ebd4067
citro3d v1.6.2 2020-12-19 17:19:38 +01:00
fincs
03235602cd
Add C3D_RenderTargetDetachOutput (called automatically on render target destroy) 2020-09-28 23:33:02 +02:00
fincs
4e02e27222
Bump version for release 2020-07-16 16:13:45 +02:00
fincs
05e8039b7f
Tell the compiler we don't check errno when calling math.h functions (work around a C stdlib design flaw) 2020-07-10 13:22:33 +02:00
fincs
f4367ad55d
Use gfxScreenSwapBuffers; implement left->right eye duplication with it 2020-07-07 13:08:56 +02:00
fincs
bbbadebeae
Pause the VBlank counters while the application is suspended 2020-07-06 20:21:03 +02:00
fincs
7999093588
Bump version for release 2020-05-10 01:18:22 +02:00
fincs
dcb3aac861
base/renderqueue: Clean up initialization code 2020-05-05 16:57:18 +02:00
fincs
3d566ac8da
Delete long-since obsolete flush functions 2020-05-05 01:06:36 +02:00
fincs
f2850e3bfe
renderqueue: Remove overengineered transfer system now that we know GSP actually processes framebuffer swaps at vblank... 2020-05-04 22:23:49 +02:00
fincs
c22c354f1a Bump version for release 2019-01-02 00:29:47 +01:00
fincs
9d51a9445d Gas changes:
- Separate gas reg writes according to stages
- Related: Properly support C3D_GasAccMax
- Add C3D_GasBeginAcc
- Set correct gas depth function matching depth test in C3Di_EffectBind
2018-05-23 13:54:49 +02:00
fincs
ebf41b6436 Make sure framebuffer is flushed/reinitialized before changing program 2018-05-22 21:23:17 +02:00
fincs
8d8979947e Remove deprecated functionality 2018-05-22 16:56:19 +02:00
fincs
81ca5d3575 Begin adding Gas rendering support {incomplete/not fully RE'd} 2018-05-22 16:25:40 +02:00
fincs
52cdcb351a Bump version for release and use DESTDIR for pacman packaging 2018-05-12 18:22:52 +02:00
fincs
5e1bc20126 Add license 2018-05-12 18:22:08 +02:00
fincs
6b6e9eef80 Fix C3D_TexShadowParams not updating TexStatus (reported by @wwylele) 2018-03-13 13:14:32 +01:00
fincs
c23a8015f3 Remove superfluous const in tex3ds.h/c 2018-03-02 19:42:22 +01:00
fincs
c2226e2182 Optimize Quat_FromMtx() 2017-12-30 21:09:10 +01:00
fincs
8f3fda7986 Deprecate C3D_SafeXYZ functions & replace them with C3D_SyncXYZ 2017-12-01 17:46:14 +01:00
fincs
b59cdc3c7c Deprecate C3D_RenderTargetSetClear, use C3D_RenderTargetClear instead 2017-12-01 17:46:14 +01:00
fincs
d24e404582 Properly validate dimensions in texture init 2017-12-01 17:46:14 +01:00
fincs
10e929d4bb Refactor texenv code in order to allow for stronger type checking 2017-12-01 17:46:14 +01:00
fincs
4ed5536bcc Add C3D_FrameEndHook() 2017-12-01 17:46:14 +01:00
Michael Theall
8764804dd5 Add tex3ds.h interface for loading assets converted by tex3ds 2017-12-01 17:42:24 +01:00
25 changed files with 1045 additions and 299 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.*/
*~ *~
*.3dsx *.3dsx
*.elf *.elf

18
LICENSE Normal file
View File

@ -0,0 +1,18 @@
Copyright (C) 2014-2018 fincs
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

View File

@ -9,7 +9,7 @@ endif
include $(DEVKITARM)/3ds_rules include $(DEVKITARM)/3ds_rules
export CITRO3D_MAJOR := 1 export CITRO3D_MAJOR := 1
export CITRO3D_MINOR := 3 export CITRO3D_MINOR := 7
export CITRO3D_PATCH := 1 export CITRO3D_PATCH := 1
VERSION := $(CITRO3D_MAJOR).$(CITRO3D_MINOR).$(CITRO3D_PATCH) VERSION := $(CITRO3D_MAJOR).$(CITRO3D_MINOR).$(CITRO3D_PATCH)
@ -22,8 +22,7 @@ VERSION := $(CITRO3D_MAJOR).$(CITRO3D_MINOR).$(CITRO3D_PATCH)
# INCLUDES is a list of directories containing header files # INCLUDES is a list of directories containing header files
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
TARGET := citro3d TARGET := citro3d
SOURCES := source \ SOURCES := source source/maths
source/maths
DATA := data DATA := data
INCLUDES := include INCLUDES := include
@ -32,11 +31,11 @@ INCLUDES := include
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -g -Wall -Werror -mword-relocations \ CFLAGS := -g -Wall -Wno-sizeof-array-div -Werror -mword-relocations \
-ffunction-sections -fdata-sections \ -ffunction-sections -fdata-sections \
$(ARCH) $(BUILD_CFLAGS) $(ARCH) $(BUILD_CFLAGS)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DCITRO3D_BUILD CFLAGS += $(INCLUDE) -D__3DS__ -DCITRO3D_BUILD
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
@ -101,8 +100,8 @@ dist-src:
dist: dist-src dist-bin dist: dist-src dist-bin
install: dist-bin install: dist-bin
mkdir -p $(DEVKITPRO)/libctru mkdir -p $(DESTDIR)$(DEVKITPRO)/libctru
bzip2 -cd citro3d-$(VERSION).tar.bz2 | tar -xf - -C $(DEVKITPRO)/libctru bzip2 -cd citro3d-$(VERSION).tar.bz2 | tar -xf - -C $(DESTDIR)$(DEVKITPRO)/libctru
lib: lib:
@[ -d $@ ] || mkdir -p $@ @[ -d $@ ] || mkdir -p $@
@ -115,7 +114,7 @@ debug:
lib/libcitro3d.a : lib release $(SOURCES) $(INCLUDES) lib/libcitro3d.a : lib release $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DNDEBUG=1 -O2 -fomit-frame-pointer" \ BUILD_CFLAGS="-DNDEBUG=1 -O2 -fomit-frame-pointer -fno-math-errno" \
DEPSDIR=$(CURDIR)/release \ DEPSDIR=$(CURDIR)/release \
--no-print-directory -C release \ --no-print-directory -C release \
-f $(CURDIR)/Makefile -f $(CURDIR)/Makefile

View File

@ -11,7 +11,6 @@ enum
}; };
bool C3D_Init(size_t cmdBufSize); bool C3D_Init(size_t cmdBufSize);
void C3D_FlushAsync(void);
void C3D_Fini(void); void C3D_Fini(void);
float C3D_GetCmdBufUsage(void); float C3D_GetCmdBufUsage(void);
@ -34,23 +33,6 @@ static inline void C3D_ImmDrawRestartPrim(void)
GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1);
} }
static inline void C3D_FlushAwait(void)
{
gspWaitForP3D();
}
static inline void C3D_Flush(void)
{
C3D_FlushAsync();
C3D_FlushAwait();
}
static inline void C3D_VideoSync(void)
{
gspWaitForEvent(GSPGPU_EVENT_VBlank0, false);
gfxSwapBuffersGpu();
}
// Fixed vertex attributes // Fixed vertex attributes
C3D_FVec* C3D_FixedAttribGetWritePtr(int id); C3D_FVec* C3D_FixedAttribGetWritePtr(int id);

View File

@ -7,6 +7,12 @@ typedef struct
u32 data[128]; u32 data[128];
} C3D_FogLut; } C3D_FogLut;
typedef struct
{
u32 diff[8];
u32 color[8];
} C3D_GasLut;
static inline float FogLut_CalcZ(float depth, float near, float far) static inline float FogLut_CalcZ(float depth, float near, float far)
{ {
return far*near/(depth*(far-near)+near); return far*near/(depth*(far-near)+near);
@ -18,3 +24,16 @@ void FogLut_Exp(C3D_FogLut* lut, float density, float gradient, float near, floa
void C3D_FogGasMode(GPU_FOGMODE fogMode, GPU_GASMODE gasMode, bool zFlip); void C3D_FogGasMode(GPU_FOGMODE fogMode, GPU_GASMODE gasMode, bool zFlip);
void C3D_FogColor(u32 color); void C3D_FogColor(u32 color);
void C3D_FogLutBind(C3D_FogLut* lut); void C3D_FogLutBind(C3D_FogLut* lut);
void GasLut_FromArray(C3D_GasLut* lut, const u32 data[9]);
void C3D_GasBeginAcc(void);
void C3D_GasDeltaZ(float value);
void C3D_GasAccMax(float value);
void C3D_GasAttn(float value);
void C3D_GasLightPlanar(float min, float max, float attn);
void C3D_GasLightView(float min, float max, float attn);
void C3D_GasLightDirection(float dotp);
void C3D_GasLutInput(GPU_GASLUTINPUT input);
void C3D_GasLutBind(C3D_GasLut* lut);

View File

@ -77,6 +77,14 @@ enum
void C3D_LightEnvFresnel(C3D_LightEnv* env, GPU_FRESNELSEL selector); void C3D_LightEnvFresnel(C3D_LightEnv* env, GPU_FRESNELSEL selector);
void C3D_LightEnvBumpMode(C3D_LightEnv* env, GPU_BUMPMODE mode); void C3D_LightEnvBumpMode(C3D_LightEnv* env, GPU_BUMPMODE mode);
void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit); void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit);
/**
* @brief Configures whether to use the z component of the normal map.
* @param[out] env Pointer to light environment structure.
* @param[in] enable false if the z component is reconstructed from the xy components
* of the normal map, true if the z component is taken from the normal map.
*/
void C3D_LightEnvBumpNormalZ(C3D_LightEnv *env, bool enable);
void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode); void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode);
void C3D_LightEnvShadowSel(C3D_LightEnv* env, int texUnit); void C3D_LightEnvShadowSel(C3D_LightEnv* env, int texUnit);
void C3D_LightEnvClampHighlights(C3D_LightEnv* env, bool clamp); void C3D_LightEnvClampHighlights(C3D_LightEnv* env, bool clamp);

View File

@ -13,7 +13,12 @@
* The one true circumference-to-radius ratio. * The one true circumference-to-radius ratio.
* See http://tauday.com/tau-manifesto * See http://tauday.com/tau-manifesto
*/ */
#define M_TAU (2*M_PI) #define M_TAU (6.28318530717958647692528676655900576)
// Define the legacy circle constant as well
#ifndef M_PI
#define M_PI (M_TAU/2)
#endif
/** /**
* @brief Convert an angle from revolutions to radians * @brief Convert an angle from revolutions to radians

View File

@ -14,9 +14,7 @@ struct C3D_RenderTarget_tag
bool linked; bool linked;
gfxScreen_t screen; gfxScreen_t screen;
gfx3dSide_t side; gfx3dSide_t side;
C3D_ClearBits clearBits;
u32 transferFlags; u32 transferFlags;
u32 clearColor, clearDepth;
}; };
// Flags for C3D_FrameBegin // Flags for C3D_FrameBegin
@ -35,6 +33,8 @@ bool C3D_FrameDrawOn(C3D_RenderTarget* target);
void C3D_FrameSplit(u8 flags); void C3D_FrameSplit(u8 flags);
void C3D_FrameEnd(u8 flags); void C3D_FrameEnd(u8 flags);
void C3D_FrameEndHook(void (* hook)(void*), void* param);
float C3D_GetDrawingTime(void); float C3D_GetDrawingTime(void);
float C3D_GetProcessingTime(void); float C3D_GetProcessingTime(void);
@ -62,9 +62,18 @@ public:
C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt); C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt);
C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt); C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt);
void C3D_RenderTargetDelete(C3D_RenderTarget* target); void C3D_RenderTargetDelete(C3D_RenderTarget* target);
void C3D_RenderTargetSetClear(C3D_RenderTarget* target, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth);
void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags); void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags);
void C3D_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags); static inline void C3D_RenderTargetDetachOutput(C3D_RenderTarget* target)
void C3D_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags); {
void C3D_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1); C3D_RenderTargetSetOutput(NULL, target->screen, target->side, 0);
}
static inline void C3D_RenderTargetClear(C3D_RenderTarget* target, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth)
{
C3D_FrameBufClear(&target->frameBuf, clearBits, clearColor, clearDepth);
}
void C3D_SyncDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags);
void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags);
void C3D_SyncMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1);

View File

@ -4,20 +4,22 @@
typedef struct typedef struct
{ {
u16 srcRgb, srcAlpha; u16 srcRgb, srcAlpha;
u16 opRgb, opAlpha; union
{
u32 opAll;
struct { u32 opRgb:12, opAlpha:12; };
};
u16 funcRgb, funcAlpha; u16 funcRgb, funcAlpha;
u32 color; u32 color;
u16 scaleRgb, scaleAlpha; u16 scaleRgb, scaleAlpha;
} C3D_TexEnv; } C3D_TexEnv;
enum typedef enum
{ {
C3D_RGB = BIT(0), C3D_RGB = BIT(0),
C3D_Alpha = BIT(1), C3D_Alpha = BIT(1),
C3D_Both = C3D_RGB | C3D_Alpha, C3D_Both = C3D_RGB | C3D_Alpha,
}; } C3D_TexEnvMode;
void TexEnv_Init(C3D_TexEnv* env);
C3D_TexEnv* C3D_GetTexEnv(int id); C3D_TexEnv* C3D_GetTexEnv(int id);
void C3D_SetTexEnv(int id, C3D_TexEnv* env); void C3D_SetTexEnv(int id, C3D_TexEnv* env);
@ -26,29 +28,57 @@ void C3D_DirtyTexEnv(C3D_TexEnv* env);
void C3D_TexEnvBufUpdate(int mode, int mask); void C3D_TexEnvBufUpdate(int mode, int mask);
void C3D_TexEnvBufColor(u32 color); void C3D_TexEnvBufColor(u32 color);
static inline void C3D_TexEnvSrc(C3D_TexEnv* env, int mode, int s1, int s2, int s3) static inline void C3D_TexEnvInit(C3D_TexEnv* env)
{ {
int param = GPU_TEVSOURCES(s1, s2, s3); env->srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0);
if (mode & C3D_RGB) env->srcAlpha = env->srcRgb;
env->opAll = 0;
env->funcRgb = GPU_REPLACE;
env->funcAlpha = env->funcRgb;
env->color = 0xFFFFFFFF;
env->scaleRgb = GPU_TEVSCALE_1;
env->scaleAlpha = GPU_TEVSCALE_1;
}
#ifdef __cplusplus
#define _C3D_DEFAULT(x) = x
#else
#define _C3D_DEFAULT(x)
#endif
static inline void C3D_TexEnvSrc(C3D_TexEnv* env, C3D_TexEnvMode mode,
GPU_TEVSRC s1,
GPU_TEVSRC s2 _C3D_DEFAULT(GPU_PRIMARY_COLOR),
GPU_TEVSRC s3 _C3D_DEFAULT(GPU_PRIMARY_COLOR))
{
int param = GPU_TEVSOURCES((int)s1, (int)s2, (int)s3);
if ((int)mode & C3D_RGB)
env->srcRgb = param; env->srcRgb = param;
if (mode & C3D_Alpha) if ((int)mode & C3D_Alpha)
env->srcAlpha = param; env->srcAlpha = param;
} }
static inline void C3D_TexEnvOp(C3D_TexEnv* env, int mode, int o1, int o2, int o3) static inline void C3D_TexEnvOpRgb(C3D_TexEnv* env,
GPU_TEVOP_RGB o1,
GPU_TEVOP_RGB o2 _C3D_DEFAULT(GPU_TEVOP_RGB_SRC_COLOR),
GPU_TEVOP_RGB o3 _C3D_DEFAULT(GPU_TEVOP_RGB_SRC_COLOR))
{ {
int param = GPU_TEVOPERANDS(o1, o2, o3); env->opRgb = GPU_TEVOPERANDS((int)o1, (int)o2, (int)o3);
if (mode & C3D_RGB)
env->opRgb = param;
if (mode & C3D_Alpha)
env->opAlpha = param;
} }
static inline void C3D_TexEnvFunc(C3D_TexEnv* env, int mode, int param) static inline void C3D_TexEnvOpAlpha(C3D_TexEnv* env,
GPU_TEVOP_A o1,
GPU_TEVOP_A o2 _C3D_DEFAULT(GPU_TEVOP_A_SRC_ALPHA),
GPU_TEVOP_A o3 _C3D_DEFAULT(GPU_TEVOP_A_SRC_ALPHA))
{ {
if (mode & C3D_RGB) env->opAlpha = GPU_TEVOPERANDS((int)o1, (int)o2, (int)o3);
}
static inline void C3D_TexEnvFunc(C3D_TexEnv* env, C3D_TexEnvMode mode, GPU_COMBINEFUNC param)
{
if ((int)mode & C3D_RGB)
env->funcRgb = param; env->funcRgb = param;
if (mode & C3D_Alpha) if ((int)mode & C3D_Alpha)
env->funcAlpha = param; env->funcAlpha = param;
} }
@ -57,10 +87,12 @@ static inline void C3D_TexEnvColor(C3D_TexEnv* env, u32 color)
env->color = color; env->color = color;
} }
static inline void C3D_TexEnvScale(C3D_TexEnv* env, int mode, int param) static inline void C3D_TexEnvScale(C3D_TexEnv* env, int mode, GPU_TEVSCALE param)
{ {
if (mode & C3D_RGB) if (mode & C3D_RGB)
env->scaleRgb = param; env->scaleRgb = param;
if (mode & C3D_Alpha) if (mode & C3D_Alpha)
env->scaleAlpha = param; env->scaleAlpha = param;
} }
#undef _C3D_DEFAULT

View File

@ -41,7 +41,7 @@ typedef struct
}; };
} C3D_Tex; } C3D_Tex;
typedef struct ALIGN(8) typedef struct CTR_ALIGN(8)
{ {
u16 width; u16 width;
u16 height; u16 height;

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#ifdef _3DS #if defined(__3DS__) || defined(_3DS)
#include <3ds.h> #include <3ds.h>
#else #else
#include <stdbool.h> #include <stdbool.h>

215
include/tex3ds.h Normal file
View File

@ -0,0 +1,215 @@
/*------------------------------------------------------------------------------
* Copyright (c) 2017
* Michael Theall (mtheall)
*
* This file is part of citro3d.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*----------------------------------------------------------------------------*/
/** @file tex3ds.h
* @brief tex3ds support
*/
#pragma once
#ifdef CITRO3D_BUILD
#include "c3d/texture.h"
#else
#include <citro3d.h>
#endif
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Subtexture
* @note If top < bottom, the subtexture is rotated 1/4 revolution counter-clockwise
*/
typedef struct Tex3DS_SubTexture
{
u16 width; ///< Sub-texture width (pixels)
u16 height; ///< Sub-texture height (pixels)
float left; ///< Left u-coordinate
float top; ///< Top v-coordinate
float right; ///< Right u-coordinate
float bottom; ///< Bottom v-coordinate
} Tex3DS_SubTexture;
/** @brief Texture */
typedef struct Tex3DS_Texture_s* Tex3DS_Texture;
/** @brief Import Tex3DS texture
* @param[in] input Input data
* @param[in] insize Size of the input data
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImport(const void* input, size_t insize, C3D_Tex* tex, C3D_TexCube* texcube, bool vram);
/** @brief Import Tex3DS texture
*
* @description
* For example, use this if you want to import from a large file without
* pulling the entire file into memory.
*
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @param[in] callback Data callback
* @param[in] userdata User data passed to callback
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImportCallback(C3D_Tex* tex, C3D_TexCube* texcube, bool vram, decompressCallback callback, void* userdata);
/** @brief Import Tex3DS texture
*
* Starts reading at the current file descriptor's offset. The file
* descriptor's position is left at the end of the decoded data. On error, the
* file descriptor's position is indeterminate.
*
* @param[in] fd Open file descriptor
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImportFD(int fd, C3D_Tex* tex, C3D_TexCube* texcube, bool vram);
/** @brief Import Tex3DS texture
*
* Starts reading at the current file stream's offset. The file stream's
* position is left at the end of the decoded data. On error, the file
* stream's position is indeterminate.
*
* @param[in] fp Open file stream
* @param[out] tex citro3d texture
* @param[out] texcube citro3d texcube
* @param[in] vram Whether to store textures in VRAM
* @returns Tex3DS texture
*/
Tex3DS_Texture Tex3DS_TextureImportStdio(FILE* fp, C3D_Tex* tex, C3D_TexCube* texcube, bool vram);
/** @brief Get number of subtextures
* @param[in] texture Tex3DS texture
* @returns Number of subtextures
*/
size_t Tex3DS_GetNumSubTextures(const Tex3DS_Texture texture);
/** @brief Get subtexture
* @param[in] texture Tex3DS texture
* @param[in] index Subtexture index
* @returns Subtexture info
*/
const Tex3DS_SubTexture* Tex3DS_GetSubTexture(const Tex3DS_Texture texture, size_t index);
/** @brief Check if subtexture is rotated
* @param[in] subtex Subtexture to check
* @returns whether subtexture is rotated
*/
static inline bool
Tex3DS_SubTextureRotated(const Tex3DS_SubTexture* subtex)
{
return subtex->top < subtex->bottom;
}
/** @brief Get bottom-left texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureBottomLeft(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->left;
*v = subtex->bottom;
} else
{
*u = subtex->bottom;
*v = subtex->left;
}
}
/** @brief Get bottom-right texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureBottomRight(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->right;
*v = subtex->bottom;
} else
{
*u = subtex->bottom;
*v = subtex->right;
}
}
/** @brief Get top-left texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureTopLeft(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->left;
*v = subtex->top;
} else
{
*u = subtex->top;
*v = subtex->left;
}
}
/** @brief Get top-right texcoords
* @param[in] subtex Subtexture
* @param[out] u u-coordinate
* @param[out] v v-coordinate
*/
static inline void
Tex3DS_SubTextureTopRight(const Tex3DS_SubTexture* subtex, float* u, float* v)
{
if (!Tex3DS_SubTextureRotated(subtex))
{
*u = subtex->right;
*v = subtex->top;
} else
{
*u = subtex->top;
*v = subtex->right;
}
}
/** @brief Free Tex3DS texture
* @param[in] texture Tex3DS texture to free
*/
void Tex3DS_TextureFree(Tex3DS_Texture texture);
#ifdef __cplusplus
}
#endif

View File

@ -8,14 +8,6 @@ C3D_Context __C3D_Context;
static aptHookCookie hookCookie; static aptHookCookie hookCookie;
__attribute__((weak)) void C3Di_RenderQueueWaitDone(void)
{
}
__attribute__((weak)) void C3Di_RenderQueueExit(void)
{
}
__attribute__((weak)) void C3Di_LightEnvUpdate(C3D_LightEnv* env) __attribute__((weak)) void C3Di_LightEnvUpdate(C3D_LightEnv* env)
{ {
(void)env; (void)env;
@ -36,6 +28,11 @@ __attribute__((weak)) void C3Di_ProcTexDirty(C3D_Context* ctx)
(void)ctx; (void)ctx;
} }
__attribute__((weak)) void C3Di_GasUpdate(C3D_Context* ctx)
{
(void)ctx;
}
static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param) static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param)
{ {
C3D_Context* ctx = C3Di_GetContext(); C3D_Context* ctx = C3Di_GetContext();
@ -45,22 +42,27 @@ static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param)
case APTHOOK_ONSUSPEND: case APTHOOK_ONSUSPEND:
{ {
C3Di_RenderQueueWaitDone(); C3Di_RenderQueueWaitDone();
C3Di_RenderQueueDisableVBlank();
break; break;
} }
case APTHOOK_ONRESTORE: case APTHOOK_ONRESTORE:
{ {
C3Di_RenderQueueEnableVBlank();
ctx->flags |= C3DiF_AttrInfo | C3DiF_BufInfo | C3DiF_Effect | C3DiF_FrameBuf ctx->flags |= C3DiF_AttrInfo | C3DiF_BufInfo | C3DiF_Effect | C3DiF_FrameBuf
| C3DiF_Viewport | C3DiF_Scissor | C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode | C3DiF_Viewport | C3DiF_Scissor | C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode
| C3DiF_TexAll | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_LightEnv; | C3DiF_TexAll | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_LightEnv | C3DiF_Gas;
C3Di_DirtyUniforms(GPU_VERTEX_SHADER); C3Di_DirtyUniforms(GPU_VERTEX_SHADER);
C3Di_DirtyUniforms(GPU_GEOMETRY_SHADER); C3Di_DirtyUniforms(GPU_GEOMETRY_SHADER);
ctx->fixedAttribDirty |= ctx->fixedAttribEverDirty; ctx->fixedAttribDirty |= ctx->fixedAttribEverDirty;
ctx->gasFlags |= C3DiG_BeginAcc | C3DiG_AccStage | C3DiG_RenderStage;
C3D_LightEnv* env = ctx->lightEnv; C3D_LightEnv* env = ctx->lightEnv;
if (ctx->fogLut) if (ctx->fogLut)
ctx->flags |= C3DiF_FogLut; ctx->flags |= C3DiF_FogLut;
if (ctx->gasLut)
ctx->flags |= C3DiF_GasLut;
if (env) if (env)
C3Di_LightEnvDirty(env); C3Di_LightEnvDirty(env);
C3Di_ProcTexDirty(ctx); C3Di_ProcTexDirty(ctx);
@ -94,10 +96,6 @@ bool C3D_Init(size_t cmdBufSize)
return false; return false;
} }
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
GX_BindQueue(&ctx->gxQueue);
gxCmdQueueRun(&ctx->gxQueue);
ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexStatus | C3DiF_TexAll; ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexStatus | C3DiF_TexAll;
// TODO: replace with direct struct access // TODO: replace with direct struct access
@ -124,11 +122,12 @@ bool C3D_Init(size_t cmdBufSize)
ctx->tex[i] = NULL; ctx->tex[i] = NULL;
for (i = 0; i < 6; i ++) for (i = 0; i < 6; i ++)
TexEnv_Init(&ctx->texEnv[i]); C3D_TexEnvInit(&ctx->texEnv[i]);
ctx->fixedAttribDirty = 0; ctx->fixedAttribDirty = 0;
ctx->fixedAttribEverDirty = 0; ctx->fixedAttribEverDirty = 0;
C3Di_RenderQueueInit();
aptHook(&hookCookie, C3Di_AptEventHook, NULL); aptHook(&hookCookie, C3Di_AptEventHook, NULL);
return true; return true;
@ -161,12 +160,6 @@ void C3Di_UpdateContext(void)
int i; int i;
C3D_Context* ctx = C3Di_GetContext(); C3D_Context* ctx = C3Di_GetContext();
if (ctx->flags & C3DiF_Program)
{
shaderProgramConfigure(ctx->program, (ctx->flags & C3DiF_VshCode) != 0, (ctx->flags & C3DiF_GshCode) != 0);
ctx->flags &= ~(C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode);
}
if (ctx->flags & C3DiF_FrameBuf) if (ctx->flags & C3DiF_FrameBuf)
{ {
ctx->flags &= ~C3DiF_FrameBuf; ctx->flags &= ~C3DiF_FrameBuf;
@ -192,6 +185,12 @@ void C3Di_UpdateContext(void)
GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, ctx->scissor, 3); GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, ctx->scissor, 3);
} }
if (ctx->flags & C3DiF_Program)
{
shaderProgramConfigure(ctx->program, (ctx->flags & C3DiF_VshCode) != 0, (ctx->flags & C3DiF_GshCode) != 0);
ctx->flags &= ~(C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode);
}
if (ctx->flags & C3DiF_AttrInfo) if (ctx->flags & C3DiF_AttrInfo)
{ {
ctx->flags &= ~C3DiF_AttrInfo; ctx->flags &= ~C3DiF_AttrInfo;
@ -233,9 +232,14 @@ void C3Di_UpdateContext(void)
if (ctx->flags & C3DiF_TexStatus) if (ctx->flags & C3DiF_TexStatus)
{ {
ctx->flags &= ~C3DiF_TexStatus; ctx->flags &= ~C3DiF_TexStatus;
GPUCMD_AddWrite(GPUREG_TEXUNIT_CONFIG, ctx->texConfig); GPUCMD_AddMaskedWrite(GPUREG_TEXUNIT_CONFIG, 0xB, ctx->texConfig);
// Clear texture cache if requested *after* configuring texture units
if (ctx->texConfig & BIT(16))
{
ctx->texConfig &= ~BIT(16);
GPUCMD_AddMaskedWrite(GPUREG_TEXUNIT_CONFIG, 0x4, BIT(16));
}
GPUCMD_AddWrite(GPUREG_TEXUNIT0_SHADOW, ctx->texShadow); GPUCMD_AddWrite(GPUREG_TEXUNIT0_SHADOW, ctx->texShadow);
ctx->texConfig &= ~BIT(16); // Remove clear-texture-cache flag
} }
if (ctx->flags & (C3DiF_ProcTex | C3DiF_ProcTexColorLut | C3DiF_ProcTexLutAll)) if (ctx->flags & (C3DiF_ProcTex | C3DiF_ProcTexColorLut | C3DiF_ProcTexLutAll))
@ -249,7 +253,7 @@ void C3Di_UpdateContext(void)
GPUCMD_AddWrite(GPUREG_FOG_COLOR, ctx->fogClr); GPUCMD_AddWrite(GPUREG_FOG_COLOR, ctx->fogClr);
} }
if (ctx->flags & C3DiF_FogLut) if ((ctx->flags & C3DiF_FogLut) && (ctx->texEnvBuf&7) != GPU_NO_FOG)
{ {
ctx->flags &= ~C3DiF_FogLut; ctx->flags &= ~C3DiF_FogLut;
if (ctx->fogLut) if (ctx->fogLut)
@ -259,6 +263,9 @@ void C3Di_UpdateContext(void)
} }
} }
if ((ctx->texEnvBuf&7) == GPU_GAS)
C3Di_GasUpdate(ctx);
if (ctx->flags & C3DiF_TexEnvAll) if (ctx->flags & C3DiF_TexEnvAll)
{ {
for (i = 0; i < 6; i ++) for (i = 0; i < 6; i ++)
@ -320,25 +327,6 @@ bool C3Di_SplitFrame(u32** pBuf, u32* pSize)
return true; return true;
} }
void C3D_FlushAsync(void)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
u32* cmdBuf;
u32 cmdBufSize;
C3Di_SplitFrame(&cmdBuf, &cmdBufSize);
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
//take advantage of GX_FlushCacheRegions to flush gsp heap
extern u32 __ctru_linear_heap;
extern u32 __ctru_linear_heap_size;
GX_FlushCacheRegions(cmdBuf, cmdBufSize*4, (u32 *) __ctru_linear_heap, __ctru_linear_heap_size, NULL, 0);
GX_ProcessCommandList(cmdBuf, cmdBufSize*4, 0x0);
}
float C3D_GetCmdBufUsage(void) float C3D_GetCmdBufUsage(void)
{ {
return C3Di_GetContext()->cmdBufUsage; return C3Di_GetContext()->cmdBufUsage;
@ -351,11 +339,8 @@ void C3D_Fini(void)
if (!(ctx->flags & C3DiF_Active)) if (!(ctx->flags & C3DiF_Active))
return; return;
C3Di_RenderQueueExit();
aptUnhook(&hookCookie); aptUnhook(&hookCookie);
gxCmdQueueStop(&ctx->gxQueue); C3Di_RenderQueueExit();
gxCmdQueueWait(&ctx->gxQueue, -1);
GX_BindQueue(NULL);
free(ctx->gxQueue.entries); free(ctx->gxQueue.entries);
linearFree(ctx->cmdBuf); linearFree(ctx->cmdBuf);
ctx->flags = 0; ctx->flags = 0;

View File

@ -93,6 +93,7 @@ void C3Di_EffectBind(C3D_Effect* e)
GPUCMD_AddWrite(GPUREG_FACECULLING_CONFIG, e->cullMode & 0x3); GPUCMD_AddWrite(GPUREG_FACECULLING_CONFIG, e->cullMode & 0x3);
GPUCMD_AddIncrementalWrites(GPUREG_DEPTHMAP_SCALE, (u32*)&e->zScale, 2); GPUCMD_AddIncrementalWrites(GPUREG_DEPTHMAP_SCALE, (u32*)&e->zScale, 2);
GPUCMD_AddIncrementalWrites(GPUREG_FRAGOP_ALPHA_TEST, (u32*)&e->alphaTest, 4); GPUCMD_AddIncrementalWrites(GPUREG_FRAGOP_ALPHA_TEST, (u32*)&e->alphaTest, 4);
GPUCMD_AddMaskedWrite(GPUREG_GAS_DELTAZ_DEPTH, 0x8, (u32)GPU_MAKEGASDEPTHFUNC((e->depthTest>>4)&7) << 24);
GPUCMD_AddWrite(GPUREG_BLEND_COLOR, e->blendClr); GPUCMD_AddWrite(GPUREG_BLEND_COLOR, e->blendClr);
GPUCMD_AddWrite(GPUREG_BLEND_FUNC, e->alphaBlend); GPUCMD_AddWrite(GPUREG_BLEND_FUNC, e->alphaBlend);
GPUCMD_AddWrite(GPUREG_LOGIC_OP, e->clrLogicOp); GPUCMD_AddWrite(GPUREG_LOGIC_OP, e->clrLogicOp);

184
source/gas.c Normal file
View File

@ -0,0 +1,184 @@
#include "internal.h"
static inline u32 calc_diff(u32 a, u32 b, int pos)
{
float fa = ((a>>pos)&0xFF)/255.0f;
float fb = ((b>>pos)&0xFF)/255.0f;
float x = fb-fa;
u32 diff = 0;
if (x < 0)
{
diff = 0x80;
x = -x;
}
diff |= (u32)(x*0x7F);
return diff<<pos;
}
static inline u32 conv_u8(float x, int pos)
{
if (x < 0.0f) x = 0.0f;
else if (x > 1.0f) x = 1.0f;
return ((u32)x*255)<<pos;
}
static inline u32 color_diff(u32 a, u32 b)
{
return calc_diff(a,b,0) | calc_diff(a,b,8) | calc_diff(a,b,16);
}
void GasLut_FromArray(C3D_GasLut* lut, const u32 data[9])
{
int i;
for (i = 0; i <= 8; i ++)
{
if (i < 8)
lut->color[i] = data[i];
if (i > 0)
lut->diff[i-1] = color_diff(data[i-1], data[i]);
}
}
void C3D_GasBeginAcc(void)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->gasFlags |= C3DiG_BeginAcc;
}
void C3D_GasDeltaZ(float value)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->flags |= C3DiF_Gas;
ctx->gasDeltaZ = (u32)(value*0x100);
ctx->gasFlags |= C3DiG_AccStage;
}
void C3D_GasAccMax(float value)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->flags |= C3DiF_Gas;
ctx->gasAccMax = f32tof16(1.0f / value);
ctx->gasFlags |= C3DiG_SetAccMax;
}
void C3D_GasAttn(float value)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->flags |= C3DiF_Gas;
ctx->gasAttn = f32tof16(value);
ctx->gasFlags |= C3DiG_RenderStage;
}
void C3D_GasLightPlanar(float min, float max, float attn)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->flags |= C3DiF_Gas;
ctx->gasLightXY = conv_u8(min,0) | conv_u8(max,8) | conv_u8(attn,16);
ctx->gasFlags |= C3DiG_RenderStage;
}
void C3D_GasLightView(float min, float max, float attn)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->flags |= C3DiF_Gas;
ctx->gasLightZ = conv_u8(min,0) | conv_u8(max,8) | conv_u8(attn,16);
ctx->gasFlags |= C3DiG_RenderStage;
}
void C3D_GasLightDirection(float dotp)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->flags |= C3DiF_Gas;
ctx->gasLightZColor &= ~0xFF;
ctx->gasLightZColor |= conv_u8(dotp,0);
ctx->gasFlags |= C3DiG_RenderStage;
}
void C3D_GasLutInput(GPU_GASLUTINPUT input)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
ctx->flags |= C3DiF_Gas;
ctx->gasLightZColor &= ~0x100;
ctx->gasLightZColor |= (input&1)<<8;
ctx->gasFlags |= C3DiG_RenderStage;
}
void C3D_GasLutBind(C3D_GasLut* lut)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
if (lut)
{
ctx->flags |= C3DiF_GasLut;
ctx->gasLut = lut;
} else
ctx->flags &= ~C3DiF_GasLut;
}
void C3Di_GasUpdate(C3D_Context* ctx)
{
if (ctx->flags & C3DiF_Gas)
{
ctx->flags &= ~C3DiF_Gas;
u32 gasFlags = ctx->gasFlags;
ctx->gasFlags = 0;
if (gasFlags & C3DiG_BeginAcc)
GPUCMD_AddMaskedWrite(GPUREG_GAS_ACCMAX_FEEDBACK, 0x3, 0);
if (gasFlags & C3DiG_AccStage)
GPUCMD_AddMaskedWrite(GPUREG_GAS_DELTAZ_DEPTH, 0x7, ctx->gasDeltaZ);
if (gasFlags & C3DiG_SetAccMax)
GPUCMD_AddWrite(GPUREG_GAS_ACCMAX, ctx->gasAccMax);
if (gasFlags & C3DiG_RenderStage)
{
GPUCMD_AddWrite(GPUREG_GAS_ATTENUATION, ctx->gasAttn);
GPUCMD_AddWrite(GPUREG_GAS_LIGHT_XY, ctx->gasLightXY);
GPUCMD_AddWrite(GPUREG_GAS_LIGHT_Z, ctx->gasLightZ);
GPUCMD_AddWrite(GPUREG_GAS_LIGHT_Z_COLOR, ctx->gasLightZColor);
}
}
if (ctx->flags & C3DiF_GasLut)
{
ctx->flags &= ~C3DiF_GasLut;
if (ctx->gasLut)
{
GPUCMD_AddWrite(GPUREG_GAS_LUT_INDEX, 0);
GPUCMD_AddWrites(GPUREG_GAS_LUT_DATA, (u32*)ctx->gasLut, 16);
}
}
}

View File

@ -52,6 +52,12 @@ typedef struct
u32 fogClr; u32 fogClr;
C3D_FogLut* fogLut; C3D_FogLut* fogLut;
u16 gasAttn, gasAccMax;
u32 gasLightXY, gasLightZ, gasLightZColor;
u32 gasDeltaZ : 24;
u32 gasFlags : 8;
C3D_GasLut* gasLut;
C3D_ProcTex* procTex; C3D_ProcTex* procTex;
C3D_ProcTexLut* procTexLut[3]; C3D_ProcTexLut* procTexLut[3];
C3D_ProcTexColorLut* procTexColorLut; C3D_ProcTexColorLut* procTexColorLut;
@ -83,6 +89,8 @@ enum
C3DiF_ProcTex = BIT(15), C3DiF_ProcTex = BIT(15),
C3DiF_ProcTexColorLut = BIT(16), C3DiF_ProcTexColorLut = BIT(16),
C3DiF_FogLut = BIT(17), C3DiF_FogLut = BIT(17),
C3DiF_Gas = BIT(18),
C3DiF_GasLut = BIT(19),
#define C3DiF_ProcTexLut(n) BIT(20+(n)) #define C3DiF_ProcTexLut(n) BIT(20+(n))
C3DiF_ProcTexLutAll = 7 << 20, C3DiF_ProcTexLutAll = 7 << 20,
@ -92,6 +100,14 @@ enum
C3DiF_TexEnvAll = 0x3F << 26, C3DiF_TexEnvAll = 0x3F << 26,
}; };
enum
{
C3DiG_BeginAcc = BIT(0),
C3DiG_AccStage = BIT(1),
C3DiG_SetAccMax = BIT(2),
C3DiG_RenderStage = BIT(3),
};
static inline C3D_Context* C3Di_GetContext(void) static inline C3D_Context* C3Di_GetContext(void)
{ {
extern C3D_Context __C3D_Context; extern C3D_Context __C3D_Context;
@ -108,6 +124,18 @@ static inline bool C3Di_TexIs2D(C3D_Tex* tex)
return !typeIsCube(C3D_TexGetType(tex)); return !typeIsCube(C3D_TexGetType(tex));
} }
static inline bool addrIsVRAM(const void* addr)
{
u32 vaddr = (u32)addr;
return vaddr >= OS_VRAM_VADDR && vaddr < OS_VRAM_VADDR + OS_VRAM_SIZE;
}
static inline vramAllocPos addrGetVRAMBank(const void* addr)
{
u32 vaddr = (u32)addr;
return vaddr < OS_VRAM_VADDR + OS_VRAM_SIZE/2 ? VRAM_ALLOC_A : VRAM_ALLOC_B;
}
void C3Di_UpdateContext(void); void C3Di_UpdateContext(void);
void C3Di_AttrInfoBind(C3D_AttrInfo* info); void C3Di_AttrInfoBind(C3D_AttrInfo* info);
void C3Di_BufInfoBind(C3D_BufInfo* info); void C3Di_BufInfoBind(C3D_BufInfo* info);
@ -115,6 +143,7 @@ void C3Di_FrameBufBind(C3D_FrameBuf* fb);
void C3Di_TexEnvBind(int id, C3D_TexEnv* env); void C3Di_TexEnvBind(int id, C3D_TexEnv* env);
void C3Di_SetTex(int unit, C3D_Tex* tex); void C3Di_SetTex(int unit, C3D_Tex* tex);
void C3Di_EffectBind(C3D_Effect* effect); void C3Di_EffectBind(C3D_Effect* effect);
void C3Di_GasUpdate(C3D_Context* ctx);
void C3Di_LightMtlBlend(C3D_Light* light); void C3Di_LightMtlBlend(C3D_Light* light);
@ -123,3 +152,9 @@ void C3Di_LoadShaderUniforms(shaderInstance_s* si);
void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type); void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type);
bool C3Di_SplitFrame(u32** pBuf, u32* pSize); bool C3Di_SplitFrame(u32** pBuf, u32* pSize);
void C3Di_RenderQueueInit(void);
void C3Di_RenderQueueExit(void);
void C3Di_RenderQueueWaitDone(void);
void C3Di_RenderQueueEnableVBlank(void);
void C3Di_RenderQueueDisableVBlank(void);

View File

@ -250,6 +250,14 @@ void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit)
env->flags |= C3DF_LightEnv_Dirty; env->flags |= C3DF_LightEnv_Dirty;
} }
void C3D_LightEnvBumpNormalZ(C3D_LightEnv *env, bool usez) {
if (usez)
env->conf.config[0] |= BIT(30);
else
env->conf.config[0] &= ~BIT(30);
env->flags |= C3DF_LightEnv_Dirty;
}
void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode) void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode)
{ {
mode &= 0xF<<16; mode &= 0xF<<16;

View File

@ -2,54 +2,61 @@
C3D_FQuat Quat_FromMtx(const C3D_Mtx* m) C3D_FQuat Quat_FromMtx(const C3D_Mtx* m)
{ {
//Taken from Gamasutra: // Original algorithm taken from here (with some optimizations):
//http://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php // https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf
//Expanded upon from: // Layout of the algorithm:
//http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ // First, we select a large (non-zero!) component "P" in the output quaternion (q)
// (we can test this just by looking at the diagonal of the matrix)
// Second, we calculate q' which is our desired output quaternion (q) scaled by 4P
// (this can be done with simple additions; the 4P factor is large and non-zero thanks to above)
// Third, we normalize q' to finally obtain q
// (this will work because normalize(kq) = q for any k scalar and q unit quaternion)
//Variables we need.
float trace, sqrtTrace;
C3D_FQuat q; C3D_FQuat q;
C3D_FVec diagonal = FVec4_New(m->r[0].x, m->r[1].y, m->r[2].z, 1.0f);
//Check the main diagonal of the passed-in matrix for positive/negative signs. // Check if x^2 + y^2 >= z^2 + w^2
trace = m->r[0].x + m->r[1].y + m->r[2].z; if (diagonal.z <= 0.0f)
if (trace > 0.0f)
{ {
//Diagonal is positive. // Check if |x| >= |y|
sqrtTrace = sqrtf(trace + 1.0f); if (diagonal.x >= diagonal.y)
q.w = sqrtTrace / 2.0f; {
sqrtTrace = 0.5 / sqrtTrace; // X case
q.x = (m->r[1].z - m->r[2].y) * sqrtTrace; q.x = diagonal.w + diagonal.x - diagonal.y - diagonal.z;
q.y = (m->r[2].x - m->r[0].z) * sqrtTrace; q.y = m->r[1].x + m->r[0].y;
q.z = (m->r[0].y - m->r[1].x) * sqrtTrace; q.z = m->r[2].x + m->r[0].z;
q.w = m->r[2].y - m->r[1].z;
} }
else else
{ {
//Diagonal is negative or equals to zero. We need to identify which major diagonal element has the greatest value. // Y case
if (m->r[0].x > m->r[1].y && m->r[0].x > m->r[2].z) q.x = m->r[1].x + m->r[0].y;
{ q.y = diagonal.w - diagonal.x + diagonal.y - diagonal.z;
sqrtTrace = 2.0f * sqrtf(1.0f + m->r[0].x - m->r[1].y - m->r[2].z); q.z = m->r[2].y + m->r[1].z;
q.w = (m->r[2].y - m->r[1].z) / sqrtTrace; q.w = m->r[0].z - m->r[2].x;
q.x = 0.25f * sqrtTrace;
q.y = (m->r[0].y - m->r[1].x) / sqrtTrace;
q.z = (m->r[0].z - m->r[2].x) / sqrtTrace;
} }
else if (m->r[1].y > m->r[2].z)
{
sqrtTrace = 2.0f * sqrtf(1.0f + m->r[1].y - m->r[0].x - m->r[2].z);
q.w = (m->r[0].z - m->r[2].x) / sqrtTrace;
q.x = (m->r[0].y - m->r[1].x) / sqrtTrace;
q.y = 0.25f * sqrtTrace;
q.z = (m->r[1].z - m->r[2].y) / sqrtTrace;
} }
else else
{ {
sqrtTrace = 2.0f * sqrtf(1.0f + m->r[2].z - m->r[0].x - m->r[1].y); // Check if |z| >= |w|
q.w = (m->r[1].x - m->r[0].y) / sqrtTrace; if (-diagonal.x >= diagonal.y)
q.x = (m->r[0].z - m->r[2].x) / sqrtTrace; {
q.y = (m->r[1].z - m->r[2].y) / sqrtTrace; // Z case
q.z = 0.25f * sqrtTrace; q.x = m->r[2].x + m->r[0].z;
q.y = m->r[2].y + m->r[1].z;
q.z = diagonal.w - diagonal.x - diagonal.y + diagonal.z;
q.w = m->r[1].x - m->r[0].y;
}
else
{
// W case
q.x = m->r[2].y - m->r[1].z;
q.y = m->r[0].z - m->r[2].x;
q.z = m->r[1].x - m->r[0].y;
q.w = diagonal.w + diagonal.x + diagonal.y + diagonal.z;
} }
} }
return q;
// Normalize the quaternion
return Quat_Normalize(q);
} }

View File

@ -8,19 +8,15 @@ static C3D_RenderTarget *linkedTarget[3];
static TickCounter gpuTime, cpuTime; static TickCounter gpuTime, cpuTime;
#define STAGE_HAS_TRANSFER(n) BIT(0+(n))
#define STAGE_HAS_ANY_TRANSFER (7<<0)
#define STAGE_NEED_TRANSFER(n) BIT(3+(n))
#define STAGE_NEED_TOP_TRANSFER (STAGE_NEED_TRANSFER(0)|STAGE_NEED_TRANSFER(1))
#define STAGE_NEED_BOT_TRANSFER STAGE_NEED_TRANSFER(2)
#define STAGE_WAIT_TRANSFER BIT(6)
static bool initialized;
static bool inFrame, inSafeTransfer, measureGpuTime; static bool inFrame, inSafeTransfer, measureGpuTime;
static u8 frameStage; static bool needSwapTop, needSwapBot, isTopStereo;
static float framerate = 60.0f; static float framerate = 60.0f;
static float framerateCounter[2] = { 60.0f, 60.0f }; static float framerateCounter[2] = { 60.0f, 60.0f };
static u32 frameCounter[2]; static u32 frameCounter[2];
static void (* frameEndCb)(void*);
static void* frameEndCbData;
static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target);
static bool framerateLimit(int id) static bool framerateLimit(int id)
{ {
@ -35,50 +31,12 @@ static bool framerateLimit(int id)
static void onVBlank0(C3D_UNUSED void* unused) static void onVBlank0(C3D_UNUSED void* unused)
{ {
if (frameStage & STAGE_NEED_TOP_TRANSFER)
{
C3D_RenderTarget *left = linkedTarget[0], *right = linkedTarget[1];
if (left && !(frameStage&STAGE_NEED_TRANSFER(0)))
left = NULL;
if (right && !(frameStage&STAGE_NEED_TRANSFER(1)))
right = NULL;
if (gfxIs3D() && !right)
right = left;
frameStage &= ~STAGE_NEED_TOP_TRANSFER;
if (left || right)
{
frameStage |= STAGE_WAIT_TRANSFER;
if (left)
C3D_FrameBufTransfer(&left->frameBuf, GFX_TOP, GFX_LEFT, left->transferFlags);
if (right)
C3D_FrameBufTransfer(&right->frameBuf, GFX_TOP, GFX_RIGHT, right->transferFlags);
if (left && left->clearBits)
C3D_FrameBufClear(&left->frameBuf, left->clearBits, left->clearColor, left->clearDepth);
if (right && right != left && right->clearBits)
C3D_FrameBufClear(&right->frameBuf, right->clearBits, right->clearColor, right->clearDepth);
gfxConfigScreen(GFX_TOP, false);
}
}
if (framerateLimit(0)) if (framerateLimit(0))
frameCounter[0]++; frameCounter[0]++;
} }
static void onVBlank1(C3D_UNUSED void* unused) static void onVBlank1(C3D_UNUSED void* unused)
{ {
if (frameStage & STAGE_NEED_BOT_TRANSFER)
{
frameStage &= ~STAGE_NEED_BOT_TRANSFER;
C3D_RenderTarget* target = linkedTarget[2];
if (target)
{
frameStage |= STAGE_WAIT_TRANSFER;
C3D_FrameBufTransfer(&target->frameBuf, GFX_BOTTOM, GFX_LEFT, target->transferFlags);
if (target->clearBits)
C3D_FrameBufClear(&target->frameBuf, target->clearBits, target->clearColor, target->clearDepth);
gfxConfigScreen(GFX_BOTTOM, false);
}
}
if (framerateLimit(1)) if (framerateLimit(1))
frameCounter[1]++; frameCounter[1]++;
} }
@ -99,12 +57,18 @@ static void onQueueFinish(gxCmdQueue_s* queue)
gxCmdQueueClear(queue); gxCmdQueueClear(queue);
} }
} }
else if (frameStage & STAGE_WAIT_TRANSFER)
frameStage &= ~STAGE_WAIT_TRANSFER;
else else
{ {
u8 needs = frameStage & STAGE_HAS_ANY_TRANSFER; if (needSwapTop)
frameStage = (frameStage&~STAGE_HAS_ANY_TRANSFER) | (needs<<3); {
gfxScreenSwapBuffers(GFX_TOP, isTopStereo);
needSwapTop = false;
}
if (needSwapBot)
{
gfxScreenSwapBuffers(GFX_BOTTOM, false);
needSwapBot = false;
}
} }
} }
@ -130,72 +94,60 @@ static bool C3Di_WaitAndClearQueue(s64 timeout)
gxCmdQueue_s* queue = &C3Di_GetContext()->gxQueue; gxCmdQueue_s* queue = &C3Di_GetContext()->gxQueue;
if (!gxCmdQueueWait(queue, timeout)) if (!gxCmdQueueWait(queue, timeout))
return false; return false;
if (timeout==0 && frameStage)
return false;
while (frameStage)
gspWaitForAnyEvent();
gxCmdQueueStop(queue); gxCmdQueueStop(queue);
gxCmdQueueClear(queue); gxCmdQueueClear(queue);
return true; return true;
} }
static void C3Di_RenderQueueInit(void) void C3Di_RenderQueueEnableVBlank(void)
{ {
gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false); gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false); gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false);
gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, onQueueFinish, NULL);
} }
static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target); void C3Di_RenderQueueDisableVBlank(void)
{
gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
}
void C3Di_RenderQueueInit(void)
{
C3D_Context* ctx = C3Di_GetContext();
C3Di_RenderQueueEnableVBlank();
GX_BindQueue(&ctx->gxQueue);
gxCmdQueueSetCallback(&ctx->gxQueue, onQueueFinish, NULL);
gxCmdQueueRun(&ctx->gxQueue);
}
void C3Di_RenderQueueExit(void) void C3Di_RenderQueueExit(void)
{ {
int i; int i;
C3D_RenderTarget *a, *next; C3D_RenderTarget *a, *next;
if (!initialized)
return;
C3Di_WaitAndClearQueue(-1); C3Di_WaitAndClearQueue(-1);
gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL);
GX_BindQueue(NULL);
C3Di_RenderQueueDisableVBlank();
for (i = 0; i < 3; i ++)
linkedTarget[i] = NULL;
for (a = firstTarget; a; a = next) for (a = firstTarget; a; a = next)
{ {
next = a->next; next = a->next;
C3Di_RenderTargetDestroy(a); C3Di_RenderTargetDestroy(a);
} }
gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL);
for (i = 0; i < 3; i ++)
linkedTarget[i] = NULL;
initialized = false;
} }
void C3Di_RenderQueueWaitDone(void) void C3Di_RenderQueueWaitDone(void)
{ {
if (!initialized)
return;
C3Di_WaitAndClearQueue(-1); C3Di_WaitAndClearQueue(-1);
} }
static bool checkRenderQueueInit(void)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return false;
if (!initialized)
{
C3Di_RenderQueueInit();
initialized = true;
}
return true;
}
float C3D_FrameRate(float fps) float C3D_FrameRate(float fps)
{ {
float old = framerate; float old = framerate;
@ -210,13 +162,17 @@ float C3D_FrameRate(float fps)
bool C3D_FrameBegin(u8 flags) bool C3D_FrameBegin(u8 flags)
{ {
C3D_Context* ctx = C3Di_GetContext();
if (inFrame) return false; if (inFrame) return false;
if (flags & C3D_FRAME_SYNCDRAW) if (flags & C3D_FRAME_SYNCDRAW)
C3D_FrameSync(); C3D_FrameSync();
if (!C3Di_WaitAndClearQueue((flags & C3D_FRAME_NONBLOCK) ? 0 : -1)) if (!C3Di_WaitAndClearQueue((flags & C3D_FRAME_NONBLOCK) ? 0 : -1))
return false; return false;
inFrame = true; inFrame = true;
osTickCounterStart(&cpuTime); osTickCounterStart(&cpuTime);
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
return true; return true;
} }
@ -241,10 +197,15 @@ void C3D_FrameSplit(u8 flags)
void C3D_FrameEnd(u8 flags) void C3D_FrameEnd(u8 flags)
{ {
C3D_Context* ctx = C3Di_GetContext(); C3D_Context* ctx = C3Di_GetContext();
if (!inFrame) return;
if (frameEndCb)
frameEndCb(frameEndCbData);
C3D_FrameSplit(flags); C3D_FrameSplit(flags);
inFrame = false; GPUCMD_SetBuffer(NULL, 0, 0);
osTickCounterUpdate(&cpuTime); osTickCounterUpdate(&cpuTime);
inFrame = false;
// Flush the entire linear memory if the user did not explicitly mandate to flush the command list // Flush the entire linear memory if the user did not explicitly mandate to flush the command list
if (!(flags & GX_CMDLIST_FLUSH)) if (!(flags & GX_CMDLIST_FLUSH))
@ -256,29 +217,35 @@ void C3D_FrameEnd(u8 flags)
int i; int i;
C3D_RenderTarget* target; C3D_RenderTarget* target;
isTopStereo = false;
for (i = 2; i >= 0; i --) for (i = 2; i >= 0; i --)
{ {
target = linkedTarget[i]; target = linkedTarget[i];
if (!target || !target->used) if (!target || !target->used)
continue; continue;
target->used = false; target->used = false;
frameStage |= STAGE_HAS_TRANSFER(i); C3D_FrameBufTransfer(&target->frameBuf, target->screen, target->side, target->transferFlags);
} if (target->screen == GFX_TOP)
for (target = firstTarget; target; target = target->next)
{ {
if (!target->used || !target->clearBits) needSwapTop = true;
continue; if (target->side == GFX_RIGHT)
target->used = false; isTopStereo = true;
C3D_FrameBufClear(&target->frameBuf, target->clearBits, target->clearColor, target->clearDepth); }
else if (target->screen == GFX_BOTTOM)
needSwapBot = true;
} }
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
measureGpuTime = true; measureGpuTime = true;
osTickCounterStart(&gpuTime); osTickCounterStart(&gpuTime);
gxCmdQueueRun(&ctx->gxQueue); gxCmdQueueRun(&ctx->gxQueue);
} }
void C3D_FrameEndHook(void (* hook)(void*), void* param)
{
frameEndCb = hook;
frameEndCbData = param;
}
float C3D_GetDrawingTime(void) float C3D_GetDrawingTime(void)
{ {
return osTickCounterRead(&gpuTime); return osTickCounterRead(&gpuTime);
@ -310,8 +277,6 @@ static void C3Di_RenderTargetFinishInit(C3D_RenderTarget* target)
C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt) C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt)
{ {
if (!checkRenderQueueInit()) goto _fail0;
GPU_DEPTHBUF depthFmtReal = GPU_RB_DEPTH16; GPU_DEPTHBUF depthFmtReal = GPU_RB_DEPTH16;
void* depthBuf = NULL; void* depthBuf = NULL;
void* colorBuf = vramAlloc(C3D_CalcColorBufSize(width,height,colorFmt)); void* colorBuf = vramAlloc(C3D_CalcColorBufSize(width,height,colorFmt));
@ -319,7 +284,10 @@ C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF col
if (C3D_DEPTHTYPE_OK(depthFmt)) if (C3D_DEPTHTYPE_OK(depthFmt))
{ {
depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt); depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
depthBuf = vramAlloc(C3D_CalcDepthBufSize(width,height,depthFmtReal)); size_t depthSize = C3D_CalcDepthBufSize(width,height,depthFmtReal);
vramAllocPos vramBank = addrGetVRAMBank(colorBuf);
depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first...
if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank
if (!depthBuf) goto _fail1; if (!depthBuf) goto _fail1;
} }
@ -348,8 +316,7 @@ _fail0:
C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt) C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt)
{ {
if (!checkRenderQueueInit()) return NULL; if (!addrIsVRAM(tex->data)) return NULL; // Render targets must be in VRAM
C3D_RenderTarget* target = C3Di_RenderTargetNew(); C3D_RenderTarget* target = C3Di_RenderTargetNew();
if (!target) return NULL; if (!target) return NULL;
@ -359,7 +326,10 @@ C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face,
if (C3D_DEPTHTYPE_OK(depthFmt)) if (C3D_DEPTHTYPE_OK(depthFmt))
{ {
GPU_DEPTHBUF depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt); GPU_DEPTHBUF depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
void* depthBuf = vramAlloc(C3D_CalcDepthBufSize(fb->width,fb->height,depthFmtReal)); size_t depthSize = C3D_CalcDepthBufSize(fb->width,fb->height,depthFmtReal);
vramAllocPos vramBank = addrGetVRAMBank(tex->data);
void* depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first...
if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank
if (!depthBuf) if (!depthBuf)
{ {
free(target); free(target);
@ -392,39 +362,35 @@ void C3D_RenderTargetDelete(C3D_RenderTarget* target)
{ {
if (inFrame) if (inFrame)
svcBreak(USERBREAK_PANIC); // Shouldn't happen. svcBreak(USERBREAK_PANIC); // Shouldn't happen.
if (target->linked)
C3D_RenderTargetDetachOutput(target);
else
C3Di_WaitAndClearQueue(-1); C3Di_WaitAndClearQueue(-1);
C3Di_RenderTargetDestroy(target); C3Di_RenderTargetDestroy(target);
} }
void C3D_RenderTargetSetClear(C3D_RenderTarget* target, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth)
{
if (!target->frameBuf.colorBuf) clearBits &= ~C3D_CLEAR_COLOR;
if (!target->frameBuf.depthBuf) clearBits &= ~C3D_CLEAR_DEPTH;
C3D_ClearBits oldClearBits = target->clearBits;
target->clearBits = clearBits;
target->clearColor = clearColor;
target->clearDepth = clearDepth;
if (clearBits &~ oldClearBits)
C3D_FrameBufClear(&target->frameBuf, clearBits, clearColor, clearDepth);
}
void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags) void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags)
{ {
int id = 0; int id = 0;
if (screen==GFX_BOTTOM) id = 2; if (screen==GFX_BOTTOM) id = 2;
else if (side==GFX_RIGHT) id = 1; else if (side==GFX_RIGHT) id = 1;
if (linkedTarget[id]) if (linkedTarget[id])
{
linkedTarget[id]->linked = false; linkedTarget[id]->linked = false;
if (!inFrame)
C3Di_WaitAndClearQueue(-1);
}
linkedTarget[id] = target; linkedTarget[id] = target;
if (target)
{
target->linked = true; target->linked = true;
target->transferFlags = transferFlags; target->transferFlags = transferFlags;
target->screen = screen; target->screen = screen;
target->side = side; target->side = side;
}
} }
void C3D_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags) static void C3Di_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags)
{ {
C3Di_WaitAndClearQueue(-1); C3Di_WaitAndClearQueue(-1);
inSafeTransfer = true; inSafeTransfer = true;
@ -432,7 +398,7 @@ void C3D_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32
gxCmdQueueRun(&C3Di_GetContext()->gxQueue); gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
} }
void C3D_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags) static void C3Di_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags)
{ {
C3Di_WaitAndClearQueue(-1); C3Di_WaitAndClearQueue(-1);
inSafeTransfer = true; inSafeTransfer = true;
@ -440,10 +406,49 @@ void C3D_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 siz
gxCmdQueueRun(&C3Di_GetContext()->gxQueue); gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
} }
void C3D_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1) static void C3Di_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1)
{ {
C3Di_WaitAndClearQueue(-1); C3Di_WaitAndClearQueue(-1);
inSafeTransfer = true; inSafeTransfer = true;
GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1); GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
gxCmdQueueRun(&C3Di_GetContext()->gxQueue); gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
} }
void C3D_SyncDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags)
{
if (inFrame)
{
C3D_FrameSplit(0);
GX_DisplayTransfer(inadr, indim, outadr, outdim, flags);
} else
{
C3Di_SafeDisplayTransfer(inadr, indim, outadr, outdim, flags);
gspWaitForPPF();
}
}
void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags)
{
if (inFrame)
{
C3D_FrameSplit(0);
GX_TextureCopy(inadr, indim, outadr, outdim, size, flags);
} else
{
C3Di_SafeTextureCopy(inadr, indim, outadr, outdim, size, flags);
gspWaitForPPF();
}
}
void C3D_SyncMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1)
{
if (inFrame)
{
C3D_FrameSplit(0);
GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
} else
{
C3Di_SafeMemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
gspWaitForPSC0();
}
}

241
source/tex3ds.c Normal file
View File

@ -0,0 +1,241 @@
/*------------------------------------------------------------------------------
* Copyright (c) 2017
* Michael Theall (mtheall)
*
* This file is part of citro3d.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*----------------------------------------------------------------------------*/
/** @file tex3ds.c
* @brief Tex3DS routines
*/
#include <tex3ds.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/** @brief Tex3DS texture
*/
struct Tex3DS_Texture_s
{
u16 numSubTextures; ///< Number of subtextures
u16 width; ///< Texture width
u16 height; ///< Texture height
u8 format; ///< Texture format
u8 mipmapLevels; ///< Number of mipmaps
Tex3DS_SubTexture subTextures[]; ///< Subtextures
};
typedef struct __attribute__((packed))
{
u16 numSubTextures;
u8 width_log2 : 3;
u8 height_log2 : 3;
u8 type : 1;
u8 format;
u8 mipmapLevels;
} Tex3DSi_Header;
typedef struct
{
u16 width, height;
u16 left, top, right, bottom;
} Tex3DSi_SubTexture;
static inline bool Tex3DSi_ReadData(decompressCallback callback, void** userdata, void* buffer, size_t size, size_t* insize)
{
if (callback)
return callback(*userdata, buffer, size) == size;
if (size > *insize)
return false;
memcpy(buffer, *userdata, size);
*userdata = (u8*)*userdata + size;
*insize -= size;
return true;
}
static Tex3DS_Texture
Tex3DSi_ImportCommon(C3D_Tex* tex, C3D_TexCube* texcube, bool vram, decompressCallback callback, void* userdata, size_t insize)
{
// Read header
Tex3DSi_Header hdr;
if (!Tex3DSi_ReadData(callback, &userdata, &hdr, sizeof(hdr), &insize))
return NULL;
// Allocate space for header + subtextures
Tex3DS_Texture texture = (Tex3DS_Texture)malloc(sizeof(struct Tex3DS_Texture_s) + hdr.numSubTextures*sizeof(Tex3DS_SubTexture));
if (!texture)
return NULL;
// Fill texture metadata structure
texture->numSubTextures = hdr.numSubTextures;
texture->width = 1 << (hdr.width_log2 + 3);
texture->height = 1 << (hdr.height_log2 + 3);
texture->format = hdr.format;
texture->mipmapLevels = hdr.mipmapLevels;
// Read subtexture info
for (size_t i = 0; i < hdr.numSubTextures; i ++)
{
Tex3DSi_SubTexture subtex;
if (!Tex3DSi_ReadData(callback, &userdata, &subtex, sizeof(Tex3DSi_SubTexture), &insize))
{
free(texture);
return NULL;
}
texture->subTextures[i].width = subtex.width;
texture->subTextures[i].height = subtex.height;
texture->subTextures[i].left = subtex.left / 1024.0f;
texture->subTextures[i].top = subtex.top / 1024.0f;
texture->subTextures[i].right = subtex.right / 1024.0f;
texture->subTextures[i].bottom = subtex.bottom / 1024.0f;
}
// Allocate texture memory
C3D_TexInitParams params;
params.width = texture->width;
params.height = texture->height;
params.maxLevel = texture->mipmapLevels;
params.format = texture->format;
params.type = (GPU_TEXTURE_MODE_PARAM)hdr.type;
params.onVram = vram;
if (!C3D_TexInitWithParams(tex, texcube, params))
{
free(texture);
return NULL;
}
// Get texture size, including mipmaps
size_t base_texsize = C3D_TexCalcTotalSize(tex->size, texture->mipmapLevels);
size_t texsize = base_texsize;
// If this is a cubemap/skybox, there are 6 textures
if (params.type == GPU_TEX_CUBE_MAP)
texsize *= 6;
if (vram)
{
// Allocate staging buffer in linear memory
void* texdata = linearAlloc(texsize);
if (!texdata)
{
C3D_TexDelete(tex);
free(texture);
return NULL;
}
// Decompress into staging buffer for VRAM upload
if (!decompress(texdata, texsize, callback, userdata, insize))
{
linearFree(texdata);
C3D_TexDelete(tex);
free(texture);
return NULL;
}
// Flush buffer to prepare DMA to VRAM
GSPGPU_FlushDataCache(texdata, texsize);
size_t texcount = 1;
if (params.type == GPU_TEX_CUBE_MAP)
texcount = 6;
// Upload texture(s) to VRAM
for (size_t i = 0; i < texcount; ++i)
C3D_TexLoadImage(tex, (u8*)texdata + i * base_texsize, i, -1);
linearFree(texdata);
} else if (params.type == GPU_TEX_CUBE_MAP)
{
decompressIOVec iov[6];
// Setup IO vectors
for (size_t i = 0; i < 6; ++i)
{
u32 size;
iov[i].data = C3D_TexCubeGetImagePtr(tex, i, -1, &size);
iov[i].size = size;
}
// Decompress into texture memory
if (!decompressV(iov, 6, callback, userdata, insize))
{
C3D_TexDelete(tex);
free(texture);
return NULL;
}
} else
{
u32 size;
void* data = C3D_Tex2DGetImagePtr(tex, -1, &size);
// Decompress into texture memory
if (!decompress(data, size, callback, userdata, insize))
{
C3D_TexDelete(tex);
free(texture);
return NULL;
}
}
return texture;
}
Tex3DS_Texture
Tex3DS_TextureImport(const void* input, size_t insize, C3D_Tex* tex, C3D_TexCube* texcube, bool vram)
{
return Tex3DSi_ImportCommon(tex, texcube, vram, NULL, (void*)input, insize);
}
Tex3DS_Texture
Tex3DS_TextureImportCallback(C3D_Tex* tex, C3D_TexCube* texcube, bool vram, decompressCallback callback, void* userdata)
{
return Tex3DSi_ImportCommon(tex, texcube, vram, callback, userdata, 0);
}
Tex3DS_Texture
Tex3DS_TextureImportFD(int fd, C3D_Tex* tex, C3D_TexCube* texcube, bool vram)
{
return Tex3DSi_ImportCommon(tex, texcube, vram, decompressCallback_FD, &fd, 0);
}
Tex3DS_Texture
Tex3DS_TextureImportStdio(FILE* fp, C3D_Tex* tex, C3D_TexCube* texcube, bool vram)
{
return Tex3DSi_ImportCommon(tex, texcube, vram, decompressCallback_Stdio, fp, 0);
}
size_t
Tex3DS_GetNumSubTextures(const Tex3DS_Texture texture)
{
return texture->numSubTextures;
}
const Tex3DS_SubTexture*
Tex3DS_GetSubTexture(const Tex3DS_Texture texture, size_t index)
{
if (index < texture->numSubTextures)
return &texture->subTextures[index];
return NULL;
}
void Tex3DS_TextureFree(Tex3DS_Texture texture)
{
free(texture);
}

View File

@ -1,18 +1,5 @@
#include "internal.h" #include "internal.h"
void TexEnv_Init(C3D_TexEnv* env)
{
env->srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0);
env->srcAlpha = env->srcRgb;
env->opRgb = GPU_TEVOPERANDS(0,0,0);
env->opAlpha = env->opRgb;
env->funcRgb = GPU_REPLACE;
env->funcAlpha = env->funcRgb;
env->color = 0xFFFFFFFF;
env->scaleRgb = GPU_TEVSCALE_1;
env->scaleAlpha = GPU_TEVSCALE_1;
}
C3D_TexEnv* C3D_GetTexEnv(int id) C3D_TexEnv* C3D_GetTexEnv(int id)
{ {
C3D_Context* ctx = C3Di_GetContext(); C3D_Context* ctx = C3Di_GetContext();
@ -31,8 +18,11 @@ void C3D_SetTexEnv(int id, C3D_TexEnv* env)
if (!(ctx->flags & C3DiF_Active)) if (!(ctx->flags & C3DiF_Active))
return; return;
memcpy(&ctx->texEnv[id], env, sizeof(*env));
ctx->flags |= C3DiF_TexEnv(id); ctx->flags |= C3DiF_TexEnv(id);
if (env)
memcpy(&ctx->texEnv[id], env, sizeof(*env));
else
C3D_TexEnvInit(&ctx->texEnv[id]);
} }
void C3D_DirtyTexEnv(C3D_TexEnv* env) void C3D_DirtyTexEnv(C3D_TexEnv* env)

View File

@ -30,10 +30,13 @@ static inline size_t fmtSize(GPU_TEXCOLOR fmt)
} }
} }
static inline bool addrIsVRAM(const void* addr) static inline bool checkTexSize(u32 size)
{ {
u32 vaddr = (u32)addr; if (size < 8 || size > 1024)
return vaddr >= 0x1F000000 && vaddr < 0x1F600000; return false;
if (size & (size-1))
return false;
return true;
} }
static inline void allocFree(void* addr) static inline void allocFree(void* addr)
@ -59,7 +62,7 @@ static void C3Di_TexCubeDelete(C3D_TexCube* cube)
bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p) bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p)
{ {
if ((p.width|p.height) & 7) return false; if (!checkTexSize(p.width) || !checkTexSize(p.height)) return false;
bool isCube = typeIsCube(p.type); bool isCube = typeIsCube(p.type);
if (isCube && !cube) return false; if (isCube && !cube) return false;
@ -116,10 +119,7 @@ void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int leve
if (!addrIsVRAM(out)) if (!addrIsVRAM(out))
memcpy(out, data, size); memcpy(out, data, size);
else else
{ C3D_SyncTextureCopy((u32*)data, 0, (u32*)out, 0, size, 8);
C3D_SafeTextureCopy((u32*)data, 0, (u32*)out, 0, size, 8);
gspWaitForPPF();
}
} }
static void C3Di_DownscaleRGBA8(u32* dst, const u32* src[4]) static void C3Di_DownscaleRGBA8(u32* dst, const u32* src[4])
@ -178,11 +178,10 @@ void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face)
u32 dst_height = src_height>>1; u32 dst_height = src_height>>1;
/* Doesn't work due to size restriction bullshit /* Doesn't work due to size restriction bullshit
C3D_SafeDisplayTransfer( C3D_SyncDisplayTransfer(
(u32*)src, GX_BUFFER_DIM(src_width,src_height), (u32*)src, GX_BUFFER_DIM(src_width,src_height),
(u32*)dst, GX_BUFFER_DIM(dst_width,dst_height), (u32*)dst, GX_BUFFER_DIM(dst_width,dst_height),
transfer_flags); transfer_flags);
gspWaitForPPF();
*/ */
u32 i,j; u32 i,j;
@ -260,6 +259,7 @@ void C3D_TexShadowParams(bool perspective, float bias)
iBias = BIT(24)-1; iBias = BIT(24)-1;
ctx->texShadow = (iBias &~ 1) | (perspective ? 0 : 1); ctx->texShadow = (iBias &~ 1) | (perspective ? 0 : 1);
ctx->flags |= C3DiF_TexStatus;
} }
void C3Di_SetTex(int unit, C3D_Tex* tex) void C3Di_SetTex(int unit, C3D_Tex* tex)

View File

@ -47,10 +47,10 @@ ICON :=
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -g -Wall -O3 -mword-relocations \ CFLAGS := -g -Wall -O3 -mword-relocations \
-fomit-frame-pointer -ffunction-sections \ -ffunction-sections \
$(ARCH) $(ARCH)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS CFLAGS += $(INCLUDE) -D__3DS__
CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++11 CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++11

View File

@ -7,7 +7,7 @@ OFILES := $(addprefix build/,$(CXXFILES:.cpp=.o)) \
DFILES := $(wildcard build/*.d) DFILES := $(wildcard build/*.d)
CFLAGS := -Wall -g -pipe -I../../include --coverage CFLAGS := -Wall -g -pipe -I../../include --coverage
CXXFLAGS := $(CFLAGS) -std=gnu++11 -DGLM_FORCE_RADIANS CXXFLAGS := $(CFLAGS) $(CPPFLAGS) -std=gnu++11 -DGLM_FORCE_RADIANS
LDFLAGS := $(ARCH) -pipe -lm --coverage LDFLAGS := $(ARCH) -pipe -lm --coverage
.PHONY: all clean lcov .PHONY: all clean lcov

View File

@ -953,13 +953,15 @@ check_quaternion(generator_t &gen, distribution_t &dist)
// check conversion to matrix // check conversion to matrix
{ {
C3D_FQuat q = randomQuat(gen, dist); C3D_FQuat q = Quat_Normalize(randomQuat(gen, dist));
glm::quat g = loadQuat(q); glm::quat g = loadQuat(q);
C3D_Mtx m; C3D_Mtx m;
Mtx_FromQuat(&m, q); Mtx_FromQuat(&m, q);
assert(m == glm::mat4_cast(g)); assert(m == glm::mat4_cast(g));
C3D_FQuat q2 = Quat_FromMtx(&m);
assert(q2 == q || q2 == FVec4_Negate(q));
} }
} }
} }