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)
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2025-07-27 (世界標準時間)。"],[],[],null,["# Migrate scripts to OpenGL ES 3.1\n\nFor workloads where GPU compute is ideal, migrating RenderScript scripts to\nOpenGL ES (GLES) allows applications written in Kotlin, Java, or using the NDK\nto take advantage of GPU hardware.\nA high-level overview follows to help you use OpenGL ES 3.1 compute shaders to\nreplace RenderScript scripts.\n| **Note:** Since Vulkan compute - accessible only from the NDK - maximizes control over GPU hardware, it may unlock more performance for your application.\n\nGLES Initialization\n-------------------\n\nInstead of creating a RenderScript context object, perform the following steps\nto create a GLES offscreen context using EGL:\n\n1. Get the default display\n\n2. Initialize EGL using the default display, specifying the GLES version.\n\n3. Choose an EGL config with a surface type of\n [`EGL_PBUFFER_BIT`](/reference/android/opengl/EGL14#EGL_PBUFFER_BIT \"EGL_PBUFFER_BIT\").\n\n4. Use the display and config to create an EGL context.\n\n5. Create the offscreen surface with\n [`eglCreatePBufferSurface`](/reference/android/opengl/EGL14#eglCreatePbufferSurface(android.opengl.EGLDisplay,%20android.opengl.EGLConfig,%20int%5B%5D,%20int) \"eglCreatePbufferSurface\"). If the context\n is going to only be used for compute, this can be a trivially small (1x1)\n surface.\n\n6. Create the render thread and call\n [`eglMakeCurrent`](/reference/android/opengl/EGL14#eglMakeCurrent(android.opengl.EGLDisplay,%20android.opengl.EGLSurface,%20android.opengl.EGLSurface,%20android.opengl.EGLContext) \"eglMakeCurrent\") in the render thread with the display, surface,\n and EGL context to bind the GL context to the thread.\n\nThe sample app demonstrates how to initialize the GLES context in\n[`GLSLImageProcessor.kt`](/guide/topics/renderscript/migrate/GLES%20Sample).\nTo learn more, see\n[EGLSurfaces and OpenGL ES](https://source.android.com/docs/core/graphics/arch-egl-opengl).\n| **Note:** An alternate method of doing image processing in GLES is to render to an offscreen surface, rather than using GL compute. This example demonstrates using GL for compute, because maps more closely to using RenderScript.\n\n### GLES debug output\n\nGetting useful errors from OpenGL uses an extension to enable debug logging\nthat sets a debug output callback. The method to do this from the SDK,\n`glDebugMessageCallbackKHR`, has never been implemented, and throws an\n[exception](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/native/opengl/tools/glgen/stubs/gles11/glDebugMessageCallback.cpp;l=3?q=glDebugMessage&sq=&ss=android%2Fplatform%2Fsuperproject%2Fmain \"Callback Exception\"). The [sample app](https://github.com/android/renderscript-samples/blob/main/RenderScriptMigrationSample/app/src/main/java/com/android/example/rsmigration/GLSLImageProcessor.kt \"GLES Sample\")\nincludes a wrapper for the callback from NDK code.\n\nGLES Allocations\n----------------\n\nA RenderScript Allocation can be migrated to an\n[Immutable storage texture](https://www.khronos.org/opengl/wiki/Texture_Storage#Immutable_storage \"Immutable Storage\") or a\n[Shader Storage Buffer Object](https://www.khronos.org/opengl/wiki/Shader_Storage_Buffer_Object \"Shader Storage Buffer Object\"). For read-only images, you\ncan use a [Sampler Object](https://www.khronos.org/opengl/wiki/Sampler_Object \"Sampler Object\"), which allows for\nfiltering.\n\nGLES resources are allocated within GLES. To avoid memory copying\noverhead when interacting with other Android components, there is an\nextension for [KHR Images](https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_image_base.txt \"KHR Image\") that allows the sharing\nof 2D arrays of image data. This extension has been required for Android devices\nbeginning with Android 8.0. The\n[graphics-core](/jetpack/androidx/releases/graphics)\n[Android Jetpack](/jetpack) library\nincludes support for creating these images within managed code and mapping\nthem to an allocated [`HardwareBuffer`](/reference/android/hardware/HardwareBuffer \"HardwareBuffer\"): \n\n val outputBuffers = Array(numberOfOutputImages) {\n HardwareBuffer.create(\n width, height, HardwareBuffer.RGBA_8888, 1,\n HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE\n )\n }\n val outputEGLImages = Array(numberOfOutputImages) { i -\u003e\n androidx.opengl.EGLExt.eglCreateImageFromHardwareBuffer(\n display,\n outputBuffers[i]\n )!!\n }\n\nUnfortunately, this doesn't create the immutable storage texture required for\na compute shader to write directly to the buffer. The sample uses\n[`glCopyTexSubImage2D`](/reference/android/opengl/GLES20#glCopyTexSubImage2D(int,%20int,%20int,%20int,%20int,%20int,%20int,%20int) \"glCopyTexSubImage2D\") to copy the storage texture used\nby the compute shader into the `KHR Image`. If the OpenGL driver supports the\n[EGL Image Storage](https://registry.khronos.org/OpenGL/extensions/EXT/EXT_EGL_image_storage.txt \"EGL Image Storage\") extension, then that extension\ncan be used to create a shared immutable storage texture to avoid the copy.\n\nConversion to GLSL compute shaders\n----------------------------------\n\nYour RenderScript scripts are converted into GLSL compute shaders.\n\n### Write a GLSL compute shader\n\nIn OpenGL ES,compute shaders are written in the\n[OpenGL Shading Language](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language)\n(GLSL).\n| **Note:** Unlike a RenderScript script, a GLSL compute shader can't have any invokable functions, and must have only one kernel.\n\n### Adaptation of script globals\n\nBased on the characteristics of the script globals, you can either use uniforms\nor uniform buffer objects for globals that are not modified within the shader:\n\n- [Uniform buffer](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap14.html#descriptorsets-uniformbuffer): Recommended for frequently-changed script globals of sizes larger than the push constant limit.\n\nFor globals that are changed within the shader, you can use use an\n[Immutable storage texture](https://www.khronos.org/opengl/wiki/Texture_Storage#Immutable_storage \"Immutable Storage\") or a\n[Shader Storage Buffer Object](https://www.khronos.org/opengl/wiki/Shader_Storage_Buffer_Object \"Shader Storage Buffer Object\").\n\nExecute Computations\n--------------------\n\nCompute shaders aren't part of the graphics pipeline; they are general purpose\nand designed to compute highly-parallelizable jobs. This lets you have\nmore control over how they execute, but it also means that you have to\nunderstand a bit more about how your job is parallelized.\n\n### Create and initialize the compute program\n\nCreating and initializing the compute program has lots in common with\nworking with any other GLES shader.\n\n1. Create the program and the compute shader associated with it.\n\n2. Attach the shader source, compile the shader (and check the results\n of the compilation).\n\n3. Attach the shader, link the program, and use the program.\n\n4. Create, initialize, and bind any uniforms.\n\n### Start a computation\n\nCompute shaders operate within an abstract 1D, 2D, or 3D space on a series\nof workgroups, which are defined within the shader source code, and represent\nthe minimum invocation size as well as the geometry of the shader.\nThe following shader works on a 2D image and defines the work groups in two\ndimensions: \n\n private const val WORKGROUP_SIZE_X = 8\n private const val WORKGROUP_SIZE_Y = 8\n private const val ROTATION_MATRIX_SHADER =\n \"\"\"#version 310 es\n layout (local_size_x = $WORKGROUP_SIZE_X, local_size_y = $WORKGROUP_SIZE_Y, local_size_z = 1) in;\n\nWorkgroups can share memory, defined by `GL_MAX_COMPUTE_SHARED_MEMORY_SIZE`,\nwhich is at least 32 KB and can make use of `memoryBarrierShared()` to provide\ncoherent memory access.\n\n#### Define workgroup size\n\nEven if your problem space works well with workgroup sizes of 1, setting an\nappropriate workgroup size is important for parallelizing the compute shader.\nIf the size is too small, the GPU driver may not parallelize your computation\nenough, for example. Ideally, these sizes should be tuned per-GPU, although\nreasonable defaults work well enough on current devices, such as the workgroup\nsize of 8x8 in the shader snippet.\n\nThere is a `GL_MAX_COMPUTE_WORK_GROUP_COUNT`, but it is substantial; it must be\nat least 65535 in all three axes according to the specification.\n\n### Dispatch the shader\n\nThe final step in executing computations is to dispatch the shader using one\nof the dispatch functions such as\n[`glDispatchCompute`](/reference/android/opengl/GLES31#glDispatchCompute(int,%20int,%20int) \"glDispatchCompute\"). The dispatch function is responsible\nfor setting the number of workgroups for each axis: \n\n GLES31.glDispatchCompute(\n roundUp(inputImage.width, WORKGROUP_SIZE_X),\n roundUp(inputImage.height, WORKGROUP_SIZE_Y),\n 1 // Z workgroup size. 1 == only one z level, which indicates a 2D kernel\n )\n\nTo return the value, first wait for the compute operation to finish using a\nmemorybarrier: \n\n GLES31.glMemoryBarrier(GLES31.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)\n\nTo chain multiple kernels together,\n(for example, to migrate code using `ScriptGroup`), create and dispatch\nmultiple programs and synchronize their access to the output with memory\nbarriers.\n\nThe [sample app](https://github.com/android/renderscript-samples/blob/main/RenderScriptMigrationSample/app/src/main/java/com/android/example/rsmigration/GLSLImageProcessor.kt \"GLES Sample\")\ndemonstrates two compute tasks:\n\n- HUE rotation: A compute task with a single compute shader. See `GLSLImageProcessor::rotateHue` for the code sample.\n- Blur: A more complex compute task that sequentially executes two compute shaders. See `GLSLImageProcessor::blur` for the code sample.\n\nTo learn more about memory barriers, refer to\n[Ensuring visibility](https://www.khronos.org/opengl/wiki/Memory_Model#Ensuring_visibility)\nas well as\n[Shared variables](https://www.khronos.org/opengl/wiki/Compute_Shader#Shared_variables)\n."]]