pcsxr/plugins/peopsxgl/pgxp_gpu.c

669 lines
16 KiB
C

/***************************************************************************
* Copyright (C) 2016 by iCatButler *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
/**************************************************************************
* pgxp_gpu.c
* PGXP - Parallel/Precision Geometry Xform Pipeline
*
* Created on: 25 Mar 2016
* Author: iCatButler
***************************************************************************/
#include "pgxp_gpu.h"
#include "stdafx.h"
#include "externals.h"
#include <math.h>
typedef struct
{
float x;
float y;
float z;
union
{
unsigned int flags;
unsigned char compFlags[4];
};
unsigned int count;
unsigned int value;
unsigned int mFlags;
} PGXP_vertex;
#define NONE 0
#define ALL 0xFFFFFFFF
#define VALID 1
#define VALID_0 (VALID << 0)
#define VALID_1 (VALID << 8)
#define VALID_2 (VALID << 16)
#define VALID_3 (VALID << 24)
#define VALID_01 (VALID_0 | VALID_1)
#define VALID_012 (VALID_0 | VALID_1 | VALID_2)
#define VALID_ALL (VALID_0 | VALID_1 | VALID_2 | VALID_3)
#define INV_VALID_ALL (ALL ^ VALID_ALL)
unsigned int PGXP_tDebug = 0;
/////////////////////////////////
//// Blade_Arma's Vertex Cache (CatBlade?)
/////////////////////////////////
const unsigned int mode_init = 0;
const unsigned int mode_write = 1;
const unsigned int mode_read = 2;
const unsigned int mode_fail = 3;
PGXP_vertex vertexCache[0x800 * 2][0x800 * 2];
unsigned int baseID = 0;
unsigned int lastID = 0;
unsigned int cacheMode = 0;
unsigned int IsSessionID(unsigned int vertID)
{
// No wrapping
if (lastID >= baseID)
return (vertID >= baseID);
// If vertID is >= baseID it is pre-wrap and in session
if (vertID >= baseID)
return 1;
// vertID is < baseID, If it is <= lastID it is post-wrap and in session
if (vertID <= lastID)
return 1;
return 0;
}
void CALLBACK GPUpgxpCacheVertex(short sx, short sy, const unsigned char* _pVertex)
{
const PGXP_vertex* pNewVertex = (const PGXP_vertex*)_pVertex;
PGXP_vertex* pOldVertex = NULL;
if (!pNewVertex)
{
cacheMode = mode_fail;
return;
}
//if (bGteAccuracy)
{
if (cacheMode != mode_write)
{
// Initialise cache on first use
if (cacheMode == mode_init)
memset(vertexCache, 0x00, sizeof(vertexCache));
// First vertex of write session (frame?)
cacheMode = mode_write;
baseID = pNewVertex->count;
}
lastID = pNewVertex->count;
if (sx >= -0x800 && sx <= 0x7ff &&
sy >= -0x800 && sy <= 0x7ff)
{
pOldVertex = &vertexCache[sy + 0x800][sx + 0x800];
// To avoid ambiguity there can only be one valid entry per-session
if (IsSessionID(pOldVertex->count) && (pOldVertex->value == pNewVertex->value))
{
// check to ensure this isn't identical
if ((fabsf(pOldVertex->x - pNewVertex->x) > 0.1f) ||
(fabsf(pOldVertex->y - pNewVertex->y) > 0.1f) ||
(fabsf(pOldVertex->z - pNewVertex->z) > 0.1f))
{
pOldVertex->mFlags = 5;
return;
}
}
// Write vertex into cache
*pOldVertex = *pNewVertex;
pOldVertex->mFlags = 1;
}
}
}
PGXP_vertex* PGXP_GetCachedVertex(short sx, short sy)
{
//if (bGteAccuracy)
{
if (cacheMode != mode_read)
{
if (cacheMode == mode_fail)
return NULL;
// Initialise cache on first use
if (cacheMode == mode_init)
memset(vertexCache, 0x00, sizeof(vertexCache));
// First vertex of read session (frame?)
cacheMode = mode_read;
}
if (sx >= -0x800 && sx <= 0x7ff &&
sy >= -0x800 && sy <= 0x7ff)
{
// Return pointer to cache entry
return &vertexCache[sy + 0x800][sx + 0x800];
}
}
return NULL;
}
/////////////////////////////////
//// PGXP Implementation
/////////////////////////////////
const unsigned int primStrideTable[] = { 1, 2, 1, 2, 2, 3, 2, 3, 0 };
const unsigned int primCountTable[] = { 3, 3, 4, 4, 3, 3, 4, 4, 0 };
PGXP_vertex* PGXP_Mem = NULL; // pointer to parallel memory
unsigned int currentAddr = 0; // address of current DMA
unsigned int numVertices = 0; // iCB: Used for glVertex3fv fix
unsigned int vertexIdx = 0;
// Set current DMA address and pointer to parallel memory
void CALLBACK GPUpgxpMemory(unsigned int addr, unsigned char* pVRAM)
{
PGXP_Mem = (PGXP_vertex*)(pVRAM);
currentAddr = addr;
}
// Set current DMA address
void PGXP_SetAddress(unsigned int addr)
{
currentAddr = addr;
}
void PGXP_SetMatrix(float left, float right, float bottom, float top, float zNear, float zFar)
{
GLfloat m[16];
for (unsigned int i = 0; i < 16; ++i)
m[i] = 0.f;
//if ((right-left) != 0)
//{
// m[0] = 2 / (right - left);
// m[12] = -((right + left) / (right - left));
//}
//if ((top-bottom) != 0)
//{
// m[5] = 2 / (top - bottom);
// m[13] = -((top + bottom) / (top - bottom));
//}
//m[10] = -2 / (zFar - zNear);
//m[14] = -((zFar + zNear) / (zFar - zNear));
//m[15] = 1;
if ((right-left) != 0)
{
m[0] = 2 / (right - left);
m[8] = -((right + left) / (right - left));
}
if ((top-bottom) != 0)
{
m[5] = 2 / (top - bottom);
m[9] = -((top + bottom) / (top - bottom));
}
m[10] = -2 / (zFar - zNear);
m[14] = -((zFar + zNear) / (zFar - zNear));
m[11] = 1;
glLoadMatrixf(m);
//glOrtho(left, right, bottom, top, zNear, zFar);
}
// Wrap glVertex3fv/glVertex4fv
void PGXP_glVertexfv(GLfloat* pV)
{
// If there are PGXP vertices expected
if (1)//(vertexIdx < numVertices)
{
float temp[4];
memcpy(temp, pV, sizeof(float) * 4);
//pre-multiply each element by w (to negate perspective divide)
for (unsigned int i = 0; i < 3; i++)
temp[i] *= temp[3];
//pass complete vertex to OpenGL
glVertex4fv(temp);
vertexIdx++;
//pV[3] = 1.f;
}
else
{
glVertex3fv(pV);
}
}
// Get parallel vertex values
int PGXP_GetVertices(unsigned int* addr, void* pOutput, int xOffs, int yOffs)
{
unsigned int primCmd = ((*addr >> 24) & 0xff); // primitive command
unsigned int primIdx = min((primCmd - 0x20) >> 2, 8); // index to primitive lookup
OGLVertex* pVertex = (OGLVertex*)pOutput; // pointer to output vertices
unsigned int stride = primStrideTable[primIdx]; // stride between vertices
unsigned int count = primCountTable[primIdx]; // number of vertices
PGXP_vertex* primStart = NULL; // pointer to first vertex
char invalidVert = 0; // Number of vertices without valid PGXP values
short* pPrimData = ((short*)addr) + 2; // primitive data for cache lookups
PGXP_vertex* pCacheVert = NULL;
// Reset vertex count
numVertices = count;
vertexIdx = 0;
// if PGXP is enabled
if (PGXP_Mem != NULL)
{
// Offset to start of primitive
primStart = &PGXP_Mem[currentAddr + 1];
// Find any invalid vertices
for (unsigned i = 0; i < count; ++i)
{
if (!((primStart[stride * i].flags & VALID_012) == VALID_012))
invalidVert++;
}
}
else
invalidVert = count;
for (unsigned i = 0; i < count; ++i)
{
if (primStart && ((primStart[stride * i].flags & VALID_01) == VALID_01) && (primStart[stride * i].value == *(unsigned int*)(&pPrimData[stride * i * 2])))
{
pVertex[i].x = (primStart[stride * i].x + xOffs);
pVertex[i].y = (primStart[stride * i].y + yOffs);
pVertex[i].z = 0.95f;
pVertex[i].w = primStart[stride * i].z;
pVertex[i].PGXP_flag = 1;
if ((primStart[stride * i].flags & VALID_2) != VALID_2)
{
pVertex[i].PGXP_flag = 6;
// __Log("GPPV No W: v:%x (%d, %d) pgxp(%f, %f)|\n", (currentAddr + 1 + (i * stride)) * 4, pPrimData[stride * i * 2], pPrimData[(stride * i * 2) + 1], primStart[stride * i].x, primStart[stride * i].y);
}
// Log incorrect vertices
//if (PGXP_tDebug &&
// (fabs((float)pPrimData[stride * i * 2] - primStart[stride * i].x) > debug_tolerance) ||
// (fabs((float)pPrimData[(stride * i * 2) + 1] - primStart[stride * i].y) > debug_tolerance))
// __Log("GPPV: v:%x (%d, %d) pgxp(%f, %f)|\n", (currentAddr + 1 + (i * stride)) * 4, pPrimData[stride * i * 2], pPrimData[(stride * i * 2) + 1], primStart[stride * i].x, primStart[stride * i].y);
}
else
{
// Default to low precision vertex data
//if (primStart && ((primStart[stride * i].flags & VALID_01) == VALID_01) && primStart[stride * i].value != *(unsigned int*)(&pPrimData[stride * i * 2]))
// pVertex[i].PGXP_flag = 6;
//else
pVertex[i].PGXP_flag = 2;
// Look in cache for valid vertex
pCacheVert = PGXP_GetCachedVertex(pPrimData[stride * i * 2], pPrimData[(stride * i * 2) + 1]);
if (pCacheVert)
{
if (IsSessionID(pCacheVert->count))
{
if (pCacheVert->mFlags == 1)
{
pVertex[i].x = (pCacheVert->x + xOffs);
pVertex[i].y = (pCacheVert->y + yOffs);
pVertex[i].z = 0.95f;
pVertex[i].w = pCacheVert->z;
pVertex[i].PGXP_flag = 3;
// reduce number of invalid vertices
invalidVert--;
}
else if(pCacheVert->mFlags > 1)
pVertex[i].PGXP_flag = 4;
}
}
// Log unprocessed vertices
//if(PGXP_tDebug)
// __Log("GPPV: v:%x (%d, %d)|\n", (currentAddr + 1 + (i * stride))*4, pPrimData[stride * i * 2], pPrimData[(stride * i * 2) + 1]);
}
}
// If there are any invalid vertices set all w values to 1
// iCB: Could use plane equation to find w for single invalid vertex in a quad
if (invalidVert > 0)
for (unsigned i = 0; i < count; ++i)
pVertex[i].w = 1;
if(PGXP_vDebug == 3)
for (unsigned i = 0; i < count; ++i)
pVertex[i].PGXP_flag = primIdx;
return 1;
}
/////////////////////////////////
//// Visual Debugging Functions
/////////////////////////////////
unsigned int PGXP_vDebug = 0;
const unsigned int PGXP_maxDebug = 4;
const char red[4] = { 255, 0, 0, 255 };
const char blue[4] = { 0, 0, 255, 255 };
const char green[4] = { 0, 255, 0, 255 };
const char yellow[4] = { 255, 255, 0, 255 };
const char magenta[4] = { 255, 0, 255, 255 };
const char cyan[4] = { 0, 255, 255, 255 };
const char orange[4] = { 255, 128 ,0 ,255 };
const char black[4] = { 0, 0, 0, 255 };
//void CALLBACK GPUtoggleDebug(void)
//{
// PGXP_tDebug = (PGXP_tDebug) ? 0 : 1;
//}
void CALLBACK GPUtoggleDebug(void)
{
PGXP_vDebug++;
if (PGXP_vDebug == PGXP_maxDebug)
PGXP_vDebug = 0;
}
void PGXP_colour(OGLVertex* vertex)
{
const char* pColour;
float fDepth;
switch (PGXP_vDebug)
{
case 1:
// Vertex origin mode
switch (vertex->PGXP_flag)
{
case 0:
pColour = yellow;
break;
case 1:
pColour = blue;
break;
case 2:
pColour = red;
break;
case 3:
pColour = green;
break;
case 4:
pColour = magenta;
break;
case 6:
pColour = cyan;
break;
default:
pColour = black;
break;
}
glColor4ubv(pColour);
break;
case 2:
// W component visualisation
fDepth = vertex->w / (float)(0xFFFF);
glColor4f(fDepth, fDepth, fDepth, 1.f);
break;
case 3:
// Primitive type
switch (vertex->PGXP_flag)
{
case 0:
pColour = yellow;
break;
case 1:
pColour = blue;
break;
case 2:
pColour = red;
break;
case 3:
pColour = green;
break;
case 4:
pColour = magenta;
break;
case 6:
pColour = cyan;
break;
case 7:
pColour = orange;
default:
pColour = black;
break;
}
glColor4ubv(pColour);
break;
}
}
int PGXP_DrawDebugTriQuad(OGLVertex* vertex1, OGLVertex* vertex2, OGLVertex* vertex3, OGLVertex* vertex4)
{
GLboolean bTexture = glIsEnabled(GL_TEXTURE_2D);
GLfloat fColour[4];
GLint iShadeModel;
//if ((vertex1->PGXP_flag == 0) ||
// (vertex2->PGXP_flag == 0) ||
// (vertex3->PGXP_flag == 0) ||
// (vertex4->PGXP_flag == 0))
// return 0;
// Quit if PGXP_flag == ignore
if ((vertex1->PGXP_flag == 5) ||
(vertex2->PGXP_flag == 5) ||
(vertex3->PGXP_flag == 5) ||
(vertex4->PGXP_flag == 5))
return 1;
glGetIntegerv(GL_SHADE_MODEL, &iShadeModel);
glGetFloatv(GL_CURRENT_COLOR, fColour);
glDisable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glBegin(GL_TRIANGLE_STRIP);
PGXP_colour(vertex1);
PGXP_glVertexfv(&vertex1->x);
PGXP_colour(vertex2);
PGXP_glVertexfv(&vertex2->x);
PGXP_colour(vertex3);
PGXP_glVertexfv(&vertex3->x);
PGXP_colour(vertex4);
PGXP_glVertexfv(&vertex4->x);
glEnd();
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);
glBegin(GL_TRIANGLE_STRIP);
glColor4ubv(black);
PGXP_glVertexfv(&vertex1->x);
PGXP_glVertexfv(&vertex2->x);
PGXP_glVertexfv(&vertex3->x);
PGXP_glVertexfv(&vertex4->x);
glColor4fv(fColour);
glEnd();
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
if(bTexture == GL_TRUE)
glEnable(GL_TEXTURE_2D);
glShadeModel(iShadeModel);
return 1;
}
int PGXP_DrawDebugTri(OGLVertex* vertex1, OGLVertex* vertex2, OGLVertex* vertex3)
{
GLboolean bTexture = glIsEnabled(GL_TEXTURE_2D);
GLfloat fColour[4];
GLint iShadeModel;
//if ((vertex1->PGXP_flag == 0) ||
// (vertex2->PGXP_flag == 0) ||
// (vertex3->PGXP_flag == 0))
// return 0;
// Quit if PGXP_flag == ignore
if ((vertex1->PGXP_flag == 5) ||
(vertex2->PGXP_flag == 5) ||
(vertex3->PGXP_flag == 5))
return 1;
glGetIntegerv(GL_SHADE_MODEL, &iShadeModel);
glGetFloatv(GL_CURRENT_COLOR, fColour);
glDisable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glBegin(GL_TRIANGLES);
PGXP_colour(vertex1);
PGXP_glVertexfv(&vertex1->x);
PGXP_colour(vertex2);
PGXP_glVertexfv(&vertex2->x);
PGXP_colour(vertex3);
PGXP_glVertexfv(&vertex3->x);
glEnd();
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);
glBegin(GL_TRIANGLE_STRIP);
glColor4ubv(black);
PGXP_glVertexfv(&vertex1->x);
PGXP_glVertexfv(&vertex2->x);
PGXP_glVertexfv(&vertex3->x);
glColor4fv(fColour);
glEnd();
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
if (bTexture == GL_TRUE)
glEnable(GL_TEXTURE_2D);
glShadeModel(iShadeModel);
return 1;
}
int PGXP_DrawDebugQuad(OGLVertex* vertex1, OGLVertex* vertex2, OGLVertex* vertex3, OGLVertex* vertex4)
{
GLboolean bTexture = glIsEnabled(GL_TEXTURE_2D);
GLfloat fColour[4];
GLint iShadeModel;
//if ((vertex1->PGXP_flag == 0) ||
// (vertex2->PGXP_flag == 0) ||
// (vertex3->PGXP_flag == 0) ||
// (vertex4->PGXP_flag == 0))
// return 0;
// Quit if PGXP_flag == ignore
if ((vertex1->PGXP_flag == 5) ||
(vertex2->PGXP_flag == 5) ||
(vertex3->PGXP_flag == 5) ||
(vertex4->PGXP_flag == 5))
return 1;
glGetIntegerv(GL_SHADE_MODEL, &iShadeModel);
glGetFloatv(GL_CURRENT_COLOR, fColour);
glDisable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glBegin(GL_QUADS);
PGXP_colour(vertex1);
PGXP_glVertexfv(&vertex1->x);
PGXP_colour(vertex2);
PGXP_glVertexfv(&vertex2->x);
PGXP_colour(vertex3);
PGXP_glVertexfv(&vertex3->x);
PGXP_colour(vertex4);
PGXP_glVertexfv(&vertex4->x);
glEnd();
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);
glBegin(GL_TRIANGLE_STRIP);
glColor4ubv(black);
PGXP_glVertexfv(&vertex1->x);
PGXP_glVertexfv(&vertex2->x);
PGXP_glVertexfv(&vertex3->x);
PGXP_glVertexfv(&vertex4->x);
glColor4fv(fColour);
glEnd();
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
if (bTexture == GL_TRUE)
glEnable(GL_TEXTURE_2D);
glShadeModel(iShadeModel);
return 1;
}