mirror of https://github.com/PCSX2/pcsx2.git
791 lines
29 KiB
C++
791 lines
29 KiB
C++
/* ZeroGS KOSMOS
|
|
* Copyright (C) 2005-2006 zerofrog@gmail.com
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#ifndef __ZZOGL_MEM_H__
|
|
#define __ZZOGL_MEM_H__
|
|
|
|
#include <assert.h>
|
|
#include <vector>
|
|
#include "GS.h"
|
|
#include "Util.h"
|
|
#include "Mem.h"
|
|
|
|
#ifndef ZZNORMAL_MEMORY
|
|
|
|
extern u32 g_blockTable32[4][8];
|
|
extern u32 g_blockTable32Z[4][8];
|
|
extern u32 g_blockTable16[8][4];
|
|
extern u32 g_blockTable16S[8][4];
|
|
|
|
extern u32 g_blockTable16Z[8][4];
|
|
|
|
extern u32 g_blockTable16SZ[8][4];
|
|
|
|
extern u32 g_blockTable8[4][8];
|
|
extern u32 g_blockTable4[8][4];
|
|
|
|
extern u32 g_columnTable32[8][8];
|
|
extern u32 g_columnTable16[8][16];
|
|
extern u32 g_columnTable8[16][16];
|
|
extern u32 g_columnTable4[16][32];
|
|
|
|
//--
|
|
|
|
extern u32 g_pageTable32[32][64];
|
|
extern u32 g_pageTable32Z[32][64];
|
|
extern u32 g_pageTable16[64][64];
|
|
extern u32 g_pageTable16S[64][64];
|
|
extern u32 g_pageTable16Z[64][64];
|
|
extern u32 g_pageTable16SZ[64][64];
|
|
extern u32 g_pageTable8[64][128];
|
|
extern u32 g_pageTable4[128][128];
|
|
|
|
|
|
//maximum PSM is 58, so our arrays have 58 + 1 = 59 elements
|
|
|
|
// This table is used for fast access to memory storage data.
|
|
extern u32 ZZ_DT[MAX_PSM][TABLE_WIDTH];
|
|
|
|
|
|
//maxium PSM is 58, so our arrays have 58 + 1 = 59 elements
|
|
extern u32** g_pageTable[MAX_PSM];
|
|
extern u32** g_blockTable[MAX_PSM];
|
|
extern u32** g_columnTable[MAX_PSM];
|
|
extern u32 g_pageTable2[MAX_PSM][127][127];
|
|
extern u32** g_pageTableNew[MAX_PSM];
|
|
|
|
// rest not visible externally
|
|
struct BLOCK
|
|
{
|
|
BLOCK() { memset(this, 0, sizeof(BLOCK)); }
|
|
|
|
// shader constants for this block
|
|
float4 vTexBlock;
|
|
float4 vTexDims;
|
|
int width, height; // dims of one page in pixels
|
|
int bpp;
|
|
int colwidth, colheight;
|
|
u32** pageTable; // offset inside each page
|
|
u32** blockTable;
|
|
u32** columnTable;
|
|
|
|
// Nobody use this, so we better remove it.
|
|
// u32 (*getPixelAddress)(int x, int y, u32 bp, u32 bw);
|
|
// u32 (*getPixelAddress_0)(int x, int y, u32 bw);
|
|
// void (*writePixel)(void* pmem, int x, int y, u32 pixel, u32 bp, u32 bw);
|
|
// void (*writePixel_0)(void* pmem, int x, int y, u32 pixel, u32 bw);
|
|
// u32 (*readPixel)(const void* pmem, int x, int y, u32 bp, u32 bw);
|
|
// u32 (*readPixel_0)(const void* pmem, int x, int y, u32 bw);
|
|
int (*TransferHostLocal)(const void* pbyMem, u32 nQWordSize);
|
|
void (*TransferLocalHost)(void* pbyMem, u32 nQWordSize);
|
|
|
|
// texture must be of dims BLOCK_TEXWIDTH and BLOCK_TEXHEIGHT
|
|
static void FillBlocks(std::vector<char>& vBlockData, std::vector<char>& vBilinearData, int floatfmt);
|
|
};
|
|
|
|
void FillBlockTables();
|
|
void DestroyBlockTables();
|
|
void FillNewPageTable();
|
|
|
|
extern BLOCK m_Blocks[];
|
|
|
|
extern u32 g_blockTable32[4][8];
|
|
extern u32 g_blockTable32Z[4][8];
|
|
extern u32 g_blockTable16[8][4];
|
|
extern u32 g_blockTable16S[8][4];
|
|
extern u32 g_blockTable16Z[8][4];
|
|
extern u32 g_blockTable16SZ[8][4];
|
|
extern u32 g_blockTable8[4][8];
|
|
extern u32 g_blockTable4[8][4];
|
|
|
|
extern u32 g_columnTable32[8][8];
|
|
extern u32 g_columnTable16[8][16];
|
|
extern u32 g_columnTable8[16][16];
|
|
extern u32 g_columnTable4[16][32];
|
|
|
|
extern u32 g_pageTable32[32][64];
|
|
extern u32 g_pageTable32Z[32][64];
|
|
extern u32 g_pageTable16[64][64];
|
|
extern u32 g_pageTable16S[64][64];
|
|
extern u32 g_pageTable16Z[64][64];
|
|
extern u32 g_pageTable16SZ[64][64];
|
|
extern u32 g_pageTable8[64][128];
|
|
extern u32 g_pageTable4[128][128];
|
|
|
|
|
|
extern u32** g_pageTable[MAX_PSM];
|
|
extern u32** g_blockTable[MAX_PSM];
|
|
extern u32** g_columnTable[MAX_PSM];
|
|
extern u32 ZZ_DT[MAX_PSM][TABLE_WIDTH];
|
|
extern u32** g_pageTableNew[MAX_PSM];
|
|
|
|
static __forceinline void MaskedOR(u32* dst, u32 pixel, u32 mask = 0xffffffff) {
|
|
if (mask == 0xffffffff)
|
|
*dst = pixel;
|
|
else
|
|
*dst = (*dst & (~mask)) | (pixel & mask);
|
|
}
|
|
|
|
// This two defines seems like idiotic code, but in reality it have one, but big importance -- this code
|
|
// made psm variable (and psm2 in second case) -- constant, so optimiser could properly pass proper function
|
|
#define PSM_SWITCHCASE(X) { \
|
|
switch (psm) { \
|
|
case PSMCT32: { \
|
|
const int psmC = PSMCT32; \
|
|
X; } \
|
|
break; \
|
|
case PSMT32Z: { \
|
|
const int psmC = PSMT32Z; \
|
|
X; } \
|
|
break; \
|
|
case PSMCT24: { \
|
|
const int psmC = PSMCT24; \
|
|
X; } \
|
|
break; \
|
|
case PSMT24Z: { \
|
|
const int psmC = PSMT24Z; \
|
|
X; } \
|
|
break; \
|
|
case PSMCT16: { \
|
|
const int psmC = PSMCT16; \
|
|
X; } \
|
|
break; \
|
|
case PSMCT16S: { \
|
|
const int psmC = PSMCT16S; \
|
|
X; } \
|
|
break; \
|
|
case PSMT16Z: { \
|
|
const int psmC = PSMT16Z; \
|
|
X; } \
|
|
break; \
|
|
case PSMT16SZ: { \
|
|
const int psmC = PSMT16SZ; \
|
|
X; } \
|
|
break; \
|
|
case PSMT8: { \
|
|
const int psmC = PSMT8; \
|
|
X; } \
|
|
break; \
|
|
case PSMT8H: { \
|
|
const int psmC = PSMT8H; \
|
|
X; } \
|
|
break; \
|
|
case PSMT4HH: { \
|
|
const int psmC = PSMT4HH; \
|
|
X; } \
|
|
break; \
|
|
case PSMT4HL: { \
|
|
const int psmC = PSMT4HL; \
|
|
X; } \
|
|
break; \
|
|
case PSMT4: { \
|
|
const int psmC = PSMT4; \
|
|
X; } \
|
|
break; \
|
|
}\
|
|
}
|
|
|
|
#define PSM_SWITCHCASE_2(X) { \
|
|
switch (psm) { \
|
|
case PSMCT32: \
|
|
if( psm2 == PSMCT32 ) { const int psmC = PSMCT32, psmC1 = PSMCT32; X; } \
|
|
else { const int psmC = PSMCT32, psmC1 = PSMT32Z; X; } \
|
|
break; \
|
|
case PSMCT24: \
|
|
if( psm2 == PSMCT24 ) { const int psmC = PSMCT24, psmC1 = PSMCT24; X; } \
|
|
else { const int psmC = PSMCT24, psmC1 = PSMT24Z; X; } \
|
|
break; \
|
|
case PSMT32Z: \
|
|
if( psm2 == PSMT32Z ) { const int psmC = PSMT32Z, psmC1 = PSMCT32; X; } \
|
|
else { const int psmC = PSMT32Z, psmC1 = PSMT32Z; X; } \
|
|
break; \
|
|
case PSMT24Z: \
|
|
if( psm2 == PSMCT24 ) { const int psmC = PSMT24Z, psmC1 = PSMCT24; X; } \
|
|
else { const int psmC = PSMT24Z, psmC1 = PSMT24Z; X; } \
|
|
break; \
|
|
case PSMCT16: \
|
|
switch(psm2) { \
|
|
case PSMCT16: { const int psmC = PSMCT16, psmC1 = PSMCT16; X; } break; \
|
|
case PSMCT16S: { const int psmC = PSMCT16, psmC1 = PSMCT16S; X; } break; \
|
|
case PSMT16Z: { const int psmC = PSMCT16, psmC1 = PSMT16Z; X; } break; \
|
|
case PSMT16SZ: { const int psmC = PSMCT16, psmC1 = PSMT16SZ; X; } break; \
|
|
} \
|
|
break; \
|
|
case PSMCT16S: \
|
|
switch(psm2) { \
|
|
case PSMCT16: { const int psmC = PSMCT16S, psmC1 = PSMCT16; X; } break; \
|
|
case PSMCT16S: { const int psmC = PSMCT16S, psmC1 = PSMCT16S; X; } break; \
|
|
case PSMT16Z: { const int psmC = PSMCT16S, psmC1 = PSMT16Z; X; } break; \
|
|
case PSMT16SZ: { const int psmC = PSMCT16S, psmC1 = PSMT16SZ; X; } break; \
|
|
} \
|
|
break; \
|
|
case PSMT16Z: \
|
|
switch(psm2) { \
|
|
case PSMCT16: { const int psmC = PSMT16Z, psmC1 = PSMCT16; X; } break; \
|
|
case PSMCT16S: { const int psmC = PSMT16Z, psmC1 = PSMCT16S; X; } break; \
|
|
case PSMT16Z: { const int psmC = PSMT16Z, psmC1 = PSMT16Z; X; } break; \
|
|
case PSMT16SZ: { const int psmC = PSMT16Z, psmC1 = PSMT16SZ; X; } break; \
|
|
} \
|
|
break; \
|
|
case PSMT16SZ: \
|
|
switch(psm2) { \
|
|
case PSMCT16: { const int psmC = PSMT16SZ, psmC1 = PSMCT16; X; } break; \
|
|
case PSMCT16S: { const int psmC = PSMT16SZ, psmC1 = PSMCT16S; X; } break; \
|
|
case PSMT16Z: { const int psmC = PSMT16SZ, psmC1 = PSMT16Z; X; } break; \
|
|
case PSMT16SZ: { const int psmC = PSMT16SZ, psmC1 = PSMT16SZ; X; } break; \
|
|
} \
|
|
break; \
|
|
case PSMT8: \
|
|
if( psm2 == PSMT8 ) { const int psmC = PSMT8, psmC1 = PSMT8; X; } \
|
|
else { const int psmC = PSMT8, psmC1 = PSMT8H; X; } \
|
|
break; \
|
|
case PSMT8H: \
|
|
if( psm2 == PSMT8H ) { const int psmC = PSMT8H, psmC1 = PSMT8; X; } \
|
|
else { const int psmC = PSMT8H, psmC1 = PSMT8H; X; } \
|
|
break; \
|
|
case PSMT4: \
|
|
switch(psm2) { \
|
|
case PSMT4: { const int psmC = PSMT4, psmC1 = PSMT4; X; } break; \
|
|
case PSMT4HL: { const int psmC = PSMT4, psmC1 = PSMT4HL; X; } break; \
|
|
case PSMT4HH: { const int psmC = PSMT4, psmC1 = PSMT4HH; X; } break; \
|
|
} \
|
|
break; \
|
|
case PSMT4HL: \
|
|
switch(psm2) { \
|
|
case PSMT4: { const int psmC = PSMT4HL, psmC1 = PSMT4; X; } break; \
|
|
case PSMT4HL: { const int psmC = PSMT4HL, psmC1 = PSMT4HL; X; } break; \
|
|
case PSMT4HH: { const int psmC = PSMT4HL, psmC1 = PSMT4HH; X; } break; \
|
|
} \
|
|
break; \
|
|
case PSMT4HH: \
|
|
switch(psm2) { \
|
|
case PSMT4: { const int psmC = PSMT4HH, psmC1 = PSMT4; X; } break; \
|
|
case PSMT4HL: { const int psmC = PSMT4HH, psmC1 = PSMT4HL; X; } break; \
|
|
case PSMT4HH: { const int psmC = PSMT4HH, psmC1 = PSMT4HH; X; } break; \
|
|
} \
|
|
break; \
|
|
} \
|
|
}
|
|
|
|
template <int psm>
|
|
static __forceinline void setPsmtConstantsX(u8& A, u8& B, u8& C, u8& D, u8& E, u8& F, u32& G, u8& H) {
|
|
switch (psm) {
|
|
case PSMCT32:
|
|
case PSMT32Z:
|
|
A = 5; B = 6; C = 0; D = 31; E = 63; F = 0; H = 1; G = 0xffffffff;
|
|
break;
|
|
|
|
case PSMCT24:
|
|
case PSMT24Z:
|
|
A = 5; B = 6; C = 0; D = 31; E = 63; F = 0; H = 1; G = 0xffffff;
|
|
break;
|
|
|
|
case PSMT8H:
|
|
A = 5; B = 6; C = 0; D = 31; E = 63; F = 24; H = 4; G = 0xff;
|
|
break;
|
|
|
|
case PSMT4HH:
|
|
A = 5; B = 6; C = 0; D = 31; E = 63; F = 28; H = 8; G = 0xf;
|
|
break;
|
|
|
|
case PSMT4HL:
|
|
A = 5; B = 6; C = 0; D = 31; E = 63; F = 24; H = 8; G = 0xf;
|
|
break;
|
|
|
|
case PSMCT16:
|
|
case PSMT16Z:
|
|
case PSMCT16S:
|
|
case PSMT16SZ:
|
|
A = 6; B = 6; C = 1; D = 63; E = 63; F = 0; H = 2; G = 0xffff;
|
|
break;
|
|
|
|
case PSMT8:
|
|
A = 6; B = 7; C = 2; D = 63; E = 127; F = 0; H = 4; G = 0xff;
|
|
break;
|
|
|
|
case PSMT4:
|
|
A = 7; B = 7; C = 3; D = 127; E = 127; F = 0; H = 8; G = 0xf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// This is where the NEW_CODE define used to be.
|
|
|
|
// ------------------------------------------ get Address functions ------------------------------------
|
|
// Yes, only 1 function to all cases of life!
|
|
// Warning! We switch bp and bw for usage of default value, so be warned! It's
|
|
// not C, it's C++, so not it.
|
|
template <int psm>
|
|
static __forceinline u32 getPixelAddress(int x, int y, u32 bw, u32 bp = 0) {
|
|
u32 basepage;
|
|
u32 word;
|
|
|
|
u8 A = 0, B = 0, C = 0, D = 0, E = 0, F = 0; u32 G = 0; u8 H= 0;
|
|
setPsmtConstantsX<psm>(A, B, C, D, E, F, G, H);
|
|
basepage = ((y>>A) * (bw>>B)) + (x>>B);
|
|
word = ((bp * 64 + basepage * 2048) << C) + g_pageTable[psm][y&D][x&E];
|
|
|
|
return word;
|
|
}
|
|
|
|
// It's Zerofrog's function. I need to eliminate them all! All access should be 32-bit aligned.
|
|
static __forceinline u32 getPixelAddress(int psm, int x, int y, u32 bw, u32 bp = 0) {
|
|
PSM_SWITCHCASE(return getPixelAddress<psmC>(x, y, bw, bp) ;)
|
|
return 0;
|
|
}
|
|
|
|
// This is compatibility code, for reference,
|
|
#define Def_getPixelAddress(psmT, psmX) \
|
|
static __forceinline u32 getPixelAddress##psmT(int x, int y, u32 bp, u32 bw) { \
|
|
return getPixelAddress<psmX>(x, y, bw, bp); } \
|
|
static __forceinline u32 getPixelAddress##psmT##_0(int x, int y, u32 bw) { \
|
|
return getPixelAddress<psmX>(x, y, bw); } \
|
|
|
|
Def_getPixelAddress(32, PSMCT32)
|
|
Def_getPixelAddress(16, PSMCT16)
|
|
Def_getPixelAddress(16S, PSMCT16S)
|
|
Def_getPixelAddress(8, PSMT8)
|
|
Def_getPixelAddress(4, PSMT4)
|
|
Def_getPixelAddress(32Z, PSMT32Z)
|
|
Def_getPixelAddress(16Z, PSMT16Z)
|
|
Def_getPixelAddress(16SZ, PSMT16SZ)
|
|
|
|
#define getPixelAddress24 getPixelAddress32
|
|
#define getPixelAddress24_0 getPixelAddress32_0
|
|
#define getPixelAddress8H getPixelAddress32
|
|
#define getPixelAddress8H_0 getPixelAddress32_0
|
|
#define getPixelAddress4HL getPixelAddress32
|
|
#define getPixelAddress4HL_0 getPixelAddress32_0
|
|
#define getPixelAddress4HH getPixelAddress32
|
|
#define getPixelAddress4HH_0 getPixelAddress32_0
|
|
#define getPixelAddress24Z getPixelAddress32Z
|
|
#define getPixelAddress24Z_0 getPixelAddress32Z_0
|
|
|
|
// Check FFX-1 (very begining) for PSMT8
|
|
// Check Tekken menu for PSMT4
|
|
// ZZ_DT[7] is needed only for PSMT8H, PSMT4HL and PSMT4HH -- at this case word contain data not from a begining.
|
|
|
|
// This function return shift from 32-bit aligned address and shift -- number of byte in u32 order.
|
|
// so if ((u32*)mem + getPixelAddress_Aligned32) is exact location of u32, where our pixel data stored.
|
|
// Just for remember:
|
|
// PMSCT32, 24, 32Z, 24Z, 8HH, 4HL and 4HH have ZZ_DT[psm] == 3, so shift is always 0.
|
|
// PSMCT16, 16S, 16SZ, 16Z have ZZ_DT[psm] == 2, so shift is 0 or 16.
|
|
// PSMT8 ZZ_DT[psm] == 1, shift is 0, 8, 16, 24
|
|
// PSMT4 ZZ_DT[psm] == 0, shift is 0, 4, 8, 12, 16, 20, 24, 28.
|
|
|
|
// It allow us to made a fast access to pixels in the same basepage: if x % N == 0 (N = 1, 2, 4, 8, .. 64)
|
|
// than we could guarantee that all pixels form x to x + N - 1 are in the same basepage.
|
|
template <int psm>
|
|
static __forceinline u32* getPixelBasepage(const void* pmem, int x, int y, u32 bw, u32 bp = 0) {
|
|
u32 basepage;
|
|
u8 A = 0, B = 0, C = 0 , D = 0, E = 0, F = 0; u32 G = 0; u8 H = 0;
|
|
setPsmtConstantsX<psm> (A, B, C, D, E, F, G, H);
|
|
basepage = ((y>>A) * (bw>>B)) + (x>>B);
|
|
return ((u32*)pmem + (bp * 64 + basepage * 2048));
|
|
}
|
|
|
|
// And this is offset for this pixels.
|
|
template <int psm>
|
|
static __forceinline u32* getPixelOffset(u32& mask, u32& shift, const void* pmem, int x, int y) {
|
|
u32 word;
|
|
|
|
u8 A = 0, B = 0, C = 0 , D = 0, E = 0, F = 0; u32 G = 0; u8 H = 0;
|
|
setPsmtConstantsX<psm> (A, B, C, D, E, F, G, H);
|
|
|
|
word = (g_pageTable[psm][y&D][x&E] << (3 - C));
|
|
shift = ((word & 0x7) << 2) + F;
|
|
mask &= G << shift;
|
|
|
|
return ((u32*)pmem + ((word & ~0x7) >> 3));
|
|
}
|
|
|
|
|
|
template <int psm>
|
|
static __forceinline u32* getPixelAddress_A32(u32& mask, u32& shift, const void* pmem, int x, int y, u32 bw, u32 bp = 0) {
|
|
return getPixelOffset<psm>(mask, shift, getPixelBasepage<psm>(pmem, x, y, bw, bp), x, y);
|
|
|
|
}
|
|
|
|
template <int psm>
|
|
static __forceinline u32* getPixelBaseAddress_A32(const void* pmem, int x, int y, u32 bw, u32 bp = 0) {
|
|
u32 word;
|
|
|
|
u8 A = 0, B = 0, C = 0 , D = 0, E = 0, F = 0; u32 G = 0; u8 H = 0;
|
|
setPsmtConstantsX<psm> (A, B, C, D, E, F, G, H);
|
|
|
|
word = (g_pageTable[psm][y&D][x&E] << (3 - C));
|
|
return ((u32*)getPixelBasepage<psm>(pmem, x, y, bw, bp) + ((word & ~0x7) >> 3));
|
|
}
|
|
|
|
// Wrapper for cases, where psm is not constant, should be avoided inside cycles
|
|
static __forceinline u32* getPixelAddress_A32(u32& mask, u32& shift, int psm, const void* pmem, int x, int y, u32 bw, u32 bp = 0) {
|
|
PSM_SWITCHCASE( return getPixelAddress_A32<psmC>(mask, shift, pmem, x, y, bw, bp) );
|
|
return 0;
|
|
}
|
|
|
|
static __forceinline u32* getClutAddress(u8* pmem, const tex0Info& tex0) {
|
|
if (PSMT_ISHALF(tex0.cpsm))
|
|
return (u32*)(pmem + 64 * (tex0.csa & 15) + (tex0.csa >= 16 ? 2 : 0) );
|
|
else
|
|
return (u32*)(pmem + 64 * (tex0.csa & 15));
|
|
}
|
|
|
|
//--------------------------------------------- Write Pixel -----------------------------------------------------------
|
|
// Set proper mask for transfering multiple bytes per word.
|
|
template <int psm>
|
|
inline u32 HandleWritemask(u32 Writemask) {
|
|
u8 G = PSM_BITS_PER_PIXEL<psm>();
|
|
u32 dmask = Writemask & ((1 << G) - 1); // drop all bits in writemask, that could not be used
|
|
u32 mask;
|
|
|
|
switch (psm) {
|
|
case PSMT8H: // modes with non-zero start bit should be handled differently
|
|
return 0xff000000;
|
|
case PSMT4HL:
|
|
return 0x0f000000;
|
|
case PSMT4HH:
|
|
return 0xf0000000;
|
|
default:
|
|
mask = dmask; // 32 targets and lower
|
|
|
|
if (G < 24) {
|
|
mask |= dmask << G; // 16 targets and lower
|
|
if (G < 16) {
|
|
mask |= dmask << (2 * G); // 8 targets and lower
|
|
mask |= dmask << (3 * G);
|
|
if (G < 8) {
|
|
mask |= dmask << (4 * G); // 4 targets
|
|
mask |= dmask << (5 * G);
|
|
mask |= dmask << (6 * G);
|
|
mask |= dmask << (7 * G);
|
|
}}}
|
|
return mask;
|
|
}
|
|
}
|
|
|
|
//push pixel data at position x,y, according psm storage format. pixel do not need to be properly masked, wrong bit's would not be used
|
|
//mask should be made according PSM.
|
|
template <int psm>
|
|
static __forceinline void writePixel(void* pmem, int x, int y, u32 pixel, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
u32 shift;
|
|
u32* p = getPixelAddress_A32<psm>(mask, shift, pmem, x, y, bw, bp);
|
|
|
|
MaskedOR (p, pixel << shift, mask);
|
|
}
|
|
|
|
static __forceinline void writePixel(int psm, void* pmem, int x, int y, u32 pixel, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
PSM_SWITCHCASE(writePixel<psmC>(pmem, x, y, pixel, bw, bp, mask));
|
|
}
|
|
|
|
// Put pixel data from memory. Pixel is p, memory start from pixel, and we should count pmove words and shift resulting word to shift
|
|
// 24 targets could be outside of 32-bit borders.
|
|
template <int psm>
|
|
static __forceinline void pushPixelMem(u32* p, u32* pixel, int pmove, int shift, u32 mask = 0xffffffff) {
|
|
if (psm != PSMCT24 || psm != PSMT24Z) {
|
|
if (shift > 0)
|
|
MaskedOR (p, (*(pixel + pmove)) << (shift), mask);
|
|
else
|
|
MaskedOR (p, (*(pixel + pmove)) >> (-shift), mask);
|
|
}
|
|
else { // for 24 and 24Z psm data could be not-aligned by 32. Merde!
|
|
u64 pixel64 = (*(u64*)(pixel + pmove) ) >> (-shift); // we read more data, but for 24 targets shift always negative and resulting data is u32
|
|
MaskedOR(p, (u32)pixel64, mask); // drop upper part, we don't need it. all data is stored in lower part of u64 after shift
|
|
|
|
// MaskedOR(p, (u32)((u8*)pixel + count * 3), mask);
|
|
}
|
|
}
|
|
|
|
// use it if pixel already shifted by needed number of bytes.
|
|
// offseted mean that we should skip basepage calculation, pmem is link to basepage'ed memory. Just a little quicker.
|
|
template <int psm, int offseted>
|
|
static __forceinline void writePixelMem(const void* pmem, int x, int y, u32* pixel, int count, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
u32 shift;
|
|
u32* p;
|
|
|
|
if (offseted)
|
|
p = getPixelOffset<psm>(mask, shift, pmem, x, y);
|
|
else
|
|
p = getPixelAddress_A32<psm>(mask, shift, pmem, x, y, bw, bp);
|
|
|
|
int A = PSM_BITS_PER_PIXEL<psm>();
|
|
|
|
int pmove = (count * A) >> 5;
|
|
int pshift = (count * A) & 31; // we assume, that if shift outside word, than user want next pixel data
|
|
|
|
pushPixelMem<psm>(p, pixel, pmove, (int)shift - pshift, mask);
|
|
}
|
|
|
|
|
|
// This function push several pixels. Note, that for 32, 24, 8HH, 4HL, 4HH it's simply write (and pixel should not be properly masked), 16 do push 2 pixels (and x should be even).
|
|
// 8 push 4 pixels: 0,0; 0,1; 1,0 and 1,1. 4 push 8: 0,0; 0,1; 1,0; 1,1; 2,0, 2,1; 3,0; 3,1.
|
|
template <int psm>
|
|
static __forceinline void writePixelWord(const void* pmem, int x, int y, u32 pixel, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
u32 maskA = mask, shift;
|
|
u32* p = getPixelAddress_A32<psm>(maskA, shift, pmem, x, y, bw, bp);
|
|
|
|
/* if (PSM_NON_FULL_WORD<psm>())
|
|
maskA = maskA & mask;
|
|
else
|
|
maskA = mask;*/
|
|
|
|
MaskedOR (p, pixel, mask);
|
|
}
|
|
|
|
// ------------------------------------- Read Pixel ---------------------------------------
|
|
template <int psm>
|
|
static __forceinline u32 readPixel(const void* pmem, int x, int y, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
u32 shift;
|
|
u32* p = getPixelAddress_A32<psm>(mask, shift, pmem, x, y, bw, bp);
|
|
|
|
return ((*p & mask) >> shift);
|
|
}
|
|
|
|
static __forceinline u32 readPixel(int psm, const void* pmem, int x, int y, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
PSM_SWITCHCASE(return readPixel<psmC>(pmem, x, y, bw, bp, mask););
|
|
return 0;
|
|
}
|
|
|
|
template <int psm>
|
|
static __forceinline u32 readPixelWord(const void* pmem, int x, int y, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
u32 maskA = 0xffffffff, shift;
|
|
if (PSM_NON_FULL_WORD<psm>())
|
|
return *getPixelAddress_A32<psm>(mask, shift, pmem, x, y, bw, bp) & mask;
|
|
else
|
|
return *getPixelAddress_A32<psm>(maskA, shift, pmem, x, y, bw, bp) & mask;
|
|
}
|
|
|
|
template <int psm>
|
|
static __forceinline void fillMemoryFromPixels(u32* dst, const void* pmem, int& count, int x, int y, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
u32 pixel;
|
|
|
|
u8 I = PSM_BITS_PER_PIXEL<psm>();
|
|
int K = count / PSM_PIXELS_STORED_PER_WORD<psm>(); // offset for pmem, count for 32, count / 2 for 16, etc.
|
|
|
|
pixel = readPixel<psm>(pmem, x, y, bw, bp, mask); // I prefer not to use for here. It's slow
|
|
if (I < 32) {
|
|
pixel += readPixel<psm>(pmem, x + 1, y, bw, bp, mask) << I;
|
|
if (I < 16) { // 8 and 4 targets
|
|
pixel += readPixel<psm>(pmem, x + 2, y, bw, bp, mask) << (2 * I);
|
|
pixel += readPixel<psm>(pmem, x + 3, y, bw, bp, mask) << (3 * I);
|
|
if (I < 8) { // This is for 4, 4HH and 4HL
|
|
pixel += readPixel<psm>(pmem, x + 4, y, bw, bp, mask) << (4 * I);
|
|
pixel += readPixel<psm>(pmem, x + 5, y, bw, bp, mask) << (5 * I);
|
|
pixel += readPixel<psm>(pmem, x + 6, y, bw, bp, mask) << (6 * I);
|
|
pixel += readPixel<psm>(pmem, x + 7, y, bw, bp, mask) << (7 * I);
|
|
}}}
|
|
|
|
if (I != 24) {
|
|
*(dst + K) = pixel;
|
|
}
|
|
else { // 24. should have special care.
|
|
// ERROR_LOG("special care %d\n", count);
|
|
MaskedOR((u32*)((u8*)dst + 3 * count), pixel, 0xffffff);
|
|
}
|
|
count += PSM_PIXELS_STORED_PER_WORD<psm>();
|
|
}
|
|
|
|
|
|
// Fill count pixels form continues memory region, starting from pmem, First pixel to read have number shift in this region.
|
|
// Read no more than count pixels. We could assert, that all this pixels would be place in the same basepage
|
|
// Shift is automaticaly increased by count (or decreased if count < 0)
|
|
template <int psm, bool offseted, int count>
|
|
static __forceinline void writePixelsFromMemory(void* dst, const void* pmem, int& shift, int x, int y, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
const void* base;
|
|
if (offseted)
|
|
base = getPixelBasepage<psm>(dst, x, y, bw, bp);
|
|
else
|
|
base = (const void*)dst;
|
|
|
|
shift += count;
|
|
writePixelMem<psm, offseted>(base, x, y, (u32*)pmem, shift - count, bw, bp, mask); // I prefer not to use for here. It's slow
|
|
if (count < 2) return;
|
|
writePixelMem<psm, offseted>(base, x + 1, y, (u32*)pmem, shift - count + 1, bw, bp, mask);
|
|
if (count < 3) return;
|
|
writePixelMem<psm, offseted>(base, x + 2, y, (u32*)pmem, shift - count + 2, bw, bp, mask);
|
|
if (count < 4) return;
|
|
writePixelMem<psm, offseted>(base, x + 3, y, (u32*)pmem, shift - count + 3, bw, bp, mask);
|
|
if (count < 5) return;
|
|
writePixelMem<psm, offseted>(base, x + 4, y, (u32*)pmem, shift - count + 4, bw, bp, mask);
|
|
if (count < 6) return;
|
|
writePixelMem<psm, offseted>(base, x + 5, y, (u32*)pmem, shift - count + 5, bw, bp, mask);
|
|
if (count < 7) return;
|
|
writePixelMem<psm, offseted>(base, x + 6, y, (u32*)pmem, shift - count + 6, bw, bp, mask);
|
|
if (count < 8) return;
|
|
writePixelMem<psm, offseted>(base, x + 7, y, (u32*)pmem, shift - count + 7, bw, bp, mask);
|
|
}
|
|
|
|
// Use it if we don't know that starting pixel is aligned for multiple-pixel write
|
|
template <int psm, bool offseted>
|
|
static __forceinline void writeUnalignedPixelsFromMemory(void* dst, int div, const void* pmem, int& shift, int x, int y, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
switch (div){
|
|
case 0: return; // Pixels are aligned, so we could move on
|
|
case 1: writePixelsFromMemory<psm, offseted, 1>(dst, pmem, shift, x, y, bw, bp, mask);
|
|
return;
|
|
case 2: writePixelsFromMemory<psm, offseted, 2>(dst, pmem, shift, x, y, bw, bp, mask);
|
|
return;
|
|
case 3: writePixelsFromMemory<psm, offseted, 3>(dst, pmem, shift, x, y, bw, bp, mask);
|
|
return;
|
|
case 4: writePixelsFromMemory<psm, offseted, 4>(dst, pmem, shift, x, y, bw, bp, mask);
|
|
return;
|
|
case 5: writePixelsFromMemory<psm, offseted, 5>(dst, pmem, shift, x, y, bw, bp, mask);
|
|
return;
|
|
case 6: writePixelsFromMemory<psm, offseted, 6>(dst, pmem, shift, x, y, bw, bp, mask);
|
|
return;
|
|
case 7: writePixelsFromMemory<psm, offseted, 7>(dst, pmem, shift, x, y, bw, bp, mask);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// This little swizzle function used to convert data form memory. z is first byte in destination block, and y is number of word, in which we look look for data.
|
|
// s is shift by number of pixels, that should be used in masking
|
|
template <int psm, int y, int z>
|
|
static __forceinline u32 BitmaskinPSM(u32* pmem, u8 x) {
|
|
|
|
u8 H = PSM_BITCOUNT<psm>();
|
|
u8 I = PSM_BITS_PER_PIXEL<psm>() ; // length of bitmask in bits.
|
|
|
|
|
|
if (PSM_BITMODE<psm>() != 1) { // PSMCT24 and 24Z should be handle separated, as it could pass 32-bit storage.
|
|
u8 k = (x & (H - 1)) * I; // shift of PC data -- in PC we use pixels from constant position: x / H word and k is shift: x = ( x % H ) * H + k / I
|
|
// in PS2 we use all bit position from 0 by I pixels.
|
|
|
|
u32 J = ((1 << I) - 1) << k; // bitmask (of length ) & mask, moved by position k
|
|
|
|
// gcc complains repeatedly about this always being false. I'll investigate later.
|
|
if (z > k)
|
|
return ((*(pmem + x/H + y)) & J) << (z - k); // we use PX data from *mem + and properly shift
|
|
else // This formula loo little swizzled.
|
|
return ((*(pmem + x/H + y)) & J) >> (k - z);
|
|
}
|
|
else { // only 24 targets
|
|
u8* mem = ((u8*)pmem + (x * 3) + 4 * y); // Our pixel's is disaligned on 32-bit. So just use u8*.
|
|
return *(u32*)mem; // Mask would be handled later
|
|
}
|
|
}
|
|
|
|
// We use this function to limit number of memory R/W. This function fill all pixels for data with coordindates x, y. inside block data.
|
|
// Only rule is x, y should be < 8 (it automatically fill all needed pixels, that lie in blockdata, but have coords more than 8).
|
|
template <int psm>
|
|
static __forceinline void fillPixelsFromMemory(u32* dst, u32* pmem, int x, int y, int pitch, u32 bw, u32 bp = 0, u32 mask = 0xffffffff) {
|
|
u32 pixel = 0;
|
|
const u8 H = PSM_PIXELS_PER_WORD<psm>();
|
|
|
|
if (PSM_PIXEL_SHIFT<psm>() == 0) // We could not use calculated constants as templated parameters.
|
|
pixel = BitmaskinPSM<psm, 0, 0>(pmem, x); // First pixel x,y is the common part of all psmt path's
|
|
else {
|
|
if (PSM_PIXEL_SHIFT<psm>() == 24) // 8H and 4HL have 1 pixel, but shifted to 24 bits. 4HH -- 28 bits.
|
|
pixel = BitmaskinPSM<psm, 0, 24>(pmem, x);
|
|
else
|
|
pixel = BitmaskinPSM<psm, 0, 28>(pmem, x);
|
|
}
|
|
if (H > 1) {
|
|
const u8 G = psm & 0x7; // Bitmode, we use it for better chance of switch optimization
|
|
int div = ( x < 4 ) ? 4 : -4; // secondary row have shift by +4 or -4 pixels
|
|
|
|
switch (G) {
|
|
case 2:
|
|
pixel |= BitmaskinPSM<psm, 4, 16>(pmem, x);
|
|
break;
|
|
case 3:
|
|
pixel |= BitmaskinPSM<psm, 2, 16>(pmem, x);
|
|
pixel |= BitmaskinPSM<psm, 0, 8>(pmem + 2 * pitch, x + div);
|
|
pixel |= BitmaskinPSM<psm, 2, 24>(pmem + 2 * pitch, x + div);
|
|
break;
|
|
case 4:
|
|
pixel |= BitmaskinPSM<psm, 1, 8>(pmem, x);
|
|
pixel |= BitmaskinPSM<psm, 2, 16>(pmem, x);
|
|
pixel |= BitmaskinPSM<psm, 3, 24>(pmem, x);
|
|
|
|
pixel |= BitmaskinPSM<psm, 0, 4>(pmem + 2 * pitch, x + div);
|
|
pixel |= BitmaskinPSM<psm, 1, 12>(pmem + 2 * pitch, x + div);
|
|
pixel |= BitmaskinPSM<psm, 2, 20>(pmem + 2 * pitch, x + div);
|
|
pixel |= BitmaskinPSM<psm, 3, 28>(pmem + 2 * pitch, x + div);
|
|
|
|
break;
|
|
}
|
|
}
|
|
writePixelWord<psm>(dst, x, y, pixel, bw, bp, HandleWritemask<psm>(mask)); // use it for 32, 24, 8H, 4HL and 4HH
|
|
}
|
|
|
|
template <int psm>
|
|
void writeWordPixel(u32* pmem, u32 pixel, u32 mask) {
|
|
if (psm == PSMT4HH || psm == PSMT8H || psm == PSMT4HL || psm == PSMCT24 || psm == PSMT24Z)
|
|
MaskedOR(pmem, pixel, mask);
|
|
else
|
|
*pmem = pixel;
|
|
}
|
|
|
|
// Get pixel from src and put in in src. We assume, that psm of both buffers are the same and (sx-dx) & E == (sy - dy) & D == 0;
|
|
// Also in this case we could transfer the whole word
|
|
template <int psm>
|
|
void transferPixelFast(void* dst, void* src, int dx, int dy, int sx, int sy, u32 dbw, u32 sbw ) {
|
|
u32 Dbasepage, Sbasepage;
|
|
u32 word, mask = 0xffffffff;
|
|
|
|
u8 A = 0, B = 0, C = 0 , D = 0, E = 0, F = 0; u32 G = 0; u8 H = 0;
|
|
setPsmtConstantsX<psm> (A, B, C, D, E, F, G, H);
|
|
assert ( ((sx-dx) & E == (sy - dy) & D) && ((sy - dy) & D == 0) );
|
|
|
|
Dbasepage = ((dy>>A) * (dbw>>B)) + (dx>>B);
|
|
Sbasepage = ((sy>>A) * (sbw>>B)) + (sx>>B);
|
|
|
|
word = (g_pageTable[psm][sy&D][sx&E] >> C);
|
|
|
|
u32* dstp = (u32*)dst + Dbasepage * 2048 + word;
|
|
u32* srcp = (u32*)src + Sbasepage * 2048 + word;
|
|
|
|
writeWordPixel<psm>(dstp, *srcp, G << F);
|
|
}
|
|
|
|
// if we could not guarantee, that buffer suize shared same page Table address
|
|
template <int psm>
|
|
void transferPixel(void* dst, void* src, int dx, int dy, int sx, int sy, u32 dbw, u32 sbw ) {
|
|
u32 mask = 0xffffffff, shift;
|
|
u32* dstp = getPixelAddress_A32<psm>(mask, shift, dst, dx, dy, dbw);
|
|
u32* srcp = getPixelAddress_A32<psm>(mask, shift, src, sx, sy, sbw);
|
|
writeWordPixel<psm>(dstp, *srcp, mask); // write whole word
|
|
}
|
|
|
|
#define Def_getReadWrite(psmT, psmX) \
|
|
static __forceinline void writePixel##psmT(void* pmem, int x, int y, u32 pixel, u32 bp, u32 bw) { \
|
|
writePixel<psmX>(pmem, x, y, pixel, bw, bp); } \
|
|
static __forceinline u32 readPixel##psmT(const void* pmem, int x, int y, u32 bp, u32 bw) { \
|
|
return readPixel<psmX>(pmem, x, y, bw, bp); } \
|
|
static __forceinline void writePixel##psmT##_0(void* pmem, int x, int y, u32 pixel, u32 bw) { \
|
|
writePixel<psmX>(pmem, x, y, pixel, bw); } \
|
|
static __forceinline u32 readPixel##psmT##_0(const void* pmem, int x, int y, u32 bw) { \
|
|
return readPixel<psmX>(pmem, x, y, bw); }
|
|
|
|
Def_getReadWrite(32, PSMCT32);
|
|
Def_getReadWrite(24, PSMCT24);
|
|
Def_getReadWrite(16, PSMCT16);
|
|
Def_getReadWrite(16S, PSMCT16);
|
|
Def_getReadWrite(8, PSMT8);
|
|
Def_getReadWrite(8H, PSMT8H);
|
|
Def_getReadWrite(4, PSMT4);
|
|
Def_getReadWrite(4HH, PSMT4HH);
|
|
Def_getReadWrite(4HL, PSMT4HL);
|
|
Def_getReadWrite(32Z, PSMCT32);
|
|
Def_getReadWrite(24Z, PSMCT24);
|
|
Def_getReadWrite(16Z, PSMCT16);
|
|
Def_getReadWrite(16SZ, PSMCT16);
|
|
|
|
#endif // Zeydlitz's code
|
|
|
|
#endif /* __ZZOGL_MEM_H__ */
|