mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 06:39:46 -06:00
fully implemented display list cache with vertex data included and added in all the plugins.
still experimental, not totally optimized but must bring a nice speed up please test for regressions an error. an please Linux people fix scons :) git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6149 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
@ -106,6 +106,16 @@ void XEmitter::ABI_CallFunctionCCP(void *func, u32 param1, u32 param2, void *par
|
||||
ABI_RestoreStack(3 * 4);
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionCCCP(void *func, u32 param1, u32 param2,u32 param3, void *param4) {
|
||||
ABI_AlignStack(4 * 4);
|
||||
PUSH(32, Imm32((u32)param4));
|
||||
PUSH(32, Imm32(param3));
|
||||
PUSH(32, Imm32(param2));
|
||||
PUSH(32, Imm32(param1));
|
||||
CALL(func);
|
||||
ABI_RestoreStack(4 * 4);
|
||||
}
|
||||
|
||||
// Pass a register as a parameter.
|
||||
void XEmitter::ABI_CallFunctionR(void *func, X64Reg reg1) {
|
||||
ABI_AlignStack(1 * 4);
|
||||
@ -236,6 +246,14 @@ void XEmitter::ABI_CallFunctionCCP(void *func, u32 param1, u32 param2, void *par
|
||||
CALL(func);
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionCCCP(void *func, u32 param1, u32 param2, u32 param3, void *param4) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
MOV(32, R(ABI_PARAM3), Imm32(param3));
|
||||
MOV(64, R(ABI_PARAM4), Imm64((u64)param4));
|
||||
CALL(func);
|
||||
}
|
||||
|
||||
// Pass a register as a parameter.
|
||||
void XEmitter::ABI_CallFunctionR(void *func, X64Reg reg1) {
|
||||
if (reg1 != ABI_PARAM1)
|
||||
|
@ -22,7 +22,7 @@ static const char ID[4] = {'D', 'C', 'A', 'C'};
|
||||
// Update this to the current SVN revision every time you change shader generation code.
|
||||
// We don't automatically get this from SVN_REV because that would mean regenerating the
|
||||
// shader cache for every revision, graphics-related or not, which is simply annoying.
|
||||
const int version = 6139;
|
||||
const int version = 6148;
|
||||
|
||||
LinearDiskCache::LinearDiskCache()
|
||||
: file_(NULL), num_entries_(0) {
|
||||
|
@ -599,6 +599,7 @@ public:
|
||||
void ABI_CallFunctionCC(void *func, u32 param1, u32 param2);
|
||||
void ABI_CallFunctionCCC(void *func, u32 param1, u32 param2, u32 param3);
|
||||
void ABI_CallFunctionCCP(void *func, u32 param1, u32 param2, void *param3);
|
||||
void ABI_CallFunctionCCCP(void *func, u32 param1, u32 param2,u32 param3, void *param4);
|
||||
void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2);
|
||||
void ABI_CallFunctionA(void *func, const Gen::OpArg &arg1);
|
||||
|
||||
|
598
Source/Core/VideoCommon/Src/DLCache.cpp
Normal file
598
Source/Core/VideoCommon/Src/DLCache.cpp
Normal file
@ -0,0 +1,598 @@
|
||||
// Copyright (C) 2003-2009 Dolphin 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
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// 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 SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// TODO: Handle cache-is-full condition :p
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "VideoCommon.h"
|
||||
#include "Hash.h"
|
||||
#include "MemoryUtil.h"
|
||||
#include "DataReader.h"
|
||||
#include "Statistics.h"
|
||||
#include "OpcodeDecoding.h" // For the GX_ constants.
|
||||
|
||||
#include "XFMemory.h"
|
||||
#include "CPMemory.h"
|
||||
#include "BPMemory.h"
|
||||
|
||||
#include "VertexLoaderManager.h"
|
||||
#include "NativeVertexWriter.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
|
||||
#include "DLCache.h"
|
||||
|
||||
#define DL_CODE_CACHE_SIZE (1024*1024*16)
|
||||
extern int frameCount;
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
namespace DLCache
|
||||
{
|
||||
|
||||
// Currently just recompiles the DLs themselves, doesn't bother with the vertex data.
|
||||
// The speed boost is pretty small. The real big boost will come when we also store
|
||||
// vertex arrays in the cached DLs.
|
||||
|
||||
enum DisplayListPass {
|
||||
DLPASS_ANALYZE,
|
||||
DLPASS_COMPILE,
|
||||
DLPASS_RUN,
|
||||
};
|
||||
|
||||
struct VDataHashRegion
|
||||
{
|
||||
u32 hash;
|
||||
u32 start_address;
|
||||
int size;
|
||||
};
|
||||
typedef u8* DataPointer;
|
||||
typedef std::map<u8, DataPointer> VdataMap;
|
||||
|
||||
struct CachedDisplayList
|
||||
{
|
||||
CachedDisplayList()
|
||||
: uncachable(false),
|
||||
pass(DLPASS_ANALYZE),
|
||||
next_check(1),
|
||||
BufferCount(0),
|
||||
num_xf_reg(0),
|
||||
num_cp_reg(0),
|
||||
num_bp_reg(0),
|
||||
num_index_xf(0),
|
||||
num_draw_call(0)
|
||||
{
|
||||
frame_count = frameCount;
|
||||
}
|
||||
|
||||
bool uncachable; // if set, this DL will always be interpreted. This gets set if hash ever changes.
|
||||
|
||||
int pass;
|
||||
u64 dl_hash;
|
||||
|
||||
int check;
|
||||
int next_check;
|
||||
|
||||
int frame_count;
|
||||
|
||||
// ... Something containing cached vertex buffers here ...
|
||||
u8 BufferCount;
|
||||
VdataMap Vdata;
|
||||
|
||||
int num_xf_reg;
|
||||
int num_cp_reg;
|
||||
int num_bp_reg;
|
||||
int num_index_xf;
|
||||
int num_draw_call;
|
||||
|
||||
// Compile the commands themselves down to native code.
|
||||
const u8* compiled_code;
|
||||
};
|
||||
|
||||
// We want to allow caching DLs that start at the same address but have different lengths,
|
||||
// so the size has to be in the ID.
|
||||
inline u64 CreateMapId(u32 address, u32 size)
|
||||
{
|
||||
return ((u64)address << 32) | size;
|
||||
}
|
||||
|
||||
typedef std::map<u64, CachedDisplayList> DLMap;
|
||||
|
||||
static DLMap dl_map;
|
||||
static DataPointer dlcode_cache;
|
||||
|
||||
static Gen::XEmitter emitter;
|
||||
|
||||
// First pass - analyze
|
||||
bool AnalyzeAndRunDisplayList(u32 address, int size, CachedDisplayList *dl)
|
||||
{
|
||||
int num_xf_reg = 0;
|
||||
int num_cp_reg = 0;
|
||||
int num_bp_reg = 0;
|
||||
int num_index_xf = 0;
|
||||
int num_draw_call = 0;
|
||||
|
||||
u8* old_pVideoData = g_pVideoData;
|
||||
u8* startAddress = Memory_GetPtr(address);
|
||||
|
||||
// Avoid the crash if Memory_GetPtr failed ..
|
||||
if (startAddress != 0)
|
||||
{
|
||||
g_pVideoData = startAddress;
|
||||
|
||||
// temporarily swap dl and non-dl (small "hack" for the stats)
|
||||
Statistics::SwapDL();
|
||||
|
||||
u8 *end = g_pVideoData + size;
|
||||
while (g_pVideoData < end)
|
||||
{
|
||||
// Yet another reimplementation of the DL reading...
|
||||
int cmd_byte = DataReadU8();
|
||||
switch (cmd_byte)
|
||||
{
|
||||
case GX_NOP:
|
||||
break;
|
||||
|
||||
case GX_LOAD_CP_REG: //0x08
|
||||
{
|
||||
u8 sub_cmd = DataReadU8();
|
||||
u32 value = DataReadU32();
|
||||
LoadCPReg(sub_cmd, value);
|
||||
INCSTAT(stats.thisFrame.numCPLoads);
|
||||
num_cp_reg++;
|
||||
}
|
||||
break;
|
||||
|
||||
case GX_LOAD_XF_REG:
|
||||
{
|
||||
u32 Cmd2 = DataReadU32();
|
||||
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
||||
u32 xf_address = Cmd2 & 0xFFFF;
|
||||
// TODO - speed this up. pshufb?
|
||||
u32 data_buffer[16];
|
||||
for (int i = 0; i < transfer_size; i++)
|
||||
data_buffer[i] = DataReadU32();
|
||||
LoadXFReg(transfer_size, xf_address, data_buffer);
|
||||
INCSTAT(stats.thisFrame.numXFLoads);
|
||||
num_xf_reg++;
|
||||
}
|
||||
break;
|
||||
|
||||
case GX_LOAD_INDX_A: //used for position matrices
|
||||
{
|
||||
LoadIndexedXF(DataReadU32(), 0xC);
|
||||
num_index_xf++;
|
||||
}
|
||||
break;
|
||||
case GX_LOAD_INDX_B: //used for normal matrices
|
||||
{
|
||||
LoadIndexedXF(DataReadU32(), 0xD);
|
||||
num_index_xf++;
|
||||
}
|
||||
break;
|
||||
case GX_LOAD_INDX_C: //used for postmatrices
|
||||
{
|
||||
LoadIndexedXF(DataReadU32(), 0xE);
|
||||
num_index_xf++;
|
||||
}
|
||||
break;
|
||||
case GX_LOAD_INDX_D: //used for lights
|
||||
{
|
||||
LoadIndexedXF(DataReadU32(), 0xF);
|
||||
num_index_xf++;
|
||||
}
|
||||
break;
|
||||
case GX_CMD_CALL_DL:
|
||||
{
|
||||
u32 addr = DataReadU32();
|
||||
u32 count = DataReadU32();
|
||||
ExecuteDisplayList(addr, count);
|
||||
}
|
||||
break;
|
||||
case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that
|
||||
DEBUG_LOG(VIDEO, "GX 0x44: %08x", cmd_byte);
|
||||
break;
|
||||
case GX_CMD_INVL_VC: // Invalidate Vertex Cache
|
||||
DEBUG_LOG(VIDEO, "Invalidate (vertex cache?)");
|
||||
break;
|
||||
case GX_LOAD_BP_REG: //0x61
|
||||
{
|
||||
u32 bp_cmd = DataReadU32();
|
||||
LoadBPReg(bp_cmd);
|
||||
INCSTAT(stats.thisFrame.numBPLoads);
|
||||
num_bp_reg++;
|
||||
}
|
||||
break;
|
||||
|
||||
// draw primitives
|
||||
default:
|
||||
if (cmd_byte & 0x80)
|
||||
{
|
||||
// load vertices (use computed vertex size from FifoCommandRunnable above)
|
||||
u16 numVertices = DataReadU16();
|
||||
|
||||
VertexLoaderManager::RunVertices(
|
||||
cmd_byte & GX_VAT_MASK, // Vertex loader index (0 - 7)
|
||||
(cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT,
|
||||
numVertices);
|
||||
num_draw_call++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(VIDEO, "OpcodeDecoding::Decode: Illegal command %02x", cmd_byte);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
INCSTAT(stats.numDListsCalled);
|
||||
INCSTAT(stats.thisFrame.numDListsCalled);
|
||||
// un-swap
|
||||
Statistics::SwapDL();
|
||||
}
|
||||
dl->num_bp_reg = num_bp_reg;
|
||||
dl->num_cp_reg = num_cp_reg;
|
||||
dl->num_draw_call = num_draw_call;
|
||||
dl->num_index_xf = num_index_xf;
|
||||
dl->num_xf_reg = num_xf_reg;
|
||||
// reset to the old pointer
|
||||
g_pVideoData = old_pVideoData;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The only sensible way to detect changes to vertex data is to convert several times
|
||||
// and hash the output.
|
||||
|
||||
// Second pass - compile
|
||||
// Since some commands can affect the size of other commands, we really have no choice
|
||||
// but to compile as we go, interpreting the list. We can't compile and then execute, we must
|
||||
// compile AND execute at the same time. The second time the display list gets called, we already
|
||||
// have the compiled code so we don't have to interpret anymore, we just run it.
|
||||
bool CompileAndRunDisplayList(u32 address, int size, CachedDisplayList *dl)
|
||||
{
|
||||
u8* old_pVideoData = g_pVideoData;
|
||||
u8* startAddress = Memory_GetPtr(address);
|
||||
|
||||
// Avoid the crash if Memory_GetPtr failed ..
|
||||
if (startAddress != 0)
|
||||
{
|
||||
g_pVideoData = startAddress;
|
||||
|
||||
// temporarily swap dl and non-dl (small "hack" for the stats)
|
||||
Statistics::SwapDL();
|
||||
|
||||
u8 *end = g_pVideoData + size;
|
||||
|
||||
emitter.AlignCode4();
|
||||
dl->compiled_code = emitter.GetCodePtr();
|
||||
emitter.ABI_EmitPrologue(4);
|
||||
|
||||
while (g_pVideoData < end)
|
||||
{
|
||||
// Yet another reimplementation of the DL reading...
|
||||
int cmd_byte = DataReadU8();
|
||||
switch (cmd_byte)
|
||||
{
|
||||
case GX_NOP:
|
||||
// Execute
|
||||
// Compile
|
||||
break;
|
||||
|
||||
case GX_LOAD_CP_REG: //0x08
|
||||
{
|
||||
// Execute
|
||||
u8 sub_cmd = DataReadU8();
|
||||
u32 value = DataReadU32();
|
||||
LoadCPReg(sub_cmd, value);
|
||||
INCSTAT(stats.thisFrame.numCPLoads);
|
||||
|
||||
// Compile
|
||||
emitter.ABI_CallFunctionCC((void *)&LoadCPReg, sub_cmd, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case GX_LOAD_XF_REG:
|
||||
{
|
||||
// Execute
|
||||
u32 Cmd2 = DataReadU32();
|
||||
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
||||
u32 xf_address = Cmd2 & 0xFFFF;
|
||||
// TODO - speed this up. pshufb?
|
||||
DataPointer real_data_buffer = (DataPointer) new u8[transfer_size * 4];
|
||||
u32 *data_buffer = (u32*)real_data_buffer;
|
||||
for (int i = 0; i < transfer_size; i++)
|
||||
data_buffer[i] = DataReadU32();
|
||||
LoadXFReg(transfer_size, xf_address, data_buffer);
|
||||
INCSTAT(stats.thisFrame.numXFLoads);
|
||||
dl->Vdata[dl->BufferCount] = real_data_buffer;
|
||||
dl->BufferCount++;
|
||||
// Compile
|
||||
emitter.ABI_CallFunctionCCP((void *)&LoadXFReg, transfer_size, xf_address, data_buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case GX_LOAD_INDX_A: //used for position matrices
|
||||
{
|
||||
u32 value = DataReadU32();
|
||||
// Execute
|
||||
LoadIndexedXF(value, 0xC);
|
||||
// Compile
|
||||
emitter.ABI_CallFunctionCC((void *)&LoadIndexedXF, value, 0xC);
|
||||
}
|
||||
break;
|
||||
case GX_LOAD_INDX_B: //used for normal matrices
|
||||
{
|
||||
u32 value = DataReadU32();
|
||||
// Execute
|
||||
LoadIndexedXF(value, 0xD);
|
||||
// Compile
|
||||
emitter.ABI_CallFunctionCC((void *)&LoadIndexedXF, value, 0xD);
|
||||
}
|
||||
break;
|
||||
case GX_LOAD_INDX_C: //used for postmatrices
|
||||
{
|
||||
u32 value = DataReadU32();
|
||||
// Execute
|
||||
LoadIndexedXF(value, 0xE);
|
||||
// Compile
|
||||
emitter.ABI_CallFunctionCC((void *)&LoadIndexedXF, value, 0xE);
|
||||
}
|
||||
break;
|
||||
case GX_LOAD_INDX_D: //used for lights
|
||||
{
|
||||
u32 value = DataReadU32();
|
||||
// Execute
|
||||
LoadIndexedXF(value, 0xF);
|
||||
// Compile
|
||||
emitter.ABI_CallFunctionCC((void *)&LoadIndexedXF, value, 0xF);
|
||||
}
|
||||
break;
|
||||
|
||||
case GX_CMD_CALL_DL:
|
||||
{
|
||||
u32 addr= DataReadU32();
|
||||
u32 count = DataReadU32();
|
||||
ExecuteDisplayList(addr, count);
|
||||
emitter.ABI_CallFunctionCC((void *)&ExecuteDisplayList, address, count);
|
||||
}
|
||||
break;
|
||||
|
||||
case GX_CMD_UNKNOWN_METRICS:
|
||||
// zelda 4 swords calls it and checks the metrics registers after that
|
||||
break;
|
||||
|
||||
case GX_CMD_INVL_VC:// Invalidate (vertex cache?)
|
||||
DEBUG_LOG(VIDEO, "Invalidate (vertex cache?)");
|
||||
break;
|
||||
|
||||
case GX_LOAD_BP_REG: //0x61
|
||||
{
|
||||
u32 bp_cmd = DataReadU32();
|
||||
// Execute
|
||||
LoadBPReg(bp_cmd);
|
||||
INCSTAT(stats.thisFrame.numBPLoads);
|
||||
// Compile
|
||||
emitter.ABI_CallFunctionC((void *)&LoadBPReg, bp_cmd);
|
||||
}
|
||||
break;
|
||||
|
||||
// draw primitives
|
||||
default:
|
||||
if (cmd_byte & 0x80)
|
||||
{
|
||||
// load vertices (use computed vertex size from FifoCommandRunnable above)
|
||||
|
||||
// Execute
|
||||
u16 numVertices = DataReadU16();
|
||||
|
||||
u64 pre_draw_video_data = (u64)g_pVideoData;
|
||||
|
||||
u8* StartAddress = VertexManager::s_pBaseBufferPointer;
|
||||
VertexManager::Flush();
|
||||
VertexLoaderManager::RunVertices(
|
||||
cmd_byte & GX_VAT_MASK, // Vertex loader index (0 - 7)
|
||||
(cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT,
|
||||
numVertices);
|
||||
u8* EndAddress = VertexManager::s_pCurBufferPointer;
|
||||
u32 Vdatasize = (u32)(EndAddress - StartAddress);
|
||||
if (size > 0)
|
||||
{
|
||||
// Compile
|
||||
DataPointer NewData = (DataPointer)new u8[Vdatasize];
|
||||
memcpy(NewData,StartAddress,Vdatasize);
|
||||
dl->Vdata[dl->BufferCount] = NewData;
|
||||
dl->BufferCount++;
|
||||
emitter.ABI_CallFunctionCCCP((void *)&VertexLoaderManager::RunCompiledVertices,cmd_byte & GX_VAT_MASK, (cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT, numVertices, NewData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(VIDEO, "DLCache::CompileAndRun: Illegal command %02x", cmd_byte);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
emitter.ABI_EmitEpilogue(4);
|
||||
INCSTAT(stats.numDListsCalled);
|
||||
INCSTAT(stats.thisFrame.numDListsCalled);
|
||||
Statistics::SwapDL();
|
||||
}
|
||||
g_pVideoData = old_pVideoData;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
dlcode_cache = (DataPointer)AllocateExecutableMemory(DL_CODE_CACHE_SIZE, false); // Don't need low memory.
|
||||
emitter.SetCodePtr(dlcode_cache);
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
Clear();
|
||||
FreeMemoryPages(dlcode_cache, DL_CODE_CACHE_SIZE);
|
||||
dlcode_cache = NULL;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
DLMap::iterator iter = dl_map.begin();
|
||||
while (iter != dl_map.end()) {
|
||||
CachedDisplayList &entry = iter->second;
|
||||
VdataMap::iterator viter = entry.Vdata.begin();
|
||||
while (viter != entry.Vdata.end())
|
||||
{
|
||||
DataPointer &ventry = viter->second;
|
||||
delete [] ventry;
|
||||
entry.Vdata.erase(viter++);
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
dl_map.clear();
|
||||
// Reset the cache pointers.
|
||||
emitter.SetCodePtr(dlcode_cache);
|
||||
}
|
||||
|
||||
void ProgressiveCleanup()
|
||||
{
|
||||
DLMap::iterator iter = dl_map.begin();
|
||||
while (iter != dl_map.end()) {
|
||||
CachedDisplayList &entry = iter->second;
|
||||
int limit = iter->second.uncachable ? 1200 : 400;
|
||||
if (entry.frame_count < frameCount - limit) {
|
||||
// entry.Destroy();
|
||||
VdataMap::iterator viter = entry.Vdata.begin();
|
||||
while (viter != entry.Vdata.end())
|
||||
{
|
||||
DataPointer &ventry = viter->second;
|
||||
delete [] ventry;
|
||||
entry.Vdata.erase(viter++);
|
||||
}
|
||||
dl_map.erase(iter++); // (this is gcc standard!)
|
||||
}
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// NOTE - outside the namespace on purpose.
|
||||
bool HandleDisplayList(u32 address, u32 size)
|
||||
{
|
||||
// Disable display list caching since the benefit isn't much to write home about
|
||||
// right now...
|
||||
//Fixed DlistCaching now is fully functional benefits still marginal but when vertex data is stored here the story will be diferent :)
|
||||
//to test remove the next line;
|
||||
|
||||
if(size == 0) return false;
|
||||
u64 dl_id = DLCache::CreateMapId(address, size);
|
||||
DLCache::DLMap::iterator iter = DLCache::dl_map.find(dl_id);
|
||||
|
||||
stats.numDListsAlive = (int)DLCache::dl_map.size();
|
||||
if (iter != DLCache::dl_map.end())
|
||||
{
|
||||
DLCache::CachedDisplayList &dl = iter->second;
|
||||
if (dl.uncachable)
|
||||
{
|
||||
// We haven't compiled it - let's return false so it gets
|
||||
// interpreted.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Got one! And it's been compiled too, so let's run the compiled code!
|
||||
switch (dl.pass)
|
||||
{
|
||||
case DLCache::DLPASS_ANALYZE:
|
||||
PanicAlert("DLPASS_ANALYZE - should have been done the first pass");
|
||||
break;
|
||||
case DLCache::DLPASS_COMPILE:
|
||||
// First, check that the hash is the same as the last time.
|
||||
if (dl.dl_hash != GetHash64(Memory_GetPtr(address), size,0))
|
||||
{
|
||||
// PanicAlert("uncachable %08x", address);
|
||||
dl.uncachable = true;
|
||||
return false;
|
||||
}
|
||||
DLCache::CompileAndRunDisplayList(address, size, &dl);
|
||||
dl.pass = DLCache::DLPASS_RUN;
|
||||
break;
|
||||
case DLCache::DLPASS_RUN:
|
||||
{
|
||||
// Every N draws, check hash
|
||||
dl.check--;
|
||||
if (dl.check <= 0)
|
||||
{
|
||||
if (dl.dl_hash != GetHash64(Memory_GetPtr(address), size,0))
|
||||
{
|
||||
dl.uncachable = true;
|
||||
DLCache::VdataMap::iterator viter = dl.Vdata.begin();
|
||||
while (viter != dl.Vdata.end())
|
||||
{
|
||||
DLCache::DataPointer &ventry = viter->second;
|
||||
delete [] ventry;
|
||||
dl.Vdata.erase(viter++);
|
||||
}
|
||||
dl.BufferCount = 0;
|
||||
return false;
|
||||
}
|
||||
dl.check = dl.next_check;
|
||||
dl.next_check *= 2;
|
||||
if (dl.next_check > 1024)
|
||||
dl.next_check = 1024;
|
||||
}
|
||||
dl.frame_count= frameCount;
|
||||
u8 *old_datareader = g_pVideoData;
|
||||
((void (*)())(void*)(dl.compiled_code))();
|
||||
Statistics::SwapDL();
|
||||
ADDSTAT(stats.thisFrame.numCPLoadsInDL,dl.num_cp_reg);
|
||||
ADDSTAT(stats.thisFrame.numXFLoadsInDL,dl.num_xf_reg);
|
||||
ADDSTAT(stats.thisFrame.numBPLoadsInDL,dl.num_bp_reg);
|
||||
|
||||
ADDSTAT(stats.thisFrame.numCPLoads,dl.num_cp_reg);
|
||||
ADDSTAT(stats.thisFrame.numXFLoads,dl.num_xf_reg);
|
||||
ADDSTAT(stats.thisFrame.numBPLoads,dl.num_bp_reg);
|
||||
|
||||
INCSTAT(stats.numDListsCalled);
|
||||
INCSTAT(stats.thisFrame.numDListsCalled);
|
||||
|
||||
Statistics::SwapDL();
|
||||
g_pVideoData = old_datareader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DLCache::CachedDisplayList dl;
|
||||
|
||||
if (DLCache::AnalyzeAndRunDisplayList(address, size, &dl)) {
|
||||
dl.dl_hash = GetHash64(Memory_GetPtr(address), size,0);
|
||||
dl.pass = DLCache::DLPASS_COMPILE;
|
||||
dl.check = 1;
|
||||
dl.next_check = 1;
|
||||
DLCache::dl_map[dl_id] = dl;
|
||||
return true;
|
||||
} else {
|
||||
dl.uncachable = true;
|
||||
DLCache::dl_map[dl_id] = dl;
|
||||
return true; // don't also interpret the list.
|
||||
}
|
||||
}
|
32
Source/Core/VideoCommon/Src/DLCache.h
Normal file
32
Source/Core/VideoCommon/Src/DLCache.h
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2003-2009 Dolphin 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
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// 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 SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _DLCACHE_H
|
||||
#define _DLCACHE_H
|
||||
|
||||
bool HandleDisplayList(u32 address, u32 size);
|
||||
|
||||
namespace DLCache {
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void ProgressiveCleanup();
|
||||
void Clear();
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // _DLCACHE_H
|
@ -23,6 +23,7 @@ namespace VertexManager
|
||||
{
|
||||
|
||||
void AddVertices(int primitive, int numvertices);
|
||||
void AddCompiledVertices(int primitive, int numvertices, u8* Vdata);
|
||||
void Flush(); // flushes the current buffer
|
||||
int GetRemainingSize(); // remaining space in the current buffer.
|
||||
int GetRemainingVertices(int primitive); // remaining number of vertices that can be processed in one AddVertices call
|
||||
|
@ -680,6 +680,63 @@ void VertexLoader::RunVertices(int vtx_attr_group, int primitive, int count)
|
||||
VertexManager::AddVertices(primitive, count - startv + extraverts);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void VertexLoader::RunCompiledVertices(int vtx_attr_group, int primitive, int count, u8* Data)
|
||||
{
|
||||
DVSTARTPROFILE();
|
||||
|
||||
m_numLoadedVertices += count;
|
||||
|
||||
// Flush if our vertex format is different from the currently set.
|
||||
if (g_nativeVertexFmt != NULL && g_nativeVertexFmt != m_NativeFmt)
|
||||
{
|
||||
// We really must flush here. It's possible that the native representations
|
||||
// of the two vtx formats are the same, but we have no way to easily check that
|
||||
// now.
|
||||
VertexManager::Flush();
|
||||
// Also move the Set() here?
|
||||
}
|
||||
g_nativeVertexFmt = m_NativeFmt;
|
||||
|
||||
if (bpmem.genMode.cullmode == 3 && primitive < 5)
|
||||
{
|
||||
// if cull mode is none, ignore triangles and quads
|
||||
DataSkip(count * m_VertexSize);
|
||||
return;
|
||||
}
|
||||
|
||||
m_NativeFmt->EnableComponents(m_NativeFmt->m_components);
|
||||
|
||||
// Load position and texcoord scale factors.
|
||||
m_VtxAttr.PosFrac = g_VtxAttr[vtx_attr_group].g0.PosFrac;
|
||||
m_VtxAttr.texCoord[0].Frac = g_VtxAttr[vtx_attr_group].g0.Tex0Frac;
|
||||
m_VtxAttr.texCoord[1].Frac = g_VtxAttr[vtx_attr_group].g1.Tex1Frac;
|
||||
m_VtxAttr.texCoord[2].Frac = g_VtxAttr[vtx_attr_group].g1.Tex2Frac;
|
||||
m_VtxAttr.texCoord[3].Frac = g_VtxAttr[vtx_attr_group].g1.Tex3Frac;
|
||||
m_VtxAttr.texCoord[4].Frac = g_VtxAttr[vtx_attr_group].g2.Tex4Frac;
|
||||
m_VtxAttr.texCoord[5].Frac = g_VtxAttr[vtx_attr_group].g2.Tex5Frac;
|
||||
m_VtxAttr.texCoord[6].Frac = g_VtxAttr[vtx_attr_group].g2.Tex6Frac;
|
||||
m_VtxAttr.texCoord[7].Frac = g_VtxAttr[vtx_attr_group].g2.Tex7Frac;
|
||||
|
||||
pVtxAttr = &m_VtxAttr;
|
||||
posScale = 1.0f / float(1 << m_VtxAttr.PosFrac);
|
||||
if (m_NativeFmt->m_components & VB_HAS_UVALL)
|
||||
for (int i = 0; i < 8; i++)
|
||||
tcScale[i] = texCoordFrac[m_VtxAttr.texCoord[i].Frac];
|
||||
for (int i = 0; i < 2; i++)
|
||||
colElements[i] = m_VtxAttr.color[i].Elements;
|
||||
|
||||
if(VertexManager::GetRemainingSize() < native_stride * count)
|
||||
VertexManager::Flush();
|
||||
memcpy_gc(VertexManager::s_pCurBufferPointer, Data, native_stride * count);
|
||||
VertexManager::s_pCurBufferPointer += native_stride * count;
|
||||
VertexManager::AddVertices(primitive, count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VertexLoader::SetVAT(u32 _group0, u32 _group1, u32 _group2)
|
||||
{
|
||||
VAT vat;
|
||||
|
@ -84,6 +84,7 @@ public:
|
||||
|
||||
int GetVertexSize() const {return m_VertexSize;}
|
||||
void RunVertices(int vtx_attr_group, int primitive, int count);
|
||||
void RunCompiledVertices(int vtx_attr_group, int primitive, int count, u8* Data);
|
||||
|
||||
// For debugging / profiling
|
||||
void AppendToString(std::string *dest) const;
|
||||
|
@ -143,6 +143,14 @@ void RunVertices(int vtx_attr_group, int primitive, int count)
|
||||
g_VertexLoaders[vtx_attr_group]->RunVertices(vtx_attr_group, primitive, count);
|
||||
}
|
||||
|
||||
void RunCompiledVertices(int vtx_attr_group, int primitive, int count, u8* Data)
|
||||
{
|
||||
if (!count || !Data)
|
||||
return;
|
||||
RefreshLoader(vtx_attr_group);
|
||||
g_VertexLoaders[vtx_attr_group]->RunCompiledVertices(vtx_attr_group, primitive, count,Data);
|
||||
}
|
||||
|
||||
int GetVertexSize(int vtx_attr_group)
|
||||
{
|
||||
RefreshLoader(vtx_attr_group);
|
||||
|
@ -30,6 +30,7 @@ namespace VertexLoaderManager
|
||||
|
||||
int GetVertexSize(int vtx_attr_group);
|
||||
void RunVertices(int vtx_attr_group, int primitive, int count);
|
||||
void RunCompiledVertices(int vtx_attr_group, int primitive, int count, u8* Data);
|
||||
|
||||
// For debugging
|
||||
void AppendListToString(std::string *dest);
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Version="9,00"
|
||||
Name="VideoCommon"
|
||||
ProjectGUID="{E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}"
|
||||
RootNamespace="VideoCommon"
|
||||
@ -634,6 +634,14 @@
|
||||
RelativePath=".\Src\DataReader.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DLCache.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\DLCache.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\VertexLoader.cpp"
|
||||
>
|
||||
|
Reference in New Issue
Block a user