Porting an SDL1.2 game with lazy redrawing

Hello list,

I know this question was probably asked before, and is also probably
addressed in the migration guide, but I just wanted to ask your opinion:

If I’m porting an SDL1.2 game, which had a lot of "Dirty Rectangle"
updating (and not redrawing everything each frame), would it be
acceptable to continue having a “screen” surface, and only
transfering it to a largish screen-wide texture as needed (probably at
the end of every frame)?

For this project, rewriting the game logic to render everything each
frame is not an option. I realize I’ll miss all the goodness of SDL
Renderer API, but I’m fine with it, only worried about performance.

Would it be fine? Are there better approaches?–
driedfruit

And that con to you, Rainer Deyke, is also a pro to me. Simple and
manageable, and not huge and unknowledgable. Of course, i think you talk 3D
while i talk 2D, then there is like no choice, unless someone abstracts
Quake 1/2 software renderer (3D on SVGAlib was a blast) ;-).

As for mail topic, SDL2 porting point might be a reason to get rid of that
dirty drawing and improve code quality and experience. Else i don’t see a
huge win on porting, nor seems that the project motivates you.

On the carry approach, couldn’t you draw to a texture (call it Screen) and
add a frame flip (RenderCopy) with it?

Cheers, would like to know the project when possible, :-).

Just use SDL_GetWindowSurface and SDL_UpdateWindowSurface.

I’m a bit scaried of them, because they sound like SDL1.3 COMPATIBILITY
layer too much. I’ll look into it. Thank you.

(And really, the SDL rendering API sucks. Both direct OpenGL and
SDL_GetWindowSurface/SDL_UpdateWindowSurface are in their own ways
much more powerful and flexible than the SDL rendering API.)

As for mail topic, SDL2 porting point might be a reason to get rid of
that dirty drawing and improve code quality and experience. Else i
don’t see a huge win on porting, nor seems that the project motivates
you.

I’ve already made several SDL1.2-to-SDL2 ports, replacing BlitSurface
with RenderCopy, I do know the pros and cons of the Renderer API, and I
pretty much enjoy it. Altho extremely limited, compared to OpenGL, it
made porting a blast.

However, this particular project is not best suited for it. It’s a
bug-for-bug reimplementation, and it tries to retain all rendering
artifacts of the original, that come from the dirty rectangle
approach. Emulating such artifacts would be a pain in the ass.

On the carry approach, couldn’t you draw to a texture (call it
Screen) and add a frame flip (RenderCopy) with it?

Hmm, right, that would make sense. Thank you.

Cheers, would like to know the project when possible, :-).

Will probbaly drop a small announcement on this list.On Fri, 01 Aug 2014 08:49:46 +0200 Rainer Deyke wrote:
On Mon, 4 Aug 2014 13:14:54 +0200 Juan Manuel Borges Ca?o wrote:


driedfruit

Just use SDL_GetWindowSurface and SDL_UpdateWindowSurface.

Don’t use these. Use the Render API with your 2D game.
UpdateWindowSurface will work for software surfaces, but it’ll bypass
the GPU on many platforms. Don’t use these. Ever.

We should really note that in the documentation.

–ryan.

(Resending, since the mailing list seems to have eaten this before.)

–ryan.-------- Forwarded Message --------
Subject: Re: [SDL] Porting an SDL1.2 game with lazy redrawing
Date: Fri, 01 Aug 2014 16:36:49 -0400
From: @icculus (icculus)
Organization: icculus.org
To: SDL Development List

If I’m porting an SDL1.2 game, which had a lot of "Dirty Rectangle"
updating (and not redrawing everything each frame), would it be
acceptable to continue having a “screen” surface, and only
transfering it to a largish screen-wide texture as needed (probably at
the end of every frame)?

Yep.

If you can refactor to move everything into SDL_Textures, and render the
entire frame with nothing but textures every frame, your CPU usage will
probably drop to just about 0%…

https://wiki.libsdl.org/MigrationGuide#If_your_game_wants_to_blit_surfaces_to_the_screen

…but continuing to render into an SDL_Surface, and then using
SDL_UpdateTexture() to get it onto the GPU before blasting it to the
screen is also a valid solution.

https://wiki.libsdl.org/MigrationGuide#If_your_game_just_wants_to_get_fully-rendered_frames_to_the_screen

You’ll probably get about the same performance as before, but also can
make use of SDL_RenderSetLogicalSize() to get basically free scaling of
the final rendering, and you’ll get Steam Overlay support, etc.

I’ve used both methods and both are more than up to the task.

You can experiment, but for Postal 1 (which renders to a software
Surface that moves to a GPU Texture at the end of the frame), I turned
off the dirty rectangle code and just fed the whole surface to
SDL_UpdateTexture() every frame. I had plenty of performance to spare
and it wasn’t worth the added complexity to write it (and part of me
suspects it’d be slower to update multiple pieces of the texture instead
of just doing the whole thing at once anyhow).

–ryan.

2014-08-07 10:09 GMT-03:00, Ryan C. Gordon :

You can experiment, but for Postal 1 (which renders to a software
Surface that moves to a GPU Texture at the end of the frame), I turned
off the dirty rectangle code and just fed the whole surface to
SDL_UpdateTexture() every frame. I had plenty of performance to spare
and it wasn’t worth the added complexity to write it (and part of me
suspects it’d be slower to update multiple pieces of the texture instead
of just doing the whole thing at once anyhow).

Yeah, it would. If you update the whole texture, the driver will know
that the previous data isn’t important and don’t even bother trying to
handle it (it’ll just overwrite the texture). Otherwise it needs to
take care of preserving the previous data and that’s slow.

Which is exactly what you want if you’re writing a software renderer, or
porting a game that already uses software rendering.

SDL_GetWindowSurface gives you the flexibility to create countless
special effects, e.g. 2D pixel lighting, through straightforward CPU
code. You can usually create the same special effects through OpenGL
fragment shaders, but then you have to write and debug and test shader
code which is a huge pain in the ass, and your code will be less
portable as a result. The 2D render API isn’t even close to powerful
enough to be an option.On 07.08.2014 15:08, Ryan C. Gordon wrote:

Just use SDL_GetWindowSurface and SDL_UpdateWindowSurface.

Don’t use these. Use the Render API with your 2D game.
UpdateWindowSurface will work for software surfaces, but it’ll bypass
the GPU on many platforms.


Rainer Deyke (rainerd at eldwood.com)

2014-08-07 12:21 GMT-03:00, Rainer Deyke :> On 07.08.2014 15:08, Ryan C. Gordon wrote:

Just use SDL_GetWindowSurface and SDL_UpdateWindowSurface.

Don’t use these. Use the Render API with your 2D game.
UpdateWindowSurface will work for software surfaces, but it’ll bypass
the GPU on many platforms.

Which is exactly what you want if you’re writing a software renderer, or
porting a game that already uses software rendering.

SDL_GetWindowSurface gives you the flexibility to create countless
special effects, e.g. 2D pixel lighting, through straightforward CPU
code. You can usually create the same special effects through OpenGL
fragment shaders, but then you have to write and debug and test shader
code which is a huge pain in the ass, and your code will be less
portable as a result. The 2D render API isn’t even close to powerful
enough to be an option.

He still mentioned the possibility of rendering to a surface then
copying it to a texture to blit it. The problem is touching the screen
itself in the first place, not so much the rendering.

Don’t use these. Use the Render API with your 2D game.
UpdateWindowSurface will work for software surfaces, but it’ll bypass
the GPU on many platforms.

Which is exactly what you want if you’re writing a software renderer, or
porting a game that already uses software rendering.

Nope.

You want to do your software renderer on the CPU, manipulating pixels in
a block of memory (perhaps even in SDL_Surface::pixels) and then you
want to move the final product to the GPU, so you can scale it to any
resolution and control vsync (and on Steam, get the Steam Overlay
working, if that’s important to you).

In many cases, doing all your rendering in software and then moving it
to an SDL_Texture will get it to the screen faster than using
SDL_UpdateWindowSurface(), too.

Not to mention that on some platforms, like the Mac,
UpdateWindowSurface() just feeds your data to OpenGL anyhow, as there is
no “software” interface provided.

SDL_UpdateWindowSurface() exists specifically because the Render API
uses it for its software renderer, but it’s not the path to the video
card you should ever prefer, given the option.

–ryan.

In many cases, doing all your rendering in software and then moving it
to an SDL_Texture will get it to the screen faster than using
SDL_UpdateWindowSurface(), too.

Then why doesn’t SDL_UpdateWindowSurface() do that internally? Oh,
wait, it does.

SDL_UpdateWindowSurface() exists specifically because the Render API
uses it for its software renderer, but it’s not the path to the video
card you should ever prefer, given the option.

Which implies that going through SDL_Texture will result in extra copy
operations.

Through SDL_UpdateWindowSurface():
Render to surface.
SDL_UpdateWindowSurface() copies surface to screen: ideally one copy
operation.

With SDL_Texture:
Render to surface.
Convert surface to texture: one copy operation.
Render texture to “screen”, which is really another software surface:
another copy operation.
SDL_RenderPresent calls SDL_UpdateWindowSurface(): at least as many
copy operations as when using SDL_UpdateWindowSurface() directly.

The first copy operation can theoretically be elided by using
SDL_LockTexture, but you lose read access (necessary for blending
operations), you lose the ability to only change a few individual pixels
(Bug 1586), and there’s no guarantee that SDL_UnlockTexture won’t
perform a copy anyway.On 08.08.2014 05:26, Ryan C. Gordon wrote:


Rainer Deyke (rainerd at eldwood.com)

2014-08-08 3:36 GMT-03:00, Rainer Deyke :

Which implies that going through SDL_Texture will result in extra copy
operations.

Through SDL_UpdateWindowSurface():
Render to surface.
SDL_UpdateWindowSurface() copies surface to screen: ideally one copy
operation.

With SDL_Texture:
Render to surface.
Convert surface to texture: one copy operation.
Render texture to “screen”, which is really another software surface:
another copy operation.
SDL_RenderPresent calls SDL_UpdateWindowSurface(): at least as many
copy operations as when using SDL_UpdateWindowSurface() directly.

This is true only with the software renderer, which is not the
default on any of the platforms (unless your configuration is screwed
up). Otherwise it’s convert suface to texture then tell GPU to render
that texture directly.

In many cases, doing all your rendering in software and then moving it
to an SDL_Texture will get it to the screen faster than using
SDL_UpdateWindowSurface(), too.

Then why doesn’t SDL_UpdateWindowSurface() do that internally? Oh,
wait, it does.

Did I make you angry? I’m trying to understand the aggression in your reply.

SDL_UpdateWindowSurface() calls the target’s UpdateWindowFramebuffer
implementation eventually…

https://hg.libsdl.org/SDL/file/c3ec7c3e6c24/src/video/SDL_video.c#l1972

…which, on X11, calls either XPutImage (slow!) or XShmPutImage (less
slow).

…on Windows, this calls BitBlt (a function from Win16!)

https://hg.libsdl.org/SDL/file/c3ec7c3e6c24/src/video/windows/SDL_windowsframebuffer.c#l95

On the other hand, if you use an SDL_Texture, you’re likely using either
OpenGL or Direct3D to get it to texture memory for the draw, and these
tend to be faster paths on modern systems.

On systems that only offer OpenGL(ES), UpdateWindowSurface() will put it
in a GL texture for you, as I said before, but you still can’t get
scaling or vsync out of the system unless you go through the Render API
or OpenGL/Direct3D directly.

SDL_RenderPresent calls SDL_UpdateWindowSurface(): at least as many

copy operations as when using SDL_UpdateWindowSurface() directly.

You’re describing SDL’s software renderer, which is not what you would
get in almost any circumstance. If you use an SDL_Texture to push an
otherwise software-rendered game to the screen, it’ll use Direct3D or
OpenGL to get the final rendering to the screen by default.

(What you’re describing is true for this case, though: if you get the
software renderer, you might have been better off just using
SDL_UpdateWindowSurface() to avoid a blit, but you’ve probably got
deeper problems if you ended up here, and it’ll still work without any
changes to your program.)

The first copy operation can theoretically be elided by using
SDL_LockTexture, but you lose read access (necessary for blending
operations), you lose the ability to only change a few individual pixels
(Bug 1586), and there’s no guarantee that SDL_UnlockTexture won’t
perform a copy anyway.

Treat textures as unidirectional: always be pushing data to them, don’t
read data back. Render to a buffer of memory (or an SDL_Surface, if you
prefer) and push that to the texture once a frame. If you need to read
back from what you’ve rendered, you read from your memory buffer.

–ryan.

In many cases, doing all your rendering in software and then moving it
to an SDL_Texture will get it to the screen faster than using
SDL_UpdateWindowSurface(), too.

Then why doesn’t SDL_UpdateWindowSurface() do that internally? Oh,
wait, it does.

Did I make you angry? I’m trying to understand the aggression in your
reply.

No, just frustrated.

SDL_UpdateWindowSurface() calls the target’s UpdateWindowFramebuffer
implementation eventually…

https://hg.libsdl.org/SDL/file/c3ec7c3e6c24/src/video/SDL_video.c#l1972

…which, on X11, calls either XPutImage (slow!) or XShmPutImage (less
slow).

That’s kind of true, but not really. Check out line 516-520 of
SDL_video.c. SDL overrides the driver’s UpdateWindowFramebuffer
implementation with a texture-based approach if its internal heuristic
thinks this is the better approach.

…on Windows, this calls BitBlt (a function from Win16!)

This, on the other hand, is true, but only because SDL’s internal
heuristic says that BitBlt is faster than Direct3D texture streaming.
That’s not really surprising - there’s no reason why Microsoft
couldn’t/shouldn’t/wouldn’t use the full capabilities of the hardware in
their BitBlt implementation.

On systems that only offer OpenGL(ES), UpdateWindowSurface() will put it
in a GL texture for you, as I said before, but you still can’t get
scaling or vsync out of the system unless you go through the Render API
or OpenGL/Direct3D directly.

I don’t want scaling. I do want vsync, but there is no API-level reason
why the renderer API should offer vsync and SDL_UpdateWindowSurface().

SDL_RenderPresent calls SDL_UpdateWindowSurface(): at least as many

copy operations as when using SDL_UpdateWindowSurface() directly.

You’re describing SDL’s software renderer, which is not what you would
get in almost any circumstance.

Actually I get fairly often when I’m running on a virtual machine.

The first copy operation can theoretically be elided by using
SDL_LockTexture, but you lose read access (necessary for blending
operations), you lose the ability to only change a few individual pixels
(Bug 1586), and there’s no guarantee that SDL_UnlockTexture won’t
perform a copy anyway.

Treat textures as unidirectional: always be pushing data to them, don’t
read data back. Render to a buffer of memory (or an SDL_Surface, if you
prefer) and push that to the texture once a frame. If you need to read
back from what you’ve rendered, you read from your memory buffer.

So, two extra copies.

To summarize my argument:

  • SDL_UpdateWindowSurface() is the superior API. Independent of its
    actual implementation, it allows better theoretical performance and is
    cleaner than the render API when used for software rendering.
  • The actual implementation of SDL_UpdateWindowSurface() is also
    superior to rendering to a texture through the render API, with
    performance characteristics that are never worse and sometimes
    significantly better.
  • SDL_UpdateWindowSurface() is for me /the/ killer SDL feature. So
    I’m understandably a little frustrated when a major SDL developer tries
    to deprecate it.On 08.08.2014 19:29, Ryan C. Gordon wrote:


Rainer Deyke (rainerd at eldwood.com)

2014-08-08 16:44 GMT-03:00, Rainer Deyke :

This, on the other hand, is true, but only because SDL’s internal
heuristic says that BitBlt is faster than Direct3D texture streaming.
That’s not really surprising - there’s no reason why Microsoft
couldn’t/shouldn’t/wouldn’t use the full capabilities of the hardware in
their BitBlt implementation.

On Windows XP, BitBlt is horribly slow (it writes directly to video
memory!). On Vista onwards, it’s rendered to a bitmap internally then
copied to a Direct3D texture which is then rendered with the GPU (so
you get the data converted twice, once by SDL and once by BitBlt).

In other words, no, BitBlt is never better than having SDL itself use a texture.

I don’t want scaling. I do want vsync, but there is no API-level reason
why the renderer API should offer vsync and SDL_UpdateWindowSurface().

Actually, there is: the underlying system API may be the one
responsible for providing the vsync. This is true for pretty much
everything except the software renderer. This is why the vsync setting
is tied to the renderer.

Actually I get fairly often when I’m running on a virtual machine.

Which is not the real thing, and virtual machines are not designed for
video performance either. Unless you’re making software meant to be
run on a VM, don’t trust one to measure performance, and a game
definitely is not something you’d normally play through a VM (I
imagine you’re doing it for testing only?).

Test on real hardware and see what happens.

  • SDL_UpdateWindowSurface() is the superior API. Independent of its
    actual implementation, it allows better theoretical performance and is
    cleaner than the render API when used for software rendering.
  • The actual implementation of SDL_UpdateWindowSurface() is also
    superior to rendering to a texture through the render API, with
    performance characteristics that are never worse and sometimes
    significantly better.
  • SDL_UpdateWindowSurface() is for me /the/ killer SDL feature. So
    I’m understandably a little frustrated when a major SDL developer tries
    to deprecate it.

How about doing a benchmark using SDL_UpdateWindowSurface vs using a
texture? On real hardware, not on a virtual machine. The texture
approach is guaranteed to not be slower (and most likely faster)
unless you really botch it when converting the surface to a texture.

Oh, and btw, my game uses software rendering to its own framebuffer, I
know perfectly well what’s the deal with this. At low resolutions you
can sustain 60Hz just fine with either method, but at high resolutions
you will feel the bandwidth usage.

2014-08-08 16:44 GMT-03:00, Rainer Deyke :

This, on the other hand, is true, but only because SDL’s internal
heuristic says that BitBlt is faster than Direct3D texture streaming.
That’s not really surprising - there’s no reason why Microsoft
couldn’t/shouldn’t/wouldn’t use the full capabilities of the hardware in
their BitBlt implementation.

On Windows XP, BitBlt is horribly slow (it writes directly to video
memory!). On Vista onwards, it’s rendered to a bitmap internally then
copied to a Direct3D texture which is then rendered with the GPU (so
you get the data converted twice, once by SDL and once by BitBlt).

Changing SDL to use Direct3D texture streaming is as easy as removing(!)
lines 178 through 182 in SDL_video.c and replacing the #elif on line 183
with and #if. You can also control this explicitly through
SDL_HINT_FRAMEBUFFER_ACCELERATION.

So even if you’re right about BitBlt being slow - and I would be very
careful about making such a generalization, which is affected by OS
versions, graphics drivers, and hardware - this is not an argument
against SDL_UpdateWindowSurface(), just an argument against the
particular heuristic that SDL_UpdateWindowSurface() uses to determine
which low-level function to call.

Actually, there is: the underlying system API may be the one
responsible for providing the vsync. This is true for pretty much
everything except the software renderer. This is why the vsync setting
is tied to the renderer.

Again, there is nothing at the SDL API level that forces SDL to use a
particular underlying system API.

Which is not the real thing, and virtual machines are not designed for
video performance either. Unless you’re making software meant to be
run on a VM, don’t trust one to measure performance, and a game
definitely is not something you’d normally play through a VM (I
imagine you’re doing it for testing only?).

I’m not using the virtual machines to run performance tests, I’m using
them to test SDL games on various platforms, which requires at least
adequate performance for the software rendering path. I also can and do
play actual games on virtual machines because I don’t feel like booting
my old and failing Windows computer every time I want to play a Windows
game that doesn’t work under Wine.On 08.08.2014 22:46, Sik the hedgehog wrote:


Rainer Deyke (rainerd at eldwood.com)

  • SDL_UpdateWindowSurface() is for me /the/ killer SDL feature. So I’m understandably a little frustrated when a major SDL developer tries to deprecate it.

Well, it’s not how I would recommend doing things, but feel free to keep using it if you’re happy with the results. The API isn’t going away in 2.0, of course.

–ryan.

2014-08-09 6:22 GMT-03:00, Rainer Deyke :

Again, there is nothing at the SDL API level that forces SDL to use a
particular underlying system API.

OK, so SDL should explicitly tell Direct3D and OpenGL to always
disable vsync no matter what? (because see, they are the ones that
handle vsync because it’s an inherent part of them) Also note that
trying to do vsync on your own (as you’re suggesting SDL to do) makes
you a lot more likely to miss the vsync (due to the way multitasking
systems work), assuming you can even do it.

Oh, and also: sometimes you can’t even prevent Direct3D or OpenGL from
doing vsync. The driver can override this, and there’s no way for you
to tell, so you’d end up with a double vsync (i.e. skipping every
other frame).

I’m not using the virtual machines to run performance tests, I’m using
them to test SDL games on various platforms, which requires at least
adequate performance for the software rendering path. I also can and do
play actual games on virtual machines because I don’t feel like booting
my old and failing Windows computer every time I want to play a Windows
game that doesn’t work under Wine.

And again you’re basically insisting that modifying the window
directly is better just because of how you use the programs even
though the absolute majority (over 99% for sure) of people will not be
doing that.

2014-08-09 6:22 GMT-03:00, Rainer Deyke :

Again, there is nothing at the SDL API level that forces SDL to use a
particular underlying system API.

OK, so SDL should explicitly tell Direct3D and OpenGL to always
disable vsync no matter what? (because see, they are the ones that
handle vsync because it’s an inherent part of them) Also note that
trying to do vsync on your own (as you’re suggesting SDL to do) makes
you a lot more likely to miss the vsync (due to the way multitasking
systems work), assuming you can even do it.

That’s the complete opposite of what I’m suggesting.

With texture framebuffer emulation turned on (which /should/ be the
default under most circumstances, for the vsync if for no other reason),
SDL_UpdateWindowSurface() calls SDL_RenderPresent(), so if the latter
gets vsync, the former also gets it. That’s the way it should be. When
texture framebuffer emulation turned off (e.g. when no hardware
acceleration is available), then SDL_RenderPresent() calls
SDL_UpdateWindowSurface(), so if the latter has no sync, neither does
the former. Again, that’s the way it should be. Use vsync when you
have it, don’t use it if it’s not available.

The generalization that SDL_RenderPresent() has vsync while
SDL_UpdateWindowSurface() does not is false. Neither guarantees vsync,
neither guarantees the absence vsync, and both should prefer vsync
whenever it is available.

And again you’re basically insisting that modifying the window
directly is better just because of how you use the programs even
though the absolute majority (over 99% for sure) of people will not be
doing that.

SDL_UpdateWindowSurface() is no more modifying the window directly than
SDL_RenderPresent().On 09.08.2014 15:48, Sik the hedgehog wrote:


Rainer Deyke (rainerd at eldwood.com)

This thread was about a developer. issue. The moment it turned about a
discussion about SDL Window Surface should have started another thread. For
order & references (archive); Would be nice to fix it on it, but… ;-D

So, just to be clear on this,

Would my initial plan of blitting everything with SDL_Surfaces, then
copying final surface to a texture, then rendering it (with
scale handled automatically) be OK?On Thu, 07 Aug 2014 09:08:05 -0400 “Ryan C. Gordon” wrote:

Just use SDL_GetWindowSurface and SDL_UpdateWindowSurface.

Don’t use these. Use the Render API with your 2D game.
UpdateWindowSurface will work for software surfaces, but it’ll bypass
the GPU on many platforms. Don’t use these. Ever.

We should really note that in the documentation.

–ryan.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


driedfruit