Commit 05cd6dfc by Luc Ferron Committed by Commit Bot

Vulkan: generateMipmaps with vkCmdBlitImage when possible

Bug: angleproject:2502 Change-Id: Ib32a128a453749c59d751e996017a8a6e2a9972e Reviewed-on: https://chromium-review.googlesource.com/1072550Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Luc Ferron <lucferron@chromium.org>
parent 86ce210a
...@@ -60,6 +60,9 @@ void MapSwizzleState(GLenum internalFormat, ...@@ -60,6 +60,9 @@ void MapSwizzleState(GLenum internalFormat,
constexpr VkBufferUsageFlags kStagingBufferFlags = constexpr VkBufferUsageFlags kStagingBufferFlags =
(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
constexpr size_t kStagingBufferSize = 1024 * 16; constexpr size_t kStagingBufferSize = 1024 * 16;
constexpr VkFormatFeatureFlags kBlitFeatureFlags =
VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
} // anonymous namespace } // anonymous namespace
// StagingStorage implementation. // StagingStorage implementation.
...@@ -314,15 +317,15 @@ gl::Error PixelBuffer::stageSubresourceUpdateAndGetData(RendererVk *renderer, ...@@ -314,15 +317,15 @@ gl::Error PixelBuffer::stageSubresourceUpdateAndGetData(RendererVk *renderer,
return gl::NoError(); return gl::NoError();
} }
gl::Error TextureVk::generateMipmapLevels(ContextVk *contextVk, gl::Error TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk,
const angle::Format &sourceFormat, const angle::Format &sourceFormat,
GLuint layer, GLuint layer,
GLuint firstMipLevel, GLuint firstMipLevel,
GLuint maxMipLevel, GLuint maxMipLevel,
const size_t sourceWidth, const size_t sourceWidth,
const size_t sourceHeight, const size_t sourceHeight,
const size_t sourceRowPitch, const size_t sourceRowPitch,
uint8_t *sourceData) uint8_t *sourceData)
{ {
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
...@@ -589,34 +592,92 @@ gl::Error TextureVk::setImageExternal(const gl::Context *context, ...@@ -589,34 +592,92 @@ gl::Error TextureVk::setImageExternal(const gl::Context *context,
return gl::InternalError(); return gl::InternalError();
} }
gl::Error TextureVk::generateMipmap(const gl::Context *context) void TextureVk::generateMipmapWithBlit(RendererVk *renderer)
{ {
ContextVk *contextVk = vk::GetImpl(context); uint32_t imageLayerCount = GetImageLayerCount(mState.getType());
RendererVk *renderer = contextVk->getRenderer(); const gl::Extents baseLevelExtents = mImage.getExtents();
vk::CommandBuffer *commandBuffer = nullptr;
getCommandBufferForWrite(renderer, &commandBuffer);
// Some data is pending, or the image has not been defined at all yet // We are able to use blitImage since the image format we are using supports it. This
if (!mImage.valid()) // is a faster way we can generate the mips.
int32_t mipWidth = baseLevelExtents.width;
int32_t mipHeight = baseLevelExtents.height;
// Manually manage the image memory barrier because it uses a lot more parameters than our
// usual one.
VkImageMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.image = mImage.getImage().getHandle();
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.pNext = nullptr;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = imageLayerCount;
barrier.subresourceRange.levelCount = 1;
for (uint32_t mipLevel = 1; mipLevel <= mState.getMipmapMaxLevel(); mipLevel++)
{ {
// lets initialize the image so we can generate the next levels. int32_t nextMipWidth = std::max<int32_t>(1, mipWidth >> 1);
if (!mPixelBuffer.empty()) int32_t nextMipHeight = std::max<int32_t>(1, mipHeight >> 1);
{
ANGLE_TRY(ensureImageInitialized(renderer)); barrier.subresourceRange.baseMipLevel = mipLevel - 1;
ASSERT(mImage.valid()); barrier.oldLayout = mImage.getCurrentLayout();
} barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
else barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
{ barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
// There is nothing to generate if there is nothing uploaded so far.
return gl::NoError(); // We can do it for all layers at once.
} commandBuffer->singleImageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, barrier);
VkImageBlit blit = {};
blit.srcOffsets[0] = {0, 0, 0};
blit.srcOffsets[1] = {mipWidth, mipHeight, 1};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = mipLevel - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = imageLayerCount;
blit.dstOffsets[0] = {0, 0, 0};
blit.dstOffsets[1] = {nextMipWidth, nextMipHeight, 1};
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = mipLevel;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = imageLayerCount;
mipWidth = nextMipWidth;
mipHeight = nextMipHeight;
commandBuffer->blitImage(mImage.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
mImage.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
VK_FILTER_LINEAR);
} }
// Before we loop to generate all the next levels, we can get the source level and copy it to a // Transition the last mip level to the same layout as all the other ones, so we can declare
// buffer. // our whole image layout to be SRC_OPTIMAL.
const angle::Format &angleFormat = mImage.getFormat().textureFormat(); barrier.subresourceRange.baseMipLevel = mState.getMipmapMaxLevel();
uint32_t imageLayerCount = GetImageLayerCount(mState.getType()); barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
// We can do it for all layers at once.
commandBuffer->singleImageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, barrier);
// This is just changing the internal state of the image helper so that the next call
// to changeLayoutWithStages will use this layout as the "oldLayout" argument.
mImage.updateLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
}
gl::Error TextureVk::generateMipmapWithCPU(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
bool newBufferAllocated = false; bool newBufferAllocated = false;
const gl::Extents baseLevelExtents = mImage.getExtents(); const gl::Extents baseLevelExtents = mImage.getExtents();
uint32_t imageLayerCount = GetImageLayerCount(mState.getType());
const angle::Format &angleFormat = mImage.getFormat().textureFormat();
GLuint sourceRowPitch = baseLevelExtents.width * angleFormat.pixelBytes; GLuint sourceRowPitch = baseLevelExtents.width * angleFormat.pixelBytes;
size_t baseLevelAllocationSize = sourceRowPitch * baseLevelExtents.height; size_t baseLevelAllocationSize = sourceRowPitch * baseLevelExtents.height;
...@@ -659,9 +720,6 @@ gl::Error TextureVk::generateMipmap(const gl::Context *context) ...@@ -659,9 +720,6 @@ gl::Error TextureVk::generateMipmap(const gl::Context *context)
ANGLE_TRY(renderer->finish(context)); ANGLE_TRY(renderer->finish(context));
// We're changing this textureVk content, make sure we let the graph know.
onResourceChanged(renderer);
// We now have the base level available to be manipulated in the baseLevelBuffer pointer. // We now have the base level available to be manipulated in the baseLevelBuffer pointer.
// Generate all the missing mipmaps with the slow path. We can optimize with vkCmdBlitImage // Generate all the missing mipmaps with the slow path. We can optimize with vkCmdBlitImage
// later. // later.
...@@ -669,12 +727,56 @@ gl::Error TextureVk::generateMipmap(const gl::Context *context) ...@@ -669,12 +727,56 @@ gl::Error TextureVk::generateMipmap(const gl::Context *context)
for (GLuint layer = 0; layer < imageLayerCount; layer++) for (GLuint layer = 0; layer < imageLayerCount; layer++)
{ {
size_t bufferOffset = layer * baseLevelAllocationSize; size_t bufferOffset = layer * baseLevelAllocationSize;
ANGLE_TRY(generateMipmapLevels(
ANGLE_TRY(generateMipmapLevelsWithCPU(
contextVk, angleFormat, layer, mState.getEffectiveBaseLevel() + 1, contextVk, angleFormat, layer, mState.getEffectiveBaseLevel() + 1,
mState.getMipmapMaxLevel(), baseLevelExtents.width, baseLevelExtents.height, mState.getMipmapMaxLevel(), baseLevelExtents.width, baseLevelExtents.height,
sourceRowPitch, baseLevelBuffers + bufferOffset)); sourceRowPitch, baseLevelBuffers + bufferOffset));
} }
mPixelBuffer.flushUpdatesToImage(renderer, &mImage, commandBuffer);
return gl::NoError();
}
gl::Error TextureVk::generateMipmap(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
// Some data is pending, or the image has not been defined at all yet
if (!mImage.valid())
{
// lets initialize the image so we can generate the next levels.
if (!mPixelBuffer.empty())
{
ANGLE_TRY(ensureImageInitialized(renderer));
ASSERT(mImage.valid());
}
else
{
// There is nothing to generate if there is nothing uploaded so far.
return gl::NoError();
}
}
VkFormatProperties imageProperties;
vk::GetFormatProperties(renderer->getPhysicalDevice(), mImage.getFormat().vkTextureFormat,
&imageProperties);
// Check if the image supports blit. If it does, we can do the mipmap generation on the gpu
// only.
if (IsMaskFlagSet(kBlitFeatureFlags, imageProperties.linearTilingFeatures))
{
generateMipmapWithBlit(renderer);
}
else
{
ANGLE_TRY(generateMipmapWithCPU(context));
}
// We're changing this textureVk content, make sure we let the graph know.
onResourceChanged(renderer);
return gl::NoError(); return gl::NoError();
} }
......
...@@ -175,15 +175,19 @@ class TextureVk : public TextureImpl, public vk::CommandGraphResource ...@@ -175,15 +175,19 @@ class TextureVk : public TextureImpl, public vk::CommandGraphResource
vk::Error ensureImageInitialized(RendererVk *renderer); vk::Error ensureImageInitialized(RendererVk *renderer);
private: private:
gl::Error generateMipmapLevels(ContextVk *contextVk, void generateMipmapWithBlit(RendererVk *renderer);
const angle::Format &sourceFormat,
GLuint layer, gl::Error generateMipmapWithCPU(const gl::Context *context);
GLuint firstMipLevel,
GLuint maxMipLevel, gl::Error generateMipmapLevelsWithCPU(ContextVk *contextVk,
size_t sourceWidth, const angle::Format &sourceFormat,
size_t sourceHeight, GLuint layer,
size_t sourceRowPitch, GLuint firstMipLevel,
uint8_t *sourceData); GLuint maxMipLevel,
size_t sourceWidth,
size_t sourceHeight,
size_t sourceRowPitch,
uint8_t *sourceData);
gl::Error copySubImageImpl(const gl::Context *context, gl::Error copySubImageImpl(const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
......
...@@ -41,6 +41,11 @@ void FillTextureFormatCaps(const VkFormatProperties &formatProperties, ...@@ -41,6 +41,11 @@ void FillTextureFormatCaps(const VkFormatProperties &formatProperties,
HasFormatFeatureBits(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, formatProperties); HasFormatFeatureBits(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, formatProperties);
} }
} // anonymous namespace
namespace vk
{
void GetFormatProperties(VkPhysicalDevice physicalDevice, void GetFormatProperties(VkPhysicalDevice physicalDevice,
VkFormat vkFormat, VkFormat vkFormat,
VkFormatProperties *propertiesOut) VkFormatProperties *propertiesOut)
...@@ -63,10 +68,7 @@ void GetFormatProperties(VkPhysicalDevice physicalDevice, ...@@ -63,10 +68,7 @@ void GetFormatProperties(VkPhysicalDevice physicalDevice,
*propertiesOut = formatProperties; *propertiesOut = formatProperties;
} }
} }
} // anonymous namespace
namespace vk
{
bool HasFullFormatSupport(VkPhysicalDevice physicalDevice, VkFormat vkFormat) bool HasFullFormatSupport(VkPhysicalDevice physicalDevice, VkFormat vkFormat)
{ {
VkFormatProperties formatProperties; VkFormatProperties formatProperties;
......
...@@ -28,6 +28,10 @@ namespace rx ...@@ -28,6 +28,10 @@ namespace rx
namespace vk namespace vk
{ {
void GetFormatProperties(VkPhysicalDevice physicalDevice,
VkFormat vkFormat,
VkFormatProperties *propertiesOut);
struct Format final : private angle::NonCopyable struct Format final : private angle::NonCopyable
{ {
Format(); Format();
......
...@@ -384,6 +384,19 @@ Error CommandBuffer::init(VkDevice device, const VkCommandBufferAllocateInfo &cr ...@@ -384,6 +384,19 @@ Error CommandBuffer::init(VkDevice device, const VkCommandBufferAllocateInfo &cr
return NoError(); return NoError();
} }
void CommandBuffer::blitImage(const Image &srcImage,
VkImageLayout srcImageLayout,
const Image &dstImage,
VkImageLayout dstImageLayout,
uint32_t regionCount,
VkImageBlit *pRegions,
VkFilter filter)
{
ASSERT(valid());
vkCmdBlitImage(mHandle, srcImage.getHandle(), srcImageLayout, dstImage.getHandle(),
dstImageLayout, regionCount, pRegions, filter);
}
Error CommandBuffer::begin(const VkCommandBufferBeginInfo &info) Error CommandBuffer::begin(const VkCommandBufferBeginInfo &info)
{ {
ASSERT(valid()); ASSERT(valid());
......
...@@ -313,6 +313,13 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer> ...@@ -313,6 +313,13 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
VkCommandBuffer releaseHandle(); VkCommandBuffer releaseHandle();
void destroy(VkDevice device, const CommandPool &commandPool); void destroy(VkDevice device, const CommandPool &commandPool);
Error init(VkDevice device, const VkCommandBufferAllocateInfo &createInfo); Error init(VkDevice device, const VkCommandBufferAllocateInfo &createInfo);
void blitImage(const Image &srcImage,
VkImageLayout srcImageLayout,
const Image &dstImage,
VkImageLayout dstImageLayout,
uint32_t regionCount,
VkImageBlit *pRegions,
VkFilter filter);
using WrappedObject::operator=; using WrappedObject::operator=;
Error begin(const VkCommandBufferBeginInfo &info); Error begin(const VkCommandBufferBeginInfo &info);
......
...@@ -200,6 +200,7 @@ ...@@ -200,6 +200,7 @@
2605 VULKAN ANDROID : dEQP-GLES2.functional.state_query.rbo.renderbuffer_component_size_depth = SKIP 2605 VULKAN ANDROID : dEQP-GLES2.functional.state_query.rbo.renderbuffer_component_size_depth = SKIP
2606 VULKAN ANDROID : dEQP-GLES2.functional.debug_marker.random = SKIP 2606 VULKAN ANDROID : dEQP-GLES2.functional.debug_marker.random = SKIP
2606 VULKAN ANDROID : dEQP-GLES2.functional.debug_marker.supported = SKIP 2606 VULKAN ANDROID : dEQP-GLES2.functional.debug_marker.supported = SKIP
2609 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.generate.* = SKIP
// Windows Linux and Mac failures // Windows Linux and Mac failures
1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.srgb8 = FAIL 1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.srgb8 = FAIL
......
...@@ -520,10 +520,6 @@ TEST_P(MipmapTest, DISABLED_ThreeLevelsInitData) ...@@ -520,10 +520,6 @@ TEST_P(MipmapTest, DISABLED_ThreeLevelsInitData)
// In particular, on D3D11 Feature Level 9_3 it ensures that both the zero LOD workaround texture AND the 'normal' texture are copied during conversion. // In particular, on D3D11 Feature Level 9_3 it ensures that both the zero LOD workaround texture AND the 'normal' texture are copied during conversion.
TEST_P(MipmapTest, GenerateMipmapFromInitDataThenRender) TEST_P(MipmapTest, GenerateMipmapFromInitDataThenRender)
{ {
// TODO(lucferron): Figure out why this test is failing only on Intel Linux.
// http://anglebug.com/2502
ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel());
// Pass in initial data so the texture is blue. // Pass in initial data so the texture is blue.
glBindTexture(GL_TEXTURE_2D, mTexture2D); glBindTexture(GL_TEXTURE_2D, mTexture2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData);
...@@ -578,10 +574,6 @@ TEST_P(MipmapTest, GenerateMipmapFromInitDataThenRender) ...@@ -578,10 +574,6 @@ TEST_P(MipmapTest, GenerateMipmapFromInitDataThenRender)
// The test ensures that the zero-level texture is correctly copied into the mipped texture before the mipmaps are generated. // The test ensures that the zero-level texture is correctly copied into the mipped texture before the mipmaps are generated.
TEST_P(MipmapTest, GenerateMipmapFromRenderedImage) TEST_P(MipmapTest, GenerateMipmapFromRenderedImage)
{ {
// TODO(lucferron): Figure out why this test is failing only on Intel Linux.
// http://anglebug.com/2502
ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel());
glBindTexture(GL_TEXTURE_2D, mTexture2D); glBindTexture(GL_TEXTURE_2D, mTexture2D);
// Clear the texture to blue. // Clear the texture to blue.
clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f); clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f);
...@@ -610,10 +602,6 @@ TEST_P(MipmapTest, GenerateMipmapFromRenderedImage) ...@@ -610,10 +602,6 @@ TEST_P(MipmapTest, GenerateMipmapFromRenderedImage)
// TODO: This test hits a texture rebind bug in the D3D11 renderer. Fix this. // TODO: This test hits a texture rebind bug in the D3D11 renderer. Fix this.
TEST_P(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap) TEST_P(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
{ {
// TODO(lucferron): Figure out why this test is failing only on Intel Linux.
// http://anglebug.com/2502
ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel());
// TODO(geofflang): Figure out why this is broken on AMD OpenGL // TODO(geofflang): Figure out why this is broken on AMD OpenGL
ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL()); ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
......
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