2010-09-30 6 views
6

¿Cómo puede I:Cómo leer con éxito de una textura 2D

  1. Enlazar cudaMallocPitch memoria flotador a una referencia de textura 2D
  2. copiar algunos datos de host a la matriz 2D en el dispositivo
  3. Añadir uno a la referencia de textura y escribir en a.) la matriz de tono 2D O b.) escribir en una matriz de memoria lineal
  4. Lea la respuesta y visualícela.

A continuación se muestra un código que debe lograr esto. Tenga en cuenta que para los tamaños de matriz NxN, mi código funciona. Para NxM donde N! = M, mi código muerde el polvo (no el resultado correcto). Si puede resolver este problema, le otorgaré 1 internets (suministro limitado). Tal vez estoy loco, pero de acuerdo con la documentación esto debería funcionar (¡y funciona para matrices cuadradas!). El código adjunto debería ejecutarse con 'nvcc whateveryoucallit.cu -o runit'.

¡Ayuda se aprecia!

#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); 
} 

Edit October 17: Así que todavía no he encontrado una solución a este problema. Nvidia guarda silencio sobre esto, parece que el mundo también lo es. Encontré una solución utilizando memorios compartidos, pero si alguien tiene una solución de textura, sería muy, por favor.

Editar Octoboer 26: Todavía no hay solución, pero sigue interesado en uno si alguien sabe.

Editar 26 de julio: Wow, han pasado 9 meses, y había pasado por alto la respuesta correcta todo el tiempo. El truco fue:

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

Como se ha señalado anteriormente. ¡Gracias a todos los que contribuyeron!

+0

He hecho algunas pruebas y parece que puedo ejecutar con éxito el código para dimensiones de matriz de (16 * M por 32 * N) donde M = 1,2,3,4 ... y N = 1,2 , 4,8 ... etc. ¡Si este es el caso, entonces Nvidia debería poner esto en su guía de programación! – Marm0t

+0

¿Cuál es su GPU? – karlphillip

+0

He probado en dos (GTX 285, Fermi uno de los más nuevos) – Marm0t

Respuesta

3

Podría tener que ver con su tamaño de bloque. En este código, intenta que un bloque de 16x16 hilos escriba en un bloque de memoria de 11x16. Eso significa que algunos de sus hilos están escribiendo en la memoria no asignada. Eso también explica por qué funcionó su prueba de (16 * M por 32 * N): no había hilos escribiendo en la memoria no asignada, ya que sus dimensiones eran un múltiplo de 16.

Una manera fácil de solucionar este problema es algo así como esto:

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

tendrá que pasar o bien la altura y la anchura de la función del núcleo o copiar una constante a la tarjeta antes de llamar al núcleo.

+0

Desde la guía de programación cudamalloc pitch does padding (supongo que con ceros, no declaran explícitamente que: "ancho redondeado al múltiplo más cercano de esto [ pitch] size y sus filas acolchadas en consecuencia. "Así que cuando la referencia de textura no acceda a la memoria en la región definida, debe acceder a ceros (la acción está definida). Puede probar esto escribiendo memoria 2D en memoria 2D (sin texturas) - Funciona bien. Si vuelve a leer una región que representa la matriz acolchada 2D definida por cmp, verá ceros en el lugar apropiado, gracias por su respuesta muy apreciada. – Marm0t

+0

@ Marm0t: Eso cubrirá las lecturas, pero no cubrirá escribe. Está intentando escribir fuera de los límites de su matriz de salida, lo que generalmente dará como resultado una "falla de lanzamiento no especificado". – Eric

+1

Esta fue la solución correcta. Gracias por ayudarme. Definitivamente debería haber leído esto dos veces antes de continuar. – Marm0t

0

Las tarjetas gráficas generalmente esperan que las texturas tengan dimensiones que son potencias de 2, esto es especialmente cierto para las tarjetas nVidia. CudaMallocPitch y cudaMemcpy2D de Cuda funcionan con estos tonos y, al mirar su código, la solución más segura es ajustar el ancho y la altura usted mismo para estar seguro. De lo contrario, Cuda podría escribir en una memoria no válida, ya que estaría esperando desplazamientos incorrectos:

#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; 

Esperemos que no han pasado por alto cualquier lugar donde horizontal_pitch/vertical_pitch debe utilizarse en lugar de la llanura de anchura/altura.

+0

Acabo de probar esto y sigo obteniendo resultados incorrectos: con esta pequeña matriz, no genera demasiados resultados. ¿Alguien puede decirme cómo hacer que esto funcione? Básicamente, la primera salida es 0 1 2 ... N donde N = (ancho-1). La segunda salida debe ser 1 2 3 ... N + 1 – Marm0t

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

Necesita un desplazamiento para llegar al centro del texel.Creo que podría haber habido algún error de redondeo para tu no múltiplo de 16 texturas. Intenté esto y funcionó para mí (ambos resultados fueron idénticos).

+0

Creo que he hecho esto antes, pero no debería importar. Usé 'texRefEx.filterMode = cudaFilterModePoint' para que se filtre a un solo valor. - Lo intentaré de nuevo como un control de cordura:) – Marm0t

+0

El muestreo de puntos no solucionaría este problema, ya que en realidad está cayendo justo fuera del borde del texel. Sin embargo, parece funcionar en el modo de ajuste y no en abrazadera. – tkerwin

+0

bueno, eso está bien, específicamente estaba interesado en el modo de ajuste (todo este problema que estaba encontrando era solo un bloqueo de curiosidad/camino). Te dejaré saber cómo funciona: si esto funciona, estaré un 95% satisfecho (si funciona, significa que necesito volver a implementar las cosas en texturas después de tener una solución de memoria compartida ...) – Marm0t

0

Tal vez echar un vistazo a este tema: http://forums.nvidia.com/index.php?showtopic=186585

Otra pieza de muestra muy útiles de código se encuentra actualmente en el SDK de NVIDIA; como se menciona en el hilo anterior en los foros de NVIDIA, el ejemplo simplePitchLinearTexture funciona bien.

Dado que estamos utilizando la memoria de textura, creo que los tamaños de la grilla 2D deben ser potencias de 2 en algunos hardware, como también se sugiere en una de las respuestas anteriores.

2

pienso:

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

debería ser

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

Para el conseguir idénticos de entrada/salida, de lo contrario la segunda columna de salida es igual a la primera columna de entrada en lugar de la segunda y la segunda La última columna de salida también es incorrecta.

Corrígeme si tiene observaciones diferentes.

Cuestiones relacionadas