이 페이지에서

Qt Quick 씬 그래프 기본 렌더러

이 문서는 기본 씬 그래프 렌더러가 내부적으로 어떻게 작동하는지 설명하여, 성능과 기능 면에서 모두 최적의 방식으로 이를 활용하는 코드를 작성할 수 있도록 돕습니다.

좋은 성능을 얻기 위해 렌더러의 내부 구조를 이해할 필요는 없습니다. 하지만 씬 그래프와 통합할 때나 그래픽 칩의 효율을 최대한 끌어내지 못하는 이유를 파악할 때는 도움이 될 수 있습니다.

참고: 모든 프레임이 고유하고 모든 것이 처음부터 새로 업로드되는경우에도 기본 렌더러는 양호한 성능을 보입니다.

QML 씬 내의 ` Qt Quick ` 항목들은 ` QSGNode ` 인스턴스 트리를 채웁니다. 일단 생성되면, 이 트리는 특정 프레임이 어떻게 렌더링되어야 하는지에 대한 완전한 설명을 담고 있습니다. 이 트리에는 ` Qt Quick ` 항목으로의 참조가 전혀 포함되어 있지 않으며, 대부분의 플랫폼에서 별도의 스레드에서 처리 및 렌더링됩니다. 렌더러는 씬 그래프의 독립적인 구성 요소로, QSGNode 트리를 탐색하며 QSGGeometryNode 에 정의된 지오메트리와 QSGMaterial 에 정의된 셰이더 상태를 사용하여 그래픽 상태를 업데이트하고 드로우 콜을 생성합니다.

필요한 경우, 내부 씬 그래프 백엔드 API를 사용하여 렌더러를 완전히 교체할 수 있습니다. 이는 비표준 하드웨어 기능을 활용하고자 하는 플랫폼 공급업체에게 주로 유용합니다. 대부분의 사용 사례에서는 기본 렌더러로 충분합니다.

기본 렌더러는 렌더링을 최적화하기 위해 두 가지 주요 전략, 즉 드로우 콜의 일괄 처리와 GPU 상의 지오메트리 유지에 중점을 둡니다.

배칭

QPainter, Cairo 또는 Context2D 와 같은 기존의 2D API는 프레임당 수천 개의 개별 드로우 콜을 처리하도록 작성된 반면, OpenGL 및 기타 하드웨어 가속 API는 드로우 콜의 수가 매우 적고 상태 변경이 최소한으로 유지될 때 최상의 성능을 발휘합니다.

참고: 다음 섹션에서는 OpenGL 를 예로들겠지만 , 동일한 개념이 다른 그래픽 API에도 적용됩니다.

다음 사용 사례를 고려해 보겠습니다.

John Doe와 Jane Doe 항목이 표시된 아이콘이 있는 세 개의 목록 항목

glDrawArrays 이 목록을 그리는 가장 간단한 방법은 셀 단위로 처리하는 것입니다. 먼저 배경을 그립니다. 이는 특정 색상의 직사각형입니다. OpenGL 용어로 말하자면, 이는 단색 채우기를 수행하는 셰이더 프로그램을 선택하고, 채우기 색상을 설정하며, x 및 y 오프셋을 포함하는 변환 행렬을 설정한 다음, 예를 들어 glDrawArrays 를 사용하여 직사각형을 구성하는 두 개의 삼각형을 그리는 것을 의미합니다. 다음으로 아이콘을 그립니다. OpenGL 용어로 설명하자면, 텍스처를 그리는 셰이더 프로그램을 선택하고, 사용할 활성 텍스처를 지정하며, 변환 행렬을 설정하고, 알파 블렌딩을 활성화한 뒤, 예를 들어 ` `를 사용하여 아이콘의 경계 사각형을 구성하는 두 개의 삼각형을 그리는 과정을 의미합니다. 셀 사이의 텍스트와 구분선도 비슷한 패턴을 따릅니다. 그리고 이 과정은 목록의 모든 셀에 대해 반복되므로, 목록이 길어질수록 OpenGL 상태 변경 및 드로잉 호출로 인한 오버헤드가 하드웨어 가속 API 사용이 제공할 수 있는 이점을 완전히 상쇄해 버립니다.

각 프리미티브의 크기가 클 때는 이 오버헤드가 무시할 수 있을 정도이지만, 일반적인 UI의 경우 작은 항목들이 많아져 상당한 오버헤드로 누적됩니다.

기본 씬 그래프 렌더러는 이러한 제한 사항 내에서 작동하며, 시각적 결과를 정확히 동일하게 유지하면서 개별 프리미티브를 배치로 병합하려고 시도합니다. 그 결과 OpenGL 상태 변경 횟수가 줄어들고 드로우 콜이 최소화되어 최적의 성능을 얻을 수 있습니다.

불투명 프리미티브

렌더러는 불투명 프리미티브와 알파 블렌딩이 필요한 프리미티브를 구분합니다. OpenGL의 Z-버퍼를 사용하고 각 프리미티브에 고유한 Z 위치를 부여함으로써, 렌더러는 화면상의 위치나 어떤 다른 요소와 겹치는지와 상관없이 불투명 프리미티브의 순서를 자유롭게 재정렬할 수 있습니다. 렌더러는 각 프리미티브의 머티리얼 상태를 확인하여 불투명 배치(opaque batches)를 생성합니다. Qt Quick 의 핵심 항목 집합에 따르면, 여기에는 불투명 색상을 가진 Rectangle 항목과 JPEG 또는 BMP와 같은 완전히 불투명한 이미지가 포함됩니다.

불투명 프리미티브를 사용할 때의 또 다른 이점은, 특히 모바일 및 임베디드 GPU에서 상당한 성능 부하를 유발할 수 있는 ‘ GL_BLEND ’ 기능을 활성화할 필요가 없다는 점입니다.

불투명 프리미티브는 glDepthMask 와 GL_DEPTH_TEST 가 활성화된 상태에서 앞쪽에서 뒤쪽으로 순서대로 렌더링됩니다. 내부적으로 조기 Z 검사를 수행하는 GPU의 경우, 이는 가려진 픽셀이나 픽셀 블록에 대해서는 프래그먼트 셰이더를 실행할 필요가 없음을 의미합니다. 단, 렌더러는 여전히 이러한 노드를 고려해야 하며, 해당 프리미티브 내의 모든 버텍스에 대해 버텍스 셰이더가 실행된다는 점에 유의하십시오. 따라서 애플리케이션에서 특정 요소가 완전히 가려져 있음을 알고 있다면, Item::visible 또는 Item::opacity 를 사용하여 명시적으로 숨기는 것이 가장 좋습니다.

참고: Item::z 는 아이템의 형제 노드들에 대한 중첩 순서를 제어하는 데 사용됩니다. 이는 렌더러나 OpenGL의 Z-버퍼와는 직접적인 관련이 없습니다.

알파 블렌딩된 프리미티브

불투명한 프리미티브가 그려지면, 렌더러는 glDepthMask 를 비활성화하고 GL_BLEND 를 활성화한 뒤, 모든 알파 블렌딩된 프리미티브를 뒤에서 앞으로의 순서대로 렌더링합니다.

알파 블렌딩된 프리미티브의 배치 처리는 렌더러 측에서 다소 더 많은 작업이 필요합니다. 알파 블렌딩이 올바르게 보이려면 겹치는 요소들을 올바른 순서로 렌더링해야 하기 때문입니다. Z-버퍼에만 의존하는 것만으로는 충분하지 않습니다. 렌더러는 모든 알파 블렌딩된 프리미티브를 순차적으로 처리하며, 어떤 요소를 배치할 수 있고 어떤 요소는 배치할 수 없는지 파악하기 위해 머티리얼 상태뿐만 아니라 바운딩 박스도 확인합니다.

일괄 처리 가능한 항목을 나란히 배치한 경우와 일괄 처리 불가능한 항목을 겹쳐 배치한 경우의 비교

가장 왼쪽의 경우, 텍스트는 그 앞에 겹쳐진 배경과만 겹치기 때문에 파란색 배경은 한 번의 호출로, 두 텍스트 요소는 또 다른 호출로 그려질 수 있습니다. 가장 오른쪽의 경우, “Item 4”의 배경이 “Item 3”의 텍스트와 겹치므로, 이 경우에는 배경과 텍스트 각각을 별도의 호출을 통해 그려야 합니다.

Z 축 측면에서, 알파 프리미티브는 불투명 노드와 교차 배열되어 있으며, 가능한 경우 early-z를 트리거할 수 있지만, 이 경우에도 Item::visible 를 false로 설정하는 것이 항상 더 빠릅니다.

3D 프리미티브와의 혼합

씬 그래프는 의사 3D 및 진정한 3D 프리미티브를 지원할 수 있습니다. 예를 들어, ` ShaderEffect `를 사용하여 "페이지 컬(page curl)" 효과를 구현하거나, ` QSGGeometry `와 사용자 정의 머티리얼을 사용하여 범프 맵이 적용된 토러스(torus)를 구현할 수 있습니다. 이때, 기본 렌더러가 이미 뎁스 버퍼를 사용하고 있다는 점을 고려해야 합니다.

렌더러는 QSGMaterialShader::vertexShader()에서 반환된 버텍스 셰이더를 수정하고, 모델-뷰 및 투영 행렬이 적용된 후 버텍스의 z 값을 압축한 다음, 올바른 z 위치에 배치하기 위해 z 축에 약간의 변위를 적용합니다.

이 압축은 z 값이 0에서 1 사이의 범위에 있다고 가정합니다.

텍스처 아틀라스

활성 텍스처는 고유한 OpenGL 상태이므로, 서로 다른 OpenGL 텍스처를 사용하는 여러 프리미티브를 일괄 처리할 수 없습니다. 이러한 이유로 Qt Quick 장면 그래프는 여러 QSGTexture 인스턴스를 더 큰 텍스처의 작은 하위 영역, 즉 텍스처 아틀라스로 할당할 수 있도록 합니다.

텍스처 아틀라스의 가장 큰 장점은 여러 QSGTexture 인스턴스가 이제 동일한 OpenGL 텍스처 인스턴스를 참조한다는 점입니다. 이를 통해 Image 항목, BorderImage 항목, ShaderEffect 항목은 물론, QSGSimpleTextureNode 및 텍스처를 사용하는 사용자 정의 QSGGeometryNodes와 같은 C++ 유형에 대한 텍스처가 적용된 드로우 콜도 일괄 처리할 수 있습니다.

참고: 대용량 텍스처는 텍스처 아틀라스에 포함되지 않습니다.

아틀라스 기반 텍스처는 ` QQuickWindow::createTextureFromImage()`에 ` QQuickWindow::TextureCanUseAtlas `를 전달하여 생성됩니다.

참고: 아틀라스 기반 텍스처는 0에서 1 사이의 텍스처 좌표를 가지지 않습니다. QSGTexture::normalizedTextureSubRect()를 사용하여 아틀라스 텍스처 좌표를 가져오십시오.

씬 그래프는 휴리스틱을 사용하여 아틀라스의 크기와 아틀라스에 포함될 텍스처의 크기 임계값을 결정합니다. 다른 값이 필요한 경우, 환경 변수 QSG_ATLAS_WIDTH=[width], QSG_ATLAS_HEIGHT=[height] 및 QSG_ATLAS_SIZE_LIMIT=[size] 을 사용하여 이를 재정의할 수 있습니다. 이러한 값을 변경하는 것은 주로 플랫폼 공급업체에게 유용할 것입니다.

배치 루트

호환되는 프리미티브를 배치로 병합하는 것 외에도, 기본 렌더러는 매 프레임마다 GPU로 전송해야 하는 데이터의 양을 최소화하려고 시도합니다. 기본 렌더러는 서로 연관된 서브트리를 식별하여 이를 별도의 배치에 포함시키려 합니다. 배치가 식별되면, 버텍스 버퍼 오브젝트(Vertex Buffer Objects)를 사용하여 병합, 업로드 및 GPU 메모리에 저장됩니다.

변환 노드

각 변환 노드( Qt Quick ) 항목은 x, y, 스케일 또는 회전을 관리하기 위해 쉐이더 변환( QSGTransformNode )을 씬 그래프 트리에 삽입합니다. 자식 항목들은 이 변환 노드 아래에 배치됩니다. 기본 렌더러는 프레임 간 변환 노드의 상태를 추적하며, 서브트리를 검토하여 특정 변환 노드가 일련의 배치에 대한 루트가 될 적합한 후보인지 판단합니다. 프레임 간에 상태가 변경되고 비교적 복잡한 서브트리를 가진 변환 노드는 배치 루트가 될 수 있습니다.

배치 루트의 서브트리에 있는 QSGGeometryNodes는 CPU에서 루트를 기준으로 미리 변환됩니다. 그런 다음 GPU로 업로드되어 유지됩니다. 변환이 변경될 때 렌더러는 개별 항목마다 업데이트할 필요 없이 루트의 행렬만 업데이트하면 되므로, 목록 및 그리드 스크롤링이 매우 빠르게 이루어집니다. 연속된 프레임의 경우, 노드가 추가되거나 제거되지 않는 한 목록 렌더링은 사실상 비용이 들지 않습니다. 새로운 콘텐츠가 서브트리에 들어오면 이를 포함하는 배치가 재구성되지만, 이 과정은 여전히 비교적 빠릅니다. 그리드나 목록을 팽잉할 때 노드가 추가되거나 제거되는 프레임마다 대개 여러 개의 변경되지 않은 프레임이 존재합니다.

변환 노드를 배치 루트로 식별하는 또 다른 이점은 렌더러가 변경되지 않은 트리의 부분을 유지할 수 있게 해준다는 점입니다. 예를 들어, UI가 목록과 버튼 행으로 구성되어 있다고 가정해 봅시다. 목록이 스크롤되면서 델리게이트가 추가되거나 제거될 때, UI의 나머지 부분인 버튼 행은 변경되지 않으므로 GPU에 이미 저장된 지오메트리를 사용하여 그릴 수 있습니다.

변환 노드가 배치 루트가 되기 위한 노드 및 버텍스 임계값은 환경 변수 QSG_RENDERER_BATCH_NODE_THRESHOLD=[count] 및 QSG_RENDERER_BATCH_VERTEX_THRESHOLD=[count] 을 사용하여 재정의할 수 있습니다. 이러한 플래그를 재정의하는 것은 주로 플랫폼 공급업체에게 유용합니다.

참고: 배치 루트아래에서는 고유한 머티리얼 상태와 지오메트리 유형 조합마다 하나의 배치가 생성됩니다.

클리핑

Item::clip 을 true로 설정하면, 지오메트리에 직사각형이 포함된 QSGClipNode 가 생성됩니다. 기본 렌더러는 OpenGL의 시저링(scissoring)을 사용하여 이 클립을 적용합니다. 아이템이 90도가 아닌 각도로 회전된 경우, OpenGL의 스텐실 버퍼가 사용됩니다. Qt Quick Item은 QML을 통해 직사각형만 클립으로 설정할 수 있지만, 씬 그래프 API와 기본 렌더러는 클리핑에 어떤 모양이든 사용할 수 있습니다.

서브트리에 클립을 적용할 때는 해당 서브트리를 고유한 OpenGL 상태로 렌더링해야 합니다. 즉, ` Item::clip `가 true인 경우, 해당 아이템의 배치 처리는 자식 요소들로만 제한됩니다. ` ListView `이나 ` GridView`처럼 자식 요소가 많거나, ` TextArea`처럼 복잡한 자식 요소가 있는 경우에는 문제가 없습니다. 그러나 버튼 레이블, 텍스트 필드, 목록 델리게이트, 테이블 셀과 같이 크기가 작은 항목의 경우, 클리핑을 적용하면 일괄 처리가 차단되므로 주의해서 사용해야 합니다. Flickable(또는 항목 뷰)에 대한 클리핑은 불투명한 항목이 Flickable 주변 영역을 덮도록 UI를 배치하거나, 그렇지 않은 경우에는 창 가장자리를 이용해 나머지 모든 요소를 클리핑함으로써 피할 수 있는 경우가 많습니다.

Item::clip 를 true 로 설정하면 QQuickItem::ItemIsViewport 플래그도 함께 설정됩니다. QQuickItem::ItemObservesViewport 플래그가 설정된 자식 항목은 대략적인 사전 클리핑 단계에서 뷰포트를 사용할 수 있습니다. 예를 들어, Text 는 뷰포트 외부에 완전히 위치한 텍스트 줄을 생략합니다. 씬 그래프 노드를 생략하거나 vertices 를 제한하는 것은 최적화 기법이며, QML에서 Item::clip 를 설정하는 대신 C++에서 flags 를 설정하여 이를 달성할 수 있습니다.

사용자 정의 항목에서 ` QQuickItem::updatePaintNode()`를 구현할 때, 넓은 기하학적 영역에 걸쳐 많은 세부 사항을 렌더링할 수 있다면 그래픽을 뷰포트로 제한하는 것이 효율적인지 고려해야 합니다. 만약 그렇다면, ` ItemObservesViewport ` 플래그를 설정하고 ` QQuickItem::clipRect()`를 통해 현재 노출된 영역을 읽어올 수 있습니다. 그 결과 updatePaintNode()가 더 자주 호출됩니다(일반적으로 뷰포트 내에서 콘텐츠가 움직일 때마다 프레임당 한 번씩).

버텍스 버퍼

각 배치는 버텍스 버퍼 객체(VBO)를 사용하여 GPU에 데이터를 저장합니다. 이 버텍스 버퍼는 프레임 간에 유지되며, 이를 나타내는 장면 그래프의 일부가 변경될 때 업데이트됩니다.

기본적으로 렌더러는 GL_STATIC_DRAW 를 사용하여 VBO에 데이터를 업로드합니다. 환경 변수 QSG_RENDERER_BUFFER_STRATEGY=[strategy] 를 설정하여 다른 업로드 전략을 선택할 수 있습니다. 유효한 값은 stream 및 dynamic 입니다. 이 값을 변경하는 것은 주로 플랫폼 공급업체에 유용합니다.

앤티앨리어싱

씬 그래프는 두 가지 유형의 앤티앨리어싱을 지원합니다. 기본적으로 사각형이나 이미지와 같은 프리미티브는 가장자리를 따라 더 많은 정점을 추가하여 가장자리가 투명하게 점차 사라지도록 앤티앨리어싱 처리됩니다. 이 방법을 버텍스 앤티앨리어싱이라고 합니다. 사용자가 ` QSurfaceFormat `을 ` QQuickWindow::setFormat()`를 사용하여 ` 0 `보다 큰 샘플 수로 설정함으로써 멀티샘플 OpenGL 컨텍스트를 요청하면, 씬 그래프는 멀티샘플 기반 앤티앨리어싱(MSAA)을 우선적으로 사용합니다. 이 두 기법은 내부 렌더링 방식에 영향을 미치며 각기 다른 제한 사항을 가지고 있습니다.

또한 환경 변수 QSG_ANTIALIASING_METHOD 을 vertex 또는 msaa 로 설정하여 사용되는 앤티앨리어싱 방식을 재정의할 수도 있습니다.

버텍스 앤티앨리어싱은 두 모서리가 수학적으로 동일하더라도 인접한 프리미티브의 모서리 사이에 이음새가 생길 수 있습니다. 반면 멀티샘플 앤티앨리어싱에서는 이러한 현상이 발생하지 않습니다.

버텍스 앤티앨리어싱

Item::antialiasing 속성을 사용하여 항목별로 버텍스 앤티앨리어싱을 활성화하거나 비활성화할 수 있습니다. 이 기능은 기본 하드웨어가 지원하는 기능과 관계없이 작동하며, 일반 렌더링된 프리미티브는 물론 ShaderEffectSource 유형을 사용하여 프레임버퍼 객체에 캡처된 프리미티브에 대해서도 더 높은 품질의 앤티앨리어싱을 제공합니다.

버텍스 앤티앨리어싱을 사용할 때의 단점은, 앤티앨리어싱이 활성화된 각 프리미티브에 대해 블렌딩 처리가 필요하다는 점입니다. 배칭 측면에서 보면, 이는 렌더러가 해당 프리미티브를 배칭할 수 있는지 여부를 파악하기 위해 더 많은 작업을 수행해야 함을 의미하며, 씬 내의 다른 요소들과의 중복으로 인해 배칭 횟수가 줄어들어 성능에 영향을 미칠 수도 있습니다.

저사양 하드웨어에서는 블렌딩 작업에 상당한 비용이 소요될 수 있으므로, 화면의 대부분을 차지하는 이미지나 둥근 직사각형의 경우, 해당 프리미티브 내부에 필요한 블렌딩 양이 많으면 프리미티브 전체를 블렌딩해야 하므로 성능 저하가 크게 발생할 수 있습니다.

멀티샘플 앤티앨리어싱

멀티샘플 앤티앨리어싱은 하드웨어가 프리미티브 내 픽셀당 커버리지 값을 계산하는 하드웨어 기능입니다. 일부 하드웨어는 매우 낮은 비용으로 멀티샘플 처리가 가능한 반면, 다른 하드웨어는 프레임을 렌더링하는 데 더 많은 메모리와 GPU 사이클이 필요할 수 있습니다.

멀티샘플 앤티앨리어싱을 사용하면 둥근 사각형이나 이미지 요소와 같은 많은 프리미티브에 앤티앨리어싱을 적용하면서도 씬 그래프 내에서 불투명성을 유지할 수 있습니다. 이는 렌더러가 배치 생성 시 작업 부담을 덜 수 있으며, 오버드로우를 방지하기 위해 얼리-Z(early-z)를 활용할 수 있음을 의미합니다.

멀티샘플 앤티앨리어싱을 사용할 경우, 프레임버퍼 객체로 렌더링되는 콘텐츠는 프레임버퍼의 멀티샘플링을 지원하기 위한 추가 확장 기능이 필요합니다. 일반적으로 GL_EXT_framebuffer_multisample 및 GL_EXT_framebuffer_blit 이 해당합니다. 대부분의 데스크톱 칩에는 이러한 확장 기능이 포함되어 있지만, 임베디드 칩에서는 덜 일반적입니다. 하드웨어에서 프레임버퍼 멀티샘플링을 지원하지 않는 경우, ShaderEffectSource 의 내용을 포함하여 프레임버퍼 객체로 렌더링된 콘텐츠에는 앤티앨리어싱이 적용되지 않습니다.

성능

서두에서 언급했듯이, 우수한 성능을 얻기 위해 렌더러의 세부적인 내용을 이해할 필요는 없습니다. 이 렌더러는 일반적인 사용 사례를 최적화하도록 작성되었으며, 거의 모든 상황에서 꽤 좋은 성능을 발휘합니다.

  • 우수한 성능은 지오메트리가 반복적으로 업로드되는 횟수를 최대한 줄이는 효과적인 배칭에서 비롯됩니다. QSG_RENDERER_DEBUG=render 환경 변수를 설정하면, 렌더러는 배칭이 얼마나 잘 진행되었는지, 몇 개의 배치가 사용되었는지, 어떤 배치가 유지되고 어떤 배치가 불투명하며 어떤 배치가 투명한지에 대한 통계를 출력합니다. 최적의 성능을 추구할 때는 정말 필요한 경우에만 업로드를 수행해야 하며, 배치 수는 10개 미만이어야 하고 그중 최소 3~4개는 불투명해야 합니다.
  • 기본 렌더러는 CPU 측에서 뷰포트 클리핑이나 오클루전 감지를 수행하지 않습니다. 보이지 않아야 할 대상은 표시되어서는 안 됩니다. 그려져서는 안 되는 항목에는 ` Item::visible: false `를 사용하십시오. 이러한 로직을 추가하지 않은 주된 이유는 추가적인 비용이 발생하여, 올바르게 동작하도록 세심하게 설계된 애플리케이션까지 부정적인 영향을 미칠 수 있기 때문입니다.
  • 텍스처 아틀라스가 사용되도록 하십시오. Image 및 BorderImage 항목은 이미지가 너무 크지 않은 한 아틀라스를 사용합니다. C++에서 생성된 텍스처의 경우, QQuickWindow::createTexture()를 호출할 때 QQuickWindow::TextureCanUseAtlas 를 전달하십시오. 환경 변수 QSG_ATLAS_OVERLAY 를 설정하면 모든 아틀라스 텍스처에 색상이 지정되어 애플리케이션에서 쉽게 식별할 수 있습니다.
  • 가능한 한 불투명 프리미티브를 사용하십시오. 불투명 프리미티브는 렌더러에서 처리 속도가 빠르고 GPU에서 렌더링하는 속도도 빠릅니다. 예를 들어, PNG 파일은 각 픽셀이 완전히 불투명하더라도 알파 채널을 포함하는 경우가 많습니다. JPG 파일은 항상 불투명합니다. QQuickImageProvider 에 이미지를 제공하거나 QQuickWindow::createTextureFromImage()를 사용하여 이미지를 생성할 때는, 가능한 경우 이미지에 QImage::Format_RGB32 를 적용하십시오.
  • 위 그림과 같이 겹쳐진 복합 항목은 일괄 처리할 수 없다는 점에 유의하십시오.
  • 클리핑은 일괄 처리를 방해합니다. 테이블 셀, 항목 델리게이트 또는 이와 유사한 곳에서는 절대 항목 단위로 클리핑을 사용하지 마십시오. 텍스트를 클리핑하는 대신 생략(eliding)을 사용하십시오. 이미지를 클리핑하는 대신, 잘린 이미지를 반환하는 QQuickImageProvider 를 생성하십시오.
  • 일괄 처리는 16비트 인덱스에서만 작동합니다. 모든 내장 항목은 16비트 인덱스를 사용하지만, 사용자 정의 지오메트리는 32비트 인덱스를 사용할 수도 있습니다.
  • 일부 머티리얼 플래그는 일괄 처리를 방지하며, 그중 가장 제한적인 것은 모든 일괄 처리를 차단하는 ` QSGMaterial::RequiresFullMatrix `입니다.
  • 단색 배경을 사용하는 애플리케이션은 최상위 Rectangle 항목을 사용하는 대신 QQuickWindow::setColor()을 사용하여 설정해야 합니다. QQuickWindow::setColor()은 glClear() 호출에서 사용되며, 이는 잠재적으로 더 빠를 수 있습니다.
  • 미프맵된 이미지 항목은 전역 아틀라스에 배치되지 않으며 배치 처리되지 않습니다.
  • 프레임버퍼 객체(FBO) 리드백과 관련된 OpenGL 드라이버의 버그로 인해 렌더링된 글리프가 손상될 수 있습니다. QML_USE_GLYPHCACHE_WORKAROUND 환경 변수를 설정하면, Qt는 RAM에 글리프의 추가 사본을 유지합니다. 이는 Qt가 CPU를 통해 추가 사본에 액세스하기 때문에 이전에 그려진 적이 없는 글리프를 그릴 때 성능이 약간 저하된다는 것을 의미합니다. 또한 글리프 캐시가 두 배의 메모리를 사용하게 된다는 뜻이기도 합니다. 품질에는 영향을 미치지 않습니다.

애플리케이션의 성능이 저조하다면, 렌더링이 실제로 병목 현상인지 확인하십시오. 프로파일러를 사용하십시오! 환경 변수 ` QSG_RENDER_TIMING=1 `는 문제의 원인을 정확히 파악하는 데 도움이 되는 여러 유용한 타이밍 매개변수를 출력합니다.

가변성 그룹

분석 결과, 렌더러가 정기적으로 업데이트되는 콘텐츠를 정적 콘텐츠와 함께 묶어 처리하는 경우가 있습니다. 이러한 경우, 업데이트할 때마다 대부분 변경되지 않은 콘텐츠로 구성된 큰 배치 전체를 업로드하기보다는 지오메트리를 두 개의 별도 배치로 나누는 것이 더 좋습니다.

다음 예제를 살펴보겠습니다.

Column {
    Timer {
        interval: 1000
        running: true
        repeat: true
        onTriggered: dynamicText.counter++
    }

    Text {
        id: dynamicText
        property int counter: 0
        text: counter
        mutabilityGroup: Item.DynamicMutabilityGroup
    }

    Text {
        id: staticText1
        text: "Static label"
    }

    Text {
        id: staticText2
        text: "Static label"
    }
}

이 애플리케이션은 1초마다 업데이트되는 ` dynamicText ` 레이블로 구성되어 있습니다. 또한, 절대 업데이트되지 않는 두 개의 정적 텍스트 레이블이 있습니다.

이 예제에서 명시적인 가변성 그룹이 설정되지 않았다면, 정적 텍스트 레이블의 지오메트리는 동적 레이블과 함께 묶여 처리될 것입니다. 이는 모두 동일한 머티리얼과 기본 속성을 사용하고 있으며, 씬 그래프 배치 렌더러가 씬 내 모든 텍스트를 표시하는 데 필요한 드로우 콜 수를 (특정 한도 내에서) 최소화하려고 시도하기 때문입니다. 결과적으로, 씬 내 정적 텍스트를 나타내는 88개의 버텍스는 동적 텍스트가 변경될 때마다 재업로드됩니다.

dynamicText 의 mutabilityGroup을 Item.DynamicMutabilityGroup 로 설정함으로써, 레이더에게 해당 레이블이 자주 업데이트될 것이라는 힌트를 제공합니다. 그 결과, 이 레이블은 기본 변경성 그룹에 속하는 나머지 콘텐츠와 함께 배치 처리되지 않습니다. 동일한 속도로 업데이트되는 항목이 여러 개 있는 경우에도, 이를 동일한 그룹에 할당하면 함께 배치 처리될 수 있습니다.

기본 그룹 0 을 포함하여 총 16개의 변경 가능성 그룹을 사용할 수 있습니다.

UI 내 대부분의 항목에는 기본 가변성 그룹을 사용해야 합니다. 주로 분석을 통해 대용량의 정적 지오메트리 재업로드로 인한 병목 현상이 발견된 경우에만 이 설정을 재정의해야 합니다.

시각화

씬 그래프의 기본 렌더러의 다양한 측면을 시각화하려면, QSG_VISUALIZE 환경 변수를 아래 각 섹션에 설명된 값 중 하나로 설정할 수 있습니다. 다음 QML 코드를 사용하여 일부 변수의 출력 예시를 제공합니다.

import QtQuick 2.2

Rectangle {
    width: 200
    height: 140

    ListView {
        id: clippedList
        x: 20
        y: 20
        width: 70
        height: 100
        clip: true
        model: ["Item A", "Item B", "Item C", "Item D"]

        delegate: Rectangle {
            color: "lightblue"
            width: parent.width
            height: 25

            Text {
                text: modelData
                anchors.fill: parent
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }
        }
    }

    ListView {
        id: clippedDelegateList
        x: clippedList.x + clippedList.width + 20
        y: 20
        width: 70
        height: 100
        clip: true
        model: ["Item A", "Item B", "Item C", "Item D"]

        delegate: Rectangle {
            color: "lightblue"
            width: parent.width
            height: 25
            clip: true

            Text {
                text: modelData
                anchors.fill: parent
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }
        }
    }
}

왼쪽의 ` ListView `의 경우, ` clip ` 속성을 ` true`로 설정했습니다. 오른쪽의 ` ListView `의 경우, 배칭에 대한 클리핑 효과를 보여주기 위해 각 델리게이트의 ` clip ` 속성을 ` true `로 설정했습니다.

원문

원본

참고: 시각화된요소들은 클리핑을 따르지 않으며, 렌더링 순서는 임의적입니다.

배치 시각화

QSG_VISUALIZE 를 batches 로 설정하면 렌더러에서 배치(batch)가 시각화됩니다. 병합된 배치는 단색으로, 병합되지 않은 배치는 대각선 선 패턴으로 표시됩니다. 고유한 색상이 적을수록 배치가 잘 이루어진 것입니다. 병합되지 않은 배치에 개별 노드가 많이 포함되어 있다면 이는 바람직하지 않습니다.

배치

QSG_VISUALIZE=batches

클리핑 시각화

QSG_VISUALIZE 를 clip 로 설정하면 클리핑을 나타내기 위해 장면 위에 빨간색 영역이 그려집니다. Qt Quick 항목은 기본적으로 클리핑되지 않으므로, 일반적으로 클리핑이 시각화되지 않습니다.

클리핑 영역이 빨간색으로 강조 표시된 항목

QSG_VISUALIZE=clip

변경 사항 시각화

QSG_VISUALIZE 를 changes 로 설정하면 렌더러의 변경 사항을 시각화합니다. 씬그래프의 변경 사항은 무작위 색상의 깜빡이는 오버레이로 표시됩니다. 프리미티브의 변경 사항은 단색으로 표시되는 반면, 행렬이나 불투명도 변경과 같은 상위 노드의 변경 사항은 패턴으로 표시됩니다.

오버드로우 시각화

QSG_VISUALIZE 를 overdraw 로 설정하면 렌더러에서 오버드로우가 시각화됩니다. 모든 항목을 3D로 시각화하여 오버드로우를 강조 표시합니다. 이 모드는 뷰포트 외부에 있는 지오메트리를 어느 정도 감지하는 데에도 사용할 수 있습니다. 불투명한 항목은 녹색 색조로 렌더링되고, 반투명한 항목은 빨간색 색조로 렌더링됩니다. 뷰포트의 바운딩 박스는 파란색으로 렌더링됩니다. 불투명한 콘텐츠는 시네그래프에서 처리하기 쉬우며, 일반적으로 렌더링 속도도 더 빠릅니다.

위 코드에서 루트 사각형은 창 자체가 흰색이기 때문에 불필요하므로, 이 경우 사각형을 그리는 것은 자원 낭비입니다. 이를 Item으로 변경하면 성능이 약간 향상될 수 있습니다.

과인출-1

과인출-2

QSG_VISUALIZE=overdraw

Qt 렌더링 하드웨어 인터페이스(RHI)를 통한 렌더링

Qt 6.0부터는 기본 구현이 항상 Qt GUI 이 모듈에서 제공하는 그래픽 추상화 계층인 Qt 렌더링 하드웨어 인터페이스(RHI)를 통해 항상 렌더링됩니다. 즉, Qt 5와 달리 씬 그래프에서 직접 OpenGL 호출을 수행하지 않습니다. 대신 RHI API를 사용하여 리소스 및 그리기 명령을 기록하면, RHI가 이 명령 스트림을 OpenGL, Vulkan, Metal 또는 Direct 3D 호출로 변환합니다. 셰이더 처리도 셰이더 코드를 한 번 작성하여 SPIR-V로 컴파일한 후, 다양한 그래픽 API에 적합한 언어로 변환함으로써 통합되었습니다.

동작을 제어하기 위해 다음 환경 변수를 사용할 수 있습니다:

환경 변수사용 가능한 값설명
QSG_RHI_BACKENDvulkan, metal, opengl, d3d11, d3d12특정 RHI 백엔드를 요청합니다. 이 변수나 이에 상응하는 C++ API로 재정의되지 않는 한, 기본적으로 플랫폼에 따라 대상 그래픽 API가 선택됩니다. 현재 기본값은 Windows의 경우 Direct3D 11, macOS의 경우 Metal, 그 외의 경우 OpenGL입니다.
QSG_INFO1OpenGL 기반 렌더링 경로와 마찬가지로, 이 변수를 설정하면 Qt Quick 장면 그래프를 초기화할 때 시스템 정보를 출력할 수 있습니다. 이는 문제 해결에 매우 유용할 수 있습니다.
QSG_RHI_DEBUG_LAYER1해당되는 경우(Vulkan, Direct3D), 그래픽 장치나 인스턴스 객체에서 사용 가능한 그래픽 API 구현체의 디버그 또는 유효성 검사 레이어를 활성화합니다. macOS의 Metal의 경우, 대신 METAL_DEVICE_WRAPPER_TYPE=1 환경 변수를 설정하십시오.
QSG_RHI_PREFER_SOFTWARE_RENDERER1소프트웨어 기반 래스터화를 사용하는 어댑터 또는 물리적 장치를 선택하도록 요청합니다. 이 옵션은 기본 API가 어댑터 열거를 지원하는 경우(예: Direct3D 또는 Vulkan)에만 적용되며, 그렇지 않은 경우에는 무시됩니다.

항상 지정된 단일 그래픽 API로 실행되기를 원하는 애플리케이션은 C++을 통해서도 이를 요청할 수 있습니다. 예를 들어, QQuickWindow 객체를 생성하기 전 main() 함수 초반에 다음 호출을 수행하면 Vulkan 사용을 강제하게 되며(그렇지 않으면 실패합니다):

QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);

QSGRendererInterface::GraphicsApi 를 참조하십시오. 열거형 값 OpenGL, Vulkan, Metal, Direct3D11, Direct3D12 는 QSG_RHI_BACKEND 가 해당 문자열 키로 설정된 상태에서 실행하는 것과 동일한 효과를 가집니다.

모든 QRhi 백엔드는 QSG_RHI_PREFER_SOFTWARE_RENDERER 또는 QT_D3D_ADAPTER_INDEX, QT_VK_PHYSICAL_DEVICE_INDEX 와 같은 백엔드별 변수에 의해 재정의되지 않는 한, 시스템 기본 GPU 어댑터나 물리적 장치를 선택합니다. 현재로서는 어댑터에 대한 추가적인 구성 기능은 제공되지 않습니다.

Qt 6.5부터는 이전에 환경 변수로만 노출되던 일부 설정을 QQuickGraphicsConfiguration 의 C++ API를 통해 사용할 수 있게 되었습니다. 예를 들어, QSG_RHI_DEBUG_LAYER 을 설정하는 것과 setDebugLayer(true)를 호출하는 것은 동일합니다.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.