הוספת פורמטים חדשים של פיקסלים ל-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. מוסיפים את פורמט הפיקסלים החדש כרשומה חדשה בסוף המאפיין המסווג (enum) 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-android-latest-release: 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, מחפשים את סוף טענות הנכוֹנוּת הסטטיות שנמצאות בתוך:

    // ----------------------------------------------------------------------------
    // 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-android-latest-release: 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
    

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

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

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

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

כדי להשתמש בפורמט הפיקסלים החדש כפורמט של 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,
      ...
    };
    

שליחת העדכון

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