2010-09-30 12 views
6

Come posso:Come leggere con successo da una texture 2D

  1. Bind cudaMallocPitch memoria galleggiante ad un riferimento di texture 2D
  2. copiare alcuni dati host a matrice 2D sul dispositivo
  3. Aggiungere uno a il riferimento di trama e scrivere su a.) l'array Pitch 2D OR b.) scrivere su un array di memoria lineare
  4. Leggere la risposta indietro e visualizzarla.

Di seguito è riportato un codice che dovrebbe eseguire ciò. Si noti che per le dimensioni degli array NxN, il mio codice funziona. Per NxM dove N! = M, il mio codice morde la polvere (non il risultato corretto). Se puoi risolvere questo problema ti assegnerò 1 internet (fornitura limitata). Forse sono pazzo, ma secondo la documentazione questo dovrebbe funzionare (e funziona con array quadrati!). Il codice allegato dovrebbe essere eseguito con 'nvcc whateveryoucallit.cu -o runit'.

L'aiuto è apprezzato!

#include<stdio.h> 
#include<cuda.h> 
#include<iostream> 
#define height 16 
#define width 11 
#define BLOCKSIZE 16 

using namespace std; 

// Device Kernels 

//Texture reference Declaration 
texture<float,2> texRefEx; 


__global__ void kernel_w_textures(float* devMPPtr, float * devMPtr, int pitch) 
{ 
// Thread indexes 
     unsigned int idx = blockIdx.x*blockDim.x + threadIdx.x; 
     unsigned int idy = blockIdx.y*blockDim.y + threadIdx.y; 

// Texutre Coordinates 
float u=(idx)/float(width); 
float v=(idy)/float(height); 
devMPtr[idy*width+idx]=devMPPtr[idy*pitch/sizeof(float)+idx]; 
// Write Texture Contents to malloc array +1 
devMPtr[idy*width+idx]= tex2D(texRefEx,u,v);//+1.0f; 
} 
int main() 
{ 
// memory size 
size_t memsize=height*width; 
size_t offset; 
float * data, // input from host 
    *h_out, // host space for output 
    *devMPPtr, // malloc Pitch ptr 
    *devMPtr; // malloc ptr 

size_t pitch; 

// Allocate space on the host 
data=(float *)malloc(sizeof(float)*memsize); 
h_out=(float *)malloc(sizeof(float)*memsize); 


// Define data 
for (int i = 0; i < height; i++) 
for (int j=0; j < width; j++) 
    data[i*width+j]=float(j); 

// Define the grid 
dim3 grid((int)(width/BLOCKSIZE)+1,(int)(height/BLOCKSIZE)+1), threads(BLOCKSIZE,BLOCKSIZE); 

// allocate Malloc Pitch 
cudaMallocPitch((void**)&devMPPtr,&pitch, width * sizeof(float), height); 

// Print the pitch 
printf("The pitch is %d \n",pitch/sizeof(float)); 

// Texture Channel Description 
//cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>(); 
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32,0,0,0,cudaChannelFormatKindFloat); 

// Bind texture to pitch mem: 
cudaBindTexture2D(&offset,&texRefEx,devMPPtr,&channelDesc,width,height,pitch); 
cout << "My Description x is " << channelDesc.x << endl; 
cout << "My Description y is " << channelDesc.y << endl; 
cout << "My Description z is " << channelDesc.z << endl; 
cout << "My Description w is " << channelDesc.w << endl; 
cout << "My Description kind is " << channelDesc.f << endl; 
cout << "Offset is " << offset << endl; 

// Set mutable properties: 
texRefEx.normalized=true; 
texRefEx.addressMode[0]=cudaAddressModeWrap; 
texRefEx.addressMode[1]=cudaAddressModeWrap; 
texRefEx.filterMode= cudaFilterModePoint; 

// Allocate cudaMalloc memory 
cudaMalloc((void**)&devMPtr,memsize*sizeof(float)); 

// Read data from host to device 
cudaMemcpy2D((void*)devMPPtr,pitch,(void*)data,sizeof(float)*width, 
    sizeof(float)*width,height,cudaMemcpyHostToDevice); 

//Read back and check this memory 
cudaMemcpy2D((void*)h_out,width*sizeof(float),(void*)devMPPtr,pitch, 
    sizeof(float)*width,height,cudaMemcpyDeviceToHost); 

// Print the memory 
for (int i=0; i<height; i++){ 
    for (int j=0; j<width; j++){ 
    printf("%2.2f ",h_out[i*width+j]); 
    } 
cout << endl; 
} 

cout << "Done" << endl; 
// Memory is fine... 

kernel_w_textures<<<grid,threads>>>(devMPPtr, devMPtr, pitch); 

// Copy back data to host 
cudaMemcpy((void*)h_out,(void*)devMPtr,width*height*sizeof(float),cudaMemcpyDeviceToHost); 


// Print the Result 
cout << endl; 
for (int i=0; i<height; i++){ 
    for (int j=0; j<width; j++){ 
    printf("%2.2f ",h_out[i*width+j]); 
    } 
cout << endl; 
} 
cout << "Done" << endl; 

return(0); 
} 

Modifica 17 ottobre: ​​quindi non ho ancora trovato una soluzione a questo problema. Nvidia è abbastanza silenzioso su questo sembra che sia anche il mondo. Ho trovato una soluzione alternativa utilizzando mem condivisa, ma se qualcuno ha una soluzione di texture sarei molto felice.

Modifica Octoboer 26: Ancora nessuna soluzione, ma ancora interessata a uno se qualcuno lo sa.

Modifica 26 luglio: Wow sono trascorsi 9 mesi e non ho tenuto conto della risposta corretta per tutto il tempo. Il trucco era:

if (idx < width && idy < height){//.... code } 

Come era stato sottolineato prima. Grazie a tutti quelli che hanno contribuito!

+0

Ho eseguito un piccolo test e sembra che possa eseguire correttamente il codice per le dimensioni dell'array di (16 * M per 32 * N) dove M = 1,2,3,4 ... e N = 1,2 , 4,8 ... ecc. Se questo è il caso, Nvidia dovrebbe metterlo in questa guida di programmazione! – Marm0t

+0

Qual è la tua GPU? – karlphillip

+0

Ho provato su due (GTX 285, Fermi uno dei più nuovi) – Marm0t

risposta

3

Potrebbe avere a che fare con il tuo blocco. In questo codice si sta tentando di scrivere un blocco di 16x16 thread in un blocco di memoria 11x16. Ciò significa che alcuni dei tuoi thread stanno scrivendo nella memoria non allocata. Questo spiega anche perché i tuoi test di (16 * M per 32 * N) funzionavano: non c'erano thread che scrivevano nella memoria non allocata, poiché le tue dimensioni erano multiple di 16.

Un modo semplice per risolvere questo problema è qualcosa di simile a questo:

if ((x < width) && (y < height)) { 
    // write output 
    devMPtr[idy*width+idx]= tex2D(texRefEx,u,v); 
} 

Avrai bisogno di passare l'altezza e la larghezza alla funzione kernel o copiare una costante per la carta prima di chiamare il kernel.

+0

Dalla guida di programmazione cudamalloc pitch fa padding (Sto indovinando con zeri, non affermano esplicitamente che: "larghezza arrotondata al multiplo più vicino di questo [ pitch] e di conseguenza le sue righe vengono riempite di conseguenza. "Così, quando il riferimento di trama accede alla memoria non nella regione definita, dovrebbe accedere agli zeri (l'azione è definita). Puoi verificarlo scrivendo la memoria 2D nella memoria 2D (senza trame) Funziona bene, se leggi una regione che rappresenta l'array 2D riempito definito da cmp, vedi gli zeri nel posto appropriato, grazie per la tua risposta molto apprezzata – Marm0t

+0

@ Marm0t: coprirà le letture, ma non coprirà Si sta tentando di scrivere oltre i limiti del proprio array di output, che di solito provocherà un "Errore di avvio non specificato" – Eric

+1

Questa era la soluzione corretta - Grazie per l'aiuto. questo due volte prima di andare avanti. – Marm0t

0

Le schede grafiche in genere si aspettano che le trame abbiano dimensioni pari a 2, ciò è particolarmente vero per le schede nVidia. Cuda's CudaMallocPitch e cudaMemcpy2D funzionano con questi pitch e guardando il tuo codice, la soluzione più sicura è quella di regolare la larghezza e l'altezza per stare dalla parte della sicurezza. In caso contrario, Cuda potrebbe scrivere a una memoria non valida perché sarebbe aspettava offset errati:

#define height 16 
#define width 11 

... 

size_t roundUpToPowerOf2(size_t v) 
{ 
    // See http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 
    --v; 
    v |= v >> 1; 
    v |= v >> 2; 
    v |= v >> 4; 
    v |= v >> 8; 
    v |= v >> 16; 
    ++v; 
    return v; 
} 
... 

size_t horizontal_pitch = roundUpToPowerOf2(width); 
size_t vertical_pitch = roundUpToPowerOf2(height); 
size_t memsize = horizontal_pitch * vertical_pitch; 

... 

// Read data from host to device 
cudaMemcpy2D((void*)devMPPtr,pitch,(void*)data,sizeof(float)*horizontal_pitch, 
    sizeof(float)*width,height,cudaMemcpyHostToDevice); 

//Read back and check this memory 
cudaMemcpy2D((void*)h_out,horizontal_pitch*sizeof(float),(void*)devMPPtr,pitch, 
    sizeof(float)*width,height,cudaMemcpyDeviceToHost); 

// Print the memory 
for (int i=0; i<height; i++){ 
    for (int j=0; j<width; j++){ 
    printf("%2.2f ",h_out[i*horizontal_pitch+j]); 
    } 
cout << endl; 
} 

... 

// Copy back data to host 
cudaMemcpy((void*)h_out,(void*)devMPtr,horizontal_pitch*vertical_pitch*sizeof(float),cudaMemcpyDeviceToHost); 

// Print the Result 
cout << endl; 
for (int i=0; i<height; i++){ 
    for (int j=0; j<width; j++){ 
    printf("%2.2f ",h_out[i*horizontal_pitch+j]); 
    } 
cout << endl; 
} 
cout << "Done" << endl; 

Speriamo che non hanno trascurato nessun luogo dove horizontal_pitch/vertical_pitch dovrebbe essere usato al posto del normale larghezza/altezza.

+0

Ho appena provato questo e sto ancora ottenendo risultati errati - con questo piccolo array non viene prodotto molto. Qualcuno può dirmi come farlo funzionare? Fondamentalmente la prima uscita è 0 1 2 ... N dove N = (larghezza-1). La seconda uscita dovrebbe essere 1 2 3 ... N + 1 – Marm0t

1
// Texutre Coordinates 
float u=(idx + 0.5)/float(width); 
float v=(idy + 0.5)/float(height); 

È necessario un offset per raggiungere il centro del texel.Penso che ci potrebbe essere stato un errore di arrotondamento per il tuo non-multiplo di 16 trame. Ho provato questo e ha funzionato per me (entrambe le uscite erano identiche).

+0

Penso di averlo fatto prima - ma non dovrebbe importare. Ho usato "texRefEx.filterMode = cudaFilterModePoint" in modo che filtri su un singolo valore. - Proverò di nuovo come controllo di integrità:) – Marm0t

+0

Il campionamento dei punti non risolverà questo problema, poiché in realtà sta cadendo appena fuori dal bordo del texel. Sembra funzionare solo sulla modalità wrap e non sul clamp. – tkerwin

+0

beh, va bene, ero particolarmente interessato alla modalità wrap (l'intero problema che stavo incontrando era solo una curiosità/blocco stradale). Ti farò sapere come va - Se funziona, sarei felice al 95% (Se funziona, ho bisogno di ri-implementare le cose nelle trame dopo avere una soluzione di memoria condivisa ...) – Marm0t

0

Forse un'occhiata a questa discussione: http://forums.nvidia.com/index.php?showtopic=186585

Un altro pezzo campione molto utile di codice è attualmente in NVIDIA SDK; come menzionato nel thread precedente sui forum NVIDIA, l'esempio simplePitchLinearTexture funziona bene.

Poiché utilizziamo la memoria di trama, credo che le dimensioni della griglia 2D debbano essere di 2 su alcuni hardware, come suggerito anche in una delle risposte sopra.

2

Credo:

float u=(idx)/float(width); 
float v=(idy)/float(height); 

dovrebbe essere

float u=(idx+0.5f)/float(width); 
float v=(idy+0.5f)/float(height); 

Per la ottiene identico input/output, altrimenti la seconda colonna di uscita uguale alla prima colonna di ingresso anziché secondo e il secondo anche l'ultima colonna di output è sbagliata.

Per favore correggimi se hai un'osservazione diversa.