GPU 컴퓨팅이 이상적인 워크로드의 경우 RenderScript 스크립트를 OpenGL ES(GLES)로 이전하면 Kotlin, Java 또는 NDK를 사용하여 작성된 애플리케이션이 GPU 하드웨어를 활용할 수 있습니다.
다음은 OpenGL ES 3.1 컴퓨팅 셰이더를 사용하여 RenderScript 스크립트를 대체하는 데 도움이 되는 대략적인 개요입니다.
GLES 초기화
RenderScript 컨텍스트 객체를 만드는 대신 다음 단계에 따라 EGL을 사용하여 GLES 오프스크린 컨텍스트를 만드세요.
OpenGL에서 유용한 오류를 가져오면 확장 프로그램을 사용하여, 디버그 출력 콜백을 설정하는 디버그 로깅을 사용 설정합니다. SDK에서 이 작업을 실행하는 메서드 glDebugMessageCallbackKHR은 구현된 적이 없으며 예외를 발생시킵니다. 샘플 앱
에는 NDK 코드의 콜백에 대한 래퍼가 포함됩니다.
GLES 리소스는 GLES 내에서 할당됩니다. 다른 Android 구성요소와 상호작용할 때 메모리 복사 오버헤드를 방지하기 위해 이미지 데이터의 2D 배열을 공유할 수 있는 KHR 이미지 확장 프로그램이 있습니다. 이 확장 프로그램은 Android 8.0부터 Android 기기에 필요합니다. graphics-coreAndroid Jetpack 라이브러리에는 관리 코드 내에서 이러한 이미지를 만들고 이를 할당된 HardwareBuffer에 매핑하기 위한 지원이 포함되어 있습니다.
안타깝게도 이 방법은 컴퓨팅 셰이더가 버퍼에 직접 쓰는 데 필요한 변경 불가능한 저장소 텍스처를 만들지 않습니다. 이 샘플은 glCopyTexSubImage2D를 사용하여 컴퓨팅 셰이더에서 사용한 저장소 텍스처를 KHR Image에 복사합니다. OpenGL 드라이버가 EGL 이미지 저장소 확장 프로그램을 지원하는 경우 변경 불가능한 공유 저장소 텍스처를 만들어 복사를 방지하는 데 이 확장 프로그램을 사용할 수 있습니다.
작업 그룹은 GL_MAX_COMPUTE_SHARED_MEMORY_SIZE로 정의된 메모리(32KB 이상)를 공유할 수 있으며 memoryBarrierShared()를 활용하여 일관된 메모리 액세스를 제공할 수 있습니다.
작업 그룹 크기 정의
문제 공간이 작업 그룹 크기 1에서 잘 작동하더라도 컴퓨팅 셰이더를 병렬화하려면 적절한 작업 그룹 크기를 설정하는 것이 중요합니다.
예를 들어 크기가 너무 작으면 GPU 드라이버가 계산을 충분히 병렬화하지 못할 수 있습니다. 이러한 크기는 GPU별로 조정하는 것이 이상적이지만, 적절한 기본값은 현재 기기에서 충분히 잘 작동합니다. 셰이더 스니펫의 작업 그룹 크기 8x8을 예로 들 수 있습니다.
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)
HCE 회전: 단일 컴퓨팅 셰이더가 있는 컴퓨팅 작업입니다. 코드 샘플은 GLSLImageProcessor::rotateHue를 참고하세요.
흐리게 처리: 컴퓨팅 셰이더 두 개를 순차적으로 실행하는 더 복잡한 컴퓨팅 작업입니다. 코드 샘플은 GLSLImageProcessor::blur를 참고하세요.
메모리 배리어에 관한 자세한 내용은 다음을 참고하세요.
가시성 보장
는 물론
공유 변수
에서 자세한 내용을 확인하실 수 있습니다.
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-27(UTC)
[[["이해하기 쉬움","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(UTC)"],[],[],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."]]