mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-17 08:20:30 +02:00
Fudge GPU cycle estimates to let GoW runs fast and GTA smooth. See comments for the horrible details.
I hope this doesn't affect other games adversely.
This commit is contained in:
1 parent
a61363d8d4
commit
70bb4e1c85
4 files changed
+86
-32
No files matched your search
@@ -321,21 +321,25 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
|
||||
// This drives all drawing. All other state we just buffer up, then we apply it only
|
||||
// when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization.
|
||||
|
||||
u32 count = data & 0xFFFF;
|
||||
u32 type = data >> 16;
|
||||
|
||||
// This also make skipping drawing very effective.
|
||||
framebufferManager_.SetRenderFrameBuffer();
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB))
|
||||
{
|
||||
transformDraw_.SetupVertexDecoder(gstate.vertType);
|
||||
// Rough estimate, not sure what's correct.
|
||||
int vertexCost = transformDraw_.EstimatePerVertexCost();
|
||||
cyclesExecuted += vertexCost * count;
|
||||
return;
|
||||
|
||||
u32 count = data & 0xFFFF;
|
||||
u32 type = data >> 16;
|
||||
}
|
||||
|
||||
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
|
||||
ERROR_LOG(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
|
||||
break;
|
||||
}
|
||||
|
||||
// Rough estimate, not sure what's correct.
|
||||
cyclesExecuted += 80 * count;
|
||||
|
||||
// TODO: Split this so that we can collect sequences of primitives, can greatly speed things up
|
||||
// on platforms where draw calls are expensive like mobile and D3D
|
||||
@@ -351,6 +355,10 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
|
||||
|
||||
int bytesRead;
|
||||
transformDraw_.SubmitPrim(verts, inds, type, count, gstate.vertType, -1, &bytesRead);
|
||||
|
||||
int vertexCost = transformDraw_.EstimatePerVertexCost();
|
||||
cyclesExecuted += vertexCost * count;
|
||||
|
||||
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
|
||||
// Some games rely on this, they don't bother reloading VADDR and IADDR.
|
||||
// Q: Are these changed reflected in the real registers? Needs testing.
|
||||
|
||||
@@ -18,17 +18,19 @@
|
||||
#include "base/timeutil.h"
|
||||
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "../../Core/MemMap.h"
|
||||
#include "../../Core/Host.h"
|
||||
#include "../../Core/System.h"
|
||||
#include "../../Core/Reporting.h"
|
||||
#include "../../native/gfx_es2/gl_state.h"
|
||||
#include "../../native/ext/cityhash/city.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
|
||||
#include "../Math3D.h"
|
||||
#include "../GPUState.h"
|
||||
#include "../ge_constants.h"
|
||||
#include "../../Core/Config.h"
|
||||
#include "native/gfx_es2/gl_state.h"
|
||||
#include "native/ext/cityhash/city.h"
|
||||
|
||||
#include "GPU/Math3D.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
||||
#include "StateMapping.h"
|
||||
#include "TextureCache.h"
|
||||
@@ -807,6 +809,48 @@ VertexDecoder *TransformDrawEngine::GetVertexDecoder(u32 vtype) {
|
||||
return dec;
|
||||
}
|
||||
|
||||
void TransformDrawEngine::SetupVertexDecoder(u32 vertType) {
|
||||
// If vtype has changed, setup the vertex decoder.
|
||||
// TODO: Simply cache the setup decoders instead.
|
||||
if (vertType != lastVType_) {
|
||||
dec_ = GetVertexDecoder(vertType);
|
||||
lastVType_ = vertType;
|
||||
}
|
||||
}
|
||||
|
||||
int TransformDrawEngine::EstimatePerVertexCost() {
|
||||
// TODO: This is transform cost, also account for rasterization cost somehow... although it probably
|
||||
// runs in parallel with transform.
|
||||
|
||||
// Also, this is all pure guesswork. If we can find a way to do measurements, that would be great.
|
||||
|
||||
// GTA wants a low value to run smooth, GoW wants a high value (otherwise it thinks things
|
||||
// went too fast and starts doing all the work over again).
|
||||
|
||||
int cost = 20;
|
||||
if (gstate.isLightingEnabled()) {
|
||||
cost += 10;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (gstate.lightEnable[i] & 1)
|
||||
cost += 20;
|
||||
}
|
||||
if (gstate.getUVGenMode() != 0) {
|
||||
cost += 20;
|
||||
}
|
||||
if (dec_ && dec_->morphcount > 1) {
|
||||
cost += 5 * dec_->morphcount;
|
||||
}
|
||||
|
||||
if (CoreTiming::GetClockFrequencyMHz() == 333) {
|
||||
// Just brutally double to make God of War happier.
|
||||
// FUDGE FACTORS! Delicious fudge factors!
|
||||
cost *= 2;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
void TransformDrawEngine::SubmitPrim(void *verts, void *inds, int prim, int vertexCount, u32 vertType, int forceIndexType, int *bytesRead) {
|
||||
if (vertexCount == 0)
|
||||
return; // we ignore zero-sized draw calls.
|
||||
@@ -814,12 +858,7 @@ void TransformDrawEngine::SubmitPrim(void *verts, void *inds, int prim, int vert
|
||||
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS)
|
||||
Flush();
|
||||
prevPrim_ = prim;
|
||||
// If vtype has changed, setup the vertex decoder.
|
||||
// TODO: Simply cache the setup decoders instead.
|
||||
if (vertType != lastVType_) {
|
||||
dec_ = GetVertexDecoder(vertType);
|
||||
lastVType_ = vertType;
|
||||
}
|
||||
SetupVertexDecoder(vertType);
|
||||
|
||||
dec_->IncrementStat(STAT_VERTSSUBMITTED, vertexCount);
|
||||
|
||||
|
||||
@@ -119,6 +119,11 @@ public:
|
||||
void DecimateTrackedVertexArrays();
|
||||
void ClearTrackedVertexArrays();
|
||||
|
||||
void SetupVertexDecoder(u32 vertType);
|
||||
|
||||
// This requires a SetupVertexDecoder call first.
|
||||
int EstimatePerVertexCost();
|
||||
|
||||
private:
|
||||
void SoftwareTransformAndDraw(int prim, u8 *decoded, LinkedShader *program, int vertexCount, u32 vertexType, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex);
|
||||
void ApplyDrawState(int prim);
|
||||
|
||||
@@ -44,12 +44,13 @@ bool CanUseHardwareTransform(int prim) {
|
||||
|
||||
// prim so we can special case for RECTANGLES :(
|
||||
void ComputeVertexShaderID(VertexShaderID *id, int prim) {
|
||||
const u32 vertType = gstate.vertType;
|
||||
int doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
|
||||
bool doTextureProjection = gstate.getUVGenMode() == 1;
|
||||
|
||||
bool hasColor = (gstate.vertType & GE_VTYPE_COL_MASK) != 0;
|
||||
bool hasNormal = (gstate.vertType & GE_VTYPE_NRM_MASK) != 0;
|
||||
bool hasBones = (gstate.vertType & GE_VTYPE_WEIGHT_MASK) != 0;
|
||||
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0;
|
||||
bool hasNormal = (vertType & GE_VTYPE_NRM_MASK) != 0;
|
||||
bool hasBones = (vertType & GE_VTYPE_WEIGHT_MASK) != 0;
|
||||
bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear();
|
||||
bool lmode = (gstate.lmode & 1) && gstate.isLightingEnabled();
|
||||
|
||||
@@ -143,15 +144,16 @@ void GenerateVertexShader(int prim, char *buffer) {
|
||||
WRITE(p, "#define lowp\n");
|
||||
WRITE(p, "#define mediump\n");
|
||||
#endif
|
||||
const u32 vertType = gstate.vertType;
|
||||
|
||||
int lmode = (gstate.lmode & 1) && gstate.isLightingEnabled();
|
||||
int doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
|
||||
|
||||
bool hwXForm = CanUseHardwareTransform(prim);
|
||||
bool hasColor = (gstate.vertType & GE_VTYPE_COL_MASK) != 0 || !hwXForm;
|
||||
bool hasNormal = (gstate.vertType & GE_VTYPE_NRM_MASK) != 0 && hwXForm;
|
||||
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0 || !hwXForm;
|
||||
bool hasNormal = (vertType & GE_VTYPE_NRM_MASK) != 0 && hwXForm;
|
||||
bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear();
|
||||
bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0;
|
||||
bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0;
|
||||
bool flipV = gstate_c.flipTexture;
|
||||
bool doTextureProjection = gstate.getUVGenMode() == 1;
|
||||
|
||||
@@ -169,7 +171,7 @@ void GenerateVertexShader(int prim, char *buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
|
||||
if ((vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
|
||||
WRITE(p, "%s", boneWeightAttrDecl[gstate.getNumBoneWeights() - 1]);
|
||||
}
|
||||
|
||||
@@ -215,8 +217,8 @@ void GenerateVertexShader(int prim, char *buffer) {
|
||||
WRITE(p, "uniform vec4 u_uvscaleoffset;\n");
|
||||
else if (gstate.getUVGenMode() == 1)
|
||||
WRITE(p, "uniform mat4 u_texmtx;\n");
|
||||
if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
|
||||
int numBones = 1 + ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT);
|
||||
if ((vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
|
||||
int numBones = 1 + ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT);
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
WRITE(p, "uniform mat4 u_bone%i;\n", i);
|
||||
}
|
||||
@@ -283,7 +285,7 @@ void GenerateVertexShader(int prim, char *buffer) {
|
||||
}
|
||||
} else {
|
||||
// Step 1: World Transform / Skinning
|
||||
if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) == GE_VTYPE_WEIGHT_NONE) {
|
||||
if ((vertType & GE_VTYPE_WEIGHT_MASK) == GE_VTYPE_WEIGHT_NONE) {
|
||||
// No skinning, just standard T&L.
|
||||
WRITE(p, " vec3 worldpos = (u_world * vec4(a_position.xyz, 1.0)).xyz;\n");
|
||||
if (hasNormal)
|
||||
@@ -292,7 +294,7 @@ void GenerateVertexShader(int prim, char *buffer) {
|
||||
WRITE(p, " vec3 worldpos = vec3(0.0);\n");
|
||||
if (hasNormal)
|
||||
WRITE(p, " vec3 worldnormal = vec3(0.0);\n");
|
||||
int numWeights = 1 + ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT);
|
||||
int numWeights = 1 + ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT);
|
||||
for (int i = 0; i < numWeights; i++) {
|
||||
const char *weightAttr = boneWeightAttr[i];
|
||||
// workaround for "cant do .x of scalar" issue
|
||||
|
||||
Reference in new issue
Block a user