Взаимодействие CUDA / OpenGL: запись на поверхность объекта не стирает предыдущее содержимое - PullRequest
0 голосов
/ 23 декабря 2018

Я пытаюсь использовать ядро ​​CUDA для изменения текстуры OpenGL, но у меня возникла странная проблема, когда мои вызовы surf2Dwrite(), кажется, смешиваются с предыдущим содержимым текстуры, как вы можете видеть на изображении ниже.Деревянная текстура сзади - это то, что находится в текстуре, прежде чем модифицировать ее с моим ядром CUDA.Ожидаемый результат будет включать ТОЛЬКО цветовые градиенты, а не текстуру дерева за ним.Я не понимаю, почему происходит это смешивание.

weird texture blending

Возможные проблемы / недоразумения

Я новичок в CUDA иOpenGL.Здесь я попытаюсь объяснить процесс мышления, который привел меня к этому коду:

  • Я использую cudaArray для доступа к текстуре (а не, например, к массиву с плавающей точкой), потому что я читаючто лучше читать локальность кэша при чтении / записи текстуры.
  • Я использую поверхности, потому что где-то читал, что это единственный способ изменить cudaArray
  • Я хотел использовать поверхностьобъекты, которые я понимаю, как новый способ ведения дел.Старый способ - использовать поверхностные ссылки.

Некоторые возможные проблемы с моим кодом, которые я не знаю, как проверять / проверять:

  • Я не согласен сформаты изображений?Может я где-то не указывал правильное количество бит / канал?Может быть, я должен использовать float с вместо unsigned char с?

Сводка кода

Вы можете найти полный минимальный рабочий пример в этом GitHub Gist ,Это довольно долго из-за всех движущихся частей, но я постараюсь подвести итог.Я приветствую предложения о том, как сократить MWE.Общая структура выглядит следующим образом:

  1. создать текстуру OpenGL из локально сохраненного файла
  2. зарегистрировать текстуру в CUDA, используя cudaGraphicsGLRegisterImage()
  3. call cudaGraphicsSubResourceGetMappedArray()чтобы получить cudaArray, представляющий текстуру
  4. , создайте cudaSurfaceObject_t, который я могу использовать для записи в cudaArray
  5. передачи объекта поверхности ядру, которое пишет в текстурус surf2Dwrite()
  6. используйте текстуру для рисования прямоугольника на экране

Создание текстуры OpenGL

Я новичок в OpenGL, поэтому я используюРаздел «Текстуры» учебников LearnOpenGL в качестве отправной точки.Вот как я могу установить текстуру (используя библиотеку изображений stb_image.h)

GLuint initTexturesGL(){
    // load texture from file
    int numChannels;
    unsigned char *data = stbi_load("img/container.jpg", &g_imageWidth, &g_imageHeight, &numChannels, 4);
    if(!data){
        std::cerr << "Error:  Failed to load texture image!" << std::endl;
        exit(1);
    }

    // opengl texture
    GLuint textureId;
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);

    // wrapping
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

    // filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // set texture image
    glTexImage2D(
        GL_TEXTURE_2D,    // target
        0,                // mipmap level
        GL_RGBA8,         // internal format (#channels, #bits/channel, ...)
        g_imageWidth,     // width
        g_imageHeight,    // height
        0,                // border (must be zero)
        GL_RGBA,          // format of input image
        GL_UNSIGNED_BYTE, // type
        data              // data
    );
    glGenerateMipmap(GL_TEXTURE_2D);

    // unbind and free image
    glBindTexture(GL_TEXTURE_2D, 0);
    stbi_image_free(data);

    return textureId;
}

CUDA Graphics Interop

После вызова функции выше, я регистрирую текстуру в CUDA:

void initTexturesCuda(GLuint textureId){
    // register texture
    HANDLE(cudaGraphicsGLRegisterImage(
        &g_textureResource,                       // resource
        textureId,                                // image
        GL_TEXTURE_2D,                            // target
        cudaGraphicsRegisterFlagsSurfaceLoadStore // flags
    ));

    // resource description for surface
    memset(&g_resourceDesc, 0, sizeof(g_resourceDesc));
    g_resourceDesc.resType = cudaResourceTypeArray;
}

Render Loop

В каждом кадре я запускаю следующее, чтобы изменить текстуру и визуализировать изображение:

while(!glfwWindowShouldClose(window)){
        // -- CUDA --

        // map
        HANDLE(cudaGraphicsMapResources(1, &g_textureResource));


        HANDLE(cudaGraphicsSubResourceGetMappedArray(
            &g_textureArray,   // array through which to access subresource
            g_textureResource, // mapped resource to access
            0,                 // array index
            0                  // mipLevel
        ));

        // create surface object (compute >= 3.0)
        g_resourceDesc.res.array.array = g_textureArray;
        HANDLE(cudaCreateSurfaceObject(&g_surfaceObj, &g_resourceDesc));

        // run kernel
        kernel<<<gridDim, blockDim>>>(g_surfaceObj, g_imageWidth, g_imageHeight);

        // unmap
        HANDLE(cudaGraphicsUnmapResources(1, &g_textureResource));

        // --- OpenGL ---

        // clear
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // use program
        shader.use();

        // triangle
        glBindVertexArray(vao);
        glBindTexture(GL_TEXTURE_2D, textureId);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        // glfw:  swap buffers and poll i/o events
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

CUDA Kernel

Фактическая CUDAЯдро выглядит следующим образом:

__global__ void kernel(cudaSurfaceObject_t surface, int nx, int ny){
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;
    if(x < nx && y < ny){
        uchar4 data = make_uchar4(x % 255, 
                                  y % 255, 
                                  0, 255);
        surf2Dwrite(data, surface, x * sizeof(uchar4), y);
    }
}

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

Оказывается, проблема в том, что я по ошибке сгенерировал mipmaps для исходной текстуры дерева, а мое ядро ​​CUDA только модифицировало mipmap уровня 0.Я заметил, что смешивание было результатом интерполяции OpenGL между моим измененным mipmap уровня 0 и версией текстуры дерева с более низким разрешением.

Вот правильный вывод, полученный путем отключения интерполяции mipmap.Извлеченный урок!

correct output, no mipmapping

0 голосов
/ 23 декабря 2018

Если я правильно понимаю, вы сначала регистрируете текстуру, наносите ее один раз, создаете объект поверхности для массива, представляющего сопоставленную текстуру, а затем снимаете карту текстуры.Каждый кадр, затем вы снова отображаете ресурс, запрашиваете массив, представляющий сопоставленную текстуру, а затем полностью игнорируете его и используете объект поверхности, созданный для массива, который вы вернули, когда вы впервые отобразили ресурс. Из документации :

[…] Значение, установленное в array, может изменяться каждый раз, когда отображается resource.

Youнеобходимо создавать новый объект поверхности каждый раз, когда вы отображаете ресурс, потому что вы можете каждый раз получать новый массив.И, по моему опыту, вы действительно будете часто получать разные.Может быть правильным сделать создание нового объекта поверхности только тогда, когда массив действительно изменяется.Документация, кажется, учитывает это, но я никогда не пробовал, поэтому я не могу сказать, работает ли это наверняка ...

Кроме этого: вы генерируете mipmaps для своей текстуры.Вы перезаписываете только mip-уровень 0. Затем вы визуализируете текстуру, используя mipmapping с трилинейной интерполяцией.Поэтому я предполагаю, что вы просто визуализируете текстуру с разрешением, которое точно не соответствует разрешению mip уровня 0, и, таким образом, вы получите интерполяцию между уровнем 0 (на котором вы написали) и уровнем 1 (которыйбыла сгенерирована из оригинальной текстуры)…

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...