- split shadowmap AABB tree into two parts. one for static and one for dynamic

- upload only the dynamic AABB subtree using glBufferSubData
- change internal raytracing stack limit from 16 to 32
- update shadowmap AABB tree after R_SetupFrame for proper frame interpolation
This commit is contained in:
Magnus Norddahl 2019-01-04 15:51:59 +01:00
parent 4f4d10ce5f
commit f10ded756e
9 changed files with 123 additions and 58 deletions

View file

@ -273,7 +273,6 @@ sector_t *FGLRenderer::RenderView(player_t* player)
fovratio = ratio;
}
UpdateShadowMap();
retsec = RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
}
All.Unclock();

View file

@ -152,6 +152,8 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came
{
R_SetupFrame (mainvp, r_viewwindow, camera);
UpdateShadowMap();
// Render (potentially) multiple views for stereo 3d
// Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode.
auto vrmode = VRMode::GetVRMode(mainview && toscreen);

View file

@ -65,7 +65,7 @@ void GLBuffer::Bind()
}
void GLBuffer::SetData(size_t size, void *data, bool staticdata)
void GLBuffer::SetData(size_t size, const void *data, bool staticdata)
{
assert(nomap); // once it's mappable, it cannot be recreated anymore.
Bind();
@ -92,6 +92,12 @@ void GLBuffer::SetData(size_t size, void *data, bool staticdata)
InvalidateBufferState();
}
void GLBuffer::SetSubData(size_t offset, size_t size, const void *data)
{
Bind();
glBufferSubData(mUseType, offset, size, data);
}
void GLBuffer::Map()
{
assert(nomap == false); // do not allow mapping of static buffers. Vulkan cannot do that so it should be blocked in OpenGL, too.

View file

@ -22,7 +22,8 @@ protected:
GLBuffer(int usetype);
~GLBuffer();
void SetData(size_t size, void *data, bool staticdata) override;
void SetData(size_t size, const void *data, bool staticdata) override;
void SetSubData(size_t offset, size_t size, const void *data) override;
void Map() override;
void Unmap() override;
void Resize(size_t newsize) override;

View file

@ -47,7 +47,8 @@ public:
IBuffer &operator=(const IBuffer &) = delete;
virtual ~IBuffer() = default;
virtual void SetData(size_t size, void *data, bool staticdata = true) = 0;
virtual void SetData(size_t size, const void *data, bool staticdata = true) = 0;
virtual void SetSubData(size_t offset, size_t size, const void *data) = 0;
virtual void *Lock(unsigned int size) = 0;
virtual void Unlock() = 0;
virtual void Resize(size_t newsize) = 0;

View file

@ -38,35 +38,36 @@ LevelAABBTree::LevelAABBTree()
centroids.Push((v1 + v2) * 0.5f);
}
// Create a list of level lines we want to add:
TArray<int> line_elements;
for (unsigned int i = 0; i < level.lines.Size(); i++)
{
if (!level.lines[i].backsector)
{
#ifdef USE_POLYOBJ_SHADOWS
if (level.lines[i].sidedef[0] && (level.lines[i].sidedef[0]->Flags & WALLF_POLYOBJ))
polylines.Push(i);
line_elements.Push(i);
#else
if (!level.lines[i].sidedef[0] || !(level.lines[i].sidedef[0]->Flags & WALLF_POLYOBJ))
line_elements.Push(i);
#endif
}
}
// Create the static subtree
if (!GenerateTree(&centroids[0], false))
return;
// GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets
TArray<int> work_buffer;
work_buffer.Resize(line_elements.Size() * 2);
int staticroot = nodes.Size() - 1;
// Generate the AABB tree
GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), &centroids[0], &work_buffer[0]);
dynamicStartNode = nodes.Size();
dynamicStartLine = lines.Size();
// Create the dynamic subtree
if (GenerateTree(&centroids[0], true))
{
int dynamicroot = nodes.Size() - 1;
// Create a shared root node
FVector2 aabb_min, aabb_max;
const auto &left = nodes[staticroot];
const auto &right = nodes[dynamicroot];
aabb_min.X = MIN(left.aabb_left, right.aabb_left);
aabb_min.Y = MIN(left.aabb_top, right.aabb_top);
aabb_max.X = MAX(left.aabb_right, right.aabb_right);
aabb_max.Y = MAX(left.aabb_bottom, right.aabb_bottom);
nodes.Push({ aabb_min, aabb_max, staticroot, dynamicroot });
}
// Add the lines referenced by the leaf nodes
lines.Resize(level.lines.Size());
for (unsigned int i = 0; i < level.lines.Size(); i++)
lines.Resize(mapLines.Size());
for (unsigned int i = 0; i < mapLines.Size(); i++)
{
const auto &line = level.lines[i];
const auto &line = level.lines[mapLines[i]];
auto &treeline = lines[i];
treeline.x = (float)line.v1->fX();
@ -76,13 +77,46 @@ LevelAABBTree::LevelAABBTree()
}
}
bool LevelAABBTree::GenerateTree(const FVector2 *centroids, bool dynamicsubtree)
{
// Create a list of level lines we want to add:
TArray<int> line_elements;
for (unsigned int i = 0; i < level.lines.Size(); i++)
{
if (!level.lines[i].backsector)
{
bool isPolyLine = level.lines[i].sidedef[0] && (level.lines[i].sidedef[0]->Flags & WALLF_POLYOBJ);
if (isPolyLine && dynamicsubtree)
{
line_elements.Push(mapLines.Size());
mapLines.Push(i);
}
else if (!isPolyLine && !dynamicsubtree)
{
line_elements.Push(mapLines.Size());
mapLines.Push(i);
}
}
}
if (line_elements.Size() == 0)
return false;
// GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets
TArray<int> work_buffer;
work_buffer.Resize(line_elements.Size() * 2);
// Generate the AABB tree
GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), centroids, &work_buffer[0]);
return true;
}
bool LevelAABBTree::Update()
{
bool modified = false;
for (unsigned int ii = 0; ii < polylines.Size(); ii++)
for (unsigned int i = dynamicStartLine; i < mapLines.Size(); i++)
{
int i = polylines[ii];
const auto &line = level.lines[i];
const auto &line = level.lines[mapLines[i]];
AABBTreeLine treeline;
treeline.x = (float)line.v1->fX();
@ -95,10 +129,10 @@ bool LevelAABBTree::Update()
TArray<int> path = FindNodePath(i, nodes.Size() - 1);
if (path.Size())
{
float x1 = (float)level.lines[i].v1->fX();
float y1 = (float)level.lines[i].v1->fY();
float x2 = (float)level.lines[i].v2->fX();
float y2 = (float)level.lines[i].v2->fY();
float x1 = (float)line.v1->fX();
float y1 = (float)line.v1->fY();
float x2 = (float)line.v2->fX();
float y2 = (float)line.v2->fY();
int nodeIndex = path[0];
nodes[nodeIndex].aabb_left = MIN(x1, x2);
@ -122,7 +156,7 @@ bool LevelAABBTree::Update()
}
}
}
return !modified;
return modified;
}
TArray<int> LevelAABBTree::FindNodePath(unsigned int line, unsigned int node)
@ -165,7 +199,7 @@ double LevelAABBTree::RayTest(const DVector3 &ray_start, const DVector3 &ray_end
double hit_fraction = 1.0;
// Walk the tree nodes
int stack[16];
int stack[32];
int stack_pos = 1;
stack[0] = nodes.Size() - 1; // root node is the last node in the list
while (stack_pos > 0)
@ -183,7 +217,7 @@ double LevelAABBTree::RayTest(const DVector3 &ray_start, const DVector3 &ray_end
hit_fraction = MIN(IntersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), hit_fraction);
stack_pos--;
}
else if (stack_pos == 16)
else if (stack_pos == 32)
{
stack_pos--; // stack overflow - tree is too deep!
}
@ -269,15 +303,15 @@ int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *c
// Find bounding box and median of the lines
FVector2 median = FVector2(0.0f, 0.0f);
FVector2 aabb_min, aabb_max;
aabb_min.X = (float)level.lines[lines[0]].v1->fX();
aabb_min.Y = (float)level.lines[lines[0]].v1->fY();
aabb_min.X = (float)level.lines[mapLines[lines[0]]].v1->fX();
aabb_min.Y = (float)level.lines[mapLines[lines[0]]].v1->fY();
aabb_max = aabb_min;
for (int i = 0; i < num_lines; i++)
{
float x1 = (float)level.lines[lines[i]].v1->fX();
float y1 = (float)level.lines[lines[i]].v1->fY();
float x2 = (float)level.lines[lines[i]].v2->fX();
float y2 = (float)level.lines[lines[i]].v2->fY();
float x1 = (float)level.lines[mapLines[lines[i]]].v1->fX();
float y1 = (float)level.lines[mapLines[lines[i]]].v1->fY();
float x2 = (float)level.lines[mapLines[lines[i]]].v2->fX();
float y2 = (float)level.lines[mapLines[lines[i]]].v2->fY();
aabb_min.X = MIN(aabb_min.X, x1);
aabb_min.X = MIN(aabb_min.X, x2);
@ -288,7 +322,7 @@ int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *c
aabb_max.Y = MAX(aabb_max.Y, y1);
aabb_max.Y = MAX(aabb_max.Y, y2);
median += centroids[lines[i]];
median += centroids[mapLines[lines[i]]];
}
median /= (float)num_lines;
@ -324,7 +358,7 @@ int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *c
{
int line_index = lines[i];
float side = FVector3(centroids[lines[i]], 1.0f) | plane;
float side = FVector3(centroids[mapLines[lines[i]]], 1.0f) | plane;
if (side >= 0.0f)
{
work_buffer[left_count] = line_index;

View file

@ -42,18 +42,26 @@ public:
// Constructs a tree for the current level
LevelAABBTree();
// Nodes in the AABB tree. Last node is the root node.
TArray<AABBTreeNode> nodes;
// Line segments for the leaf nodes in the tree.
TArray<AABBTreeLine> lines;
// Shoot a ray from ray_start to ray_end and return the closest hit as a fractional value between 0 and 1. Returns 1 if no line was hit.
double RayTest(const DVector3 &ray_start, const DVector3 &ray_end);
bool Update();
const void *Nodes() const { return nodes.Data(); }
const void *Lines() const { return lines.Data(); }
size_t NodesSize() const { return nodes.Size() * sizeof(AABBTreeNode); }
size_t LinesSize() const { return lines.Size() * sizeof(AABBTreeLine); }
const void *DynamicNodes() const { return nodes.Data() + dynamicStartNode; }
const void *DynamicLines() const { return lines.Data() + dynamicStartLine; }
size_t DynamicNodesSize() const { return (nodes.Size() - dynamicStartNode) * sizeof(AABBTreeNode); }
size_t DynamicLinesSize() const { return (lines.Size() - dynamicStartLine) * sizeof(AABBTreeLine); }
size_t DynamicNodesOffset() const { return dynamicStartNode * sizeof(AABBTreeNode); }
size_t DynamicLinesOffset() const { return dynamicStartLine * sizeof(AABBTreeLine); }
private:
bool GenerateTree(const FVector2 *centroids, bool dynamicsubtree);
// Test if a ray overlaps an AABB node or not
bool OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node);
@ -65,7 +73,16 @@ private:
TArray<int> FindNodePath(unsigned int line, unsigned int node);
TArray<int> polylines;
// Nodes in the AABB tree. Last node is the root node.
TArray<AABBTreeNode> nodes;
// Line segments for the leaf nodes in the tree.
TArray<AABBTreeLine> lines;
int dynamicStartNode = 0;
int dynamicStartLine = 0;
TArray<int> mapLines;
};
} // namespace

View file

@ -155,7 +155,7 @@ bool IShadowMap::ValidateAABBTree()
}
if (mAABBTree)
return mAABBTree->Update();
return true;
mAABBTree.reset(new hwrenderer::LevelAABBTree());
return false;
@ -198,11 +198,16 @@ void IShadowMap::UploadAABBTree()
{
if (!mNodesBuffer)
mNodesBuffer = screen->CreateDataBuffer(2, true);
mNodesBuffer->SetData(sizeof(hwrenderer::AABBTreeNode) * mAABBTree->nodes.Size(), &mAABBTree->nodes[0]);
mNodesBuffer->SetData(mAABBTree->NodesSize(), mAABBTree->Nodes());
if (!mLinesBuffer)
mLinesBuffer = screen->CreateDataBuffer(3, true);
mLinesBuffer->SetData(sizeof(hwrenderer::AABBTreeLine) * mAABBTree->lines.Size(), &mAABBTree->lines[0]);
mLinesBuffer->SetData(mAABBTree->LinesSize(), mAABBTree->Lines());
}
else if (mAABBTree->Update())
{
mNodesBuffer->SetSubData(mAABBTree->DynamicNodesOffset(), mAABBTree->DynamicNodesSize(), mAABBTree->DynamicNodes());
mLinesBuffer->SetSubData(mAABBTree->DynamicLinesOffset(), mAABBTree->DynamicLinesSize(), mAABBTree->DynamicLines());
}
}

View file

@ -109,7 +109,7 @@ float rayTest(vec2 ray_start, vec2 ray_end)
// Walk the AABB binary tree searching for nodes touching the ray line segment's AABB box.
// When it reaches a leaf node, use a line segment intersection test to see if we got a hit.
int stack[16];
int stack[32];
int stack_pos = 1;
stack[0] = nodes.length() - 1;
while (stack_pos > 0)
@ -125,7 +125,7 @@ float rayTest(vec2 ray_start, vec2 ray_end)
t = min(intersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), t);
stack_pos--;
}
else if (stack_pos == 16)
else if (stack_pos == 32)
{
stack_pos--; // stack overflow
}