경량 (Flyweight)

 

공유를 통해 많은 수의 소립(fine-grained) 객체들을 효과적으로 지원합니다.

 

객체의 공통된 상태를 공유시켜서 메모리를 확보한다.

 

예제

 

게임 내에 수천 그루의 나무가 있고 그 나무들이 렌더링된다면 각 나무마다 가지고 있는 메시, 텍스처, 위치 등의 정보를 매 프레임마다 렌더링 해야 할 것이다.

특히 메시나 텍스처는 용량도 크기때문에 매 프레임마다 버스를 통해 GPU에 수많은 오브젝트들을 전달하기에는 양이 너무 많다.

 

하지만 나무가 수천 그루 있다고 해도 결국 전체 구조는 동일하고 비슷해 보이기 때문에 위치 등을 제외한 대부분의 데이터가 인스턴스별로 다르지 않다.

 

class Tree {
private:
    Mesh mesh;
    Texture bark;
    Texture leaves;
    Vector position;
    double height;
    double thickness;
    Color barkTint;
    Color leafTint;
};

 

나무가 이런 정보들을 가지고 있다고 한다면,

 

class TreeModel {
private:
    Mesh mesh;
    Texture bark;
    Texture leaves;
};

 

같은 메시나 텍스쳐는 메모리에 여러번 올릴 이유가 없기 때문에 따로 빼내서 하나의 공유 객체만 존재하게 만든다.

그뒤에 각 나무 인스턴스들은 공유 객체를 참조하기만 하면 된다.

 

class Tree {
private:
    TreeModel* model;
    
    Vector position;
    double height;
    double height;
    double thickness;
    Color barkTint;
    Color leafTint;
};

 

많은 수의 오브젝트가 하나의 공유 데이터를 사용하기 때문에 메모리 사용은 크게 줄일 수 있다.

하지만 렌더링으로 넘어가면 얘기가 조금 다르다. 공유 데이터는 GPU에 딱 한번만 보내주어야 효율적일 것이다.

공유 데이터를 한번 보내고 난 뒤에, 나머지 속성들을 전달하고 최종적으로 공유 데이터를 사용하라고 일러주기만 하면 된다.

 

이를 인스턴스 렌더링이라고 하며 보통 그래픽카드나 API에서 제공해준다.

두 개의 데이터 스트림을 사용해서 첫 번째 데이터 스트림에는 공유 데이터, 두 번째 데이터 스트림에는 인스턴스 목록과 속성들이 들어간다.

하나의 공유 데이터만을 사용한다면 드로우콜 한번으로 모든 오브젝트를 다 그릴수 있게 된다.

 

 

경량 패턴

인스턴스 렌더링에서는 메모리의 크기보다는 렌더링할 데이터를 GPU 버스로 보내는 데 걸리는 시간이 더 중요하다. 하지만 공유 데이터를 사용함으로써 성능을 끌어올린다는 기본 개념은 경량 패턴과 같다.

 

경량 패턴은 객체 데이터를 공유할 수 있는 데이터인 고유 상태(자유 문맥)와 인스턴스별로 값이 다른 외부 상태로 나눈다.

고유 상태는 반드시 불변 객체임이 보증되어야 한다.

 

어떤 경량 객체가 실제로 필요한지 예측할 수 없다면, 필요할 때 만드는 것이 낫다. 공유기능 유지를 원한다면 이미 만들어져 있는지를 확인해보는 절차가 필요하다.

+ Recent posts