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