Tistory View
Android GPU로 계산하기 : ComputeShader #4 Texture다루기
What should I do? 2021. 7. 2. 05:223.0까지 texture에 직접 쓸 수 있는 방법이 없었다. FBO로 감싸서 그려야 했고, 그 과정은 코드로는 간단하지만, 실제 GPU에서는 상당한 작업이 들어가는 작업이었다. 이제 ComputeShader는 바로 읽기/쓰기를 지원한다. 하지만 여기에는 제약이 있는 데, 기존에 사용하던 방식이 아닌 다른 함수를 통해 텍스쳐를 만들어야 한다.
Immutable-storage에 텍스쳐만들기
말이 좀 해깔리지만(Immutable:바꿀 수 없는) 그냥 ComputeShader는 Immutable-storage의 텍스쳐만 쓸(write) 수 있다고 생각하면 된다. 이렇게 만들 수 있는 함수는 glTexStorage*()함수들이다.
텍스쳐 만드는 코드
GLuint texId = 0;
glGenTextures( 1, &texId );
glBindTexture( GL_TEXTURE_2D, texId );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexStorage2D( GL_TEXTURE_2D, 1, GL_RGBA8, w, h );
//glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 픽셀 데이터);
다른 것은 다 동일하고 단지 glTexImage2D대신 glTexStorage2D함수를 이용하고 있다.
void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height );
여기서 좀 주의 할 것은 levels는 만들어질 개수로 0이 아닌 1을 넣었다. 말 그대로 만들어 질 개수를 넣는 것이다, texture의 Lod Index를 넣는 것이 아니다. 이 것은 할당을 할 뿐 실제 이미지 데이터를 보내는 것은 glTexSubImage2D함수를 그대로 이용하여 필요한 위치에 이미지 데이터를 보낼 수 있다.
사용할 수 있는 internalformat은 [이 곳 glTexStoreage2D Manual]을 참고 하면 된다.
위 링크에 [Table 1. Sized Internal Formats]을 보면 Renderable과 Filterable에 값이 있다.
Renderable 이 "Y"가 아닌 경우 이 텍스쳐를 화면에 그려도 그려지지 않을 것이고, Filterable이 "Y"가 아니면 아무리 난리를 쳐도 부드러운 화면을 보지 못 할 것이다.(절때~ 필자가 이 것 때문에 고생했다는 것은 아니다... 아니라구 믿어~)
이제 예제로 설명을 계속 하겠다.
ColorMatrix using ComputeShader
필자가 이전에 포스트한 Android Java의 ColorMatrix가 있다. 이 것을 ComputeShader로 구현을 해 보도록 하겠다.
ColorMatrix Shader
#version 310 es
layout( local_size_x = 8, local_size_y = 8, local_size_z = 1 ) in;
precision highp float;
layout(binding = 0, rgba8 ) readonly uniform highp restrict image2D uSrc;
layout(binding = 1, rgba8 ) writeonly uniform highp restrict image2D uDst;
layout( location = 0 ) uniform ivec2 dim;
layout( location = 2 ) uniform mat4 matColor;
layout( location = 3 ) uniform vec4 vecColor0;
void main() {
ivec2 pos = ivec2( gl_WorkGroupID.xy * gl_WorkGroupSize.xy + gl_LocalInvocationID.xy );
if( all( lessThan( pos, dim ) ) ) {
imageStore( uDst, pos, matColor * imageLoad( uSrc, pos ) + vecColor0 );
}
}
local_size를 8x8x1로 지정했다. 이 쓰레드가 텍스쳐내의 어떤 픽셀을 처리할 지 구분하여 pos값을 만들고, 해당 픽셀을 불러와 계산 후 다시 저장하는 단순한 shader다.
Dispatch 코드
// srcW, srcH : texture크기
// gray-scale ColorMatrix
float mat[16] = { 0.2989f, 0.5870f, 0.1140f, 0.0F,
0.2989f, 0.5870f, 0.1140f, 0.0F,
0.2989f, 0.5870f, 0.1140f, 0.0F,
0.0000F, 0.0000F, 0.0000F, 1.0F };
float vec0[4] = { 0.0F, 0.0F, 0.0F, 0.0F };
const int32_t localSizeX = 8; // shader의 local_size_x
const int32_t localSizeY = 8; // shader의 local_size_y
const int32_t groupSize_x = ( srcW + localSizeX - 1 ) / localSizeX;
const int32_t groupSize_y = ( srcH + localSizeY - 1 ) / localSizeY;
glMemoryBarrier( GL_SHADER_IMAGE_ACCESS_BARRIER_BIT );
glUseProgram(programTex);
glUniform2i( 0, srcW, srcH ); // 이미지의 크기를 보내준다.
// transpose[pivot]한다. java에서 matrix순서를 opengl용으로 바꾼다.col <-> row
glUniformMatrix4fv( 2, 1, GL_TRUE, mat );
glUniform4fv( 3, 1, vec0 );
glBindImageTexture(0, srcTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8 );
glBindImageTexture(1, dstTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8 );
glDispatchCompute( groupSize_x, groupSize_y, 1 );
glUseProgram( 0 );
goupSize_*은 다음과 같이 계산된다. 예를 들어 70x90의 이미지가 있다면
localsize_x값은 8 이므로 가로pixel 처리를 위한 쓰레드 수는
workgroup_x = 70 / 8 = 8.75번 이 필요하다. 따라서 9번 돌려 줘야 한다.
따라서 가로 픽셀들을 처리하는 Thread수는 9x8 = 72번 이 되는 데, 70번째 Thread가 가로의 가장 마지막 픽셀을 처리하게 되므로 71번째, 72번째 쓰레드는 할 일이 없다.세로도 마찬가지로 계산이 된다.
따라서 다음의 쉐이더코드가 처리 할지 말지를 구분하는 것 뿐이다.
if( all( lessThan( pos, dim ) ) ) {
// 처리한다.
}
왜 굳이 localsize_x와 y를 8로 정했는지는... 시간나면 쓰겠다.
texture를 읽고 쓰는 함수인 imageLoad와 imageStore는 texture내의 어느위치나 읽고 쓸 수 있다. fragment shader와 다르게 쓸 위치를 random하게 할 수 있다는 점이 이 ComputeShader의 장점이다.
texture는 readonly나 writeonly를 꼭 써줘야 한다. 즉 읽거나 쓰거나 하나만 할 수 있다. 다른 상황이 하나 있는 데, 둘 다 쓰는 것도 인정된다. 이 경우 읽을 수도 쓸 수도 없지만, imageSize함수를 통해 texture의 크기만 알아 낼 때 쓰기도 한다.
glBindImageTexture함수
// shader code
layout(binding = 0, rgba8 ) readonly uniform highp restrict image2D uSrc;
// c code
glBindImageTexture(0, srcTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8 );
glBindImageTexture( GLuint unit, GLuint texture,
GLint level, GLboolean layered,
GLint layer, GLenum access, GLenum format)
다른 파라미터는 바로 느낌이 온다. 필요한 것만 이야기 하면,
unit는 shader의 binding 값
format은 shader에서 어떤 형태로 사용할 것인지를 나타낸다.
(텍스처를 만들 때의 값과는 호환이 되어야 한다는데.. 필자는 그냥 동일하게 사용해서)
예제에서 GL_RGBA8을 전달 하기에 shader에서는 rgba8로 받아 서로 맞춰줘야 한다.
각각의 매칭값은 다음과 같다.
C | shader | type |
GL_RGBA32F | rgba32f | float |
GL_RGBA16F | rgba16f | float |
GL_R32F | r32f | float |
GL_RGBA32UI | rgba32ui | uint |
GL_RGBA16UI | rgba16ui | uint |
GL_RGBA8UI | rgba8ui | uint |
GL_R32UI | r32ui | uint |
GL_RGBA32I | rgba32i | int |
GL_RGBA16I | rgba16i | int |
GL_RGBA8I | rgba8i | int |
GL_R32I | r32i | int |
GL_RGBA8 | rgba8 | float |
GL_RGBA8_SNORM | rgba8_snorm | float |
type값은 이미지를 읽거나 쓰는 imageLoad/imageStore에서 사용되는 형태다.
imageLoad를 예로 들면 "리턴값은 GL_RGBA8인 경우 vec4로 받아야 한다. GL_RGBA16I일 경우는 ivec4로 받아야 한다."는 이야기다.
// C++
// GL_RGBA8은 float형
glBindImageTexture(0, srcTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8 );
// shader
vec4 color = imageLoad( tex, ivec( x,y ) );
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GL_RGBA8UI은 uint형
glBindImageTexture(0, srcTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8UI );
// shader
uvec4 color = imageLoad( tex, ivec( x,y ) );
'Android Develop > OpenGLES' 카테고리의 다른 글
Android GPU로 계산하기 : ComputeShader #5 Barrier기본, API Barrier (0) | 2021.07.02 |
---|---|
Android GPU로 계산하기 : ComputeShader #2 컴파일/기본예제 (0) | 2021.06.29 |
Android OpenGL ES 버전 호환성 맞추기 (0) | 2021.06.26 |
Android GPU로 계산하기 : ComputeShader #1 소개 (0) | 2021.06.26 |
OpenGL ES : (Texture or FBO) to (Texture or FBO) 복사 정리 (0) | 2021.06.21 |
- Total
- Today
- Yesterday
- TTS
- 전기료
- 에어콘
- 텍스처
- 공유 컨텍스트
- 에어컨
- 재테크
- ComputeShader
- OpenGLes
- 전기세
- Android
- 예금
- 사용료
- OpenGL ES
- choreographer
- 전기요금
- 컴퓨트셰이더
- 블로그
- 아끼는 법
- 티스토리
- 컴퓨트쉐이더
- 재태크
- gpgpu
- 경제보복
- 금리
- 적금
- texture
- 애드센스
- 애드핏
- 안드로이드
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |