1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/imaging/hdSt/renderPass.h"
25 
26 #include "pxr/imaging/glf/contextCaps.h"
27 
28 #include "pxr/imaging/hdSt/debugCodes.h"
29 #include "pxr/imaging/hdSt/drawItemsCache.h"
30 #include "pxr/imaging/hdSt/glUtils.h"
31 #include "pxr/imaging/hdSt/indirectDrawBatch.h"
32 #include "pxr/imaging/hdSt/resourceRegistry.h"
33 #include "pxr/imaging/hdSt/renderParam.h"
34 #include "pxr/imaging/hdSt/renderPassShader.h"
35 #include "pxr/imaging/hdSt/renderPassState.h"
36 
37 #include "pxr/imaging/hdSt/drawItem.h"
38 #include "pxr/imaging/hdSt/renderDelegate.h"
39 #include "pxr/imaging/hdSt/shaderCode.h"
40 #include "pxr/imaging/hdSt/tokens.h"
41 
42 #include "pxr/imaging/hgi/graphicsCmds.h"
43 #include "pxr/imaging/hgi/graphicsCmdsDesc.h"
44 #include "pxr/imaging/hgi/hgi.h"
45 #include "pxr/imaging/hgi/tokens.h"
46 
47 #include "pxr/imaging/hd/renderDelegate.h"
48 #include "pxr/imaging/hd/vtBufferSource.h"
49 
50 #include "pxr/base/tf/envSetting.h"
51 #include "pxr/base/gf/frustum.h"
52 
53 
54 // XXX We do not want to include specific HgiXX backends, but we need to do
55 // this temporarily until Storm has transitioned fully to Hgi.
56 #include "pxr/imaging/hgiGL/graphicsCmds.h"
57 
58 PXR_NAMESPACE_OPEN_SCOPE
59 
60 TF_DEFINE_ENV_SETTING(HDST_ENABLE_DRAW_ITEMS_CACHE, false,
61                 "Enable usage of the draw items cache in Storm.");
62 
63 static bool
_IsDrawItemsCacheEnabled()64 _IsDrawItemsCacheEnabled()
65 {
66     static const bool enabled = TfGetEnvSetting(HDST_ENABLE_DRAW_ITEMS_CACHE);
67     return enabled;
68 }
69 
70 void
_ExecuteDraw(HdStCommandBuffer * cmdBuffer,HdStRenderPassStateSharedPtr const & stRenderPassState,HdStResourceRegistrySharedPtr const & resourceRegistry)71 _ExecuteDraw(
72     HdStCommandBuffer* cmdBuffer,
73     HdStRenderPassStateSharedPtr const& stRenderPassState,
74     HdStResourceRegistrySharedPtr const& resourceRegistry)
75 {
76     cmdBuffer->ExecuteDraw(stRenderPassState, resourceRegistry);
77 }
78 
79 unsigned int
_GetDrawBatchesVersion(HdRenderIndex * renderIndex)80 _GetDrawBatchesVersion(HdRenderIndex *renderIndex)
81 {
82     HdStRenderParam *stRenderParam = static_cast<HdStRenderParam*>(
83         renderIndex->GetRenderDelegate()->GetRenderParam());
84 
85     return stRenderParam->GetDrawBatchesVersion();
86 }
87 
88 unsigned int
_GetMaterialTagsVersion(HdRenderIndex * renderIndex)89 _GetMaterialTagsVersion(HdRenderIndex *renderIndex)
90 {
91     HdStRenderParam *stRenderParam = static_cast<HdStRenderParam*>(
92         renderIndex->GetRenderDelegate()->GetRenderParam());
93 
94     return stRenderParam->GetMaterialTagsVersion();
95 }
96 
97 unsigned int
_GetGeomSubsetDrawItemsVersion(HdRenderIndex * renderIndex)98 _GetGeomSubsetDrawItemsVersion(HdRenderIndex *renderIndex)
99 {
100     HdStRenderParam *stRenderParam = static_cast<HdStRenderParam*>(
101         renderIndex->GetRenderDelegate()->GetRenderParam());
102 
103     return stRenderParam->GetGeomSubsetDrawItemsVersion();
104 }
105 
HdSt_RenderPass(HdRenderIndex * index,HdRprimCollection const & collection)106 HdSt_RenderPass::HdSt_RenderPass(HdRenderIndex *index,
107                                  HdRprimCollection const &collection)
108     : HdRenderPass(index, collection)
109     , _lastSettingsVersion(0)
110     , _useTinyPrimCulling(false)
111     , _collectionVersion(0)
112     , _rprimRenderTagVersion(0)
113     , _taskRenderTagsVersion(0)
114     , _materialTagsVersion(0)
115     , _geomSubsetDrawItemsVersion(0)
116     , _collectionChanged(false)
117     , _drawItemCount(0)
118     , _drawItemsChanged(false)
119     , _hgi(nullptr)
120 {
121     HdStRenderDelegate* renderDelegate =
122         static_cast<HdStRenderDelegate*>(index->GetRenderDelegate());
123     _hgi = renderDelegate->GetHgi();
124 }
125 
~HdSt_RenderPass()126 HdSt_RenderPass::~HdSt_RenderPass()
127 {
128 }
129 
130 bool
HasDrawItems() const131 HdSt_RenderPass::HasDrawItems() const
132 {
133     // Note that using the material tag alone isn't a sufficient filter.
134     // The collection paths and task render tags also matter. Factoring them
135     // requires querying the render index, which is an expensive operation that
136     // we avoid here.
137     const HdStRenderParam * const renderParam =
138         static_cast<HdStRenderParam *>(
139             GetRenderIndex()->GetRenderDelegate()->GetRenderParam());
140 
141     return renderParam->HasMaterialTag(GetRprimCollection().GetMaterialTag());
142 }
143 
144 static
145 const GfVec3i &
_GetFramebufferSize(const HgiGraphicsCmdsDesc & desc)146 _GetFramebufferSize(const HgiGraphicsCmdsDesc &desc)
147 {
148     for (const HgiTextureHandle &color : desc.colorTextures) {
149         return color->GetDescriptor().dimensions;
150     }
151     if (desc.depthTexture) {
152         return desc.depthTexture->GetDescriptor().dimensions;
153     }
154 
155     static const GfVec3i fallback(0);
156     return fallback;
157 }
158 
159 static
160 GfVec4i
_FlipViewport(const GfVec4i & viewport,const GfVec3i & framebufferSize)161 _FlipViewport(const GfVec4i &viewport,
162               const GfVec3i &framebufferSize)
163 {
164     const int height = framebufferSize[1];
165     if (height > 0) {
166         return GfVec4i(viewport[0],
167                        height - (viewport[1] + viewport[3]),
168                        viewport[2],
169                        viewport[3]);
170     } else {
171         return viewport;
172     }
173 }
174 
175 static
176 GfVec4i
_ToVec4i(const GfVec4f & v)177 _ToVec4i(const GfVec4f &v)
178 {
179     return GfVec4i(int(v[0]), int(v[1]), int(v[2]), int(v[3]));
180 }
181 
182 static
183 GfVec4i
_ToVec4i(const GfRect2i & r)184 _ToVec4i(const GfRect2i &r)
185 {
186     return GfVec4i(r.GetMinX(),  r.GetMinY(),
187                    r.GetWidth(), r.GetHeight());
188 }
189 
190 static
191 GfVec4i
_ComputeViewport(HdRenderPassStateSharedPtr const & renderPassState,const HgiGraphicsCmdsDesc & desc,const bool flip)192 _ComputeViewport(HdRenderPassStateSharedPtr const &renderPassState,
193                  const HgiGraphicsCmdsDesc &desc,
194                  const bool flip)
195 {
196     const CameraUtilFraming &framing = renderPassState->GetFraming();
197     if (framing.IsValid()) {
198         // Use data window for clients using the new camera framing
199         // API.
200         const GfVec4i viewport = _ToVec4i(framing.dataWindow);
201         if (flip) {
202             // Note that in OpenGL, the coordinates for the viewport
203             // are y-Up but the camera framing is y-Down.
204             return _FlipViewport(viewport, _GetFramebufferSize(desc));
205         } else {
206             return viewport;
207         }
208     }
209 
210     // For clients not using the new camera framing API, fallback
211     // to the viewport they specified.
212     return _ToVec4i(renderPassState->GetViewport());
213 }
214 
215 void
_Execute(HdRenderPassStateSharedPtr const & renderPassState,TfTokenVector const & renderTags)216 HdSt_RenderPass::_Execute(HdRenderPassStateSharedPtr const &renderPassState,
217                           TfTokenVector const& renderTags)
218 {
219     HD_TRACE_FUNCTION();
220     HF_MALLOC_TAG_FUNCTION();
221 
222     // Downcast render pass state
223     HdStRenderPassStateSharedPtr stRenderPassState =
224         std::dynamic_pointer_cast<HdStRenderPassState>(
225         renderPassState);
226     TF_VERIFY(stRenderPassState);
227 
228     // Validate and update draw batches.
229     _UpdateCommandBuffer(renderTags);
230 
231     // CPU frustum culling (if chosen)
232     _FrustumCullCPU(stRenderPassState);
233 
234     // Downcast the resource registry
235     HdStResourceRegistrySharedPtr const& resourceRegistry =
236         std::dynamic_pointer_cast<HdStResourceRegistry>(
237         GetRenderIndex()->GetResourceRegistry());
238     TF_VERIFY(resourceRegistry);
239 
240     _cmdBuffer.PrepareDraw(stRenderPassState, resourceRegistry);
241 
242     // Create graphics work to render into aovs.
243     const HgiGraphicsCmdsDesc desc =
244         stRenderPassState->MakeGraphicsCmdsDesc(GetRenderIndex());
245     HgiGraphicsCmdsUniquePtr gfxCmds = _hgi->CreateGraphicsCmds(desc);
246     if (!TF_VERIFY(gfxCmds)) {
247         return;
248     }
249     HdRprimCollection const &collection = GetRprimCollection();
250     std::string passName = "HdSt_RenderPass: " +
251         collection.GetMaterialTag().GetString();
252     gfxCmds->PushDebugGroup(passName.c_str());
253 
254     gfxCmds->SetViewport(
255         _ComputeViewport(
256             renderPassState,
257             desc,
258             /* flip = */ _hgi->GetAPIName() == HgiTokens->OpenGL));
259 
260     HdStCommandBuffer* cmdBuffer = &_cmdBuffer;
261     HgiGLGraphicsCmds* glGfxCmds =
262         dynamic_cast<HgiGLGraphicsCmds*>(gfxCmds.get());
263 
264     // XXX: The Bind/Unbind calls below set/restore GL state.
265     // This will be reworked to use Hgi.
266     stRenderPassState->Bind();
267 
268     if (glGfxCmds) {
269         // XXX Tmp code path to allow non-hgi code to insert functions into
270         // HgiGL ops-stack. Will be removed once Storms uses Hgi everywhere
271         auto executeDrawOp = [cmdBuffer, stRenderPassState, resourceRegistry] {
272             _ExecuteDraw(cmdBuffer, stRenderPassState, resourceRegistry);
273         };
274         glGfxCmds->InsertFunctionOp(executeDrawOp);
275     } else {
276         _ExecuteDraw(cmdBuffer, stRenderPassState, resourceRegistry);
277     }
278 
279     if (gfxCmds) {
280         gfxCmds->PopDebugGroup();
281         _hgi->SubmitCmds(gfxCmds.get());
282     }
283 
284     stRenderPassState->Unbind();
285 }
286 
287 void
_MarkCollectionDirty()288 HdSt_RenderPass::_MarkCollectionDirty()
289 {
290     // Force any cached data based on collection to be refreshed.
291     _collectionChanged = true;
292     _collectionVersion = 0;
293 }
294 
295 static
296 HdStDrawItemsCachePtr
_GetDrawItemsCache(HdRenderIndex * renderIndex)297 _GetDrawItemsCache(HdRenderIndex *renderIndex)
298 {
299     HdStRenderDelegate* renderDelegate =
300         static_cast<HdStRenderDelegate*>(renderIndex->GetRenderDelegate());
301     return renderDelegate->GetDrawItemsCache();
302 }
303 
304 void
_UpdateDrawItems(TfTokenVector const & renderTags)305 HdSt_RenderPass::_UpdateDrawItems(TfTokenVector const& renderTags)
306 {
307     HD_TRACE_FUNCTION();
308 
309     HdRprimCollection const &collection = GetRprimCollection();
310     if (_IsDrawItemsCacheEnabled()) {
311         HdStDrawItemsCachePtr cache = _GetDrawItemsCache(GetRenderIndex());
312 
313         HdDrawItemConstPtrVectorSharedPtr cachedEntry =
314             cache->GetDrawItems(
315                 collection, renderTags, GetRenderIndex(), _drawItems);
316 
317         if (_drawItems != cachedEntry) {
318             _drawItems = cachedEntry;
319             _drawItemsChanged = true;
320             _drawItemCount = _drawItems->size();
321         }
322         // We don't rely on this state when using the cache. Reset always.
323         _collectionChanged = false;
324 
325         return;
326     }
327 
328     HdChangeTracker const &tracker = GetRenderIndex()->GetChangeTracker();
329 
330     const int collectionVersion =
331         tracker.GetCollectionVersion(collection.GetName());
332 
333     const int rprimRenderTagVersion = tracker.GetRenderTagVersion();
334 
335     const unsigned int materialTagsVersion =
336         _GetMaterialTagsVersion(GetRenderIndex());
337 
338     const unsigned int geomSubsetDrawItemsVersion =
339         _GetGeomSubsetDrawItemsVersion(GetRenderIndex());
340 
341     const bool collectionChanged = _collectionChanged ||
342         (_collectionVersion != collectionVersion);
343 
344     const bool rprimRenderTagChanged = _rprimRenderTagVersion != rprimRenderTagVersion;
345 
346     const bool materialTagsChanged =
347         _materialTagsVersion != materialTagsVersion;
348 
349     const bool geomSubsetDrawItemsChanged =
350         _geomSubsetDrawItemsVersion != geomSubsetDrawItemsVersion;
351 
352     const int taskRenderTagsVersion = tracker.GetTaskRenderTagsVersion();
353     bool taskRenderTagsChanged = false;
354     if (_taskRenderTagsVersion != taskRenderTagsVersion) {
355         _taskRenderTagsVersion = taskRenderTagsVersion;
356         if (_renderTags != renderTags) {
357             _renderTags = renderTags;
358             taskRenderTagsChanged = true;
359         }
360     }
361 
362     if (collectionChanged ||
363         rprimRenderTagChanged ||
364         materialTagsChanged ||
365         geomSubsetDrawItemsChanged ||
366         taskRenderTagsChanged) {
367 
368         if (TfDebug::IsEnabled(HDST_DRAW_ITEM_GATHER)) {
369             if (collectionChanged) {
370                 TfDebug::Helper::Msg(
371                     "CollectionChanged: %s (repr = %s, version = %d -> %d)\n",
372                         collection.GetName().GetText(),
373                         collection.GetReprSelector().GetText(),
374                         _collectionVersion,
375                         collectionVersion);
376             }
377 
378             if (rprimRenderTagChanged) {
379                 TfDebug::Helper::Msg("RprimRenderTagChanged (version = %d -> %d)\n",
380                         _rprimRenderTagVersion, rprimRenderTagVersion);
381             }
382             if (materialTagsChanged) {
383                 TfDebug::Helper::Msg(
384                     "MaterialTagsChanged (version = %d -> %d)\n",
385                     _materialTagsVersion, materialTagsVersion);
386             }
387             if (geomSubsetDrawItemsChanged) {
388                 TfDebug::Helper::Msg(
389                     "GeomSubsetDrawItemsChanged (version = %d -> %d)\n",
390                     _geomSubsetDrawItemsVersion, geomSubsetDrawItemsVersion);
391             }
392             if (taskRenderTagsChanged) {
393                 TfDebug::Helper::Msg( "TaskRenderTagsChanged\n" );
394             }
395         }
396 
397         const HdStRenderParam * const renderParam =
398             static_cast<HdStRenderParam *>(
399                 GetRenderIndex()->GetRenderDelegate()->GetRenderParam());
400         if (renderParam->HasMaterialTag(collection.GetMaterialTag())) {
401             _drawItems = std::make_shared<HdDrawItemConstPtrVector>(
402                 GetRenderIndex()->GetDrawItems(collection, renderTags));
403             HD_PERF_COUNTER_INCR(HdStPerfTokens->drawItemsFetched);
404         } else {
405             // No need to even call GetDrawItems when we know that
406             // there is no prim with the desired material tag.
407             _drawItems = std::make_shared<HdDrawItemConstPtrVector>();
408         }
409         _drawItemCount = _drawItems->size();
410         _drawItemsChanged = true;
411 
412         _collectionVersion = collectionVersion;
413         _collectionChanged = false;
414 
415         _rprimRenderTagVersion = rprimRenderTagVersion;
416         _materialTagsVersion = materialTagsVersion;
417         _geomSubsetDrawItemsVersion = geomSubsetDrawItemsVersion;
418     }
419 }
420 
421 void
_UpdateCommandBuffer(TfTokenVector const & renderTags)422 HdSt_RenderPass::_UpdateCommandBuffer(TfTokenVector const& renderTags)
423 {
424     HD_TRACE_FUNCTION();
425 
426     // -------------------------------------------------------------------
427     // SCHEDULE PREPARATION
428     // -------------------------------------------------------------------
429     // We know what must be drawn and that the stream needs to be updated,
430     // so iterate over each prim, cull it and schedule it to be drawn.
431 
432     // Ensure that the drawItems are always up-to-date before building the
433     // command buffers.
434     _UpdateDrawItems(renderTags);
435 
436     const int batchVersion = _GetDrawBatchesVersion(GetRenderIndex());
437     // Rebuild draw batches based on new draw items
438     if (_drawItemsChanged) {
439         _cmdBuffer.SetDrawItems(_drawItems, batchVersion);
440 
441         _drawItemsChanged = false;
442         size_t itemCount = _cmdBuffer.GetTotalSize();
443         HD_PERF_COUNTER_SET(HdTokens->totalItemCount, itemCount);
444     } else {
445         // validate command buffer to not include expired drawItems,
446         // which could be produced by migrating BARs at the new repr creation.
447         _cmdBuffer.RebuildDrawBatchesIfNeeded(batchVersion);
448     }
449 
450     // -------------------------------------------------------------------
451     // RENDER SETTINGS
452     // -------------------------------------------------------------------
453     HdRenderDelegate *renderDelegate = GetRenderIndex()->GetRenderDelegate();
454     int currentSettingsVersion = renderDelegate->GetRenderSettingsVersion();
455     if (_lastSettingsVersion != currentSettingsVersion) {
456         _lastSettingsVersion = currentSettingsVersion;
457         _useTinyPrimCulling = renderDelegate->GetRenderSetting<bool>(
458             HdStRenderSettingsTokens->enableTinyPrimCulling, false);
459     }
460 
461     _cmdBuffer.SetEnableTinyPrimCulling(_useTinyPrimCulling);
462 }
463 
464 void
_FrustumCullCPU(HdStRenderPassStateSharedPtr const & renderPassState)465 HdSt_RenderPass::_FrustumCullCPU(
466     HdStRenderPassStateSharedPtr const &renderPassState)
467 {
468     // This process should be moved to HdSt_DrawBatch::PrepareDraw
469     // to be consistent with GPU culling.
470 
471     GlfContextCaps const &caps = GlfContextCaps::GetInstance();
472     HdChangeTracker const &tracker = GetRenderIndex()->GetChangeTracker();
473 
474     const bool
475        skipCulling = TfDebug::IsEnabled(HDST_DISABLE_FRUSTUM_CULLING) ||
476            (caps.multiDrawIndirectEnabled
477                && HdSt_IndirectDrawBatch::IsEnabledGPUFrustumCulling());
478     bool freezeCulling = TfDebug::IsEnabled(HD_FREEZE_CULL_FRUSTUM);
479 
480     if(skipCulling) {
481         // Since culling state is stored across renders,
482         // we need to update all items visible state
483         _cmdBuffer.SyncDrawItemVisibility(tracker.GetVisibilityChangeCount());
484 
485         TF_DEBUG(HD_DRAWITEMS_CULLED).Msg("CULLED: skipped\n");
486     }
487     else {
488         if (!freezeCulling) {
489             // Re-cull the command buffer.
490             _cmdBuffer.FrustumCull(renderPassState->GetCullMatrix());
491         }
492 
493         if (TfDebug::IsEnabled(HD_DRAWITEMS_CULLED)) {
494             TF_DEBUG(HD_DRAWITEMS_CULLED).Msg("CULLED: %zu drawItems\n",
495                                               _cmdBuffer.GetCulledSize());
496         }
497     }
498 }
499 
500 PXR_NAMESPACE_CLOSE_SCOPE
501