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

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

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

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

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

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

כדי להוסיף פיקסל חדש ל-AIDL, פועלים לפי השלבים הבאים:

  1. מוסיפים את פורמט הפיקסל החדש כרשומה חדשה בסוף ה-enum‏ PixelFormat ב-PixelFormat.aidl. לשם כך, פועלים לפי מוסכמת הקוד הקיימת ומגדירים את הערך ההקסדצימלי של הרשומה כך שיהיה גדול ב-1 מהערך של הרשומה הקודמת. משווים את השינויים בקוד לערכים הקודמים. דוגמה לפורמט של פיקסל: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,
    

    הודעת השגיאה הבאה מופיעה כשמבצעים שינויים ב-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
    

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

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

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

כדי להוסיף פיקסל חדש ל-AHB, פועלים לפי השלבים הבאים:

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

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

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

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

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

  2. אם יש פורמט OpenGL ES שמשויך לפורמט הפיקסל, מוסיפים את פורמט הפיקסל לבדיקות האופציונליות בקטע CTS. כדי לעשות את זה, מוסיפים את הפורמט החדש של 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 חדש לפורמט הפיקסלים החדש, באמצעות הערך enum ‏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;
    

    הערך ההקסדצימלי של רשומה זו ב-enum חייב להיות זהה לערך ההקסדצימלי של הרשומה מ-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
    

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

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

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

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

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

שינויים ב-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, מוסיפים רשומה חדשה ל-enum‏ androidFormats, כמו שמוצג כאן:
    static const GLenum androidFormats[] =
    {
      ...
      GL_RGBA8,
      ...
    };
    

שליחת העדכון

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