Skip to content

Commit ee08c94

Browse files
Add mipmap support to createTextureWithData.
1 parent 273e4a5 commit ee08c94

14 files changed

Lines changed: 190 additions & 57 deletions

include/API/Device.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ class Device {
358358
virtual uint32_t
359359
getTextureUploadRowStrideInBytes(const TextureCreateDesc &Desc) const = 0;
360360

361+
// The layout an upload buffer must have to feed createTextureWithData /
362+
// copyBufferToTexture for the given texture description. Encodes per-mip
363+
// offsets, row pitch, and total size in the backend's required alignment.
364+
virtual TextureUploadLayout
365+
getTextureUploadLayout(const TextureCreateDesc &Desc) const = 0;
366+
361367
virtual llvm::Expected<std::unique_ptr<RenderPass>>
362368
createRenderPass(const RenderPassDesc &Desc) = 0;
363369

@@ -425,6 +431,9 @@ createBufferWithData(Device &Dev, std::string Name,
425431
size_t SizeInBytes, ComputeEncoder *Encoder,
426432
std::unique_ptr<offloadtest::Buffer> *OutUploadBuffer);
427433

434+
// Create a texture and upload `Data` (tightly-packed across mip levels) into
435+
// it via a staging buffer recorded on `Encoder`. The staging buffer is handed
436+
// back through `OutUploadBuffer` and must outlive command-buffer submission.
428437
llvm::Expected<std::unique_ptr<offloadtest::Texture>>
429438
createTextureWithData(Device &Dev, std::string Name,
430439
const TextureCreateDesc &Desc, const void *Data,

include/API/Texture.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "API/Resources.h"
1717

1818
#include "llvm/ADT/BitmaskEnum.h"
19+
#include "llvm/ADT/SmallVector.h"
1920
#include "llvm/ADT/StringRef.h"
2021
#include "llvm/Support/Casting.h"
2122
#include "llvm/Support/Error.h"
@@ -148,6 +149,24 @@ struct TileShape {
148149
uint32_t Depth = 1;
149150
};
150151

152+
struct SubresourceFootprint {
153+
uint64_t Offset = 0; // Byte offset of this subresource in the buffer.
154+
uint32_t RowPitchInBytes = 0; // Destination row stride (may include padding).
155+
uint32_t RowSizeInBytes = 0; // Tightly-packed bytes per row to copy.
156+
uint32_t NumRows = 0; // Number of rows in this subresource.
157+
};
158+
159+
struct TextureUploadLayout {
160+
llvm::SmallVector<SubresourceFootprint> Subresources; // One entry per mip.
161+
uint64_t TotalSizeInBytes = 0;
162+
};
163+
164+
// Compute a tightly-packed upload layout (no row or subresource padding) for
165+
// the given texture description. Suitable for backends whose buffer-to-texture
166+
// copy consumes a tightly-packed staging buffer (e.g. Vulkan, Metal).
167+
TextureUploadLayout
168+
computeTightTextureUploadLayout(const TextureCreateDesc &Desc);
169+
151170
class Texture {
152171
GPUAPI API;
153172

lib/API/DX/Device.cpp

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -891,15 +891,20 @@ class DXComputeEncoder : public offloadtest::ComputeEncoder {
891891
D3D12_RESOURCE_STATE_COPY_DEST);
892892
CB.flushBarrier();
893893

894-
const uint32_t ElementSize = getFormatSizeInBytes(DXDst.Desc.Fmt);
895-
const D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint{
896-
0,
897-
CD3DX12_SUBRESOURCE_FOOTPRINT(
898-
getDXGIFormat(DXDst.Desc.Fmt), DXDst.Desc.Width, DXDst.Desc.Height,
899-
1, getAlignedTexturePitch(DXDst.Desc.Width, ElementSize))};
900-
const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(DXDst.Resource.Get(), 0);
901-
const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(DXSrc.Buffer.Get(), Footprint);
902-
CB.CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr);
894+
const D3D12_RESOURCE_DESC TexDesc = DXDst.Resource->GetDesc();
895+
const uint32_t NumSubresources = TexDesc.MipLevels;
896+
llvm::SmallVector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> Layouts(
897+
NumSubresources);
898+
ComPtr<ID3D12DeviceX> Device;
899+
DXDst.Resource->GetDevice(IID_PPV_ARGS(&Device));
900+
Device->GetCopyableFootprints(&TexDesc, 0, NumSubresources, 0,
901+
Layouts.data(), nullptr, nullptr, nullptr);
902+
for (uint32_t Sub = 0; Sub < NumSubresources; ++Sub) {
903+
const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(DXDst.Resource.Get(), Sub);
904+
const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(DXSrc.Buffer.Get(),
905+
Layouts[Sub]);
906+
CB.CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr);
907+
}
903908

904909
if (DXSrc.PreferredState != D3D12_RESOURCE_STATE_COPY_SOURCE)
905910
CB.addResourceTransition(DXSrc.Buffer.Get(),
@@ -2300,6 +2305,43 @@ class DXDevice : public offloadtest::Device {
23002305
return getAlignedTexturePitch(Desc.Width, getFormatSizeInBytes(Desc.Fmt));
23012306
}
23022307

2308+
TextureUploadLayout
2309+
getTextureUploadLayout(const TextureCreateDesc &Desc) const override {
2310+
// Only the fields GetCopyableFootprints consults are needed here; layout,
2311+
// flags, and clear value do not affect the copyable footprint.
2312+
D3D12_RESOURCE_DESC TexDesc = {};
2313+
TexDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
2314+
TexDesc.Width = Desc.Width;
2315+
TexDesc.Height = Desc.Height;
2316+
TexDesc.DepthOrArraySize = 1;
2317+
TexDesc.MipLevels = static_cast<UINT16>(Desc.MipLevels);
2318+
TexDesc.Format = getDXGIFormat(Desc.Fmt);
2319+
TexDesc.SampleDesc.Count = 1;
2320+
2321+
const uint32_t NumSubresources = Desc.MipLevels;
2322+
llvm::SmallVector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> Footprints(
2323+
NumSubresources);
2324+
llvm::SmallVector<UINT> NumRows(NumSubresources);
2325+
llvm::SmallVector<UINT64> RowSizes(NumSubresources);
2326+
UINT64 TotalBytes = 0;
2327+
Device->GetCopyableFootprints(&TexDesc, 0, NumSubresources, 0,
2328+
Footprints.data(), NumRows.data(),
2329+
RowSizes.data(), &TotalBytes);
2330+
2331+
TextureUploadLayout Layout;
2332+
Layout.TotalSizeInBytes = TotalBytes;
2333+
Layout.Subresources.reserve(NumSubresources);
2334+
for (uint32_t I = 0; I < NumSubresources; ++I) {
2335+
SubresourceFootprint Sub;
2336+
Sub.Offset = Footprints[I].Offset;
2337+
Sub.RowPitchInBytes = Footprints[I].Footprint.RowPitch;
2338+
Sub.RowSizeInBytes = static_cast<uint32_t>(RowSizes[I]);
2339+
Sub.NumRows = NumRows[I];
2340+
Layout.Subresources.push_back(Sub);
2341+
}
2342+
return Layout;
2343+
}
2344+
23032345
static llvm::Expected<std::unique_ptr<offloadtest::Device>>
23042346
create(ComPtr<IDXCoreAdapter> Adapter, const DeviceConfig &Config) {
23052347
ComPtr<ID3D12DeviceX> Device;

lib/API/Device.cpp

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -530,51 +530,54 @@ offloadtest::createTextureWithData(
530530
Device &Dev, std::string Name, const TextureCreateDesc &Desc,
531531
const void *Data, size_t SizeInBytes, ComputeEncoder *Encoder,
532532
std::unique_ptr<offloadtest::Buffer> *OutUploadBuffer) {
533-
534-
const uint64_t PackedRowStrideInBytes =
535-
Desc.Width * getFormatSizeInBytes(Desc.Fmt);
536-
if (SizeInBytes < PackedRowStrideInBytes * Desc.Height)
533+
if (Encoder == nullptr)
537534
return llvm::createStringError(
538-
"Data upload is not enough for texture size.");
535+
"An encoder is required to upload texture data.");
536+
if (OutUploadBuffer == nullptr)
537+
return llvm::createStringError(
538+
"An upload buffer is required to create a texture with data.");
539539

540540
auto TextureOrErr = Dev.createTexture(Name, Desc);
541541
if (!TextureOrErr)
542542
return TextureOrErr.takeError();
543543
auto Texture = std::move(*TextureOrErr);
544544

545-
if (OutUploadBuffer == nullptr)
546-
return llvm::createStringError("An upload buffer is required to create a "
547-
"GpuOnly texture with data.");
545+
const TextureUploadLayout Layout = Dev.getTextureUploadLayout(Desc);
548546

549-
const uint64_t TexRowStrideInBytes =
550-
Dev.getTextureUploadRowStrideInBytes(Desc);
551-
const uint64_t UploadBufferSizeInBytes =
552-
(Desc.Height - 1) * TexRowStrideInBytes + PackedRowStrideInBytes;
547+
// The source data is tightly packed across mips, so its required size is the
548+
// sum of each subresource's tight row size times its row count, independent
549+
// of any backend row/offset padding in the upload buffer.
550+
uint64_t PackedSizeInBytes = 0;
551+
for (const SubresourceFootprint &Sub : Layout.Subresources)
552+
PackedSizeInBytes += uint64_t(Sub.RowSizeInBytes) * Sub.NumRows;
553+
if (SizeInBytes < PackedSizeInBytes)
554+
return llvm::createStringError(
555+
"Data upload is not enough for texture size.");
553556

554-
// Create Upload buffer
555557
const BufferCreateDesc UploadDesc = BufferCreateDesc::uploadBuffer();
556558
const std::string UploadBufferName = Name + " (Upload Buffer)";
557559
auto UploadBufferOrErr =
558-
Dev.createBuffer(UploadBufferName, UploadDesc, UploadBufferSizeInBytes);
560+
Dev.createBuffer(UploadBufferName, UploadDesc, Layout.TotalSizeInBytes);
559561
if (!UploadBufferOrErr)
560562
return UploadBufferOrErr.takeError();
561563
*OutUploadBuffer = std::move(*UploadBufferOrErr);
562564

563565
auto MappedPtrOrErr = (*OutUploadBuffer)->map();
564566
if (!MappedPtrOrErr)
565567
return MappedPtrOrErr.takeError();
566-
567-
uint8_t *DstPtr = (uint8_t *)*MappedPtrOrErr;
568-
const uint8_t *SrcPtr = (const uint8_t *)Data;
569-
570-
for (uint32_t Y = 0; Y < Desc.Height; ++Y) {
571-
memcpy(DstPtr, SrcPtr, PackedRowStrideInBytes);
572-
DstPtr += TexRowStrideInBytes;
573-
SrcPtr += PackedRowStrideInBytes;
568+
auto *const DstBase = static_cast<uint8_t *>(*MappedPtrOrErr);
569+
const auto *SrcPtr = static_cast<const uint8_t *>(Data);
570+
571+
for (const SubresourceFootprint &Sub : Layout.Subresources) {
572+
uint8_t *DstPtr = DstBase + Sub.Offset;
573+
for (uint32_t Row = 0; Row < Sub.NumRows; ++Row) {
574+
memcpy(DstPtr, SrcPtr, Sub.RowSizeInBytes);
575+
DstPtr += Sub.RowPitchInBytes;
576+
SrcPtr += Sub.RowSizeInBytes;
577+
}
574578
}
575579
(*OutUploadBuffer)->unmap();
576580

577-
// Copy Buffer to Texture
578581
if (auto Err = Encoder->copyBufferToTexture(**OutUploadBuffer, *Texture))
579582
return Err;
580583

lib/API/MTL/MTLDevice.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -743,21 +743,27 @@ class MTLComputeEncoder : public offloadtest::ComputeEncoder {
743743
auto &MTLSrc = static_cast<MTLBuffer &>(Src);
744744
auto &MTLDst = static_cast<MTLTexture &>(Dst);
745745

746-
// The upload buffer is laid out with a tightly packed row stride matching
747-
// getTextureUploadRowStrideInBytes(), so the source bytes-per-row is the
748-
// texture width times the element size.
746+
// The upload buffer holds tightly packed texel data for every mip level
747+
// (see createTextureWithData): each mip's rows are contiguous with no
748+
// padding, and the mips follow one another. Copy one mip at a time, with
749+
// the source bytes-per-row being that mip's width times the element size.
749750
const size_t ElemSize = getFormatSizeInBytes(MTLDst.Desc.Fmt);
750-
const size_t RowBytes = MTLDst.Desc.Width * ElemSize;
751-
const size_t ImageBytes = RowBytes * MTLDst.Desc.Height;
752-
const MTL::Size CopySize(MTLDst.Desc.Width, MTLDst.Desc.Height, 1);
753751

754752
insertDebugSignpost(llvm::formatv("copyBufferToTexture {0} -> {1}",
755753
MTLSrc.Name, MTLDst.Name)
756754
.str());
757-
BlitEnc->copyFromBuffer(MTLSrc.Buf, /*sourceOffset=*/0, RowBytes,
758-
ImageBytes, CopySize, MTLDst.Tex,
759-
/*destinationSlice=*/0, /*destinationLevel=*/0,
760-
MTL::Origin(0, 0, 0));
755+
size_t CurrentOffset = 0;
756+
for (uint32_t I = 0; I < MTLDst.Desc.MipLevels; ++I) {
757+
const uint32_t MipWidth = std::max(1u, MTLDst.Desc.Width >> I);
758+
const uint32_t MipHeight = std::max(1u, MTLDst.Desc.Height >> I);
759+
const size_t RowBytes = MipWidth * ElemSize;
760+
const size_t ImageBytes = RowBytes * MipHeight;
761+
BlitEnc->copyFromBuffer(MTLSrc.Buf, CurrentOffset, RowBytes, ImageBytes,
762+
MTL::Size(MipWidth, MipHeight, 1), MTLDst.Tex,
763+
/*destinationSlice=*/0, /*destinationLevel=*/I,
764+
MTL::Origin(0, 0, 0));
765+
CurrentOffset += ImageBytes;
766+
}
761767
addBarrierScope(MTL::BarrierScopeTextures);
762768

763769
return llvm::Error::success();
@@ -2418,6 +2424,12 @@ class MTLDevice : public offloadtest::Device {
24182424
return Desc.Width * getFormatSizeInBytes(Desc.Fmt);
24192425
}
24202426

2427+
TextureUploadLayout
2428+
getTextureUploadLayout(const TextureCreateDesc &Desc) const override {
2429+
// copyBufferToTexture consumes a tightly-packed staging buffer.
2430+
return computeTightTextureUploadLayout(Desc);
2431+
}
2432+
24212433
llvm::Expected<std::unique_ptr<offloadtest::CommandBuffer>>
24222434
createCommandBuffer() override {
24232435
auto CBOrErr = MTLCommandBuffer::create(GraphicsQueue.Queue);

lib/API/Texture.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "API/Texture.h"
22
#include "API/Device.h"
33

4+
#include <algorithm>
5+
46
// Calculate the size in bytes of the texture data given a linear layout
57
// Useful for calculating the size for an upload or readback buffer.
68
size_t
@@ -10,3 +12,24 @@ offloadtest::Texture::calculateLinearSizeInBytes(const Device &Dev) const {
1012
return (Desc.Height - 1) * Stride +
1113
Desc.Width * getFormatSizeInBytes(Desc.Fmt);
1214
}
15+
16+
offloadtest::TextureUploadLayout
17+
offloadtest::computeTightTextureUploadLayout(const TextureCreateDesc &Desc) {
18+
const uint32_t ElementSize = getFormatSizeInBytes(Desc.Fmt);
19+
TextureUploadLayout Layout;
20+
Layout.Subresources.reserve(Desc.MipLevels);
21+
uint64_t Offset = 0;
22+
for (uint32_t I = 0; I < Desc.MipLevels; ++I) {
23+
const uint32_t MipWidth = std::max(1u, Desc.Width >> I);
24+
const uint32_t MipHeight = std::max(1u, Desc.Height >> I);
25+
SubresourceFootprint Sub;
26+
Sub.Offset = Offset;
27+
Sub.RowSizeInBytes = MipWidth * ElementSize;
28+
Sub.RowPitchInBytes = Sub.RowSizeInBytes;
29+
Sub.NumRows = MipHeight;
30+
Layout.Subresources.push_back(Sub);
31+
Offset += uint64_t(Sub.RowSizeInBytes) * Sub.NumRows;
32+
}
33+
Layout.TotalSizeInBytes = Offset;
34+
return Layout;
35+
}

lib/API/VK/Device.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,11 +1061,32 @@ class VKComputeEncoder : public offloadtest::ComputeEncoder {
10611061
VK_ACCESS_TRANSFER_WRITE_BIT);
10621062
CB.flushBarrier();
10631063

1064+
const VkImageAspectFlags AspectMask = isDepthFormat(VKDst.Desc.Fmt)
1065+
? VK_IMAGE_ASPECT_DEPTH_BIT
1066+
: VK_IMAGE_ASPECT_COLOR_BIT;
1067+
const uint32_t ElementSize = getFormatSizeInBytes(VKDst.Desc.Fmt);
1068+
llvm::SmallVector<VkBufferImageCopy> Regions;
1069+
uint64_t CurrentOffset = 0;
1070+
for (uint32_t I = 0; I < VKDst.Desc.MipLevels; ++I) {
1071+
const uint32_t MipWidth = std::max(1u, VKDst.Desc.Width >> I);
1072+
const uint32_t MipHeight = std::max(1u, VKDst.Desc.Height >> I);
1073+
VkBufferImageCopy Region = {};
1074+
Region.bufferOffset = CurrentOffset;
1075+
Region.imageSubresource.aspectMask = AspectMask;
1076+
Region.imageSubresource.mipLevel = I;
1077+
Region.imageSubresource.baseArrayLayer = 0;
1078+
Region.imageSubresource.layerCount = 1;
1079+
Region.imageExtent = {MipWidth, MipHeight, 1};
1080+
Regions.push_back(Region);
1081+
CurrentOffset += uint64_t(MipWidth) * MipHeight * ElementSize;
1082+
}
1083+
10641084
insertDebugSignpost(
1065-
llvm::formatv("copyTextureToBuffer {0} -> {1}", VKSrc.Name, VKDst.Name)
1085+
llvm::formatv("copyBufferToTexture {0} -> {1}", VKSrc.Name, VKDst.Name)
10661086
.str());
10671087
vkCmdCopyBufferToImage(CB.CmdBuffer, VKSrc.Buffer, VKDst.Image,
1068-
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, nullptr);
1088+
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, Regions.size(),
1089+
Regions.data());
10691090

10701091
CB.addImageTransition(VK_ACCESS_TRANSFER_WRITE_BIT, /*SrcAccessMask*/
10711092
VK_ACCESS_NONE, /*DstAccessMask*/
@@ -2944,6 +2965,12 @@ class VulkanDevice : public offloadtest::Device {
29442965
TightRow, Props.limits.optimalBufferCopyRowPitchAlignment));
29452966
}
29462967

2968+
TextureUploadLayout
2969+
getTextureUploadLayout(const TextureCreateDesc &Desc) const override {
2970+
// copyBufferToTexture consumes a tightly-packed staging buffer.
2971+
return computeTightTextureUploadLayout(Desc);
2972+
}
2973+
29472974
const Capabilities &getCapabilities() override {
29482975
if (Caps.empty())
29492976
queryCapabilities();

lib/Support/OffloadMigration.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ llvm::Error createResources(Device &Dev, Pipeline &P,
272272
CreateDesc.Fmt = *FormatOrErr;
273273
CreateDesc.Width = R.BufferPtr->OutputProps.Width;
274274
CreateDesc.Height = R.BufferPtr->OutputProps.Height;
275-
CreateDesc.MipLevels = 1;
275+
CreateDesc.MipLevels = R.BufferPtr->OutputProps.MipLevels;
276276

277277
for (auto &Data : R.BufferPtr->Data) {
278278
std::unique_ptr<offloadtest::Buffer> UploadBuffer;
@@ -290,7 +290,7 @@ llvm::Error createResources(Device &Dev, Pipeline &P,
290290
Texture = std::move(*TextureOrErr);
291291
} else {
292292
auto TextureOrErr =
293-
createTextureWithData(Dev, "Texture", CreateDesc, Data.get(),
293+
createTextureWithData(Dev, R.Name, CreateDesc, Data.get(),
294294
R.size(), Enc.get(), &UploadBuffer);
295295
if (!TextureOrErr)
296296
return TextureOrErr.takeError();

test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ Results:
126126
...
127127
#--- end
128128

129-
# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558
130-
# XFAIL: DirectX || Metal
129+
# Unimplemented: Metal: https://github.com/llvm/llvm-project/issues/101558
130+
# XFAIL: Metal
131131

132132
# RUN: split-file %s %t
133133
# RUN: %dxc_target -T vs_6_6 -E mainVS -Fo %t-vs.o %t/vertex.hlsl

test/Feature/Textures/Texture2D.GetDimensions.test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ Results:
114114
...
115115
#--- end
116116

117-
# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558
118-
# XFAIL: DirectX || Metal
117+
# Unimplemented: Metal: https://github.com/llvm/llvm-project/issues/101558
118+
# XFAIL: Metal
119119

120120
# Bug https://github.com/llvm/llvm-project/issues/197837
121121
# XFAIL: Clang && Vulkan

0 commit comments

Comments
 (0)