Commit bb35bb4e by Jamie Madill Committed by Commit Bot

Vulkan: Implement simple case ANGLE_get_image.

A couple cases are left unimplemented: Incomplete/unused Textures. This leads to a slightly more tricky implementation. Since we need to read back from the staging buffer we will need to flush the Image contents to a temporary buffer. Depth/stencil readback. Requires a more complex pixel packing step. 3D/Cube/2D Array readback. Also requires a more complex packing step. Bug: angleproject:3944 Change-Id: Ic5d9a606177ba7e3e5ab945feb5f555afa11741f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1879964 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent ec5c3d5f
......@@ -458,10 +458,14 @@ angle::Result FramebufferVk::readPixels(const gl::Context *context,
return angle::Result::Continue;
}
const gl::State &glState = contextVk->getState();
gl::Buffer *packBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelPack);
GLuint outputSkipBytes = 0;
PackPixelsParams params;
ANGLE_TRY(vk::ImageHelper::GetReadPixelsParams(contextVk, format, type, area, clippedArea,
&params, &outputSkipBytes));
ANGLE_TRY(vk::ImageHelper::GetReadPixelsParams(contextVk, glState.getPackState(), packBuffer,
format, type, area, clippedArea, &params,
&outputSkipBytes));
if (contextVk->isViewportFlipEnabledForReadFBO())
{
......
......@@ -225,16 +225,22 @@ void RenderbufferVk::releaseImage(ContextVk *contextVk)
mImageViews.release(renderer);
}
const gl::InternalFormat &RenderbufferVk::getImplementationSizedFormat() const
{
GLenum internalFormat = mImage->getFormat().actualImageFormat().glInternalFormat;
return gl::GetSizedInternalFormatInfo(internalFormat);
}
GLenum RenderbufferVk::getColorReadFormat(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
const gl::InternalFormat &sizedFormat = getImplementationSizedFormat();
return sizedFormat.format;
}
GLenum RenderbufferVk::getColorReadType(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
const gl::InternalFormat &sizedFormat = getImplementationSizedFormat();
return sizedFormat.type;
}
angle::Result RenderbufferVk::getRenderbufferImage(const gl::Context *context,
......@@ -244,7 +250,13 @@ angle::Result RenderbufferVk::getRenderbufferImage(const gl::Context *context,
GLenum type,
void *pixels)
{
UNIMPLEMENTED();
return angle::Result::Continue;
// Storage not defined.
if (!mImage || !mImage->valid())
return angle::Result::Continue;
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(mImage->flushAllStagedUpdates(contextVk));
return mImage->readPixelsForGetImage(contextVk, packState, packBuffer, 0, 0, format, type,
pixels);
}
} // namespace rx
......@@ -68,6 +68,8 @@ class RenderbufferVk : public RenderbufferImpl
size_t width,
size_t height);
const gl::InternalFormat &getImplementationSizedFormat() const;
bool mOwnsImage;
vk::ImageHelper *mImage;
vk::ImageViewHelper mImageViews;
......
......@@ -1269,9 +1269,7 @@ angle::Result TextureVk::ensureImageInitialized(ContextVk *contextVk, ImageMipLe
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
const gl::Extents &baseLevelExtents = baseLevelDesc.size;
const uint32_t levelCount = getMipLevelCount(mipLevels);
const vk::Format &format =
contextVk->getRenderer()->getFormat(baseLevelDesc.format.info->sizedInternalFormat);
const vk::Format &format = getBaseLevelFormat(contextVk->getRenderer());
return ensureImageInitializedImpl(contextVk, baseLevelExtents, levelCount, format);
}
......@@ -1704,16 +1702,34 @@ angle::Result TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk,
return angle::Result::Continue;
}
const gl::InternalFormat &TextureVk::getImplementationSizedFormat(const gl::Context *context) const
{
GLenum sizedFormat = GL_NONE;
if (mImage && mImage->valid())
{
sizedFormat = mImage->getFormat().actualImageFormat().glInternalFormat;
}
else
{
ContextVk *contextVk = vk::GetImpl(context);
const vk::Format &format = getBaseLevelFormat(contextVk->getRenderer());
sizedFormat = format.actualImageFormat().glInternalFormat;
}
return gl::GetSizedInternalFormatInfo(sizedFormat);
}
GLenum TextureVk::getColorReadFormat(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
const gl::InternalFormat &sizedFormat = getImplementationSizedFormat(context);
return sizedFormat.format;
}
GLenum TextureVk::getColorReadType(const gl::Context *context)
{
UNIMPLEMENTED();
return GL_NONE;
const gl::InternalFormat &sizedFormat = getImplementationSizedFormat(context);
return sizedFormat.type;
}
angle::Result TextureVk::getTexImage(const gl::Context *context,
......@@ -1725,7 +1741,26 @@ angle::Result TextureVk::getTexImage(const gl::Context *context,
GLenum type,
void *pixels)
{
ContextVk *contextVk = vk::GetImpl(context);
if (mImage && mImage->valid())
{
ANGLE_TRY(mImage->flushAllStagedUpdates(contextVk));
size_t layer =
gl::IsCubeMapFaceTarget(target) ? gl::CubeMapTextureTargetToFaceIndex(target) : 0;
return mImage->readPixelsForGetImage(contextVk, packState, packBuffer, level,
static_cast<uint32_t>(layer), format, type, pixels);
}
// Incomplete or unused texture. Will require a staging texture.
// TODO(http://anglebug.com/4058): Incomplete texture readback.
UNIMPLEMENTED();
return angle::Result::Continue;
}
const vk::Format &TextureVk::getBaseLevelFormat(RendererVk *renderer) const
{
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
return renderer->getFormat(baseLevelDesc.format.info->sizedInternalFormat);
}
} // namespace rx
......@@ -342,6 +342,8 @@ class TextureVk : public TextureImpl
void onStagingBufferChange() { onStateChange(angle::SubjectMessage::SubjectChanged); }
angle::Result changeLevels(ContextVk *contextVk, GLuint baseLevel, GLuint maxLevel);
const gl::InternalFormat &getImplementationSizedFormat(const gl::Context *context) const;
const vk::Format &getBaseLevelFormat(RendererVk *renderer) const;
bool mOwnsImage;
......
......@@ -2958,6 +2958,8 @@ angle::Result ImageHelper::copyImageDataToBuffer(ContextVk *contextVk,
// static
angle::Result ImageHelper::GetReadPixelsParams(ContextVk *contextVk,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
GLenum format,
GLenum type,
const gl::Rectangle &area,
......@@ -2965,9 +2967,6 @@ angle::Result ImageHelper::GetReadPixelsParams(ContextVk *contextVk,
PackPixelsParams *paramsOut,
GLuint *skipBytesOut)
{
const gl::State &glState = contextVk->getState();
const gl::PixelPackState &packState = glState.getPackState();
const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type);
GLuint outputPitch = 0;
......@@ -2983,10 +2982,48 @@ angle::Result ImageHelper::GetReadPixelsParams(ContextVk *contextVk,
const angle::Format &angleFormat = GetFormatFromFormatType(format, type);
*paramsOut = PackPixelsParams(clippedArea, angleFormat, outputPitch, packState.reverseRowOrder,
glState.getTargetBuffer(gl::BufferBinding::PixelPack), 0);
packBuffer, 0);
return angle::Result::Continue;
}
angle::Result ImageHelper::readPixelsForGetImage(ContextVk *contextVk,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
uint32_t level,
uint32_t layer,
GLenum format,
GLenum type,
void *pixels)
{
const angle::Format &angleFormat = GetFormatFromFormatType(format, type);
// Depth/stencil readback is not yet implemented.
// TODO(http://anglebug.com/4058): Depth/stencil readback.
if (angleFormat.depthBits > 0 || angleFormat.stencilBits > 0)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
PackPixelsParams params;
GLuint outputSkipBytes = 0;
uint32_t width = std::max(1u, mExtents.width >> level);
uint32_t height = std::max(1u, mExtents.height >> level);
gl::Rectangle area(0, 0, width, height);
ANGLE_TRY(GetReadPixelsParams(contextVk, packState, packBuffer, format, type, area, area,
&params, &outputSkipBytes));
// Use a temporary staging buffer. Could be optimized.
vk::RendererScoped<vk::DynamicBuffer> stagingBuffer(contextVk->getRenderer());
stagingBuffer.get().init(contextVk->getRenderer(), VK_BUFFER_USAGE_TRANSFER_DST_BIT, 1,
kStagingBufferSize, true);
return readPixels(contextVk, area, params, VK_IMAGE_ASPECT_COLOR_BIT, level, 0,
static_cast<uint8_t *>(pixels) + outputSkipBytes, &stagingBuffer.get());
}
angle::Result ImageHelper::readPixels(ContextVk *contextVk,
const gl::Rectangle &area,
const PackPixelsParams &packPixelsParams,
......@@ -3101,12 +3138,10 @@ angle::Result ImageHelper::readPixels(ContextVk *contextVk,
// created with the host coherent bit.
ANGLE_TRY(stagingBuffer->invalidate(contextVk));
const gl::State &glState = contextVk->getState();
gl::Buffer *packBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelPack);
if (packBuffer != nullptr)
if (packPixelsParams.packBuffer)
{
// Must map the PBO in order to read its contents (and then unmap it later)
BufferVk *packBufferVk = GetImpl(packBuffer);
BufferVk *packBufferVk = GetImpl(packPixelsParams.packBuffer);
void *mapPtr = nullptr;
ANGLE_TRY(packBufferVk->mapImpl(contextVk, &mapPtr));
uint8_t *dest = static_cast<uint8_t *>(mapPtr) + reinterpret_cast<ptrdiff_t>(pixels);
......
......@@ -892,6 +892,8 @@ class ImageHelper final : public CommandGraphResource
uint8_t **outDataPtr);
static angle::Result GetReadPixelsParams(ContextVk *contextVk,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
GLenum format,
GLenum type,
const gl::Rectangle &area,
......@@ -899,6 +901,15 @@ class ImageHelper final : public CommandGraphResource
PackPixelsParams *paramsOut,
GLuint *skipBytesOut);
angle::Result readPixelsForGetImage(ContextVk *contextVk,
const gl::PixelPackState &packState,
gl::Buffer *packBuffer,
uint32_t level,
uint32_t layer,
GLenum format,
GLenum type,
void *pixels);
angle::Result readPixels(ContextVk *contextVk,
const gl::Rectangle &area,
const PackPixelsParams &packPixelsParams,
......
......@@ -37,31 +37,37 @@ class GetImageTestNoExtensions : public ANGLETest
GetImageTestNoExtensions() { setExtensionsEnabled(false); }
};
GLTexture InitSimpleTexture()
GLTexture InitTextureWithSize(uint32_t size, void *pixelData)
{
std::vector<GLColor> pixelData(kSize * kSize, GLColor::red);
// Create a simple texture.
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return tex;
}
GLRenderbuffer InitSimpleRenderbuffer()
GLTexture InitSimpleTexture()
{
std::vector<GLColor> pixelData(kSize * kSize, GLColor::red);
return InitTextureWithSize(kSize, pixelData.data());
}
GLRenderbuffer InitRenderbufferWithSize(uint32_t size)
{
// Create a simple renderbuffer.
GLRenderbuffer renderbuf;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, kSize, kSize);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, size, size);
return renderbuf;
}
GLRenderbuffer InitSimpleRenderbuffer()
{
return InitRenderbufferWithSize(kSize);
}
// Test validation for the extension functions.
TEST_P(GetImageTest, NegativeAPI)
{
......@@ -139,6 +145,71 @@ TEST_P(GetImageTest, NegativeAPI)
}
}
// Simple test for GetTexImage
TEST_P(GetImageTest, GetTexImage)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr uint32_t kSmallSize = 2;
std::vector<GLColor> expectedData = {GLColor::red, GLColor::blue, GLColor::green,
GLColor::yellow};
glViewport(0, 0, kSmallSize, kSmallSize);
// Draw once with simple texture.
GLTexture tex = InitTextureWithSize(kSmallSize, expectedData.data());
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::vector<GLColor> actualData(kSmallSize * kSmallSize);
glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
// Simple test for GetRenderbufferImage
TEST_P(GetImageTest, GetRenderbufferImage)
{
// Verify the extension is enabled.
ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
constexpr uint32_t kSmallSize = 2;
std::vector<GLColor> expectedData = {GLColor::red, GLColor::blue, GLColor::green,
GLColor::yellow};
glViewport(0, 0, kSmallSize, kSmallSize);
// Set up a simple Framebuffer with a Renderbuffer.
GLRenderbuffer renderbuffer = InitRenderbufferWithSize(kSmallSize);
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Draw once with simple texture.
GLTexture tex = InitTextureWithSize(kSmallSize, expectedData.data());
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
ASSERT_GL_NO_ERROR();
// Pack pixels tightly.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Verify GetImage.
std::vector<GLColor> actualData(kSmallSize * kSmallSize);
glGetRenderbufferImageANGLE(GL_RENDERBUFFER, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
EXPECT_GL_NO_ERROR();
EXPECT_EQ(expectedData, actualData);
}
// Verifies that the extension enums and entry points are invalid when the extension is disabled.
TEST_P(GetImageTestNoExtensions, EntryPointsInactive)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment