Open oviano opened 3 years ago
I have stuff like this planned for a complete overhaul of the resource update functions, but it will be a while until I'll get around to this. There might be some destination-coordinates restrictions in the different 3D APIs which I'm not yet aware of (e.g. I could imagine that updating compressed images only works at block granularity).
But a bigger problem is the restriction of only one update per frame, so you can't simply incrementally build a texture in the same frame, But even when distributing the update over several frames, the next problem is that dynamic textures are internally double-buffered, partial updates in two follupup frames would actually update different textures.
For a current workaround see sokol_fontstash.h, this keeps a backup "texture" (just a memory blob) in system RAM which is managed and updated by FontStash (this is essentially a dynamic texture atlas where FontStash adds new font glyphs as needed), and FontStash sets a dirty-flag when the sokol-texture needs be updated, which then copies the whole texture data:
Eventually I want to create a "proper" set of dynamic resource update functions which allow to copy regions of buffers and textures between cpu- and gpu-memory, and also between gpu resources.
Btw, I just found this in PVRTC texture compression spec (https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt):
5) Is sub-texturing supported?
Resolution: Only for the reloading of complete
images. Sub-images are not supportable because the PVRTC
algorithm uses significant adjacency information, so there is
no discrete block of texels that can be decoded as a standalone
sub-unit, and so it follows that no stand alone sub-unit of
data can be loaded without changing the decoding of surrounding
texels.
...so for compressed texture formats (at least PVRTC-compressed textures), partial image updates won't be possible.
Hi. What about updating one or some textures in texture array? It will be great.
+1
My use case is exactly the same as the OP. Being able to update parts of a texture without having to hold a copy of the pixels would be great.
I know this is not helpful, but as I'm coding, I've found another situation for partial texture updates.
I'm emulating consoles and computers, and some can change their resolution as needed by the game. I know the maximum width and height, and could allocate a texture that big and update only part of the sub-texture depending on the current resolution.
Right now, I have to always destroy and re-create the texture when the resolution changes.
My own use-case is virtual texturing (not necessarily with sparse textures). I want to essentially be able to fit large amounts of data into fixed amounts of RAM.
It might be a common enough use-case to warrant a separate extension (as a stop-gap without needing "full-blown" partial updates support --- though such an approach could take advantage of sparse texturing extensions).
Alos +1-ing this — exactly for a font texture atlas use.
a coarse but working snippet:
void updateTextureW4K(sg_image simg, int objmetah, const void* data, sg_pixel_format format)
{
_sg_image_t* img = _sg_lookup_image(&_sg.pools, simg.id);
_sg_gl_cache_store_texture_binding(0);
_sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]);
GLenum gl_img_target = img->gl.target;
glTexSubImage2D(gl_img_target, 0,
0, 0,
4096, objmetah,
_sg_gl_teximage_format(format), _sg_gl_teximage_type(format),
data);
_sg_gl_cache_restore_texture_binding(0);
}
I did some thinking on this, and one way to get this done in a way that's compatible with different APIs would be to introduce explicit sync points into the sokol_gfx API.
Not the best solution, but it would open up other possibilities in the future (e.g. compute shaders). Essentially something like:
sg_sync_point sync;
sg_update_subimage(img, &sync, data);
...
sg_sync(&sync); // or `sg_barrier` or whatever you call it
sg_draw(...);
(in things like OpenGL [ES], where glTexSubImage{2D,...}
are auto-synced, sg_sync
is simply a no-op … it would be nice to be able to make a small buffer write dependent upon another sync point, exactly for the purpose of virtual texturing, though)
I'd argue it'd fit a "sokol_gfx V2" more than the existing API, though; for one, it's a lower-level concept, and a completely new object/API concept. Arguably also not 100% in line with Sokol's philosophy (there's a reason as to why sokol_gfx does ring buffers for you, after all), but.
The alternative to explicit syncing is to simply do this internally, at the cost of stalls whenever you do a draw that reads from the same texture; an acceptable cost in many cases, but it causes problems for some use-cases[^1]. It would be closer to sokol's current APIs though.
[^1]: Basically wherever you might want to keep rendering despite parts of the texture not being updated yet — a typical case would be virtual textures, but also possibly font atlases, if you accept that some characters might be invisible for a few frames.
I am trying to implement a dynamic texture atlas and I realised that at the moment there doesn't seem to be a way of only updating a certain region of a texture.
Is it as simple as being able to pass (x, y, w, h) into sg_update_image() and have this propagated down to the relevant Metal replaceRegion, GL glTexSubImage2D etc, the assumption being that the data is structured in the same way as the original data (bytes per row etc)?