2017-03-17 2 views
2

Ci-dessous se trouve un simple combo de vertex et fragment shader dans metal qui rend 64 quads 2D identiques.Des performances inattendues ont été atteintes dans le métal en changeant les coordonnées uv

vertex VertexOut vertexMain(uint k [[ vertex_id ]], 
          uint ii [[instance_id]], 
          device float2* tex [[buffer(2)]], 
          device float2* position [[buffer(1)]], 
          device float* state [[buffer(0)]]){ 
    VertexOut output; 
    int i = 4*ii+1; 
    float2 pos = position[k]; 
    pos *= float2(state[i+2],state[i+3]); 
    pos += float2(state[i],state[i+1]); 
    pos.x *= state[0]; 
    output.position = float4(pos,0,1); 
    output.tex = tex[k]*float2(du,dv); 
    return output; 
}; 
fragment float4 fragmentMain(VertexOut input [[stage_in]], 
          texture2d<float> texture [[texture(0)]], 
          sampler sam [[sampler(0)]]){ 
    return texture.sample(sam, input.tex); 
}; 

L'échantillonneur est l'utilisation de coordonnées normalisées de sorte du et dv peut varier de 0 à 1 et de contrôler la taille d'un clip de la texture sera échantillonné en partant du coin inférieur gauche.

Il semble que j'ai un malentendu sur la façon dont l'échantillonnage fonctionne dans le métal. Je m'attendrais à ce que le coût de calcul reste constant quelles que soient les valeurs du et dv. Cependant, comme j'augmente du et dv à 1, la fréquence d'images diminue. Je n'utilise aucun mipmapping et je ne change pas la taille des quads qui sont pixellisés à l'écran. L'effet est plus spectaculaire avec le filtrage linéaire, mais se produit également avec le filtrage le plus proche. Il me semble que puisque le nombre de pixels dessinés à l'écran est le même, la charge sur le GPU ne doit pas dépendre de du et dv. Qu'est-ce que je rate?

EDIT: Voici mon sampler et l'attachement couleur:

let samplerDescriptor = MTLSamplerDescriptor() 
    samplerDescriptor.normalizedCoordinates = true 
    samplerDescriptor.minFilter = .linear 
    samplerDescriptor.magFilter = .linear 
    let sampler = device.makeSamplerState(descriptor: samplerDescriptor) 

    let attachment = pipelineStateDescriptor.colorAttachments[0] 
      attachment?.isBlendingEnabled = true 
      attachment?.sourceRGBBlendFactor = .one 
      attachment?.destinationRGBBlendFactor = .oneMinusSourceAlpha 
+0

Pouvez-vous quantifier la quantité de fréquence d'images que vous rencontrez? – warrenm

+0

De 60 à 40 avec échantillonnage linéaire. De 60 à 50 avec échantillonnage le plus proche. – gloo

+0

Sur quel périphérique et quelle version du système d'exploitation? – warrenm

Répondre

0

Comme vous augmentez du et dv vos quadriceps sont de votre plus affichent la texture. Les GPU ont tendance à avoir des caches plus petits pour les données de texture, et comme vous affichez plus de votre texture, vous rejetez et remplissez ce cache plus. Thrashing le cache de texture utilisera plus de bande passante mémoire qui est une ressource assez limitée, souvent la bande passante de la mémoire de texture n'est pas le goulot d'étranglement, mais comme votre fragment shader ne fait presque rien d'autre que des récupérations de texture, il n'est pas surprenant que c'est ton goulot d'étranglement. Par conséquent, il n'est pas surprenant que la modification de vos UV ait un effet sur les performances. Ce qui est surprenant, c'est que le framerate tombe en dessous de 60 sur ces appareils très puissants, alors que tout ce que vous faites est de rendre 64 quads (iPad Pro en particulier est un appareil très puissant). Cela dit, peut-être que si les 64 quads couvraient la plus grande partie de l'écran, la chute de fréquence pourrait être compréhensible. Pour améliorer les performances, vous devez réduire la quantité de données de texture devant être pelliculées par le GPU. Passer du format de texture 32 bits (8888) à 16 bits (565/4444) ou 4 bits (textures compressées PVRTC) aurait un grand impact.

La grande victoire est probablement de permettre le mipmapping. En supposant qu'avec des valeurs élevées de du et dv, vous finissez par minimiser la texture, l'utilisation du mipmapping donnera un gain de performance énorme, et en bonus, vos textures seront plus belles aussi (cela corrigera l'aliasing). Pas un mauvais retour pour une augmentation de la mémoire de texture de 33%.

+0

C'est très intéressant. J'utilise le rendu d'instance, ce qui signifie que tous les 64 quads sont dessinés avec une seule liaison de texture et un seul appel draw. Est-ce que ce problème de mise en cache persisterait dans ce scénario? – gloo

+0

Oui, les caches de texture ne sont généralement que de quelques Ko, la plupart des textures sont de quelques centaines de Ko. C'est un bon début: https: //fgiesen.wordpress.com/2011/07/04/a-trip-through-the-graphics-pipeline-2011-part-4/ Il pourrait être utile de mieux comprendre votre scénario de rendu - une capture d'écran peut-être. 64 petits quads n'est rien et quelque chose de drôle se passe si vous ne pouvez pas frapper 60fps. 64 quads qui couvrent chacun l'écran entier alors je pourrais comprendre le problème. – Columbo

+0

Chaque quad couvre seulement 1% de l'écran. J'ai ajouté un peu plus de code décrivant mon pipeline. – gloo