Clipper: Compact buffers on each clipping pass

Use a new buffer management scheme in the clipper that allows using a
bounded minimal amount of buffer space. Even though it copies more data
it is still slightly faster likely due to using less cache.
This commit is contained in:
Yuri Kunde Schlesner 2014-12-28 23:05:15 -02:00
parent da04976437
commit 7e9bc85cc8
1 changed files with 27 additions and 28 deletions

View File

@ -100,13 +100,15 @@ static void InitScreenCoordinates(OutputVertex& vtx)
void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
using boost::container::static_vector; using boost::container::static_vector;
// TODO (neobrain): // Clipping a planar n-gon against a plane will remove at least 1 vertex and introduces 2 at
// The list of output vertices has some fixed maximum size, // the new edge (or less in degenerate cases). As such, we can say that each clipping plane
// however I haven't taken the time to figure out what it is exactly. // introduces at most 1 new vertex to the polygon. Since we start with a triangle and have a
// For now, we hence just assume a maximal size of 256 vertices. // fixed 6 clipping planes, the maximum number of vertices of the clipped polygon is 3 + 6 = 9.
static const size_t MAX_VERTICES = 256; static const size_t MAX_VERTICES = 9;
static_vector<OutputVertex, MAX_VERTICES> buffer_vertices; static_vector<OutputVertex, MAX_VERTICES> buffer_a = { v0, v1, v2 };
static_vector<OutputVertex*, MAX_VERTICES> output_list = { &v0, &v1, &v2 }; static_vector<OutputVertex, MAX_VERTICES> buffer_b;
auto* output_list = &buffer_a;
auto* input_list = &buffer_b;
// Simple implementation of the Sutherland-Hodgman clipping algorithm. // Simple implementation of the Sutherland-Hodgman clipping algorithm.
// TODO: Make this less inefficient (currently lots of useless buffering overhead happens here) // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here)
@ -117,48 +119,45 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
ClippingEdge(ClippingEdge::POS_Z, float24::FromFloat32(+1.0)), ClippingEdge(ClippingEdge::POS_Z, float24::FromFloat32(+1.0)),
ClippingEdge(ClippingEdge::NEG_Z, float24::FromFloat32(-1.0)) }) { ClippingEdge(ClippingEdge::NEG_Z, float24::FromFloat32(-1.0)) }) {
const static_vector<OutputVertex*, MAX_VERTICES> input_list = output_list; std::swap(input_list, output_list);
output_list.clear(); output_list->clear();
const OutputVertex* reference_vertex = input_list.back(); const OutputVertex* reference_vertex = &input_list->back();
for (const auto& vertex : input_list) { for (const auto& vertex : *input_list) {
// NOTE: This algorithm changes vertex order in some cases! // NOTE: This algorithm changes vertex order in some cases!
if (edge.IsInside(*vertex)) { if (edge.IsInside(vertex)) {
if (edge.IsOutSide(*reference_vertex)) { if (edge.IsOutSide(*reference_vertex)) {
buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex)); output_list->push_back(edge.GetIntersection(vertex, *reference_vertex));
output_list.push_back(&(buffer_vertices.back()));
} }
output_list.push_back(vertex); output_list->push_back(vertex);
} else if (edge.IsInside(*reference_vertex)) { } else if (edge.IsInside(*reference_vertex)) {
buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex)); output_list->push_back(edge.GetIntersection(vertex, *reference_vertex));
output_list.push_back(&(buffer_vertices.back()));
} }
reference_vertex = &vertex;
reference_vertex = vertex;
} }
// Need to have at least a full triangle to continue... // Need to have at least a full triangle to continue...
if (output_list.size() < 3) if (output_list->size() < 3)
return; return;
} }
InitScreenCoordinates(*(output_list[0])); InitScreenCoordinates((*output_list)[0]);
InitScreenCoordinates(*(output_list[1])); InitScreenCoordinates((*output_list)[1]);
for (size_t i = 0; i < output_list.size() - 2; i ++) { for (size_t i = 0; i < output_list->size() - 2; i ++) {
OutputVertex& vtx0 = *(output_list[0]); OutputVertex& vtx0 = (*output_list)[0];
OutputVertex& vtx1 = *(output_list[i+1]); OutputVertex& vtx1 = (*output_list)[i+1];
OutputVertex& vtx2 = *(output_list[i+2]); OutputVertex& vtx2 = (*output_list)[i+2];
InitScreenCoordinates(vtx2); InitScreenCoordinates(vtx2);
LOG_TRACE(Render_Software, LOG_TRACE(Render_Software,
"Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), " "Triangle %lu/%lu at position (%.3f, %.3f, %.3f, %.3f), "
"(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and "
"screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)",
i,output_list.size(), buffer_vertices.size(), i, output_list->size(),
vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(), vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),
vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(),
vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(),