הוספת פורמטים חדשים של פיקסלים ל-Android

כל הפורמטים החדשים של הפיקסלים שנוספו ל-Android חייבים להיכלל בשפת הגדרה לבניית ממשק Android‏ (AIDL) ובAndroid Hardware Buffer‏ (AHB). ל-AIDL ול-AHB יש דרישות קפדניות של יציבות וסטנדרטיזציה, שמחייבות תהליך זהיר כשמגדילים את הפונקציונליות. כל הפורמטים החדשים של הפיקסלים חייבים להיכנס ל-AOSP, וכל העדכונים חייבים לקבל אישור בנפרד על ידי מומחי AIDL ו-AHB. תהליך האישור הזה הוא גורם חשוב ביצירת סטנדרטים לפורמטים חדשים של פיקסלים בפלטפורמה.

בדף הזה מפורטים השינויים הנדרשים בקוד של AOSP והתהליך הנדרש להוספת פורמטים חדשים של פיקסלים ב-AOSP.

לפני שמוסיפים פורמט פיקסלים חדש, צריך להוריד את המקור ולהעלות תיקונים כפי שמתואר בקטע שליחת תיקונים.

הוספת פורמט פיקסלים חדש ל-AIDL

כדי להוסיף תמיכה בפורמט פיקסל חדש, צריך לבצע שינויים בשני הקבצים של PixelFormat.aidl שממוקמים בתוך AIDL. קוד המקור של AIDL זמין ב-hardware/interfaces/graphics/common/aidl/.

כדי להוסיף פורמט פיקסל חדש ל-AIDL:

  1. מוסיפים את פורמט הפיקסלים החדש כרשומה חדשה בסוף המאפיין PixelFormat ב-PixelFormat.aidl, לפי נוהל הקוד הקיים, ומגדירים את הערך הקסדצימלי של הרשומה כערך אחד גבוה יותר מהרשומה הקודמת. מתאימים את השינויים בקוד לרשומים הקודמים. עיינו בדוגמה הבאה לערך של פורמט הפיקסל RGBA_8888:
    /**
     * 32-bit format that has 8-bit R, G, B, and A components, in that order,
     * from the lowest memory address to the highest memory address.
     *
     * The component values are unsigned normalized to the range [0, 1], whose
     * interpretation is defined by the dataspace.
     */
    RGBA_8888 = 0x1,
    

    הודעת השגיאה הבאה מופיעה כשמפעילים את ה-build של הקוד אחרי שמבצעים שינויים ב-PixelFormat.aidl:

    android_developer:~/android/aosp-main: m
    ...
    ###############################################################################
    # ERROR: AIDL API change detected                                             #
    ###############################################################################
    Above AIDL file(s) has changed. Run `m android.hardware.graphics.common-update-api` to reflect the changes
    to the current version so that it is reviewed by
    android-aidl-api-council@google.com
    And then you need to change dependency on android.hardware.graphics.common-V(n)-* to android.hardware.graphics.common-V(n+1)-* to use
    new APIs.
    
  2. כדי להסיר את השגיאה, מריצים את הפקודה הבאה, כפי שצוין בהודעת השגיאה, כדי לשנות את PixelFormat.aidl בספרייה aidl_api:

    m android.hardware.graphics.common-update-api
    

    הרצת הפקודה שלמעלה מעדכנת את הקובץ הנכון כדי שאפשר יהיה לבצע build באופן תקין.

הוספת פורמט פיקסלים חדש ל-AHB

כדי להוסיף תמיכה בפורמט פיקסלים חדש, צריך לבצע שינויים ב-hardware_buffer.h וב-AHardwareBuffer.cpp. קוד המקור של AHB מופיע ב-frameworks/native/libs/nativewindow.

כדי להוסיף פורמט פיקסל חדש ל-AHB:

  1. ב-hardware_buffer.h, מוסיפים את פורמט הפיקסלים החדש כרשומה חדשה בסוף המאפיין המנוהל AHardwareBuffer_Format. צריך לפעול בהתאם למוסכמות הקוד הקיימות.

    לפי הדוגמה של פורמט הפיקסלים RGBA_8888, מוסיפים את הרשומה החדשה של פורמט הפיקסלים באופן הבא:

    /**
     * Corresponding formats:
     *   Vulkan: VK_FORMAT_R8G8B8A8_UNORM
     *   OpenGL ES: GL_RGBA8
     */
    AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1,
    

    שימו לב שפורמט הפיקסלים החדש מקבל שם ב-AHB, שחייב להתחיל ב-AHARDWAREBUFFER_FORMAT_, ואחריו קיצורי הדרך של הערוצים ועומקי הביטים, ולסיום הקידוד. הערך של רשומת המניין הזה צריך להיות זהה לערך הקסדצימלי של PixelFormat.aidl.

    פורמט הפיקסל אמור לכלול אחד מפורמט Vulkan או OpenGL ES, או את שניהם. במקרים המתאימים, מציינים את הפורמט המשויך. אם אין פורמט משויך, מציינים את הערך N/A.

  2. צריך להוסיף את פורמט הפיקסל לבדיקה אופציונלית במסגרת CTS, אם משויך אליו פורמט OpenGL ES. כדי לעשות זאת, מוסיפים את פורמט GL החדש ל-AHardwareBufferGLTest.cpp ב-AHBFormatAsString(int32_t format) עם FORMAT_CASE(...) ו-GL_FORMAT_CASE(...) לפורמט החדש, כפי שמוצג בהמשך:

    const char* AHBFormatAsString(int32_t format) {
      switch (format) {
          ...
          FORMAT_CASE(R8G8B8A8_UNORM);
          ...
          GL_FORMAT_CASE(GL_RGB8);
      }
      return "";
    }
    
  3. בשלב הבא, מוסיפים בדיקה חדשה ל-AHardwareBufferGLTest.cpp, כפי שמתואר בהמשך:

    class RGBA8Test : public AHardwareBufferGLTest {};
    
    // Verify that if we can allocate an RGBA8 AHB we can render to it.
    TEST_P(RGBA8Test, Write) {
        AHardwareBuffer_Desc desc = GetParam();
        desc.usage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
        if (!SetUpBuffer(desc)) {
            return;
        }
    
        ASSERT_NO_FATAL_FAILURE(SetUpFramebuffer(desc.width, desc.height, 0, kBufferAsRenderbuffer));
        ASSERT_NO_FATAL_FAILURE(
            SetUpProgram(kVertexShader, kColorFragmentShader, kPyramidPositions, 0.5f));
    
        glDrawArrays(GL_TRIANGLES, 0, kPyramidVertexCount);
        ASSERT_EQ(GLenum{GL_NO_ERROR}, glGetError());
    }
    
    INSTANTIATE_TEST_CASE_P(
        SingleLayer, RGBA8Test,
        ::testing::Values(
            AHardwareBuffer_Desc{57, 33, 1, AHARDWAREBUFFER_FORMAT_R16G16_UINT, 0, 0, 0, 0}),
        &GetTestName);
    

    מציינים לפחות קבוצה אחת של ערכים של AHardwareBuffer_Desc. מוסיפים עוד ערכים לפי הצורך.

  4. ב-AHardwareBuffer.cpp, מחפשים את סוף טענות הנכונות (assertions) הסטטיים שנמצאים בתוך:

    // ----------------------------------------------------------------------------
    // Validate hardware_buffer.h and PixelFormat.aidl agree
    // ----------------------------------------------------------------------------
    

    מוסיפים static_assert חדש לפורמט הפיקסלים החדש, באמצעות המאפיין המסווג PixelFormat:: ולא באמצעות הקבוע HAL_PIXEL_FORMAT. בעזרת אותה דוגמה לפורמט הפיקסלים RGBA_8888 מהוספת פורמט פיקסלים חדש ל-AIDL, מוסיפים את הרשומה של פורמט הפיקסלים החדש באופן הבא:

    static_assert(static_cast(aidl::android::hardware::graphics::common::PixelFormat::RGBA_8888) ==
      AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
    "HAL and AHardwareBuffer pixel format don't match");
    
  5. מוסיפים את פורמט הפיקסלים החדש לבדיקות המתאימות על ידי הוספת פורמט הפיקסלים החדש לסוף הערך PrintAhbFormat() ב-AHardwareBufferTest.cpp. יש לפעול לפי המוסכמה הקיימת בנוגע לקוד, כפי שמוצג בהמשך:

    void PrintAhbFormat(std::ostream& os, uint64_t format) {
        switch (format) {
            ...
            FORMAT_CASE(R8G8B8A8_UNORM);
            default: os << "unknown"; break;
        }
    }
    
  6. מוסיפים את פורמט הפיקסלים החדש ל-SDK של HardwareBuffer בקובץ HardwareBuffer.java: מוסיפים רשומה חדשה לקובץ @IntDef. לדוגמה, הרשומה של הפורמט RGBA_8888 מוצגת כך:

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "RGB", "BLOB", "YCBCR_", "D_", "DS_", "S_" }, value = {
      ...
      RGBA_8888,
    })
    

    אם ערכי הרכיבים לא עברו נורמליזציה ללא סימן, צריך לציין את הערך באופן מפורש בשם המשתנה. לדוגמה, שם המשתנה לפורמט של ערוץ אדום בלבד של מספר שלם ללא סימן באורך 16 ביט חייב להיות R_16UI, ואותו פורמט עם ערוץ ירוק נוסף של מספר שלם ללא סימן באורך 16 ביט חייב להיות RG_16UI16UI.

  7. מוסיפים את פורמט הפיקסלים החדש כ-static int ב-HardwareBuffer.java, על ידי צירוף משתנה חבר חדש שגלוי לכולם בסוף @Format:

    @Format
    ...
    /** Format: 8 bits each red, green, blue, alpha */
    public static final int RGBA_8888 = 0x1;
    

    הערך של רשומת המניין הזה צריך להיות זהה לערך ההקסדצימלי של PixelFormat.aidl ושל hardware_buffer.h. פועלים לפי המוסכמות קיימות.

  8. ניסיון לבצע build עם השינויים האלה בקוד יגרום לשגיאת build:

    android_developer:~/android/aosp-main: m
    ...
    ******************************
    You have tried to change the API from what has been previously approved.
    
    To make these errors go away, you have two choices:
       1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)
          to the new methods, etc. shown in the above diff.
    
       2. You can update current.txt and/or removed.txt by executing the following command:
             m api-stubs-docs-non-updatable-update-current-api
    
          To submit the revised current.txt to the main Android repository,
          you will need approval.
    ******************************
    ...
    

    כדי לבטל את השגיאה הזו, מריצים את הפקודה הבאה, כפי שצוין בהודעת השגיאה, כדי לשנות את הערך של current.txt:

    m api-stubs-docs-non-updatable-update-current-api
    

    הרצת הפקודה שלמעלה מעדכנת את הקובץ הנכון כדי שיהיה אפשר לבנות אותו כרגיל.

  9. מוסיפים את פורמט הפיקסל החדש לבדיקות Java על ידי הוספת פורמט הפיקסלים החדש לסוף הערך paramsForTestCreateOptionalFormats() ב-HardwareBufferTest.java, באופן הבא:

    private static Object[] paramsForTestCreateOptionalFormats() {
      return new Integer[]{
          HardwareBuffer.RGBA_8888
      };
    

הוספת פורמט פיקסלים חדש לשילוב של מערכת החלונות

כדי להשתמש בפורמט הפיקסלים החדש כפורמט של framebuffer ב-API של גרפיקה, צריך להוסיף אותו לשילוב המתאים של מערכת החלונות (WSI) ל-API הגרפיקה הרלוונטי. באפליקציה או בתהליך מערכת שמשתמשים ב-Vulkan API, מעדכנים את Vulkan Swapchain. באפליקציה או בתהליך מערכת שמשתמשים ב-OpenGL ES API, צריך לעדכן את ה-API של EGL.

שינויים ב-Vulkan WSI לפורמטים חדשים של פיקסלים

מעדכנים את Vulkan WSI באופן הבא:
  1. מוסיפים מקרה חדש לפונקציה GetNativePixelFormat(VkFormat format) בקטע swapchain.cpp:

    android::PixelFormat GetNativePixelFormat(VkFormat format) {
      ...
      switch (format) {
          ...
          case VK_FORMAT_R8G8B8A8_UNORM:
              native_format = PixelFormat::RGBA_8888;
              break;
          ...
          default:
              ALOGV("unsupported swapchain format %d", format);
              break;
      }
      return native_format;
    }
    
  2. שולחים שאילתה להרחבת Vulkan אם פורמט הפיקסלים מחייב הרחבת Vulkan כדי לפעול. לדוגמה, לתוספים צדדיים, משתמשים ב-instance_data, כפי שמוצג בהמשך:
    bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
    

    בתוספים בצד המכשיר, צריך להשתמש באפשרויות הבאות:

    bool rgba10x6_formats_ext = false;
    uint32_t exts_count;
    const auto& driver = GetData(pdev).driver;
    driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
                                              nullptr);
    std::vector props(exts_count);
    driver.EnumerateDeviceExtensionProperties(pdev, nullptr, &exts_count,
                                              props.data());
    for (uint32_t i = 0; i < exts_count; i++) {
        VkExtensionProperties prop = props[i];
        if (strcmp(prop.extensionName,
                   VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME) == 0) {
            rgba10x6_formats_ext = true;
        }
    }
    

    Google מטפלת בתשתית הנדרשת כדי לחשוף מכונה או תוסף למכשיר ל-swapchain.cpp. רשימת השינויים הראשונית לא נדרשת כדי להגדיר את התוספים בצורה נכונה מהמטען של Vulkan.

  3. בשלב הבא, מפרטים את הצמדים של הפורמטים ומרחבי הצבעים:
    desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
    if (AHardwareBuffer_isSupported(&desc) && rgba10x6_formats_ext) {
      all_formats.emplace_back(
          VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
                             VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
      if (colorspace_ext) {
        all_formats.emplace_back(
            VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
                               VK_COLOR_SPACE_PASS_THROUGH_EXT});
        all_formats.emplace_back(
            VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
                               VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
    }
    

    צריך להכיר את הצירופים התואמים של הפורמטים ומרחבי הצבעים.

  4. מוסיפים את הפורמט החדש לקטע dEQP-VK שנמצא בכתובת external/deqp.
  5. מעדכנים את בדיקות התאימות ל-Vulkan ב-vktApiExternalMemoryTests.cpp וב-vktExternalMemoryUtil.cpp על ידי הסקת השינויים הנדרשים מהמקור הקיים או פנייה לתמיכה של Android לקבלת מידע.

שינויי EGL לפורמטים חדשים של פיקסלים

מעדכנים את ה-EGL באופן הבא:

  1. בפונקציה getNativePixelFormat(), משנים את העץ if-else כך שיחזיר את המאפיין enum של AIDL לפורמט הפיקסלים החדש. לדוגמה, בפורמט הפיקסלים RGBA_8888:
    if (a == 0) {
      ...
    } else {
      if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
          if (colorDepth > 24) {
              ...
          } else {
              *format = PixelFormat::RGBA_8888;
          }
      } else {
        ...
      }
    }
    
  2. כדי להוסיף את הפורמט החדש ל-dEQP, מוסיפים רשומה חדשה למערך הערכים androidFormats, כפי שמתואר בהמשך:
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

שליחת העדכון

פועלים לפי ההוראות במאמר למשתתפים כדי ליצור את רשימות השינויים ולשתף אותן עם הצוות המתאים.