2017-06-26 2 views
2

J'ai besoin de votre aide aujourd'hui! Je commence à travailler avec le shader de calcul dans un cas d'utilisation très simple: J'ai une caméra de profondeur et je veux calculer la boîte de délimitation d'un objet près de la caméra.Unity Compute Shader Synchronisation

Mais j'ai trop de pixels à traiter et je veux utiliser GPGPU, calculer le shader et la parallélisation pour le calculer.

J'ai actuellement un problème, quand je cours mon programme, j'ai les mêmes coordonnées min et max. Je pense donc que tout mon groupe et tous mes fils écrivent en même temps sur mon StructuredBuffers.

Avez-vous une idée de comment faire cela?

Voici le code de mon Compute Shader:

#pragma kernel ComputeBoundingBox 

//We define the size of a group in the x, y and z directions, y and z direction will just be one (because 1D array is used for depthData) 
#define thread_group_size_x 1024 
#define thread_group_size_y 1 
#define thread_group_size_z 1 
//Size of the depthData picture 
#define width 512; 
#define height 424; 

//DataBuffer = depthData of the camera 
//minBuffer, maxBuffer, array of size 3 with min/max x, y and z 
//mask = image area to process 
RWStructuredBuffer<float> dataBuffer; 
globallycoherent RWStructuredBuffer<float>minBuffer; 
globallycoherent RWStructuredBuffer<float> maxBuffer; 
RWStructuredBuffer<float> mask; 


float xValue = 0, yValue = 0, zValue = 0; 

[numthreads(thread_group_size_x, thread_group_size_y, thread_group_size_z)] 
void ComputeBoundingBox(uint3 id : SV_DispatchThreadID) 
{ 
    //xValue and yValue = [X,Y] index in 2D 
    //zValue = depthValue of [X,Y] index 
    xValue = (id.x + 1) % width; 
    yValue = (id.x + 1)/width; 
    zValue = dataBuffer[id.x]; 

    if (mask[id.x] > 0.49) 
    { 
     if (zValue > 500 && zValue < 1500) 
     { 
      if (xValue < minBuffer[0]) 
       minBuffer[0] = xValue; 
      else if (xValue > maxBuffer[0]) 
       maxBuffer[0] = xValue; 
      if (yValue < minBuffer[1]) 
       minBuffer[1] = yValue; 
      else if (yValue > maxBuffer[1]) 
       maxBuffer[1] = yValue; 
      if (zValue < minBuffer[2]) 
       minBuffer[2] = zValue; 
      else if (zValue > maxBuffer[2]) 
       maxBuffer[2] = zValue; 
     } 
    } 
} 

Voici la partie du Code qui appellent le shader Compute:

void RunShader() 
    { 
     dataBuffer.SetData(depthDataFloat); 
     minDataBuffer.SetData(reinitialiseMinBuffer); 
     maxDataBuffer.SetData(reinitialiseMaxBuffer); 
     maskBuffer.SetData(mask); 

     computeShader.SetBuffer(_kernel, "dataBuffer", dataBuffer); 
     computeShader.SetBuffer(_kernel, "minBuffer", minDataBuffer); 
     computeShader.SetBuffer(_kernel, "maxBuffer", maxDataBuffer); 
     computeShader.SetBuffer(_kernel, "mask", maskBuffer); 

     computeShader.Dispatch(_kernel, 212, 1, 1); 
    } 

Répondre

0

Dans votre cas, vous ne gérez pas des courses de données, donc plusieurs threads peuvent écrire au même endroit. Pour vous assurer que vos écritures sont atomiques, vous devez utiliser des fonctions interverrouillées. Ceux-ci ne fonctionnent qu'avec uint, mais dans votre cas (en supposant que les données de profondeur sont toujours> 0), comparaison binaire de float correspondra à la comparaison de leurs valeurs.

Voici le shader modifié:

#pragma kernel ComputeBoundingBox 

#define thread_group_size_x 1024 
#define thread_group_size_y 1 
#define thread_group_size_z 1 
//Size of the depthData picture 
#define width 512; 
#define height 424; 

//DataBuffer = depthData of the camera 
//minBuffer, maxBuffer, array of size 3 with min/max x, y and z 
//mask = image area to process 
StructuredBuffer<float> dataBuffer; 
RWStructuredBuffer<float>minBuffer; 
RWStructuredBuffer<float> maxBuffer; 
StructuredBuffer<float> mask; 

[numthreads(thread_group_size_x, thread_group_size_y, thread_group_size_z)] 
void ComputeBoundingBox(uint3 id : SV_DispatchThreadID) 
{ 
    //xValue and yValue = [X,Y] index in 2D 
    //zValue = depthValue of [X,Y] index 
    uint xValue = (id.x + 1) % width; 
    uint yValue = (id.x + 1)/width; 
    uint zValue = asuint(dataBuffer[id.x]); 

    if (mask[id.x] > 0.49) 
    { 
     if (zValue > 500 && zValue < 1500) 
     { 
      uint oldValue; 
      InterlockedMin(minBuffer[0],xValue,oldValue); 
      InterlockedMax(maxBuffer[0],xValue,oldValue); 

      InterlockedMin(minBuffer[1],yValue,oldValue); 
      InterlockedMax(maxBuffer[1],yValue,oldValue); 

      InterlockedMin(minBuffer[2],zValue,oldValue); 
      InterlockedMax(maxBuffer[2],zValue,oldValue); 
     } 
    } 
} 

Je l'ai assigner DataBuffer et masque StructuredBuffers aussi (puisque vous lisez seulement à ceux, il est généralement plus rapide avec eux lié en tant que telle).

Vous devez également vous assurer que vos tampons min/max sont effacés avec une valeur appropriée en premier (c'est-à-dire avant d'appeler ce shader).

Cela peut être fait avec un shader simple Compute (envoyer un seul thread):

RWStructuredBuffer<float> minBuffer; 
RWStructuredBuffer<float> maxBuffer; 

[numthreads(1, 1, 1)] 
void ClearBuffers(uint3 id : SV_DispatchThreadID) 
{ 
    uint maxUint = 0xffffffff; 
    uint minUint = 0; 
    minBuffer[0]= asfloat(maxUint); 
    minBuffer[1]= asfloat(maxUint); 
    minBuffer[2]= asfloat(maxUint); 

    maxBuffer[0]= asfloat(minUint); 
    maxBuffer[1]= asfloat(minUint); 
    maxBuffer[2]= asfloat(minUint); 
} 

S'il vous plaît noter que uint/flotteur aliasing dans ce cas va fonctionner, de sorte que vous n'avez pas besoin d'effectuer une conversion .