מהדרים של תוכנות הצללה (shader) Vulkan ב-Android

הניהול של תוכנות הצללה (shader) באפליקציית Vulkan צריך להיות שונה מהאופן שבו אפליקציית OpenGL ES: ב-OpenGL ES, אתם משתמשים בכלי להצללה כקבוצת מחרוזות שיוצרות את טקסט המקור של תוכנת תוכנת ההצללה (shader) GLSL. לעומת זאת, Vulkan API דורש לספק תוכנת הצללה בצורת נקודת כניסה במודול SPIR-V.

NDK Release 12 גרסה 12 ואילך כולל ספרייה של סביבת זמן ריצה להרכבת GLSL ל-SPIR-V. הספרייה בסביבת זמן הריצה זהה לספרייה פרויקט Shaderc בקוד פתוח, ומשתמש באותו מהדר הפניות של Glslang GLSL בתור הקצה העורפי. כברירת מחדל, גרסת Shaderc של compiler מניח שאתם מבצעים הידור עבור Vulkan. אחרי שבדקנו אם הקוד תקף עבור Vulkan, המהדר מפעיל באופן אוטומטי את התוסף KHR_vulkan_glsl. הדרק של המהדר (compiler) יוצר גם קוד SPIR-V תואם Vulkan.

אפשר לבחור להדר מודולים של SPIR-V באפליקציית Vulkan במהלך הפיתוח, השיטה שנקראת מקדמה בזמן, או AOT, הידור (compilation). לחלופין, אפשר לבקש מהאפליקציה להדר אותם מתוכנות הצללה שנשלחו או שנוצרו באופן פרוצדורלי במקור כשצריך במהלך זמן הריצה. השיטה הזו נקראת הידור לגבי זמן ריצה. ב-Android Studio יש תמיכה משולבת בבניית תוכנות הצללה (shader) של Vulkan.

בהמשך הדף הזה נספק פרטים נוספים על כל אחד מהתרגולים, ולאחר מכן נסביר איך לשלב אוסף של תוכנות הצללה (shader) באפליקציית Vulkan.

אוסף AOT

יש שתי דרכים ליצור הידור של תוכנת הצללה (shader), כפי שמתואר בקטעים הבאים.

שימוש ב-Android Studio

בזמן הוספת תוכנות הצללה אל app/src/main/shaders/, מערכת Android Studio מזהה תוכנות הצללה לפי את סיומות הקבצים שלהם והם יבצעו את הפעולות הבאות:

  • הידור כל קובצי ההצללה באופן רקורסיבי בספרייה הזו.
  • מוסיפים את הסיומת .spv לקובצי ההצללה SPIR-V שעברו הידור.
  • אורזים את הצלליות SPIRV בספריית assets/shaders/ של ה-APK.

האפליקציה תטען את תוכנות ההצללה שהורכבו מהמיקום assets/shaders/ המתאים בזמן הריצה. מבנה הקובץ של תוכנת ההצללה (shader) ה-spv המורכב זהה למבנה הקובץ של תוכנת ההצללה (shader) GLSL של האפליקציה ב-app/src/main/shaders/:

AAsset* file = AAssetManager_open(assetManager,
                     "shaders/tri.vert.spv", AASSET_MODE_BUFFER);
size_t fileLength = AAsset_getLength(file);
char* fileContent = new char[fileLength];
AAsset_read(file, fileContent, fileLength);

ניתן להגדיר דגלי הידור של Shaderc בתוך בלוק gradle DSL shaders, כמו בדוגמה הבאה:

מגניב

android {
  defaultConfig {
    shaders {
      glslcArgs.addAll(['-c', '-g'])
      scopedArgs.create('lights') {
        glslcArgs.addAll(['-DLIGHT1=1', '-DLIGHT2=0'])
      }
    }
  }
}

Kotlin

android {
  defaultConfig {
    shaders {
        glslcArgs += listOf("-c", "-g")
        glslcScopedArgs("lights", "-DLIGHT1=1", "-DLIGHT2=0")
    }
  }
}

glslcArgs יחולו על כל האוספים של תוכנות הצללה (shader). scopedArgs חל רק במהלך הידור להיקף הזה. הדוגמה שלמעלה יוצרת ארגומנט של היקף lights, שיחול רק על תוכנות הצללה ל-GLSL בספרייה app/src/main/shaders/lights/. פרטים נוספים glslc כדי לראות את הרשימה המלאה של דגלי הידור הזמינים. שימו לב ש-Saderc בתוך NDK הוא תמונת מצב מהמאגר הזה של github ב- זמן השקה של NDK; אפשר לקבל את הדגלים המדויקים שנתמכים בגרסה הזו באמצעות הפקודה glslc --help, כפי שיתועד בקטע הבא.

אוסף של שורת פקודה אופליין

ניתן להדר שידר של GLSL ל-SPIR-V ללא תלות באפליקציה הראשית באמצעות מהדר שורת הפקודה glslc. NDK גרסה 12 ואילך כולל גרסה של glslc מוכנים מראש בספרייה <android-ndk-dir>/shader-tools/, שיתמכו במודל השימוש הזה.

המהדר זמין גם מ-Shaderc פרויקט; פועלים לפי ההוראות המפורטות שם כדי לבנות גרסה בינארית.

glslc מספק קבוצה עשיר של לאפשרויות שורת הפקודה הידור של תוכנת ההצללה (shader) כדי לעמוד בדרישות שונות של האפליקציה.

הכלי glslc מהדק קובץ מקור יחיד למודול SPIR-V עם תוכנת הצללה (shader) אחת לנקודת הכניסה. כברירת מחדל, השם של קובץ הפלט זהה לשם של קובץ המקור, אבל עם התוסף .spv שמצורף אליו.

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

הידור של סביבת זמן הריצה

לאוסף JIT של תוכנות הצללה במהלך זמן ריצה, ה-NDK מספק את ספריית libshaderc, שיש לו גם ממשקי API של C וגם של C++.

באפליקציות C++ צריך להשתמש ב-C++ API. מומלץ להפעיל אפליקציות בשפות אחרות להשתמש ב-C API כי ה-ABI הוא ברמה נמוכה יותר וסביר להניח שהוא יספק יציבות יותר.

תוכלו להיעזר בדוגמה הבאה כדי להשתמש ב-C++ API:

#include <iostream>
#include <string>
#include <vector>
#include <shaderc/shaderc.hpp>

std::vector<uint32_t> compile_file(const std::string& name,
                                   shaderc_shader_kind kind,
                                   const std::string& data) {
  shaderc::Compiler compiler;
  shaderc::CompileOptions options;

  // Like -DMY_DEFINE=1
  options.AddMacroDefinition("MY_DEFINE", "1");

  shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv(
      data.c_str(), data.size(), kind, name.c_str(), options);

  if (module.GetCompilationStatus() !=
      shaderc_compilation_status_success) {
    std::cerr << module.GetErrorMessage();
  }

  std::vector<uint32_t> result(module.cbegin(), module.cend());
  return result;
}

שילוב בפרויקטים

אפשר לשלב את המהדר של תוכנת ההצללה (shader) Vulkan באפליקציה באמצעות קובץ Android.mk או Gradle.

Android.mk

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

  1. כוללים את השורות הבאות בקובץ Android.mk:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. הגדרה של APP_STL לאחת מהאפשרויות הבאות: c++_static, c++_shared, gnustl_static או gnustl_shared ב-Application.mk של האפליקציה

שילוב CMake של Gradle

  1. בחלון הטרמינל, עוברים אל ndk_root/sources/third_party/shaderc/
  2. מריצים את הפקודה הבאה כדי ליצור את Shaderc של NDK. צריך להריץ את הפקודה הזו רק פעם אחת בכל גרסת NDK שבה משתמשים:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    הפקודה הזו ממקמת שתי תיקיות ב-<ndk_root>/sources/third_party/shaderc/. המדריך המבנה שלהם הוא כזה:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. הוספת הכללים והקישורים שנוצרו באמצעות target_include_directories וגם target_link_libraries, כמו בדרך כלל באתרים דומים חיצוני ספריות. סוג ה-STL של האפליקציה חייב להתאים לאחד מסוגי stl שצוינו ב- stl_version. NDK ממליץ להשתמש ב-c++_shared או c++_static, אף על פי gnustl_static ו יש תמיכה גם ב-gnustl_shared.

מורידים את הגרסה האחרונה של Shaderc

Shaderc ב-NDK מגיע מעץ המקור של Android, תמונת מצב של מאגר Shaderc ב-upstream. אם אתם צריכים את הגרסה העדכנית ביותר של Shaderc, אפשר לעיין במאמר build instruction for details. השלבים הכלליים הם:

  1. מורידים את הגרסה האחרונה של Shaderc:
    git clone https://github.com/google/shaderc.git
  2. מעדכנים את יחסי התלות:
    ./utils/git-sync-deps
  3. Build Shaderc:
    <ndk_dir>/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
        APP_STL:=c++_static APP_ABI=all libshaderc_combined -j16
    
  4. מגדירים את הפרויקט כך שישתמש ב-build של Shaderc בקובץ סקריפט ה-build.