SDL2 and Software Rendering

For starters, SDL2 would need to be modified to dynamically load the unicode WinAPI functions (from unicows.dll if on Win9x, user32.dll otherwise), and the Direct3D renderer would need to be disabled (DirectX 8.0a is the last version supported by Windows 95, although I guess Windows 98 supports DirectX 9?). Also, any code using NT kernel or XP+ features like XInput, Raw Input, or IOCP would need to be altered to dynamically load the function calls (also define the structures in-code), which would bloat the source code, and either report an error or use some other method as a fallback (for example, a fallback for IOCP would be a restructured implementation using select() ).

This is just what I know from fixing my own code to run on Windows 98 (I’ve never actually developed for Windows 98 directly), so SDL will most likely need other modifications as well.------------------------
Nate Fries

I do like Ryan’s suggestion to do the palette conversion in OpenGL,

For those that are curious, here’s the code for that…

// call prepareOpenGLShaders() after setting up your GL context.

struct OpenGLVertexAttribs
{
GLfloat x;
GLfloat y;
GLfloat u;
GLfloat v;
};

static bool prepareOpenGLShaders(const int w, const int h)
{
static const char *glslVertexShader =
"#version 110\n"
“attribute vec2 pos;”
“attribute vec2 tex;”
“void main() {”
“gl_Position = vec4(pos.xy, 0.0, 1.0);”
“gl_TexCoord[0].xy = tex;”
"}"
;

 static const char *glslFragmentShader2D =
     "#version 110\n"
     "uniform sampler2D image;"
     "uniform sampler2D palette;"
     "void main() {"
         "gl_FragColor = texture2D(palette, vec2(texture2D(image, 

gl_TexCoord[0].xy).x, 0.0));"
"}"
;

 static const char *glslFragmentShaderRect =
     "#version 110\n"
     "#extension GL_ARB_texture_rectangle : enable\n"
     "uniform sampler2DRect image;"
     "uniform sampler2DRect palette;"
     "void main() {"
         "gl_FragColor = texture2DRect(palette, 

vec2(texture2DRect(image, gl_TexCoord[0].xy).x * 255.0, 0.0));"
"}"
;

 GLuint vertex = 0;
 GLuint fragment = 0;
 GLuint program = 0;
 GLint ok = 0;
 GLint shaderlen = 0;

 ok = 0;
 shaderlen = (GLint) strlen(glslVertexShader);
 vertex = pglCreateShader(GL_VERTEX_SHADER);
 pglShaderSource(vertex, 1, (const GLchar **) &glslVertexShader, 

&shaderlen);
pglCompileShader(vertex);
pglGetShaderiv(vertex, GL_COMPILE_STATUS, &ok);
if (!ok)
{
char errbuf[256];
GLsizei len = 0;
pglGetShaderInfoLog(vertex, sizeof (errbuf), &len, (GLchar *)
errbuf);
printf(“POSTAL1 vertex shader compile error:\n%s\n\n”, errbuf);
pglDeleteShader(vertex);
return false;
} // if

 const bool isTextureRect = (OpenGLTextureTarget == 

GL_TEXTURE_RECTANGLE_ARB);
const char *glslFragmentShader = isTextureRect ?
glslFragmentShaderRect : glslFragmentShader2D;

 ok = 0;
 shaderlen = (GLint) strlen(glslFragmentShader);
 fragment = pglCreateShader(GL_FRAGMENT_SHADER);
 pglShaderSource(fragment, 1, (const GLchar **) &glslFragmentShader, 

&shaderlen);
pglCompileShader(fragment);
pglGetShaderiv(fragment, GL_COMPILE_STATUS, &ok);
if (!ok)
{
char errbuf[256];
GLsizei len = 0;
pglGetShaderInfoLog(fragment, sizeof (errbuf), &len, (GLchar *)
errbuf);
printf(“POSTAL1 fragment shader compile error:\n%s\n\n”, errbuf);
pglDeleteShader(vertex);
pglDeleteShader(fragment);
return false;
} // if

 ok = 0;
 OpenGLProgram = pglCreateProgram();
 pglAttachShader(OpenGLProgram, vertex);
 pglAttachShader(OpenGLProgram, fragment);
 pglBindAttribLocation(OpenGLProgram, 0, "pos");
 pglBindAttribLocation(OpenGLProgram, 1, "tex");
 pglLinkProgram(OpenGLProgram);
 pglDeleteShader(vertex);
 pglDeleteShader(fragment);
 pglGetProgramiv(OpenGLProgram, GL_LINK_STATUS, &ok);
 if (!ok)
 {
     pglDeleteProgram(OpenGLProgram);
     OpenGLProgram = 0;
     return false;
 } // if

 pglUseProgram(OpenGLProgram);
 pglUniform1i(pglGetUniformLocation(OpenGLProgram, "image"), 0);
 pglUniform1i(pglGetUniformLocation(OpenGLProgram, "palette"), 1);

 const float left = -1.0f;
 const float right = 1.0f;
 const float top = 1.0f;
 const float bottom = -1.0f;

 OpenGLVertexAttribs verts[4] = {
     { left, top, 0.0f, 0.0f },
     { right, top, 1.0f, 0.0f },
     { left, bottom, 0.0f, 1.0f },
     { right, bottom, 1.0f, 1.0f }
 };

 if (isTextureRect)
 {
     for (int i = 0; i < (sizeof (verts) / sizeof (verts[0])); i++)
     {
         verts[i].u *= (GLfloat) w;
         verts[i].v *= (GLfloat) h;
     }
 }

 pglGenBuffers(1, &OpenGLVBO);
 pglBindBuffer(GL_ARRAY_BUFFER, OpenGLVBO);
 pglBufferData(GL_ARRAY_BUFFER, sizeof (verts), verts, GL_STATIC_DRAW);

 const OpenGLVertexAttribs *ptr = NULL;  // it's a bound VBO.

 pglVertexAttribPointer(0, 2, GL_FLOAT, 0, sizeof (verts[0]), &ptr->x);
 pglEnableVertexAttribArray(0);

 pglVertexAttribPointer(1, 2, GL_FLOAT, 0, sizeof (verts[0]), &ptr->u);
 pglEnableVertexAttribArray(1);

 return true;

}

// Make sure your image texture and palette texture are on texunits 0
// and 1. Everything else is all set up in here, so other than making
// sure the textures are set up, here’s how you get it to the screen:

 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 SDL_GL_SwapBuffers();  // this is SDL_GL_SwapWindow() in SDL2.

The FBOs to stretch-blit add another dozen or two lines of code, but
that’s the gist otherwise.

however, I will still need the pure software fallback where OpenGL is
not available. When working on a game like Wolfenstein 3D, you’re kind
of expected to support old hardware.

Id Software’s original build is still available for people still running
DOS. :stuck_out_tongue:

–ryan.

2013/4/9, Ryan C. Gordon :

however, I will still need the pure software fallback where OpenGL is
not available. When working on a game like Wolfenstein 3D, you’re kind
of expected to support old hardware.

Id Software’s original build is still available for people still running
DOS. :stuck_out_tongue:

He’s working on a modified version of the engine with things like
textured floors and such, so that was probably a poor excuse :stuck_out_tongue:

“That said, for Windows 98 there are hacks that backport some of the XP
new features that allow XP programs to run on it”

I believe at the moment this is practically the only way to even get a
fully updated Windows 98 system since Windows update infinite refresh
loops last I tried. I installed the “unofficial Windows 98 service
pack” on mine (since it claims to include all the official updates) and,
according to dependency walker, it looks like only a handful of
functions are missing from the OS.

KERNEL32.DLL

  • GetFileSizeEx
  • SetFilePointerEx
    OLE32.DLL (This one might not even be relevant since it’s under SHLWAPI.DLL)
  • CoWaitForMultipleHandles
    SHELL32.DLL
  • SHBindToParent
    USER32.DLL
  • GetRawInputData
  • RegisterRawInputDevices

I’ll have to play with this more when I’m not quite as busy, but from
the surface it looks like it’s feasible to get SDL2 running acceptably
on patched Windows 98 systems. All of the unicode functions seem
resolve at least.

  • Blzut3

Hey Ryan, this is a great trick! :)On Sun, Apr 7, 2013 at 5:26 PM, Ryan C. Gordon wrote:

But yeah, you’ll need to convert from 8-bit to 32-bit on the fly.

I was in the same boat with Postal 1; it always writes to a 640x480,
8-bit, paletted surface.

I had moved it from SDL 1.2 to SDL 2.0, using an 8-bit shadow surface,
which I would blit to the window surface. Postal uses dirty rects and such,
so it would do this reasonably efficiently, at least on the application
side of things.

This worked, but it used a lot of CPU on the Mac I tried it on. I hadn’t
debugged it or even sanity-checked the code to make sure I wasn’t doing
something stupid.

I decided to move it to OpenGL directly:

  • We keep a 640x480 memory buffer. The game renders into this, 8 bits per
    pixel, thinking it’s the usual SDL 1.2 video surface.
  • We also have a 640x480 GL_R8 texture, which we update once a frame with
    glTexSubImage2D() from that memory buffer. We don’t try to do dirty
    rectangles. At the end of the frame, the whole texture is updated,
    unconditionally, because it was easier this way.
  • We have an 256x1 GL_RGBA texture. It’s the palette.
  • There’s a VBO that has the vertex array we use to draw a single quad
    with this texture.
  • There’s GLSL to make it take the 8-bit texture and the palette texture,
    and render the proper colors to the framebuffer.
  • For extra credit, we have an FBO that we can then do a GL_LINEAR
    stretch-blit to the real window system framebuffer, so the game doesn’t
    have to be 640x480 anymore. Since the 8-bit values have to line up exactly
    for the palette to work, that has to render at 640x480 to get the right
    colors, but then you can do a nice linear filter when going to the screen.
    Now it looks like it would if you dropped your monitor’s resolution to
    640x480 to fill the whole display, but the Steam Overlay still renders at
    full resolution.

This took a few hours to implement, got some cool benefits, and this Mac
uses about 4% of the CPU to play the game.

–ryan.

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