Visão geral da arquitetura
Arquivos-fonte
Esta página foi gerada com base nos seguintes arquivos-fonte:
O microprofile é uma biblioteca de profiling embutível para CPU e GPU, projetada para oferecer análise de desempenho em tempo real com baixa sobrecarga. O sistema permite instrumentar código através de regiões hierárquicas, contadores e visualização integrada via interface in-game ou servidor web embutido [README.md:1-22].
A arquitetura do projeto é baseada em um modelo header-only, onde toda a funcionalidade principal está contida em um único arquivo de cabeçalho (microprofile.h). Esta decisão de design facilita a integração em projetos existentes, exigindo apenas a definição de MICROPROFILE_IMPL em um arquivo de implementação para gerar o código necessário.
O sistema opera através de macros de instrumentação que capturam timestamps de CPU e GPU, armazenando esses dados em buffers circulares para análise posterior. A visualização pode ser feita através de três modalidades: interface in-game renderizada via callbacks fornecidos pela aplicação, servidor web embutido que gera HTML dinamicamente, ou exportação para arquivo HTML estático [README.md:10-19].
Visão Geral da Arquitetura
O microprofile adota uma arquitetura modular com separação clara entre instrumentação, coleta de dados, backends de GPU e sistemas de visualização. O diagrama abaixo ilustra os principais componentes e suas interações:
正在加载图表渲染器...
Pontos-chave da arquitetura:
-
Instrumentação via Macros: O ponto de entrada principal são os macros como
MICROPROFILE_SCOPEeMICROPROFILE_SCOPEGPU, que capturam automaticamente o início e fim de escopos de código através do padrão RAII [microprofile.h:39-52]. -
Unificação CPU/GPU: O sistema trata timers de CPU e GPU de forma unificada através das mesmas macros, com detecção automática do tipo de grupo. Isso simplifica a instrumentação e permite análise correlacionada de ambos os domínios [README.md:43-44].
-
Backends GPU Dinâmicos: Suporte para múltiplos backends gráficos pode ser compilado simultaneamente, com inicialização dinâmica em runtime. Isso permite que o mesmo binário suporte diferentes APIs gráficas dependendo do ambiente de execução [README.md:46-47].
-
Buffers por Thread: Cada thread mantém seus próprios buffers de dados de profiling, evitando contenção de locks durante a captura de timestamps. O sistema suporta múltiplas threads com nomes customizados via
MicroProfileOnThreadCreate[microprofile.h:30]. -
Visualização Múltipla: Os dados coletados podem ser visualizados em tempo real via interface in-game ou servidor web, ou exportados para análise offline em HTML [README.md:17].
Módulo de Instrumentação
O módulo de instrumentação é responsável por fornecer a API de macros que desenvolvedores usam para marcar regiões de código. Este módulo implementa o padrão RAII (Resource Acquisition Is Initialization) para garantir captura automática de timestamps de entrada e saída.
Responsabilidades e Limites
O módulo de instrumentação faz:
- Define macros para declaração e definição de timers estáticos (
MICROPROFILE_DECLARE,MICROPROFILE_DEFINE) - Fornece macros de escopo para uso em blocos de código (
MICROPROFILE_SCOPE,MICROPROFILE_SCOPEI) - Suporta timers GPU através de macros dedicados (
MICROPROFILE_SCOPEGPU,MICROPROFILE_SCOPEGPUI) - Permite adicionar metadados e labels dinâmicos via
MICROPROFILE_METAeMICROPROFILE_LABEL
O módulo não faz:
- Gerenciamento de memória dos buffers (delegado ao módulo de buffers)
- Renderização direta de dados (delegado aos callbacks da aplicação)
- Sincronização de threads (cada thread tem seus próprios dados)
APIs de Entrada Principais
As macros de instrumentação são divididas em categorias baseadas no tipo de uso:
| Macro | Categoria | Propósito |
|---|---|---|
MICROPROFILE_DECLARE | Declaração | Declara um timer estático para uso em múltiplos arquivos |
MICROPROFILE_DEFINE | Definição | Define um timer estático com grupo, nome e cor |
MICROPROFILE_SCOPE | Escopo CPU | Captura timing de um escopo usando timer pré-definido |
MICROPROFILE_SCOPEI | Escopo CPU | Captura timing com nome inline (sem declaração prévia) |
MICROPROFILE_SCOPEGPU | Escopo GPU | Captura timing GPU usando timer pré-definido |
MICROPROFILE_SCOPEGPUI | Escopo GPU | Captura timing GPU com nome inline |
MICROPROFILE_META | Metadados | Adiciona contador ou valor associado ao escopo atual |
MICROPROFILE_LABEL | Labels | Adiciona string descritiva ao escopo atual |
MICROPROFILE_LABELF | Labels | Adiciona string formatada (printf-style) ao escopo |
Padrão de Uso Típico
O uso padrão segue um fluxo de declaração (opcional) e instrumentação de escopo:
cpp1// Declaração global (opcional, para ordenação consistente) 2MICROPROFILE_DEFINE(g_ProfileFisk, "Fisk", "Skalle", nSomeColorRgb); 3 4// Em outro arquivo que usa o timer 5MICROPROFILE_DECLARE(g_ProfileFisk); 6 7void foo() { 8 MICROPROFILE_SCOPE(g_ProfileFisk); 9 // Código a ser medido 10} 11 12// Alternativa inline (sem declaração prévia) 13void bar() { 14 MICROPROFILE_SCOPEI("GroupName", "TimerName", nColorRgb); 15 // Código a ser medido 16}
Estruturas de Dados Internas
Cada timer definido cria uma entrada em uma tabela global de metadados contendo:
- Hash do nome para identificação rápida
- Índice do grupo ao qual pertence
- Cor para visualização (pode ser -1 para seleção automática baseada em hash)
- Ponteiros para estatísticas acumuladas
O escopo RAII gerado pelos macros mantém um ponteiro para o timer ativo e captura o timestamp de entrada no construtor e saída no destrutor. Para timers GPU, o construtor também insere um comando de timestamp na fila de comandos gráficos.
Tratamento de Erros e Condições de Borda
O módulo de instrumentação implementa várias proteções contra condições de erro:
-
Limite de Timers: O sistema define um número máximo de timers únicos. Tentativas de criar timers além desse limite são ignoradas silenciosamente para evitar overflow de buffers.
-
Recursão: Timers recursivos são tratados corretamente, com cada nível de recursão contribuindo para as estatísticas do mesmo timer.
-
Thread Safety: Cada thread mantém sua própria pilha de escopos ativos, eliminando a necessidade de sincronização durante a instrumentação.
-
Inicialização Lazy: Buffers grandes são alocados sob demanda quando grupos são efetivamente capturados, reduzindo overhead de memória quando o profiler está inativo [README.md:49].
Sistema de Timers CPU e GPU
O sistema de timers é responsável pela captura, armazenamento e sincronização de timestamps de CPU e GPU. Este módulo abstrai as diferenças entre as diversas APIs de timing disponíveis em diferentes plataformas.
Responsabilidades
O sistema de timers faz:
- Captura timestamps de alta resolução para CPU usando
QueryPerformanceCounter(Windows) ouclock_gettime(POSIX) - Gerencia queries de timestamp GPU através de callbacks específicos de backend
- Sincroniza timestamps CPU/GPU para visualização correlacionada
- Converte ticks para unidades de tempo consistentes
O sistema não faz:
- Renderização de gráficos de timeline
- Gerenciamento do ciclo de vida de objetos GPU
- Processamento de comandos de entrada do usuário
APIs de Timestamp
O sistema define três callbacks que devem ser implementados para suporte a GPU timing:
cpp1uint32_t MicroProfileGpuInsertTimer(void* pContext); 2uint64_t MicroProfileGpuGetTimeStamp(uint32_t nKey); 3uint64_t MicroProfileTicksPerSecondGpu();
Estas funções permitem que o microprofile:
- Insira um comando de timestamp na fila GPU (retornando uma chave para recuperação posterior)
- Recupere o valor do timestamp quando a GPU completar a execução
- Converta ticks GPU para segundos para exibição consistente
Fluxo de Captura de Timestamps
正在加载图表渲染器...
Explicação do fluxo:
-
Entrada de Escopo: Quando o código entra em um escopo instrumentado, o construtor do objeto RAII captura imediatamente o timestamp CPU e insere uma query de timestamp na fila GPU [microprofile.h:81].
-
Execução do Escopo: O código dentro do escopo executa normalmente enquanto as queries GPU permanecem pendentes.
-
Saída de Escopo: O destrutor captura o timestamp final e insere uma segunda query GPU, então armazena todos os dados no buffer de frame.
-
Resolução Assíncrona: Os timestamps GPU são resolvidos assincronamente quando a GPU completa a execução. O sistema faz polling dos resultados em frames subsequentes.
-
Sincronização: Uma vez que ambos os timestamps estão disponíveis, o sistema calcula a correlação entre clocks CPU e GPU para exibição precisa na timeline.
Backends GPU Suportados
O sistema suporta múltiplos backends gráficos através de implementações condicionais:
| Backend | Macro de Ativação | Função de Inicialização | Notas |
|---|---|---|---|
| OpenGL | MICROPROFILE_GPU_TIMERS_GL | MicroProfileGpuInitGL() | Requer contexto OpenGL ativo |
| D3D11 | MICROPROFILE_GPU_TIMERS_D3D11 | MicroProfileGpuInitD3D11(Device) | Single-threaded por padrão |
| D3D12 | MICROPROFILE_GPU_TIMERS_D3D12 | MicroProfileGpuInitD3D12(Device, Queue) | Suporta modo multi-threaded |
| Vulkan | MICROPROFILE_GPU_TIMERS_VK | MicroProfileGpuInitVK(Device, PhysicalDevice, Queue) | Similar ao D3D12 |
Tratamento de Casos Especiais
Modo Multi-threaded (D3D12/Vulkan):
Para APIs modernas que suportam gravação de comandos em múltiplas threads, o sistema fornece um protocolo específico:
cpp1#define MICROPROFILE_GPU_TIMERS_MULTITHREADED 1 2 3// Na thread de gravação: 4MicroProfileGpuBegin(CommandList); 5// ... gravar comandos com markers ... 6work = MicroProfileGpuEnd(); 7 8// Na thread de submissão: 9MicroProfileGpuSubmit(work); // Mesma ordem que ExecuteCommandLists
Limitação Conhecida: GPU timestamps só podem ser inseridos de uma única thread por contexto, conforme documentado em [microprofile.h:113].
Sistema de Visualização e Interface
O sistema de visualização é responsável por transformar os dados de profiling brutos em representações visuais compreensíveis. O microprofile suporta três modos de visualização distintos, cada um com suas características específicas.
Modalidades de Visualização
| Modo | Descrição | Requisitos |
|---|---|---|
| In-Game UI | Interface renderizada sobre a aplicação | Callbacks de renderização implementados |
| Servidor Web | Interface HTML servida via HTTP embutido | Thread dedicada para servidor |
| Exportação HTML | Arquivo estático para análise offline | Nenhum requisito em runtime |
Callbacks de Renderização Obrigatórios
Para a interface in-game, a aplicação deve implementar três callbacks de renderização:
cpp1void MicroProfileDrawText(int nX, int nY, uint32_t nColor, 2 const char* pText, uint32_t nNumCharacters); 3void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, 4 uint32_t nColor, MicroProfileBoxType); 5void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, 6 uint32_t nColor);
Estes callbacks permitem que o microprofile desenhe sua interface usando qualquer backend de renderização suportado pela aplicação (OpenGL, DirectX, Vulkan, ou até software rendering).
Ciclo de Atualização da Interface
O sistema de visualização opera em um ciclo por-frame coordenado por três funções principais:
-
MicroProfileFlip(): Chamada uma vez por frame para finalizar a captura do frame atual e preparar o próximo. Esta função gerencia a rotação dos buffers circulares e dispara a geração de dados para o servidor web se ativo [microprofile.h:34]. -
MicroProfileDraw(): Chamada uma vez por frame para renderizar a interface in-game. Esta função usa os callbacks de renderização para desenhar a UI sobre a aplicação [microprofile.h:35]. -
MicroProfileToggleDisplayMode(): Alterna entre os modos de exibição (oculto, barras de frame, interface completa). Deve ser vinculado a uma tecla pelo desenvolvedor [microprofile.h:36].
Modos de Exibição
A interface in-game suporta múltiplos modos de exibição:
- Modo Completo: Exibe toda a interface com timelines, contadores e controles
- Modo Frame Bar: Exibe apenas a barra de frames para detecção de spikes sem obstruir gameplay [README.md:39-40]
- Modo Oculto: Profiling continua ativo mas a interface não é renderizada
Sistema de Contadores
Além de timers, o sistema suporta contadores para rastrear valores que mudam ao longo do tempo:
cpp1// Exemplo de uso de contador 2MICROPROFILE_COUNTER_ADD("Memory", "Allocations", 1); 3MICROPROFILE_COUNTER_SET("Network", "BytesReceived", totalBytes);
Contadores podem ser grafados em tempo real junto com timers, permitindo correlacionar métricas de desempenho com valores de estado da aplicação [README.md:15-16].
Integração e Dependências
A integração do microprofile em um projeto requer configuração de build, implementação de callbacks obrigatórios e inicialização dos sistemas apropriados.
Funções de Callback Obrigatórias
Além dos callbacks de renderização, o sistema requer implementação de funções de threading:
cpp1const char* MicroProfileGetThreadName(); // Retorna nome da thread atual
Esta função é usada para exibir nomes de threads significativos na visualização detalhada, facilitando a identificação de gargalos em aplicações multi-threaded.
Fluxo de Inicialização
正在加载图表渲染器...
Etapas do fluxo:
-
Configuração de Build: O arquivo de implementação deve definir
MICROPROFILE_IMPLantes de incluirmicroprofile.hpara gerar as definições de todas as funções [microprofile.h:89]. -
Inicialização de Thread: Cada thread que usa profiling deve chamar
MicroProfileOnThreadCreate()no início de sua execução [microprofile.h:30]. -
Inicialização GPU (Opcional): Se usar GPU profiling, inicializar o backend apropriado (
MicroProfileGpuInitGL(),MicroProfileGpuInitD3D11(), etc.) [microprofile.h:91]. -
Loop Principal: Integrar
MicroProfileFlip()eMicroProfileDraw()no loop principal da aplicação [microprofile.h:34-35]. -
Controles de Entrada: Vincular
MicroProfileToggleDisplayMode()eMicroProfileTogglePause()a teclas ou controles de UI [microprofile.h:36-37].
Dependências de Plataforma
O microprofile é projetado para ser altamente portátil, com suporte conhecido para:
- Windows XP e superiores
- Linux
- macOS (OSX)
- iOS
- Android
- Xbox One
A portabilidade é alcançada através de abstrações condicionais de plataforma para:
- Timers de alta resolução
- Primitivas de sincronização (mutexes, condition variables)
- Threads e TLS (Thread-Local Storage)
- Sockets para servidor web
Configuração de Backends GPU
A tabela abaixo resume as opções de configuração para cada backend GPU:
| Backend | Defines Necessários | Inicialização | Contexto Multi-thread |
|---|---|---|---|
| OpenGL | MICROPROFILE_GPU_TIMERS_GL 1 | MicroProfileGpuInitGL() | Não suportado |
| D3D11 | MICROPROFILE_GPU_TIMERS_D3D11 1 | MicroProfileGpuInitD3D11(Device) | Não suportado |
| D3D12 | MICROPROFILE_GPU_TIMERS_D3D12 1 | MicroProfileGpuInitD3D12(Device, Queue) | MICROPROFILE_GPU_TIMERS_MULTITHREADED 1 |
| Vulkan | MICROPROFILE_GPU_TIMERS_VK 1 | MicroProfileGpuInitVK(Device, PhysicalDevice, Queue) | MICROPROFILE_GPU_TIMERS_MULTITHREADED 1 |
Decisões de Design e Trade-offs
O microprofile faz várias escolhas de design arquiteturais que refletem suas prioridades de baixa sobrecarga e facilidade de integração.
1. Header-Only Library
Decisão: Toda a funcionalidade é implementada em um único arquivo de cabeçalho.
Vantagens:
- Integração trivial (apenas incluir o header)
- Sem dependências de bibliotecas externas
- Build simplificado
Desvantagens:
- Tempo de compilação aumentado quando
MICROPROFILE_IMPLé definido - Código fonte exposto ao projeto integrador
Justificativa: A simplicidade de integração foi priorizada sobre tempo de compilação, já que o profiler é tipicamente usado durante desenvolvimento e não em builds de produção.
2. Unificação de APIs CPU/GPU
Decisão: Macros únicos (MicroProfileEnter/MicroProfileLeave) detectam automaticamente se o grupo é CPU ou GPU.
Vantagens:
- API mais simples para desenvolvedores
- Menos macros para aprender
- Código de instrumentação mais limpo
Desvantagens:
- Overhead mínimo de verificação de tipo em runtime
- Menos controle explícito sobre comportamento
Justificativa: A detecção automática reduz erros de uso e simplifica a instrumentação de código misto CPU/GPU [README.md:43].
3. Buffers Circulares por Thread
Decisão: Cada thread mantém seus próprios buffers de dados de profiling.
Vantagens:
- Elimina contenção de locks durante captura
- Escalabilidade em sistemas multi-core
- Baixa sobrecarga por medição
Desvantagens:
- Uso de memória proporcional ao número de threads
- Agregação requer processamento de múltiplos buffers
Justificativa: A contenção de locks seria inaceitável para um profiler de desempenho, tornando esta escolha obrigatória.
4. Alocação Lazy de Buffers
Decisão: Buffers grandes são alocados apenas quando grupos são efetivamente capturados.
Vantagens:
- Reduz overhead de memória quando profiler está inativo
- Permite compilar em builds de produção sem penalidade
Desvantagens:
- Possível stutter na primeira captura de cada grupo
- Complexidade adicional no gerenciamento de memória
Justificativa: Permite que o profiler seja incluído em builds de produção sem impacto significativo em memória [README.md:49].
5. Servidor Web em Thread Dedicada
Decisão: O servidor web embutido roda em sua própria thread.
Vantagens:
- Não bloqueia o thread principal da aplicação
- Performance melhorada na geração de HTML
- Responsividade da UI web mantida
Desvantagens:
- Complexidade de sincronização entre threads
- Requer suporte a threading na plataforma
Justificativa: A geração de HTML pode ser custosa; executá-la em thread separada mantém a aplicação principal responsiva [README.md:47].
6. Seleção Dinâmica de Backend GPU
Decisão: Múltiplos backends GPU podem ser compilados e um é selecionado em runtime.
Vantagens:
- Mesmo binário funciona em diferentes configurações de hardware
- Flexibilidade para aplicações multi-plataforma
- Teste simplificado de múltiplos backends
Desvantagens:
- Tamanho do binário aumentado
- Código morto se apenas um backend é usado
Justificativa: Aplicações gráficas frequentemente suportam múltiplas APIs; permitir seleção dinâmica simplifica o código da aplicação [README.md:46].
7. Cores Automáticas Baseadas em Hash
Decisão: Valor de cor -1 gera cor automaticamente baseada no hash do nome.
Vantagens:
- Menos trabalho para desenvolvedores
- Cores consistentes entre execuções
- Visualização automaticamente diversificada
Desvantagens:
- Menos controle sobre estética
- Possíveis colisões de cor
Justificativa: A conveniência supera a perda de controle fino sobre cores [README.md:42].
8. Limitação de Timestamps GPU a Uma Thread
Decisão: GPU timestamps só podem ser inseridos de uma thread por contexto.
Vantagens:
- Simplifica implementação
- Evita complexidade de sincronização GPU
- Compatível com modelos de uso comuns
Desvantagens:
- Limita profiling GPU em engines multi-threaded
- Requer design cuidadoso em alguns casos
Justificativa: A maioria das APIs gráficas tem restrições similares; a limitação é aceitável dado o benefício em simplicidade [microprofile.h:113].
Tabela de Seleção de Tecnologias
| Tecnologia | Uso | Justificativa | Alternativas Consideradas |
|---|---|---|---|
| Header-only | Distribuição | Integração trivial, sem dependências | Biblioteca estática, dinâmica |
| Macros C++ | Instrumentação | Zero-overhead quando desabilitado, RAII automático | Funções inline, templates |
| Buffers circulares | Armazenamento | Uso de memória limitado, sem alocações em runtime | Vetores dinâmicos, mapas |
| TLS (Thread-Local Storage) | Dados por thread | Elimina contenção, escalável | Mutexes, lock-free queues |
| Callbacks de renderização | Visualização in-game | Flexibilidade de backend, sem dependências gráficas | Renderização embutida (OpenGL/DX) |
| Servidor HTTP embutido | Visualização web | Sem dependências externas, funciona em qualquer navegador | WebSocket, bibliotecas externas |
| Query objects GPU | Timing GPU | Precisão de nanosegundos, suportado por todas APIs | Estimativas baseadas em CPU |
| Hash de nomes | Identificação de timers | Comparação rápida, IDs estáveis | Strings, enums |
| Alocação lazy | Gerenciamento de memória | Overhead zero quando inativo | Alocação antecipada |
| Cores por hash | Visualização | Conveniência, consistência | Paletas manuais |
Relações de Dependência entre Módulos
O diagrama abaixo ilustra as dependências entre os principais módulos do sistema:
正在加载图表渲染器...
Análise de dependências:
-
Macros → Timer Manager: Os macros de instrumentação dependem do gerenciador de timers para obter e registrar timestamps.
-
Timer Manager → CPU/GPU Timers: O gerenciador abstrai as diferenças entre plataformas e backends, delegando a captura real para implementações específicas.
-
Timer Manager → Buffers: Dados de timing são armazenados em buffers circulares para análise posterior.
-
Buffers → Sistemas de Saída: Todos os sistemas de visualização consomem dados dos mesmos buffers, garantindo consistência.
-
Thread Management → Buffers/Web: O sistema de threads gerencia buffers por thread e a thread dedicada do servidor web.
-
UI → Render Callbacks: A interface in-game é independente de backend gráfico, delegando renderização para callbacks implementados pela aplicação.
Configuração e Fluxo de Inicialização Detalhado
A configuração do microprofile envolve várias etapas que devem ser realizadas na ordem correta para garantir funcionamento adequado.
Etapa 1: Configuração de Build
No arquivo de implementação (tipicamente microprofile.cpp):
cpp1#define MICROPROFILE_IMPL 2#include "microprofile.h" 3 4// Opcional: habilitar backends GPU 5#define MICROPROFILE_GPU_TIMERS_GL 1 6// ou 7#define MICROPROFILE_GPU_TIMERS_D3D11 1 8// etc.
A ordem é crítica: MICROPROFILE_IMPL deve ser definido antes de incluir o header para gerar as definições de funções [microprofile.h:89].
Etapa 2: Implementação de Callbacks
A aplicação deve implementar os callbacks obrigatórios:
cpp1// Renderização (necessários para UI in-game) 2void MicroProfileDrawText(int nX, int nY, uint32_t nColor, 3 const char* pText, uint32_t nNumCharacters) { 4 // Usar sistema de texto da aplicação 5} 6 7void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, 8 uint32_t nColor, MicroProfileBoxType type) { 9 // Usar sistema de renderização 2D da aplicação 10} 11 12void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, 13 uint32_t nColor) { 14 // Desenhar linhas 2D 15} 16 17// Threading 18const char* MicroProfileGetThreadName() { 19 // Retornar nome significativo para a thread atual 20 return "MainThread"; 21}
Etapa 3: Inicialização em Runtime
A inicialização deve ocorrer no início da aplicação, antes de qualquer código instrumentado:
cpp1void InitializeProfiler() { 2 // Inicializar thread principal 3 MicroProfileOnThreadCreate("MainThread"); 4 5 // Inicializar backend GPU (se aplicável) 6 #if MICROPROFILE_GPU_TIMERS_GL 7 MicroProfileGpuInitGL(); 8 #elif MICROPROFILE_GPU_TIMERS_D3D11 9 MicroProfileGpuInitD3D11(g_pD3D11Device); 10 #elif MICROPROFILE_GPU_TIMERS_D3D12 11 MicroProfileGpuInitD3D12(g_pD3D12Device, g_pCommandQueue); 12 #elif MICROPROFILE_GPU_TIMERS_VK 13 MicroProfileGpuInitVK(g_pVkDevice, g_pVkPhysicalDevice, g_pVkQueue); 14 #endif 15}
Etapa 4: Integração no Loop Principal
O loop principal deve integrar as funções de atualização:
cpp1void MainLoop() { 2 while (running) { 3 // Início do frame 4 BeginFrame(); 5 6 // Processar entrada 7 if (KeyPressed(KEY_F1)) { 8 MicroProfileToggleDisplayMode(); 9 } 10 if (KeyPressed(KEY_F2)) { 11 MicroProfileTogglePause(); 12 } 13 14 // Atualizar lógica (código instrumentado) 15 Update(); 16 17 // Renderizar (código instrumentado) 18 Render(); 19 20 // Finalizar frame de profiling 21 MicroProfileFlip(); 22 23 // Renderizar UI do profiler (sobre a aplicação) 24 MicroProfileDraw(); 25 26 // Apresentar frame 27 Present(); 28 } 29}
Configuração para Modo Multi-threaded GPU
Para D3D12 ou Vulkan com gravação multi-threaded:
cpp1// Configuração 2#define MICROPROFILE_GPU_TIMERS_MULTITHREADED 1 3 4// Thread de gravação 5void RecordCommandList(VkCommandBuffer cmd) { 6 MicroProfileGpuBegin(cmd); 7 8 // Gravar comandos com markers de profiling 9 MICROPROFILE_SCOPEGPU("RenderPass", "Geometry", -1); 10 // ... comandos ... 11 12 MicroProfileGpuEnd(); 13} 14 15// Thread de submissão 16void SubmitCommandLists() { 17 // Coletar work de todas as threads 18 for (auto& work : pendingWork) { 19 MicroProfileGpuSubmit(work); 20 } 21 22 // Submeter à GPU 23 vkQueueSubmit(...); 24}
Verificação de Integração Correta
Para verificar se a integração está funcionando:
-
UI In-Game: Pressionar a tecla vinculada a
MicroProfileToggleDisplayMode()ou clicar no canto superior esquerdo da tela [microprofile.h:72]. -
Servidor Web: Acessar
http://localhost:1337(porta padrão) em um navegador. -
Exportação HTML: Chamar
MicroProfileDumpHtmlToFile()para gerar arquivo estático.
Se nenhum dado aparecer, verificar:
- Callbacks de renderização estão implementados corretamente
MicroProfileFlip()está sendo chamado todo frame- Código está instrumentado com macros
- Thread principal foi registrada com
MicroProfileOnThreadCreate()
