Draw sorted by pipeline

This commit is contained in:
Magnus Norddahl 2025-05-30 18:52:44 +02:00
commit 1791167fe5
9 changed files with 103 additions and 170 deletions

View file

@ -369,94 +369,3 @@ void MeshBufferUploads::Add(int position, int count)
auto rightPos = right - Ranges.begin();
Ranges.Delete(leftPos, rightPos - leftPos);
}
/////////////////////////////////////////////////////////////////////////////
void LevelMeshDrawList::Add(int position, int count)
{
if (count <= 0)
return;
MeshBufferRange range = { position, position + count };
// First element?
if (Ranges.Size() == 0)
{
Ranges.push_back(range);
return;
}
// Find start position in ranges
auto right = std::lower_bound(Ranges.begin(), Ranges.end(), range, [](const auto& a, const auto& b) { return a.Start < b.Start; });
bool leftExists = right != Ranges.begin();
bool rightExists = right != Ranges.end();
auto left = right;
if (leftExists)
--left;
// Is this a gap between two ranges?
if ((!leftExists || left->End < range.Start) && (!rightExists || right->Start > range.End))
{
Ranges.Insert(right - Ranges.begin(), range);
return;
}
// Are we extending the left or the right range?
if (leftExists && range.Start <= left->End)
{
left->End = std::max(left->End, range.End);
right = left;
}
else // if (rightExists && right->Start <= range.End)
{
right->Start = range.Start;
right->End = std::max(right->End, range.End);
left = right;
}
// Merge overlaps to the right
while (true)
{
++right;
if (right == Ranges.end() || right->Start > range.End)
break;
left->End = std::max(right->End, range.End);
}
// Remove ranges now covered by the extended range
//ranges.erase(++left, right);
++left;
auto leftPos = left - Ranges.begin();
auto rightPos = right - Ranges.begin();
Ranges.Delete(leftPos, rightPos - leftPos);
}
void LevelMeshDrawList::Remove(int position, int count)
{
if (count <= 0)
return;
MeshBufferRange range = { position, position + count };
auto entry = std::lower_bound(Ranges.begin(), Ranges.end(), range, [](const auto& a, const auto& b) { return a.End < b.End; });
if (entry->Start == range.Start && entry->End == range.End)
{
Ranges.Delete(entry - Ranges.begin());
}
else if (entry->Start == range.Start)
{
entry->Start = range.End;
}
else if (entry->End == range.End)
{
entry->End = range.Start;
}
else
{
MeshBufferRange split;
split.Start = entry->Start;
split.End = range.Start;
entry->Start = range.End;
Ranges.Insert(entry - Ranges.begin(), split);
}
}

View file

@ -92,17 +92,6 @@ private:
TArray<MeshBufferRange> Ranges;
};
class LevelMeshDrawList
{
public:
void Add(int position, int count);
void Remove(int position, int count);
const TArray<MeshBufferRange>& GetRanges() const { return Ranges; }
private:
TArray<MeshBufferRange> Ranges;
};
struct LevelMeshLimits
{
int MaxVertices = 0;
@ -111,15 +100,6 @@ struct LevelMeshLimits
int MaxIndexes = 0;
};
enum class LevelMeshDrawType
{
Opaque,
Masked,
Portal,
Translucent,
NumDrawTypes
};
class LevelMesh
{
public:

View file

@ -828,7 +828,7 @@ public:
// Draw level mesh
virtual void ApplyLevelMesh() { }
virtual void DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader) {}
virtual void DrawLevelMeshList(const TArray<TArray<MeshBufferRange>>& drawList, bool masked, bool noFragmentShader) {}
virtual void DispatchLightTiles(const VSMatrix& worldToView, float m5) { }
virtual int GetNextQueryIndex() { return 0; }
virtual void BeginQuery() { }

View file

@ -919,7 +919,6 @@ void VkRenderState::ApplyLevelMesh()
ApplyStencilRef();
ApplyDepthBias();
mNeedApply = true;
mLevelMeshPipelineID = -1;
VkBuffer vertexBuffers[2] = { fb->GetLevelMesh()->GetVertexBuffer()->buffer, fb->GetLevelMesh()->GetUniformIndexBuffer()->buffer };
VkDeviceSize vertexBufferOffsets[] = { 0, 0 };
@ -1061,16 +1060,22 @@ void VkRenderState::DispatchLightTiles(const VSMatrix& worldToView, float m5)
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkRenderState::DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader)
void VkRenderState::DrawLevelMeshList(const TArray<TArray<MeshBufferRange>>& drawList, bool masked, bool noFragmentShader)
{
if (pipelineID != mLevelMeshPipelineID)
int pipelineID = 0;
for (const TArray<MeshBufferRange>& ranges : drawList)
{
const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID);
ApplyLevelMeshPipeline(mCommandBuffer, key, drawType, noFragmentShader);
mLevelMeshPipelineID = pipelineID;
if (ranges.size() != 0)
{
const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID);
ApplyLevelMeshPipeline(mCommandBuffer, key, masked, noFragmentShader);
for (const MeshBufferRange& range : ranges)
{
mCommandBuffer->drawIndexed(range.Count(), 1, range.Start, 0, 0);
}
}
pipelineID++;
}
mCommandBuffer->drawIndexed(indexCount, 1, firstIndex, 0, 0);
}
void VkRenderState::BeginQuery()
@ -1107,9 +1112,9 @@ void VkRenderState::GetQueryResults(int queryStart, int queryCount, TArray<bool>
}
}
void VkRenderState::ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, LevelMeshDrawType drawType, bool noFragmentShader)
void VkRenderState::ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, bool masked, bool noFragmentShader)
{
if (drawType == LevelMeshDrawType::Masked && noFragmentShader)
if (masked && noFragmentShader)
{
// We unfortunately have to run the fragment shader to know which pixels are masked. Use a simplified version to reduce the cost.
noFragmentShader = false;

View file

@ -58,7 +58,7 @@ public:
// Draw level mesh
void ApplyLevelMesh() override;
void DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader) override;
void DrawLevelMeshList(const TArray<TArray<MeshBufferRange>>& drawList, bool masked, bool noFragmentShader) override;
void DispatchLightTiles(const VSMatrix& worldToView, float m5) override;
int GetNextQueryIndex() override;
void BeginQuery() override;
@ -92,7 +92,7 @@ protected:
void WaitForStreamBuffers();
void RunZMinMaxPass();
void ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, LevelMeshDrawType drawType, bool noFragmentShader);
void ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, bool masked, bool noFragmentShader);
VulkanRenderDevice* fb = nullptr;
@ -107,8 +107,6 @@ protected:
bool mNeedApply = true;
bool mDrawLine = false;
int mLevelMeshPipelineID = -1;
int mScissorX = 0, mScissorY = 0, mScissorWidth = -1, mScissorHeight = -1;
int mViewportX = 0, mViewportY = 0, mViewportWidth = -1, mViewportHeight = -1;
float mViewportDepthMin = 0.0f, mViewportDepthMax = 1.0f;

View file

@ -318,24 +318,24 @@ void DoomLevelMesh::SetLimits(FLevelLocals& doomMap)
Reset(limits);
}
void DoomLevelMesh::DrawSectors(FRenderState& renderstate, const TArray<int>& sectors, LevelMeshDrawType drawType, bool noFragmentShader)
void DoomLevelMesh::AddSectorsToDrawLists(const TArray<int>& sectors, LevelMeshDrawLists& lists)
{
for (int sectorIndex : sectors)
{
for (const DrawRangeInfo& di : Flats[sectorIndex].DrawRanges[(int)drawType])
for (const DrawRangeInfo& di : Flats[sectorIndex].DrawRanges)
{
renderstate.DrawLevelMeshRange(di.IndexStart, di.IndexCount, di.PipelineID, drawType, noFragmentShader);
lists.Add(di.DrawType, di.PipelineID, { di.IndexStart, di.IndexStart + di.IndexCount });
}
}
}
void DoomLevelMesh::DrawSides(FRenderState& renderstate, const TArray<int>& sides, LevelMeshDrawType drawType, bool noFragmentShader)
void DoomLevelMesh::AddSidesToDrawLists(const TArray<int>& sides, LevelMeshDrawLists& lists)
{
for (int sideIndex : sides)
{
for (const DrawRangeInfo& di : Sides[sideIndex].DrawRanges[(int)drawType])
for (const DrawRangeInfo& di : Sides[sideIndex].DrawRanges)
{
renderstate.DrawLevelMeshRange(di.IndexStart, di.IndexCount, di.PipelineID, drawType, noFragmentShader);
lists.Add(di.DrawType, di.PipelineID, { di.IndexStart, di.IndexStart + di.IndexCount });
}
}
}
@ -653,8 +653,7 @@ void DoomLevelMesh::FreeSide(FLevelLocals& doomMap, unsigned int sideIndex)
FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount);
Sides[sideIndex].Geometries.Clear();
for (auto& list : Sides[sideIndex].DrawRanges)
list.Clear();
Sides[sideIndex].DrawRanges.Clear();
for (auto& uni : Sides[sideIndex].Uniforms)
FreeUniforms(uni.Start, uni.Count);
@ -696,8 +695,7 @@ void DoomLevelMesh::FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex)
FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount);
Flats[sectorIndex].Geometries.Clear();
for (auto& list : Flats[sectorIndex].DrawRanges)
list.Clear();
Flats[sectorIndex].DrawRanges.Clear();
for (auto& uni : Flats[sectorIndex].Uniforms)
FreeUniforms(uni.Start, uni.Count);
@ -1379,13 +1377,14 @@ void DoomLevelMesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, Mesh
Sides[sideIndex].Geometries.Push(ginfo);
Sides[sideIndex].Uniforms.Push(uinfo);
AddToDrawList(Sides[sideIndex].DrawRanges[(int)drawType], pipelineID, ginfo.IndexStart, ginfo.IndexCount);
AddToDrawList(Sides[sideIndex].DrawRanges, drawType, pipelineID, ginfo.IndexStart, ginfo.IndexCount);
}
}
void DoomLevelMesh::AddToDrawList(TArray<DrawRangeInfo>& drawRanges, int pipelineID, int indexStart, int indexCount)
void DoomLevelMesh::AddToDrawList(TArray<DrawRangeInfo>& drawRanges, LevelMeshDrawType drawType, int pipelineID, int indexStart, int indexCount)
{
DrawRangeInfo info;
info.DrawType = drawType;
info.IndexStart = indexStart;
info.IndexCount = indexCount;
info.PipelineID = pipelineID;
@ -1663,7 +1662,7 @@ void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state
SetSubsectorLightmap(sinfo.Index);
}
AddToDrawList(Flats[sectorIndex].DrawRanges[(int)drawType], pipelineID, ginfo.IndexStart, ginfo.IndexCount);
AddToDrawList(Flats[sectorIndex].DrawRanges, drawType, pipelineID, ginfo.IndexStart, ginfo.IndexCount);
}
}

View file

@ -52,6 +52,7 @@ struct GeometryFreeInfo
struct DrawRangeInfo
{
LevelMeshDrawType DrawType = {};
int PipelineID = 0;
int IndexStart = 0;
int IndexCount = 0;
@ -75,7 +76,7 @@ struct SideSurfaceBlock
TArray<HWDecalCreateInfo> Decals;
bool InSidePortalsList = false;
bool InSideDecalsList = false;
TArray<DrawRangeInfo> DrawRanges[(int)LevelMeshDrawType::NumDrawTypes];
TArray<DrawRangeInfo> DrawRanges;
SurfaceUpdateType UpdateType = SurfaceUpdateType::None;
LightListAllocInfo Lights;
};
@ -85,11 +86,45 @@ struct FlatSurfaceBlock
int FirstSurface = -1;
TArray<GeometryFreeInfo> Geometries;
TArray<UniformsAllocInfo> Uniforms;
TArray<DrawRangeInfo> DrawRanges[(int)LevelMeshDrawType::NumDrawTypes];
TArray<DrawRangeInfo> DrawRanges;
SurfaceUpdateType UpdateType = SurfaceUpdateType::None;
TArray<LightListAllocInfo> Lights;
};
enum class LevelMeshDrawType
{
Opaque,
Masked,
Portal,
Translucent,
NumDrawTypes
};
class LevelMeshDrawLists
{
public:
TArray<TArray<MeshBufferRange>> List[static_cast<int>(LevelMeshDrawType::NumDrawTypes)];
void Clear()
{
for (auto& l : List)
{
for (auto& p : l)
{
p.Clear();
}
}
}
void Add(LevelMeshDrawType drawType, int pipelineID, const MeshBufferRange& range)
{
int listIndex = static_cast<int>(drawType);
if (pipelineID >= (int)List[listIndex].Size())
List[listIndex].Resize(pipelineID + 1);
List[listIndex][pipelineID].Push(range);
}
};
class DoomLevelMesh : public LevelMesh, public UpdateLevelMesh
{
public:
@ -106,8 +141,9 @@ public:
void ProcessDecals(HWDrawInfo* drawinfo, FRenderState& state);
void DrawSectors(FRenderState& renderstate, const TArray<int>& sectors, LevelMeshDrawType drawType, bool noFragmentShader);
void DrawSides(FRenderState& renderstate, const TArray<int>& sides, LevelMeshDrawType drawType, bool noFragmentShader);
void AddSectorsToDrawLists(const TArray<int>& sectors, LevelMeshDrawLists& lists);
void AddSidesToDrawLists(const TArray<int>& sides, LevelMeshDrawLists& lists);
TArray<HWWall>& GetSidePortals(int sideIndex);
TArray<int> SideDecals;
@ -196,7 +232,7 @@ private:
void UpdateLight(FDynamicLight* light);
void CopyToMeshLight(FDynamicLight* light, LevelMeshLight& meshlight, int portalgroup);
void AddToDrawList(TArray<DrawRangeInfo>& drawRanges, int pipelineID, int indexStart, int indexCount);
void AddToDrawList(TArray<DrawRangeInfo>& drawRanges, LevelMeshDrawType drawType, int pipelineID, int indexStart, int indexCount);
void UploadDynLights(FLevelLocals& doomMap);

View file

@ -559,6 +559,11 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
}
}
SeenFlatsDrawLists.Clear();
SeenSidesDrawLists.Clear();
level.levelMesh->AddSectorsToDrawLists(SeenSectors.Get(), SeenFlatsDrawLists);
level.levelMesh->AddSidesToDrawLists(SeenSides.Get(), SeenSidesDrawLists);
// And now the crappy hacks that have to be done to avoid rendering anomalies.
// These cannot be multithreaded when the time comes because all these depend
// on the global 'validcount' variable.
@ -779,30 +784,42 @@ void HWDrawInfo::RenderScene(FRenderState &state)
state.EnableTexture(gl_texture);
state.EnableBrightmap(true);
// To do: replace this is classic light lists
// To do: replace this with classic light lists
state.ApplyLevelMesh();
DrawSeenSides(state, LevelMeshDrawType::Opaque, true);
DrawSeenFlats(state, LevelMeshDrawType::Opaque, true);
state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast<int>(LevelMeshDrawType::Opaque)], false, true);
state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast<int>(LevelMeshDrawType::Opaque)], false, true);
if (uselevelmesh)
state.DispatchLightTiles(VPUniforms.mViewMatrix, VPUniforms.mProjectionMatrix.get()[5]);
RenderWall.Clock();
state.ApplyLevelMesh();
DrawSeenSides(state, LevelMeshDrawType::Opaque, false);
state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast<int>(LevelMeshDrawType::Opaque)], false, false);
RenderWall.Unclock();
drawlists[GLDL_PLAINWALLS].DrawWalls(this, state, false);
RenderFlat.Clock();
state.ApplyLevelMesh();
DrawSeenFlats(state, LevelMeshDrawType::Opaque, false);
state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast<int>(LevelMeshDrawType::Opaque)], false, false);
RenderFlat.Unclock();
drawlists[GLDL_PLAINFLATS].DrawFlats(this, state, false);
// Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
RenderWall.Clock();
state.ApplyLevelMesh();
DrawSeenSides(state, LevelMeshDrawType::Masked, false);
state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast<int>(LevelMeshDrawType::Masked)], true, false);
RenderWall.Unclock();
drawlists[GLDL_MASKEDWALLS].DrawWalls(this, state, false);
RenderFlat.Clock();
state.ApplyLevelMesh();
DrawSeenFlats(state, LevelMeshDrawType::Masked, false);
state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast<int>(LevelMeshDrawType::Masked)], true, false);
RenderFlat.Unclock();
drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false);
// Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use.
@ -826,20 +843,6 @@ void HWDrawInfo::RenderScene(FRenderState &state)
RenderAll.Unclock();
}
void HWDrawInfo::DrawSeenSides(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader)
{
RenderWall.Clock();
level.levelMesh->DrawSides(state, SeenSides.Get(), drawType, noFragmentShader);
RenderWall.Unclock();
}
void HWDrawInfo::DrawSeenFlats(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader)
{
RenderFlat.Clock();
level.levelMesh->DrawSectors(state, SeenSectors.Get(), drawType, noFragmentShader);
RenderFlat.Unclock();
}
//-----------------------------------------------------------------------------
//
// RenderTranslucent
@ -859,9 +862,14 @@ void HWDrawInfo::RenderTranslucent(FRenderState &state)
state.SetDepthMask(false);
// To do: this needs to be sorted
RenderWall.Clock();
state.ApplyLevelMesh();
DrawSeenSides(state, LevelMeshDrawType::Translucent, false);
DrawSeenFlats(state, LevelMeshDrawType::Translucent, false);
state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast<int>(LevelMeshDrawType::Translucent)], false, false);
RenderWall.Unclock();
RenderFlat.Clock();
state.ApplyLevelMesh();
state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast<int>(LevelMeshDrawType::Translucent)], false, false);
RenderFlat.Unclock();
drawlists[GLDL_TRANSLUCENT].DrawSorted(this, state);
state.EnableBrightmap(false);

View file

@ -227,6 +227,7 @@ struct HWDrawInfo
};
VisList SeenSectors, SeenSides, SeenSubsectors, SeenHackedSubsectors;
LevelMeshDrawLists SeenFlatsDrawLists, SeenSidesDrawLists;
TArray<bool> QueryResultsBuffer;
@ -427,9 +428,6 @@ private:
void AddSpecialPortalLines(subsector_t* sub, sector_t* sector, linebase_t* line, FRenderState& state);
void UpdateLightmaps();
void DrawSeenSides(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader);
void DrawSeenFlats(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader);
};
void CleanSWDrawer();