mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
GE: Bound block transfers, indexed draws and list reads to valid memory
- Block transfer sizes counted the stride in pixels rather than bytes, so the range check covered a quarter of a 32-bit copy and the memcpy wrote past the end of RAM or VRAM. - Indexed draws, curves and bounding boxes now check the index buffer and every vertex the indices can reach (IsValidDrawRange). Only the start address was checked before. - The run loop's downcount stops at the end of memory, and a list that runs off the end errors out. The PRIM fast loop, its CALL, the matrix fast-loaders and END's look at the previous word are bounded the same way. - sceGeListUpdateStallAddr with an unaligned address no longer hangs the emulator, since the stall is now word aligned like the PC. - The software renderer leaves out rows that would reach past the end of VRAM's mirrors with a wide 32-bit framebuffer stride. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5b3ddcc062
commit
05cca98c31
6 files changed
+111
-74
No files matched your search
@@ -201,6 +201,25 @@ void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBo
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidIndexedDrawRange(u32 vertexAddr, u32 indexAddr, int count, u32 vertType, int vertexSize) {
|
||||
const u32 idx = vertType & GE_VTYPE_IDX_MASK;
|
||||
const int indexShift = (idx >> GE_VTYPE_IDX_SHIFT) - 1;
|
||||
if (!Memory::IsValidRange(indexAddr, count << indexShift)) {
|
||||
return false;
|
||||
}
|
||||
// Indices are at most 16 bits, so this usually saves looking at them.
|
||||
const u32 maxIndex = idx == GE_VTYPE_IDX_8BIT ? 0xFF : 0xFFFF;
|
||||
if (Memory::IsValidRange(vertexAddr, (maxIndex + 1) * vertexSize)) {
|
||||
return true;
|
||||
}
|
||||
if (count <= 0) {
|
||||
return true;
|
||||
}
|
||||
u16 lowerBound, upperBound;
|
||||
GetIndexBounds(Memory::GetPointerUnchecked(indexAddr), count, vertType, &lowerBound, &upperBound);
|
||||
return Memory::IsValidRange(vertexAddr, (upperBound + 1) * vertexSize);
|
||||
}
|
||||
|
||||
void PrintDecodedVertex(const VertexReader &vtx) {
|
||||
if (vtx.hasNormal()) {
|
||||
float nrm[3];
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "Common/Data/Collections/Hashmaps.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/LogReporting.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/Common/ShaderCommon.h"
|
||||
#include "GPU/GPUState.h"
|
||||
@@ -92,6 +93,17 @@ struct DecVtxFormat {
|
||||
|
||||
void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBound, u16 *indexUpperBound);
|
||||
|
||||
bool IsValidIndexedDrawRange(u32 vertexAddr, u32 indexAddr, int count, u32 vertType, int vertexSize);
|
||||
|
||||
// Checks that the indices of a draw, and every vertex they can reach, are in valid memory.
|
||||
// Inline since it runs on every draw, and nearly all of them are non-indexed.
|
||||
inline bool IsValidDrawRange(u32 vertexAddr, u32 indexAddr, int count, u32 vertType, int vertexSize) {
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_NONE) {
|
||||
return Memory::IsValidRange(vertexAddr, count * vertexSize);
|
||||
}
|
||||
return IsValidIndexedDrawRange(vertexAddr, indexAddr, count, vertType, vertexSize);
|
||||
}
|
||||
|
||||
inline constexpr int RoundUp4(int x) {
|
||||
return (x + 3) & ~3;
|
||||
}
|
||||
|
||||
+26
-15
@@ -696,7 +696,8 @@ u32 GPUCommon::UpdateStall(int listid, u32 newstall, bool *runList) {
|
||||
if (dl.state == PSP_GE_DL_STATE_COMPLETED)
|
||||
return SCE_KERNEL_ERROR_ALREADY;
|
||||
|
||||
dl.stall = newstall & 0x0FFFFFFF;
|
||||
// The GE fetches whole words, and an unaligned stall would never equal the PC.
|
||||
dl.stall = newstall & 0x0FFFFFFC;
|
||||
|
||||
gpuStats.perFrame.numUpdateStall++;
|
||||
|
||||
@@ -875,6 +876,12 @@ bool GPUCommon::SlowRunLoop(DisplayList &list) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// How many commands can run from pc: up to the stall, and never past the end of memory.
|
||||
static int ListDowncount(u32 pc, u32 stall) {
|
||||
const u32 toStall = stall == 0 ? 0x0FFFFFFF : (stall - pc) / 4;
|
||||
return (int)std::min(toStall, std::min(Memory::MaxSizeAtAddress(pc) / 4, (u32)0x0FFFFFFF));
|
||||
}
|
||||
|
||||
// The newPC parameter is used for jumps, we don't count cycles between.
|
||||
void GPUCommon::UpdatePC(u32 currentPC, u32 newPC) {
|
||||
// Rough estimate, 2 CPU ticks (it's double the clock rate) per GPU instruction.
|
||||
@@ -884,7 +891,7 @@ void GPUCommon::UpdatePC(u32 currentPC, u32 newPC) {
|
||||
|
||||
// Exit the runloop and recalculate things. This happens a lot in some games.
|
||||
if (currentList)
|
||||
downcount = currentList->stall == 0 ? 0x0FFFFFFF : (currentList->stall - newPC) / 4;
|
||||
downcount = ListDowncount(newPC, currentList->stall);
|
||||
else
|
||||
downcount = 0;
|
||||
}
|
||||
@@ -1003,7 +1010,7 @@ DLResult GPUCommon::ProcessDLQueue() {
|
||||
|
||||
cycleLastPC = list.pc;
|
||||
cyclesExecuted += 60;
|
||||
downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
|
||||
downcount = ListDowncount(list.pc, list.stall);
|
||||
list.state = PSP_GE_DL_STATE_RUNNING;
|
||||
list.interrupted = false;
|
||||
|
||||
@@ -1016,7 +1023,7 @@ DLResult GPUCommon::ProcessDLQueue() {
|
||||
resumingFromDebugBreak_ = false;
|
||||
// The bottom part of the gpuState loop below, that wasn't executed
|
||||
// when we bailed.
|
||||
downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
|
||||
downcount = ListDowncount(list.pc, list.stall);
|
||||
if (gpuState == GPUSTATE_STALL && list.pc != list.stall) {
|
||||
// Unstalled (Can this happen?)
|
||||
gpuState = GPUSTATE_RUNNING;
|
||||
@@ -1030,6 +1037,11 @@ DLResult GPUCommon::ProcessDLQueue() {
|
||||
if (list.pc == list.stall) {
|
||||
gpuState = GPUSTATE_STALL;
|
||||
downcount = 0;
|
||||
} else if (downcount == 0 && !Memory::IsValidAddress(list.pc)) {
|
||||
// Ran (or jumped) off the end of memory. There's nothing more to run.
|
||||
ERROR_LOG(Log::G3D, "Display list %d ran off the end of memory at %08x", listIndex, list.pc);
|
||||
gpuState = GPUSTATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (useFastRunLoop) {
|
||||
@@ -1048,7 +1060,7 @@ DLResult GPUCommon::ProcessDLQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
|
||||
downcount = ListDowncount(list.pc, list.stall);
|
||||
if (gpuState == GPUSTATE_STALL && list.pc != list.stall) {
|
||||
// Unstalled (Can this happen?)
|
||||
gpuState = GPUSTATE_RUNNING;
|
||||
@@ -1250,7 +1262,7 @@ void GPUCommon::Execute_End(u32 op, u32 diff) {
|
||||
Flush();
|
||||
}
|
||||
|
||||
const u32 prev = Memory::ReadUnchecked_U32(currentList->pc - 4);
|
||||
const u32 prev = Memory::IsValidAddress(currentList->pc - 4) ? Memory::ReadUnchecked_U32(currentList->pc - 4) : 0;
|
||||
UpdatePC(currentList->pc, currentList->pc);
|
||||
// Count in a few extra cycles on END.
|
||||
cyclesExecuted += 60;
|
||||
@@ -1480,8 +1492,7 @@ void GPUCommon::Execute_BoundingBox(u32 op, u32 diff) {
|
||||
|
||||
const void *inds = nullptr;
|
||||
if (useInds) {
|
||||
const int indexSizeShift = ((vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT) - 1;
|
||||
if (!Memory::IsValidRange(gstate_c.indexAddr, count << indexSizeShift)) {
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, count, vertType, dec->VertexSize())) {
|
||||
ERROR_LOG_REPORT_ONCE(boundingboxInds, Log::G3D, "Invalid inds in bounding box check");
|
||||
currentList->bboxResult = true;
|
||||
return;
|
||||
@@ -1680,7 +1691,7 @@ void GPUCommon::Execute_WorldMtxNum(u32 op, u32 diff) {
|
||||
// We must record the individual data commands while debugRecording_.
|
||||
bool fastLoad = !debugRecording_ && end > 0;
|
||||
// Stalling in the middle of a matrix would be stupid, I doubt this check is necessary.
|
||||
if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {
|
||||
if ((currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) || !Memory::IsValidRange(currentList->pc + 4, end * 4)) {
|
||||
fastLoad = false;
|
||||
}
|
||||
|
||||
@@ -1733,7 +1744,7 @@ void GPUCommon::Execute_ViewMtxNum(u32 op, u32 diff) {
|
||||
int i = 0;
|
||||
|
||||
bool fastLoad = !debugRecording_ && end > 0;
|
||||
if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {
|
||||
if ((currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) || !Memory::IsValidRange(currentList->pc + 4, end * 4)) {
|
||||
fastLoad = false;
|
||||
}
|
||||
|
||||
@@ -1786,7 +1797,7 @@ void GPUCommon::Execute_ProjMtxNum(u32 op, u32 diff) {
|
||||
int i = 0;
|
||||
|
||||
bool fastLoad = !debugRecording_;
|
||||
if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {
|
||||
if ((currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) || !Memory::IsValidRange(currentList->pc + 4, end * 4)) {
|
||||
fastLoad = false;
|
||||
}
|
||||
|
||||
@@ -1840,7 +1851,7 @@ void GPUCommon::Execute_TgenMtxNum(u32 op, u32 diff) {
|
||||
int i = 0;
|
||||
|
||||
bool fastLoad = !debugRecording_ && end > 0;
|
||||
if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {
|
||||
if ((currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) || !Memory::IsValidRange(currentList->pc + 4, end * 4)) {
|
||||
fastLoad = false;
|
||||
}
|
||||
|
||||
@@ -1894,7 +1905,7 @@ void GPUCommon::Execute_BoneMtxNum(u32 op, u32 diff) {
|
||||
int i = 0;
|
||||
|
||||
bool fastLoad = !debugRecording_ && end > 0;
|
||||
if (currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) {
|
||||
if ((currentList->pc < currentList->stall && currentList->pc + end * 4 >= currentList->stall) || !Memory::IsValidRange(currentList->pc + 4, end * 4)) {
|
||||
fastLoad = false;
|
||||
}
|
||||
|
||||
@@ -2387,8 +2398,8 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
|
||||
// NOTE: The sizes are only used for validity checks and memory info tracking.
|
||||
const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp;
|
||||
const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp;
|
||||
const uint32_t srcSize = ((height - 1) * srcStride) + width * bpp;
|
||||
const uint32_t dstSize = ((height - 1) * dstStride) + width * bpp;
|
||||
const uint32_t srcSize = ((height - 1) * srcStride + width) * bpp;
|
||||
const uint32_t dstSize = ((height - 1) * dstStride + width) * bpp;
|
||||
|
||||
bool srcDstOverlap = src + srcSize > dst && dst + dstSize > src;
|
||||
bool srcValid = Memory::IsValidRange(src, srcSize);
|
||||
|
||||
+25
-32
@@ -986,7 +986,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
VertexDecoder *decoder = drawEngineCommon_->GetVertexDecoder(vertTypeID);
|
||||
|
||||
// Nothing is mapped past the end of RAM or VRAM, so a draw running off the end would fault on the first read.
|
||||
if (!inds && !Memory::IsValidRange(gstate_c.vertexAddr, count * decoder->VertexSize())) {
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, count, vertexType, decoder->VertexSize())) {
|
||||
ERROR_LOG(Log::G3D, "Bad vertex range %08x (%d verts of %d bytes)!", gstate_c.vertexAddr, count, decoder->VertexSize());
|
||||
return;
|
||||
}
|
||||
@@ -1088,6 +1088,11 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
const u32_le *start = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4);
|
||||
const u32_le *src = start;
|
||||
const u32_le *stall = currentList->stall ? (const u32_le *)Memory::GetPointerUnchecked(currentList->stall) : 0;
|
||||
// Without a stall ahead, stop at the end of memory.
|
||||
const u32_le *end = start + Memory::MaxSizeAtAddress(currentList->pc + 4) / 4;
|
||||
if (!stall || stall < start || stall > end) {
|
||||
stall = end;
|
||||
}
|
||||
|
||||
// Optimized submission of sequences of PRIM. Allows us to avoid going through all the mess
|
||||
// above for each one. This can be expanded to support additional games that intersperse
|
||||
@@ -1101,7 +1106,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
if (!useFastRunLoop_ || readsFramebufferPerDraw)
|
||||
goto bail; // we're either recording or stepping, or each draw has to go alone.
|
||||
|
||||
while (src != stall) {
|
||||
while (src < stall) {
|
||||
uint32_t data = *src;
|
||||
switch (data >> 24) {
|
||||
case GE_CMD_PRIM:
|
||||
@@ -1131,19 +1136,13 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
|
||||
verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
inds = nullptr;
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, count, vertexType, decoder->VertexSize())) {
|
||||
// Bad index or vertex range, leave it to Execute_Prim.
|
||||
goto bail;
|
||||
}
|
||||
if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
const u32 indexAddr = gstate_c.indexAddr;
|
||||
const int indexShift = ((vertexType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT) - 1;
|
||||
if (!Memory::IsValidRange(gstate_c.indexAddr, count << indexShift)) {
|
||||
// Bad index range. Let's give up the fast loop.
|
||||
goto bail;
|
||||
}
|
||||
inds = Memory::GetPointerUnchecked(indexAddr);
|
||||
inds = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
} else {
|
||||
if (!Memory::IsValidRange(gstate_c.vertexAddr, count * decoder->VertexSize())) {
|
||||
// Bad vertex range, leave it to Execute_Prim.
|
||||
goto bail;
|
||||
}
|
||||
// We can extend again after submitting a normal draw.
|
||||
canExtend = isTriangle;
|
||||
}
|
||||
@@ -1267,7 +1266,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
{
|
||||
// A bone matrix probably. If not we bail.
|
||||
const u32 target = gstate_c.getRelativeAddress(data & 0x00FFFFFC);
|
||||
if (currentList->HardwareCallDepth() < 2 &&
|
||||
if (currentList->HardwareCallDepth() < 2 && Memory::IsValidRange(target, 13 * 4) &&
|
||||
(Memory::ReadUnchecked_U32(target) >> 24) == GE_CMD_BONEMATRIXDATA &&
|
||||
(Memory::ReadUnchecked_U32(target + 11 * 4) >> 24) == GE_CMD_BONEMATRIXDATA &&
|
||||
(Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET &&
|
||||
@@ -1352,17 +1351,14 @@ void GPUCommonHW::Execute_Bezier(u32 op, u32 diff) {
|
||||
const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
const void *indices = NULL;
|
||||
const u32 vertType = gstate.vertType;
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
|
||||
ERROR_LOG(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
} else if (!Memory::IsValidRange(gstate_c.vertexAddr, (op & 0xFF) * ((op >> 8) & 0xFF) * drawEngineCommon_->GetVertexDecoder(GetVertTypeID(vertType, gstate.getUVGenMode()))->VertexSize())) {
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x!", gstate_c.vertexAddr);
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, (op & 0xFF) * ((op >> 8) & 0xFF), vertType, drawEngineCommon_->GetVertexDecoder(GetVertTypeID(vertType, gstate.getUVGenMode()))->VertexSize())) {
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x (indices %08x)!", gstate_c.vertexAddr, gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
if (vertTypeIsSkinningEnabled(vertType)) {
|
||||
DEBUG_LOG_REPORT(Log::G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", vertType, (vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(vertType));
|
||||
@@ -1429,17 +1425,14 @@ void GPUCommonHW::Execute_Spline(u32 op, u32 diff) {
|
||||
const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
const void *indices = NULL;
|
||||
const u32 vertType = gstate.vertType;
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
|
||||
ERROR_LOG(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
} else if (!Memory::IsValidRange(gstate_c.vertexAddr, (op & 0xFF) * ((op >> 8) & 0xFF) * drawEngineCommon_->GetVertexDecoder(GetVertTypeID(vertType, gstate.getUVGenMode()))->VertexSize())) {
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x!", gstate_c.vertexAddr);
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, (op & 0xFF) * ((op >> 8) & 0xFF), vertType, drawEngineCommon_->GetVertexDecoder(GetVertTypeID(vertType, gstate.getUVGenMode()))->VertexSize())) {
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x (indices %08x)!", gstate_c.vertexAddr, gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
if (vertTypeIsSkinningEnabled(vertType)) {
|
||||
WARN_LOG_ONCE(unusualcurve, Log::G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", vertType, (vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(vertType));
|
||||
|
||||
@@ -262,6 +262,17 @@ void BinManager::UpdateState() {
|
||||
drawTargetAddr_ = gstate.getFrameBufAddress();
|
||||
DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1());
|
||||
DrawingCoords scissorBR(std::min(gstate.getScissorX2(), gstate.getRegionX2()), std::min(gstate.getScissorY2(), gstate.getRegionY2()));
|
||||
// fb.data points into VRAM's mirrors, which end 8MB from its start. A wide 32-bit stride can
|
||||
// reach past that within 1024 rows, so leave out the rows that would.
|
||||
const int fbStride = gstate.FrameBufStride();
|
||||
if (fbStride > 0) {
|
||||
const int bpp = gstate.FrameBufFormat() == GE_FORMAT_8888 ? 4 : 2;
|
||||
const int bytesLeft = 0x00800000 - (int)gstate.getFrameBufRawAddress() - (scissorBR.x + 1) * bpp;
|
||||
const int maxY = bytesLeft / (fbStride * bpp);
|
||||
if (scissorBR.y > maxY) {
|
||||
scissorBR.y = maxY;
|
||||
}
|
||||
}
|
||||
ScreenCoords screenScissorTL = TransformUnit::DrawingToScreen(scissorTL, 0);
|
||||
ScreenCoords screenScissorBR = TransformUnit::DrawingToScreen(scissorBR, 0);
|
||||
|
||||
|
||||
+18
-27
@@ -969,17 +969,14 @@ void SoftGPU::Execute_Prim(u32 op, u32 diff) {
|
||||
const void *indices = NULL;
|
||||
|
||||
const u32 vertType = gstate.vertType;
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
|
||||
ERROR_LOG(Log::G3D, "Software: Bad index address %08x!", gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
} else if (!Memory::IsValidRange(gstate_c.vertexAddr, count * drawEngine_->FindVertexDecoder(vertType)->VertexSize())) {
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
ERROR_LOG(Log::G3D, "Software: Bad vertex range %08x (%d verts)!", gstate_c.vertexAddr, count);
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, count, vertType, drawEngine_->FindVertexDecoder(vertType)->VertexSize())) {
|
||||
ERROR_LOG(Log::G3D, "Software: Bad vertex range %08x (%d verts, indices %08x)!", gstate_c.vertexAddr, count, gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
if (gstate.isModeThrough()) {
|
||||
cyclesExecuted += EstimateFillCycles(prim, verts, indices, count, drawEngine_->FindVertexDecoder(vertType), vertType);
|
||||
@@ -1052,17 +1049,14 @@ void SoftGPU::Execute_Bezier(u32 op, u32 diff) {
|
||||
const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
const void *indices = NULL;
|
||||
const u32 vertType = gstate.vertType;
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
|
||||
ERROR_LOG(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
} else if (!Memory::IsValidRange(gstate_c.vertexAddr, (op & 0xFF) * ((op >> 8) & 0xFF) * drawEngine_->FindVertexDecoder(vertType)->VertexSize())) {
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x!", gstate_c.vertexAddr);
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, (op & 0xFF) * ((op >> 8) & 0xFF), vertType, drawEngine_->FindVertexDecoder(vertType)->VertexSize())) {
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x (indices %08x)!", gstate_c.vertexAddr, gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
if ((vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(vertType)) {
|
||||
DEBUG_LOG(Log::G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", vertType, (vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(vertType));
|
||||
@@ -1112,17 +1106,14 @@ void SoftGPU::Execute_Spline(u32 op, u32 diff) {
|
||||
const void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
const void *indices = NULL;
|
||||
const u32 vertType = gstate.vertType;
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
|
||||
ERROR_LOG(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
} else if (!Memory::IsValidRange(gstate_c.vertexAddr, (op & 0xFF) * ((op >> 8) & 0xFF) * drawEngine_->FindVertexDecoder(vertType)->VertexSize())) {
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x!", gstate_c.vertexAddr);
|
||||
// Nothing is mapped past the end of RAM or VRAM, so this would fault on the first read.
|
||||
if (!IsValidDrawRange(gstate_c.vertexAddr, gstate_c.indexAddr, (op & 0xFF) * ((op >> 8) & 0xFF), vertType, drawEngine_->FindVertexDecoder(vertType)->VertexSize())) {
|
||||
ERROR_LOG(Log::G3D, "Bad control point range %08x (indices %08x)!", gstate_c.vertexAddr, gstate_c.indexAddr);
|
||||
return;
|
||||
}
|
||||
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
|
||||
indices = Memory::GetPointerUnchecked(gstate_c.indexAddr);
|
||||
}
|
||||
|
||||
if ((vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(vertType)) {
|
||||
DEBUG_LOG(Log::G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", vertType, (vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(vertType));
|
||||
|
||||
Reference in new issue
Block a user