-
Notifications
You must be signed in to change notification settings - Fork 522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Read contents of offscreen render target? #282
Comments
No, reading resource content back to the CPU side is generally not supported in sokol_gfx.h, that's also true for render targets. I'd suggest using the extension function approach described in #171 and calling the backend 3D-API specific functions required to get the data back directly. |
Yep, makes sense (it is indeed very slow). I ended up implementing this little function:
I found that you have to call this function after the second onscreen pass. That is, the first pass renders the texture off-screen, then the second pass renders it to a full-screen quad. Right before the |
How to do this on other backends (Metal/DX11/WebGPU), does anyone have this extension implemented for those other backends? Reading pixels from render target textures is useful for my use case where I need to cache heavy rendering output to the disk and also to save screenshots to disk. |
I needed that and ended implementing a DownloadTexture function for DirectX 11 and Metal. You can't read the texture directly and have to copy it first. Here is the extract for Metal:
|
@Fra-Ktus Thanks for the code, I've ended up with some similar code for Metal. @floooh What's your opnion on this being officially supported by |
No objections, a PR would be welcome. I guess it's better to have any way to extract pixel data to the CPU then none at all, even if it's slow ;) |
@edubart Here is my DirectX11 code to download the bitmaps:
|
FWIW, we have started to collect some of these methods in a separate |
To be honest I'm a bit undecided again on whether this should go into the core API after sleeping over it... Maybe it would indeed make sense to put it into an extension header after all (there could be an "exts" directory in the sokol repository similar to the "utils" directory. (the point being that reading out pixels "naively" is a blocking operation on all APIs, and a proper async version is either a lot more effort, or impossible (e.g. GL))... |
It is not like it would cause any harm, but in some cases it is heavily important to have such ability (rendering expensive stuff which is cached within a texture). Asynchronous reading would be difficult, indeed, but I think that overall it is not needed as the caller should know exactly how expensive such request is and that it needs to be used with care. Anyway, @Fra-Ktus - you are forgetting about one important thing. Sokol gfx has an internal buffer which needs to be committed before attempting to read back, otherwise you might end up with gibberish data like I did experience myself (rendering to an offscreen target to capture it and cache right away after ending pass for a framebuffer). Also, capturing cannot work inside an on-going pass in APIs with command buffers, so before capturing following example should be included: SOKOL_ASSERT(!_sg.mtl.in_pass);
if(_sg_mtl_cmd_buffer) {
#if defined(_SG_TARGET_MACOS)
[_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)];
#endif
[_sg_mtl_cmd_buffer commit];
[_sg_mtl_cmd_buffer waitUntilCompleted];
_sg_mtl_cmd_buffer = [_sg_mtl_cmd_queue commandBufferWithUnretainedReferences];
} It's a piece of code from _sg_mtl_commit without encoding presentDrawable because we don't want to do that yet. |
@iryont - Yes, I am doing that. See the shader browser I build using those functions to read back the content of rendered shaders... |
Yea, you did show a good example. It is precisely my point why reading back is an important feature in some cases since real-time rendering would be rather impossible due to required computation power. |
Sorry for digging this post up. I'm trying to use this implementation for picking for my editor that I'm currently working on but I'm seeing a bunch of strange problems that I now suspect are due to the ordering of the calls to glgetteximage() and other operations. I suspect the function above is missing a call to bind the texture before reading it. |
Would be super cool to have something in the official distro for reading pixel from a rendertarget texture. +2000 to that |
For now I'd prefer this in some sort of optional "extension header". I'm planning an overhaul of the entire resource management area for copying data into and between resources (might be a while though). Maybe that's an opportunity to tackle the "copy data out of resources" problem, but any solution that's created before would be broken anyway. |
Just to add-on another read-pixel solution for opengl, I was happy to see my PBO RAII implementation (desktop only iirc, or maybe I never finished an ES) worked straight away with a couple of lines. (This is faster than glGetTexImage on FBO) Usage; Needs putting in a nice sokol-style approach :) In this SoyLib there's also a few other approaches (eg. apple's CPU-memory-backed-buffer for textures which is ultra fast read/write) |
Continuing to bump this post, I’ve significantly improved performance of my implementation to limit the copy to only the pixel that the mouse is over. Copy times went from 100s of ms to <1ms. If anyone is interested I can figure out how to share my Sokol ‘additions’ to get this working. |
@floooh have you locked into a solution for this yet? There's been some increased discussion for this being needed for V UI and wondering if we're any closer to having something you feel could work. |
any updates on this? it would be cool to have a simple screenshot() function that works with all API's and also readpixels() for render targets, this could be useful for people who want to render to video file. |
Maybe would be nice to have just a complete example for OpenGL / Metal / DirectX available that can be copied without having to figure it out through this issue.. Going to implement as suggested in this issue, in order to write the texture contents into a file to be able to do test driven development with image comparison to reference images. @Fra-Ktus, @iryont or @edubart care to share your code for this? Btw @Fra-Ktus the shader browser looks pretty cool, would have you have source available for that to look at the implementation? |
Take a look at https://github.com/edubart/sokol_gp/blob/beedb873724c53eb56fe3539882218e7a92aac80/sokol_gfx_ext.h But keep in mind that I'm no longer supporting that code in this project, because it was out of scope for the project, so the file was removed in more recent commits. However, the code worked fine. The Metal part of that code was made by @iryont , so credits goes to him too. |
Thank you @edubart , that's a nice and clean implementation, and works perfectly when testing with OpenGL. Could easily clean it up though, seems there is only SDL_ConvertPixels and some constants used from SDL. |
@Sakari369 You don't need SDL2 for that matter at all. I just wanted a static output format of RGBA despite either ARGB or ABGR framebuffer underlying format. |
@iryont Ah okay, thank you for describing that out. |
+1 Could use texture read back. I don't care that it is slow/blocking - I have to get the pixels in order to save as an image to disk for an "export" feature. If you want to encourage people away from slow paths, call it something like |
I have a friend using a renderer I built around sokol to generate data for an ML project. |
IMO this feature would be valuable for debugging, for example being able to save the RT context to a .png. |
Is it possible to access the pixels of an offscreen render target texture? Issue #171 seems to imply that reading the contents of the screen is not supported by sokol. Does this apply to render targets? My setup is similar to the one in the offscreen example:
The text was updated successfully, but these errors were encountered: