Skip to content

Commit edcb2f0

Browse files
authored
Add implementation for 'uploadTextureData' in metal (#6443)
Close issue: #6386. - Implement uploadTextureData in metal - Fix a bug in 'copyTextureToBuffer' in metal The last parameter for Metal::copyFromTexture is the 'destinationBytesPerImage', but previous implementation fill in the destination size which is wrong in 3-D texture.
1 parent 4e89718 commit edcb2f0

File tree

2 files changed

+108
-3
lines changed

2 files changed

+108
-3
lines changed

tools/gfx/metal/metal-command-encoder.cpp

+105-2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ void ResourceCommandEncoder::copyTextureToBuffer(
127127
assert(srcSubresource.mipLevelCount <= 1);
128128

129129
auto encoder = m_commandBuffer->getMetalBlitCommandEncoder();
130+
auto& desc = *static_cast<TextureResourceImpl*>(src)->getDesc();
131+
const TextureResource::Extents mipSize = calcMipSize(desc.size, srcSubresource.mipLevel);
132+
Size bytesPerImage = mipSize.height * dstRowStride;
133+
130134
encoder->copyFromTexture(
131135
static_cast<TextureResourceImpl*>(src)->m_texture.get(),
132136
srcSubresource.baseArrayLayer,
@@ -136,7 +140,7 @@ void ResourceCommandEncoder::copyTextureToBuffer(
136140
static_cast<BufferResourceImpl*>(dst)->m_buffer.get(),
137141
dstOffset,
138142
dstRowStride,
139-
dstSize);
143+
extent.depth == 1 ? 0 : bytesPerImage);
140144
}
141145

142146
void ResourceCommandEncoder::uploadBufferData(
@@ -156,7 +160,106 @@ void ResourceCommandEncoder::uploadTextureData(
156160
ITextureResource::SubresourceData* subResourceData,
157161
GfxCount subResourceDataCount)
158162
{
159-
SLANG_UNIMPLEMENTED_X("uploadTextureData");
163+
auto dstTexture = static_cast<TextureResourceImpl*>(dst);
164+
auto& desc = *dstTexture->getDesc();
165+
166+
// Calculate buffer size needed
167+
Size bufferSize = 0;
168+
FormatInfo sizeInfo;
169+
gfxGetFormatInfo(desc.format, &sizeInfo);
170+
MTL::PixelFormat pixelFormat = MetalUtil::translatePixelFormat(desc.format);
171+
bool isCompressed = gfxIsCompressedFormat(desc.format);
172+
173+
Size rowAlignment =
174+
isCompressed
175+
? 1
176+
: m_commandBuffer->m_device->m_device->minimumLinearTextureAlignmentForPixelFormat(
177+
pixelFormat);
178+
179+
for (GfxIndex i = 0; i < subResourceRange.mipLevelCount; ++i)
180+
{
181+
GfxIndex currentLevel = subResourceRange.mipLevel + i;
182+
const TextureResource::Extents mipSize = calcMipSize(desc.size, currentLevel);
183+
184+
auto rowSizeInBytes = (mipSize.width + sizeInfo.blockWidth - 1) / sizeInfo.blockWidth *
185+
sizeInfo.blockSizeInBytes;
186+
rowSizeInBytes = (rowSizeInBytes + rowAlignment - 1) & ~(rowAlignment - 1);
187+
188+
auto numRows = (mipSize.height + sizeInfo.blockHeight - 1) / sizeInfo.blockHeight;
189+
bufferSize += (rowSizeInBytes * numRows) * mipSize.depth;
190+
}
191+
bufferSize *= subResourceRange.layerCount;
192+
193+
// Create staging buffer
194+
NS::SharedPtr<MTL::Buffer> stagingBuffer = NS::TransferPtr(
195+
m_commandBuffer->m_device->m_device->newBuffer(bufferSize, MTL::ResourceStorageModeShared));
196+
197+
if (!stagingBuffer)
198+
return;
199+
200+
auto encoder = m_commandBuffer->getMetalBlitCommandEncoder();
201+
if (!encoder)
202+
return;
203+
204+
// Copy data to staging buffer and then to texture
205+
Size bufferOffset = 0;
206+
for (GfxIndex i = 0; i < subResourceRange.layerCount; i++)
207+
{
208+
// We only allocate staging buffer with size of one slice.
209+
GfxIndex currentSlice = subResourceRange.baseArrayLayer + i;
210+
uint8_t* bufferData = (uint8_t*)stagingBuffer->contents();
211+
Size dstOffset = 0;
212+
213+
for (GfxIndex j = 0; j < subResourceRange.mipLevelCount; j++)
214+
{
215+
GfxIndex currentLevel = subResourceRange.mipLevel + j;
216+
const auto& subresourceData = subResourceData[j];
217+
const TextureResource::Extents mipSize = calcMipSize(desc.size, currentLevel);
218+
219+
auto rowSizeInBytes = (mipSize.width + sizeInfo.blockWidth - 1) / sizeInfo.blockWidth *
220+
sizeInfo.blockSizeInBytes;
221+
auto rowSizeInBytesAligned = (rowSizeInBytes + rowAlignment - 1) & ~(rowAlignment - 1);
222+
223+
auto numRows = (mipSize.height + sizeInfo.blockHeight - 1) / sizeInfo.blockHeight;
224+
225+
const uint8_t* srcData = (const uint8_t*)subresourceData.data;
226+
if (rowSizeInBytesAligned == rowSizeInBytes)
227+
{
228+
// If the row size is already aligned, we can copy the data directly.
229+
memcpy(bufferData + dstOffset, srcData, rowSizeInBytes * numRows * mipSize.depth);
230+
}
231+
else
232+
{
233+
for (GfxIndex k = 0; k < mipSize.depth; ++k)
234+
{
235+
for (GfxIndex row = 0; row < numRows; ++row)
236+
{
237+
// Copy data to staging buffer, note that the staging buffer has to have the
238+
// same alignment as the texture while the src data doesn't have such
239+
// requirement, therefore we have to copy the data row by row. We don't care
240+
// about the content of the alignment padding.
241+
memcpy(bufferData + dstOffset, srcData, rowSizeInBytes);
242+
dstOffset += rowSizeInBytesAligned;
243+
srcData += rowSizeInBytes;
244+
}
245+
}
246+
}
247+
248+
// Copy from staging buffer to texture
249+
encoder->copyFromBuffer(
250+
stagingBuffer.get(),
251+
bufferOffset,
252+
rowSizeInBytesAligned,
253+
rowSizeInBytesAligned * numRows,
254+
MTL::Size(mipSize.width, mipSize.height, mipSize.depth),
255+
dstTexture->m_texture.get(),
256+
currentSlice,
257+
currentLevel,
258+
MTL::Origin(offset.x, offset.y, offset.z));
259+
260+
bufferOffset += rowSizeInBytes * numRows * mipSize.depth;
261+
}
262+
}
160263
}
161264

162265
void ResourceCommandEncoder::bufferBarrier(

tools/gfx/metal/metal-device.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,9 @@ Result DeviceImpl::getTextureAllocationInfo(
331331
FormatInfo formatInfo;
332332
gfxGetFormatInfo(desc.format, &formatInfo);
333333
MTL::PixelFormat pixelFormat = MetalUtil::translatePixelFormat(desc.format);
334-
Size alignment = m_device->minimumLinearTextureAlignmentForPixelFormat(pixelFormat);
334+
bool isCompressed = gfxIsCompressedFormat(desc.format);
335+
Size alignment =
336+
isCompressed ? 1 : m_device->minimumLinearTextureAlignmentForPixelFormat(pixelFormat);
335337
Size size = 0;
336338
ITextureResource::Extents extents = desc.size;
337339
extents.width = extents.width ? extents.width : 1;

0 commit comments

Comments
 (0)