Fix invalid pointer errors in Burnout 2.

Yet another story of games loading weird shit into registers.

For some reason, Burnout 2 would (in rare situations) load invalid
addresses into cp_state.array_bases. What would the real hardware
do in this situation? Who knows, Burnout 2 doesn't actually enable
the vertex array with the invalid address so nothing kinky happens.

But dolphin tries to optimise things and starts using the address
as soon as it is loaded into memory. This causes GetPointer (which is
now much more vocal) to throw an error.

The Fix:  We don't call GetPointer until we are sure the vertex array
has been enabled.
This commit is contained in:
Scott Mansell 2015-05-29 18:25:19 +12:00
parent 68d6f07b5c
commit 6d916762fb
4 changed files with 21 additions and 8 deletions

View File

@ -88,6 +88,12 @@ union TVtxDesc
{
u32 Hex0, Hex1;
};
// Easily index into the Position..Tex7Coord fields.
u32 GetVertexArrayStatus(int idx)
{
return (Hex >> (9 + idx * 2)) & 0x3;
}
};
union UVAT_group0
@ -239,6 +245,7 @@ class VertexLoaderBase;
// STATE_TO_SAVE
struct CPState final
{
// Only 12 of these arrays are used.
u32 array_bases[16];
u32 array_strides[16];
TMatrixIndexA matrix_index_a;

View File

@ -212,7 +212,6 @@ void VideoBackendHardware::DoState(PointerWrap& p)
if (p.GetMode() == PointerWrap::MODE_READ)
{
m_invalid = true;
RecomputeCachedArraybases();
// Clear all caches that touch RAM
// (? these don't appear to touch any emulation state that gets saved. moved to on load only.)

View File

@ -42,7 +42,6 @@ void Init()
map_entry = nullptr;
for (auto& map_entry : g_preprocess_cp_state.vertex_loaders)
map_entry = nullptr;
RecomputeCachedArraybases();
SETSTAT(stats.numVertexLoaders, 0);
}
@ -136,6 +135,11 @@ static VertexLoaderBase* RefreshLoader(int vtx_attr_group, bool preprocess = fal
} else {
loader = state->vertex_loaders[vtx_attr_group];
}
// Lookup pointers for any vertex arrays.
if (!preprocess)
ComputeCachedArrayBases();
return loader;
}
@ -232,8 +236,6 @@ void LoadCPReg(u32 sub_cmd, u32 value, bool is_preprocess)
// Pointers to vertex arrays in GC RAM
case 0xA0:
state->array_bases[sub_cmd & 0xF] = value;
if (update_global_state)
cached_arraybases[sub_cmd & 0xF] = Memory::GetPointer(value);
break;
case 0xB0:
@ -263,10 +265,15 @@ void FillCPMemoryArray(u32 *memory)
}
}
void RecomputeCachedArraybases()
void ComputeCachedArrayBases()
{
for (int i = 0; i < 16; i++)
// Some games such as Burnout 2 can put invalid addresses into
// the array base registers. (see issue 8591)
// But the vertex arrays with invalid addresses aren't actually enabled.
for (int i = 0; i < 12; i++)
{
cached_arraybases[i] = Memory::GetPointer(g_main_cp_state.array_bases[i]);
// Only update the array base if the vertex description states we are going to use it.
if (g_main_cp_state.vtx_desc.GetVertexArrayStatus(i) >= 0x2)
cached_arraybases[i] = Memory::GetPointer(g_main_cp_state.array_bases[i]);
}
}

View File

@ -26,4 +26,4 @@ namespace VertexLoaderManager
NativeVertexFormat* GetCurrentVertexFormat();
}
void RecomputeCachedArraybases();
void ComputeCachedArrayBases();