저는 최근 OpenGL을 접하기 시작했고 OpenGL이 직접 또는 간접 렌더링을 위해 X Window System을 사용하는 방법을 연구하고 있습니다.
제가 이해한 바에 따르면 직접 렌더링에는 DRI(Direct Rendering Infrastructure)라는 것이 사용됩니다. 그런 다음 DRI는 DRM(Direct Render Manager)을 활용하여 하드웨어에 직접 액세스합니다.. (제대로 이해했는지 모르겠습니다.)
OpenGL이 필요한 이유는 무엇이며, DRM을 사용하여 하드웨어에 직접 액세스할 수 없는 이유는 무엇입니까?
답변1
OpenGL은 무엇이며 그래픽 카드는 무엇입니까?
OpenGL은 고급 3D 그래픽 라이브러리입니다.
"그래픽 카드" 또는 오히려 GPU와 통신하는 데 사용되는 반면, 많은 3D 지원 카드 간의 많은 차이점을 OpenGL의 더 간단하고 일관된 "개념 공간"으로 추상화합니다. OpenGL은 컴퓨터에 PCI가 있는지, SoC인지, 카드에 제어 레지스터가 몇 개 있는지 또는 기타 중요하지 않은 하드웨어 세부 정보는 신경 쓰지 않습니다.
최신 OpenGL 지원 GPU(예: nvidia, ati 또는 모바일 칩셋의 GPU)는 실제로 "그래픽" 카드가 아닙니다.
실제로 오늘날 최신 GPU의 실제 "그래픽 카드"는 회로의 0.01%만을 차지합니다.
사실, 최신 GPU는 매우 복잡한 "3D 그래픽 엔진"입니다. GPU 칩의 99.99%가 이 엔진일 정도로 말입니다.
또한 픽셀과 같은 낮은 수준의 기본 요소에 대해서도 이야기하지 않습니다.
예를 들어, 거의 모든 과거 GPU(약 2000년)에는 소위 "2D 블리터" 엔진, 즉 비트맵 전송을 위한 실제 물리적 회로가 있었습니다."비트 블록 전송".
SNES와 같은 구형 콘솔에는 이미지 스프라이트를 프레임 버퍼 메모리로 전송, 변환, 늘리기 및 회전(한 단계로)할 수 있는 유사한 회로(전송)가 있었습니다. 우리는 이 회로를 "로터리 스케일러"라고 부르는 경향이 있습니다. 하드웨어로 표현되었기 때문에 SNES는 8/16비트 컴퓨터에서도 쉽게 60fps의 유동적인 그래픽을 구현할 수 있었습니다(당연히 스프라이트 수는 주어진 칩 설계에 따라 제한되었습니다).
최신 GPU에는 더 이상 "2D 블리터" 또는 "로토줌머"가 없습니다. 특별히 구성된 직교 투영에서 2개의 삼각형으로 구성된 3D 쿼드를 그릴 때 GPU의 3D 엔진은 본질적으로 기존 블리터/로토줌머 작동처럼 작동할 수 있기 때문입니다. 하지만 실제 3D 회로로 처리되기 때문에 프래그먼트 셰이더, 정점 셰이더, Z 버퍼, 스텐실 버퍼, 정점 배열 등 놀라운 유연성을 갖게 됩니다. 스테로이드에 대한 슈퍼 스핀 스케일러라고 생각하십시오.
실제로 오늘날 대부분의 GPU는 화면의 수많은 인스턴스에서 복잡한 3D 모델을 완전히 자율적으로 그리고 텍스처링하고 음영 처리할 수 있을 정도로 발전했습니다. 즉, 최신 GPU는 전체 장면을 스스로 그립니다!
사실 그들은 컴퓨터 속의 컴퓨터입니다. 생각해 보세요. GPU는 실제로 (!) 모델 데이터(모서리 목록, 다각형/삼각형 목록, 텍스처, 변환 행렬, 심지어 텐서까지), 모델 변환, 원근 투영 변환 등을 이해합니다.
게다가 이 모든 것은 "프로그래밍 가능"합니다(!). 즉, "마이크로프로그램"을 작성하고 이를 카드 프로세서 명령으로 컴파일한 다음 카드 자체에 (영구적이지 않음) "업로드"할 수 있습니다.
OpenGL은 이러한 모든 작업을 프로그래밍 언어 독립적인 형식으로 표현합니다. 예를 들어 gcc나 clang(llvm)과 크게 다르지 않은 완전히 최적화된 셰이딩 언어 컴파일러가 포함되어 있지만 프래그먼트 셰이더에 대한 어셈블리 지침도 볼 수 없습니다. 단순히 필요하지 않습니다.
그러나 낮은 수준(시스템 수준)에서는 이 모든 모델 데이터와 셰이더 프로그램이 어떻게든 카드에 들어가야 합니다. 또한 카드를 "구동"해야 합니다(예: 명령을 보내십시오: 이것을 그리세요, 저것을 그리십시오, 화면을 지우십시오). 컴파일된 셰이더 프로그램 및 모델 데이터와 같은 일부 데이터는 메모리 청크 단위로 제공됩니다. 명령과 같은 다른 것들은 일반적으로 CPU에서와 마찬가지로 GPU 레지스터를 통해 작동합니다.
PCI 기술을 사용하는 PC 유형 컴퓨터에서 이 모든 작업은 일반적으로 카드의 레지스터 세트와 "메모리 창"(데이터 전송 구멍)을 PCI 공간에서 프로세서 메모리 공간으로 매핑하여 수행됩니다. 그러나 태블릿이나 휴대폰에서는 완전히 다를 수 있습니다(많은 SoC에는 PCI도 없습니다).
그래픽 프로그래머로서 당신은 이러한 세부 사항에 관심이 없습니다.
전통적인 "유닉스" "그래픽"
이제 전통적으로 "unix"(BSD, Linux, Solaris 등)는 그래픽에 관심이 없었습니다. X11 서버는 이러한 목적으로 설계된 도구입니다.
사실, 오래된 "유닉스" 시스템에는 디스플레이 드라이버라는 개념조차 없었습니다. "유닉스"는 그래픽 카드가 무엇인지조차 몰랐습니다!
그것과 그것의 핵심에 관한 한, 그것은 텔레타이프나 디스크 드라이브와는 달리 쓸모없고, 죽고, 전력을 많이 소모합니다. :).
그때 X는 어떻게 그림을 그렸는지 물어보실 수도 있겠네요.
와 함께 .
따라서 이 "구멍"을 통해 실제로 카드를 말하고 직접 "구동"하는 사람은 X입니다. 생각해보세요. 이것은 마이크로커널/FUSE와 유사한 디자인입니다.
이것은 작동합니다 ...
……한동안……
...그러나 그것은 우리가 생각했던 것만큼 효과적이지 않다는 것이 밝혀졌습니다.
처음에는 적절했지만 카드와 GUI가 더욱 복잡해지면서 이 디자인은 로드 및 사용 요구 사항을 잘 충족하지 못했습니다.
원래 디자인은 아름다웠어요:
- X를 충돌해도 시스템이 충돌하지는 않습니다.
- 프로그램은 X에게 그림을 그리도록 요청하고, X는 그들 사이의 그림을 중재하며 하드웨어를 직접 구동합니다.
- X와의 통신은 소켓을 통해 이루어지기 때문에 그리기는 네트워크를 통해서도 가능합니다(네트워크 투명성을 위해).
"지상" 상황도 간단합니다.
- 그래픽 카드에는 실제로 프레임 버퍼, 문자 생성기, 디스플레이 컨트롤러, 고급 카드인 경우 비트 버퍼 등 몇 가지 구성 요소만 있습니다.
- 응용 프로그램은 대부분 간단한 벡터 2D 그래픽을 그리기 위해 간단한 명령을 실행합니다.
- X는 그래픽 알고리즘의 "지식 포인트"입니다(이제는 GPU 회로와 셰이더 코드의 조합입니다. 둘 다 X 외부에 있습니다).
우리는 더 필요하거나 더 원한다는 것이 밝혀졌습니다…
사용자 모드의 바람직하지 않은 효과
첫째, 하드웨어를 구동하는 사용자 모드 프로그램은 대기 시간과 모든 종류의 이상한 문제를 직접적으로 발생시킵니다.
예를 들어 그래픽 카드가 인터럽트를 생성하는 경우가 있습니다. 커널 슬레이브 장치(관심하지 않음)에서 사용자 모드 X 서버 프로세스로 인터럽트를 어떻게 전파합니까?
때로는 이 인터럽트에 대한 응답이 즉각적이어야 하지만 Xorg가 현재 예약되어 있고 ssh가 현재 들어오는 트래픽을 처리하고 있다면 어떻게 될까요?
또는 또 다른 것: 무엇이든 그리려면 프로그램의 사용자 모드에서 커널 모드로 전환해야 하고, X의 사용자 모드로 전환해야 합니다(지연). 사실, 보시다시피 훨씬 더 나쁩니다.
비슷한 질문이 많이 생기기 시작했습니다.
둘째, X는 이러한 카드와 통신하기 위해 자체 드라이버가 필요합니다. 이는 커널 드라이버가 아니지만 사용자 모드 X가 하드웨어와 통신하기 위한 FUSE와 유사한 X 드라이버입니다. 좀 지저분해요.
셋째, 느리지만 확실하게 사람들은 관련성을 유지하려면 커널이 그래픽에 대해 알아야 할 사항을 발견하고 있습니다(그래픽 부팅 화면을 원할 경우 커널은 그래픽 모드 및 디스플레이 등에 대해 알아야 합니다. - 일부 장치) (예: 휴대폰)은 텍스트 모드 그래픽도 인식하지 못합니다. 그래서 커널은 어쨌든 자체 드라이버를 개발하기 시작했습니다. 그래서 어느 시점에서 모든 카드에 대한 중복 드라이버가 시작되었습니다. 하나는 X용이고 다른 하나는 커널용입니다.
넷째, 최신 "그래픽 카드"는 더 이상 그래픽 카드가 아닙니다. 프래그먼트 셰이더, 정점 셰이더, 텍스처, 정점 버퍼, 디스플레이 버퍼, 디스플레이, 모니터 등 모든 종류의 흥미로운 개체를 아는 지능형 엔진입니다.
최신 Unix 계열 멀티태스킹 운영 체제에는 여러 사용자, 서로 다른 모니터에서 동시에 실행되는 여러 응용 프로그램이 있을 수 있으며, 많은 응용 프로그램이 동시에 카드의 하드웨어 개체를 사용합니다. 즉, 계정(할당/할당 해제)을 수행해야 합니다. , 액세스 제어 및 곧.
X는 사용자 공간의 응용 프로그램 사이에서 이러한 모든 개체를 관리합니다. 하지만 X가 충돌하면 이러한 개체는 어떻게 되나요? 카드에는 계속 할당되어 있지만 시스템에서는 해당 카드에 대한 모든 청구 정보를 잃어버렸습니다. 이 문제를 해결하는 유일한 방법은 모든 것을 잊어버리고 다시 시작하도록 카드를 강제 재설정하는 것입니다.
마지막으로 X 기반 소켓 그리기가 어떻게 작동하는지 생각해 보세요. 애플리케이션이 10K 정점 3D 모델을 그리려고 한다고 가정해 보겠습니다.
- 애플리케이션은 모델 데이터를 보관할 메모리 버퍼를 준비해야 합니다.
- 모델 데이터를 올바른 형식으로 포맷해야 합니다.
- 그런 다음 유닉스 소켓(또는 미친 경우 네트워크)을 통해 X로 보냅니다.
- X는 데이터를 수신하기 위해 메모리 버퍼를 준비해야 합니다.
- 커널은 데이터를 섞는다
- X는 카드와의 통신을 준비해야 합니다.
- X는 데이터를 카드에 매핑했습니다(즉, 카드로 "전송").
- 커널은 데이터를 섞는다
- X는 작업 결과를 검색하고 패키지한 후 X 소켓을 통해 반환해야 합니다.
- 커널은 데이터를 섞는다
- 애플리케이션은 메시지로 결과를 수신해야 합니다.
- 애플리케이션은 수신된 메시지를 보고 작업이 실패했다는 알림을 받을 수 있습니다.
위의 작업은 제공되거나 실행되어야 하는 다른 작업(오디오 플레이어, SSH, 디스크 등)에 의해 선점될 수 있다는 점을 명심하세요. 게다가 이 모든 과정은 최신 OpenGL 게임이 수백만 개의 상세한 나무가 포함된 프레임을 렌더링하는 데 걸리는 시간보다 더 오래(지연) 걸릴 수 있습니다.
현대: 커널은 풍선껌을 씹고 걷는 것뿐만 아니라 밥 로스처럼 그리는 법도 동시에 배웠습니다.
따라서 KMS와 DRI라는 두 가지 새로운 기술이 도입되었습니다.
KMS - 커널 모드 설정 - 커널이 그래픽 카드와 그 기능(프레임 버퍼, 디스플레이 링크, 모니터/디스플레이 및 해상도)을 인식하도록 합니다.
DRI - 다이렉트 렌더링 인프라 - DRM(다이렉트 렌더 관리자)을 사용하여 커널을 확장하여 일부 카드가 객체(버퍼/다양한 메모리 블록(프레임 버퍼, 모델, 텍스처, 메모리 홀))를 포함할 수 있는 복잡한 3D 그래픽 엔진임을 인식합니다. " 데이터), 셰이더, 기타 등등. 또한 이에 대한 액세스도 고려합니다.
결국 커널은 시스템의 시스템 개체(프로세스, 파일, 메모리 맵)에 대한 최고의 통계를 가지고 있습니다. DRM은 이를 그래픽 개체로 확장합니다. X(또는 이를 사용하는 다른 것)가 충돌하면 커널은 해당 이벤트를 선택하고 카드 자체에서 모든 프로세스 관련 리소스를 지웁니다. 더 이상 객체 누출 및 하드 카드 재설정이 없습니다.
이러한 모든 인터페이스는 소켓(예: X) 대신 시스템 호출, 파일 설명자 및 메모리 맵을 사용하여 구현되며 필요할 때 커널로 직접 이동합니다. 따라서 인메모리 매핑 "홀"의 경우 대기 시간이 크게 줄어들고 거의 즉각적입니다.
이는 소켓을 통해 전송되는 버퍼가 아닌 시스템 호출 및 기타 커널 개체이므로 전체 하드웨어 속도에서 제로 복사 데이터 전송이 달성됩니다.
마지막으로 DRI의 경우 그리기 응용 프로그램 자체(X가 아님)가 카드와 직접 통신한다는 점을 이해해야 합니다.
그래서 "직접"이라고 부릅니다.
예를 들어, X를 실행하고 DRI 응용 프로그램(예: OpenGL 기반 응용 프로그램)을 사용하는 경우 이 창에 그려진 콘텐츠는 일반적으로 X가 아닌 DRI의 응용 프로그램별 개인 코드 경로를 통해 카드로 들어옵니다. X는 창이 X 디스플레이의 해당 위치에 매핑된 것처럼 가장합니다.
최신 OpenGL은 DRI를 기반으로 구축되었습니다. 하지만 DRI는 그림을 그리는 것이 아닙니다...
왜 DRI를 사용하지 않는가? 왜냐면 요점이 뭐죠?
OpenGL을 사용하고 있다면 이미 직접 사용하고 있는 것입니다. 이 경우에만 OpenGL 라이브러리가 드라이버 DRI의 프로세스 주소 공간에 로드됩니다.
그러나 그래픽 응용 프로그램 프로그래머에게는 DRI가 너무 낮은 수준이므로 귀찮게 할 필요가 없습니다. DRI는 액세스 권한, 할당 추적 및 정리, 그리기 컨텍스트 전환, 명령 다중화, 메모리 매핑 등과 같은 시스템 문제를 처리합니다.
DRI에는 "그림"이 많지 않습니다. 그래픽 프로그래머가 이 하위 시스템을 조작하면 어떤 이점을 얻을 수 있습니까? 말 그대로 아무것도 아닙니다.
또한 DRI는 유닉스 시스템에 특화되어 있으므로(모든 주요 BSD는 Linux의 DRI 인프라를 사용함) 유닉스용 Direct3D와 약간 비슷하지만 훨씬 낮은 수준이므로 여기에서의 Direct3D 비교는 공평하지 않습니다.
예를 들어 DRI를 직접 사용하는 또 다른 새로운 API는 Vulkan입니다. Vulkan 코드를 보면 동일한 OpenGL 코드보다 훨씬 더 복잡합니다. 이는 OpenGL보다 훨씬 낮은 수준으로 이동할 수 있기 때문입니다. 그러나 DRI는 낮은 수준에 있으며 여기에는 "그림"이 포함되지 않습니다.
내부적으로 DRI를 사용할 수 있지만 3D 그래픽과 아무 관련이 없고 비디오 디코딩의 완전한 직교 세계를 다루는 또 다른 API는 VDAPU입니다. VDAPU의 내부 구조는 애플리케이션 사용자(미디어 플레이어)와 완전히 분리되어 있습니다.
DRI는 특정 카드의 하드웨어 세부 사항과 직접적으로 관련되어 있으며 DRM 및 그래픽 메모리 관리자의 상당 부분은 애플리케이션이나 사용자 공간이 아닌 커널에 있습니다.
응용 프로그램 프로그래머나 심지어 그래픽 프로그래머라도 특정 시스템 프로그래밍 작업(예: DRI 수정)을 수행하려는 경우가 아니면 DRI 수준으로 직접 이동하는 것은 의미가 없습니다. 그렇게 할 수 있다면 오픈 소스 그래픽 드라이버를 쉽게 사용할 수 있을 것입니다. 또는 OpenGL, Vulkan 또는 VDPAU 라이브러리 자체에서.
OpenGL과 Vulkan은 그래픽 프로그래머가 수행하려는 모든 작업을 충분히 표현할 수 있는 "얇은" 크로스 플랫폼 레이어로 카드 간의 차이점을 추상화합니다.
그래픽 응용 프로그램 프로그래머는 병렬로 실행되는 다른 응용 프로그램에서 텍스처 할당이 어떻게 작동하는지 또는 어떤 텍스처가 특정 DRM 파일 설명자에 바인딩되어 있는지 신경 쓰지 않습니다.
그들이 관심을 갖는 것은 앱을 통해 원하는 방식으로 카드를 운전하는 것뿐입니다.