Commit 5850c748 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Emulated RGB copies in compute

The copy between emulated RGB formats can take a number of paths: - Sample from src (reinterpreted as UINT), output to dst - Sample from src, output to temp buffer, copy to dst - Copy src to temp buffer, output to dst - Copy src to temp buffer, convert to another temp buffer, copy to dst While directly sampling from src and outputting to dst is more efficient, these are not always possible. The former may not have SAMPLED_IMAGE usage bit for the reinterpreted UINT format, and the latter may not have STORAGE_IMAGE usage at all. This change takes the universal approach of using two temp buffers. The ConvertVertex shader is used to transform between RGB and RGBA when copying from the first temp buffer to the second. Bug: angleproject:5278 Change-Id: I63d916cfdb4c389f5b817d89cd7348fdea703ce5 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2556467 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarBrandon Schade <b.schade@samsung.com>
parent 113b7e63
......@@ -32,25 +32,32 @@ namespace GenerateMipmap_comp = vk::InternalShader::GenerateMipmap
namespace
{
constexpr uint32_t kConvertIndexDestinationBinding = 0;
constexpr uint32_t kConvertVertexDestinationBinding = 0;
constexpr uint32_t kConvertVertexSourceBinding = 1;
constexpr uint32_t kImageCopySourceBinding = 0;
constexpr uint32_t kBlitResolveColorOrDepthBinding = 0;
constexpr uint32_t kBlitResolveStencilBinding = 1;
constexpr uint32_t kBlitResolveSamplerBinding = 2;
constexpr uint32_t kConvertIndexDestinationBinding = 0;
constexpr uint32_t kConvertVertexDestinationBinding = 0;
constexpr uint32_t kConvertVertexSourceBinding = 1;
constexpr uint32_t kImageCopySourceBinding = 0;
constexpr uint32_t kBlitResolveColorOrDepthBinding = 0;
constexpr uint32_t kBlitResolveStencilBinding = 1;
constexpr uint32_t kBlitResolveSamplerBinding = 2;
constexpr uint32_t kBlitResolveStencilNoExportDestBinding = 0;
constexpr uint32_t kBlitResolveStencilNoExportSrcBinding = 1;
constexpr uint32_t kBlitResolveStencilNoExportSamplerBinding = 2;
constexpr uint32_t kOverlayCullCulledWidgetsBinding = 0;
constexpr uint32_t kOverlayCullWidgetCoordsBinding = 1;
constexpr uint32_t kOverlayDrawOutputBinding = 0;
constexpr uint32_t kOverlayDrawTextWidgetsBinding = 1;
constexpr uint32_t kOverlayDrawGraphWidgetsBinding = 2;
constexpr uint32_t kOverlayDrawCulledWidgetsBinding = 3;
constexpr uint32_t kOverlayDrawFontBinding = 4;
constexpr uint32_t kGenerateMipmapDestinationBinding = 0;
constexpr uint32_t kGenerateMipmapSourceBinding = 1;
constexpr uint32_t kOverlayCullCulledWidgetsBinding = 0;
constexpr uint32_t kOverlayCullWidgetCoordsBinding = 1;
constexpr uint32_t kOverlayDrawOutputBinding = 0;
constexpr uint32_t kOverlayDrawTextWidgetsBinding = 1;
constexpr uint32_t kOverlayDrawGraphWidgetsBinding = 2;
constexpr uint32_t kOverlayDrawCulledWidgetsBinding = 3;
constexpr uint32_t kOverlayDrawFontBinding = 4;
constexpr uint32_t kGenerateMipmapDestinationBinding = 0;
constexpr uint32_t kGenerateMipmapSourceBinding = 1;
constexpr uint32_t kFloatOneAsUint = 0x3F80'0000u;
......@@ -1292,8 +1299,6 @@ angle::Result UtilsVk::convertVertexBuffer(ContextVk *contextVk,
vk::BufferHelper *src,
const ConvertVertexParameters &params)
{
ANGLE_TRY(ensureConvertVertexResourcesInitialized(contextVk));
vk::CommandBufferAccess access;
access.onBufferComputeShaderRead(src);
access.onBufferComputeShaderWrite(dest);
......@@ -1375,6 +1380,18 @@ angle::Result UtilsVk::convertVertexBuffer(ContextVk *contextVk,
UNREACHABLE();
}
return convertVertexBufferImpl(contextVk, dest, src, flags, commandBuffer, shaderParams);
}
angle::Result UtilsVk::convertVertexBufferImpl(ContextVk *contextVk,
vk::BufferHelper *dest,
vk::BufferHelper *src,
uint32_t flags,
vk::CommandBuffer *commandBuffer,
const ConvertVertexShaderParams &shaderParams)
{
ANGLE_TRY(ensureConvertVertexResourcesInitialized(contextVk));
VkDescriptorSet descriptorSet;
vk::RefCountedDescriptorPoolBinding descriptorPoolBinding;
ANGLE_TRY(allocateDescriptorSet(contextVk, Function::ConvertVertexBuffer,
......@@ -1985,7 +2002,7 @@ angle::Result UtilsVk::stencilBlitResolveNoShaderExport(ContextVk *contextVk,
ASSERT(depthStencilRenderTarget != nullptr);
vk::ImageHelper *depthStencilImage = &depthStencilRenderTarget->getImageForWrite();
// Change source layout prior to computation.
// Change layouts prior to computation.
vk::CommandBufferAccess access;
access.onImageComputeShaderRead(src->getAspectFlags(), src);
access.onImageTransferWrite(depthStencilRenderTarget->getLevelIndex(), 1,
......@@ -2049,10 +2066,8 @@ angle::Result UtilsVk::stencilBlitResolveNoShaderExport(ContextVk *contextVk,
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
// Use the all pipe stage to keep the state management simple.
commandBuffer->pipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &memoryBarrier, 0, nullptr,
0, nullptr);
commandBuffer->memoryBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, &memoryBarrier);
// Copy the resulting buffer into dest.
VkBufferImageCopy region = {};
......@@ -2061,8 +2076,7 @@ angle::Result UtilsVk::stencilBlitResolveNoShaderExport(ContextVk *contextVk,
region.bufferImageHeight = params.blitArea.height;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
region.imageSubresource.mipLevel =
depthStencilImage->toVkLevel(gl::LevelIndex(depthStencilRenderTarget->getLevelIndex()))
.get();
depthStencilImage->toVkLevel(depthStencilRenderTarget->getLevelIndex()).get();
region.imageSubresource.baseArrayLayer = depthStencilRenderTarget->getLayerIndex();
region.imageSubresource.layerCount = 1;
region.imageOffset.x = params.blitArea.x;
......@@ -2261,6 +2275,237 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
return angle::Result::Continue;
}
angle::Result UtilsVk::copyImageBits(ContextVk *contextVk,
vk::ImageHelper *dest,
vk::ImageHelper *src,
const CopyImageBitsParameters &params)
{
// This function is used to copy the bit representation of an image to another, and is used to
// support EXT_copy_image when a format is emulated. Currently, only RGB->RGBA emulation is
// possible, and so this function is tailored to this specific kind of emulation.
//
// The copy can be done with various degrees of efficiency:
//
// - If the UINT reinterpretation format for src supports SAMPLED usage, texels can be read
// directly from that. Otherwise vkCmdCopyImageToBuffer can be used and data then read from
// the buffer.
// - If the UINT reinterpretation format for dest supports STORAGE usage, texels can be written
// directly to that. Otherwise conversion can be done to a buffer and then
// vkCmdCopyBufferToImage used.
//
// This requires four different shaders. For simplicity, this function unconditionally copies
// src to a temp buffer, transforms to another temp buffer and copies to the dest. No known
// applications use EXT_copy_image on RGB formats, so no further optimization is currently
// necessary.
//
// The conversion between buffers can be done with ConvertVertex.comp in UintToUint mode, so no
// new shader is necessary. The srcEmulatedAlpha parameter is used to make sure the destination
// alpha value is correct, if dest is RGBA.
const vk::Format &srcFormat = src->getFormat();
const vk::Format &dstFormat = dest->getFormat();
// This path should only be necessary for when RGBA is used as fallback for RGB. No other
// format which can be used with EXT_copy_image has a fallback.
ASSERT(srcFormat.intendedFormat().blueBits > 0 && srcFormat.intendedFormat().alphaBits == 0);
ASSERT(dstFormat.intendedFormat().blueBits > 0 && dstFormat.intendedFormat().alphaBits == 0);
const angle::Format &srcImageFormat = srcFormat.actualImageFormat();
const angle::Format &dstImageFormat = dstFormat.actualImageFormat();
// Create temporary buffers.
vk::RendererScoped<vk::BufferHelper> srcBuffer(contextVk->getRenderer());
vk::RendererScoped<vk::BufferHelper> dstBuffer(contextVk->getRenderer());
const uint32_t srcPixelBytes = srcImageFormat.pixelBytes;
const uint32_t dstPixelBytes = dstImageFormat.pixelBytes;
const uint32_t totalPixelCount =
params.copyExtents[0] * params.copyExtents[1] * params.copyExtents[2];
// Note that buffer sizes are rounded up a multiple of uint size, as that the granularity in
// which the compute shader accesses these buffers.
const VkDeviceSize srcBufferSize =
roundUpPow2<uint32_t>(srcPixelBytes * totalPixelCount, sizeof(uint32_t));
const VkDeviceSize dstBufferSize =
roundUpPow2<uint32_t>(dstPixelBytes * totalPixelCount, sizeof(uint32_t));
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.flags = 0;
bufferInfo.size = srcBufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
bufferInfo.queueFamilyIndexCount = 0;
bufferInfo.pQueueFamilyIndices = nullptr;
ANGLE_TRY(srcBuffer.get().init(contextVk, bufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
bufferInfo.size = dstBufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
ANGLE_TRY(dstBuffer.get().init(contextVk, bufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
srcBuffer.get().retain(&contextVk->getResourceUseList());
dstBuffer.get().retain(&contextVk->getResourceUseList());
bool isSrc3D = src->getType() == VK_IMAGE_TYPE_3D;
bool isDst3D = dest->getType() == VK_IMAGE_TYPE_3D;
// Change layouts prior to computation.
vk::CommandBufferAccess access;
access.onImageTransferRead(src->getAspectFlags(), src);
access.onImageTransferWrite(params.dstLevel, 1, isDst3D ? 0 : params.dstOffset[2],
isDst3D ? 1 : params.copyExtents[2], VK_IMAGE_ASPECT_COLOR_BIT,
dest);
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
// Copy src into buffer, completely packed.
VkBufferImageCopy srcRegion = {};
srcRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
srcRegion.imageSubresource.mipLevel = src->toVkLevel(params.srcLevel).get();
srcRegion.imageSubresource.baseArrayLayer = isSrc3D ? 0 : params.srcOffset[2];
srcRegion.imageSubresource.layerCount = isSrc3D ? 1 : params.copyExtents[2];
srcRegion.imageOffset.x = params.srcOffset[0];
srcRegion.imageOffset.y = params.srcOffset[1];
srcRegion.imageOffset.z = isSrc3D ? params.srcOffset[2] : 0;
srcRegion.imageExtent.width = params.copyExtents[0];
srcRegion.imageExtent.height = params.copyExtents[1];
srcRegion.imageExtent.depth = isSrc3D ? params.copyExtents[2] : 1;
commandBuffer->copyImageToBuffer(src->getImage(), src->getCurrentLayout(),
srcBuffer.get().getBuffer().getHandle(), 1, &srcRegion);
// Add a barrier prior to dispatch call.
VkMemoryBarrier memoryBarrier = {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
commandBuffer->memoryBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, &memoryBarrier);
// Set up ConvertVertex shader to convert between the formats. Only the following three cases
// are possible:
//
// - RGB -> RGBA: Ns = 3, Ss = src.pixelBytes,
// Nd = 4, Sd = dst.pixelBytes, use srcEmulatedAlpha
//
// - RGBA -> RGBA: Ns = 3, Ss = src.pixelBytes,
// Nd = 4, Sd = dst.pixelBytes, use srcEmulatedAlpha
//
// - RGBA -> RGB: Ns = 3, Ss = src.pixelBytes,
// Nd = 3, Sd = dst.pixelBytes
//
// The trick here is with RGBA -> RGBA, where Ns is specified as 3, so that the emulated alpha
// from source is not taken (as uint), but rather one is provided such that the destination
// alpha would contain the correct emulated alpha.
//
ConvertVertexShaderParams shaderParams;
shaderParams.Ns = 3;
shaderParams.Bs = srcImageFormat.pixelBytes / srcImageFormat.channelCount;
shaderParams.Ss = srcImageFormat.pixelBytes;
shaderParams.Nd = dstImageFormat.channelCount;
shaderParams.Bd = dstImageFormat.pixelBytes / dstImageFormat.channelCount;
shaderParams.Sd = shaderParams.Nd * shaderParams.Bd;
// The component size is expected to either be 1, 2 or 4 bytes.
ASSERT(4 % shaderParams.Bs == 0);
ASSERT(4 % shaderParams.Bd == 0);
shaderParams.Es = 4 / shaderParams.Bs;
shaderParams.Ed = 4 / shaderParams.Bd;
// Total number of output components is simply the number of pixels by number of components in
// each.
shaderParams.componentCount = totalPixelCount * shaderParams.Nd;
// Total number of 4-byte outputs is the number of components divided by how many components can
// fit in a 4-byte value. Note that this value is also the invocation size of the shader.
shaderParams.outputCount = shaderParams.componentCount / shaderParams.Ed;
shaderParams.srcOffset = 0;
shaderParams.destOffset = 0;
shaderParams.isSrcHDR = 0;
shaderParams.isSrcA2BGR10 = 0;
// Due to the requirements of EXT_copy_image, the channel size of src and dest must be
// identical. Usage of srcEmulatedAlpha relies on this as it's used to output an alpha value in
// dest through the source.
ASSERT(shaderParams.Bs == shaderParams.Bd);
// The following RGB formats are allowed in EXT_copy_image:
//
// - RGB32F, RGB32UI, RGB32I
// - RGB16F, RGB16UI, RGB16I
// - RGB8, RGB8_SNORM, SRGB8, RGB8UI, RGB8I
//
// The value of emulated alpha is:
//
// - 1 for all RGB*I and RGB*UI formats
// - bit representation of 1.0f for RGB32F
// - bit representation of half-float 1.0f for RGB16F
// - 0xFF for RGB8 and SRGB8
// - 0x7F for RGB8_SNORM
if (dstImageFormat.isInt())
{
shaderParams.srcEmulatedAlpha = 1;
}
else if (dstImageFormat.isUnorm())
{
ASSERT(shaderParams.Bd == 1);
shaderParams.srcEmulatedAlpha = 0xFF;
}
else if (dstImageFormat.isSnorm())
{
ASSERT(shaderParams.Bd == 1);
shaderParams.srcEmulatedAlpha = 0x7F;
}
else if (shaderParams.Bd == 2)
{
ASSERT(dstImageFormat.isFloat());
// 1.0 in half-float is represented with 0 01111 0000000000
shaderParams.srcEmulatedAlpha = 0x3C00;
}
else if (shaderParams.Bd == 4)
{
ASSERT(dstImageFormat.isFloat());
ASSERT(ValidateFloatOneAsUint());
shaderParams.srcEmulatedAlpha = kFloatOneAsUint;
}
else
{
UNREACHABLE();
}
// Use UintToUint conversion to preserve the bit pattern during transfer.
const uint32_t flags = ConvertVertex_comp::kUintToUint;
ANGLE_TRY(convertVertexBufferImpl(contextVk, &dstBuffer.get(), &srcBuffer.get(), flags,
commandBuffer, shaderParams));
// Add a barrier prior to copy.
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
commandBuffer->memoryBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, &memoryBarrier);
// Copy buffer into dst. It's completely packed.
VkBufferImageCopy dstRegion = {};
dstRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
dstRegion.imageSubresource.mipLevel = dest->toVkLevel(params.dstLevel).get();
dstRegion.imageSubresource.baseArrayLayer = isDst3D ? 0 : params.dstOffset[2];
dstRegion.imageSubresource.layerCount = isDst3D ? 1 : params.copyExtents[2];
dstRegion.imageOffset.x = params.dstOffset[0];
dstRegion.imageOffset.y = params.dstOffset[1];
dstRegion.imageOffset.z = isDst3D ? params.dstOffset[2] : 0;
dstRegion.imageExtent.width = params.copyExtents[0];
dstRegion.imageExtent.height = params.copyExtents[1];
dstRegion.imageExtent.depth = isDst3D ? params.copyExtents[2] : 1;
commandBuffer->copyBufferToImage(dstBuffer.get().getBuffer().getHandle(), dest->getImage(),
dest->getCurrentLayout(), 1, &dstRegion);
return angle::Result::Continue;
}
angle::Result UtilsVk::generateMipmap(ContextVk *contextVk,
vk::ImageHelper *src,
const vk::ImageView *srcLevelZeroView,
......@@ -2384,8 +2629,8 @@ angle::Result UtilsVk::unresolve(ContextVk *contextVk,
ASSERT(depthStencilSrc->getSamples() == 1);
gl::TextureType textureType = vk::Get2DTextureType(depthStencilSrc->getLayerCount(), 1);
const vk::LevelIndex levelIndex = gl_vk::GetLevelIndex(
depthStencilRenderTarget->getLevelIndex(), depthStencilSrc->getBaseLevel());
const vk::LevelIndex levelIndex =
depthStencilSrc->toVkLevel(depthStencilRenderTarget->getLevelIndex());
const uint32_t layerIndex = depthStencilRenderTarget->getLayerIndex();
if (params.unresolveDepth)
......
......@@ -14,6 +14,8 @@
// unsupported formats to their fallbacks.
// - Image clear: Used by FramebufferVk::clearWithDraw().
// - Image copy: Used by TextureVk::copySubImageImplWithDraw().
// - Image copy bits: Used by ImageHelper::CopyImageSubData() to perform bitwise copies between
// RGB formats where at least one of src and dest use RGBA as fallback.
// - Color blit/resolve: Used by FramebufferVk::blit() to implement blit or multisample resolve
// on color images.
// - Depth/Stencil blit/resolve: Used by FramebufferVk::blit() to implement blit or multisample
......@@ -141,6 +143,15 @@ class UtilsVk : angle::NonCopyable
SurfaceRotation srcRotation;
};
struct CopyImageBitsParameters
{
int srcOffset[3];
gl::LevelIndex srcLevel;
int dstOffset[3];
gl::LevelIndex dstLevel;
uint32_t copyExtents[3];
};
struct OverlayCullParameters
{
uint32_t subgroupSize[2];
......@@ -236,6 +247,11 @@ class UtilsVk : angle::NonCopyable
const vk::ImageView *srcView,
const CopyImageParameters &params);
angle::Result copyImageBits(ContextVk *contextVk,
vk::ImageHelper *dest,
vk::ImageHelper *src,
const CopyImageBitsParameters &params);
using GenerateMipmapDestLevelViews =
std::array<const vk::ImageView *, kGenerateMipmapMaxLevels>;
angle::Result generateMipmap(ContextVk *contextVk,
......@@ -496,6 +512,14 @@ class UtilsVk : angle::NonCopyable
const gl::Rectangle &renderArea,
vk::CommandBuffer **commandBufferOut);
// Set up descriptor set and call dispatch.
angle::Result convertVertexBufferImpl(ContextVk *contextVk,
vk::BufferHelper *dest,
vk::BufferHelper *src,
uint32_t flags,
vk::CommandBuffer *commandBuffer,
const ConvertVertexShaderParams &shaderParams);
// Blits or resolves either color or depth/stencil, based on which view is given.
angle::Result blitResolveImpl(ContextVk *contextVk,
FramebufferVk *framebuffer,
......
......@@ -639,11 +639,15 @@ bool CanCopyWithTransferForCopyImage(RendererVk *renderer,
const vk::Format &destFormat,
VkImageTiling destTilingMode)
{
// Transfers for copy image must have the source and destination formats be size compatible
const angle::Format &srcFormatActual = srcFormat.actualImageFormat();
const angle::Format &destFormatActual = destFormat.actualImageFormat();
// Neither source nor destination formats can be emulated for copy image through transfer,
// unless they are emualted with the same format!
bool isFormatCompatible =
(!srcFormat.hasEmulatedImageFormat() && !destFormat.hasEmulatedImageFormat()) ||
srcFormat.actualImageFormatID == destFormat.actualImageFormatID;
bool isFormatCompatible = srcFormatActual.pixelBytes == destFormatActual.pixelBytes;
// If neither formats are emulated, GL validation ensures that pixelBytes is the same for both.
ASSERT(!isFormatCompatible ||
srcFormat.actualImageFormat().pixelBytes == destFormat.actualImageFormat().pixelBytes);
return isFormatCompatible &&
CanCopyWithTransfer(renderer, srcFormat, srcTilingMode, destFormat, destTilingMode);
......@@ -3923,17 +3927,17 @@ angle::Result ImageHelper::initLayerImageViewImpl(
return angle::Result::Continue;
}
angle::Result ImageHelper::initAliasedLayerImageView(Context *context,
gl::TextureType textureType,
VkImageAspectFlags aspectMask,
const gl::SwizzleState &swizzleMap,
ImageView *imageViewOut,
LevelIndex baseMipLevelVk,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
VkImageUsageFlags imageUsageFlags,
VkFormat imageViewFormat) const
angle::Result ImageHelper::initReinterpretedLayerImageView(Context *context,
gl::TextureType textureType,
VkImageAspectFlags aspectMask,
const gl::SwizzleState &swizzleMap,
ImageView *imageViewOut,
LevelIndex baseMipLevelVk,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
VkImageUsageFlags imageUsageFlags,
VkFormat imageViewFormat) const
{
VkImageViewUsageCreateInfo imageViewUsageCreateInfo = {};
imageViewUsageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO;
......@@ -4466,13 +4470,14 @@ angle::Result ImageHelper::CopyImageSubData(const gl::Context *context,
const vk::Format &destVkFormat = dstImage->getFormat();
VkImageTiling destTilingMode = dstImage->getTilingMode();
const gl::LevelIndex srcLevelGL = gl::LevelIndex(srcLevel);
const gl::LevelIndex dstLevelGL = gl::LevelIndex(dstLevel);
if (CanCopyWithTransferForCopyImage(contextVk->getRenderer(), sourceVkFormat, srcTilingMode,
destVkFormat, destTilingMode))
{
bool isSrc3D = (srcImage->getType() == VK_IMAGE_TYPE_3D);
bool isDst3D = (dstImage->getType() == VK_IMAGE_TYPE_3D);
const gl::LevelIndex srcLevelGL = gl::LevelIndex(srcLevel);
const gl::LevelIndex dstLevelGL = gl::LevelIndex(dstLevel);
bool isSrc3D = srcImage->getType() == VK_IMAGE_TYPE_3D;
bool isDst3D = dstImage->getType() == VK_IMAGE_TYPE_3D;
srcImage->retain(&contextVk->getResourceUseList());
dstImage->retain(&contextVk->getResourceUseList());
......@@ -4515,12 +4520,30 @@ angle::Result ImageHelper::CopyImageSubData(const gl::Context *context,
commandBuffer->copyImage(srcImage->getImage(), srcImage->getCurrentLayout(),
dstImage->getImage(), dstImage->getCurrentLayout(), 1, &region);
}
else if (!sourceVkFormat.intendedFormat().isBlock && !destVkFormat.intendedFormat().isBlock)
{
// The source and destination image formats may be using a fallback in the case of RGB
// images. A compute shader is used in such a case to perform the copy.
UtilsVk &utilsVk = contextVk->getUtils();
UtilsVk::CopyImageBitsParameters params;
params.srcOffset[0] = srcX;
params.srcOffset[1] = srcY;
params.srcOffset[2] = srcZ;
params.srcLevel = srcLevelGL;
params.dstOffset[0] = dstX;
params.dstOffset[1] = dstY;
params.dstOffset[2] = dstZ;
params.dstLevel = dstLevelGL;
params.copyExtents[0] = srcWidth;
params.copyExtents[1] = srcHeight;
params.copyExtents[2] = srcDepth;
ANGLE_TRY(utilsVk.copyImageBits(contextVk, dstImage, srcImage, params));
}
else
{
// TODO (anglebug.com/5278) - implement fallback path
// There is a possibility for the underlying source and destination VK image formats to be
// incompatible. An example scenario would be if the source image (RGB8UI) falls back
// to an emulated format(RGBA8UI) but the destination image is natively supported(RGB8I).
// No support for emulated compressed formats.
UNIMPLEMENTED();
ANGLE_VK_CHECK(contextVk, false, VK_ERROR_FEATURE_NOT_PRESENT);
}
......@@ -6604,7 +6627,7 @@ angle::Result ImageViewHelper::initSRGBReadViewsImpl(ContextVk *contextVk,
if (!mPerLevelLinearReadImageViews[mCurrentMaxLevel.get()].valid())
{
ANGLE_TRY(image.initAliasedLayerImageView(
ANGLE_TRY(image.initReinterpretedLayerImageView(
contextVk, viewType, aspectFlags, readSwizzle,
&mPerLevelLinearReadImageViews[mCurrentMaxLevel.get()], baseLevel, levelCount,
baseLayer, layerCount, imageUsageFlags, linearFormat));
......@@ -6612,7 +6635,7 @@ angle::Result ImageViewHelper::initSRGBReadViewsImpl(ContextVk *contextVk,
if (srgbOverrideFormat != VK_FORMAT_UNDEFINED &&
!mPerLevelSRGBReadImageViews[mCurrentMaxLevel.get()].valid())
{
ANGLE_TRY(image.initAliasedLayerImageView(
ANGLE_TRY(image.initReinterpretedLayerImageView(
contextVk, viewType, aspectFlags, readSwizzle,
&mPerLevelSRGBReadImageViews[mCurrentMaxLevel.get()], baseLevel, levelCount, baseLayer,
layerCount, imageUsageFlags, srgbOverrideFormat));
......@@ -6628,7 +6651,7 @@ angle::Result ImageViewHelper::initSRGBReadViewsImpl(ContextVk *contextVk,
if (!mPerLevelLinearFetchImageViews[mCurrentMaxLevel.get()].valid())
{
ANGLE_TRY(image.initAliasedLayerImageView(
ANGLE_TRY(image.initReinterpretedLayerImageView(
contextVk, fetchType, aspectFlags, readSwizzle,
&mPerLevelLinearFetchImageViews[mCurrentMaxLevel.get()], baseLevel, levelCount,
baseLayer, layerCount, imageUsageFlags, linearFormat));
......@@ -6636,7 +6659,7 @@ angle::Result ImageViewHelper::initSRGBReadViewsImpl(ContextVk *contextVk,
if (srgbOverrideFormat != VK_FORMAT_UNDEFINED &&
!mPerLevelSRGBFetchImageViews[mCurrentMaxLevel.get()].valid())
{
ANGLE_TRY(image.initAliasedLayerImageView(
ANGLE_TRY(image.initReinterpretedLayerImageView(
contextVk, fetchType, aspectFlags, readSwizzle,
&mPerLevelSRGBFetchImageViews[mCurrentMaxLevel.get()], baseLevel, levelCount,
baseLayer, layerCount, imageUsageFlags, srgbOverrideFormat));
......@@ -6645,7 +6668,7 @@ angle::Result ImageViewHelper::initSRGBReadViewsImpl(ContextVk *contextVk,
if (!mPerLevelLinearCopyImageViews[mCurrentMaxLevel.get()].valid())
{
ANGLE_TRY(image.initAliasedLayerImageView(
ANGLE_TRY(image.initReinterpretedLayerImageView(
contextVk, fetchType, aspectFlags, formatSwizzle,
&mPerLevelLinearCopyImageViews[mCurrentMaxLevel.get()], baseLevel, levelCount,
baseLayer, layerCount, imageUsageFlags, linearFormat));
......@@ -6653,7 +6676,7 @@ angle::Result ImageViewHelper::initSRGBReadViewsImpl(ContextVk *contextVk,
if (srgbOverrideFormat != VK_FORMAT_UNDEFINED &&
!mPerLevelSRGBCopyImageViews[mCurrentMaxLevel.get()].valid())
{
ANGLE_TRY(image.initAliasedLayerImageView(
ANGLE_TRY(image.initReinterpretedLayerImageView(
contextVk, fetchType, aspectFlags, formatSwizzle,
&mPerLevelSRGBCopyImageViews[mCurrentMaxLevel.get()], baseLevel, levelCount, baseLayer,
layerCount, imageUsageFlags, srgbOverrideFormat));
......@@ -6685,9 +6708,9 @@ angle::Result ImageViewHelper::getLevelStorageImageView(ContextVk *contextVk,
}
// Create the view. Note that storage images are not affected by swizzle parameters.
return image.initAliasedLayerImageView(contextVk, viewType, image.getAspectFlags(),
gl::SwizzleState(), imageView, levelVk, 1, layer,
image.getLayerCount(), imageUsageFlags, vkImageFormat);
return image.initReinterpretedLayerImageView(
contextVk, viewType, image.getAspectFlags(), gl::SwizzleState(), imageView, levelVk, 1,
layer, image.getLayerCount(), imageUsageFlags, vkImageFormat);
}
angle::Result ImageViewHelper::getLevelLayerStorageImageView(ContextVk *contextVk,
......@@ -6716,9 +6739,9 @@ angle::Result ImageViewHelper::getLevelLayerStorageImageView(ContextVk *contextV
// Create the view. Note that storage images are not affected by swizzle parameters.
gl::TextureType viewType = Get2DTextureType(1, image.getSamples());
return image.initAliasedLayerImageView(contextVk, viewType, image.getAspectFlags(),
gl::SwizzleState(), imageView, levelVk, 1, layer, 1,
imageUsageFlags, vkImageFormat);
return image.initReinterpretedLayerImageView(contextVk, viewType, image.getAspectFlags(),
gl::SwizzleState(), imageView, levelVk, 1, layer,
1, imageUsageFlags, vkImageFormat);
}
angle::Result ImageViewHelper::getLevelLayerDrawImageView(ContextVk *contextVk,
......
......@@ -1339,17 +1339,17 @@ class ImageHelper final : public Resource, public angle::Subject
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) const;
angle::Result initAliasedLayerImageView(Context *context,
gl::TextureType textureType,
VkImageAspectFlags aspectMask,
const gl::SwizzleState &swizzleMap,
ImageView *imageViewOut,
LevelIndex baseMipLevelVk,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
VkImageUsageFlags imageUsageFlags,
VkFormat imageViewFormat) const;
angle::Result initReinterpretedLayerImageView(Context *context,
gl::TextureType textureType,
VkImageAspectFlags aspectMask,
const gl::SwizzleState &swizzleMap,
ImageView *imageViewOut,
LevelIndex baseMipLevelVk,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
VkImageUsageFlags imageUsageFlags,
VkFormat imageViewFormat) const;
angle::Result initImageView(Context *context,
gl::TextureType textureType,
VkImageAspectFlags aspectMask,
......
......@@ -8619,6 +8619,79 @@ TEST_P(TextureBufferTestES31, QueryWidthAfterBufferResize)
}
}
class CopyImageTestES31 : public ANGLETest
{
protected:
CopyImageTestES31() {}
};
// Test that copies between RGB formats doesn't affect the emulated alpha channel, if any.
TEST_P(CopyImageTestES31, PreserveEmulatedAlpha)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_copy_image"));
constexpr GLsizei kSize = 1;
GLTexture src, dst;
// Set up the textures
glBindTexture(GL_TEXTURE_2D, src);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, kSize, kSize);
const GLColor kInitColor(50, 100, 150, 200);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kSize, kSize, GL_RGB, GL_UNSIGNED_BYTE, &kInitColor);
glBindTexture(GL_TEXTURE_2D, dst);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8UI, kSize, kSize);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Copy from src to dst
glCopyImageSubDataEXT(src, GL_TEXTURE_2D, 0, 0, 0, 0, dst, GL_TEXTURE_2D, 0, 0, 0, 0, kSize,
kSize, 1);
// Bind dst as image
glBindImageTexture(0, dst, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8UI);
// Create a buffer for output
constexpr GLsizei kBufferSize = kSize * kSize * sizeof(uint32_t) * 4;
GLBuffer buffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, kBufferSize, nullptr, GL_STATIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffer);
constexpr char kCS[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(rgba8ui, binding = 0) readonly uniform highp uimage2D imageIn;
layout(std140, binding = 1) buffer dataOut {
uvec4 data[];
};
void main()
{
uvec4 color = imageLoad(imageIn, ivec2(0));
data[0] = color;
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
glUseProgram(program);
glDispatchCompute(1, 1, 1);
EXPECT_GL_NO_ERROR();
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
const uint32_t *ptr = reinterpret_cast<uint32_t *>(
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT));
EXPECT_EQ(ptr[0], kInitColor.R);
EXPECT_EQ(ptr[1], kInitColor.G);
EXPECT_EQ(ptr[2], kInitColor.B);
// Expect alpha to be 1, even if the RGB format is emulated with RGBA.
EXPECT_EQ(ptr[3], 1u);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
#define ES2_EMULATE_COPY_TEX_IMAGE() \
......@@ -8673,5 +8746,6 @@ ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(Texture2DDepthTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(PBOCompressedTextureTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ETC1CompressedTextureTest);
ANGLE_INSTANTIATE_TEST_ES31(TextureBufferTestES31);
ANGLE_INSTANTIATE_TEST_ES31(CopyImageTestES31);
} // anonymous namespace
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