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/garch/glApi.h"
25 
26 #include "pxr/imaging/glf/diagnostic.h"
27 
28 #include "pxr/imaging/hdSt/immediateDrawBatch.h"
29 
30 #include "pxr/imaging/hdSt/bufferArrayRange.h"
31 #include "pxr/imaging/hdSt/commandBuffer.h"
32 #include "pxr/imaging/hdSt/drawItem.h"
33 #include "pxr/imaging/hdSt/drawItemInstance.h"
34 #include "pxr/imaging/hdSt/geometricShader.h"
35 #include "pxr/imaging/hdSt/glslProgram.h"
36 #include "pxr/imaging/hdSt/renderPassState.h"
37 #include "pxr/imaging/hdSt/resourceRegistry.h"
38 #include "pxr/imaging/hdSt/shaderCode.h"
39 
40 #include "pxr/imaging/hd/debugCodes.h"
41 #include "pxr/imaging/hd/mesh.h"
42 #include "pxr/imaging/hd/perfLog.h"
43 #include "pxr/imaging/hd/tokens.h"
44 
45 #include "pxr/base/tf/diagnostic.h"
46 
47 PXR_NAMESPACE_OPEN_SCOPE
48 
49 
HdSt_ImmediateDrawBatch(HdStDrawItemInstance * drawItemInstance)50 HdSt_ImmediateDrawBatch::HdSt_ImmediateDrawBatch(
51     HdStDrawItemInstance * drawItemInstance)
52     : HdSt_DrawBatch(drawItemInstance)
53 {
54     _Init(drawItemInstance);
55 }
56 
57 void
_Init(HdStDrawItemInstance * drawItemInstance)58 HdSt_ImmediateDrawBatch::_Init(HdStDrawItemInstance * drawItemInstance)
59 {
60     HdSt_DrawBatch::_Init(drawItemInstance);
61     drawItemInstance->SetBatchIndex(0);
62     drawItemInstance->SetBatch(this);
63     _bufferArraysHash =
64         drawItemInstance->GetDrawItem()->GetBufferArraysHash();
65 }
66 
~HdSt_ImmediateDrawBatch()67 HdSt_ImmediateDrawBatch::~HdSt_ImmediateDrawBatch()
68 {
69 }
70 
71 HdSt_DrawBatch::ValidationResult
Validate(bool deepValidation)72 HdSt_ImmediateDrawBatch::Validate(bool deepValidation)
73 {
74     if (!TF_VERIFY(!_drawItemInstances.empty())) {
75         return ValidationResult::RebuildAllBatches;
76     }
77 
78     HdStDrawItem const* batchItem = _drawItemInstances.front()->GetDrawItem();
79 
80     // check the hash to see if anything's been reallocated/migrated.
81     // note that we just need to compare the hash of the first item,
82     // since draw items are aggregated and ensure that they are sharing
83     // the same buffer arrays.
84     size_t bufferArraysHash = batchItem->GetBufferArraysHash();
85     if (_bufferArraysHash != bufferArraysHash) {
86         _bufferArraysHash = bufferArraysHash;
87         return ValidationResult::RebuildBatch;
88     }
89 
90     // Deep validation is flagged explicitly when a drawItem has changes to
91     // its BARs (e.g. buffer spec, aggregation) or when its
92     // surface shader or geometric shader changes.
93     if (deepValidation) {
94         HD_TRACE_SCOPE("Immediate draw batch deep validation");
95 
96         // look through all draw items to be still compatible
97 
98         size_t numDrawItemInstances = _drawItemInstances.size();
99         for (size_t item = 0; item < numDrawItemInstances; ++item) {
100             HdStDrawItem const * drawItem
101                 = _drawItemInstances[item]->GetDrawItem();
102 
103             if (!TF_VERIFY(drawItem->GetGeometricShader())) {
104                 return ValidationResult::RebuildAllBatches;
105             }
106 
107             if (!_IsAggregated(batchItem, drawItem)) {
108                 return ValidationResult::RebuildAllBatches;
109             }
110         }
111     }
112 
113     return ValidationResult::ValidBatch;
114 }
115 
116 void
PrepareDraw(HdStRenderPassStateSharedPtr const & renderPassState,HdStResourceRegistrySharedPtr const & resourceRegistry)117 HdSt_ImmediateDrawBatch::PrepareDraw(
118     HdStRenderPassStateSharedPtr const &renderPassState,
119     HdStResourceRegistrySharedPtr const &resourceRegistry)
120 {
121 }
122 
123 static int
_GetElementOffset(HdBufferArrayRangeSharedPtr const & range)124 _GetElementOffset(HdBufferArrayRangeSharedPtr const& range)
125 {
126     return range? range->GetElementOffset() : 0;
127 }
128 
129 void
ExecuteDraw(HdStRenderPassStateSharedPtr const & renderPassState,HdStResourceRegistrySharedPtr const & resourceRegistry)130 HdSt_ImmediateDrawBatch::ExecuteDraw(
131     HdStRenderPassStateSharedPtr const &renderPassState,
132     HdStResourceRegistrySharedPtr const &resourceRegistry)
133 {
134     HD_TRACE_FUNCTION();
135     GLF_GROUP_FUNCTION();
136 
137     HdStBufferArrayRangeSharedPtr indexBarCurrent;
138     HdStBufferArrayRangeSharedPtr topVisBarCurrent;
139     HdStBufferArrayRangeSharedPtr elementBarCurrent;
140     HdStBufferArrayRangeSharedPtr vertexBarCurrent;
141     HdStBufferArrayRangeSharedPtr varyingBarCurrent;
142     HdStBufferArrayRangeSharedPtr constantBarCurrent;
143     HdStBufferArrayRangeSharedPtr fvarBarCurrent;
144     HdStBufferArrayRangeSharedPtr instanceIndexBarCurrent;
145     HdStBufferArrayRangeSharedPtr shaderBarCurrent;
146     std::vector<HdStBufferArrayRangeSharedPtr> instanceBarCurrents;
147 
148     if (_drawItemInstances.empty()) return;
149 
150     if (!glUseProgram) return;  // glew initialized
151 
152     // bind program
153     _DrawingProgram & program = _GetDrawingProgram(renderPassState,
154                                                    /*indirect=*/false,
155                                                    resourceRegistry);
156 
157     HdStGLSLProgramSharedPtr const &glslProgram = program.GetGLSLProgram();
158     if (!TF_VERIFY(glslProgram)) return;
159     if (!TF_VERIFY(glslProgram->Validate())) return;
160 
161     const HdSt_ResourceBinder &binder = program.GetBinder();
162     const HdStShaderCodeSharedPtrVector &shaders = program.GetComposedShaders();
163 
164     GLuint programId = glslProgram->GetProgram()->GetRawResource();
165     TF_VERIFY(programId);
166 
167     glUseProgram(programId);
168 
169     for (HdStShaderCodeSharedPtr const & shader : shaders) {
170         shader->BindResources(programId, binder, *renderPassState);
171     }
172 
173     // Set up geometric shader states
174     // all batch item should have the same geometric shader.
175     HdSt_GeometricShaderSharedPtr const &geometricShader
176         = program.GetGeometricShader();
177     geometricShader->BindResources(programId, binder, *renderPassState);
178 
179     size_t numItemsDrawn = 0;
180     for (HdStDrawItemInstance const * drawItemInstance : _drawItemInstances) {
181         if(!drawItemInstance->IsVisible()) {
182             continue;
183         }
184 
185         HdStDrawItem const * drawItem = drawItemInstance->GetDrawItem();
186 
187         ++numItemsDrawn;
188 
189         //
190         // index buffer data
191         //
192         HdBufferArrayRangeSharedPtr const & indexBar_ =
193             drawItem->GetTopologyRange();
194 
195         HdStBufferArrayRangeSharedPtr indexBar =
196             std::static_pointer_cast<HdStBufferArrayRange>(indexBar_);
197 
198         if (indexBar && (!indexBar->IsAggregatedWith(indexBarCurrent))) {
199             binder.UnbindBufferArray(indexBarCurrent);
200             binder.BindBufferArray(indexBar);
201             indexBarCurrent = indexBar;
202         }
203 
204         //
205         // topology visibility buffer data
206         //
207         HdBufferArrayRangeSharedPtr const &
208             topVisBar_ = drawItem->GetTopologyVisibilityRange();
209 
210         HdStBufferArrayRangeSharedPtr topVisBar =
211             std::static_pointer_cast<HdStBufferArrayRange>(topVisBar_);
212 
213         if (topVisBar && (!topVisBar->IsAggregatedWith(topVisBarCurrent))) {
214             binder.UnbindInterleavedBuffer(topVisBarCurrent,
215                                            HdTokens->topologyVisibility);
216             binder.BindInterleavedBuffer(topVisBar,
217                                          HdTokens->topologyVisibility);
218             topVisBarCurrent = topVisBar;
219         }
220 
221         //
222         // per-face buffer data (fetched through ElementID in primitiveParam)
223         //
224         HdBufferArrayRangeSharedPtr const & elementBar_ =
225             drawItem->GetElementPrimvarRange();
226 
227         HdStBufferArrayRangeSharedPtr elementBar =
228             std::static_pointer_cast<HdStBufferArrayRange>(elementBar_);
229 
230         if (elementBar && (!elementBar->IsAggregatedWith(elementBarCurrent))) {
231             binder.UnbindBufferArray(elementBarCurrent);
232             binder.BindBufferArray(elementBar);
233             elementBarCurrent = elementBar;
234         }
235 
236         //
237         // vertex attrib buffer data
238         //
239         HdBufferArrayRangeSharedPtr const & vertexBar_ =
240             drawItem->GetVertexPrimvarRange();
241 
242         HdStBufferArrayRangeSharedPtr vertexBar =
243             std::static_pointer_cast<HdStBufferArrayRange>(vertexBar_);
244 
245         if (vertexBar && (!vertexBar->IsAggregatedWith(vertexBarCurrent))) {
246             binder.UnbindBufferArray(vertexBarCurrent);
247             binder.BindBufferArray(vertexBar);
248             vertexBarCurrent = vertexBar;
249         }
250 
251         //
252         // varying buffer data
253         //
254         HdBufferArrayRangeSharedPtr const & varyingBar_ =
255             drawItem->GetVaryingPrimvarRange();
256 
257         HdStBufferArrayRangeSharedPtr varyingBar =
258             std::static_pointer_cast<HdStBufferArrayRange>(varyingBar_);
259 
260         if (varyingBar && (!varyingBar->IsAggregatedWith(varyingBarCurrent))) {
261             binder.UnbindBufferArray(varyingBarCurrent);
262             binder.BindBufferArray(varyingBar);
263             varyingBarCurrent = varyingBar;
264         }
265 
266         //
267         // constant (uniform) buffer data
268         //
269         HdBufferArrayRangeSharedPtr const & constantBar_ =
270             drawItem->GetConstantPrimvarRange();
271 
272         HdStBufferArrayRangeSharedPtr constantBar =
273             std::static_pointer_cast<HdStBufferArrayRange>(constantBar_);
274 
275         if (constantBar && (!constantBar->IsAggregatedWith(constantBarCurrent))) {
276             binder.UnbindConstantBuffer(constantBarCurrent);
277             binder.BindConstantBuffer(constantBar);
278             constantBarCurrent = constantBar;
279         }
280 
281         //
282         // facevarying buffer data
283         //
284         HdBufferArrayRangeSharedPtr const & fvarBar_ =
285             drawItem->GetFaceVaryingPrimvarRange();
286 
287         HdStBufferArrayRangeSharedPtr fvarBar =
288             std::static_pointer_cast<HdStBufferArrayRange>(fvarBar_);
289 
290         if (fvarBar && (!fvarBar->IsAggregatedWith(fvarBarCurrent))) {
291             binder.UnbindBufferArray(fvarBarCurrent);
292             binder.BindBufferArray(fvarBar);
293             fvarBarCurrent = fvarBar;
294         }
295 
296         //
297         // instance buffer data
298         //
299         int instancerNumLevels = drawItem->GetInstancePrimvarNumLevels();
300         int instanceIndexWidth = instancerNumLevels + 1;
301         for (int i = 0; i < instancerNumLevels; ++i) {
302             HdBufferArrayRangeSharedPtr const & instanceBar_ =
303                 drawItem->GetInstancePrimvarRange(i);
304 
305             HdStBufferArrayRangeSharedPtr instanceBar =
306                 std::static_pointer_cast<HdStBufferArrayRange>(instanceBar_);
307 
308             if (instanceBar) {
309                 if (static_cast<size_t>(i) >= instanceBarCurrents.size()) {
310                     instanceBarCurrents.push_back(instanceBar);
311                     binder.BindInstanceBufferArray(instanceBar, i);
312                     continue;
313                 } else if (!instanceBar->IsAggregatedWith(
314                                instanceBarCurrents[i])) {
315                     binder.UnbindInstanceBufferArray(instanceBarCurrents[i], i);
316                     binder.BindInstanceBufferArray(instanceBar, i);
317                 }
318                 instanceBarCurrents[i] = instanceBar;
319             }
320         }
321 
322         //
323         // instance index indirection buffer
324         //
325         HdBufferArrayRangeSharedPtr const & instanceIndexBar_ =
326             drawItem->GetInstanceIndexRange();
327 
328         HdStBufferArrayRangeSharedPtr instanceIndexBar =
329             std::static_pointer_cast<HdStBufferArrayRange>(instanceIndexBar_);
330 
331         if (instanceIndexBar &&
332             (!instanceIndexBar->IsAggregatedWith(instanceIndexBarCurrent))) {
333             binder.UnbindBufferArray(instanceIndexBarCurrent);
334             binder.BindBufferArray(instanceIndexBar);
335             instanceIndexBarCurrent = instanceIndexBar;
336         }
337 
338         //
339         // shader buffer
340         //
341         HdBufferArrayRangeSharedPtr const & shaderBar_ =
342             !program.GetMaterialNetworkShader()
343                 ? HdStBufferArrayRangeSharedPtr()
344                 : program.GetMaterialNetworkShader()->GetShaderData();
345         HdStBufferArrayRangeSharedPtr shaderBar =
346             std::static_pointer_cast<HdStBufferArrayRange> (shaderBar_);
347 
348         // shaderBar isn't needed when the material is overridden
349         if (shaderBar && (!shaderBar->IsAggregatedWith(shaderBarCurrent))) {
350             if (shaderBarCurrent) {
351                 binder.UnbindBuffer(HdTokens->materialParams,
352                                     shaderBarCurrent->GetResource());
353             }
354             binder.BindBuffer(HdTokens->materialParams,
355                               shaderBar->GetResource());
356             shaderBarCurrent = shaderBar;
357         }
358 
359         //
360         // shader textures
361         //
362         if (program.GetMaterialNetworkShader()) {
363             program.GetMaterialNetworkShader()->BindResources(
364                 programId, binder, *renderPassState);
365         }
366 
367         /*
368           Drawing coord is a unified cursor which locates a subset of
369           aggregated buffer in GPU. The primary role of drawing coord is
370           to provide a way to access buffers from glsl shader code.
371 
372           We have some aggregated buffers of different granularities.
373           They are associated to class/variability specifiers in GL/prman spec.
374           ( see http://renderman.pixar.com/view/Appnote22 )
375 
376           |   | drawing coord |  hd buffer   |     OpenGL     |     PRMan      |
377           ----------------------------------------------------------------------
378           | 0 | ModelDC       |  (reserved)  |    uniform     |    constant    |
379           | 1 | ConstantDC    |  constantBar |    uniform     |    constant    |
380           | 2 | VertexDC      |  vertexBar   |gl_BaseVertex(^)| vertex/varying |
381           | 3 | ElementDC     |  elementBar  |       (*)      |    uniform     |
382           | 4 | PrimitiveDC   |  indexBar    | gl_PrimitiveID |       (*)      |
383           | 5 | FVarDC        |  fvarBar     | gl_PrimitiveID |    facevarying |
384           | 6 | InstanceIndex |  inst-idxBar | (gl_InstanceID)|      n/a       |
385           | 7 | ShaderDC      |  shaderBar   |    uniform     |                |
386           | 8 | TopVisDC      |  topVisBar   |                |                |
387           | 9 | VaryingDC     |  varyingBar  |gl_BaseVertex(^)|    varying     |
388           | 10| InstanceDC[0] |  instanceBar | (gl_InstanceID)|    constant    |
389           | 11| InstanceDC[1] |  instanceBar | (gl_InstanceID)|    constant    |
390           |...| ...           |  instanceBar | (gl_InstanceID)|    constant    |
391           ----------------------------------------------------------------------
392 
393           We put these offsets into 4 variables,
394            - ivec4 drawingCoord0  {ModelDC, ConstantDC, ElementDC, PrimitiveDC}
395            - ivec4 drawingCoord1  {FVarDC, InstanceIndex, ShaderDC, VertexDC}
396            - ivec2 drawingCoord2  {TopoVisibilityDC, VaryingDC}
397            - int[] drawingCoordI  (InstanceDC)
398           so that the shaders can access any of these aggregated data.
399 
400           (^) gl_BaseVertex requires GLSL 4.60 or the ARB_shader_draw_parameters
401               extension. We simply plumb the baseVertex(Offset) as a generic
402               solution.
403           (*) primitiveParam buffer can be used to reinterpret GL-primitive
404               ID back to element ID.
405          */
406 
407         int vertexOffset = 0;
408         int vertexCount = 0;
409         if (vertexBar) {
410             vertexOffset = vertexBar->GetElementOffset();
411             vertexCount = vertexBar->GetNumElements();
412         }
413 
414         //
415         // Get parameters from our buffer range objects to
416         // allow drawing to access the correct elements from
417         // aggregated buffers.
418         //
419         int numIndicesPerPrimitive = geometricShader->GetPrimitiveIndexSize();
420         int indexCount = indexBar ? indexBar->GetNumElements() * numIndicesPerPrimitive : 0;
421         int firstIndex = indexBar ? indexBar->GetElementOffset() * numIndicesPerPrimitive : 0;
422         int baseVertex = vertexOffset;
423         int instanceCount = instanceIndexBar
424             ? instanceIndexBar->GetNumElements()/instanceIndexWidth : 1;
425 
426         // if delegate fails to get vertex primvars, it could be empty.
427         // skip the drawitem to prevent drawing uninitialized vertices.
428         if (vertexCount == 0) continue;
429 
430         // update standalone uniforms
431         int drawingCoord0[4] = {
432             0, // reserved for modelBar
433             _GetElementOffset(constantBar),
434             _GetElementOffset(elementBar),
435             _GetElementOffset(indexBar),
436         };
437         int drawingCoord1[4] = {
438             _GetElementOffset(fvarBar),
439             _GetElementOffset(instanceIndexBar),
440             _GetElementOffset(shaderBar),
441             baseVertex
442         };
443         int drawingCoord2[2] = {
444             _GetElementOffset(topVisBar),
445             _GetElementOffset(varyingBar),
446         };
447         binder.BindUniformi(HdTokens->drawingCoord0, 4, drawingCoord0);
448         binder.BindUniformi(HdTokens->drawingCoord1, 4, drawingCoord1);
449         binder.BindUniformi(HdTokens->drawingCoord2, 2, drawingCoord2);
450 
451         // instance coordinates
452         std::vector<int> instanceDrawingCoords(instancerNumLevels);
453         for (int i = 0; i < instancerNumLevels; ++i) {
454             instanceDrawingCoords[i] =
455                 _GetElementOffset(instanceBarCurrents[i]);
456         }
457         if (instancerNumLevels > 0) {
458             binder.BindUniformArrayi(HdTokens->drawingCoordI,
459                                      instancerNumLevels, &instanceDrawingCoords[0]);
460         }
461 
462         if (indexCount > 0 && indexBar) {
463             glDrawElementsInstancedBaseVertex(
464                 geometricShader->GetPrimitiveMode(),
465                 indexCount,
466                 GL_UNSIGNED_INT, // GL_INT is invalid: indexBar->GetResource(HdTokens->indices)->GetGLDataType(),
467                 (void *)(firstIndex * sizeof(uint32_t)),
468                 instanceCount,
469                 baseVertex);
470         } else if (vertexCount > 0) {
471             glDrawArraysInstanced(
472                 geometricShader->GetPrimitiveMode(),
473                 baseVertex,
474                 vertexCount,
475                 instanceCount);
476         }
477 
478         if (program.GetMaterialNetworkShader()) {
479             program.GetMaterialNetworkShader()->UnbindResources(
480                 programId, binder, *renderPassState);
481         }
482 
483         HD_PERF_COUNTER_INCR(HdPerfTokens->drawCalls);
484     }
485 
486     HD_PERF_COUNTER_ADD(HdTokens->itemsDrawn, numItemsDrawn);
487 
488     for (HdStShaderCodeSharedPtr const & shader : shaders) {
489         shader->UnbindResources(programId, binder, *renderPassState);
490     }
491     geometricShader->UnbindResources(programId, binder, *renderPassState);
492 
493     // unbind (make non resident all bindless buffers)
494     if (constantBarCurrent)
495         binder.UnbindConstantBuffer(constantBarCurrent);
496     if (vertexBarCurrent)
497         binder.UnbindBufferArray(vertexBarCurrent);
498     if (varyingBarCurrent)
499         binder.UnbindBufferArray(varyingBarCurrent);
500     if (elementBarCurrent)
501         binder.UnbindBufferArray(elementBarCurrent);
502     if (fvarBarCurrent)
503         binder.UnbindBufferArray(fvarBarCurrent);
504     for (size_t i = 0; i < instanceBarCurrents.size(); ++i) {
505         binder.UnbindInstanceBufferArray(instanceBarCurrents[i], i);
506     }
507     if (instanceIndexBarCurrent)
508         binder.UnbindBufferArray(instanceIndexBarCurrent);
509     if (indexBarCurrent)
510         binder.UnbindBufferArray(indexBarCurrent);
511     if (topVisBarCurrent)
512         binder.UnbindBufferArray(topVisBarCurrent);
513     if (shaderBarCurrent) {
514         binder.UnbindBuffer(HdTokens->materialParams,
515                             shaderBarCurrent->GetResource());
516     }
517 
518     glUseProgram(0);
519 }
520 
521 PXR_NAMESPACE_CLOSE_SCOPE
522 
523