diff --git a/src/swrenderer/line/r_line.cpp b/src/swrenderer/line/r_line.cpp index 17a475862..ddecf5879 100644 --- a/src/swrenderer/line/r_line.cpp +++ b/src/swrenderer/line/r_line.cpp @@ -64,7 +64,6 @@ namespace swrenderer void SWRenderLine::Render(seg_t *line, subsector_t *subsector, sector_t *sector, sector_t *fakebacksector, VisiblePlane *linefloorplane, VisiblePlane *lineceilingplane, bool infog, FDynamicColormap *colormap) { - static sector_t tempsec; // killough 3/8/98: ceiling/water hack bool solid; DVector2 pt1, pt2; @@ -284,11 +283,7 @@ namespace swrenderer rw_floorstat = wallbottom.Project(frontsector->floorplane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); } - static SWRenderLine *self = this; - bool visible = Thread->ClipSegments->Clip(WallC.sx1, WallC.sx2, solid, [](int x1, int x2) -> bool - { - return self->RenderWallSegment(x1, x2); - }); + bool visible = Thread->ClipSegments->Clip(WallC.sx1, WallC.sx2, solid, this); if (visible) { diff --git a/src/swrenderer/line/r_line.h b/src/swrenderer/line/r_line.h index 75b6d69b3..ef38e9e65 100644 --- a/src/swrenderer/line/r_line.h +++ b/src/swrenderer/line/r_line.h @@ -15,6 +15,7 @@ #include "vectors.h" #include "r_wallsetup.h" +#include "swrenderer/segments/r_clipsegment.h" struct seg_t; struct subsector_t; @@ -48,7 +49,7 @@ namespace swrenderer void InitFromLine(RenderThread *thread, const DVector2 &left, const DVector2 &right); }; - class SWRenderLine + class SWRenderLine : VisibleSegmentRenderer { public: SWRenderLine(RenderThread *thread); @@ -57,7 +58,7 @@ namespace swrenderer RenderThread *Thread = nullptr; private: - bool RenderWallSegment(int x1, int x2); + bool RenderWallSegment(int x1, int x2) override; void SetWallVariables(bool needlights); void RenderWallSegmentTextures(int x1, int x2); @@ -133,5 +134,7 @@ namespace swrenderer ProjectedWallLine wallupper; ProjectedWallLine walllower; ProjectedWallTexcoords walltexcoords; + + sector_t tempsec; // killough 3/8/98: ceiling/water hack }; } diff --git a/src/swrenderer/line/r_walldraw.cpp b/src/swrenderer/line/r_walldraw.cpp index 9a07198e5..ea165ab04 100644 --- a/src/swrenderer/line/r_walldraw.cpp +++ b/src/swrenderer/line/r_walldraw.cpp @@ -41,6 +41,7 @@ #include "swrenderer/line/r_walldraw.h" #include "swrenderer/line/r_wallsetup.h" #include "swrenderer/r_renderthread.h" +#include "swrenderer/r_memory.h" namespace swrenderer { @@ -188,14 +189,22 @@ namespace swrenderer drawerargs.dc_viewpos.Z = (float)((viewport->CenterY - y1 - 0.5) / viewport->InvZtoScale * zcol); drawerargs.dc_viewpos_step.Z = (float)(-zcol / viewport->InvZtoScale); - static TriLight lightbuffer[64 * 1024]; - static int nextlightindex = 0; + // Calculate max lights that can touch column so we can allocate memory for the list + int max_lights = 0; + FLightNode *cur_node = light_list; + while (cur_node) + { + if (!(cur_node->lightsource->flags2&MF2_DORMANT)) + max_lights++; + cur_node = cur_node->nextLight; + } + + drawerargs.dc_num_lights = 0; + drawerargs.dc_lights = Thread->FrameMemory->AllocMemory(max_lights); // Setup lights for column - drawerargs.dc_num_lights = 0; - drawerargs.dc_lights = lightbuffer + nextlightindex; - FLightNode *cur_node = light_list; - while (cur_node && nextlightindex < 64 * 1024) + cur_node = light_list; + while (cur_node) { if (!(cur_node->lightsource->flags2&MF2_DORMANT)) { @@ -220,7 +229,6 @@ namespace swrenderer uint32_t green = cur_node->lightsource->GetGreen(); uint32_t blue = cur_node->lightsource->GetBlue(); - nextlightindex++; auto &light = drawerargs.dc_lights[drawerargs.dc_num_lights++]; light.x = lconstant; light.y = nlconstant; @@ -232,9 +240,6 @@ namespace swrenderer cur_node = cur_node->nextLight; } - - if (nextlightindex == 64 * 1024) - nextlightindex = 0; } else { diff --git a/src/swrenderer/r_renderthread.cpp b/src/swrenderer/r_renderthread.cpp index 53b5f62f8..ba48993ca 100644 --- a/src/swrenderer/r_renderthread.cpp +++ b/src/swrenderer/r_renderthread.cpp @@ -56,9 +56,10 @@ namespace swrenderer { - RenderThread::RenderThread(RenderScene *scene) + RenderThread::RenderThread(RenderScene *scene, bool mainThread) { Scene = scene; + MainThread = mainThread; FrameMemory = std::make_unique(); DrawQueue = std::make_shared(this); OpaquePass = std::make_unique(this); diff --git a/src/swrenderer/r_renderthread.h b/src/swrenderer/r_renderthread.h index 8d60de5c7..8a4e8c54b 100644 --- a/src/swrenderer/r_renderthread.h +++ b/src/swrenderer/r_renderthread.h @@ -47,12 +47,13 @@ namespace swrenderer class RenderThread { public: - RenderThread(RenderScene *scene); + RenderThread(RenderScene *scene, bool mainThread = true); ~RenderThread(); RenderScene *Scene; int X1 = 0; int X2 = MAXWIDTH; + bool MainThread = false; std::unique_ptr FrameMemory; std::unique_ptr OpaquePass; @@ -65,7 +66,11 @@ namespace swrenderer std::unique_ptr DrawSegments; std::unique_ptr ClipSegments; DrawerCommandQueuePtr DrawQueue; - + + // VisibleSprite working buffers + short clipbot[MAXWIDTH]; + short cliptop[MAXWIDTH]; + SWPixelFormatDrawers *Drawers(); private: diff --git a/src/swrenderer/scene/r_scene.cpp b/src/swrenderer/scene/r_scene.cpp index 8c6ee7198..16a73ec91 100644 --- a/src/swrenderer/scene/r_scene.cpp +++ b/src/swrenderer/scene/r_scene.cpp @@ -52,6 +52,8 @@ EXTERN_CVAR(Bool, r_shadercolormaps) EXTERN_CVAR(Int, r_clearbuffer) +CVAR(Bool, r_scene_multithreaded, false, 0); + namespace swrenderer { cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; @@ -100,8 +102,19 @@ namespace swrenderer { MainThread()->DrawQueue->Push(CameraLight::Instance()->ShaderColormap(), screen); } - - DrawerThreads::Execute({ MainThread()->DrawQueue }); + + RenderDrawQueues(); + } + + void RenderScene::RenderDrawQueues() + { + // Use reverse order so main thread is drawn last + std::vector queues; + for (auto it = Threads.rbegin(); it != Threads.rend(); ++it) + { + queues.push_back((*it)->DrawQueue); + } + DrawerThreads::Execute(queues); } void RenderScene::RenderActorView(AActor *actor, bool dontmaplines) @@ -132,48 +145,7 @@ namespace swrenderer camera->renderflags |= RF_INVISIBLE; } - MainThread()->FrameMemory->Clear(); - MainThread()->Clip3DFloors->Cleanup(); - MainThread()->Clip3DFloors->ResetClip(); // reset clips (floor/ceiling) - MainThread()->Portal->CopyStackedViewParameters(); - MainThread()->ClipSegments->Clear(0, viewwidth); - MainThread()->DrawSegments->Clear(); - MainThread()->PlaneList->Clear(); - MainThread()->TranslucentPass->Clear(); - MainThread()->OpaquePass->ClearClip(); - MainThread()->OpaquePass->ResetFakingUnderwater(); // [RH] Hack to make windows into underwater areas possible - MainThread()->Portal->SetMainPortal(); - - // Cull things outside the range seen by this thread - if (MainThread()->X1 > 0) - MainThread()->ClipSegments->Clip(0, MainThread()->X1, true, [](int, int) { return true; }); - if (MainThread()->X2 < viewwidth) - MainThread()->ClipSegments->Clip(MainThread()->X2, viewwidth, true, [](int, int) { return true; }); - - WallCycles.Clock(); - MainThread()->OpaquePass->RenderScene(); - MainThread()->Clip3DFloors->ResetClip(); // reset clips (floor/ceiling) - WallCycles.Unclock(); - - NetUpdate(); - - if (viewactive) - { - PlaneCycles.Clock(); - MainThread()->PlaneList->Render(); - MainThread()->Portal->RenderPlanePortals(); - PlaneCycles.Unclock(); - - MainThread()->Portal->RenderLinePortals(); - - NetUpdate(); - - MaskedCycles.Clock(); - MainThread()->TranslucentPass->Render(); - MaskedCycles.Unclock(); - - NetUpdate(); - } + RenderThreadSlices(); camera->renderflags = savedflags; interpolator.RestoreInterpolations(); @@ -186,6 +158,94 @@ namespace swrenderer } } + void RenderScene::RenderThreadSlices() + { + int numThreads = r_scene_multithreaded ? 8 : 1; + + while (Threads.size() > (size_t)numThreads) + { + Threads.pop_back(); + } + + while (Threads.size() < (size_t)numThreads) + { + Threads.push_back(std::make_unique(this)); + } + + for (int i = 0; i < numThreads; i++) + { + Threads[i]->X1 = viewwidth * i / numThreads; + Threads[i]->X2 = viewwidth * (i + 1) / numThreads; + } + + for (int i = 0; i < numThreads; i++) + { + RenderThreadSlice(Threads[i].get()); + } + } + + void RenderScene::RenderThreadSlice(RenderThread *thread) + { + thread->FrameMemory->Clear(); + thread->Clip3DFloors->Cleanup(); + thread->Clip3DFloors->ResetClip(); // reset clips (floor/ceiling) + thread->Portal->CopyStackedViewParameters(); + thread->ClipSegments->Clear(0, viewwidth); + thread->DrawSegments->Clear(); + thread->PlaneList->Clear(); + thread->TranslucentPass->Clear(); + thread->OpaquePass->ClearClip(); + thread->OpaquePass->ResetFakingUnderwater(); // [RH] Hack to make windows into underwater areas possible + thread->Portal->SetMainPortal(); + + // Cull things outside the range seen by this thread + VisibleSegmentRenderer visitor; + if (thread->X1 > 0) + thread->ClipSegments->Clip(0, thread->X1, true, &visitor); + if (thread->X2 < viewwidth) + thread->ClipSegments->Clip(thread->X2, viewwidth, true, &visitor); + + if (thread->MainThread) + WallCycles.Clock(); + + thread->OpaquePass->RenderScene(); + thread->Clip3DFloors->ResetClip(); // reset clips (floor/ceiling) + + if (thread == MainThread()) + WallCycles.Unclock(); + + if (thread->MainThread) + NetUpdate(); + + if (viewactive) + { + if (thread->MainThread) + PlaneCycles.Clock(); + + thread->PlaneList->Render(); + thread->Portal->RenderPlanePortals(); + + if (thread->MainThread) + PlaneCycles.Unclock(); + + thread->Portal->RenderLinePortals(); + + if (thread->MainThread) + NetUpdate(); + + if (thread->MainThread) + MaskedCycles.Clock(); + + thread->TranslucentPass->Render(); + + if (thread->MainThread) + MaskedCycles.Unclock(); + + if (thread->MainThread) + NetUpdate(); + } + } + void RenderScene::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines) { auto viewport = RenderViewport::Instance(); @@ -202,8 +262,7 @@ namespace swrenderer viewport->SetViewport(width, height, WidescreenRatio); RenderActorView(actor, dontmaplines); - - DrawerThreads::Execute({ MainThread()->DrawQueue }); + RenderDrawQueues(); viewport->RenderTarget = screen; diff --git a/src/swrenderer/scene/r_scene.h b/src/swrenderer/scene/r_scene.h index a592ae1d3..d3053884c 100644 --- a/src/swrenderer/scene/r_scene.h +++ b/src/swrenderer/scene/r_scene.h @@ -46,6 +46,9 @@ namespace swrenderer private: void RenderActorView(AActor *actor, bool dontmaplines = false); + void RenderDrawQueues(); + void RenderThreadSlices(); + void RenderThreadSlice(RenderThread *thread); bool dontmaplines = false; int clearcolor = 0; diff --git a/src/swrenderer/segments/r_clipsegment.cpp b/src/swrenderer/segments/r_clipsegment.cpp index 6cfaaa2a0..4e25daa35 100644 --- a/src/swrenderer/segments/r_clipsegment.cpp +++ b/src/swrenderer/segments/r_clipsegment.cpp @@ -86,7 +86,7 @@ namespace swrenderer return true; } - bool RenderClipSegment::Clip(int first, int last, bool solid, VisibleSegmentCallback callback) + bool RenderClipSegment::Clip(int first, int last, bool solid, VisibleSegmentRenderer *visitor) { cliprange_t *next, *start; int i, j; @@ -104,7 +104,7 @@ namespace swrenderer if (last <= start->first) { // Post is entirely visible (above start). - if (!callback(first, last)) + if (!visitor->RenderWallSegment(first, last)) return true; // Insert a new clippost for solid walls. @@ -131,7 +131,7 @@ namespace swrenderer } // There is a fragment above *start. - if (callback(first, start->first) && solid) + if (visitor->RenderWallSegment(first, start->first) && solid) { start->first = first; // Adjust the clip size for solid walls } @@ -146,7 +146,7 @@ namespace swrenderer while (last >= (next + 1)->first) { // There is a fragment between two posts. - clipsegment = callback(next->last, (next + 1)->first); + clipsegment = visitor->RenderWallSegment(next->last, (next + 1)->first); next++; if (last <= next->last) @@ -158,7 +158,7 @@ namespace swrenderer } // There is a fragment after *next. - clipsegment = callback(next->last, last); + clipsegment = visitor->RenderWallSegment(next->last, last); crunch: if (!clipsegment) diff --git a/src/swrenderer/segments/r_clipsegment.h b/src/swrenderer/segments/r_clipsegment.h index b2fde462e..48a083ba6 100644 --- a/src/swrenderer/segments/r_clipsegment.h +++ b/src/swrenderer/segments/r_clipsegment.h @@ -17,11 +17,18 @@ namespace swrenderer { typedef bool(*VisibleSegmentCallback)(int x1, int x2); + class VisibleSegmentRenderer + { + public: + virtual ~VisibleSegmentRenderer() { } + virtual bool RenderWallSegment(int x1, int x2) { return true; } + }; + class RenderClipSegment { public: void Clear(short left, short right); - bool Clip(int x1, int x2, bool solid, VisibleSegmentCallback callback); + bool Clip(int x1, int x2, bool solid, VisibleSegmentRenderer *visitor); bool Check(int first, int last); bool IsVisible(int x1, int x2); diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp index 78e9975e6..0fb0b27c0 100644 --- a/src/swrenderer/things/r_playersprite.cpp +++ b/src/swrenderer/things/r_playersprite.cpp @@ -78,7 +78,6 @@ namespace swrenderer DPSprite* psp; DPSprite* weapon; sector_t* sec = NULL; - static sector_t tempsec; int floorlight, ceilinglight; F3DFloor *rover; diff --git a/src/swrenderer/things/r_playersprite.h b/src/swrenderer/things/r_playersprite.h index 09274702a..588f9f8dc 100644 --- a/src/swrenderer/things/r_playersprite.h +++ b/src/swrenderer/things/r_playersprite.h @@ -88,5 +88,6 @@ namespace swrenderer enum { BASEYCENTER = 100 }; TArray AcceleratedSprites; + sector_t tempsec; }; } diff --git a/src/swrenderer/things/r_visiblesprite.cpp b/src/swrenderer/things/r_visiblesprite.cpp index ac411db57..17996b5e9 100644 --- a/src/swrenderer/things/r_visiblesprite.cpp +++ b/src/swrenderer/things/r_visiblesprite.cpp @@ -43,8 +43,8 @@ namespace swrenderer { void VisibleSprite::Render(RenderThread *thread) { - static short clipbot[MAXWIDTH]; - static short cliptop[MAXWIDTH]; + short *clipbot = thread->clipbot; + short *cliptop = thread->cliptop; VisibleSprite *spr = this;