Skip to content

Commit b31e096

Browse files
Add createTextureWithData function. (#1307)
Another small PR as a step towards a fully generic API for creating resources and uploading data. The function is not actually being called in this PR, since doing that would make the PR very large, create a lot of extra work, and make it difficult to review as well. This all builds on top of building blocks that have been layed out while doing the same logic for buffers, so none of the logic here is really new other than just variants of already existing code.
1 parent e22dcf7 commit b31e096

6 files changed

Lines changed: 186 additions & 0 deletions

File tree

include/API/Device.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ class Device {
257257
virtual llvm::Expected<std::unique_ptr<Texture>>
258258
createTexture(std::string Name, const TextureCreateDesc &Desc) = 0;
259259

260+
// The row stride required when uploading data to (or reading back from) a
261+
// texture created with the given description, via an upload buffer.
262+
virtual uint32_t
263+
getTextureUploadRowStrideInBytes(const TextureCreateDesc &Desc) const = 0;
264+
260265
virtual llvm::Expected<std::unique_ptr<RenderPass>>
261266
createRenderPass(const RenderPassDesc &Desc) = 0;
262267

@@ -324,6 +329,12 @@ createBufferWithData(Device &Dev, std::string Name,
324329
size_t SizeInBytes, ComputeEncoder *Encoder,
325330
std::unique_ptr<offloadtest::Buffer> *OutUploadBuffer);
326331

332+
llvm::Expected<std::unique_ptr<offloadtest::Texture>>
333+
createTextureWithData(Device &Dev, std::string Name,
334+
const TextureCreateDesc &Desc, const void *Data,
335+
size_t SizeInBytes, ComputeEncoder *Encoder,
336+
std::unique_ptr<offloadtest::Buffer> *OutUploadBuffer);
337+
327338
// TLAS handles come in pre-allocated because the caller's binding loop
328339
// stamps the AS pointer into descriptor bundles before this helper runs;
329340
// BLAS handles are allocated inline since BLASes aren't user-bindable.

include/API/Encoder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
namespace offloadtest {
2323

2424
class Buffer;
25+
class Texture;
2526
class PipelineState;
2627
class AccelerationStructure;
2728
struct BLASBuildRequest;
@@ -97,6 +98,11 @@ class ComputeEncoder : public CommandEncoder {
9798
Buffer &Dst, size_t DstOffset,
9899
size_t Size) = 0;
99100

101+
/// Copy a buffer into a texture. The caller is expected to set up correct
102+
/// striding using the stride acquired from
103+
/// `Device::getTextureUploadRowStrideInBytes`.
104+
virtual llvm::Error copyBufferToTexture(Buffer &Src, Texture &Dst) = 0;
105+
100106
/// Build a batch of acceleration structures in a single barrier slot. All
101107
/// items in `Items` must be independent — no item may depend on another's
102108
/// build output. Backends may issue this as one native batch call (Vulkan)

lib/API/DX/Device.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,42 @@ class DXComputeEncoder : public offloadtest::ComputeEncoder {
806806
return llvm::Error::success();
807807
}
808808

809+
llvm::Error copyBufferToTexture(Buffer &Src, Texture &Dst) override {
810+
auto &DXSrc = llvm::cast<DXBuffer>(Src);
811+
auto &DXDst = llvm::cast<DXTexture>(Dst);
812+
813+
if (DXSrc.PreferredState != D3D12_RESOURCE_STATE_COPY_SOURCE)
814+
CB.addResourceTransition(DXSrc.Buffer.Get(), DXSrc.PreferredState,
815+
D3D12_RESOURCE_STATE_COPY_SOURCE);
816+
817+
if (DXDst.PreferredState != D3D12_RESOURCE_STATE_COPY_DEST)
818+
CB.addResourceTransition(DXDst.Resource.Get(), DXDst.PreferredState,
819+
D3D12_RESOURCE_STATE_COPY_DEST);
820+
CB.flushBarrier();
821+
822+
const uint32_t ElementSize = getFormatSizeInBytes(DXDst.Desc.Fmt);
823+
const D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint{
824+
0,
825+
CD3DX12_SUBRESOURCE_FOOTPRINT(
826+
getDXGIFormat(DXDst.Desc.Fmt), DXDst.Desc.Width, DXDst.Desc.Height,
827+
1, getAlignedTexturePitch(DXDst.Desc.Width, ElementSize))};
828+
const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(DXDst.Resource.Get(), 0);
829+
const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(DXSrc.Buffer.Get(), Footprint);
830+
CB.CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr);
831+
832+
if (DXSrc.PreferredState != D3D12_RESOURCE_STATE_COPY_SOURCE)
833+
CB.addResourceTransition(DXSrc.Buffer.Get(),
834+
D3D12_RESOURCE_STATE_COPY_SOURCE,
835+
DXSrc.PreferredState);
836+
837+
if (DXDst.PreferredState != D3D12_RESOURCE_STATE_COPY_DEST)
838+
CB.addResourceTransition(DXDst.Resource.Get(),
839+
D3D12_RESOURCE_STATE_COPY_DEST,
840+
DXDst.PreferredState);
841+
842+
return llvm::Error::success();
843+
}
844+
809845
// Defined out-of-line below — needs DXDevice's full type for access to the
810846
// ID3D12Device5 entry point and helper allocators.
811847
llvm::Error batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) override;
@@ -1740,6 +1776,11 @@ class DXDevice : public offloadtest::Device {
17401776
return Tex;
17411777
}
17421778

1779+
uint32_t getTextureUploadRowStrideInBytes(
1780+
const TextureCreateDesc &Desc) const override {
1781+
return getAlignedTexturePitch(Desc.Width, getFormatSizeInBytes(Desc.Fmt));
1782+
}
1783+
17431784
static llvm::Expected<std::unique_ptr<offloadtest::Device>>
17441785
create(ComPtr<IDXCoreAdapter> Adapter, const DeviceConfig &Config) {
17451786
ComPtr<ID3D12DeviceX> Device;

lib/API/Device.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,59 @@ offloadtest::createBufferWithData(
297297

298298
return Buffer;
299299
}
300+
301+
llvm::Expected<std::unique_ptr<offloadtest::Texture>>
302+
offloadtest::createTextureWithData(
303+
Device &Dev, std::string Name, const TextureCreateDesc &Desc,
304+
const void *Data, size_t SizeInBytes, ComputeEncoder *Encoder,
305+
std::unique_ptr<offloadtest::Buffer> *OutUploadBuffer) {
306+
307+
const uint64_t PackedRowStrideInBytes =
308+
Desc.Width * getFormatSizeInBytes(Desc.Fmt);
309+
if (SizeInBytes < PackedRowStrideInBytes * Desc.Height)
310+
return llvm::createStringError(
311+
"Data upload is not enough for texture size.");
312+
313+
auto TextureOrErr = Dev.createTexture(Name, Desc);
314+
if (!TextureOrErr)
315+
return TextureOrErr.takeError();
316+
auto Texture = std::move(*TextureOrErr);
317+
318+
if (OutUploadBuffer == nullptr)
319+
return llvm::createStringError("An upload buffer is required to create a "
320+
"GpuOnly texture with data.");
321+
322+
const uint64_t TexRowStrideInBytes =
323+
Dev.getTextureUploadRowStrideInBytes(Desc);
324+
const uint64_t UploadBufferSizeInBytes =
325+
(Desc.Height - 1) * TexRowStrideInBytes + PackedRowStrideInBytes;
326+
327+
// Create Upload buffer
328+
const BufferCreateDesc UploadDesc = BufferCreateDesc::uploadBuffer();
329+
const std::string UploadBufferName = Name + " (Upload Buffer)";
330+
auto UploadBufferOrErr =
331+
Dev.createBuffer(UploadBufferName, UploadDesc, UploadBufferSizeInBytes);
332+
if (!UploadBufferOrErr)
333+
return UploadBufferOrErr.takeError();
334+
*OutUploadBuffer = std::move(*UploadBufferOrErr);
335+
336+
auto MappedPtrOrErr = (*OutUploadBuffer)->map();
337+
if (!MappedPtrOrErr)
338+
return MappedPtrOrErr.takeError();
339+
340+
uint8_t *DstPtr = (uint8_t *)*MappedPtrOrErr;
341+
const uint8_t *SrcPtr = (const uint8_t *)Data;
342+
343+
for (uint32_t Y = 0; Y < Desc.Height; ++Y) {
344+
memcpy(DstPtr, SrcPtr, PackedRowStrideInBytes);
345+
DstPtr += TexRowStrideInBytes;
346+
SrcPtr += PackedRowStrideInBytes;
347+
}
348+
(*OutUploadBuffer)->unmap();
349+
350+
// Copy Buffer to Texture
351+
if (auto Err = Encoder->copyBufferToTexture(**OutUploadBuffer, *Texture))
352+
return Err;
353+
354+
return Texture;
355+
}

lib/API/MTL/MTLDevice.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,33 @@ class MTLComputeEncoder : public offloadtest::ComputeEncoder {
587587
return llvm::Error::success();
588588
}
589589

590+
llvm::Error copyBufferToTexture(offloadtest::Buffer &Src,
591+
offloadtest::Texture &Dst) override {
592+
if (auto Err = ensureBlitEncoder())
593+
return Err;
594+
auto &MTLSrc = static_cast<MTLBuffer &>(Src);
595+
auto &MTLDst = static_cast<MTLTexture &>(Dst);
596+
597+
// The upload buffer is laid out with a tightly packed row stride matching
598+
// getTextureUploadRowStrideInBytes(), so the source bytes-per-row is the
599+
// texture width times the element size.
600+
const size_t ElemSize = getFormatSizeInBytes(MTLDst.Desc.Fmt);
601+
const size_t RowBytes = MTLDst.Desc.Width * ElemSize;
602+
const size_t ImageBytes = RowBytes * MTLDst.Desc.Height;
603+
const MTL::Size CopySize(MTLDst.Desc.Width, MTLDst.Desc.Height, 1);
604+
605+
insertDebugSignpost(llvm::formatv("copyBufferToTexture {0} -> {1}",
606+
MTLSrc.Name, MTLDst.Name)
607+
.str());
608+
BlitEnc->copyFromBuffer(MTLSrc.Buf, /*sourceOffset=*/0, RowBytes,
609+
ImageBytes, CopySize, MTLDst.Tex,
610+
/*destinationSlice=*/0, /*destinationLevel=*/0,
611+
MTL::Origin(0, 0, 0));
612+
addBarrierScope(MTL::BarrierScopeTextures);
613+
614+
return llvm::Error::success();
615+
}
616+
590617
// Defined out-of-line below — needs MTLDevice's full type for access to the
591618
// MTL::Device handle (used to allocate scratch and instance buffers).
592619
llvm::Error batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) override;
@@ -1708,6 +1735,11 @@ class MTLDevice : public offloadtest::Device {
17081735
return std::make_unique<MTLTexture>(Tex, Name, Desc);
17091736
}
17101737

1738+
uint32_t getTextureUploadRowStrideInBytes(
1739+
const TextureCreateDesc &Desc) const override {
1740+
return Desc.Width * getFormatSizeInBytes(Desc.Fmt);
1741+
}
1742+
17111743
llvm::Expected<std::unique_ptr<offloadtest::CommandBuffer>>
17121744
createCommandBuffer() override {
17131745
auto CBOrErr = MTLCommandBuffer::create(GraphicsQueue.Queue);

lib/API/VK/Device.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,38 @@ class VKComputeEncoder : public offloadtest::ComputeEncoder {
903903
return llvm::Error::success();
904904
}
905905

906+
llvm::Error copyBufferToTexture(offloadtest::Buffer &Src,
907+
offloadtest::Texture &Dst) override {
908+
auto &VKSrc = llvm::cast<VulkanBuffer>(Src);
909+
auto &VKDst = llvm::cast<VulkanTexture>(Dst);
910+
911+
CB.addImageTransition(CB.PendingSrcAccess, /*SrcAccessMask*/
912+
VK_ACCESS_TRANSFER_WRITE_BIT, /*DstAccessMask*/
913+
VKDst.preferredLayoutOrUndefined(), /*OldLayout*/
914+
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, /*NewLayout*/
915+
VKDst);
916+
VKDst.IsInUndefinedLayout = false;
917+
918+
CB.addPendingBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
919+
VK_ACCESS_TRANSFER_READ_BIT |
920+
VK_ACCESS_TRANSFER_WRITE_BIT);
921+
CB.flushBarrier();
922+
923+
insertDebugSignpost(
924+
llvm::formatv("copyTextureToBuffer {0} -> {1}", VKSrc.Name, VKDst.Name)
925+
.str());
926+
vkCmdCopyBufferToImage(CB.CmdBuffer, VKSrc.Buffer, VKDst.Image,
927+
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, nullptr);
928+
929+
CB.addImageTransition(VK_ACCESS_TRANSFER_WRITE_BIT, /*SrcAccessMask*/
930+
VK_ACCESS_NONE, /*DstAccessMask*/
931+
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, /*OldLayout*/
932+
VKDst.preferredLayoutOrUndefined(), /*NewLayout*/
933+
VKDst);
934+
935+
return llvm::Error::success();
936+
}
937+
906938
// Defined out-of-line below — needs VulkanDevice's full type for access to
907939
// the device-loaded ray-tracing entry points and helpers.
908940
llvm::Error batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) override;
@@ -2618,6 +2650,14 @@ class VulkanDevice : public offloadtest::Device {
26182650
return Tex;
26192651
}
26202652

2653+
uint32_t getTextureUploadRowStrideInBytes(
2654+
const TextureCreateDesc &Desc) const override {
2655+
const uint64_t TightRow =
2656+
uint64_t(Desc.Width) * getFormatSizeInBytes(Desc.Fmt);
2657+
return static_cast<uint32_t>(llvm::alignTo(
2658+
TightRow, Props.limits.optimalBufferCopyRowPitchAlignment));
2659+
}
2660+
26212661
const Capabilities &getCapabilities() override {
26222662
if (Caps.empty())
26232663
queryCapabilities();

0 commit comments

Comments
 (0)