Skip to content

Commit

Permalink
UPBGE: Cache polygon center for polygon sort.
Browse files Browse the repository at this point in the history
Previously during polygon sort, centers of polygon (triangles actually)
were computed by the summary of triangle vertices position.
But this operation was repeated for each polygon sort. To optimize
polygon sort we cache the polygon centers into a the list m_polygonCenters
owned by the RAS_IDisplayArray, this list is cleared through a call to
InvalidatePolygonCenters each time vertices are moved and that polygons
centers are invalid, so the function SortPolygons check if cache size
is equivalent to polygons count, if not the cache is regenerated.
After this cache check the polygon centers are used as before in a temporary
struct which hold the dot product with the camera near plane plus the
index of the first vertices in the polygon tested.

Tested with one material and 73000 polygons:
Rasterizer time:
previous: 7.4ms
current: 5.6ms
  • Loading branch information
panzergame committed Sep 16, 2017
1 parent c24d294 commit 6817669
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 122 deletions.
2 changes: 1 addition & 1 deletion source/gameengine/Ketsji/KX_FontObject.cpp
Expand Up @@ -136,7 +136,7 @@ void KX_FontObject::AddMeshUser()
RAS_BucketManager *bucketManager = GetScene()->GetBucketManager();
RAS_DisplayArrayBucket *arrayBucket = bucketManager->GetTextDisplayArrayBucket();

RAS_MeshSlot *ms = new RAS_MeshSlot(nullptr, m_meshUser, arrayBucket);
RAS_MeshSlot *ms = new RAS_MeshSlot(m_meshUser, arrayBucket);
m_meshUser->AddMeshSlot(ms);
}

Expand Down
8 changes: 7 additions & 1 deletion source/gameengine/Rasterizer/RAS_DisplayArrayBucket.cpp
Expand Up @@ -150,9 +150,15 @@ void RAS_DisplayArrayBucket::UpdateActiveMeshSlots(RAS_Rasterizer *rasty)
}

if (m_displayArray) {
if (m_displayArray->GetModifiedFlag() & RAS_IDisplayArray::MESH_MODIFIED) {
const unsigned short modifiedFlag = m_displayArray->GetModifiedFlag();
if (modifiedFlag & RAS_IDisplayArray::MESH_MODIFIED) {
arrayModified = true;
m_displayArray->SetModifiedFlag(RAS_IDisplayArray::NONE_MODIFIED);

if (modifiedFlag & RAS_IDisplayArray::POSITION_MODIFIED) {
// Reset polygons center cache to ask update.
m_displayArray->InvalidatePolygonCenters();
}
}

// Create the storage info if it was destructed or not yet created.
Expand Down
72 changes: 72 additions & 0 deletions source/gameengine/Rasterizer/RAS_IDisplayArray.cpp
Expand Up @@ -29,6 +29,32 @@

#include "GPU_glew.h"

#include <algorithm>

struct PolygonSort
{
/// Distance from polygon center to camera near plane.
float m_z;
/// Index of the first vertex in the polygon.
unsigned int m_first;

PolygonSort() = default;

void Init(unsigned int first, const MT_Vector3& center, const MT_Vector3& pnorm)
{
m_first = first;
m_z = pnorm.dot(center);
}

struct BackToFront
{
bool operator()(const PolygonSort &a, const PolygonSort &b) const
{
return a.m_z < b.m_z;
}
};
};

RAS_IDisplayArray::RAS_IDisplayArray(PrimitiveType type, const RAS_TexVertFormat& format)
:m_type(type),
m_modifiedFlag(NONE_MODIFIED),
Expand Down Expand Up @@ -71,6 +97,52 @@ RAS_IDisplayArray *RAS_IDisplayArray::ConstructArray(RAS_IDisplayArray::Primitiv
#undef NEW_DISPLAY_ARRAY_UV
#undef NEW_DISPLAY_ARRAY_COLOR

void RAS_IDisplayArray::SortPolygons(const MT_Transform& transform, unsigned int *indexmap)
{
const unsigned int totpoly = GetIndexCount() / 3;

if (totpoly <= 1 || m_type == LINES) {
return;
}

// Extract camera Z plane.
const MT_Vector3 pnorm(transform.getBasis()[2]);

if (m_polygonCenters.size() != totpoly) {
m_polygonCenters.resize(totpoly);
for (unsigned int i = 0; i < totpoly; ++i) {
// Compute polygon center.
MT_Vector3& center = m_polygonCenters[i];
for (unsigned short j = 0; j < 3; ++j) {
/* Note that we don't divide by 3 as it is not needed
* to compare polygons. */
center += GetVertex(m_indices[i * 3 + j])->xyz();
}
}
}

std::vector<PolygonSort> sortedPoly(totpoly);
// Get indices and polygon distance into temporary array.
for (unsigned int i = 0; i < totpoly; ++i) {
sortedPoly[i].Init(i * 3, pnorm, m_polygonCenters[i]);
}

std::sort(sortedPoly.begin(), sortedPoly.end(), PolygonSort::BackToFront());

// Get indices from temporary array.
for (unsigned int i = 0; i < totpoly; ++i) {
const unsigned int first = sortedPoly[i].m_first;
for (unsigned short j = 0; j < 3; ++j) {
indexmap[i * 3 + j] = m_indices[first + j];
}
}
}

void RAS_IDisplayArray::InvalidatePolygonCenters()
{
m_polygonCenters.clear();
}

RAS_IDisplayArray::PrimitiveType RAS_IDisplayArray::GetPrimitiveType() const
{
return m_type;
Expand Down
8 changes: 8 additions & 0 deletions source/gameengine/Rasterizer/RAS_IDisplayArray.h
Expand Up @@ -58,6 +58,11 @@ class RAS_IDisplayArray
/// The indices used for rendering.
std::vector<unsigned int> m_indices;

/** Polygon centers cache used to sort polygons depending on depth.
* This list is stored here because we store per array not per entire mesh.
*/
std::vector<MT_Vector3> m_polygonCenters;

public:
RAS_IDisplayArray(PrimitiveType type, const RAS_TexVertFormat& format);
virtual ~RAS_IDisplayArray();
Expand Down Expand Up @@ -135,6 +140,9 @@ class RAS_IDisplayArray
return m_indices.size();
}

void SortPolygons(const MT_Transform &transform, unsigned int *indexmap);
void InvalidatePolygonCenters();

virtual RAS_ITexVert *CreateVertex(
const MT_Vector3& xyz,
const MT_Vector2 * const uvs,
Expand Down
106 changes: 1 addition & 105 deletions source/gameengine/Rasterizer/RAS_MeshObject.cpp
Expand Up @@ -46,62 +46,6 @@

#include "CM_Message.h"

#include <algorithm>

// polygon sorting

struct RAS_MeshObject::polygonSlot
{
float m_z;
int m_index[4];

polygonSlot()
{
}

/* pnorm is the normal from the plane equation that the distance from is
* used to sort again. */
void get(RAS_IDisplayArray *array, int offset, int nvert, const MT_Vector3& pnorm)
{
MT_Vector3 center(0.0f, 0.0f, 0.0f);

for (unsigned short i = 0; i < nvert; ++i) {
m_index[i] = array->GetIndex(offset + i);
center += array->GetVertex(m_index[i])->xyz();
}

/* note we don't divide center by the number of vertices, since all
* polygons have the same number of vertices, and that we leave out
* the 4-th component of the plane equation since it is constant. */
m_z = MT_dot(pnorm, center);
}

void set(unsigned int *indexmap, int offset, int nvert)
{
for (unsigned short i = 0; i < nvert; ++i) {
indexmap[offset + i] = m_index[i];
}
}
};

struct RAS_MeshObject::backtofront
{
bool operator()(const polygonSlot &a, const polygonSlot &b) const
{
return a.m_z < b.m_z;
}
};

struct RAS_MeshObject::fronttoback
{
bool operator()(const polygonSlot &a, const polygonSlot &b) const
{
return a.m_z > b.m_z;
}
};

// mesh object

RAS_MeshObject::RAS_MeshObject(Mesh *mesh, const LayersInfo& layersInfo)
:m_name(mesh->id.name + 2),
m_layersInfo(layersInfo),
Expand Down Expand Up @@ -349,7 +293,7 @@ RAS_MeshUser* RAS_MeshObject::AddMeshUser(void *clientobj, RAS_Deformer *deforme
arrayBucket = mmat->GetDisplayArrayBucket();
}

RAS_MeshSlot *ms = new RAS_MeshSlot(this, meshUser, arrayBucket);
RAS_MeshSlot *ms = new RAS_MeshSlot(meshUser, arrayBucket);
meshUser->AddMeshSlot(ms);
}
return meshUser;
Expand Down Expand Up @@ -404,54 +348,6 @@ void RAS_MeshObject::GenerateAttribLayers()
}
}

void RAS_MeshObject::SortPolygons(RAS_IDisplayArray *array, const MT_Transform &transform, unsigned int *indexmap)
{
// Limitations: sorting is quite simple, and handles many
// cases wrong, partially due to polygons being sorted per
// bucket.
//
// a) mixed triangles/quads are sorted wrong
// b) mixed materials are sorted wrong
// c) more than 65k faces are sorted wrong
// d) intersecting objects are sorted wrong
// e) intersecting polygons are sorted wrong
//
// a) can be solved by making all faces either triangles or quads
// if they need to be z-sorted. c) could be solved by allowing
// larger buckets, b) and d) cannot be solved easily if we want
// to avoid excessive state changes while drawing. e) would
// require splitting polygons.

// If there's no vertex array it means that the we're using modifier deformer.
if (!array) {
return;
}

unsigned int nvert = 3;
unsigned int totpoly = array->GetIndexCount() / nvert;

if (totpoly <= 1)
return;

// Extract camera Z plane...
const MT_Vector3 pnorm(transform.getBasis()[2]);
// unneeded: const MT_Scalar pval = transform.getOrigin()[2];

std::vector<polygonSlot> poly_slots(totpoly);

// get indices and z into temporary array
for (unsigned int j = 0; j < totpoly; j++)
poly_slots[j].get(array, j * nvert, nvert, pnorm);

// sort (stable_sort might be better, if flickering happens?)
std::sort(poly_slots.begin(), poly_slots.end(), backtofront());

// get indices from temporary array again
for (unsigned int j = 0; j < totpoly; j++)
poly_slots[j].set(indexmap, j * nvert, nvert);
}


bool RAS_MeshObject::HasColliderPolygon()
{
for (const RAS_Polygon& poly : m_polygons) {
Expand Down
8 changes: 0 additions & 8 deletions source/gameengine/Rasterizer/RAS_MeshObject.h
Expand Up @@ -94,11 +94,6 @@ class RAS_MeshObject

std::vector<RAS_Polygon> m_polygons;

/* polygon sorting */
struct polygonSlot;
struct backtofront;
struct fronttoback;

/// The mesh bounding box.
RAS_BoundingBox *m_boundingBox;

Expand Down Expand Up @@ -165,9 +160,6 @@ class RAS_MeshObject
*/
void GenerateAttribLayers();

// polygon sorting by Z for alpha
void SortPolygons(RAS_IDisplayArray *array, const MT_Transform &transform, unsigned int *indexmap);

bool HasColliderPolygon();

// for construction to find shared vertices
Expand Down
7 changes: 3 additions & 4 deletions source/gameengine/Rasterizer/RAS_MeshSlot.cpp
Expand Up @@ -33,7 +33,7 @@
#include "RAS_MeshUser.h"
#include "RAS_IPolygonMaterial.h"
#include "RAS_TexVert.h"
#include "RAS_MeshObject.h"
#include "RAS_IDisplayArray.h"
#include "RAS_IStorageInfo.h"

#ifdef _MSC_VER
Expand All @@ -47,10 +47,9 @@
static RAS_DummyNodeData dummyNodeData;

// mesh slot
RAS_MeshSlot::RAS_MeshSlot(RAS_MeshObject *mesh, RAS_MeshUser *meshUser, RAS_DisplayArrayBucket *arrayBucket)
RAS_MeshSlot::RAS_MeshSlot(RAS_MeshUser *meshUser, RAS_DisplayArrayBucket *arrayBucket)
:m_node(this, &dummyNodeData, std::mem_fn(&RAS_MeshSlot::RunNode), nullptr),
m_displayArrayBucket(arrayBucket),
m_mesh(mesh),
m_pDerivedMesh(nullptr),
m_meshUser(meshUser),
m_batchPartIndex(-1)
Expand Down Expand Up @@ -88,7 +87,7 @@ void RAS_MeshSlot::RunNode(const RAS_MeshSlotNodeTuple& tuple)

RAS_IStorageInfo *storage = displayArrayData->m_storageInfo;
if (materialData->m_zsort && storage) {
m_mesh->SortPolygons(displayArrayData->m_array, managerData->m_trans * MT_Transform(m_meshUser->GetMatrix()),
displayArrayData->m_array->SortPolygons(managerData->m_trans * MT_Transform(m_meshUser->GetMatrix()),
storage->GetIndexMap());
storage->FlushIndexMap();
}
Expand Down
4 changes: 1 addition & 3 deletions source/gameengine/Rasterizer/RAS_MeshSlot.h
Expand Up @@ -37,7 +37,6 @@
#include <vector>

class RAS_DisplayArrayBucket;
class RAS_MeshObject;
class RAS_MeshUser;
struct DerivedMesh;

Expand All @@ -49,14 +48,13 @@ class RAS_MeshSlot
public:
// for rendering
RAS_DisplayArrayBucket *m_displayArrayBucket;
RAS_MeshObject *m_mesh;
DerivedMesh *m_pDerivedMesh;
RAS_MeshUser *m_meshUser;

/// Batch index used for batching render.
short m_batchPartIndex;

RAS_MeshSlot(RAS_MeshObject *mesh, RAS_MeshUser *meshUser, RAS_DisplayArrayBucket *arrayBucket);
RAS_MeshSlot(RAS_MeshUser *meshUser, RAS_DisplayArrayBucket *arrayBucket);
virtual ~RAS_MeshSlot();

void SetDisplayArrayBucket(RAS_DisplayArrayBucket *arrayBucket);
Expand Down

0 comments on commit 6817669

Please sign in to comment.