diff --git a/extensions/EGL_ANGLE_timestamp_surface_attribute.txt b/extensions/EGL_ANGLE_timestamp_surface_attribute.txt new file mode 100644 index 000000000..bfdab09a7 --- /dev/null +++ b/extensions/EGL_ANGLE_timestamp_surface_attribute.txt @@ -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 diff --git a/include/platform/FeaturesVk_autogen.h b/include/platform/FeaturesVk_autogen.h index 5acb73f78..682a6ae40 100644 --- a/include/platform/FeaturesVk_autogen.h +++ b/include/platform/FeaturesVk_autogen.h @@ -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; diff --git a/include/platform/vk_features.json b/include/platform/vk_features.json index 69a3b09b7..79fb502b7 100644 --- a/include/platform/vk_features.json +++ b/include/platform/vk_features.json @@ -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" } ] } diff --git a/scripts/code_generation_hashes/ANGLE_features.json b/scripts/code_generation_hashes/ANGLE_features.json index d6726adfe..33ec85ab9 100644 --- a/scripts/code_generation_hashes/ANGLE_features.json +++ b/scripts/code_generation_hashes/ANGLE_features.json @@ -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" } \ No newline at end of file diff --git a/src/common/vulkan/vk_headers.h b/src/common/vulkan/vk_headers.h index 957ae6210..127cf75af 100644 --- a/src/common/vulkan/vk_headers.h +++ b/src/common/vulkan/vk_headers.h @@ -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 diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp index e028d0b1a..10e21f96e 100644 --- a/src/libANGLE/Caps.cpp +++ b/src/libANGLE/Caps.cpp @@ -1276,6 +1276,7 @@ std::vector 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); diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h index 7c9a00b61..e7f405459 100644 --- a/src/libANGLE/Caps.h +++ b/src/libANGLE/Caps.h @@ -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; diff --git a/src/libANGLE/renderer/vulkan/DisplayVk.cpp b/src/libANGLE/renderer/vulkan/DisplayVk.cpp index 1803ea23e..839e7693d 100644 --- a/src/libANGLE/renderer/vulkan/DisplayVk.cpp +++ b/src/libANGLE/renderer/vulkan/DisplayVk.cpp @@ -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 diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp index 998b75a5e..58bd44e18 100644 --- a/src/libANGLE/renderer/vulkan/RendererVk.cpp +++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp @@ -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 diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp index 179958783..fca24f6c1 100644 --- a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp +++ b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp @@ -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; diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.h b/src/libANGLE/renderer/vulkan/SurfaceVk.h index d4b8599c9..41908b890 100644 --- a/src/libANGLE/renderer/vulkan/SurfaceVk.h +++ b/src/libANGLE/renderer/vulkan/SurfaceVk.h @@ -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, diff --git a/src/libANGLE/renderer/vulkan/vk_utils.cpp b/src/libANGLE/renderer/vulkan/vk_utils.cpp index 15cb45400..0f8fa3a6a 100644 --- a/src/libANGLE/renderer/vulkan/vk_utils.cpp +++ b/src/libANGLE/renderer/vulkan/vk_utils.cpp @@ -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 diff --git a/src/libANGLE/renderer/vulkan/vk_utils.h b/src/libANGLE/renderer/vulkan/vk_utils.h index 1dcdc1d41..a8d6977e4 100644 --- a/src/libANGLE/renderer/vulkan/vk_utils.h +++ b/src/libANGLE/renderer/vulkan/vk_utils.h @@ -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); diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp index 5ed7720a5..95ea694bd 100644 --- a/src/libANGLE/validationEGL.cpp +++ b/src/libANGLE/validationEGL.cpp @@ -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 " diff --git a/src/tests/egl_tests/EGLSurfaceTest.cpp b/src/tests/egl_tests/EGLSurfaceTest.cpp index ac2a4646b..5dea39a70 100644 --- a/src/tests/egl_tests/EGLSurfaceTest.cpp +++ b/src/tests/egl_tests/EGLSurfaceTest.cpp @@ -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, ×tampEnabled)); + 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, ×tampEnabled)); + 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; diff --git a/util/angle_features_autogen.cpp b/util/angle_features_autogen.cpp index f713ebdbd..68a2ad6f8 100644 --- a/util/angle_features_autogen.cpp +++ b/util/angle_features_autogen.cpp @@ -277,6 +277,7 @@ constexpr PackedEnumMap kFeatureNames = {{ {Feature::SupportsSurfaceProtectedCapabilitiesExtension, "supportsSurfaceProtectedCapabilitiesExtension"}, {Feature::SupportsSurfaceProtectedSwapchains, "supportsSurfaceProtectedSwapchains"}, + {Feature::SupportsTimestampSurfaceAttribute, "supportsTimestampSurfaceAttribute"}, {Feature::SupportsTransformFeedbackExtension, "supportsTransformFeedbackExtension"}, {Feature::SupportsYUVSamplerConversion, "supportsYUVSamplerConversion"}, {Feature::SupportsYuvTarget, "supportsYuvTarget"}, diff --git a/util/angle_features_autogen.h b/util/angle_features_autogen.h index 68d945ceb..b1ce61584 100644 --- a/util/angle_features_autogen.h +++ b/util/angle_features_autogen.h @@ -261,6 +261,7 @@ enum class Feature SupportsSurfacelessQueryExtension, SupportsSurfaceProtectedCapabilitiesExtension, SupportsSurfaceProtectedSwapchains, + SupportsTimestampSurfaceAttribute, SupportsTransformFeedbackExtension, SupportsYUVSamplerConversion, SupportsYuvTarget,