diff --git a/src/common/rendering/hwrenderer/data/hw_renderstate.h b/src/common/rendering/hwrenderer/data/hw_renderstate.h index 016b85dd3..d7c5a98c6 100644 --- a/src/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/src/common/rendering/hwrenderer/data/hw_renderstate.h @@ -691,9 +691,10 @@ public: // Draw level mesh virtual void DrawLevelMeshDepthPass() { } virtual void DrawLevelMeshOpaquePass() { } + virtual int GetNextQueryIndex() { return 0; } virtual void BeginQuery() { } virtual void EndQuery() { } - virtual void GetQueryResults(TArray& results) { } + virtual void GetQueryResults(int start, int count, TArray& results) { } friend class Mesh; }; diff --git a/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp b/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp index 2b1911486..08a3854f4 100644 --- a/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp +++ b/src/common/rendering/vulkan/buffers/vk_rsbuffers.cpp @@ -105,6 +105,10 @@ VkRSBuffers::VkRSBuffers(VulkanRenderDevice* fb) .Create(fb->GetDevice()); Fogballbuffer.Data = Fogballbuffer.UBO->Map(0, Fogballbuffer.UBO->size); + + OcclusionQuery.QueryPool = QueryPoolBuilder() + .QueryType(VK_QUERY_TYPE_OCCLUSION, OcclusionQuery.MaxQueries) + .Create(fb->GetDevice()); } VkRSBuffers::~VkRSBuffers() diff --git a/src/common/rendering/vulkan/buffers/vk_rsbuffers.h b/src/common/rendering/vulkan/buffers/vk_rsbuffers.h index 65cb2dbf5..1eaddf676 100644 --- a/src/common/rendering/vulkan/buffers/vk_rsbuffers.h +++ b/src/common/rendering/vulkan/buffers/vk_rsbuffers.h @@ -61,6 +61,13 @@ public: std::unique_ptr MatrixBuffer; std::unique_ptr SurfaceUniformsBuffer; + + struct + { + const int MaxQueries = 64000; + std::unique_ptr QueryPool; + int NextIndex = 0; + } OcclusionQuery; }; class VkStreamBuffer diff --git a/src/common/rendering/vulkan/vk_renderstate.cpp b/src/common/rendering/vulkan/vk_renderstate.cpp index f360a15ac..03b518cbf 100644 --- a/src/common/rendering/vulkan/vk_renderstate.cpp +++ b/src/common/rendering/vulkan/vk_renderstate.cpp @@ -740,8 +740,9 @@ void VkRenderState::BeginFrame() mRSBuffers->Lightbuffer.UploadIndex = 0; mRSBuffers->Bonebuffer.UploadIndex = 0; mRSBuffers->Fogballbuffer.UploadIndex = 0; + mRSBuffers->OcclusionQuery.NextIndex = 0; - mNextOcclusionQueryIndex = 0; + fb->GetCommands()->GetDrawCommands()->resetQueryPool(mRSBuffers->OcclusionQuery.QueryPool.get(), 0, mRSBuffers->OcclusionQuery.MaxQueries); } void VkRenderState::EndRenderPass() @@ -862,12 +863,14 @@ void VkRenderState::DrawLevelMeshDepthPass() pipelineKey.ShaderKey.NoFragmentShader = true; DrawLevelMeshRange(mCommandBuffer, pipelineKey, range.Start, range.Count); } + /* for (LevelSubmeshDrawRange& range : submesh->PortalList) { VkPipelineKey pipelineKey = fb->GetLevelMeshPipelineKey(range.PipelineID); pipelineKey.ShaderKey.NoFragmentShader = true; DrawLevelMeshRange(mCommandBuffer, pipelineKey, range.Start, range.Count); } + */ } void VkRenderState::DrawLevelMeshOpaquePass() @@ -883,18 +886,34 @@ void VkRenderState::DrawLevelMeshOpaquePass() void VkRenderState::BeginQuery() { - // mCommandBuffer->beginQuery(mNextOcclusionQueryIndex++); + mCommandBuffer->beginQuery(mRSBuffers->OcclusionQuery.QueryPool.get(), mRSBuffers->OcclusionQuery.NextIndex++, 0); } void VkRenderState::EndQuery() { - // mCommandBuffer->endQuery(mNextOcclusionQueryIndex - 1); + mCommandBuffer->endQuery(mRSBuffers->OcclusionQuery.QueryPool.get(), mRSBuffers->OcclusionQuery.NextIndex - 1); } -void VkRenderState::GetQueryResults(TArray& results) +int VkRenderState::GetNextQueryIndex() { - //vkGetQueryPoolResults(fb->GetDevice()->device, ...) - results.Clear(); + return mRSBuffers->OcclusionQuery.NextIndex; +} + +void VkRenderState::GetQueryResults(int queryStart, int queryCount, TArray& results) +{ + fb->GetCommands()->FlushCommands(false); + + mQueryResultsBuffer.Resize(queryCount); + VkResult result = vkGetQueryPoolResults(fb->GetDevice()->device, mRSBuffers->OcclusionQuery.QueryPool->pool, queryStart, queryCount, mQueryResultsBuffer.Size() * sizeof(uint32_t), mQueryResultsBuffer.Data(), sizeof(uint32_t), VK_QUERY_RESULT_WAIT_BIT); + CheckVulkanError(result, "Could not query occlusion query results"); + if (result == VK_NOT_READY) + VulkanError("Occlusion query results returned VK_NOT_READY!"); + + results.Resize(queryCount); + for (int i = 0; i < queryCount; i++) + { + results[i] = mQueryResultsBuffer[i] != 0; + } } void VkRenderState::DrawLevelMeshRange(VulkanCommandBuffer* cmdbuffer, const VkPipelineKey& pipelineKey, int start, int count) diff --git a/src/common/rendering/vulkan/vk_renderstate.h b/src/common/rendering/vulkan/vk_renderstate.h index 9ca947870..dbe594279 100644 --- a/src/common/rendering/vulkan/vk_renderstate.h +++ b/src/common/rendering/vulkan/vk_renderstate.h @@ -59,9 +59,10 @@ public: // Draw level mesh void DrawLevelMeshDepthPass() override; void DrawLevelMeshOpaquePass() override; + int GetNextQueryIndex() override; void BeginQuery() override; void EndQuery() override; - void GetQueryResults(TArray& results) override; + void GetQueryResults(int start, int count, TArray& results) override; // Worker threads void FlushCommands() override { EndRenderPass(); } @@ -119,8 +120,6 @@ protected: int mColorMask = 15; int mCullMode = 0; - int mNextOcclusionQueryIndex = 0; - PushConstants mPushConstants = {}; uint32_t mLastViewpointOffset = 0xffffffff; @@ -150,6 +149,8 @@ protected: VkSampleCountFlagBits Samples = VK_SAMPLE_COUNT_1_BIT; int DrawBuffers = 1; } mRenderTarget; + + TArray mQueryResultsBuffer; }; class VkRenderStateMolten : public VkRenderState diff --git a/src/rendering/hwrenderer/doom_levelsubmesh.cpp b/src/rendering/hwrenderer/doom_levelsubmesh.cpp index 372641cb1..149634dff 100644 --- a/src/rendering/hwrenderer/doom_levelsubmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelsubmesh.cpp @@ -63,6 +63,7 @@ void DoomLevelSubmesh::UpdateDynamic(FLevelLocals& doomMap, int lightmapStartInd void DoomLevelSubmesh::Reset() { Surfaces.Clear(); + WallPortals.Clear(); MeshVertices.Clear(); MeshElements.Clear(); MeshSurfaceIndexes.Clear(); @@ -168,6 +169,11 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap) surf.PipelineID = pipelineID; Surfaces.Push(surf); } + + for (const HWWall& portal : result.portals) + { + WallPortals.Push(portal); + } } // Create surfaces for all flats diff --git a/src/rendering/hwrenderer/doom_levelsubmesh.h b/src/rendering/hwrenderer/doom_levelsubmesh.h index 93792b783..5874d53e8 100644 --- a/src/rendering/hwrenderer/doom_levelsubmesh.h +++ b/src/rendering/hwrenderer/doom_levelsubmesh.h @@ -2,6 +2,7 @@ #pragma once #include "hw_levelmesh.h" +#include "scene/hw_drawstructs.h" #include "tarray.h" #include "vectors.h" #include "r_defs.h" @@ -14,16 +15,6 @@ typedef dp::rect_pack::RectPacker RectPacker; struct FLevelLocals; struct FPolyObj; -enum DoomLevelMeshSurfaceType -{ - ST_NONE, - ST_MIDDLESIDE, - ST_UPPERSIDE, - ST_LOWERSIDE, - ST_CEILING, - ST_FLOOR -}; - struct DoomLevelMeshSurface : public LevelMeshSurface { DoomLevelMeshSurfaceType Type = ST_NONE; @@ -58,6 +49,8 @@ public: TArray> PolyLMSurfaces; + TArray WallPortals; + private: void Reset(); void BuildSectorGroups(const FLevelLocals& doomMap); diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index bcd4f1d87..1861768c1 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -35,6 +35,7 @@ #include "hw_renderstate.h" #include "hw_drawinfo.h" #include "hw_drawcontext.h" +#include "hw_walldispatcher.h" #include "po_man.h" #include "models.h" #include "hw_clock.h" @@ -403,21 +404,59 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state) if (gl_levelmesh) { - // To do: - // 1) draw level into depth buffer - // 2) use occlusion queries on all portals in level to decide which are visible - // 3) draw opaque level so the GPU has something to do while we examine the query results - // 4) retrieve the query results and use them to fill the portal manager with portals - // + // Give the DrawInfo the viewpoint in fixed point because that's what the nodes are. + viewx = FLOAT2FIXED(Viewpoint.Pos.X); + viewy = FLOAT2FIXED(Viewpoint.Pos.Y); + + validcount++; // used for processing sidedefs only once by the renderer. + + auto& wallPortals = static_cast(level.levelMesh->StaticMesh.get())->WallPortals; + + // draw level into depth buffer state.DrawLevelMeshDepthPass(); - // for (HWWall& wall : PortalWalls) { state.BeginQuery(); wall.Draw(state); state.EndQuery(); } - // for (HWFlat& flat : PortalFlats) { state.BeginQuery(); flat.Draw(state); state.EndQuery(); } + + // use occlusion queries on all portals in level to decide which are visible + int queryStart = state.GetNextQueryIndex(); + state.SetColorMask(false); + state.SetDepthMask(false); + state.EnableTexture(false); + state.SetEffect(EFF_FOGBOUNDARY); + state.AlphaFunc(Alpha_GEqual, 0.f); + for (HWWall& wall : wallPortals) + { + state.BeginQuery(); + + wall.MakeVertices(state, false); + wall.RenderWall(state, HWWall::RWF_BLANK); + wall.vertcount = 0; + + state.EndQuery(); + } + state.SetEffect(EFF_NONE); + state.EnableTexture(true); + state.SetColorMask(true); + state.SetDepthMask(true); + int queryEnd = state.GetNextQueryIndex(); + + // draw opaque level so the GPU has something to do while we examine the query results state.DrawLevelMeshOpaquePass(); - state.GetQueryResults(QueryResultsBuffer); + + // retrieve the query results and use them to fill the portal manager with portals + state.GetQueryResults(queryStart, queryEnd - queryStart, QueryResultsBuffer); for (unsigned int i = 0, count = QueryResultsBuffer.Size(); i < count; i++) { bool portalVisible = QueryResultsBuffer[i]; + if (portalVisible) + { + PutWallPortal(wallPortals[i], state); + } } + + // Process all the sprites on the current portal's back side which touch the portal. + if (mCurrentPortal != nullptr) mCurrentPortal->RenderAttached(this, state); + + if (drawpsprites) + PreparePlayerSprites(Viewpoint.sector, in_area, state); } else { @@ -447,6 +486,34 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state) } +void HWDrawInfo::PutWallPortal(HWWall wall, FRenderState& state) +{ + HWWallDispatcher ddi(this); + + int portaltype = wall.portaltype; + int portalplane = wall.portalplane; + + HWSkyInfo skyinfo; + if (portaltype == PORTALTYPE_SKY) + { + skyinfo.init(this, wall.frontsector->sky, wall.Colormap.FadeColor); + wall.sky = &skyinfo; + } + else if (portaltype == PORTALTYPE_SECTORSTACK) + { + if (screen->instack[1 - portalplane]) + return; + } + else if (portaltype == PORTALTYPE_PLANEMIRROR) + { + auto vpz = Viewpoint.Pos.Z; + if ((portalplane == sector_t::ceiling && vpz > wall.frontsector->ceilingplane.fD()) || (portalplane == sector_t::floor && vpz < -wall.frontsector->floorplane.fD())) + return; + wall.planemirror = (portalplane == sector_t::ceiling) ? &wall.frontsector->ceilingplane : &wall.frontsector->floorplane; + } + wall.PutPortal(&ddi, state, portaltype, portalplane); +} + void HWDrawInfo::UpdateLightmaps() { if (!outer && VisibleSurfaces.Size() < unsigned(lm_background_updates)) @@ -850,7 +917,7 @@ void HWDrawInfo::DrawScene(int drawmode, FRenderState& state) } state.SetDepthMask(true); - if (!gl_no_skyclear) drawctx->portalState.RenderFirstSkyPortal(recursion, this, state); + if (!gl_no_skyclear && !gl_levelmesh) drawctx->portalState.RenderFirstSkyPortal(recursion, this, state); RenderScene(state); diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index acaed30dd..255cffd60 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -262,6 +262,7 @@ public: void DrawScene(int drawmode, FRenderState& state); void CreateScene(bool drawpsprites, FRenderState& state); + void PutWallPortal(HWWall wall, FRenderState& state); void RenderScene(FRenderState &state); void RenderTranslucent(FRenderState &state); void RenderPortal(HWPortal *p, FRenderState &state, bool usestencil); diff --git a/src/rendering/hwrenderer/scene/hw_drawstructs.h b/src/rendering/hwrenderer/scene/hw_drawstructs.h index 3b4e68c9b..3f2769133 100644 --- a/src/rendering/hwrenderer/scene/hw_drawstructs.h +++ b/src/rendering/hwrenderer/scene/hw_drawstructs.h @@ -8,7 +8,6 @@ #include "renderstyle.h" #include "textures.h" #include "r_data/colormaps.h" -#include "doom_levelmesh.h" #ifdef _MSC_VER #pragma warning(disable:4244) @@ -33,6 +32,7 @@ struct HWDecal; struct FSection; enum area_t : int; class HWDrawContext; +class DoomLevelMeshSurface; enum HWRenderStyle { @@ -67,6 +67,16 @@ enum PortalTypes PORTALTYPE_LINETOLINE, }; +enum DoomLevelMeshSurfaceType +{ + ST_NONE, + ST_MIDDLESIDE, + ST_UPPERSIDE, + ST_LOWERSIDE, + ST_CEILING, + ST_FLOOR +}; + //========================================================================== // // One sector plane, still in fixed point diff --git a/src/rendering/hwrenderer/scene/hw_sky.cpp b/src/rendering/hwrenderer/scene/hw_sky.cpp index 9e01ba1bc..5cb47ea3e 100644 --- a/src/rendering/hwrenderer/scene/hw_sky.cpp +++ b/src/rendering/hwrenderer/scene/hw_sky.cpp @@ -126,9 +126,9 @@ void HWWall::SkyPlane(HWWallDispatcher *di, FRenderState& state, sector_t *secto { HWSkyInfo skyinfo; skyinfo.init(di->di, sector->sky, Colormap.FadeColor); - ptype = PORTALTYPE_SKY; sky = &skyinfo; } + ptype = PORTALTYPE_SKY; PutPortal(di, state, ptype, plane); } else if (sportal != nullptr)