본문 바로가기

카테고리 없음

프로그래밍패턴 03 경량

예제
https://refactoring.guru/design-patterns/flyweight

Flyweight

/ Design Patterns / Structural Patterns Flyweight Also known as: Cache Intent Flyweight is a structural design pattern that lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keep

refactoring.guru


탕탕특공대 막연히 오브젝트풀을 잘 썼다고만 생각했는데 경량이려나


1. 주메모리에 올리는 데이터의 경우
다같이 사용하는 공용 데이터를 하나로 모은다

class TreeModel
{
private:
	Mesh mesh_;
    Texture bark_;
    Texture leaves_;
};

class Tree
{
private:
	TreeModel* model;
    
    Veoctor position_;
    double height_;
    Color barkTint_;
}

각 나무 객체들은 위치 등을 나타내는 매개변수만 따로 있고, 공통되는 데이터 TreeModel은 공용으로 쓴다.
각 나무 인스턴스는 공유 객체인 TreeModel을 참조하기만 하면 된다.

3.2 수천 개의 인스턴스
인스턴스 렌더링
GPU로 보내는 데이터 양을 최소화하기 위해서는 공유 데이터인 TreeModel을 딱 한 번만 보낼 수 있어야 한다. 그 이후 나무마다 값이 다른 위치, 색, 크기 등을 전달하고, 마지막으로 GPU에 '전체 나무 인스턴스를 그릴 때 공유 데이터를 사용ㅇ해' 라고 말하면 된다.

인스턴스렌더링 : 두 개의 데이터 스트림
데이터스트림1 : 여러 번 렌더링 되어야 하는 공유 데이터 - ex)메시, 텍스처 등
데이터스트림2 : 인스턴스 목록과, 첫 번째 스트림데이터를 이용해 그릴 때 각기 다르게 보이기 위해 필요한 변수들
=>draw 호출 한 번으로 그린다.

_______________________________________________________________________________

3.3 경량 패턴
어떤 객체의 개수가 너무 많아서 좀 더 가볍게 만들고 싶을 때
- 객체 데이터를 두 종류로 나눈다.
1. 자유문맥 : geometry, texture
== context-free
== 고유상태
2. 외부 상태 : 나무의 위치, 크기, 색 등



지형 클래스를 만든다고 했을 때
'고유'한 '자유문맥'인 Terrian을 만든다. (위치 등의 외부 데이터 배제)

class Terrain
{
public:
	Terrain(int movementCost, bool isWater, Texture texture)
    : movementCost_(movementCost)
    , isWater_(isWater)
    , texture_(texture)
    {
    }
    
    int getMovementCost() const;
    bool isWater() const;
    const Texture& getTexture() const;
    
private:
	int movementCost_;
    bool isWater_; 
    Texture texture_;
}

하지만 지역 타일마다 Terrian 인스턴스를 가질 필요는 없다.
지형에 들어가는 모든 풀밭 타일은 동일하기 때문에, World 클래스 격자 멤버 변수에 Terrian객체의 포인터를 넣을 수 있다.

class World
{
private:
	Terrain* tiles_[WIDTH][HEIGHT];
}

지형이 같은 타일들은 모두 같은 Terrian 인스턴스 포인터를 가진다.



Terrian인스턴스가 여러 곳에서 사용되다 보니, 동적으로 할당하면 생명주기를 관리하기가 좀 더 어렵다. 따라서 World클래스에 저장한다.

class World
{
public:
	World()
    : grassTerrain_(1, false, GRASS_TEXTURE)
    , hillTerrain_(3, false, HILL_TEXTURE)
    , riverTerrain_(2, true, RIVER_TEXTURE)
{}

private:
	Terrain grassTerrain_;
    Terrain hillTerrain_;
    Terrain riverTerrain_;
}

void World::generateTerrain()
{
	//언덕으로
	tiles_[9][2] = &hillTerrain_;
    //강으로
    tiles_[9][1] = &riverTerrain_;
  
}




이렇게 하면 지형 속성 값을 World의 메서드 대신 Terrain 객체에서 바로 얻을 수 있다.

const Terrain& World::getTile(int x, int y) const
{
	return *tiles_[x][y];
}

int cost = world.getTile(2,3).getMovementCost();


//이전코드는
class World
{

    int getMovementCost(int x, int y)
    {
        switch(tiles_[x][y])
        {
            case TERRAIN_GRASS : return 1;
            ...
        }
    }
}

World 클래스는 지형의 세부 정보와 커플링되지 않고, 타일 속성은 Terrain 객체에서 바로 얻을 수 있다.