summary refs log tree commit diff
path: root/src/Graphics_PS3.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Graphics_PS3.c
initial commit
Diffstat (limited to 'src/Graphics_PS3.c')
-rw-r--r--src/Graphics_PS3.c703
1 files changed, 703 insertions, 0 deletions
diff --git a/src/Graphics_PS3.c b/src/Graphics_PS3.c
new file mode 100644
index 0000000..1fbe3f8
--- /dev/null
+++ b/src/Graphics_PS3.c
@@ -0,0 +1,703 @@
+#include "Core.h"
+#if defined CC_BUILD_PS3
+#include "_GraphicsBase.h"
+#include "Errors.h"
+#include "Logger.h"
+#include "Window.h"
+#include <malloc.h>
+#include <rsx/rsx.h>
+#include <sysutil/video.h>
+static cc_bool renderingDisabled;
+
+static gcmContextData* context;
+static u32 cur_fb;
+
+#define CB_SIZE   0x100000 // TODO: smaller command buffer?
+#define HOST_SIZE (32 * 1024 * 1024)
+
+
+/*########################################################################################################################*
+*----------------------------------------------------- Vertex Shaders ----------------------------------------------------*
+*#########################################################################################################################*/
+typedef struct CCVertexProgram {
+	rsxVertexProgram* prog;
+	void* ucode;
+	rsxProgramConst* mvp;
+	rsxProgramConst* uv_offset;
+} VertexProgram;
+
+extern const u8 vs_coloured_vpo[];
+extern const u8 vs_textured_vpo[];
+extern const u8 vs_offset_vpo[];
+
+static VertexProgram  VP_list[3];
+static VertexProgram* VP_active;
+
+static cc_bool textureOffseting;
+static float textureOffset[4] __attribute__((aligned(16)));
+static struct Matrix mvp      __attribute__((aligned(64)));
+
+
+static void VP_Load(VertexProgram* vp, const u8* source) {
+	vp->prog = (rsxVertexProgram*)source;
+	u32 size = 0;
+	rsxVertexProgramGetUCode(vp->prog, &vp->ucode, &size);
+	
+	vp->mvp       = rsxVertexProgramGetConst(vp->prog, "mvp");
+	vp->uv_offset = rsxVertexProgramGetConst(vp->prog, "uv_offset");
+}
+
+static void LoadVertexPrograms(void) {
+	VP_Load(&VP_list[0], vs_coloured_vpo);
+	VP_Load(&VP_list[1], vs_textured_vpo);
+	VP_Load(&VP_list[2], vs_offset_vpo);
+}
+
+static void VP_SwitchActive(void) {
+	int index = gfx_format == VERTEX_FORMAT_TEXTURED ? 1 : 0;
+	if (textureOffseting) index = 2;
+	
+	VertexProgram* VP = &VP_list[index];
+	if (VP == VP_active) return;
+	VP_active = VP;
+	
+	rsxLoadVertexProgram(context, VP->prog, VP->ucode);
+}
+
+static void VP_UpdateUniforms() {
+	// TODO: dirty uniforms instead
+	for (int i = 0; i < Array_Elems(VP_list); i++)
+	{
+		VertexProgram* vp = &VP_list[i];
+		rsxSetVertexProgramParameter(context, vp->prog, vp->mvp, (float*)&mvp);
+	}
+	
+	if (VP_active == &VP_list[2]) {
+		VertexProgram* vp = &VP_list[2];
+		rsxSetVertexProgramParameter(context, vp->prog, vp->uv_offset, textureOffset);
+	}
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------- Fragment Shaders ---------------------------------------------------*
+*#########################################################################################################################*/
+typedef struct CCFragmentProgram {
+	rsxFragmentProgram* prog;
+	void* ucode;
+	u32* buffer;
+	u32 offset;
+} FragmentProgram;
+
+extern const u8 ps_textured_fpo[];
+extern const u8 ps_coloured_fpo[];
+
+static FragmentProgram  FP_list[2];
+static FragmentProgram* FP_active;
+
+
+static void FP_Load(FragmentProgram* fp, const u8* source) {
+	fp->prog = (rsxFragmentProgram*)source;
+	u32 size = 0;
+	rsxFragmentProgramGetUCode(fp->prog, &fp->ucode, &size);
+	
+	fp->buffer = (u32*)rsxMemalign(128, size);
+	Mem_Copy(fp->buffer, fp->ucode, size);
+	rsxAddressToOffset(fp->buffer, &fp->offset);
+}
+
+static void LoadFragmentPrograms(void) {
+	FP_Load(&FP_list[0], ps_coloured_fpo);
+	FP_Load(&FP_list[1], ps_textured_fpo);
+}
+
+static void FP_SwitchActive(void) {
+	int index = gfx_format == VERTEX_FORMAT_TEXTURED ? 1 : 0;
+
+	FragmentProgram* FP = &FP_list[index];
+	if (FP == FP_active) return;
+	FP_active = FP;
+	
+	rsxLoadFragmentProgramLocation(context, FP->prog, FP->offset, GCM_LOCATION_RSX);
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------- Setup---------------------------------------------------------*
+*#########################################################################################################################*/
+static u32  color_pitch;
+static u32  color_offset[2];
+static u32* color_buffer[2];
+
+static u32  depth_pitch;
+static u32  depth_offset;
+static u32* depth_buffer;
+
+static void CreateContext(void) {
+	void* host_addr = memalign(1024 * 1024, HOST_SIZE);
+	rsxInit(&context, CB_SIZE, HOST_SIZE, host_addr);
+}
+
+static void ConfigureVideo(void) {
+	videoState state;
+	videoGetState(0, 0, &state);
+
+	videoConfiguration vconfig = { 0 };
+	vconfig.resolution = state.displayMode.resolution;
+	vconfig.format     = VIDEO_BUFFER_FORMAT_XRGB;
+	vconfig.pitch      = DisplayInfo.Width * sizeof(u32);
+	
+	videoConfigure(0, &vconfig, NULL, 0);
+}
+
+static void SetupBlendingState(void) {
+	rsxSetBlendFunc(context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
+	rsxSetBlendEquation(context, GCM_FUNC_ADD, GCM_FUNC_ADD);
+}
+
+static void AllocColorSurface(u32 i) {
+	color_pitch     = DisplayInfo.Width * 4;
+	color_buffer[i] = (u32*)rsxMemalign(64, DisplayInfo.Height * color_pitch);
+	
+	rsxAddressToOffset(color_buffer[i], &color_offset[i]);
+	gcmSetDisplayBuffer(i, color_offset[i], color_pitch,
+		DisplayInfo.Width, DisplayInfo.Height);
+}
+
+static void AllocDepthSurface(void) {
+	depth_pitch  = DisplayInfo.Width * 4;
+	depth_buffer = (u32*)rsxMemalign(64, DisplayInfo.Height * depth_pitch);
+	
+	rsxAddressToOffset(depth_buffer, &depth_offset);
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------General---------------------------------------------------------*
+*#########################################################################################################################*/
+void SetRenderTarget(u32 index) {
+	gcmSurface sf;
+
+	sf.colorFormat		= GCM_SURFACE_X8R8G8B8;
+	sf.colorTarget		= GCM_SURFACE_TARGET_0;
+	sf.colorLocation[0]	= GCM_LOCATION_RSX;
+	sf.colorOffset[0]	= color_offset[index];
+	sf.colorPitch[0]	= color_pitch;
+
+	sf.colorLocation[1]	= GCM_LOCATION_RSX;
+	sf.colorLocation[2]	= GCM_LOCATION_RSX;
+	sf.colorLocation[3]	= GCM_LOCATION_RSX;
+	sf.colorOffset[1]	= 0;
+	sf.colorOffset[2]	= 0;
+	sf.colorOffset[3]	= 0;
+	sf.colorPitch[1]	= 64;
+	sf.colorPitch[2]	= 64;
+	sf.colorPitch[3]	= 64;
+
+	sf.depthFormat		= GCM_SURFACE_ZETA_Z24S8;
+	sf.depthLocation	= GCM_LOCATION_RSX;
+	sf.depthOffset		= depth_offset;
+	sf.depthPitch		= depth_pitch;
+
+	sf.type		= GCM_SURFACE_TYPE_LINEAR;
+	sf.antiAlias		= GCM_SURFACE_CENTER_1;
+
+	sf.width		= DisplayInfo.Width;
+	sf.height		= DisplayInfo.Height;
+	sf.x			= 0;
+	sf.y			= 0;
+
+	rsxSetSurface(context,&sf);
+}
+
+static void InitGfxContext(void) {
+	Gfx.MaxTexWidth  = 1024;
+	Gfx.MaxTexHeight = 1024;
+	Gfx.Created      = true;
+	
+	// https://github.com/ps3dev/PSL1GHT/blob/master/ppu/include/rsx/rsx.h#L30
+	CreateContext();
+	ConfigureVideo();
+	gcmSetFlipMode(GCM_FLIP_VSYNC);
+	
+	AllocColorSurface(0);
+	AllocColorSurface(1);
+	AllocDepthSurface();
+	gcmResetFlipStatus();
+	
+	SetupBlendingState();
+	SetRenderTarget(cur_fb);
+	
+	LoadVertexPrograms();
+	LoadFragmentPrograms();
+}
+static GfxResourceID white_square;
+
+void Gfx_Create(void) {
+	// TODO rethink all this
+	if (!Gfx.Created) InitGfxContext();
+	
+	Gfx_RestoreState();
+	gfx_format = -1;
+}
+
+void Gfx_Free(void) { Gfx_FreeState(); }
+
+
+cc_bool Gfx_TryRestoreContext(void) { return true; }
+cc_bool Gfx_WarnIfNecessary(void)   { return false; }
+
+void Gfx_RestoreState(void) {
+	InitDefaultResources();
+	
+	// 1x1 dummy white texture
+	struct Bitmap bmp;
+	BitmapCol pixels[1] = { BITMAPCOLOR_WHITE };
+	Bitmap_Init(bmp, 1, 1, pixels);
+	white_square = Gfx_CreateTexture(&bmp, 0, false);
+}
+
+void Gfx_FreeState(void) {
+	FreeDefaultResources(); 
+	Gfx_DeleteTexture(&white_square);
+}
+
+
+u32* Gfx_AllocImage(u32* offset, s32 w, s32 h) {
+	u32* pixels = (u32*)rsxMemalign(64, w * h * 4);
+	rsxAddressToOffset(pixels, offset);
+	return pixels;
+}
+
+void Gfx_TransferImage(u32 offset, s32 w, s32 h) {
+	rsxSetTransferImage(context, GCM_TRANSFER_LOCAL_TO_LOCAL,
+		color_offset[cur_fb], color_pitch, 0, 0,
+		offset, w * 4, 0, 0, 
+		w, h, 4);
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------State management----------------------------------------------------*
+*#########################################################################################################################*/
+static cc_uint32 clearColor;
+
+void Gfx_SetFaceCulling(cc_bool enabled) {
+	rsxSetCullFaceEnable(context, enabled);
+}
+
+static void SetAlphaBlend(cc_bool enabled) {
+	rsxSetBlendEnable(context, enabled);
+}
+void Gfx_SetAlphaArgBlend(cc_bool enabled) { }
+
+void Gfx_ClearColor(PackedCol color) {
+	cc_uint32 R = PackedCol_R(color);
+	cc_uint32 G = PackedCol_G(color);
+	cc_uint32 B = PackedCol_B(color);
+
+	clearColor  = B | (G << 8) | (R << 16) | (0xFF << 24);
+}
+
+void Gfx_SetDepthWrite(cc_bool enabled) {
+	rsxSetDepthWriteEnable(context, enabled);
+}
+
+void Gfx_SetDepthTest(cc_bool enabled) {
+	rsxSetDepthTestEnable(context, enabled);
+}
+
+static void SetAlphaTest(cc_bool enabled) {
+	rsxSetAlphaTestEnable(context, enabled);
+}
+
+static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) {
+	unsigned mask = 0;
+	if (r) mask |= GCM_COLOR_MASK_R;
+	if (g) mask |= GCM_COLOR_MASK_G;
+	if (b) mask |= GCM_COLOR_MASK_B;
+	if (a) mask |= GCM_COLOR_MASK_A;
+
+	rsxSetColorMask(context, mask);
+}
+
+void Gfx_DepthOnlyRendering(cc_bool depthOnly) {
+	cc_bool enabled = !depthOnly;
+	SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], 
+				  enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]);
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Matrices--------------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
+	// Same as Direct3D9
+	// TODO: should it be like OpenGL? ???
+	*matrix = Matrix_Identity;
+
+	matrix->row1.x =  2.0f / width;
+	matrix->row2.y = -2.0f / height;
+	matrix->row3.z =  1.0f / (zNear - zFar);
+
+	matrix->row4.x = -1.0f;
+	matrix->row4.y =  1.0f;
+	matrix->row4.z = zNear / (zNear - zFar);
+}
+
+static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); }
+void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
+	float zNear = 0.1f;
+	float c = Cotangent(0.5f * fov);
+
+	// Same as Direct3D9
+	// TODO: should it be like OpenGL? ???
+	*matrix = Matrix_Identity;
+
+	matrix->row1.x =  c / aspect;
+	matrix->row2.y =  c;
+	matrix->row3.z = zFar / (zNear - zFar);
+	matrix->row3.w = -1.0f;
+	matrix->row4.z = (zNear * zFar) / (zNear - zFar);
+	matrix->row4.w =  0.0f;
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------------Misc----------------------------------------------------------*
+*#########################################################################################################################*/
+cc_result Gfx_TakeScreenshot(struct Stream* output) {
+	return ERR_NOT_SUPPORTED;
+}
+
+void Gfx_GetApiInfo(cc_string* info) {
+	int pointerSize = sizeof(void*) * 8;
+
+	String_Format1(info, "-- Using PS3 (%i bit) --\n", &pointerSize);
+	PrintMaxTextureInfo(info);
+}
+
+void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) {
+	gfx_minFrameMs = minFrameMs;
+	gfx_vsync      = vsync;
+}
+
+static void ResetFrameState(void) {
+	// Seems these states aren't persisted across frames
+	// TODO: Why is that
+	rsxSetAlphaFunc(context, GCM_GREATER, 0.5f);
+	rsxSetBlendFunc(context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
+        rsxSetBlendEquation(context, GCM_FUNC_ADD, GCM_FUNC_ADD);
+        
+	rsxSetColorMaskMrt(context, 0);
+	rsxSetClearColor(context, clearColor);
+	rsxSetClearDepthStencil(context, 0xFFFFFFFF);
+
+	rsxSetDepthFunc(context, GCM_LEQUAL);	
+	rsxSetDepthWriteEnable(context, true);
+	rsxSetDepthTestEnable(context,  true);
+	
+	rsxSetUserClipPlaneControl(context, GCM_USER_CLIP_PLANE_DISABLE,
+						GCM_USER_CLIP_PLANE_DISABLE,
+						GCM_USER_CLIP_PLANE_DISABLE,
+						GCM_USER_CLIP_PLANE_DISABLE,
+						GCM_USER_CLIP_PLANE_DISABLE,
+						GCM_USER_CLIP_PLANE_DISABLE);
+        
+	// NOTE: Must be called each frame, otherwise renders upside down at 4x zoom
+	Gfx_SetViewport(0, 0, Game.Width, Game.Height);
+}
+
+// https://github.com/ps3dev/PSL1GHT/blob/master/ppu/include/rsx/rsx.h#L30
+static cc_bool everFlipped;
+void Gfx_BeginFrame(void) {
+	// TODO: remove everFlipped
+	if (everFlipped) {
+		while (gcmGetFlipStatus() != 0) usleep(200);
+	}
+	
+	ResetFrameState();
+	everFlipped = true;
+	gcmResetFlipStatus();
+}
+
+void Gfx_ClearBuffers(GfxBuffers buffers) {
+	int targets = 0;
+	if (buffers & GFX_BUFFER_COLOR) targets |= (GCM_CLEAR_R | GCM_CLEAR_G | GCM_CLEAR_B | GCM_CLEAR_A);
+	if (buffers & GFX_BUFFER_DEPTH) targets |= (GCM_CLEAR_S | GCM_CLEAR_Z);
+	
+	rsxClearSurface(context, targets); 
+}
+
+void Gfx_EndFrame(void) {
+	gcmSetFlip(context, cur_fb);
+	rsxFlushBuffer(context);
+	gcmSetWaitFlip(context);
+
+	cur_fb ^= 1;
+	SetRenderTarget(cur_fb);
+	
+	if (gfx_minFrameMs) LimitFPS();
+}
+
+void Gfx_OnWindowResize(void) {
+	Gfx_SetViewport(0, 0, Game.Width, Game.Height);
+}
+
+void Gfx_SetViewport(int x, int y, int w, int h) {
+	f32 scale[4], offset[4];
+	f32 zmin = 0.0f;
+	f32 zmax = 1.0f;
+	y = Game.Height - y - h;
+	
+	scale[0]  = w *  0.5f;
+	scale[1]  = h * -0.5f;
+	scale[2]  = (zmax - zmin) * 0.5f;
+	scale[3]  = 0.0f;
+	offset[0] = x + w * 0.5f;
+	offset[1] = x + h * 0.5f;
+	offset[2] = (zmax + zmin) * 0.5f;
+	offset[3] = 0.0f;
+
+	rsxSetViewport(context, x, y, w, h, zmin, zmax, scale, offset);
+	rsxSetScissor(context,  x, y, w, h);
+	
+	// TODO: even needed?
+	for (int i = 0; i < 8; i++)
+	{
+		rsxSetViewportClip(context, i, w, h);
+	}
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Index buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+static int vb_size;
+
+GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) {
+	return 1;/* TODO */
+}
+
+void Gfx_BindIb(GfxResourceID ib) { }
+void Gfx_DeleteIb(GfxResourceID* ib) { }
+
+
+/*########################################################################################################################*
+*------------------------------------------------------Vertex buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) {
+	return rsxMemalign(128, count * strideSizes[fmt]);
+}
+
+void Gfx_BindVb(GfxResourceID vb) { 
+	u32 offset;
+	rsxAddressToOffset(vb, &offset);
+	
+	if (gfx_format == VERTEX_FORMAT_TEXTURED) {
+		rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_POS,    0, offset, 
+			SIZEOF_VERTEX_TEXTURED, 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
+		rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_COLOR0, 0, offset + 12, 
+			SIZEOF_VERTEX_TEXTURED, 4, GCM_VERTEX_DATA_TYPE_U8,  GCM_LOCATION_RSX);
+		rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_TEX0,   0, offset + 16,
+			SIZEOF_VERTEX_TEXTURED, 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
+	} else {
+		rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_POS,    0, offset, 
+			SIZEOF_VERTEX_COLOURED, 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
+		rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_COLOR0, 0, offset + 12, 
+			SIZEOF_VERTEX_COLOURED, 4, GCM_VERTEX_DATA_TYPE_U8,  GCM_LOCATION_RSX);
+	}
+}
+
+void Gfx_DeleteVb(GfxResourceID* vb) {
+	GfxResourceID data = *vb;/* TODO */
+	if (data) rsxFree(data);
+	*vb = 0;
+}
+
+void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
+	vb_size = count * strideSizes[fmt];
+	return vb;
+}
+
+void Gfx_UnlockVb(GfxResourceID vb) { 
+	Gfx_BindVb(vb);
+	rsxInvalidateVertexCache(context); // TODO needed?
+}
+
+
+static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) {
+	return rsxMemalign(128, maxVertices * strideSizes[fmt]);
+}
+
+void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); }
+
+void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) {
+	vb_size = count * strideSizes[fmt];
+	return vb;
+}
+
+void Gfx_UnlockDynamicVb(GfxResourceID vb) {
+	Gfx_BindVb(vb);
+	rsxInvalidateVertexCache(context); // TODO needed?
+}
+
+void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); }
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Textures--------------------------------------------------------*
+*#########################################################################################################################*/
+typedef struct CCTexture_ {
+	cc_uint32 width, height;
+	cc_uint32 pad[(128 - 8)/4]; // TODO better way of aligning to 128 bytes
+	cc_uint32 pixels[];
+} CCTexture;
+
+static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) {
+	int size = bmp->width * bmp->height * 4;
+	CCTexture* tex = (CCTexture*)rsxMemalign(128, 128 + size);
+	
+	tex->width  = bmp->width;
+	tex->height = bmp->height;
+	CopyTextureData(tex->pixels, bmp->width * 4, bmp, rowWidth << 2);
+	return tex;
+}
+
+void Gfx_BindTexture(GfxResourceID texId) {
+	CCTexture* tex = (CCTexture*)texId;
+	if (!tex) tex  = white_square; 
+	/* TODO */
+	
+	u32 offset;
+	rsxAddressToOffset(tex->pixels, &offset);
+	gcmTexture texture;
+
+	texture.format		= GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN;
+	texture.mipmap		= 1;
+	texture.dimension	= GCM_TEXTURE_DIMS_2D;
+	texture.cubemap	= GCM_FALSE;
+	texture.remap		= 		  ((GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT) |
+						   (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT) |
+						   (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT) |
+						   (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT) |
+						   (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT) |
+						   (GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT) |
+						   (GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT) |
+						   (GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT));
+	texture.width		= tex->width;
+	texture.height		= tex->height;
+	texture.depth		= 1;
+	texture.location	= GCM_LOCATION_RSX;
+	texture.pitch		= tex->width * 4;
+	texture.offset		= offset;
+	
+	rsxInvalidateTextureCache(context,GCM_INVALIDATE_TEXTURE); // TODO needed
+	
+	rsxLoadTexture(context,    0, &texture);
+	rsxTextureControl(context, 0, GCM_TRUE, 0<<8, 12<<8, GCM_TEXTURE_MAX_ANISO_1);
+	rsxTextureFilter(context, 0, 0, GCM_TEXTURE_NEAREST, GCM_TEXTURE_NEAREST,
+		GCM_TEXTURE_CONVOLUTION_QUINCUNX);			
+	rsxTextureWrapMode(context, 0, GCM_TEXTURE_REPEAT, GCM_TEXTURE_REPEAT, GCM_TEXTURE_REPEAT, 0, GCM_TEXTURE_ZFUNC_LESS, 0);
+}
+
+void Gfx_DeleteTexture(GfxResourceID* texId) {
+	GfxResourceID data = *texId;
+	if (data) rsxFree(data);
+	*texId = NULL;
+}
+
+void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
+	CCTexture* tex = (CCTexture*)texId;
+	
+	// NOTE: Only valid for LINEAR textures
+	cc_uint32* dst = (tex->pixels + x) + y * tex->width;	
+	CopyTextureData(dst, tex->width * 4, part, rowWidth << 2);
+	
+	rsxInvalidateTextureCache(context, GCM_INVALIDATE_TEXTURE);
+	/* TODO */
+}
+
+void Gfx_EnableMipmaps(void)  { }
+void Gfx_DisableMipmaps(void) { }
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------State management----------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_SetFog(cc_bool enabled) {/* TODO */
+}
+
+void Gfx_SetFogCol(PackedCol color) {/* TODO */
+}
+
+void Gfx_SetFogDensity(float value) {/* TODO */
+}
+
+void Gfx_SetFogEnd(float value) {/* TODO */
+}
+
+void Gfx_SetFogMode(FogFunc func) {/* TODO */
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Matrices--------------------------------------------------------*
+*#########################################################################################################################*/
+static struct Matrix _view, _proj;
+
+void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
+	struct Matrix* dst = type == MATRIX_PROJECTION ? &_proj : &_view;
+	*dst = *matrix;
+
+	Matrix_Mul(&mvp, &_view, &_proj);
+	VP_UpdateUniforms();
+}
+
+void Gfx_LoadIdentityMatrix(MatrixType type) {
+	Gfx_LoadMatrix(type, &Matrix_Identity);
+}
+
+void Gfx_EnableTextureOffset(float x, float y) {
+	textureOffseting = true;
+	textureOffset[0] = x;
+	textureOffset[1] = y;
+	
+	VP_SwitchActive();
+	VP_UpdateUniforms();
+}
+
+void Gfx_DisableTextureOffset(void) {
+	textureOffseting = false;
+	VP_SwitchActive();
+}
+
+
+/*########################################################################################################################*
+*----------------------------------------------------------Drawing--------------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_SetVertexFormat(VertexFormat fmt) {
+	if (fmt == gfx_format) return;
+	gfx_format = fmt;
+	gfx_stride = strideSizes[fmt];/* TODO */
+	
+	VP_SwitchActive();
+	FP_SwitchActive();
+}
+
+void Gfx_DrawVb_Lines(int verticesCount) {/* TODO */
+	rsxDrawVertexArray(context, GCM_TYPE_LINES, 0, verticesCount);
+}
+
+void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {/* TODO */
+	rsxDrawVertexArray(context, GCM_TYPE_QUADS, startVertex, verticesCount);
+}
+
+void Gfx_DrawVb_IndexedTris(int verticesCount) {/* TODO */
+	rsxDrawVertexArray(context, GCM_TYPE_QUADS, 0, verticesCount);
+}
+
+void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {/* TODO */
+	rsxDrawVertexArray(context, GCM_TYPE_QUADS, startVertex, verticesCount);
+}
+#endif