Skip to content

Commit c306f84

Browse files
committed
Optimize sorting and rendering splats where only the rendering is now done twice and other steps are done only once.
1 parent 25fc55d commit c306f84

File tree

2 files changed

+75
-18
lines changed

2 files changed

+75
-18
lines changed

package/Runtime/GaussianSplatRenderer.cs

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ class GaussianSplatRenderSystem
3131

3232
CommandBuffer m_CommandBuffer;
3333

34+
// Keep track of the prepared splats for stereo rendering
35+
public class PreparedRenderData
36+
{
37+
public Material matComposite;
38+
public List<(GaussianSplatRenderer gs, Material displayMat, MaterialPropertyBlock mpb, int indexCount, int instanceCount, MeshTopology topology)> renderItems = new();
39+
}
40+
41+
private PreparedRenderData m_LastPreparedData;
42+
3443
public void RegisterSplat(GaussianSplatRenderer r)
3544
{
3645
if (m_Splats.Count == 0)
@@ -104,24 +113,32 @@ public bool GatherSplatsForCamera(Camera cam)
104113
return true;
105114
}
106115

116+
// New optimized method that prepares everything once for stereo rendering
117+
// This does the sorting and calculates view data, but doesn't actually render
107118
// ReSharper disable once MemberCanBePrivate.Global - used by HDRP/URP features that are not always compiled
108-
public Material SortAndRenderSplats(Camera cam, CommandBuffer cmb, int eyeIndex = -1)
119+
public PreparedRenderData PrepareSplats(Camera cam, CommandBuffer cmb)
109120
{
121+
if (m_LastPreparedData == null)
122+
m_LastPreparedData = new PreparedRenderData();
123+
else
124+
m_LastPreparedData.renderItems.Clear();
125+
110126
Material matComposite = null;
127+
111128
foreach (var kvp in m_ActiveSplats)
112129
{
113130
var gs = kvp.Item1;
114131
gs.EnsureMaterials();
115132
matComposite = gs.m_MatComposite;
116133
var mpb = kvp.Item2;
117134

118-
// sort
135+
// Sort the splats
119136
var matrix = gs.transform.localToWorldMatrix;
120137
if (gs.m_FrameCounter % gs.m_SortNthFrame == 0)
121138
gs.SortPoints(cmb, cam, matrix);
122139
++gs.m_FrameCounter;
123140

124-
// cache view
141+
// Prepare material and view data
125142
kvp.Item2.Clear();
126143
Material displayMat = gs.m_RenderMode switch
127144
{
@@ -134,11 +151,10 @@ public Material SortAndRenderSplats(Camera cam, CommandBuffer cmb, int eyeIndex
134151
if (displayMat == null)
135152
continue;
136153

137-
gs.SetAssetDataOnMaterial(mpb, eyeIndex);
154+
// Set up everything except eye-specific parameters
155+
gs.SetAssetDataOnMaterial(mpb, -1); // -1 for initial setup without eye index
138156
mpb.SetBuffer(GaussianSplatRenderer.Props.SplatChunks, gs.m_GpuChunks);
139-
140157
mpb.SetBuffer(GaussianSplatRenderer.Props.SplatViewData, gs.m_GpuView);
141-
142158
mpb.SetBuffer(GaussianSplatRenderer.Props.OrderBuffer, gs.m_GpuSortKeys);
143159
mpb.SetFloat(GaussianSplatRenderer.Props.SplatScale, gs.m_SplatScale);
144160
mpb.SetFloat(GaussianSplatRenderer.Props.SplatOpacityScale, gs.m_OpacityScale);
@@ -148,11 +164,12 @@ public Material SortAndRenderSplats(Camera cam, CommandBuffer cmb, int eyeIndex
148164
mpb.SetInteger(GaussianSplatRenderer.Props.DisplayIndex, gs.m_RenderMode == GaussianSplatRenderer.RenderMode.DebugPointIndices ? 1 : 0);
149165
mpb.SetInteger(GaussianSplatRenderer.Props.DisplayChunks, gs.m_RenderMode == GaussianSplatRenderer.RenderMode.DebugChunkBounds ? 1 : 0);
150166

167+
// Calculate view data once for stereo (will calculate for both eyes)
151168
cmb.BeginSample(s_ProfCalcView);
152169
gs.CalcViewData(cmb, cam);
153170
cmb.EndSample(s_ProfCalcView);
154171

155-
// draw
172+
// Set up draw parameters
156173
int indexCount = 6;
157174
int instanceCount = gs.splatCount;
158175
MeshTopology topology = MeshTopology.Triangles;
@@ -161,11 +178,45 @@ public Material SortAndRenderSplats(Camera cam, CommandBuffer cmb, int eyeIndex
161178
if (gs.m_RenderMode == GaussianSplatRenderer.RenderMode.DebugChunkBounds)
162179
instanceCount = gs.m_GpuChunksValid ? gs.m_GpuChunks.count : 0;
163180

181+
// Store the prepared data for rendering later
182+
m_LastPreparedData.renderItems.Add((gs, displayMat, mpb, indexCount, instanceCount, topology));
183+
}
184+
185+
m_LastPreparedData.matComposite = matComposite;
186+
return m_LastPreparedData;
187+
}
188+
189+
// New optimized method that just draws the prepared splats for a specific eye
190+
// ReSharper disable once MemberCanBePrivate.Global - used by HDRP/URP features that are not always compiled
191+
public void RenderPreparedSplats(CommandBuffer cmb, int eyeIndex)
192+
{
193+
if (m_LastPreparedData == null || m_LastPreparedData.renderItems.Count == 0)
194+
return;
195+
196+
foreach (var (gs, displayMat, mpb, indexCount, instanceCount, topology) in m_LastPreparedData.renderItems)
197+
{
198+
// Set the eye index for this specific render
199+
mpb.SetInteger(GaussianSplatRenderer.Props.EyeIndex, eyeIndex);
200+
mpb.SetInteger(GaussianSplatRenderer.Props.IsStereo, 1);
201+
202+
// Draw
164203
cmb.BeginSample(s_ProfDraw);
165-
cmb.DrawProcedural(gs.m_GpuIndexBuffer, matrix, displayMat, 0, topology, indexCount, instanceCount, mpb);
204+
cmb.DrawProcedural(gs.m_GpuIndexBuffer, gs.transform.localToWorldMatrix, displayMat, 0, topology, indexCount, instanceCount, mpb);
166205
cmb.EndSample(s_ProfDraw);
167206
}
168-
return matComposite;
207+
}
208+
209+
// ReSharper disable once MemberCanBePrivate.Global - used by HDRP/URP features that are not always compiled
210+
public Material SortAndRenderSplats(Camera cam, CommandBuffer cmb)
211+
{
212+
// Prepare the splats (sort and calculate view data)
213+
var renderData = PrepareSplats(cam, cmb);
214+
215+
// Render the prepared splats
216+
RenderPreparedSplats(cmb, -1);
217+
218+
// Return the composite material
219+
return renderData.matComposite;
169220
}
170221

171222
// ReSharper disable once MemberCanBePrivate.Global - used by HDRP/URP features that are not always compiled
@@ -200,7 +251,7 @@ void OnPreCullCamera(Camera cam)
200251
m_CommandBuffer.SetGlobalTexture(GaussianSplatRenderer.Props.CameraTargetTexture, BuiltinRenderTextureType.CameraTarget);
201252

202253
// add sorting, view calc and drawing commands for each splat object
203-
Material matComposite = SortAndRenderSplats(cam, m_CommandBuffer, -1);
254+
Material matComposite = SortAndRenderSplats(cam, m_CommandBuffer);
204255

205256
// compose
206257
m_CommandBuffer.BeginSample(s_ProfCompose);

package/Runtime/GaussianSplatURPFeature.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,26 +80,32 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer
8080

8181
if (data.IsStereo)
8282
{
83-
// Left eye rendering
83+
// Clear the render target for both eyes
84+
CoreUtils.SetRenderTarget(commandBuffer, data.GaussianSplatRT, ClearFlag.Color, Color.clear);
85+
86+
// Prepare the splats once - sort them and calculate view data
87+
var renderData = GaussianSplatRenderSystem.instance.PrepareSplats(data.CameraData.camera, commandBuffer);
88+
89+
// Render to left eye
8490
CoreUtils.SetRenderTarget(commandBuffer, data.GaussianSplatRT, ClearFlag.Color, Color.clear, 0, CubemapFace.Unknown, 0);
85-
Material matComposite = GaussianSplatRenderSystem.instance.SortAndRenderSplats(data.CameraData.camera, commandBuffer, 0);
91+
GaussianSplatRenderSystem.instance.RenderPreparedSplats(commandBuffer, 0);
8692

87-
// Right eye rendering
93+
// Render to right eye
8894
CoreUtils.SetRenderTarget(commandBuffer, data.GaussianSplatRT, ClearFlag.Color, Color.clear, 0, CubemapFace.Unknown, 1);
89-
GaussianSplatRenderSystem.instance.SortAndRenderSplats(data.CameraData.camera, commandBuffer, 1);
95+
GaussianSplatRenderSystem.instance.RenderPreparedSplats(commandBuffer, 1);
9096

91-
matComposite.SetTexture(s_gaussianSplatRT, data.GaussianSplatRT);
92-
9397
// Composite to the final target
9498
commandBuffer.BeginSample(GaussianSplatRenderSystem.s_ProfCompose);
9599
commandBuffer.SetGlobalTexture(s_gaussianSplatRT, data.GaussianSplatRT);
100+
renderData.matComposite.SetTexture(s_gaussianSplatRT, data.GaussianSplatRT);
101+
96102
commandBuffer.SetRenderTarget(data.SourceTexture, 0, CubemapFace.Unknown, 0);
97103
commandBuffer.SetGlobalInt("_CustomStereoEyeIndex", 0); // emulate left
98-
commandBuffer.DrawProcedural(Matrix4x4.identity, matComposite, 0, MeshTopology.Triangles, 3, 1);
104+
commandBuffer.DrawProcedural(Matrix4x4.identity, renderData.matComposite, 0, MeshTopology.Triangles, 3, 1);
99105

100106
commandBuffer.SetRenderTarget(data.SourceTexture, 0, CubemapFace.Unknown, 1);
101107
commandBuffer.SetGlobalInt("_CustomStereoEyeIndex", 1); // emulate right
102-
commandBuffer.DrawProcedural(Matrix4x4.identity, matComposite, 0, MeshTopology.Triangles, 3, 1);
108+
commandBuffer.DrawProcedural(Matrix4x4.identity, renderData.matComposite, 0, MeshTopology.Triangles, 3, 1);
103109
commandBuffer.EndSample(GaussianSplatRenderSystem.s_ProfCompose);
104110
}
105111
else

0 commit comments

Comments
 (0)