Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion framework/files/runtime/include/gpu_temporal_match.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ static inline void gpu_temporal_lerp(const GpuTemporalTriangle *prev,
((double)curr->x[vertex] - prev->x[vertex]) * alpha) * scale);
result.y[vertex] = (float)(((double)prev->y[vertex] +
((double)curr->y[vertex] - prev->y[vertex]) * alpha) * scale);
result.w[vertex] = (float)weight;
result.w[vertex] = (float)weight != 0.0f ? (float)weight : -1e-30f; /* 0 would read as a corner without a weight */
result.q[vertex] = (float)((double)prev->q[vertex] +
((double)curr->q[vertex] - prev->q[vertex]) * alpha);
continue;
Expand All @@ -287,4 +287,12 @@ static inline void gpu_temporal_lerp(const GpuTemporalTriangle *prev,
*out = result;
}

/* The depth of a corner gpu_temporal_lerp placed: a weight is the corner's depth there over its depth at an unweighted end. */
static inline float gpu_temporal_lerp_depth(const GpuTemporalTriangle *curr,
const GpuTemporalTriangle *lerped, int vertex) {
const float after = curr->w[vertex] != 0.0f ? curr->w[vertex] : 1.0f;
const float here = lerped->w[vertex] != 0.0f ? lerped->w[vertex] : 1.0f;
return here > 0.0f ? curr->depth[vertex] * here / after : curr->depth[vertex];
}

#endif /* PSXRECOMP_GPU_TEMPORAL_MATCH_H */
3 changes: 2 additions & 1 deletion framework/files/runtime/src/gpu_gl_temporal.c.inc
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ static void temporal_underlay(float alpha,int covered) {
for (int k=0;k<3;++k) {
float *v=&s_temp_under[(j+k)*TEXV];
v[0]=v[t?20:7]=show?out.x[k]:0; v[1]=v[t?21:8]=show?out.y[k]:0;
v[t?23:9]=show?temporal_order(p->gone_to[ti].depth[k]*(covered?TEMP_ORDER_CLEARANCE:1.0f)):TEMP_ORDER_FAR;
v[t?23:9]=show?temporal_order(gpu_temporal_lerp_depth(&p->gone_to[ti],&out,k)*(covered?TEMP_ORDER_CLEARANCE:1.0f)):TEMP_ORDER_FAR;
}
any|=show;
}
Expand Down Expand Up @@ -890,6 +890,7 @@ static int temporal_present(float alpha,int lx,int ly,int lw,int lh) {
float *v=&s_temp_scratch[(j+k)*TEXV];
v[0]=v[t?20:7]=out.x[facing?k:0]; v[1]=v[t?21:8]=out.y[facing?k:0];
if (t) v[22]=out.q[k];
if (now->depth[k]>0.0f) v[t?23:9]=temporal_order(gpu_temporal_lerp_depth(now,&out,k));
}
} else for (int j=0;j<c->count;++j)
s_temp_scratch[j*TEXV+9]=c->first_tri<=first_world || (s_world_depth && c->first_tri<=last_world)
Expand Down
8 changes: 7 additions & 1 deletion tests/contracts/test_temporal_presenter_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ def shader(source: str, name: str) -> str:
)
require(
"glDepthFunc(covered ? GL_LESS : GL_ALWAYS); glDepthMask(covered ? GL_FALSE : GL_TRUE);" in underlay
and "v[t?23:9]=show?temporal_order(p->gone_to[ti].depth[k]*(covered?TEMP_ORDER_CLEARANCE:1.0f)):TEMP_ORDER_FAR;" in underlay
and "v[t?23:9]=show?temporal_order(gpu_temporal_lerp_depth(&p->gone_to[ti],&out,k)*(covered?TEMP_ORDER_CLEARANCE:1.0f)):TEMP_ORDER_FAR;"
in underlay
and "#define TEMP_ORDER_CLEARANCE 1.03f" in temporal
and "glDepthFunc(GL_ALWAYS); glDepthMask(GL_TRUE);" in underlay,
"the second pass must win only against current faces that lie farther away, and leave depth alone",
Expand All @@ -100,6 +101,11 @@ def shader(source: str, name: str) -> str:
" : ti<first_world ? TEMP_ORDER_FAR : TEMP_ORDER_NEAR;" in present,
"a current face must carry its camera depth, the backdrop the far plane and everything else the near plane",
)
require(
" gpu_temporal_lerp(&s->from[ti],now,alpha,&out);\n" in present
and " if (now->depth[k]>0.0f) v[t?23:9]=temporal_order(gpu_temporal_lerp_depth(now,&out,k));\n" in present,
"a face between two frames must be ordered by the depth it has there, or a wall and its pieces part",
)
require(
" temporal_submit(s,c,s->height,s_temp_scratch,split);\n"
" temporal_underlay(alpha,0);\n"
Expand Down
91 changes: 91 additions & 0 deletions tests/unit/gpu_temporal_match_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,96 @@ void invalid_geometry() {
"affine interpolation retains the zero-depth sentinel");
}

struct Point {
double x, y, z;
};

float screen_x(const Point &point) { return static_cast<float>(160.0 + 160.0 * point.x / point.z); }
float screen_y(const Point &point) { return static_cast<float>(120.0 + 160.0 * point.y / point.z); }

GpuTemporalTriangle placed(const std::array<Point, 3> &points) {
auto face = triangle();
for (int vertex = 0; vertex < 3; ++vertex) {
face.x[vertex] = screen_x(points[vertex]);
face.y[vertex] = screen_y(points[vertex]);
face.depth[vertex] = static_cast<float>(points[vertex].z);
}
return face;
}

GpuTemporalTriangle weighted(const std::array<Point, 3> &points, const std::array<Point, 3> &other) {
auto face = placed(other);
for (int vertex = 0; vertex < 3; ++vertex) {
face.w[vertex] = static_cast<float>(points[vertex].z / other[vertex].z);
face.x[vertex] = screen_x(points[vertex]) * face.w[vertex];
face.y[vertex] = screen_y(points[vertex]) * face.w[vertex];
}
return face;
}

bool close(double value, double expected) { return std::fabs(value - expected) <= 0.001 * std::fabs(expected); }

void interpolated_depth() {
const Point a_then{-50.0, 10.0, 600.0}, b_then{350.0, -20.0, 900.0}, c_then{-60.0, 110.0, 610.0};
const Point a_now{-100.0, 0.0, 200.0}, b_now{300.0, 0.0, 800.0}, c_now{-100.0, 100.0, 200.0};
const Point m_then{(a_then.x + b_then.x) / 2, (a_then.y + b_then.y) / 2, (a_then.z + b_then.z) / 2};
const Point m_now{(a_now.x + b_now.x) / 2, (a_now.y + b_now.y) / 2, (a_now.z + b_now.z) / 2};
const std::array<Point, 3> wall_then{a_then, b_then, c_then}, wall_now{a_now, b_now, c_now};
const std::array<Point, 3> piece_then{a_then, m_then, c_then}, piece_now{a_now, m_now, c_now};
const auto wall = placed(wall_now), piece = placed(piece_now);
const auto wall_from = weighted(wall_then, wall_now), piece_from = weighted(piece_then, piece_now);
GpuTemporalTriangle out{}, part{};
for (const float alpha : {0.0f, 0.1f, 0.5f, 0.9f, 1.0f}) {
gpu_temporal_lerp(&wall_from, &wall, alpha, &out);
for (int vertex = 0; vertex < 3; ++vertex)
expect(close(gpu_temporal_lerp_depth(&wall, &out, vertex),
wall_then[vertex].z + (wall_now[vertex].z - wall_then[vertex].z) * alpha),
"a corner placed from its earlier weight is as deep as it is on the way");
const auto start = placed(wall_then), gone_to = weighted(wall_now, wall_then);
auto end = gone_to;
for (int vertex = 0; vertex < 3; ++vertex) end.depth[vertex] = static_cast<float>(wall_now[vertex].z);
gpu_temporal_lerp(&start, &end, alpha, &out);
for (int vertex = 0; vertex < 3; ++vertex)
expect(close(gpu_temporal_lerp_depth(&end, &out, vertex),
wall_then[vertex].z + (wall_now[vertex].z - wall_then[vertex].z) * alpha),
"a departed corner placed toward its later weight is as deep as it is on the way");
}
gpu_temporal_lerp(&wall_from, &wall, 0.5f, &out);
gpu_temporal_lerp(&piece_from, &piece, 0.5f, &part);
const double edge_x = out.x[1] - out.x[0], edge_y = out.y[1] - out.y[0];
const double along = ((part.x[1] - out.x[0]) * edge_x + (part.y[1] - out.y[0]) * edge_y) / (edge_x * edge_x + edge_y * edge_y);
const double off = (part.x[1] - out.x[0]) * edge_y - (part.y[1] - out.y[0]) * edge_x;
expect(std::fabs(off) < 0.01 * std::sqrt(edge_x * edge_x + edge_y * edge_y) && along > 0.0 && along < 1.0,
"a corner in the middle of a wall's edge stays on that edge between the frames");
const double wall_there = 1.0 / ((1.0 - along) / gpu_temporal_lerp_depth(&wall, &out, 0) +
along / gpu_temporal_lerp_depth(&wall, &out, 1));
expect(close(wall_there, gpu_temporal_lerp_depth(&piece, &part, 1)),
"a wall and the piece drawn over it are equally deep where they meet between the frames");
const double stale = 1.0 / ((1.0 - along) / wall.depth[0] + along / wall.depth[1]);
expect(std::fabs(stale - piece.depth[1]) > 0.03 * piece.depth[1],
"the depths of the later frame alone would part the wall from its piece by more than the margin");
const auto still = triangle(), moved = triangle(8.0f, -6.0f);
auto deep = moved;
for (float &depth : deep.depth) depth = 300.0f;
gpu_temporal_lerp(&still, &deep, 0.5f, &out);
expect(gpu_temporal_lerp_depth(&deep, &out, 0) == 300.0f, "a corner without a weight keeps the depth it ends at");
auto behind = wall_from;
behind.w[0] = -0.5f;
gpu_temporal_lerp(&behind, &wall, 0.2f, &out);
expect(out.w[0] < 0.0f && gpu_temporal_lerp_depth(&wall, &out, 0) == wall.depth[0],
"a corner not yet in front of the viewer keeps the depth it ends at");
behind.w[0] = -1.0f;
gpu_temporal_lerp(&behind, &wall, 0.5f, &out);
expect(out.w[0] < 0.0f && gpu_temporal_lerp_depth(&wall, &out, 0) == wall.depth[0],
"a weight that comes to nothing on the way is not read as a corner without a weight");
auto leaving = placed(wall_then);
leaving.w[0] = -0.5f;
leaving.depth[0] = wall.depth[0] * leaving.w[0];
gpu_temporal_lerp(&wall, &leaving, 0.2f, &out);
expect(close(gpu_temporal_lerp_depth(&leaving, &out, 0), wall.depth[0] + (leaving.depth[0] - wall.depth[0]) * 0.2),
"a departed corner on its way behind the viewer is as deep as it is on the way");
}

void scale_and_fallback() {
constexpr int count = GPU_TEMPORAL_MAX_TRIANGLES;
std::vector<GpuTemporalTriangle> previous, current;
Expand Down Expand Up @@ -176,6 +266,7 @@ int main() {
interpolation();
correspondence();
invalid_geometry();
interpolated_depth();
scale_and_fallback();
if (failures) return 1;
std::cout << "GPU temporal geometry matching tests passed\n";
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/gpu_temporal_mesh_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,11 @@ void facing_and_camera_bounds() {
auto ghost = current;
ghost.w[0] = static_cast<float>(GPU_TEMPORAL_MIN_WEIGHT) * 0.5f;
expect(!gpu_temporal_mesh_facing(&current, &ghost), "a corner not yet in front of the viewer hides its face");
auto behind = current, crossing = current;
behind.w[0] = -1.0f;
gpu_temporal_lerp(&behind, &current, 0.5f, &crossing);
expect(crossing.w[0] < 0.0f && !gpu_temporal_mesh_facing(&current, &crossing),
"a corner in the viewer's own plane hides its face, its weight of nothing is not a missing weight");
auto broken = current;
broken.x[1] = std::numeric_limits<float>::infinity();
expect(gpu_temporal_mesh_camera_safe(&current, &flipped) && !gpu_temporal_mesh_camera_safe(&current, &broken),
Expand Down
Loading