greedily creating RenderTextures at the maximum texture size
This is the root of the problem.
Your approach of "trial and error" creation might work, but to be honest, it's kind of sloppy. No graphics/game/OpenGL developer would even consider pushing the GL until it returns errors just to find out what it can/can't do. This is simply the wrong approach. GL errors are errors, not warnings or information. They aren't supposed to happen in well-behaved applications and if they do, they should be fixed if the software is to be considered well-written.
The other issue is that the GL_OUT_OF_MEMORY error can be slightly misleading. One might think at first that the error means that GPU memory has been exhausted, since the general assumption is that all SFML OpenGL resources reside in GPU memory. This isn't always true. You have to think of GPU memory just like your system RAM.
Many people might not know, but if your task/process manager reports that you only have 200MiB of RAM left unallocated, you can still open up a program that uses 500MiB of RAM and expect it to work properly. Where does this extra space come from? Something people tend to forget about called a pagefile.
Just like the host system, the GPU also uses virtual memory. You might be able to (depending on implementation) allocate 4GiB of textures even if your GPU only has 2GiB of memory, 2GiB will end up in unpaged memory somewhere in your system RAM. In your case, this is probably already happening, and you are significantly reducing the performance of your application because of this.
Checking the residency of GL textures used to be done in the legacy API using
glAreTexturesResident, however that function has been deprecated and removed from the core profile simply because applications should be designed not to have to care about residency in the first place.
The driver often knows better than you do how to handle virtual memory, so if you are getting GL_OUT_OF_MEMORY errors, you know you are doing something really really bad.
What SFML
can provide you (and it has already been up for discussion in the past) is a unified/simplified way of querying the hardware for capability data. Remember, this data should only be used as
hints. This way you know how much graphics RAM you have available and can estimate how much data you can actually end up fitting in it before it might get paged out. It isn't exactly rocket science if you know the data formats SFML uses, you just need to add a bit of overhead leeway on top of that as well.
TL;DR: You also don't do things like "greedily creating std::vectors at the maximum std::vector size" until std::bad_alloc is thrown. The same applies to graphics memory.