This repository was archived by the owner on Sep 15, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathgfx9CmdUtil.cpp
More file actions
5130 lines (4426 loc) · 247 KB
/
Copy pathgfx9CmdUtil.cpp
File metadata and controls
5130 lines (4426 loc) · 247 KB
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
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
***********************************************************************************************************************
*
* Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
**********************************************************************************************************************/
#include "core/cmdStream.h"
#include "core/hw/gfxip/pipeline.h"
#include "core/hw/gfxip/gfx9/gfx9CmdUtil.h"
#include "core/hw/gfxip/gfx9/gfx9Device.h"
#include "g_gfx9Settings.h"
#include "palInlineFuncs.h"
#include "palIterator.h"
#include "palMath.h"
using namespace Util;
namespace Pal
{
namespace Gfx9
{
static constexpr ME_EVENT_WRITE_event_index_enum VgtEventIndex[]=
{
event_index__me_event_write__other, // 0x0: Reserved_0x00,
event_index__me_event_write__sample_streamoutstats__GFX10, // 0x1: SAMPLE_STREAMOUTSTATS1,
event_index__me_event_write__sample_streamoutstats__GFX10, // 0x2: SAMPLE_STREAMOUTSTATS2,
event_index__me_event_write__sample_streamoutstats__GFX10, // 0x3: SAMPLE_STREAMOUTSTATS3,
event_index__me_event_write__other, // 0x4: CACHE_FLUSH_TS,
event_index__me_event_write__other, // 0x5: CONTEXT_DONE,
event_index__me_event_write__other, // 0x6: CACHE_FLUSH,
event_index__me_event_write__cs_vs_ps_partial_flush, // 0x7: CS_PARTIAL_FLUSH,
event_index__me_event_write__other, // 0x8: VGT_STREAMOUT_SYNC,
event_index__me_event_write__other, // 0x9: Reserved_0x09,
event_index__me_event_write__other, // 0xa: VGT_STREAMOUT_RESET,
event_index__me_event_write__other, // 0xb: END_OF_PIPE_INCR_DE,
event_index__me_event_write__other, // 0xc: END_OF_PIPE_IB_END,
event_index__me_event_write__other, // 0xd: RST_PIX_CNT,
event_index__me_event_write__other, // 0xe: BREAK_BATCH,
event_index__me_event_write__cs_vs_ps_partial_flush, // 0xf: VS_PARTIAL_FLUSH,
event_index__me_event_write__cs_vs_ps_partial_flush, // 0x10: PS_PARTIAL_FLUSH,
event_index__me_event_write__other, // 0x11: FLUSH_HS_OUTPUT,
event_index__me_event_write__other, // 0x12: FLUSH_DFSM,
event_index__me_event_write__other, // 0x13: RESET_TO_LOWEST_VGT,
event_index__me_event_write__other, // 0x14: CACHE_FLUSH_AND_INV_TS_EVENT,
event_index__me_event_write__pixel_pipe_stat_control_or_dump, // 0x15: ZPASS_DONE,
event_index__me_event_write__other, // 0x16: CACHE_FLUSH_AND_INV_EVENT,
event_index__me_event_write__other, // 0x17: PERFCOUNTER_START,
event_index__me_event_write__other, // 0x18: PERFCOUNTER_STOP,
event_index__me_event_write__other, // 0x19: PIPELINESTAT_START,
event_index__me_event_write__other, // 0x1a: PIPELINESTAT_STOP,
event_index__me_event_write__other, // 0x1b: PERFCOUNTER_SAMPLE,
event_index__me_event_write__other, // 0x1c: FLUSH_ES_OUTPUT,
event_index__me_event_write__other, // 0x1d: BIN_CONF_OVERRIDE_CHECK,
event_index__me_event_write__sample_pipelinestat, // 0x1e: SAMPLE_PIPELINESTAT,
event_index__me_event_write__other, // 0x1f: SO_VGTSTREAMOUT_FLUSH,
event_index__me_event_write__sample_streamoutstats__GFX10, // 0x20: SAMPLE_STREAMOUTSTATS,
event_index__me_event_write__other, // 0x21: RESET_VTX_CNT,
event_index__me_event_write__other, // 0x22: BLOCK_CONTEXT_DONE,
event_index__me_event_write__other, // 0x23: CS_CONTEXT_DONE,
event_index__me_event_write__other, // 0x24: VGT_FLUSH,
event_index__me_event_write__other, // 0x25: TGID_ROLLOVER,
event_index__me_event_write__other, // 0x26: SQ_NON_EVENT,
event_index__me_event_write__other, // 0x27: SC_SEND_DB_VPZ,
event_index__me_event_write__other, // 0x28: BOTTOM_OF_PIPE_TS,
event_index__me_event_write__other, // 0x29: FLUSH_SX_TS,
event_index__me_event_write__other, // 0x2a: DB_CACHE_FLUSH_AND_INV,
event_index__me_event_write__other, // 0x2b: FLUSH_AND_INV_DB_DATA_TS,
event_index__me_event_write__other, // 0x2c: FLUSH_AND_INV_DB_META,
event_index__me_event_write__other, // 0x2d: FLUSH_AND_INV_CB_DATA_TS,
event_index__me_event_write__other, // 0x2e: FLUSH_AND_INV_CB_META,
event_index__me_event_write__other, // 0x2f: CS_DONE,
event_index__me_event_write__other, // 0x30: PS_DONE,
event_index__me_event_write__other, // 0x31: FLUSH_AND_INV_CB_PIXEL_DATA,
event_index__me_event_write__other, // 0x32: SX_CB_RAT_ACK_REQUEST,
event_index__me_event_write__other, // 0x33: THREAD_TRACE_START,
event_index__me_event_write__other, // 0x34: THREAD_TRACE_STOP,
event_index__me_event_write__other, // 0x35: THREAD_TRACE_MARKER,
event_index__me_event_write__other, // 0x36: THREAD_TRACE_FLUSH/DRAW,
event_index__me_event_write__other, // 0x37: THREAD_TRACE_FINISH,
event_index__me_event_write__pixel_pipe_stat_control_or_dump, // 0x38: PIXEL_PIPE_STAT_CONTROL,
event_index__me_event_write__pixel_pipe_stat_control_or_dump, // 0x39: PIXEL_PIPE_STAT_DUMP,
event_index__me_event_write__other, // 0x3a: PIXEL_PIPE_STAT_RESET,
event_index__me_event_write__other, // 0x3b: CONTEXT_SUSPEND,
event_index__me_event_write__other, // 0x3c: OFFCHIP_HS_DEALLOC,
event_index__me_event_write__other, // 0x3d: ENABLE_NGG_PIPELINE,
event_index__me_event_write__other, // 0x3e: ENABLE_LEGACY_PIPELINE,
event_index__me_event_write__other, // 0x3f: DRAW_DONE,
};
static constexpr bool VgtEventHasTs[]=
{
false, // 0x0: Reserved_0x00,
false, // 0x1: SAMPLE_STREAMOUTSTATS1,
false, // 0x2: SAMPLE_STREAMOUTSTATS2,
false, // 0x3: SAMPLE_STREAMOUTSTATS3,
true, // 0x4: CACHE_FLUSH_TS,
false, // 0x5: CONTEXT_DONE,
false, // 0x6: CACHE_FLUSH,
false, // 0x7: CS_PARTIAL_FLUSH,
false, // 0x8: VGT_STREAMOUT_SYNC,
false, // 0x9: Reserved_0x09,
false, // 0xa: VGT_STREAMOUT_RESET,
false, // 0xb: END_OF_PIPE_INCR_DE,
false, // 0xc: END_OF_PIPE_IB_END,
false, // 0xd: RST_PIX_CNT,
false, // 0xe: BREAK_BATCH,
false, // 0xf: VS_PARTIAL_FLUSH,
false, // 0x10: PS_PARTIAL_FLUSH,
false, // 0x11: FLUSH_HS_OUTPUT,
false, // 0x12: FLUSH_DFSM,
false, // 0x13: RESET_TO_LOWEST_VGT,
true, // 0x14: CACHE_FLUSH_AND_INV_TS_EVENT,
false, // 0x15: ZPASS_DONE,
false, // 0x16: CACHE_FLUSH_AND_INV_EVENT,
false, // 0x17: PERFCOUNTER_START,
false, // 0x18: PERFCOUNTER_STOP,
false, // 0x19: PIPELINESTAT_START,
false, // 0x1a: PIPELINESTAT_STOP,
false, // 0x1b: PERFCOUNTER_SAMPLE,
false, // 0x1c: Available_0x1c,
false, // 0x1d: Available_0x1d,
false, // 0x1e: SAMPLE_PIPELINESTAT,
false, // 0x1f: SO_VGTSTREAMOUT_FLUSH,
false, // 0x20: SAMPLE_STREAMOUTSTATS,
false, // 0x21: RESET_VTX_CNT,
false, // 0x22: BLOCK_CONTEXT_DONE,
false, // 0x23: CS_CONTEXT_DONE,
false, // 0x24: VGT_FLUSH,
false, // 0x25: TGID_ROLLOVER,
false, // 0x26: SQ_NON_EVENT,
false, // 0x27: SC_SEND_DB_VPZ,
true, // 0x28: BOTTOM_OF_PIPE_TS,
true, // 0x29: FLUSH_SX_TS,
false, // 0x2a: DB_CACHE_FLUSH_AND_INV,
true, // 0x2b: FLUSH_AND_INV_DB_DATA_TS,
false, // 0x2c: FLUSH_AND_INV_DB_META,
true, // 0x2d: FLUSH_AND_INV_CB_DATA_TS,
false, // 0x2e: FLUSH_AND_INV_CB_META,
false, // 0x2f: CS_DONE,
false, // 0x30: PS_DONE,
false, // 0x31: FLUSH_AND_INV_CB_PIXEL_DATA,
false, // 0x32: SX_CB_RAT_ACK_REQUEST,
false, // 0x33: THREAD_TRACE_START,
false, // 0x34: THREAD_TRACE_STOP,
false, // 0x35: THREAD_TRACE_MARKER,
false, // 0x36: THREAD_TRACE_FLUSH,
false, // 0x37: THREAD_TRACE_FINISH,
false, // 0x38: PIXEL_PIPE_STAT_CONTROL,
false, // 0x39: PIXEL_PIPE_STAT_DUMP,
false, // 0x3a: PIXEL_PIPE_STAT_RESET,
false, // 0x3b: CONTEXT_SUSPEND,
false, // 0x3c: OFFCHIP_HS_DEALLOC,
false, // 0x3d: ENABLE_NGG_PIPELINE,
false, // 0x3e: ENABLE_LEGACY_PIPELINE,
false, // 0x3f: Reserved_0x3f,
};
// Lookup table for converting a AtomicOp index into a TC_OP on Gfx9 hardware.
constexpr TC_OP AtomicOpConversionTable[] =
{
TC_OP_ATOMIC_ADD_RTN_32, // AddInt32
TC_OP_ATOMIC_SUB_RTN_32, // SubInt32
TC_OP_ATOMIC_UMIN_RTN_32, // MinUint32
TC_OP_ATOMIC_UMAX_RTN_32, // MaxUint32
TC_OP_ATOMIC_SMIN_RTN_32, // MinSint32
TC_OP_ATOMIC_SMAX_RTN_32, // MaxSing32
TC_OP_ATOMIC_AND_RTN_32, // AndInt32
TC_OP_ATOMIC_OR_RTN_32, // OrInt32
TC_OP_ATOMIC_XOR_RTN_32, // XorInt32
TC_OP_ATOMIC_INC_RTN_32, // IncUint32
TC_OP_ATOMIC_DEC_RTN_32, // DecUint32
TC_OP_ATOMIC_ADD_RTN_64, // AddInt64
TC_OP_ATOMIC_SUB_RTN_64, // SubInt64
TC_OP_ATOMIC_UMIN_RTN_64, // MinUint64
TC_OP_ATOMIC_UMAX_RTN_64, // MaxUint64
TC_OP_ATOMIC_SMIN_RTN_64, // MinSint64
TC_OP_ATOMIC_SMAX_RTN_64, // MaxSint64
TC_OP_ATOMIC_AND_RTN_64, // AndInt64
TC_OP_ATOMIC_OR_RTN_64, // OrInt64
TC_OP_ATOMIC_XOR_RTN_64, // XorInt64
TC_OP_ATOMIC_INC_RTN_64, // IncUint64
TC_OP_ATOMIC_DEC_RTN_64, // DecUint64
};
// Size of the AtomicOp conversion table, in entries.
constexpr size_t AtomicOpConversionTableSize = ArrayLen(AtomicOpConversionTable);
// The AtomicOp table should contain one entry for each AtomicOp.
static_assert((AtomicOpConversionTableSize == static_cast<size_t>(AtomicOp::Count)),
"AtomicOp conversion table has too many/few entries");
constexpr size_t PackedRegPairPacketSize = PM4_PFP_SET_SH_REG_PAIRS_PACKED_SIZEDW__GFX11;
static_assert((PackedRegPairPacketSize == PM4_PFP_SET_SH_REG_PAIRS_PACKED_SIZEDW__GFX11) &&
(PackedRegPairPacketSize == PM4_PFP_SET_CONTEXT_REG_PAIRS_PACKED_SIZEDW__GFX11) &&
(PackedRegPairPacketSize == PM4_PFP_SET_SH_REG_PAIRS_PACKED_N_SIZEDW__GFX11),
"PAIR_PACKED packet sizes do not match!");
// Maximum number of registers that may be written with a fixed length packed register pair packet.
constexpr uint32 MaxNumPackedFixLengthRegs = 8;
// Minimum FW version required to use the expanded fixed length range. Prior FW versions only support up to 8 registers.
constexpr uint32 MinExpandedPackedFixLengthPfpVersion = 1463;
constexpr uint32 MaxNumPackedFixLengthRegsExpanded = 14;
// Minimum number of registers that may be written with a fixed length packed register pair packet.
constexpr uint32 MinNumPackedFixLengthRegs = 2;
// Minimum FW version required to use PERF_COUNTER_WINDOW packet
constexpr uint32 MinPerfCounterWindowPfpVersion = 2240;
constexpr uint32 MinPerfCounterWindowMecVersion = 2290;
// GCR_CNTL bit fields for ACQUIRE_MEM and RELEASE_MEM are slightly different.
union Gfx10AcquireMemGcrCntl
{
struct
{
uint32 gliInv : 2;
uint32 gl1Range : 2;
uint32 glmWb : 1;
uint32 glmInv : 1;
uint32 glkWb : 1;
uint32 glkInv : 1;
uint32 glvInv : 1;
uint32 gl1Inv : 1;
uint32 gl2Us : 1;
uint32 gl2Range : 2;
uint32 gl2Discard : 1;
uint32 gl2Inv : 1;
uint32 gl2Wb : 1;
uint32 seq : 2;
uint32 reserved : 14;
} bits;
uint32 u32All;
};
union Gfx10ReleaseMemGcrCntl
{
struct
{
uint32 glmWb : 1;
uint32 glmInv : 1;
uint32 glvInv : 1;
uint32 gl1Inv : 1;
uint32 gl2Us : 1;
uint32 gl2Range : 2;
uint32 gl2Discard : 1;
uint32 gl2Inv : 1;
uint32 gl2Wb : 1;
uint32 seq : 2;
uint32 gfx11GlkWb : 1;
uint32 reserved : 19;
} bits;
uint32 u32All;
};
// =====================================================================================================================
// Returns a 32-bit quantity that corresponds to a type-3 packet header. "count" is the actual size of the packet in
// terms of DWORDs, including the header.
//
// The shaderType argument doesn't matter (can be left at its default) for all packets except the following:
// - load_sh_reg
// - set_base
// - set_sh_reg
// - set_sh_reg_offset
// - write_gds
PM4_ME_TYPE_3_HEADER CmdUtil::Type3Header(
IT_OpCodeType opCode,
uint32 count,
bool resetFilterCam,
Pm4ShaderType shaderType,
Pm4Predicate predicate)
{
// PFP and ME headers are the same structure... doesn't really matter which one we use.
PM4_ME_TYPE_3_HEADER header = {};
header.predicate = predicate;
header.shaderType = shaderType;
header.type = 3; // type-3 packet
header.opcode = opCode;
header.count = (count - 2);
header.resetFilterCam = resetFilterCam;
return header;
}
// =====================================================================================================================
// Returns a 32-bit quantity that corresponds to a ordinal 2 of packets that are similar to
// typedef struct PM4_PFP_SET_CONTEXT_REG
// {
// union
// {
// PM4_PFP_TYPE_3_HEADER header; ///header
// uint32_t ordinal1;
// };
//
// union
// {
// struct
// {
// uint32_t reg_offset:16;
// uint32_t reserved1:12;
// PFP_SET_CONTEXT_REG_index_enum index:4;
// } bitfields2;
// uint32_t ordinal2;
// };
//
// // uint32_t reg_data[]; // N-DWords
//
// } PM4_PFP_SET_CONTEXT_REG, *PPM4_PFP_SET_CONTEXT_REG;
// This is done with shifts to avoid a read-modify-write of the destination memory.
static uint32 Type3Ordinal2(
uint32 regOffset,
uint32 index)
{
constexpr uint32 IndexShift = 28;
return regOffset |
(index << IndexShift);
}
// =====================================================================================================================
// Note that this constructor is invoked before settings have been committed.
CmdUtil::CmdUtil(
const Device& device)
:
m_flags{}
#if PAL_ENABLE_PRINTS_ASSERTS
, m_verifyShadowedRegisters(false),
m_device(device)
#endif
{
const Pal::Device& parent = *(device.Parent());
memset(&m_registerInfo, 0, sizeof(m_registerInfo));
if (IsGfx10(parent))
{
if (IsGfx101(parent))
{
m_registerInfo.mmDbDfsmControl = Gfx10::mmDB_DFSM_CONTROL;
}
else if (IsGfx103(parent))
{
m_registerInfo.mmDbDfsmControl = Gfx10::mmDB_DFSM_CONTROL;
}
}
}
// =====================================================================================================================
// LateInit to setup any state needed.
void CmdUtil::LateInit(
const Device& device)
{
const auto* pParent = device.Parent();
const GpuChipProperties& chipProps = pParent->ChipProperties();
const Gfx9PalSettings& settings = device.Settings();
const uint32 cpUcodeVer = chipProps.cpUcodeVersion;
m_prefetchClampSize = device.CoreSettings().prefetchClampSize;
constexpr uint32 MinUcodeVerForCsPartialFlushGfx10_1 = 32;
constexpr uint32 MinUcodeVerForCsPartialFlushGfx10_3 = 35;
constexpr uint32 MinUcodeVerForWaIndexBufferZeroSize = 29;
m_flags.isGfx10 = IsGfx10(chipProps.gfxLevel);
m_flags.isGfx101 = IsGfx101(chipProps.gfxLevel);
m_flags.isGfx103 = IsGfx103(chipProps.gfxLevel);
m_flags.isGfx103CorePlus = IsGfx103CorePlus(chipProps.gfxLevel);
m_flags.isGfx11 = IsGfx11(chipProps.gfxLevel);
m_flags.supportsSwStrmout = chipProps.gfxip.supportsSwStrmout;
m_flags.gfx11EnableReleaseMemWaitCpDma = settings.gfx11EnableReleaseMemWaitCpDma;
m_flags.waReplaceEventsWithTsEvents = settings.waReplaceEventsWithTsEvents;
m_flags.gfx11EnableZpassPacketOptimization = settings.gfx11EnableZpassPacketOptimization;
m_flags.disableAceCsPartialFlush = settings.disableAceCsPartialFlush;
m_flags.waIndexBufferZeroSize = settings.waIndexBufferZeroSize &&
(cpUcodeVer >= MinUcodeVerForWaIndexBufferZeroSize);
m_flags.usePwsLateAcquirePoint = pParent->UsePwsLateAcquirePoint(EngineTypeUniversal);
m_flags.supportsExpandedPackedRegPairs = (chipProps.pfpUcodeVersion >= MinExpandedPackedFixLengthPfpVersion);
m_flags.pfpSupportsPerfCounterWindow = (chipProps.pfpUcodeVersion >= MinPerfCounterWindowPfpVersion);
m_flags.mecSupportsPerfCounterWindow = (chipProps.mecUcodeVersion >= MinPerfCounterWindowMecVersion);
m_flags.uCodeSupportsLoadShRegIndexIndAddr = (cpUcodeVer >= Gfx103UcodeVersionLoadShRegIndexIndirectAddr);
m_flags.uCodeSupportsCsPartialFlush10_1 = (cpUcodeVer >= MinUcodeVerForCsPartialFlushGfx10_1);
m_flags.uCodeSupportsCsPartialFlush10_3 = (cpUcodeVer >= MinUcodeVerForCsPartialFlushGfx10_3);
#if PAL_ENABLE_PRINTS_ASSERTS
m_verifyShadowedRegisters = pParent->Settings().cmdUtilVerifyShadowedRegRanges;
#endif
}
// =====================================================================================================================
// Returns if we can use CS_PARTIAL_FLUSH events on the given engine.
bool CmdUtil::CanUseCsPartialFlush(
EngineType engineType
) const
{
// There is a CP ucode bug which causes CS_PARTIAL_FLUSH to return early if compute wave save restore (CWSR) is
// enabled. CWSR was added in gfx8 and the bug was undetected for a few generations. The bug has been fixed in
// certain versions of the gfx9+ CP ucode. Thus, in the long term we can enable cspf for all ASICs on the gfx9
// HWL but we still need a fallback if someone runs with old CP ucode.
bool useCspf = true;
// We will only try to disable cspf if this is an async compute engine on an ASIC that at some point had the bug.
if ((Pal::Device::EngineSupportsGraphics(engineType) == false) && m_flags.isGfx10)
{
if (m_flags.disableAceCsPartialFlush)
{
// Always disable ACE support if someone set the debug setting.
useCspf = false;
}
else if (m_flags.isGfx101)
{
useCspf = m_flags.uCodeSupportsCsPartialFlush10_1;
}
else if (m_flags.isGfx103)
{
useCspf = m_flags.uCodeSupportsCsPartialFlush10_3;
}
else
{
// Otherwise, assume the bug exists and wasn't fixed.
useCspf = false;
}
}
return useCspf;
}
// =====================================================================================================================
// If we have support for the indirect_addr index and compute engines.
bool CmdUtil::HasEnhancedLoadShRegIndex() const
{
bool hasEnhancedLoadShRegIndex = false;
if (m_flags.isGfx11)
{
// This function should return true for Gfx11 by default.
hasEnhancedLoadShRegIndex = true;
}
else
{
// This was only implemented on gfx10.3+.
hasEnhancedLoadShRegIndex = (m_flags.uCodeSupportsLoadShRegIndexIndAddr && m_flags.isGfx103CorePlus);
}
return hasEnhancedLoadShRegIndex;
}
// =====================================================================================================================
// Returns the number of dwords required to chain two pm4 packet chunks together.
uint32 CmdUtil::ChainSizeInDwords(
EngineType engineType)
{
uint32 size = 0;
// The packet used for chaining indirect-buffers together differs based on the queue we're executing on.
if (Pal::Device::EngineSupportsGraphics(engineType))
{
size = PM4_PFP_INDIRECT_BUFFER_SIZEDW__CORE;
}
else if (engineType == EngineTypeCompute)
{
size = PM4_MEC_INDIRECT_BUFFER_SIZEDW__CORE;
}
else
{
// Other engine types do not support chaining.
}
return size;
}
// =====================================================================================================================
// True if the specified register is in context reg space, false otherwise.
bool CmdUtil::IsContextReg(
uint32 regAddr)
{
const bool isContextReg = ((regAddr >= CONTEXT_SPACE_START) && (regAddr <= Gfx10::CONTEXT_SPACE_END));
// Assert if we need to extend our internal range of context registers we actually set.
PAL_DEBUG_BUILD_ONLY_ASSERT((isContextReg == false) || ((regAddr - CONTEXT_SPACE_START) < CntxRegUsedRangeSize));
return isContextReg;
}
// =====================================================================================================================
// True if the specified register is in user-config reg space, false otherwise.
bool CmdUtil::IsUserConfigReg(
uint32 regAddr)
{
return ((regAddr >= UCONFIG_SPACE_START) && (regAddr <= UCONFIG_SPACE_END));
}
// =====================================================================================================================
// True if the specified register is in persistent data space, false otherwise.
bool CmdUtil::IsShReg(
uint32 regAddr)
{
const bool isShReg = ((regAddr >= PERSISTENT_SPACE_START) && (regAddr <= PERSISTENT_SPACE_END));
// Assert if we need to extend our internal range of SH registers we actually set.
PAL_ASSERT((isShReg == false) || ((regAddr - PERSISTENT_SPACE_START) < ShRegUsedRangeSize));
return isShReg;
}
// =====================================================================================================================
// If AcquireMem packet supports flush or invalidate requested RB cache sync flags.
bool CmdUtil::CanUseAcquireMem(
SyncRbFlags rbSync
) const
{
bool canUse = true;
// Can't flush or invalidate CB metadata using an ACQUIRE_MEM as not supported. Additionally GFX11 doesn't support
// phase-II RB cache flush.
if (TestAnyFlagSet(rbSync, SyncCbMetaWbInv) || (m_flags.isGfx11 && (rbSync != 0)))
{
canUse = false;
}
return canUse;
}
// =====================================================================================================================
size_t CmdUtil::BuildAcquireMemGeneric(
const AcquireMemGeneric& info,
void* pBuffer
) const
{
return BuildAcquireMemInternal(info, info.engineType, {}, pBuffer);
}
// =====================================================================================================================
size_t CmdUtil::BuildAcquireMemGfxSurfSync(
const AcquireMemGfxSurfSync& info,
void* pBuffer
) const
{
return BuildAcquireMemInternal(info, EngineTypeUniversal, info.flags, pBuffer);
}
// Mask of CP_ME_COHER_CNTL bits which stall based on all CB base addresses.
static constexpr uint32 CpMeCoherCntlStallCb = CP_ME_COHER_CNTL__CB0_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB1_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB2_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB3_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB4_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB5_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB6_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB7_DEST_BASE_ENA_MASK;
// Mask of CP_ME_COHER_CNTL bits which stall based on all DB base addresses (depth and stencil).
static constexpr uint32 CpMeCoherCntlStallDb = CP_ME_COHER_CNTL__DB_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__DEST_BASE_0_ENA_MASK;
// Mask of CP_ME_COHER_CNTL bits which stall based on all base addresses. (CB + DB + unused)
static constexpr uint32 CpMeCoherCntlStallAll = CP_ME_COHER_CNTL__CB0_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB1_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB2_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB3_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB4_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB5_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB6_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__CB7_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__DB_DEST_BASE_ENA_MASK |
CP_ME_COHER_CNTL__DEST_BASE_0_ENA_MASK |
CP_ME_COHER_CNTL__DEST_BASE_1_ENA_MASK |
CP_ME_COHER_CNTL__DEST_BASE_2_ENA_MASK |
CP_ME_COHER_CNTL__DEST_BASE_3_ENA_MASK;
// =====================================================================================================================
// A helper heuristic used to program the "range" fields in acquire_mem packets.
static bool UseRangeBasedGcr(
gpusize base,
gpusize size)
{
// The L1 / L2 caches are physical address based. When specifying the range, the GCR will perform virtual address
// to physical address translation before the wb / inv. If the acquired op is full sync, we must ignore the range,
// otherwise page fault may occur because page table cannot cover full range virtual address.
// When the source address is virtual , the GCR block will have to perform the virtual address to physical
// address translation before the wb / inv. Since the pages in memory are a collection of fragments, you can't
// specify the full range without walking into a page that has no PTE triggering a fault. In the cases where
// the driver wants to wb / inv the entire cache, you should not use range based method, and instead flush the
// entire cache without it. The range based method is not meant to be used this way, it is for selective page
// invalidation.
//
// So that's a good reason to return false if the base or size are the special "full" values. It's also a good idea
// to disable range-based GCRs if the sync range is too big, as walking a large VA range has a large perf cost.
return ((base != 0) && (size != 0) && (size <= CmdUtil::Gfx10AcquireMemGl1Gl2RangedCheckMaxSurfaceSizeBytes));
}
static_assert(PM4_MEC_ACQUIRE_MEM_SIZEDW__CORE == PM4_ME_ACQUIRE_MEM_SIZEDW__CORE,
"GFX10: ACQUIRE_MEM packet size is different between ME compute and ME graphics!");
// =====================================================================================================================
size_t CmdUtil::BuildAcquireMemInternal(
const AcquireMemCore& info,
EngineType engineType,
SurfSyncFlags surfSyncFlags,
void* pBuffer
) const
{
// The surf sync dest_base stalling feature is only supported on graphics engines. ACE acquires are immediate.
// The RB caches can only be flushed and invalidated on graphics queues as well. This assert should never fire
// because the public functions that call this function hard code their arguments such that it will never be false.
PAL_ASSERT(Pal::Device::EngineSupportsGraphics(engineType) || (surfSyncFlags.u8All == 0));
// These are such long names... some temps will help.
const bool cbDataWbInv = surfSyncFlags.gfx10CbDataWbInv != 0;
const bool dbWbInv = surfSyncFlags.gfx10DbWbInv != 0;
// Gfx11 removed support for flushing and invalidating RB caches in an acquire_mem.
PAL_ASSERT((m_flags.isGfx11 == false) || ((cbDataWbInv == false) && (dbWbInv == false)));
constexpr uint32 PacketSize = PM4_ME_ACQUIRE_MEM_SIZEDW__CORE;
PM4_ME_ACQUIRE_MEM packet = {};
packet.ordinal1.header = Type3Header(IT_ACQUIRE_MEM, PacketSize);
// The DEST_BASE bits in CP_ME_COHER_CNTL control the surf sync context stalling feature.
const bool cbStall = surfSyncFlags.cbTargetStall != 0;
const bool dbStall = surfSyncFlags.dbTargetStall != 0;
const uint32 cpMeCoherCntl = (cbStall && dbStall) ? CpMeCoherCntlStallAll :
cbStall ? CpMeCoherCntlStallCb :
dbStall ? CpMeCoherCntlStallDb : 0;
// Note that the other ACTION_ENA flags are not used on gfx10+, they go in the gcr_cntl instead.
regCP_COHER_CNTL cpCoherCntl = {};
cpCoherCntl.bits.CB_ACTION_ENA = cbDataWbInv;
cpCoherCntl.bits.DB_ACTION_ENA = dbWbInv;
// Both COHER_CNTL registers get combined into our packet's coher_cntl field.
packet.ordinal2.bitfieldsA.coher_cntl = cpCoherCntl.u32All | cpMeCoherCntl;
// Note that this field isn't used on ACE.
if (Pal::Device::EngineSupportsGraphics(engineType))
{
packet.ordinal2.bitfieldsA.engine_sel = (surfSyncFlags.pfpWait != 0)
? ME_ACQUIRE_MEM_engine_sel_enum(engine_sel__pfp_acquire_mem__prefetch_parser)
: ME_ACQUIRE_MEM_engine_sel_enum(engine_sel__me_acquire_mem__micro_engine);
}
// The coher base and size are in units of 256 bytes. Rather than require the caller to align them to 256 bytes we
// just expand the base and size to the next 256-byte multiple if they're not already aligned.
//
// Note that we're required to set every bit in base to '0' and every bit in size to '1' for a full range acquire.
// AcquireMemCore requires the caller to use base = 0 and size = 0 for a full range acquire so the math just works
// for coher_base, but coher_size requires us to substitute a special constant.
const gpusize coherBase = Pow2AlignDown(info.rangeBase, 256);
const gpusize padSize = info.rangeSize + info.rangeBase % 256;
const gpusize coherSize = (info.rangeSize == 0) ? Pow2AlignDown(UINT64_MAX, 256) : Pow2Align(padSize, 256);
packet.ordinal3.coher_size = Get256BAddrLo(coherSize);
if (m_flags.isGfx11)
{
packet.ordinal4.bitfieldsA.gfx11.coher_size_hi = Get256BAddrHi(coherSize);
}
else
{
packet.ordinal4.bitfieldsA.gfx10.coher_size_hi = Get256BAddrHi(coherSize);
}
packet.ordinal5.coher_base_lo = Get256BAddrLo(coherBase);
packet.ordinal6.bitfieldsA.coher_base_hi = Get256BAddrHi(coherBase);
packet.ordinal7.bitfieldsA.poll_interval = Pal::Device::PollInterval;
if (info.cacheSync != 0)
{
// Note that glmWb is unimplemented in HW so we don't bother setting it. Everything else we want zeroed.
//
// We always prefer parallel cache ops but must force sequential (L0->L1->L2) mode when we're writing back a
// non-write-through L0 before an L2 writeback.
Gfx10AcquireMemGcrCntl cntl = {};
cntl.bits.gliInv = TestAnyFlagSet(info.cacheSync, SyncGliInv);
cntl.bits.glmInv = TestAnyFlagSet(info.cacheSync, SyncGlmInv);
cntl.bits.glkWb = TestAnyFlagSet(info.cacheSync, SyncGlkWb);
cntl.bits.glkInv = TestAnyFlagSet(info.cacheSync, SyncGlkInv);
cntl.bits.glvInv = TestAnyFlagSet(info.cacheSync, SyncGlvInv);
cntl.bits.gl1Inv = TestAnyFlagSet(info.cacheSync, SyncGl1Inv);
cntl.bits.gl2Inv = TestAnyFlagSet(info.cacheSync, SyncGl2Inv);
cntl.bits.gl2Wb = TestAnyFlagSet(info.cacheSync, SyncGl2Wb);
cntl.bits.seq = cntl.bits.gl2Wb & cntl.bits.glkWb;
// We default to whole-cache operations unless this heuristic says we should do a range-based GCR.
if (UseRangeBasedGcr(info.rangeBase, info.rangeSize))
{
cntl.bits.gl1Range = 2;
cntl.bits.gl2Range = 2;
}
packet.ordinal8.bitfields.gcr_cntl = cntl.u32All;
}
memcpy(pBuffer, &packet, PacketSize * sizeof(uint32));
return PacketSize;
}
// =====================================================================================================================
size_t CmdUtil::BuildAcquireMemGfxPws(
const AcquireMemGfxPws& info,
void* pBuffer
) const
{
// PWS isn't going to work on pre-gfx11 hardware.
PAL_ASSERT(m_flags.isGfx11);
// There are a couple of cases where we need to modify the caller's stage select before applying it.
ME_ACQUIRE_MEM_pws_stage_sel_enum stageSel = info.stageSel;
// We need to wait at one of the CP stages if we want it to do a GCR after waiting. Rather than force the caller
// to get this right we just silently handle it. It can't cause any correctness issues, it's just a perf hit.
if ((info.cacheSync != 0) &&
(stageSel != pws_stage_sel__me_acquire_mem__cp_me__GFX11) &&
(stageSel != pws_stage_sel__me_acquire_mem__cp_pfp__GFX11))
{
stageSel = pws_stage_sel__me_acquire_mem__cp_me__GFX11;
}
constexpr uint32 PacketSize = PM4_ME_ACQUIRE_MEM_SIZEDW__CORE;
PM4_ME_ACQUIRE_MEM packet = {};
packet.ordinal1.header = Type3Header(IT_ACQUIRE_MEM, PacketSize);
packet.ordinal2.bitfieldsB.gfx11.pws_stage_sel = stageSel;
packet.ordinal2.bitfieldsB.gfx11.pws_counter_sel = info.counterSel;
packet.ordinal2.bitfieldsB.gfx11.pws_ena2 = pws_ena2__me_acquire_mem__pixel_wait_sync_enable__GFX11;
packet.ordinal2.bitfieldsB.gfx11.pws_count = info.syncCount;
// The GCR base and size are in units of 128 bytes. Rather than require the caller to align them to 128 bytes we
// just expand the base and size to the next 128-byte multiple if they're not already aligned.
//
// Note that we're required to set every bit in base to '0' and every bit in size to '1' for a full range acquire.
// AcquireMemCore requires the caller to use base = 0 and size = 0 for a full range acquire so the math just works
// for gcr_base, but gcr_size requires us to substitute a special constant.
const gpusize gcrBase = Pow2AlignDown(info.rangeBase, 128);
const gpusize padSize = info.rangeSize + info.rangeBase % 128;
const gpusize gcrSize = (info.rangeSize == 0) ? Pow2AlignDown(UINT64_MAX, 128) : Pow2Align(padSize, 128);
packet.ordinal3.gcr_size = Get128BAddrLo(gcrSize);
packet.ordinal4.bitfieldsB.gfx11.gcr_size_hi = Get128BAddrHi(gcrSize);
packet.ordinal5.gcr_base_lo = Get128BAddrLo(gcrBase);
packet.ordinal6.bitfieldsB.gfx11.gcr_base_hi = Get128BAddrHi(gcrBase);
packet.ordinal7.bitfieldsB.gfx11.pws_ena = pws_ena__me_acquire_mem__pixel_wait_sync_enable__GFX11;
if (info.cacheSync != 0)
{
// Note that glmWb is unimplemented in HW so we don't bother setting it. Everything else we want zeroed.
//
// We always prefer parallel cache ops but must force sequential (L0->L1->L2) mode when we're writing back a
// non-write-through L0 before an L2 writeback. The only writable L0 that a PWS acquire can flush is the K$.
Gfx10AcquireMemGcrCntl cntl = {};
cntl.bits.gliInv = TestAnyFlagSet(info.cacheSync, SyncGliInv);
cntl.bits.glmInv = TestAnyFlagSet(info.cacheSync, SyncGlmInv);
cntl.bits.glkWb = TestAnyFlagSet(info.cacheSync, SyncGlkWb);
cntl.bits.glkInv = TestAnyFlagSet(info.cacheSync, SyncGlkInv);
cntl.bits.glvInv = TestAnyFlagSet(info.cacheSync, SyncGlvInv);
cntl.bits.gl1Inv = TestAnyFlagSet(info.cacheSync, SyncGl1Inv);
cntl.bits.gl2Inv = TestAnyFlagSet(info.cacheSync, SyncGl2Inv);
cntl.bits.gl2Wb = TestAnyFlagSet(info.cacheSync, SyncGl2Wb);
cntl.bits.seq = cntl.bits.gl2Wb & cntl.bits.glkWb;
// We default to whole-cache operations unless this heuristic says we should do a range-based GCR.
if (UseRangeBasedGcr(info.rangeBase, info.rangeSize))
{
cntl.bits.gl1Range = 2;
cntl.bits.gl2Range = 2;
}
packet.ordinal8.bitfields.gcr_cntl = cntl.u32All;
}
memcpy(pBuffer, &packet, PacketSize * sizeof(uint32));
return PacketSize;
}
// =====================================================================================================================
// True if the specified atomic operation acts on 32-bit values.
static bool Is32BitAtomicOp(
AtomicOp atomicOp)
{
// AddInt64 is the first 64-bit operation.
return (static_cast<int32>(atomicOp) < static_cast<int32>(AtomicOp::AddInt64));
}
// =====================================================================================================================
// Builds an ATOMIC_MEM packet. The caller should make sure that atomicOp is valid. This method assumes that pPacket has
// been initialized to zeros. Returns the size of the PM4 command assembled, in DWORDs.
size_t CmdUtil::BuildAtomicMem(
AtomicOp atomicOp,
gpusize dstMemAddr,
uint64 srcData, // Constant operand for the atomic operation.
void* pBuffer) // [out] Build the PM4 packet in this buffer.
{
static_assert(PM4_ME_ATOMIC_MEM_SIZEDW__CORE == PM4_MEC_ATOMIC_MEM_SIZEDW__CORE,
"Atomic Mem packets don't match between ME and MEC!");
static_assert(((static_cast<uint32>(command__me_atomic_mem__single_pass_atomic) ==
static_cast<uint32>(command__mec_atomic_mem__single_pass_atomic)) &&
(static_cast<uint32>(command__me_atomic_mem__loop_until_compare_satisfied) ==
static_cast<uint32>(command__mec_atomic_mem__loop_until_compare_satisfied))),
"Atomic Mem command enum is different between ME and MEC!");
static_assert(((static_cast<uint32>(cache_policy__me_atomic_mem__lru) ==
static_cast<uint32>(cache_policy__mec_atomic_mem__lru)) &&
(static_cast<uint32>(cache_policy__me_atomic_mem__stream) ==
static_cast<uint32>(cache_policy__mec_atomic_mem__stream))),
"Atomic Mem cache policy enum is different between ME and MEC!");
static_assert(((static_cast<uint32>(cache_policy__me_atomic_mem__noa) ==
static_cast<uint32>(cache_policy__mec_atomic_mem__noa)) &&
(static_cast<uint32>(cache_policy__me_atomic_mem__bypass) ==
static_cast<uint32>(cache_policy__mec_atomic_mem__bypass))),
"Atomic Mem cache policy enum is different between ME and MEC!");
// The destination address must be aligned to the size of the operands.
PAL_ASSERT((dstMemAddr != 0) && IsPow2Aligned(dstMemAddr, (Is32BitAtomicOp(atomicOp) ? 4 : 8)));
constexpr uint32 PacketSize = PM4_ME_ATOMIC_MEM_SIZEDW__CORE;
PM4_ME_ATOMIC_MEM packet = {};
packet.ordinal1.header = Type3Header(IT_ATOMIC_MEM, PacketSize);
packet.ordinal2.bitfields.atomic = AtomicOpConversionTable[static_cast<uint32>(atomicOp)];
packet.ordinal2.bitfields.command = command__me_atomic_mem__single_pass_atomic;
packet.ordinal2.bitfields.cache_policy = cache_policy__me_atomic_mem__lru;
packet.ordinal3.addr_lo = LowPart(dstMemAddr);
packet.ordinal4.addr_hi = HighPart(dstMemAddr);
packet.ordinal5.src_data_lo = LowPart(srcData);
packet.ordinal6.src_data_hi = HighPart(srcData);
static_assert(PacketSize * sizeof(uint32) == sizeof(packet), "");
memcpy(pBuffer, &packet, sizeof(packet));
return PacketSize;
}
// =====================================================================================================================
// Builds a PM4 packet which issues a clear state command. Returns the size of the PM4 command assembled, in DWORDs.
size_t CmdUtil::BuildClearState(
PFP_CLEAR_STATE_cmd_enum command,
void* pBuffer) // [out] Build the PM4 packet in this buffer.
{
static_assert(PM4_PFP_CLEAR_STATE_SIZEDW__CORE == PM4_ME_CLEAR_STATE_SIZEDW__CORE,
"Clear state packets don't match between PFP and ME!");
constexpr uint32 PacketSize = PM4_PFP_CLEAR_STATE_SIZEDW__CORE;
PM4_PFP_CLEAR_STATE packet = {};
packet.ordinal1.header.u32All = (Type3Header(IT_CLEAR_STATE, PacketSize)).u32All;
packet.ordinal2.bitfields.cmd = command;
static_assert(PacketSize * sizeof(uint32) == sizeof(packet), "");
memcpy(pBuffer, &packet, sizeof(packet));
return PacketSize;
}
// =====================================================================================================================
// Generates a basic "COND_EXEC" packet. Returns the size, in DWORDs, of the generated packet.
size_t CmdUtil::BuildCondExec(
gpusize gpuVirtAddr,
uint32 sizeInDwords,
void* pBuffer)
{
static_assert(PM4_PFP_COND_EXEC_SIZEDW__CORE == PM4_MEC_COND_EXEC_SIZEDW__CORE,
"Conditional execute packets don't match between GFX and compute!");
constexpr uint32 PacketSize = PM4_MEC_COND_EXEC_SIZEDW__CORE;
PM4_MEC_COND_EXEC packet = {};
packet.ordinal1.header.u32All = (Type3Header(IT_COND_EXEC, PacketSize)).u32All;
packet.ordinal2.u32All = LowPart(gpuVirtAddr);
PAL_ASSERT(packet.ordinal2.bitfields.reserved1 == 0);
packet.ordinal3.addr_hi = HighPart(gpuVirtAddr);
packet.ordinal5.bitfields.exec_count = sizeInDwords;
static_assert(PacketSize * sizeof(uint32) == sizeof(packet), "");
memcpy(pBuffer, &packet, sizeof(packet));
return PacketSize;
}
// =====================================================================================================================
// Generates a basic "COND_INDIRECT_BUFFER" packet. The branch locations must be filled in later. Returns the
// size, in DWORDs, of the generated packet.
size_t CmdUtil::BuildCondIndirectBuffer(
CompareFunc compareFunc,
gpusize compareGpuAddr,
uint64 data,
uint64 mask,
void* pBuffer)
{
static_assert(PM4_PFP_COND_INDIRECT_BUFFER_SIZEDW__CORE == PM4_MEC_COND_INDIRECT_BUFFER_SIZEDW__CORE,
"Conditional indirect buffer packets don't match between GFX and compute!");
// The CP doesn't implement a "never" compare function. It is the caller's responsibility to detect
// this case and work around it. The "funcTranslation" table defines an entry for "never" only to
// make indexing into it easy.
PAL_ASSERT(compareFunc != CompareFunc::Never);
constexpr static PFP_COND_INDIRECT_BUFFER_function_enum FuncTranslation[]=
{
function__pfp_cond_indirect_buffer__always_pass, // Never
function__pfp_cond_indirect_buffer__less_than_ref_value, // Less
function__pfp_cond_indirect_buffer__equal_to_the_reference_value, // Equal
function__pfp_cond_indirect_buffer__less_than_equal_to_the_ref_value, // LessEqual
function__pfp_cond_indirect_buffer__greater_than_reference_value, // Greater
function__pfp_cond_indirect_buffer__not_equal_reference_value, // NotEqual
function__pfp_cond_indirect_buffer__greater_than_or_equal_reference_value, // GreaterEqual
function__pfp_cond_indirect_buffer__always_pass // _Always
};
constexpr uint32 PacketSize = PM4_PFP_COND_INDIRECT_BUFFER_SIZEDW__CORE;
PM4_PFP_COND_INDIRECT_BUFFER packet = {};
packet.ordinal1.header.u32All = (Type3Header(IT_INDIRECT_BUFFER, PacketSize)).u32All;
packet.ordinal2.bitfields.function = FuncTranslation[static_cast<uint32>(compareFunc)];
// We always implement both a "then" and an "else" clause
packet.ordinal2.bitfields.mode = mode__pfp_cond_indirect_buffer__if_then_else;
// Make sure our comparison address is aligned properly.
// Note that the packet definition makes it seem like 8 byte alignment is required, but only 4 is actually
// necessary.
PAL_ASSERT(IsPow2Aligned(compareGpuAddr, 4));
packet.ordinal3.u32All = LowPart(compareGpuAddr);
packet.ordinal4.compare_addr_hi = HighPart(compareGpuAddr);
packet.ordinal5.mask_lo = LowPart(mask);
packet.ordinal6.mask_hi = HighPart(mask);
packet.ordinal7.reference_lo = LowPart(data);
packet.ordinal8.reference_hi = HighPart(data);
static_assert(PacketSize * sizeof(uint32) == sizeof(packet), "");
memcpy(pBuffer, &packet, sizeof(packet));
// Size and locations of the IB are not yet known, will be patched later.
return PacketSize;
}
// =====================================================================================================================
// Builds a CONTEXT_CONTROL packet with both load and shadowing disabled. Returns the size, in DWORDs, of the
// generated packet.
size_t CmdUtil::BuildContextControl(
const PM4_PFP_CONTEXT_CONTROL& contextControl,
void* pBuffer) // [out] Build the PM4 packet in this buffer.
{
static_assert(PM4_PFP_CONTEXT_CONTROL_SIZEDW__CORE == PM4_ME_CONTEXT_CONTROL_SIZEDW__CORE,
"Context control packet doesn't match between PFP and ME!");
constexpr uint32 PacketSize = PM4_PFP_CONTEXT_CONTROL_SIZEDW__CORE;
auto*const pPacket = static_cast<PM4_PFP_CONTEXT_CONTROL*>(pBuffer);
pPacket->ordinal1.header.u32All = (Type3Header(IT_CONTEXT_CONTROL, PacketSize)).u32All;
pPacket->ordinal2.u32All = contextControl.ordinal2.u32All;
pPacket->ordinal3.u32All = contextControl.ordinal3.u32All;
return PacketSize;
}
// =====================================================================================================================
// Builds a COPY_DATA packet for the compute/ graphics engine. Returns the size, in DWORDs, of the assembled PM4 command
size_t CmdUtil::BuildCopyData(
EngineType engineType,
uint32 engineSel, // Ignored on async compute
uint32 dstSel,
gpusize dstAddr, // Dest addr of the copy, see dstSel for exact meaning
uint32 srcSel,
gpusize srcAddr, // Source address (or value) of the copy, see srcSel for exact meaning
uint32 countSel,
uint32 wrConfirm,
void* pBuffer // [out] Build the PM4 packet in this buffer.
) const
{
static_assert(PM4_ME_COPY_DATA_SIZEDW__CORE == PM4_MEC_COPY_DATA_SIZEDW__CORE,
"CopyData packet size is different between ME and MEC!");
static_assert(((static_cast<uint32>(src_sel__mec_copy_data__mem_mapped_register) ==
static_cast<uint32>(src_sel__me_copy_data__mem_mapped_register)) &&
(static_cast<uint32>(src_sel__mec_copy_data__tc_l2) ==
static_cast<uint32>(src_sel__me_copy_data__tc_l2)) &&
(static_cast<uint32>(src_sel__mec_copy_data__gds) ==
static_cast<uint32>(src_sel__me_copy_data__gds)) &&