mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Feat] (MG_Backend/DirectVulkan): implement combined depth-stencil texture upload via per-aspect de-interleaved staging copies
This commit is contained in:
@@ -383,6 +383,86 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Combined depth-stencil shadow storage keeps the GL client texel layout produced by the state
|
||||
// layer: packed UNSIGNED_INT_24_8 words (depth in bits 8-31, stencil in bits 0-7) for
|
||||
// Depth24Stencil8/DepthStencil, and FLOAT_32_UNSIGNED_INT_24_8_REV pairs (float32 depth word,
|
||||
// then a word with stencil in bits 0-7) for Depth32FStencil8. VkBufferImageCopy regions must
|
||||
// address exactly one aspect, so de-interleave the shadow texels into a depth region (32-bit
|
||||
// words with depth in bits 0-23 for VK_FORMAT_D24_UNORM_S8_UINT, tightly packed float32 for
|
||||
// VK_FORMAT_D32_SFLOAT_S8_UINT) followed by a tightly packed one-byte-per-texel stencil
|
||||
// region. The blob is padded to a 4-byte multiple so subsequent staging offsets keep the
|
||||
// alignment depth-stencil buffer-image copies require.
|
||||
static Bool DeinterleaveDepthStencilSource(const void* source, SizeT sourceByteSize, const IntVec3& texelSize,
|
||||
TextureInternalFormat internalFormat, VkFormat imageFormat,
|
||||
Vector<Uint8>& outDeinterleavedData, SizeT& outDepthAspectByteSize) {
|
||||
MOBILEGL_ASSERT(source != nullptr, "DeinterleaveDepthStencilSource: source is null");
|
||||
|
||||
const Bool packedDepth24Source = internalFormat == TextureInternalFormat::Depth24Stencil8 ||
|
||||
internalFormat == TextureInternalFormat::DepthStencil;
|
||||
const Bool floatDepthSource = internalFormat == TextureInternalFormat::Depth32FStencil8;
|
||||
if (!packedDepth24Source && !floatDepthSource) {
|
||||
return false;
|
||||
}
|
||||
if (imageFormat != VK_FORMAT_D24_UNORM_S8_UINT && imageFormat != VK_FORMAT_D32_SFLOAT_S8_UINT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SizeT depth = static_cast<SizeT>(std::max(texelSize.z(), 1));
|
||||
const SizeT pixelCount = static_cast<SizeT>(texelSize.x()) * static_cast<SizeT>(texelSize.y()) * depth;
|
||||
MOBILEGL_ASSERT(pixelCount > 0, "DeinterleaveDepthStencilSource: invalid texel size (%d, %d, %d)",
|
||||
texelSize.x(), texelSize.y(), texelSize.z());
|
||||
const SizeT sourcePixelSize = packedDepth24Source ? 4 : 8;
|
||||
if (sourceByteSize != pixelCount * sourcePixelSize) {
|
||||
MGLOG_E("DeinterleaveDepthStencilSource: unexpected source byte size=%zu for pixelCount=%zu "
|
||||
"sourcePixelSize=%zu",
|
||||
sourceByteSize, pixelCount, sourcePixelSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
const SizeT depthAspectByteSize = pixelCount * sizeof(Uint32);
|
||||
const SizeT paddedByteSize = (depthAspectByteSize + pixelCount + 3) / 4 * 4;
|
||||
outDeinterleavedData.resize(paddedByteSize);
|
||||
|
||||
const auto* src = static_cast<const Uint8*>(source);
|
||||
Uint8* depthDst = outDeinterleavedData.data();
|
||||
Uint8* stencilDst = depthDst + depthAspectByteSize;
|
||||
for (SizeT pixel = 0; pixel < pixelCount; ++pixel) {
|
||||
Uint32 depthWord = 0; // depth in bits 0-23 for D24_UNORM, raw float32 bits for D32_SFLOAT
|
||||
Uint8 stencilValue = 0;
|
||||
if (packedDepth24Source) {
|
||||
Uint32 packed = 0;
|
||||
std::memcpy(&packed, src + pixel * 4, sizeof(packed));
|
||||
stencilValue = static_cast<Uint8>(packed & 0xFFu);
|
||||
const Uint32 depth24 = packed >> 8;
|
||||
if (imageFormat == VK_FORMAT_D24_UNORM_S8_UINT) {
|
||||
depthWord = depth24;
|
||||
} else {
|
||||
const Float depthFloat = static_cast<Float>(depth24) / 16777215.0f;
|
||||
std::memcpy(&depthWord, &depthFloat, sizeof(depthWord));
|
||||
}
|
||||
} else {
|
||||
Uint32 stencilWord = 0;
|
||||
std::memcpy(&depthWord, src + pixel * 8, sizeof(depthWord));
|
||||
std::memcpy(&stencilWord, src + pixel * 8 + sizeof(Uint32), sizeof(stencilWord));
|
||||
stencilValue = static_cast<Uint8>(stencilWord & 0xFFu);
|
||||
if (imageFormat == VK_FORMAT_D24_UNORM_S8_UINT) {
|
||||
Float depthFloat = 0.0f;
|
||||
std::memcpy(&depthFloat, &depthWord, sizeof(depthFloat));
|
||||
depthFloat = std::min(std::max(depthFloat, 0.0f), 1.0f);
|
||||
depthWord = static_cast<Uint32>(depthFloat * 16777215.0f + 0.5f);
|
||||
}
|
||||
}
|
||||
std::memcpy(depthDst + pixel * sizeof(Uint32), &depthWord, sizeof(depthWord));
|
||||
stencilDst[pixel] = stencilValue;
|
||||
}
|
||||
for (SizeT pad = depthAspectByteSize + pixelCount; pad < paddedByteSize; ++pad) {
|
||||
outDeinterleavedData[pad] = 0;
|
||||
}
|
||||
|
||||
outDepthAspectByteSize = depthAspectByteSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
static VkComponentSwizzle ToVkComponentSwizzle(TextureSwizzleParam swizzle) {
|
||||
switch (swizzle) {
|
||||
case TextureSwizzleParam::Red:
|
||||
@@ -1353,6 +1433,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Uint32 level = 0;
|
||||
Uint32 baseArrayLayer = 0;
|
||||
SizeT uploadByteSize = 0;
|
||||
SizeT depthAspectByteSize = 0;
|
||||
IntVec3 texelSize = {0, 0, 0};
|
||||
const void* source = nullptr;
|
||||
Vector<Uint8> expandedData;
|
||||
@@ -1368,6 +1449,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(mipmapTexture.GetFormat());
|
||||
|
||||
// Combined depth-stencil images need per-aspect de-interleaved copies: VkBufferImageCopy's
|
||||
// imageSubresource.aspectMask must have exactly one bit set.
|
||||
const VkImageAspectFlags aspectMask = GetAspectMaskForFormat(outResource.format);
|
||||
const Bool isCombinedDepthStencil = (aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 &&
|
||||
(aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
|
||||
|
||||
VkDeviceSize stagingSize = 0;
|
||||
for (const TextureUploadTarget target : targets) {
|
||||
const Uint32 definedMipLevels = GetUploadMipLevelCount(mipmapTexture, target);
|
||||
@@ -1411,6 +1498,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
mipmapTexture.GetExternalIndex(),
|
||||
MG_Util::ConvertTextureUploadTargetToString(target).c_str(), level);
|
||||
uploadItem.uploadByteSize = uploadItem.expandedData.size();
|
||||
} else if (isCombinedDepthStencil) {
|
||||
const Bool deinterleaved = DeinterleaveDepthStencilSource(
|
||||
source, byteSize, texelSize, mipmapTexture.GetFormat(), outResource.format,
|
||||
uploadItem.expandedData, uploadItem.depthAspectByteSize);
|
||||
if (!deinterleaved) {
|
||||
MGLOG_E("UploadDirtyMipLevels: failed to de-interleave depth-stencil textureId=%d target=%s "
|
||||
"level=%u",
|
||||
mipmapTexture.GetExternalIndex(),
|
||||
MG_Util::ConvertTextureUploadTargetToString(target).c_str(), level);
|
||||
return false;
|
||||
}
|
||||
uploadItem.uploadByteSize = uploadItem.expandedData.size();
|
||||
}
|
||||
uploadItems.push_back(Move(uploadItem));
|
||||
if (!uploadItems.back().expandedData.empty()) {
|
||||
@@ -1424,19 +1523,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Combined depth-stencil images need per-aspect de-interleaved copies (VkBufferImageCopy
|
||||
// aspectMask must have exactly one bit set). Until that is implemented, skip the upload
|
||||
// instead of recording an invalid command buffer that kills the process.
|
||||
const VkImageAspectFlags uploadAspectMask = GetAspectMaskForFormat(outResource.format);
|
||||
if ((uploadAspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) && (uploadAspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
||||
MGLOG_E("UploadDirtyMipLevels: skipping unimplemented depth-stencil data upload for textureId=%d",
|
||||
mipmapTexture.GetExternalIndex());
|
||||
for (const auto& item : uploadItems) {
|
||||
mipmapTexture.MarkStorageDirty(item.target, item.level, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VkBuffer stagingBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation stagingAllocation = nullptr;
|
||||
|
||||
@@ -1473,7 +1559,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture)");
|
||||
|
||||
const VkImageAspectFlags aspectMask = GetAspectMaskForFormat(outResource.format);
|
||||
VkPipelineStageFlags uploadSrcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags uploadSrcAccessMask = 0;
|
||||
GetImageTransitionSourceState(outResource.layout, uploadSrcStageMask, uploadSrcAccessMask);
|
||||
@@ -1499,8 +1584,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
copy.imageOffset = {0, 0, 0};
|
||||
copy.imageExtent = {static_cast<Uint32>(item.texelSize.x()), static_cast<Uint32>(item.texelSize.y()),
|
||||
item.texelSize.z() > 0 ? static_cast<Uint32>(item.texelSize.z()) : 1u};
|
||||
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, outResource.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, ©);
|
||||
if (isCombinedDepthStencil) {
|
||||
// One copy per aspect: the staging blob holds the depth region followed by the
|
||||
// tightly packed one-byte-per-texel stencil region.
|
||||
VkBufferImageCopy aspectCopies[2] = {copy, copy};
|
||||
aspectCopies[0].imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
aspectCopies[1].imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
aspectCopies[1].bufferOffset = item.offset + item.depthAspectByteSize;
|
||||
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, outResource.image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 2, aspectCopies);
|
||||
} else {
|
||||
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, outResource.image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
||||
}
|
||||
}
|
||||
|
||||
const VkImageLayout finalLayout = ResolveSampledReadOnlyLayout(aspectMask);
|
||||
|
||||
Reference in New Issue
Block a user