Files
dolphin/docs/GPU_Emulation.md
Yaya48 a77c156bc8 IOS: add Starlet LLE milestone through HBC
Boot original Wii firmware through boot0, boot1, boot2 and IOS on an emulated ARM Starlet. Model the required IPC, memory, SD, USB and Bluetooth hardware behavior, including IOS reload into IOS58, and add focused tests, launch utilities and architecture documentation.
2026-08-25 17:11:49 +02:00

52 KiB
Raw Permalink Blame History

Émulation du GPU GameCube/Wii dans Dolphin

État du code documenté : 38e70fda6597dab6b7e5e4d3949b199c0b4f2244.

Cette carte décrit le comportement du dépôt à cette révision. Elle distingue systématiquement le matériel invité, l'état émulé, les optimisations de Dolphin et le travail confié au GPU hôte.

1. Résumé en une phrase

Dolphin n'émule pas un jeu d'instructions de shader : il reçoit le flux de commandes fixe du GPU GX, reproduit ses trois banques d'état CP/XF/BP, convertit les sommets, traduit la transformation et le TEV en shaders ou en calcul logiciel, rend dans une EFB émulée, copie cette EFB vers la RAM/XFB, puis laisse la VI émulée décider quand présenter l'image.

Cette distinction est fondamentale : le GPU invité est une machine à états alimentée par FIFO, alors que le GPU hôte exécute des pipelines modernes créés à partir d'un instantané de cet état.

2. Vocabulaire et frontières

Terme Sens dans cette documentation
PPC / CPU invité Le processeur PowerPC émulé qui exécute le jeu et écrit les commandes GX.
Gather pipe Tampon matériel de 32 octets alimenté par les stores PPC.
PI FIFO Vue CPU du FIFO circulaire en RAM, gérée par le Processor Interface.
CP Command Processor : registres du FIFO et état décrivant les données de sommets.
XF Transform Unit : matrices, éclairage, viewport, projection et génération de coordonnées.
BP Registres raster/TEV/Pixel Engine envoyés dans le flux de commandes.
TEV Texture Environment Unit fixe, jusqu'à 16 étages de combinaison couleur/alpha.
EFB Embedded Frame Buffer natif de 640 × 528, couleur et profondeur.
XFB External Frame Buffer en RAM, lu par la Video Interface pour l'affichage.
VI Video Interface : timings de champs/lignes, adresses XFB et déclenchement de la présentation.
Backend matériel D3D11, D3D12, Metal, OpenGL ou Vulkan ; traduit l'état GX vers l'API hôte.
Backend logiciel Transforme, découpe, rastérise et exécute le TEV sur le CPU hôte.

Les constantes de dimensions sont définies dans VideoCommon.h : EFB 640 × 528, XFB maximal 720 × 576.

3. Carte d'ensemble

flowchart LR
    PPC["PPC invité<br/>stores GX"] --> MMU["MMU<br/>détection WPAR"]
    MMU --> GP["Gather pipe<br/>32 octets"]
    GP --> RAMFIFO["FIFO circulaire<br/>en RAM invitée"]
    GP --> CPREG["Registres CP<br/>pointeurs, distance,<br/>watermarks"]
    RAMFIFO --> FBUF["Tampon vidéo Dolphin<br/>2 MiB"]
    CPREG --> FBUF
    FBUF --> DEC["OpcodeDecoder"]

    DEC --> CPM["État CP<br/>VCD, VAT, arrays"]
    DEC --> XFM["Mémoire XF<br/>matrices, éclairage,<br/>projection"]
    DEC --> BPM["Mémoire BP<br/>raster, textures,<br/>TEV, copies"]
    DEC --> VL["VertexLoader"]

    CPM --> VL
    XFM --> PIPE["Constantes et shaders"]
    BPM --> PIPE
    VL --> VM["VertexManager<br/>batch + indices"]
    VM --> PIPE
    BPM --> TC["TextureCache + TMEM"]
    TC --> PIPE
    PIPE --> GFX["AbstractGfx"]
    GFX --> HOST["API / GPU hôte<br/>ou rasteriseur logiciel"]
    HOST --> EFB["EFB émulée"]

    BPM --> COPY["Copie EFB"]
    EFB --> COPY
    COPY --> XFB["RAM XFB et/ou<br/>copie VRAM"]
    XFB --> VI["Video Interface"]
    VI --> PRES["Presenter"]
    PRES --> SCREEN["Backbuffer / écran"]

    CPREG -. "IRQ CP" .-> PPC
    BPM -. "token / finish" .-> PE["Pixel Engine MMIO"]
    PE -. "IRQ PE" .-> PPC
    PPC -. "peek / poke EFB" .-> EFB

La partie située avant AbstractGfx décide ce que le GPU invité doit faire. La partie située après AbstractGfx décide comment l'API graphique disponible peut le faire.

4. Carte des composants du code

Responsabilité Fichiers principaux Points d'entrée
Détection des writes gather pipe MMU.cpp, GPFifo.cpp MMU::WriteToHardware, GPFifoManager::Write*, UpdateGatherPipe
Vue CPU du FIFO ProcessorInterface.cpp registres PI_FIFO_*
Vue GPU du FIFO et IRQ CP CommandProcessor.cpp RegisterMMIO, GatherPipeBursted, SetCPStatusFromCPU/GPU
Ordonnancement CPU/GPU Fifo.cpp RunGpuLoop, RunGpuOnCpu, SyncGPU, WaitForGpuThread
Syntaxe du flux GX OpcodeDecoding.h, OpcodeDecoding.cpp Run, RunCommand, RunFifo
État CP CPMemory.h, CPMemory.cpp CPState::LoadCPReg
État XF XFMemory.h, XFStructs.cpp, XFStateManager.cpp LoadXFReg, LoadIndexedXF, InvalidateXFRange
État BP et effets de bord BPMemory.h, BPStructs.cpp, BPFunctions.cpp LoadBPReg, BPWritten
Conversion des sommets VertexLoaderManager.cpp, VertexLoader.cpp RefreshLoader, RunVertices
Assemblage et draw VertexManagerBase.cpp, IndexGenerator.cpp PrepareForAdditionalData, Flush, RenderDrawCall
Traduction en shaders VertexShaderGen.cpp, PixelShaderGen.cpp, ShaderCache.cpp Get*ShaderUid, Generate*ShaderCode, GetPipelineForUid
Textures et TMEM TextureCacheBase.cpp, TextureInfo.cpp, TMEM.cpp Load, GetTexture, BindTextures, TMEM::Bind
EFB FramebufferManager.cpp, EFBInterface.cpp BindEFBFramebuffer, PeekEFB*, PokeEFB*, ClearEFB
Copies EFB/XFB BPStructs.cpp, TextureCacheBase.cpp BPMEM_TRIGGER_EFB_COPY, CopyRenderTargetToTexture
Tokens, finish et IRQ PE PixelEngine.cpp SetToken, SetFinish, UpdateInterrupts
Scanout VI VideoInterface.cpp Update, BeginField, EndField, OutputField
Présentation VideoBackendBase.cpp, Present.cpp Video_OutputXFB, ViSwap, Present
Frontière API hôte AbstractGfx.h CreatePipeline, SetPipeline, DrawIndexed, PresentBackbuffer
Sauvegarde de l'état vidéo VideoState.cpp VideoCommon_DoState

5. Initialisation et modèle de threads

5.1 Création du backend

Core.cpp initialise le backend sur le thread qui l'utilisera. Cette contrainte est notamment requise par OpenGL.

VideoBackendBase::InitializeShared construit les objets communs : AbstractGfx, VertexManagerBase, cache de shaders, FramebufferManager, cache de textures, Presenter, compteurs de performance et bounding box. Il initialise ensuite CP, FIFO, PE, BP, chargeurs de sommets, managers de constantes XF et TMEM.

5.2 Les trois chemins d'exécution

Mode Propriétaire du décodage et du rendu Fonction centrale Particularité
Single Core Thread CPU-GPU unique FifoManager::RunGpuOnCpu Le temps GPU est consommé par des événements CoreTiming; les requêtes vidéo sont exécutées directement.
Dual Core normal Thread vidéo dédié FifoManager::RunGpuLoop Le CPU produit le FIFO pendant que le thread vidéo le consomme.
Dual Core déterministe CPU pour la copie/prélecture, thread vidéo pour le décodage réel RunGpuOnCpu + branche déterministe de RunGpuLoop Un préprocesseur CPU fige les dépendances mémoire et maintient un second état CP.

Le réglage « Synchronize GPU Thread » est orthogonal. Quand il est actif, m_sync_ticks mesure la distance temporelle émulée : le GPU est réveillé au seuil minimal et le CPU attend au seuil maximal. MAIN_SYNC_GPU_OVERCLOCK met à l'échelle le coût estimé des commandes.

5.3 Pourquoi le mode déterministe prétraite le FIFO

Lire une display list ou une source d'indexed XF load directement dans la RAM invitée depuis le thread vidéo crée une course : le CPU peut modifier cette RAM avant que le GPU émulé ne l'ait lue. Le mode déterministe résout ce problème ainsi :

  1. le CPU copie chaque burst de 32 octets dans m_video_buffer ;
  2. RunFifo<true> décode juste assez pour maintenir g_preprocess_cp_state ;
  3. les display lists et indexed XF loads sont copiés dans m_fifo_aux_data ;
  4. le thread vidéo exécute RunFifo<false> et consomme ces instantanés plutôt que la RAM devenue mutable ;
  5. les commandes PE token/finish sont planifiées par le préprocesseur, puis ne le sont pas une seconde fois par le décodage principal.

g_main_cp_state est donc l'état qui rend ; g_preprocess_cp_state est l'état qui permet de calculer la taille des futures commandes et leurs dépendances mémoire.

5.4 Frontière des requêtes asynchrones

AsyncRequests transporte vers le thread vidéo les opérations initiées côté CPU : présentation, lectures EFB, résultats de performance, bounding box et savestates. Avant de vider cette file, PullEvents appelle g_vertex_manager->Flush() afin qu'une lecture observe tous les draws antérieurs.

Une requête bloquante attend son résultat. Une requête non bloquante réveille le FIFO et sera traitée au prochain passage du thread vidéo. En Single Core, le mode passthrough exécute directement le callback.

5.5 Quatre opérations souvent confondues

Opération Ce qu'elle attend ou soumet
VertexManagerBase::Flush Termine le batch GX courant et émet éventuellement un draw hôte.
FifoManager::FlushGpu Attend que la boucle FIFO du thread vidéo atteigne un point de repos.
AbstractGfx::Flush Soumet/segmente le command buffer de l'API hôte ; ne signifie pas forcément GPU hôte idle.
AbstractGfx::WaitForGPUIdle Attend réellement l'achèvement du GPU hôte quand un backend l'implémente.

6. Du store PPC au FIFO GPU

6.1 Détection du gather pipe

Le WPAR du PPC pointe habituellement sur l'adresse physique 0x0C008000, constante GATHER_PIPE_PHYSICAL_ADDRESS. MMU.cpp reconnaît les stores non cachés correspondants et les redirige vers GPFifoManager::Write8/16/32/64.

Les écritures multi-octets sont converties en big-endian avant d'être ajoutées au tampon, de sorte que le flux mémoire corresponde au format GX. Les JIT x64 et ARM64 possèdent des chemins rapides qui peuvent différer le contrôle de remplissage jusqu'à la fin d'un bloc compilé.

6.2 Burst de 32 octets

À chaque bloc complet :

  1. GPFifoManager::UpdateGatherPipe copie 32 octets vers ProcessorInterface::m_fifo_cpu_write_pointer en RAM invitée ;
  2. le pointeur PI avance de 32 octets ou reboucle de end vers base ;
  3. CommandProcessorManager::GatherPipeBursted met à jour la vue CP si PI et CP sont liés ;
  4. le FIFO vidéo est réveillé ;
  5. les octets excédentaires du tampon gather sont ramenés au début.

Le tampon alloué accepte jusqu'à 16 bursts afin que les chemins JIT rapides puissent grouper les writes. Le bit WPAR BNE n'est pas réellement émulé : IsBNE() renvoie toujours false pour éviter les blocages de logiciels utilisant les display lists.

6.3 Deux vues du même anneau

Le PI expose la base, la fin et le write pointer CPU. Le CP expose en plus le read pointer, la distance lecture-écriture, les seuils haut/bas et un breakpoint. Quand GPLinkEnable vaut 1, GatherPipeBursted maintient les pointeurs PI et CP identiques, avance CPWritePointer et ajoute 32 à CPReadWriteDistance.

Les adresses CP sont alignées sur 32 octets. Le masque physique est 0x03ffffff sur GameCube et 0x1fffffff sur Wii. La fin de l'anneau est inclusive dans la logique de pointeur : un pointeur égal à CPEnd reboucle vers CPBase au burst suivant.

Si les FIFO ne sont pas liés, le burst reste écrit par le PI en RAM, mais le CP n'avance pas automatiquement. Dolphin réveille tout de même le GPU et protège un cas de double-buffering en Dual Core par un FlushGpu ciblé.

6.4 Statut, watermarks, breakpoint et interruption CP

SetCPStatusFromCPU/GPU calcule :

  • overflow si CPReadWriteDistance > CPHiWatermark ;
  • underflow si CPReadWriteDistance < CPLoWatermark ;
  • breakpoint si CPReadPointer == CPBreakpoint et le contrôle l'autorise.

Chaque cause est combinée avec son bit d'activation, puis l'ensemble est encore conditionné par GPReadEnable. L'interruption CP utilise la cause PI 0x800. En Dual Core, le thread vidéo planifie sa modification sur le thread CPU via CoreTiming et se bloque temporairement avec m_interrupt_waiting afin de préserver l'ordre.

Une lecture du registre de statut synchronise d'abord le GPU. En Dual Core, les lectures publiques du read pointer et de la distance utilisent SafeCPReadPointer, qui n'est avancé que lorsque tous les octets déjà copiés dans le tampon Dolphin ont été consommés. Cela évite d'annoncer au jeu qu'une commande partielle est terminée.

Limites de cette interface :

  • le registre CP clear est intentionnellement sans effet ;
  • la plupart des métriques CP renvoient zéro, CLKS_PER_VTX_OUT renvoie 4 ;
  • les timings de FIFO ne sont pas une simulation cycle par cycle.

7. Consommation du FIFO

7.1 Tampon intermédiaire

Le thread consommateur lit la RAM invitée par blocs de 32 octets et les copie dans m_video_buffer, un tampon de 2 MiB avec 4 octets de marge pour les overreads SIMD. Ce second tampon est nécessaire parce qu'une commande GX peut traverser la frontière d'un burst.

Après chaque burst en mode normal :

  1. OpcodeDecoder::RunFifo consomme toutes les commandes complètes disponibles ;
  2. CPReadPointer avance ou reboucle ;
  3. CPReadWriteDistance diminue de 32 ;
  4. le statut CP et les interruptions sont recalculés ;
  5. les requêtes asynchrones sont traitées.

Quand le FIFO devient vide, le VertexManager est flushé et le cache de peeks EFB peut être rafraîchi. Vider le FIFO n'implique donc pas qu'un batch reste indéfiniment en attente.

7.2 Séquence Dual Core normale

sequenceDiagram
    participant CPU as Thread CPU
    participant GP as Gather pipe
    participant RAM as FIFO en RAM
    participant CP as Command Processor
    participant GPU as Thread vidéo
    participant OD as OpcodeDecoder
    participant VM as VertexManager
    participant API as API/GPU hôte

    CPU->>GP: store GX
    loop chaque tranche complète de 32 octets
        GP->>RAM: CopyToEmu(write_ptr, 32)
        GP->>CP: GatherPipeBursted()
        CP->>CP: write_ptr += 32, distance += 32
        CP-->>GPU: réveil RunGpu()
    end
    GPU->>RAM: CopyFromEmu(read_ptr, 32)
    GPU->>OD: RunFifo(octets disponibles)
    alt commande complète
        OD->>VM: état ou primitive
    else commande partielle
        OD-->>GPU: 0 octet consommé
    end
    GPU->>CP: read_ptr += 32, distance -= 32
    VM->>API: Flush puis DrawIndexed

8. Grammaire du flux GX

OpcodeDecoding.h contient la grammaire commune. RunCommand renvoie zéro si la commande n'est pas encore entière ; aucun octet n'est alors perdu.

Opcode Taille encodée Effet Coût estimé dans RunCallback
0x00 NOP 1 par NOP Fusionne une suite de NOP en un callback. 6 cycles par NOP
0x08 LOAD_CP 6 Sous-commande 8 bits + valeur big-endian 32 bits. 12
0x10 LOAD_XF 5 + 4 × n, n=1..16 Adresse XF 16 bits + n mots 32 bits. 18 + 6 × n
0x20/28/30/38 LOAD_INDX 5 Copie indexée vers XF via les arrays CP A/B/C/D. 6
0x40 CALL_DL 9 Adresse + taille, toutes deux forcées à l'alignement 32. 6 + contenu
0x44 métriques 1 Reconnue et journalisée, sans sémantique de métrique. 6
0x48 invalidation vertex cache 1 Reconnue et journalisée, sans cache de sommets matériel à invalider. 6
0x61 LOAD_BP 5 Adresse BP 8 bits + valeur big-endian 24 bits. 12
0x80..0xBF primitive 3 + count × vertex_size Type dans les bits 3..6, VAT dans les bits 0..2, count 16 bits. 12 × count + 6
autre 1 Avertissement/erreur selon l'opcode. 1

Le vertex_size n'est pas présent dans la commande : il est dérivé de l'état CP courant. C'est la raison pour laquelle une display list ne peut pas être précompilée indépendamment de l'état qui la précède.

Les display lists sont interprétées récursivement avec le même callback, mais un booléen interdit l'imbrication récursive. En mode déterministe, leur contenu est capturé lors du prétraitement. En mode normal, il est lu dans la RAM au moment de l'exécution par le thread vidéo.

Lors d'un enregistrement FIFO, les commandes internes d'une display list sont aplaties dans le flux enregistré ; le CALL_DL lui-même n'est pas écrit une seconde fois.

9. Les trois banques d'état

9.1 CP : comment lire les sommets

CPState contient :

  • deux registres d'indices de matrices ;
  • le Vertex Component Descriptor (VCD) indiquant, pour chaque attribut, absent/direct/index 8/index 16 ;
  • huit Vertex Attribute Tables (VAT) décrivant nombre de composantes, type, fraction et format de couleur ;
  • 16 bases et strides d'arrays, dont 12 pour les attributs de sommets et 4 pour les indexed XF loads.
Groupe CP Commandes Rôle
Matrices 0x30, 0x40 Matrice position/normale et matrices texture 0..7.
VCD 0x50, 0x60 Présence et adressage de position, normale, couleurs, texcoords et indices de matrices.
VAT A/B/C 0x70..0x77, 0x80..0x87, 0x90..0x97 Huit formats complets de sommet, répartis sur trois groupes.
Array base 0xA0..0xAF Adresse physique de chaque array.
Array stride 0xB0..0xBF Pas 8 bits de chaque array.

Une modification VCD/VAT marque les chargeurs concernés comme sales. Une modification de base invalide les pointeurs d'arrays résolus. Les changements de matrices sont transmis au XFStateManager, qui flushe si nécessaire le batch courant.

9.2 XF : transformer les sommets

La structure XFMemory reproduit l'espace d'adressage XF :

Plage XF Contenu
0x0000..0x00FF Matrices de position/transformation.
0x0400..0x045F Matrices normales.
0x0500..0x05FF Matrices post-texture.
0x0600..0x067F Huit lumières.
0x1000..0x1057 Registres : vertex spec, canaux, matériaux, viewport, projection, texgen.

LoadXFReg peut traverser la frontière mémoire/registres. Les mots sont convertis depuis le big-endian. Une écriture mémoire flushe le batch puis invalide précisément les plages de constantes touchées. Une écriture de registre flushe uniquement quand l'ancien état pourrait être utilisé par des sommets déjà accumulés.

Les indexed XF loads calculent :

source = cp.array_base[array] + cp.array_stride[array] × index
destination = xfmem[address .. address + size)

En Dual Core déterministe, ces octets source passent par le FIFO auxiliaire.

9.3 BP : raster, textures, TEV et opérations

BPMemory est un tableau logique de 256 mots de 24 bits. Les groupes les plus importants sont :

Adresses BP Contenu principal
0x00 Nombre de texgens/canaux/étages TEV/indirects, culling, zfreeze.
0x06..0x1F Matrices et commandes de textures indirectes.
0x20..0x3F Scissor, lignes/points, ordres TEV, tailles texcoords.
0x40..0x44 Z test, blend/logic op, destination alpha, format EFB, field mask.
0x45..0x59 Draw done, tokens, paramètres/trigger de copie EFB, clear et bounding box.
0x60..0x69 Préchargement TMEM, TLUT, invalidation et quelques métriques/modes de champ.
0x80..0xBF Huit unités de texture : sampling, LOD, taille, format, adresses RAM/TMEM/TLUT.
0xC0..0xDF 16 combinateurs TEV couleur et alpha.
0xE0..0xE7 Quatre registres TEV et quatre couleurs constantes.
0xE8..0xF2 Fog range, paramètres et couleur du fog.
0xF3..0xF5 Alpha test et Z texture.
0xF6..0xFD Sélection des constantes et tables de swizzle.
0xFE Masque one-shot de la prochaine écriture BP.

LoadBPReg applique le masque, calcule les bits modifiés, réinitialise le masque sauf lorsqu'il est lui-même écrit, puis appelle BPWritten.

Une écriture identique est normalement ignorée. Les commandes à effets de bord — copie, token, draw done, TLUT, invalidation, preload, clear bbox/perf — restent exécutées même si leur valeur est inchangée. Toute autre écriture effective commence par FlushPipeline, donc aucun sommet accumulé n'est rendu avec l'état BP nouveau par erreur.

10. Décodage et conversion des sommets

10.1 Création d'un chargeur

VertexLoaderUID est dérivé du VCD et du VAT sélectionné. VertexLoaderManager réutilise un VertexLoaderBase déjà compilé pour cet UID ou en crée un nouveau. Selon l'architecture hôte, le chargeur est générique, x64 ou ARM64.

Le chargeur :

  1. calcule la taille exacte d'un sommet dans le FIFO ;
  2. lit chaque attribut direct ou son index 8/16 bits ;
  3. résout les bases/strides CP pour les attributs indexés ;
  4. convertit positions, normales, couleurs et texcoords vers un PortableVertexDeclaration adapté à l'API hôte ;
  5. conserve certains derniers attributs pour les comportements matériels zfreeze, normales manquantes et emboss mapping.

Les bases invalides ne sont résolues que si le VCD active réellement l'array correspondant, ce qui tolère les jeux laissant des adresses poubelles dans des arrays inutilisés.

10.2 Cohérence CP/XF

Avant la première primitive après un changement pertinent, CheckCPConfiguration compare le nombre de couleurs, normales et texcoords produit par CP au vertex spec attendu par XF. Il compare aussi les indices de matrices. Dolphin journalise, déclenche des analytics et continue autant que possible ; le matériel réel semble pouvoir se bloquer sur certaines incohérences.

10.3 Batching et primitives

VertexManagerBase::PrepareForAdditionalData choisit la topologie hôte, vérifie la place restante et flushe si le type de primitive, le format ou la capacité l'exige. IndexGenerator convertit :

  • quads, triangles, strips et fans vers listes/strips de triangles ;
  • lignes et line strips vers la topologie disponible ou vers une expansion shader ;
  • points vers points natifs ou quads expansés selon les capacités.

Le primitive restart est utilisé quand le backend le supporte. Les très grandes commandes faciles à scinder sont découpées en groupes de 16 380 sommets. Le CPU culling optionnel peut éviter d'émettre un batch entièrement rejeté ; CullMode::All continue tout de même la conversion nécessaire au calcul de la pente zfreeze.

Un changement de NativeVertexFormat force un flush. Les sommets et indices convertis sont placés dans des stream buffers propres au backend matériel, ou dans les buffers CPU du backend logiciel.

11. Traduction du pipeline fixe GX

11.1 Vertex stage

VertexShaderGen.cpp transforme l'état CP/XF en un UID et en source de shader. Le shader reproduit notamment :

  • choix global ou par sommet de la matrice position/normale ;
  • transformation position et normales ;
  • éclairage des deux canaux couleur/alpha avec huit lumières ;
  • jusqu'à huit texgens réguliers, emboss ou dérivés de couleurs ;
  • matrices texture et post-matrices ;
  • projection, viewport et ajustements de profondeur.

Le réglage pixel lighting peut déplacer une partie de l'éclairage vers le pixel shader. Le geometry shader commun sert aux fonctions qui ne se mappent pas directement, notamment certaines expansions ligne/point et sorties multicouches/stéréo.

11.2 Pixel stage et TEV

PixelShaderGen.cpp encode dans le shader l'état BP qui change la structure du calcul :

  • échantillonnage des textures et mipmaps ;
  • jusqu'à quatre étages indirects et 16 étages TEV ;
  • swizzles, registres prev/c0/c1/c2, constantes K et opérations compare/add/sub ;
  • alpha test ;
  • fog ;
  • Z texture, zfreeze et emplacement early/late du test Z ;
  • quantification/dithering du format EFB ;
  • destination alpha, logic ops et bounding box quand nécessaire.

Les valeurs TEV sont majoritairement manipulées comme entiers dans les shaders générés afin de reproduire les plages et arrondis du combinateur fixe, plutôt que comme un simple mélange flottant.

11.3 État fixe hôte

RasterizationState, DepthState et BlendingState extraient de bpmem ce que l'API hôte peut représenter directement. Le reste est injecté dans les shaders. Les différences de capacités — dual-source blend, framebuffer fetch, early Z, logic ops, plage de profondeur inversée — sont déclarées dans g_backend_info et influencent la variante générée.

11.4 UIDs, cache et ubershaders

À chaque flush, UpdatePipelineConfig construit deux clés :

  • GXPipelineUid pour les shaders spécialisés, contenant format de sommet, UID VS/GS/PS et états raster/depth/blend ;
  • GXUberPipelineUid pour une variante générique dont davantage d'état arrive par constantes.

Les modes de compilation ont les comportements suivants :

Mode Comportement lorsqu'un pipeline spécialisé manque
Synchronous Compile/charge immédiatement et bloque.
Synchronous UberShaders Utilise exclusivement les ubershaders.
Asynchronous UberShaders Compile le spécialisé en arrière-plan et rend provisoirement avec l'ubershader.
Asynchronous Skip Rendering Lance la compilation et saute le draw jusqu'à disponibilité.

ShaderCache conserve modules, pipelines et UIDs sur disque quand le backend le permet. Les managers de constantes VS/GS/PS suivent séparément les plages sales afin de ne réenvoyer que les données XF/BP modifiées.

11.5 Émission du draw

VertexManagerBase::Flush suit cet ordre :

  1. vérifie la cohérence XF/BP ;
  2. charge les textures utilisées et calcule leurs samplers ;
  3. met à jour les constantes et la pente Z ;
  4. applique éventuellement les mods graphiques ;
  5. lie textures et palettes ;
  6. choisit/crée le pipeline ;
  7. upload les constantes, sommets et indices ;
  8. appelle g_gfx->SetPipeline puis DrawIndexed ;
  9. marque les caches EFB potentiellement obsolètes.

AbstractGfx ne connaît aucun registre GameCube/Wii. Par exemple, Vulkan finit par vkCmdDrawIndexed, D3D11 par ID3D11DeviceContext::DrawIndexed, et OpenGL par la commande GL équivalente.

12. Textures, palettes et TMEM

12.1 De BP à une texture hôte

Les registres BP de chacune des huit unités définissent wrap, filtres, LOD, taille, format, adresses RAM/TMEM et TLUT. TextureInfo valide et expose les niveaux de mipmap, les dimensions en blocs et les pointeurs source.

TextureCacheBase::Load :

  1. consulte l'état TMEM et le binding existant ;
  2. calcule un hash des données et, si nécessaire, de la palette ;
  3. cherche une entrée compatible par adresse puis par hash ;
  4. réutilise, réinterprète ou applique une palette à une copie EFB existante si possible ;
  5. sinon décode la texture sur CPU ou par compute shader ;
  6. charge les mipmaps et crée le sampler ;
  7. lie l'entrée à l'unité de texture.

Le cache accepte plusieurs interprétations à la même adresse. Il invalide ou met à jour partiellement les entrées recouvertes par une écriture/copie EFB. Les hashes sûrs améliorent la compatibilité au prix du temps CPU.

12.2 Mémoire TMEM concrète

s_tex_mem est un tableau de 1 MiB. Les commandes de preload et TLUT copient réellement les octets depuis la RAM invitée vers ce tableau. Une texture RGBA8 préchargée peut utiliser séparément les bancs AR et GB. Les indexed palettes référencent aussi cette mémoire.

12.3 Modèle de cache TMEM

TMEM.cpp ne simule pas chaque remplissage de ligne. Il suit, pour chaque unité, deux bancs configurables even et odd, leur taille estimée, leur chevauchement et trois états : INVALID, VALID, CACHED.

  • toutes les textures utilisent le banc even ;
  • mipmapping ou texture 32 bits active aussi odd ;
  • les LOD pairs/impairs ou les moitiés de canaux sont répartis entre les bancs ;
  • une texture qui tient et ne chevauche aucune unité active devient CACHED ;
  • une texture trop grande ou chevauchée reste VALID et devra être revalidée/hashée ;
  • une invalidation BP invalide actuellement toutes les unités, car le sens exact de son paramètre n'est pas connu.

Ce modèle est explicitement heuristique. Il reproduit les jeux qui réutilisent volontairement une ancienne texture encore en TMEM et ceux qui comptent sur l'éviction naturelle d'une grande texture, sans prétendre reconstruire le contenu de cache texel par texel.

13. EFB : cible de rendu et accès CPU

13.1 Représentation dans les backends matériels

FramebufferManager crée une texture couleur, une texture profondeur et un framebuffer hôte. La taille est la résolution interne configurée appliquée aux 640 × 528 pixels natifs. Des textures supplémentaires servent aux resolves MSAA, conversions de format, readbacks et caches de peeks.

Les backends matériels utilisent normalement une couleur RGBA8 et une profondeur 24 bits ou plus, même quand le jeu choisit RGB8, RGBA6, RGB565/Z16 ou Z-only. Dolphin réintroduit la quantification pertinente dans les shaders, clears et lectures. Le depth buffer hôte est inversé lorsque nécessaire pour conserver davantage de précision.

Un changement de format EFB peut réinterpréter les bits existants par un pipeline de conversion si bEFBEmulateFormatChanges est actif. Sans ce réglage, seul le nouvel état est conservé.

13.2 Peek et poke via l'espace mémoire PPC

Les accès PPC dans la plage EFB sont interceptés par MMU.cpp. Les coordonnées sont dérivées de l'adresse :

x = (address & 0xFFF) >> 2
y = (address >> 12) & 0x3FF
bit 0x00400000 = plan profondeur ; sinon plan couleur
bit 0x00800000 = écriture/lecture Z+couleur combinée, non implémentée

Une lecture matérielle :

  1. synchronise la file vidéo au moyen d'une requête bloquante ;
  2. lit une tuile du cache EFB, avec readback GPU si elle n'est pas fraîche ;
  3. reconvertit couleur ou profondeur au format attendu par le jeu ;
  4. applique le mode alpha read du registre PE.

Les pokes matériels sont mis en file et groupés en petits draws, donc ils ne bloquent pas le CPU. Ils sont flushés avant la prochaine primitive car ils partagent les stream buffers.

Les accès hors EFB ou désactivés par bEFBAccessEnable renvoient zéro/ne font rien. Dans le backend logiciel actuel, les peeks lisent l'EFB logicielle, mais PokeColor, PokeDepth et la réinterprétation de format sont des stubs.

13.3 Clear

Une copie EFB peut demander un clear après la copie. BPFunctions::ClearScreen respecte les bits d'écriture couleur/alpha/Z, supprime l'alpha des formats qui n'en ont pas et quantifie couleur/Z au format invité avant d'appeler FramebufferManager::ClearEFB. Le scissor et le viewport GX sont ensuite restaurés.

14. Copies EFB vers texture ou XFB

14.1 Déclenchement BP

L'écriture BPMEM_TRIGGER_EFB_COPY :

  • calcule l'adresse destination copyTexDest << 5 ;
  • calcule le stride copyDestStride << 5 ;
  • construit le rectangle source inclusif à partir de top-left et width/height-minus-one ;
  • choisit copie couleur ou profondeur ;
  • choisit texture tuilée ou XFB YUYV ;
  • applique half scale, Y scale, intensité, gamma, clamps et filtre vertical ;
  • déclenche le clear optionnel après la copie.

Les copies complètement hors EFB sont ignorées. Les copies partiellement hors limites sont clampées. Le matériel produit vraisemblablement des valeurs dépendant de son adressage interne ; Dolphin ne reproduit pas ces valeurs indéterminées.

14.2 Trois représentations compatibles

flowchart TD
    EFB["EFB couleur/profondeur"] --> TRIGGER["CopyRenderTargetToTexture"]
    TRIGGER --> RAM["Encodage natif dans RAM invitée"]
    TRIGGER --> VRAM["Copie convertie dans texture hôte"]
    RAM --> CPU["Lecture/modification par le CPU invité"]
    RAM --> DEC["Redécodage lors d'un futur Load"]
    VRAM --> FAST["Réutilisation rapide et copie upscalée"]
    CPU --> HASH["Hash différent"]
    HASH --> DEC
    VRAM --> HASHOK["Hash RAM identique"]
    HASHOK --> FAST

CopyRenderTargetToTexture peut maintenir :

  • une copie RAM exacte au format invité, nécessaire si le CPU la lit ou la modifie ;
  • une copie VRAM directement réutilisable, plus rapide et éventuellement upscalée ;
  • les deux, avec hash pour détecter si la RAM a divergé.

Si les copies RAM sont différées, le readback encodé reste dans une staging texture associée à l'entrée. Il est flushé avant un token, draw done, savestate, invalidation ou toute utilisation qui doit rendre la RAM observable. Une nouvelle copie couvrant entièrement l'ancienne peut jeter ce readback en attente.

Si le jeu relit la destination comme texture et que le hash est inchangé, Dolphin garde la copie VRAM. S'il a changé, l'entrée devient dynamique et la RAM est redécodée. Un stride trop petit force le chemin RAM, car seule cette représentation conserve l'image volontairement « brouillée ».

14.3 Spécificités XFB

Une copie XFB convertit l'EFB en lignes YUYV, applique le Y scale et produit une hauteur de sortie. Le cache peut conserver la version VRAM tout en écrivant — ou en laissant des valeurs neutralisées dans — la RAM selon la configuration.

Des jeux composent une image à partir de plusieurs copies XFB partielles. StitchXFBCopy cherche les copies qui recouvrent l'adresse demandée par la VI, les trie dans l'ordre de création et les assemble dans une texture conteneur. Si aucune copie VRAM valide n'existe, GetXFBTexture décode le YUYV de la RAM, puis applique les éventuelles copies partielles encore valides.

L'événement after_frame_event est déclenché à chaque copie XFB. C'est le meilleur marqueur de fin de frame disponible dans le flux, mais pas une vérité absolue : certains jeux font plusieurs copies par image.

15. Pixel Engine et retour vers le CPU

Le Pixel Engine possède des registres MMIO CPU à la base physique 0x0C001000. Ses deux retours essentiels sont :

  • token : valeur 16 bits, avec interruption optionnelle PI 0x200 ;
  • finish/draw done : interruption PI 0x400 et notification de frame au thread CPU.

Les commandes BP token/draw done flushent d'abord les copies EFB en attente, les bindings de texture obsolètes et le cache de peeks. PixelEngineManager fusionne les événements en attente sous mutex, puis planifie SetTokenFinish_OnMainThread par CoreTiming.

En Dual Core normal, l'événement est injecté depuis le thread non-CPU avec délai nul, car Dolphin ne modélise pas le timing GPU avec assez de précision. En Single Core et mode déterministe, un délai minimal de 500 cycles est imposé pour laisser au jeu le temps d'armer l'interruption.

Les registres MMIO Z/blend/alpha du PE sont stockés séparément de bpmem. Dans cette révision, le mode alpha read influence les peeks, mais les autres registres de poke ne pilotent pas un pipeline complet de Z-test/blend pour les writes CPU.

16. Video Interface et présentation

16.1 La VI ne rend pas la 3D

La VI lit une XFB déjà produite. Elle possède ses propres registres MMIO à 0x0C002000 et progresse par demi-lignes selon CoreTiming. VideoInterfaceManager::Update déclenche les interruptions de raster et les débuts/fins des champs odd/even.

À OutputField, elle calcule :

fbWidth  = WPL × 16 pixels
fbStride = STD × 16 × 2 octets
fbHeight = ACV lignes
xfbAddr  = registre top ou bottom selon le champ

Le mode force-progressive peut réunir les deux champs en divisant le stride et en doublant la hauteur. L'option early XFB output choisit le début plutôt que la fin de la zone active pour réduire la latence.

16.2 De la VI au backbuffer

Sans Immediate XFB :

  1. Video_OutputXFB synchronise le FIFO déterministe si nécessaire ;
  2. une requête vidéo appelle Presenter::ViSwap avec l'instant émulé et l'instant hôte cible ;
  3. FetchXFB obtient une texture XFB du cache ;
  4. les XFB identiques peuvent être reconnues comme frames dupliquées ;
  5. Present flushe le batch GX, lie et efface le backbuffer ;
  6. le post-processeur blitte la XFB avec aspect ratio, crop et stéréo ;
  7. l'UI est dessinée, puis PresentBackbuffer remet l'image au système de fenêtres.

Avec Immediate XFB, la copie BP appelle Presenter::ImmediateSwap immédiatement, sans attendre le scanout VI. Cette option réduit la latence mais modifie volontairement le moment de présentation.

La VI travaille actuellement avec un instantané unique des registres pour tout un champ. Un jeu qui modifie les registres pendant le scanout ne reçoit donc pas une reconstruction ligne par ligne ; le réglage early/late choisit seulement quel instantané est le plus acceptable.

17. Backends

17.1 Backends accélérés

Backend Répertoire Dernière frontière
D3D11 VideoBackends/D3D ID3D11DeviceContext::DrawIndexed
D3D12 VideoBackends/D3D12 command list D3D12
Metal VideoBackends/Metal encodeur Metal
OpenGL VideoBackends/OGL draw GL
Vulkan VideoBackends/Vulkan vkCmdDrawIndexed

Ils partagent le décodage GX, les états CP/XF/BP, la génération de shaders, la gestion EFB et le cache de textures. Ils diffèrent surtout par les objets API, stream buffers, compilateurs de shaders, synchronisation de command buffers et capacités déclarées.

17.2 Backend logiciel

VideoBackends/Software réutilise le même flux jusqu'aux sommets convertis, puis remplace le draw hôte par :

SWVertexLoader
  -> TransformUnit (matrices, éclairage, texgen)
  -> SetupUnit (assemblage)
  -> Clipper
  -> Rasterizer
  -> Tev + TextureSampler
  -> SWEfbInterface

Il est beaucoup plus lent et destiné au débogage. Il donne une seconde implémentation de nombreuses règles GX, utile pour comparer les résultats, mais il ne constitue pas une référence parfaite : plusieurs opérations EFB et améliorations ne sont pas implémentées.

17.3 Backend Null

VideoBackends/Null consomme le flux et maintient la logique commune sans produire d'image. Il permet d'isoler le coût CPU du frontend et de valider qu'un flux se décode sans dépendre d'une API graphique.

18. Niveau de fidélité réel

La table suivante fait partie du contrat de cette documentation. « Émulé » ne signifie pas nécessairement « cycle exact ».

Domaine État dans cette révision Conséquence
Protocole gather/FIFO 32 octets Émulé structurellement Pointeurs, wrap, distance et commandes partielles sont conservés.
Timing CP/GPU Approximatif Coûts fixes par commande ; pas de simulation des files internes ou stalls exacts.
WPAR BNE Stub false Évite des hangs, ne reproduit pas les transferts outstanding.
Registres clear/métriques CP Stub ou constantes Les jeux ne voient pas de compteurs matériels réels.
Grammaire principale GX Émulée CP/XF/BP, primitives et display lists sont décodées.
Opcodes métriques / invalidate VC Reconnus sans effet complet Journalisation et timing seulement.
VCD/VAT et conversion des sommets Émulés et testés Large couverture de formats, chemins générique/x64/ARM64.
Incohérences CP/XF/BP Tolérées autant que possible Dolphin peut rendre alors que le matériel aurait bloqué.
XF error/diag/clock/perf et registres inconnus Non implémentés Valeurs stockées/loggées sans pipeline matériel associé.
Raster/TEV hardware backend Traduction fonctionnelle Dépend des capacités/précisions de l'API et des contournements shader.
Raster/TEV software backend Implémentation CPU séparée Utile au diagnostic, mais pas complète pour toutes les opérations périphériques.
TMEM Contenu preload/TLUT réel + cache heuristique Pas d'éviction texel/ligne exacte ni sampler feedback.
Invalidation TMEM paramétrée Invalidation globale Plus conservateur que le matériel.
EFB couleur/profondeur Représentation hôte améliorée Formats 6/16 bits et Z16 sont quantifiés logiquement, pas stockés nativement.
MSAA 3 échantillons du GPU invité Non émulé directement Remplacé par le MSAA hôte configuré et une EFB plus précise.
Changement de format EFB Optionnel La réinterprétation exacte dépend de bEFBEmulateFormatChanges.
Accès EFB Z+couleur 64 bits Non implémenté Log d'erreur, aucune sémantique complète.
Alpha des pokes EFB matériels Sémantique à confirmer Le canal est converti et écrit, mais le code signale que sa dépendance au mode PE reste à vérifier.
Pokes du backend logiciel Stubs Les writes CPU EFB n'y modifient pas l'image.
EFB du backend logiciel en RGB565/Z16 et MSAA Incomplet Le code marque RGB565/Z16 comme incorrect et le multisampling comme non pris en charge.
Copies EFB hors limites Approximation clamp/ignore Les valeurs indéterminées du matériel ne sont pas reproduites.
Downsample copies à IR > 2× Filtrage approximatif Le commentaire du code signale qu'un filtrage plus complexe serait nécessaire.
BP field mask / field mode TODO La VI et des hacks gèrent l'affichage, mais pas l'écriture EFB par champ exacte.
Tokens/finish Ordre logique émulé, timing approximatif Délai nul Dual Core ou minimum 500 cycles dans d'autres modes.
Timing analogique VI Approximatif Les transformateurs de retour horizontal/vertical sont modélisés par une dent de scie idéale.
Scanout VI Champs et demi-lignes Pas de composition des changements de registres pendant chaque ligne.
Backend/API hôte Couche d'adaptation Les bugs drivers et capacités modifient la stratégie sans changer l'état invité.

18.1 Réglages qui changent volontairement le résultat

Les améliorations et hacks vivent dans VideoConfig.h. Les plus structurants sont :

  • résolution interne, MSAA/SSAA, anisotropie et filtrage forcé ;
  • true color, HDR, correction colorimétrique et post-processing ;
  • widescreen hack, crop, stéréo et vertex rounding ;
  • fast depth, pixel lighting et CPU culling ;
  • skip EFB/XFB copy to RAM, scaled EFB copies et deferred copies ;
  • désactivation/tiling des EFB accesses, performance queries et bounding box ;
  • Immediate XFB et suppression des XFB dupliquées ;
  • textures haute résolution et mods graphiques.

Ces options ne sont pas de simples optimisations invisibles. Certaines améliorent la fidélité perçue, d'autres échangent explicitement exactitude, latence et performance.

19. Savestates et invariants

VideoCommon_DoState sérialise :

  • bpmem, g_main_cp_state, xfmem et s_tex_mem ;
  • état heuristique TMEM ;
  • tampon FIFO, pointeurs et registres CP ;
  • PE, managers de constantes et VertexManager ;
  • contenu EFB, texture cache, Presenter, bounding box et widescreen.

Le GPU est synchronisé autour de la sauvegarde ; g_preprocess_cp_state n'est donc pas stocké séparément et est recopié depuis l'état principal au chargement. BPReload reconstruit les effets de bord backend et tous les chargeurs de sommets sont marqués sales.

Invariants importants vérifiés par assertions ou logs :

  • FIFO lié : base/end/write pointer PI et CP identiques ;
  • distance FIFO jamais négative et jamais supérieure à la capacité ;
  • commande primitive : octets consommés = vertex_size × count ;
  • format CP compatible avec le vertex spec XF ;
  • nombre de texgens/canaux XF compatible avec BP avant un draw ;
  • pipeline GX flushé avant toute utility draw qui réutilise ses buffers.

20. Validation existante

20.1 Tests unitaires

VertexLoaderTest.cpp est actuellement le seul fichier de tests sous Source/UnitTests/VideoCommon. Il couvre notamment :

  • unicité des VertexLoaderUID ;
  • positions directes et indexées dans de nombreux formats ;
  • couleurs, normales, tangentes/binormales et texcoords ;
  • composantes absentes ou sautées ;
  • fractions, conversions, endianness et chemins de chargeur disponibles.

Cette couverture solide du décodage de sommets ne constitue pas une couverture automatique du FIFO, des registres, du TEV, des copies ou de la VI.

20.2 FIFO recorder/player

Core/FifoPlayer enregistre les commandes GP et les plages mémoire qu'elles consultent : arrays, indexed XF, display lists, textures, TMEM et destinations générées. Le player reconstruit les registres CP et rejoue le FIFO sans exécuter le jeu.

C'est l'outil d'intégration central pour :

  • reproduire une frame GPU ;
  • comparer deux backends ;
  • inspecter l'état CP/XF/BP à une commande ;
  • détecter une régression de shader, texture ou EFB ;
  • isoler un problème graphique d'un problème CPU/timing du jeu.

20.3 Diagnostics d'exécution

Le code possède aussi :

  • logs nommés CP/BP/XF/PE/VI ;
  • alertes d'opcode inconnu avec état complet du FIFO ;
  • analytics de quirks pour commandes ou formats atypiques ;
  • statistiques par frame sur loads, primitives, draws, copies, peeks et tokens ;
  • backend Software et backend Null comme chemins de comparaison.

21. Lacunes de validation à combler

Pour transformer cette carte en spécification régressive complète, les tests prioritaires sont :

  1. Opcode decoder : taille partielle/complète, endianness, alignement display list et chaque callback.
  2. FIFO circulaire : wrap inclusif, linked/unlinked, watermarks, safe read pointer et breakpoint.
  3. Modes de threads : même ordre de callbacks entre Single Core, Dual Core et déterministe.
  4. BP mask/side effects : écriture identique, masque one-shot, token, finish, TLUT et copy.
  5. XF indexed loads : snapshot déterministe et invalidation précise des constantes.
  6. Primitive/index generation : toutes les topologies avec et sans primitive restart.
  7. Shader golden tests : UID et source produits pour des états CP/XF/BP représentatifs.
  8. Texture/TMEM : chevauchements even/odd, palettes, EFB copy modifiée par le CPU et invalidation.
  9. EFB/XFB : formats, strides, copies partielles, stitching, clear et round-trip RAM.
  10. PE/VI : ordre interrupt/token/finish et présentation odd/even/duplicate/immediate.

Les comportements marqués heuristiques ou inconnus exigent en plus des tests sur console réelle ; un test Dolphin ne peut pas, à lui seul, établir la vérité matérielle.

22. Chemins d'appel de référence

22.1 Une primitive normale

PPC store
 -> MMU::WriteToHardware
 -> GPFifoManager::Write* / UpdateGatherPipe
 -> CommandProcessorManager::GatherPipeBursted
 -> FifoManager::RunGpuLoop ou RunGpuOnCpu
 -> OpcodeDecoder::RunFifo
 -> RunCallback::OnPrimitiveCommand
 -> VertexLoaderManager::RunVertices
 -> VertexManagerBase::PrepareForAdditionalData / AddIndices / FlushData
 -> VertexManagerBase::Flush
 -> ShaderCache + TextureCacheBase
 -> VertexManagerBase::RenderDrawCall
 -> AbstractGfx::DrawIndexed
 -> EFB

22.2 Une écriture de registre BP

Opcode 0x61
 -> RunCallback::OnBP
 -> LoadBPReg
 -> application de bpmem.bpMask
 -> BPWritten
 -> FlushPipeline
 -> mise à jour bpmem
 -> dirty flag / état hôte / copie / token selon l'adresse

22.3 Une copie puis présentation

BPMEM_TRIGGER_EFB_COPY
 -> TextureCacheBase::CopyRenderTargetToTexture
 -> RAM invitée et/ou texture XFB en VRAM
 -> VideoEvents::after_frame_event
 -> VideoInterfaceManager::OutputField
 -> VideoBackendBase::Video_OutputXFB
 -> Presenter::ViSwap
 -> TextureCacheBase::GetXFBTexture
 -> Presenter::Present
 -> AbstractGfx::PresentBackbuffer

22.4 Un peek EFB

PPC load dans la plage EFB
 -> MMU::EFB_Read
 -> EFBInterfaceBase::PeekColor/PeekDepth
 -> AsyncRequests::PushBlockingEvent
 -> VertexManagerBase::Flush
 -> FramebufferManager::PeekEFB*
 -> cache/readback du GPU hôte
 -> conversion au format GX + alpha read PE
 -> valeur PPC

23. Guide de diagnostic

Symptôme Première zone à vérifier Questions utiles
Opcode inconnu / FIFO corrompu Gather, CP, Fifo, OpcodeDecoding La distance et les pointeurs sont-ils cohérents ? Une commande partielle a-t-elle été consommée ? Le mode Dual Core change-t-il le bug ?
Géométrie déformée CP, VertexLoader, XF VCD/VAT, bases/strides, endianness, matrix index et vertex spec correspondent-ils ?
Primitive manquante IndexGenerator, culling, pipeline async Topologie/primitive restart corrects ? Cull all ? Shader spécialisé encore absent en skip mode ?
Couleur/alpha/fog faux BP, PixelShaderGen, constantes Étages TEV, swizzles, K colors, alpha test, dst alpha et quantification EFB corrects ?
Texture ancienne ou scintillante TextureCacheBase, TMEM Invalidation, hash, overlap, palette, preload et EFB copy dynamique corrects ?
Effet écran/miroir faux Copie EFB Format, stride, y scale, half scale, RAM vs VRAM et clear corrects ?
Image 3D correcte mais affichage faux XFB, VI, Presenter Adresse top/bottom, WPL/STD/ACV, interlace, stitching et aspect ratio corrects ?
Jeu bloqué en attente GPU CP/PE interrupts et synchronisation GPRead, breakpoint, watermark, token/finish enable et événement CPU sont-ils dans le bon ordre ?
Bug uniquement backend matériel Shader UID, RenderState, capacités backend Le backend Software reproduit-il le bug ? Une capacité ou un workaround driver change-t-il le pipeline ?
Bug uniquement à haute résolution EFB scale, copies, viewport/scissor Coordonnées natives et scalées sont-elles mélangées ? Le downsample/copy filter est-il exact ?

24. Modèle mental final

Pour raisonner correctement sur un bug GPU Dolphin, suivre quatre états dans l'ordre :

  1. Flux : quels octets le PPC a-t-il réellement placés dans le FIFO, et quand sont-ils devenus visibles au CP ?
  2. Machine GX : quel état CP/XF/BP existe exactement au début de la primitive ?
  3. Traduction : quels sommets, textures, constantes, shaders et états hôte cet instantané a-t-il produits ?
  4. Sortie : comment le résultat EFB a-t-il été copié en XFB, scanné par la VI et présenté ?

Une anomalie visible peut provenir de chacune de ces couches. L'erreur classique consiste à inspecter le shader final alors que le vrai défaut est un read pointer FIFO, ou à inspecter le rasteriseur alors que l'image EFB correcte est ensuite mal assemblée dans la XFB.