Post

Gerenciamento de Memória em um Jogo

Poderiamos usar o método de alocar memória dinamicamente conforme o jogo vai acontecendo.

É comum entre vários devs usar new e delete para alocar/desalocar a memória do heap.

Contudo, há outra maneira.

Podemos alocar todo o espaço necessário quando o jogo se inicia e trabalhar nesse pool disponivel.

Primeiro alocamos a memória na platform-layer e passamos no game_update para ser particionado/fragmentado nas ocasiões que o game necessitar.

A struct é definida com um tamanho de memória para dados permanentes (persistente) e dados transients.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Game_Memory
{
    bool initialized;

    void *permanent;
    u64 permanent_size;

    void *transient;
    u64 transient_size;
};

//
// Visible only Game Layer
//
struct Game_State
{
    int tone_hz;
    int xo;
    int yo;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
game_update(Game_Memory *memory, Game_Back_Buffer *buffer, Game_Input *input)
{
    // garantimos que o espaço do estado sempre caiba no storage.
    ASSERT(sizeof(Game_State) <= memory->permanent_size);

    // Casting para usarmos a memória baseada na struct
    Game_State *game_state = (Game_State *)memory->permanent;

    // atribui valores padrões e garante que já foi iniciado a memória.
    if (!memory->initialized) {
        game_state->tone_hz = 256;
        game_state->xo      = 0;
        game_state->yo      = 0;

        memory->initialized = true;
    }

    // ... code
	
    // atualiza o estado do jogo (permanente)
    game_state->xo += (int) (4.0f * controller->stick_avg_x);
    game_state->tone_hz = 256 + (int) (128.0f * controller->stick_avg_x);

    update_gradient(buffer, game_state->xo, game_state->yo);
}

E para alocar, vamos usar o VirtualAlloc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Game_Memory game_memory = {};
game_memory.permanent_size = MEGA_BYTES(64);
game_memory.transient_size = GIGA_BYTES(4);

#if INTERNAL
    // Se for em desenv. vamos alocar um endereço base.
    // Isso ajudará futuramente para conseguirmos fazer um live code
    // e gravar em disco os estados do jogo (screen record)
    LPVOID base_address = (LPVOID) TERA_BYTES(2);
#else
    LPVOID base_address = 0;
#endif

// 4gb + 64mb será o total da memória.
u64 total_size = game_memory.permanent_size + game_memory.transient_size;
game_memory.permanent = VirtualAlloc(base_address, (size_t) total_size,
	MEM_RESERVE|MEM_COMMIT,
	PAGE_READWRITE);

// O espaço transient iniciará a partir do espaço permanente.
// faremos o casting para 1 byte.
game_memory.transient = ((u8 *) game_memory.permanent + game_memory.permanent_size);
Esta postagem está licenciada sob CC BY 4.0 pelo autor.