- cache the results of hw_FakeFlat for the remainder of the current scene instead of storing this in local variables.

An exception is made for the sprite drawer which needs to call this in the worker thread on some occasions for as-yet unprocessed sectors.
This case may not alter the cache to avoid having to add thread synchronization to it.

The main reason for this change is that pointers to such manipulated sectors can now be considered static in the renderer.
Due to them being short lived local buffers it was not possible to carry them along with the render data for information retrieval.
This commit is contained in:
Christoph Oelckers 2018-11-10 20:06:10 +01:00
parent 9a7f570b19
commit a90655b295
10 changed files with 87 additions and 54 deletions

View file

@ -53,6 +53,7 @@
#include "hwrenderer/postprocessing/hw_shadowmapshader.h"
#include "hwrenderer/data/flatvertices.h"
#include "hwrenderer/scene/hw_skydome.h"
#include "hwrenderer/scene/hw_fakeflat.h"
#include "gl/shaders/gl_postprocessshaderinstance.h"
#include "gl/textures/gl_samplers.h"
#include "hwrenderer/dynlights/hw_lightbuffer.h"
@ -231,6 +232,8 @@ sector_t *FGLRenderer::RenderView(player_t* player)
}
else
{
hw_ClearFakeFlat();
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
checkBenchActive();
@ -302,6 +305,7 @@ void FGLRenderer::BindToFrameBuffer(FMaterial *mat)
void FGLRenderer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV)
{
// This doesn't need to clear the fake flat cache. It can be shared between camera textures and the main view of a scene.
FMaterial * gltex = FMaterial::ValidateTexture(tex, false);
int width = gltex->TextureWidth();
@ -343,7 +347,8 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i
// Switch to render buffers dimensioned for the savepic
mBuffers = mSaveBuffers;
P_FindParticleSubsectors(); // make sure that all recently spawned particles have a valid subsector.
hw_ClearFakeFlat();
P_FindParticleSubsectors(); // make sure that all recently spawned particles have a valid subsector.
gl_RenderState.SetVertexBuffer(screen->mVertexData);
screen->mVertexData->Reset();
screen->mLights->Clear();

View file

@ -122,8 +122,6 @@ private:
};
#include "hwrenderer/scene/hw_fakeflat.h"
struct TexFilter_s
{
int minfilter;

View file

@ -96,7 +96,7 @@ static RenderJobQueue jobQueue; // One static queue is sufficient here. This cod
void HWDrawInfo::WorkerThread()
{
sector_t fakefront, fakeback, *front, *back;
sector_t *front, *back;
WTTotal.Clock();
isWorkerThread = true; // for adding asserts in GL API code. The worker thread may never call any GL API.
@ -118,6 +118,8 @@ void HWDrawInfo::WorkerThread()
_mm_pause();
_mm_pause();
}
// Note that the main thread MUST have prepared the fake sectors that get used below!
// This worker thread cannot prepare them itself without costly synchronization.
else switch (job->type)
{
case RenderJob::TerminateJob:
@ -130,7 +132,7 @@ void HWDrawInfo::WorkerThread()
SetupWall.Clock();
wall.sub = job->sub;
front = hw_FakeFlat(job->sub->sector, &fakefront, in_area, false);
front = hw_FakeFlat(job->sub->sector, in_area, false);
auto seg = job->seg;
if (seg->backsector)
{
@ -140,7 +142,7 @@ void HWDrawInfo::WorkerThread()
}
else
{
back = hw_FakeFlat(seg->backsector, &fakeback, in_area, true);
back = hw_FakeFlat(seg->backsector, in_area, true);
}
}
else back = nullptr;
@ -156,7 +158,7 @@ void HWDrawInfo::WorkerThread()
GLFlat flat;
SetupFlat.Clock();
flat.section = job->sub->section;
front = hw_FakeFlat(job->sub->render_sector, &fakefront, in_area, false);
front = hw_FakeFlat(job->sub->render_sector, in_area, false);
flat.ProcessSector(this, front);
SetupFlat.Unclock();
break;
@ -164,14 +166,14 @@ void HWDrawInfo::WorkerThread()
case RenderJob::SpriteJob:
SetupSprite.Clock();
front = hw_FakeFlat(job->sub->sector, &fakefront, in_area, false);
front = hw_FakeFlat(job->sub->sector, in_area, false);
RenderThings(job->sub, front);
SetupSprite.Unclock();
break;
case RenderJob::ParticleJob:
SetupSprite.Clock();
front = hw_FakeFlat(job->sub->sector, &fakefront, in_area, false);
front = hw_FakeFlat(job->sub->sector, in_area, false);
RenderParticles(job->sub, front);
SetupSprite.Unclock();
break;
@ -228,7 +230,6 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
#endif
sector_t * backsector = nullptr;
sector_t bs;
if (portalclip)
{
@ -291,7 +292,7 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
// clipping checks are only needed when the backsector is not the same as the front sector
if (in_area == area_default) in_area = hw_CheckViewArea(seg->v1, seg->v2, seg->frontsector, seg->backsector);
backsector = hw_FakeFlat(seg->backsector, &bs, in_area, true);
backsector = hw_FakeFlat(seg->backsector, in_area, true);
if (hw_CheckClip(seg->sidedef, currentsector, backsector))
{
@ -575,7 +576,6 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
{
sector_t * sector;
sector_t * fakesector;
sector_t fake;
#ifdef _DEBUG
if (sub->sector->sectornum==931)
@ -600,7 +600,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
}
if (mClipper->IsBlocked()) return; // if we are inside a stacked sector portal which hasn't unclipped anything yet.
fakesector=hw_FakeFlat(sector, &fake, in_area, false);
fakesector=hw_FakeFlat(sector, in_area, false);
if (mClipPortal)
{
@ -677,7 +677,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub)
sector = sub->render_sector;
// the planes of this subsector are faked to belong to another sector
// This means we need the heightsec parts and light info of the render sector, not the actual one.
fakesector = hw_FakeFlat(sector, &fake, in_area, false);
fakesector = hw_FakeFlat(sector, in_area, false);
}
uint8_t &srf = section_renderflags[level.sections.SectionIndex(sub->section)];

View file

@ -193,8 +193,6 @@ private:
subsector_t *currentsubsector; // used by the line processing code.
sector_t *currentsector;
sector_t fakesec; // this is a struct member because it gets used in recursively called functions so it cannot be put on the stack.
void WorkerThread();
void UnclipSubsector(subsector_t *sub);

View file

@ -36,6 +36,7 @@
#include "hwrenderer/utility/hw_clock.h"
#include "hw_renderstate.h"
#include "hw_drawinfo.h"
#include "hw_fakeflat.h"
FMemArena RenderDataAllocator(1024*1024); // Use large blocks to reduce allocation time.

View file

@ -36,6 +36,9 @@
#include "hwrenderer/utility/hw_cvars.h"
#include "r_utility.h"
static sector_t **fakesectorbuffer;
extern thread_local bool isWorkerThread;
//==========================================================================
//
@ -182,6 +185,24 @@ area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, secto
return area_default;
}
//==========================================================================
//
//
//
//==========================================================================
static FMemArena FakeSectorAllocator(20 * sizeof(sector_t));
static sector_t *allocateSector(sector_t *sec)
{
if (fakesectorbuffer == nullptr)
{
fakesectorbuffer = (sector_t**)FakeSectorAllocator.Alloc(level.sectors.Size() * sizeof(sector_t*));
memset(fakesectorbuffer, 0, level.sectors.Size() * sizeof(sector_t*));
}
auto sectornum = sec->sectornum;
fakesectorbuffer[sectornum] = (sector_t*)FakeSectorAllocator.Alloc(sizeof(sector_t));
return fakesectorbuffer[sectornum];
}
//==========================================================================
//
@ -189,7 +210,8 @@ area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, secto
// by hardware rendering
//
//==========================================================================
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back)
sector_t * hw_FakeFlat(sector_t * sec, area_t in_area, bool back, sector_t *localcopy)
{
if (!sec->GetHeightSec() || sec->heightsec==sec)
{
@ -197,6 +219,8 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
// visual glitches because upper amd lower textures overlap.
if (back && (sec->MoreFlags & SECMF_OVERLAPPING))
{
if (fakesectorbuffer && fakesectorbuffer[sec->sectornum]) return fakesectorbuffer[sec->sectornum];
auto dest = localcopy? localcopy : allocateSector(sec);
*dest = *sec;
dest->ceilingplane = sec->floorplane;
dest->ceilingplane.FlipVert();
@ -215,6 +239,12 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
}
#endif
if (fakesectorbuffer && fakesectorbuffer[sec->sectornum])
{
return fakesectorbuffer[sec->sectornum];
}
assert(!(isWorkerThread && localcopy == nullptr));
if (in_area==area_above)
{
if (sec->heightsec->MoreFlags&SECMF_FAKEFLOORONLY /*|| sec->GetTexture(sector_t::ceiling)==skyflatnum*/) in_area=area_normal;
@ -223,11 +253,8 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
int diffTex = (sec->heightsec->MoreFlags & SECMF_CLIPFAKEPLANES);
sector_t * s = sec->heightsec;
#if 0
*dest=*sec; // This will invoke the copy operator which isn't really needed here. Memcpy is faster.
#else
memcpy(dest, sec, sizeof(sector_t));
#endif
auto dest = localcopy ? localcopy : allocateSector(sec);
*dest = *sec;
// Replace floor and ceiling height with control sector's heights.
if (diffTex)
@ -309,7 +336,7 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
dest->lightlevel = s->lightlevel;
}
if (!back)
//if (!back)
{
dest->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false);
dest->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform;
@ -362,7 +389,7 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
dest->lightlevel = s->lightlevel;
}
if (!back)
//if (!back)
{
dest->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false);
dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false);
@ -386,3 +413,10 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac
}
return dest;
}
void hw_ClearFakeFlat()
{
FakeSectorAllocator.FreeAll();
fakesectorbuffer = nullptr;
}

View file

@ -11,6 +11,7 @@ enum area_t : int
// Global functions.
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector);
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
void hw_ClearFakeFlat();
sector_t * hw_FakeFlat(sector_t * sec, area_t in_area, bool back, sector_t *localcopy = nullptr);
area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector);

View file

@ -38,8 +38,7 @@
#include "hwrenderer/data/flatvertices.h"
#include "hwrenderer/dynlights/hw_lightbuffer.h"
#include "hwrenderer/scene/hw_portal.h"
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
#include "hw_fakeflat.h"
//==========================================================================
//
@ -53,32 +52,31 @@ void HWDrawInfo::DispatchRenderHacks()
TMap<int, gl_floodrendernode*>::Pair *fpair;
TMap<int, gl_subsectorrendernode*>::Iterator ofi(otherFloorPlanes);
GLFlat glflat;
sector_t fakesec;
glflat.section = nullptr;
while (ofi.NextPair(pair))
{
auto sec = hw_FakeFlat(&level.sectors[pair->Key], &fakesec, in_area, false);
auto sec = hw_FakeFlat(&level.sectors[pair->Key], in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_PLANEHACK);
}
TMap<int, gl_subsectorrendernode*>::Iterator oci(otherCeilingPlanes);
while (ofi.NextPair(pair))
{
auto sec = hw_FakeFlat(&level.sectors[pair->Key], &fakesec, in_area, false);
auto sec = hw_FakeFlat(&level.sectors[pair->Key], in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_PLANEHACK);
}
TMap<int, gl_floodrendernode*>::Iterator ffi(floodFloorSegs);
while (ffi.NextPair(fpair))
{
auto sec = hw_FakeFlat(&level.sectors[fpair->Key], &fakesec, in_area, false);
auto sec = hw_FakeFlat(&level.sectors[fpair->Key], in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_FLOODHACK);
}
TMap<int, gl_floodrendernode*>::Iterator fci(floodCeilingSegs);
while (fci.NextPair(fpair))
{
auto sec = hw_FakeFlat(&level.sectors[fpair->Key], &fakesec, in_area, false);
auto sec = hw_FakeFlat(&level.sectors[fpair->Key], in_area, false);
glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_FLOODHACK);
}
}
@ -336,7 +334,7 @@ bool HWDrawInfo::DoOneSectorUpper(subsector_t * subsec, float Planez, area_t in_
// Note: if this is a real line between sectors
// we can be sure that render_sector is the real sector!
sector_t * sec = hw_FakeFlat(seg->backsector, &fakesec, in_area, true);
sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true);
// Don't bother with slopes
if (sec->ceilingplane.isSlope()) return false;
@ -394,7 +392,7 @@ bool HWDrawInfo::DoOneSectorLower(subsector_t * subsec, float Planez, area_t in_
// Note: if this is a real line between sectors
// we can be sure that render_sector is the real sector!
sector_t * sec = hw_FakeFlat(seg->backsector, &fakesec, in_area, true);
sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true);
// Don't bother with slopes
if (sec->floorplane.isSlope()) return false;
@ -453,7 +451,7 @@ bool HWDrawInfo::DoFakeBridge(subsector_t * subsec, float Planez, area_t in_area
// Note: if this is a real line between sectors
// we can be sure that render_sector is the real sector!
sector_t * sec = hw_FakeFlat(seg->backsector, &fakesec, in_area, true);
sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true);
// Don't bother with slopes
if (sec->floorplane.isSlope()) return false;
@ -506,7 +504,7 @@ bool HWDrawInfo::DoFakeCeilingBridge(subsector_t * subsec, float Planez, area_t
// Note: if this is a real line between sectors
// we can be sure that render_sector is the real sector!
sector_t * sec = hw_FakeFlat(seg->backsector, &fakesec, in_area, true);
sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true);
// Don't bother with slopes
if (sec->ceilingplane.isSlope()) return false;
@ -538,8 +536,6 @@ bool HWDrawInfo::DoFakeCeilingBridge(subsector_t * subsec, float Planez, area_t
//==========================================================================
void HWDrawInfo::HandleMissingTextures(area_t in_area)
{
sector_t fake;
for (unsigned int i = 0; i < MissingUpperTextures.Size(); i++)
{
if (!MissingUpperTextures[i].seg) continue;
@ -589,7 +585,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
{
// It isn't a hole. Now check whether it might be a fake bridge
sector_t * fakesector = hw_FakeFlat(MissingUpperTextures[i].seg->frontsector, &fake, in_area, false);
sector_t * fakesector = hw_FakeFlat(MissingUpperTextures[i].seg->frontsector, in_area, false);
float planez = (float)fakesector->GetPlaneTexZ(sector_t::ceiling);
backsub->validcount = validcount;
@ -655,7 +651,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area)
{
// It isn't a hole. Now check whether it might be a fake bridge
sector_t * fakesector = hw_FakeFlat(MissingLowerTextures[i].seg->frontsector, &fake, in_area, false);
sector_t * fakesector = hw_FakeFlat(MissingLowerTextures[i].seg->frontsector, in_area, false);
float planez = (float)fakesector->GetPlaneTexZ(sector_t::floor);
backsub->validcount = validcount;
@ -729,9 +725,8 @@ void HWDrawInfo::CreateFloodPoly(wallseg * ws, FFlatVertex *vertices, float plan
void HWDrawInfo::PrepareUpperGap(seg_t * seg)
{
wallseg ws;
sector_t ffake, bfake;
sector_t * fakefsector = hw_FakeFlat(seg->frontsector, &ffake, in_area, true);
sector_t * fakebsector = hw_FakeFlat(seg->backsector, &bfake, in_area, false);
sector_t * fakefsector = hw_FakeFlat(seg->frontsector, in_area, false);
sector_t * fakebsector = hw_FakeFlat(seg->backsector, in_area, true);
vertex_t * v1, *v2;
@ -786,9 +781,8 @@ void HWDrawInfo::PrepareUpperGap(seg_t * seg)
void HWDrawInfo::PrepareLowerGap(seg_t * seg)
{
wallseg ws;
sector_t ffake, bfake;
sector_t * fakefsector = hw_FakeFlat(seg->frontsector, &ffake, in_area, true);
sector_t * fakebsector = hw_FakeFlat(seg->backsector, &bfake, in_area, false);
sector_t * fakefsector = hw_FakeFlat(seg->frontsector, in_area, false);
sector_t * fakebsector = hw_FakeFlat(seg->backsector, in_area, true);
vertex_t * v1, *v2;
@ -1220,7 +1214,7 @@ void HWDrawInfo::CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor
if (sub->numlines>2 && !(ss_renderflags[sub->Index()]&SSRF_PROCESSED)) return;
// Must be the exact same visplane
sector_t * me = hw_FakeFlat(sub->render_sector, &fakesec, in_area, false);
sector_t * me = hw_FakeFlat(sub->render_sector, in_area, false);
if (me->GetTexture(sector_t::ceiling) != anchor->GetTexture(sector_t::ceiling) ||
me->ceilingplane != anchor->ceilingplane ||
me->GetCeilingLight() != anchor->GetCeilingLight() ||
@ -1264,7 +1258,7 @@ void HWDrawInfo::CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor,
if (sub->numlines>2 && !(ss_renderflags[sub->Index()]&SSRF_PROCESSED)) return;
// Must be the exact same visplane
sector_t * me = hw_FakeFlat(sub->render_sector, &fakesec, in_area, false);
sector_t * me = hw_FakeFlat(sub->render_sector, in_area, false);
if (me->GetTexture(sector_t::floor) != anchor->GetTexture(sector_t::floor) ||
me->floorplane != anchor->floorplane ||
me->GetFloorLight() != anchor->GetFloorLight() ||
@ -1303,7 +1297,7 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area)
validcount++;
for (i=0;i<CeilingStacks.Size (); i++)
{
sector_t *sec = hw_FakeFlat(CeilingStacks[i], &fakesec, in_area, false);
sector_t *sec = hw_FakeFlat(CeilingStacks[i], in_area, false);
auto portal = sec->GetPortalGroup(sector_t::ceiling);
if (portal != NULL) for(int k=0;k<sec->subsectorcount;k++)
{
@ -1347,7 +1341,7 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area)
validcount++;
for (i=0;i<FloorStacks.Size (); i++)
{
sector_t *sec = hw_FakeFlat(FloorStacks[i], &fakesec, in_area, false);
sector_t *sec = hw_FakeFlat(FloorStacks[i], in_area, false);
auto portal = sec->GetPortalGroup(sector_t::floor);
if (portal != NULL) for(int k=0;k<sec->subsectorcount;k++)
{

View file

@ -755,7 +755,9 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t
if (sector->sectornum != thing->Sector->sectornum && !thruportal)
{
rendersector = hw_FakeFlat(thing->Sector, &rs, in_area, false);
// This cannot create a copy in the fake sector cache because it'd interfere with the main thread, so provide a local buffer for the copy.
// Adding synchronization for this one case would cost more than it might save if the result here could be cached.
rendersector = hw_FakeFlat(thing->Sector, in_area, false, &rs);
}
else
{
@ -1287,7 +1289,8 @@ void HWDrawInfo::ProcessActorsInPortal(FLinePortalSpan *glport, area_t in_area)
th->Prev += newpos - savedpos;
GLSprite spr;
spr.Process(this, th, hw_FakeFlat(th->Sector, &fakesector, in_area, false), in_area, 2);
// This is called from the worker thread and must not alter the fake sector cache.
spr.Process(this, th, hw_FakeFlat(th->Sector, in_area, false, &fakesector), in_area, 2);
th->Angles.Yaw = savedangle;
th->SetXYZ(savedpos);
th->Prev -= newpos - savedpos;

View file

@ -222,8 +222,7 @@ static WeaponLighting GetWeaponLighting(sector_t *viewsector, const DVector3 &po
}
else
{
sector_t fs;
auto fakesec = hw_FakeFlat(viewsector, &fs, in_area, false);
auto fakesec = hw_FakeFlat(viewsector, in_area, false);
// calculate light level for weapon sprites
l.lightlevel = hw_ClampLight(fakesec->lightlevel);