Skip to content

Commit 877ac53

Browse files
Implement sparse texture support.
1 parent ea24bb7 commit 877ac53

8 files changed

Lines changed: 254 additions & 48 deletions

File tree

include/API/Buffer.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
namespace offloadtest {
2222

23+
class Device;
24+
2325
enum class BufferShaderAccessType {
2426
Raw,
2527
Typed,
@@ -85,11 +87,8 @@ class Buffer {
8587
virtual ~Buffer();
8688
virtual size_t getSizeInBytes() const = 0;
8789

88-
// The granularity, in bytes, of a single sparse tile for this buffer. Tile
89-
// counts are computed as ceil(sizeInBytes / granularity). Only meaningful for
90-
// buffers created with MemoryBacking::Sparse. The value is backend-defined
91-
// (e.g. 64 KiB on DX12/Vulkan, commonly 16 KiB on Metal/Apple Silicon).
92-
virtual size_t querySparseTileSizeInBytes() const = 0;
90+
// The granularity, in bytes, of a single sparse tile for this buffer.
91+
virtual size_t querySparseTileSizeInBytes(const Device &Dev) const = 0;
9392

9493
// Maps the buffer's memory for host access. Only valid for CpuToGpu and
9594
// GpuToCpu buffers; returns an error for GpuOnly. Each successful map() must

include/API/Device.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,13 @@ llvm::Expected<std::unique_ptr<offloadtest::Buffer>> createSparseBufferWithData(
395395
std::unique_ptr<offloadtest::Buffer> &OutUploadBuffer,
396396
std::unique_ptr<offloadtest::MemoryHeap> &OutBackingMemoryHeap);
397397

398+
llvm::Expected<std::unique_ptr<offloadtest::Texture>>
399+
createSparseTextureWithData(
400+
Device &Dev, Queue &Q, std::string Name, const TextureCreateDesc &Desc,
401+
const void *Data, size_t SizeInBytes, ComputeEncoder &Encoder,
402+
std::unique_ptr<offloadtest::Buffer> &OutUploadBuffer,
403+
std::unique_ptr<offloadtest::MemoryHeap> &OutBackingMemoryHeap);
404+
398405
// TLAS handles come in pre-allocated because the caller's binding loop
399406
// stamps the AS pointer into descriptor bundles before this helper runs;
400407
// BLAS handles are allocated inline since BLASes aren't user-bindable.

include/API/Texture.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,13 @@ using ClearValue = std::variant<ClearColor, ClearDepthStencil>;
6868
// cube, or array textures, add a TextureType enum and validation between usage
6969
// and type (e.g. 3D textures cannot be used as DepthStencil).
7070
struct TextureCreateDesc {
71-
MemoryLocation Location;
72-
TextureUsage Usage;
73-
Format Fmt;
74-
uint32_t Width;
75-
uint32_t Height;
76-
uint32_t MipLevels;
71+
MemoryLocation Location = MemoryLocation::GpuOnly;
72+
MemoryBacking Backing = MemoryBacking::Automatic;
73+
TextureUsage Usage = {};
74+
Format Fmt = Format::RGBA32Float;
75+
uint32_t Width = 1;
76+
uint32_t Height = 1;
77+
uint32_t MipLevels = 1;
7778
// Clear value for render target or depth/stencil textures.
7879
// How and when this is applied depends on the backend:
7980
// - DX uses it as an optimized clear hint at resource creation time
@@ -144,6 +145,15 @@ inline llvm::Error validateTextureCreateDesc(const TextureCreateDesc &Desc) {
144145
return llvm::Error::success();
145146
}
146147

148+
// The texel dimensions of a single sparse tile. A tile is a fixed byte size
149+
// (~64 KiB) laid out as this WxHxD box of texels; Depth is 1 for 2D textures.
150+
// Tile counts are computed per dimension as ceil(extent / tileExtent).
151+
struct TileShape {
152+
uint32_t Width = 1;
153+
uint32_t Height = 1;
154+
uint32_t Depth = 1;
155+
};
156+
147157
class Texture {
148158
GPUAPI API;
149159

@@ -154,7 +164,12 @@ class Texture {
154164

155165
// Calculate the size in bytes of the texture data given a linear layout
156166
// Useful for calculating the size for an upload or readback buffer.
157-
size_t calculateLinearSizeInBytes(Device &Dev) const;
167+
size_t calculateLinearSizeInBytes(const Device &Dev) const;
168+
169+
// The texel dimensions of a single sparse tile for this texture. The shape
170+
// varies by format, dimensionality, and sample count. Only meaningful for a
171+
// texture created with MemoryBacking::Sparse.
172+
virtual TileShape querySparseTileShape(const Device &Dev) const = 0;
158173

159174
GPUAPI getAPI() const { return API; }
160175
virtual const TextureCreateDesc &getDesc() const = 0;

lib/API/DX/Device.cpp

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ static BufferShaderAccessType bufferShaderAccessTypeFromResourceKind(
193193
}
194194

195195
namespace {
196+
class DXDevice;
196197

197198
class DXBuffer : public offloadtest::Buffer {
198199
public:
@@ -231,7 +232,7 @@ class DXBuffer : public offloadtest::Buffer {
231232

232233
size_t getSizeInBytes() const override { return SizeInBytes; }
233234

234-
size_t querySparseTileSizeInBytes() const override {
235+
size_t querySparseTileSizeInBytes(const Device & /*Dev*/) const override {
235236
return D3D12_TILED_RESOURCE_TILE_SIZE_IN_BYTES;
236237
}
237238

@@ -284,6 +285,8 @@ class DXTexture : public offloadtest::Texture {
284285
PreferredState(PreferredState), SRVHandle(SRVHandle),
285286
UAVHandle(UAVHandle), Name(Name), Desc(Desc) {}
286287

288+
TileShape querySparseTileShape(const Device &Dev) const override;
289+
287290
const TextureCreateDesc &getDesc() const override { return Desc; }
288291

289292
static bool classof(const offloadtest::Texture *T) {
@@ -1149,7 +1152,7 @@ class DXDevice : public offloadtest::Device {
11491152
// raw ID3D12Device for scratch buffer allocation.
11501153
friend class DXComputeEncoder;
11511154

1152-
private:
1155+
public:
11531156
ComPtr<IDXCoreAdapter> Adapter;
11541157
ComPtr<ID3D12DeviceX> Device;
11551158
DXQueue GraphicsQueue;
@@ -1175,31 +1178,36 @@ class DXDevice : public offloadtest::Device {
11751178
std::unique_ptr<MemoryHeap> BackingMemory,
11761179
std::unique_ptr<offloadtest::Buffer> Readback,
11771180
std::unique_ptr<offloadtest::Buffer> CounterReadback)
1178-
: UploadBuffer(std::move(UploadBuffer)), Buffer(std::move(Buffer)),
1179-
BackingMemory(std::move(BackingMemory)),
1181+
: UploadBuffer(std::move(UploadBuffer)),
1182+
BackingMemory(std::move(BackingMemory)), Buffer(std::move(Buffer)),
1183+
11801184
Readback(std::move(Readback)),
11811185
CounterReadback(std::move(CounterReadback)) {}
11821186
ResourceSet(std::unique_ptr<offloadtest::Buffer> UploadBuffer,
11831187
std::unique_ptr<offloadtest::Texture> Texture,
1188+
std::unique_ptr<MemoryHeap> BackingMemory,
11841189
std::unique_ptr<offloadtest::Buffer> Readback)
1185-
: UploadBuffer(std::move(UploadBuffer)), Texture(std::move(Texture)),
1190+
: UploadBuffer(std::move(UploadBuffer)),
1191+
BackingMemory(std::move(BackingMemory)), Texture(std::move(Texture)),
1192+
11861193
Readback(std::move(Readback)) {}
11871194
explicit ResourceSet(AccelerationStructure *AS) : AS(AS) {}
11881195

11891196
ResourceSet(const ResourceSet &) = delete;
11901197
ResourceSet &operator=(const ResourceSet &) = delete;
11911198

11921199
ResourceSet(ResourceSet &&A)
1193-
: UploadBuffer(std::move(A.UploadBuffer)), Buffer(std::move(A.Buffer)),
1194-
Texture(std::move(A.Texture)),
1200+
: UploadBuffer(std::move(A.UploadBuffer)),
11951201
BackingMemory(std::move(A.BackingMemory)),
1196-
Readback(std::move(A.Readback)),
1202+
Buffer(std::move(A.Buffer)),
1203+
1204+
Texture(std::move(A.Texture)), Readback(std::move(A.Readback)),
11971205
CounterReadback(std::move(A.CounterReadback)), AS(A.AS) {}
11981206
ResourceSet &operator=(ResourceSet &&A) {
11991207
UploadBuffer = std::move(A.UploadBuffer);
1208+
BackingMemory = std::move(A.BackingMemory);
12001209
Buffer = std::move(A.Buffer);
12011210
Texture = std::move(A.Texture);
1202-
BackingMemory = std::move(A.BackingMemory);
12031211
Readback = std::move(A.Readback);
12041212
CounterReadback = std::move(A.CounterReadback);
12051213
AS = A.AS;
@@ -1284,6 +1292,10 @@ class DXDevice : public offloadtest::Device {
12841292
llvm::StringRef getAPIName() const override { return "DirectX"; }
12851293
GPUAPI getAPI() const override { return GPUAPI::DirectX; }
12861294

1295+
static bool classof(const offloadtest::Device *D) {
1296+
return D->getAPI() == GPUAPI::DirectX;
1297+
}
1298+
12871299
Queue &getGraphicsQueue() override { return GraphicsQueue; }
12881300

12891301
llvm::Error
@@ -1770,9 +1782,14 @@ class DXDevice : public offloadtest::Device {
17701782
TexDesc.MipLevels = static_cast<UINT16>(Desc.MipLevels);
17711783
TexDesc.Format = getDXGIFormat(Desc.Fmt);
17721784
TexDesc.SampleDesc.Count = 1;
1773-
TexDesc.Layout = Desc.Location == MemoryLocation::GpuOnly
1774-
? D3D12_TEXTURE_LAYOUT_UNKNOWN
1775-
: D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
1785+
if (Desc.Location == MemoryLocation::GpuOnly) {
1786+
if (Desc.Backing == MemoryBacking::Sparse)
1787+
TexDesc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
1788+
else
1789+
TexDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
1790+
} else {
1791+
TexDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
1792+
}
17761793
TexDesc.Flags = getDXResourceFlags(Desc.Usage);
17771794

17781795
const D3D12_CLEAR_VALUE *ClearValuePtr = nullptr;
@@ -1803,12 +1820,20 @@ class DXDevice : public offloadtest::Device {
18031820
InitialState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
18041821

18051822
ComPtr<ID3D12Resource> TextureObject;
1806-
if (auto Err = HR::toError(Device->CreateCommittedResource(
1807-
&HeapProps, D3D12_HEAP_FLAG_NONE, &TexDesc,
1808-
InitialState, ClearValuePtr,
1809-
IID_PPV_ARGS(&TextureObject)),
1810-
"Failed to create texture."))
1811-
return Err;
1823+
if (Desc.Backing == MemoryBacking::Sparse) {
1824+
if (auto Err = HR::toError(Device->CreateReservedResource(
1825+
&TexDesc, InitialState, ClearValuePtr,
1826+
IID_PPV_ARGS(&TextureObject)),
1827+
"Failed to create reserved texture."))
1828+
return Err;
1829+
} else {
1830+
if (auto Err = HR::toError(Device->CreateCommittedResource(
1831+
&HeapProps, D3D12_HEAP_FLAG_NONE, &TexDesc,
1832+
InitialState, ClearValuePtr,
1833+
IID_PPV_ARGS(&TextureObject)),
1834+
"Failed to create texture."))
1835+
return Err;
1836+
}
18121837

18131838
D3D12_CPU_DESCRIPTOR_HANDLE SRVHandle = {};
18141839
{
@@ -2250,7 +2275,8 @@ class DXDevice : public offloadtest::Device {
22502275
if (R.isBuffer()) {
22512276
BufferCreateDesc CreateDesc = {};
22522277
CreateDesc.Location = MemoryLocation::GpuOnly;
2253-
CreateDesc.Backing = R.IsReserved ? MemoryBacking::Sparse : MemoryBacking::Automatic;
2278+
CreateDesc.Backing =
2279+
R.IsReserved ? MemoryBacking::Sparse : MemoryBacking::Automatic;
22542280
CreateDesc.Usage = bufferUsageFromResourceKind(R.Kind);
22552281
CreateDesc.AccessType = bufferShaderAccessTypeFromResourceKind(
22562282
R, CreateDesc.AccessTypeParams);
@@ -2319,6 +2345,8 @@ class DXDevice : public offloadtest::Device {
23192345

23202346
TextureCreateDesc CreateDesc = {};
23212347
CreateDesc.Location = MemoryLocation::GpuOnly;
2348+
CreateDesc.Backing =
2349+
R.IsReserved ? MemoryBacking::Sparse : MemoryBacking::Automatic;
23222350
CreateDesc.Usage = TextureUsage::Sampled;
23232351
if (R.Kind == ResourceKind::RWTexture2D)
23242352
CreateDesc.Usage |= TextureUsage::Storage;
@@ -2329,12 +2357,26 @@ class DXDevice : public offloadtest::Device {
23292357

23302358
for (auto &Data : R.BufferPtr->Data) {
23312359
std::unique_ptr<offloadtest::Buffer> UploadBuffer;
2332-
auto TextureOrErr =
2333-
createTextureWithData(*this, "Texture", CreateDesc, Data.get(),
2334-
R.size(), Enc.get(), &UploadBuffer);
2335-
if (!TextureOrErr)
2336-
return TextureOrErr.takeError();
2337-
auto Texture = std::move(*TextureOrErr);
2360+
std::unique_ptr<offloadtest::MemoryHeap> BackingMemoryHeap;
2361+
2362+
std::unique_ptr<offloadtest::Texture> Texture;
2363+
if (R.IsReserved) {
2364+
auto TextureOrErr = createSparseTextureWithData(
2365+
*this, GraphicsQueue, "Sparse Texture", CreateDesc, Data.get(),
2366+
R.size(), *Enc.get(), UploadBuffer, BackingMemoryHeap);
2367+
if (!TextureOrErr)
2368+
return TextureOrErr.takeError();
2369+
2370+
Texture = std::move(*TextureOrErr);
2371+
} else {
2372+
auto TextureOrErr =
2373+
createTextureWithData(*this, "Texture", CreateDesc, Data.get(),
2374+
R.size(), Enc.get(), &UploadBuffer);
2375+
if (!TextureOrErr)
2376+
return TextureOrErr.takeError();
2377+
2378+
Texture = std::move(*TextureOrErr);
2379+
}
23382380

23392381
std::unique_ptr<Buffer> ReadbackBuffer;
23402382
if (getDescriptorKind(R.Kind) == DescriptorKind::UAV) {
@@ -2349,6 +2391,7 @@ class DXDevice : public offloadtest::Device {
23492391
}
23502392

23512393
ResourceSet RSet(std::move(UploadBuffer), std::move(Texture),
2394+
std::move(BackingMemoryHeap),
23522395
std::move(ReadbackBuffer));
23532396
ResBundle.push_back(std::move(RSet));
23542397
}
@@ -3048,6 +3091,18 @@ class DXDevice : public offloadtest::Device {
30483091
}
30493092
};
30503093

3094+
TileShape DXTexture::querySparseTileShape(const Device &Dev) const {
3095+
const DXDevice &DeviceDX = llvm::cast<DXDevice>(Dev);
3096+
3097+
D3D12_TILE_SHAPE Shape = {};
3098+
UINT NumTiles = 0;
3099+
UINT NumSubresourceTilings = 0;
3100+
DeviceDX.Device->GetResourceTiling(Resource.Get(), &NumTiles, nullptr, &Shape,
3101+
&NumSubresourceTilings, 0, nullptr);
3102+
return TileShape{Shape.WidthInTexels, Shape.HeightInTexels,
3103+
Shape.DepthInTexels};
3104+
}
3105+
30513106
llvm::Error DXComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
30523107
if (Items.empty())
30533108
return llvm::Error::success();

0 commit comments

Comments
 (0)