diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index ef1e6221d..af6a3acac 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -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(); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 67d4fba28..f3765d382 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -122,8 +122,6 @@ private: }; -#include "hwrenderer/scene/hw_fakeflat.h" - struct TexFilter_s { int minfilter; diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index 31a3694ee..39d4145f3 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -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)]; diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 6fc0e212f..24d6c2218 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -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); diff --git a/src/hwrenderer/scene/hw_drawlist.cpp b/src/hwrenderer/scene/hw_drawlist.cpp index 1ccbc31f3..067c5dee5 100644 --- a/src/hwrenderer/scene/hw_drawlist.cpp +++ b/src/hwrenderer/scene/hw_drawlist.cpp @@ -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. diff --git a/src/hwrenderer/scene/hw_fakeflat.cpp b/src/hwrenderer/scene/hw_fakeflat.cpp index 555504bc4..38cfaeefc 100644 --- a/src/hwrenderer/scene/hw_fakeflat.cpp +++ b/src/hwrenderer/scene/hw_fakeflat.cpp @@ -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; +} diff --git a/src/hwrenderer/scene/hw_fakeflat.h b/src/hwrenderer/scene/hw_fakeflat.h index c56f3798d..2f670d222 100644 --- a/src/hwrenderer/scene/hw_fakeflat.h +++ b/src/hwrenderer/scene/hw_fakeflat.h @@ -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); diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 5592a03ab..afa1eae78 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -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::Pair *fpair; TMap::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::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::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::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;iGetPortalGroup(sector_t::ceiling); if (portal != NULL) for(int k=0;ksubsectorcount;k++) { @@ -1347,7 +1341,7 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area) validcount++; for (i=0;iGetPortalGroup(sector_t::floor); if (portal != NULL) for(int k=0;ksubsectorcount;k++) { diff --git a/src/hwrenderer/scene/hw_sprites.cpp b/src/hwrenderer/scene/hw_sprites.cpp index 4bda3ab71..f3935dab6 100644 --- a/src/hwrenderer/scene/hw_sprites.cpp +++ b/src/hwrenderer/scene/hw_sprites.cpp @@ -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; diff --git a/src/hwrenderer/scene/hw_weapon.cpp b/src/hwrenderer/scene/hw_weapon.cpp index bdcf1f08e..d0bb20e4d 100644 --- a/src/hwrenderer/scene/hw_weapon.cpp +++ b/src/hwrenderer/scene/hw_weapon.cpp @@ -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);