Skip to content

Commit 8b09d75

Browse files
committed
[VK] Fixed reading stencil components from a depth-stencil texture in Vulkan.
Vulkan cannot copy combined depth-stencil components into a buffer from an image directly. Instead, copy the stencil components into the first part of the buffer and then the depth component into the second part. Then perform two ConvertImageBuffer() calls to extract each component separately into the output buffer. Also ignore VKSwapChain::Present() when nothing has been rendered since last call. TODO: This also needs to be fixed for WriteTexture(). And the copy texture/buffer commands will need to address this as well, likely with a built-in compute shader similar to D3D11, but only specifically for depth-and-stencil formats.
1 parent c2aa84c commit 8b09d75

File tree

8 files changed

+332
-154
lines changed

8 files changed

+332
-154
lines changed

include/LLGL/ImageFlags.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ If this is less than 2, no multi-threading is used. If this is equal to \c LLGL_
139139
the number of threads will be determined by the workload and the available CPU cores the system supports (e.g. 4 on a quad-core processor).
140140
Note that this does not guarantee the maximum number of threads the system supports if the workload does not demand it. By default 0.
141141
\param[in] copyUnchangedImage Specifies whether to copy the source buffer into the destination buffer if no conversion was necessary. By default false.
142+
\param[in] depthMask Specifies a bitmask for the depth components. This determines what depth bits will be overrideen in the destination buffer. By default 0xFFFFFFFF.
143+
\param[in] stencilMask Specifies a bitmask for the stencil components. This determines what stencil bits will be overrideen in the destination buffer. By default 0xFFFFFFFF.
142144
143145
\return Number of bytes that have been written to the destination buffer.
144146
If this is 0, no conversion was necessary and the destination buffer is not modified.
@@ -160,7 +162,9 @@ LLGL_EXPORT std::size_t ConvertImageBuffer(
160162
const MutableImageView& dstImageView,
161163
const Extent3D& extent,
162164
unsigned threadCount = 0,
163-
bool copyUnchangedImage = false
165+
bool copyUnchangedImage = false,
166+
std::uint32_t depthMask = ~0u,
167+
std::uint32_t stencilMask = ~0u
164168
);
165169

166170
/**

sources/Core/ImageFlags.cpp

Lines changed: 151 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -520,25 +520,110 @@ static void WriteRGBAFormattedVariant(
520520
TransferRGBAFormattedVariantColor(dstFormat, dataType, dstBuffer, idx, value);
521521
}
522522

523+
// Worker thread procedure for the "ConvertImageBufferFormat" function
524+
static void ConvertImageBufferFormatWorker(
525+
const ImageView& srcImageView,
526+
const MutableImageView& dstImageView,
527+
const ImageOperationMemoryInfo& memoryInfo,
528+
const Extent3D& extent,
529+
std::size_t begin,
530+
std::size_t end)
531+
{
532+
VariantConstBuffer srcBuffer = srcImageView.data;
533+
VariantBuffer dstBuffer = dstImageView.data;
534+
535+
ApplyPaddingOffset(srcBuffer, dstBuffer, begin, memoryInfo, extent);
536+
537+
const std::uint32_t layerSize = extent.width * extent.height;
538+
539+
/* Initialize default variant color (0, 0, 0, 1) */
540+
VariantColor colorValue{ UninitializeTag{} };
541+
542+
SetVariantMinMax(srcImageView.dataType, colorValue.r, true);
543+
SetVariantMinMax(srcImageView.dataType, colorValue.g, true);
544+
SetVariantMinMax(srcImageView.dataType, colorValue.b, true);
545+
SetVariantMinMax(srcImageView.dataType, colorValue.a, false);
546+
547+
for_subrange(i, begin, end)
548+
{
549+
/* Apply source and destination stride when passing an edge */
550+
AdvancePaddingOffsetAtEdge(srcBuffer, dstBuffer, i, begin, memoryInfo, extent.width, layerSize);
551+
552+
/* Read RGBA variant from source buffer */
553+
ReadRGBAFormattedVariant(srcImageView.format, srcImageView.dataType, srcBuffer, i, colorValue);
554+
555+
/* Write RGBA variant to destination buffer */
556+
WriteRGBAFormattedVariant(dstImageView.format, dstImageView.dataType, dstBuffer, i, colorValue);
557+
}
558+
}
559+
560+
static std::size_t ConvertImageBufferFormat(
561+
const ImageView& srcImageView,
562+
const MutableImageView& dstImageView,
563+
const Extent3D& extent,
564+
unsigned threadCount)
565+
{
566+
LLGL_ASSERT(srcImageView.dataType == dstImageView.dataType);
567+
568+
/* Validate destination buffer size */
569+
const std::size_t numPixels = extent.width * extent.height * extent.depth;
570+
571+
ImageOperationMemoryInfo memoryInfo = {};
572+
GetImageOperationMemoryInfo(memoryInfo, srcImageView, dstImageView, extent);
573+
574+
LLGL_ASSERT(
575+
dstImageView.dataSize >= memoryInfo.dstImageSize,
576+
"destination image buffer is too small to convert image format; expected %zu, but %zu was specified",
577+
memoryInfo.dstImageSize, dstImageView.dataSize
578+
);
579+
580+
/* Get variant buffer for source and destination images */
581+
DoConcurrentRange(
582+
std::bind(
583+
ConvertImageBufferFormatWorker,
584+
std::cref(srcImageView),
585+
std::cref(dstImageView),
586+
std::cref(memoryInfo),
587+
std::cref(extent),
588+
std::placeholders::_1,
589+
std::placeholders::_2
590+
),
591+
numPixels,
592+
threadCount
593+
);
594+
595+
return memoryInfo.dstImageSize;
596+
}
597+
598+
static float UnpackD24UNorm(std::uint32_t value)
599+
{
600+
return (static_cast<float>(value) / static_cast<float>(0x00FFFFFFu));
601+
}
602+
523603
static void ReadDepthStencilValue(
524604
ImageFormat srcFormat, DataType dataType, const VariantConstBuffer& srcBuffer, std::size_t idx, DepthStencilValue& value)
525605
{
526606
if (srcFormat == ImageFormat::Depth && dataType == DataType::UInt16)
527607
{
528-
/* Read D16UNorm format: Decompress 16-bit float */
529-
value.depth = DecompressFloat16(srcBuffer.uint16[idx]);
608+
/* Read D16UNorm format: Decode 16-bit unsigned normalized depth */
609+
value.depth = static_cast<float>(srcBuffer.uint16[idx]) / static_cast<float>(0x0000FFFFu);
530610
}
531-
else if (srcFormat == ImageFormat::DepthStencil && dataType == DataType::UInt32)
611+
else if (srcFormat == ImageFormat::Depth && dataType == DataType::Float16)
532612
{
533-
/* Read D24UNormS8UInt format: Decompress 24-bit float and 8-bit unsigned integer */
534-
value.depth = static_cast<float>(srcBuffer.uint32[idx] & 0x00FFFFFFu) / static_cast<float>(0x00FFFFFFu);
535-
value.stencil = srcBuffer.uint32[idx] >> 24;
613+
/* Read R16Float format: Decompress 16-bit float */
614+
value.depth = DecompressFloat16(srcBuffer.uint16[idx]);
536615
}
537616
else if (srcFormat == ImageFormat::Depth && dataType == DataType::Float32)
538617
{
539618
/* Read D32Float format: Copy 32-bit float */
540619
value.depth = srcBuffer.real32[idx];
541620
}
621+
else if (srcFormat == ImageFormat::DepthStencil && dataType == DataType::UInt32)
622+
{
623+
/* Read D24UNormS8UInt format: Decode 24-bit unsigned normalized depth and 8-bit unsigned stencil */
624+
value.depth = UnpackD24UNorm(srcBuffer.uint32[idx] & 0x00FFFFFF);
625+
value.stencil = (srcBuffer.uint32[idx] >> 24);
626+
}
542627
else if (srcFormat == ImageFormat::DepthStencil && dataType == DataType::Float32)
543628
{
544629
/* Read D32FloatS8X24UInt format: Copy 32-bit float and 8-bit unsigned integer */
@@ -557,25 +642,34 @@ static void ReadDepthStencilValue(
557642
}
558643
}
559644

645+
static std::uint32_t PackD24UNorm(float value)
646+
{
647+
return static_cast<std::uint32_t>(value * static_cast<float>(0x00FFFFFFu));
648+
}
649+
560650
static void WriteDepthStencilValue(
561651
ImageFormat dstFormat, DataType dataType, VariantBuffer& dstBuffer, std::size_t idx, const DepthStencilValue& value)
562652
{
563653
if (dstFormat == ImageFormat::Depth && dataType == DataType::UInt16)
564654
{
565-
/* Write D16UNorm format: Compress 16-bit float */
566-
dstBuffer.uint16[idx] = CompressFloat16(value.depth);
655+
/* Write D16UNorm format: Encode 16-bit unsigned normalized depth */
656+
dstBuffer.uint16[idx] = static_cast<std::uint16_t>(value.depth * static_cast<float>(0x0000FFFFu));
567657
}
568-
else if (dstFormat == ImageFormat::DepthStencil && dataType == DataType::UInt32)
658+
else if (dstFormat == ImageFormat::Depth && dataType == DataType::Float16)
569659
{
570-
/* Write D24UNormS8UInt format: Decompress 24-bit float and 8-bit unsigned integer */
571-
const std::uint32_t depth24 = static_cast<std::uint32_t>(value.depth * static_cast<float>(0x00FFFFFFu));
572-
dstBuffer.uint32[idx] = ((value.stencil & 0x000000FFu)) << 24 | (depth24 & 0x00FFFFFFu);
660+
/* Write R16Float format: Compress 16-bit float */
661+
dstBuffer.uint16[idx] = CompressFloat16(value.depth);
573662
}
574663
else if (dstFormat == ImageFormat::Depth && dataType == DataType::Float32)
575664
{
576665
/* Write D32Float format: Copy 32-bit float */
577666
dstBuffer.real32[idx] = value.depth;
578667
}
668+
else if (dstFormat == ImageFormat::DepthStencil && dataType == DataType::UInt32)
669+
{
670+
/* Write D24UNormS8UInt format: Encode 24-bit unsigned normalized depth and 8-bit unsigned stencil */
671+
dstBuffer.uint32[idx] = (value.stencil << 24) | (PackD24UNorm(value.depth) & 0x00FFFFFF);
672+
}
579673
else if (dstFormat == ImageFormat::DepthStencil && dataType == DataType::Float32)
580674
{
581675
/* Read D32FloatS8X24UInt format: Copy 32-bit float and 8-bit unsigned integer */
@@ -594,12 +688,31 @@ static void WriteDepthStencilValue(
594688
}
595689
}
596690

597-
// Worker thread procedure for the "ConvertImageBufferFormat" function
598-
static void ConvertImageBufferFormatWorker(
691+
static float UInt32AsFloat32(std::uint32_t value)
692+
{
693+
return *reinterpret_cast<const float*>(&value);
694+
}
695+
696+
static std::uint32_t Float32AsUInt32(float value)
697+
{
698+
return *reinterpret_cast<const std::uint32_t*>(&value);
699+
}
700+
701+
static void MergeDepthStencilValues(
702+
DepthStencilValue& dst, const DepthStencilValue& src, std::uint32_t srcDepthMask, std::uint32_t srcStencilMask)
703+
{
704+
dst.depth = UInt32AsFloat32((Float32AsUInt32(dst.depth) & ~srcDepthMask) | (Float32AsUInt32(src.depth) & srcDepthMask));
705+
dst.stencil = (dst.stencil & ~srcStencilMask) | (src.stencil & srcStencilMask);
706+
}
707+
708+
// Worker thread procedure for the "ConvertDepthStencilImageBufferFormat" function
709+
static void ConvertDepthStencilImageBufferFormatWorker(
599710
const ImageView& srcImageView,
600711
const MutableImageView& dstImageView,
601712
const ImageOperationMemoryInfo& memoryInfo,
602713
const Extent3D& extent,
714+
std::uint32_t depthMask,
715+
std::uint32_t stencilMask,
603716
std::size_t begin,
604717
std::size_t end)
605718
{
@@ -610,54 +723,36 @@ static void ConvertImageBufferFormatWorker(
610723

611724
const std::uint32_t layerSize = extent.width * extent.height;
612725

613-
if (IsDepthOrStencilFormat(srcImageView.format))
614-
{
615-
/* Initialize default depth-stencil value (0, 0) */
616-
DepthStencilValue depthStencilValue{ 0.0f, 0u };
726+
/* Initialize default depth-stencil value (0, 0) */
727+
DepthStencilValue depthStencilValueSrc{ 0.0f, 0u };
728+
DepthStencilValue depthStencilValueDst{ 0.0f, 0u };
617729

618-
for_subrange(i, begin, end)
619-
{
620-
/* Apply source and destination stride when passing an edge */
621-
AdvancePaddingOffsetAtEdge(srcBuffer, dstBuffer, i, begin, memoryInfo, extent.width, layerSize);
622-
623-
/* Read depth-stencil value from source buffer */
624-
ReadDepthStencilValue(srcImageView.format, srcImageView.dataType, srcBuffer, i, depthStencilValue);
625-
626-
/* Write depth-stencil value to destination buffer */
627-
WriteDepthStencilValue(dstImageView.format, dstImageView.dataType, dstBuffer, i, depthStencilValue);
628-
}
629-
}
630-
else
730+
for_subrange(i, begin, end)
631731
{
632-
/* Initialize default variant color (0, 0, 0, 1) */
633-
VariantColor colorValue{ UninitializeTag{} };
732+
/* Apply source and destination stride when passing an edge */
733+
AdvancePaddingOffsetAtEdge(srcBuffer, dstBuffer, i, begin, memoryInfo, extent.width, layerSize);
634734

635-
SetVariantMinMax(srcImageView.dataType, colorValue.r, true);
636-
SetVariantMinMax(srcImageView.dataType, colorValue.g, true);
637-
SetVariantMinMax(srcImageView.dataType, colorValue.b, true);
638-
SetVariantMinMax(srcImageView.dataType, colorValue.a, false);
735+
/* Read depth-stencil value from source buffer */
736+
ReadDepthStencilValue(srcImageView.format, srcImageView.dataType, srcBuffer, i, depthStencilValueSrc);
737+
ReadDepthStencilValue(dstImageView.format, dstImageView.dataType, VariantConstBuffer{ dstBuffer.uint8 }, i, depthStencilValueDst);
639738

640-
for_subrange(i, begin, end)
641-
{
642-
/* Apply source and destination stride when passing an edge */
643-
AdvancePaddingOffsetAtEdge(srcBuffer, dstBuffer, i, begin, memoryInfo, extent.width, layerSize);
739+
/* Apply masking */
740+
MergeDepthStencilValues(depthStencilValueDst, depthStencilValueSrc, depthMask, stencilMask);
644741

645-
/* Read RGBA variant from source buffer */
646-
ReadRGBAFormattedVariant(srcImageView.format, srcImageView.dataType, srcBuffer, i, colorValue);
647-
648-
/* Write RGBA variant to destination buffer */
649-
WriteRGBAFormattedVariant(dstImageView.format, dstImageView.dataType, dstBuffer, i, colorValue);
650-
}
742+
/* Write depth-stencil value to destination buffer */
743+
WriteDepthStencilValue(dstImageView.format, dstImageView.dataType, dstBuffer, i, depthStencilValueDst);
651744
}
652745
}
653746

654-
static std::size_t ConvertImageBufferFormat(
747+
static std::size_t ConvertDepthStencilImageBufferFormat(
655748
const ImageView& srcImageView,
656749
const MutableImageView& dstImageView,
657750
const Extent3D& extent,
751+
std::uint32_t depthMask,
752+
std::uint32_t stencilMask,
658753
unsigned threadCount)
659754
{
660-
LLGL_ASSERT(IsDepthOrStencilFormat(srcImageView.format) || srcImageView.dataType == dstImageView.dataType);
755+
LLGL_ASSERT(IsDepthOrStencilFormat(srcImageView.format));
661756

662757
/* Validate destination buffer size */
663758
const std::size_t numPixels = extent.width * extent.height * extent.depth;
@@ -674,11 +769,13 @@ static std::size_t ConvertImageBufferFormat(
674769
/* Get variant buffer for source and destination images */
675770
DoConcurrentRange(
676771
std::bind(
677-
ConvertImageBufferFormatWorker,
772+
ConvertDepthStencilImageBufferFormatWorker,
678773
std::cref(srcImageView),
679774
std::cref(dstImageView),
680775
std::cref(memoryInfo),
681776
std::cref(extent),
777+
depthMask,
778+
stencilMask,
682779
std::placeholders::_1,
683780
std::placeholders::_2
684781
),
@@ -726,7 +823,9 @@ LLGL_EXPORT std::size_t ConvertImageBuffer(
726823
const MutableImageView& dstImageView,
727824
const Extent3D& extent,
728825
unsigned threadCount,
729-
bool copyUnchangedImage)
826+
bool copyUnchangedImage,
827+
std::uint32_t depthMask,
828+
std::uint32_t stencilMask)
730829
{
731830
/* Validate input parameters */
732831
ValidateSourceImageView(srcImageView);
@@ -736,7 +835,7 @@ LLGL_EXPORT std::size_t ConvertImageBuffer(
736835
if (IsDepthOrStencilFormat(srcImageView.format))
737836
{
738837
/* Convert depth-stencil image format */
739-
return ConvertImageBufferFormat(srcImageView, dstImageView, extent, threadCount);
838+
return ConvertDepthStencilImageBufferFormat(srcImageView, dstImageView, extent, depthMask, stencilMask, threadCount);
740839
}
741840
else if (srcImageView.dataType != dstImageView.dataType && srcImageView.format != dstImageView.format)
742841
{

0 commit comments

Comments
 (0)