Edit

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

Branch :

  • Show log

    Commit

  • Author : Jeff Vigil
    Date : 2021-03-25 15:40:58
    Hash : d2b659f9
    Message : EGL: implement EGL_EXT_protected_content Context Add Validation check to Contexts and Surfaces Add Vulkan protected memory features and properties Add protected member to Surfaces and Contexts Implement hasProtectedContent in Vulkan Add QueueFamily helper, extent DeviceQueueMap Protected Swapchains always on for Android Add EGLProtectedContentTest Test: angle_end2end_test --gtest_filter=EGLProtectedContentTest Bug: angleproject:3965 Change-Id: I9352b1e594f71bb4e89cee7137a468940d186b1b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2800413 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/renderer/vulkan/SyncVk.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.
    //
    // SyncVk.cpp:
    //    Implements the class methods for SyncVk.
    //
    
    #include "libANGLE/renderer/vulkan/SyncVk.h"
    
    #include "common/debug.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/Display.h"
    #include "libANGLE/renderer/vulkan/ContextVk.h"
    #include "libANGLE/renderer/vulkan/DisplayVk.h"
    
    #if !defined(ANGLE_PLATFORM_WINDOWS)
    #    include <poll.h>
    #    include <unistd.h>
    #else
    #    include <io.h>
    #endif
    
    namespace
    {
    // Wait for file descriptor to be signaled
    VkResult SyncWaitFd(int fd, uint64_t timeoutNs)
    {
    #if !defined(ANGLE_PLATFORM_WINDOWS)
        struct pollfd fds;
        int ret;
    
        // Convert nanoseconds to milliseconds
        int timeoutMs = static_cast<int>(timeoutNs / 1000000);
        // If timeoutNs was non-zero but less than one millisecond, make it a millisecond.
        if (timeoutNs > 0 && timeoutNs < 1000000)
        {
            timeoutMs = 1;
        }
    
        ASSERT(fd >= 0);
    
        fds.fd     = fd;
        fds.events = POLLIN;
    
        do
        {
            ret = poll(&fds, 1, timeoutMs);
            if (ret > 0)
            {
                if (fds.revents & (POLLERR | POLLNVAL))
                {
                    return VK_ERROR_UNKNOWN;
                }
                return VK_SUCCESS;
            }
            else if (ret == 0)
            {
                return VK_TIMEOUT;
            }
        } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
    
        return VK_ERROR_UNKNOWN;
    #else
        UNREACHABLE();
        return VK_ERROR_UNKNOWN;
    #endif
    }
    
    }  // anonymous namespace
    
    namespace rx
    {
    namespace vk
    {
    SyncHelper::SyncHelper() {}
    
    SyncHelper::~SyncHelper() {}
    
    void SyncHelper::releaseToRenderer(RendererVk *renderer)
    {
        renderer->collectGarbageAndReinit(&mUse, &mEvent);
    }
    
    angle::Result SyncHelper::initialize(ContextVk *contextVk, bool isEglSyncObject)
    {
        ASSERT(!mEvent.valid());
    
        // Break the current render pass to ensure the proper ordering of the sync object in the
        // commands.
        ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
    
        RendererVk *renderer = contextVk->getRenderer();
        VkDevice device      = renderer->getDevice();
    
        VkEventCreateInfo eventCreateInfo = {};
        eventCreateInfo.sType             = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
        eventCreateInfo.flags             = 0;
    
        DeviceScoped<Event> event(device);
        ANGLE_VK_TRY(contextVk, event.get().init(device, eventCreateInfo));
    
        mEvent = event.release();
    
        vk::CommandBuffer *commandBuffer;
        ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
        commandBuffer->setEvent(mEvent.getHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
        retain(&contextVk->getResourceUseList());
    
        if (isEglSyncObject)
        {
            contextVk->onEGLSyncHelperInitialize();
        }
        else
        {
            contextVk->onSyncHelperInitialize();
        }
    
        return angle::Result::Continue;
    }
    
    angle::Result SyncHelper::clientWait(Context *context,
                                         ContextVk *contextVk,
                                         bool flushCommands,
                                         uint64_t timeout,
                                         VkResult *outResult)
    {
        RendererVk *renderer = context->getRenderer();
    
        // If the event is already set, don't wait
        bool alreadySignaled = false;
        ANGLE_TRY(getStatus(context, &alreadySignaled));
        if (alreadySignaled)
        {
            *outResult = VK_EVENT_SET;
            return angle::Result::Continue;
        }
    
        // We defer (ignore) flushes, so it's possible that the glFence's signal operation is pending
        // submission.
        if (contextVk)
        {
            if (flushCommands || usedInRecordedCommands())
            {
                ANGLE_TRY(contextVk->flushImpl(nullptr));
            }
        }
        else
        {
            if (!mUse.getSerial().valid())
            {
                // The sync object wasn't flushed before waiting, so the wait will always necessarily
                // time out.
                WARN() << "clientWaitSync called without flushing sync object and/or a valid context "
                          "active.";
                *outResult = VK_TIMEOUT;
                return angle::Result::Continue;
            }
        }
    
        // If timeout is zero, there's no need to wait, so return timeout already.
        // Do this after (possibly) flushing, since some apps/tests/traces are relying on this behavior.
        if (timeout == 0)
        {
            *outResult = VK_TIMEOUT;
            return angle::Result::Continue;
        }
    
        ASSERT(mUse.getSerial().valid());
    
        VkResult status = VK_SUCCESS;
        ANGLE_TRY(renderer->waitForSerialWithUserTimeout(context, mUse.getSerial(), timeout, &status));
    
        // Check for errors, but don't consider timeout as such.
        if (status != VK_TIMEOUT)
        {
            ANGLE_VK_TRY(context, status);
        }
    
        *outResult = status;
        return angle::Result::Continue;
    }
    
    angle::Result SyncHelper::serverWait(ContextVk *contextVk)
    {
        vk::CommandBuffer *commandBuffer;
        ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
        commandBuffer->waitEvents(1, mEvent.ptr(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
                                  VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, nullptr, 0, nullptr, 0,
                                  nullptr);
        retain(&contextVk->getResourceUseList());
        return angle::Result::Continue;
    }
    
    angle::Result SyncHelper::getStatus(Context *context, bool *signaled) const
    {
        VkResult result = mEvent.getStatus(context->getDevice());
        if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
        {
            ANGLE_VK_TRY(context, result);
        }
        *signaled = (result == VK_EVENT_SET);
        return angle::Result::Continue;
    }
    
    SyncHelperNativeFence::SyncHelperNativeFence() : mNativeFenceFd(kInvalidFenceFd) {}
    
    SyncHelperNativeFence::~SyncHelperNativeFence()
    {
        if (mNativeFenceFd != kInvalidFenceFd)
        {
            close(mNativeFenceFd);
        }
    }
    
    void SyncHelperNativeFence::releaseToRenderer(RendererVk *renderer)
    {
        renderer->collectGarbageAndReinit(&mUse, &mFenceWithFd);
    }
    
    // Note: We have mFenceWithFd hold the FD, so that ownership is with ICD. Meanwhile we store a dup
    // of FD in SyncHelperNativeFence for further reference, i.e. dup of FD. Any call to clientWait
    // or serverWait will ensure the FD or dup of FD goes to application or ICD. At release, above
    // it's Garbage collected/destroyed. Otherwise we can't time when to close(fd);
    angle::Result SyncHelperNativeFence::initializeWithFd(ContextVk *contextVk, int inFd)
    {
        ASSERT(inFd >= kInvalidFenceFd);
    
        // If valid FD provided by application - import it to fence.
        if (inFd > kInvalidFenceFd)
        {
            // File descriptor ownership: EGL_ANDROID_native_fence_sync
            // Whenever a file descriptor is passed into or returned from an
            // EGL call in this extension, ownership of that file descriptor is
            // transferred. The recipient of the file descriptor must close it when it is
            // no longer needed, and the provider of the file descriptor must dup it
            // before providing it if they require continued use of the native fence.
            mNativeFenceFd = inFd;
            return angle::Result::Continue;
        }
    
        RendererVk *renderer = contextVk->getRenderer();
        VkDevice device      = renderer->getDevice();
    
        DeviceScoped<vk::Fence> fence(device);
    
        VkExportFenceCreateInfo exportCreateInfo = {};
        exportCreateInfo.sType                   = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO;
        exportCreateInfo.pNext                   = nullptr;
        exportCreateInfo.handleTypes             = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
    
        // Create fenceInfo base.
        VkFenceCreateInfo fenceCreateInfo = {};
        fenceCreateInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
        fenceCreateInfo.flags             = 0;
        fenceCreateInfo.pNext             = &exportCreateInfo;
    
        // Initialize/create a VkFence handle
        ANGLE_VK_TRY(contextVk, fence.get().init(device, fenceCreateInfo));
    
        // invalid FD provided by application - create one with fence.
        /*
          Spec: "When a fence sync object is created or when an EGL native fence sync
          object is created with the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute set to
          EGL_NO_NATIVE_FENCE_FD_ANDROID, eglCreateSyncKHR also inserts a fence command
          into the command stream of the bound client API's current context and associates it
          with the newly created sync object.
        */
        // Flush first because the fence comes after current pending set of commands.
        ANGLE_TRY(contextVk->flushImpl(nullptr));
    
        retain(&contextVk->getResourceUseList());
    
        Serial serialOut;
        // exportFd is exporting VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR type handle which
        // obeys copy semantics. This means that the fence must already be signaled or the work to
        // signal it is in the graphics pipeline at the time we export the fd. Thus we need to
        // EnsureSubmitted here.
        ANGLE_TRY(renderer->queueSubmitOneOff(
            contextVk, vk::PrimaryCommandBuffer(), contextVk->hasProtectedContent(),
            contextVk->getPriority(), &fence.get(), vk::SubmitPolicy::EnsureSubmitted, &serialOut));
    
        VkFenceGetFdInfoKHR fenceGetFdInfo = {};
        fenceGetFdInfo.sType               = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
        fenceGetFdInfo.fence               = fence.get().getHandle();
        fenceGetFdInfo.handleType          = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
        ANGLE_VK_TRY(contextVk, fence.get().exportFd(device, fenceGetFdInfo, &mNativeFenceFd));
    
        mFenceWithFd = fence.release();
    
        return angle::Result::Continue;
    }
    
    angle::Result SyncHelperNativeFence::clientWait(Context *context,
                                                    ContextVk *contextVk,
                                                    bool flushCommands,
                                                    uint64_t timeout,
                                                    VkResult *outResult)
    {
        RendererVk *renderer = context->getRenderer();
    
        // If already signaled, don't wait
        bool alreadySignaled = false;
        ANGLE_TRY(getStatus(context, &alreadySignaled));
        if (alreadySignaled)
        {
            *outResult = VK_SUCCESS;
            return angle::Result::Continue;
        }
    
        // If timeout is zero, there's no need to wait, so return timeout already.
        if (timeout == 0)
        {
            *outResult = VK_TIMEOUT;
            return angle::Result::Continue;
        }
    
        if (flushCommands && contextVk)
        {
            ANGLE_TRY(contextVk->flushImpl(nullptr));
        }
    
        VkResult status = VK_SUCCESS;
        if (mUse.valid())
        {
            // We have a valid serial to wait on
            ANGLE_TRY(
                renderer->waitForSerialWithUserTimeout(context, mUse.getSerial(), timeout, &status));
        }
        else
        {
            // We need to wait on the file descriptor
    
            status = SyncWaitFd(mNativeFenceFd, timeout);
            if (status != VK_TIMEOUT)
            {
                ANGLE_VK_TRY(contextVk, status);
            }
        }
    
        *outResult = status;
        return angle::Result::Continue;
    }
    
    angle::Result SyncHelperNativeFence::serverWait(ContextVk *contextVk)
    {
        RendererVk *renderer = contextVk->getRenderer();
        VkDevice device      = renderer->getDevice();
    
        DeviceScoped<Semaphore> waitSemaphore(device);
        // Wait semaphore for next vkQueueSubmit().
        // Create a Semaphore with imported fenceFd.
        ANGLE_VK_TRY(contextVk, waitSemaphore.get().init(device));
    
        VkImportSemaphoreFdInfoKHR importFdInfo = {};
        importFdInfo.sType                      = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
        importFdInfo.semaphore                  = waitSemaphore.get().getHandle();
        importFdInfo.flags                      = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR;
        importFdInfo.handleType                 = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
        importFdInfo.fd                         = dup(mNativeFenceFd);
        ANGLE_VK_TRY(contextVk, waitSemaphore.get().importFd(device, importFdInfo));
    
        // Flush current work, block after current pending commands.
        ANGLE_TRY(contextVk->flushImpl(nullptr));
    
        // Add semaphore to next submit job.
        contextVk->addWaitSemaphore(waitSemaphore.get().getHandle(),
                                    VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
        contextVk->addGarbage(&waitSemaphore.get());  // This releases the handle.
        return angle::Result::Continue;
    }
    
    angle::Result SyncHelperNativeFence::getStatus(Context *context, bool *signaled) const
    {
        // We've got a serial, check if the serial is still in use
        if (mUse.valid())
        {
            *signaled = !isCurrentlyInUse(context->getRenderer()->getLastCompletedQueueSerial());
            return angle::Result::Continue;
        }
    
        // We don't have a serial, check status of the file descriptor
        VkResult result = SyncWaitFd(mNativeFenceFd, 0);
        if (result != VK_TIMEOUT)
        {
            ANGLE_VK_TRY(context, result);
        }
        *signaled = (result == VK_SUCCESS);
        return angle::Result::Continue;
    }
    
    angle::Result SyncHelperNativeFence::dupNativeFenceFD(Context *context, int *fdOut) const
    {
        if (!mFenceWithFd.valid() || mNativeFenceFd == kInvalidFenceFd)
        {
            return angle::Result::Stop;
        }
    
        *fdOut = dup(mNativeFenceFd);
    
        return angle::Result::Continue;
    }
    
    }  // namespace vk
    
    SyncVk::SyncVk() : SyncImpl() {}
    
    SyncVk::~SyncVk() {}
    
    void SyncVk::onDestroy(const gl::Context *context)
    {
        mSyncHelper.releaseToRenderer(vk::GetImpl(context)->getRenderer());
    }
    
    angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
    {
        ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
        ASSERT(flags == 0);
    
        return mSyncHelper.initialize(vk::GetImpl(context), false);
    }
    
    angle::Result SyncVk::clientWait(const gl::Context *context,
                                     GLbitfield flags,
                                     GLuint64 timeout,
                                     GLenum *outResult)
    {
        ContextVk *contextVk = vk::GetImpl(context);
    
        ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
    
        bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
        VkResult result;
    
        ANGLE_TRY(mSyncHelper.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout),
                                         &result));
    
        switch (result)
        {
            case VK_EVENT_SET:
                *outResult = GL_ALREADY_SIGNALED;
                return angle::Result::Continue;
    
            case VK_SUCCESS:
                *outResult = GL_CONDITION_SATISFIED;
                return angle::Result::Continue;
    
            case VK_TIMEOUT:
                *outResult = GL_TIMEOUT_EXPIRED;
                return angle::Result::Incomplete;
    
            default:
                UNREACHABLE();
                *outResult = GL_WAIT_FAILED;
                return angle::Result::Stop;
        }
    }
    
    angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
    {
        ASSERT(flags == 0);
        ASSERT(timeout == GL_TIMEOUT_IGNORED);
    
        ContextVk *contextVk = vk::GetImpl(context);
        return mSyncHelper.serverWait(contextVk);
    }
    
    angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
    {
        ContextVk *contextVk = vk::GetImpl(context);
        if (contextVk->getShareGroupVk()->isSyncObjectPendingFlush())
        {
            ANGLE_TRY(contextVk->flushImpl(nullptr));
        }
    
        bool signaled = false;
        ANGLE_TRY(mSyncHelper.getStatus(contextVk, &signaled));
    
        *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
        return angle::Result::Continue;
    }
    
    EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs)
        : EGLSyncImpl(), mSyncHelper(nullptr), mAttribs(attribs)
    {}
    
    EGLSyncVk::~EGLSyncVk()
    {
        SafeDelete<vk::SyncHelper>(mSyncHelper);
    }
    
    void EGLSyncVk::onDestroy(const egl::Display *display)
    {
        mSyncHelper->releaseToRenderer(vk::GetImpl(display)->getRenderer());
    }
    
    egl::Error EGLSyncVk::initialize(const egl::Display *display,
                                     const gl::Context *context,
                                     EGLenum type)
    {
        ASSERT(context != nullptr);
        mType = type;
    
        switch (type)
        {
            case EGL_SYNC_FENCE_KHR:
                ASSERT(mAttribs.isEmpty());
                mSyncHelper = new vk::SyncHelper();
                if (mSyncHelper->initialize(vk::GetImpl(context), true) == angle::Result::Stop)
                {
                    return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
                }
                return egl::NoError();
            case EGL_SYNC_NATIVE_FENCE_ANDROID:
            {
                vk::SyncHelperNativeFence *syncHelper = new vk::SyncHelperNativeFence();
                mSyncHelper                           = syncHelper;
                int nativeFd = static_cast<EGLint>(mAttribs.getAsInt(EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
                                                                     EGL_NO_NATIVE_FENCE_FD_ANDROID));
                return angle::ToEGL(syncHelper->initializeWithFd(vk::GetImpl(context), nativeFd),
                                    vk::GetImpl(display), EGL_BAD_ALLOC);
            }
            default:
                UNREACHABLE();
                return egl::Error(EGL_BAD_ALLOC);
        }
    }
    
    egl::Error EGLSyncVk::clientWait(const egl::Display *display,
                                     const gl::Context *context,
                                     EGLint flags,
                                     EGLTime timeout,
                                     EGLint *outResult)
    {
        ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
    
        bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
        VkResult result;
    
        ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
        if (mSyncHelper->clientWait(vk::GetImpl(display), contextVk, flush,
                                    static_cast<uint64_t>(timeout), &result) == angle::Result::Stop)
        {
            return egl::Error(EGL_BAD_ALLOC);
        }
    
        switch (result)
        {
            case VK_EVENT_SET:
                // fall through.  EGL doesn't differentiate between event being already set, or set
                // before timeout.
            case VK_SUCCESS:
                *outResult = EGL_CONDITION_SATISFIED_KHR;
                return egl::NoError();
    
            case VK_TIMEOUT:
                *outResult = EGL_TIMEOUT_EXPIRED_KHR;
                return egl::NoError();
    
            default:
                UNREACHABLE();
                *outResult = EGL_FALSE;
                return egl::Error(EGL_BAD_ALLOC);
        }
    }
    
    egl::Error EGLSyncVk::serverWait(const egl::Display *display,
                                     const gl::Context *context,
                                     EGLint flags)
    {
        // Server wait requires a valid bound context.
        ASSERT(context);
    
        // No flags are currently implemented.
        ASSERT(flags == 0);
    
        DisplayVk *displayVk = vk::GetImpl(display);
        ContextVk *contextVk = vk::GetImpl(context);
    
        return angle::ToEGL(mSyncHelper->serverWait(contextVk), displayVk, EGL_BAD_ALLOC);
    }
    
    egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
    {
        bool signaled = false;
        if (mSyncHelper->getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
        {
            return egl::Error(EGL_BAD_ALLOC);
        }
    
        *outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
        return egl::NoError();
    }
    
    egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const
    {
        switch (mType)
        {
            case EGL_SYNC_NATIVE_FENCE_ANDROID:
                return angle::ToEGL(mSyncHelper->dupNativeFenceFD(vk::GetImpl(display), fdOut),
                                    vk::GetImpl(display), EGL_BAD_PARAMETER);
            default:
                return egl::EglBadDisplay();
        }
    }
    
    }  // namespace rx