VOGONS


Reply 60 of 64, by ElTentakel

User metadata
Rank Newbie
Rank
Newbie
myne wrote on 2026-10-22, 02:17:
I was randomly looking at the code, and with my limited understanding it looks like there's some optimisations to be found. […]
Show full quote

I was randomly looking at the code, and with my limited understanding it looks like there's some optimisations to be found.

This, at face value looks like an important function probably called a lot.
If it's not a hot path used thousands of times a second, it doesn't matter.
If it IS, then it looks like it should be broken into different functions and the correct one selected during init.

As an example, imagine I ask you to cut 5000 pieces of wood and you're an idiot that can only follow a flow chart.
Every time you are given a piece of wood, your first question is
"what is this?"
"Should I use scissors, a saw or a knife?"
Only after a bunch of decisions for EACH piece, do you start cutting.

IF this is a hot path, that's wildly inefficient.
You make the decisions first. Set up a cutting saw function, and just dump wood into the "in box".

Again, I could be very wrong. This could be the "upfront" decision tree and the actual drawing function is elsewhere, but it DOES SAY "Apply fog" which is what makes me think it is the thing that adds fog.

If I were to optimise it, I'd split it into "apply_no_fog", "apply_fixed_fog", "Apply_multi_fog" or something like that and then just set the correct one as your current "cutting saw" station and blindly feed it data.

Reusing code is often very useful. But there's a time and a place to get fancy with one flexible multitool, and a time to build 10 custom single purpose factories.

https://github.com/ElTentakel/Glidulator/blob … voodoo_render.h

#define APPLY_FOG(src_r, src_g, src_b, z, ia, w)  \
do { \
if (params->fogMode & FOG_CONSTANT) { \
src_r += params->fogColor.r; \
src_g += params->fogColor.g; \
src_b += params->fogColor.b; \
} else { \
int fog_r, fog_g, fog_b, fog_a = 0; \
int fog_idx; \
\
if (!(params->fogMode & FOG_ADD)) { \
fog_r = params->fogColor.r; \
fog_g = params->fogColor.g; \
fog_b = params->fogColor.b; \
} else \
fog_r = fog_g = fog_b = 0; \
\
if (!(params->fogMode & FOG_MULT)) { \
fog_r -= src_r; \
fog_g -= src_g; \
fog_b -= src_b; \
} \
\
switch (params->fogMode & (FOG_Z | FOG_ALPHA)) { \
case 0: \
fog_idx = (w_depth >> 10) & 0x3f; \
\
fog_a = params->fogTable[fog_idx].fog; \
fog_a += (params->fogTable[fog_idx].dfog * ((w_depth >> 2) & 0xff)) >> 10; \
break; \
case FOG_Z: \
fog_a = (z >> 20) & 0xff; \
break; \
case FOG_ALPHA: \
fog_a = CLAMP(ia >> 12); \
break; \
case FOG_W: \
fog_a = CLAMP((w >> 32) & 0xff); \
break; \
} \
fog_a++; \
\
fog_r = (fog_r * fog_a) >> 8; \
fog_g = (fog_g * fog_a) >> 8; \
fog_b = (fog_b * fog_a) >> 8; \
\
if (params->fogMode & FOG_MULT) { \
src_r = fog_r; \
src_g = fog_g; \
src_b = fog_b; \
} else { \
src_r += fog_r; \
src_g += fog_g; \
src_b += fog_b; \
} \
} \
\
src_r = CLAMP(src_r); \
src_g = CLAMP(src_g); \
src_b = CLAMP(src_b); \
…Show last 1 line
    } while (0)

I've tested it, but it seems that there is not much difference. It seems that the branch prediction is good enough, so there is no need to optimize the decision. And the JIT version doesn't even make those decisions, so there is no need to optimize it either. But thank you for sharing this idea 😀.

Reply 61 of 64, by ElTentakel

User metadata
Rank Newbie
Rank
Newbie
DoZator wrote on 2026-10-19, 10:17:
I tried the new version "0.9.2" on Windows 98, using "GLSMP.VxD" + "GLIDE2X.DLL" as an external library for DOSBOX-X, and it act […]
Show full quote

I tried the new version "0.9.2" on Windows 98, using "GLSMP.VxD" + "GLIDE2X.DLL" as an external library for DOSBOX-X, and it actually works in Multi Thread. Without any issues or glitches.

I also tested the same "PYL Demo" in the "BURNMEM + HIMEM + EMM386" configuration, together with HDPMI32I, since without it, GLIDULATOR still crashes in MS‑DOS mode when running this game, upon loading a save or the first level. The "CPUID Limit" is also enabled in the BIOS (disabled in parentheses). Results:

before launching Windows:

- 1 Core - HT = 1/1 Cores 14.7 FPS (1/1 14.7)
- 1 Core + HT = 1/2 Cores 14.7 FPS (1/1 14.7)
- 2 Core - HT = 1/2 Cores 14.7 FPS (1/2 15.1)
- 2 Core + HT = 4/4 Cores 29.6 FPS (1/2 14.7)
- 3 Core - HT = 3/3 Cores 29.5 FPS (3/3 30.3)
- 3 Core + HT = 5/6 Cores 30.3 FPS (3/3 29.5)
- 4 Core - HT = 4/4 Cores 45.3 FPS (4/4 45.2)
- 4 Core + HT = 5/8 Cores 29.9 FPS (4/4 45.3)

in Windows:

- only Single Thread (15.2 FPS);

after exiting Windows:

- without HDPMI32I and EMM386, Multi Thread works, but the game crashes when loading a level;
- works fine in both cases when combined with "HDPMI32I", but only in Single Thread mode (14.7 FPS).

The attachment PYL1.PNG is no longer available
The attachment PYL2.PNG is no longer available
The attachment PYL3.PNG is no longer available

Thank you for the screenshots and for adding a more visual indication of the flows. By the way, the statistics are not included in the screenshots, and it would be useful to have a switch "GLIDULATOR_SCREENSHOT=2" (Displaying statistics in screenshots). There is no explicit display of the current operating mode in a single letter - (R)ing0, (P)aging, (F)lat, (G)lsmp, or (O)ff.

It would also be good to add support for "GLSMP.VxD" at the same time in "GLIDE2X.OVL" for DOS games that work well on Windows. This feature would be very useful.

I've added your "GLIDULATOR_SCREENSHOT=2" recommendation for a next release.

Thanks you for testing. With CPUID Limit enabled Glidulator can't see which cores are HT and uses them as real cores. That is why 4 Core + HT is slower. Without the limit it works as intended. I will try to fix the detection and make tests with the PYL Demo.

Reply 62 of 64, by ElTentakel

User metadata
Rank Newbie
Rank
Newbie
DoZator wrote on 2026-10-19, 10:17:

I also tested the same "PYL Demo" in the "BURNMEM + HIMEM + EMM386" configuration, together with HDPMI32I, since without it, GLIDULATOR still crashes in MS‑DOS mode when running this game, upon loading a save or the first level.

I found out that too much memory was used by Glidulator, so the game ran out of RAM. I'm not sure if that fixes your issue completely, but we will see. The fix will be in the next release. I will post the next release here, then you can test it on your machine.

Reply 63 of 64, by myne

User metadata
Rank l33t
Rank
l33t
ElTentakel wrote on 2026-10-25, 16:22:
myne wrote on 2026-10-22, 02:17:
I was randomly looking at the code, and with my limited understanding it looks like there's some optimisations to be found. […]
Show full quote

I was randomly looking at the code, and with my limited understanding it looks like there's some optimisations to be found.

This, at face value looks like an important function probably called a lot.
If it's not a hot path used thousands of times a second, it doesn't matter.
If it IS, then it looks like it should be broken into different functions and the correct one selected during init.

As an example, imagine I ask you to cut 5000 pieces of wood and you're an idiot that can only follow a flow chart.
Every time you are given a piece of wood, your first question is
"what is this?"
"Should I use scissors, a saw or a knife?"
Only after a bunch of decisions for EACH piece, do you start cutting.

IF this is a hot path, that's wildly inefficient.
You make the decisions first. Set up a cutting saw function, and just dump wood into the "in box".

Again, I could be very wrong. This could be the "upfront" decision tree and the actual drawing function is elsewhere, but it DOES SAY "Apply fog" which is what makes me think it is the thing that adds fog.

If I were to optimise it, I'd split it into "apply_no_fog", "apply_fixed_fog", "Apply_multi_fog" or something like that and then just set the correct one as your current "cutting saw" station and blindly feed it data.

Reusing code is often very useful. But there's a time and a place to get fancy with one flexible multitool, and a time to build 10 custom single purpose factories.

https://github.com/ElTentakel/Glidulator/blob … voodoo_render.h

#define APPLY_FOG(src_r, src_g, src_b, z, ia, w)  \
do { \
if (params->fogMode & FOG_CONSTANT) { \
src_r += params->fogColor.r; \
src_g += params->fogColor.g; \
src_b += params->fogColor.b; \
} else { \
int fog_r, fog_g, fog_b, fog_a = 0; \
int fog_idx; \
\
if (!(params->fogMode & FOG_ADD)) { \
fog_r = params->fogColor.r; \
fog_g = params->fogColor.g; \
fog_b = params->fogColor.b; \
} else \
fog_r = fog_g = fog_b = 0; \
\
if (!(params->fogMode & FOG_MULT)) { \
fog_r -= src_r; \
fog_g -= src_g; \
fog_b -= src_b; \
} \
\
switch (params->fogMode & (FOG_Z | FOG_ALPHA)) { \
case 0: \
fog_idx = (w_depth >> 10) & 0x3f; \
\
fog_a = params->fogTable[fog_idx].fog; \
fog_a += (params->fogTable[fog_idx].dfog * ((w_depth >> 2) & 0xff)) >> 10; \
break; \
case FOG_Z: \
fog_a = (z >> 20) & 0xff; \
break; \
case FOG_ALPHA: \
fog_a = CLAMP(ia >> 12); \
break; \
case FOG_W: \
fog_a = CLAMP((w >> 32) & 0xff); \
break; \
} \
fog_a++; \
\
fog_r = (fog_r * fog_a) >> 8; \
fog_g = (fog_g * fog_a) >> 8; \
fog_b = (fog_b * fog_a) >> 8; \
\
if (params->fogMode & FOG_MULT) { \
src_r = fog_r; \
src_g = fog_g; \
src_b = fog_b; \
} else { \
src_r += fog_r; \
src_g += fog_g; \
src_b += fog_b; \
} \
} \
\
src_r = CLAMP(src_r); \
src_g = CLAMP(src_g); \
src_b = CLAMP(src_b); \
…Show last 1 line
    } while (0)

I've tested it, but it seems that there is not much difference. It seems that the branch prediction is good enough, so there is no need to optimize the decision. And the JIT version doesn't even make those decisions, so there is no need to optimize it either. But thank you for sharing this idea 😀.

Fair enough. Compilers can be crazy efficient.
Also, I could have been wrong as I said.
If it only runs once it doesn't matter much.

Are you able to note something like the "hottest" 10 loops/functions with some counters based on say 2 minutes of game footage?
That might help people know where to look.
Most likely it will follow a power law with the hottest one being streets ahead.

Personally, I have a suspicion that some handcrafted sse/avx applied to 2-3 functions will make a drastic improvement.
But which ones are important?

I built:
Convert old ASUS ASC boardviews to KICAD PCB!
Re: A comprehensive guide to install and play MechWarrior 2 on new versions on Windows.
Dos+Windows 3.11+tcp+vbe_svga auto-install iso template
Script to backup Win9x\ME drivers from a working install
Re: The thing no one asked for: KICAD 440bx reference schematic

Reply 64 of 64, by ElTentakel

User metadata
Rank Newbie
Rank
Newbie

Release v0.9.3 is out. It includes the fixes mentioned above and mipmap dithering. Unfortunately I have to change my test setup. I'm not able to test the HT detection with CPUID Limit and DOS after Windows 9x, since my little Shuttle PC is missing a PS/2 port. After switching, I'll return to that

myne wrote on 2026-10-26, 01:39:

Personally, I have a suspicion that some handcrafted sse/avx applied to 2-3 functions will make a drastic improvement.
But which ones are important?

I agree, I will have a look at it after getting another test system ready.