Edit

kc3-lang/angle/src/libANGLE/renderer/vulkan/vk_format_utils.cpp

Branch :

  • Show log

    Commit

  • Author : Charlie Lao
    Date : 2021-09-01 18:09:14
    Hash : 856a0e03
    Message : Vulkan: Make vk::Format from struct to class With all the recent changes that there are two actualImageFormatIDs, retrieve the actual format requires pass in a renderable boolean. And the vertex format also has a similar requirement to the real format may differ depends on if it is compressed or not. This struct no longer safe to expose the underline data members directly. This CL turns it into a class and expose the actual format via method that requires renderable or compressed boolean. Bug: b/196456356 Change-Id: Ie2f8308cc408bde1b0787e0b392e143187cc4425 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3139236 Commit-Queue: Charlie Lao <cclao@google.com> Reviewed-by: Tim Van Patten <timvp@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>

  • src/libANGLE/renderer/vulkan/vk_format_utils.cpp
  • //
    // Copyright 2016 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // vk_format_utils:
    //   Helper for Vulkan format code.
    
    #include "libANGLE/renderer/vulkan/vk_format_utils.h"
    
    #include "libANGLE/Texture.h"
    #include "libANGLE/formatutils.h"
    #include "libANGLE/renderer/load_functions_table.h"
    #include "libANGLE/renderer/load_texture_border_functions_table.h"
    #include "libANGLE/renderer/vulkan/ContextVk.h"
    #include "libANGLE/renderer/vulkan/RendererVk.h"
    #include "libANGLE/renderer/vulkan/vk_caps_utils.h"
    
    namespace rx
    {
    namespace
    {
    void FillTextureFormatCaps(RendererVk *renderer,
                               angle::FormatID formatID,
                               gl::TextureCaps *outTextureCaps)
    {
        const VkPhysicalDeviceLimits &physicalDeviceLimits =
            renderer->getPhysicalDeviceProperties().limits;
        bool hasColorAttachmentFeatureBit =
            renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
        bool hasDepthAttachmentFeatureBit = renderer->hasImageFormatFeatureBits(
            formatID, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
    
        outTextureCaps->texturable =
            renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
        outTextureCaps->filterable = renderer->hasImageFormatFeatureBits(
            formatID, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);
        outTextureCaps->blendable =
            renderer->hasImageFormatFeatureBits(formatID, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
    
        // For renderbuffer and texture attachments we require transfer and sampling for
        // GLES 2.0 CopyTexImage support. Sampling is also required for other features like
        // blits and EGLImages.
        outTextureCaps->textureAttachment =
            outTextureCaps->texturable &&
            (hasColorAttachmentFeatureBit || hasDepthAttachmentFeatureBit);
        outTextureCaps->renderbuffer = outTextureCaps->textureAttachment;
    
        if (outTextureCaps->renderbuffer)
        {
            if (hasColorAttachmentFeatureBit)
            {
                vk_gl::AddSampleCounts(physicalDeviceLimits.framebufferColorSampleCounts,
                                       &outTextureCaps->sampleCounts);
            }
            if (hasDepthAttachmentFeatureBit)
            {
                // Some drivers report different depth and stencil sample counts.  We'll AND those
                // counts together, limiting all depth and/or stencil formats to the lower number of
                // sample counts.
                vk_gl::AddSampleCounts((physicalDeviceLimits.framebufferDepthSampleCounts &
                                        physicalDeviceLimits.framebufferStencilSampleCounts),
                                       &outTextureCaps->sampleCounts);
            }
        }
    }
    
    bool HasFullBufferFormatSupport(RendererVk *renderer, angle::FormatID formatID)
    {
        // Note: GL_EXT_texture_buffer support uses the same vkBufferFormat that is determined by
        // Format::initBufferFallback, which uses this function.  That relies on the fact that formats
        // required for GL_EXT_texture_buffer all have mandatory VERTEX_BUFFER feature support in
        // Vulkan.  If this function is changed to test for more features in such a way that makes any
        // of those formats use a fallback format, the implementation of GL_EXT_texture_buffer must be
        // modified not to use vkBufferFormat.
        return renderer->hasBufferFormatFeatureBits(formatID, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT);
    }
    
    using SupportTest = bool (*)(RendererVk *renderer, angle::FormatID formatID);
    
    template <class FormatInitInfo>
    int FindSupportedFormat(RendererVk *renderer,
                            const FormatInitInfo *info,
                            size_t skip,
                            int numInfo,
                            SupportTest hasSupport)
    {
        ASSERT(numInfo > 0);
        const int last = numInfo - 1;
    
        for (int i = static_cast<int>(skip); i < last; ++i)
        {
            ASSERT(info[i].format != angle::FormatID::NONE);
            if (hasSupport(renderer, info[i].format))
                return i;
        }
    
        if (skip > 0 && !hasSupport(renderer, info[last].format))
        {
            // We couldn't find a valid fallback, try again without skip
            return FindSupportedFormat(renderer, info, 0, numInfo, hasSupport);
        }
    
        ASSERT(info[last].format != angle::FormatID::NONE);
        ASSERT(hasSupport(renderer, info[last].format));
        return last;
    }
    
    bool HasNonFilterableTextureFormatSupport(RendererVk *renderer, angle::FormatID formatID)
    {
        constexpr uint32_t kBitsColor =
            VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
        constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
    
        return renderer->hasImageFormatFeatureBits(formatID, kBitsColor) ||
               renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
    }
    }  // anonymous namespace
    
    namespace vk
    {
    // Format implementation.
    Format::Format()
        : mIntendedFormatID(angle::FormatID::NONE),
          mIntendedGLFormat(GL_NONE),
          mActualSampleOnlyImageFormatID(angle::FormatID::NONE),
          mActualRenderableImageFormatID(angle::FormatID::NONE),
          mActualBufferFormatID(angle::FormatID::NONE),
          mActualCompressedBufferFormatID(angle::FormatID::NONE),
          mImageInitializerFunction(nullptr),
          mTextureLoadFunctions(),
          mRenderableTextureLoadFunctions(),
          mVertexLoadFunction(nullptr),
          mCompressedVertexLoadFunction(nullptr),
          mVertexLoadRequiresConversion(false),
          mCompressedVertexLoadRequiresConversion(false),
          mVkBufferFormatIsPacked(false),
          mVkFormatIsInt(false),
          mVkFormatIsUnsigned(false)
    {}
    
    void Format::initImageFallback(RendererVk *renderer, const ImageFormatInitInfo *info, int numInfo)
    {
        size_t skip                 = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0;
        SupportTest testFunction    = HasNonRenderableTextureFormatSupport;
        const angle::Format &format = angle::Format::Get(info[0].format);
        if (format.isInt() || (format.isFloat() && format.redBits >= 32))
        {
            // Integer formats don't support filtering in GL, so don't test for it.
            // Filtering of 32-bit float textures is not supported on Android, and
            // it's enabled by the extension OES_texture_float_linear, which is
            // enabled automatically by examining format capabilities.
            testFunction = HasNonFilterableTextureFormatSupport;
        }
    
        int i = FindSupportedFormat(renderer, info, skip, static_cast<uint32_t>(numInfo), testFunction);
        mActualSampleOnlyImageFormatID = info[i].format;
        mImageInitializerFunction      = info[i].initializer;
    
        // Set renderable format.
        if (testFunction != HasNonFilterableTextureFormatSupport && !format.isSnorm() &&
            !format.isBlock)
        {
            // Rendering to SNORM textures is not supported on Android, and it's
            // enabled by the extension EXT_render_snorm.
            // Compressed textures also need to perform this check.
            testFunction = HasFullTextureFormatSupport;
            i = FindSupportedFormat(renderer, info, skip, static_cast<uint32_t>(numInfo), testFunction);
            mActualRenderableImageFormatID = info[i].format;
        }
    }
    
    void Format::initBufferFallback(RendererVk *renderer,
                                    const BufferFormatInitInfo *info,
                                    int numInfo,
                                    int compressedStartIndex)
    {
        {
            size_t skip = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0;
            int i       = FindSupportedFormat(renderer, info, skip, compressedStartIndex,
                                        HasFullBufferFormatSupport);
    
            mActualBufferFormatID         = info[i].format;
            mVkBufferFormatIsPacked       = info[i].vkFormatIsPacked;
            mVertexLoadFunction           = info[i].vertexLoadFunction;
            mVertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion;
        }
    
        if (renderer->getFeatures().compressVertexData.enabled && compressedStartIndex < numInfo)
        {
            int i = FindSupportedFormat(renderer, info, compressedStartIndex, numInfo,
                                        HasFullBufferFormatSupport);
    
            mActualCompressedBufferFormatID         = info[i].format;
            mVkCompressedBufferFormatIsPacked       = info[i].vkFormatIsPacked;
            mCompressedVertexLoadFunction           = info[i].vertexLoadFunction;
            mCompressedVertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion;
        }
    }
    
    size_t Format::getVertexInputAlignment(bool compressed) const
    {
        const angle::Format &bufferFormat = getActualBufferFormat(compressed);
        size_t pixelBytes                 = bufferFormat.pixelBytes;
        return mVkBufferFormatIsPacked ? pixelBytes : (pixelBytes / bufferFormat.channelCount);
    }
    
    bool HasEmulatedImageChannels(const angle::Format &intendedFormat,
                                  const angle::Format &actualFormat)
    {
        return (intendedFormat.alphaBits == 0 && actualFormat.alphaBits > 0) ||
               (intendedFormat.blueBits == 0 && actualFormat.blueBits > 0) ||
               (intendedFormat.greenBits == 0 && actualFormat.greenBits > 0) ||
               (intendedFormat.depthBits == 0 && actualFormat.depthBits > 0) ||
               (intendedFormat.stencilBits == 0 && actualFormat.stencilBits > 0);
    }
    
    bool HasEmulatedImageFormat(angle::FormatID intendedFormatID, angle::FormatID actualFormatID)
    {
        return actualFormatID != intendedFormatID;
    }
    
    bool operator==(const Format &lhs, const Format &rhs)
    {
        return &lhs == &rhs;
    }
    
    bool operator!=(const Format &lhs, const Format &rhs)
    {
        return &lhs != &rhs;
    }
    
    // FormatTable implementation.
    FormatTable::FormatTable() {}
    
    FormatTable::~FormatTable() {}
    
    void FormatTable::initialize(RendererVk *renderer,
                                 gl::TextureCapsMap *outTextureCapsMap,
                                 std::vector<GLenum> *outCompressedTextureFormats)
    {
        for (size_t formatIndex = 0; formatIndex < angle::kNumANGLEFormats; ++formatIndex)
        {
            Format &format                           = mFormatData[formatIndex];
            const auto intendedFormatID              = static_cast<angle::FormatID>(formatIndex);
            const angle::Format &intendedAngleFormat = angle::Format::Get(intendedFormatID);
    
            format.initialize(renderer, intendedAngleFormat);
            format.mIntendedFormatID = intendedFormatID;
    
            if (format.mActualRenderableImageFormatID == angle::FormatID::NONE)
            {
                // If renderable format was not set, it means there is no fallback format for
                // renderable. We populate this the same formatID as sampleOnly formatID so that
                // getActualFormatID() will be simpler.
                format.mActualRenderableImageFormatID = format.mActualSampleOnlyImageFormatID;
            }
    
            if (!format.valid())
            {
                continue;
            }
    
            gl::TextureCaps textureCaps;
            FillTextureFormatCaps(renderer, format.mActualSampleOnlyImageFormatID, &textureCaps);
    
            if (textureCaps.texturable)
            {
                format.mTextureLoadFunctions = GetLoadFunctionsMap(
                    format.mIntendedGLFormat, format.mActualSampleOnlyImageFormatID);
                format.mTextureBorderLoadFunctions = GetLoadTextureBorderFunctionsMap(
                    format.mIntendedGLFormat, format.mActualSampleOnlyImageFormatID);
            }
    
            if (format.mActualRenderableImageFormatID == format.mActualSampleOnlyImageFormatID)
            {
                outTextureCapsMap->set(intendedFormatID, textureCaps);
                format.mRenderableTextureLoadFunctions = format.mTextureLoadFunctions;
            }
            else
            {
                FillTextureFormatCaps(renderer, format.mActualRenderableImageFormatID, &textureCaps);
                outTextureCapsMap->set(intendedFormatID, textureCaps);
                if (textureCaps.texturable)
                {
                    format.mRenderableTextureLoadFunctions = GetLoadFunctionsMap(
                        format.mIntendedGLFormat, format.mActualRenderableImageFormatID);
                }
            }
    
            if (intendedAngleFormat.isBlock)
            {
                outCompressedTextureFormats->push_back(format.mIntendedGLFormat);
            }
        }
    }
    
    size_t GetImageCopyBufferAlignment(angle::FormatID actualFormatID)
    {
        // vkCmdCopyBufferToImage must have an offset that is a multiple of 4 as well as a multiple
        // of the texel size (if uncompressed) or pixel block size (if compressed).
        // https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkBufferImageCopy.html
        //
        // We need lcm(4, texelSize) (lcm = least common multiplier).  For compressed images,
        // |texelSize| would contain the block size.  Since 4 is constant, this can be calculated as:
        //
        //                      | texelSize             texelSize % 4 == 0
        //                      | 4 * texelSize         texelSize % 4 == 1
        // lcm(4, texelSize) = <
        //                      | 2 * texelSize         texelSize % 4 == 2
        //                      | 4 * texelSize         texelSize % 4 == 3
        //
        // This means:
        //
        // - texelSize % 2 != 0 gives a 4x multiplier
        // - else texelSize % 4 != 0 gives a 2x multiplier
        // - else there's no multiplier.
        //
        const angle::Format &actualFormat = angle::Format::Get(actualFormatID);
    
        ASSERT(actualFormat.pixelBytes != 0);
        const size_t texelSize  = actualFormat.pixelBytes;
        const size_t multiplier = texelSize % 2 != 0 ? 4 : texelSize % 4 != 0 ? 2 : 1;
        const size_t alignment  = multiplier * texelSize;
    
        return alignment;
    }
    
    size_t GetValidImageCopyBufferAlignment(angle::FormatID intendedFormatID,
                                            angle::FormatID actualFormatID)
    {
        constexpr size_t kMinimumAlignment = 16;
        return (intendedFormatID == angle::FormatID::NONE)
                   ? kMinimumAlignment
                   : GetImageCopyBufferAlignment(actualFormatID);
    }
    
    VkImageUsageFlags GetMaximalImageUsageFlags(RendererVk *renderer, angle::FormatID formatID)
    {
        constexpr VkFormatFeatureFlags kImageUsageFeatureBits =
            VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT |
            VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
            VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
        VkFormatFeatureFlags featureBits =
            renderer->getImageFormatFeatureBits(formatID, kImageUsageFeatureBits);
        VkImageUsageFlags imageUsageFlags = 0;
        if (featureBits & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
            imageUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
        if (featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)
            imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
        if (featureBits & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)
            imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
        if (featureBits & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
            imageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
        if (featureBits & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT)
            imageUsageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
        if (featureBits & VK_FORMAT_FEATURE_TRANSFER_DST_BIT)
            imageUsageFlags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
        imageUsageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
        return imageUsageFlags;
    }
    }  // namespace vk
    
    bool HasFullTextureFormatSupport(RendererVk *renderer, angle::FormatID formatID)
    {
        constexpr uint32_t kBitsColor = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
                                        VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
                                        VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
    
        // In OpenGL ES, all renderable formats except 32-bit floating-point support blending.
        // 32-bit floating-point case validation is handled by ANGLE's frontend.
        uint32_t kBitsColorFull = kBitsColor;
        switch (formatID)
        {
            case angle::FormatID::R32_FLOAT:
            case angle::FormatID::R32G32_FLOAT:
            case angle::FormatID::R32G32B32A32_FLOAT:
                break;
            default:
                kBitsColorFull |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
                break;
        }
    
        constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
    
        return renderer->hasImageFormatFeatureBits(formatID, kBitsColorFull) ||
               renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
    }
    
    bool HasNonRenderableTextureFormatSupport(RendererVk *renderer, angle::FormatID formatID)
    {
        constexpr uint32_t kBitsColor =
            VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
        constexpr uint32_t kBitsDepth = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
    
        return renderer->hasImageFormatFeatureBits(formatID, kBitsColor) ||
               renderer->hasImageFormatFeatureBits(formatID, kBitsDepth);
    }
    
    GLenum GetSwizzleStateComponent(const gl::SwizzleState &swizzleState, GLenum component)
    {
        switch (component)
        {
            case GL_RED:
                return swizzleState.swizzleRed;
            case GL_GREEN:
                return swizzleState.swizzleGreen;
            case GL_BLUE:
                return swizzleState.swizzleBlue;
            case GL_ALPHA:
                return swizzleState.swizzleAlpha;
            default:
                return component;
        }
    }
    
    gl::SwizzleState ApplySwizzle(const gl::SwizzleState &formatSwizzle,
                                  const gl::SwizzleState &toApply)
    {
        gl::SwizzleState result;
    
        result.swizzleRed   = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleRed);
        result.swizzleGreen = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleGreen);
        result.swizzleBlue  = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleBlue);
        result.swizzleAlpha = GetSwizzleStateComponent(formatSwizzle, toApply.swizzleAlpha);
    
        return result;
    }
    
    gl::SwizzleState GetFormatSwizzle(const ContextVk *contextVk,
                                      const angle::Format &angleFormat,
                                      const bool sized)
    {
        gl::SwizzleState internalSwizzle;
    
        if (angleFormat.isLUMA())
        {
            GLenum swizzleRGB, swizzleA;
            if (angleFormat.luminanceBits > 0)
            {
                swizzleRGB = GL_RED;
                swizzleA   = (angleFormat.alphaBits > 0 ? GL_GREEN : GL_ONE);
            }
            else
            {
                swizzleRGB = GL_ZERO;
                swizzleA   = GL_RED;
            }
            internalSwizzle.swizzleRed   = swizzleRGB;
            internalSwizzle.swizzleGreen = swizzleRGB;
            internalSwizzle.swizzleBlue  = swizzleRGB;
            internalSwizzle.swizzleAlpha = swizzleA;
        }
        else
        {
            if (angleFormat.hasDepthOrStencilBits())
            {
                // In OES_depth_texture/ARB_depth_texture, depth
                // textures are treated as luminance.
                // If the internalformat was not sized, use OES_depth_texture behavior
                bool hasGB = (angleFormat.depthBits > 0) && !sized;
    
                internalSwizzle.swizzleRed   = GL_RED;
                internalSwizzle.swizzleGreen = hasGB ? GL_RED : GL_ZERO;
                internalSwizzle.swizzleBlue  = hasGB ? GL_RED : GL_ZERO;
                internalSwizzle.swizzleAlpha = GL_ONE;
            }
            else
            {
                // Color bits are all zero for blocked formats
                if (!angleFormat.isBlock)
                {
                    // Set any missing channel to default in case the emulated format has that channel.
                    internalSwizzle.swizzleRed   = angleFormat.redBits > 0 ? GL_RED : GL_ZERO;
                    internalSwizzle.swizzleGreen = angleFormat.greenBits > 0 ? GL_GREEN : GL_ZERO;
                    internalSwizzle.swizzleBlue  = angleFormat.blueBits > 0 ? GL_BLUE : GL_ZERO;
                    internalSwizzle.swizzleAlpha = angleFormat.alphaBits > 0 ? GL_ALPHA : GL_ONE;
                }
            }
        }
    
        return internalSwizzle;
    }
    }  // namespace rx