2016-06-28 5 views
0

J'ai une fonction du noyau (shader de calcul) qui lit les pixels proches d'un pixel à partir d'une texture et sur la base des anciennes valeurs de pixels proches met à jour la valeur du pixel courant (ce n'est pas une simple convolution).iOS Metal - lire les anciennes valeurs tout en écrivant dans la texture

J'ai essayé de créer une copie de la texture en utilisant BlitCommandEncoder et en alimentant la fonction noyau avec 2 textures - une en lecture seule et une autre en écriture seule. Malheureusement, cette approche nécessite beaucoup de temps pour le GPU. Quelle est la manière la plus efficace (GPU et mémoire) de lire les anciennes valeurs d'une texture tout en mettant à jour son contenu?

+0

Avez-vous profilé votre application avec Time Profiler (dans les instruments) et GPU Frame Capture (dans Xcode)? Déterminer combien de temps vous consacrez l'allocation des ressources, la copie et l'exécution de votre noyau vous aidera à déterminer ce qu'il faut optimiser en premier. – warrenm

+0

@warrenm Merci de votre réponse. Il s'avère que la routine travaillant avec le 'BlitCommandEncoder' faisait appel à une autre fonction, qui prenait beaucoup de temps, ce qui causait le retard, les opérations Blit n'étaient pas la principale source de retard - c'était de ma faute. Cependant, en regardant dans GPU Frame Capture, j'ai trouvé qu'il n'y a pas mentionné la durée des opérations Blit (par opposition aux opérations de calcul) - est-il impossible de les mesurer (ou ai-je oublié quelque chose)? – sarasvati

+0

Je ne pense pas qu'il existe actuellement un moyen de capturer des durées de codage de calcul dans GPU Frame Capture, désolé. – warrenm

Répondre

2

(peu en retard mais bon)

Il n'y a aucun moyen que vous pourriez le faire fonctionner avec une seule texture, parce que le GPU est un processeur hautement parallèle: Votre noyau que vous avez écrit pour un seul pixel est appelé dans parallèle sur tous les pixels, vous ne pouvez pas dire lequel va en premier.

Vous avez donc absolument besoin de 2 textures. La façon dont vous devriez probablement le faire est en utilisant 2 textures où l'un est le "vieux" et l'autre le "nouveau". Entre les passes, vous changez le rôle des textures, maintenant l'ancien est nouveau et le nouveau est ancien. Voici quelques pseudoswift:

var currentText = MTLTexture() 
var nextText = MTLTexture() 

let semaphore = dispatch_semaphore_create(1) 

func update() { 
    dispatch_semaphore_wait(semaphore) // Wait for updating done signal 

    let commands = commandQueue.commandBuffer() 
    let encoder = commands.computeCommandEncoder() 

    encoder.setTexture(currentText, atIndex: 0) 
    encoder.setTexture(nextText, atIndex: 1) 

    encoder.dispatchThreadgroups(...) 
    encoder.endEncoding() 

    // When updating done, swap the textures and signal that it's done updating 
    commands.addCompletionHandler { 
     swap(&currentText, &nextText) 
     dispatch_semaphore_signal(semaphore) 
    } 
    commands.commit() 
} 
0

J'ai écrit beaucoup de code iOS Metal qui échantillonne (ou lit) à partir de la même texture dans laquelle il est rendu. J'utilise le pipeline de rendu, en définissant ma texture en tant que pièce jointe de la cible de rendu et en la chargeant comme texture source. Cela fonctionne très bien.

Pour être clair, une approche plus efficace est d'utiliser l'attribut color() dans votre fragment shader, mais ce ne convient si tout ce que vous avez besoin est la valeur du fragment courant , pas d'autres positions voisines. Si vous avez besoin de lire d'autres positions dans la cible de rendu, je chargerais simplement la cible de rendu en tant que texture source dans le fragment shader.