Tistory View

이전 글에서 간단히 SSBO를 활용한 연산을 해 보았다, 이 글은 좀 더 자세한 사항을 다루도록 하겠다.

 

일단 GPU에서 사용될 Buffer를 만들어야한다. 이 버퍼에 데이터를 담아 GPU로 보내면 GPU는 이 곳에서 데이터를 읽을 수 있고, GPU에서 데이터를 이 곳에 쓰면 CPU에서 읽어 낼 수 있다.

 

일단 만드는 것은 다른 버퍼들과 동일하다.

GLint ssbo = 0;
glGenBuffers( 1, &ssbo );
glBindBuffer( GL_SHADER_STORAGE_BUFFER, ssbo );
glBufferData( GL_SHADER_STORAGE_BUFFER, 크기, nullptr, GL_STREAM_DRAW );
glBindBuffer( GL_SHADER_STORAGE_BUFFER, 0 );

glBufferData를 통해 데이터를 지정하거나, 공간을 할당하는 것 또한 동일하다.

glBufferSubData로 데이터의 일부를 변경하는 것도 동일하고, glMapBufferRange를 통해 읽기/쓰기 또한 동일하다.

GL_STREAM_DRAW를 쓸지 GL_STATIC_DRAW를 쓸지 또한 사용법이 동일하다.

 

쉐이더코드에서 이 버퍼를 지정하는 코드는 다음과 같다.

layout(binding = 0 ) writeonly buffer Ninenine { // buffer지시어는 ssbo임을 나타낸다
    uvec4 e[];
} ninenine;

이전에 사용한 코드와 동일한 코드를 가지고 왔다.

쉐이더 코드내에서 접근은 ninenine.e[인덱스]로 사용하면 된다.

readonly/writeonly는 읽기/쓰기에 한정하는 지시어 일 뿐이고, 사용하지 않으면 읽기/쓰기 모두가 가능하다.

(GPU의 입장에서 == 쉐이더 코드에서)

 

 

 

 

이 것을 CPU코드에서 바인딩하는 것은 다음과 같다.

glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 0, ssbo );

첫번째 파라미터는 그냥 [GL_SHADER_STORAGE_BUFFER]를 넘기고, 두번째 파리미터는 binding=0에 지정한 "0"값을 넘겨주면 된다.

 

glBindBufferBase에 GL_SHADER_STORAGE_BUFFER를 넘겨 주지만, 실제 여기서 예로든 ssbo는 GL_SHADER_STORAGE_BUFFER일 필요는 없다. 이미 만들어진 PBO를 사용해도 되고, GL_ARRAY_BUFFER나 GL_ELEMENT_BUFFER를 사용해도 된다. 단지 이 것을은 GPU에서 접근이 가능한 걍 버퍼이기에 뭘로 만들든 그냥 사용할 수가 있다. 단지 glBindBufferBase의 파라미터만 GL_SHADER_STORAGE_BUFFER로 지정하면 된다.

 

std430 vs std140

아래의 두 예제를 비교해서 보자.

layout(std140, binding = 0) buffer SSBO140 {
    float e[];
} buf140;
layout(std430, binding = 0) buffer SSBO430 {
    float e[];
} buf430;

 

쉐이더 코드만 다르게 CPU에서 다음의 코드로 데이터를 넣으면

GLfloat data[8] = {
                    1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
};
glBufferData( GL_SHADER_STORAGE_BUFFER, sizeof(GLfloat) * 8, data, GL_STREAM_DRAW );

쉐에더에서는

std140 std430
buf140[0] = 1.0
buf140[1] = 0.0
buf140[2] = 0.0
buf140[3] = 0.0
buf140[4] = 2.0
buf140[5] = 0.0
buf140[6] = 0.0
buf140[7] = 0.0

buf140[0] = 1.0
buf140[1] = 2.0
buf140[2] = 3.0
buf140[3] = 4.0
buf140[4] = 5.0
buf140[5] = 6.0
buf140[6] = 7.0
buf140[7] = 8.0

위와 같은 형태를 갖는다. 굳이 std140을 쓸 일은 그리 많지 않은 관계로 자세한 내용은 적지 않겠다.

std430은 SSBO에서만 가능하다(아마 Shader 코드에서만 처리해주면된다. binding을 "buffer"로만 해주면 되는 것 같다.)

 

 

크기알아내기

배열의 크기는 다른 쉐이더와 마찬가지로 element.length() 로 알아 낼수 있다.

 

기본값을 std430으로

필자는 사실상 std140을 쓰지 않기에 모든 ssbo를 std430으로 쓴다. layout지시어에 std430을 빼고 싶으면 shader코드 상단에 다음의 코드를 추가하면 이후 std140으로 강제로 지정하지 않는 이상 std430방식을 쓰게 된다. 원래의 기본값이 std140인 것으로 알고 있다.

layout(std430) buffer; // SSBO의 기본 layout을 std430으로 한다.

 

 

 

 

 

Replies
Reply Write