Vulkan: Enable VK_IMAGE_USAGE_STORAGE_BIT when it is needed

VK_IMAGE_USAGE_STORAGE_BIT is always enabled for vkImage, this
increases memory bandwidth in some platforms.
This CL changes the behavior to enable VK_IMAGE_USAGE_STORAGE_BIT
when necessary.

Bug: angleproject:3904
Test: angle_end2end_tests
Test: angle_deqp_gles2_tests
Change-Id: I8ffd37efa8d99d04328fa6232de0755be3273d9e
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1857799
Commit-Queue: Sunny Sun <sunny.sun@arm.com>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Sunny Sun 2019-10-24 09:22:39 +08:00 коммит произвёл Commit Bot
Родитель 67527cb452
Коммит df41552841
10 изменённых файлов: 245 добавлений и 52 удалений

Просмотреть файл

@ -165,3 +165,4 @@ Samsung Electronics, Inc.
Arm Ltd.
Fei Yang
Xinyi He
Sunny Sun

Просмотреть файл

@ -486,6 +486,7 @@ void Context::initialize()
mDrawDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES);
mDrawDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM);
mDrawDirtyObjects.set(State::DIRTY_OBJECT_SAMPLERS);
mDrawDirtyObjects.set(State::DIRTY_OBJECT_IMAGES);
mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_DRAW_FRAMEBUFFER);
mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_VERTEX_ARRAY);

Просмотреть файл

@ -2881,6 +2881,10 @@ void State::setImageUnit(const Context *context,
mImageUnits[unit].format = format;
mDirtyBits.set(DIRTY_BIT_IMAGE_BINDINGS);
if (texture)
{
texture->onBindImageTexture();
}
onImageStateChange(context, unit);
}

Просмотреть файл

@ -99,6 +99,7 @@ TextureState::TextureState(TextureType type)
mBaseLevel(0),
mMaxLevel(1000),
mDepthStencilTextureMode(GL_DEPTH_COMPONENT),
mBoundAsImageTexture(false),
mImmutableFormat(false),
mImmutableLevels(0),
mUsage(GL_NONE),
@ -1908,4 +1909,14 @@ angle::Result Texture::getTexImage(const Context *context,
return mTexture->getTexImage(context, packState, packBuffer, target, level, format, type,
pixels);
}
void Texture::onBindImageTexture()
{
if (!mState.mBoundAsImageTexture)
{
mDirtyBits.set(DIRTY_BIT_BOUND_AS_IMAGE);
mState.mBoundAsImageTexture = true;
}
}
} // namespace gl

Просмотреть файл

@ -190,6 +190,7 @@ class TextureState final : private angle::NonCopyable
GLenum mDepthStencilTextureMode;
bool mBoundAsImageTexture;
bool mImmutableFormat;
GLuint mImmutableLevels;
@ -412,6 +413,7 @@ class Texture final : public RefCountObject<TextureID>,
angle::Result setEGLImageTarget(Context *context, TextureType type, egl::Image *imageTarget);
angle::Result generateMipmap(Context *context);
void onBindImageTexture();
egl::Surface *getBoundSurface() const;
egl::Stream *getBoundStream() const;
@ -493,6 +495,9 @@ class Texture final : public RefCountObject<TextureID>,
DIRTY_BIT_MAX_LEVEL,
DIRTY_BIT_DEPTH_STENCIL_TEXTURE_MODE,
// Image state
DIRTY_BIT_BOUND_AS_IMAGE,
// Misc
DIRTY_BIT_LABEL,
DIRTY_BIT_USAGE,

Просмотреть файл

@ -1462,6 +1462,9 @@ angle::Result TextureGL::syncState(const gl::Context *context,
// This special dirty bit is used to signal the front-end that the implementation
// has local dirty bits. The real dirty bits are in mLocalDirty bits.
break;
case gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE:
// Only used for Vulkan.
break;
default:
UNREACHABLE();

Просмотреть файл

@ -107,7 +107,8 @@ TextureVk::TextureVk(const gl::TextureState &state, RendererVk *renderer)
mImageLayerOffset(0),
mImageLevelOffset(0),
mImage(nullptr),
mStagingBufferInitialSize(vk::kStagingBufferSize)
mStagingBufferInitialSize(vk::kStagingBufferSize),
mImageUsageFlags(0)
{}
TextureVk::~TextureVk() = default;
@ -727,10 +728,6 @@ angle::Result TextureVk::setStorage(const gl::Context *context,
{
releaseImage(contextVk);
}
gl::Format glFormat(internalFormat);
ANGLE_TRY(
initImage(contextVk, format, glFormat.info->sized, size, static_cast<uint32_t>(levels)));
return angle::Result::Continue;
}
@ -867,6 +864,21 @@ angle::Result TextureVk::ensureImageAllocated(ContextVk *contextVk, const vk::Fo
updateImageHelper(contextVk, format);
}
mImageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
// If the image has depth/stencil support, add those as possible usage.
if (contextVk->getRenderer()->hasImageFormatFeatureBits(
format.vkImageFormat, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
{
mImageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
else if (contextVk->getRenderer()->hasImageFormatFeatureBits(
format.vkImageFormat, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
{
mImageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
return angle::Result::Continue;
}
@ -1052,15 +1064,24 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
// Redefine the images with mipmaps.
// Copy image to the staging buffer and stage an update to the new one.
vk::BufferHelper *stagingBuffer = nullptr;
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, baseLevelDesc, false,
getNativeImageLayer(0), 0, mImage->getBaseLevel()));
getNativeImageLayer(0), 0, mImage->getBaseLevel(),
&stagingBuffer));
onStagingBufferChange();
// Release the origin image and recreate it with new mipmap counts.
releaseImage(contextVk);
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::FullMipChain));
// Set up read dependency, we are now reading from this buffer to the new image.
if (stagingBuffer)
{
stagingBuffer->onRead(contextVk, mImage, VK_ACCESS_TRANSFER_READ_BIT);
// Different parts of the buffer might be read from or write to.
stagingBuffer->onSelfReadWrite(contextVk, VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
}
// Check if the image supports blit. If it does, we can do the mipmap generation on the gpu
// only.
@ -1082,7 +1103,8 @@ angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk,
bool ignoreLayerCount,
uint32_t currentLayer,
uint32_t sourceMipLevel,
uint32_t stagingDstMipLevel)
uint32_t stagingDstMipLevel,
vk::BufferHelper **stagingBuffer)
{
const gl::Extents &baseLevelExtents = desc.size;
const gl::InternalFormat &baseLevelFormat = *desc.format.info;
@ -1101,18 +1123,21 @@ angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk,
}
// Copy from the base level image to the staging buffer
vk::BufferHelper *stagingBuffer = nullptr;
VkDeviceSize stagingBufferOffset = 0;
ANGLE_TRY(mImage->copyImageDataToBuffer(contextVk, sourceMipLevel, layerCount, currentLayer,
area, &stagingBuffer, &stagingBufferOffset, nullptr));
area, stagingBuffer, &stagingBufferOffset, nullptr));
// Stage an update to the new image
size_t bufferSize = updatedExtents.width * updatedExtents.height * updatedExtents.depth *
baseLevelFormat.pixelBytes;
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(contextVk, bufferSize, stagingDstMipLevel,
currentLayer, layerCount, updatedExtents,
offset, stagingBuffer, stagingBufferOffset));
ASSERT(*stagingBuffer);
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(
contextVk, bufferSize, stagingDstMipLevel, currentLayer, layerCount, updatedExtents, offset,
*stagingBuffer, stagingBufferOffset));
// Set up write dependency, we are writing to this buffer from the old image.
(*stagingBuffer)->onWrite(contextVk, mImage, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
return angle::Result::Continue;
}
@ -1122,7 +1147,10 @@ angle::Result TextureVk::setBaseLevel(const gl::Context *context, GLuint baseLev
return angle::Result::Continue;
}
angle::Result TextureVk::changeLevels(ContextVk *contextVk, GLuint baseLevel, GLuint maxLevel)
angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer)
{
if (!mImage)
{
@ -1151,15 +1179,27 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk, GLuint baseLevel, GL
return angle::Result::Continue;
}
// If we get here, we already have a valid image and it needs to be recreated
// to reflect new base or max levels.
return changeLevels(contextVk, previousBaseLevel, baseLevel, maxLevel, stagingBuffer);
}
angle::Result TextureVk::changeLevels(ContextVk *contextVk,
GLuint previousBaseLevel,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer)
{
// Recreate the image to reflect new base or max levels.
// First, flush any pending updates so we have good data in the existing vkImage
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
ANGLE_TRY(mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), mImage->getLevelCount(),
getNativeImageLayer(0), mImage->getLayerCount(),
commandBuffer));
if (mImage->valid() && mImage->hasStagedUpdates())
{
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
ANGLE_TRY(mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0),
mImage->getLevelCount(), getNativeImageLayer(0),
mImage->getLayerCount(), commandBuffer));
}
bool baseLevelChanged = baseLevel != previousBaseLevel;
// After flushing, track the new levels (they are used in the flush, hence the wait)
mImage->setBaseAndMaxLevels(baseLevel, maxLevel);
@ -1198,14 +1238,13 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk, GLuint baseLevel, GL
uint32_t srcLevelVK = baseLevelChanged ? level - previousBaseLevel : level;
ASSERT(srcLevelVK <= mImage->getLevelCount());
ANGLE_TRY(
copyImageDataToStagingBuffer(contextVk, desc, true, layer, srcLevelVK, level));
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, desc, true, layer, srcLevelVK, level,
stagingBuffer));
}
}
// Inform the front end that we've updated the staging buffer
onStagingBufferChange();
// Now that we've staged all the updates, release the current image so that it will be
// recreated with the correct number of mip levels, base level, and max level.
releaseImage(contextVk);
@ -1326,17 +1365,39 @@ angle::Result TextureVk::syncState(const gl::Context *context,
{
ContextVk *contextVk = vk::GetImpl(context);
vk::BufferHelper *stagingBuffer = nullptr;
// Create a new image if the storage state is enabled for the first time.
if (dirtyBits.test(gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE))
{
// Recreate the image to include storage bit if needed.
if (!(mImageUsageFlags & VK_IMAGE_USAGE_STORAGE_BIT))
{
mImageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
ANGLE_TRY(changeLevels(contextVk, mImage->getBaseLevel(),
mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel(),
&stagingBuffer));
}
}
// Set base and max level before initializing the image
if (dirtyBits.test(gl::Texture::DIRTY_BIT_MAX_LEVEL) ||
dirtyBits.test(gl::Texture::DIRTY_BIT_BASE_LEVEL))
{
ANGLE_TRY(
changeLevels(contextVk, mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel()));
ANGLE_TRY(updateBaseMaxLevels(contextVk, mState.getEffectiveBaseLevel(),
mState.getEffectiveMaxLevel(), &stagingBuffer));
}
// Initialize the image storage and flush the pixel buffer.
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
if (stagingBuffer)
{
stagingBuffer->onRead(contextVk, mImage, VK_ACCESS_TRANSFER_READ_BIT);
// Different parts of the buffer might be read from or write to.
stagingBuffer->onSelfReadWrite(contextVk, VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
if (dirtyBits.none() && mSampler.valid())
{
return angle::Result::Continue;
@ -1520,34 +1581,11 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
{
RendererVk *renderer = contextVk->getRenderer();
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
// If the image has depth/stencil support, add those as possible usage.
if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
{
imageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
else if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
{
imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
// If the image has storage support, add it for ES3.1 image support.
if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
{
imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
}
VkExtent3D vkExtent;
uint32_t layerCount;
gl_vk::GetExtentsAndLayerCount(mState.getType(), extents, &vkExtent, &layerCount);
ANGLE_TRY(mImage->init(contextVk, mState.getType(), vkExtent, format, 1, imageUsageFlags,
ANGLE_TRY(mImage->init(contextVk, mState.getType(), vkExtent, format, 1, mImageUsageFlags,
mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel(),
levelCount, layerCount));

Просмотреть файл

@ -322,7 +322,8 @@ class TextureVk : public TextureImpl
bool ignoreLayerCount,
uint32_t currentLayer,
uint32_t sourceLevel,
uint32_t stagingDstMipLevel);
uint32_t stagingDstMipLevel,
vk::BufferHelper **stagingBuffer);
angle::Result initImageViews(ContextVk *contextVk,
const vk::Format &format,
const bool sized,
@ -341,9 +342,20 @@ 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;
// Re-create the image.
angle::Result changeLevels(ContextVk *contextVk,
GLuint previousBaseLevel,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer);
// Update base and max levels, and re-create image if needed.
angle::Result updateBaseMaxLevels(ContextVk *contextVk,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer);
bool mOwnsImage;
@ -380,6 +392,9 @@ class TextureVk : public TextureImpl
// Overridden in some tests.
size_t mStagingBufferInitialSize;
// The created vkImage usage flag.
VkImageUsageFlags mImageUsageFlags;
};
} // namespace rx

Просмотреть файл

@ -574,6 +574,10 @@
// Fixed in later driver versions.
2727 VULKAN ANDROID : dEQP-GLES3.functional.shaders.builtin_variable.pointcoord = FAIL
// New or broken formats in ES 3.0, may be it relates to VK_IMAGE_USAGE_STORAGE_BIT
3816 VULKAN PIXEL2ORXL : dEQP-GLES3.functional.texture.specification.texstorage3d.format.rgba16* = FAIL
3816 VULKAN PIXEL2ORXL : dEQP-GLES3.functional.texture.specification.texstorage3d.format.rgb16* = FAIL
3816 VULKAN PIXEL2ORXL : dEQP-GLES3.functional.texture.specification.texstorage3d.format.rg32* = FAIL
// Fails only with SwiftShader:

Просмотреть файл

@ -3508,6 +3508,117 @@ void main()
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
// Test that render pipeline and compute pipeline access to the same texture.
// Steps:
// 1. Clear the texture and DrawArrays.
// 2. DispatchCompute to set the image's first pixel to a specific color.
// 3. DrawArrays and check data.
TEST_P(ComputeShaderTest, DrawDispatchDrawPreserve)
{
const char kCSSource[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1) in;
layout(rgba8, binding = 0) writeonly uniform highp image2D image;
void main()
{
imageStore(image, ivec2(0, 0), vec4(0.0, 0.0, 1.0, 1.0));
})";
const char kVSSource[] = R"(#version 310 es
layout (location = 0) in vec2 pos;
in vec4 inTex;
out vec4 texCoord;
void main(void) {
texCoord = inTex;
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFSSource[] = R"(#version 310 es
precision highp float;
uniform sampler2D tex;
in vec4 texCoord;
out vec4 fragColor;
void main(void) {
fragColor = texture(tex, texCoord.xy);
})";
GLuint aPosLoc = 0;
ANGLE_GL_PROGRAM(program, kVSSource, kFSSource);
glBindAttribLocation(program, aPosLoc, "pos");
unsigned char *data = new unsigned char[4 * getWindowWidth() * getWindowHeight()];
for (int i = 0; i < getWindowWidth() * getWindowHeight(); i++)
{
data[i * 4] = 0xff;
data[i * 4 + 1] = 0;
data[i * 4 + 2] = 0;
data[i * 4 + 3] = 0xff;
}
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexStorage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth(), getWindowHeight());
// Clear the texture level 0 to Red.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA,
GL_UNSIGNED_BYTE, data);
for (int i = 0; i < getWindowWidth() * getWindowHeight(); i++)
{
data[i * 4] = 0;
data[i * 4 + 1] = 0xff;
data[i * 4 + 2] = 0;
data[i * 4 + 3] = 0xff;
}
// Clear the texture level 1 to Green.
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, getWindowWidth() / 2, getWindowHeight() / 2, GL_RGBA,
GL_UNSIGNED_BYTE, data);
delete[] data;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glUseProgram(program);
GLfloat vertices[] = {-1, -1, 1, -1, -1, 1, 1, 1};
GLfloat texCoords[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f};
GLint pos = glGetAttribLocation(program, "pos");
glEnableVertexAttribArray(pos);
glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0, vertices);
GLint posTex = glGetAttribLocation(program, "inTex");
glEnableVertexAttribArray(posTex);
glVertexAttribPointer(posTex, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
// Draw with level 0, the whole frame buffer should be Red.
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
// Draw with level 1, the whole frame buffer should be Green.
glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2 - 1, getWindowHeight() / 2 - 1, GLColor::green);
// Clear the texture level 0's (0, 0) position to Blue.
glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
ANGLE_GL_COMPUTE_PROGRAM(csProgram, kCSSource);
glUseProgram(csProgram);
glDispatchCompute(1, 1, 1);
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
EXPECT_GL_NO_ERROR();
glFinish();
glUseProgram(program);
// Draw with level 0, the first position should be Blue.
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
// Draw with level 1, the whole frame buffer should be Green.
glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2 - 1, getWindowHeight() / 2 - 1, GLColor::green);
}
ANGLE_INSTANTIATE_TEST(ComputeShaderTest,
ES31_OPENGL(),
ES31_OPENGLES(),