pcsx2/pcsx2/GS/GSState.h

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();
}