Compare commits

...

5 Commits

17 changed files with 1286 additions and 57 deletions

View File

@ -45,6 +45,12 @@
#include "pcsx2/VMManager.h"
#include "svnrev.h"
#include "debug.h"
#if MY_DEBUG == 1
#include <cstdlib>
extern bool savePoints;
#endif
namespace GSRunner
{
@ -141,6 +147,20 @@ bool GSRunner::InitializeConfig()
si.SetStringValue("MemoryCards", fmt::format("Slot{}_Filename", i + 1).c_str(), "");
}
#if MY_DEBUG == 1
if (false)
{
si.SetBoolValue("EmuCore/GS", "dump", true);
si.SetIntValue("EmuCore/GS", "saven", 0);
si.SetIntValue("EmuCore/GS", "savel", 100);
si.SetBoolValue("EmuCore/GS", "save", true);
si.SetBoolValue("EmuCore/GS", "savef", true);
si.SetBoolValue("EmuCore/GS", "savet", true);
si.SetBoolValue("EmuCore/GS", "savez", true);
si.SetStringValue("EmuCore/GS", "HWDumpDirectory", "C:\\Users\\tchan\\Desktop\\ps2_debug");
si.SetStringValue("EmuCore/GS", "SWDumpDirectory", "C:\\Users\\tchan\\Desktop\\ps2_debug");
}
#endif
VMManager::Internal::LoadStartupSettings();
return true;
}
@ -857,8 +877,16 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
#if MY_DEBUG == 1
extern void dumpRanges();
#endif
int wmain(int argc, wchar_t** argv)
{
#if MY_DEBUG == 1
if (savePoints)
atexit(dumpRanges);
#endif
std::vector<std::string> u8_args;
u8_args.reserve(static_cast<size_t>(argc));
for (int i = 0; i < argc; i++)

View File

@ -5,7 +5,11 @@
#include "GS/GSGL.h"
#include "GS/GS.h"
#include "GS/GSUtil.h"
#include "GS/GSState.h"
// FIXME: RENAME THIS FUNCTION AND CHANGE ARGS NAMES TO BE NICER!
// MAKE SURE BEING CALLED WITH ARGS IN THE RIGHT ORDER!
static int findmax(int tl, int br, int limit, int wm, int minuv, int maxuv)
{
// return max possible texcoord.
@ -38,10 +42,8 @@ static int findmax(int tl, int br, int limit, int wm, int minuv, int maxuv)
{
// REGION_REPEAT adhears to the original texture size, even if offset outside the texture (with MAXUV).
minuv &= limit;
if (tl < 0)
uv = minuv | maxuv; // wrap around, just use (any & mask) | fix.
else
uv = std::min(uv, minuv) | maxuv; // (any & mask) cannot be larger than mask, select br if that is smaller (not br & mask because there might be a larger value between tl and br when &'ed with the mask).
int ignore;
GSState::UsesRegionRepeat(maxuv, minuv, tl, br, &ignore, &uv);
}
return uv;
@ -130,18 +132,18 @@ GIFRegTEX0 GSDrawingContext::GetSizeFixedTEX0(const GSVector4& st, bool linear,
if (tw + th >= 19) // smaller sizes aren't worth, they just create multiple entries in the textue cache and the saved memory is less
{
tw = reduce(uv.x, tw);
th = reduce(uv.y, th);
tw = reduce(uv.x + 1, tw);
th = reduce(uv.y + 1, th);
}
if (wms == CLAMP_REGION_CLAMP || wms == CLAMP_REGION_REPEAT)
{
tw = extend(uv.x, tw);
tw = extend(uv.x + 1, tw);
}
if (wmt == CLAMP_REGION_CLAMP || wmt == CLAMP_REGION_REPEAT)
{
th = extend(uv.y, th);
th = extend(uv.y + 1, th);
}
GIFRegTEX0 res = TEX0;

View File

@ -18,10 +18,774 @@
#include <iomanip>
#include <bit>
#include "debug.h"
#define WALK_FULL_EDGE 1
int GSState::s_n = 0;
int GSState::s_last_transfer_draw_n = 0;
int GSState::s_transfer_n = 0;
#if MY_DEBUG == 1
bool savePoints = false;
int s_n_debug = -1;
int s_n_exit = -1;
int primID = 0;
int* primIDSW = 0;
std::map<int, std::tuple<int, int, int, int>> pointsHackRange;
std::map<int, std::tuple<int, int, int, int>> pointsSWRange;
std::vector<std::tuple<int, int, int, int, int>> pointsHackDebug;
std::vector<std::tuple<int, int, int, int, int>> pointsSWDebug;
std::map<std::tuple<int, int>, std::tuple<double, double, double, double>> pointsHackDebugOrig;
std::map<std::tuple<int, int>, std::tuple<double, double, double, double>> pointsSWDebugOrig;
void sortPoints(std::vector<std::tuple<int, int, int, int, int>>& v)
{
std::sort(v.begin(), v.end(), [](const std::tuple<int, int, int, int, int>& a, const std::tuple<int, int, int, int, int>& b) {
int ai[5] = {std::get<0>(a), std::get<2>(a), std::get<1>(a), std::get<3>(a), std::get<4>(a)}; // prim, y, x, u, v
int bi[5] = {std::get<0>(b), std::get<2>(b), std::get<1>(b), std::get<3>(b), std::get<4>(b)}; // prim, y, x, u, v
for (int i = 0; i < 5; i++)
{
if (ai[i] < bi[i])
return true;
if (ai[i] > bi[i])
return false;
}
return false;
});
}
void dumpRanges()
{
FILE* file = fopen("C:\\Users\\tchan\\Desktop\\log_files\\pointsHackRange.txt", "w");
for (auto it = pointsHackRange.begin(); it != pointsHackRange.end(); it++)
{
auto [x, y, u, v] = it->second;
fprintf(file, "%d,%d,%d,%d,%d\n", it->first, x, y, u, v);
}
fclose(file);
char fileName[1024];
sprintf(&fileName[0], "C:\\Users\\tchan\\Desktop\\log_files\\pointsHackDebug_%d.txt", s_n_debug);
file = fopen(fileName, "w");
sortPoints(pointsHackDebug);
for (auto it = pointsHackDebug.begin(); it != pointsHackDebug.end(); it++)
{
auto [n, x, y, u, v] = *it;
fprintf(file, "%d,%d,%d,%d,%d\n", n, x, y, u, v);
}
fclose(file);
sprintf(&fileName[0], "C:\\Users\\tchan\\Desktop\\log_files\\pointsHackDebugOrig_%d.txt", s_n_debug);
file = fopen(fileName, "w");
for (auto it = pointsHackDebugOrig.begin(); it != pointsHackDebugOrig.end(); it++)
{
auto [prim_id, vert_id] = it->first;
auto [x, y, u, v] = it->second;
fprintf(file, "%d,%d,%f,%f,%f,%f Hack\n", prim_id, vert_id, x, y, u, v);
}
fclose(file);
file = fopen("C:\\Users\\tchan\\Desktop\\log_files\\pointsSWRange.txt", "w");
for (auto it = pointsSWRange.begin(); it != pointsSWRange.end(); it++)
{
auto [x, y, u, v] = it->second;
fprintf(file, "%d,%d,%d,%d,%d\n", it->first, x, y, u, v);
}
fclose(file);
sprintf(&fileName[0], "C:\\Users\\tchan\\Desktop\\log_files\\pointsSWDebug_%d.txt", s_n_debug);
file = fopen(fileName, "w");
for (auto it = pointsSWDebug.begin(); it != pointsSWDebug.end(); it++)
{
auto [n, x, y, u, v] = *it;
fprintf(file, "%d,%d,%d,%d,%d\n", n, x, y, u, v);
}
fclose(file);
sprintf(&fileName[0], "C:\\Users\\tchan\\Desktop\\log_files\\pointsSWDebugOrig_%d.txt", s_n_debug);
file = fopen(fileName, "w");
sortPoints(pointsSWDebug);
for (auto it = pointsSWDebugOrig.begin(); it != pointsSWDebugOrig.end(); it++)
{
auto [prim_id, vert_id] = it->first;
auto [x, y, u, v] = it->second;
fprintf(file, "%d,%d,%f,%f,%f,%f SW\n", prim_id, vert_id, x, y, u, v);
}
fclose(file);
// Compare
for (auto it = pointsHackRange.begin(); it != pointsHackRange.end(); it++)
{
if (pointsSWRange.contains(it->first))
{
auto [x1, y1, u1, v1] = it->second;
auto [x2, y2, u2, v2] = pointsSWRange[it->first];
if (x1 != x2 || y1 != y2 || u1 != u2 || v1 != v2)
{
char c[1024];
sprintf(c, "Wrong range: %d; %d %d %d %d; %d %d %d %d;", it->first, x1, y1, u1, v1, x2, y2, u2, v2);
//throw std::exception(c);
}
}
}
}
#endif
__forceinline GSState::EdgeFunction GSState::GetEdgeFunction(const Point& a, const Point& b)
{
return {a.y - b.y, b.x - a.x, a.x * b.y - a.y * b.x};
}
__forceinline bool GSState::CheckEdgeFunction(double e, EdgeType edgeType)
{
if (edgeType == EdgeType::TOP || edgeType == EdgeType::LEFT)
{
return e >= 0;
}
else if (edgeType == EdgeType::RIGHT || edgeType == EdgeType::BOTTOM)
{
return e > 0;
}
else
{
pxFail("Invalid edge type");
}
}
__forceinline std::tuple<double, double> GSState::InterpolateEdgeFunctionsUV(
double e01, double e12, double e20, const Point& p0, const Point& p1, const Point& p2)
{
return {(e12 * p0.U + e20 * p1.U + e01 * p2.U) / (e01 + e12 + e20), (e12 * p0.V + e20 * p1.V + e01 * p2.V) / (e01 + e12 + e20)};
}
__forceinline std::tuple<double, double, double> GSState::InterpolateEdgeFunctionsSTQ(
double e01, double e12, double e20, const Point& p0, const Point& p1, const Point& p2)
{
double S = (e12 * p0.S + e20 * p1.S + e01 * p2.S) / (e01 + e12 + e20);
double T = (e12 * p0.T + e20 * p1.T + e01 * p2.T) / (e01 + e12 + e20);
double Q = (e12 * p0.Q + e20 * p1.Q + e01 * p2.Q) / (e01 + e12 + e20);
return {S, T, Q};
}
__forceinline bool GSState::CheckXYBounds(double x, double y, int minX, int minY, int maxX, int maxY, bool dxBigger, bool dyBigger)
{
if (dxBigger)
{
return minX <= x && x <= maxX;
} else if (dyBigger)
{
return minY <= y && y <= maxY;
} else
{
return (minX <= x && x <= maxX) || (minY <= y && y <= maxY);
}
}
// Order points by y (ascending) then x (ascending)
// Make sure conforms to rule where interior of triangle is on the right of each directed edge.
__forceinline std::tuple<GSState::Point, GSState::Point, GSState::Point> GSState::SortPoints(Point p0, Point p1, Point p2)
{
if ((p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x) < 0)
{
std::swap(p1, p2);
}
Point p[3] = {p0, p1, p2};
int i0 = 0;
for (int i = 1; i < 3; ++i)
{
if (p[i].y < p[i0].y || (p[i].y == p[i0].y && p[i].x < p[i0].x))
{
i0 = i;
}
}
return {p[i0], p[(i0 + 1) % 3], p[(i0 + 2) % 3]};
}
// Order the points so that p0-p1 is a top or right edge, p1-p2 is a right or bottom edge, and p0-p2 is a left edge.
__forceinline std::tuple<GSState::Point, GSState::Point, GSState::Point, GSState::EdgeType, GSState::EdgeType, GSState::EdgeType>
GSState::SortAndClassifyTriangleVerts(Point p0, Point p1, Point p2)
{
std::tie(p0, p1, p2) = SortPoints(p0, p1, p2);
EdgeType t01 = (p0.y == p1.y) ? EdgeType::TOP : EdgeType::RIGHT;
EdgeType t12 = (p1.y < p2.y) ? EdgeType::RIGHT : (p1.y > p2.y ? EdgeType::LEFT : EdgeType::BOTTOM);
EdgeType t20 = EdgeType::LEFT;
return {p0, p1, p2, t01, t12, t20};
}
__forceinline bool GSState::CheckScissor(int x, int y, int SCAX0, int SCAY0, int SCAX1, int SCAY1)
{
return (SCAX0 <= x && x <= SCAX1) && (SCAY0 <= y && y <= SCAY1);
}
// Calculate the final UV coordinates of the rasterized points by performing wrapping/clamping.
// Then adjust the min/max UV values accordingly.
// TODO: We can remove this function. Not used.
std::tuple<int, int> GSState::CalculateUVHelper(int U, int V, int W, int H, const GIFRegCLAMP& clamp)
{
// Initial clamping done on all UVs
U = std::max(-2047, std::min(2047, U));
V = std::max(-2047, std::min(2047, V));
// Clamping/wrapping for U
const int MINU = static_cast<int>(clamp.MINU);
const int MAXU = static_cast<int>(clamp.MAXU);
const int MSKU = MINU, FIXU = MAXU;
switch (clamp.WMS) // U clamping/wrapping mode
{
case CLAMP_REPEAT:
U &= W - 1; // W is a power of 2
break;
case CLAMP_CLAMP:
U = std::max(0, std::min(W - 1, U));
break;
case CLAMP_REGION_CLAMP:
U = std::max(MINU, std::min(MAXU, U));
break;
case CLAMP_REGION_REPEAT:
U = (U & MSKU) | FIXU;
break;
}
// Clamping/wrapping for V
int MINV = static_cast<int>(clamp.MINV);
int MAXV = static_cast<int>(clamp.MAXV);
int MSKV = MINV, FIXV = MAXV;
switch (clamp.WMT) // V clamping/wrapping mode
{
case CLAMP_REPEAT:
V &= H - 1; // H is a power of 2
break;
case CLAMP_CLAMP:
V = std::max(0, std::min(H - 1, V));
break;
case CLAMP_REGION_CLAMP:
V = std::max(MINV, std::min(MAXV, V));
break;
case CLAMP_REGION_REPEAT:
V = (V & MSKV) | FIXV;
break;
default:
ASSUME(0);
}
return {U, V};
}
#if MY_DEBUG == 1
bool g_switchOrient = false;
#endif
void GSState::CalculateUV(double e01, double e12, double e20, EdgeType t0, EdgeType t1, EdgeType t2,
const Point& p0, const Point& p1, const Point& p2,
int W, int H, bool FST, bool bilinear, const GIFRegCLAMP& clamp, int& minU, int& minV, int& maxU, int& maxV)
{
double U, V;
if (FST)
{
std::tie(U, V) = InterpolateEdgeFunctionsUV(e01, e12, e20, p0, p1, p2);
}
else
{
auto [S, T, Q] = InterpolateEdgeFunctionsSTQ(e01, e12, e20, p0, p1, p2);
U = W * (S / Q);
V = H * (T / Q);
}
// Clamp to valid UV range
U = std::max(-2047.0, std::min(2047.0, U));
V = std::max(-2047.0, std::min(2047.0, V));
if (bilinear)
{
#if MY_DEBUG == 1
if (GSState::s_n == s_n_debug)
{
int x = (int)((e01 * p2.x + e12 * p0.x + e20 * p1.x) / (e01 + e12 + e20));
int y = (int)((e01 * p2.y + e12 * p0.y + e20 * p1.y) / (e01 + e12 + e20));
if (g_switchOrient)
y = -y;
pointsHackDebug.push_back({primID, x, y, (int)std::floor(U - 0.5), (int)std::floor(V - 0.5)});
pointsHackDebug.push_back({primID, x, y, (int)std::floor(U - 0.5), (int)std::floor(V + 0.5)});
pointsHackDebug.push_back({primID, x, y, (int)std::floor(U + 0.5), (int)std::floor(V - 0.5)});
pointsHackDebug.push_back({primID, x, y, (int)std::floor(U + 0.5), (int)std::floor(V + 0.5)});
}
#endif
minU = std::min(static_cast<int>(std::floor(U - 0.5)), minU);
minV = std::min(static_cast<int>(std::floor(V - 0.5)), minV);
maxU = std::max(static_cast<int>(std::floor(U + 0.5)), maxU);
maxV = std::max(static_cast<int>(std::floor(V + 0.5)), maxV);
}
else
{
#if MY_DEBUG == 1
if (GSState::s_n == s_n_debug)
{
int x = (int)((e01 * p2.x + e12 * p0.x + e20 * p1.x) / (e01 + e12 + e20));
int y = (int)((e01 * p2.y + e12 * p0.y + e20 * p1.y) / (e01 + e12 + e20));
if (g_switchOrient)
y = -y;
pointsHackDebug.push_back({primID, x, y, (int)std::floor(U), (int)std::floor(V)});
}
#endif
minU = std::min(static_cast<int>(std::floor(U)), minU);
minV = std::min(static_cast<int>(std::floor(V)), minV);
maxU = std::max(static_cast<int>(std::floor(U)), maxU);
maxV = std::max(static_cast<int>(std::floor(V)), maxV);
}
}
// Test if any of the 4 scissor corners will be rasterized.
// Helps with getting the proper min/max when the triangle is scissored
void GSState::CheckScissorUV(Point p0, Point p1, Point p2, EdgeType t01, EdgeType t12, EdgeType t20, int W, int H, bool FST, bool bilinear,
const GIFRegSCISSOR& scissor, const GIFRegCLAMP& clamp, int& minU, int& minV, int& maxU, int& maxV)
{
int SCAX0 = static_cast<int>(scissor.SCAX0);
int SCAY0 = static_cast<int>(scissor.SCAY0);
int SCAX1 = static_cast<int>(scissor.SCAX1);
int SCAY1 = static_cast<int>(scissor.SCAY1);
// Get edge function coefficients
EdgeFunction E01 = GetEdgeFunction(p0, p1);
EdgeFunction E12 = GetEdgeFunction(p1, p2);
EdgeFunction E20 = GetEdgeFunction(p2, p0);
for (int x : {SCAX0, SCAX1})
{
for (int y : {SCAY0, SCAY1})
{
double e01 = E01.a * x + E01.b * y + E01.c;
double e12 = E12.a * x + E12.b * y + E12.c;
double e20 = E20.a * x + E20.b * y + E20.c;
if (CheckEdgeFunction(e01, t01) && CheckEdgeFunction(e12, t12) && CheckEdgeFunction(e20, t20))
{
CalculateUV(e01, e12, e20, t01, t12, t20, p0, p1, p2, W, H, FST, bilinear, clamp, minU, minV, maxU, maxV);
}
}
}
}
void GSState::EdgeWalkTriangleMinMaxUVImpl(Point p0, Point p1, Point p2,
EdgeType t01, EdgeType t12, EdgeType t20, int W, int H, bool FST, bool bilinear,
const GIFRegSCISSOR& scissor, const GIFRegCLAMP& clamp, bool switchOrient, int& minU, int& minV, int& maxU, int& maxV)
{
int SCAX0 = static_cast<int>(scissor.SCAX0);
int SCAY0 = static_cast<int>(scissor.SCAY0);
int SCAX1 = static_cast<int>(scissor.SCAX1);
int SCAY1 = static_cast<int>(scissor.SCAY1);
#if MY_DEBUG == 1
g_switchOrient = switchOrient;
#endif
// To traverse in the opposite direction we swap the first and second points
// and flip the whole triangle vertically (this is so that right-hand interior rule can still be followed).
// This only affects XY so doesn't matter for computing UV ranges.
if (switchOrient)
{
std::swap(p0, p1);
std::swap(t12, t20);
p0.y = -p0.y;
p1.y = -p1.y;
p2.y = -p2.y;
std::tie(SCAY0, SCAY1) = std::tuple(-SCAY1, -SCAY0);
};
// Get edge function coefficients
EdgeFunction E01 = GetEdgeFunction(p0, p1);
EdgeFunction E12 = GetEdgeFunction(p1, p2);
EdgeFunction E20 = GetEdgeFunction(p2, p0);
// Initialize deltas and steps
double dx = p1.x - p0.x;
double dy = p1.y - p0.y;
int sx = (dx > 0) ? 1.0 : -1.0;
int sy = (dy > 0) ? 1.0 : -1.0;
// Initialize starting point by rounding correctly
// Use right-hand-interior rule and brute force case analysis to get the correct starting point
// FIXME: THIS IS WRONG!!! THERE CAN REALLY BE 3 CANDIDDATE POINTS
int x, y;
if (dy == 0.0)
{
if (dx > 0.0)
{
x = static_cast<int>(std::floor(p0.x));
y = static_cast<int>(std::ceil(p0.y));
sy = 1;
}
else
{
x = static_cast<int>(std::ceil(p0.x));
y = static_cast<int>(std::floor(p0.y));
sy = -1;
}
}
else if (dx == 0.0)
{
if (dy > 0.0)
{
y = static_cast<int>(std::floor(p0.y));
x = static_cast<int>(std::floor(p0.x));
sx = -1;
}
else
{
y = static_cast<int>(std::ceil(p0.y));
x = static_cast<int>(std::ceil(p0.x));
sx = 1;
}
}
else if ((dx > 0.0) && (dy > 0.0))
{
if (E01.a * std::ceil(p0.x) + E01.b * std::ceil(p0.y) + E01.c >= 0.0)
{
x = static_cast<int>(std::ceil(p0.x));
y = static_cast<int>(std::ceil(p0.y));
}
else if (E01.a * std::floor(p0.x) + E01.b * std::ceil(p0.y) + E01.c >= 0.0)
{
x = static_cast<int>(std::floor(p0.x));
y = static_cast<int>(std::ceil(p0.y));
}
else
{
// Should be unreachable
pxFail("Invalid edge function");
}
}
else if ((dx < 0.0) && (dy > 0.0))
{
if (E01.a * std::floor(p0.x) + E01.b * std::ceil(p0.y) + E01.c >= 0)
{
x = static_cast<int>(std::floor(p0.x));
y = static_cast<int>(std::ceil(p0.y));
}
else if (E01.a * std::floor(p0.x) + E01.b * std::floor(p0.y) + E01.c >= 0)
{
x = static_cast<int>(std::floor(p0.x));
y = static_cast<int>(std::floor(p0.y));
}
else
{
// Should be unreachable
pxFail("Invalid edge function");
}
}
else if ((dx < 0.0) && (dy < 0.0))
{
if (E01.a * std::floor(p0.x) + E01.b * std::floor(p0.y) + E01.c >= 0)
{
x = static_cast<int>(std::floor(p0.x));
y = static_cast<int>(std::floor(p0.y));
}
else if (E01.a * std::ceil(p0.x) + E01.b * std::floor(p0.y) + E01.c >= 0)
{
x = static_cast<int>(std::ceil(p0.x));
y = static_cast<int>(std::floor(p0.y));
}
else
{
// Should be unreachable
pxFail("Invalid edge function");
}
}
else if ((dx > 0) && (dy < 0))
{
if (E01.a * std::ceil(p0.x) + E01.b * std::floor(p0.y) + E01.c >= 0)
{
x = static_cast<int>(std::ceil(p0.x));
y = static_cast<int>(std::floor(p0.y));
}
else if (E01.a * std::ceil(p0.x) + E01.b * std::ceil(p0.y) + E01.c >= 0)
{
x = static_cast<int>(std::ceil(p0.x));
y = static_cast<int>(std::ceil(p0.y));
}
else
{
pxFail("Invalid edge function");
}
}
// Initialize edge function values
double e01 = E01.a * x + E01.b * y + E01.c;
double e12 = E12.a * x + E12.b * y + E12.c;
double e20 = E20.a * x + E20.b * y + E20.c;
// Bounding box of the edge
const int minX = static_cast<int>(std::floor(std::min(p0.x, p1.x)));
const int minY = static_cast<int>(std::floor(std::min(p0.y, p1.y)));
const int maxX = static_cast<int>(std::ceil(std::max(p0.x, p1.x)));
const int maxY = static_cast<int>(std::ceil(std::max(p0.y, p1.y)));
bool dxBigger = std::abs(dx) > std::abs(dy);
bool dyBigger = std::abs(dy) > std::abs(dx);
// Walk along the longest axis
while (CheckXYBounds(x, y, minX, minY, maxX, maxY, dxBigger, dyBigger)) // Check if the current point is inside the bounds of the line
{
// Update min/max UV values if the current point is inside the triangle and XY is inside the scissor
if (CheckEdgeFunction(e01, t01) && CheckEdgeFunction(e12, t12) && CheckEdgeFunction(e20, t20) && CheckScissor(x, y, SCAX0, SCAY0, SCAX1, SCAY1))
{
CalculateUV(e01, e12, e20, t01, t12, t20, p0, p1, p2, W, H, FST, bilinear, clamp, minU, minV, maxU, maxV);
// FIXME: TEST WITH dx == 0 and dy == 0 optimization for
// horiz and vert edges!
if (!WALK_FULL_EDGE)
// if (!WALK_FULL_EDGE || dx == 0 || dy == 0)
{
// Break after the first rasterizable point. If a horizontal or vertical edge, this should be sufficient
// since we walk along the edge in both directions.
// Otherwise, this might not always be the "most outside" point but should be much faster.
break;
}
}
// Test the horizontal and vertical edge functions
double e01x = e01 + E01.a * sx;
double e01y = e01 + E01.b * sy;
if (!CheckEdgeFunction(e01x, t01))
{
// Cannot go horizontally, so go vertically
y += sy;
e01 += E01.b * sy;
e12 += E12.b * sy;
e20 += E20.b * sy;
}
else if (!CheckEdgeFunction(e01y, t01))
{
// Cannot go vertically, so go horizontally
x += sx;
e01 += E01.a * sx;
e12 += E12.a * sx;
e20 += E20.a * sx;
}
else if (e01x < e01y)
{
// Can go both, but horizontal is closer to the line
x += sx;
e01 += E01.a * sx;
e12 += E12.a * sx;
e20 += E20.a * sx;
}
else if (e01x > e01y)
{
// Can go both, but diagonal is closer to the line
y += sy;
e01 += E01.b * sy;
e12 += E12.b * sy;
e20 += E20.b * sy;
}
else
{
// Should be unreachable
pxFail("Invalid edge function");
}
}
}
void GSState::EdgeWalkTriangleMinMaxUV(Point p0, Point p1, Point p2, int W, int H, bool FST, bool bilinear, GIFRegSCISSOR scissor, GIFRegCLAMP clamp,
int& minU, int& minV, int& maxU, int& maxV)
{
// Rearrange the points in the correct order and walk along all edges
auto [v0_, v1_, v2_, t01, t12, t20] = SortAndClassifyTriangleVerts(p0, p1, p2);
// Check if the corners of the scissor region are inside the triangle
CheckScissorUV(v0_, v1_, v2_, t01, t12, t20, W, H, FST, bilinear, scissor, clamp, minU, minV, maxU, maxV);
// Walk along edges in the clockwise direction (if Y-axis points down)
EdgeWalkTriangleMinMaxUVImpl(v0_, v1_, v2_, t01, t12, t20, W, H, FST, bilinear, scissor, clamp, false, minU, minV, maxU, maxV);
EdgeWalkTriangleMinMaxUVImpl(v1_, v2_, v0_, t12, t20, t01, W, H, FST, bilinear, scissor, clamp, false, minU, minV, maxU, maxV);
EdgeWalkTriangleMinMaxUVImpl(v2_, v0_, v1_, t20, t01, t12, W, H, FST, bilinear, scissor, clamp, false, minU, minV, maxU, maxV);
// Walk along edges in the anti-clockwise direction (if Y-axis points down)
EdgeWalkTriangleMinMaxUVImpl(v0_, v1_, v2_, t01, t12, t20, W, H, FST, bilinear, scissor, clamp, true, minU, minV, maxU, maxV);
EdgeWalkTriangleMinMaxUVImpl(v1_, v2_, v0_, t12, t20, t01, W, H, FST, bilinear, scissor, clamp, true, minU, minV, maxU, maxV);
EdgeWalkTriangleMinMaxUVImpl(v2_, v0_, v1_, t20, t01, t12, W, H, FST, bilinear, scissor, clamp, true, minU, minV, maxU, maxV);
}
// Get the minimum and maximum UV coordinates of all triangles in the current vertex buffer
// This should only be called if all vertices in the buffer are complete triangles
// W and H are the texture width and height
// minU, minV, maxU, maxV are the current min/max UV values and the output
// bilinear is whether or not to use bilinear interpolation (if false, nearest neighbor is used)
void GSState::GetTriangleMinMaxUV(int W, int H, bool bilinear, int& minU, int& minV, int& maxU, int& maxV) const
{
// Get XY offset values to get XY in window coordinates
int OFX = static_cast<int>(m_context->XYOFFSET.OFX);
int OFY = static_cast<int>(m_context->XYOFFSET.OFY);
pxAssert((m_index.tail % 3) == 0); // should be a multiple of 3 for triangles
// Initialize min/max UV values
minU = std::numeric_limits<int>::max();
minV = std::numeric_limits<int>::max();
maxU = std::numeric_limits<int>::min();
maxV = std::numeric_limits<int>::min();
// Iterate through each triangle and get min/max UVs
for (size_t tri_i = 0; tri_i < m_index.tail / 3; tri_i++)
{
#if MY_DEBUG == 1
primID = tri_i;
if (s_n == 58 && tri_i == 1094)
{
printf("\n");
}
#endif
Point verts[3];
for (int vert_i = 0; vert_i < 3; vert_i++)
{
Point p;
const int xi = static_cast<int>(m_vertex.buff[m_index.buff[3 * tri_i + vert_i]].XYZ.X);
const int yi = static_cast<int>(m_vertex.buff[m_index.buff[3 * tri_i + vert_i]].XYZ.Y);
p.x = static_cast<double>(xi - OFX) / 16.0;
p.y = static_cast<double>(yi - OFY) / 16.0;
if (m_draw_env->PRIM.FST)
{
p.U = static_cast<double>(m_vertex.buff[m_index.buff[3 * tri_i + vert_i]].U) / 16.0;
p.V = static_cast<double>(m_vertex.buff[m_index.buff[3 * tri_i + vert_i]].V) / 16.0;
}
else
{
p.S = static_cast<double>(m_vertex.buff[m_index.buff[3 * tri_i + vert_i]].ST.S);
p.T = static_cast<double>(m_vertex.buff[m_index.buff[3 * tri_i + vert_i]].ST.T);
p.Q = static_cast<double>(m_vertex.buff[m_index.buff[3 * tri_i + vert_i]].RGBAQ.Q);
}
verts[vert_i] = p;
}
#if MY_DEBUG == 1
if (s_n == s_n_debug)
{
pointsHackDebugOrig[{tri_i, 0}] = {verts[0].x, verts[0].y, verts[0].S, verts[0].T};
pointsHackDebugOrig[{tri_i, 1}] = {verts[1].x, verts[1].y, verts[1].S, verts[1].T};
pointsHackDebugOrig[{tri_i, 2}] = {verts[2].x, verts[2].y, verts[2].S, verts[2].T};
}
#endif
EdgeWalkTriangleMinMaxUV(verts[0], verts[1], verts[2], W, H, m_draw_env->PRIM.FST, bilinear, m_context->SCISSOR, m_context->CLAMP, minU, minV, maxU, maxV);
}
GSState::GetClampWrapMinMaxUV(W, m_context->CLAMP.WMS, m_context->CLAMP.MINU, m_context->CLAMP.MAXU, minU, maxU, minU, maxU);
GSState::GetClampWrapMinMaxUV(H, m_context->CLAMP.WMT, m_context->CLAMP.MINV, m_context->CLAMP.MAXV, minV, maxV, minV, maxV);
}
// Get the minimum and maximum UV coordinates of all sprites in the current vertex buffer
// This should only be called if all vertices in the buffer are complete sprites
// W and H are the texture width and height
// minU, minV, maxU, maxV are the current min/max UV values and the output
// bilinear is whether or not to use bilinear interpolation (if false, nearest neighbor is used)
void GSState::GetSpriteMinMaxUV(int W, int H, bool bilinear, int& minU, int& minV, int& maxU, int& maxV) const
{
// Get XY offset values to get XY in window coordinates
int OFX = static_cast<int>(m_context->XYOFFSET.OFX);
int OFY = static_cast<int>(m_context->XYOFFSET.OFY);
pxAssert((m_index.tail % 2) == 0); // should be a multiple of 2 for sprites
// Initialize min/max UV values
minU = std::numeric_limits<int>::max();
minV = std::numeric_limits<int>::max();
maxU = std::numeric_limits<int>::min();
maxV = std::numeric_limits<int>::min();
// Iterate through each sprite and get min/max UVs
for (size_t sprite_i = 0; sprite_i < m_index.tail / 2; sprite_i++)
{
Point verts[2];
for (int vert_i = 0; vert_i < 2; vert_i++)
{
int xi = static_cast<int>(m_vertex.buff[m_index.buff[2 * sprite_i + vert_i]].XYZ.X);
int yi = static_cast<int>(m_vertex.buff[m_index.buff[2 * sprite_i + vert_i]].XYZ.Y);
Point p;
p.x = static_cast<double>(xi - OFX) / 16.0;
p.y = static_cast<double>(yi - OFY) / 16.0;
if (m_draw_env->PRIM.FST)
{
p.U = static_cast<double>(m_vertex.buff[m_index.buff[2 * sprite_i + vert_i]].U) / 16.0;
p.V = static_cast<double>(m_vertex.buff[m_index.buff[2 * sprite_i + vert_i]].V) / 16.0;
}
else
{
p.U = static_cast<double>(m_vertex.buff[m_index.buff[2 * sprite_i + vert_i]].ST.S) * W;
p.T = static_cast<double>(m_vertex.buff[m_index.buff[2 * sprite_i + vert_i]].ST.T) * H;
p.Q = static_cast<double>(m_vertex.buff[m_index.buff[2 * sprite_i + vert_i]].RGBAQ.Q);
}
verts[vert_i] = p;
}
if (!m_draw_env->PRIM.FST)
{
// For sprites, always use the second Q value
verts[0].S = verts[0].S / verts[1].Q;
verts[0].T = verts[0].T / verts[1].Q;
verts[1].S = verts[1].S / verts[1].Q;
verts[1].T = verts[1].T / verts[1].Q;
}
double x0 = std::min(verts[0].x, verts[1].x);
double y0 = std::min(verts[0].y, verts[1].y);
double x1 = std::max(verts[0].x, verts[1].x);
double y1 = std::max(verts[0].y, verts[1].y);
double U0 = verts[0].x == x0 ? verts[0].U : verts[1].U;
double V0 = verts[0].y == y0 ? verts[0].V : verts[1].V;
double U1 = verts[0].x == x1 ? verts[0].U : verts[1].U;
double V1 = verts[0].y == y1 ? verts[0].V : verts[1].V;
int xVals[2] = { static_cast<int>(std::ceil(x0)), static_cast<int>(std::floor(x1)) };
int yVals[2] = { static_cast<int>(std::ceil(y0)), static_cast<int>(std::floor(y1)) };
if (std::floor(x1) == x1) // omit right edges
xVals[1]--;
if (std::floor(y1) == y1) // omit bottom edges
yVals[1]--;
// scissoring
int SCAX0 = static_cast<int>(m_context->SCISSOR.SCAX0);
int SCAY0 = static_cast<int>(m_context->SCISSOR.SCAY0);
int SCAX1 = static_cast<int>(m_context->SCISSOR.SCAX1);
int SCAY1 = static_cast<int>(m_context->SCISSOR.SCAY1);
for (int i = 0; i < 2; i++)
xVals[i] = std::max(SCAX0, std::min(xVals[i], SCAX1));
for (int i = 0; i < 2; i++)
yVals[i] = std::max(SCAY0, std::min(yVals[i], SCAY1));
if (xVals[0] <= xVals[1] && yVals[0] <= yVals[1])
{
for (double x : xVals)
{
const double U = ((x1 - x) * U0 + (x - x0) * U1) / (x1 - x0);
if (bilinear)
{
minU = std::min(static_cast<int>(std::floor(U - 0.5)), minU);
maxU = std::max(static_cast<int>(std::floor(U + 0.5)), maxU);
}
else
{
minU = std::min(static_cast<int>(std::floor(U)), minU);
maxU = std::max(static_cast<int>(std::floor(U)), maxU);
}
}
for (double y : yVals)
{
const double V = ((y1 - y) * V0 + (y - y0) * V1) / (y1 - y0);
if (bilinear)
{
minV = std::min(static_cast<int>(std::floor(V - 0.5)), minV);
maxV = std::max(static_cast<int>(std::floor(V + 0.5)), maxV);
}
else
{
minV = std::min(static_cast<int>(std::floor(V)), minV);
maxV = std::max(static_cast<int>(std::floor(V)), maxV);
}
}
}
}
GSState::GetClampWrapMinMaxUV(W, m_context->CLAMP.WMS, m_context->CLAMP.MINU, m_context->CLAMP.MAXU, minU, maxU, minU, maxU);
GSState::GetClampWrapMinMaxUV(H, m_context->CLAMP.WMT, m_context->CLAMP.MINV, m_context->CLAMP.MAXV, minV, maxV, minV, maxV);
}
static __fi bool IsAutoFlushEnabled()
{
return GSIsHardwareRenderer() ? (GSConfig.UserHacks_AutoFlush != GSHWAutoFlushLevel::Disabled) : GSConfig.AutoFlushSW;
@ -1675,6 +2439,7 @@ void GSState::FlushPrim()
}
#endif
// TODO: Put the accurate UV calculation here?
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
// Texel coordinate rounding
@ -3716,16 +4481,117 @@ __forceinline void GSState::VertexKick(u32 skip)
Flush(VERTEXCOUNT);
}
// FIXME: Replace old UsesRepeatRange to this.
// Maps the range [ min .. max ] under the region repeat function determined by MSK and FIX.
// The region repeat function is f(x) = (x & MSK) | FIX.
// Return true if f(x) != x for at least one x in [ min .. max ].
bool GSState::GetRegionRepeatMinMaxUV(int MSK, int FIX, int min, int max, int& min_out, int& max_out)
{
// If we cross from -1 to 0 combine the negative and positive parts separately
// as the below algorithm only works if min <= max as unsigned integers.
if (min < 0 && 0 <= max)
{
int min_out_1, max_out_1, min_out_2, max_out_2;
const bool modified_1 = GSState::GetRegionRepeatMinMaxUV(MSK, FIX, min, -1, min_out_1, max_out_1);
const bool modified_2 = GSState::GetRegionRepeatMinMaxUV(MSK, FIX, 0, max, min_out_2, max_out_2);
min_out = std::min(min_out_1, min_out_2);
max_out = std::max(max_out_1, max_out_2);
return modified_1 || modified_2;
}
const int cleared_bits = ~MSK & ~FIX; // Bits that are always cleared by applying msk and fix
const int set_bits = FIX; // Bits that are always set by applying msk and fix
unsigned long msb;
int variable_bits = min ^ max;
if (_BitScanReverse(&msb, variable_bits))
variable_bits |= (1 << msb) - 1; // Fill in all lower bits
const int always_set = min & ~variable_bits; // Bits that are set in every value in min...max
const int sometimes_set = min | variable_bits; // Bits that are set in at least one value in min...max
const bool sets_bits = (set_bits | always_set) != always_set; // At least one bit in min...max is set by applying msk and fix
const bool clears_bits = (cleared_bits & sometimes_set) != 0; // At least one bit in min...max is cleared by applying msk and fix
const int overwritten_variable_bits = (cleared_bits | set_bits) & variable_bits;
// A variable bit that's `0` in `min` will at some point switch to a `1` (because it's variable)
// When it does, all bits below it will switch to a `0` (that's how incrementing works)
// If the 0 to 1 switch is reflected in the final output (not masked and not replaced by a fixed value),
// the final value would be larger than the previous. Otherwise, the final value will be less.
// The true minimum value is `min` with all bits below the most significant replaced variable `0` bit cleared
const int min_overwritten_variable_zeros = ~min & overwritten_variable_bits;
if (_BitScanReverse(&msb, min_overwritten_variable_zeros))
min &= (~0u << msb);
// Similar thing for max, but the first masked `1` bit
const int max_overwritten_variable_ones = max & overwritten_variable_bits;
if (_BitScanReverse(&msb, max_overwritten_variable_ones))
max |= (1 << msb) - 1;
min_out = (MSK & min) | FIX;
max_out = (MSK & max) | FIX;
return sets_bits || clears_bits;
}
// Get the min/max texel coordinate (U or V) assuming it takes the values min .. max and is then
// wrapped/clamped according to the mode WM.
// SIZE: Width/height of texture (power of 2)
// MIN/MAX: Either the clamping range (in REGION_CLAMP mode) or the MKS/FIX parameters (in REGION_REPEAT mode)
// Returns true if any of the values are changed. I.e., if f(x) is the mapping function for clamp/wrap mode,
// return true if f(x) != x for some x in [ min .. max ]
bool GSState::GetClampWrapMinMaxUV(int SIZE, int WM, int MIN, int MAX, int min, int max, int& min_out, int& max_out)
{
const int MSK = MIN;
const int FIX = MAX;
if (WM == CLAMP_REPEAT)
{
// If we cross the SIZE boundary then we always get the largest/smallest possible wrapped value
if ((min & ~(SIZE - 1)) != (max & ~(SIZE - 1)))
{
min_out = 0;
max_out = SIZE - 1;
}
else
{
min_out = min & (SIZE - 1);
max_out = max & (SIZE - 1);
}
return 0 <= min && max <= SIZE - 1;
}
else if (WM == CLAMP_CLAMP)
{
min_out = std::max(0, std::min(SIZE - 1, min));
max_out = std::max(0, std::min(SIZE - 1, max));
return 0 <= min && max <= SIZE - 1;
}
else if (WM == CLAMP_REGION_CLAMP)
{
min_out = std::max(MIN, std::min(MAX, min));
max_out = std::max(MIN, std::min(MAX, max));
return MIN <= min && max <= MAX;
}
else if (WM == CLAMP_REGION_REPEAT)
{
return GSState::GetRegionRepeatMinMaxUV(MSK, FIX, min, max, min_out, max_out);
}
else
{
pxAssertMsg(false, "Invalid clamp/wrap mode");
return false;
}
}
/// Checks if region repeat is used (applying it does something to at least one of the values in min...max)
/// Also calculates the real min and max values seen after applying the region repeat to all values in min...max
static bool UsesRegionRepeat(int fix, int msk, int min, int max, int* min_out, int* max_out)
/// FIXME: CHANGE MEMBER NAMES AND MAKE SURE CALLS HAVE ARGS IN IN RIGHT ORDER!!!
bool GSState::UsesRegionRepeat(int fix, int msk, int min, int max, int* min_out, int* max_out)
{
if ((min < 0) != (max < 0))
{
// Algorithm doesn't work properly if bits overflow when incrementing (happens on the -1 → 0 crossing)
// Conveniently, crossing zero guarantees you use the full range
*min_out = fix;
*max_out = (fix | msk) + 1;
*max_out = fix | msk;
return true;
}
@ -3757,7 +4623,7 @@ static bool UsesRegionRepeat(int fix, int msk, int min, int max, int* min_out, i
max |= (1 << msb) - 1;
*min_out = (msk & min) | fix;
*max_out = ((msk & max) | fix) + 1;
*max_out = (msk & max) | fix;
return sets_bits || clears_bits;
}

View File

@ -22,6 +22,9 @@ public:
virtual ~GSState();
static constexpr int GetSaveStateSize();
static bool UsesRegionRepeat(int fix, int msk, int min, int max, int* min_out, int* max_out);
static bool GetRegionRepeatMinMaxUV(int MSK, int FIX, int min, int max, int& min_out, int& max_out);
static bool GetClampWrapMinMaxUV(int SIZE, int WM, int MSK, int FIX, int min, int max, int& min_out, int& max_out);
private:
// RESTRICT prevents multiple loads of the same part of the register when accessing its bitfields (the compiler is happy to know that memory writes in-between will not go there)
@ -361,6 +364,76 @@ public:
void CalculateDisplayOffset(bool scanmask);
} PCRTCDisplays;
public:
struct Point
{
double x, y;
union
{
double U;
double S;
};
union
{
double T;
double V;
};
double Q;
Point(double x = 0, double y = 0, double US = NAN, double VT = NAN, double Q = NAN)
: x(x)
, y(y)
, U(US)
, V(VT)
, Q(Q)
{
}
};
struct EdgeFunction
{
double a, b, c;
EdgeFunction(double a, double b, double c)
: a(a)
, b(b)
, c(c)
{
}
};
enum class EdgeType
{
TOP,
RIGHT,
LEFT,
BOTTOM
};
public:
static EdgeFunction GetEdgeFunction(const Point& a, const Point& b);
static bool CheckEdgeFunction(double e, EdgeType edgeType);
static std::tuple<double, double> InterpolateEdgeFunctionsUV(
double e01, double e12, double e20, const Point& p0, const Point& p1, const Point& p2);
static std::tuple<double, double, double> InterpolateEdgeFunctionsSTQ(
double e01, double e12, double e20, const Point& p0, const Point& p1, const Point& p2);
static bool CheckXYBounds(double x, double y, int minX, int minY, int maxX, int maxY, bool dxBigger, bool dyBigger);
static std::tuple<Point, Point, Point> SortPoints(Point p0, Point p1, Point p2);
static __forceinline bool CheckScissor(int x, int y, int SCAX0, int SCAY0, int SCAX1, int SCAY1);
static std::tuple<Point, Point, Point, EdgeType, EdgeType, EdgeType> SortAndClassifyTriangleVerts(Point p0, Point p1, Point p2);
static std::tuple<int, int> CalculateUVHelper(int U, int V, int W, int H, const GIFRegCLAMP& clamp);
static void CalculateUV(double e01, double e12, double e20, EdgeType t0, EdgeType t1, EdgeType t2,
const Point& p0, const Point& p1, const Point& p2,
int W, int H, bool FST, bool bilinear, const GIFRegCLAMP& clamp, int& minU, int& minV, int& maxU, int& maxV);
static void CheckScissorUV(Point p0, Point p1, Point p2, EdgeType t01, EdgeType t12, EdgeType t20, int W, int H, bool FST, bool bilinear,
const GIFRegSCISSOR& scissor, const GIFRegCLAMP& clamp, int& minU, int& minV, int& maxU, int& maxV);
static void EdgeWalkTriangleMinMaxUVImpl(Point p0, Point p1, Point p2, EdgeType t01, EdgeType t12, EdgeType t20, int W, int H, bool FST, bool bilinear,
const GIFRegSCISSOR& scissor, const GIFRegCLAMP& clamp, bool switchOrient, int& minU, int& minV, int& maxU, int& maxV);
static void EdgeWalkTriangleMinMaxUV(Point p0, Point p1, Point p2, int W, int H, bool FST, bool bilinear, GIFRegSCISSOR scissor, GIFRegCLAMP clamp,
int& minU, int& minV, int& maxU, int& maxV);
void GetTriangleMinMaxUV(int W, int H, bool bilinear, int& minU, int& minV, int& maxU, int& maxV) const;
void GetSpriteMinMaxUV(int W, int H, bool bilinear, int& minU, int& minV, int& maxU, int& maxV) const;
public:
/// Returns the appropriate directory for draw dumping.
static std::string GetDrawDumpPath(const char* format, ...);

View File

@ -183,6 +183,14 @@ void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u
for (; i < (count - 1); i += 2) // 2x loop unroll
{
processVertices(v[index[i + 0]], v[index[i + 1]], true);
if (GSState::s_n == 58)
{
fprintf(stderr, "%d: %f %f %f %f\n", i, tmin.x, tmin.y, tmax.x, tmax.y);
}
if (GSState::s_n == 58 && i == 164)
{
fprintf(stderr, "hi\n");
}
}
if (count & 1)
{

View File

@ -10,8 +10,21 @@
#include <fstream>
#include "debug.h"
#if MY_DEBUG == 1
#include <algorithm>
extern bool savePoints;
extern std::map<int, std::tuple<int, int, int, int>> pointsHackRange;
extern std::map<int, std::tuple<int, int, int, int>> pointsSWRange;
extern std::vector<std::tuple<int, int, int, int, int>> pointsHackDebug;
extern std::vector<std::tuple<int, int, int, int, int>> pointsSWDebug;
extern int s_n_debug;
extern int* primIDSW;
#else
// Comment to disable all dynamic code generation.
#define ENABLE_JIT_RASTERIZER
#endif
#if MULTI_ISA_COMPILE_ONCE
// Lack of a better home
@ -627,8 +640,18 @@ __ri void GSDrawScanline::CDrawScanline(int pixels, int left, int top, const GSV
}
}
#if MY_DEBUG == 1
int x = left - 4;
#endif
while (1)
{
#if MY_DEBUG == 1
x += 4;
#endif
if (global.s_n == 58 && top == 188 && (*primIDSW - 1 == 1094))
{
printf("");
}
do
{
int fa = 0, za = 0;
@ -712,6 +735,33 @@ __ri void GSDrawScanline::CDrawScanline(int pixels, int left, int top, const GSV
case ZTST_GREATER: test |= zso <= zdo; break;
}
#if MY_DEBUG == 1
if (savePoints)
{
for (int i = 0; i < 4; i++)
{
if (!pointsSWRange.contains(global.s_n))
{
pointsSWRange[global.s_n] = {10000, 10000, -10000, -10000};
}
if (global.s_n == s_n_debug)
{
if (sel.ltf)
{
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, -1, -1});
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, -1, -1});
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, -1, -1});
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, -1, -1});
}
else
{
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, -1, -1});
}
}
}
}
#endif
if (test.alltrue())
continue;
}
@ -1119,6 +1169,48 @@ __ri void GSDrawScanline::CDrawScanline(int pixels, int left, int top, const GSV
uv1 = clamp.blend8(repeat, VectorI::broadcast128(global.t.mask));
}
#if MY_DEBUG == 1
if (savePoints)
{
for (int i = 0; i < 4; i++)
{
if (test.U32[i] == 0 || sel.notest)
{
if (!pointsSWRange.contains(global.s_n))
{
pointsSWRange[global.s_n] = {10000, 10000, -10000, -10000};
}
std::get<0>(pointsSWRange[global.s_n]) = std::min(std::get<0>(pointsSWRange[global.s_n]), (int)uv0.U16[i]);
std::get<1>(pointsSWRange[global.s_n]) = std::min(std::get<1>(pointsSWRange[global.s_n]), (int)uv0.U16[i + 4]);
std::get<2>(pointsSWRange[global.s_n]) = std::max(std::get<2>(pointsSWRange[global.s_n]), (int)uv0.U16[i]);
std::get<3>(pointsSWRange[global.s_n]) = std::max(std::get<3>(pointsSWRange[global.s_n]), (int)uv0.U16[i + 4]);
if (sel.ltf)
{
std::get<0>(pointsSWRange[global.s_n]) = std::min(std::get<0>(pointsSWRange[global.s_n]), (int)uv1.U16[i]);
std::get<1>(pointsSWRange[global.s_n]) = std::min(std::get<1>(pointsSWRange[global.s_n]), (int)uv1.U16[i + 4]);
std::get<2>(pointsSWRange[global.s_n]) = std::max(std::get<2>(pointsSWRange[global.s_n]), (int)uv1.U16[i]);
std::get<3>(pointsSWRange[global.s_n]) = std::max(std::get<3>(pointsSWRange[global.s_n]), (int)uv1.U16[i + 4]);
}
if (global.s_n == s_n_debug)
{
if (sel.ltf)
{
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, (int)uv0.U16[i], (int)uv0.U16[i + 4]});
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, (int)uv0.U16[i], (int)uv1.U16[i + 4]});
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, (int)uv1.U16[i], (int)uv0.U16[i + 4]});
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, (int)uv1.U16[i], (int)uv1.U16[i + 4]});
}
else
{
pointsSWDebug.push_back({*primIDSW - 1, x + i, top, (int)uv0.U16[i], (int)uv0.U16[i + 4]});
}
}
}
}
}
#endif
VectorI y0 = uv0.uph16() << (sel.tw + 3);
VectorI x0 = uv0.upl16();

View File

@ -19,6 +19,21 @@ MULTI_ISA_UNSHARED_IMPL;
int GSRasterizerData::s_counter = 0;
#include "debug.h"
#if MY_DEBUG == 1
extern bool savePoints;
extern int s_n_debug;
extern int s_n_exit;
extern int primID;
extern int* primIDSW;
extern std::map<int, std::tuple<int, int, int, int>> pointsHackRange;
extern std::map<int, std::tuple<int, int, int, int>> pointsSWRange;
extern std::vector<std::tuple<int, int, int, int, int>> pointsHackDebug;
extern std::vector<std::tuple<int, int, int, int, int>> pointsSWDebug;
extern std::map<std::tuple<int, int>, std::tuple<double, double, double, double>> pointsHackDebugOrig;
extern std::map<std::tuple<int, int>, std::tuple<double, double, double, double>> pointsSWDebugOrig;
#endif
static int compute_best_thread_height(int threads)
{
// - for more threads screen segments should be smaller to better distribute the pixels
@ -56,6 +71,10 @@ GSRasterizer::GSRasterizer(GSDrawScanline* ds, int id, int threads)
{
m_scanline[i] = (i % threads) == id ? 1 : 0;
}
#if MY_DEBUG == 1
primIDSW = &m_primcount;
#endif
}
GSRasterizer::~GSRasterizer()
@ -597,6 +616,19 @@ void GSRasterizer::DrawTriangleSection(int top, int bottom, GSVertexSW2& RESTRIC
void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u16* index)
{
#if MY_DEBUG == 1
if (GSState::s_n == s_n_debug)
{
// FIXME; WHAT IS SCALING FOR UV?
double scaleTX = 1 / (double)(1 << m_local.gd->TW) / 256.0 / 256.0;
double scaleTY = 1 / (double)(1 << m_local.gd->TH) / 256.0 / 256.0;
pointsSWDebugOrig[{m_primcount, 0}] = {vertex[index[0]].p.x, vertex[index[0]].p.y, vertex[index[0]].t.x * scaleTX, vertex[index[0]].t.y * scaleTY};
pointsSWDebugOrig[{m_primcount, 1}] = {vertex[index[1]].p.x, vertex[index[1]].p.y, vertex[index[1]].t.x * scaleTX, vertex[index[1]].t.y * scaleTY};
pointsSWDebugOrig[{m_primcount, 2}] = {vertex[index[2]].p.x, vertex[index[2]].p.y, vertex[index[2]].t.x * scaleTX, vertex[index[2]].t.y * scaleTY};
}
#endif
m_primcount++;
GSVertexSW edge;

View File

@ -10,8 +10,25 @@
MULTI_ISA_UNSHARED_IMPL;
#define USE_HACK 1
#include "debug.h"
#if MY_DEBUG == 1
extern bool savePoints;
extern int s_n_debug;
extern int s_n_exit;
extern std::map<int, std::tuple<int, int, int, int>> pointsHackRange;
extern std::map<int, std::tuple<int, int, int, int>> pointsSWRange;
extern void dumpRanges();
#endif
GSRenderer* CURRENT_ISA::makeGSRendererSW(int threads)
{
#if MY_DEBUG == 1
if (savePoints)
threads = 0;
#endif
return new GSRendererSW(threads);
}
@ -431,8 +448,6 @@ void GSRendererSW::Draw()
sd->UsePages(fb_pages, m_context->offset.fb.psm(), zb_pages, m_context->offset.zb.psm());
//
if (GSConfig.DumpGSData)
{
Sync(2);
@ -556,6 +571,11 @@ void GSRendererSW::Queue(GSRingHeap::SharedPtr<GSRasterizerData>& item)
fflush(s_fp);
}
#if MY_DEBUG == 1
sd->global.s_n = s_n;
sd->global.TW = m_context->TEX0.TW;
sd->global.TH = m_context->TEX0.TH;
#endif
m_rl->Queue(item);
// invalidate new parts rendered onto
@ -1057,7 +1077,54 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
GIFRegTEX0 TEX0 = m_context->GetSizeFixedTEX0(m_vt.m_min.t.xyxy(m_vt.m_max.t), m_vt.IsLinear(), mipmap);
GSVector4i r = GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf, true).coverage;
GSVector4i r;
#if MY_DEBUG == 1
fprintf(stderr, "%d\n", s_n);
#endif
if (m_vt.m_primclass == GS_TRIANGLE_CLASS && USE_HACK)
{
int minU = std::numeric_limits<int>::max();
int minV = std::numeric_limits<int>::max();
int maxU = std::numeric_limits<int>::min();
int maxV = std::numeric_limits<int>::min();
//GetTriangleMinMaxUV(1 << TEX0.TW, 1 << TEX0.TH, gd.sel.ltf, minU, minV, maxU, maxV);
GetTriangleMinMaxUV(1 << m_context->TEX0.TW, 1 << m_context->TEX0.TH, gd.sel.ltf, minU, minV, maxU, maxV);
r = GSVector4i(minU, minV, maxU + 1, maxV + 1);
#if MY_DEBUG == 1
if (s_n == 58)
{
printf("");
}
GSVector4i r2 = GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf, true).coverage;
if (savePoints)
{
pointsHackRange[s_n] = {minU, minV, maxU, maxV};
}
#endif
}
else if (m_vt.m_primclass == GS_SPRITE_CLASS && USE_HACK)
{
int minU = std::numeric_limits<int>::max();
int minV = std::numeric_limits<int>::max();
int maxU = std::numeric_limits<int>::min();
int maxV = std::numeric_limits<int>::min();
//GetSpriteMinMaxUV(1 << TEX0.TW, 1 << TEX0.TH, gd.sel.ltf, minU, minV, maxU, maxV);
GetSpriteMinMaxUV(1 << m_context->TEX0.TW, 1 << m_context->TEX0.TH, gd.sel.ltf, minU, minV, maxU, maxV);
r = GSVector4i(minU, minV, maxU + 1, maxV + 1);
#if MY_DEBUG == 1
GSVector4i r2 = GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf, true).coverage;
if (savePoints)
{
pointsHackRange[s_n] = {minU, minV, maxU, maxV};
}
#endif
}
else
{
r = GetTextureMinMax(TEX0, context->CLAMP, gd.sel.ltf, true).coverage;
}
GSTextureCacheSW::Texture* t = m_tc->Lookup(TEX0, env.TEXA);

View File

@ -9,6 +9,8 @@
#include <cstdio>
#include <string>
#include "debug.h"
union GSScanlineSelector
{
struct
@ -117,6 +119,12 @@ union GSScanlineSelector
struct alignas(32) GSScanlineGlobalData // per batch variables, this is like a pixel shader constant buffer
{
// TODO: REMOVE DEBUG CODE
#if MY_DEBUG == 1
int s_n = -1;
int TW = -1, TH = -1;
#endif
GSScanlineSelector sel;
// - the data of vm, tex may change, multi-threaded drawing must be finished before that happens, clut and dimx are copies

View File

@ -215,7 +215,8 @@ bool GSTextureCacheSW::Texture::Update(const GSVector4i& rect)
GSVector4i r = rect;
r = r.ralign<Align_Outside>(bs);
// FIXME: Is this an ok rintersect hack to make sure we don't read outside the texture?
r = r.ralign<Align_Outside>(bs).rintersect(GSVector4i(0, 0, tw, th));
if (r.eq(GSVector4i(0, 0, tw, th)))
{

View File

@ -10,6 +10,7 @@
#include <ctime>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>

View File

@ -93,19 +93,19 @@ namespace usb_pad
u8 right_turntable = 0x80;
if (data.left_turntable_ccw > 0)
{
left_turntable -= static_cast<u8>(std::min<int>(data.left_turntable_ccw * turntable_multiplier, 0x7F));
left_turntable -= static_cast<u8>(std::min<int>(data.left_turntable_ccw, 0x7F));
}
else
{
left_turntable += static_cast<u8>(std::min<int>(data.left_turntable_cw * turntable_multiplier, 0x7F));
left_turntable += static_cast<u8>(std::min<int>(data.left_turntable_cw, 0x7F));
}
if (data.right_turntable_ccw > 0)
{
right_turntable -= static_cast<u8>(std::min<int>(data.right_turntable_ccw * turntable_multiplier, 0x7F));
right_turntable -= static_cast<u8>(std::min<int>(data.right_turntable_ccw, 0x7F));
}
else
{
right_turntable += static_cast<u8>(std::min<int>(data.right_turntable_cw * turntable_multiplier, 0x7F));
right_turntable += static_cast<u8>(std::min<int>(data.right_turntable_cw, 0x7F));
}
buf[3] = 0x80;
buf[4] = 0x80;
@ -369,19 +369,19 @@ namespace usb_pad
break;
case CID_DJ_LEFT_TURNTABLE_CW:
s->data.left_turntable_cw = static_cast<u32>(std::clamp<long>(std::lroundf(value * 128.0f), 0, 128));
s->data.left_turntable_cw = static_cast<u32>(std::clamp<long>(std::lroundf(value * s->turntable_multiplier * 128.0f), 0, 128));
break;
case CID_DJ_LEFT_TURNTABLE_CCW:
s->data.left_turntable_ccw = static_cast<u32>(std::clamp<long>(std::lroundf(value * 128.0f), 0, 128));
s->data.left_turntable_ccw = static_cast<u32>(std::clamp<long>(std::lroundf(value * s->turntable_multiplier * 128.0f), 0, 128));
break;
case CID_DJ_RIGHT_TURNTABLE_CW:
s->data.right_turntable_cw = static_cast<u32>(std::clamp<long>(std::lroundf(value * 128.0f), 0, 128));
s->data.right_turntable_cw = static_cast<u32>(std::clamp<long>(std::lroundf(value * s->turntable_multiplier * 128.0f), 0, 128));
break;
case CID_DJ_RIGHT_TURNTABLE_CCW:
s->data.right_turntable_ccw = static_cast<u32>(std::clamp<long>(std::lroundf(value * 128.0f), 0, 128));
s->data.right_turntable_ccw = static_cast<u32>(std::clamp<long>(std::lroundf(value * s->turntable_multiplier * 128.0f), 0, 128));
break;
case CID_DJ_DPAD_UP:
@ -446,8 +446,8 @@ namespace usb_pad
{"EffectsKnobRight", TRANSLATE_NOOP("USB", "Effects Knob Right"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_EFFECTSKNOB_RIGHT, GenericInputBinding::RightStickRight},
{"LeftTurntableCW", TRANSLATE_NOOP("USB", "Left Turntable Clockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_CW, GenericInputBinding::LeftStickRight},
{"LeftTurntableCCW", TRANSLATE_NOOP("USB", "Left Turntable Counterclockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_CCW, GenericInputBinding::LeftStickLeft},
{"RightTurntableCW", TRANSLATE_NOOP("USB", "Right Turntable Clockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CW, GenericInputBinding::LeftStickDown},
{"RightTurntableCCW", TRANSLATE_NOOP("USB", "Right Turntable Counterclockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CCW, GenericInputBinding::LeftStickUp},
{"RightTurntableCW", TRANSLATE_NOOP("USB", "Right Turntable Clockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CW, GenericInputBinding::LeftStickUp},
{"RightTurntableCCW", TRANSLATE_NOOP("USB", "Right Turntable Counterclockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CCW, GenericInputBinding::LeftStickDown},
{"LeftTurntableGreen", TRANSLATE_NOOP("USB", "Left Turntable Green"), nullptr, InputBindingInfo::Type::Button, CID_DJ_LEFT_GREEN, GenericInputBinding::Unknown},
{"LeftTurntableRed", TRANSLATE_NOOP("USB", "Left Turntable Red"), nullptr, InputBindingInfo::Type::Button, CID_DJ_LEFT_RED, GenericInputBinding::Unknown},
{"LeftTurntableBlue", TRANSLATE_NOOP("USB", "Left Turntable Blue"), nullptr, InputBindingInfo::Type::Button, CID_DJ_LEFT_BLUE, GenericInputBinding::Unknown},
@ -464,8 +464,8 @@ namespace usb_pad
{
static constexpr const SettingInfo info[] = {
{SettingInfo::Type::Float, "TurntableMultiplier", TRANSLATE_NOOP("USB", "Turntable Multiplier"),
TRANSLATE_NOOP("USB", "Apply a multiplier to the turntable"),
"1.00", "0.00", "100.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}};
TRANSLATE_NOOP("USB", "Apply a sensitivity multiplier to turntable rotation.\nXbox 360 turntables require a 256x multiplier, most other turntables can use the default 1x multiplier."),
"1.00", "0.00", "512.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}};
return info;
}

2
pcsx2/debug.h Normal file
View File

@ -0,0 +1,2 @@
#pragma once
#define MY_DEBUG 1

View File

@ -592,6 +592,7 @@
<ClInclude Include="CDVD\IsoHasher.h" />
<ClInclude Include="CDVD\ThreadedFileReader.h" />
<ClInclude Include="CDVD\zlib_indexed.h" />
<ClInclude Include="debug.h" />
<ClInclude Include="DebugTools\Breakpoints.h" />
<ClInclude Include="DebugTools\DebugInterface.h" />
<ClInclude Include="DebugTools\DisassemblyManager.h" />
@ -1025,4 +1026,4 @@
<Import Condition="$(Configuration.Contains(Debug)) Or $(Configuration.Contains(Devel))" Project="$(SolutionDir)3rdparty\winpixeventruntime\WinPixEventRuntime.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -220,15 +220,6 @@
<Filter Include="System\Ps2\GS\Shaders\Direct3D">
<UniqueIdentifier>{eb697f5b-85f5-424a-a7e4-8d8b73d3426e}</UniqueIdentifier>
</Filter>
<Filter Include="Tools">
<UniqueIdentifier>{9153e32b-e1e3-49ac-b490-b56adfd1692f}</UniqueIdentifier>
</Filter>
<Filter Include="Tools\Input Recording">
<UniqueIdentifier>{03ba2aa7-2cd9-48cb-93c6-fc93d5bdc938}</UniqueIdentifier>
</Filter>
<Filter Include="Tools\Input Recording\Utilities">
<UniqueIdentifier>{78c9db9c-9c7c-4385-90e7-9fa71b922f60}</UniqueIdentifier>
</Filter>
<Filter Include="System\Ps2\USB\qemu-usb">
<UniqueIdentifier>{e068b724-9319-42e5-9ea7-63d80989ea1d}</UniqueIdentifier>
</Filter>
@ -283,12 +274,21 @@
<Filter Include="System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif\Dynarec\arm64">
<UniqueIdentifier>{8aea3ae6-9722-463a-94ac-34f3738a3153}</UniqueIdentifier>
</Filter>
<Filter Include="Tools\arm64">
<UniqueIdentifier>{cf847f4e-744e-4c27-a7ac-8564726fb4e6}</UniqueIdentifier>
</Filter>
<Filter Include="System\Ps2\EmotionEngine\EE\Dynarec\arm64">
<UniqueIdentifier>{cd8ec519-2196-43f7-86de-7faced2d4296}</UniqueIdentifier>
</Filter>
<Filter Include="System\Tools">
<UniqueIdentifier>{9153e32b-e1e3-49ac-b490-b56adfd1692f}</UniqueIdentifier>
</Filter>
<Filter Include="System\Tools\Input Recording">
<UniqueIdentifier>{03ba2aa7-2cd9-48cb-93c6-fc93d5bdc938}</UniqueIdentifier>
</Filter>
<Filter Include="System\Tools\Input Recording\Utilities">
<UniqueIdentifier>{78c9db9c-9c7c-4385-90e7-9fa71b922f60}</UniqueIdentifier>
</Filter>
<Filter Include="System\Tools\arm64">
<UniqueIdentifier>{cf847f4e-744e-4c27-a7ac-8564726fb4e6}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="Docs\License.txt">
@ -1119,19 +1119,19 @@
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
<ClCompile Include="Recording\InputRecording.cpp">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="Recording\InputRecordingControls.cpp">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="Recording\InputRecordingFile.cpp">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="Recording\PadData.cpp">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="Recording\Utilities\InputRecordingLogger.cpp">
<Filter>Tools\Input Recording\Utilities</Filter>
<Filter>System\Tools\Input Recording\Utilities</Filter>
</ClCompile>
<ClCompile Include="USB\USB.cpp">
<Filter>System\Ps2\USB</Filter>
@ -1233,7 +1233,7 @@
<Filter>System\Ps2\SPU2</Filter>
</ClCompile>
<ClCompile Include="GSDumpReplayer.cpp">
<Filter>Tools</Filter>
<Filter>System\Tools</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\DX11\D3D11ShaderCache.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D11</Filter>
@ -1302,7 +1302,7 @@
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="PerformanceMetrics.cpp">
<Filter>Tools</Filter>
<Filter>System\Tools</Filter>
</ClCompile>
<ClCompile Include="Input\InputSource.cpp">
<Filter>Misc\Input</Filter>
@ -1329,7 +1329,7 @@
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="Achievements.cpp">
<Filter>Tools</Filter>
<Filter>System\Tools</Filter>
</ClCompile>
<ClCompile Include="Hotkeys.cpp">
<Filter>Misc</Filter>
@ -1432,7 +1432,7 @@
<Filter>System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif\Dynarec\arm64</Filter>
</ClCompile>
<ClCompile Include="arm64\AsmHelpers.cpp">
<Filter>Tools\arm64</Filter>
<Filter>System\Tools\arm64</Filter>
</ClCompile>
<ClCompile Include="arm64\RecStubs.cpp">
<Filter>System\Ps2\EmotionEngine\EE\Dynarec\arm64</Filter>
@ -2059,19 +2059,19 @@
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
<ClInclude Include="Recording\InputRecording.h">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClInclude>
<ClInclude Include="Recording\InputRecordingControls.h">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClInclude>
<ClInclude Include="Recording\InputRecordingFile.h">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClInclude>
<ClInclude Include="Recording\PadData.h">
<Filter>Tools\Input Recording</Filter>
<Filter>System\Tools\Input Recording</Filter>
</ClInclude>
<ClInclude Include="Recording\Utilities\InputRecordingLogger.h">
<Filter>Tools\Input Recording\Utilities</Filter>
<Filter>System\Tools\Input Recording\Utilities</Filter>
</ClInclude>
<ClInclude Include="ShaderCacheVersion.h">
<Filter>System\Include</Filter>
@ -2191,7 +2191,7 @@
<Filter>System\Ps2\IPU</Filter>
</ClInclude>
<ClInclude Include="GSDumpReplayer.h">
<Filter>Tools</Filter>
<Filter>System\Tools</Filter>
</ClInclude>
<ClInclude Include="GS.h">
<Filter>System\Ps2\GS\GIF</Filter>
@ -2391,7 +2391,7 @@
<Filter>System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif\Dynarec\arm64</Filter>
</ClInclude>
<ClInclude Include="arm64\AsmHelpers.h">
<Filter>Tools\arm64</Filter>
<Filter>System\Tools\arm64</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadJogcon.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
@ -2399,6 +2399,9 @@
<ClInclude Include="SIO\Pad\PadNegcon.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="debug.h">
<Filter>System\Ps2\GS</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include="rdebug\deci2.h">
@ -2428,4 +2431,4 @@
<Filter>System\Ps2\GS</Filter>
</Natvis>
</ItemGroup>
</Project>
</Project>

View File

@ -1595,6 +1595,14 @@ static void iopRecRecompile(const u32 startpc)
while (1)
{
BASEBLOCK* pblock = PSX_GETBLOCK(i);
if (i != startpc && pblock->GetFnptr() != (uptr)iopJITCompile)
{
// branch = 3
willbranch3 = 1;
s_nEndBlock = i;
break;
}
psxRegs.code = iopMemRead32(i);

View File

@ -2294,6 +2294,8 @@ static void recRecompile(const u32 startpc)
while (1)
{
BASEBLOCK* pblock = PC_GETBLOCK(i);
// stop before breakpoints
if (isBreakpointNeeded(i) != 0 || isMemcheckNeeded(i) != 0)
{
@ -2311,6 +2313,13 @@ static void recRecompile(const u32 startpc)
eeRecPerfLog.Write("Pagesplit @ %08X : size=%d insts", startpc, (i - startpc) / 4);
break;
}
if (pblock->GetFnptr() != (uptr)JITCompile)
{
willbranch3 = 1;
s_nEndBlock = i;
break;
}
}
//HUH ? PSM ? whut ? THIS IS VIRTUAL ACCESS GOD DAMMIT
@ -2614,6 +2623,34 @@ StartRecomp:
pxAssert((pc - startpc) >> 2 <= 0xffff);
s_pCurBlockEx->size = (pc - startpc) >> 2;
if (HWADDR(pc) <= Ps2MemSize::ExposedRam)
{
BASEBLOCKEX* oldBlock;
int i;
i = recBlocks.LastIndex(HWADDR(pc) - 4);
while ((oldBlock = recBlocks[i--]))
{
if (oldBlock == s_pCurBlockEx)
continue;
if (oldBlock->startpc >= HWADDR(pc))
continue;
if ((oldBlock->startpc + oldBlock->size * 4) <= HWADDR(startpc))
break;
if (memcmp(&recRAMCopy[oldBlock->startpc / 4], PSM(oldBlock->startpc),
oldBlock->size * 4))
{
recClear(startpc, (pc - startpc) / 4);
s_pCurBlockEx = recBlocks.Get(HWADDR(startpc));
pxAssert(s_pCurBlockEx->startpc == HWADDR(startpc));
break;
}
}
memcpy(&recRAMCopy[HWADDR(startpc) / 4], PSM(startpc), pc - startpc);
}
s_pCurBlock->SetFnptr((uptr)recPtr);
if (!(pc & 0x10000000))