Skip to content

Commit 184c0f0

Browse files
authored
Add partially mapped resources tests (#476)
This PR is the first step in bringing support to testing with partially mapped and unmapped resources. It lays out the infrastructure necessary to initialize resources with only a certain number of tiles mapped. The Load overload that takes the 2nd status argument is implemented in DXC, so we take advantage of that and test that when accessing data in an unmapped resource, we get 0'd data and the status integer is also 0. The test is currently unsupported in Clang and Vulkan, but will be supported in the future. Fixes #182
1 parent 8167a5b commit 184c0f0

File tree

5 files changed

+251
-28
lines changed

5 files changed

+251
-28
lines changed

include/Support/Pipeline.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ struct Resource {
143143
std::optional<VulkanBinding> VKBinding;
144144
Buffer *BufferPtr = nullptr;
145145
bool HasCounter;
146+
std::optional<uint32_t> TilesMapped;
146147

147148
bool isRaw() const {
148149
switch (Kind) {

lib/API/DX/Device.cpp

Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,14 @@ static D3D12_RESOURCE_DESC getResourceDescription(const Resource &R) {
149149
const uint32_t Width =
150150
R.isTexture() ? B.OutputProps.Width : getUAVBufferSize(R);
151151
const uint32_t Height = R.isTexture() ? B.OutputProps.Height : 1;
152-
const D3D12_TEXTURE_LAYOUT Layout = R.isTexture()
153-
? D3D12_TEXTURE_LAYOUT_UNKNOWN
154-
: D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
152+
D3D12_TEXTURE_LAYOUT Layout;
153+
if (R.isTexture())
154+
Layout = getDXKind(R.Kind) == SRV
155+
? D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE
156+
: D3D12_TEXTURE_LAYOUT_UNKNOWN;
157+
else
158+
Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
159+
155160
const D3D12_RESOURCE_FLAGS Flags =
156161
R.isReadWrite() ? D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
157162
: D3D12_RESOURCE_FLAG_NONE;
@@ -244,9 +249,11 @@ class DXDevice : public offloadtest::Device {
244249
ComPtr<ID3D12Resource> Upload;
245250
ComPtr<ID3D12Resource> Buffer;
246251
ComPtr<ID3D12Resource> Readback;
252+
ComPtr<ID3D12Heap> Heap;
247253
ResourceSet(ComPtr<ID3D12Resource> Upload, ComPtr<ID3D12Resource> Buffer,
248-
ComPtr<ID3D12Resource> Readback)
249-
: Upload(Upload), Buffer(Buffer), Readback(Readback) {}
254+
ComPtr<ID3D12Resource> Readback,
255+
ComPtr<ID3D12Heap> Heap = nullptr)
256+
: Upload(Upload), Buffer(Buffer), Readback(Readback), Heap(Heap) {}
250257
};
251258

252259
// ResourceBundle will contain one ResourceSet for a singular resource
@@ -521,51 +528,107 @@ class DXDevice : public offloadtest::Device {
521528
addUploadEndBarrier(IS, Destination, R.isReadWrite());
522529
}
523530

531+
UINT getNumTiles(std::optional<uint32_t> NumTiles, uint32_t Width) {
532+
UINT Ret;
533+
if (NumTiles.has_value())
534+
Ret = static_cast<UINT>(*NumTiles);
535+
else {
536+
// Map the entire buffer by computing how many 64KB tiles cover it
537+
Ret = static_cast<UINT>(
538+
(Width + D3D12_TILED_RESOURCE_TILE_SIZE_IN_BYTES - 1) /
539+
D3D12_TILED_RESOURCE_TILE_SIZE_IN_BYTES);
540+
// check for overflow
541+
assert(Width < std::numeric_limits<UINT>::max() -
542+
D3D12_TILED_RESOURCE_TILE_SIZE_IN_BYTES - 1);
543+
}
544+
return Ret;
545+
}
546+
524547
llvm::Expected<ResourceBundle> createSRV(Resource &R, InvocationState &IS) {
525548
ResourceBundle Bundle;
526-
527-
const D3D12_HEAP_PROPERTIES HeapProp =
528-
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
529549
const D3D12_RESOURCE_DESC ResDesc = getResourceDescription(R);
530-
const D3D12_HEAP_PROPERTIES UploadHeapProp =
531-
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
532550
const D3D12_RESOURCE_DESC UploadResDesc =
533551
CD3DX12_RESOURCE_DESC::Buffer(R.size());
552+
const D3D12_HEAP_PROPERTIES UploadHeapProps =
553+
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
534554

535555
uint32_t RegOffset = 0;
556+
536557
for (const auto &ResData : R.BufferPtr->Data) {
537558
llvm::outs() << "Creating SRV: { Size = " << R.size() << ", Register = t"
538559
<< R.DXBinding.Register + RegOffset
539-
<< ", Space = " << R.DXBinding.Space << " }\n";
560+
<< ", Space = " << R.DXBinding.Space;
561+
562+
if (R.TilesMapped)
563+
llvm::outs() << ", TilesMapped = " << *R.TilesMapped;
564+
llvm::outs() << " }\n";
540565

541566
ComPtr<ID3D12Resource> Buffer;
542-
if (auto Err = HR::toError(
543-
Device->CreateCommittedResource(
544-
&HeapProp, D3D12_HEAP_FLAG_NONE, &ResDesc,
545-
D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&Buffer)),
546-
"Failed to create committed resource (buffer)."))
567+
if (auto Err =
568+
HR::toError(Device->CreateReservedResource(
569+
&ResDesc, D3D12_RESOURCE_STATE_COMMON, nullptr,
570+
IID_PPV_ARGS(&Buffer)),
571+
"Failed to create reserved resource (buffer)."))
547572
return Err;
548573

574+
// Committed upload buffer
549575
ComPtr<ID3D12Resource> UploadBuffer;
550576
if (auto Err = HR::toError(
551577
Device->CreateCommittedResource(
552-
&UploadHeapProp, D3D12_HEAP_FLAG_NONE, &UploadResDesc,
578+
&UploadHeapProps, D3D12_HEAP_FLAG_NONE, &UploadResDesc,
553579
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
554580
IID_PPV_ARGS(&UploadBuffer)),
555581
"Failed to create committed resource (upload buffer)."))
556582
return Err;
557583

558-
// Initialize the SRV data
584+
// Tile mapping setup (only skipped when TilesMapped is set to 0)
585+
const UINT NumTiles = getNumTiles(R.TilesMapped, ResDesc.Width);
586+
ComPtr<ID3D12Heap> Heap; // optional, only created if NumTiles > 0
587+
588+
if (NumTiles > 0) {
589+
// Create a Heap large enough for the mapped tiles
590+
D3D12_HEAP_DESC HeapDesc = {};
591+
HeapDesc.Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
592+
HeapDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
593+
HeapDesc.SizeInBytes = static_cast<UINT64>(NumTiles) *
594+
D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
595+
HeapDesc.Flags = D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES;
596+
597+
if (auto Err =
598+
HR::toError(Device->CreateHeap(&HeapDesc, IID_PPV_ARGS(&Heap)),
599+
"Failed to create heap for tiled SRV resource."))
600+
return Err;
601+
602+
// Define one contiguous mapping region
603+
const D3D12_TILED_RESOURCE_COORDINATE StartCoord = {0, 0, 0, 0};
604+
D3D12_TILE_REGION_SIZE RegionSize = {};
605+
RegionSize.NumTiles = NumTiles;
606+
RegionSize.UseBox = FALSE;
607+
608+
const D3D12_TILE_RANGE_FLAGS RangeFlag = D3D12_TILE_RANGE_FLAG_NONE;
609+
const UINT HeapRangeStartOffset = 0;
610+
const UINT RangeTileCount = NumTiles;
611+
612+
ID3D12CommandQueue *CommandQueue = IS.Queue.Get();
613+
CommandQueue->UpdateTileMappings(
614+
Buffer.Get(), 1, &StartCoord, &RegionSize, // One region
615+
Heap.Get(), 1, &RangeFlag, &HeapRangeStartOffset, &RangeTileCount,
616+
D3D12_TILE_MAPPING_FLAG_NONE);
617+
}
618+
619+
// Upload data initialization
559620
void *ResDataPtr = nullptr;
560-
if (auto Err = HR::toError(UploadBuffer->Map(0, nullptr, &ResDataPtr),
561-
"Failed to acquire UAV data pointer."))
562-
return Err;
563-
memcpy(ResDataPtr, ResData.get(), R.size());
564-
UploadBuffer->Unmap(0, nullptr);
621+
if (SUCCEEDED(UploadBuffer->Map(0, NULL, &ResDataPtr))) {
622+
memcpy(ResDataPtr, ResData.get(), R.size());
623+
UploadBuffer->Unmap(0, nullptr);
624+
} else {
625+
return llvm::createStringError(std::errc::io_error,
626+
"Failed to map SRV upload buffer.");
627+
}
565628

566629
addResourceUploadCommands(R, IS, Buffer, UploadBuffer);
567630

568-
Bundle.emplace_back(UploadBuffer, Buffer, nullptr);
631+
Bundle.emplace_back(UploadBuffer, Buffer, nullptr, Heap);
569632
RegOffset++;
570633
}
571634
return Bundle;
@@ -684,6 +747,7 @@ class DXDevice : public offloadtest::Device {
684747
llvm::outs() << "UAV: HeapIdx = " << HeapIdx << " EltSize = " << EltSize
685748
<< " NumElts = " << NumElts
686749
<< " HasCounter = " << R.HasCounter << "\n";
750+
687751
D3D12_CPU_DESCRIPTOR_HANDLE UAVHandle = UAVHandleHeapStart;
688752
UAVHandle.ptr += HeapIdx * DescHandleIncSize;
689753
ID3D12Resource *CounterBuffer = R.HasCounter ? RS.Buffer.Get() : nullptr;

lib/API/VK/Device.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -789,8 +789,8 @@ class VKDevice : public offloadtest::Device {
789789
std::errc::invalid_argument,
790790
"No RenderTarget buffer specified for graphics pipeline.");
791791
Resource FrameBuffer = {
792-
ResourceKind::Texture2D, "RenderTarget", {}, {},
793-
P.Bindings.RTargetBufferPtr, false};
792+
ResourceKind::Texture2D, "RenderTarget", {}, {},
793+
P.Bindings.RTargetBufferPtr, false, std::nullopt};
794794
IS.FrameBufferResource.Size = P.Bindings.RTargetBufferPtr->size();
795795
IS.FrameBufferResource.BufferPtr = P.Bindings.RTargetBufferPtr;
796796
IS.FrameBufferResource.ImageLayout =
@@ -817,8 +817,8 @@ class VKDevice : public offloadtest::Device {
817817
std::errc::invalid_argument,
818818
"No Vertex buffer specified for graphics pipeline.");
819819
const Resource VertexBuffer = {
820-
ResourceKind::StructuredBuffer, "VertexBuffer", {}, {},
821-
P.Bindings.VertexBufferPtr, false};
820+
ResourceKind::StructuredBuffer, "VertexBuffer", {}, {},
821+
P.Bindings.VertexBufferPtr, false, std::nullopt};
822822
auto ExVHostBuf =
823823
createBuffer(IS, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
824824
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VertexBuffer.size(),

lib/Support/Pipeline.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ void MappingTraits<offloadtest::Resource>::mapping(IO &I,
291291
I.mapRequired("Name", R.Name);
292292
I.mapRequired("Kind", R.Kind);
293293
I.mapOptional("HasCounter", R.HasCounter, 0);
294+
I.mapOptional("TilesMapped", R.TilesMapped);
294295
I.mapRequired("DirectXBinding", R.DXBinding);
295296
I.mapOptional("VulkanBinding", R.VKBinding);
296297
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#--- source.hlsl
2+
3+
StructuredBuffer<int4> X : register(t0);
4+
StructuredBuffer<int4> Y : register(t1);
5+
6+
RWStructuredBuffer<int> Out : register(u2);
7+
RWStructuredBuffer<bool> CAFM : register(u3);
8+
9+
[numthreads(1,1,1)]
10+
void main() {
11+
// 4096 int4's inside X or Y occupy 64KB of data.
12+
// (4096 int4's * 4 ints * 4 bytes per int)
13+
// So, any index into the buffer >= [4096] will access a new "tile"
14+
15+
uint idx = 0;
16+
17+
uint status;
18+
int4 Result = X.Load(0, status);
19+
bool CAFMResult = CheckAccessFullyMapped(status);
20+
CAFM[idx] = CAFMResult;
21+
if (CAFMResult)
22+
Out[idx] = Result.x;
23+
else
24+
Out[idx] = 9003;
25+
26+
idx += 1;
27+
28+
Result = X.Load(4100, status);
29+
CAFMResult = CheckAccessFullyMapped(status);
30+
CAFM[idx] = CAFMResult;
31+
if (CAFMResult)
32+
Out[idx] = Result.x;
33+
else
34+
Out[idx] = 9003;
35+
36+
idx += 1;
37+
38+
Result = Y.Load(0, status);
39+
CAFMResult = CheckAccessFullyMapped(status);
40+
CAFM[idx] = CAFMResult;
41+
if (CAFMResult)
42+
Out[idx] = Result.x;
43+
else
44+
Out[idx] = 9003;
45+
46+
idx += 1;
47+
48+
Result = Y.Load(4100, status);
49+
CAFMResult = CheckAccessFullyMapped(status);
50+
CAFM[idx] = CAFMResult;
51+
if (CAFMResult)
52+
Out[idx] = Result.x;
53+
else
54+
Out[idx] = 9003;
55+
56+
idx += 1;
57+
}
58+
59+
//--- pipeline.yaml
60+
61+
---
62+
Shaders:
63+
- Stage: Compute
64+
Entry: main
65+
DispatchSize: [1, 1, 1]
66+
Buffers:
67+
- Name: X
68+
Format: Int32
69+
Stride: 16
70+
FillSize: 131072
71+
FillValue: 9001
72+
- Name: Y
73+
Format: Int32
74+
Stride: 16
75+
FillSize: 131072
76+
FillValue: 9002
77+
- Name: Out
78+
Format: Int32
79+
Stride: 4
80+
FillSize: 16
81+
- Name: ExpectedOut
82+
Format: Int32
83+
Stride: 4
84+
# first 4 values are the actual data retrieved. For non-resident loads, 0 is expected.
85+
Data: [9001, 9003, 9003, 9003]
86+
- Name: CAFM
87+
Format: Bool
88+
Stride: 4
89+
FillSize: 16
90+
FillValue: 0
91+
- Name: ExpectedCAFM
92+
Format: Bool
93+
Stride: 4
94+
# Only the first data access should be accessing fully mapped memory
95+
Data: [1, 0, 0, 0]
96+
97+
Results:
98+
- Result: Test
99+
Rule: BufferExact
100+
Actual: Out
101+
Expected: ExpectedOut
102+
- Result: TestCAFM
103+
Rule: BufferExact
104+
Actual: CAFM
105+
Expected: ExpectedCAFM
106+
DescriptorSets:
107+
- Resources:
108+
- Name: X
109+
Kind: StructuredBuffer
110+
DirectXBinding:
111+
Register: 0
112+
Space: 0
113+
VulkanBinding:
114+
Binding: 0
115+
TilesMapped: 1
116+
- Name: Y
117+
Kind: StructuredBuffer
118+
DirectXBinding:
119+
Register: 1
120+
Space: 0
121+
VulkanBinding:
122+
Binding: 1
123+
TilesMapped: 0
124+
- Name: Out
125+
Kind: RWStructuredBuffer
126+
DirectXBinding:
127+
Register: 2
128+
Space: 0
129+
VulkanBinding:
130+
Binding: 2
131+
- Name: CAFM
132+
Kind: RWStructuredBuffer
133+
DirectXBinding:
134+
Register: 3
135+
Space: 0
136+
VulkanBinding:
137+
Binding: 3
138+
#--- end
139+
140+
# Unimplemented https://github.com/llvm/llvm-project/issues/138910
141+
# AND https://github.com/llvm/llvm-project/issues/99204
142+
# XFAIL: Clang
143+
144+
# Unimplemented https://github.com/llvm/llvm-project/issues/138910
145+
# AND https://github.com/llvm/llvm-project/issues/99204
146+
# XFAIL: Vulkan
147+
148+
# Bug https://github.com/llvm/offload-test-suite/issues/182
149+
# Metal API seems to have problems with reserved resources
150+
# XFAIL: Metal
151+
152+
# Bug https://github.com/llvm/offload-test-suite/issues/485
153+
# XFAIL: Intel
154+
155+
# RUN: split-file %s %t
156+
# RUN: %dxc_target -T cs_6_5 -Fo %t.o %t/source.hlsl
157+
# RUN: %offloader %t/pipeline.yaml %t.o

0 commit comments

Comments
 (0)