Commit efb45eda by Brandon Schade Committed by Commit Bot

Vulkan: Accelerate Texture PBO updates

If the format of the image and the PBO match, use a vkCmdCopyBufferToImage transfer operation. Test: angle_end2end_tests --gtest_filter=*PBOCompressedSubImage* angle_end2end_tests --gtest_filter=*PBOWithMultipleDraws* dEQP-GLES3.functional.texture.specification.tex*image*d_pbo* Bug: angleproject:3777 Change-Id: I3f271024a635be113202a16f8893a199c194172d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1906203Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
parent f9c3eaf4
......@@ -150,7 +150,8 @@ angle::Result TextureVk::setSubImage(const gl::Context *context,
const vk::Format &vkFormat =
contextVk->getRenderer()->getFormat(levelDesc.format.info->sizedInternalFormat);
return setSubImageImpl(context, index, area, formatInfo, type, unpack, pixels, vkFormat);
return setSubImageImpl(context, index, area, formatInfo, type, unpack, unpackBuffer, pixels,
vkFormat);
}
angle::Result TextureVk::setCompressedImage(const gl::Context *context,
......@@ -180,9 +181,11 @@ angle::Result TextureVk::setCompressedSubImage(const gl::Context *context,
const gl::ImageDesc &levelDesc = mState.getImageDesc(index);
const vk::Format &vkFormat =
contextVk->getRenderer()->getFormat(levelDesc.format.info->sizedInternalFormat);
const gl::State &glState = contextVk->getState();
gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, pixels,
vkFormat);
return setSubImageImpl(context, index, area, formatInfo, GL_UNSIGNED_BYTE, unpack, unpackBuffer,
pixels, vkFormat);
}
angle::Result TextureVk::setImageImpl(const gl::Context *context,
......@@ -205,9 +208,11 @@ angle::Result TextureVk::setImageImpl(const gl::Context *context,
{
return angle::Result::Continue;
}
const gl::State &glState = contextVk->getState();
gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
return setSubImageImpl(context, index, gl::Box(0, 0, 0, size.width, size.height, size.depth),
formatInfo, type, unpack, pixels, vkFormat);
formatInfo, type, unpack, unpackBuffer, pixels, vkFormat);
}
angle::Result TextureVk::setSubImageImpl(const gl::Context *context,
......@@ -216,28 +221,64 @@ angle::Result TextureVk::setSubImageImpl(const gl::Context *context,
const gl::InternalFormat &formatInfo,
GLenum type,
const gl::PixelUnpackState &unpack,
gl::Buffer *unpackBuffer,
const uint8_t *pixels,
const vk::Format &vkFormat)
{
ContextVk *contextVk = vk::GetImpl(context);
const gl::State &glState = contextVk->getState();
gl::Buffer *unpackBuffer = glState.getTargetBuffer(gl::BufferBinding::PixelUnpack);
ContextVk *contextVk = vk::GetImpl(context);
if (unpackBuffer)
{
BufferVk *unpackBufferVk = vk::GetImpl(unpackBuffer);
void *mapPtr = nullptr;
ANGLE_TRY(unpackBufferVk->mapImpl(contextVk, &mapPtr));
const uint8_t *source =
static_cast<const uint8_t *>(mapPtr) + reinterpret_cast<ptrdiff_t>(pixels);
BufferVk *unpackBufferVk = vk::GetImpl(unpackBuffer);
vk::BufferHelper &bufferHelper = unpackBufferVk->getBuffer();
uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
GLuint inputRowPitch = 0;
GLuint inputDepthPitch = 0;
GLuint inputSkipBytes = 0;
ANGLE_TRY(mImage->stageSubresourceUpdate(
contextVk, getNativeImageIndex(index), gl::Extents(area.width, area.height, area.depth),
gl::Offset(area.x, area.y, area.z), formatInfo, unpack, type, source, vkFormat));
ANGLE_TRY(mImage->CalculateBufferInfo(
contextVk, gl::Extents(area.width, area.height, area.depth), formatInfo, unpack, type,
index.usesTex3D(), &inputRowPitch, &inputDepthPitch, &inputSkipBytes));
unpackBufferVk->unmapImpl(contextVk);
size_t offsetBytes = static_cast<size_t>(offset + inputSkipBytes);
onStagingBufferChange();
if (isFastUnpackPossible(vkFormat, offsetBytes))
{
GLuint pixelSize = formatInfo.pixelBytes;
GLuint blockWidth = formatInfo.compressedBlockWidth;
GLuint blockHeight = formatInfo.compressedBlockHeight;
if (!formatInfo.compressed)
{
pixelSize = formatInfo.computePixelBytes(type);
blockWidth = 1;
blockHeight = 1;
}
ASSERT(pixelSize != 0 && inputRowPitch != 0 && blockWidth != 0 && blockHeight != 0);
GLuint rowLengthPixels = inputRowPitch / pixelSize * blockWidth;
GLuint imageHeightPixels = inputDepthPitch / inputRowPitch * blockHeight;
ANGLE_TRY(copyBufferDataToImage(contextVk, &bufferHelper, index, rowLengthPixels,
imageHeightPixels, area, offsetBytes));
}
else
{
void *mapPtr = nullptr;
ANGLE_TRY(unpackBufferVk->mapImpl(contextVk, &mapPtr));
const uint8_t *source =
static_cast<const uint8_t *>(mapPtr) + reinterpret_cast<ptrdiff_t>(pixels);
ANGLE_TRY(mImage->stageSubresourceUpdateImpl(
contextVk, getNativeImageIndex(index),
gl::Extents(area.width, area.height, area.depth),
gl::Offset(area.x, area.y, area.z), formatInfo, unpack, type, source, vkFormat,
inputRowPitch, inputDepthPitch, inputSkipBytes));
unpackBufferVk->unmapImpl(contextVk);
onStagingBufferChange();
}
}
else if (pixels)
{
......@@ -983,6 +1024,62 @@ angle::Result TextureVk::copyImageDataToBufferAndGetData(ContextVk *contextVk,
return angle::Result::Continue;
}
angle::Result TextureVk::copyBufferDataToImage(ContextVk *contextVk,
vk::BufferHelper *srcBuffer,
const gl::ImageIndex index,
uint32_t rowLength,
uint32_t imageHeight,
const gl::Box &sourceArea,
size_t offset)
{
ANGLE_TRACE_EVENT0("gpu.angle", "TextureVk::copyBufferDataToImage");
// Vulkan Spec requires the bufferOffset to be a multiple of 4 for vkCmdCopyBufferToImage.
ASSERT((offset & (kBufferOffsetMultiple - 1)) == 0);
GLuint layerCount = 0;
GLuint layerIndex = 0;
GetRenderTargetLayerCountAndIndex(mImage, index, &layerCount, &layerIndex);
// Make sure the source is initialized and its images are flushed.
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
mImage->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferDst, commandBuffer);
// Source's layout change should happen before the copy
// Also updates the serial of the srcBuffer
srcBuffer->addReadDependency(contextVk, mImage);
VkBufferImageCopy region = {};
region.bufferOffset = offset;
region.bufferRowLength = rowLength;
region.bufferImageHeight = imageHeight;
region.imageExtent.width = sourceArea.width;
region.imageExtent.height = sourceArea.height;
region.imageExtent.depth = sourceArea.depth;
region.imageOffset.x = sourceArea.x;
region.imageOffset.y = sourceArea.y;
region.imageOffset.z = sourceArea.z;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.baseArrayLayer = layerIndex;
region.imageSubresource.layerCount = 1;
region.imageSubresource.mipLevel = static_cast<uint32_t>(index.getLevelIndex());
if (index.getType() == gl::TextureType::_2DArray)
{
region.imageExtent.depth = 1;
region.imageSubresource.layerCount = sourceArea.depth;
}
commandBuffer->copyBufferToImage(srcBuffer->getBuffer().getHandle(), mImage->getImage(),
mImage->getCurrentLayout(), 1, &region);
return angle::Result::Continue;
}
angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
......
......@@ -27,6 +27,9 @@ enum class ImageMipLevels
InvalidEnum = 2,
};
// vkCmdCopyBufferToImage buffer offset multiple
constexpr VkDeviceSize kBufferOffsetMultiple = 4;
class TextureVk : public TextureImpl
{
public:
......@@ -148,6 +151,22 @@ class TextureVk : public TextureImpl
angle::Result initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) override;
ANGLE_INLINE bool isFastUnpackPossible(const vk::Format &vkFormat, size_t offset)
{
// Conditions to determine if fast unpacking is possible
// 1. Image must be well defined to unpack directly to it
// TODO(http://anglebug.com/3777) Create and stage a temp image instead
// 2. Can't perform a fast copy for emulated formats
// 3. vkCmdCopyBufferToImage requires byte offset to be a multiple of 4
if (mImage->valid() && (vkFormat.intendedFormatID == vkFormat.actualImageFormatID) &&
((offset & (kBufferOffsetMultiple - 1)) == 0))
{
return true;
}
return false;
}
const vk::ImageHelper &getImage() const
{
ASSERT(mImage && mImage->valid());
......@@ -247,6 +266,7 @@ class TextureVk : public TextureImpl
const gl::InternalFormat &formatInfo,
GLenum type,
const gl::PixelUnpackState &unpack,
gl::Buffer *unpackBuffer,
const uint8_t *pixels,
const vk::Format &vkFormat);
......@@ -256,6 +276,14 @@ class TextureVk : public TextureImpl
const gl::Rectangle &sourceArea,
uint8_t **outDataPtr);
angle::Result copyBufferDataToImage(ContextVk *contextVk,
vk::BufferHelper *srcBuffer,
const gl::ImageIndex index,
uint32_t rowLength,
uint32_t imageHeight,
const gl::Box &sourceArea,
size_t offset);
angle::Result generateMipmapsWithCPU(const gl::Context *context);
angle::Result generateMipmapLevelsWithCPU(ContextVk *contextVk,
......
......@@ -2208,31 +2208,19 @@ void ImageHelper::removeStagedUpdates(ContextVk *contextVk, const gl::ImageIndex
}
}
angle::Result ImageHelper::stageSubresourceUpdate(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &glExtents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels,
const Format &vkFormat)
angle::Result ImageHelper::stageSubresourceUpdateImpl(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &glExtents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels,
const Format &vkFormat,
const GLuint inputRowPitch,
const GLuint inputDepthPitch,
const GLuint inputSkipBytes)
{
GLuint inputRowPitch = 0;
ANGLE_VK_CHECK_MATH(contextVk,
formatInfo.computeRowPitch(type, glExtents.width, unpack.alignment,
unpack.rowLength, &inputRowPitch));
GLuint inputDepthPitch = 0;
ANGLE_VK_CHECK_MATH(
contextVk, formatInfo.computeDepthPitch(glExtents.height, unpack.imageHeight, inputRowPitch,
&inputDepthPitch));
GLuint inputSkipBytes = 0;
ANGLE_VK_CHECK_MATH(contextVk,
formatInfo.computeSkipBytes(type, inputRowPitch, inputDepthPitch, unpack,
index.usesTex3D(), &inputSkipBytes));
const angle::Format &storageFormat = vkFormat.actualImageFormat();
size_t outputRowPitch;
......@@ -2429,6 +2417,54 @@ angle::Result ImageHelper::stageSubresourceUpdate(ContextVk *contextVk,
return angle::Result::Continue;
}
angle::Result ImageHelper::CalculateBufferInfo(ContextVk *contextVk,
const gl::Extents &glExtents,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
bool is3D,
GLuint *inputRowPitch,
GLuint *inputDepthPitch,
GLuint *inputSkipBytes)
{
ANGLE_VK_CHECK_MATH(contextVk,
formatInfo.computeRowPitch(type, glExtents.width, unpack.alignment,
unpack.rowLength, inputRowPitch));
ANGLE_VK_CHECK_MATH(contextVk,
formatInfo.computeDepthPitch(glExtents.height, unpack.imageHeight,
*inputRowPitch, inputDepthPitch));
ANGLE_VK_CHECK_MATH(
contextVk, formatInfo.computeSkipBytes(type, *inputRowPitch, *inputDepthPitch, unpack, is3D,
inputSkipBytes));
return angle::Result::Continue;
}
angle::Result ImageHelper::stageSubresourceUpdate(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &glExtents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels,
const Format &vkFormat)
{
GLuint inputRowPitch = 0;
GLuint inputDepthPitch = 0;
GLuint inputSkipBytes = 0;
ANGLE_TRY(CalculateBufferInfo(contextVk, glExtents, formatInfo, unpack, type, index.usesTex3D(),
&inputRowPitch, &inputDepthPitch, &inputSkipBytes));
ANGLE_TRY(stageSubresourceUpdateImpl(contextVk, index, glExtents, offset, formatInfo, unpack,
type, pixels, vkFormat, inputRowPitch, inputDepthPitch,
inputSkipBytes));
return angle::Result::Continue;
}
angle::Result ImageHelper::stageSubresourceUpdateAndGetData(ContextVk *contextVk,
size_t allocationSize,
const gl::ImageIndex &imageIndex,
......
......@@ -785,6 +785,19 @@ class ImageHelper final : public CommandGraphResource
// Data staging
void removeStagedUpdates(ContextVk *contextVk, const gl::ImageIndex &index);
angle::Result stageSubresourceUpdateImpl(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &glExtents,
const gl::Offset &offset,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
const uint8_t *pixels,
const Format &vkFormat,
const GLuint inputRowPitch,
const GLuint inputDepthPitch,
const GLuint inputSkipBytes);
angle::Result stageSubresourceUpdate(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &glExtents,
......@@ -924,6 +937,16 @@ class ImageHelper final : public CommandGraphResource
void *pixels,
DynamicBuffer *stagingBuffer);
angle::Result CalculateBufferInfo(ContextVk *contextVk,
const gl::Extents &glExtents,
const gl::InternalFormat &formatInfo,
const gl::PixelUnpackState &unpack,
GLenum type,
bool is3D,
GLuint *inputRowPitch,
GLuint *inputDepthPitch,
GLuint *inputSkipBytes);
private:
void forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
......
......@@ -16,6 +16,9 @@ namespace
constexpr GLuint kPixelTolerance = 1u;
constexpr GLfloat kPixelTolerance32F = 0.01f;
// Single compressed ETC2 block of source pixels all set red
constexpr uint8_t kCompressedImageETC2[] = {0x7E, 0x80, 0x04, 0x7F, 0x00, 0x07, 0xE0, 0x00};
// Take a pixel, and reset the components not covered by the format to default
// values. In particular, the default value for the alpha component is 255
// (1.0 as unsigned normalized fixed point value).
......@@ -1395,6 +1398,32 @@ class Texture3DIntegerTestES3 : public Texture3DTestES3
}
};
class PBOCompressedTextureTest : public Texture2DTest
{
protected:
PBOCompressedTextureTest() : Texture2DTest() {}
void testSetUp() override
{
TexCoordDrawTest::testSetUp();
glGenTextures(1, &mTexture2D);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
EXPECT_GL_NO_ERROR();
setUpProgram();
glGenBuffers(1, &mPBO);
}
void testTearDown() override
{
glDeleteBuffers(1, &mPBO);
Texture2DTest::testTearDown();
}
GLuint mPBO;
};
TEST_P(Texture2DTest, NegativeAPISubImage)
{
glBindTexture(GL_TEXTURE_2D, mTexture2D);
......@@ -1739,53 +1768,176 @@ TEST_P(Texture2DTest, TexStorage)
// initialized the image with a default color.
TEST_P(Texture2DTest, TexStorageWithPBO)
{
if (IsGLExtensionEnabled("NV_pixel_buffer_object"))
// http://anglebug.com/4126
ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL());
if (getClientMajorVersion() < 3)
{
int width = getWindowWidth();
int height = getWindowHeight();
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_storage"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_NV_pixel_buffer_object"));
}
GLuint tex2D;
glGenTextures(1, &tex2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex2D);
const int width = getWindowWidth();
const int height = getWindowHeight();
const size_t pixelCount = width * height;
const int componentCount = 3;
// Fill with red
std::vector<GLubyte> pixels(3 * 16 * 16);
for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
{
pixels[pixelId * 3 + 0] = 255;
pixels[pixelId * 3 + 1] = 0;
pixels[pixelId * 3 + 2] = 0;
}
GLuint tex2D;
glGenTextures(1, &tex2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex2D);
// Fill with red
std::vector<GLubyte> pixels(componentCount * pixelCount);
for (size_t pixelId = 0; pixelId < pixelCount; ++pixelId)
{
pixels[pixelId * componentCount + 0] = 255;
pixels[pixelId * componentCount + 1] = 0;
pixels[pixelId * componentCount + 2] = 0;
}
// Read 16x16 region from red backbuffer to PBO
GLuint pbo;
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, 3 * 16 * 16, pixels.data(), GL_STATIC_DRAW);
// Read 16x16 region from red backbuffer to PBO
GLuint pbo;
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, componentCount * pixelCount, pixels.data(),
GL_STATIC_DRAW);
// ANGLE internally uses RGBA as the DirectX format for RGB images
// therefore glTexStorage2DEXT initializes the image to a default color to get a consistent
// alpha color. The data is kept in a CPU-side image and the image is marked as dirty.
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGB8, 16, 16);
// ANGLE internally uses RGBA as the DirectX format for RGB images
// therefore glTexStorage2DEXT initializes the image to a default color to get a consistent
// alpha color. The data is kept in a CPU-side image and the image is marked as dirty.
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGB8, width, height);
// Initializes the color of the upper-left 8x8 pixels, leaves the other pixels untouched.
// glTexSubImage2D should take into account that the image is dirty.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Initializes the color of the upper-left quadrant of pixels, leaves the other pixels
// untouched. glTexSubImage2D should take into account that the image is dirty.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RGB, GL_UNSIGNED_BYTE,
nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
setUpProgram();
setUpProgram();
glUseProgram(mProgram);
glUniform1i(mTexture2DUniformLocation, 0);
drawQuad(mProgram, "position", 0.5f);
glDeleteTextures(1, &tex2D);
glDeleteBuffers(1, &pbo);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(3 * width / 4, 3 * height / 4, 0, 0, 0, 255);
EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255);
glUseProgram(mProgram);
glUniform1i(mTexture2DUniformLocation, 0);
drawQuad(mProgram, "position", 0.5f);
glDeleteTextures(1, &tex2D);
glDeleteBuffers(1, &pbo);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(3 * width / 4, 3 * height / 4, 0, 0, 0, 255);
EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255);
}
// Test that glTexSubImage2D combined with a PBO works properly after deleting the PBO
// and drawing with the texture
// Pseudo code for the follow test:
// 1. Upload PBO to mTexture2D
// 2. Delete PBO
// 3. Draw with otherTexture (x5)
// 4. Draw with mTexture2D
// 5. Validate color output
TEST_P(Texture2DTest, PBOWithMultipleDraws)
{
if (getClientMajorVersion() < 3)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_storage"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_NV_pixel_buffer_object"));
}
const GLuint width = getWindowWidth();
const GLuint height = getWindowHeight();
const GLuint windowPixelCount = width * height;
std::vector<GLColor> pixelsRed(windowPixelCount, GLColor::red);
std::vector<GLColor> pixelsGreen(windowPixelCount, GLColor::green);
// Create secondary draw that does not use mTexture
const char *vertexShaderSource = getVertexShaderSource();
const char *fragmentShaderSource = getFragmentShaderSource();
ANGLE_GL_PROGRAM(otherProgram, vertexShaderSource, fragmentShaderSource);
GLint uniformLoc = glGetUniformLocation(otherProgram, getTextureUniformName());
ASSERT_NE(-1, uniformLoc);
glUseProgram(0);
// Create secondary Texture to draw with
GLTexture otherTexture;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, otherTexture);
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
pixelsRed.data());
ASSERT_GL_NO_ERROR();
// Setup primary Texture
glBindTexture(GL_TEXTURE_2D, mTexture2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
ASSERT_GL_NO_ERROR();
// Setup PBO
GLuint pbo = 0;
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, pixelsGreen.size() * 4u, pixelsGreen.data(),
GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
// Write PBO to mTexture
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
ASSERT_GL_NO_ERROR();
// Delete PBO as ANGLE should be properly handling refcount of this buffer
glDeleteBuffers(1, &pbo);
pixelsGreen.clear();
// Do 5 draws not involving primary texture that the PBO updated
glUseProgram(otherProgram);
glUniform1i(uniformLoc, 0);
glBindTexture(GL_TEXTURE_2D, otherTexture);
drawQuad(otherProgram, "position", 0.5f);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glUseProgram(otherProgram);
glUniform1i(uniformLoc, 0);
glBindTexture(GL_TEXTURE_2D, otherTexture);
drawQuad(otherProgram, "position", 0.5f);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glUseProgram(otherProgram);
glUniform1i(uniformLoc, 0);
glBindTexture(GL_TEXTURE_2D, otherTexture);
drawQuad(otherProgram, "position", 0.5f);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glUseProgram(otherProgram);
glUniform1i(uniformLoc, 0);
glBindTexture(GL_TEXTURE_2D, otherTexture);
drawQuad(otherProgram, "position", 0.5f);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
ASSERT_GL_NO_ERROR();
std::vector<GLColor> output(windowPixelCount, GLColor::black);
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
output.data());
EXPECT_EQ(pixelsRed, output);
setUpProgram();
// Draw using PBO updated texture
glUseProgram(mProgram);
glUniform1i(mTexture2DUniformLocation, 0);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
drawQuad(mProgram, "position", 0.5f);
ASSERT_GL_NO_ERROR();
std::vector<GLColor> actual(windowPixelCount, GLColor::black);
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
actual.data());
// Value should be green as it was updated during PBO transfer to mTexture
std::vector<GLColor> expected(windowPixelCount, GLColor::green);
EXPECT_EQ(expected, actual);
}
// Tests CopySubImage for float formats
......@@ -5653,6 +5805,65 @@ TEST_P(Texture3DIntegerTestES3, NonZeroBaseLevel)
EXPECT_PIXEL_COLOR_EQ(width - 1, height - 1, color);
}
// Test that uses glCompressedTexSubImage2D combined with a PBO
TEST_P(PBOCompressedTextureTest, PBOCompressedSubImage)
{
// ETC texture formats are not supported on Mac OpenGL. http://anglebug.com/3853
ANGLE_SKIP_TEST_IF(IsOSX() && IsDesktopOpenGL());
// http://anglebug.com/4115
ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsDesktopOpenGL());
ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsDesktopOpenGL());
if (getClientMajorVersion() < 3)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_storage"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_NV_pixel_buffer_object"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_compressed_ETC2_RGB8_texture"));
}
const GLuint width = 4u;
const GLuint height = 4u;
setWindowWidth(width);
setWindowHeight(height);
// Setup primary Texture
glBindTexture(GL_TEXTURE_2D, mTexture2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (getClientMajorVersion() < 3)
{
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB8_ETC2, width, height);
}
else
{
glTexStorage2D(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB8_ETC2, width, height);
}
ASSERT_GL_NO_ERROR();
// Setup PBO and fill it with a red
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_UNPACK_BUFFER, width * height / 2u, kCompressedImageETC2, GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
// Write PBO to mTexture
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_COMPRESSED_RGB8_ETC2,
width * height / 2u, nullptr);
ASSERT_GL_NO_ERROR();
setUpProgram();
// Draw using PBO updated texture
glUseProgram(mProgram);
glUniform1i(mTexture2DUniformLocation, 0);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
drawQuad(mProgram, "position", 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(Texture2DTest);
......@@ -5691,5 +5902,6 @@ ANGLE_INSTANTIATE_TEST_ES3(Texture2DIntegerProjectiveOffsetTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DArrayIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture3DIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(Texture2DDepthTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(PBOCompressedTextureTest);
} // anonymous namespace
......@@ -109,6 +109,69 @@ class TextureUploadFullMipBenchmark : public TextureUploadBenchmarkBase
void drawBenchmark() override;
};
class PBOSubImageBenchmark : public TextureUploadBenchmarkBase
{
public:
PBOSubImageBenchmark() : TextureUploadBenchmarkBase("PBO") {}
void initializeBenchmark() override
{
TextureUploadBenchmarkBase::initializeBenchmark();
const auto &params = GetParam();
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
glGenBuffers(1, &mPBO);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_UNPACK_BUFFER, params.baseSize * params.baseSize * 4,
mTextureData.data(), GL_STREAM_DRAW);
}
void destroyBenchmark()
{
TextureUploadBenchmarkBase::destroyBenchmark();
glDeleteBuffers(1, &mPBO);
}
void drawBenchmark() override;
private:
GLuint mPBO;
};
class PBOCompressedSubImageBenchmark : public TextureUploadBenchmarkBase
{
public:
PBOCompressedSubImageBenchmark() : TextureUploadBenchmarkBase("PBOCompressed") {}
void initializeBenchmark() override
{
TextureUploadBenchmarkBase::initializeBenchmark();
const auto &params = GetParam();
glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB8_ETC2, params.baseSize,
params.baseSize);
glGenBuffers(1, &mPBO);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_UNPACK_BUFFER, params.subImageSize * params.subImageSize / 2,
mTextureData.data(), GL_STREAM_DRAW);
}
void destroyBenchmark()
{
TextureUploadBenchmarkBase::destroyBenchmark();
glDeleteBuffers(1, &mPBO);
}
void drawBenchmark() override;
private:
GLuint mPBO;
};
TextureUploadBenchmarkBase::TextureUploadBenchmarkBase(const char *benchmarkName)
: ANGLERenderTest(benchmarkName, GetParam())
{
......@@ -228,6 +291,46 @@ void TextureUploadFullMipBenchmark::drawBenchmark()
ASSERT_GL_NO_ERROR();
}
void PBOSubImageBenchmark::drawBenchmark()
{
const auto &params = GetParam();
startGpuTimer();
for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
{
glTexSubImage2D(GL_TEXTURE_2D, 0, rand() % (params.baseSize - params.subImageSize),
rand() % (params.baseSize - params.subImageSize), params.subImageSize,
params.subImageSize, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// Perform a draw just so the texture data is flushed. With the position attributes not
// set, a constant default value is used, resulting in a very cheap draw.
glDrawArrays(GL_TRIANGLES, 0, 3);
}
stopGpuTimer();
ASSERT_GL_NO_ERROR();
}
void PBOCompressedSubImageBenchmark::drawBenchmark()
{
const auto &params = GetParam();
startGpuTimer();
for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
{
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, params.subImageSize, params.subImageSize,
GL_COMPRESSED_RGB8_ETC2,
params.subImageSize * params.subImageSize / 2, 0);
// Perform a draw just so the texture data is flushed. With the position attributes not
// set, a constant default value is used, resulting in a very cheap draw.
glDrawArrays(GL_TRIANGLES, 0, 3);
}
stopGpuTimer();
ASSERT_GL_NO_ERROR();
}
TextureUploadParams D3D11Params(bool webglCompat)
{
TextureUploadParams params;
......@@ -252,6 +355,30 @@ TextureUploadParams VulkanParams(bool webglCompat)
return params;
}
TextureUploadParams VulkanPBOParams(GLsizei baseSize, GLsizei subImageSize)
{
TextureUploadParams params;
params.eglParameters = egl_platform::VULKAN();
params.webgl = false;
params.trackGpuTime = false;
params.baseSize = baseSize;
params.subImageSize = subImageSize;
return params;
}
TextureUploadParams ES3OpenGLPBOParams(GLsizei baseSize, GLsizei subImageSize)
{
TextureUploadParams params;
params.eglParameters = egl_platform::OPENGL();
params.majorVersion = 3;
params.minorVersion = 0;
params.webgl = false;
params.trackGpuTime = false;
params.baseSize = baseSize;
params.subImageSize = subImageSize;
return params;
}
} // anonymous namespace
TEST_P(TextureUploadSubImageBenchmark, Run)
......@@ -264,6 +391,16 @@ TEST_P(TextureUploadFullMipBenchmark, Run)
run();
}
TEST_P(PBOSubImageBenchmark, Run)
{
run();
}
TEST_P(PBOCompressedSubImageBenchmark, Run)
{
run();
}
using namespace params;
ANGLE_INSTANTIATE_TEST(TextureUploadSubImageBenchmark,
......@@ -283,3 +420,11 @@ ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark,
VulkanParams(false),
NullDevice(VulkanParams(false)),
VulkanParams(true));
ANGLE_INSTANTIATE_TEST(PBOSubImageBenchmark,
ES3OpenGLPBOParams(1024, 128),
VulkanPBOParams(1024, 128));
ANGLE_INSTANTIATE_TEST(PBOCompressedSubImageBenchmark,
ES3OpenGLPBOParams(128, 128),
VulkanPBOParams(128, 128));
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