Android Vulkan에서 HISM, ISM의 인스턴싱 문제가 있습니다

[Image Removed]

Android Vulkan에서 HISM, ISM의 인스턴싱 문제가 있습니다

인스턴싱이 인스턴싱 컴포넌트(HISM, ISM)가 한번에 64개까지밖에 렌더링을 못하고 최대 64개의 obj단위로 쪼개지는 현상인데,

UE4.23~4.27까지는 분명 이러한 제약이 없었던것으로 기억합니다(다른 제약으로 1000개까지만 되어서 그렇게 쪼개서 사용하긴했지만요)

​GPT답변으로는 특정 모바일(Mali 계열의 64KB 버퍼 한계) 칩셋의 제약으로 유니폼 버퍼를 이용해서 인스턴싱할때의 제약이며

r.Mobile.UseGPUSceneTexture=1 명령어로 해결가능하다고 안내되는데, 이 콘솔자체가 4.27까지만 존재하는듯하네요..

어떻게 해결가능할지 방법이 뾰족히 보이지않아서 문의드립니다, 언리얼 이전버전 처럼 인스턴싱 하려면 어떻게 해야할지 가이드가 필요합니다

재현 방법
순정버전 UE5.5에서 HISM 혹은 ISM 컴포넌트에 인스턴스를 3000개 넣고 android vulkan 패키징을 합니다

RenderDoc으로 모바일 렌더링 상황을 캡쳐해서 디버깅

안녕하세요.

질문해주신 사항에 대해 확인 후 답변드리겠습니다.

감사합니다.

안녕하세요.

UE5에서 r.Mobile.UseGPUSceneTexture는 GPUScene을 Texture2D에 보관하던 경로를 제거하고, SSBO(StructuredBuffer) 기반으로 정리하는 과정에서 함께 삭제되었습니다(CL 15415286). 관련 변경은 UE 5.4 릴리스 노트의 Mobile Renderer 섹션에서도 확인하실 수 있습니다.

https://dev.epicgames.com/documentation/en\-us/unreal\-engine/unreal\-engine\-5\.4\-release\-notes?application\_version\=5\.4\#mobilerenderer

GPUScene 프리미티브 저장소는 SSBO로 정리되었지만, 인스턴싱 배치 전달은 플랫폼에 따라 UBV 경로를 사용할 수 있습니다. UBV 경로에서는 UniformBuffer 접근 범위(UBO range) 를 넘지 않도록 배치를 쪼갭니다.

실제 코드(InstanceCullingContext.cpp)에서도 UBV 경로에서는 플랫폼의 UBO 접근 범위를 넘지 않도록 한 번의 드로우에 담을 인스턴스 수의 상한을 계산하고(MaxGenericBatchSize = PLATFORM_MAX_UNIFORM_BUFFER_RANGE / UniformViewInstanceStride[0]), 이를 초과하는 인스턴스는 여러 묶음으로 나누어 여러 개의 인다이렉트 드로우로 연달아 그립니다(FInstanceCullingContext::AddInstancesToDrawCommand). RenderDoc에서 <…, 64>처럼 보이는 값은 고정 상수가 아니라 UBO 범위 ÷ 인스턴스 stride로 얻어진 계산 결과이며, OpenGL이든 Vulkan이든 해당 플랫폼이 UBV 경로를 쓰면 동작은 동일합니다.

성능 관점에서 보면 모바일은 UBO 범위가 작기 때문에 엔진이 규칙적인 크기(예: 64) 로 페이로드를 패킹해 읽게 하는 방식이 드라이버가 내부에서 임의로 쪼개는 것보다 메모리 접근과 캐시 패턴을 예측 가능하게 만들어 평균적으로 안정적인 퍼포먼스를 내는 경우가 많습니다. 더불어 UE5.4 이후의 GPUScene 파이프라인은 인스턴스 컬링과 드로우 머지, 인다이렉트 제출을 통해 CPU 제출 오버헤드를 크게 줄이고, 보이지 않는 인스턴스를 제거하여 실제 GPU 작업량 자체를 낮추는 이점이 있습니다. 따라서 가능하다면 엔진의 기본 방향(= GPUScene + Instance Culling) 을 따르는 것을 권장드립니다.

다만 장면 특성이 항상 가시 인스턴스가 매우 많고, 머티리얼/상태가 단일이며, 셰이더가 가벼운 특수한 경우에는 GPUScene을 끄고(r.Mobile.SupportGPUScene=0) 전통적인 per-instance 스트림/구조화 버퍼 경로로 “한 번에 크게” 그리는 방식이 비슷하거나 약간 유리할 수도 있습니다. 이 경우에는 UBV 기반 배치 상한이 적용되지 않아 더 큰 배치가 가능하지만, 대신 GPUScene 기반의 인스턴스 컬링과 간접 드로우 통합 등 최적화 이점이 사라질 수 있으므로, 동일 장면에서 두 경로를 A/B 프로파일링하여 실제 GPU 시간과 드로우/인스턴스 지표로 판단해 보시길 권장드립니다.

감사합니다.

안녕하세요.

GPUScene을 켠 상태에서 ISM을 한 번에 그리게 만드는 일은 코드 수정을 하면 가능할 수 있으나, 현 구조와 강하게 충돌하기 때문에 대규모 수정 없이는 어렵습니다. 모바일 GPUScene은 CL 26354848에서 UBO 범위 기반 배치 분할·간접 드로우·인스턴스 컬링을 축으로 파이프라인이 리워크되어, 데이터 전달·셰이더 퍼뮤테이션·드로우 커맨드 구성이 레거시 경로와 근본적으로 다릅니다.

구현 가능성을 검토하실 때는 아래 코드 진입점부터 차례로 확인해 보시길 권장드립니다.

  1. FInstanceCullingContext::SetupDrawCommands — UBO 범위/stride로 배치 상한 계산
  2. AddInstancesToDrawCommand — 상한 초과 시 배치 분할 및 IndirectArgs 생성
  3. SubmitDrawCommands — 배치별 오프셋으로 연속 제출
  4. ISM Vertex Factory 컴파일 분기 — USE_INSTANCE_CULLING vs USE_INSTANCING
  5. FMeshMaterialShader::GetElementShaderBindings — Primitive UBO 금지 검사 흐름

목표가 “분할 제거”라면, GPUScene은 유지하되 UBV만 끄는 방향으로 전 패스 셰이더를 GetPrimitiveData(PrimitiveId) 기반으로 일원화하고, 퍼뮤테이션/바인딩/PSO/캐시를 모두 재정렬해야 합니다.

현실적인 권장 방향은 UE5.4 이후의 기본 경로(GPUScene + Instance Culling) 를 따르는 것입니다. 다만 GPUScene을 활성화한 채 ISM을 한 번에 그리는 기능이 반드시 필요하시다면, 위 진입점을 기준으로 수정 범위와 리스크를 충분히 검토하신 뒤 진행하시길 권합니다.

감사합니다.

감사합니다 android 기기마다 차이가 있을지는 모르겟으나 삼성 갤럭시 S25 Ultra기기에서 테스트 진행했습니다

네 자세한 답변 감사합니다, GPUScene 켜진상태로는 이전 방식의 인스턴싱 렌더링은 불가능할까요? 만약 작은 부분의 엔진커스터마이징으로 가능한 부분이라면 시도해보고싶습니다

좋은 답변 감사합니다!!