mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-26 19:58:36 +01:00
HLE: Slice the very slow memset/memcpy variants.
When they take an especially long time, this allows thread switches meanwhile. Important for cases where they might consume more than a total frame worth of cycles in a background thread.
This commit is contained in:
1 parent
053831bf4d
commit
e1eecb475a
1 file changed
+53
-15
+53
-15
@@ -182,23 +182,33 @@ static int Replace_memcpy_jak() {
|
||||
u32 destPtr = PARAM(0);
|
||||
u32 srcPtr = PARAM(1);
|
||||
u32 bytes = PARAM(2);
|
||||
bool skip = false;
|
||||
|
||||
if (bytes == 0) {
|
||||
RETURN(destPtr);
|
||||
return 5;
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
bool sliced = false;
|
||||
static constexpr uint32_t SLICE_SIZE = 32768;
|
||||
|
||||
currentMIPS->InvalidateICache(srcPtr, bytes);
|
||||
if ((skipGPUReplacements & (int)GPUReplacementSkip::MEMCPY) == 0) {
|
||||
if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) {
|
||||
skip = gpu->PerformMemoryCopy(destPtr, srcPtr, bytes);
|
||||
}
|
||||
}
|
||||
if (!skip && bytes > SLICE_SIZE && bytes != 512 * 272 * 4) {
|
||||
// This is a very slow func. To avoid thread blocking, do a slice at a time.
|
||||
// Avoiding exactly 512 * 272 * 4 to detect videos, though.
|
||||
bytes = SLICE_SIZE;
|
||||
sliced = true;
|
||||
}
|
||||
if (!skip && bytes != 0) {
|
||||
u8 *dst = Memory::GetPointerWriteRange(destPtr, bytes);
|
||||
const u8 *src = Memory::GetPointerRange(srcPtr, bytes);
|
||||
|
||||
if (!dst || !src) {
|
||||
} else {
|
||||
if (dst && src) {
|
||||
// Jak style overlap.
|
||||
for (u32 i = 0; i < bytes; i++) {
|
||||
dst[i] = src[i];
|
||||
@@ -206,13 +216,20 @@ static int Replace_memcpy_jak() {
|
||||
}
|
||||
}
|
||||
|
||||
// Jak relies on more registers coming out right than the ABI specifies.
|
||||
// See the disassembly of the function for the explanations for these...
|
||||
currentMIPS->r[MIPS_REG_T0] = 0;
|
||||
currentMIPS->r[MIPS_REG_A0] = -1;
|
||||
currentMIPS->r[MIPS_REG_A2] = 0;
|
||||
currentMIPS->r[MIPS_REG_A3] = destPtr + bytes;
|
||||
RETURN(destPtr);
|
||||
if (sliced) {
|
||||
currentMIPS->r[MIPS_REG_A0] += SLICE_SIZE;
|
||||
currentMIPS->r[MIPS_REG_A1] += SLICE_SIZE;
|
||||
currentMIPS->r[MIPS_REG_A2] -= SLICE_SIZE;
|
||||
} else {
|
||||
// Jak relies on more registers coming out right than the ABI specifies.
|
||||
// See the disassembly of the function for the explanations for these...
|
||||
currentMIPS->r[MIPS_REG_T0] = 0;
|
||||
currentMIPS->r[MIPS_REG_A0] = -1;
|
||||
currentMIPS->r[MIPS_REG_A2] = 0;
|
||||
// Even after slicing, this ends up correct.
|
||||
currentMIPS->r[MIPS_REG_A3] = destPtr + bytes;
|
||||
RETURN(destPtr);
|
||||
}
|
||||
|
||||
if (MemBlockInfoDetailed(bytes)) {
|
||||
// It's pretty common that games will copy video data.
|
||||
@@ -231,6 +248,10 @@ static int Replace_memcpy_jak() {
|
||||
}
|
||||
}
|
||||
|
||||
if (sliced) {
|
||||
// Negative causes the function to be run again for the next slice.
|
||||
return 5 + bytes * -8 + 2;
|
||||
}
|
||||
return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop..
|
||||
}
|
||||
|
||||
@@ -364,9 +385,16 @@ static int Replace_memset_jak() {
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
bool sliced = false;
|
||||
static constexpr uint32_t SLICE_SIZE = 32768;
|
||||
if (Memory::IsVRAMAddress(destPtr) && (skipGPUReplacements & (int)GPUReplacementSkip::MEMSET) == 0) {
|
||||
skip = gpu->PerformMemorySet(destPtr, value, bytes);
|
||||
}
|
||||
if (!skip && bytes > SLICE_SIZE) {
|
||||
// This is a very slow func. To avoid thread blocking, do a slice at a time.
|
||||
bytes = SLICE_SIZE;
|
||||
sliced = true;
|
||||
}
|
||||
if (!skip && bytes != 0) {
|
||||
u8 *dst = Memory::GetPointerWriteRange(destPtr, bytes);
|
||||
if (dst) {
|
||||
@@ -374,14 +402,24 @@ static int Replace_memset_jak() {
|
||||
}
|
||||
}
|
||||
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemset");
|
||||
|
||||
if (sliced) {
|
||||
currentMIPS->r[MIPS_REG_A0] += SLICE_SIZE;
|
||||
currentMIPS->r[MIPS_REG_A2] -= SLICE_SIZE;
|
||||
|
||||
// This is approximate, and must be a negative value.
|
||||
// Negative causes the function to be run again for the next slice.
|
||||
return 5 + (int)SLICE_SIZE * -6 + 2;
|
||||
}
|
||||
|
||||
// Even after slicing, this ends up correct.
|
||||
currentMIPS->r[MIPS_REG_T0] = destPtr + bytes;
|
||||
currentMIPS->r[MIPS_REG_A2] = -1;
|
||||
currentMIPS->r[MIPS_REG_A3] = -1;
|
||||
RETURN(destPtr);
|
||||
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemset");
|
||||
|
||||
return 5 + bytes * 6 + 2; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..)
|
||||
return 5 + bytes * 6 + 2; // approximation
|
||||
}
|
||||
|
||||
static uint32_t SafeStringLen(const uint32_t ptr, uint32_t maxLen = 0x07FFFFFF) {
|
||||
@@ -1449,12 +1487,12 @@ static const ReplacementTableEntry entries[] = {
|
||||
{ "ceilf", &Replace_ceilf, 0, REPFLAG_DISABLED },
|
||||
|
||||
{ "memcpy", &Replace_memcpy, 0, 0 },
|
||||
{ "memcpy_jak", &Replace_memcpy_jak, 0, 0 },
|
||||
{ "memcpy_jak", &Replace_memcpy_jak, 0, REPFLAG_SLICED },
|
||||
{ "memcpy16", &Replace_memcpy16, 0, 0 },
|
||||
{ "memcpy_swizzled", &Replace_memcpy_swizzled, 0, 0 },
|
||||
{ "memmove", &Replace_memmove, 0, 0 },
|
||||
{ "memset", &Replace_memset, 0, 0 },
|
||||
{ "memset_jak", &Replace_memset_jak, 0, 0 },
|
||||
{ "memset_jak", &Replace_memset_jak, 0, REPFLAG_SLICED },
|
||||
{ "strlen", &Replace_strlen, 0, REPFLAG_DISABLED },
|
||||
{ "strcpy", &Replace_strcpy, 0, REPFLAG_DISABLED },
|
||||
{ "strncpy", &Replace_strncpy, 0, REPFLAG_DISABLED },
|
||||
|
||||
Reference in new issue
Block a user