Compare commits

...

5 Commits

17 changed files with 1286 additions and 57 deletions

View File

@ -45,6 +45,12 @@
#include "pcsx2/VMManager.h" #include "pcsx2/VMManager.h"
#include "svnrev.h" #include "svnrev.h"
#include "debug.h"
#if MY_DEBUG == 1
#include <cstdlib>
extern bool savePoints;
#endif
namespace GSRunner namespace GSRunner
{ {
@ -141,6 +147,20 @@ bool GSRunner::InitializeConfig()
si.SetStringValue("MemoryCards", fmt::format("Slot{}_Filename", i + 1).c_str(), ""); 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(); VMManager::Internal::LoadStartupSettings();
return true; return true;
} }
@ -857,8 +877,16 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
return DefWindowProcW(hwnd, msg, wParam, lParam); return DefWindowProcW(hwnd, msg, wParam, lParam);
} }
#if MY_DEBUG == 1
extern void dumpRanges();
#endif
int wmain(int argc, wchar_t** argv) int wmain(int argc, wchar_t** argv)
{ {
#if MY_DEBUG == 1
if (savePoints)
atexit(dumpRanges);
#endif
std::vector<std::string> u8_args; std::vector<std::string> u8_args;
u8_args.reserve(static_cast<size_t>(argc)); u8_args.reserve(static_cast<size_t>(argc));
for (int i = 0; i < argc; i++) for (int i = 0; i < argc; i++)

View File

@ -5,7 +5,11 @@
#include "GS/GSGL.h" #include "GS/GSGL.h"
#include "GS/GS.h" #include "GS/GS.h"
#include "GS/GSUtil.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) static int findmax(int tl, int br, int limit, int wm, int minuv, int maxuv)
{ {
// return max possible texcoord. // 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). // REGION_REPEAT adhears to the original texture size, even if offset outside the texture (with MAXUV).
minuv &= limit; minuv &= limit;
if (tl < 0) int ignore;
uv = minuv | maxuv; // wrap around, just use (any & mask) | fix. GSState::UsesRegionRepeat(maxuv, minuv, tl, br, &ignore, &uv);
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).
} }
return 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 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); tw = reduce(uv.x + 1, tw);
th = reduce(uv.y, th); th = reduce(uv.y + 1, th);
} }
if (wms == CLAMP_REGION_CLAMP || wms == CLAMP_REGION_REPEAT) 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) if (wmt == CLAMP_REGION_CLAMP || wmt == CLAMP_REGION_REPEAT)
{ {
th = extend(uv.y, th); th = extend(uv.y + 1, th);
} }
GIFRegTEX0 res = TEX0; GIFRegTEX0 res = TEX0;

View File

@ -18,10 +18,774 @@
#include <iomanip> #include <iomanip>
#include <bit> #include <bit>
#include "debug.h"
#define WALK_FULL_EDGE 1
int GSState::s_n = 0; int GSState::s_n = 0;
int GSState::s_last_transfer_draw_n = 0; int GSState::s_last_transfer_draw_n = 0;
int GSState::s_transfer_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() static __fi bool IsAutoFlushEnabled()
{ {
return GSIsHardwareRenderer() ? (GSConfig.UserHacks_AutoFlush != GSHWAutoFlushLevel::Disabled) : GSConfig.AutoFlushSW; return GSIsHardwareRenderer() ? (GSConfig.UserHacks_AutoFlush != GSHWAutoFlushLevel::Disabled) : GSConfig.AutoFlushSW;
@ -1675,6 +2439,7 @@ void GSState::FlushPrim()
} }
#endif #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)); m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
// Texel coordinate rounding // Texel coordinate rounding
@ -3716,16 +4481,117 @@ __forceinline void GSState::VertexKick(u32 skip)
Flush(VERTEXCOUNT); 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) /// 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 /// 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)) if ((min < 0) != (max < 0))
{ {
// Algorithm doesn't work properly if bits overflow when incrementing (happens on the -1 → 0 crossing) // 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 // Conveniently, crossing zero guarantees you use the full range
*min_out = fix; *min_out = fix;
*max_out = (fix | msk) + 1; *max_out = fix | msk;
return true; 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; max |= (1 << msb) - 1;
*min_out = (msk & min) | fix; *min_out = (msk & min) | fix;
*max_out = ((msk & max) | fix) + 1; *max_out = (msk & max) | fix;
return sets_bits || clears_bits; return sets_bits || clears_bits;
} }

View File

@ -22,6 +22,9 @@ public:
virtual ~GSState(); virtual ~GSState();
static constexpr int GetSaveStateSize(); 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: 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) // 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); void CalculateDisplayOffset(bool scanmask);
} PCRTCDisplays; } 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: public:
/// Returns the appropriate directory for draw dumping. /// Returns the appropriate directory for draw dumping.
static std::string GetDrawDumpPath(const char* format, ...); 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 for (; i < (count - 1); i += 2) // 2x loop unroll
{ {
processVertices(v[index[i + 0]], v[index[i + 1]], true); 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) if (count & 1)
{ {

View File

@ -10,8 +10,21 @@
#include <fstream> #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. // Comment to disable all dynamic code generation.
#define ENABLE_JIT_RASTERIZER #define ENABLE_JIT_RASTERIZER
#endif
#if MULTI_ISA_COMPILE_ONCE #if MULTI_ISA_COMPILE_ONCE
// Lack of a better home // 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) while (1)
{ {
#if MY_DEBUG == 1
x += 4;
#endif
if (global.s_n == 58 && top == 188 && (*primIDSW - 1 == 1094))
{
printf("");
}
do do
{ {
int fa = 0, za = 0; 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; 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()) if (test.alltrue())
continue; 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)); 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 y0 = uv0.uph16() << (sel.tw + 3);
VectorI x0 = uv0.upl16(); VectorI x0 = uv0.upl16();

View File

@ -19,6 +19,21 @@ MULTI_ISA_UNSHARED_IMPL;
int GSRasterizerData::s_counter = 0; 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) static int compute_best_thread_height(int threads)
{ {
// - for more threads screen segments should be smaller to better distribute the pixels // - 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; m_scanline[i] = (i % threads) == id ? 1 : 0;
} }
#if MY_DEBUG == 1
primIDSW = &m_primcount;
#endif
} }
GSRasterizer::~GSRasterizer() GSRasterizer::~GSRasterizer()
@ -597,6 +616,19 @@ void GSRasterizer::DrawTriangleSection(int top, int bottom, GSVertexSW2& RESTRIC
void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u16* index) 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++; m_primcount++;
GSVertexSW edge; GSVertexSW edge;

View File

@ -10,8 +10,25 @@
MULTI_ISA_UNSHARED_IMPL; 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) GSRenderer* CURRENT_ISA::makeGSRendererSW(int threads)
{ {
#if MY_DEBUG == 1
if (savePoints)
threads = 0;
#endif
return new GSRendererSW(threads); 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()); sd->UsePages(fb_pages, m_context->offset.fb.psm(), zb_pages, m_context->offset.zb.psm());
//
if (GSConfig.DumpGSData) if (GSConfig.DumpGSData)
{ {
Sync(2); Sync(2);
@ -556,6 +571,11 @@ void GSRendererSW::Queue(GSRingHeap::SharedPtr<GSRasterizerData>& item)
fflush(s_fp); 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); m_rl->Queue(item);
// invalidate new parts rendered onto // 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); 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); GSTextureCacheSW::Texture* t = m_tc->Lookup(TEX0, env.TEXA);

View File

@ -9,6 +9,8 @@
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include "debug.h"
union GSScanlineSelector union GSScanlineSelector
{ {
struct struct
@ -117,6 +119,12 @@ union GSScanlineSelector
struct alignas(32) GSScanlineGlobalData // per batch variables, this is like a pixel shader constant buffer 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; GSScanlineSelector sel;
// - the data of vm, tex may change, multi-threaded drawing must be finished before that happens, clut and dimx are copies // - 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; 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))) if (r.eq(GSVector4i(0, 0, tw, th)))
{ {

View File

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

View File

@ -93,19 +93,19 @@ namespace usb_pad
u8 right_turntable = 0x80; u8 right_turntable = 0x80;
if (data.left_turntable_ccw > 0) 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 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) 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 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[3] = 0x80;
buf[4] = 0x80; buf[4] = 0x80;
@ -369,19 +369,19 @@ namespace usb_pad
break; break;
case CID_DJ_LEFT_TURNTABLE_CW: 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; break;
case CID_DJ_LEFT_TURNTABLE_CCW: 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; break;
case CID_DJ_RIGHT_TURNTABLE_CW: 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; break;
case CID_DJ_RIGHT_TURNTABLE_CCW: 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; break;
case CID_DJ_DPAD_UP: 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}, {"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}, {"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}, {"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}, {"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::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}, {"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}, {"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}, {"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[] = { static constexpr const SettingInfo info[] = {
{SettingInfo::Type::Float, "TurntableMultiplier", TRANSLATE_NOOP("USB", "Turntable Multiplier"), {SettingInfo::Type::Float, "TurntableMultiplier", TRANSLATE_NOOP("USB", "Turntable Multiplier"),
TRANSLATE_NOOP("USB", "Apply a multiplier to the turntable"), 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", "100.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}}; "1.00", "0.00", "512.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}};
return info; 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\IsoHasher.h" />
<ClInclude Include="CDVD\ThreadedFileReader.h" /> <ClInclude Include="CDVD\ThreadedFileReader.h" />
<ClInclude Include="CDVD\zlib_indexed.h" /> <ClInclude Include="CDVD\zlib_indexed.h" />
<ClInclude Include="debug.h" />
<ClInclude Include="DebugTools\Breakpoints.h" /> <ClInclude Include="DebugTools\Breakpoints.h" />
<ClInclude Include="DebugTools\DebugInterface.h" /> <ClInclude Include="DebugTools\DebugInterface.h" />
<ClInclude Include="DebugTools\DisassemblyManager.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 Condition="$(Configuration.Contains(Debug)) Or $(Configuration.Contains(Devel))" Project="$(SolutionDir)3rdparty\winpixeventruntime\WinPixEventRuntime.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" /> <ImportGroup Label="ExtensionTargets" />
</Project> </Project>

View File

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

View File

@ -1595,6 +1595,14 @@ static void iopRecRecompile(const u32 startpc)
while (1) 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); psxRegs.code = iopMemRead32(i);

View File

@ -2294,6 +2294,8 @@ static void recRecompile(const u32 startpc)
while (1) while (1)
{ {
BASEBLOCK* pblock = PC_GETBLOCK(i);
// stop before breakpoints // stop before breakpoints
if (isBreakpointNeeded(i) != 0 || isMemcheckNeeded(i) != 0) 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); eeRecPerfLog.Write("Pagesplit @ %08X : size=%d insts", startpc, (i - startpc) / 4);
break; break;
} }
if (pblock->GetFnptr() != (uptr)JITCompile)
{
willbranch3 = 1;
s_nEndBlock = i;
break;
}
} }
//HUH ? PSM ? whut ? THIS IS VIRTUAL ACCESS GOD DAMMIT //HUH ? PSM ? whut ? THIS IS VIRTUAL ACCESS GOD DAMMIT
@ -2614,6 +2623,34 @@ StartRecomp:
pxAssert((pc - startpc) >> 2 <= 0xffff); pxAssert((pc - startpc) >> 2 <= 0xffff);
s_pCurBlockEx->size = (pc - startpc) >> 2; 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); s_pCurBlock->SetFnptr((uptr)recPtr);
if (!(pc & 0x10000000)) if (!(pc & 0x10000000))