Vulkan: Add support for setting timestamp surface attribute

On Android the EGL wrapper handles most of the functionality required
by EGL_ANDROID_get_frame_timestamps. However if for some reason the
swapchain is recreated, the timestamp state would be lost resulting in
stuttering.

Introduce EGL_ANGLE_timestamp_surface_attribute extension that adds
support for toggling the EGL_TIMESTAMPS_ANDROID attribute of a surface.
Cache this state and recreate the swapchain accordingly.

Bug: angleproject:7489
Test: EGLSurfaceTest.TimestampSurfaceAttribute*
Test: dEQP-EGL.functional.get_frame_timestamps*
Change-Id: I3660f7137c006d904164d243a682a4ff520eabd8
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3753396
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Ian Elliott <ianelliott@google.com>
Commit-Queue: mohan maiya <m.maiya@samsung.com>
This commit is contained in:
Mohan Maiya
2022-08-23 09:47:02 -07:00
committed by Angle LUCI CQ
parent f10625d3cc
commit aa2a558ec2
17 changed files with 240 additions and 12 deletions

View File

@@ -0,0 +1,95 @@
Name
ANGLE_timestamp_surface_attribute
Name Strings
EGL_ANGLE_timestamp_surface_attribute
Contributors
Mohan Maiya
Ian Elliot
Contacts
Mohan Maiya, Samsung (m.maiya 'at' samsung 'dot' com)
Notice
Copyright (c) 2022 The Khronos Group Inc. Copyright terms at
http://www.khronos.org/registry/speccopyright.html
Status
Draft.
Version
Version 1, July 12, 2022
Number
EGL Extension ###
Dependencies
Requires support for EGL_ANDROID_get_frame_timestamps extension.
Refer to EGL_ANDROID_get_frame_timestamps specification for other
dependencies.
Overview
On Android most of the functionality required by EGL_ANDROID_get_frame_timestamps
is handled by a wrapper outside of the EGL driver. However it is necessary
to be aware of EGL_TIMESTAMPS_ANDROID state on some drivers
(like ANGLE, which layers GLES APIs over Vulkan, which needs this information
to setup Vulkan swapchains appropriately).
This extension allows ANGLE's EGL implementation to rely on the Android EGL
loader to support and implement the EGL_ANDROID_get_frame_timestamps extension,
with the exception that ANGLE's EGL implementation will support caching
EGL_TIMESTAMPS_ANDROID state.
For details about EGL_TIMESTAMPS_ANDROID and other timestamp related terminology
please refer to the EGL_ANDROID_get_frame_timestamps specification.
New Types
None.
New Procedures and Functions
None.
New Tokens
None.
For clarity, restating the section of EGL_ANDROID_get_frame_timestamps specification
that will be supported by this extension:
Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
"Surface Attributes", page 43:
If attribute is EGL_TIMESTAMPS_ANDROID, then values specifies whether to
enable/disable timestamp collection for this surface. A value of EGL_TRUE
enables timestamp collection, while a value of EGL_FALSE disables it. The
initial value is false. If surface is not a window surface this has no
effect.
Issues
None.
Revision History
#2 - (August 23, 2022) Mohan Maiya
Added language to clarify that the extension will require support,
from an external module, of the EGL_ANDROID_get_frame_timestamps
extension for the most part with the exception of providing support
for caching EGL_TIMESTAMPS_ANDROID state.
#1 - (July 12, 2022) Mohan Maiya
Original draft

View File

@@ -687,11 +687,9 @@ struct FeaturesVk : FeatureSetBase
"http://anglebug.com/7553"};
FeatureInfo preferLinearFilterForYUV = {
"preferLinearFilterForYUV",
FeatureCategory::VulkanFeatures,
"Prefer to use VK_FILTER_LINEAR for VkSamplerYcbcrConversion",
&members,
};
"preferLinearFilterForYUV", FeatureCategory::VulkanFeatures,
"Prefer to use VK_FILTER_LINEAR for VkSamplerYcbcrConversion", &members,
"https://anglebug.com/7382"};
FeatureInfo supportsYuvTarget = {
"supportsYuvTarget",
@@ -713,6 +711,11 @@ struct FeaturesVk : FeatureSetBase
"spaces",
&members,
};
FeatureInfo supportsTimestampSurfaceAttribute = {
"supportsTimestampSurfaceAttribute", FeatureCategory::VulkanFeatures,
"Platform supports setting frame timestamp surface attribute", &members,
"https://anglebug.com/7489"};
};
inline FeaturesVk::FeaturesVk() = default;

View File

@@ -933,7 +933,8 @@
"category": "Features",
"description": [
"Prefer to use VK_FILTER_LINEAR for VkSamplerYcbcrConversion"
]
],
"issue": "https://anglebug.com/7382"
},
{
"name": "supports_yuv_target",
@@ -958,6 +959,14 @@
"Use VK_COLOR_SPACE_PASS_THROUGH_EXT for EGL_NONE or unspecifed color ",
"spaces"
]
},
{
"name": "supports_timestamp_surface_attribute",
"category": "Features",
"description": [
"Platform supports setting frame timestamp surface attribute"
],
"issue": "https://anglebug.com/7489"
}
]
}

View File

@@ -6,7 +6,7 @@
"include/platform/FeaturesMtl_autogen.h":
"6b6d49c35bc9246361f8dac0a5445a02",
"include/platform/FeaturesVk_autogen.h":
"c7a1f002d5309d937136394bc698eaad",
"c9c02b6fa5cca1c79b6a247dc66d311c",
"include/platform/FrontendFeatures_autogen.h":
"834af301a444786b363a1005c80e1f53",
"include/platform/d3d_features.json":
@@ -20,9 +20,9 @@
"include/platform/mtl_features.json":
"1fabfe4d5c2eb3683a5b567ab60ad83c",
"include/platform/vk_features.json":
"2cdaa09ec1457cf65147c4e104c0978d",
"a0e1f206156a4562886362b1aeb38a49",
"util/angle_features_autogen.cpp":
"b666b866b5ced187639a1c9db2c47b51",
"5ac11ec0033f29fd8a8475658a1f24fa",
"util/angle_features_autogen.h":
"40154ba910574b9eff2310addf557b9b"
"c1b1c0fc7a7ccd997403857b1e6f204f"
}

View File

@@ -153,6 +153,9 @@ extern PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT
extern PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR vkGetPhysicalDeviceFragmentShadingRatesKHR;
extern PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR;
// VK_GOOGLE_display_timing
extern PFN_vkGetPastPresentationTimingGOOGLE vkGetPastPresentationTimingGOOGLE;
} // namespace rx
#endif // ANGLE_SHARED_LIBVULKAN

View File

@@ -1276,6 +1276,7 @@ std::vector<std::string> DisplayExtensions::getStrings() const
InsertExtensionString("EGL_ANDROID_framebuffer_target", framebufferTargetANDROID, &extensionStrings);
InsertExtensionString("EGL_ANDROID_image_native_buffer", imageNativeBuffer, &extensionStrings);
InsertExtensionString("EGL_ANDROID_get_frame_timestamps", getFrameTimestamps, &extensionStrings);
InsertExtensionString("EGL_ANGLE_timestamp_surface_attribute", timestampSurfaceAttributeANGLE, &extensionStrings);
InsertExtensionString("EGL_ANDROID_recordable", recordable, &extensionStrings);
InsertExtensionString("EGL_ANGLE_power_preference", powerPreference, &extensionStrings);
InsertExtensionString("EGL_ANGLE_image_d3d11_texture", imageD3D11Texture, &extensionStrings);

View File

@@ -554,6 +554,9 @@ struct DisplayExtensions
// EGL_ANDROID_get_frame_timestamps
bool getFrameTimestamps = false;
// EGL_ANGLE_timestamp_surface_attribute
bool timestampSurfaceAttributeANGLE = false;
// EGL_ANDROID_recordable
bool recordable = false;

View File

@@ -352,6 +352,9 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
getRenderer()->getFeatures().supportsLockSurfaceExtension.enabled;
outExtensions->partialUpdateKHR = true;
outExtensions->timestampSurfaceAttributeANGLE =
getRenderer()->getFeatures().supportsTimestampSurfaceAttribute.enabled;
}
void DisplayVk::generateCaps(egl::Caps *outCaps) const

View File

@@ -2454,6 +2454,15 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
mEnabledDeviceExtensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
}
if (getFeatures().supportsTimestampSurfaceAttribute.enabled)
{
mEnabledDeviceExtensions.push_back(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME);
#if !defined(ANGLE_SHARED_LIBVULKAN)
InitGetPastPresentationTimingGoogleFunction(mDevice);
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
ASSERT(vkGetPastPresentationTimingGOOGLE);
}
std::sort(mEnabledDeviceExtensions.begin(), mEnabledDeviceExtensions.end(), StrLess);
ANGLE_VK_TRY(displayVk,
VerifyExtensionsPresent(deviceExtensionNames, mEnabledDeviceExtensions));
@@ -3811,6 +3820,13 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
// Only enable it on integrations without EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID passthrough.
ANGLE_FEATURE_CONDITION(&mFeatures, forceContinuousRefreshOnSharedPresent, false);
// Enable setting frame timestamp surface attribute on Android platform.
// Frame timestamp is enabled by calling into "vkGetPastPresentationTimingGOOGLE"
// which, on Android platforms, makes the necessary ANativeWindow API calls.
ANGLE_FEATURE_CONDITION(&mFeatures, supportsTimestampSurfaceAttribute,
IsAndroid() && ExtensionFound(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
deviceExtensionNames));
ApplyFeatureOverrides(&mFeatures, displayVk->getState());
// Disable async command queue when using Vulkan secondary command buffers temporarily to avoid

View File

@@ -1397,6 +1397,17 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
mSwapchain = newSwapChain;
mSwapchainPresentMode = mDesiredSwapchainPresentMode;
// If frame timestamp was enabled for the surface, [re]enable it when [re]creating the swapchain
if (renderer->getFeatures().supportsTimestampSurfaceAttribute.enabled &&
mState.timestampsEnabled)
{
// The implementation of "vkGetPastPresentationTimingGOOGLE" on Android calls into the
// appropriate ANativeWindow API that enables frame timestamps.
uint32_t count = 0;
ANGLE_VK_TRY(context,
vkGetPastPresentationTimingGOOGLE(device, mSwapchain, &count, nullptr));
}
// Initialize the swapchain image views.
uint32_t imageCount = 0;
ANGLE_VK_TRY(context, vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, nullptr));
@@ -1953,6 +1964,12 @@ bool WindowSurfaceVk::hasStagedUpdates() const
return mSwapchainImages[mCurrentSwapchainImageIndex].image.hasStagedUpdatesInAllocatedLevels();
}
void WindowSurfaceVk::setTimestampsEnabled(bool enabled)
{
// The frontend has already cached the state, nothing to do.
ASSERT(IsAndroid());
}
void WindowSurfaceVk::deferAcquireNextImage()
{
mNeedToAcquireNextSwapchainImage = true;

View File

@@ -301,6 +301,8 @@ class WindowSurfaceVk : public SurfaceVk
bool hasStagedUpdates() const;
void setTimestampsEnabled(bool enabled) override;
protected:
angle::Result prepareSwapImpl(const gl::Context *context);
angle::Result swapImpl(const gl::Context *context,

View File

@@ -1038,6 +1038,9 @@ PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT = null
PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR vkGetPhysicalDeviceFragmentShadingRatesKHR = nullptr;
PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR = nullptr;
// VK_GOOGLE_display_timing
PFN_vkGetPastPresentationTimingGOOGLE vkGetPastPresentationTimingGOOGLE = nullptr;
void InitDebugUtilsEXTFunctions(VkInstance instance)
{
GET_INSTANCE_FUNC(vkCreateDebugUtilsMessengerEXT);
@@ -1194,6 +1197,12 @@ void InitFragmentShadingRateKHRFunctions(VkDevice device)
GET_DEVICE_FUNC(vkCmdSetFragmentShadingRateKHR);
}
// VK_GOOGLE_display_timing
void InitGetPastPresentationTimingGoogleFunction(VkDevice device)
{
GET_DEVICE_FUNC(vkGetPastPresentationTimingGOOGLE);
}
# undef GET_INSTANCE_FUNC
# undef GET_DEVICE_FUNC

View File

@@ -1031,6 +1031,9 @@ void InitExtendedDynamicState2EXTFunctions(VkDevice device);
// VK_KHR_fragment_shading_rate
void InitFragmentShadingRateKHRFunctions(VkDevice device);
// VK_GOOGLE_display_timing
void InitGetPastPresentationTimingGoogleFunction(VkDevice device);
#endif // !defined(ANGLE_SHARED_LIBVULKAN)
GLenum CalculateGenerateMipmapFilter(ContextVk *contextVk, angle::FormatID formatID);

View File

@@ -5358,7 +5358,8 @@ bool ValidateSurfaceAttrib(const ValidationContext *val,
break;
case EGL_TIMESTAMPS_ANDROID:
if (!display->getExtensions().getFrameTimestamps)
if (!display->getExtensions().getFrameTimestamps &&
!display->getExtensions().timestampSurfaceAttributeANGLE)
{
val->setError(EGL_BAD_ATTRIBUTE,
"EGL_TIMESTAMPS_ANDROID cannot be used without "
@@ -5507,7 +5508,8 @@ bool ValidateQuerySurface(const ValidationContext *val,
break;
case EGL_TIMESTAMPS_ANDROID:
if (!display->getExtensions().getFrameTimestamps)
if (!display->getExtensions().getFrameTimestamps &&
!display->getExtensions().timestampSurfaceAttributeANGLE)
{
val->setError(EGL_BAD_ATTRIBUTE,
"EGL_TIMESTAMPS_ANDROID cannot be used without "

View File

@@ -1642,6 +1642,66 @@ TEST_P(EGLSurfaceTest, CreateSurfaceSwapIntervalANGLE)
}
}
// Test that setting a surface's timestamp attribute works when the extension
// EGL_ANGLE_timestamp_surface_attribute is supported.
TEST_P(EGLSurfaceTest, TimestampSurfaceAttribute)
{
initializeDisplay();
ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
mConfig = chooseDefaultConfig(true);
ASSERT_NE(mConfig, nullptr);
initializeSurface(mConfig);
ASSERT_NE(mWindowSurface, EGL_NO_SURFACE);
initializeContext();
ASSERT_NE(mContext, EGL_NO_CONTEXT);
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
const bool extensionSupported =
IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANDROID_get_frame_timestamps") ||
IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_timestamp_surface_attribute");
EGLBoolean setSurfaceAttrib =
eglSurfaceAttrib(mDisplay, mWindowSurface, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
if (extensionSupported)
{
EXPECT_EGL_TRUE(setSurfaceAttrib);
// Swap so the swapchain gets created.
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, mWindowSurface));
// Query to confirm the attribute persists across swaps.
EGLint timestampEnabled = 0;
EXPECT_EGL_TRUE(
eglQuerySurface(mDisplay, mWindowSurface, EGL_TIMESTAMPS_ANDROID, &timestampEnabled));
EXPECT_NE(timestampEnabled, 0);
// Resize window and swap.
mOSWindow->resize(256, 256);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EGL_TRUE(eglSwapBuffers(mDisplay, mWindowSurface));
// Query to confirm the attribute persists across swapchain recreations.
timestampEnabled = 0;
EXPECT_EGL_TRUE(
eglQuerySurface(mDisplay, mWindowSurface, EGL_TIMESTAMPS_ANDROID, &timestampEnabled));
EXPECT_NE(timestampEnabled, 0);
}
else
{
EXPECT_EGL_FALSE(setSurfaceAttrib);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
}
TEST_P(EGLSingleBufferTest, OnCreateWindowSurface)
{
EGLConfig config = EGL_NO_CONFIG_KHR;

View File

@@ -277,6 +277,7 @@ constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{
{Feature::SupportsSurfaceProtectedCapabilitiesExtension,
"supportsSurfaceProtectedCapabilitiesExtension"},
{Feature::SupportsSurfaceProtectedSwapchains, "supportsSurfaceProtectedSwapchains"},
{Feature::SupportsTimestampSurfaceAttribute, "supportsTimestampSurfaceAttribute"},
{Feature::SupportsTransformFeedbackExtension, "supportsTransformFeedbackExtension"},
{Feature::SupportsYUVSamplerConversion, "supportsYUVSamplerConversion"},
{Feature::SupportsYuvTarget, "supportsYuvTarget"},

View File

@@ -261,6 +261,7 @@ enum class Feature
SupportsSurfacelessQueryExtension,
SupportsSurfaceProtectedCapabilitiesExtension,
SupportsSurfaceProtectedSwapchains,
SupportsTimestampSurfaceAttribute,
SupportsTransformFeedbackExtension,
SupportsYUVSamplerConversion,
SupportsYuvTarget,