2016-06-17 1 views
0

Contexte: Je développe un plugin natif unité C++ 5 qui lit les données de texture compressés DXT et le télécharge sur le GPU pour une utilisation ultérieure dans l'unité. L'objectif est de créer un lecteur de séquence d'images rapide, en actualisant les données d'image à la volée. Les textures sont compressées avec une application de console hors ligne. Unity peut fonctionner avec différents moteurs graphiques, je vise DirectX11 et OpenGL 3.3+.Erreur mise à jour d'exécution de DXT textures compressées avec DirectX11

Problème: Le code de mise à jour de texture DirectX Runtime, via une sous-ressource mappée, donne différentes sorties sur différents pilotes graphiques. La mise à jour d'une texture à l'aide d'une telle ressource mappée signifie que vous mettez en correspondance un pointeur avec les données de texture et que vous mémorisez les données du tampon RAM dans le tampon GPU mappé. Ce faisant, différents pilotes semblent s'attendre à des paramètres différents pour la valeur de hauteur de ligne lors de la copie d'octets. Je n'ai jamais eu de problème sur les GPU Nvidia que j'ai testés, mais les GPU AMD et Intel semblent agir différemment et j'ai une sortie déformée comme montré ci-dessous. De plus, je travaille avec des données de pixel DXT1 (0.5bpp) et des données DXT5 (1bpp). Je n'arrive pas à obtenir le bon paramètre de hauteur pour ces textures DXT.

code: Le code d'initialisation suivante pour générer la texture d3d11 et de le remplir avec des données de texture initiale - par exemple, la première image d'une séquence d'images - fonctionne parfaitement sur tous les pilotes. Le pointeur de joueur points à une classe personnalisée qui gère tous les fichiers lit et contient getters pour la charge de courant trame compressée DXT, ses dimensions, etc ...

if (s_DeviceType == kUnityGfxRendererD3D11) 
    { 
     HRESULT hr; 
     DXGI_FORMAT format = (compression_type == DxtCompressionType::DXT_TYPE_DXT1_NO_ALPHA) ? DXGI_FORMAT_BC1_UNORM : DXGI_FORMAT_BC3_UNORM; 

     // Create texture 
     D3D11_TEXTURE2D_DESC desc; 
     desc.Width = w; 
     desc.Height = h; 
     desc.MipLevels = 1; 
     desc.ArraySize = 1; 
     desc.Format = format; 
     // no anti-aliasing 
     desc.SampleDesc.Count = 1; 
     desc.SampleDesc.Quality = 0; 
     desc.Usage = D3D11_USAGE_DYNAMIC; 
     desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; 
     desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 
     desc.MiscFlags = 0; 

     // Initial data: first frame 
     D3D11_SUBRESOURCE_DATA data; 
     data.pSysMem = player->getBufferPtr(); 
     data.SysMemPitch = 16 * (player->getWidth()/4); 
     data.SysMemSlicePitch = 0; // just a 2d texture, no depth 

     // Init with initial data 
     hr = g_D3D11Device->CreateTexture2D(&desc, &data, &dxt_d3d_tex); 

     if (SUCCEEDED(hr) && dxt_d3d_tex != 0) 
     { 
      DXT_VERBOSE("Succesfully created D3D Texture."); 

      DXT_VERBOSE("Creating D3D SRV."); 
      D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; 
      memset(&SRVDesc, 0, sizeof(SRVDesc)); 
      SRVDesc.Format = format; 
      SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 
      SRVDesc.Texture2D.MipLevels = 1; 

      hr = g_D3D11Device->CreateShaderResourceView(dxt_d3d_tex, &SRVDesc, &textureView); 
      if (FAILED(hr)) 
      { 
       dxt_d3d_tex->Release(); 
       return hr; 
      } 
      DXT_VERBOSE("Succesfully created D3D SRV."); 
     } 
     else 
     { 
      DXT_ERROR("Error creating D3D texture.") 
     } 
    } 

Le code de mise à jour suivante qui fonctionne pour chaque nouveau cadre a l'erreur quelque part. S'il vous plaît noter la ligne commentée contenant la méthode 1 en utilisant un memcpy simple sans n'importe quel rowpitch spécifié qui fonctionne bien sur les pilotes NVIDIA.
Vous pouvez voir plus loin dans la méthode 2 que je connecte les différentes valeurs de pas de ligne. Par exemple pour un cadre de 1920x960, je reçois 1920 pour la foulée du tampon, et 2048 pour la foulée de l'exécution. Cette différence de 128 pixels doit probablement être complétée (comme on peut le voir dans l'exemple de l'image ci-dessous) mais je n'arrive pas à comprendre comment. Quand je viens d'utiliser le mappedResource.RowPitch sans le diviser par 4 (fait par le bitshift), Unity se bloque.

ID3D11DeviceContext* ctx = NULL; 
    g_D3D11Device->GetImmediateContext(&ctx); 

    if (dxt_d3d_tex && bShouldUpload) 
    { 
     if (player->gather_stats) before_upload = ns(); 

     D3D11_MAPPED_SUBRESOURCE mappedResource; 
     ctx->Map(dxt_d3d_tex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); 

     /* 1: THIS CODE WORKS ON ALL NVIDIA DRIVERS BUT GENERATES DISTORTED OR NO OUTPUT ON AMD/INTEL: */ 
     //memcpy(mappedResource.pData, player->getBufferPtr(), player->getBytesPerFrame()); 

     /* 2: THIS CODE GENERATES OUTPUT BUT SEEMS TO NEED PADDING? */ 
     BYTE* mappedData = reinterpret_cast<BYTE*>(mappedResource.pData); 
     BYTE* buffer = player->getBufferPtr(); 
     UINT height = player->getHeight(); 
     UINT buffer_stride = player->getBytesPerFrame()/player->getHeight(); 
     UINT runtime_stride = mappedResource.RowPitch >> 2; 

     DXT_VERBOSE("Buffer stride: %d", buffer_stride); 
     DXT_VERBOSE("Runtime stride: %d", runtime_stride); 

     for (UINT i = 0; i < height; ++i) 
     { 
      memcpy(mappedData, buffer, buffer_stride); 
      mappedData += runtime_stride; 
      buffer += buffer_stride; 
     } 

     ctx->Unmap(dxt_d3d_tex, 0); 
    } 

Exemple pic 1 - ouput déformé lors de l'utilisation memcpy pour copier tampon entier sans utiliser de pas de rangée séparée sur AMD/INTEL (méthode 1)

distorted output

Exemple pic 2 - meilleure sortie, mais encore erronée, lors de l'utilisation du code ci-dessus avec mappedResource.RowPitch sur AMD/INTEL (méthode 2). Les barres bleues indiquent la zone d'erreur et doivent disparaître pour que tous les pixels s'alignent bien et forment une image.

enter image description here

Merci pour tous les pointeurs! Best, Vincent

Répondre

0

La hauteur de la ligne de données mappée est en octets, lorsque vous divisez par quatre, c'est définitivement un problème.

UINT runtime_stride = mappedResource.RowPitch >> 2; 
... 
mappedData += runtime_stride; // here you are only jumping one quarter of a row 

Il est la hauteur compter avec un format de la Colombie-Britannique qui est divisé par 4.

également un format BC1 est de 8 octets par bloc de 4x4, de sorte que la ligne ci-dessous devrait par 8 * et non 16 *, mais comme Tant que vous manipulez correctement la foulée, d3d comprendra, vous perdrez la moitié de la mémoire ici.

data.SysMemPitch = 16 * (player->getWidth()/4); 
+0

Nous vous remercions de votre réponse @ galop1n. Je suis d'accord que la valeur RowPitch reçue du pilote ne doit pas être divisée par quatre (ou n'importe quel autre numéro d'ailleurs). Mais quand je ne fais pas cela, l'application se bloque. Ce qui est étrange car même si je copie sans rembourrage d'une manière ou d'une autre, il ne devrait pas atteindre les limites de ressources mappées en mémoire. Peut-être un bug de pilote? –

+0

@VincentJacobs Il n'y a pas de bug de pilote ici, vous avez manqué la ligne sur le point de diviser la hauteur par quatre lignes. Lorsque vous travaillez avec des textures BC, vous travaillez en blocs de 4x4, donc la largeur et la hauteur de l'image sont divisées par 4, et un bloc est de 8 octets dans BC1 et de 16 octets dans les autres. Une ligne est alors 'blocksize * width/4', et vous avez' height/4' rows. – galop1n

+0

Merci @ galop1n. Ajuster le code comme vous l'avez proposé a résolu le problème. Merci beaucoup! –