सिंक करने का फ़्रेमवर्क

सिंक्रनाइज़ेशन फ़्रेमवर्क, Android ग्राफ़िक्स सिस्टम में अलग-अलग एसिंक्रोनस कार्रवाइयों के बीच की डिपेंडेंसी के बारे में साफ़ तौर पर बताता है. फ़्रेमवर्क, एक ऐसा एपीआई उपलब्ध कराता है जिसकी मदद से कॉम्पोनेंट यह बता सकते हैं कि बफ़र कब रिलीज़ किए जाते हैं. यह फ़्रेमवर्क, सिंक्रोनाइज़ेशन प्रिमिटिव को कर्नल से यूज़रस्पेस और यूज़रस्पेस प्रोसेस के बीच पास करने की अनुमति देता है.

उदाहरण के लिए, कोई ऐप्लिकेशन जीपीयू में किए जाने वाले काम को क्रम से लगा सकता है. इसके बाद, जीपीयू उस इमेज को रेंडर करना शुरू कर देता है. हालांकि, इमेज को अब तक मेमोरी में नहीं डाला गया है, लेकिन बफ़र पॉइंटर को विंडो कंपोज़िटर को पास कर दिया जाता है. साथ ही, एक फ़ेंस भी पास किया जाता है, जिससे यह पता चलता है कि GPU का काम कब पूरा होगा. विंडो कंपोज़िटर, समय से पहले प्रोसेसिंग शुरू कर देता है और डिसप्ले कंट्रोलर को काम सौंप देता है. इसी तरह, सीपीयू का काम भी समय से पहले पूरा हो जाता है. जीपीयू के काम पूरा करने के बाद, डिसप्ले कंट्रोलर तुरंत इमेज दिखाता है.

सिंक करने की सुविधा देने वाला फ़्रेमवर्क, हार्डवेयर बनाने वाली कंपनियों को अपने हार्डवेयर कॉम्पोनेंट में सिंक करने के संसाधनों का इस्तेमाल करने की अनुमति देता है. आखिर में, फ़्रेमवर्क, ग्राफ़िक्स पाइपलाइन की जानकारी देता है, ताकि डीबग करने में मदद मिल सके.

एक्सप्लिसिट सिंक्रोनाइज़ेशन

एक्सप्लिसिट सिंक्रनाइज़ेशन की मदद से, ग्राफ़िक बफ़र बनाने वाले और उनका इस्तेमाल करने वाले लोग यह सिग्नल दे सकते हैं कि उन्होंने बफ़र का इस्तेमाल कर लिया है. एक्सप्लिसिट सिंक्रनाइज़ेशन को कर्नल-स्पेस में लागू किया जाता है.

डेटा को साफ़ तौर पर सिंक करने के ये फ़ायदे हैं:

  • डिवाइसों के बीच बिहेवियर में कम अंतर
  • डीबग करने की बेहतर सुविधा
  • टेस्टिंग की बेहतर मेट्रिक

सिंक फ़्रेमवर्क में तीन तरह के ऑब्जेक्ट होते हैं:

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline एक ऐसी टाइमलाइन है जो लगातार बढ़ती है. वेंडर को इसे हर ड्राइवर इंस्टेंस के लिए लागू करना चाहिए. जैसे, GL कॉन्टेक्स्ट, डिसप्ले कंट्रोलर या 2D ब्लिटर. sync_timeline किसी हार्डवेयर के लिए कर्नल को सबमिट किए गए कामों की संख्या. sync_timeline कार्रवाइयों के क्रम को तय करता है और हार्डवेयर के हिसाब से लागू करने की सुविधा देता है.

sync_timeline लागू करते समय इन दिशा-निर्देशों का पालन करें:

  • सभी ड्राइवर, टाइमलाइन, और फ़ेंस के लिए काम के नाम दें, ताकि डीबग करना आसान हो.
  • टाइमलाइन में timeline_value_str और pt_value_str ऑपरेटर लागू करें, ताकि डीबग करने के आउटपुट को आसानी से पढ़ा जा सके.
  • अगर ज़रूरत हो, तो उपयोगकर्ताओं को निजी टाइमलाइन डेटा का ऐक्सेस देने के लिए, फ़िल driver_data लागू करें. जैसे, GL लाइब्रेरी. data_driver की मदद से, वेंडर sync_fence और sync_pts के बारे में जानकारी पास कर सकते हैं. इससे, इनके आधार पर कमांड लाइनें बनाई जा सकती हैं.
  • उपयोगकर्ता स्थान को स्पष्ट रूप से फ़ेंस बनाने या सिग्नल देने की अनुमति न दें. सिग्नल/फ़ेंस को साफ़ तौर पर बनाने से, सेवा से इनकार करने वाला हमला होता है. इससे पाइपलाइन की सुविधा काम करना बंद कर देती है.
  • sync_timeline, sync_pt या sync_fence एलिमेंट को साफ़ तौर पर ऐक्सेस न करें. एपीआई में सभी ज़रूरी फ़ंक्शन उपलब्ध होते हैं.

sync_pt

sync_pt, sync_timeline पर मौजूद एक वैल्यू या पॉइंट है. किसी पॉइंट की तीन स्थितियां होती हैं: चालू, सिग्नल दिया गया, और गड़बड़ी. पॉइंट, चालू स्थिति में शुरू होते हैं और सिग्नल या गड़बड़ी की स्थितियों में बदल जाते हैं. उदाहरण के लिए, जब किसी इमेज कंज्यूमर को बफ़र की ज़रूरत नहीं होती है, तो sync_pt का सिग्नल दिया जाता है. इससे इमेज प्रोड्यूसर को पता चलता है कि बफ़र में फिर से लिखा जा सकता है.

sync_fence

sync_fence, sync_pt वैल्यू का कलेक्शन है. इसमें अक्सर अलग-अलग sync_timeline पैरंट होते हैं. जैसे, डिसप्ले कंट्रोलर और जीपीयू के लिए. sync_fence, sync_pt, और sync_timeline मुख्य प्रिमिटिव हैं. इनका इस्तेमाल ड्राइवर और यूज़रस्पेस, अपनी डिपेंडेंसी के बारे में बताने के लिए करते हैं. जब फ़ेंस का सिग्नल मिलता है, तो फ़ेंस से पहले दिए गए सभी निर्देश पूरे हो जाते हैं. ऐसा इसलिए होता है, क्योंकि कर्नल ड्राइवर या हार्डवेयर ब्लॉक, निर्देशों को क्रम से लागू करता है.

सिंक फ़्रेमवर्क की मदद से, कई उपभोक्ता या प्रोड्यूसर यह सिग्नल दे सकते हैं कि उन्होंने बफ़र का इस्तेमाल कर लिया है. साथ ही, वे एक फ़ंक्शन पैरामीटर के साथ डिपेंडेंसी की जानकारी दे सकते हैं. फ़ेंस को फ़ाइल डिस्क्रिप्टर से बैक किया जाता है और इन्हें कर्नल स्पेस से यूज़रस्पेस में पास किया जाता है. उदाहरण के लिए, किसी फ़ेंस में दो sync_pt वैल्यू हो सकती हैं. इनसे यह पता चलता है कि दो अलग-अलग इमेज कंज्यूमर ने बफ़र को कब पढ़ना बंद किया. जब फ़ेंस का सिग्नल मिलता है, तो इमेज बनाने वाले लोगों को पता चल जाता है कि दोनों उपयोगकर्ताओं ने इमेज देख ली है.

sync_pt वैल्यू की तरह फ़ेंस, डिफ़ॉल्ट रूप से चालू होते हैं. ये अपने पॉइंट की स्थिति के आधार पर, अपनी स्थिति बदलते हैं. अगर सभी sync_pt वैल्यू को सिग्नल मिल जाता है, तो sync_fence को सिग्नल मिल जाता है. अगर एक sync_pt में कोई गड़बड़ी होती है, तो पूरे sync_fence में गड़बड़ी होती है.

sync_fence में सदस्यता लेने के बाद, उसमें बदलाव नहीं किया जा सकता. किसी फ़ेंस में एक से ज़्यादा पॉइंट पाने के लिए, मर्ज किया जाता है. इसमें दो अलग-अलग फ़ेंस के पॉइंट को तीसरे फ़ेंस में जोड़ा जाता है. अगर उनमें से एक पॉइंट को ओरिजनल फ़ेंस में सिग्नल किया गया है और दूसरे को नहीं, तो तीसरे फ़ेंस को भी सिग्नल नहीं किया जाएगा.

सिंक करने की सुविधा को लागू करने के लिए, यह जानकारी दें:

  • यह कर्नल-स्पेस सबसिस्टम है. यह किसी खास हार्डवेयर ड्राइवर के लिए, सिंक फ़्रेमवर्क लागू करता है. फ़ेंस के बारे में जानकारी रखने वाले ड्राइवर, आम तौर पर ऐसे ड्राइवर होते हैं जो हार्डवेयर कंपोज़र (एचडब्ल्यूसी) को ऐक्सेस करते हैं या उससे कम्यूनिकेट करते हैं. मुख्य फ़ाइलों में ये शामिल हैं:
    • कोर इंप्लीमेंटेशन:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • kernel/common/Documentation/sync.txt पर दस्तावेज़
    • platform/system/core/libsync में कर्नल स्पेस से कम्यूनिकेट करने के लिए लाइब्रेरी
  • वेंडर को हार्डवेयर ऐब्स्ट्रैक्शन लेयर (एचएएल) में validateDisplay() और presentDisplay() फ़ंक्शन के पैरामीटर के तौर पर, सही सिंक्रनाइज़ेशन फ़ेंस देने होंगे.
  • बाउंड्री से जुड़े दो GL एक्सटेंशन (EGL_ANDROID_native_fence_sync और EGL_ANDROID_wait_sync) और ग्राफ़िक्स ड्राइवर में बाउंड्री की सुविधा.

केस स्टडी: डिसप्ले ड्राइवर लागू करना

सिंक करने की सुविधा के साथ काम करने वाले एपीआई का इस्तेमाल करने के लिए, ऐसा डिसप्ले ड्राइवर डेवलप करें जिसमें डिसप्ले बफ़र फ़ंक्शन हो. सिंक्रनाइज़ेशन फ़्रेमवर्क के मौजूद होने से पहले, यह फ़ंक्शन dma-buf ऑब्जेक्ट पाता था, उन बफ़र को डिसप्ले पर रखता था, और बफ़र के दिखने के दौरान ब्लॉक करता था. उदाहरण के लिए:

/*
 * assumes buffer is ready to be displayed.  returns when buffer is no longer on
 * screen.
 */
void display_buffer(struct dma_buf *buffer);

सिंक करने के फ़्रेमवर्क के साथ, display_buffer फ़ंक्शन ज़्यादा जटिल होता है. डिसप्ले पर बफ़र डालते समय, बफ़र को एक फ़ेंस से जोड़ा जाता है. इससे पता चलता है कि बफ़र कब तैयार होगा. फ़ेंस क्लियर होने के बाद, काम को लाइन में लगाया जा सकता है और शुरू किया जा सकता है.

फ़ेंस के क्लियर होने के बाद, काम को लाइन में लगाने और शुरू करने से कुछ भी ब्लॉक नहीं होता. आपको तुरंत अपना फ़ेंस वापस करना होगा. इससे यह पता चलता है कि बफ़र कब डिसप्ले से हट जाएगा. बफ़र को कतार में लगाते समय, कर्नल सिंक्रनाइज़ेशन फ़्रेमवर्क के साथ डिपेंडेंसी की सूची दिखाता है:

/*
 * displays buffer when fence is signaled.  returns immediately with a fence
 * that signals when buffer is no longer displayed.
 */
struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence
*fence);

सिंक इंटिग्रेशन

इस सेक्शन में बताया गया है कि कर्नल-स्पेस सिंक फ़्रेमवर्क को Android फ़्रेमवर्क के यूज़रस्पेस वाले हिस्सों और उन ड्राइवर के साथ कैसे इंटिग्रेट किया जाता है जिन्हें एक-दूसरे के साथ कम्यूनिकेट करना होता है. कर्नेल-स्पेस ऑब्जेक्ट को उपयोगकर्ता स्पेस में फ़ाइल डिस्क्रिप्टर के तौर पर दिखाया जाता है.

इंटिग्रेशन के लिए तय किए गए नियम

Android एचएएल इंटरफ़ेस के नियमों का पालन करें:

  • अगर एपीआई, sync_pt को रेफ़र करने वाला फ़ाइल डिस्क्रिप्टर उपलब्ध कराता है, तो एपीआई का इस्तेमाल करने वाले वेंडर के ड्राइवर या HAL को फ़ाइल डिस्क्रिप्टर बंद करना होगा.
  • अगर वेंडर ड्राइवर या एचएएल, एपीआई फ़ंक्शन को ऐसा फ़ाइल डिस्क्रिप्टर पास करता है जिसमें sync_pt शामिल है, तो वेंडर ड्राइवर या एचएएल को फ़ाइल डिस्क्रिप्टर बंद नहीं करना चाहिए.
  • फ़ेंस फ़ाइल डिस्क्रिप्टर का इस्तेमाल जारी रखने के लिए, वेंडर ड्राइवर या HAL को डिस्क्रिप्टर को डुप्लीकेट करना होगा.

बफ़रक्यू से पास होने पर, हर बार फ़ेंस ऑब्जेक्ट का नाम बदल जाता है. कर्नल फ़ेंस की सुविधा की मदद से, फ़ेंस के नाम के लिए स्ट्रिंग का इस्तेमाल किया जा सकता है. इसलिए, सिंक फ़्रेमवर्क, फ़ेंस का नाम रखने के लिए विंडो के नाम और बफ़र इंडेक्स का इस्तेमाल करता है. जैसे, SurfaceView:0. यह डीबग करने में मदद करता है, ताकि डेडलॉक के सोर्स की पहचान की जा सके. ऐसा इसलिए, क्योंकि नाम /d/sync और गड़बड़ी की रिपोर्ट के आउटपुट में दिखते हैं.

ANativeWindow इंटिग्रेशन

ANativeWindow, फ़ेंस के बारे में जानता है. dequeueBuffer, queueBuffer, और cancelBuffer में फ़ेंस पैरामीटर मौजूद हैं.

OpenGL ES इंटिग्रेशन

OpenGL ES सिंक इंटिग्रेशन, दो EGL एक्सटेंशन पर निर्भर करता है:

  • EGL_ANDROID_native_fence_sync, EGLSyncKHR ऑब्जेक्ट में नेटिव Android फ़ेंस फ़ाइल डिस्क्रिप्टर को रैप करने या बनाने का तरीका उपलब्ध कराता है.
  • EGL_ANDROID_wait_sync की वजह से, सीपीयू के बजाय जीपीयू को इंतज़ार करना पड़ता है. इसलिए, जीपीयू EGLSyncKHR के लिए इंतज़ार करता है. EGL_ANDROID_wait_sync एक्सटेंशन और EGL_KHR_wait_sync एक्सटेंशन एक ही हैं.

इन एक्सटेंशन का अलग-अलग इस्तेमाल करने के लिए, EGL_ANDROID_native_fence_sync एक्सटेंशन को लागू करें. साथ ही, इससे जुड़े कर्नल सपोर्ट को भी लागू करें. इसके बाद, अपने ड्राइवर में EGL_ANDROID_wait_sync एक्सटेंशन चालू करें. EGL_ANDROID_native_fence_sync एक्सटेंशन में, एक अलग नेटिव फ़ेंस EGLSyncKHR ऑब्जेक्ट टाइप होता है. इस वजह से, मौजूदा EGLSyncKHR ऑब्जेक्ट टाइप पर लागू होने वाले एक्सटेंशन, ज़रूरी नहीं कि EGL_ANDROID_native_fence ऑब्जेक्ट पर भी लागू हों. इससे, अनचाहे इंटरैक्शन से बचा जा सकता है.

EGL_ANDROID_native_fence_sync एक्सटेंशन, इससे जुड़े नेटिव फ़ेंस फ़ाइल डिस्क्रिप्टर एट्रिब्यूट का इस्तेमाल करता है. इसे सिर्फ़ फ़ाइल बनाते समय सेट किया जा सकता है. साथ ही, इसे मौजूदा सिंक ऑब्जेक्ट से सीधे तौर पर क्वेरी नहीं किया जा सकता. इस एट्रिब्यूट को दो में से किसी एक मोड पर सेट किया जा सकता है:

  • मान्य फ़ेंस फ़ाइल डिस्क्रिप्टर, मौजूदा नेटिव Android फ़ेंस फ़ाइल डिस्क्रिप्टर को EGLSyncKHR ऑब्जेक्ट में रैप करता है.
  • -1, EGLSyncKHR ऑब्जेक्ट से एक नेटिव Android फ़ेंस फ़ाइल डिस्क्रिप्टर बनाता है.

नेटिव Android फ़ेंस फ़ाइल डिस्क्रिप्टर से EGLSyncKHR ऑब्जेक्ट निकालने के लिए, DupNativeFenceFD() फ़ंक्शन कॉल का इस्तेमाल करें. इससे सेट किए गए एट्रिब्यूट के बारे में क्वेरी करने जैसा ही नतीजा मिलता है. हालांकि, इसमें इस नियम का पालन किया जाता है कि डेटा पाने वाला व्यक्ति फ़ेंस को बंद करता है. इसलिए, यह डुप्लीकेट ऑपरेशन है. आखिर में, EGLSyncKHR ऑब्जेक्ट को डिस्ट्रॉय करने से, इंटरनल फ़ेंस एट्रिब्यूट बंद हो जाता है.

हार्डवेयर कंपोज़र इंटिग्रेशन

एचडब्ल्यूसी, तीन तरह के सिंक फ़ेंस को मैनेज करता है:

  • Acquire fences को इनपुट बफ़र के साथ setLayerBuffer और setClientTarget कॉल में पास किया जाता है. ये बफ़र में लिखे जाने वाले डेटा को दिखाते हैं. SurfaceFlinger या HWC को कंपोज़िशन करने के लिए, इससे जुड़े बफ़र से डेटा पढ़ने से पहले, इन्हें सिग्नल देना होगा.
  • रिलीज़ फ़ेंस, getReleaseFences कॉल का इस्तेमाल करके presentDisplay को कॉल करने के बाद वापस पाए जाते हैं. ये उसी लेयर पर, पिछले बफ़र से पढ़े जाने वाले डेटा को दिखाते हैं. रिलीज़ फ़ेंस, तब सिग्नल देता है, जब एचडब्ल्यूसी पिछले बफ़र का इस्तेमाल नहीं कर रहा होता है. ऐसा इसलिए होता है, क्योंकि मौजूदा बफ़र ने डिसप्ले पर पिछले बफ़र की जगह ले ली है. रिलीज़ फ़ेंस को ऐप्लिकेशन पर वापस भेज दिया जाता है. साथ ही, पिछले बफ़र भी भेजे जाते हैं. इन बफ़र को मौजूदा कंपोज़िशन के दौरान बदल दिया जाएगा. ऐप्लिकेशन को तब तक इंतज़ार करना होगा, जब तक रिलीज़ फ़ेंस के सिग्नल न मिल जाएं. इसके बाद ही, वह उस बफ़र में नया कॉन्टेंट लिख सकता है जिसे उसे वापस भेजा गया था.
  • मौजूदा फ़ेंस, हर फ़्रेम के लिए एक बार दिखाए जाते हैं. ये presentDisplay को कॉल करने के दौरान दिखाए जाते हैं. मौजूदा फ़ेंस यह बताते हैं कि इस फ़्रेम की कंपोज़िशन कब पूरी हुई या इसके अलावा, पिछले फ़्रेम की कंपोज़िशन का नतीजा कब तक ज़रूरी नहीं है. फ़िज़िकल डिसप्ले के लिए, presentDisplay स्क्रीन पर मौजूदा फ़्रेम दिखने पर, मौजूदा फ़ेंस दिखाता है. मौजूदा फ़ेंस के वापस आने के बाद, SurfaceFlinger के टारगेट बफ़र में फिर से लिखना सुरक्षित होता है. हालांकि, ऐसा तब ही किया जा सकता है, जब यह लागू हो. वर्चुअल डिसप्ले के लिए, मौजूदा फ़ेंस तब दिखाए जाते हैं, जब आउटपुट बफ़र से पढ़ना सुरक्षित हो.