2P by GN⁺ | ★ favorite | 댓글 1개
  • M1은 수년간 OpenGL 4.1에 머물렀지만, Fedora Asahi Remix의 최신 M1/M2 드라이버가 OpenGL 4.6OpenGL ES 3.2를 완전 지원함
  • Asahi의 오픈소스 Linux 드라이버는 벤더의 비호환 4.1 드라이버와 달리 Khronos 목록에 오른 적합성 인증 드라이버로, Blender 같은 현대 OpenGL 워크로드 호환성을 넓힘
  • OpenGL 4.6은 robustness, SPIR-V, clip control, cull distance, compute shaders, 향상된 transform feedback을 요구하지만, M1 하드웨어는 최신 그래픽 표준에 그대로 맞지 않음
  • 하드웨어에 없는 기능은 드라이버와 컴파일러 기법으로 보완됨: geometry shaders, tessellation, transform feedback은 compute shaders로, cull distance와 clip control은 shader 변환으로 처리함
  • 100,000개 이상의 적합성 테스트를 통과하기 위해 버퍼와 이미지 robustness를 소프트웨어로 구현하고, clamp·preamble 최적화·mipmap 우회로 추가 비용을 줄임

Fedora Asahi Remix에서 OpenGL 4.6/ES 3.2 사용 가능

  • M1은 그동안 OpenGL 4.1까지만 지원했지만, 이제 OpenGL 4.6OpenGL ES 3.2를 지원함
  • 최신 M1/M2 시리즈 드라이버는 Fedora Asahi Remix 설치로 사용할 수 있음
  • 이미 설치한 사용자는 다음 명령으로 업데이트함
    • dnf upgrade --refresh
  • Asahi의 오픈소스 Mesa 드라이버는 벤더의 비호환 4.1 드라이버와 달리 최신 OpenGL 버전에 대한 적합성(conformance) 을 갖춤
  • 적합한 4.6/3.2 드라이버는 정확성을 보장하기 위해 100,000개 이상의 테스트를 통과해야 함
  • 6개월 전 OpenGL ES 3.1 드라이버로 M1의 표준 그래픽 API용 첫 적합 드라이버가 나온 데 이어, 이번에는 OpenGL 4.6까지 완료됨
  • Vulkan 지원도 진행 중임

OpenGL 4.1 장벽을 넘기 위한 기능 구현

  • OpenGL 4.6은 4.1 대비 여러 필수 기능을 추가함
    • Robustness
    • SPIR-V
    • Clip control
    • Cull distance
    • Compute shaders
    • 업그레이드된 transform feedback
  • M1은 OpenGL ES 3.1보다 최신 그래픽 표준에 잘 맞지 않음
    • Vulkan은 일부 기능을 선택 사항으로 만들지만, DirectX와 OpenGL을 그 위에 계층화하려면 빠진 기능이 필요함
    • M1의 기존 솔루션은 OpenGL 4.1 기능 세트를 넘지 못함
  • 하드웨어 지원이 없는 새 기능은 드라이버 기법으로 구현됨
    • Geometry shaders, tessellation, transform feedback은 compute shaders로 처리함
    • Cull distance는 변환된 보간 값으로 다룸
    • Clip control은 vertex shader epilogue로 구현함

버퍼 robustness와 M1의 소프트웨어 보정

  • GPU는 전통적으로 안전성보다 성능을 우선시해, shader가 버퍼 범위를 벗어나 읽는 잘못된 코드에서 정의되지 않은 동작이 발생할 수 있음
  • 웹 브라우저처럼 신뢰할 수 없는 shader를 다루는 애플리케이션에는 이런 절충이 바람직하지 않음
    • 그래픽 API 자체가 보안 경계는 아니므로 일부 sanitization은 필요함
    • API의 정의되지 않은 동작을 줄이면 방어 심층화에 도움이 됨
  • Robustness를 켜면 애플리케이션은 일부 성능을 포기하는 대신 범위 밖 접근에 대해 정의된 동작을 선택할 수 있음
  • API별 범위 밖 버퍼 로드 결과는 다름
    • Direct3D와 Vulkan robustBufferAccess2: 0 반환
    • OpenGL과 Vulkan robustBufferAccess: 0 또는 버퍼 안의 일부 데이터 반환
    • OpenGL ES: 임의 값 가능, 단 크래시는 안 됨
  • OpenGL 요구사항은 범위 밖 접근에서 0 또는 버퍼 내 데이터를 반환하는 것이므로, 마지막 유효 인덱스와 접근 인덱스의 unsigned minimum을 계산해 안전한 인덱스로 로드할 수 있음
    • robustness 없는 uniform buffer 로드: load.i32 result, buffer, index
    • robustness 적용 후: umin idx, index, lastload.i32 result, buffer, idx
  • M1의 preamble은 모든 thread에서 같은 값을 반복 계산하지 않고 한 번 계산해 재사용함
    • uniform buffer 크기는 고정되어 있어 추가 robustness 산술도 preamble로 이동할 수 있음
    • robust storage buffer에서도 load/store 자체가 이동하지 못하더라도 clamp 계산은 preamble로 옮길 수 있음

Vertex buffer robustness 구현

  • 그래픽 API에서 애플리케이션은 vertex buffer의 GPU base address와 attribute layout을 설정함
    • 각 attribute는 offset과 format을 가짐
    • buffer는 vertex당 바이트 수를 나타내는 stride를 가짐
    • vertex shader는 vertex 기준으로 attribute를 암묵적으로 인덱싱해 읽음
  • 일부 하드웨어는 robust vertex fetch를 네이티브로 구현하거나 bounds-checked buffer로 소프트웨어 fetch를 가속하지만, M1에는 둘 다 없음
  • M1 GPU 메모리 로드는 64비트 base address와 element 단위 offset을 받으며, imad 정수 multiply-add 명령도 제공함
    • 32비트 attribute 로드는 imad idx, stride/4, vertex, offset/4load.i32 result, base, idx 두 명령으로 구현 가능함
    • 4개의 32비트 값이 조밀하게 배치된 vector attribute는 load.v4i32 result, base, vertex << 2 한 명령으로 로드할 수 있음
  • Robustness에는 clamp가 필요하지만, vertex buffer 크기는 byte 단위이고 최적화된 load는 vertex 단위 index를 사용함
  • 한 buffer 안의 여러 attribute와 offset을 attribute별 별도 base address처럼 재해석해 문제를 풀음
    • offset을 shader에서 더하는 대신 attribute별 base를 전달함
    • byte 단위 buffer 크기를 각 attribute에 대한 vertex 단위 크기로 변환할 수 있음
    • offset이 아니라 vertex index를 clamp함
  • driver는 각 attribute format의 크기를 이용해 마지막 유효 vertex index를 미리 계산해 shader에 전달함
  • buffer가 아무것도 로드할 수 없을 만큼 작으면 clamp로 해결되지 않으므로, 해당 attribute의 buffer를 작은 zero buffer로 바꿈
    • attribute별 base address를 쓰기 때문에 이 판단도 attribute별로 가능함
  • 최종적으로 약간의 driver 계산과 umin 한 개 비용으로 robust vertex buffer를 구현함

Image robustness와 mipmap 우회

  • Buffer robustness 외에도 image robustness가 필요하며, 범위 밖 image load는 0을 반환해야 함
  • Mipmapped image는 여러 level of detail을 포함함
    • base level은 원본 이미지임
    • 각 다음 level은 이전 level을 축소한 이미지임
    • 렌더링 시 하드웨어는 화면 크기에 가까운 level을 선택해 효율과 시각 품질을 개선함
  • 명세는 robustness에서 image load가 다음 경우 0을 반환해야 한다고 요구함
    • X 또는 Y 좌표가 범위 밖일 때
    • level이 범위 밖일 때
  • M1 GPU의 image load 동작은 요구사항과 다름
    • X 또는 Y 좌표가 범위 밖이면 0을 반환함
    • level이 범위 밖이면 마지막 level의 값을 반환함
  • 벤더가 하드웨어 문서를 공개하지 않아 이 동작이 의도인지 하드웨어 버그인지는 알 수 없고, 적합성 통과를 위해 우회가 필요함
  • 단순 우회는 level이 유효할 때만 load하고 아니면 0을 반환하는 branch 방식이지만, branch는 비효율적임
  • 더 나은 방식은 범위 밖 level에서도 load가 크래시하지 않는 점을 이용해 먼저 load하고 compare-select로 0을 고르는 것임
    • 하지만 M1 GPU의 명령 집합은 scalar이고, image load는 red/green/blue/alpha 4개 component vector를 반환함
    • component마다 ulesel이 필요해 assembly가 커짐
  • 최종 우회는 X 또는 Y가 범위 밖이면 하드웨어가 0을 반환한다는 점을 이용함
    • 최대 image 폭이 16384px이므로, X를 20000 같은 값으로 바꾸면 범위 밖이 됨
    • level이 유효하면 원래 X를 쓰고, 유효하지 않으면 X를 20000으로 바꿔 image load가 0을 반환하게 함
  • 이 방식은 vector 전체를 선택하지 않고 scalar 하나만 바꾸므로 compact한 assembly로 컴파일됨
    • 상수를 uniform register에 미리 올리면 우회 비용은 단일 명령이 됨
    • 이 방식으로 conformance를 통과함

댓글과 토론

Hacker News 의견들
  • Alyssa Rosenzweig는 커뮤니티에 계속 기여하는 큰 선물 같음
    블로그 글마다 현대 그래픽 하드웨어 내부 구조에 대해 몰랐던 것을 반드시 배우게 됨

  • 말보다 실력이 매번 이긴다는 걸 보여주는 작업임
    블로그만 읽어도 머리가 뜨거워질 정도로 풀어볼 내용이 많고, 결론은 마지막 문장이 아니라 두 번째 문장에 있는데도 결국 비트 조작 하나하나를 따라 토끼굴로 들어가게 됨
    문단당 깨달음 수 같은 벤치마크가 있다면 Alyssa가 전부 1등일 것 같음

  • 언젠가 Apple이 OpenGL 3.3 core를 폐기하면 결국 모두가 따라 폐기하게 될지도 모르겠음
    일반적으로 OpenGL이 Vulkan보다 쓰기 쉽다고 들었는데, 너무 복잡한 API는 경험이 적은 개발자가 GPU를 활용하기 어렵게 만들고 진입 장벽이 되어 인디 게임 개발자를 밀어낼 수 있음
    요즘은 다들 Unity와 Unreal을 쓰니 처음부터 만들거나 다른 엔진을 쓰는 게 이상하게 보이고, Unity가 더 잠그려 한 뒤 게임 개발 쪽이 깨어나는 모습을 보는 건 흥미롭지만 짜증도 남
    게임 개발에서 오픈소스는 늘 빠듯했고, Godot가 있긴 해도 Unity와 Unreal에 진지하게 맞서기는 어려워 보임
    Godot가 충분히 가능하더라도 인디 개발자들은 Unity와 Unreal에 더 익숙해서 그대로 남을 가능성이 큼
    게임 개발의 오픈소스 상태는 때때로 절망적으로 느껴지고, 차세대 그래픽 API의 등장은 일을 더 쉽게 만들지 않음

    • OpenGL이 Vulkan보다 쉽다는 말은 꽤 타당함
      OpenGL 삼각형 렌더링 예제는 약 200줄이고, Vulkan 삼각형 렌더링 예제는 약 1000줄임
    • 개인적으로 Metal은 Vulkan보다 쓰기 쉬운 편이라고 봄
      Vulkan은 매우 유연하게 설계된 대신 편의 기능이 많지 않음
      어느 쪽이든 OpenGL은 드라이버의 직접 API로 노출하기엔 너무 고수준이었고, Vulkan 같은 저수준 API를 기반 계층으로 두고 그 위에 OpenGL 같은 것을 올리는 편이 GPU 하드웨어 동작 방식과 더 잘 맞음
      또한 모두가 Unity와 Unreal을 쓰는 것도 아님
      The Game Awards 2023의 올해의 게임 후보 6개는 전부 자체 엔진으로 만들어졌고, 인디에서도 Hades처럼 자체 엔진을 만드는 개발자가 여전히 있음
      다만 대다수는 기성 엔진을 쓰는 게 맞음
    • OpenGL은 폐기된 게 아니라 더 단순하고, Vulkan이 과한 곳에서 계속 쓰임
      필요한 기능을 모두 충족하고 상태 기반 렌더링 파이프라인을 감수할 수 있다면 새 프로젝트에서 쓰는 것도 좋은 선택임
    • macOS와 iOS에서는 이미 몇 년 전부터 OpenGL이 폐기 예정 상태임
      아직 동작은 하고 요즘은 Metal 위의 계층으로 실행되지만, macOS나 iOS용 GL 코드를 빌드하면 폐기 경고가 계속 뜸
      define으로 끌 수는 있음
    • WGPU는 Vulkan보다 쓰기 쉬운 크로스 플랫폼 API로 이 문제를 해결하려는 쪽에 가까움
      OpenGL의 문제는 GPU 동작 방식과 너무 멀어서 좋은 성능을 내기 어렵다는 데 있음
  • 이 작업 중 얼마나 많은 부분이 M1 GPU 코드에 묶여 있고, 얼마나 많은 기능-위-기능 구현이 다른 곳에서도 재사용될 수 있는지 궁금함
    Zink가 더 원시적인 Vulkan 위에서 복잡한 OpenGL 기능을 돌리는 방식과 매우 비슷해 보이지만, M1에는 아직 대상으로 삼을 Vulkan 백엔드가 없음

    • 더 일반적으로는 복잡한 OpenGL이나 Vulkan을 CPU 소프트웨어 렌더링과 하드웨어별 네이티브 가속 지원의 임의 조합 위에서 실행할 수 있음
      결국 작업량의 문제이고, 다양한 하드웨어에 재사용될 수 있음
      오래되어 잘 이해됐지만 현대 워크로드에는 단독으로 쓰기 어려운 하드웨어에도 도움이 될 수 있음
  • 이로 인한 성능 영향이 특히 macOS에서 Metal을 직접 쓰는 것과 비교해 어느 정도인지 매우 궁금함
    답은 분명 “상황에 따라 다르다”겠지만 그래도 궁금함
    글 안에 답이 있을 수도 있는데 대부분 이해하지 못했음

    • 기능을 드라이버의 컴퓨트 코드로 구현하는 것과 GPU 하드웨어 지원으로 구현하는 것 사이에 반드시 큰 차이가 있는 건 아님
      “하드웨어 지원”도 보통은 GPU 마이크로코드로 구현되고 같은 실리콘을 지나가는 경우가 많음
      어떤 기능이든 성능 병목이 될 수 있고, 실제로 해보기 전에는 어디가 막힐지 알기 어려움
    • Alyssa의 표현은 다소 이상하게 느껴짐
      Apple GPU가 지오메트리 셰이더를 네이티브로 지원하지 않는 건 맞지만, 지오메트리 셰이더는 설계가 좋지 않고 GPU 하드웨어에 잘 맞지 않음
      실제 지원한다는 하드웨어에서도 느린 것으로 알려져 있고, Nvidia가 메쉬 셰이딩을 설계한 데는 이유가 있음
      변환 피드백도 자주 거론되지만, Apple GPU는 어떤 셰이더 단계에서도 임의 메모리 위치에 쓸 수 있어서 변환 피드백이 사실상 불필요함
      핵심은 Apple이 간결한 컴퓨트 아키텍처를 구현하면서 오래된 잔재와 잘 작동하지 않는 것으로 알려진 기능을 많이 잘라냈다는 점임
      “M1이 OpenGL 4.1에 갇혔다”는 표현은 적절하지 않아 보임
      OpenGL을 오래전에 더는 따라가지 않아서 4.1 이후 어떤 기능을 말하는지는 모르겠지만, OpenGL에서 되는데 Metal에서 못 하는 기능이 있다면 매우 놀랄 것 같음
      반대로 Metal에서는 가능하지만 OpenGL에서는 전혀 불가능한 일이 많고, Metal 셰이딩 언어에 완전한 포인터가 있다는 점부터가 그 예임
  • 이건 M1의 Fedora를 위한 것임
    macOS에서도 가능해지면 놀라울 텐데, 그런 걸 만들려면 무엇이 필요할지 궁금함

    • 결국 명령 버퍼를 만들고 GPU로 보내는 구조이므로, macOS에서 그걸 할 방법이 필요함
      M1 GPU용 초기 Mesa 드라이버도 IOKit으로 macOS의 AGX 드라이버에 명령 버퍼를 보내는 방식으로 부트스트랩했음
      https://rosenzweig.io/blog/asahi-gpu-part-2.html
      https://github.com/AsahiLinux/gpu/blob/main/demo/iokit.c
      그래서 GPU의 surface를 macOS 화면에 합성할 수 있는 대상으로 넘기기 위한 Mesa 쪽 접착 코드가 조금 더 필요함
    • 개발자들에 따르면 Apple에 안정적인 공개 커널 API가 없어서 실제로는 어렵다고 함: https://social.treehouse.systems/@AsahiLinux/111930744188229065
    • 이미 MoltenVK → Vulkan → Zink 경로로 가능할 수도 있지 않을까 싶음
    • Apple은 서드파티 커널 드라이버를 금지하는 것 같음
      제대로 된 Vulkan이나 OpenGL 구현을 만들려면 GPU 처리를 담당하는 커널 쪽 짝이 필요하다고 이해하고 있음
      아마 그래서 macOS용 네이티브 Vulkan을 구현하려는 사람이 없는 듯함
      다만 Apple 드라이버 위에서 가능하다면 잘 모르겠음
    • Metal 위에 OpenGL 드라이버를 구현할 수는 있음
      하지만 최적이 아닌 레거시 API를 위해 그렇게 많은 리소스를 쓸 이유가 있는지는 모르겠음
  • 범위 밖 접근을 트랩에서 임의 데이터 반환으로 바꾸는 걸 견고성이라고 부르는 게 꽤 웃김
    그래픽 프로그래밍은 확실히 이상함

    • 그래픽 드라이버를 작성하는 관점에서는 말이 되고, 포스텔의 법칙, 즉 견고성 원칙과도 맞음
      GPU 드라이버는 망가진 애플리케이션을 실행시키거나 더 빠르게 돌리는 일이 핵심임
      기본값을 엄격하게 만든다고 깨진 코드를 출시하는 비디오 게임 업계의 구조적 문제가 고쳐지지는 않고, 사용자만 떠나게 됨
      분기가 대체로 매우 비싼 하드웨어에서는, 시스템이 경계 사례를 가장 효율적인 방식으로 조용히 처리하도록 알려주는 플래그가 유용해 보임
      프로그래머가 그런 경계 사례가 최종 렌더링 프레임에 거의 영향을 주지 않는다고 합리적으로 확신할 수 있는 유효한 사용처도 많을 것 같음
    • 견고성 검사가 없다고 범위 밖 접근이 반드시 트랩되는 건 아니어서, 여기서의 견고성은 그런 이상한 경우에도 알려진 결과를 제공한다는 의미임
      GPU가 전반적으로 트랩을 별로 좋아하지 않는다는 점과 합치면 말이 됨
      Carmack도 megatexture를 설계할 때 제조사들이 가상 메모리 아이디어를 받아들이게 하는 게 고통스러웠다고 말한 적이 있음
    • 그래서 그래픽, 고성능 컴퓨팅, 고에너지 물리, 초단타 매매 영역에서는 C와 C++ 의 앞날이 여전히 밝음
      “성능이 안전보다 우선”인 문화가 지배하는 분야에서 다른 프로그래밍 언어를 이야기하는 건 벽에 대고 말하는 것과 비슷함
  • 분명 매우 흥미로운 일이지만, 왜 Vulkan을 먼저 목표로 하지 않았는지 궁금함
    요즘 더 중요한 대상처럼 보이고, 그 위에는 이미 OpenGL 구현도 있음

    • Vulkan 위 OpenGL 호환 계층이 마법은 아님
      특정 OpenGL 기능을 지원하려면 Vulkan 드라이버가 그에 해당하는 기능을 지원해야 하고, 보통은 확장이 필요함
      즉 기본 Vulkan 드라이버만 구현했다고 OGL 4.6 지원이 공짜로 생기지 않으며, Mesa가 OGL 4.6을 Vulkan으로 번역하게 하려면 Vulkan 드라이버에 OGL 4.6 기능을 전부 구현해야 함
      게다가 Alyssa는 역공학과 OpenGL 드라이버 프로젝트를 이미 해본 사람임
      자세한 사정은 모르지만 익숙한 API용 드라이버를 만드는 쪽이 익숙하지 않은 API용 드라이버보다 훨씬 쉽고 빠를 가능성이 큼
    • 먼저 오래된 OpenGL을 목표로 기본 기능 세트를 동작하게 만들었음
      거기서 더 최신 OpenGL까지 올리는 일이 완전한 Vulkan 구현보다 적은 작업이었을 가능성이 크고, Vulkan에 필요한 것들도 많이 배웠을 것 같음
    • 비슷한 생각을 했지만, OpenGL을 Vulkan 위에서 지원하려면 어차피 더 높은 버전의 Vulkan이 필요하고 작업량도 큼
      그래서 더 빨리 동작하는 무언가를 얻기 위해 낮은 버전의 OpenGL부터 선택한 것으로 보임
  • 90년대에 John Carmack이 Quake II에서 OpenGL을 쓰는 데 집착하지 않았다면, 3D 게임에서 OpenGL이 존재감을 갖지 못했을 수도 있다고 생각하면 꽤 놀라움

    • Quake는 역사에서 아마 작은 일부일 뿐임
      OpenGL을 결국 그 모습으로 만든 건 SGI와, 여러 시스템과 아키텍처에서 호환 구현을 만들기 위한 거대한 노력임
    • 재미있는 사실로, 가장 오래 보관된 OpenGL 사이트는 애니메이션 Quake 1 그래픽과 다른 메뉴가 있는 큼지막한 FAST GAMES GRAPHICS 배너였음 :-P
      https://web.archive.org/web/19970707113513/http://www.opengl.org/
    • 유일한 이유였는지는 모르겠지만, Carmack이 OpenGL을 밀어준 건 분명 도움이 됨
      3D 게임과 관련된 많은 것들이 Doom과 Quake 덕분임
    • 몇 년 뒤인 2011년에 John Carmack은 Direct3D가 당시에는 더 나은 API라고 말했음
      Microsoft는 API 개선을 위해 큰 비호환 변경을 계속할 용기가 있었고, OpenGL은 호환성 우려 때문에 발목이 잡혔다고 봤음
      Direct3D는 멀티스레딩을 더 잘 다루고, 최신 버전은 상태 관리도 더 낫다고 했음
      그래도 id Software가 OpenGL에 남아 있는 건 관성 때문이며, 장점에도 불구하고 Direct3D로 옮길 계획은 없다고 말함
      출처: https://www.bit-tech.net/news/gaming/pc/carmack-directx-better-opengl/1/
    • 맥락 자료: https://www.chrishecker.com/OpenGL/Press_Release