Commit e20f36f4 by Jeff Vigil Committed by Commit Bot

EGL: Implement EGL_ANDROID_native_fence_sync on vulkan

Check the following - 1. Vulkan ICD supports VK_KHR_external_fence_fd 2. ExternalFenceProperties and ExternalSemaphoreProperties support Android FD. eglCreateSync - if FD was provided import to VkFence, else create VkFence with new FD and then flush and submit VkFence on next vkQueueSubmit. eglGetSyncAttrib - for status call vkGetFenceStatus. eglDupNativeFenceFdANDROID - return FD from vkGetFenceFD call. eglClientWaitSync - call vkWaitForFences. eglWaitSync - dup FD, create VkSemaphore and import FD, then flush() and add VkSemaphore to next vkQueueSubmit as a waiting semaphore. Extended end2end test suite with nativefence test cases. Bug: angleproject:2517 Test: angle_end2end_tests --gtest_filter=EGLSyncTest.AndroidNativeFence_* Change-Id: I8f6a6f4c3d71d83007f662b78377aa015a740035 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2026177 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 963759a3
...@@ -120,6 +120,16 @@ struct FeaturesVk : FeatureSetBase ...@@ -120,6 +120,16 @@ struct FeaturesVk : FeatureSetBase
"supports_external_memory_fuchsia", FeatureCategory::VulkanFeatures, "supports_external_memory_fuchsia", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_FUCHSIA_external_memory extension", &members}; "VkDevice supports the VK_FUCHSIA_external_memory extension", &members};
// Whether the VkDevice supports the VK_KHR_external_fence_capabilities extension.
Feature supportsExternalFenceCapabilities = {
"supports_external_fence_capabilities", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_KHR_external_fence_capabilities extension", &members};
// Whether the VkDevice supports the VK_KHR_external_semaphore_capabilities extension.
Feature supportsExternalSemaphoreCapabilities = {
"supports_external_semaphore_capabilities", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_KHR_external_semaphore_capabilities extension", &members};
// Whether the VkDevice supports the VK_KHR_external_semaphore_fd extension, on which the // Whether the VkDevice supports the VK_KHR_external_semaphore_fd extension, on which the
// GL_EXT_semaphore_fd extension can be layered. // GL_EXT_semaphore_fd extension can be layered.
Feature supportsExternalSemaphoreFd = { Feature supportsExternalSemaphoreFd = {
...@@ -132,6 +142,19 @@ struct FeaturesVk : FeatureSetBase ...@@ -132,6 +142,19 @@ struct FeaturesVk : FeatureSetBase
"supports_external_semaphore_fuchsia", FeatureCategory::VulkanFeatures, "supports_external_semaphore_fuchsia", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_FUCHSIA_external_semaphore extension", &members}; "VkDevice supports the VK_FUCHSIA_external_semaphore extension", &members};
// Whether the VkDevice supports the VK_KHR_external_fence_fd extension, on which the
// EGL_ANDROID_native_fence extension can be layered.
Feature supportsExternalFenceFd = {"supports_external_fence_fd",
FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_KHR_external_fence_fd extension",
&members, "http://anglebug.com/2517"};
// Whether the VkDevice can support EGL_ANDROID_native_fence_sync extension.
Feature supportsAndroidNativeFenceSync = {
"supports_android_native_fence_sync", FeatureCategory::VulkanFeatures,
"VkDevice supports the EGL_ANDROID_native_fence_sync extension", &members,
"http://anglebug.com/2517"};
// Whether the VkDevice supports the VK_EXT_shader_stencil_export extension, which is used to // Whether the VkDevice supports the VK_EXT_shader_stencil_export extension, which is used to
// perform multisampled resolve of stencil buffer. A multi-step workaround is used instead if // perform multisampled resolve of stencil buffer. A multi-step workaround is used instead if
// this extension is not available. // this extension is not available.
......
...@@ -21,9 +21,21 @@ Sync::Sync(rx::EGLImplFactory *factory, EGLenum type, const AttributeMap &attrib ...@@ -21,9 +21,21 @@ Sync::Sync(rx::EGLImplFactory *factory, EGLenum type, const AttributeMap &attrib
: mFence(factory->createSync(attribs)), : mFence(factory->createSync(attribs)),
mLabel(nullptr), mLabel(nullptr),
mType(type), mType(type),
mCondition(EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR),
mNativeFenceFD( mNativeFenceFD(
attribs.getAsInt(EGL_SYNC_NATIVE_FENCE_FD_ANDROID, EGL_NO_NATIVE_FENCE_FD_ANDROID)) attribs.getAsInt(EGL_SYNC_NATIVE_FENCE_FD_ANDROID, EGL_NO_NATIVE_FENCE_FD_ANDROID))
{} {
// Per extension spec: Signaling Condition.
// "If the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute is not
// EGL_NO_NATIVE_FENCE_FD_ANDROID then the EGL_SYNC_CONDITION_KHR attribute
// is set to EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID and the EGL_SYNC_STATUS_KHR
// attribute is set to reflect the signal status of the native fence object.
if ((mType == EGL_SYNC_NATIVE_FENCE_ANDROID) &&
(mNativeFenceFD != EGL_NO_NATIVE_FENCE_FD_ANDROID))
{
mCondition = EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID;
}
}
void Sync::onDestroy(const Display *display) void Sync::onDestroy(const Display *display)
{ {
......
...@@ -61,7 +61,7 @@ class Sync final : public angle::RefCountObject<Display, angle::Result>, public ...@@ -61,7 +61,7 @@ class Sync final : public angle::RefCountObject<Display, angle::Result>, public
EGLLabelKHR mLabel; EGLLabelKHR mLabel;
EGLenum mType; EGLenum mType;
static constexpr EGLint mCondition = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR; EGLint mCondition;
EGLint mNativeFenceFD; EGLint mNativeFenceFD;
}; };
......
...@@ -48,7 +48,7 @@ class EGLSyncImpl : angle::NonCopyable ...@@ -48,7 +48,7 @@ class EGLSyncImpl : angle::NonCopyable
EGLint flags) = 0; EGLint flags) = 0;
virtual egl::Error getStatus(const egl::Display *display, EGLint *outStatus) = 0; virtual egl::Error getStatus(const egl::Display *display, EGLint *outStatus) = 0;
virtual egl::Error dupNativeFenceFD(const egl::Display *display, EGLint *result) const = 0; virtual egl::Error dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const = 0;
}; };
} // namespace rx } // namespace rx
......
...@@ -221,6 +221,11 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const ...@@ -221,6 +221,11 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
outExtensions->contextPriority = !getRenderer()->getFeatures().allocateNonZeroMemory.enabled; outExtensions->contextPriority = !getRenderer()->getFeatures().allocateNonZeroMemory.enabled;
outExtensions->noConfigContext = true; outExtensions->noConfigContext = true;
#if defined(ANGLE_PLATFORM_ANDROID)
outExtensions->nativeFenceSyncANDROID =
getRenderer()->getFeatures().supportsAndroidNativeFenceSync.enabled;
#endif // defined(ANGLE_PLATFORM_ANDROID)
#if defined(ANGLE_PLATFORM_GGP) #if defined(ANGLE_PLATFORM_GGP)
outExtensions->ggpStreamDescriptor = true; outExtensions->ggpStreamDescriptor = true;
outExtensions->swapWithFrameToken = true; outExtensions->swapWithFrameToken = true;
......
...@@ -563,6 +563,54 @@ gl::Version LimitVersionTo(const gl::Version &current, const gl::Version &lower) ...@@ -563,6 +563,54 @@ gl::Version LimitVersionTo(const gl::Version &current, const gl::Version &lower)
{ {
return std::min(current, lower); return std::min(current, lower);
} }
ANGLE_MAYBE_UNUSED bool FencePropertiesCompatibleWithAndroid(
const VkExternalFenceProperties &externalFenceProperties)
{
// handleType here is the external fence type -
// we want type compatible with creating and export/dup() Android FD
// Imported handleType that can be exported - need for vkGetFenceFdKHR()
if ((externalFenceProperties.exportFromImportedHandleTypes &
VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR) == 0)
{
return false;
}
// HandleTypes which can be specified at creating a fence
if ((externalFenceProperties.compatibleHandleTypes &
VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR) == 0)
{
return false;
}
constexpr VkExternalFenceFeatureFlags kFeatureFlags =
(VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT_KHR |
VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT_KHR);
if ((externalFenceProperties.externalFenceFeatures & kFeatureFlags) != kFeatureFlags)
{
return false;
}
return true;
}
ANGLE_MAYBE_UNUSED bool SemaphorePropertiesCompatibleWithAndroid(
const VkExternalSemaphoreProperties &externalSemaphoreProperties)
{
// handleType here is the external semaphore type -
// we want type compatible with importing an Android FD
constexpr VkExternalSemaphoreFeatureFlags kFeatureFlags =
(VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR);
if ((externalSemaphoreProperties.externalSemaphoreFeatures & kFeatureFlags) != kFeatureFlags)
{
return false;
}
return true;
}
} // namespace } // namespace
// RendererVk implementation. // RendererVk implementation.
...@@ -576,6 +624,8 @@ RendererVk::RendererVk() ...@@ -576,6 +624,8 @@ RendererVk::RendererVk()
mDebugUtilsMessenger(VK_NULL_HANDLE), mDebugUtilsMessenger(VK_NULL_HANDLE),
mDebugReportCallback(VK_NULL_HANDLE), mDebugReportCallback(VK_NULL_HANDLE),
mPhysicalDevice(VK_NULL_HANDLE), mPhysicalDevice(VK_NULL_HANDLE),
mExternalFenceProperties{},
mExternalSemaphoreProperties{},
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()), mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mMaxVertexAttribDivisor(1), mMaxVertexAttribDivisor(1),
mMaxVertexAttribStride(0), mMaxVertexAttribStride(0),
...@@ -1044,6 +1094,32 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt ...@@ -1044,6 +1094,32 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt
vkGetPhysicalDeviceFeatures2KHR(mPhysicalDevice, &deviceFeatures); vkGetPhysicalDeviceFeatures2KHR(mPhysicalDevice, &deviceFeatures);
vkGetPhysicalDeviceProperties2KHR(mPhysicalDevice, &deviceProperties); vkGetPhysicalDeviceProperties2KHR(mPhysicalDevice, &deviceProperties);
// Fence properties
if (mFeatures.supportsExternalFenceCapabilities.enabled)
{
mExternalFenceProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES;
VkPhysicalDeviceExternalFenceInfo externalFenceInfo = {};
externalFenceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO;
externalFenceInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
vkGetPhysicalDeviceExternalFencePropertiesKHR(mPhysicalDevice, &externalFenceInfo,
&mExternalFenceProperties);
}
// Semaphore properties
if (mFeatures.supportsExternalSemaphoreCapabilities.enabled)
{
mExternalSemaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES;
VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {};
externalSemaphoreInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO;
externalSemaphoreInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
&mExternalSemaphoreProperties);
}
// Clean up pNext chains // Clean up pNext chains
mLineRasterizationFeatures.pNext = nullptr; mLineRasterizationFeatures.pNext = nullptr;
mProvokingVertexFeatures.pNext = nullptr; mProvokingVertexFeatures.pNext = nullptr;
...@@ -1198,11 +1274,26 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF ...@@ -1198,11 +1274,26 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME); enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
} }
if (getFeatures().supportsExternalSemaphoreCapabilities.enabled)
{
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
}
if (getFeatures().supportsExternalFenceCapabilities.enabled)
{
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
}
if (getFeatures().supportsExternalSemaphoreFd.enabled) if (getFeatures().supportsExternalSemaphoreFd.enabled)
{ {
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
} }
if (getFeatures().supportsExternalFenceFd.enabled)
{
enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME);
}
if (getFeatures().supportsExternalSemaphoreFuchsia.enabled) if (getFeatures().supportsExternalSemaphoreFuchsia.enabled)
{ {
enabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME); enabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
...@@ -1636,6 +1727,14 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1636,6 +1727,14 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
ExtensionFound(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME, deviceExtensionNames)); ExtensionFound(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION( ANGLE_FEATURE_CONDITION(
&mFeatures, supportsExternalFenceCapabilities,
ExtensionFound(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION(&mFeatures, supportsExternalSemaphoreCapabilities,
ExtensionFound(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME,
deviceExtensionNames));
ANGLE_FEATURE_CONDITION(
&mFeatures, supportsExternalSemaphoreFd, &mFeatures, supportsExternalSemaphoreFd,
ExtensionFound(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, deviceExtensionNames)); ExtensionFound(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, deviceExtensionNames));
...@@ -1644,6 +1743,29 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1644,6 +1743,29 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
ExtensionFound(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, deviceExtensionNames)); ExtensionFound(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION( ANGLE_FEATURE_CONDITION(
&mFeatures, supportsExternalFenceFd,
ExtensionFound(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, deviceExtensionNames));
#if defined(ANGLE_PLATFORM_ANDROID)
if (mFeatures.supportsExternalFenceCapabilities.enabled &&
mFeatures.supportsExternalSemaphoreCapabilities.enabled)
{
ANGLE_FEATURE_CONDITION(
&mFeatures, supportsAndroidNativeFenceSync,
(mFeatures.supportsExternalFenceFd.enabled &&
FencePropertiesCompatibleWithAndroid(mExternalFenceProperties) &&
mFeatures.supportsExternalSemaphoreFd.enabled &&
SemaphorePropertiesCompatibleWithAndroid(mExternalSemaphoreProperties)));
}
else
{
ANGLE_FEATURE_CONDITION(&mFeatures, supportsAndroidNativeFenceSync,
(mFeatures.supportsExternalFenceFd.enabled &&
mFeatures.supportsExternalSemaphoreFd.enabled));
}
#endif // defined(ANGLE_PLATFORM_ANDROID)
ANGLE_FEATURE_CONDITION(
&mFeatures, supportsShaderStencilExport, &mFeatures, supportsShaderStencilExport,
ExtensionFound(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, deviceExtensionNames)); ExtensionFound(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, deviceExtensionNames));
......
...@@ -287,6 +287,8 @@ class RendererVk : angle::NonCopyable ...@@ -287,6 +287,8 @@ class RendererVk : angle::NonCopyable
VkPhysicalDevice mPhysicalDevice; VkPhysicalDevice mPhysicalDevice;
VkPhysicalDeviceProperties mPhysicalDeviceProperties; VkPhysicalDeviceProperties mPhysicalDeviceProperties;
VkPhysicalDeviceFeatures mPhysicalDeviceFeatures; VkPhysicalDeviceFeatures mPhysicalDeviceFeatures;
VkExternalFenceProperties mExternalFenceProperties;
VkExternalSemaphoreProperties mExternalSemaphoreProperties;
VkPhysicalDeviceLineRasterizationFeaturesEXT mLineRasterizationFeatures; VkPhysicalDeviceLineRasterizationFeaturesEXT mLineRasterizationFeatures;
VkPhysicalDeviceProvokingVertexFeaturesEXT mProvokingVertexFeatures; VkPhysicalDeviceProvokingVertexFeaturesEXT mProvokingVertexFeatures;
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT mVertexAttributeDivisorFeatures; VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT mVertexAttributeDivisorFeatures;
......
...@@ -108,16 +108,206 @@ angle::Result SyncHelper::serverWait(ContextVk *contextVk) ...@@ -108,16 +108,206 @@ angle::Result SyncHelper::serverWait(ContextVk *contextVk)
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result SyncHelper::getStatus(Context *context, bool *signaled) angle::Result SyncHelper::getStatus(Context *context, bool *signaled) const
{ {
VkResult result = mEvent.getStatus(context->getDevice()); VkResult result = mEvent.getStatus(context->getDevice());
if (result != VK_EVENT_SET && result != VK_EVENT_RESET) if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
{ {
ANGLE_VK_TRY(context, result); ANGLE_VK_TRY(context, result);
} }
*signaled = result == VK_EVENT_SET; *signaled = (result == VK_EVENT_SET);
return angle::Result::Continue; return angle::Result::Continue;
} }
void SyncHelperNativeFence::releaseToRenderer(RendererVk *renderer)
{
renderer->collectGarbageAndReinit(&mUse, &mFenceWithFd);
}
// Note: Having mFenceWithFd hold the FD, so that ownership is with ICD. 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 can't time when to close(fd);
angle::Result SyncHelperNativeFence::initializeWithFd(ContextVk *contextVk, int inFd)
{
RendererVk *renderer = contextVk->getRenderer();
VkDevice device = renderer->getDevice();
// Create fenceInfo base.
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = 0;
// If valid FD provided by application - import it to fence.
if (inFd > kInvalidFenceFd)
{
// Initialize/create a VkFence handle
ANGLE_VK_TRY(contextVk, mFenceWithFd.init(device, fenceCreateInfo));
// Import FD - after creating fence.
VkImportFenceFdInfoKHR importFenceFdInfo = {};
importFenceFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR;
importFenceFdInfo.pNext = nullptr;
importFenceFdInfo.fence = mFenceWithFd.getHandle();
importFenceFdInfo.flags = VK_FENCE_IMPORT_TEMPORARY_BIT_KHR;
importFenceFdInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
importFenceFdInfo.fd = inFd;
VkResult result = mFenceWithFd.importFd(device, importFenceFdInfo);
if (result != VK_SUCCESS)
{
mFenceWithFd.destroy(device);
ANGLE_VK_TRY(contextVk, result);
}
retain(&contextVk->getResourceUseList());
return angle::Result::Continue;
}
// If invalid FD provided by application - create one with fence.
if (inFd == kInvalidFenceFd)
{
// Attach export FD-handleType, pNext struct, to indicate we may want to export FD.
VkExportFenceCreateInfo exportCreateInfo = {};
exportCreateInfo.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO;
exportCreateInfo.handleTypes = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
fenceCreateInfo.pNext = &exportCreateInfo;
// Initialize/create a VkFence handle
ANGLE_VK_TRY(contextVk, mFenceWithFd.init(device, fenceCreateInfo));
/*
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;
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
if (renderer->queueSubmit(contextVk, contextVk->getPriority(), submitInfo, &mFenceWithFd,
&serialOut) != angle::Result::Continue)
{
mFenceWithFd.destroy(device);
return angle::Result::Stop;
}
return angle::Result::Continue;
}
// Should not get here
return angle::Result::Stop;
}
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));
}
// Wait for mFenceWithFd to be signaled.
VkResult status = mFenceWithFd.wait(renderer->getDevice(), timeout);
// 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 SyncHelperNativeFence::serverWait(ContextVk *contextVk)
{
if (!mFenceWithFd.valid())
{
return angle::Result::Stop;
}
RendererVk *renderer = contextVk->getRenderer();
VkDevice device = renderer->getDevice();
// Export an FD from mFenceWithFd
int fenceFd = kInvalidFenceFd;
VkFenceGetFdInfoKHR getFdInfo = {};
getFdInfo.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
getFdInfo.fence = mFenceWithFd.getHandle();
getFdInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
ANGLE_VK_TRY(contextVk, mFenceWithFd.exportFd(device, getFdInfo, &fenceFd));
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 = fenceFd;
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->insertWaitSemaphore(&waitSemaphore.get());
contextVk->addGarbage(&waitSemaphore.get()); // This releases the handle.
return angle::Result::Continue;
}
angle::Result SyncHelperNativeFence::getStatus(Context *context, bool *signaled) const
{
VkResult result = mFenceWithFd.getStatus(context->getDevice());
if (result != VK_SUCCESS && result != VK_NOT_READY)
{
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())
{
return angle::Result::Stop;
}
VkFenceGetFdInfoKHR fenceGetFdInfo = {};
fenceGetFdInfo.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
fenceGetFdInfo.fence = mFenceWithFd.getHandle();
fenceGetFdInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
ANGLE_VK_TRY(context, mFenceWithFd.exportFd(context->getDevice(), fenceGetFdInfo, fdOut));
return angle::Result::Continue;
}
} // namespace vk } // namespace vk
SyncVk::SyncVk() : SyncImpl() {} SyncVk::SyncVk() : SyncImpl() {}
...@@ -126,7 +316,7 @@ SyncVk::~SyncVk() {} ...@@ -126,7 +316,7 @@ SyncVk::~SyncVk() {}
void SyncVk::onDestroy(const gl::Context *context) void SyncVk::onDestroy(const gl::Context *context)
{ {
mFenceSync.releaseToRenderer(vk::GetImpl(context)->getRenderer()); mSyncHelper.releaseToRenderer(vk::GetImpl(context)->getRenderer());
} }
angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags) angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
...@@ -134,7 +324,7 @@ angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfie ...@@ -134,7 +324,7 @@ angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfie
ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE); ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
ASSERT(flags == 0); ASSERT(flags == 0);
return mFenceSync.initialize(vk::GetImpl(context)); return mSyncHelper.initialize(vk::GetImpl(context));
} }
angle::Result SyncVk::clientWait(const gl::Context *context, angle::Result SyncVk::clientWait(const gl::Context *context,
...@@ -149,8 +339,8 @@ angle::Result SyncVk::clientWait(const gl::Context *context, ...@@ -149,8 +339,8 @@ angle::Result SyncVk::clientWait(const gl::Context *context,
bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0; bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
VkResult result; VkResult result;
ANGLE_TRY(mFenceSync.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout), ANGLE_TRY(mSyncHelper.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout),
&result)); &result));
switch (result) switch (result)
{ {
...@@ -179,43 +369,62 @@ angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, G ...@@ -179,43 +369,62 @@ angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, G
ASSERT(timeout == GL_TIMEOUT_IGNORED); ASSERT(timeout == GL_TIMEOUT_IGNORED);
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
return mFenceSync.serverWait(contextVk); return mSyncHelper.serverWait(contextVk);
} }
angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult) angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
{ {
bool signaled = false; bool signaled = false;
ANGLE_TRY(mFenceSync.getStatus(vk::GetImpl(context), &signaled)); ANGLE_TRY(mSyncHelper.getStatus(vk::GetImpl(context), &signaled));
*outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED; *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
return angle::Result::Continue; return angle::Result::Continue;
} }
EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs) : EGLSyncImpl() EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs)
: EGLSyncImpl(), mSyncHelper(nullptr), mAttribs(attribs)
{}
EGLSyncVk::~EGLSyncVk()
{ {
ASSERT(attribs.isEmpty()); SafeDelete<vk::SyncHelper>(mSyncHelper);
} }
EGLSyncVk::~EGLSyncVk() {}
void EGLSyncVk::onDestroy(const egl::Display *display) void EGLSyncVk::onDestroy(const egl::Display *display)
{ {
mFenceSync.releaseToRenderer(vk::GetImpl(display)->getRenderer()); mSyncHelper->releaseToRenderer(vk::GetImpl(display)->getRenderer());
} }
egl::Error EGLSyncVk::initialize(const egl::Display *display, egl::Error EGLSyncVk::initialize(const egl::Display *display,
const gl::Context *context, const gl::Context *context,
EGLenum type) EGLenum type)
{ {
ASSERT(type == EGL_SYNC_FENCE_KHR);
ASSERT(context != nullptr); ASSERT(context != nullptr);
mType = type;
if (mFenceSync.initialize(vk::GetImpl(context)) == angle::Result::Stop) switch (type)
{ {
return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object"); case EGL_SYNC_FENCE_KHR:
ASSERT(mAttribs.isEmpty());
mSyncHelper = new vk::SyncHelper();
if (mSyncHelper->initialize(vk::GetImpl(context)) == 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);
} }
return egl::NoError();
} }
egl::Error EGLSyncVk::clientWait(const egl::Display *display, egl::Error EGLSyncVk::clientWait(const egl::Display *display,
...@@ -230,8 +439,8 @@ egl::Error EGLSyncVk::clientWait(const egl::Display *display, ...@@ -230,8 +439,8 @@ egl::Error EGLSyncVk::clientWait(const egl::Display *display,
VkResult result; VkResult result;
ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr; ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
if (mFenceSync.clientWait(vk::GetImpl(display), contextVk, flush, if (mSyncHelper->clientWait(vk::GetImpl(display), contextVk, flush,
static_cast<uint64_t>(timeout), &result) == angle::Result::Stop) static_cast<uint64_t>(timeout), &result) == angle::Result::Stop)
{ {
return egl::Error(EGL_BAD_ALLOC); return egl::Error(EGL_BAD_ALLOC);
} }
...@@ -269,13 +478,13 @@ egl::Error EGLSyncVk::serverWait(const egl::Display *display, ...@@ -269,13 +478,13 @@ egl::Error EGLSyncVk::serverWait(const egl::Display *display,
DisplayVk *displayVk = vk::GetImpl(display); DisplayVk *displayVk = vk::GetImpl(display);
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
return angle::ToEGL(mFenceSync.serverWait(contextVk), displayVk, EGL_BAD_ALLOC); return angle::ToEGL(mSyncHelper->serverWait(contextVk), displayVk, EGL_BAD_ALLOC);
} }
egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus) egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
{ {
bool signaled = false; bool signaled = false;
if (mFenceSync.getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop) if (mSyncHelper->getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
{ {
return egl::Error(EGL_BAD_ALLOC); return egl::Error(EGL_BAD_ALLOC);
} }
...@@ -284,10 +493,16 @@ egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus) ...@@ -284,10 +493,16 @@ egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
return egl::NoError(); return egl::NoError();
} }
egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *result) const egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const
{ {
UNREACHABLE(); switch (mType)
return egl::EglBadDisplay(); {
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 } // namespace rx
...@@ -23,24 +23,33 @@ namespace rx ...@@ -23,24 +23,33 @@ namespace rx
{ {
namespace vk namespace vk
{ {
// The behaviors of SyncImpl and EGLSyncImpl as fence syncs (only supported type) are currently
// Represents an invalid native fence FD.
constexpr int kInvalidFenceFd = EGL_NO_NATIVE_FENCE_FD_ANDROID;
// Implementation of fence types - glFenceSync, and EGLSync(EGL_SYNC_FENCE_KHR).
// The behaviors of SyncVk and EGLFenceSyncVk as fence syncs are currently
// identical for the Vulkan backend, and this class implements both interfaces. // identical for the Vulkan backend, and this class implements both interfaces.
class SyncHelper : public vk::Resource class SyncHelper : public vk::Resource
{ {
public: public:
SyncHelper(); SyncHelper();
~SyncHelper(); virtual ~SyncHelper();
void releaseToRenderer(RendererVk *renderer); virtual void releaseToRenderer(RendererVk *renderer);
angle::Result initialize(ContextVk *contextVk); virtual angle::Result initialize(ContextVk *contextVk);
angle::Result clientWait(Context *context, virtual angle::Result clientWait(Context *context,
ContextVk *contextVk, ContextVk *contextVk,
bool flushCommands, bool flushCommands,
uint64_t timeout, uint64_t timeout,
VkResult *outResult); VkResult *outResult);
angle::Result serverWait(ContextVk *contextVk); virtual angle::Result serverWait(ContextVk *contextVk);
angle::Result getStatus(Context *context, bool *signaled); virtual angle::Result getStatus(Context *context, bool *signaled) const;
virtual angle::Result dupNativeFenceFD(Context *context, int *fdOut) const
{
return angle::Result::Stop;
}
private: private:
// The vkEvent that's signaled on `init` and can be waited on in `serverWait`, or queried with // The vkEvent that's signaled on `init` and can be waited on in `serverWait`, or queried with
...@@ -50,8 +59,33 @@ class SyncHelper : public vk::Resource ...@@ -50,8 +59,33 @@ class SyncHelper : public vk::Resource
// `clientWait` waits on this fence. // `clientWait` waits on this fence.
Shared<Fence> mFence; Shared<Fence> mFence;
}; };
// Implementation of sync types: EGLSync(EGL_SYNC_ANDROID_NATIVE_FENCE_ANDROID).
class SyncHelperNativeFence : public SyncHelper
{
public:
SyncHelperNativeFence() {}
~SyncHelperNativeFence() override {}
void releaseToRenderer(RendererVk *renderer) override;
angle::Result initializeWithFd(ContextVk *contextVk, int inFd);
angle::Result clientWait(Context *context,
ContextVk *contextVk,
bool flushCommands,
uint64_t timeout,
VkResult *outResult) override;
angle::Result serverWait(ContextVk *contextVk) override;
angle::Result getStatus(Context *context, bool *signaled) const override;
angle::Result dupNativeFenceFD(Context *context, int *fdOut) const override;
private:
vk::Fence mFenceWithFd;
};
} // namespace vk } // namespace vk
// Implementor for glFenceSync.
class SyncVk final : public SyncImpl class SyncVk final : public SyncImpl
{ {
public: public:
...@@ -71,9 +105,10 @@ class SyncVk final : public SyncImpl ...@@ -71,9 +105,10 @@ class SyncVk final : public SyncImpl
angle::Result getStatus(const gl::Context *context, GLint *outResult) override; angle::Result getStatus(const gl::Context *context, GLint *outResult) override;
private: private:
vk::SyncHelper mFenceSync; vk::SyncHelper mSyncHelper;
}; };
// Implementor for EGLSync.
class EGLSyncVk final : public EGLSyncImpl class EGLSyncVk final : public EGLSyncImpl
{ {
public: public:
...@@ -95,10 +130,12 @@ class EGLSyncVk final : public EGLSyncImpl ...@@ -95,10 +130,12 @@ class EGLSyncVk final : public EGLSyncImpl
EGLint flags) override; EGLint flags) override;
egl::Error getStatus(const egl::Display *display, EGLint *outStatus) override; egl::Error getStatus(const egl::Display *display, EGLint *outStatus) override;
egl::Error dupNativeFenceFD(const egl::Display *display, EGLint *result) const override; egl::Error dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const override;
private: private:
vk::SyncHelper mFenceSync; EGLenum mType;
vk::SyncHelper *mSyncHelper; // SyncHelper or SyncHelperNativeFence decided at run-time.
const egl::AttributeMap &mAttribs;
}; };
} // namespace rx } // namespace rx
......
...@@ -427,6 +427,8 @@ class Semaphore final : public WrappedObject<Semaphore, VkSemaphore> ...@@ -427,6 +427,8 @@ class Semaphore final : public WrappedObject<Semaphore, VkSemaphore>
void destroy(VkDevice device); void destroy(VkDevice device);
VkResult init(VkDevice device); VkResult init(VkDevice device);
VkResult init(VkDevice device, const VkSemaphoreCreateInfo &createInfo);
VkResult importFd(VkDevice device, const VkImportSemaphoreFdInfoKHR &importFdInfo) const;
}; };
class Framebuffer final : public WrappedObject<Framebuffer, VkFramebuffer> class Framebuffer final : public WrappedObject<Framebuffer, VkFramebuffer>
...@@ -600,6 +602,8 @@ class Fence final : public WrappedObject<Fence, VkFence> ...@@ -600,6 +602,8 @@ class Fence final : public WrappedObject<Fence, VkFence>
VkResult reset(VkDevice device); VkResult reset(VkDevice device);
VkResult getStatus(VkDevice device) const; VkResult getStatus(VkDevice device) const;
VkResult wait(VkDevice device, uint64_t timeout) const; VkResult wait(VkDevice device, uint64_t timeout) const;
VkResult importFd(VkDevice device, const VkImportFenceFdInfoKHR &importFenceFdInfo) const;
VkResult exportFd(VkDevice device, const VkFenceGetFdInfoKHR &fenceGetFdInfo, int *outFd) const;
}; };
class QueryPool final : public WrappedObject<QueryPool, VkQueryPool> class QueryPool final : public WrappedObject<QueryPool, VkQueryPool>
...@@ -1263,6 +1267,19 @@ ANGLE_INLINE VkResult Semaphore::init(VkDevice device) ...@@ -1263,6 +1267,19 @@ ANGLE_INLINE VkResult Semaphore::init(VkDevice device)
return vkCreateSemaphore(device, &semaphoreInfo, nullptr, &mHandle); return vkCreateSemaphore(device, &semaphoreInfo, nullptr, &mHandle);
} }
ANGLE_INLINE VkResult Semaphore::init(VkDevice device, const VkSemaphoreCreateInfo &createInfo)
{
ASSERT(valid());
return vkCreateSemaphore(device, &createInfo, nullptr, &mHandle);
}
ANGLE_INLINE VkResult Semaphore::importFd(VkDevice device,
const VkImportSemaphoreFdInfoKHR &importFdInfo) const
{
ASSERT(valid());
return vkImportSemaphoreFdKHR(device, &importFdInfo);
}
// Framebuffer implementation. // Framebuffer implementation.
ANGLE_INLINE void Framebuffer::destroy(VkDevice device) ANGLE_INLINE void Framebuffer::destroy(VkDevice device)
{ {
...@@ -1675,6 +1692,21 @@ ANGLE_INLINE VkResult Fence::wait(VkDevice device, uint64_t timeout) const ...@@ -1675,6 +1692,21 @@ ANGLE_INLINE VkResult Fence::wait(VkDevice device, uint64_t timeout) const
return vkWaitForFences(device, 1, &mHandle, true, timeout); return vkWaitForFences(device, 1, &mHandle, true, timeout);
} }
ANGLE_INLINE VkResult Fence::importFd(VkDevice device,
const VkImportFenceFdInfoKHR &importFenceFdInfo) const
{
ASSERT(valid());
return vkImportFenceFdKHR(device, &importFenceFdInfo);
}
ANGLE_INLINE VkResult Fence::exportFd(VkDevice device,
const VkFenceGetFdInfoKHR &fenceGetFdInfo,
int *fdOut) const
{
ASSERT(valid());
return vkGetFenceFdKHR(device, &fenceGetFdInfo, fdOut);
}
// QueryPool implementation. // QueryPool implementation.
ANGLE_INLINE void QueryPool::destroy(VkDevice device) ANGLE_INLINE void QueryPool::destroy(VkDevice device)
{ {
......
...@@ -4221,12 +4221,6 @@ Error ValidateDupNativeFenceFDANDROID(const Display *display, const Sync *sync) ...@@ -4221,12 +4221,6 @@ Error ValidateDupNativeFenceFDANDROID(const Display *display, const Sync *sync)
ANGLE_TRY(ValidateSync(display, sync)); ANGLE_TRY(ValidateSync(display, sync));
if (sync->getNativeFenceFD() == EGL_NO_NATIVE_FENCE_FD_ANDROID)
{
return EglBadParameter() << "EGL_NATIVE_FENCE_FD_ANDROID attribute of sync is "
"EGL_NO_NATIVE_FENCE_FD_ANDROID";
}
return NoError(); return NoError();
} }
......
...@@ -27,6 +27,12 @@ class EGLSyncTest : public ANGLETest ...@@ -27,6 +27,12 @@ class EGLSyncTest : public ANGLETest
IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_wait_sync"); IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_wait_sync");
} }
bool hasGLSyncExtension() const { return IsGLExtensionEnabled("GL_OES_EGL_sync"); } bool hasGLSyncExtension() const { return IsGLExtensionEnabled("GL_OES_EGL_sync"); }
bool hasAndroidNativeFenceSyncExtension() const
{
return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(),
"EGL_ANDROID_native_fence_sync");
}
}; };
// Test error cases for all EGL_KHR_fence_sync functions // Test error cases for all EGL_KHR_fence_sync functions
...@@ -244,6 +250,235 @@ TEST_P(EGLSyncTest, WaitNative) ...@@ -244,6 +250,235 @@ TEST_P(EGLSyncTest, WaitNative)
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red); EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
} }
// Verify eglDupNativeFence for EGL_ANDROID_native_fence_sync
TEST_P(EGLSyncTest, AndroidNativeFence_DupNativeFenceFD)
{
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
EGLDisplay display = getEGLWindow()->getDisplay();
// We can ClientWait on this
EGLSyncKHR syncWithGeneratedFD =
eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
int fd = eglDupNativeFenceFDANDROID(display, syncWithGeneratedFD);
EXPECT_EGL_SUCCESS();
// Clean up created objects.
if (fd != EGL_NO_NATIVE_FENCE_FD_ANDROID)
{
close(fd);
}
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
}
// Verify CreateSync and ClientWait for EGL_ANDROID_native_fence_sync
TEST_P(EGLSyncTest, AndroidNativeFence_ClientWait)
{
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
EGLint value = 0;
EGLDisplay display = getEGLWindow()->getDisplay();
// We can ClientWait on this
EGLSyncKHR syncWithGeneratedFD =
eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
// Create work to do
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFinish();
// Wait for draw to complete
EXPECT_EQ(EGL_CONDITION_SATISFIED,
eglClientWaitSyncKHR(display, syncWithGeneratedFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
1000000000));
EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithGeneratedFD, EGL_SYNC_STATUS_KHR, &value));
EXPECT_EQ(value, EGL_SIGNALED_KHR);
// Clean up created objects.
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
}
// Verify WaitSync with EGL_ANDROID_native_fence_sync
// Simulate passing FDs across processes by passing across Contexts.
TEST_P(EGLSyncTest, AndroidNativeFence_WaitSync)
{
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
EGLint value = 0;
EGLDisplay display = getEGLWindow()->getDisplay();
EGLSurface surface = getEGLWindow()->getSurface();
/*- First Context ------------------------*/
// We can ClientWait on this
EGLSyncKHR syncWithGeneratedFD =
eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
int fd = eglDupNativeFenceFDANDROID(display, syncWithGeneratedFD);
EXPECT_EGL_SUCCESS(); // Can return -1 (when signaled) or valid FD.
// Create work to do
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFinish();
/*- Second Context ------------------------*/
if (fd > EGL_NO_NATIVE_FENCE_FD_ANDROID)
{
EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EGLContext context2 = getEGLWindow()->createContext(EGL_NO_CONTEXT);
EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context2));
// We can eglWaitSync on this - import FD from first sync.
EGLint syncAttribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, (EGLint)fd, EGL_NONE};
EGLSyncKHR syncWithDupFD =
eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, syncAttribs);
EXPECT_NE(syncWithDupFD, EGL_NO_SYNC_KHR);
// Second draw waits for first to complete. May already be signaled - ignore error.
if (eglWaitSyncKHR(display, syncWithDupFD, 0) == EGL_TRUE)
{
// Create work to do
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFinish();
}
// Wait for second draw to complete
EXPECT_EQ(EGL_CONDITION_SATISFIED,
eglClientWaitSyncKHR(display, syncWithDupFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
1000000000));
EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithDupFD, EGL_SYNC_STATUS_KHR, &value));
EXPECT_EQ(value, EGL_SIGNALED_KHR);
// Reset to default context and surface.
EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, getEGLWindow()->getContext()));
// Clean up created objects.
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithDupFD));
EXPECT_EGL_TRUE(eglDestroyContext(display, context2));
}
// Wait for first draw to complete
EXPECT_EQ(EGL_CONDITION_SATISFIED,
eglClientWaitSyncKHR(display, syncWithGeneratedFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
1000000000));
EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithGeneratedFD, EGL_SYNC_STATUS_KHR, &value));
EXPECT_EQ(value, EGL_SIGNALED_KHR);
// Clean up created objects.
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
}
// Verify EGL_ANDROID_native_fence_sync
// Simulate passing FDs across processes by passing across Contexts.
TEST_P(EGLSyncTest, AndroidNativeFence_withFences)
{
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension());
ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
ANGLE_SKIP_TEST_IF(!hasAndroidNativeFenceSyncExtension());
EGLint value = 0;
EGLDisplay display = getEGLWindow()->getDisplay();
EGLSurface surface = getEGLWindow()->getSurface();
/*- First Context ------------------------*/
// Extra fence syncs to ensure that Fence and Android Native fences work together
EGLSyncKHR syncFence1 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
EXPECT_NE(syncFence1, EGL_NO_SYNC_KHR);
// We can ClientWait on this
EGLSyncKHR syncWithGeneratedFD =
eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
EXPECT_NE(syncWithGeneratedFD, EGL_NO_SYNC_KHR);
int fd = eglDupNativeFenceFDANDROID(display, syncWithGeneratedFD);
EXPECT_EGL_SUCCESS(); // Can return -1 (when signaled) or valid FD.
EGLSyncKHR syncFence2 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
EXPECT_NE(syncFence2, EGL_NO_SYNC_KHR);
// Create work to do
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
/*- Second Context ------------------------*/
if (fd > EGL_NO_NATIVE_FENCE_FD_ANDROID)
{
EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EGLContext context2 = getEGLWindow()->createContext(EGL_NO_CONTEXT);
EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context2));
// check that Fence and Android fences work together
EGLSyncKHR syncFence3 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
EXPECT_NE(syncFence3, EGL_NO_SYNC_KHR);
// We can eglWaitSync on this
EGLint syncAttribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, (EGLint)fd, EGL_NONE};
EGLSyncKHR syncWithDupFD =
eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, syncAttribs);
EXPECT_NE(syncWithDupFD, EGL_NO_SYNC_KHR);
EGLSyncKHR syncFence4 = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
EXPECT_NE(syncFence4, EGL_NO_SYNC_KHR);
// Second draw waits for first to complete. May already be signaled - ignore error.
if (eglWaitSyncKHR(display, syncWithDupFD, 0) == EGL_TRUE)
{
// Create work to do
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
}
// Wait for second draw to complete
EXPECT_EQ(EGL_CONDITION_SATISFIED,
eglClientWaitSyncKHR(display, syncWithDupFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
1000000000));
EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithDupFD, EGL_SYNC_STATUS_KHR, &value));
EXPECT_EQ(value, EGL_SIGNALED_KHR);
// Reset to default context and surface.
EXPECT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_EGL_TRUE(eglMakeCurrent(display, surface, surface, getEGLWindow()->getContext()));
// Clean up created objects.
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence3));
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence4));
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithDupFD));
EXPECT_EGL_TRUE(eglDestroyContext(display, context2));
}
// Wait for first draw to complete
EXPECT_EQ(EGL_CONDITION_SATISFIED,
eglClientWaitSyncKHR(display, syncWithGeneratedFD, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
1000000000));
EXPECT_EGL_TRUE(eglGetSyncAttribKHR(display, syncWithGeneratedFD, EGL_SYNC_STATUS_KHR, &value));
EXPECT_EQ(value, EGL_SIGNALED_KHR);
// Clean up created objects.
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence1));
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncFence2));
EXPECT_EGL_TRUE(eglDestroySyncKHR(display, syncWithGeneratedFD));
}
ANGLE_INSTANTIATE_TEST(EGLSyncTest, ANGLE_INSTANTIATE_TEST(EGLSyncTest,
ES2_D3D9(), ES2_D3D9(),
ES2_D3D11(), ES2_D3D11(),
......
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