mirror of https://github.com/PCSX2/pcsx2.git
433 lines
14 KiB
C++
433 lines
14 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 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 PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "GS/GS.h"
|
|
#include "GS/GSLocalMemory.h"
|
|
#include "GS/GSDrawingContext.h"
|
|
#include "GS/GSDrawingEnvironment.h"
|
|
#include "GS/Renderers/Common/GSVertex.h"
|
|
#include "GS/Renderers/Common/GSVertexTrace.h"
|
|
#include "GS/Renderers/Common/GSDevice.h"
|
|
#include "GS/GSVector.h"
|
|
#include "GSAlignedClass.h"
|
|
|
|
class GSDumpBase;
|
|
|
|
class GSState : public GSAlignedClass<32>
|
|
{
|
|
public:
|
|
GSState();
|
|
virtual ~GSState();
|
|
|
|
static constexpr int GetSaveStateSize();
|
|
|
|
private:
|
|
// RESTRICT prevents multiple loads of the same part of the register when accessing its bitfields (the compiler is happy to know that memory writes in-between will not go there)
|
|
|
|
typedef void (GSState::*GIFPackedRegHandler)(const GIFPackedReg* RESTRICT r);
|
|
|
|
GIFPackedRegHandler m_fpGIFPackedRegHandlers[16] = {};
|
|
GIFPackedRegHandler m_fpGIFPackedRegHandlerXYZ[8][4] = {};
|
|
|
|
void CheckFlushes();
|
|
|
|
void GIFPackedRegHandlerNull(const GIFPackedReg* RESTRICT r);
|
|
void GIFPackedRegHandlerRGBA(const GIFPackedReg* RESTRICT r);
|
|
void GIFPackedRegHandlerSTQ(const GIFPackedReg* RESTRICT r);
|
|
void GIFPackedRegHandlerUV(const GIFPackedReg* RESTRICT r);
|
|
void GIFPackedRegHandlerUV_Hack(const GIFPackedReg* RESTRICT r);
|
|
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFPackedRegHandlerXYZF2(const GIFPackedReg* RESTRICT r);
|
|
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFPackedRegHandlerXYZ2(const GIFPackedReg* RESTRICT r);
|
|
void GIFPackedRegHandlerFOG(const GIFPackedReg* RESTRICT r);
|
|
void GIFPackedRegHandlerA_D(const GIFPackedReg* RESTRICT r);
|
|
void GIFPackedRegHandlerNOP(const GIFPackedReg* RESTRICT r);
|
|
|
|
typedef void (GSState::*GIFRegHandler)(const GIFReg* RESTRICT r);
|
|
|
|
GIFRegHandler m_fpGIFRegHandlers[256] = {};
|
|
GIFRegHandler m_fpGIFRegHandlerXYZ[8][4] = {};
|
|
|
|
typedef void (GSState::*GIFPackedRegHandlerC)(const GIFPackedReg* RESTRICT r, u32 size);
|
|
|
|
GIFPackedRegHandlerC m_fpGIFPackedRegHandlersC[2] = {};
|
|
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZF2[8] = {};
|
|
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZ2[8] = {};
|
|
|
|
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u32 size);
|
|
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32 size);
|
|
void GIFPackedRegHandlerNOP(const GIFPackedReg* RESTRICT r, u32 size);
|
|
|
|
template<int i> void ApplyTEX0(GIFRegTEX0& TEX0);
|
|
void ApplyPRIM(u32 prim);
|
|
|
|
void GIFRegHandlerNull(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerPRIM(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerRGBAQ(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerST(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerUV(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerUV_Hack(const GIFReg* RESTRICT r);
|
|
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFRegHandlerXYZF2(const GIFReg* RESTRICT r);
|
|
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFRegHandlerXYZ2(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerTEX0(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerCLAMP(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerFOG(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerNOP(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerTEX1(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerTEX2(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerXYOFFSET(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerPRMODECONT(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerPRMODE(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerTEXCLUT(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerSCANMSK(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerMIPTBP1(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerMIPTBP2(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerTEXA(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerFOGCOL(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerTEXFLUSH(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerSCISSOR(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerALPHA(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerDIMX(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerDTHE(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerCOLCLAMP(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerTEST(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerPABE(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerFBA(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerFRAME(const GIFReg* RESTRICT r);
|
|
template<int i> void GIFRegHandlerZBUF(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerBITBLTBUF(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerTRXPOS(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerTRXREG(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r);
|
|
void GIFRegHandlerHWREG(const GIFReg* RESTRICT r);
|
|
|
|
template<bool auto_flush, bool index_swap>
|
|
void SetPrimHandlers();
|
|
|
|
struct GSTransferBuffer
|
|
{
|
|
int x = 0, y = 0;
|
|
int start = 0, end = 0, total = 0;
|
|
u8* buff = nullptr;
|
|
GIFRegBITBLTBUF m_blit = {};
|
|
bool write = false;
|
|
|
|
GSTransferBuffer();
|
|
~GSTransferBuffer();
|
|
|
|
void Init(int tx, int ty, const GIFRegBITBLTBUF& blit, bool write);
|
|
bool Update(int tw, int th, int bpp, int& len);
|
|
|
|
} m_tr;
|
|
|
|
protected:
|
|
GSVertex m_v = {};
|
|
float m_q = 1.0f;
|
|
GSVector4i m_scissor_cull_min = {};
|
|
GSVector4i m_scissor_cull_max = {};
|
|
GSVector4i m_xyof = {};
|
|
|
|
struct
|
|
{
|
|
GSVertex* buff;
|
|
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
|
|
u32 xy_tail;
|
|
GSVector4i xy[4];
|
|
GSVector4i xyhead;
|
|
} m_vertex = {};
|
|
|
|
struct
|
|
{
|
|
u16* buff;
|
|
u32 tail;
|
|
} m_index = {};
|
|
|
|
void UpdateContext();
|
|
void UpdateScissor();
|
|
|
|
void UpdateVertexKick();
|
|
|
|
void GrowVertexBuffer();
|
|
bool IsAutoFlushDraw(u32 prim);
|
|
template<u32 prim, bool index_swap>
|
|
void HandleAutoFlush();
|
|
void CLUTAutoFlush(u32 prim);
|
|
|
|
template <u32 prim, bool auto_flush, bool index_swap>
|
|
void VertexKick(u32 skip);
|
|
|
|
// following functions need m_vt to be initialized
|
|
|
|
GSVertexTrace m_vt;
|
|
GSVertexTrace::VertexAlpha& GetAlphaMinMax()
|
|
{
|
|
if (!m_vt.m_alpha.valid)
|
|
CalcAlphaMinMax(0, 500);
|
|
return m_vt.m_alpha;
|
|
}
|
|
struct TextureMinMaxResult
|
|
{
|
|
enum UsesBoundary
|
|
{
|
|
USES_BOUNDARY_LEFT = 1 << 0,
|
|
USES_BOUNDARY_TOP = 1 << 1,
|
|
USES_BOUNDARY_RIGHT = 1 << 2,
|
|
USES_BOUNDARY_BOTTOM = 1 << 3,
|
|
USES_BOUNDARY_U = USES_BOUNDARY_LEFT | USES_BOUNDARY_RIGHT,
|
|
USES_BOUNDARY_V = USES_BOUNDARY_TOP | USES_BOUNDARY_BOTTOM,
|
|
};
|
|
GSVector4i coverage; ///< Part of the texture used
|
|
u8 uses_boundary; ///< Whether or not the usage touches the left, top, right, or bottom edge (and therefore needs wrap modes preserved)
|
|
};
|
|
TextureMinMaxResult GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCLAMP CLAMP, bool linear, bool clamp_to_tsize);
|
|
bool TryAlphaTest(u32& fm, u32& zm);
|
|
bool IsOpaque();
|
|
bool IsMipMapDraw();
|
|
bool IsMipMapActive();
|
|
bool IsCoverageAlpha();
|
|
void CalcAlphaMinMax(const int tex_min, const int tex_max);
|
|
|
|
public:
|
|
struct GSUploadQueue
|
|
{
|
|
GIFRegBITBLTBUF blit;
|
|
GSVector4i rect;
|
|
int draw;
|
|
bool zero_clear;
|
|
};
|
|
|
|
GIFPath m_path[4] = {};
|
|
const GIFRegPRIM* PRIM = nullptr;
|
|
GSPrivRegSet* m_regs = nullptr;
|
|
GSLocalMemory m_mem;
|
|
GSDrawingEnvironment m_env = {};
|
|
GSDrawingEnvironment m_prev_env = {};
|
|
const GSDrawingEnvironment* m_draw_env = &m_env;
|
|
GSDrawingContext* m_context = nullptr;
|
|
GSVector4i temp_draw_rect = {};
|
|
std::unique_ptr<GSDumpBase> m_dump;
|
|
bool m_scissor_invalid = false;
|
|
bool m_nativeres = false;
|
|
bool m_mipmap = false;
|
|
bool m_texflush_flag = false;
|
|
bool m_isPackedUV_HackFlag = false;
|
|
bool m_channel_shuffle = false;
|
|
u8 m_scanmask_used = 0;
|
|
u32 m_dirty_gs_regs = 0;
|
|
int m_backed_up_ctx = 0;
|
|
std::vector<GSUploadQueue> m_draw_transfers;
|
|
|
|
static int s_n;
|
|
static int s_last_transfer_draw_n;
|
|
static int s_transfer_n;
|
|
|
|
static constexpr u32 STATE_VERSION = 8;
|
|
|
|
enum REG_DIRTY
|
|
{
|
|
DIRTY_REG_ALPHA,
|
|
DIRTY_REG_CLAMP,
|
|
DIRTY_REG_COLCLAMP,
|
|
DIRTY_REG_DIMX,
|
|
DIRTY_REG_DTHE,
|
|
DIRTY_REG_FBA,
|
|
DIRTY_REG_FOGCOL,
|
|
DIRTY_REG_FRAME,
|
|
DIRTY_REG_MIPTBP1,
|
|
DIRTY_REG_MIPTBP2,
|
|
DIRTY_REG_PABE,
|
|
DIRTY_REG_PRIM,
|
|
DIRTY_REG_SCANMSK,
|
|
DIRTY_REG_SCISSOR,
|
|
DIRTY_REG_TEST,
|
|
DIRTY_REG_TEX0,
|
|
DIRTY_REG_TEX1,
|
|
DIRTY_REG_TEXA,
|
|
DIRTY_REG_XYOFFSET,
|
|
DIRTY_REG_ZBUF
|
|
};
|
|
|
|
enum GSFlushReason
|
|
{
|
|
UNKNOWN = 1 << 0,
|
|
RESET = 1 << 1,
|
|
CONTEXTCHANGE = 1 << 2,
|
|
CLUTCHANGE = 1 << 3,
|
|
GSTRANSFER = 1 << 4,
|
|
UPLOADDIRTYTEX = 1 << 5,
|
|
UPLOADDIRTYFRAME = 1 << 6,
|
|
UPLOADDIRTYZBUF = 1 << 7,
|
|
LOCALTOLOCALMOVE = 1 << 8,
|
|
DOWNLOADFIFO = 1 << 9,
|
|
SAVESTATE = 1 << 10,
|
|
LOADSTATE = 1 << 11,
|
|
AUTOFLUSH = 1 << 12,
|
|
VSYNC = 1 << 13,
|
|
GSREOPEN = 1 << 14,
|
|
VERTEXCOUNT = 1 << 15,
|
|
};
|
|
|
|
GSFlushReason m_state_flush_reason = UNKNOWN;
|
|
|
|
enum PRIM_OVERLAP
|
|
{
|
|
PRIM_OVERLAP_UNKNOW,
|
|
PRIM_OVERLAP_YES,
|
|
PRIM_OVERLAP_NO
|
|
};
|
|
|
|
PRIM_OVERLAP m_prim_overlap = PRIM_OVERLAP_UNKNOW;
|
|
std::vector<size_t> m_drawlist;
|
|
|
|
struct GSPCRTCRegs
|
|
{
|
|
struct PCRTCDisplay
|
|
{
|
|
bool enabled;
|
|
int FBP;
|
|
int FBW;
|
|
int PSM;
|
|
GSRegDISPFB prevFramebufferReg;
|
|
GSVector2i prevDisplayOffset;
|
|
GSVector2i displayOffset;
|
|
GSVector4i displayRect;
|
|
GSVector2i magnification;
|
|
GSVector2i prevFramebufferOffsets;
|
|
GSVector2i framebufferOffsets;
|
|
GSVector4i framebufferRect;
|
|
|
|
__fi int Block() const { return FBP << 5; }
|
|
};
|
|
|
|
int videomode = 0;
|
|
int interlaced = 0;
|
|
int FFMD = 0;
|
|
bool PCRTCSameSrc = false;
|
|
bool toggling_field = false;
|
|
PCRTCDisplay PCRTCDisplays[2] = {};
|
|
|
|
bool IsAnalogue();
|
|
|
|
// Calculates which display is closest to matching zero offsets in either direction.
|
|
GSVector2i NearestToZeroOffset();
|
|
|
|
void SetVideoMode(GSVideoMode videoModeIn);
|
|
|
|
// Enable each of the displays.
|
|
void EnableDisplays(GSRegPMODE pmode, GSRegSMODE2 smode2, bool smodetoggle);
|
|
|
|
void CheckSameSource();
|
|
|
|
bool FrameWrap();
|
|
|
|
// If the start point of both frames match, we can do a single read
|
|
bool FrameRectMatch();
|
|
|
|
GSVector2i GetResolution();
|
|
|
|
GSVector4i GetFramebufferRect(int display);
|
|
|
|
int GetFramebufferBitDepth();
|
|
|
|
GSVector2i GetFramebufferSize(int display);
|
|
|
|
// Sets up the rectangles for both the framebuffer read and the displays for the merge circuit.
|
|
void SetRects(int display, GSRegDISPLAY displayReg, GSRegDISPFB framebufferReg);
|
|
|
|
// Calculate framebuffer read offsets, should be considered if only one circuit is enabled, or difference is more than 1 line.
|
|
// Only considered if "Anti-blur" is enabled.
|
|
void CalculateFramebufferOffset(bool scanmask);
|
|
|
|
// Used in software mode to align the buffer when reading. Offset is accounted for (block aligned) by GetOutput.
|
|
void RemoveFramebufferOffset(int display);
|
|
|
|
// If the two displays are offset from each other, move them to the correct offsets.
|
|
// If using screen offsets, calculate the positions here.
|
|
void CalculateDisplayOffset(bool scanmask);
|
|
} PCRTCDisplays;
|
|
|
|
public:
|
|
/// Returns the appropriate directory for draw dumping.
|
|
static std::string GetDrawDumpPath(const char* format, ...);
|
|
|
|
/// Expands dither matrix, suitable for software renderer.
|
|
static void ExpandDIMX(GSVector4i* dimx, const GIFRegDIMX DIMX);
|
|
|
|
/// Returns a string representing the flush reason.
|
|
static const char* GetFlushReasonString(GSFlushReason reason);
|
|
|
|
void ResetHandlers();
|
|
void ResetPCRTC();
|
|
|
|
GSVideoMode GetVideoMode();
|
|
|
|
bool isinterlaced();
|
|
bool isReallyInterlaced();
|
|
|
|
float GetTvRefreshRate();
|
|
|
|
virtual void Reset(bool hardware_reset);
|
|
virtual void UpdateSettings(const Pcsx2Config::GSOptions& old_config);
|
|
|
|
void Flush(GSFlushReason reason);
|
|
void FlushPrim();
|
|
bool TestDrawChanged();
|
|
void FlushWrite();
|
|
virtual void Draw() = 0;
|
|
virtual void PurgeTextureCache();
|
|
virtual void ReadbackTextureCache();
|
|
virtual void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) {}
|
|
virtual void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) {}
|
|
|
|
virtual void Move();
|
|
|
|
GSVector4i GetTEX0Rect();
|
|
void CheckWriteOverlap(bool req_write, bool req_read);
|
|
void Write(const u8* mem, int len);
|
|
void Read(u8* mem, int len);
|
|
void InitReadFIFO(u8* mem, int len);
|
|
|
|
void SoftReset(u32 mask);
|
|
void WriteCSR(u32 csr) { m_regs->CSR.U32[1] = csr; }
|
|
void ReadFIFO(u8* mem, int size);
|
|
void ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF, GIFRegTRXPOS TRXPOS, GIFRegTRXREG TRXREG);
|
|
template<int index> void Transfer(const u8* mem, u32 size);
|
|
int Freeze(freezeData* fd, bool sizeonly);
|
|
int Defrost(const freezeData* fd);
|
|
|
|
u8* GetRegsMem() const { return reinterpret_cast<u8*>(m_regs); }
|
|
void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); }
|
|
|
|
void DumpVertices(const std::string& filename);
|
|
|
|
bool TrianglesAreQuads() const;
|
|
PRIM_OVERLAP PrimitiveOverlap();
|
|
GIFRegTEX0 GetTex0Layer(u32 lod);
|
|
};
|
|
|
|
// We put this in the header because of Multi-ISA.
|
|
inline void GSState::ExpandDIMX(GSVector4i* dimx, const GIFRegDIMX DIMX)
|
|
{
|
|
dimx[1] = GSVector4i(DIMX.DM00, 0, DIMX.DM01, 0, DIMX.DM02, 0, DIMX.DM03, 0);
|
|
dimx[0] = dimx[1].xxzzlh();
|
|
dimx[3] = GSVector4i(DIMX.DM10, 0, DIMX.DM11, 0, DIMX.DM12, 0, DIMX.DM13, 0);
|
|
dimx[2] = dimx[3].xxzzlh();
|
|
dimx[5] = GSVector4i(DIMX.DM20, 0, DIMX.DM21, 0, DIMX.DM22, 0, DIMX.DM23, 0);
|
|
dimx[4] = dimx[5].xxzzlh();
|
|
dimx[7] = GSVector4i(DIMX.DM30, 0, DIMX.DM31, 0, DIMX.DM32, 0, DIMX.DM33, 0);
|
|
dimx[6] = dimx[7].xxzzlh();
|
|
}
|