단순히 문서 해석과 궁금한 점을 덧붙여 작성하였습니다.
시리즈는 아래와 같습니다.
Framework Concepts
Framework Concepts - GPU
Overview
Mediapipe는 GPU 계산과 렌더링을 위한 calculator nodes 를 지원하고, CPU 기반의 calculator nodes 와 함께 그들을 잘 섞는 만큼 여러 개의 GPU nodes 를 합치는 것을 허용합니다. 모바일 플랫폼에서의 여러 GPU APIs 가 존재합니다.(예를 들어 OpenGL ES, Metal 과 Vulkan) Mediapipe 는 하나의 cross-API GPU 추상화를 제공하는 것을 시도하지 않습니다. 각 nodes는 다른 APIs를 사용하여 쓰여질 수 있고, 필요할 때 플랫폼에 특화된 특징들의 장점을 허용합니다.
GPU 지원은 모바일 플랫폼에서 좋은 퍼포먼스를 위해 필수적입니다. 특히나 실시간 비디오를 위해서 말입니다. Mediapipe 는 이후에 나올 GPU의 사용을 지원하는 GPU 와 호환되는 calculators를 개발자들이 쓸 수 있도록 합니다:
- on-device real-time processing, batch processing 뿐만이 아닌
- 비디오 랜더링과 효과들, 분석뿐만이 아닌
아래는 Mediapipe에서 GPU 지원을 위한 설계 원칙들입니다.
- GPU 기반의 calculators는 그래프 내의 어디서든지 발생할 수 있어야 하고, on-screen 랜더링을 위한 사용이 어쩔수 없지 않아야 합니다.
- GPU 기반의 calculator 하나로부터 다른 곳까지의 프레임 데이터의 전송은 빨라야 하며, 비싼 복사 연산이 발생하지 않아야 합니다.
- CPU 와 GPU 사이의 프레임 데이터의 전송은 플랫폼이 허용하는 한 효율적이어야 합니다.
- 왜냐하면 다른 플랫폼은 최고의 퍼포먼스를 위해 다른 기술을 필요로 할 수 있기 때문입니다, 그 API는 뒤에서 구현된 것들의 방식대로 유연성을 허용해야 합니다.
- calculator는 그것의 연산의 모든 부분을 위하여 GPU를 사용하는 것 내에서 최대 유연성을 허용해야 하며, 만약 필요할 경우 CPU와 함께 조합할 수 있어야 합니다.
OpenGL ES Support
Mediapipe 는 OpenGL ES 버전 3.2 를 안드로이드/리눅스에서 지원하고, ES 3.0을 iOS에서 지원합니다. 더불어, Mediapipe는 또한 iOS에서 Metal을 지원합니다.
머신 러닝 인퍼런스 calculators와 graphs를 실행하기 위한 OpenGL ES 3.1 혹은 이상 버전이 필요합니다.(안드로이드/리눅스 시스템에서)
Mediapipe는 여러 개의 GL contexts 에서 OpenGL을 실행하기 위한 graphs를 허용합니다. 예를 들어, 빠른 GPU rendering path (예를 들면 30FPS 에서) 와 느린 GPU inference path (예를 들어 10FPS 에서)를 조합한 그래프 내에서 이것은 매우 유용할 수 있습니다: 왜냐하면 하나의 GL context 는 하나의 연속적인 명령 큐에 부합하기 때문에, 두 개의 tasks를 위한 같은 context를 사용하는 것은 frame rate를 랜더링 하는 것을 줄일 수 있습니다.
Mediapipe가 여러개의 contexts를 사용하는 것으로 해결할 수 있는 과제 중 하나는 contexts 간에 통신할 수 있는 능력입니다. 시나리오 예시는 rendering과 inferences paths 모두에 전송된 input video와 함께하는 것이며, rendering은 inference로부터 최근 ouput에 접근할 수 있어야 합니다.
OpenGL context는 동시에 여러 쓰레드에 의해 접근될 수 없습니다. 더 나아가, 활성화된 GL context를 같은 쓰레드 안에서 스위칭하는 것은 일부 안드로이드 기기에서 느려질 수 있습니다. 그래서, 우리의 접근은 context당 하나의 전용 쓰레드를 가지는 것입니다. 각 쓰레드는 GL 명령들을 발생시키고, 그것의 context에서 여러 개의 명령 큐를 구축하고, 그러면 그것은 GPU 비동기에 의해 실행됩니다.
Life of a GPU Calculator
이번 섹션은 GISimpleCalculator 기본 클래스로부터 나온 GPU calculator의 Process method의 기본 구조를 나타냅니다. GPU calculator Calculator 는 예제로 보여집니다. 메소드 LuminanceCalculator::GlRender 는 GlSimpleCalculator::Process로부터 호출됩니다.
// Converts RGB images into luminance images, still stored in RGB format.
// See GlSimpleCalculator for inputs, outputs and input side packets.
class LuminanceCalculator : public GlSimpleCalculator {
public:
absl::Status GlSetup() override;
absl::Status GlRender(const GlTexture& src,
const GlTexture& dst) override;
absl::Status GlTeardown() override;
private:
GLuint program_ = 0;
GLint frame_;
};
REGISTER_CALCULATOR(LuminanceCalculator);
absl::Status LuminanceCalculator::GlRender(const GlTexture& src,
const GlTexture& dst) {
static const GLfloat square_vertices[] = {
-1.0f, -1.0f, // bottom left
1.0f, -1.0f, // bottom right
-1.0f, 1.0f, // top left
1.0f, 1.0f, // top right
};
static const GLfloat texture_vertices[] = {
0.0f, 0.0f, // bottom left
1.0f, 0.0f, // bottom right
0.0f, 1.0f, // top left
1.0f, 1.0f, // top right
};
// program
glUseProgram(program_);
glUniform1i(frame_, 1);
// vertex storage
GLuint vbo[2];
glGenBuffers(2, vbo);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// vbo 0
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), square_vertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, nullptr);
// vbo 1
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), texture_vertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(ATTRIB_TEXTURE_POSITION);
glVertexAttribPointer(ATTRIB_TEXTURE_POSITION, 2, GL_FLOAT, 0, 0, nullptr);
// draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// cleanup
glDisableVertexAttribArray(ATTRIB_VERTEX);
glDisableVertexAttribArray(ATTRIB_TEXTURE_POSITION);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(2, vbo);
return absl::OkStatus();
}
위에 언급된 설계 원칙은 뒤에 따라오는 Mediapipe GPU 지원을 위한 설계 선택들에 대한 결과입니다:
- 우리는 GPU data type을 가지고 있고, 그것은 GpuBuffer라고 불리며, image data를 나타내기 위함이며, GPU 사용을 위해 최적화되었습니다. 이 데이터 타입의 정확한 contents 는 불투명하고 플랫폼에 특정됩니다.
- low-level API는 composition에 기반하며, 이 composition은 GPU를 사용하려는 모든 calculator가 만들어내거나, GlCalculatorHelper 클래스의 인스턴스를 소유하도록 만들길 원하는 곳입니다. 이 클래스는 OpenGL context를 관리하기 위해 inputs 과 outputs을 위해 textures를 구성하면서 플랫폼에 구애받지 않는 API를 제공합니다.
- high-level API는 subclassing에 기반하며, 이 subclassing은 GlSimpleCalculator로부터 subclass를 필터링하는 이미지를 구현하는 간단한 calculators이고, superclass가 모든 plumbling을 관리하기 때문에 그들의 특정한 OpenGL code와 같이 쓰는 몇 개의 가상 메소드만을 오버라이드 할 필요가 있습니다.
- 모든 GPU 기반의 calculators 사이에서 공유되어야 하는 Data는 외부의 input으로써 제공됩니다. 이 외부 input은 그래프 서비스로써 구현되고 GlCalculatorHelper 클래스로부터 관리됩니다.
- Calculator 에 특정한 도우미들과 공유된 그래프 서비스의 조합은 우리에게 GPU 리소스를 관리하는 것에 있어 굉장한 유연성을 허용합니다: 우리는 calculator 당 분리된 context를 가질 수 있고, single context 를 나눌 수 있고, 잠금 혹은 다른 동기화 기본요소들을 나눌 수 있습니다, 등등 - 그리고 이것 모두는 helper에 의해 관리되고, 개별적인 calculators로부터 숨겨집니다.
GpuBuffer to ImageFrame Converters
우리는 GpuBufferToImageFrameCalculator 와 ImageFrameToGpuBufferCalculator라고 불리는 두 개의 calcualtors를 제공합니다. 이 calculators는 GPU와 CPU calculators를 결합하는 그래프의 구조를 허용하면서 ImageFrame과 GpuBuffer 사이에서 변환합니다. 그들은 iOS 와 Android 모두 지원합니다.
가능하면, 이 calculators 가 플랫폼에 특정한 기능성으로 CPU와 GPU 사이의 데이터를 공유하는 것을 복사 없이 사용합니다.
아래의 다이어그램은 카메라로부터 비디오를 캡쳐하고, 미디어 파이프 그래프로 통과시켜 실행하고, 결과로 스크린데 실시간으로 랜더하는 모바일 애플리케이션의 데이터 흐름을 보여줍니다. 파선(dashed line)은 어떤 파트가 미디어 파이프 내부에 적절히 있는지를 나타냅니다. 이 애플리케이션은 OpenCV를 사용하여 CPU에서 Canny edge-detection 필터를 실행하고, GPU를 사용하여 기존 비디오의 위에 덮어씌웁니다.
카메라로부터 얻은 비디오 프레임들은 GpuBuffer packets으로 그래프에 들어가게 됩니다. input stream은 평행한 두 개의 calculators에 의해 접근됩니다. GpuBufferToImageFrameCalculator는 ImageFrame으로 버퍼를 변환시키고, ImageFrame은 그레이스케일 컨버터를 통해 전달되고 canny filter(둘 다 OpenCV 기반에 CPU에서 실행된다) 의 output은 GpuBuffer로 다시 변환됩니다. 여러개의 인푸을 가진 GPU calculator, GlOverlayCalculator 는 원래 GpuBuffer와 edge detector 로부터 나오는 하나 이렇게 두 가지 input으로써 여겨지고, shader를 사용해서 그들을 덮어씌웁니다. 이 결과물은 callback calculator를 사용하면서 어플리케이션으로 되돌아가게 되고, OpenGL을 사용해서 애플리케이션은 이미지를 스크린으로 랜더합니다.
References
https://google.github.io/mediapipe/framework_concepts/gpu.html
'개발 > mediapipe' 카테고리의 다른 글
Framework Concepts (0) | 2022.03.30 |
---|---|
Framework Concepts - Real-time Streams (0) | 2022.03.22 |
Framework Concepts - Synchronization (0) | 2022.03.14 |
빌드 실행 모음 (0) | 2022.03.06 |
Framework Concepts - Packets (0) | 2022.03.05 |