2012-11-01 15:19:01 +00:00
|
|
|
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
|
|
|
|
// 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
|
2012-11-04 22:01:49 +00:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
// 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 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
2013-08-20 20:34:47 +00:00
|
|
|
#include "GPU/ge_constants.h"
|
|
|
|
#include "GPU/GPUState.h"
|
|
|
|
#include "GPU/GLES/ShaderManager.h"
|
|
|
|
#include "GPU/GLES/GLES_GPU.h"
|
|
|
|
#include "GPU/Null/NullGpu.h"
|
|
|
|
#include "GPU/Software/SoftGpu.h"
|
2014-08-24 12:21:35 +00:00
|
|
|
|
|
|
|
#if defined(_WIN32)
|
2013-09-16 06:22:10 +00:00
|
|
|
#include "GPU/Directx9/helper/global.h"
|
2013-09-15 15:53:21 +00:00
|
|
|
#include "GPU/Directx9/GPU_DX9.h"
|
2013-08-17 13:11:27 +00:00
|
|
|
#endif
|
2014-08-24 12:21:35 +00:00
|
|
|
|
2014-04-13 21:02:00 +00:00
|
|
|
#include "Common/ChunkFile.h"
|
2013-08-20 20:34:47 +00:00
|
|
|
#include "Core/CoreParameter.h"
|
2013-11-10 12:18:52 +00:00
|
|
|
#include "Core/Config.h"
|
2013-08-20 20:34:47 +00:00
|
|
|
#include "Core/System.h"
|
2013-11-14 13:02:31 +00:00
|
|
|
#include "Core/MemMap.h"
|
|
|
|
#ifdef _M_SSE
|
|
|
|
#include <emmintrin.h>
|
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-11-10 12:18:52 +00:00
|
|
|
// This must be aligned so that the matrices within are aligned.
|
|
|
|
GPUgstate MEMORY_ALIGNED16(gstate);
|
|
|
|
// Let's align this one too for good measure.
|
|
|
|
GPUStateCache MEMORY_ALIGNED16(gstate_c);
|
|
|
|
|
2012-11-06 16:05:27 +00:00
|
|
|
GPUInterface *gpu;
|
2013-09-22 17:22:33 +00:00
|
|
|
GPUDebugInterface *gpuDebug;
|
2012-11-18 12:04:49 +00:00
|
|
|
GPUStatistics gpuStats;
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-09-22 17:22:33 +00:00
|
|
|
template <typename T>
|
|
|
|
static void SetGPU(T *obj) {
|
|
|
|
gpu = obj;
|
|
|
|
gpuDebug = obj;
|
|
|
|
}
|
|
|
|
|
2013-09-08 05:30:30 +00:00
|
|
|
bool GPU_Init() {
|
2013-08-07 15:07:13 +00:00
|
|
|
switch (PSP_CoreParameter().gpuCore) {
|
|
|
|
case GPU_NULL:
|
2013-09-22 17:22:33 +00:00
|
|
|
SetGPU(new NullGPU());
|
2013-08-07 15:07:13 +00:00
|
|
|
break;
|
|
|
|
case GPU_GLES:
|
2013-09-22 17:22:33 +00:00
|
|
|
SetGPU(new GLES_GPU());
|
2013-08-07 15:07:13 +00:00
|
|
|
break;
|
|
|
|
case GPU_SOFTWARE:
|
2013-09-22 17:22:33 +00:00
|
|
|
SetGPU(new SoftGPU());
|
2013-09-08 05:30:30 +00:00
|
|
|
break;
|
|
|
|
case GPU_DIRECTX9:
|
2014-08-24 12:21:35 +00:00
|
|
|
#if defined(_WIN32)
|
2013-09-22 17:22:33 +00:00
|
|
|
SetGPU(new DIRECTX9_GPU());
|
2013-09-12 08:56:20 +00:00
|
|
|
#endif
|
2013-09-15 15:53:21 +00:00
|
|
|
break;
|
2013-08-18 08:17:23 +00:00
|
|
|
}
|
2013-09-08 05:30:30 +00:00
|
|
|
|
|
|
|
return gpu != NULL;
|
2013-08-07 15:07:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GPU_Shutdown() {
|
|
|
|
delete gpu;
|
|
|
|
gpu = 0;
|
2013-09-22 17:22:33 +00:00
|
|
|
gpuDebug = 0;
|
2013-08-07 15:07:13 +00:00
|
|
|
}
|
|
|
|
|
2013-11-28 23:34:41 +00:00
|
|
|
void GPU_Reinitialize() {
|
|
|
|
if (gpu) {
|
|
|
|
gpu->Reinitialize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-20 07:34:18 +00:00
|
|
|
void InitGfxState() {
|
2012-11-01 15:19:01 +00:00
|
|
|
memset(&gstate, 0, sizeof(gstate));
|
2013-02-02 12:38:34 +00:00
|
|
|
memset(&gstate_c, 0, sizeof(gstate_c));
|
2012-11-01 15:19:01 +00:00
|
|
|
for (int i = 0; i < 256; i++) {
|
|
|
|
gstate.cmdmem[i] = i << 24;
|
|
|
|
}
|
2012-11-24 14:19:29 +00:00
|
|
|
|
2013-09-20 07:34:18 +00:00
|
|
|
// Lighting is not enabled by default, matrices are zero initialized.
|
|
|
|
memset(gstate.worldMatrix, 0, sizeof(gstate.worldMatrix));
|
|
|
|
memset(gstate.viewMatrix, 0, sizeof(gstate.viewMatrix));
|
|
|
|
memset(gstate.projMatrix, 0, sizeof(gstate.projMatrix));
|
|
|
|
memset(gstate.tgenMatrix, 0, sizeof(gstate.tgenMatrix));
|
|
|
|
memset(gstate.boneMatrix, 0, sizeof(gstate.boneMatrix));
|
2012-11-06 16:05:27 +00:00
|
|
|
}
|
|
|
|
|
2013-09-20 07:34:18 +00:00
|
|
|
void ShutdownGfxState() {
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// When you have changed state outside the psp gfx core,
|
|
|
|
// or saved the context and has reloaded it, call this function.
|
2013-09-20 07:33:32 +00:00
|
|
|
void ReapplyGfxState() {
|
2012-11-18 12:04:49 +00:00
|
|
|
if (!gpu)
|
|
|
|
return;
|
2013-08-04 22:15:50 +00:00
|
|
|
gpu->ReapplyGfxState();
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
2013-09-20 07:33:32 +00:00
|
|
|
|
|
|
|
struct CmdRange {
|
|
|
|
u8 start;
|
|
|
|
u8 end;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const CmdRange contextCmdRanges[] = {
|
|
|
|
{0x00, 0x02},
|
|
|
|
// Skip: {0x03, 0x0F},
|
|
|
|
{0x10, 0x10},
|
|
|
|
// Skip: {0x11, 0x11},
|
|
|
|
{0x12, 0x28},
|
|
|
|
// Skip: {0x29, 0x2B},
|
|
|
|
{0x2c, 0x33},
|
|
|
|
// Skip: {0x34, 0x35},
|
|
|
|
{0x36, 0x38},
|
|
|
|
// Skip: {0x39, 0x41},
|
|
|
|
{0x42, 0x4D},
|
|
|
|
// Skip: {0x4E, 0x4F},
|
|
|
|
{0x50, 0x51},
|
|
|
|
// Skip: {0x52, 0x52},
|
|
|
|
{0x53, 0x58},
|
|
|
|
// Skip: {0x59, 0x5A},
|
|
|
|
{0x5B, 0xB5},
|
|
|
|
// Skip: {0xB6, 0xB7},
|
|
|
|
{0xB8, 0xC3},
|
|
|
|
// Skip: {0xC4, 0xC4},
|
|
|
|
{0xC5, 0xD0},
|
|
|
|
// Skip: {0xD1, 0xD1}
|
|
|
|
{0xD2, 0xE9},
|
|
|
|
// Skip: {0xEA, 0xEA},
|
|
|
|
{0xEB, 0xEC},
|
|
|
|
// Skip: {0xED, 0xED},
|
|
|
|
{0xEE, 0xEE},
|
|
|
|
// Skip: {0xEF, 0xEF},
|
|
|
|
{0xF0, 0xF6},
|
|
|
|
// Skip: {0xF7, 0xF7},
|
|
|
|
{0xF8, 0xF9},
|
|
|
|
// Skip: {0xFA, 0xFF},
|
|
|
|
};
|
|
|
|
|
|
|
|
void GPUgstate::Save(u32_le *ptr) {
|
|
|
|
// Not sure what the first 10 values are, exactly, but these seem right.
|
|
|
|
ptr[5] = gstate_c.vertexAddr;
|
|
|
|
ptr[6] = gstate_c.indexAddr;
|
|
|
|
ptr[7] = gstate_c.offsetAddr;
|
|
|
|
|
2013-09-20 16:42:09 +00:00
|
|
|
// Command values start 17 ints in.
|
2013-09-20 07:33:32 +00:00
|
|
|
u32_le *cmds = ptr + 17;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(contextCmdRanges); ++i) {
|
|
|
|
for (int n = contextCmdRanges[i].start; n <= contextCmdRanges[i].end; ++n) {
|
|
|
|
*cmds++ = cmdmem[n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Memory::IsValidAddress(getClutAddress()))
|
|
|
|
*cmds++ = loadclut;
|
|
|
|
|
|
|
|
// Seems like it actually writes commands to load the matrices and then reset the counts.
|
|
|
|
*cmds++ = boneMatrixNumber;
|
|
|
|
*cmds++ = worldmtxnum;
|
|
|
|
*cmds++ = viewmtxnum;
|
|
|
|
*cmds++ = projmtxnum;
|
|
|
|
*cmds++ = texmtxnum;
|
|
|
|
|
|
|
|
u8 *matrices = (u8 *)cmds;
|
|
|
|
memcpy(matrices, boneMatrix, sizeof(boneMatrix)); matrices += sizeof(boneMatrix);
|
|
|
|
memcpy(matrices, worldMatrix, sizeof(worldMatrix)); matrices += sizeof(worldMatrix);
|
|
|
|
memcpy(matrices, viewMatrix, sizeof(viewMatrix)); matrices += sizeof(viewMatrix);
|
|
|
|
memcpy(matrices, projMatrix, sizeof(projMatrix)); matrices += sizeof(projMatrix);
|
|
|
|
memcpy(matrices, tgenMatrix, sizeof(tgenMatrix)); matrices += sizeof(tgenMatrix);
|
|
|
|
}
|
|
|
|
|
2013-11-14 13:02:31 +00:00
|
|
|
void GPUgstate::FastLoadBoneMatrix(u32 addr) {
|
2013-12-06 12:59:12 +00:00
|
|
|
const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(addr);
|
2013-11-14 13:02:31 +00:00
|
|
|
u32 num = boneMatrixNumber;
|
|
|
|
u32 *dst = (u32 *)(boneMatrix + (num & 0x7F));
|
|
|
|
|
|
|
|
#ifdef _M_SSE
|
|
|
|
__m128i row1 = _mm_slli_epi32(_mm_loadu_si128((const __m128i *)src), 8);
|
|
|
|
__m128i row2 = _mm_slli_epi32(_mm_loadu_si128((const __m128i *)(src + 4)), 8);
|
|
|
|
__m128i row3 = _mm_slli_epi32(_mm_loadu_si128((const __m128i *)(src + 8)), 8);
|
|
|
|
if ((num & 0x3) == 0) {
|
|
|
|
_mm_store_si128((__m128i *)dst, row1);
|
|
|
|
_mm_store_si128((__m128i *)(dst + 4), row2);
|
|
|
|
_mm_store_si128((__m128i *)(dst + 8), row3);
|
|
|
|
} else {
|
|
|
|
_mm_storeu_si128((__m128i *)dst, row1);
|
|
|
|
_mm_storeu_si128((__m128i *)(dst + 4), row2);
|
|
|
|
_mm_storeu_si128((__m128i *)(dst + 8), row3);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
for (int i = 0; i < 12; i++) {
|
|
|
|
dst[i] = src[i] << 8;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
num += 12;
|
|
|
|
gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F);
|
|
|
|
}
|
|
|
|
|
2013-09-20 07:33:32 +00:00
|
|
|
void GPUgstate::Restore(u32_le *ptr) {
|
|
|
|
// Not sure what the first 10 values are, exactly, but these seem right.
|
|
|
|
gstate_c.vertexAddr = ptr[5];
|
|
|
|
gstate_c.indexAddr = ptr[6];
|
|
|
|
gstate_c.offsetAddr = ptr[7];
|
|
|
|
|
2013-09-20 16:42:09 +00:00
|
|
|
// Command values start 17 ints in.
|
2013-09-20 07:33:32 +00:00
|
|
|
u32_le *cmds = ptr + 17;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(contextCmdRanges); ++i) {
|
|
|
|
for (int n = contextCmdRanges[i].start; n <= contextCmdRanges[i].end; ++n) {
|
|
|
|
cmdmem[n] = *cmds++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Memory::IsValidAddress(getClutAddress()))
|
|
|
|
loadclut = *cmds++;
|
|
|
|
boneMatrixNumber = *cmds++;
|
|
|
|
worldmtxnum = *cmds++;
|
|
|
|
viewmtxnum = *cmds++;
|
|
|
|
projmtxnum = *cmds++;
|
|
|
|
texmtxnum = *cmds++;
|
|
|
|
|
|
|
|
u8 *matrices = (u8 *)cmds;
|
|
|
|
memcpy(boneMatrix, matrices, sizeof(boneMatrix)); matrices += sizeof(boneMatrix);
|
|
|
|
memcpy(worldMatrix, matrices, sizeof(worldMatrix)); matrices += sizeof(worldMatrix);
|
|
|
|
memcpy(viewMatrix, matrices, sizeof(viewMatrix)); matrices += sizeof(viewMatrix);
|
|
|
|
memcpy(projMatrix, matrices, sizeof(projMatrix)); matrices += sizeof(projMatrix);
|
|
|
|
memcpy(tgenMatrix, matrices, sizeof(tgenMatrix)); matrices += sizeof(tgenMatrix);
|
|
|
|
}
|
2013-11-10 12:18:52 +00:00
|
|
|
|
|
|
|
bool vertTypeIsSkinningEnabled(u32 vertType) {
|
|
|
|
if (g_Config.bSoftwareSkinning && ((vertType & GE_VTYPE_MORPHCOUNT_MASK) == 0))
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return ((vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE);
|
|
|
|
}
|
2014-04-13 21:02:00 +00:00
|
|
|
|
|
|
|
struct GPUStateCache_v0
|
|
|
|
{
|
|
|
|
u32 vertexAddr;
|
|
|
|
u32 indexAddr;
|
|
|
|
|
|
|
|
u32 offsetAddr;
|
|
|
|
|
|
|
|
bool textureChanged;
|
|
|
|
bool textureFullAlpha;
|
|
|
|
bool vertexFullAlpha;
|
|
|
|
bool framebufChanged;
|
|
|
|
|
|
|
|
int skipDrawReason;
|
|
|
|
|
|
|
|
UVScale uv;
|
|
|
|
bool flipTexture;
|
|
|
|
};
|
|
|
|
|
|
|
|
void GPUStateCache::DoState(PointerWrap &p) {
|
2014-09-08 21:10:23 +00:00
|
|
|
auto s = p.Section("GPUStateCache", 0, 4);
|
2014-04-13 21:02:00 +00:00
|
|
|
if (!s) {
|
|
|
|
// Old state, this was not versioned.
|
|
|
|
GPUStateCache_v0 old;
|
|
|
|
p.Do(old);
|
|
|
|
|
|
|
|
vertexAddr = old.vertexAddr;
|
|
|
|
indexAddr = old.indexAddr;
|
|
|
|
offsetAddr = old.offsetAddr;
|
|
|
|
textureChanged = TEXCHANGE_UPDATED;
|
|
|
|
textureFullAlpha = old.textureFullAlpha;
|
|
|
|
vertexFullAlpha = old.vertexFullAlpha;
|
|
|
|
framebufChanged = old.framebufChanged;
|
2014-04-13 22:16:51 +00:00
|
|
|
skipDrawReason = old.skipDrawReason;
|
|
|
|
uv = old.uv;
|
|
|
|
flipTexture = old.flipTexture;
|
|
|
|
} else {
|
|
|
|
p.Do(vertexAddr);
|
|
|
|
p.Do(indexAddr);
|
|
|
|
p.Do(offsetAddr);
|
2014-04-13 21:02:00 +00:00
|
|
|
|
2014-04-13 22:16:51 +00:00
|
|
|
p.Do(textureChanged);
|
|
|
|
p.Do(textureFullAlpha);
|
|
|
|
p.Do(vertexFullAlpha);
|
|
|
|
p.Do(framebufChanged);
|
2014-04-13 21:02:00 +00:00
|
|
|
|
2014-04-13 22:16:51 +00:00
|
|
|
p.Do(skipDrawReason);
|
2014-04-13 21:02:00 +00:00
|
|
|
|
2014-04-13 22:16:51 +00:00
|
|
|
p.Do(uv);
|
|
|
|
p.Do(flipTexture);
|
|
|
|
}
|
2014-04-13 21:02:00 +00:00
|
|
|
|
2014-08-28 08:20:21 +00:00
|
|
|
// needShaderTexClamp and bgraTexture don't need to be saved.
|
2014-06-07 19:21:52 +00:00
|
|
|
|
2014-05-11 21:11:36 +00:00
|
|
|
if (s >= 3) {
|
|
|
|
p.Do(textureSimpleAlpha);
|
|
|
|
} else {
|
|
|
|
textureSimpleAlpha = false;
|
|
|
|
}
|
|
|
|
|
2014-04-18 12:30:18 +00:00
|
|
|
if (s < 2) {
|
|
|
|
float l12[12];
|
2014-04-22 05:40:50 +00:00
|
|
|
float l4[4];
|
2014-04-18 12:30:18 +00:00
|
|
|
p.Do(l12); // lightpos
|
|
|
|
p.Do(l12); // lightdir
|
|
|
|
p.Do(l12); // lightattr
|
|
|
|
p.Do(l12); // lightcol0
|
|
|
|
p.Do(l12); // lightcol1
|
|
|
|
p.Do(l12); // lightcol2
|
|
|
|
p.Do(l4); // lightangle
|
|
|
|
p.Do(l4); // lightspot
|
|
|
|
}
|
|
|
|
|
2014-04-13 21:02:00 +00:00
|
|
|
p.Do(morphWeights);
|
|
|
|
|
|
|
|
p.Do(curTextureWidth);
|
|
|
|
p.Do(curTextureHeight);
|
|
|
|
p.Do(actualTextureHeight);
|
2014-06-08 20:52:05 +00:00
|
|
|
// curTextureXOffset and curTextureYOffset don't need to be saved. Well, the above don't either...
|
2014-04-13 21:02:00 +00:00
|
|
|
|
|
|
|
p.Do(vpWidth);
|
|
|
|
p.Do(vpHeight);
|
2014-09-08 21:10:23 +00:00
|
|
|
if (s >= 4) {
|
|
|
|
p.Do(vpDepth);
|
|
|
|
} else {
|
|
|
|
vpDepth = 1.0f; // any positive value should be fine
|
|
|
|
}
|
2014-04-13 21:02:00 +00:00
|
|
|
|
|
|
|
p.Do(curRTWidth);
|
|
|
|
p.Do(curRTHeight);
|
2014-06-14 05:49:28 +00:00
|
|
|
|
2014-06-15 00:08:41 +00:00
|
|
|
// curRTBufferWidth, curRTBufferHeight, and cutRTOffsetX don't need to be saved.
|
2014-04-13 21:02:00 +00:00
|
|
|
}
|