Há muito trabalho necessário na CPU para configurar um quadro para a GPU, e boa parte desse trabalho está dentro do driver gráfico. Antes do DX12 / Vulkan, o trabalho do driver gráfico era essencialmente forçado a ser encadeado pelo design da API.
A esperança é que o DX12 / Vulkan elimine essa restrição, permitindo que o trabalho do driver seja executado em paralelo em vários threads da CPU em um quadro. Isso permitirá o uso mais eficiente de CPUs multicore, permitindo que os mecanismos de jogo forneçam cenas mais complexas sem se tornarem dependentes da CPU. Essa é a esperança - se isso será realizado na prática é algo que teremos que esperar para ver nos próximos anos.
Para elaborar um pouco: a saída de um renderizador de mecanismo de jogo é um fluxo de chamadas da API DX / GL que descrevem a sequência de operações para renderizar um quadro. No entanto, existe uma grande distância entre o fluxo de chamadas da API e os buffers reais de comando binário que o hardware da GPU consome. O driver precisa "compilar" as chamadas da API no idioma da máquina da GPU, por assim dizer. Esse não é um processo trivial - envolve muita conversão de conceitos de API em realidades de hardware de baixo nível, validação para garantir que a GPU nunca seja configurada em um estado inválido, disputando alocações e dados de memória, rastreando alterações de estado para emitir o corrigir comandos de baixo nível, e assim por diante. O driver gráfico é responsável por tudo isso.
Nas APIs DX11 / GL4 e anteriores, esse trabalho geralmente é realizado por um único thread de driver. Mesmo se você chamar a API a partir de vários threads (o que você pode fazer usando as listas de comandos adiadas do DX11, por exemplo), ele adicionará algum trabalho a uma fila para que o thread do driver chegue mais tarde. Uma grande razão para isso é o rastreamento de estado que mencionei antes. Muitos dos detalhes de configuração da GPU no nível de hardware requerem conhecimento do estado atual do pipeline de gráficos, portanto, não há uma boa maneira de dividir a lista de comandos em partes que podem ser processadas em paralelo - cada parte precisa saber exatamente em que estado deve iniciar com, mesmo que o pedaço anterior ainda não tenha sido processado.
Essa é uma das grandes coisas que mudaram no DX12 / Vulkan. Por um lado, eles incorporam quase todo o estado do pipeline gráfico em um objeto e, em outro (pelo menos no DX12), quando você começa a criar uma lista de comandos, deve fornecer um estado inicial do pipeline; o estado não é herdado de uma lista de comandos para a seguinte. Em princípio, isso permite que o driver não precise saber nada sobre as listas de comandos anteriores antes de começar a compilar - e que, por sua vez, permite que o aplicativo divida sua renderização em pedaços paralelizáveis, produzindo listas de comandos totalmente compiladas, que podem ser concatenados juntos e enviados à GPU com um mínimo de confusão.
Obviamente, existem muitas outras mudanças nas novas APIs, mas no que diz respeito ao multithreading, essa é a parte mais importante.