pcsx2/plugins/GSdx/GPUState.cpp

810 lines
13 KiB
C++

/*
* Copyright (C) 2007-2009 Gabest
* http://www.gabest.org
*
* 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, 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 GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include "stdafx.h"
#include "GPUState.h"
GPUState::GPUState()
: s_n(0)
{
memset(m_status, 0, sizeof(m_status));
for(int i = 0; i < countof(m_fpGPUStatusCommandHandlers); i++)
{
m_fpGPUStatusCommandHandlers[i] = &GPUState::SCH_Null;
}
m_fpGPUStatusCommandHandlers[0x00] = &GPUState::SCH_ResetGPU;
m_fpGPUStatusCommandHandlers[0x01] = &GPUState::SCH_ResetCommandBuffer;
m_fpGPUStatusCommandHandlers[0x02] = &GPUState::SCH_ResetIRQ;
m_fpGPUStatusCommandHandlers[0x03] = &GPUState::SCH_DisplayEnable;
m_fpGPUStatusCommandHandlers[0x04] = &GPUState::SCH_DMASetup;
m_fpGPUStatusCommandHandlers[0x05] = &GPUState::SCH_StartOfDisplayArea;
m_fpGPUStatusCommandHandlers[0x06] = &GPUState::SCH_HorizontalDisplayRange;
m_fpGPUStatusCommandHandlers[0x07] = &GPUState::SCH_VerticalDisplayRange;
m_fpGPUStatusCommandHandlers[0x08] = &GPUState::SCH_DisplayMode;
m_fpGPUStatusCommandHandlers[0x10] = &GPUState::SCH_GPUInfo;
m_fpGPUPacketHandler[0] = &GPUState::PH_Command;
m_fpGPUPacketHandler[1] = &GPUState::PH_Polygon;
m_fpGPUPacketHandler[2] = &GPUState::PH_Line;
m_fpGPUPacketHandler[3] = &GPUState::PH_Sprite;
m_fpGPUPacketHandler[4] = &GPUState::PH_Move;
m_fpGPUPacketHandler[5] = &GPUState::PH_Write;
m_fpGPUPacketHandler[6] = &GPUState::PH_Read;
m_fpGPUPacketHandler[7] = &GPUState::PH_Environment;
Reset();
}
GPUState::~GPUState()
{
}
void GPUState::Reset()
{
m_env.Reset();
m_mem.Invalidate(GSVector4i(0, 0, 1024, 512));
memset(&m_v, 0, sizeof(m_v));
}
void GPUState::Flush()
{
FlushPrim();
}
void GPUState::SetPrim(GPUReg* r)
{
if(m_env.PRIM.TYPE != r->PRIM.TYPE)
{
ResetPrim();
}
GPURegPRIM PRIM = r->PRIM;
PRIM.VTX = 0;
switch(r->PRIM.TYPE)
{
case GPU_POLYGON:
PRIM.u32 = (r->PRIM.u32 & 0xF7000000) | 3; // TYPE IIP TME ABE TGE
break;
case GPU_LINE:
PRIM.u32 = (r->PRIM.u32 & 0xF2000000) | 2; // TYPE IIP ABE
PRIM.TGE = 1; // ?
break;
case GPU_SPRITE:
PRIM.u32 = (r->PRIM.u32 & 0xE7000000) | 2; // TYPE TME ABE TGE
break;
}
if(m_env.PRIM.u32 != PRIM.u32)
{
Flush();
m_env.PRIM = PRIM;
}
}
void GPUState::SetCLUT(GPUReg* r)
{
uint32 mask = 0xFFFF0000; // X Y
uint32 value = (m_env.CLUT.u32 & ~mask) | (r->u32 & mask);
if(m_env.CLUT.u32 != value)
{
Flush();
m_env.CLUT.u32 = value;
}
}
void GPUState::SetTPAGE(GPUReg* r)
{
uint32 mask = 0x000001FF; // TP ABR TY TX
uint32 value = (m_env.STATUS.u32 & ~mask) | ((r->u32 >> 16) & mask);
if(m_env.STATUS.u32 != value)
{
Flush();
m_env.STATUS.u32 = value;
}
}
void GPUState::Invalidate(const GSVector4i& r)
{
m_mem.Invalidate(r);
}
void GPUState::WriteData(const uint8* mem, uint32 size)
{
GSPerfMonAutoTimer pmat(&m_perfmon);
size <<= 2;
m_write.Append(mem, size);
int i = 0;
while(i < m_write.bytes)
{
GPUReg* r = (GPUReg*)&m_write.buff[i];
int ret = (this->*m_fpGPUPacketHandler[r->PACKET.TYPE])(r, (m_write.bytes - i) >> 2);
if(ret == 0) return; // need more data
i += ret << 2;
}
m_write.Remove(i);
}
void GPUState::ReadData(uint8* mem, uint32 size)
{
GSPerfMonAutoTimer pmat(&m_perfmon);
int remaining = m_read.bytes - m_read.cur;
int bytes = (int)size << 2;
if(bytes > remaining)
{
// ASSERT(0);
// printf"WARNING: ReadData\n");
// memset(&mem[remaining], 0, bytes - remaining);
bytes = remaining;
}
memcpy(mem, &m_read.buff[m_read.cur], bytes);
m_read.cur += bytes;
if(m_read.cur >= m_read.bytes)
{
m_env.STATUS.IMG = 0;
}
}
void GPUState::WriteStatus(uint32 status)
{
GSPerfMonAutoTimer pmat(&m_perfmon);
uint32 b = status >> 24;
m_status[b] = status;
(this->*m_fpGPUStatusCommandHandlers[b])((GPUReg*)&status);
}
uint32 GPUState::ReadStatus()
{
GSPerfMonAutoTimer pmat(&m_perfmon);
m_env.STATUS.LCF = ~m_env.STATUS.LCF; // ?
return m_env.STATUS.u32;
}
void GPUState::Freeze(GPUFreezeData* data)
{
data->status = m_env.STATUS.u32;
memcpy(data->control, m_status, 256 * 4);
m_mem.ReadRect(GSVector4i(0, 0, 1024, 512), data->vram);
}
void GPUState::Defrost(const GPUFreezeData* data)
{
m_env.STATUS.u32 = data->status;
memcpy(m_status, data->control, 256 * 4);
m_mem.WriteRect(GSVector4i(0, 0, 1024, 512), data->vram);
for(int i = 0; i <= 8; i++)
{
WriteStatus(m_status[i]);
}
}
void GPUState::SCH_Null(GPUReg* r)
{
ASSERT(0);
}
void GPUState::SCH_ResetGPU(GPUReg* r)
{
Reset();
}
void GPUState::SCH_ResetCommandBuffer(GPUReg* r)
{
// ?
}
void GPUState::SCH_ResetIRQ(GPUReg* r)
{
// ?
}
void GPUState::SCH_DisplayEnable(GPUReg* r)
{
m_env.STATUS.DEN = r->DEN.DEN;
}
void GPUState::SCH_DMASetup(GPUReg* r)
{
m_env.STATUS.DMA = r->DMA.DMA;
}
void GPUState::SCH_StartOfDisplayArea(GPUReg* r)
{
m_env.DAREA = r->DAREA;
}
void GPUState::SCH_HorizontalDisplayRange(GPUReg* r)
{
m_env.DHRANGE = r->DHRANGE;
}
void GPUState::SCH_VerticalDisplayRange(GPUReg* r)
{
m_env.DVRANGE = r->DVRANGE;
}
void GPUState::SCH_DisplayMode(GPUReg* r)
{
m_env.STATUS.WIDTH0 = r->DMODE.WIDTH0;
m_env.STATUS.HEIGHT = r->DMODE.HEIGHT;
m_env.STATUS.ISPAL = r->DMODE.ISPAL;
m_env.STATUS.ISRGB24 = r->DMODE.ISRGB24;
m_env.STATUS.ISINTER = r->DMODE.ISINTER;
m_env.STATUS.WIDTH1 = r->DMODE.WIDTH1;
}
void GPUState::SCH_GPUInfo(GPUReg* r)
{
uint32 value = 0;
switch(r->GPUINFO.PARAM)
{
case 0x2:
value = m_env.TWIN.u32;
break;
case 0x0:
case 0x1:
case 0x3:
value = m_env.DRAREATL.u32;
break;
case 0x4:
value = m_env.DRAREABR.u32;
break;
case 0x5:
case 0x6:
value = m_env.DROFF.u32;
break;
case 0x7:
value = 2;
break;
case 0x8:
case 0xf:
value = 0xBFC03720; // ?
break;
default:
ASSERT(0);
break;
}
m_read.RemoveAll();
m_read.Append((uint8*)&value, 4);
m_read.cur = 0;
}
int GPUState::PH_Command(GPUReg* r, int size)
{
switch(r->PACKET.OPTION)
{
case 0: // ???
return 1;
case 1: // clear cache
return 1;
case 2: // fillrect
if(size < 3) return 0;
Flush();
GSVector4i r2;
r2.left = r[1].XY.X;
r2.top = r[1].XY.Y;
r2.right = r2.left + r[2].XY.X;
r2.bottom = r2.top + r[2].XY.Y;
uint16 c = (uint16)(((r[0].RGB.R >> 3) << 10) | ((r[0].RGB.R >> 3) << 5) | (r[0].RGB.R >> 3));
m_mem.FillRect(r2, c);
Invalidate(r2);
Dump("f");
return 3;
}
ASSERT(0);
return 1;
}
int GPUState::PH_Polygon(GPUReg* r, int size)
{
int required = 1;
int vertices = r[0].POLYGON.VTX ? 4 : 3;
required += vertices;
if(r[0].POLYGON.TME) required += vertices;
if(r[0].POLYGON.IIP) required += vertices - 1;
if(size < required) return 0;
//
SetPrim(r);
if(r[0].POLYGON.TME)
{
SetCLUT(&r[2]);
SetTPAGE(&r[r[0].POLYGON.IIP ? 5 : 4]);
}
//
GPUVertex v[4];
for(int i = 0, j = 0; j < vertices; j++)
{
v[j].RGB = r[r[0].POLYGON.IIP ? i : 0].RGB;
if(j == 0 || r[0].POLYGON.IIP) i++;
v[j].XY = r[i++].XY;
if(r[0].POLYGON.TME)
{
v[j].UV.X = r[i].UV.U;
v[j].UV.Y = r[i].UV.V;
i++;
}
}
for(int i = 0; i <= vertices - 3; i++)
{
// TODO: sse
int y0 = v[i + 0].XY.Y;
int y1 = v[i + 1].XY.Y;
int y2 = v[i + 2].XY.Y;
if(std::abs(y0 - y1) >= 512
|| std::abs(y0 - y2) >= 512
|| std::abs(y1 - y2) >= 512)
{
continue;
}
int x0 = v[i + 0].XY.X;
int x1 = v[i + 1].XY.X;
int x2 = v[i + 2].XY.X;
if(std::abs(x0 - x1) >= 1024
|| std::abs(x0 - x2) >= 1024
|| std::abs(x1 - x2) >= 1024)
{
continue;
}
//
for(int j = 0; j < 3; j++)
{
m_v = v[i + j];
VertexKick();
}
}
//
return required;
}
int GPUState::PH_Line(GPUReg* r, int size)
{
int required = 1;
int vertices = 0;
if(r->LINE.PLL)
{
required++;
for(int i = 1; i < size; i++)
{
if((r[i].u32 & 0xf000f000) == 0x50005000)
{
vertices = i - 1;
}
}
if(vertices < 2)
{
return 0;
}
}
else
{
vertices = 2;
}
required += vertices;
if(r->LINE.IIP) required += vertices - 1;
//
SetPrim(r);
//
for(int i = 0, j = 0; j < vertices; j++)
{
if(j >= 2) VertexKick();
m_v.RGB = r[r[0].LINE.IIP ? i : 0].RGB;
if(j == 0 || r[0].LINE.IIP) i++;
m_v.XY = r[i++].XY;
VertexKick();
}
//
return required;
}
int GPUState::PH_Sprite(GPUReg* r, int size)
{
int required = 2;
if(r[0].SPRITE.TME) required++;
if(r[0].SPRITE.SIZE == 0) required++;
if(size < required) return 0;
//
SetPrim(r);
if(r[0].SPRITE.TME)
{
SetCLUT(&r[2]);
}
//
int i = 0;
m_v.RGB = r[i++].RGB;
m_v.XY = r[i++].XY;
if(r[0].SPRITE.TME)
{
m_v.UV.X = r[i].UV.U;
m_v.UV.Y = r[i].UV.V;
i++;
}
VertexKick();
int w = 0;
int h = 0;
switch(r[0].SPRITE.SIZE)
{
case 0: w = r[i].XY.X; h = r[i].XY.Y; i++; break;
case 1: w = h = 1; break;
case 2: w = h = 8; break;
case 3: w = h = 16; break;
default: __assume(0);
}
m_v.XY.X += w;
m_v.XY.Y += h;
if(r[0].SPRITE.TME)
{
m_v.UV.X += w;
m_v.UV.Y += h;
}
VertexKick();
//
return required;
}
int GPUState::PH_Move(GPUReg* r, int size)
{
if(size < 4) return 0;
Flush();
int sx = r[1].XY.X;
int sy = r[1].XY.Y;
int dx = r[2].XY.X;
int dy = r[2].XY.Y;
int w = r[3].XY.X;
int h = r[3].XY.Y;
m_mem.MoveRect(sx, sy, dx, dy, w, h);
Invalidate(GSVector4i(dx, dy, dx + w, dy + h));
// Dump("m");
return 4;
}
int GPUState::PH_Write(GPUReg* r, int size)
{
if(size < 3) return 0;
int w = r[2].XY.X;
int h = r[2].XY.Y;
int required = 3 + ((w * h + 1) >> 1);
if(size < required) return 0;
Flush();
GSVector4i r2;
r2.left = r[1].XY.X;
r2.top = r[1].XY.Y;
r2.right = r2.left + w;
r2.bottom = r2.top + h;
m_mem.WriteRect(r2, (const uint16*)&r[3]);
Invalidate(r2);
Dump("w");
m_perfmon.Put(GSPerfMon::Swizzle, w * h * 2);
return required;
}
int GPUState::PH_Read(GPUReg* r, int size)
{
if(size < 3) return 0;
Flush();
int w = r[2].XY.X;
int h = r[2].XY.Y;
if(w > 0 && h > 0)
{
GSVector4i r2;
r2.left = r[1].XY.X;
r2.top = r[1].XY.Y;
r2.right = r2.left + w;
r2.bottom = r2.top + h;
m_read.bytes = ((w * h + 1) & ~1) * 2;
m_read.cur = 0;
m_read.Reserve(m_read.bytes);
m_mem.ReadRect(r2, (uint16*)m_read.buff);
Dump("r");
}
m_env.STATUS.IMG = 1;
return 3;
}
int GPUState::PH_Environment(GPUReg* r, int size)
{
switch(r->PACKET.OPTION)
{
case 1: // draw mode setting
if(((m_env.STATUS.u32 ^ r->MODE.u32) & 0x7ff) != 0)
{
Flush();
m_env.STATUS.TX = r->MODE.TX;
m_env.STATUS.TY = r->MODE.TY;
m_env.STATUS.ABR = r->MODE.ABR;
m_env.STATUS.TP = r->MODE.TP;
m_env.STATUS.DTD = r->MODE.DTD;
m_env.STATUS.DFE = r->MODE.DFE;
}
return 1;
case 2: // texture window setting
if(((m_env.TWIN.u32 ^ r->TWIN.u32) & 0xfffff) != 0)
{
Flush();
m_env.TWIN = r->TWIN;
}
return 1;
case 3: // set drawing area top left
if(((m_env.DRAREATL.u32 ^ r->DRAREA.u32) & 0xfffff) != 0)
{
Flush();
m_env.DRAREATL = r->DRAREA;
}
return 1;
case 4: // set drawing area bottom right
if(((m_env.DRAREABR.u32 ^ r->DRAREA.u32) & 0xfffff) != 0)
{
Flush();
m_env.DRAREABR = r->DRAREA;
}
return 1;
case 5: // drawing offset
if(((m_env.DROFF.u32 ^ r->DROFF.u32) & 0x3fffff) != 0)
{
Flush();
m_env.DROFF = r->DROFF;
}
return 1;
case 6: // mask setting
if(m_env.STATUS.MD != r->MASK.MD || m_env.STATUS.ME != r->MASK.ME)
{
Flush();
m_env.STATUS.MD = r->MASK.MD;
m_env.STATUS.ME = r->MASK.ME;
}
return 1;
}
ASSERT(0);
return 1;
}
//
GPUState::Buffer::Buffer()
{
bytes = 0;
maxbytes = 4096;
buff = (uint8*)_aligned_malloc(maxbytes, 16);
cur = 0;
}
GPUState::Buffer::~Buffer()
{
_aligned_free(buff);
}
void GPUState::Buffer::Reserve(int size)
{
if(size > maxbytes)
{
int new_maxbytes = (maxbytes + size + 1023) & ~1023;
uint8* new_buff = (uint8*)_aligned_malloc(new_maxbytes, 16);
if(buff != NULL)
{
memcpy(new_buff, buff, maxbytes);
_aligned_free(buff);
}
maxbytes = new_maxbytes;
buff = new_buff;
}
}
void GPUState::Buffer::Append(const uint8* src, int size)
{
Reserve(bytes + (int)size);
memcpy(&buff[bytes], src, size);
bytes += size;
}
void GPUState::Buffer::Remove(int size)
{
ASSERT(size <= bytes);
if(size < bytes)
{
memmove(&buff[0], &buff[size], bytes - size);
bytes -= size;
}
else
{
bytes = 0;
}
#ifdef DEBUG
memset(&buff[bytes], 0xff, maxbytes - bytes);
#endif
}
void GPUState::Buffer::RemoveAll()
{
bytes = 0;
}