1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
|
/*
* Copyright 2019-2021 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* In no event and under no legal theory, whether in tort (including negligence),
* contract, or otherwise, unless required by applicable law (such as deliberate
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
* liable for any damages, including any direct, indirect, special, incidental,
* or consequential damages of any character arising as a result of this License or
* out of the use or inability to use the software (including but not limited to damages
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
* all other commercial damages or losses), even if such Contributor has been advised
* of the possibility of such damages.
*/
#include "DeviceContextBase.hpp"
#include "GraphicsAccessories.hpp"
namespace Diligent
{
#if DILIGENT_DEBUG
# define CHECK_PARAMETER(Expr, ...) \
do \
{ \
VERIFY(Expr, __VA_ARGS__); \
if (!(Expr)) return false; \
} while (false)
#else
# define CHECK_PARAMETER(Expr, ...) \
do \
{ \
if (!(Expr)) \
{ \
LOG_ERROR_MESSAGE(__VA_ARGS__); \
return false; \
} \
} while (false)
#endif
bool VerifyDrawAttribs(const DrawAttribs& Attribs)
{
#define CHECK_DRAW_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw attribs are invalid: ", __VA_ARGS__)
CHECK_DRAW_ATTRIBS(Attribs.NumVertices != 0, "NumVertices must not be zero.");
#undef CHECK_DRAW_ATTRIBS
return true;
}
bool VerifyDrawIndexedAttribs(const DrawIndexedAttribs& Attribs)
{
#define CHECK_DRAW_INDEXED_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw indexed attribs are invalid: ", __VA_ARGS__)
CHECK_DRAW_INDEXED_ATTRIBS(Attribs.IndexType == VT_UINT16 || Attribs.IndexType == VT_UINT32,
"IndexType (", GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32.");
CHECK_DRAW_INDEXED_ATTRIBS(Attribs.NumIndices != 0, "NumIndices must not be zero.");
#undef CHECK_DRAW_INDEXED_ATTRIBS
return true;
}
bool VerifyDrawMeshAttribs(Uint32 MaxDrawMeshTasksCount, const DrawMeshAttribs& Attribs)
{
#define CHECK_DRAW_MESH_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw mesh attribs are invalid: ", __VA_ARGS__)
CHECK_DRAW_MESH_ATTRIBS(Attribs.ThreadGroupCount != 0, "ThreadGroupCount must not be zero.");
CHECK_DRAW_MESH_ATTRIBS(Attribs.ThreadGroupCount <= MaxDrawMeshTasksCount,
"ThreadGroupCount (", Attribs.ThreadGroupCount, ") must not exceed ", MaxDrawMeshTasksCount);
#undef CHECK_DRAW_MESH_ATTRIBS
return true;
}
bool VerifyDrawIndirectAttribs(const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)
{
#define CHECK_DRAW_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw indirect attribs are invalid: ", __VA_ARGS__)
CHECK_DRAW_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect draw arguments buffer must not be null.");
CHECK_DRAW_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0,
"indirect draw arguments buffer '", pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag.");
#undef CHECK_DRAW_INDIRECT_ATTRIBS
return true;
}
bool VerifyDrawIndexedIndirectAttribs(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)
{
#define CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw indexed indirect attribs are invalid: ", __VA_ARGS__)
CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect draw arguments buffer must not null.");
CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS(Attribs.IndexType == VT_UINT16 || Attribs.IndexType == VT_UINT32,
"IndexType (", GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32.");
CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0,
"indirect draw arguments buffer '",
pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag.");
#undef CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS
return true;
}
bool VerifyDrawMeshIndirectAttribs(const DrawMeshIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)
{
#define CHECK_DRAW_MESH_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw mesh indirect attribs are invalid: ", __VA_ARGS__)
CHECK_DRAW_MESH_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect draw arguments buffer must not be null.");
CHECK_DRAW_MESH_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0,
"indirect draw arguments buffer '", pAttribsBuffer->GetDesc().Name,
"' was not created with BIND_INDIRECT_DRAW_ARGS flag.");
#undef CHECK_DRAW_MESH_INDIRECT_ATTRIBS
return true;
}
bool VerifyDrawMeshIndirectCountAttribs(const DrawMeshIndirectCountAttribs& Attribs, const IBuffer* pAttribsBuffer, const IBuffer* pCountBuff, Uint32 IndirectCmdStride)
{
#define CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw mesh indirect count attribs are invalid: ", __VA_ARGS__)
CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS(pAttribsBuffer != nullptr, "indirect draw arguments buffer must not be null.");
const auto& IDesc = pAttribsBuffer->GetDesc();
CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS((IDesc.BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0,
"indirect draw arguments buffer '", IDesc.Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag.");
CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS(Attribs.IndirectDrawArgsOffset + IndirectCmdStride * Attribs.MaxCommandCount <= IDesc.uiSizeInBytes,
"invalid IndirectDrawArgsOffset or indirect draw arguments buffer '", IDesc.Name, "' is too small.");
CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS(pCountBuff != nullptr, "count buffer must not be null.");
const auto& CDesc = pAttribsBuffer->GetDesc();
CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS((CDesc.BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0,
"count buffer '", CDesc.Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag.");
CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS(Attribs.CountBufferOffset + 4 <= CDesc.uiSizeInBytes,
"invalid CountBufferOffset or count buffer '", CDesc.Name, "' is too small.");
#undef CHECK_DRAW_MESH_INDIRECT_COUNT_ATTRIBS
return true;
}
bool VerifyDispatchComputeAttribs(const DispatchComputeAttribs& Attribs)
{
#define CHECK_DISPATCH_COMPUTE_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Dispatch compute attribs are invalid: ", __VA_ARGS__)
CHECK_DISPATCH_COMPUTE_ATTRIBS(Attribs.ThreadGroupCountX != 0, "ThreadGroupCountX must no be zero.");
CHECK_DISPATCH_COMPUTE_ATTRIBS(Attribs.ThreadGroupCountY != 0, "ThreadGroupCountY must no be zero.");
CHECK_DISPATCH_COMPUTE_ATTRIBS(Attribs.ThreadGroupCountZ != 0, "ThreadGroupCountZ must no be zero.");
#undef CHECK_DISPATCH_COMPUTE_ATTRIBS
return true;
}
bool VerifyDispatchComputeIndirectAttribs(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)
{
#define CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Dispatch compute indirect attribs are invalid: ", __VA_ARGS__)
CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect dispatch arguments buffer must not be null.");
CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0,
"indirect dispatch arguments buffer '",
pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag.");
#undef CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS
return true;
}
bool VerifyResolveTextureSubresourceAttribs(const ResolveTextureSubresourceAttribs& ResolveAttribs,
const TextureDesc& SrcTexDesc,
const TextureDesc& DstTexDesc)
{
#define CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Resolve texture subresource attribs are invalid: ", __VA_ARGS__)
CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(SrcTexDesc.SampleCount > 1, "source texture '", SrcTexDesc.Name, "' of a resolve operation is not multi-sampled.");
CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(DstTexDesc.SampleCount == 1, "destination texture '", DstTexDesc.Name, "' of a resolve operation is multi-sampled.");
auto SrcMipLevelProps = GetMipLevelProperties(SrcTexDesc, ResolveAttribs.SrcMipLevel);
auto DstMipLevelProps = GetMipLevelProperties(DstTexDesc, ResolveAttribs.DstMipLevel);
CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(SrcMipLevelProps.LogicalWidth == DstMipLevelProps.LogicalWidth && SrcMipLevelProps.LogicalHeight == DstMipLevelProps.LogicalHeight,
"the size (", SrcMipLevelProps.LogicalWidth, "x", SrcMipLevelProps.LogicalHeight,
") of the source subresource of a resolve operation (texture '",
SrcTexDesc.Name, "', mip ", ResolveAttribs.SrcMipLevel, ", slice ", ResolveAttribs.SrcSlice,
") does not match the size (", DstMipLevelProps.LogicalWidth, "x", DstMipLevelProps.LogicalHeight,
") of the destination subresource (texture '", DstTexDesc.Name, "', mip ", ResolveAttribs.DstMipLevel, ", slice ",
ResolveAttribs.DstSlice, ").");
const auto& SrcFmtAttribs = GetTextureFormatAttribs(SrcTexDesc.Format);
const auto& DstFmtAttribs = GetTextureFormatAttribs(DstTexDesc.Format);
const auto& ResolveFmtAttribs = GetTextureFormatAttribs(ResolveAttribs.Format);
if (!SrcFmtAttribs.IsTypeless && !DstFmtAttribs.IsTypeless)
{
CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(SrcTexDesc.Format == DstTexDesc.Format,
"source (", SrcFmtAttribs.Name, ") and destination (", DstFmtAttribs.Name,
") texture formats of a resolve operation must match exaclty or be compatible typeless formats.");
CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(ResolveAttribs.Format == TEX_FORMAT_UNKNOWN || SrcTexDesc.Format == ResolveAttribs.Format, "Invalid format of a resolve operation.");
}
if (SrcFmtAttribs.IsTypeless && DstFmtAttribs.IsTypeless)
{
CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(ResolveAttribs.Format != TEX_FORMAT_UNKNOWN,
"format of a resolve operation must not be unknown when both src and dst texture formats are typeless.");
}
if (SrcFmtAttribs.IsTypeless || DstFmtAttribs.IsTypeless)
{
CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(!ResolveFmtAttribs.IsTypeless,
"format of a resolve operation must not be typeless when one of the texture formats is typeless.");
}
#undef CHECK_RESOLVE_TEX_SUBRES_ATTRIBS
return true;
}
bool VerifyBeginRenderPassAttribs(const BeginRenderPassAttribs& Attribs)
{
#define CHECK_BEGIN_RENDER_PASS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Begin render pass attribs are invalid: ", __VA_ARGS__)
CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.pRenderPass != nullptr, "pRenderPass pass must not be null.");
CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.pFramebuffer != nullptr, "pFramebuffer must not be null.");
const auto& RPDesc = Attribs.pRenderPass->GetDesc();
Uint32 NumRequiredClearValues = 0;
for (Uint32 i = 0; i < RPDesc.AttachmentCount; ++i)
{
const auto& Attchmnt = RPDesc.pAttachments[i];
if (Attchmnt.LoadOp == ATTACHMENT_LOAD_OP_CLEAR)
NumRequiredClearValues = i + 1;
const auto& FmtAttribs = GetTextureFormatAttribs(Attchmnt.Format);
if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL)
{
if (Attchmnt.StencilLoadOp == ATTACHMENT_LOAD_OP_CLEAR)
NumRequiredClearValues = i + 1;
}
}
CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.ClearValueCount >= NumRequiredClearValues,
"at least ", NumRequiredClearValues, " clear values are required, but only ",
Uint32{Attribs.ClearValueCount}, " are provided.");
CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.ClearValueCount == 0 || Attribs.pClearValues != nullptr,
"pClearValues must not be null when ClearValueCount (", Attribs.ClearValueCount, ") is not zero.");
#undef CHECK_BEGIN_RENDER_PASS_ATTRIBS
return true;
}
bool VerifyStateTransitionDesc(const IRenderDevice* pDevice, const StateTransitionDesc& Barrier)
{
#define CHECK_STATE_TRANSITION_DESC(Expr, ...) CHECK_PARAMETER(Expr, "State transition parameters are invalid: ", __VA_ARGS__)
CHECK_STATE_TRANSITION_DESC(Barrier.pResource != nullptr, "pResource must not be null.");
CHECK_STATE_TRANSITION_DESC(Barrier.NewState != RESOURCE_STATE_UNKNOWN, "NewState state can't be UNKNOWN.");
RESOURCE_STATE OldState = RESOURCE_STATE_UNKNOWN;
if (RefCntAutoPtr<ITexture> pTexture{Barrier.pResource, IID_Texture})
{
const auto& TexDesc = pTexture->GetDesc();
CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(Barrier.NewState, true), "invlaid new state specified for texture '", TexDesc.Name, "'.");
OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pTexture->GetState();
CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN,
"the state of texture '", TexDesc.Name,
"' is unknown to the engine and is not explicitly specified in the barrier.");
CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(OldState, true), "invlaid old state specified for texture '", TexDesc.Name, "'.");
CHECK_STATE_TRANSITION_DESC(Barrier.FirstMipLevel < TexDesc.MipLevels, "first mip level (", Barrier.FirstMipLevel,
") specified by the barrier is out of range. Texture '",
TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s).");
CHECK_STATE_TRANSITION_DESC(Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.FirstMipLevel + Barrier.MipLevelsCount <= TexDesc.MipLevels,
"mip level range ", Barrier.FirstMipLevel, "..", Barrier.FirstMipLevel + Barrier.MipLevelsCount - 1,
" specified by the barrier is out of range. Texture '",
TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s).");
CHECK_STATE_TRANSITION_DESC(Barrier.FirstArraySlice < TexDesc.ArraySize, "first array slice (", Barrier.FirstArraySlice,
") specified by the barrier is out of range. Array size of texture '",
TexDesc.Name, "' is ", TexDesc.ArraySize);
CHECK_STATE_TRANSITION_DESC(Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.FirstArraySlice + Barrier.ArraySliceCount <= TexDesc.ArraySize,
"array slice range ", Barrier.FirstArraySlice, "..", Barrier.FirstArraySlice + Barrier.ArraySliceCount - 1,
" specified by the barrier is out of range. Array size of texture '",
TexDesc.Name, "' is ", TexDesc.ArraySize);
auto DevType = pDevice->GetDeviceCaps().DevType;
if (DevType != RENDER_DEVICE_TYPE_D3D12 && DevType != RENDER_DEVICE_TYPE_VULKAN)
{
CHECK_STATE_TRANSITION_DESC(Barrier.FirstMipLevel == 0 && (Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.MipLevelsCount == TexDesc.MipLevels),
"failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device.");
CHECK_STATE_TRANSITION_DESC(Barrier.FirstArraySlice == 0 && (Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.ArraySliceCount == TexDesc.ArraySize),
"failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device.");
}
}
else if (RefCntAutoPtr<IBuffer> pBuffer{Barrier.pResource, IID_Buffer})
{
const auto& BuffDesc = pBuffer->GetDesc();
CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(Barrier.NewState, false), "invlaid new state specified for buffer '", BuffDesc.Name, "'.");
OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pBuffer->GetState();
CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN, "the state of buffer '", BuffDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier.");
CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(OldState, false), "invlaid old state specified for buffer '", BuffDesc.Name, "'.");
}
else if (RefCntAutoPtr<IBottomLevelAS> pBottomLevelAS{Barrier.pResource, IID_BottomLevelAS})
{
const auto& BLASDesc = pBottomLevelAS->GetDesc();
OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pBottomLevelAS->GetState();
CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN, "the state of BLAS '", BLASDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier.");
CHECK_STATE_TRANSITION_DESC(Barrier.NewState == RESOURCE_STATE_BUILD_AS_READ || Barrier.NewState == RESOURCE_STATE_BUILD_AS_WRITE,
"invlaid new state specified for BLAS '", BLASDesc.Name, "'.");
CHECK_STATE_TRANSITION_DESC(Barrier.TransitionType != STATE_TRANSITION_TYPE_IMMEDIATE, "split barriers are not supported for BLAS.");
}
else if (RefCntAutoPtr<ITopLevelAS> pTopLevelAS{Barrier.pResource, IID_TopLevelAS})
{
const auto& TLASDesc = pTopLevelAS->GetDesc();
OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pTopLevelAS->GetState();
CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN, "the state of TLAS '", TLASDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier.");
CHECK_STATE_TRANSITION_DESC(Barrier.NewState == RESOURCE_STATE_BUILD_AS_READ || Barrier.NewState == RESOURCE_STATE_BUILD_AS_WRITE || Barrier.NewState == RESOURCE_STATE_RAY_TRACING,
"invlaid new state specified for TLAS '", TLASDesc.Name, "'.");
CHECK_STATE_TRANSITION_DESC(Barrier.TransitionType != STATE_TRANSITION_TYPE_IMMEDIATE, "split barriers are not supported for TLAS.");
}
else
{
UNEXPECTED("unsupported resource type");
}
if (OldState == RESOURCE_STATE_UNORDERED_ACCESS && Barrier.NewState == RESOURCE_STATE_UNORDERED_ACCESS)
{
CHECK_STATE_TRANSITION_DESC(Barrier.TransitionType == STATE_TRANSITION_TYPE_IMMEDIATE, "for UAV barriers, transition type must be STATE_TRANSITION_TYPE_IMMEDIATE.");
}
if (Barrier.TransitionType == STATE_TRANSITION_TYPE_BEGIN)
{
CHECK_STATE_TRANSITION_DESC(!Barrier.UpdateResourceState, "resource state can't be updated in begin-split barrier.");
}
#undef CHECK_STATE_TRANSITION_DESC
return true;
}
bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs)
{
#define CHECK_BUILD_BLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Build BLAS attribs are invalid: ", __VA_ARGS__)
CHECK_BUILD_BLAS_ATTRIBS(Attribs.pBLAS != nullptr, "pBLAS must not be null.");
CHECK_BUILD_BLAS_ATTRIBS(Attribs.pScratchBuffer != nullptr, "pScratchBuffer must not be null.");
CHECK_BUILD_BLAS_ATTRIBS((Attribs.BoxDataCount != 0) ^ (Attribs.TriangleDataCount != 0), "exactly one of TriangleDataCount and BoxDataCount must be non-zero.");
CHECK_BUILD_BLAS_ATTRIBS(Attribs.pBoxData != nullptr || Attribs.BoxDataCount == 0, "BoxDataCount is ", Attribs.BoxDataCount, ", but pBoxData is null.");
CHECK_BUILD_BLAS_ATTRIBS(Attribs.pTriangleData != nullptr || Attribs.TriangleDataCount == 0, "TriangleDataCount is ", Attribs.TriangleDataCount, ", but pTriangleData is null.");
const auto& BLASDesc = Attribs.pBLAS->GetDesc();
CHECK_BUILD_BLAS_ATTRIBS(Attribs.BoxDataCount <= BLASDesc.BoxCount, "BoxDataCount (", Attribs.BoxDataCount, ") must be less than or equal to pBLAS->GetDesc().BoxCount (", BLASDesc.BoxCount, ").");
CHECK_BUILD_BLAS_ATTRIBS(Attribs.TriangleDataCount <= BLASDesc.TriangleCount, "TriangleDataCount (", Attribs.TriangleDataCount, ") must be less than or equal to pBLAS->GetDesc().TriangleCount (", BLASDesc.TriangleCount, ").");
if (Attribs.Update)
{
CHECK_BUILD_BLAS_ATTRIBS((BLASDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_UPDATE) == RAYTRACING_BUILD_AS_ALLOW_UPDATE,
"Update is true, but BLAS was created without RAYTRACING_BUILD_AS_ALLOW_UPDATE flag.");
const Uint32 GeomCount = Attribs.pBLAS->GetActualGeometryCount();
CHECK_BUILD_BLAS_ATTRIBS(Attribs.BoxDataCount == 0 || Attribs.BoxDataCount == GeomCount,
"Update is true, but BoxDataCount (", Attribs.BoxDataCount, ") does not match the previous value (", GeomCount, ").");
CHECK_BUILD_BLAS_ATTRIBS(Attribs.TriangleDataCount == 0 || Attribs.TriangleDataCount == GeomCount,
"Update is true, but TriangleDataCount (", Attribs.TriangleDataCount, ") does not match the previous value (", GeomCount, ").");
}
for (Uint32 i = 0; i < Attribs.TriangleDataCount; ++i)
{
const auto& tri = Attribs.pTriangleData[i];
const Uint32 VertexSize = GetValueSize(tri.VertexValueType) * tri.VertexComponentCount;
const Uint32 VertexDataSize = tri.VertexStride * tri.VertexCount;
const Uint32 GeomIndex = Attribs.pBLAS->GetGeometryDescIndex(tri.GeometryName);
CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != INVALID_INDEX,
"pTriangleData[", i, "].GeometryName (", tri.GeometryName, ") is not found in BLAS description.");
const auto& TriDesc = BLASDesc.pTriangles[GeomIndex];
CHECK_BUILD_BLAS_ATTRIBS(tri.VertexValueType == VT_UNDEFINED || tri.VertexValueType == TriDesc.VertexValueType,
"pTriangleData[", i, "].VertexValueType must be undefined or match the VertexValueType in geometry description.");
CHECK_BUILD_BLAS_ATTRIBS(tri.VertexComponentCount == 0 || tri.VertexComponentCount == TriDesc.VertexComponentCount,
"pTriangleData[", i, "].VertexComponentCount (", Uint32{tri.VertexComponentCount}, ") must be 0 or match the VertexComponentCount (",
Uint32{TriDesc.VertexComponentCount}, ") in geometry description.");
CHECK_BUILD_BLAS_ATTRIBS(tri.VertexCount <= TriDesc.MaxVertexCount,
"pTriangleData[", i, "].VertexCount (", tri.VertexCount, ") must not be greater than MaxVertexCount(", TriDesc.MaxVertexCount, ").");
CHECK_BUILD_BLAS_ATTRIBS(tri.VertexStride >= VertexSize,
"pTriangleData[", i, "].VertexStride (", tri.VertexStride, ") must be at least ", VertexSize, " bytes.");
CHECK_BUILD_BLAS_ATTRIBS(tri.pVertexBuffer != nullptr, "pTriangleData[", i, "].pVertexBuffer must not be null.");
const BufferDesc& VertBufDesc = tri.pVertexBuffer->GetDesc();
CHECK_BUILD_BLAS_ATTRIBS((VertBufDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING,
"pTriangleData[", i, "].pVertexBuffer was not created with BIND_RAY_TRACING flag.");
CHECK_BUILD_BLAS_ATTRIBS(tri.VertexOffset + VertexDataSize <= VertBufDesc.uiSizeInBytes,
"pTriangleData[", i, "].pVertexBuffer is too small for the specified VertexStride (", tri.VertexStride, ") and VertexCount (",
tri.VertexCount, "): at least ", tri.VertexOffset + VertexDataSize, " bytes are required.");
CHECK_BUILD_BLAS_ATTRIBS(tri.IndexType == VT_UNDEFINED || tri.IndexType == TriDesc.IndexType,
"pTriangleData[", i, "].IndexType (", GetValueTypeString(tri.IndexType), ") must match the IndexType (",
GetValueTypeString(TriDesc.IndexType), ") in geometry description.");
CHECK_BUILD_BLAS_ATTRIBS(tri.PrimitiveCount <= TriDesc.MaxPrimitiveCount,
"pTriangleData[", i, "].PrimitiveCount (", tri.PrimitiveCount, ") must not be greater than MaxPrimitiveCount (",
TriDesc.MaxPrimitiveCount, ").");
if (TriDesc.IndexType != VT_UNDEFINED)
{
CHECK_BUILD_BLAS_ATTRIBS(tri.pIndexBuffer != nullptr, "pTriangleData[", i, "].pIndexBuffer must not be null.");
const BufferDesc& InstBufDesc = tri.pIndexBuffer->GetDesc();
const Uint32 IndexDataSize = tri.PrimitiveCount * 3 * GetValueSize(tri.IndexType);
CHECK_BUILD_BLAS_ATTRIBS((InstBufDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING,
"pTriangleData[", i, "].pIndexBuffer was not created with BIND_RAY_TRACING flag.");
CHECK_BUILD_BLAS_ATTRIBS(tri.IndexOffset + IndexDataSize <= InstBufDesc.uiSizeInBytes,
"pTriangleData[", i, "].pIndexBuffer is too small for specified IndexType and IndexCount: at least",
tri.IndexOffset + IndexDataSize, " bytes are required.");
}
else
{
CHECK_BUILD_BLAS_ATTRIBS(tri.VertexCount == tri.PrimitiveCount * 3,
"pTriangleData[", i, "].VertexCount (", tri.VertexCount, ") must be equal to PrimitiveCount * 3 (",
tri.PrimitiveCount * 3, ").");
CHECK_BUILD_BLAS_ATTRIBS(tri.pIndexBuffer == nullptr, "pTriangleData[", i, "].pIndexBuffer must be null if IndexType is VT_UNDEFINED.");
}
if (tri.pTransformBuffer != nullptr)
{
CHECK_BUILD_BLAS_ATTRIBS((tri.pTransformBuffer->GetDesc().BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING,
"pTriangleData[", i, "].pTransformBuffer was not created with BIND_RAY_TRACING flag.");
CHECK_BUILD_BLAS_ATTRIBS(TriDesc.AllowsTransforms, "pTriangleData[", i, "] uses transform buffer, but AllowsTransforms is false.");
}
}
for (Uint32 i = 0; i < Attribs.BoxDataCount; ++i)
{
const auto& box = Attribs.pBoxData[i];
const Uint32 BoxSize = sizeof(float) * 6;
const Uint32 GeomIndex = Attribs.pBLAS->GetGeometryDescIndex(box.GeometryName);
CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != INVALID_INDEX,
"pBoxData[", i, "].GeometryName (", box.GeometryName, ") is not found in BLAS description.");
const auto& BoxDesc = BLASDesc.pBoxes[GeomIndex];
CHECK_BUILD_BLAS_ATTRIBS(box.BoxCount <= BoxDesc.MaxBoxCount,
"pBoxData[", i, "].BoxCount (", box.BoxCount, ") must not be greated than MaxBoxCount (", BoxDesc.MaxBoxCount, ").");
CHECK_BUILD_BLAS_ATTRIBS(box.BoxStride >= BoxSize,
"pBoxData[", i, "].BoxStride (", box.BoxStride, ") must be at least ", BoxSize, " bytes.");
CHECK_BUILD_BLAS_ATTRIBS(box.BoxStride % 8 == 0,
"pBoxData[", i, "].BoxStride (", box.BoxStride, ") must be aligned to 8 bytes.");
CHECK_BUILD_BLAS_ATTRIBS(box.pBoxBuffer != nullptr, "pBoxData[", i, "].pBoxBuffer must not be null.");
CHECK_BUILD_BLAS_ATTRIBS((box.pBoxBuffer->GetDesc().BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING,
"pBoxData[", i, "].pBoxBuffer was not created with BIND_RAY_TRACING flag.");
}
const auto& ScratchDesc = Attribs.pScratchBuffer->GetDesc();
CHECK_BUILD_BLAS_ATTRIBS(Attribs.ScratchBufferOffset <= ScratchDesc.uiSizeInBytes,
"ScratchBufferOffset (", Attribs.ScratchBufferOffset, ") is greater than the buffer size (", ScratchDesc.uiSizeInBytes, ").");
if (Attribs.Update)
{
CHECK_BUILD_BLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pBLAS->GetScratchBufferSizes().Update,
"pScratchBuffer size is too small, use pBLAS->GetScratchBufferSizes().Update to get the required size for the scratch buffer.");
}
else
{
CHECK_BUILD_BLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pBLAS->GetScratchBufferSizes().Build,
"pScratchBuffer size is too small, use pBLAS->GetScratchBufferSizes().Build to get the required size for the scratch buffer.");
}
CHECK_BUILD_BLAS_ATTRIBS((ScratchDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING,
"pScratchBuffer was not created with BIND_RAY_TRACING flag.");
#undef CHECK_BUILD_BLAS_ATTRIBS
return true;
}
bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs)
{
#define CHECK_BUILD_TLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Build TLAS attribs are invalid: ", __VA_ARGS__)
CHECK_BUILD_TLAS_ATTRIBS(Attribs.pTLAS != nullptr, "pTLAS must not be null.");
CHECK_BUILD_TLAS_ATTRIBS(Attribs.pScratchBuffer != nullptr, "pScratchBuffer must not be null.");
CHECK_BUILD_TLAS_ATTRIBS(Attribs.pInstances != nullptr, "pInstances must not be null.");
CHECK_BUILD_TLAS_ATTRIBS(Attribs.pInstanceBuffer != nullptr, "pInstanceBuffer must not be null.");
CHECK_BUILD_TLAS_ATTRIBS(Attribs.BindingMode == HIT_GROUP_BINDING_MODE_USER_DEFINED || Attribs.HitGroupStride != 0,
"HitGroupStride must be greater than 0 if BindingMode is not HIT_GROUP_BINDING_MODE_USER_DEFINED.");
const auto& TLASDesc = Attribs.pTLAS->GetDesc();
CHECK_BUILD_TLAS_ATTRIBS(Attribs.InstanceCount <= TLASDesc.MaxInstanceCount,
"InstanceCount (", Attribs.InstanceCount, ") must be less than or equal to pTLAS->GetDesc().MaxInstanceCount (",
TLASDesc.MaxInstanceCount, ").");
if (Attribs.Update)
{
CHECK_BUILD_TLAS_ATTRIBS((TLASDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_UPDATE) == RAYTRACING_BUILD_AS_ALLOW_UPDATE,
"Update is true, but TLAS created without RAYTRACING_BUILD_AS_ALLOW_UPDATE flag.");
const Uint32 PrevInstanceCount = Attribs.pTLAS->GetBuildInfo().InstanceCount;
CHECK_BUILD_TLAS_ATTRIBS(PrevInstanceCount == Attribs.InstanceCount,
"Update is true, but InstanceCount (", Attribs.InstanceCount, ") does not match the previous value (", PrevInstanceCount, ").");
}
const auto& InstDesc = Attribs.pInstanceBuffer->GetDesc();
const auto InstDataSize = size_t{Attribs.InstanceCount} * size_t{TLAS_INSTANCE_DATA_SIZE};
Uint32 AutoOffsetCounter = 0;
// Calculate instance data size
for (Uint32 i = 0; i < Attribs.InstanceCount; ++i)
{
constexpr Uint32 BitMask = (1u << 24) - 1;
const auto& Inst = Attribs.pInstances[i];
VERIFY((Inst.CustomId & ~BitMask) == 0, "Only the lower 24 bits are used.");
VERIFY(Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO ||
(Inst.ContributionToHitGroupIndex & ~BitMask) == 0,
"Only the lower 24 bits are used.");
CHECK_BUILD_TLAS_ATTRIBS(Inst.InstanceName != nullptr, "pInstances[", i, "].InstanceName must not be null.");
CHECK_BUILD_TLAS_ATTRIBS(Inst.pBLAS != nullptr, "pInstances[", i, "].pBLAS must not be null.");
if (Attribs.Update)
{
const TLASInstanceDesc IDesc = Attribs.pTLAS->GetInstanceDesc(Inst.InstanceName);
CHECK_BUILD_TLAS_ATTRIBS(IDesc.InstanceIndex != INVALID_INDEX, "Update is true, but pInstances[", i, "].InstanceName does not exists.");
}
if (Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO)
++AutoOffsetCounter;
CHECK_BUILD_TLAS_ATTRIBS(Attribs.BindingMode == HIT_GROUP_BINDING_MODE_USER_DEFINED || Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO,
"pInstances[", i,
"].ContributionToHitGroupIndex must be TLAS_INSTANCE_OFFSET_AUTO "
"if BindingMode is not HIT_GROUP_BINDING_MODE_USER_DEFINED.");
}
CHECK_BUILD_TLAS_ATTRIBS(AutoOffsetCounter == 0 || AutoOffsetCounter == Attribs.InstanceCount,
"all pInstances[i].ContributionToHitGroupIndex must be TLAS_INSTANCE_OFFSET_AUTO, or none of them should.");
CHECK_BUILD_TLAS_ATTRIBS(Attribs.InstanceBufferOffset <= InstDesc.uiSizeInBytes,
"InstanceBufferOffset (", Attribs.InstanceBufferOffset, ") is greater than the buffer size (", InstDesc.uiSizeInBytes, ").");
CHECK_BUILD_TLAS_ATTRIBS(InstDesc.uiSizeInBytes - Attribs.InstanceBufferOffset >= InstDataSize,
"pInstanceBuffer size (", InstDesc.uiSizeInBytes, ") is too small: at least ",
InstDataSize + Attribs.InstanceBufferOffset, " bytes are required.");
CHECK_BUILD_TLAS_ATTRIBS((InstDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING,
"pInstanceBuffer was not created with BIND_RAY_TRACING flag.");
const auto& ScratchDesc = Attribs.pScratchBuffer->GetDesc();
CHECK_BUILD_TLAS_ATTRIBS(Attribs.ScratchBufferOffset <= ScratchDesc.uiSizeInBytes,
"ScratchBufferOffset (", Attribs.ScratchBufferOffset, ") is greater than the buffer size (", ScratchDesc.uiSizeInBytes, ").");
if (Attribs.Update)
{
CHECK_BUILD_TLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pTLAS->GetScratchBufferSizes().Update,
"pScratchBuffer size is too small, use pTLAS->GetScratchBufferSizes().Update to get the required size for scratch buffer.");
}
else
{
CHECK_BUILD_TLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pTLAS->GetScratchBufferSizes().Build,
"pScratchBuffer size is too small, use pTLAS->GetScratchBufferSizes().Build to get the required size for scratch buffer.");
}
CHECK_BUILD_TLAS_ATTRIBS((ScratchDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING,
"pScratchBuffer was not created with BIND_RAY_TRACING flag.");
#undef CHECK_BUILD_TLAS_ATTRIBS
return true;
}
bool VerifyCopyBLASAttribs(const IRenderDevice* pDevice, const CopyBLASAttribs& Attribs)
{
#define CHECK_COPY_BLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Copy BLAS attribs are invalid: ", __VA_ARGS__)
CHECK_COPY_BLAS_ATTRIBS(Attribs.pSrc != nullptr, "pSrc must not be null.");
CHECK_COPY_BLAS_ATTRIBS(Attribs.pDst != nullptr, "pDst must not be null.");
if (Attribs.Mode == COPY_AS_MODE_CLONE)
{
if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_VULKAN)
{
auto& SrcDesc = Attribs.pSrc->GetDesc();
auto& DstDesc = Attribs.pDst->GetDesc();
CHECK_COPY_BLAS_ATTRIBS(SrcDesc.TriangleCount == DstDesc.TriangleCount,
"Src BLAS triangle count (", SrcDesc.TriangleCount, ") must be equal to the dst BLAS triangle count (", DstDesc.TriangleCount, ").");
CHECK_COPY_BLAS_ATTRIBS(SrcDesc.BoxCount == DstDesc.BoxCount,
"Src BLAS box count (", SrcDesc.BoxCount, ") must be equal to the dst BLAS box count (", DstDesc.BoxCount, ").");
CHECK_COPY_BLAS_ATTRIBS(SrcDesc.Flags == DstDesc.Flags,
"Source and destination BLASes must have been created with the same flags.");
for (Uint32 i = 0; i < SrcDesc.TriangleCount; ++i)
{
const BLASTriangleDesc& SrcTri = SrcDesc.pTriangles[i];
const Uint32 Index = Attribs.pDst->GetGeometryDescIndex(SrcTri.GeometryName);
CHECK_COPY_BLAS_ATTRIBS(Index != INVALID_INDEX,
"Src GeometryName ('", SrcTri.GeometryName, "') at index ", i, " is not found in pDst.");
const BLASTriangleDesc& DstTri = DstDesc.pTriangles[Index];
CHECK_COPY_BLAS_ATTRIBS(SrcTri.MaxVertexCount == DstTri.MaxVertexCount,
"MaxVertexCount value (", SrcTri.MaxVertexCount, ") in source triangle description at index ", i,
" does not match MaxVertexCount value (", DstTri.MaxVertexCount, ") in the destination description.");
CHECK_COPY_BLAS_ATTRIBS(SrcTri.VertexValueType == DstTri.VertexValueType,
"VertexValueType value (", GetValueTypeString(SrcTri.VertexValueType), ") in source triangle description at index ", i,
" does not match VertexValueType value (", GetValueTypeString(DstTri.VertexValueType), ") in destination description.");
CHECK_COPY_BLAS_ATTRIBS(SrcTri.VertexComponentCount == DstTri.VertexComponentCount,
"VertexComponentCount value (", Uint32{SrcTri.VertexComponentCount}, ") in source triangle description at index ", i,
" does not match VertexComponentCount value (", Uint32{DstTri.VertexComponentCount}, ") in destination description.");
CHECK_COPY_BLAS_ATTRIBS(SrcTri.MaxPrimitiveCount == DstTri.MaxPrimitiveCount,
"MaxPrimitiveCount value (", SrcTri.MaxPrimitiveCount, ") in source triangle description at index ", i,
" does not match MaxPrimitiveCount value (", DstTri.MaxPrimitiveCount, ") in destination description.");
CHECK_COPY_BLAS_ATTRIBS(SrcTri.IndexType == DstTri.IndexType,
"IndexType value (", GetValueTypeString(SrcTri.IndexType), ") in source triangle description at index ", i,
" does not match IndexType value (", GetValueTypeString(DstTri.IndexType), ") in destination description.");
CHECK_COPY_BLAS_ATTRIBS(SrcTri.AllowsTransforms == DstTri.AllowsTransforms,
"AllowsTransforms value (", (SrcTri.AllowsTransforms ? "true" : "false"), ") in source triangle description at index ", i,
" does not match AllowsTransforms value (", (DstTri.AllowsTransforms ? "true" : "false"), ") in destination description.");
}
for (Uint32 i = 0; i < SrcDesc.BoxCount; ++i)
{
const BLASBoundingBoxDesc& SrcBox = SrcDesc.pBoxes[i];
const Uint32 Index = Attribs.pDst->GetGeometryDescIndex(SrcBox.GeometryName);
if (Index == INVALID_INDEX)
{
LOG_ERROR_MESSAGE("Copy BLAS attribs are invalid: pSrc->GetDesc().pBoxes[", i, "].GeometryName ('", SrcBox.GeometryName, "') is not found in pDst.");
return false;
}
const BLASBoundingBoxDesc& DstBox = DstDesc.pBoxes[Index];
CHECK_COPY_BLAS_ATTRIBS(SrcBox.MaxBoxCount == DstBox.MaxBoxCount,
"MaxBoxCountt value (", SrcBox.MaxBoxCount, ") in source box description at index ", i,
" does not match MaxBoxCount value (", DstBox.MaxBoxCount, ") in destination description.");
}
}
}
else if (Attribs.Mode == COPY_AS_MODE_COMPACT)
{
auto& SrcDesc = Attribs.pSrc->GetDesc();
auto& DstDesc = Attribs.pDst->GetDesc();
CHECK_COPY_BLAS_ATTRIBS((SrcDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION, "must be have been create with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag.");
CHECK_COPY_BLAS_ATTRIBS(DstDesc.CompactedSize != 0, "pDst must have been create with non-zero CompactedSize.");
}
else
{
LOG_ERROR_MESSAGE("IDeviceContext::CopyBLAS: unknown Mode.");
return false;
}
#undef CHECK_COPY_BLAS_ATTRIBS
return true;
}
bool VerifyCopyTLASAttribs(const CopyTLASAttribs& Attribs)
{
#define CHECK_COPY_TLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Copy TLAS attribs are invalid: ", __VA_ARGS__)
CHECK_COPY_TLAS_ATTRIBS(Attribs.pSrc != nullptr, "pSrc must not be null.");
CHECK_COPY_TLAS_ATTRIBS(Attribs.pDst != nullptr, "pDst must not be null.");
if (Attribs.Mode == COPY_AS_MODE_CLONE)
{
auto& SrcDesc = Attribs.pSrc->GetDesc();
auto& DstDesc = Attribs.pDst->GetDesc();
CHECK_COPY_TLAS_ATTRIBS(SrcDesc.MaxInstanceCount == DstDesc.MaxInstanceCount && SrcDesc.Flags == DstDesc.Flags,
"pDst must have been created with the same parameters as pSrc.");
}
else if (Attribs.Mode == COPY_AS_MODE_COMPACT)
{
auto& SrcDesc = Attribs.pSrc->GetDesc();
auto& DstDesc = Attribs.pDst->GetDesc();
CHECK_COPY_TLAS_ATTRIBS((SrcDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION, "pSrc was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag.");
CHECK_COPY_TLAS_ATTRIBS(DstDesc.CompactedSize != 0, "pDst must have been create with non-zero CompactedSize.");
}
else
{
LOG_ERROR_MESSAGE("IDeviceContext::CopyTLAS: unknown Mode.");
return false;
}
#undef CHECK_COPY_TLAS_ATTRIBS
return true;
}
bool VerifyWriteBLASCompactedSizeAttribs(const IRenderDevice* pDevice, const WriteBLASCompactedSizeAttribs& Attribs)
{
#define CHECK_WRITE_BLAS_SIZE_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Write compacted BLAS size attribs are invalid: ", __VA_ARGS__)
CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.pBLAS != nullptr, "pBLAS must not be null.");
CHECK_WRITE_BLAS_SIZE_ATTRIBS((Attribs.pBLAS->GetDesc().Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION,
"pBLAS was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag.");
CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.pDestBuffer != nullptr, "pDestBuffer must not be null.");
const BufferDesc& DstDesc = Attribs.pDestBuffer->GetDesc();
CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.DestBufferOffset + sizeof(Uint64) <= DstDesc.uiSizeInBytes, "pDestBuffer is too small.");
if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_D3D12)
{
CHECK_WRITE_BLAS_SIZE_ATTRIBS((DstDesc.BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS,
"pDestBuffer must have been created with BIND_UNORDERED_ACCESS flag in Direct3D12.");
}
#undef CHECK_WRITE_BLAS_SIZE_ATTRIBS
return true;
}
bool VerifyWriteTLASCompactedSizeAttribs(const IRenderDevice* pDevice, const WriteTLASCompactedSizeAttribs& Attribs)
{
#define CHECK_WRITE_TLAS_SIZE_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Write compacted TLAS size attribs are invalid: ", __VA_ARGS__)
CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.pTLAS != nullptr, "pTLAS must not be null.");
CHECK_WRITE_TLAS_SIZE_ATTRIBS((Attribs.pTLAS->GetDesc().Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION,
"pTLAS was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag.");
CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.pDestBuffer != nullptr, "pDestBuffer must not be null.");
const BufferDesc& DstDesc = Attribs.pDestBuffer->GetDesc();
CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.DestBufferOffset + sizeof(Uint64) <= DstDesc.uiSizeInBytes, "pDestBuffer is too small.");
if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_D3D12)
{
CHECK_WRITE_TLAS_SIZE_ATTRIBS((DstDesc.BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS,
"pDestBuffer must have been created with BIND_UNORDERED_ACCESS flag.");
}
#undef CHECK_WRITE_TLAS_SIZE_ATTRIBS
return true;
}
bool VerifyTraceRaysAttribs(const TraceRaysAttribs& Attribs)
{
#define CHECK_TRACE_RAYS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Trace rays attribs are invalid: ", __VA_ARGS__)
CHECK_TRACE_RAYS_ATTRIBS(Attribs.pSBT != nullptr, "pSBT must not be null.");
#ifdef DILIGENT_DEVELOPMENT
CHECK_TRACE_RAYS_ATTRIBS(Attribs.pSBT->Verify(VERIFY_SBT_FLAG_SHADER_ONLY | VERIFY_SBT_FLAG_TLAS),
"not all shaders in SBT are bound or instance to shader mapping is incorrect.");
#endif // DILIGENT_DEVELOPMENT
CHECK_TRACE_RAYS_ATTRIBS(Attribs.DimensionX != 0, "DimensionX must not be zero.");
CHECK_TRACE_RAYS_ATTRIBS(Attribs.DimensionY != 0, "DimensionY must not be zero.");
CHECK_TRACE_RAYS_ATTRIBS(Attribs.DimensionZ != 0, "DimensionZ must not be zero.");
#undef CHECK_TRACE_RAYS_ATTRIBS
return true;
}
bool VerifyTraceRaysIndirectAttribs(const IRenderDevice* pDevice, const TraceRaysIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer, Uint32 SBTSize)
{
#define CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Trace rays indirect attribs are invalid: ", __VA_ARGS__)
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(Attribs.pSBT != nullptr, "pSBT must not be null");
#ifdef DILIGENT_DEVELOPMENT
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(Attribs.pSBT->Verify(VERIFY_SBT_FLAG_SHADER_ONLY | VERIFY_SBT_FLAG_TLAS),
"not all shaders in SBT are bound or instance to shader mapping is incorrect.");
#endif // DILIGENT_DEVELOPMENT
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect dispatch arguments buffer must not be null.");
const auto& Desc = pAttribsBuffer->GetDesc();
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS((Desc.BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0,
"indirect trace rays arguments buffer '", Desc.Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag.");
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(Attribs.ArgsByteOffset + Attribs.ArgsByteSize <= Desc.uiSizeInBytes,
"indirect trace rays arguments buffer '", Desc.Name, "' is too small.");
constexpr Uint32 DimSize = sizeof(Uint32) * 3;
if (pDevice->GetDeviceCaps().IsVulkanDevice())
{
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS((Desc.BindFlags & BIND_RAY_TRACING) != 0,
"indirect trace rays arguments buffer '", Desc.Name, "' was not created with BIND_RAY_TRACING flag.");
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(Attribs.ArgsByteSize == DimSize || Attribs.ArgsByteSize == DimSize + SBTSize,
"ArgsByteSize must be (", DimSize, ") or (", DimSize + SBTSize, ") bytes");
}
else
{
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(Attribs.ArgsByteSize == DimSize + SBTSize, "ArgsByteSize must be (", DimSize + SBTSize, ") bytes");
CHECK_TRACE_RAYS_INDIRECT_ATTRIBS(Desc.Usage == USAGE_DEFAULT,
"pAttribsBuffer will be updated inside TraceRaysIndirect(), buffer must be created only with USAGE_DEFAULT");
}
#undef CHECK_TRACE_RAYS_INDIRECT_ATTRIBS
return true;
}
} // namespace Diligent
|