Kirino 的游戏宝藏
← 022호로 돌아가기
게임 관찰

보이지 않는 육십 프레임과 바이트 차분이 영인 대조표, Esteban Estrada가 look sheet로 《Guildhall》 왕국 전체의 최적화를 비가시적 승리로 못 박은 방식

브라우저에서 살아 있는 왕국을 돌리는 업계 기본값이 종종 「먼저 화면을 예쁘게 만들고, 끊기면 그림자를 내리고, 원경을 깎고, LOD가 렌즈 앞에서 딱 바뀌게 한 뒤, 보도자료에 모바일 최적화라고 쓴다」라면, 2026년 10월 18일 Esteban Estrada가 codestz.

저장한 글 ↗

브라우저에서 살아 있는 왕국을 돌리는 업계 기본값이 종종 「먼저 화면을 예쁘게 만들고, 끊기면 그림자를 내리고, 원경을 깎고, LOD가 렌즈 앞에서 딱 바뀌게 한 뒤, 보도자료에 모바일 최적화라고 쓴다」라면, 2026년 10월 18일 Esteban Estrada가 codestz.dev에 공개한 《Invisible Wins: 60 FPS for a Whole Kingdom, Even on a Phone》은 그 청구서를 거의 공개적으로 다시 쓰고 있다. 작품은 Guildhall. WebGL 브라우저 탭에서 돌아가며, 목표는 직설적이다. M1 Pro에서 육십을 유지하고, 전화에서도 매끄럽게. 본고는 완성품을 오래 플레이하지 않았다. 아래 장면과 숫자는 저자의 공개 실험 노트와 데모 사이트 자료에서 온다.

만이천 파일 섬은 이천팔백십삼 개의 땅 타일과 약 삼만오십이 개의 부품이 된다. 집, 나무, 울타리, 작물, 성. 그 위에 일과로 움직이는 군중, 파도가 있는 바다, 하늘, 하루를 돌며 모든 그림자를 끌고 가는 태양. 「열리면 이긴다」는 데모가 아니다. 저자가 2026년 10월 12일 결정 파일에 쓴 한 줄은 어떤 프레임레이트 슬로건보다 단단하다. Performance must not change what you see——성능은 당신이 보는 것을 바꿔서는 안 된다.

이 글을 관통하는 이미지는 look sheet이고, 「보이지 않음」이다. QA 도구 이름처럼 들린다. 제작 윤리로는 검증 가능한 약속이다. 먼저 픽셀 단위로 화면을 맞추고, 그다음에 어느 한 칼이 시간을 아꼈는지 말한다. 아낀 것은 장부에 남겨야 한다. 바뀌는 것은 비용. 렌즈 속 왕국은 바이트 일치로 남는다.

Guildhall 만이천 파일 섬의 무거운 끝

일. 기본 논리: 최적화는 화면을 바꾸는 것, 프레임만 되면 이긴다

업계 기본 논리는 깨끗하고, 그래서 게으르다.

3D 웹이 데스크톱에서 전화로 내려가면 팀은 익숙한 메뉴를 연다. 디바이스 픽셀 비를 낮추고, 그림자 거리를 줄이고, 캐스케이드를 끄고, 먼 메시를 거친 껍질로 바꾸고, 「비싸 보이는」 후처리를 반으로 깎는다. 플레이어도 같은 어휘를 배운다——「받아들일 만하다」, 「원경은 흐려도 된다」, 「전화는 중설정이면 충분하다」. 그 암묵이 「보이는 다운그레이드」를 용서받을 수 있는 업계 문법으로 만든다.

Guildhall은 그 문법에 잘 들어가지 않는다. 섬 전체 스케일, 태양과 함께 도는 주야 그림자, 군중 일정, 날씨와 쇼 프로그램, 게다가 브라우저 안에서 육십 프레임. 예산은 「왕국 전체」 체험을 향한다. 업계 직감은 거의 자동으로 「원경을 바꾸고, 그림자를 거칠게 하고, 줌에서 LOD가 튀게 한다」로 간다.

V1은 그 함정에 빠졌다. 줌 인·아웃하면 세계가 눈에 보이게 바뀐다. 물건이 튀고, 거리마다 조금 다른 모습. 그래서 어떤 성능 커밋보다 먼저 저자는 규칙을 못 박았다. 그 한 줄은 일을 훨씬 어렵게, 훨씬 좋게 만들었다. 변경은 보이지 않을 때만 센다. 「보이지 않음」을 증명하려면 숫자와 그림이 필요하다. 의견이 아니다——저자 자신의 의견도 포함해서.

분기점은 여기다. 기본은 프레임이 모자랄 때 어떤 룩을 자를지 묻는다. 그들은 화면에서는 바이트 일치로 남으면서 어떤 구현을 더 싸게 바꿔야 하는지——그리고 「확실한 승리」처럼 들리는 어떤 아이디어를 측정이 되돌릴지 묻는다.

이. 각본을 부수기: 실험실 프로토콜, 그리고 장면보다 먼저 기계

성능은 주장하기 쉽고 증명하기 어렵다. 확실한 승리처럼 들리는 아이디어는 종종 그렇지 않다. 저자는 경험 목록을 들고 들어왔다. LOD, 캐시 그림자, 더 가벼운 원거리 그림자, 먼 클러스터의 매크로 껍질, 군중을 원거리 그림자에서 빼기, 극소 그림자 투영체 컬링, 먼 값싼 물. 모두 진짜 기법이다. 하지만 목록은 추측 목록일 뿐이다. 이기는 것, 거의 비용이 없는 것, 조용히 보이는 것을 바꾸는 것이 있다.

그래서 최적화 전에 계약과 계획을 쓰고, 긴 측정과 구현을 더 큰 모델 Fable 5.1에 계획대로 맡기고, 무엇을 본선에 넣을지는 자신이 결정했다. 계약은 작업표보다 실험실 절차에 가깝다. 고정 기위: 히어로, 중경, 광각, 십구 시 삼십 분 등불, 붐비는 정오 광장, 해면 저공——사천과 만이천 파일에서 각 세 번, 다른 것은 돌리지 않는다. 장부: 변경마다 자세별 before → after를 추가; 측정 노이즈보다 작은 이득은 되돌린다. 막는 불변조건: look sheet(다섯 줌 × 여러 시각, 전후), 팝 없는 줌 스윕, 그림자가 낡거나 뛰지 않을 것. 커밋마다 기법은 하나, 각각 숫자 포함. 조용한 기계, 실기기, 인간 체크포인트: 벤치 중 다른 브라우저 없음; 실제 전화 확인; 중간에 본인이 장부와 look sheet를 봄; 끝에서 코드를 쓰지 않은 에이전트가 전부 다시 돌림.

측정이 먼저 발견한 것은 느린 셰이더가 아니었다. 기계 자체였다.

Mac GPU 타이머는 거짓말한다. ANGLE over Metal에서 `EXT_disjoint_timer_query_webgl2`는 각 커맨드 버퍼의 벽시계 시간을 읽고, 이삼십 프레임 뒤에야 해석되며, 그 자체로 프레임당 약 일점삼 밀리초를 먹는다. 타이머는 선택으로 바꿨고, 비용은 뺄셈으로 쟀다. 바쁜 탭 하나가 프레임 시간을 약 세 배로 만든다. 「조용한」 벤치에서는 각 자세가 약 삼십사 밀리초; 저자 본인 Chrome에는 살아 있는 Guildhall 탭이 열려 있었고 GPU 프로세스가 CPU의 구에서 십팔 퍼센트. 닫으면 같은 커밋이 구점칠 / 십이점이 밀리초(p50 / p95). 아무것도 퇴행하지 않았다. 전화가 노트북을 이길 수도 있다. 저자의 Xiaomi에서는 섬이 육십 이상, Mac이 더 느리게 느껴졌다——에이전트 헤드리스가 GPU를 나눠 썼기 때문이다. 이후 모든 벤치는 `ps`로 다른 브라우저를 확인하고 장부에 적는다.

그것이 경험이 주는 규칙이다. 장면을 최적화하기 전에 기계를 격리하라. 그렇지 않으면 최적화하는 것은 노이즈다.

성능 라운드 3 look sheet: 정오·최근접 줌

삼. 프레임 예산의 구체적 칼: 인스턴스, 그림자, 셰이더, 워커, 바이트

프레임이 어디로 가는지, 저자는 중요도 순으로 연다. 각 칸이 한 수업이다.

정적 부품은 모두 하나의 three.js `BatchedMesh`에 들어간다. 지오메트리는 한 세트 버퍼를 공유하고, 섬은 한 번의 multi-draw. 풀 프레임은 약 삼십오 드로우콜——그래서 당시 WebGPU로 바꿀 가치는 아직 없었다. 병목은 드로우콜이 아니다. Metal에서 진짜 비싼 것은 인스턴스 수이지 삼각형 수가 아니다. 각 패스 전에 three 배치가 모든 인스턴스를 걸어 시야추 테스트하고 드로우 리스트에 넣어 업로드한다. 약 만팔천 인스턴스에서는 그 걷기가 청구서다. 저자는 자작 `LodBatchedMesh`로 바꿨다. LOD 단계는 같은 버퍼에 살고, 단계 사이는 두 번 나열해 디더 코드로 약 이에서 이점팔 CSS 픽셀 띠에서 크로스페이드; 먼 클러스터는 하나의 껍질 인스턴스; 카메라와 인스턴스가 변하지 않으면 이전 프레임 리스트를 재사용. LOD를 끄면(`?lod=0`) 광각이 이십삼에서 이십오 밀리초 느려진다. 하지만 차이가 약 한 픽셀 이내인 곳에서만 바꾸고, 페이드한다.

그림자는 태양을 따르되 매번 섬을 다시 걷지 않아도 된다. 캐스케이드 셋. 근거리는 약 삼십 미터 폭·텍셀당 약 이점삼 센티미터, 중단 백 미터, 그리고 마을 전체. 카메라 정지 시에는 재렌더하지 않는다. 태양이 사분의 일 도(`SHADOW_TURN = 0.25°`) 돌 때마다 셋을 다시 그린다. 낮이 달리면 초당 약 삼십 회의 캐스케이드 재렌더. 장부는 예상 밖 숫자를 냈다. 낮이 달리면 히어로 시점에서 약 삼십 프레임매초를 먹는다(노트북 프로필 팔십오→오십육, 전화 백오→오십팔). 더 거친 회전각이면 덜 그리지만 그림자가 눈에 보이게 계단처럼 뛴다. 규칙은 노. 네 번째 라운드는 사이드 패스에 자체 버퍼를 줘 캐스케이드 재렌더 뒤 정지 카메라에서 약 만팔천을 다시 걷고 업로드하지 않게 했다. 다섯 번째는 마을 캐스케이드 리스트를 유지(어떤 태양각에서도 섬 전체를 덮는 박스, 인스턴스 변화까지 한 번만 구축)하고 약 삼십이 × 삼십이 컬 격자를 추가; 순회는 인스턴스 순 그대로, 리스트는 같은 순·같은 내용, 테스트가 증명한다. 낮 주행 CPU p95(CPU ×4 스로틀) 노트북 이십이점사→십구점팔 밀리초(네 번째), 히어로 십구점삼→십육점이(다섯 번째), 전화 히어로 십팔점오→십육점이; 다섯 번째 낮 주행 행은 모두 이점삼에서 삼점사 밀리초 떨어졌다. 못 한 일도 정직하게 쓴다. 태양이 도는 프레임에서는 GPU가 여전히 캐스케이드 셋을 그리며, 노트북 광각은 움직이지 않았다.

히치도 예상 밖이었다. 이십오 밀리초 넘는 프레임은 셰이더가 첫 사용에서 링크되는 한 점으로 수렴했다. ANGLE over Metal에서 일반 프로그램은 삼십에서 오십 밀리초, 무거운 것은 수백. 병렬 컴파일 확장은 Metal이 끝나기 전에 ready를 보고할 수 있다. 이 초 유예 기간을 재도 여전히 삼십오 밀리초 히치. 그래서 세계가 쓸 프로그램은 모두 로딩 커버 아래에서 링크하고, 쇼용은 고정; 배치 워밍업은 실속성 있는 한 삼각형 대용으로 바꿨다. 만이천 파일 섬에서 이십오 밀리초 초과 프레임은 십→일; reveal 전 컴파일된 프로그램은 사십→삼십이.

세계는 워커에서 짓는다. 두 번째 라운드에서 생활 계획도 워커로——계획은 바이트 단위로 같고, 테스트가 증명. 첫 프레임 뒤 최장 메인스레드 작업: 사천 파일 천구백구십구→삼백이십삼 밀리초; 만이천 삼천칠백육십칠→사백구십오. 남은 약 이점이 초 정체는 CPU도 셰이더도 아니었다. 지오메트리를 슬라이스 복사하는 동안 배치가 그려지고 있었다. 이전 프레임이 아직 그리는 버퍼에 대한 `bufferSubData`마다 ANGLE/Metal이 약 백삼십 메가를 통째로 다시 복사했다. 수정은 거의 지루하다. 지오메트리가 다 들어갈 때까지 배치를 숨기고 한 번만 업로드. 배치 구축 사점이팔에서 오점이→일점이에서 일점사 초; 섬 전체 십점이오→사점이오 초. 누구의 목록에도 없었다. 트레이스를 읽고, 한가한 GPU에서 왜 GL 호출이 기다리는지 물은 결과다.

전송에서 Vercel은 텍스트와 JS를 압축하되 `.glb`는 그대로 보낸다. 캐릭터 리그만 삼점이사 메가. 빌드는 라이브러리 GLB를 모두 gzip하고, 페이지는 `DecompressionStream`으로 푼다. 판정은 파일명이 아니라 바이트 `1f 8b`. 마을 첫 로드 구점이오→오점이사 메가; GLB 칠점이팔구→사점이공이; 리그 삼점이사→영점이팔육; 요청 사백십이→삼백십일.

두 번째 라운드 끝의 작은 별도 커밋도 각각 계측. 낮에 꺼진 등, 공유 팔레트 머티리얼, SMAA와 겹치는 캔버스 MSAA 제거, 프레임마다 쓰레기 줄이기. 상한 없음 사천 파일·히어로 p50/p95 팔점이구 / 십일점이칠→육점삼 / 팔점이; 만이천 히어로 구점이영 / 십이점사→육점삼 / 팔점삼. 프레임 시간은 대략 이십오에서 삼십오 퍼센트 감소. 그림은 같다.

세 번째 라운드는 처음 몇 초. 예전에는 마을을 열면 Studio 크롬 아래 창백한 빈 캔버스가 한순간; 지금은 인라인 부트 커버가 첫 프레임부터 그려지고 세계 자체의 커버로 페이드한다. 캡처에 첫 페인트 뒤 빈 프레임은 없다. 생활 워커는 약 일 초에 계획을 시작해 배치가 아직 짓는 동안 계획이 착지한다. 콜드, vsync 온, 세 번의 중앙값: 사천 reveal 사점이구→삼점이칠 초; 만이천 칠점이→오점이 초. reveal의 육백 밀리초는 커버 자체의 페이드. 저자는 남겼다. 페이드가 그 순간의 일부이기 때문이다.

내비 후 약 오백사 밀리초: 빈 캔버스와 부트 커버 전후

사. 화질보다 먼저 페이스, 그리고 정직한 거부 표

「매끄러움」의 마지막 수업은 어떤 화질 하락보다 먼저 페이스가 있다는 것이다. 육십·사십·삼십을 오가는 레이트는 안정된 삼십보다 나쁘게 느껴진다. 스테이지는 n번째 vsync에 그리고, 건너뛴 vsync는 그리지 않고 훅도 돌리지 않는다; 애니메이션은 모두 실제 경과 시간이므로 삼십에서도 느려지지 않는다. Auto는 나쁜 초가 두 번 연속이면 내리고, 고요한 여덟 초 뒤에만 올린다; 버티지 못하면 다음 대기를 두 배로. Settings → Motion은 Auto, Smooth 30, Max. 모의 전화(삼구공×팔사사, DPR 삼, CPU ×6)에서 삼십삼점사 / 사십일점이영→십육점이칠 / 십팔점사 밀리초. 백이십 헤르츠 패널에서 안정 육십. 삼십 리듬조차 버티지 못할 때만 품질 사다리가 한 칸씩 내려간다——`TIERS`는 전체 룩(dprFloor 일점이오, shadowMap 이공사팔, lodBias 일, 입자 이백오십육)에서 최저(영점이칠오 / 일공이사 / 이점이오 / 육십사). 전화는 한 칸 아래, 약한 GPU는 두 칸 아래부터; Auto는 이십 초의 고요 뒤 전체 룩으로 돌아온다. 저자의 M1에서는 단이 프레임을 거의 움직이지 않는다——그림자 해상도와 무관하게 섬은 GPU 바운드——그래서 사다리는 페이스 뒤에 앉는다.

매 라운드 마무리는 같다. look sheet: 같은 섬, 같은 동결 시계, 정오·십육 시 삼십·십구 시 삼십에서 다섯 줌, 전후 캡처 후 픽셀 차분. 세 번째 라운드 십오 장 모두 일치, 평균 차 영점영영영 / 이백오십오. 바이트 대 바이트. 넷·다섯에서 유일한 차이는 같은 빌드의 두 캡처 사이에 나타났다 사라지는 최근접 줌의 납작한 한 조각. 사십팔 프레임의 움직이는 태양 스트립도 바이트 일치. 그것이 문턱이다. 그림이 움직이면 최적화는 본선에 들어가지 않는다.

장부에서 가장 좋아하는 부분은 정직한 부정이다. 네 번째 라운드 목록의 「확실한 승리」 다수는 측정이 아니라고 했다. 한 텍셀 미만 그림자 투영체 컬링——그림자가 비싼 곳에 서브텍셀은 거의 없고, 두 텍셀이면 보이는 그림자 점이 사라진다——만들지 않음; 군중을 원거리 캐스케이드에서 빼기——원래 투영하지 않음, 할 일 없음; 섬 아래 물 건너뛰기 / 먼 값싼 물——두 번 만들어 쟀고 광각·히어로 불변, 진짜 원거리 물을 아끼려면 파도를 더 일찍 페이드해야 함=룩 변경——폐기; 더 가벼운 원거리 캐스케이드 소프트닝, 캐스케이드 재렌더를 프레임에 분산, 태양 회전 시 그림자 재그리기 줄이기——비용 같거나 보임——만들지 않거나 거부. 받아들인 한 가지: 만이천 파일 섬을 최광각에서 보면 노트북 프로필에서는 여전히 무겁다. 남은 레버는 모두 룩에 손댄다. 결정 파일의 메모. 「12k is too big and crazy.」 데모 규모 섬은 매끄럽다. 더 흐린 왕국을 싣기보다 그렇게 말하는 쪽을 고른다.

시리즈 주제는 여기서 더 선명해진다. 경험이 문제·규칙·수락을 정의하고, AI는 빠르게 실행한다. 타이핑의 거의는 모델이 했다. 저자는 계약과 계획을 쓰고, 긴 패스를 위임하고, 무엇을 내보낼지 정했다. 실행이 빠른 것은 규칙이 분명해서다. 경험에서 온 것은, 첫 커밋 전에 「성능은 보이지 않게」라고 쓴 것; 숫자를 믿기 전에 기계를 격리한 것; 트레이스를 읽고 버퍼 복사가 한가한 GPU에서 기다리는 이유를 물은 것; look sheet가 아니라고 할 때 자신의 아이디어를 거부하게 한 것.

맺음: 인디는 「보이지 않는」 수락에 값을 치르는 것

이 십월 십팔일 노트를 2026년 가을 브라우저 게임 현장으로 되돌리면, 먼저 치는 것은 조용한 제작 관성이다. 최적화는 화면을 바꾸는 것이라고 기본 가정하고, FPS 슬로건이 픽셀 증거보다 위에 있다고 가정하고, 「돌아가게 됐다」면 충분하다고 가정하며, 고친 뒤의 왕국이 같은지 묻는 사람은 적다. Estrada는 다른 길을 연다. 계약은 여섯 고정 자세와 세 번 반복만큼 소박해도 된다. 수락은 평균 차 영점영영영 / 이백오십오만큼 단단해도 된다. 거부 표는 「확실한 승리」를 공개적으로 못 박을 수 있다. AI는 빨라도 된다. 배를 띄울지는 사람이 정한다.

소규모 팀에게 이 재료는 특히 아프고, 특히 유용하다. 만팔천 인스턴스 BatchedMesh도 Fable 5.1도 없어도 된다. 「이미 충분하다」 목록은 반드시 있다. 평균 프레임만 보고 조용한 기계를 보지 않는 최적화. 줌에서 튀는 LOD를 「플레이어는 모른다」고 쓰는 것. FPS를 위해 계단처럼 뛰는 그림자. 아무것도 그리지 않는 변종을 컴파일한 워밍업. 전후가 같은데 본선에 「승리」로 남긴 「물 절약」. Guildhall의 공개 작업은 비용을 투박하게 쓴다. look sheet는 검게 영이 될 수 있다. 장부는 한 줄에 기법 하나면 된다. 비싼 것은 머지 전 인간 체크포인트에 값을 치르는 것, 그리고 만이천을 최광각에서 보면 여전히 미쳤다고 결정 파일에 인정하는 것이다.

실행 가능한 짧은 팁.

  • 첫 성능 커밋 전에 거부권 규칙을 써라. 어떤 룩이 변형되면 최적화는 이긴 것이 아닌가. 「보이지 않음」을 결정 파일에 넣고, 사후 변명에 넣지 마라.
  • 고정 자세·반복·조용한 기계·실기기——적어도 한 세트를 착지시켜라. 노이즈 미만 이득은 되돌려라. 체감으로 승리를 선언하지 마라.
  • 커밋마다 기법은 하나, 각각 before → after. 화면을 움직일 수 있는 컬링과 소프트닝은 먼저 look sheet나 바이트 차분. 검어지면 그다음 머지.
  • 화질 사다리보다 먼저 페이스. 육십과 삼십 사이를 펄떡거리기보다 안정된 삼십. 사다리는 삼십 자체도 버티지 못할 때만 움직이고, 어떤 「확실한 승리」를 거부했는지 기록하라.

만이천 파일, 약 삼만 개, 삼십오 드로우, 차분 영점영영영, 그리고 한 줄——「12k is too big and crazy.」 데모가 아직 돌고 코드와 장부가 공개된 사이에, 제작자는 이미 감사 기준을 못 박았다. 육십에 닿을 수 있느냐가 아니라, 육십에 닿은 뒤 당신이 보는 것이 여전히 같은 왕국이냐.

小光

출처와 더 읽기

Esteban Estrada (2026-10-18) Invisible Wins: 60 FPS for a Whole Kingdom, Even on a Phone

Esteban Estrada (2026-10-18) Feet That Don't Skate: Why 'It Looks Right' Is the Hardest Requirement

데모 https://guildhall.codestz.dev

로그인하고 읽기 기록을 남겨 보세요.

다른 기기에서도 좋아요와 저장한 글을 확인하세요.

로그인