How to map/unmap a texture?

glMapBuffer() doesn’t return nullptr, but why it doesn’t work?

struct ImageMappedResource {
    uint8_t   *_rgba_data;
    size_t     _rowPitch;
    glm::ivec3 _extent;

    void     setTexel(const glm::ivec2 &pt, uint32_t color);
    uint32_t getTexel(const glm::ivec2 &pt);
    void     drawRect(const glm::ivec2 &a, const glm::ivec2 &b, uint32_t color);
};

ImageMappedResource _Image::map() {
    // Referenced from [Map and fill texture using PBO (OpenGL 3.3)](https://gamedev.stackexchange.com/questions/35486/map-and-fill-texture-using-pbo-opengl-3-3)

    ImageMappedResource mappedResource = {};    
    DEBUG_VERIFY(_extent.z == 1); // Make sure not 3D
    glGenBuffers(1, &this->_gl._pboID);
    glBindTexture(GL_TEXTURE_2D, this->_gl.tex);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->_gl._pboID);
    glBufferData(GL_PIXEL_UNPACK_BUFFER, (_extent.x * _extent.y * 4), nullptr, GL_STREAM_DRAW); // Allocate memory for PBO
    mappedResource._rgba_data = (uint8_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE);
    gl_checkErrors("glMapBuffer");
    mappedResource._rowPitch = (4*_extent.x);
    mappedResource._extent = _extent;
    return mappedResource;
}

void _Image::unmap() {
    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // Unmap
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind
    glDeleteBuffers(1, &this->_gl._pboID); // Delete PBO
}

Let me explain it doesn’t work like how? Let ‘image’ an image from file, so I want to draw a rectangle from (0, 0) to (20, 20), but the image is not modified, I mean it’s impossible to modify the memory, but why?

ImageMappedResource mappedResource = image->map();
    mappedResource.drawRect(glm::ivec2(0, 0), glm::ivec2(20, 20), 0xFF00FF00);
image->unmap();

You’re missing a glTexImage/glTexSubImage call.

Binding a buffer to GL_PIXEL_UNPACK_BUFFER simply causes those calls to read from the buffer rather than from client memory (and avoids synchronisation).

What do you mean with missing glTexImage/glTexSubImage call? Where should I place it? According to my understand, if I update the unmap() function then should it look like this but this makes the program crashes with an error: OpenGL error in “useProgram”: 1282 (GL_INVALID_OPERATION)

void _Image::unmap() {
    // Update texture with new data (Where glTexImage2D should be placed?).
    glTexImage2D(
        GL_TEXTURE_2D,
        0,
        GL_RGBA,
        _extent.x,
        _extent.y,
        0,
        GL_RGBA,
        GL_UNSIGNED_BYTE,
        nullptr // Should I use mappedResource._rgba_data instead of nullptr?
    );

    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // Unmap
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind
    glDeleteBuffers(1, &this->_gl._pboID); // Delete PBO
}

You need to unmap the buffer before calling glTexImage2D:

In general, buffers have to be unmapped when accessed by OpenGL commands (i.e. the buffer can be accessible from the client or from the implementation, but not both at once). This restriction doesn’t apply to persistently-mapped buffers (GL_MAP_PERSISTENT_BIT set both in glBufferStorage and glMapBufferRange), but those require OpenGL 4.4 or the ARB_buffer_storage extension.

1 Like

@GClements > Thank you, unmap() is fixed now, which means that if there is a modification between map() and unmap() then the texture will be updated.

But, why when I call map() then the image is cleared which means it becomes empty? If there is no modification between map() and unmap() then the image should stay the same instead of becoming empty. How to fix it?

ImageMappedResource _Image::map() {
    // Referenced from [Map and fill texture using PBO (OpenGL 3.3)](https://gamedev.stackexchange.com/questions/35486/map-and-fill-texture-using-pbo-opengl-3-3)

    ImageMappedResource mappedResource = {};    
    DEBUG_VERIFY(_extent.z == 1); // Make sure not 3D
    glGenBuffers(1, &this->_gl._pboID);
    glBindTexture(GL_TEXTURE_2D, this->_gl.tex);

    // TODO: Keep the previous image of that texture above.

    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->_gl._pboID);
    glBufferData(GL_PIXEL_UNPACK_BUFFER, (_extent.x * _extent.y * 4), nullptr, GL_STREAM_DRAW); // Allocate memory for PBO
    mappedResource._rgba_data = (uint8_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE);
    gl_checkErrors("glMapBuffer");
    mappedResource._rowPitch = (4*_extent.x);
    mappedResource._extent = _extent;
    return mappedResource;
}

void _Image::unmap() {
    // Unmap
    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);

    // Update texture with new data
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _extent.x, _extent.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    glBindTexture(GL_TEXTURE_2D, 0); // Unbind
    glDeleteBuffers(1, &this->_gl._pboID); // Delete PBO
}

That’s what you told it to do. You allocated a new buffer of storage (that’s what glBufferData does) in map. Then you uploaded whatever is in that new buffer into the image in unmap.

Remember: you are not “mapping the texture”. You are mapping a buffer from which you later transfer pixel data to the texture. So its up to you to properly manage the contents of that buffer’s storage.

1 Like

Thank you, so I need to big modify the function map() in order to do what I want, it will need a lot of skill, maybe in the future. I realize that the goal of map / unmap is for advanced feature such as texture painting or dynamically update voxel texture with CPU, etc. But now, I use a basic method such as when I want to update a texture, I delete it and create a new texture with new data, it’s still ok because no big performance is required.