Skip to content
Draft
91 changes: 67 additions & 24 deletions lib/API/DX/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,13 @@ getResourceDescription(const Resource &R) {
const D3D12_RESOURCE_DIMENSION Dimension = getDXDimension(R.Kind);
const offloadtest::CPUBuffer &B = *R.BufferPtr;

if (B.OutputProps.MipLevels != 1)
return llvm::createStringError(std::errc::not_supported,
"Multiple mip levels are not yet supported "
"for DirectX textures.");

const DXGI_FORMAT Format =
R.isTexture() ? getDXFormat(B.Format, B.Channels) : DXGI_FORMAT_UNKNOWN;
const uint32_t Width =
R.isTexture() ? B.OutputProps.Width : getUAVBufferSize(R);
const uint32_t Height = R.isTexture() ? B.OutputProps.Height : 1;
const uint16_t MipLevels =
R.isTexture() ? static_cast<uint16_t>(B.OutputProps.MipLevels) : 1;
D3D12_TEXTURE_LAYOUT Layout;

if (R.isTexture())
Expand All @@ -265,8 +262,8 @@ getResourceDescription(const Resource &R) {
const D3D12_RESOURCE_FLAGS Flags =
R.isReadWrite() ? D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
: D3D12_RESOURCE_FLAG_NONE;
const D3D12_RESOURCE_DESC ResDesc = {Dimension, 0, Width, Height, 1, 1,
Format, {1, 0}, Layout, Flags};
const D3D12_RESOURCE_DESC ResDesc = {
Dimension, 0, Width, Height, 1, MipLevels, Format, {1, 0}, Layout, Flags};
return ResDesc;
}

Expand Down Expand Up @@ -294,7 +291,8 @@ static D3D12_SHADER_RESOURCE_VIEW_DESC getSRVDescription(const Resource &R) {
break;
case ResourceKind::Texture2D:
Desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
Desc.Texture2D = D3D12_TEX2D_SRV{0, 1, 0, 0};
Desc.Texture2D = D3D12_TEX2D_SRV{
0, static_cast<UINT>(R.BufferPtr->OutputProps.MipLevels), 0, 0.0f};
break;
case ResourceKind::RWStructuredBuffer:
case ResourceKind::RWBuffer:
Expand Down Expand Up @@ -1559,21 +1557,31 @@ class DXDevice : public offloadtest::Device {
return std::make_unique<DXRenderPass>(Desc);
}

void addResourceUploadCommands(Resource &R, InvocationState &IS,
ComPtr<ID3D12Resource> Destination,
ComPtr<ID3D12Resource> Source) {
void addResourceUploadCommands(
Resource &R, InvocationState &IS, ComPtr<ID3D12Resource> Destination,
ComPtr<ID3D12Resource> Source,
llvm::ArrayRef<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> MipFootprints = {}) {
addUploadBeginBarrier(IS, Destination);
if (R.isTexture()) {
const offloadtest::CPUBuffer &B = *R.BufferPtr;
const D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint{
0, CD3DX12_SUBRESOURCE_FOOTPRINT(
getDXFormat(B.Format, B.Channels), B.OutputProps.Width,
B.OutputProps.Height, 1,
B.OutputProps.Width * B.getElementSize())};
const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(Destination.Get(), 0);
const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(Source.Get(), Footprint);

IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr);
if (!MipFootprints.empty()) {
for (uint32_t Mip = 0; Mip < MipFootprints.size(); ++Mip) {
const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(Destination.Get(), Mip);
const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(Source.Get(),
MipFootprints[Mip]);
IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr);
}
} else {
const D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint{
0, CD3DX12_SUBRESOURCE_FOOTPRINT(
getDXFormat(B.Format, B.Channels), B.OutputProps.Width,
B.OutputProps.Height, 1,
B.OutputProps.Width * B.getElementSize())};
const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(Destination.Get(), 0);
const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(Source.Get(), Footprint);

IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr);
}
} else
IS.CB->CmdList->CopyBufferRegion(Destination.Get(), 0, Source.Get(), 0,
R.size());
Expand Down Expand Up @@ -1655,8 +1663,24 @@ class DXDevice : public offloadtest::Device {
const D3D12_RESOURCE_DESC ResDesc = *ResDescOrErr;
const D3D12_HEAP_PROPERTIES UploadHeapProp =
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
const D3D12_RESOURCE_DESC UploadResDesc =
CD3DX12_RESOURCE_DESC::Buffer(R.size());

const bool IsMipMappedTexture =
R.isTexture() && R.BufferPtr->OutputProps.MipLevels > 1;
llvm::SmallVector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> MipFootprints;
llvm::SmallVector<UINT> MipNumRows;
llvm::SmallVector<UINT64> MipRowSizes;
UINT64 MipTotalBytes = 0;
if (IsMipMappedTexture) {
const uint32_t MipLevels = R.BufferPtr->OutputProps.MipLevels;
MipFootprints.resize(MipLevels);
MipNumRows.resize(MipLevels);
MipRowSizes.resize(MipLevels);
Device->GetCopyableFootprints(&ResDesc, 0, MipLevels, 0,
MipFootprints.data(), MipNumRows.data(),
MipRowSizes.data(), &MipTotalBytes);
}
const D3D12_RESOURCE_DESC UploadResDesc = CD3DX12_RESOURCE_DESC::Buffer(
IsMipMappedTexture ? MipTotalBytes : R.size());

uint32_t RegOffset = 0;

Expand Down Expand Up @@ -1708,14 +1732,33 @@ class DXDevice : public offloadtest::Device {
// Upload data initialization
void *ResDataPtr = nullptr;
if (SUCCEEDED(UploadBuffer->Map(0, NULL, &ResDataPtr))) {
memcpy(ResDataPtr, ResData.get(), R.size());
if (IsMipMappedTexture) {
// Source CPU data is tightly packed for all mips (per docs).
// Destination upload buffer has D3D12-aligned per-mip layout from
// GetCopyableFootprints; copy each mip row-by-row applying the
// possibly-padded row pitch.
const uint8_t *Src = reinterpret_cast<const uint8_t *>(ResData.get());
uint8_t *Dst = static_cast<uint8_t *>(ResDataPtr);
for (uint32_t Mip = 0; Mip < MipFootprints.size(); ++Mip) {
const auto &FP = MipFootprints[Mip];
const size_t TightRowBytes = static_cast<size_t>(MipRowSizes[Mip]);
const size_t PaddedRowPitch =
static_cast<size_t>(FP.Footprint.RowPitch);
for (UINT Row = 0; Row < MipNumRows[Mip]; ++Row)
memcpy(Dst + FP.Offset + Row * PaddedRowPitch,
Src + Row * TightRowBytes, TightRowBytes);
Src += TightRowBytes * MipNumRows[Mip];
}
} else {
memcpy(ResDataPtr, ResData.get(), R.size());
}
UploadBuffer->Unmap(0, nullptr);
} else {
return llvm::createStringError(std::errc::io_error,
"Failed to map SRV upload buffer.");
}

addResourceUploadCommands(R, IS, Buffer, UploadBuffer);
addResourceUploadCommands(R, IS, Buffer, UploadBuffer, MipFootprints);

Bundle.emplace_back(UploadBuffer, Buffer, nullptr, Heap);
RegOffset++;
Expand Down
21 changes: 21 additions & 0 deletions lib/Support/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,27 @@ void MappingTraits<offloadtest::Pipeline>::mapping(IO &I,
if (!R.BufferPtr)
I.setError(Twine("Referenced buffer ") + R.Name + " not found!");
}

// MipLevels only applies to textures; non-texture resources ignore
// the field in the backends.
if (!R.isTexture())
continue;

// Prevent a null deref if buffer resolution above failed; the missing
// buffer was already reported.
if (!R.BufferPtr)
continue;

const int Mips = R.BufferPtr->OutputProps.MipLevels;
if (Mips < 1)
I.setError(Twine("Resource '") + R.Name +
"': MipLevels must be >= 1. Auto-generated mip chains "
"(MipLevels = 0) are not supported by the test "
"framework; per-mip CPU data must be provided");
else if (Mips > 1 && getDescriptorKind(R.Kind) != DescriptorKind::SRV)
I.setError(Twine("Resource '") + R.Name +
"': multiple mip levels are only supported for "
"read-only SRV textures");
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/Feature/Textures/Texture2D.GetDimensions.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Results:
#--- end

# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558
# XFAIL: DirectX || Metal
# XFAIL: (Clang && DirectX) || Metal

# Bug https://github.com/llvm/llvm-project/issues/197837
# XFAIL: Clang && Vulkan
Expand Down
80 changes: 80 additions & 0 deletions test/Feature/Textures/Texture2D.Load.MipMaps.test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#--- source.hlsl
[[vk::binding(0, 0)]] Texture2D<float4> Tex : register(t0);
[[vk::binding(1, 0)]] RWBuffer<float4> Out : register(u0);

[numthreads(1, 1, 1)]
void main() {
// Texture2D::Load(int3(x, y, mip))
// Mip 0 (4x4) is Red
Out[0] = Tex.Load(int3(0, 0, 0));
Out[1] = Tex.Load(int3(3, 3, 0));
// Mip 1 (2x2) is Green
Out[2] = Tex.Load(int3(0, 0, 1));
Out[3] = Tex.Load(int3(1, 1, 1));
// Mip 2 (1x1) is Blue
Out[4] = Tex.Load(int3(0, 0, 2));
}

//--- pipeline.yaml
---
Shaders:
- Stage: Compute
Entry: main

Buffers:
- Name: Tex
Format: Float32
Channels: 4
OutputProps: { Width: 4, Height: 4, Depth: 1, MipLevels: 3 }
Data: [
# --- Mip 0 (4x4 = 16 RGBA texels) - Red ---
1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
# --- Mip 1 (2x2 = 4 RGBA texels) - Green ---
0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,
# --- Mip 2 (1x1 = 1 RGBA texel) - Blue ---
0.0, 0.0, 1.0, 1.0
]

- Name: Out
Format: Float32
Channels: 4
FillSize: 80 # 5 * sizeof(float4)

- Name: Expected
Format: Float32
Channels: 4
Data: [ 1.0, 0.0, 0.0, 1.0, # [0] Red (mip 0 corner)
1.0, 0.0, 0.0, 1.0, # [1] Red (mip 0 corner)
0.0, 1.0, 0.0, 1.0, # [2] Green (mip 1 corner)
0.0, 1.0, 0.0, 1.0, # [3] Green (mip 1 corner)
0.0, 0.0, 1.0, 1.0 ] # [4] Blue (mip 2)

DescriptorSets:
- Resources:
- Name: Tex
Kind: Texture2D
DirectXBinding: { Register: 0, Space: 0 }
VulkanBinding: { Binding: 0 }
- Name: Out
Kind: RWBuffer
DirectXBinding: { Register: 0, Space: 0 }
VulkanBinding: { Binding: 1 }

Results:
- Result: LoadMipsTest
Rule: BufferExact
Actual: Out
Expected: Expected
...
#--- end

# XFAIL: Clang && DirectX
# XFAIL: Metal

# RUN: split-file %s %t
# RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl
# RUN: %offloader %t/pipeline.yaml %t.o
2 changes: 1 addition & 1 deletion test/Feature/Textures/Texture2D.OperatorIndex.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Results:
#--- end

# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558
# XFAIL: DirectX
# XFAIL: Clang && DirectX

# RUN: split-file %s %t
# RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl
Expand Down
4 changes: 2 additions & 2 deletions test/Feature/Textures/Texture2D.mips.OperatorIndex.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ Results:
#--- end

# Unimplemented: https://github.com/llvm/offload-test-suite/issues/1039
# XFAIL: DirectX

# DXC+DX (d3d12/warp) now supported; Clang+DX still has a separate issue.
# XFAIL: Clang && DirectX
# XFAIL: Metal

# RUN: split-file %s %t
Expand Down
Loading