Mar 17 2007

MD3DM: Onde foram parar meus Meshes?

Autor: Marcos Dell Antonio - Categorias: .NET, Direct3D, Mobilidade

Dando continuidade à série de posts sobre o meu Trabalho de Conclusão de Curso, vou escrever hoje sobre um erro muito comum que qualquer iniciante pode cometer quando for desenvolver utilizando o .NET CF 2.0 e o Managed Direct3D Mobile.

Para maiores informações sobre o assunto, veja meus outros posts aqui.

- O problema

Em todos os exemplos que desenvolvi até agora, sempre carreguei os objetos a serem desenhados dentro do método Render(), que é chamado no OnPaint() (saiba mais sobre esse conjunto de métodos aqui). Em outras palavras, quando era preciso desenhar, eu criava os objetos e desenhava. Ok, sem problemas à primeira vista, mas e a performance? E se os objetos forem gigantes, vindos de arquivos fonte, que é ainda mais demorado para ler e carregar todas as informações na memória do dispositivo?

Solução: carregar os objetos no início da aplicação e mantê-los em memória para que na hora da renderização não seja necessário carregar coisa alguma. Certo? Errado!

A implementação sugerida acima fica desta maneira:

- Inicialização do Device e do objeto a ser desenhado (Mesh)

private void InitializeGraphics()
{
    try
    {
        PresentParameters presentParams =
            new PresentParameters();
        presentParams.Windowed = true;
        presentParams.SwapEffect = SwapEffect.Discard;

        // Device
        device = new Device(
            0,
            DeviceType.Default,
            this,
            CreateFlags.None,
            presentParams);
        device.RenderState.CullMode = Cull.None;
        device.RenderState.Lighting = false;

        mesh = Mesh.Box(device, 1, 1, 0);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Erro na inicialização: " +
            ex.Message);
    }
}

- Renderização do Mesh

/// <summary>
/// Render the data
/// </summary>
private void Render()
{
    // Clear the device
    device.Clear(ClearFlags.Target, Color.White, 1.0f, 0);

    // Begin the scene
    device.BeginScene();

    mesh.DrawSubset(0);

    // End the scene and present
    device.EndScene();
    device.Present();
}

A princípio não há problema algum nessa implementação. Porém, ao executá-la a seguinte exception (System.ObjectDisposedException) é lançada na chamada do método mesh.DrawSubset(0):


Ora pois, se criei o Mesh e mandei o Device desenhá-lo não deveria acontecer este problema… mas isso aqui é computação, esqueceu? Faz parte do show.

- A Solução

Ao invés de criar o objeto Mesh logo após a criação do Device, devemos criá-lo quando o Device é reiniciado.

Por que hããm? Pois antes de renderizar as informações, o Device é reiniciado e perde todas as definições que o Mesh tinha quando foi criado.

Como resolver este problema? Simples:

- Alterar a criação do device (repare no evento OnDeviceReset)

private void InitializeGraphics()
{
    try
    {
        PresentParameters presentParams =
            new PresentParameters();
        presentParams.Windowed = true;
        presentParams.SwapEffect = SwapEffect.Discard;

        // Device
        device = new Device(
            0,
            DeviceType.Default,
            this,
            CreateFlags.None,
            presentParams);
        device.RenderState.CullMode = Cull.None;
        device.RenderState.Lighting = false;

        device.DeviceReset += new EventHandler(OnDeviceReset);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Erro na inicialização: " +
            ex.Message);
    }
}

- Criar o Mesh quando o Device for reiniciado

protected void OnDeviceReset(object sender, EventArgs e)
{
    mesh = Mesh.Box(device, 1, 1, 0);
}

Discretamente vou dar um palpite da origem do erro: todas as informações do Mesh (VertexBuffer, IndexBuffer, etc) ficam na memória do Device. Assim que é reiniciado essas informações são apagadas, conforme descrito em um post anterior.

É isso ae! Até +. ;)

Adicione ao del.icio.us del.icio.us | Adicione ao Rec6 Rec6

Faça um comentário