نقل النصوص البرمجية إلى OpenGL ES 3.1

بالنسبة إلى أحمال العمل التي تكون فيها طريقة احتساب وحدة معالجة الرسومات مثالية، يتيح نقل نصوص RenderScript إلى OpenGL ES (GLES) للتطبيقات المكتوبة بلغة Kotlin أو Java أو التي تستخدم NDK الاستفادة من أجهزة وحدة معالجة الرسومات. في ما يلي نظرة عامة عالية المستوى لمساعدتك في استخدام أدوات التظليل الحوسبية OpenGL ES 3.1 لاستبدال النصوص البرمجية RenderScript.

إعداد GLES

بدلاً من إنشاء كائن سياق RenderScript، نفِّذ الخطوات التالية لإنشاء سياق GLES خارج الشاشة باستخدام EGL:

  1. الحصول على الشاشة التلقائية

  2. قم بتهيئة EGL باستخدام الشاشة الافتراضية، مع تحديد إصدار GLES.

  3. اختَر إعدادات EGL مع نوع السطح EGL_PBUFFER_BIT.

  4. يمكنك استخدام شاشة العرض والإعداد لإنشاء سياق EGL.

  5. يمكنك إنشاء سطح خارج الشاشة باستخدام eglCreatePBufferSurface. إذا كان سيتم استخدام السياق للحوسبة فقط، فقد يكون هذا سطحًا صغيرًا جدًا (1×1).

  6. أنشِئ سلسلة محادثات لعرض الطلب واطلب الرمز eglMakeCurrent في سلسلة العرض مع تضمين سياق الشاشة والعرض وسياق EGL لربط سياق GL بسلسلة المحادثات.

يوضّح نموذج التطبيق كيفية إعداد سياق GLES في GLSLImageProcessor.kt. ولمعرفة المزيد من المعلومات، يُرجى الاطّلاع على EGLSurfaces وOpen ES.

ناتج تصحيح أخطاء GLES

يؤدي الحصول على أخطاء مفيدة من OpenGL إلى استخدام إضافة لتفعيل تسجيل تصحيح الأخطاء الذي يؤدي إلى تعيين استدعاء ناتج تصحيح الأخطاء. لم يتم مطلقًا تنفيذ طريقة تنفيذ ذلك من حزمة تطوير البرامج (SDK) glDebugMessageCallbackKHR، وهناك استثناء. يتضمّن نموذج التطبيق برنامج تضمين لمعاودة الاتصال من رمز NDK.

تخصيصات خدمات الموقع الجغرافي في Google (GLES)

يمكن نقل تخصيص RenderScript إلى زخرفة غير قابلة للتغيير في مساحة التخزين أو كائن Shader Storage Buffer. بالنسبة إلى الصور للقراءة فقط، يمكنك استخدام كائن العينة، الذي يسمح بالتصفية.

يتم تخصيص موارد GLES ضمن GLES. لتجنُّب استخدام النسخ الزائد للذاكرة عند التفاعل مع مكوّنات Android الأخرى، هناك إضافة KHR (صور KHR) تسمح بمشاركة بيانات الصور ثنائية الأبعاد. هذه الإضافة مطلوبة لأجهزة Android التي تبدأ بـ Android 8.0. تتضمن مكتبة graphics-core Android Jetpack إتاحة إنشاء هذه الصور ضمن رمز مُدار وربطها بعنصر HardwareBuffer مخصص:

val outputBuffers = Array(numberOfOutputImages) {
  HardwareBuffer.create(
    width, height, HardwareBuffer.RGBA_8888, 1,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
  )
}
val outputEGLImages = Array(numberOfOutputImages) { i ->
    androidx.opengl.EGLExt.eglCreateImageFromHardwareBuffer(
        display,
        outputBuffers[i]
    )!!
}

ولسوء الحظ، لا يؤدي هذا إلى إنشاء زخرفة وحدة التخزين غير القابلة للتغيير والمطلوبة من أداة تظليل الحوسبة للكتابة مباشرةً إلى المخزن المؤقت. يستخدم النموذج glCopyTexSubImage2D لنسخ بنية مساحة التخزين التي تستخدمها أداة تظليل الحوسبة إلى KHR Image. إذا كان برنامج تشغيل OpenGL متوافقًا مع الإضافة EGL Image Storage، يمكن استخدام تلك الإضافة لإنشاء زخرفة مشتركة غير قابلة للتغيير في مساحة التخزين لتجنّب النسخ.

التحويل إلى أدوات تظليل الحوسبة في GLSL

يتم تحويل نصوص RenderScript إلى أدوات تظليل للحوسبة في GLSL.

كتابة أداة تظليل الحوسبة GLSL

في OpenGL ES، تتم كتابة برامج التظليل الحوسبية بلغة OpenGL Shading Language (GLSL).

تكييف النص البرمجي globals

استنادًا إلى خصائص النص البرمجي globals (globals)، يمكنك إما استخدام أزياء موحَّدة أو كائنات مخزن مؤقّت موحَّد للأرقام العامة للأرقام العامة التي لا يتم تعديلها داخل أداة التظليل:

  • مخزن مؤقت موحّد: يُنصح بهذا الخيار لأرقام التعريف العامة للنصوص البرمجية التي يتم تغييرها بشكل متكرر لأحجام أكبر من الحد الثابت للدفع الثابت.

بالنسبة إلى العوالم العامة التي تم تغييرها في أداة التظليل، يمكنك استخدام زخرفة غير قابلة للتغيير في مساحة التخزين أو عنصر Shader Storage Buffer.

تنفيذ العمليات الحسابية

لا تُعد أجهزة تظليل الحوسبة جزءًا من مسار الرسومات؛ فهي تُستخدم للأغراض العامة ومصممة لحساب الوظائف القابلة للتمييز للغاية. يتيح لك هذا مزيدًا من التحكم في كيفية تنفيذها، ولكن يعني أيضًا أنه عليك فهم المزيد حول كيفية موازاة عملك.

إنشاء برنامج الحوسبة وإعداده

هناك الكثير من القواسم المشتركة بين إنشاء وتهيئة برنامج الحوسبة والعمل مع أي برنامج تظليل GLES آخر.

  1. أنشِئ البرنامج وأداة تظليل الحوسبة المرتبطة به.

  2. أرفِق مصدر أداة التظليل، واجمع أداة التظليل (وتحقق من نتائج التحويل).

  3. إرفاق أداة التظليل وربط البرنامج واستخدام البرنامج.

  4. إنشاء أي أزياء موحدة وإعدادها وربطها.

بدء عملية حسابية

تعمل أدوات تظليل الحوسبة ضمن مساحة تجريدية ذات أبعاد 1D أو 2D أو 3D في سلسلة من مجموعات العمل التي يتم تحديدها ضمن رمز مصدر أداة التظليل وتمثل الحد الأدنى من حجم الاستدعاء بالإضافة إلى هندسة أداة التظليل. تعمل أداة التظليل التالية على صورة ثنائية الأبعاد وتحدد مجموعات العمل في بعدين:

private const val WORKGROUP_SIZE_X = 8
private const val WORKGROUP_SIZE_Y = 8
private const val ROTATION_MATRIX_SHADER =
    """#version 310 es
    layout (local_size_x = $WORKGROUP_SIZE_X, local_size_y = $WORKGROUP_SIZE_Y, local_size_z = 1) in;

يمكن لمجموعات العمل مشاركة الذاكرة، التي يتم تحديدها من خلال GL_MAX_COMPUTE_SHARED_MEMORY_SIZE، وحجمها لا يقل عن 32 كيلوبايت، ويمكنها استخدام memoryBarrierShared() لتوفير إمكانية وصول مترابط للذاكرة.

تحديد حجم مجموعة العمل

حتى لو كانت مساحة المشكلة تعمل بشكل جيد مع أحجام مجموعات العمل البالغة 1، فإن تعيين حجم مناسب لمجموعة عمل يُعد أمرًا مهمًا من أجل موازاة أداة تظليل الحوسبة. إذا كان الحجم صغيرًا جدًا، فقد لا يوازي برنامج تشغيل وحدة معالجة الرسومات إجراء العمليات الحسابية بما يكفي، على سبيل المثال. ومن الناحية المثالية، يجب ضبط هذه الأحجام لكل وحدة معالجة رسومات، على الرغم من أن الإعدادات الافتراضية المعقولة تعمل بشكل جيد بما فيه الكفاية على الأجهزة الحالية، مثل حجم مجموعة العمل البالغ 8×8 في مقتطف أداة التظليل.

هناك السمة GL_MAX_COMPUTE_WORK_GROUP_COUNT، ولكنّها كبيرة الحجم. يجب أن تكون القيمة 65535 على الأقل في جميع المحاور الثلاثة وفقًا للمواصفات.

نقل أداة التظليل

الخطوة الأخيرة في تنفيذ العمليات الحسابية هي إرسال أداة التظليل باستخدام إحدى دوال الإرسال مثل glDispatchCompute. تعد دالة الإرسال مسئولة عن تحديد عدد مجموعات العمل لكل محور:

GLES31.glDispatchCompute(
  roundUp(inputImage.width, WORKGROUP_SIZE_X),
  roundUp(inputImage.height, WORKGROUP_SIZE_Y),
  1 // Z workgroup size. 1 == only one z level, which indicates a 2D kernel
)

لعرض القيمة، انتظر أولاً حتى تنتهي عملية الحوسبة باستخدام أحد حواجز الذاكرة:

GLES31.glMemoryBarrier(GLES31.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)

لربط عدة نواة معًا (على سبيل المثال، لنقل الرمز باستخدام ScriptGroup)، يمكنك إنشاء برامج متعددة وإرسالها ومزامنة وصولها إلى الإخراج باستخدام حواجز الذاكرة.

يوضّح نموذج التطبيق مهمتَي احتساب هما:

  • تدوير HUE: مهمة حوسبة باستخدام أداة تظليل حوسبية واحدة. يمكنك الانتقال إلى GLSLImageProcessor::rotateHue للاطّلاع على نموذج الرموز البرمجية.
  • التمويه: مهمة حوسبة أكثر تعقيدًا تنفّذ بشكل تسلسلي تظليلَين للحوسبة. يمكنك الانتقال إلى GLSLImageProcessor::blur للاطّلاع على عيّنة من الرمز.

للتعرف على مزيد من المعلومات حول حواجز الذاكرة، يمكنك الرجوع إلى ضمان الرؤية بالإضافة إلى المتغيرات المشتركة .