1
\$\begingroup\$

Lets suppose that I have a large, 2048 by 2048 pixel texture uploaded to the GPU.

I'd like to occasionally be able to edit parts of it, e.g. a 64x64 area.

Re-uploading the entire texture would be a huge waste of resources so is there any other way of doing this?

\$\endgroup\$
  • 1
    \$\begingroup\$ Divide it into 16 smaller ones, and only change those. \$\endgroup\$ – Bálint Jun 7 '17 at 11:09
  • \$\begingroup\$ Why did my question get downvoted? \$\endgroup\$ – Lolums Jun 7 '17 at 11:13
  • 1
    \$\begingroup\$ @Bálint isn't switching between textures is expensive? \$\endgroup\$ – Lolums Jun 7 '17 at 11:15
  • \$\begingroup\$ You can technically have them in a single texture array, and only activate that \$\endgroup\$ – Bálint Jun 7 '17 at 11:19
  • 1
    \$\begingroup\$ I'm not fluent in OpenGL, but I think you can do this without treating each block as its own texture the way Bálint suggests. From a quick search, it looks like glTexSubInage2D may be the method you're looking for. I'd have to defer to someone with more OpenGL experience to describe how to use it correctly. \$\endgroup\$ – DMGregory Jun 7 '17 at 12:19
2
\$\begingroup\$

glTexSubImage2D will do exactly what you wish.

Assume that you have already created the base texture:

glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, 2048, 2048, 0, GL_RGBA, GL_UNSIGNED_BYTE, dataPtr);

Assume that you have a 64x64 region of the data that you wish to replace and you have the new data ready in a system memory array:

unsigned int newData[64][64];

Assume that you wish to transfer this data to a 64x64 rectangle in the original texture positioned at xofs, yofs from it's origin (and remembering that the origin in OpenGL is at bottom-left).

Using glTexSubImage2D to transfer this to the texture, you would make the following calls:

// tell OpenGL that each row of the original texture is 2048 texels wide
// so that it can correctly transfer a smaller width rectangle to it
glPixelStorei (GL_UNPACK_ROW_LENGTH, 2048);

// transfer the data
glTexSubImage2D (GL_TEXTURE_2D, 0, xofs, yofs, 64, 64, GL_RGBA, GL_UNSIGNED_BYTE, newData);

// reset to default behaviour
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);

If your new data is not in GL_RGBA format (typically problem cases occur if you use GL_RGB and your source data may therefore not be aligned to 4 bytes) then you may need to also make the following call before glTexSubImage2D:

// source data may not be aligned to 4 bytes
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

And the following after glTexSubImage2D.

// reset to default behaviour
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);

It doesn't otherwise matter where these calls are made with respect to your other glPixelStorei calls.


Note that this answer assumes a full conformant desktop OpenGL.

If you are targetting older hardware (and by this I mean 1990s vintage 3DFX hardware) then you will need to be aware that these typically did not use a full OpenGL driver and their mini-driver may or may not support the required calls (particularly the glPixelStorei stuff).

If you're only interested in hardware from this century you'll be OK: even Microsoft's default software OpenGL fully supports the glPixelStorei stuff.

OpenGL ES is more interesting; GL_UNPACK_ROW_LENGTH only became available with ES 3.0 so if you're targetting ES 2.0 or below you'll need to find alternate solutions (maybe upload the full width of the texture).


Performance

Depending on how you manage this kind of texture update, it can have anything from an insignificant to a catastrophic impact on performance. I can only provide general guidelines.

  • It used to be the case that using GL_BGRA and GL_UNSIGNED_INT_8_8_8_8_REV for your format and type parameters gave the most consistent performance across all hardware. This required OpenGL 1.2 or higher which shouldn't be a problem. On any hardware from the past 10 years this is no longer as relevant as it once was.

  • If you find yourself doing multiple small updates per frame try to batch them into few large updates instead.

  • If you update a texture that has pending draw calls on it, your driver may need to stall the pipeline for the draw calls to complete before it can perform the update (some drivers might copy off the data so that they can perform the update later).

  • Typical advice for the next step with this kind of texture updating is to "use a PBO"; your texture updates will become faster for sure, but now you need to carefully manage your PBO updates instead. You're better off, if you already have acceptable peformance, to not complicate things.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.