summary refs log tree commit diff
path: root/src/Graphics_PS2.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_PS2.c
initial commit
Diffstat (limited to 'src/Graphics_PS2.c')
-rw-r--r--src/Graphics_PS2.c666
1 files changed, 666 insertions, 0 deletions
diff --git a/src/Graphics_PS2.c b/src/Graphics_PS2.c
new file mode 100644
index 0000000..9eeb9d2
--- /dev/null
+++ b/src/Graphics_PS2.c
@@ -0,0 +1,666 @@
+#include "Core.h"
+#if defined CC_BUILD_PS2
+#include "_GraphicsBase.h"
+#include "Errors.h"
+#include "Window.h"
+#include <packet.h>
+#include <dma_tags.h>
+#include <gif_tags.h>
+#include <gs_privileged.h>
+#include <gs_gp.h>
+#include <gs_psm.h>
+#include <dma.h>
+#include <graph.h>
+#include <draw.h>
+#include <draw3d.h>
+#include <malloc.h>
+
+static void* gfx_vertices;
+extern framebuffer_t fb_colors[2];
+extern zbuffer_t     fb_depth;
+static float vp_hwidth, vp_hheight;
+
+// double buffering
+static packet_t* packets[2];
+static packet_t* current;
+static int context;
+static qword_t* dma_tag;
+static qword_t* q;
+
+static GfxResourceID white_square;
+static int primitive_type;
+
+void Gfx_RestoreState(void) {
+	InitDefaultResources();
+	
+	// 16x16 dummy white texture
+	struct Bitmap bmp;
+	BitmapCol pixels[16 * 16];
+	Mem_Set(pixels, 0xFF, sizeof(pixels));
+	Bitmap_Init(bmp, 16, 16, pixels);
+	white_square = Gfx_CreateTexture(&bmp, 0, false);
+}
+
+void Gfx_FreeState(void) {
+	FreeDefaultResources();
+	Gfx_DeleteTexture(&white_square);
+}
+
+static qword_t* SetTextureWrapping(qword_t* q, int context) {
+	PACK_GIFTAG(q, GIF_SET_TAG(1,0,0,0, GIF_FLG_PACKED, 1), GIF_REG_AD);
+	q++;
+
+	PACK_GIFTAG(q, GS_SET_CLAMP(WRAP_REPEAT, WRAP_REPEAT, 0, 0, 0, 0), 
+					GS_REG_CLAMP + context);
+	q++;
+	return q;
+}
+
+static qword_t* SetTextureSampling(qword_t* q, int context) {
+	PACK_GIFTAG(q, GIF_SET_TAG(1,0,0,0, GIF_FLG_PACKED, 1), GIF_REG_AD);
+	q++;
+
+	// TODO: should mipmapselect (first 0 after MIN_NEAREST) be 1?
+	PACK_GIFTAG(q, GS_SET_TEX1(LOD_USE_K, 0, LOD_MAG_NEAREST, LOD_MIN_NEAREST, 0, 0, 0), 
+					GS_REG_TEX1 + context);
+	q++;
+	return q;
+}
+
+static qword_t* SetAlphaBlending(qword_t* q, int context) {
+	PACK_GIFTAG(q, GIF_SET_TAG(1,0,0,0, GIF_FLG_PACKED, 1), GIF_REG_AD);
+	q++;
+
+	// https://psi-rockin.github.io/ps2tek/#gsalphablending
+	// Output = (((A - B) * C) >> 7) + D
+	//        = (((src - dst) * alpha) >> 7) + dst
+	//        =  (src * alpha - dst * alpha) / 128 + dst
+	//        =  (src * alpha - dst * alpha) / 128 + dst * 128 / 128
+	//        = ((src * alpha + dst * (128 - alpha)) / 128
+	PACK_GIFTAG(q, GS_SET_ALPHA(BLEND_COLOR_SOURCE, BLEND_COLOR_DEST, BLEND_ALPHA_SOURCE,
+								BLEND_COLOR_DEST, 0x80), GS_REG_ALPHA + context);
+	q++;
+	return q;
+}
+
+static void InitDrawingEnv(void) {
+	packet_t *packet = packet_init(30, PACKET_NORMAL); // TODO: is 30 too much?
+	qword_t *q = packet->data;
+	
+	q = draw_setup_environment(q, 0, &fb_colors[0], &fb_depth);
+	// GS can render from 0 to 4096, so set primitive origin to centre of that
+	q = draw_primitive_xyoffset(q, 0, 2048 - vp_hwidth, 2048 - vp_hheight);
+
+	q = SetTextureWrapping(q, 0);
+	q = SetTextureSampling(q, 0);
+	q = SetAlphaBlending(q,   0); // TODO has no effect ?
+	q = draw_finish(q);
+
+	dma_channel_send_normal(DMA_CHANNEL_GIF,packet->data,q - packet->data, 0, 0);
+	dma_wait_fast();
+
+	packet_free(packet);
+}
+
+// TODO: Find a better way than just increasing this hardcoded size
+static void InitDMABuffers(void) {
+	packets[0] = packet_init(50000, PACKET_NORMAL);
+	packets[1] = packet_init(50000, PACKET_NORMAL);
+}
+
+static void FlipContext(void) {
+	context ^= 1;
+	current  = packets[context];
+	
+	dma_tag = current->data;
+	// increment past the dmatag itself
+	q = dma_tag + 1;
+}
+
+static int tex_offset;
+void Gfx_Create(void) {
+	vp_hwidth  = DisplayInfo.Width  / 2;
+	vp_hheight = DisplayInfo.Height / 2;
+	primitive_type = 0; // PRIM_POINT, which isn't used here
+	
+	InitDrawingEnv();
+	InitDMABuffers();
+	tex_offset = graph_vram_allocate(256, 256, GS_PSM_32, GRAPH_ALIGN_BLOCK);
+	
+	context = 1;
+	FlipContext();
+	
+// TODO maybe Min not actually needed?
+	Gfx.MinTexWidth  = 4;
+	Gfx.MinTexHeight = 4;	
+	Gfx.MaxTexWidth  = 1024;
+	Gfx.MaxTexHeight = 1024;
+	Gfx.MaxTexSize   = 512 * 512;
+	Gfx.Created      = true;
+	
+	Gfx_RestoreState();
+}
+
+void Gfx_Free(void) { 
+	Gfx_FreeState();
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Textures--------------------------------------------------------*
+*#########################################################################################################################*/
+typedef struct CCTexture_ {
+	cc_uint32 width, height;
+	cc_uint32 log2_width, log2_height;
+	cc_uint32 pad[(64 - 16)/4];
+	cc_uint32 pixels[]; // aligned to 64 bytes (only need 16?)
+} 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*)memalign(16, 64 + size);
+	
+	tex->width       = bmp->width;
+	tex->height      = bmp->height;
+	tex->log2_width  = draw_log2(bmp->width);
+	tex->log2_height = draw_log2(bmp->height);
+	
+	CopyTextureData(tex->pixels, bmp->width * 4, bmp, rowWidth << 2);
+	return tex;
+}
+
+static void UpdateTextureBuffer(int context, texbuffer_t *texture, CCTexture* tex) {
+	PACK_GIFTAG(q, GIF_SET_TAG(1,0,0,0, GIF_FLG_PACKED, 1), GIF_REG_AD);
+	q++;
+
+	PACK_GIFTAG(q, GS_SET_TEX0(texture->address >> 6, texture->width >> 6, GS_PSM_32,
+							   tex->log2_width, tex->log2_height, TEXTURE_COMPONENTS_RGBA, TEXTURE_FUNCTION_MODULATE,
+							   0, 0, CLUT_STORAGE_MODE1, 0, CLUT_NO_LOAD), GS_REG_TEX0 + context);
+	q++;
+}
+
+void Gfx_BindTexture(GfxResourceID texId) {
+	if (!texId) texId = white_square;
+	CCTexture* tex = (CCTexture*)texId;
+	
+	texbuffer_t texbuf;
+	texbuf.width   = max(256, tex->width);
+	texbuf.address = tex_offset;
+	
+	// TODO terrible perf
+	DMATAG_END(dma_tag, (q - current->data) - 1, 0, 0, 0);
+	dma_channel_send_chain(DMA_CHANNEL_GIF, current->data, q - current->data, 0, 0);
+	dma_wait_fast();
+	
+	packet_t *packet = packet_init(200, PACKET_NORMAL);
+
+	qword_t *Q = packet->data;
+
+	Q = draw_texture_transfer(Q, tex->pixels, tex->width, tex->height, GS_PSM_32, tex_offset, max(256, tex->width));
+	Q = draw_texture_flush(Q);
+
+	dma_channel_send_chain(DMA_CHANNEL_GIF,packet->data, Q - packet->data, 0,0);
+	dma_wait_fast();
+
+	packet_free(packet);
+	
+	// TODO terrible perf
+	q = dma_tag + 1;
+	UpdateTextureBuffer(0, &texbuf, tex);
+}
+		
+void Gfx_DeleteTexture(GfxResourceID* texId) {
+	GfxResourceID data = *texId;
+	if (data) Mem_Free(data);
+	*texId = NULL;
+}
+
+void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
+	// TODO
+}
+
+void Gfx_EnableMipmaps(void)  { }
+void Gfx_DisableMipmaps(void) { }
+
+
+/*########################################################################################################################*
+*------------------------------------------------------State management---------------------------------------------------*
+*#########################################################################################################################*/
+static int clearR, clearG, clearB;
+static cc_bool gfx_depthTest;
+static cc_bool stateDirty;
+
+void Gfx_SetFog(cc_bool enabled)    { }
+void Gfx_SetFogCol(PackedCol col)   { }
+void Gfx_SetFogDensity(float value) { }
+void Gfx_SetFogEnd(float value)     { }
+void Gfx_SetFogMode(FogFunc func)   { }
+
+// 
+static void UpdateState(int context) {
+	// TODO: toggle Enable instead of method ?
+	int aMethod = gfx_alphaTest ? ATEST_METHOD_GREATER_EQUAL : ATEST_METHOD_ALLPASS;
+	int zMethod = gfx_depthTest ? ZTEST_METHOD_GREATER_EQUAL : ZTEST_METHOD_ALLPASS;
+	
+	PACK_GIFTAG(q, GIF_SET_TAG(1,0,0,0, GIF_FLG_PACKED, 1), GIF_REG_AD);
+	q++;
+	PACK_GIFTAG(q, GS_SET_TEST(DRAW_ENABLE,  aMethod, 0x80, ATEST_KEEP_FRAMEBUFFER,
+							   DRAW_DISABLE, DRAW_DISABLE,
+							   DRAW_ENABLE,  zMethod), GS_REG_TEST + context);
+	q++;
+	
+	stateDirty = false;
+}
+
+void Gfx_SetFaceCulling(cc_bool enabled) {
+	// TODO
+}
+
+static void SetAlphaTest(cc_bool enabled) {
+	stateDirty = true;
+}
+
+static void SetAlphaBlend(cc_bool enabled) {
+	// TODO update primitive state
+}
+
+void Gfx_SetAlphaArgBlend(cc_bool enabled) { }
+
+void Gfx_ClearBuffers(GfxBuffers buffers) {
+	// TODO clear only some buffers
+	q = draw_disable_tests(q, 0, &fb_depth);
+	q = draw_clear(q, 0, 2048.0f - fb_colors[0].width / 2.0f, 2048.0f - fb_colors[0].height / 2.0f,
+					fb_colors[0].width, fb_colors[0].height, clearR, clearG, clearB);
+	UpdateState(0);
+}
+
+void Gfx_ClearColor(PackedCol color) {
+	clearR = PackedCol_R(color);
+	clearG = PackedCol_G(color);
+	clearB = PackedCol_B(color);
+}
+
+void Gfx_SetDepthTest(cc_bool enabled) {
+	gfx_depthTest = enabled;
+	stateDirty    = true;
+}
+
+void Gfx_SetDepthWrite(cc_bool enabled) {
+	// TODO
+}
+
+static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) {
+	// TODO
+}
+
+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]);
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Index buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) {
+	return (void*)1;
+}
+
+void Gfx_BindIb(GfxResourceID ib) { }
+void Gfx_DeleteIb(GfxResourceID* ib) { }
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Vertex buffers----------------------------------------------------*
+*#########################################################################################################################*/
+static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) {
+	return Mem_TryAlloc(count, strideSizes[fmt]);
+}
+
+void Gfx_BindVb(GfxResourceID vb) { gfx_vertices = vb; }
+
+void Gfx_DeleteVb(GfxResourceID* vb) {
+	GfxResourceID data = *vb;
+	if (data) Mem_Free(data);
+	*vb = 0;
+}
+
+void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
+	return vb;
+}
+
+void Gfx_UnlockVb(GfxResourceID vb) { 
+	gfx_vertices = vb; 
+}
+
+
+static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) {
+	return Mem_TryAlloc(maxVertices, strideSizes[fmt]);
+}
+
+void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); }
+
+void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) {
+	return vb; 
+}
+
+void Gfx_UnlockDynamicVb(GfxResourceID vb) { 
+	gfx_vertices = vb;
+}
+
+void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); }
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Matrices--------------------------------------------------------*
+*#########################################################################################################################*/
+static struct Matrix _view, _proj, mvp;
+
+void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
+	if (type == MATRIX_VIEW)       _view = *matrix;
+	if (type == MATRIX_PROJECTION) _proj = *matrix;
+
+	Matrix_Mul(&mvp, &_view, &_proj);
+	// TODO
+}
+
+void Gfx_LoadIdentityMatrix(MatrixType type) {
+	Gfx_LoadMatrix(type, &Matrix_Identity);
+	// TODO
+}
+
+void Gfx_EnableTextureOffset(float x, float y) {
+	// TODO
+}
+
+void Gfx_DisableTextureOffset(void) {
+	// TODO
+}
+
+void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
+	/* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glortho */
+	/*   The simplified calculation below uses: L = 0, R = width, T = 0, B = height */
+	*matrix = Matrix_Identity;
+
+	matrix->row1.x =  2.0f / width;
+	matrix->row2.y = -2.0f / height;
+	matrix->row3.z = -2.0f / (zFar - zNear);
+
+	matrix->row4.x = -1.0f;
+	matrix->row4.y =  1.0f;
+	matrix->row4.z = -(zFar + zNear) / (zFar - zNear);
+}
+
+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_ = zFar;
+	float zFar_  = 0.1f;
+	float c = Cotangent(0.5f * fov);
+
+	/* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */
+	/* For pos FOV based perspective matrix, left/right/top/bottom are calculated as: */
+	/*   left = -c * aspect, right = c * aspect, bottom = -c, top = c */
+	/* Calculations are simplified because of left/right and top/bottom symmetry */
+	*matrix = Matrix_Identity;
+	// TODO: Check is Frustum culling needs changing for this
+
+	matrix->row1.x =  c / aspect;
+	matrix->row2.y =  c;
+	matrix->row3.z = -(zFar_ + zNear_) / (zFar_ - zNear_);
+	matrix->row3.w = -1.0f;
+	matrix->row4.z = -(2.0f * zFar_ * zNear_) / (zFar_ - zNear_);
+	matrix->row4.w =  0.0f;
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Rendering-------------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_SetVertexFormat(VertexFormat fmt) {
+	gfx_format = fmt;
+	gfx_stride = strideSizes[fmt];
+	// TODO update cached primitive state
+}
+
+typedef struct Vector4 { float x, y, z, w; } Vector4;
+
+static cc_bool NotClipped(Vector4 pos) {
+	// The code below clips to the viewport clip planes
+	//  For e.g. X this is [2048 - vp_width / 2, 2048 + vp_width / 2]
+	//  However the guard band itself ranges from 0 to 4096
+	// To reduce need to clip, clip against guard band on X/Y axes instead
+	/*return
+		xAdj  >= -pos.w && xAdj  <= pos.w &&
+		yAdj  >= -pos.w && yAdj  <= pos.w &&
+		pos.z >= -pos.w && pos.z <= pos.w;*/	
+		
+	// Rescale clip planes to guard band extent:
+	//  X/W * vp_hwidth <= vp_hwidth -- clipping against viewport
+	//              X/W <= 1
+	//              X   <= W
+	//  X/W * vp_hwidth <= 2048      -- clipping against guard band
+	//              X/W <= 2048 / vp_hwidth
+	//              X * vp_hwidth / 2048 <= W
+	float xAdj = pos.x * (vp_hwidth/2048);
+	float yAdj = pos.y * (vp_hheight/2048);
+	
+	// X/W * vp_hwidth <= 2048
+	// 
+		
+	// Clip X/Y to INSIDE the guard band regions
+	// NOTE: This seems to result in lockup at end of frame
+	return
+		xAdj > -pos.w && xAdj < pos.w &&
+		yAdj > -pos.w && yAdj < pos.w &&
+		pos.z >= -pos.w && pos.z <= pos.w;
+}
+
+static Vector4 TransformVertex(struct VertexTextured* pos) {
+	Vector4 coord;
+	coord.x = pos->x * mvp.row1.x + pos->y * mvp.row2.x + pos->z * mvp.row3.x + mvp.row4.x;
+	coord.y = pos->x * mvp.row1.y + pos->y * mvp.row2.y + pos->z * mvp.row3.y + mvp.row4.y;
+	coord.z = pos->x * mvp.row1.z + pos->y * mvp.row2.z + pos->z * mvp.row3.z + mvp.row4.z;
+	coord.w = pos->x * mvp.row1.w + pos->y * mvp.row2.w + pos->z * mvp.row3.w + mvp.row4.w;
+	return coord;
+}
+
+//#define VCopy(dst, src) dst.x = vp_hwidth  * (1 + src.x / src.w); dst.y = vp_hheight * (1 - src.y / src.w); dst.z = src.z / src.w; dst.w = src.w;
+static xyz_t FinishVertex(struct Vector4 src, float invW) {
+	float x = (vp_hwidth /2048) * (src.x * invW);
+	float y = (vp_hheight/2048) * (src.y * invW);
+	float z = src.z * invW;
+	
+	int originX = ftoi4(2048);
+	int originY = ftoi4(2048);
+	unsigned int maxZ = 1 << (32 - 1); // TODO: half this? or << 24 instead?
+	
+	xyz_t xyz;
+	xyz.x = (short)((x + 1.0f) *  originX);
+	xyz.y = (short)((y + 1.0f) * -originY);
+	xyz.z = (unsigned int)((z + 1.0f) * maxZ);
+	return xyz;
+}
+
+static void DrawTriangle(Vector4 v0, Vector4 v1, Vector4 v2, struct VertexTextured* V0, struct VertexTextured* V1, struct VertexTextured* V2) {
+	//Platform_Log4("X: %f3, Y: %f3, Z: %f3, W: %f3", &v0.x, &v0.y, &v0.z, &v0.w);	
+	u64* dw;
+	
+	Vector4 verts[3] = { v0, v1, v2 };
+	struct VertexTextured* v[] = { V0, V1, V2 };
+	
+	//Platform_Log4("   X: %f3, Y: %f3, Z: %f3, W: %f3", &in_vertices[0].x, &in_vertices[0].y, &in_vertices[0].z, &in_vertices[0].w);	
+	
+	// 3 "primitives" follow in the GIF packet (vertices in this case)
+	// 2 registers per "primitive" (colour, position)
+	PACK_GIFTAG(q, GIF_SET_TAG(3,1,0,0, GIF_FLG_REGLIST, 3), DRAW_STQ_REGLIST);
+	q++;
+
+	// TODO optimise
+	// Add the "primitives" to the GIF packet
+	dw = (u64*)q;
+	for (int i = 0; i < 3; i++)
+	{
+		float Q   = 1.0f / verts[i].w;
+		xyz_t xyz = FinishVertex(verts[i], Q);
+		color_t color;
+		texel_t texel;
+		
+		// See 'Colour Functions' https://psi-rockin.github.io/ps2tek/#gstextures
+		// Essentially, colour blending is calculated as
+		//   finalR = (vertexR * textureR) >> 7
+		// However, this behaves contrary to standard expectations
+		//  and results in final vertex colour being too bright
+		//
+		// For instance, if vertexR was white and textureR was grey:
+		//   finalR = (255 * 127) / 128 = 255
+		// White would be produced as the final colour instead of expected grey
+		//
+		// To counteract this, just divide all vertex colours by 2 first
+		color.rgbaq = (v[i]->Col & 0xFEFEFEFE) >> 1;
+		color.q     = Q;
+		texel.u     = v[i]->U * Q;
+		texel.v     = v[i]->V * Q;
+		
+		*dw++ = color.rgbaq;
+		*dw++ = texel.uv;
+		*dw++ = xyz.xyz;
+	}
+	dw++; // one more to even out number of doublewords
+	q = (qword_t*)dw;
+}
+
+static void DrawTriangles(int verticesCount, int startVertex) {
+	if (stateDirty) UpdateState(0);
+	if (gfx_format == VERTEX_FORMAT_COLOURED) return;
+	cc_bool texturing = gfx_format == VERTEX_FORMAT_TEXTURED;
+	
+	// TODO: only when vertex format changes ?
+	PACK_GIFTAG(q, GIF_SET_TAG(1,0,0,0, GIF_FLG_PACKED, 1), GIF_REG_AD);
+	q++;
+	PACK_GIFTAG(q, GS_SET_PRIM(PRIM_TRIANGLE, PRIM_SHADE_GOURAUD, texturing, DRAW_DISABLE,
+							  gfx_alphaBlend, DRAW_DISABLE, PRIM_MAP_ST,
+							  0, PRIM_UNFIXED), GS_REG_PRIM);
+	q++;
+	
+	struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex;
+	for (int i = 0; i < verticesCount / 4; i++, v += 4)
+	{
+		Vector4 V0 = TransformVertex(v + 0);
+		Vector4 V1 = TransformVertex(v + 1);
+		Vector4 V2 = TransformVertex(v + 2);
+		Vector4 V3 = TransformVertex(v + 3);
+		
+		
+	//Platform_Log3("X: %f3, Y: %f3, Z: %f3", &v[0].x, &v[0].y, &v[0].z);
+	//Platform_Log3("X: %f3, Y: %f3, Z: %f3", &v[1].x, &v[1].y, &v[1].z);
+	//Platform_Log3("X: %f3, Y: %f3, Z: %f3", &v[2].x, &v[2].y, &v[2].z);
+	//Platform_Log3("X: %f3, Y: %f3, Z: %f3", &v[3].x, &v[3].y, &v[3].z);
+	//Platform_LogConst(">>>>>>>>>>");
+		
+		if (NotClipped(V0) && NotClipped(V1) && NotClipped(V2)) {
+			DrawTriangle(V0, V1, V2, v + 0, v + 1, v + 2);
+		}
+		
+		if (NotClipped(V2) && NotClipped(V3) && NotClipped(V0)) {
+			DrawTriangle(V2, V3, V0, v + 2, v + 3, v + 0);
+		}
+		
+		//Platform_LogConst("-----");
+	}
+}
+
+// TODO: Can this be used? need to understand EOP more
+static void SetPrimitiveType(int type) {
+	if (primitive_type == type) return;
+	primitive_type = type;
+	
+	PACK_GIFTAG(q, GIF_SET_TAG(1,0,0,0, GIF_FLG_PACKED, 1), GIF_REG_AD);
+	q++;
+	PACK_GIFTAG(q, GS_SET_PRIM(type, PRIM_SHADE_GOURAUD, DRAW_DISABLE, DRAW_DISABLE,
+							  DRAW_DISABLE, DRAW_DISABLE, PRIM_MAP_ST,
+							  0, PRIM_UNFIXED), GS_REG_PRIM);
+	q++;
+}
+
+void Gfx_DrawVb_Lines(int verticesCount) {
+	//SetPrimitiveType(PRIM_LINE);
+} /* TODO */
+
+void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {
+	//SetPrimitiveType(PRIM_TRIANGLE);
+	DrawTriangles(verticesCount, startVertex);
+}
+
+void Gfx_DrawVb_IndexedTris(int verticesCount) {
+	//SetPrimitiveType(PRIM_TRIANGLE);
+	DrawTriangles(verticesCount, 0);
+	// TODO
+}
+
+void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {
+	//SetPrimitiveType(PRIM_TRIANGLE);
+	DrawTriangles(verticesCount, startVertex);
+	// TODO
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Other/Misc------------------------------------------------------*
+*#########################################################################################################################*/
+cc_result Gfx_TakeScreenshot(struct Stream* output) {
+	return ERR_NOT_SUPPORTED;
+}
+
+cc_bool Gfx_WarnIfNecessary(void) {
+	return false;
+}
+
+void Gfx_BeginFrame(void) { 
+	Platform_LogConst("--- Frame ---");
+}
+
+void Gfx_EndFrame(void) {
+	Platform_LogConst("--- EF1 ---");
+	// Double buffering
+	graph_set_framebuffer_filtered(fb_colors[context].address,
+                                   fb_colors[context].width,
+                                   fb_colors[context].psm, 0, 0);
+
+	q = draw_framebuffer(q, 0, &fb_colors[context ^ 1]);
+	q = draw_finish(q);
+	
+	// Fill out and then send DMA chain
+	DMATAG_END(dma_tag, (q - current->data) - 1, 0, 0, 0);
+	dma_wait_fast();
+	dma_channel_send_chain(DMA_CHANNEL_GIF, current->data, q - current->data, 0, 0);
+	Platform_LogConst("--- EF2 ---");
+		
+	draw_wait_finish();
+	Platform_LogConst("--- EF3 ---");
+	
+	if (gfx_vsync) graph_wait_vsync();
+	if (gfx_minFrameMs) LimitFPS();
+	
+	FlipContext();
+	Platform_LogConst("--- EF4 ---");
+}
+
+void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) {
+	gfx_minFrameMs = minFrameMs;
+	gfx_vsync      = vsync;
+}
+
+void Gfx_OnWindowResize(void) {
+	// TODO
+}
+
+void Gfx_SetViewport(int x, int y, int w, int h) { }
+
+void Gfx_GetApiInfo(cc_string* info) {
+	String_AppendConst(info, "-- Using PS2 --\n");
+	PrintMaxTextureInfo(info);
+}
+
+cc_bool Gfx_TryRestoreContext(void) { return true; }
+#endif