Read back texture to host memory from cudaTextureObject

Hi,

Is it possible to read back a texture represented by a cudaTextureObject to host memory (for debugging purposes)? How would I do this? I can’t seem to find any examples.

Thanks in advance,

– Chuppa

Sure. A cuda texture object is created with either a pointer to linear memory or a cudaArray type, as the data source. A linear memory source could be copied back to host using a cudaMemcpy operation, and a cudaArray has to be set up from the host anyway, so you already have that data on the host (somewhere).

If you wan to provide a specific, complete example of how you are using cudaTextureObject, I can probably suggest something fairly simple.

1 Like

Hi, thanks for the quick answer!

I am using the cudaTextureObject to store UV world data (position, normal, etc.) for objects in a 3D scene. Here is what the function looks like where I am creating the textures:

  void Renderer::prepareUVWorldPositionsPerObject()
    {
        int numObjects = scene.getGameObjects().size();

        UVWorldPositionsTextures.resize(numObjects);
        UVNormalsTextures.resize(numObjects);
        UVDiffuseColorTextures.resize(numObjects);
        diffuseTextureUVsTextures.resize(numObjects);
        objectHasTexture.resize(numObjects);

        for (int o = 0; o < numObjects; o++)
        {
            objectHasTexture[o] = scene.getGameObjects()[o]->model->mesh->diffuseTextureID > -1;

            int texSize = directTextureSizes[o];

            std::vector<float> worldPositions(texSize * texSize * 4, -1000.0f);
            std::vector<float> worldNormals(texSize * texSize * 4, 0.0f);
            std::vector<float> diffuseColors(texSize * texSize * 4, 0.0f);
            std::vector<float> diffuseTextureUVs(texSize * texSize * 4, 0.0f);

            float progress = 0.1f;
            for (int i = 0; i < texSize * texSize; i++)
            {
                const float u = float(i % texSize) / float(texSize);
                // (i - i % texSize) / texSize gives us the row number, divided by texSize gives us the V coordinate 
                const float v = (float((i - (i % texSize))) / float(texSize)) / float(texSize);
                glm::vec2 uv = glm::vec2{ u,v };
                UVWorldData uvWorldData = UVto3DPerObject(uv, scene.getGameObjects()[o]);

                worldPositions[i * 4 + 0] = uvWorldData.worldPosition.x;
                worldPositions[i * 4 + 1] = uvWorldData.worldPosition.y;
                worldPositions[i * 4 + 2] = uvWorldData.worldPosition.z;
                worldPositions[i * 4 + 3] = 0.0f;

                worldNormals[i * 4 + 0] = uvWorldData.worldNormal.x;
                worldNormals[i * 4 + 1] = uvWorldData.worldNormal.y;
                worldNormals[i * 4 + 2] = uvWorldData.worldNormal.z;
                worldNormals[i * 4 + 3] = 0.0f;

                diffuseColors[i * 4 + 0] = uvWorldData.diffuseColor.x;
                diffuseColors[i * 4 + 1] = uvWorldData.diffuseColor.y;
                diffuseColors[i * 4 + 2] = uvWorldData.diffuseColor.z;
                diffuseColors[i * 4 + 3] = 0.0f;

                diffuseTextureUVs[i * 4 + 0] = uvWorldData.diffuseTextureUV.x;
                diffuseTextureUVs[i * 4 + 1] = uvWorldData.diffuseTextureUV.y;
                diffuseTextureUVs[i * 4 + 2] = 0.0f;
                diffuseTextureUVs[i * 4 + 3] = 0.0f;

                // Assign this UV to the cell that it belongs to, so in the scattering pass we can operate on local UVs
                scene.grid.assignUVToCells(uv, uvWorldData.worldPosition, o);

                if (float(i) / float(texSize * texSize) > progress)
                {
                    std::cout << "Progress: " << progress * 100 << "%." << std::endl;
                    progress += 0.1f;
                }
            }

            cudaChannelFormatDesc channelDesc;
            channelDesc = cudaCreateChannelDesc<float4>();
            int32_t width = texSize;
            int32_t height = texSize;
            int32_t pitch = width * sizeof(float4);

            // Initialize and allocate CUDA arrays
            cudaArray* worldPosArray;
            cudaArray* worldNormalArray;
            cudaArray* diffuseColorArray;
            cudaArray* diffuseTextureUVArray;


            CUDA_CHECK(MallocArray(&worldPosArray,
                &channelDesc,
                width,
                height,
                cudaArraySurfaceLoadStore));

            CUDA_CHECK(MallocArray(&worldNormalArray,
                &channelDesc,
                width,
                height,
                cudaArraySurfaceLoadStore));

            CUDA_CHECK(MallocArray(&diffuseColorArray,
                &channelDesc,
                width,
                height,
                cudaArraySurfaceLoadStore));

            CUDA_CHECK(MallocArray(&diffuseTextureUVArray,
                &channelDesc,
                width,
                height,
                cudaArraySurfaceLoadStore));

            CUDA_CHECK(Memcpy2DToArray(worldPosArray,
                /* offset */0, 0,
                worldPositions.data(),
                pitch, pitch, height,
                cudaMemcpyHostToDevice));


            CUDA_CHECK(Memcpy2DToArray(worldNormalArray,
                /* offset */0, 0,
                worldNormals.data(),
                pitch, pitch, height,
                cudaMemcpyHostToDevice));


            CUDA_CHECK(Memcpy2DToArray(diffuseColorArray,
                /* offset */0, 0,
                diffuseColors.data(),
                pitch, pitch, height,
                cudaMemcpyHostToDevice));

            CUDA_CHECK(Memcpy2DToArray(diffuseTextureUVArray,
                /* offset */0, 0,
                diffuseTextureUVs.data(),
                pitch, pitch, height,
                cudaMemcpyHostToDevice));

            // Resource description for the CUDA arrays
            cudaResourceDesc resourceDescPositions = {};
            resourceDescPositions.resType = cudaResourceTypeArray;
            resourceDescPositions.res.array.array = worldPosArray;

            cudaResourceDesc resourceDescNormals = {};
            resourceDescNormals.resType = cudaResourceTypeArray;
            resourceDescNormals.res.array.array = worldNormalArray;

            cudaResourceDesc resourceDescColors = {};
            resourceDescColors.resType = cudaResourceTypeArray;
            resourceDescColors.res.array.array = diffuseColorArray;

            cudaResourceDesc resourceDescDiffuseUVs = {};
            resourceDescDiffuseUVs.resType = cudaResourceTypeArray;
            resourceDescDiffuseUVs.res.array.array = diffuseTextureUVArray;

            // Texture object creation
            cudaTextureDesc texDesc = {};
            texDesc.addressMode[0] = cudaAddressModeWrap;
            texDesc.addressMode[1] = cudaAddressModeWrap;
            texDesc.filterMode = cudaFilterModePoint;
            texDesc.readMode = cudaReadModeElementType;
            texDesc.normalizedCoords = 1;
            texDesc.maxAnisotropy = 1;
            texDesc.maxMipmapLevelClamp = 99;
            texDesc.minMipmapLevelClamp = 0;
            texDesc.mipmapFilterMode = cudaFilterModePoint;
            texDesc.borderColor[0] = 1.0f;
            texDesc.sRGB = 0;

            // Create texture objects
            cudaTextureObject_t cudaTexPositions = 0;
            CUDA_CHECK(CreateTextureObject(&cudaTexPositions, &resourceDescPositions, &texDesc, nullptr));
            UVWorldPositionsTextures[o] = cudaTexPositions;

            cudaTextureObject_t cudaTexNormals = 0;
            CUDA_CHECK(CreateTextureObject(&cudaTexNormals, &resourceDescNormals, &texDesc, nullptr));
            UVNormalsTextures[o] = cudaTexNormals;

            cudaTextureObject_t cudaTexDiffuseColors = 0;
            CUDA_CHECK(CreateTextureObject(&cudaTexDiffuseColors, &resourceDescColors, &texDesc, nullptr));
            UVDiffuseColorTextures[o] = cudaTexDiffuseColors;

            cudaTextureObject_t cudaTexDiffuseTextureUVs = 0;
            CUDA_CHECK(CreateTextureObject(&cudaTexDiffuseTextureUVs, &resourceDescDiffuseUVs, &texDesc, nullptr));
            diffuseTextureUVsTextures[o] = cudaTexDiffuseTextureUVs;
        }
}

As you can see in this example I have 4 vectors of cudaTextureObject objects. I would like a method to read them out to host memory and visualize them with an image writing library like stb. I am using cudaArrays, but they seem to be strictly local to this function. Given what you said in your response, I assume this is bad practice and I should store them as a member variable, so I can access them later?

That is the data in the texture right there. The thing that is worldPositions.data().

and similarly for the others.

No need to copy it back, you already have it in host memory, right there.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.