اسکریپت ها را به OpenGL ES 3.1 منتقل کنید

برای بارهای کاری که محاسبات GPU در آنها ایده آل است، انتقال اسکریپت های RenderScript به OpenGL ES (GLES) به برنامه های کاربردی نوشته شده در Kotlin، Java، یا استفاده از NDK اجازه می دهد تا از مزیت سخت افزار GPU استفاده کنند. یک نمای کلی در سطح بالا در ادامه می آید تا به شما کمک کند از سایه زن های محاسباتی OpenGL ES 3.1 برای جایگزینی اسکریپت های RenderScript استفاده کنید.

راه اندازی GLES

به جای ایجاد یک شی زمینه RenderScript، مراحل زیر را برای ایجاد یک زمینه GLES خارج از صفحه با استفاده از EGL انجام دهید:

  1. نمایشگر پیش فرض را دریافت کنید

  2. EGL را با استفاده از نمایشگر پیش فرض، با مشخص کردن نسخه GLES، مقداردهی اولیه کنید.

  3. یک پیکربندی EGL با نوع سطح EGL_PBUFFER_BIT را انتخاب کنید.

  4. از صفحه نمایش و پیکربندی برای ایجاد زمینه EGL استفاده کنید.

  5. سطح خارج از صفحه را با eglCreatePBufferSurface ایجاد کنید. اگر قرار است از متن فقط برای محاسبه استفاده شود، این می تواند یک سطح بسیار کوچک (1x1) باشد.

  6. رشته رندر را ایجاد کنید و eglMakeCurrent در رشته رندر با صفحه نمایش، سطح و زمینه EGL فراخوانی کنید تا زمینه GL را به رشته متصل کنید.

برنامه نمونه نحوه تنظیم اولیه زمینه GLES در GLSLImageProcessor.kt را نشان می دهد. برای کسب اطلاعات بیشتر، به EGLSurfaces و OpenGL ES مراجعه کنید.

خروجی اشکال زدایی GLES

دریافت خطاهای مفید از OpenGL از یک برنامه افزودنی برای فعال کردن گزارش اشکال زدایی استفاده می کند که یک پاسخ خروجی اشکال زدایی را تنظیم می کند. روش انجام این کار از SDK، glDebugMessageCallbackKHR ، هرگز پیاده سازی نشده است و یک استثنا ایجاد می کند. برنامه نمونه شامل یک بسته بندی برای پاسخ به تماس از کد NDK است.

تخصیص GLES

یک تخصیص RenderScript را می توان به یک بافت ذخیره سازی Immutable یا یک شی بافر ذخیره سازی Shader منتقل کرد. برای تصاویر فقط خواندنی، می توانید از یک شی نمونه استفاده کنید که امکان فیلتر کردن را فراهم می کند.

منابع GLES در GLES تخصیص داده می شود. برای جلوگیری از کپی سربار حافظه هنگام تعامل با سایر مؤلفه‌های Android، افزونه‌ای برای KHR Images وجود دارد که امکان اشتراک‌گذاری آرایه‌های ۲ بعدی داده‌های تصویر را فراهم می‌کند. این افزونه برای دستگاه های اندرویدی که با اندروید 8.0 شروع می شوند مورد نیاز است. کتابخانه هسته گرافیکی 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 (GLSL) نوشته می‌شوند.

انطباق جهانی های اسکریپت

بر اساس ویژگی‌های جهانی‌های اسکریپت، می‌توانید از یونیفرم‌ها یا اشیاء بافر یکنواخت برای جهانی‌هایی که در سایه‌زن اصلاح نشده‌اند استفاده کنید:

  • بافر یکنواخت : برای اسکریپت های جهانی با اندازه های بزرگتر از حد ثابت فشاری که اغلب تغییر می کنند، توصیه می شود.

برای جهانی‌هایی که در سایه‌زن تغییر می‌کنند، می‌توانید از یک بافت ذخیره‌سازی Immutable یا یک شی بافر ذخیره‌سازی Shader استفاده کنید.

اجرای محاسبات

شیدرهای محاسباتی بخشی از خط لوله گرافیکی نیستند. آنها هدف کلی هستند و برای محاسبه مشاغل بسیار قابل موازی سازی طراحی شده اند. این به شما امکان می دهد تا کنترل بیشتری بر نحوه اجرای آنها داشته باشید، اما همچنین به این معنی است که باید کمی بیشتر در مورد نحوه موازی شدن کار خود بدانید.

برنامه محاسباتی را ایجاد و مقداردهی اولیه کنید

ایجاد و مقداردهی اولیه برنامه محاسباتی با کار با هر سایه زن دیگر GLES مشترکات زیادی دارد.

  1. برنامه و شیدر محاسباتی مرتبط با آن را ایجاد کنید.

  2. منبع سایه زن را وصل کنید، شیدر را کامپایل کنید (و نتایج کامپایل را بررسی کنید).

  3. سایه زن را ضمیمه کنید، برنامه را پیوند دهید و از برنامه استفاده کنید.

  4. هر یونیفرمی را ایجاد، مقداردهی اولیه و متصل کنید.

یک محاسبات را شروع کنید

شیدرهای محاسباتی در یک فضای انتزاعی 1 بعدی، 2 بعدی یا سه بعدی روی یک سری از گروه های کاری، که در کد منبع سایه زن تعریف شده اند، عمل می کنند و حداقل اندازه فراخوانی و همچنین هندسه سایه زن را نشان می دهند. سایه زن زیر روی یک تصویر دو بعدی کار می کند و گروه های کاری را در دو بعد تعریف می کند:

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 به خوبی کار کند، تنظیم اندازه گروه کاری مناسب برای موازی کردن سایه‌زن محاسباتی مهم است. اگر اندازه خیلی کوچک باشد، برای مثال، درایور GPU ممکن است محاسبات شما را به اندازه کافی موازی نکند. در حالت ایده‌آل، این اندازه‌ها باید به ازای هر GPU تنظیم شوند، اگرچه پیش‌فرض‌های معقول به اندازه کافی در دستگاه‌های فعلی مانند اندازه گروه کاری ۸×۸ در قطعه سایه‌زن به خوبی کار می‌کنند.

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
)

برای برگرداندن مقدار، ابتدا منتظر بمانید تا عملیات محاسبه با استفاده از یک memorybarrier به پایان برسد:

GLES31.glMemoryBarrier(GLES31.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)

برای زنجیره‌ای کردن چندین هسته با هم، (مثلاً برای انتقال کد با استفاده از ScriptGroup )، چندین برنامه ایجاد و ارسال کنید و دسترسی آنها به خروجی را با موانع حافظه همگام‌سازی کنید.

برنامه نمونه دو کار محاسباتی را نشان می دهد:

  • چرخش HUE: یک کار محاسباتی با یک شیدر محاسباتی. برای نمونه کد به GLSLImageProcessor::rotateHue مراجعه کنید.
  • Blur: یک کار محاسباتی پیچیده تر که به طور متوالی دو سایه زن محاسباتی را اجرا می کند. برای نمونه کد به GLSLImageProcessor::blur مراجعه کنید.

برای کسب اطلاعات بیشتر در مورد موانع حافظه، به اطمینان از دید و همچنین متغیرهای مشترک مراجعه کنید.