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 /// \file simpleShadowArray.cpp
25 
26 #include "pxr/imaging/garch/glApi.h"
27 
28 #include "pxr/imaging/glf/simpleShadowArray.h"
29 #include "pxr/imaging/glf/debugCodes.h"
30 #include "pxr/imaging/glf/diagnostic.h"
31 #include "pxr/imaging/glf/glContext.h"
32 #include "pxr/imaging/hio/image.h"
33 
34 #include "pxr/base/arch/fileSystem.h"
35 #include "pxr/base/gf/vec2i.h"
36 #include "pxr/base/gf/vec4d.h"
37 #include "pxr/base/tf/debug.h"
38 #include "pxr/base/tf/envSetting.h"
39 #include "pxr/base/tf/stringUtils.h"
40 
41 #include <string>
42 #include <vector>
43 
44 
45 PXR_NAMESPACE_OPEN_SCOPE
46 
47 TF_DEFINE_ENV_SETTING(GLF_ENABLE_BINDLESS_SHADOW_TEXTURES, true,
48                       "Enable use of bindless shadow maps");
49 
GlfSimpleShadowArray()50 GlfSimpleShadowArray::GlfSimpleShadowArray() :
51     // bindful state
52     _size(0),
53     _numLayers(0),
54     _bindfulTexture(0),
55     _shadowDepthSampler(0),
56     // common state
57     _framebuffer(0),
58     _shadowCompareSampler(0),
59     _unbindRestoreDrawFramebuffer(0),
60     _unbindRestoreReadFramebuffer(0),
61     _unbindRestoreViewport{0,0,0,0}
62 {
63 }
64 
~GlfSimpleShadowArray()65 GlfSimpleShadowArray::~GlfSimpleShadowArray()
66 {
67     _FreeResources();
68 }
69 
70 /*static*/
71 bool
GetBindlessShadowMapsEnabled()72 GlfSimpleShadowArray::GetBindlessShadowMapsEnabled()
73 {
74     // Note: We do not test the GL context caps for the availability of the
75     // bindless texture and int64 extensions.
76     static bool usingBindlessShadowMaps =
77         TfGetEnvSetting(GLF_ENABLE_BINDLESS_SHADOW_TEXTURES);
78 
79     return usingBindlessShadowMaps;
80 }
81 
82 
83 // --------- (public) Bindful API ----------
84 void
SetSize(GfVec2i const & size)85 GlfSimpleShadowArray::SetSize(GfVec2i const & size)
86 {
87     if (GetBindlessShadowMapsEnabled()) {
88         TF_CODING_ERROR("Using bindful API %s when bindless "
89             "shadow maps are enabled\n", TF_FUNC_NAME().c_str());
90         return;
91     }
92     if (_size != size) {
93         _FreeBindfulTextures();
94         _size = size;
95     }
96 }
97 
98 void
SetNumLayers(size_t numLayers)99 GlfSimpleShadowArray::SetNumLayers(size_t numLayers)
100 {
101     if (GetBindlessShadowMapsEnabled()) {
102         TF_CODING_ERROR("Using bindful API %s when bindless "
103             "shadow maps are enabled\n", TF_FUNC_NAME().c_str());
104         return;
105     }
106 
107     if (_numLayers != numLayers) {
108         _viewMatrix.resize(numLayers, GfMatrix4d().SetIdentity());
109         _projectionMatrix.resize(numLayers, GfMatrix4d().SetIdentity());
110         _FreeBindfulTextures();
111         _numLayers = numLayers;
112     }
113 }
114 
115 GLuint
GetShadowMapTexture() const116 GlfSimpleShadowArray::GetShadowMapTexture() const
117 {
118     if (GetBindlessShadowMapsEnabled()) {
119         TF_CODING_ERROR("Using bindful API in %s when bindless "
120             "shadow maps are enabled\n",  TF_FUNC_NAME().c_str());
121         return -1;
122     }
123     return _bindfulTexture;
124 }
125 
126 GLuint
GetShadowMapDepthSampler() const127 GlfSimpleShadowArray::GetShadowMapDepthSampler() const
128 {
129     if (GetBindlessShadowMapsEnabled()) {
130         TF_CODING_ERROR("Using bindful API in %s when bindless "
131             "shadow maps are enabled\n",  TF_FUNC_NAME().c_str());
132         return -1;
133     }
134     return _shadowDepthSampler;
135 }
136 
137 GLuint
GetShadowMapCompareSampler() const138 GlfSimpleShadowArray::GetShadowMapCompareSampler() const
139 {
140     if (GetBindlessShadowMapsEnabled()) {
141         TF_CODING_ERROR("Using bindful API in %s when bindless "
142             "shadow maps are enabled\n",  TF_FUNC_NAME().c_str());
143         return -1;
144     }
145     return _shadowCompareSampler;
146 }
147 
148 // --------- (public) Bindless API ----------
149 void
SetShadowMapResolutions(std::vector<GfVec2i> const & resolutions)150 GlfSimpleShadowArray::SetShadowMapResolutions(
151     std::vector<GfVec2i> const& resolutions)
152 {
153     if (_resolutions == resolutions) {
154         return;
155     }
156 
157     _resolutions = resolutions;
158 
159     _FreeBindlessTextures();
160 
161     size_t numShadowMaps = _resolutions.size();
162     if (_viewMatrix.size() != numShadowMaps ||
163         _projectionMatrix.size() != numShadowMaps) {
164         _viewMatrix.resize(numShadowMaps, GfMatrix4d().SetIdentity());
165         _projectionMatrix.resize(numShadowMaps, GfMatrix4d().SetIdentity());
166     }
167 
168 }
169 
170 std::vector<uint64_t> const&
GetBindlessShadowMapHandles() const171 GlfSimpleShadowArray::GetBindlessShadowMapHandles() const
172 {
173     return _bindlessTextureHandles;
174 }
175 
176 // --------- (public) Common API ----------
177 size_t
GetNumShadowMapPasses() const178 GlfSimpleShadowArray::GetNumShadowMapPasses() const
179 {
180     // In both the bindful and bindless cases, we require one pass per shadow
181     // map.
182     if (GetBindlessShadowMapsEnabled()) {
183         return _resolutions.size();
184     } else {
185         return _numLayers;
186     }
187 }
188 
189 GfVec2i
GetShadowMapSize(size_t index) const190 GlfSimpleShadowArray::GetShadowMapSize(size_t index) const
191 {
192     GfVec2i shadowMapSize(0);
193     if (GetBindlessShadowMapsEnabled()) {
194         if (TF_VERIFY(index < _resolutions.size())) {
195             shadowMapSize = _resolutions[index];
196         }
197     } else {
198         // In the bindful case, all shadow map textures use the same size.
199         shadowMapSize = _size;
200     }
201 
202     return shadowMapSize;
203 }
204 
205 GfMatrix4d
GetViewMatrix(size_t index) const206 GlfSimpleShadowArray::GetViewMatrix(size_t index) const
207 {
208     if (!TF_VERIFY(index < _viewMatrix.size())) {
209         return GfMatrix4d(1.0);
210     }
211 
212     return _viewMatrix[index];
213 }
214 
215 void
SetViewMatrix(size_t index,GfMatrix4d const & matrix)216 GlfSimpleShadowArray::SetViewMatrix(size_t index, GfMatrix4d const & matrix)
217 {
218     if (!TF_VERIFY(index < _viewMatrix.size())) {
219         return;
220     }
221 
222     _viewMatrix[index] = matrix;
223 }
224 
225 GfMatrix4d
GetProjectionMatrix(size_t index) const226 GlfSimpleShadowArray::GetProjectionMatrix(size_t index) const
227 {
228     if (!TF_VERIFY(index < _projectionMatrix.size())) {
229         return GfMatrix4d(1.0);
230     }
231 
232     return _projectionMatrix[index];
233 }
234 
235 void
SetProjectionMatrix(size_t index,GfMatrix4d const & matrix)236 GlfSimpleShadowArray::SetProjectionMatrix(size_t index, GfMatrix4d const & matrix)
237 {
238     if (!TF_VERIFY(index < _projectionMatrix.size())) {
239         return;
240     }
241 
242     _projectionMatrix[index] = matrix;
243 }
244 
245 GfMatrix4d
GetWorldToShadowMatrix(size_t index) const246 GlfSimpleShadowArray::GetWorldToShadowMatrix(size_t index) const
247 {
248     GfMatrix4d size = GfMatrix4d().SetScale(GfVec3d(0.5, 0.5, 0.5));
249     GfMatrix4d center = GfMatrix4d().SetTranslate(GfVec3d(0.5, 0.5, 0.5));
250     return GetViewMatrix(index) * GetProjectionMatrix(index) * size * center;
251 }
252 
253 void
BeginCapture(size_t index,bool clear)254 GlfSimpleShadowArray::BeginCapture(size_t index, bool clear)
255 {
256     _BindFramebuffer(index);
257 
258     if (clear) {
259         glClear(GL_DEPTH_BUFFER_BIT);
260     }
261 
262     // save the current viewport
263     glGetIntegerv(GL_VIEWPORT, _unbindRestoreViewport);
264 
265     GfVec2i resolution = GetShadowMapSize(index);
266     glViewport(0, 0, resolution[0], resolution[1]);
267 
268     // depth 1.0 means infinity (no occluders).
269     // This value is also used as a border color
270     glDepthRange(0, 0.99999);
271     glEnable(GL_DEPTH_CLAMP);
272 
273     GLF_POST_PENDING_GL_ERRORS();
274 }
275 
276 void
EndCapture(size_t index)277 GlfSimpleShadowArray::EndCapture(size_t index)
278 {
279     // reset to GL default, except viewport
280     glDepthRange(0, 1.0);
281     glDisable(GL_DEPTH_CLAMP);
282 
283     if (TfDebug::IsEnabled(GLF_DEBUG_DUMP_SHADOW_TEXTURES)) {
284         HioImage::StorageSpec storage;
285         GfVec2i resolution = GetShadowMapSize(index);
286         storage.width = resolution[0];
287         storage.height = resolution[1];
288         storage.format = HioFormatFloat32;
289 
290         // In OpenGL, (0, 0) is the lower left corner.
291         storage.flipped = true;
292 
293         const int numPixels = storage.width * storage.height;
294         std::vector<GLfloat> pixelData(static_cast<size_t>(numPixels));
295         storage.data = static_cast<void*>(pixelData.data());
296 
297         glReadPixels(0,
298                      0,
299                      storage.width,
300                      storage.height,
301                      GL_DEPTH_COMPONENT,
302                      GL_FLOAT,
303                      storage.data);
304 
305         GLfloat minValue = std::numeric_limits<float>::max();
306         GLfloat maxValue = -std::numeric_limits<float>::max();
307         for (int i = 0; i < numPixels; ++i) {
308             const GLfloat pixelValue = pixelData[i];
309             if (pixelValue < minValue) {
310                 minValue = pixelValue;
311             }
312             if (pixelValue > maxValue) {
313                 maxValue = pixelValue;
314             }
315         }
316 
317         // Remap the pixel data so that the furthest depth sample is white and
318         // the nearest depth sample is black.
319         for (int i = 0; i < numPixels; ++i) {
320             pixelData[i] = (pixelData[i] - minValue) / (maxValue - minValue);
321         }
322 
323         const std::string outputImageFile = ArchNormPath(
324             TfStringPrintf("%s/GlfSimpleShadowArray.index_%zu.tif",
325                            ArchGetTmpDir(),
326                            index));
327         HioImageSharedPtr image = HioImage::OpenForWriting(outputImageFile);
328         if (image->Write(storage)) {
329             TfDebug::Helper().Msg(
330                 "Wrote shadow texture: %s\n", outputImageFile.c_str());
331         } else {
332             TfDebug::Helper().Msg(
333                 "Failed to write shadow texture: %s\n", outputImageFile.c_str()
334             );
335         }
336     }
337 
338     _UnbindFramebuffer();
339 
340     // restore viewport
341     glViewport(_unbindRestoreViewport[0],
342                _unbindRestoreViewport[1],
343                _unbindRestoreViewport[2],
344                _unbindRestoreViewport[3]);
345 
346     GLF_POST_PENDING_GL_ERRORS();
347 }
348 
349 // --------- private helpers ----------
350 bool
_ShadowMapExists() const351 GlfSimpleShadowArray::_ShadowMapExists() const
352 {
353     return GetBindlessShadowMapsEnabled() ? !_bindlessTextures.empty() :
354                                              _bindfulTexture;
355 }
356 
357 void
_AllocResources()358 GlfSimpleShadowArray::_AllocResources()
359 {
360     // Samplers
361     GLfloat border[] = {1, 1, 1, 1};
362 
363     if (!_shadowDepthSampler) {
364         glGenSamplers(1, &_shadowDepthSampler);
365         glSamplerParameteri(
366             _shadowDepthSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
367         glSamplerParameteri(
368             _shadowDepthSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
369         glSamplerParameteri(
370             _shadowDepthSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
371         glSamplerParameteri(
372             _shadowDepthSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
373         glSamplerParameterfv(
374             _shadowDepthSampler, GL_TEXTURE_BORDER_COLOR, border);
375     }
376 
377     if (!_shadowCompareSampler) {
378         glGenSamplers(1, &_shadowCompareSampler);
379         glSamplerParameteri(
380             _shadowCompareSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
381         glSamplerParameteri(
382             _shadowCompareSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
383         glSamplerParameteri(
384             _shadowCompareSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
385         glSamplerParameteri(
386             _shadowCompareSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
387         glSamplerParameterfv(
388             _shadowCompareSampler, GL_TEXTURE_BORDER_COLOR, border);
389         glSamplerParameteri(
390             _shadowCompareSampler, GL_TEXTURE_COMPARE_MODE,
391             GL_COMPARE_REF_TO_TEXTURE);
392         glSamplerParameteri(
393             _shadowCompareSampler, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
394     }
395 
396     // Shadow maps
397     if (GetBindlessShadowMapsEnabled()) {
398         _AllocBindlessTextures();
399     } else {
400        _AllocBindfulTextures();
401     }
402 
403     // Framebuffer
404     if (!_framebuffer) {
405         glGenFramebuffers(1, &_framebuffer);
406     }
407 }
408 
409 void
_AllocBindfulTextures()410 GlfSimpleShadowArray::_AllocBindfulTextures()
411 {
412     glGenTextures(1, &_bindfulTexture);
413     glBindTexture(GL_TEXTURE_2D_ARRAY, _bindfulTexture);
414 
415     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F,
416                 _size[0], _size[1], _numLayers, 0,
417                 GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
418     glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
419 
420     TF_DEBUG(GLF_DEBUG_SHADOW_TEXTURES).Msg(
421         "Created bindful shadow map texture array with %lu %dx%d textures\n"
422         , _numLayers, _size[0], _size[1]);
423 }
424 
425 void
_AllocBindlessTextures()426 GlfSimpleShadowArray::_AllocBindlessTextures()
427 {
428     if (!TF_VERIFY(_shadowCompareSampler) ||
429         !TF_VERIFY(_bindlessTextures.empty()) ||
430         !TF_VERIFY(_bindlessTextureHandles.empty())) {
431         TF_CODING_ERROR("Unexpected entry state in %s\n",
432                         TF_FUNC_NAME().c_str());
433         return;
434     }
435 
436     // Commenting out the line below results in the residency check in
437     // _FreeBindlessTextures failing.
438     GlfSharedGLContextScopeHolder sharedContextScopeHolder;
439 
440     // XXX: Currently, we allocate/reallocate ALL shadow maps each time.
441     for (GfVec2i const& size : _resolutions) {
442         GLuint id;
443         glGenTextures(1, &id);
444         glBindTexture(GL_TEXTURE_2D, id);
445         glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F,
446             size[0], size[1], 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
447         _bindlessTextures.push_back(id);
448 
449         GLuint64 handle =
450             glGetTextureSamplerHandleARB(id, _shadowCompareSampler);
451 
452         _bindlessTextureHandles.push_back(handle);
453 
454         if (TF_VERIFY(!glIsTextureHandleResidentARB(handle))) {
455             glMakeTextureHandleResidentARB(handle);
456         } else {
457             GLF_POST_PENDING_GL_ERRORS();
458         }
459 
460         TF_DEBUG(GLF_DEBUG_SHADOW_TEXTURES).Msg(
461             "Created bindless shadow map texture of size %dx%d "
462             "(id %#x, handle %#lx)\n" , size[0], size[1], id, handle);
463     }
464 
465     glBindTexture(GL_TEXTURE_2D, 0);
466 }
467 
468 void
_FreeResources()469 GlfSimpleShadowArray::_FreeResources()
470 {
471     GlfSharedGLContextScopeHolder sharedContextScopeHolder;
472 
473     if (GetBindlessShadowMapsEnabled()) {
474         _FreeBindlessTextures();
475     } else {
476         _FreeBindfulTextures();
477     }
478 
479     if (_framebuffer) {
480         glDeleteFramebuffers(1, &_framebuffer);
481         _framebuffer = 0;
482     }
483     if (_shadowDepthSampler) {
484         glDeleteSamplers(1, &_shadowDepthSampler);
485         _shadowDepthSampler = 0;
486     }
487     if (_shadowCompareSampler) {
488         glDeleteSamplers(1, &_shadowCompareSampler);
489         _shadowCompareSampler = 0;
490     }
491 }
492 
493 void
_FreeBindfulTextures()494 GlfSimpleShadowArray::_FreeBindfulTextures()
495 {
496     GlfSharedGLContextScopeHolder sharedContextScopeHolder;
497 
498     if (_bindfulTexture) {
499         glDeleteTextures(1, &_bindfulTexture);
500         _bindfulTexture = 0;
501     }
502 
503     GLF_POST_PENDING_GL_ERRORS();
504 }
505 
506 void
_FreeBindlessTextures()507 GlfSimpleShadowArray::_FreeBindlessTextures()
508 {
509     GlfSharedGLContextScopeHolder sharedContextScopeHolder;
510     // XXX: Ideally, we don't deallocate all textures, and only those that have
511     // resolution modified.
512 
513     if (!_bindlessTextureHandles.empty()) {
514         for (uint64_t handle : _bindlessTextureHandles) {
515             // Handles are made resident on creation.
516             if (TF_VERIFY(glIsTextureHandleResidentARB(handle))) {
517                 glMakeTextureHandleNonResidentARB(handle);
518             }
519         }
520         _bindlessTextureHandles.clear();
521     }
522 
523     for (GLuint const& id : _bindlessTextures) {
524         if (id) {
525             glDeleteTextures(1, &id);
526         }
527     }
528     _bindlessTextures.clear();
529 
530     GLF_POST_PENDING_GL_ERRORS();
531 }
532 
533 void
_BindFramebuffer(size_t index)534 GlfSimpleShadowArray::_BindFramebuffer(size_t index)
535 {
536     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING,
537                   (GLint*)&_unbindRestoreDrawFramebuffer);
538     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING,
539                   (GLint*)&_unbindRestoreReadFramebuffer);
540 
541     if (!_framebuffer || !_ShadowMapExists()) {
542         _AllocResources();
543     }
544 
545     glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
546     if (GetBindlessShadowMapsEnabled()) {
547         glFramebufferTexture(GL_FRAMEBUFFER,
548             GL_DEPTH_ATTACHMENT, _bindlessTextures[index], 0);
549     } else {
550         glFramebufferTextureLayer(GL_FRAMEBUFFER,
551             GL_DEPTH_ATTACHMENT, _bindfulTexture, 0, index);
552     }
553 
554     GLF_POST_PENDING_GL_ERRORS();
555 }
556 
557 void
_UnbindFramebuffer()558 GlfSimpleShadowArray::_UnbindFramebuffer()
559 {
560     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _unbindRestoreDrawFramebuffer);
561     glBindFramebuffer(GL_READ_FRAMEBUFFER, _unbindRestoreReadFramebuffer);
562 
563     GLF_POST_PENDING_GL_ERRORS();
564 }
565 
566 
567 PXR_NAMESPACE_CLOSE_SCOPE
568 
569