Implement GL_NV_read_depth_stencil
The implementation will perform two readPixels calls, once for each
aspect, and then interleave and pack the result.
Bug: angleproject:4688
Change-Id: I46390df893de50b93e855e9333ffab567215a2bb
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3702686
Reviewed-by: Cody Northrop <cnorthrop@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Cody Northrop <cnorthrop@google.com>
Auto-Submit: Constantine Shablya <constantine.shablya@collabora.com>
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index 5f90bdf..983a1ce 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -486,6 +486,8 @@
return getDepthAttachment();
case GL_STENCIL_INDEX_OES:
return getStencilOrDepthStencilAttachment();
+ case GL_DEPTH_STENCIL_OES:
+ return getDepthStencilAttachment();
default:
return getReadAttachment();
}
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index fbcb7e6..f3646b8 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -850,6 +850,7 @@
{
case GL_DEPTH_COMPONENT:
case GL_STENCIL_INDEX_OES:
+ case GL_DEPTH_STENCIL_OES:
return getDepthStencilRenderTarget();
default:
return getColorReadRenderTarget();
@@ -864,6 +865,8 @@
return VK_IMAGE_ASPECT_DEPTH_BIT;
case GL_STENCIL_INDEX_OES:
return VK_IMAGE_ASPECT_STENCIL_BIT;
+ case GL_DEPTH_STENCIL_OES:
+ return vk::IMAGE_ASPECT_DEPTH_STENCIL;
default:
return VK_IMAGE_ASPECT_COLOR_BIT;
}
diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
index cc3fe80..92007f9 100644
--- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
@@ -1127,6 +1127,11 @@
// GL_KHR_parallel_shader_compile
mNativeExtensions.parallelShaderCompileKHR = false;
+ // GL_NV_read_depth, GL_NV_read_depth_stencil, GL_NV_read_stencil
+ mNativeExtensions.readDepthNV = true;
+ mNativeExtensions.readDepthStencilNV = true;
+ mNativeExtensions.readStencilNV = true;
+
// GL_QCOM_shading_rate
mNativeExtensions.shadingRateQCOM = mFeatures.supportsFragmentShadingRate.enabled;
}
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index b35f048..e5c49eb 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -8623,22 +8623,19 @@
if (angleFormat.redBits > 0 || angleFormat.blueBits > 0 || angleFormat.greenBits > 0 ||
angleFormat.alphaBits > 0 || angleFormat.luminanceBits > 0)
{
- aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
+ aspectFlags = static_cast<VkImageAspectFlagBits>(aspectFlags | VK_IMAGE_ASPECT_COLOR_BIT);
}
else
{
if (angleFormat.depthBits > 0)
{
- if (angleFormat.stencilBits != 0)
- {
- // TODO (anglebug.com/4688) Support combined depth stencil for GetTexImage
- WARN() << "Unable to pull stencil from combined depth/stencil for GetTexImage";
- }
- aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
+ aspectFlags =
+ static_cast<VkImageAspectFlagBits>(aspectFlags | VK_IMAGE_ASPECT_DEPTH_BIT);
}
- else if (angleFormat.stencilBits > 0)
+ if (angleFormat.stencilBits > 0)
{
- aspectFlags = VK_IMAGE_ASPECT_STENCIL_BIT;
+ aspectFlags =
+ static_cast<VkImageAspectFlagBits>(aspectFlags | VK_IMAGE_ASPECT_STENCIL_BIT);
}
}
@@ -8766,6 +8763,105 @@
void *pixels)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ImageHelper::readPixels");
+
+ const angle::Format &readFormat = getActualFormat();
+
+ if (readFormat.depthBits == 0)
+ {
+ copyAspectFlags =
+ static_cast<VkImageAspectFlagBits>(copyAspectFlags & ~VK_IMAGE_ASPECT_DEPTH_BIT);
+ }
+ if (readFormat.stencilBits == 0)
+ {
+ copyAspectFlags =
+ static_cast<VkImageAspectFlagBits>(copyAspectFlags & ~VK_IMAGE_ASPECT_STENCIL_BIT);
+ }
+
+ if (copyAspectFlags == IMAGE_ASPECT_DEPTH_STENCIL)
+ {
+ const angle::Format &depthFormat =
+ GetDepthStencilImageToBufferFormat(readFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
+ const angle::Format &stencilFormat =
+ GetDepthStencilImageToBufferFormat(readFormat, VK_IMAGE_ASPECT_STENCIL_BIT);
+
+ int depthOffset = 0;
+ int stencilOffset = 0;
+ switch (readFormat.id)
+ {
+ case angle::FormatID::D24_UNORM_S8_UINT:
+ depthOffset = 1;
+ stencilOffset = 0;
+ break;
+
+ case angle::FormatID::D32_FLOAT_S8X24_UINT:
+ depthOffset = 0;
+ stencilOffset = 4;
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ ASSERT(depthOffset > 0 || stencilOffset > 0);
+ ASSERT(depthOffset + depthFormat.depthBits / 8 <= readFormat.pixelBytes);
+ ASSERT(stencilOffset + stencilFormat.stencilBits / 8 <= readFormat.pixelBytes);
+
+ // Read the depth values, tightly-packed
+ angle::MemoryBuffer depthBuffer;
+ ANGLE_VK_CHECK_ALLOC(contextVk,
+ depthBuffer.resize(depthFormat.pixelBytes * area.width * area.height));
+ ANGLE_TRY(
+ readPixelsImpl(contextVk, area,
+ PackPixelsParams(area, depthFormat, depthFormat.pixelBytes * area.width,
+ false, nullptr, 0),
+ VK_IMAGE_ASPECT_DEPTH_BIT, levelGL, layer, depthBuffer.data()));
+
+ // Read the stencil values, tightly-packed
+ angle::MemoryBuffer stencilBuffer;
+ ANGLE_VK_CHECK_ALLOC(
+ contextVk, stencilBuffer.resize(stencilFormat.pixelBytes * area.width * area.height));
+ ANGLE_TRY(readPixelsImpl(
+ contextVk, area,
+ PackPixelsParams(area, stencilFormat, stencilFormat.pixelBytes * area.width, false,
+ nullptr, 0),
+ VK_IMAGE_ASPECT_STENCIL_BIT, levelGL, layer, stencilBuffer.data()));
+
+ // Interleave them together
+ angle::MemoryBuffer readPixelBuffer;
+ ANGLE_VK_CHECK_ALLOC(
+ contextVk, readPixelBuffer.resize(readFormat.pixelBytes * area.width * area.height));
+ readPixelBuffer.fill(0);
+ for (int i = 0; i < area.width * area.height; i++)
+ {
+ uint8_t *readPixel = readPixelBuffer.data() + i * readFormat.pixelBytes;
+ memcpy(readPixel + depthOffset, depthBuffer.data() + i * depthFormat.pixelBytes,
+ depthFormat.depthBits / 8);
+ memcpy(readPixel + stencilOffset, stencilBuffer.data() + i * stencilFormat.pixelBytes,
+ stencilFormat.stencilBits / 8);
+ }
+
+ // Pack the interleaved depth and stencil into user-provided
+ // destination, per user's pack pixels params
+
+ // The compressed format path in packReadPixelBuffer isn't applicable
+ // to our case, let's make extra sure we won't hit it
+ ASSERT(!readFormat.isBlock);
+ return packReadPixelBuffer(contextVk, area, packPixelsParams, readFormat, readFormat,
+ readPixelBuffer.data(), levelGL, pixels);
+ }
+
+ return readPixelsImpl(contextVk, area, packPixelsParams, copyAspectFlags, levelGL, layer,
+ pixels);
+}
+
+angle::Result ImageHelper::readPixelsImpl(ContextVk *contextVk,
+ const gl::Rectangle &area,
+ const PackPixelsParams &packPixelsParams,
+ VkImageAspectFlagBits copyAspectFlags,
+ gl::LevelIndex levelGL,
+ uint32_t layer,
+ void *pixels)
+{
RendererVk *renderer = contextVk->getRenderer();
// If the source image is multisampled, we need to resolve it into a temporary image before
@@ -8931,8 +9027,27 @@
// TODO(jmadill): Don't block on asynchronous readback.
ANGLE_TRY(contextVk->finishImpl(RenderPassClosureReason::GLReadPixels));
- if (readFormat->isBlock)
+ return packReadPixelBuffer(contextVk, area, packPixelsParams, getActualFormat(), *readFormat,
+ readPixelBuffer, levelGL, pixels);
+}
+
+angle::Result ImageHelper::packReadPixelBuffer(ContextVk *contextVk,
+ const gl::Rectangle &area,
+ const PackPixelsParams &packPixelsParams,
+ const angle::Format &readFormat,
+ const angle::Format &aspectFormat,
+ const uint8_t *readPixelBuffer,
+ gl::LevelIndex levelGL,
+ void *pixels)
+{
+ const vk::Format &vkFormat = contextVk->getRenderer()->getFormat(readFormat.id);
+ const gl::InternalFormat &storageFormatInfo =
+ vkFormat.getInternalFormatInfo(readFormat.componentType);
+
+ if (readFormat.isBlock)
{
+ ASSERT(readFormat == aspectFormat);
+
const LevelIndex levelVk = toVkLevel(levelGL);
gl::Extents levelExtents = getLevelExtents(levelVk);
@@ -8950,13 +9065,13 @@
void *mapPtr = nullptr;
ANGLE_TRY(packBufferVk->mapImpl(contextVk, GL_MAP_WRITE_BIT, &mapPtr));
uint8_t *dst = static_cast<uint8_t *>(mapPtr) + reinterpret_cast<ptrdiff_t>(pixels);
- PackPixels(packPixelsParams, *readFormat, area.width * readFormat->pixelBytes,
+ PackPixels(packPixelsParams, aspectFormat, area.width * aspectFormat.pixelBytes,
readPixelBuffer, static_cast<uint8_t *>(dst));
ANGLE_TRY(packBufferVk->unmapImpl(contextVk));
}
else
{
- PackPixels(packPixelsParams, *readFormat, area.width * readFormat->pixelBytes,
+ PackPixels(packPixelsParams, aspectFormat, area.width * aspectFormat.pixelBytes,
readPixelBuffer, static_cast<uint8_t *>(pixels));
}
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index 8f8210e..ab72342 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -1637,6 +1637,9 @@
Image,
};
+constexpr VkImageAspectFlagBits IMAGE_ASPECT_DEPTH_STENCIL =
+ static_cast<VkImageAspectFlagBits>(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
+
bool FormatHasNecessaryFeature(RendererVk *renderer,
angle::FormatID formatID,
VkImageTiling tilingMode,
@@ -2472,6 +2475,23 @@
VkFormat imageFormat,
VkImageUsageFlags usageFlags) const;
+ angle::Result readPixelsImpl(ContextVk *contextVk,
+ const gl::Rectangle &area,
+ const PackPixelsParams &packPixelsParams,
+ VkImageAspectFlagBits copyAspectFlags,
+ gl::LevelIndex levelGL,
+ uint32_t layer,
+ void *pixels);
+
+ angle::Result packReadPixelBuffer(ContextVk *contextVk,
+ const gl::Rectangle &area,
+ const PackPixelsParams &packPixelsParams,
+ const angle::Format &readFormat,
+ const angle::Format &aspectFormat,
+ const uint8_t *readPixelBuffer,
+ gl::LevelIndex levelGL,
+ void *pixels);
+
bool canCopyWithTransformForReadPixels(const PackPixelsParams &packPixelsParams,
const angle::Format *readFormat);
// Vulkan objects.
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 5c2cf3a..2b93949 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -167,8 +167,7 @@
const gl::InternalFormat *info,
GLenum type)
{
- bool supportsReadDepthNV = (context->getExtensions().readDepthNV && (info->depthBits > 0) &&
- (info->componentCount == 1));
+ bool supportsReadDepthNV = context->getExtensions().readDepthNV && (info->depthBits > 0);
switch (type)
{
case GL_UNSIGNED_SHORT:
@@ -185,7 +184,7 @@
GLenum type)
{
return context->getExtensions().readDepthNV && (type == GL_FLOAT) &&
- context->getExtensions().depthBufferFloat2NV && (info->componentCount == 1);
+ context->getExtensions().depthBufferFloat2NV;
}
bool ValidReadPixelsFormatType(const Context *context,
@@ -210,6 +209,9 @@
return context->getExtensions().readStencilNV && (type == GL_UNSIGNED_BYTE);
case GL_DEPTH_COMPONENT:
return ValidReadPixelsUnsignedNormalizedDepthType(context, info, type);
+ case GL_DEPTH_STENCIL_OES:
+ return context->getExtensions().readDepthStencilNV &&
+ (type == GL_UNSIGNED_INT_24_8_OES) && info->stencilBits > 0;
case GL_RGBX8_ANGLE:
return context->getExtensions().rgbxInternalFormatANGLE &&
(type == GL_UNSIGNED_BYTE);
@@ -234,6 +236,9 @@
return (type == GL_FLOAT);
case GL_DEPTH_COMPONENT:
return ValidReadPixelsFloatDepthType(context, info, type);
+ case GL_DEPTH_STENCIL_OES:
+ return context->getExtensions().readDepthStencilNV &&
+ type == GL_FLOAT_32_UNSIGNED_INT_24_8_REV && info->stencilBits > 0;
default:
return false;
}
@@ -7386,6 +7391,7 @@
readBuffer = readFramebuffer->getDepthAttachment();
break;
case GL_STENCIL_INDEX_OES:
+ case GL_DEPTH_STENCIL_OES:
readBuffer = readFramebuffer->getStencilOrDepthStencilAttachment();
break;
default:
@@ -7442,6 +7448,7 @@
{
case GL_DEPTH_COMPONENT:
case GL_STENCIL_INDEX_OES:
+ case GL_DEPTH_STENCIL_OES:
// Only rely on ValidReadPixelsFormatType for depth/stencil formats
break;
default:
diff --git a/src/tests/capture_replay_tests/capture_replay_expectations.txt b/src/tests/capture_replay_tests/capture_replay_expectations.txt
index a775e9e..92d8299 100644
--- a/src/tests/capture_replay_tests/capture_replay_expectations.txt
+++ b/src/tests/capture_replay_tests/capture_replay_expectations.txt
@@ -168,3 +168,6 @@
7449 : MultisampleTest.Line/* = SKIP_FOR_CAPTURE
7449 : MultisampleTest.Point/* = SKIP_FOR_CAPTURE
7449 : MultisampleTest.Triangle/* = SKIP_FOR_CAPTURE
+
+7483 : MultisampleTest.AlphaToSampleCoverage/* = SKIP_FOR_CAPTURE
+7483 : MultisampleTestES3.ResolveToFBO/* = SKIP_FOR_CAPTURE
diff --git a/src/tests/gl_tests/DepthStencilFormatsTest.cpp b/src/tests/gl_tests/DepthStencilFormatsTest.cpp
index 0ebfc36..73f94ae 100644
--- a/src/tests/gl_tests/DepthStencilFormatsTest.cpp
+++ b/src/tests/gl_tests/DepthStencilFormatsTest.cpp
@@ -172,6 +172,11 @@
bool hasReadDepthSupport() const { return IsGLExtensionEnabled("GL_NV_read_depth"); }
+ bool hasReadDepthStencilSupport() const
+ {
+ return IsGLExtensionEnabled("GL_NV_read_depth_stencil");
+ }
+
bool hasReadStencilSupport() const { return IsGLExtensionEnabled("GL_NV_read_stencil"); }
bool hasFloatDepthSupport() const { return IsGLExtensionEnabled("GL_NV_depth_buffer_float2"); }
@@ -356,8 +361,7 @@
GLubyte actualPixels[destRes * destRes * 8];
glReadPixels(0, 0, destRes, destRes, GL_DEPTH_COMPONENT,
hasFloatDepth ? GL_FLOAT : GL_UNSIGNED_SHORT, actualPixels);
- // NV_read_depth and NV_read_stencil do not support packed depth/stencil
- if (hasReadDepthSupport() && type.format != GL_DEPTH_STENCIL)
+ if (hasReadDepthSupport())
{
EXPECT_GL_NO_ERROR();
if (hasFloatDepth)
@@ -371,17 +375,12 @@
}
else
{
- auto scale = [](float f) {
- return static_cast<uint16_t>(
- static_cast<float>(std::numeric_limits<uint16_t>::max()) * f);
- };
-
constexpr unsigned short kEpsilon = 2;
const unsigned short *pixels = reinterpret_cast<const unsigned short *>(actualPixels);
- ASSERT_NEAR(pixels[0], scale(d00), kEpsilon);
- ASSERT_NEAR(pixels[0 + destRes], scale(d01), kEpsilon);
- ASSERT_NEAR(pixels[1], scale(d10), kEpsilon);
- ASSERT_NEAR(pixels[1 + destRes], scale(d11), kEpsilon);
+ ASSERT_NEAR(pixels[0], gl::unorm<16>(d00), kEpsilon);
+ ASSERT_NEAR(pixels[0 + destRes], gl::unorm<16>(d01), kEpsilon);
+ ASSERT_NEAR(pixels[1], gl::unorm<16>(d10), kEpsilon);
+ ASSERT_NEAR(pixels[1 + destRes], gl::unorm<16>(d11), kEpsilon);
}
}
else
@@ -395,7 +394,39 @@
{
EXPECT_GL_NO_ERROR();
ASSERT_TRUE((actualPixels[0] == 1) && (actualPixels[1] == 2) &&
- (actualPixels[0 + destRes] == 3) && (actualPixels[1 + destRes] = 4));
+ (actualPixels[0 + destRes] == 3) && (actualPixels[1 + destRes] == 4));
+ }
+ else
+ {
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+ }
+
+ ASSERT(!hasFloatDepth);
+
+ glReadPixels(0, 0, destRes, destRes, GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES,
+ actualPixels);
+ if (hasReadDepthStencilSupport())
+ {
+ EXPECT_GL_NO_ERROR();
+
+ struct Pixel
+ {
+ uint32_t x;
+
+ uint32_t d24() const { return x >> 8; }
+
+ uint8_t s8() const { return x & 0xff; }
+ };
+
+ constexpr unsigned short kEpsilon = 2;
+ const Pixel *pixels = reinterpret_cast<const Pixel *>(actualPixels);
+
+ ASSERT_NEAR(pixels[0].d24(), gl::unorm<24>(d00), kEpsilon);
+ ASSERT_NEAR(pixels[0 + destRes].d24(), gl::unorm<24>(d01), kEpsilon);
+ ASSERT_NEAR(pixels[1].d24(), gl::unorm<24>(d10), kEpsilon);
+ ASSERT_NEAR(pixels[1 + destRes].d24(), gl::unorm<24>(d11), kEpsilon);
+ ASSERT_TRUE((pixels[0].s8() == 1) && (pixels[1].s8() == 2) &&
+ (pixels[0 + destRes].s8() == 3) && (pixels[1 + destRes].s8() == 4));
}
else
{
@@ -445,6 +476,53 @@
depthStencilReadbackCase(type);
}
+// Verify that packed D/S readPixels with a D32_FLOAT_S8X24_UINT attachment
+TEST_P(DepthStencilFormatsTestES3, DepthStencilReadback_DepthFloatStencil)
+{
+ ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_packed_depth_stencil") ||
+ !IsGLExtensionEnabled("GL_NV_depth_buffer_float2") ||
+ !IsGLExtensionEnabled("GL_NV_read_depth") ||
+ !IsGLExtensionEnabled("GL_NV_read_depth_stencil") ||
+ !IsGLExtensionEnabled("GL_NV_read_stencil"));
+
+ GLFramebuffer FBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, FBO);
+ ASSERT_GL_NO_ERROR();
+
+ GLTexture depthStencilTexture;
+ glBindTexture(GL_TEXTURE_2D, depthStencilTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH32F_STENCIL8, getWindowWidth(), getWindowHeight(), 0,
+ GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, nullptr);
+ ASSERT_GL_NO_ERROR();
+
+ glBindRenderbuffer(GL_RENDERBUFFER, depthStencilTexture);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH32F_STENCIL8, getWindowWidth(),
+ getWindowHeight());
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
+ depthStencilTexture);
+ ASSERT_GL_NO_ERROR();
+
+ constexpr float kDepthClearValue = 0.123f;
+ constexpr uint8_t kStencilClearValue = 0x42;
+
+ glClearDepthf(kDepthClearValue);
+ glClearStencil(kStencilClearValue);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ ASSERT_GL_NO_ERROR();
+
+ struct
+ {
+ float depth;
+ uint8_t stencil;
+ char unused[3];
+ } pixel = {};
+ glReadPixels(0, 0, 1, 1, GL_DEPTH_STENCIL_OES, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, &pixel);
+ EXPECT_GL_NO_ERROR();
+
+ EXPECT_FLOAT_EQ(pixel.depth, kDepthClearValue);
+ EXPECT_EQ(pixel.stencil, kStencilClearValue);
+}
+
// This test will initialize a depth texture and then render with it and verify
// pixel correctness.
// This is modeled after webgl-depth-texture.html