![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
Hi, I'm back to ask if anyone can comment on the following issue.
I have some code which crashes on resizing a window. As far as I can tell this is a design fault in SDL and cannot be fixed, but I could be wrong. Some rough history: SDL originally was designed for full screen games. But a window was allowed as well to make debugging and development easier. In SDL2, the old windowing API was partly scrapped and redesigned to allow multiple windows, and more advanced API to allow accelerated drawing introduced. Unfortunately the documentation is a bit light, and the some legacy API still exists which simply cannot work. To do stuff in SDL you need a surface or a renderer. Blits use a surface, drawing uses a renderer. Blits can be done rendering too by making a texture. There is a renderer which can be used on an in-memory surface, the so-called software renderer. To get a surface from a window you call a function: SDL_GetWindowSurface you can then blit to that surface, or make a software renderer and draw on it. Then you update the window to put the drawing on the actual hardware window. But there's a problem. You must not free the surface obtained from a window. It is free'd automatically when the window is closed. Unfortunately, that's not the end of it. If the window is resizable and resized, the surface obviously MUST be "wrong" at some point. It's not clear exactly when. It's not clear what happens. It IS clear, however, that in the absence of a lock, the API simply cannot work. It's a design fault. The window is resized asynchronously. So the ONLY possible way the API could work is that any previously obtained surface remains valid until the next attempt to obtain a surface (or the window is closed). or some other *specified* function is called. This means that the surface dimensions, pixels, etc, will be wrong, but the surface should still update the window without crashing. However if you then get another surface, it would be different, having the right dimensions, so the old one would HAVE to be deleted. Otherwise every call to get another surface would have to add all the surfaces to a list and only delete them all when the window is closed. Leading to a massive leak. But the documentation does not say that this happens. It actually says the surface is invalidated by a resize, but only because I myself recently added that note. Now I suspect I'm wrong. I suspect it IS in fact invalidated by a resize, i.e. it is deleted. But this is not acceptable. Because the window resize is an asynchronous event driven by the user, and the program can only detect it has happened AFTER the fact by reading a resize event from the event queue. Which is too late if you were actually drawing the window during the resize, the surface would have been deleted under your feet. SO: in order to safely use the surface you get from a window it is *essential* that it be documented when that surface is no longer valid and that *has* to be in terms of a set of API calls that may invalidate it. It cannot be invalidated (deleted) by merely resizing the window asynchronously. The only sane behaviour I can think of is that it is invalidated by an update. It doesn't say that in the documentation. So you are safe if you draw on the surface, do an update, and refetch the surface. [The update could fail if the window is resized, but that won't cause a segfault from accessing free()d memory] I actually do this and my code still crashes. I'm running OSX 10.6.8 from SDL2.0.3 binary. Unfortunately I can't compile SDL on this platform and I can't upgrade at the moment either. I had a look at the software renderer in the repository, and the clipping code doesn't seem right (seems one pixel off). On OSX, when I resize the window I get crud everywhere: I'm not refilling the background. It looks like the surface pixels are being realloc()d (because some of the old stuff is visible, but stretched diagonally). [BTW: SDL says I do NOT need to lock the surface] It is quite possible there's a bug in my code causing the crash, but the fact remains the SDL2 API is broken: if it isn't bugged, then at least the documentation is. My code is written in Felix with a binding I wrote which translates it all to C++. I wrote a small C program to demonstrate the problem but it doesn't crash :) -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||
|
![]() |
resize window SDL2 | ![]() |
Alex Szpakowski
Guest
![]() |
![]() |
Internal window resizing happens inside SDL_PumpEvents (which is called implicitly by SDL_PumpEvent), so it’s not asynchronous.
SDL_LockSurface and SDL_UnlockSurface also exist – are you making sure to use those when necessary?
_______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||
|
![]() |
resize window SDL2 | ![]() |
Alex Szpakowski
Guest
![]() |
![]() |
Sorry, a typo: SDL_PumpEvents is called by SDL_PollEvent.
Also, in OS X in particular SDL_GetWindowSurface uses a SDL_Renderer and SDL_Texture internally. It might benefit your code to just use SDL_Render directly rather than using the Window Surface functions.
_______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
On 16/02/2015, at 9:26 AM, Alex Szpakowski wrote:
[On previous email: SDL says no need to lock the surface]
Ok, that makes sense, so you're saying, the window surface remains valid until SDL_PumpEvents (called by SDL_PollEvent). This doesn't explain my crash, but it could be caused by some other problem.
Yes, I know. However, there's a reason for starting with the more basic functions. One of these is roughly that code which starts with a surface and creates a renderer will work on a window surface and a separate surface the same way and in particular, it should work be possible to use those functions in any thread (since it's all in memory stuff). This may well be slower, but for a GUI this probably doesn't matter. For a game doing graphics with a high frame rate it probably does matter. A renderer created directly by a window doesn't offer the same assurance (it might and probably does call thread-unsafe hardware accelerated operations eg via OpenGL). In any case I'm writing a tutorial/test suite so starting with the most basic operations and working up seems to make some sense. -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||||
|
![]() |
resize window SDL2 | ![]() |
Jonny D
![]() |
![]() |
Irrespective of what might be going on here, I would suggest staying away from SDL_GetWindowSurface(). It's almost always going to be emulated, and hence, a feature that is not very future-proof.
Working solely on surfaces is not a recommended modern practice and doesn't translate well to any other modern graphics API. Surfaces are great for operations that make sense in software, but just don't expect code (or tutorials) that use surfaces exclusively to be relevant for long. Jonny D On Mon, Feb 16, 2015 at 8:12 AM, john skaller wrote:
|
||||||||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
On 17/02/2015, at 3:38 AM, Jonathan Dearborn wrote:
I take you point, but I wonder how to work on a RAM only canvas. I suppose I can work on independent surfaces and just render them to the hardware window. Perhaps that would be better. -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
Hi, I could use some help on this.
Some background first: Java was developed by Sun to compete on the desktop with Windows. In particular Java was "write once run anywhere" and it came with a GUI. Neither Java nor the GUI were very good, but the appeal of a platform independent GUI was one of the main reasons for Java's success. I am developing a "write once run anywhere" system too, but it is uses a different technology. Instead of a VM and bytecode compiler, my system generates C++ source which is then compiled down to machine binary by your system C++ compiler. This should be faster, and in particular, better for game development :-) Because I generate C++, bindings to C libraries are particularly easy to create and usually involve mapping types but little or no executable code (unlike, say, bindings in Python). Here's an example of a library of bindings .. for SDL of course: http://felix-lang.org/$/usr/local/lib/felix/felix-latest/share/lib/sdl Before developing games I need some tools. In particular a GUI would be useful. And of course it has to be platform independent! So of course I'm trying to use SDL because the aim of SDL is to provide a platform independent game development environment. http://felix-lang.org/$/usr/local/lib/felix/felix-latest/share/lib/gui It's a bit crude at the moment, but the methodology seems to work up to a point: http://felix-lang.org/$/usr/local/lib/felix/felix-latest/share/src/web/tutopt//sdlgui But I have a problem. Tests gui_04_* on do event handling. When I resize a window the program seems to work, unless the bounding rect of the stuff I'm drawing is entirely outsize the window rect. That seems to crash my program every time. This is a clear indication of a bug in SDL because none of my code knows anything about clipping etc.. Indeed, examining some of the SDL code and docs, it is not specified clearly what happens intersecting non-intersecting rects and it is not clear that every drawing routine in the whole of SDL correctly handles negative width or height. SDL_IntersectRect DOES product a negative dimension if there is no intersection, and this is set to a SDL_Surface clipping rectangle. A lot of SDL code does not do any special checks for this. In some cases it works anyhow. It's hard to be sure. Unfortunately I cannot experiment because I'm running OSX 10.6.8 and can no longer compile SDL from source. However here's the quandary. I have tried to make a C program doing roughly the same drawing operations crash with a resize and it doesn't. Which is a strong indication SDL works and the bug is in my code :-) [And I'm sure I'm not the only SDL user about .. :] So now I have a contradiction. Using gdb I find more than one of my programs crashes with exactly the same single symbol on the stack, unfortunately it has nothing to do with the problem. It's not even a function. The indication is corruption. Using Valgrind, my program crashes, Valgrind crashes, the iTerm terminal emulator crashes and my computer hangs. A hardware reset is required. The crash also occurs on Linux, so it isn't OSX specific. [Not surprising, since I'm using software rendering]. I am not asking for debugging help but thinking help! Logically, the bug is in SDL because my code is entirely independent of the context creating the crash, and the SDL C handling this is very suspicious, but my C code designed to demonstrate this fails to crash, indicating a corruption caused by my system or my test code, bindings, or compiler. But the bindings and test code work without resizing and the compiler compiles a lot of other code and is fairly robust after 20+ years of development. -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
On 22/02/2015, at 11:16 AM, john skaller wrote:
Ok, I have managed to get Valgrind to tell where the error is: ==1417== Invalid write of size 8 ==1417== at 0x1002A11E0: ??? (in /Library/Frameworks/SDL2.framework/Versions/A/SDL2) ==1417== by 0x100009FB0: flxusr::gui_04_wm_01::_init_::resume() (gui_04_wm_01.cpp:602) ==1417== by 0x100067243: flx::rtl::fthread_t::run() (flx_rtl.cpp:80) ==1417== by 0x1000694E8: flx::run::sync_sched::frun() (flx_sync.cpp:209) ==1417== by 0x1000640D7: flx::run::async_sched::prun(flx::run::async_sched::block_flag_t) (flx_async_world.cpp:132) ==1417== by 0x10006AE09: flx::run::flx_world::run_until_complete() (flx_world.cpp:327) ==1417== by 0x10000384A: felix_run(int, char**) (flx_run.include:330) ==1417== by 0x100003961: main (flx_run_main.cxx:4) ==1417== Address 0x11b2a90f0 is not stack'd, malloc'd or (recently) free'd ==1417== ==1417== ==1417== Process terminating with default action of signal 11 (SIGSEGV) ==1417== Access not within mapped region at address 0x11B2A90F8 surf = (SDL_Surface*) ::SDL_GetWindowSurface(w_uncurry); //assign simple pixelformat = (*surf).format; //assign simple bgpixels = (_a5115t_55683) ::SDL_MapRGB(pixelformat, c.r, c.g, c.b); //assign simple _genout_urv55214 = (int) ::SDL_SetClipRect(surf, (SDL_Rect*)&r); //init _genout_urv55144 = _genout_urv55214; //init _genout_urv55131 = _genout_urv55144; //init (void)_genout_urv55131; _genout_urv55215 = SDL_FillRect (surf, NULL, bgpixels); //init The last line there is line 602. now, the docs say "NULL to fill the entire surface". Certainly this is wrong. The actual code in the Mercurial repository says: /* If 'rect' == NULL, then fill the whole surface */ if (rect) { /* Perform clipping */ if (!SDL_IntersectRect(rect, &dst->clip_rect, &clipped)) { return 0; } rect = &clipped; } else { rect = &dst->clip_rect; } which clearly fills the clip rect of the surface NOT the whole surface. The cliprect is set by SDL_SetClipRect, which sets the actual cliprect to the intersection of the supplied cliprect and the window bounding rect, which has non-positive width or height. Then there's a bit of a nasty selection of an optimal fill routine. Includes special code for SSE etc .. I would not be surprised if these routine cannot handle negative width/height values, they're pretty complicated. So it is again looking like a bug in SDL. The reason my C code doesn't crash is it calls SDL_FillRect with an actual rectangle. The generated C++ sets the clip rect then calls SDL_FillRect with NULL. -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
On 23/02/2015, at 12:43 AM, john skaller wrote:
Ok, it is DEFINITELY a bug in SDL. Perhaps someone can try this test code. It crashes immediately on OSX 10.6.8, Macbook Pro. #include "SDL2/SDL.h" void draw(SDL_Window *w, int x, int y) { SDL_Rect r; r.x=x; r.y=y; r.w=100; r.h=100; SDL_Surface *s = SDL_GetWindowSurface (w); SDL_SetClipRect (s,&r); SDL_PixelFormat *f = s->format; uint32_t bgpixels = SDL_MapRGB(f, 200,200,200); SDL_FillRect(s,NULL,bgpixels); SDL_UpdateWindowSurface (w); } void fred() { int x = 190; int y = 170; SDL_Init(SDL_INIT_VIDEO); SDL_Window *w = SDL_CreateWindow ("title",20,20,100,100,SDL_WINDOW_RESIZABLE); draw (w,x,y); SDL_PumpEvents(); SDL_Event e; SDL_WaitEvent(&e); while (e.type != SDL_QUIT) { fprintf (stderr, "Event ..\n"); draw(w, x,y); SDL_WaitEvent(&e); } } int main() { fred(); return 0; } -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
On 23/02/2015, at 1:09 AM, john skaller wrote:
Confirmed, crashes on Linux too: Program received signal SIGSEGV, Segmentation fault. SDL_FillRect4SSE (h=<optimized out>, w=<optimized out>, color=13158600, pitch=400, pixels=0x7ffff7fff148 <error: Cannot access memory at address 0x7ffff7fff148>) at /home/fletch/project/SDL2-2.0.3/src/video/SDL_fillrect.c:130 130 DEFINE_SSE_FILLRECT(4, Uint32) This has the added advantage of identifying the specific sub-routine SDL_FillRect4SSE(). The culprit is in fact line 261 of SDL_FillRect.c in Mercurial repository which clearly cannot handle negative height or width, The solution is quite simple. on Line 255 of SDL_FillRect.c in the repo, add this code: if (SDL_NullRect (rect)) return SDL_True; now go off an check that other routines make this test too, eg Blit, line drawing, etc. I think these work however.
-- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
On 23/02/2015, at 9:22 AM, john skaller wrote:
Correction: SDL_RectEmpty. I'm having a friend check this fixes the problem right now. [I cannot compile SDL any more on OSX 10.6.8]. -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||||
|
![]() |
Re: resize window SDL2 | ![]() |
DLudwig
![]() |
![]() |
This looks like a bug to me. I'm able to reproduce it on Win32, and on iOS with a few minor modifications. I've added some comments to the Bugzilla entry on it (at https://bugzilla.libsdl.org/show_bug.cgi?id=2868 ), along with a simplified test program, and John's fix encoded as a patch (tested + working).
Sam, Ryan -- if you like, and the patch looks good to you, I'd be happy to push the fix to hg.libsdl.org. Cheers! -- David L. |
||||||||||
|
![]() |
resize window SDL2 | ![]() |
john skaller
Guest
![]() |
![]() |
On 23/02/2015, at 12:42 PM, DLudwig wrote:
Quick question: why return SDL_FALSE? Wouldn't one return that if the operation failed? Writing completely off the surface (by doing nothing) is no more a failure than a partial (i.e. clipped) write. Is there a consistent convention here? BTW: thanks for your work David!! -- john skaller http://felix-lang.org _______________________________________________ SDL mailing list http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org |
||||||||||||
|
![]() |
Re: resize window SDL2 | ![]() |