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:
Henrik Rydgard committed 2013-04-20 20:21:12 +02:00
1 parent a61363d8d4
commit 70bb4e1c85
4 files changed
+86 -32

No files matched your search

+13 -5
View File
@@ -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.
+55 -16
View File
@@ -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);
+5
View File
@@ -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);
+13 -11
View File
@@ -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