Fundo:
Estou projetando um sistema de renderização 3D simples para uma arquitetura de tipo de sistema de componente de entidade usando C ++ e OpenGL. O sistema consiste em um renderizador e um gráfico de cena. Quando eu terminar a primeira iteração do renderizador, posso distribuir o gráfico da cena na arquitetura do ECS. Por enquanto, é um espaço reservado de uma maneira ou de outra. Se possível, meus objetivos para o renderizador a seguir:
- Simplicidade . Isto é para um projeto de pesquisa e eu quero poder mudar e expandir meus sistemas com facilidade (daí a abordagem ECS).
- Performance . Minha cena pode ter muitos modelos pequenos e também grandes volumes com muita geometria. Não é aceitável adquirir objetos do contexto OGL e geometria do buffer a cada quadro de renderização. Estou buscando a localidade dos dados para evitar falhas de cache.
- Flexibilidade . Ele deve ser capaz de renderizar sprites, modelos e volumes (voxels).
- Desacoplado . O gráfico da cena pode ser refatorado na arquitetura central do ECS depois que eu escrever meu renderizador.
- Modular . Seria bom poder trocar diferentes renderizadores sem alterar meu gráfico de cena.
- Transparência referencial , o que significa que, a qualquer momento, posso fornecer uma cena válida e ela sempre renderiza a mesma imagem para essa cena. Este objetivo em particular não é necessariamente necessário. Eu pensei que isso ajudaria a simplificar a serialização de cenas (precisarei ser capaz de salvar e carregar cenas) e me dar flexibilidade para trocar cenas diferentes durante o tempo de execução para fins de teste / experimentação.
Problema e idéias:
Eu vim com algumas abordagens diferentes para tentar, mas estou tendo dificuldades em saber como armazenar em cache os recursos do OGL (VAO, VBOs, shaders etc.) para cada nó de renderização. A seguir, estão os diferentes conceitos de cache que eu pensei até agora:
- Cache centralizado. Cada nó da cena possui um ID e o renderizador possui um cache que mapeia os IDs para renderizar os nós. Cada nó de renderização contém os VAO e VBOs associados à geometria. Uma falta de cache adquire recursos e mapeia a geometria para um nó de renderização no cache. Quando a geometria é alterada, um sinalizador sujo é definido. Se o renderizador vir um sinalizador de geometria suja ao iterar pelos nós da cena, ele rebuffará os dados usando o nó de renderização. Quando um nó de cena é removido, um evento é transmitido e o renderizador remove o nó de renderização associado do cache enquanto libera recursos. Como alternativa, o nó é marcado para remoção e o representante é responsável por removê-lo. Eu acho que essa abordagem atinge mais de perto o objetivo 6, considerando também 4 e 5. 2 sofre com a complexidade extra e a perda de localidade dos dados com pesquisas de mapa em vez de acesso à matriz.
- Cache distribuído . Semelhante acima, exceto que cada nó da cena possui um nó de renderização. Isso ignora a pesquisa do mapa. Para endereçar a localidade dos dados, os nós de renderização podem ser armazenados no renderizador. Em seguida, os nós da cena poderiam ter ponteiros para renderizar os nós e o renderizador define o ponteiro em uma falta de cache. Eu acho que esse tipo de imita uma abordagem de componente de entidade, por isso seria consistente com o restante da arquitetura. O problema aqui é que agora os nós da cena retêm dados específicos da implementação do renderizador. Se eu mudar a forma como as coisas são renderizadas no renderizador (como renderizar sprites x volumes), agora preciso alterar o nó de renderização ou adicionar mais "componentes" ao nó da cena (o que significa alterar também o gráfico da cena). No lado positivo, essa parece ser a maneira mais simples de colocar meu renderizador de primeira iteração em funcionamento.
- Metadados distribuídos . Um componente de metadados do cache do renderizador é armazenado em cada nó da cena. Esses dados não são específicos da implementação, mas contêm um ID, tipo e qualquer outro dado relevante necessário ao cache. A pesquisa de cache pode ser feita diretamente em uma matriz usando o ID, e o tipo pode indicar qual tipo de abordagem de renderização usar (como sprites x volumes).
- Visitante + mapeamento distribuído . O renderizador é um visitante e os nós da cena são elementos no padrão do visitante. Cada nó da cena contém uma chave de cache (como os metadados, mas apenas um ID) que apenas o renderizador manipula. O ID pode ser usado para matriz em vez de consulta geral do mapa. O renderizador pode permitir que o nó da cena despache uma função de renderização diferente com base no tipo do nó da cena, e o ID pode ser usado por qualquer cache. Um ID padrão ou fora do intervalo indicaria uma falta de cache.
Como resolveria este problema? Ou você tem alguma sugestão? Obrigado por ler minha parede de texto!