시리즈: AI와 함께 코딩 배우기

핑크빛 그래픽, 폭주하는 점수, XR 크래시. 하루 토요일에 버그 네 개.

서로 무관한 통합 버그 네 개, 한 번의 토요일.

버그 네 개, 토요일 하루 겹치는 서브시스템은 없고, 통합 실패는 하나 죽음의 핑크 셰이더 없음 폴백 색상 점수 x2 구독자 둘, 이벤트 하나 XR 크래시 디바이스 준비 전 초기화 카메라 실종 비동기 함선 스폰 경쟁 조건
각 서브시스템은 혼자서는 잘 작동했다. 통합이야말로 데모가 살고 죽는 지점이다.

각자 유닛 테스트를 통과하는 네 개 서브시스템의 버그 네 개 — 그것이 부서진 데모를 출시하게 만드는 실패 모드다. 그것을 잡아내는 것이 RakuAI에서 실제로 실행할 수 있는 샘플을 만드는 규율이다.

데모를 출시해 본 사람이라면 누구나 아는 특정 종류의 토요일이 있다. 데모는 지난 주말이 끝날 때까지 잘 작동했다. 이번 토요일 아침에 다시 열었다. 이제 네 가지 서로 다른 방식으로 망가져 있고, 그 방식들은 서로 아무 관련이 없다.

오늘이 그날이었다. 데모는 우리가 대표적인 샘플 중 하나로 출시하는 슈터 게임이었다. 버그들은 아무도 초대하지 않은 파티 손님들처럼 기다리고 있었다.

무엇이 망가졌나

데모는 부팅됐다. 그게 좋은 소식이었다. 그 이후로는.

그래픽이 핑크였다. 실시간 렌더러에서 일해 본 사람이라면 누구나 죽음의 핑크를 안다. “셰이더 없음” 플레이스홀더 색상, 형광 핑크로, 렌더링 파이프라인의 무언가가 기대했던 셰이더를 찾지 못했다고 비명을 지른다. 데모 안의 절차적으로 생성된 모든 오브젝트가 이 색상으로 렌더링되고 있었다. 함선들. 소행성들. 파티클들. 전부 핑크. 어디를 봐도 핑크.

점수가 폭주했다. 모든 킬이 두 번씩 등록됐다. 킬 카운터가 두 개씩 짝지어 올라갔다. 적 스무 마리를 잡았는데 “점수: 40.” 마흔 마리를 잡았는데 “점수: 80.” 종이 위에서는 게임이 후해 보이지만, 실제로는 리더보드가 부풀려졌고 최고 점수 로직이 완전히 잘못된 임계값에서 발동하고 있었다.

XR 부팅이 크래시했다. 평면 화면에서 데모는 깨끗하게 부팅됐다. 헤드셋을 꽂고 XR 모드로 전환하는 순간, 하드 종료. 런처를 통해 백트레이스가 하나도 표시되지 않았다. 런처가 로그를 잡기도 전에 렌더러 스레드가 사라졌다.

카메라가 함선에 잠기지 않았다. 표준 3인칭 궤도 카메라로, 함선을 프레임 안에 유지해야 했다. 부팅 시 카메라는 월드 원점에 스폰되어 그 자리에 계속 머물렀다. 함선은 저 멀리 허공을 가로지르는 작은 점으로 보일 뿐이었다.

버그 네 개. 서브시스템 중첩은 제로. 워크플로가 제대로 갖춰졌는지 아닌지를 판가름하는 그런 종류의 토요일 아침이었다.

각각의 원인

각각을 짚고 넘어가고 싶다. 에이전트 기반 워크플로에서 코드베이스가 어떻게 자라고 부서지는지에 대해 각자 다른 교훈을 주기 때문이다.

핑크 그래픽

근본 원인은 셰이더 핸들이 렌더 호출마다 문자열 이름으로 조회되고 있었고, 연휴 동안 있었던 리팩터가 셰이더 캐시 초기화를 부팅 시퀀스의 다른 지점으로 옮겼다는 것이었다. 절차적 생성기들이 이제 셰이더 캐시가 워밍업되기 전에 렌더링되고 있었다. 그것들은 널 핸들 폴백을 돌려받고 있었고, 렌더러는 그것을 성실하게 형광 핑크로 렌더링했다.

수정은 첫 사용 시 해석된 셰이더 참조를 캐시하고, 절차적 생성기들이 부팅 순서를 신경 쓰지 않고 호출할 수 있는 동기식 GetOrCreate API를 노출하는 명시적 ShaderCache 유틸리티를 만드는 것이었다. 모든 절차적 생성기가 이것을 사용하도록 업데이트됐다. 핑크는 사라졌다.

교훈: 부팅 시퀀스 안에 암묵적으로 살고 있던 초기화 순서 가정이, 그 가정을 표시하지 않은 리팩터에 의해 깨졌다. 암묵적 순서 지정은 취약하다. 새 캐시는 순서를 명시적이고 자가 치유되게 만든다.

점수 폭주

근본 원인은 GameplayHUD가 UI 쪽에서 킬을 집계하고 있었고, ScoreManager도 시뮬레이션 쪽에서 킬을 집계하고 있었다는 것이었다. 둘 다 같은 킬 이벤트 시그널을 듣고 있었다. 둘 다 공유된 점수 필드에 더하고 있었다. 점수는 킬당 두 번씩 증가했다.

수정은 점수의 정본 소유자를 정하는 것이었다. ScoreManager가 진실을 소유한다. GameplayHUDScoreManager에서 읽어 렌더링한다. HUD 안의 중복 증가는 제거됐다.

교훈: 이벤트 기반 엔진에서, 같은 이벤트에 대한 두 구독자는 둘 다 실행된다. 둘 다 같은 상태에 쓴다면, 그 상태는 몇 명의 구독자가 썼는지에 비례해 잘못될 것이다. 수정은 누가 어떤 상태를 소유하는지 명확한 선을 긋는 것이다. 일단 그어지면 이 버그는 불가능해진다.

XR 부팅 크래시

근본 원인은 XR 서브시스템이 기저 그래픽 디바이스가 준비되기 전에 초기화를 시도하고 있었다는 것이었다. 평면 화면 부팅에서는, 사용자가 “시작”을 누를 때쯤이면 그래픽 디바이스가 항상 준비되어 있었기 때문에 순서가 마침 잘 맞아떨어졌다. XR 부팅에서는, 헤드셋 감지가 그래픽 디바이스 준비가 확인되기 전에 XR 초기화 경로를 트리거했다. XR 서브시스템이 널 디바이스 핸들을 역참조했다. 크래시.

수정은 부팅 중 가장 이른 시점에 XR을 감지하고, 그래픽 디바이스가 준비 완료를 확인할 때까지 실제 XR 초기화를 미루며, 체인 안의 무언가가 결함을 보고하면 우아하게 종료하는 XRBootstrap이었다. 크래시는 이제 깔끔한 에러 메시지가 되고 사용자는 평면 화면으로 폴백한다.

교훈: 하드 크래시는 최악의 종류의 에러다. 무슨 일이 있었는지 말해줬을 로깅을 죽여버리기 때문이다. 실패를 더 일찍 감지하고 깔끔하게 보고하는 것은 그만한 엔지니어링 비용을 치를 가치가 있다.

잠기지 않는 카메라

근본 원인은 궤도 카메라가 부팅 시 플레이어 함선을 목표로 삼으려 한다는 것이었다. 함선은 카메라가 초기화된 몇 프레임 후에 절차적 생성기에 의해 비동기적으로 스폰된다. 카메라는 아주 첫 프레임에 함선을 찾고 있었고, 아무것도 찾지 못했으며, 그 후 다시는 시도하지 않았다.

수정은 궤도 카메라에 재시도 메커니즘을 주는 것이었다. 그것은 포기하기 전 한정된 프레임 수만큼 함선을 찾고, 일단 잠기면 계속 잠긴 상태를 유지한다. 카메라는 이제 잠시 원점에 스폰된 다음, 게임플레이 첫 1초 안에 함선으로 스냅한다.

교훈: 약간씩 다른 스케줄로 초기화되는 시스템들 사이의 경쟁 조건은 여러분을 물 것이다. 수정은 영원히 루프를 돌지 않도록 한정되고, 재시도가 소진되면 유용한 에러를 만들어내는 명시적 타임아웃을 갖춘 작은 재시도다.

슈터 데모에 대해 구체적으로 배운 것

세 가지, 모두 불편한 것들이다.

각 서브시스템은 고립된 채로는 작동하고 있었다. 통합이 망가져 있었다. 렌더러는 작동했다. 점수 시스템은 작동했다. XR 서브시스템은 작동했다. 카메라는 작동했다. 통합된 경험으로서의 슈터 데모는 작동하지 않았다. 이것은 유닛 테스트가 매번 놓치는 종류의 실패다. 유닛 테스트는 본질적으로 고립된 상태에서 사물을 검증하기 때문이다.

12월 말 휴가 기간의 리팩터가 이 버그들 중 적어도 두 개의 근인이었다. 셰이더 캐시 재구성과 킬 이벤트 구독 분리 둘 다 휴가 스프린트 기간에 착지했다. 둘 다 고립된 상태에서는 올바른 PR이었다. 둘 다 눈에 띄지 않는 방식으로 데모를 망가뜨렸다. 교훈은 유닛 테스트뿐만 아니라 통합 데모도 CI의 일부로 실행하라는 것이다. 그 작업은 오늘 아침 별도의 PR로 착지했다.

XR 부팅 경로는 자체 부팅 가디언이 필요했다. 하드 크래시는 용납할 수 없다. 진단을 죽여버리기 때문이다. 사소하지 않은 하드웨어를 건드리는 모든 부팅 경로는 가디언이 필요하다. 지금 XR에 대해서는 그것이 있다. 공간 오디오나 Wi-Fi 7 링크에 대해서는 아직 없다. 둘 다 추가하는 것이 다음 두 번의 토요일 큐에 올라 있다.

파트너와 빌더가 여기서 얻어야 할 것

초기화 순서 의존성이 있는 여러 서브시스템을 통합하는 무언가를 만들고 있다면, 교훈은 슈터 데모가 방금 배운 것과 같다. 초기화 순서를 명시적으로 만들어라. 실패 모드를 우아하게 만들어라. 유닛 테스트뿐만 아니라 통합 데모도 CI에서 실행하라.

Raku 채택을 고려하는 인디 팀이라면, 슈터 데모는 우리가 모든 릴리스마다 테스트하는 실제 샘플이다. 그것이 망가지면, 여러분도 그것이 망가지는 것을 본다. 그것이 기능 목록보다 더 중요한 종류의 공개 규율 신호다.

AI 랩이 모델을 AR 경험에 꽂는 것을 고려하고 있다면, XR 부팅 경로는 이제 방어 가능한 가디언을 갖췄다. 여러분의 모델은 들어오는 길에 하드 크래시를 보지 않을 것이다. 체인 안의 무언가가 실패하면 깔끔한 에러 보고를 받게 될 것이다. 인프라는 준비되어 있다.

버그 네 개. 토요일 하루. 데모는 이제 깨끗하게 빌드된다. 파티 손님들은 배웅됐다.

다시 만드는 일로 돌아간다.

자체 샘플을 실행하는 런타임 위에 출시하라

RakuAI는 통합 CI와 우아한 XR 부팅 가디언으로 모든 릴리스마다 데모를 테스트한다. 그것이 기능 목록보다 더 중요한 공개 규율 신호다. 그 위에서 만들기 시작하라.

← 전체 글