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