VOGONS


Reply 60 of 75, 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 75, 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 75, 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 75, 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 75, 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.

Reply 65 of 75, by RayeR

User metadata
Rank Oldbie
Rank
Oldbie

I tried under DOS now it can run also in v86 mode (jemm) on single core, as expected.

Gigabyte GA-P67-DS3-B3, Core i7-2600K @4,5GHz, 8GB DDR3, 128GB SSD, GTX970(GF7900GT), SB Audigy + YMF724F + DreamBlaster combo + LPC2ISA

Reply 66 of 75, by DoZator

User metadata
Rank Member
Rank
Member

Thank you for the new release. GLIDULATOR is getting better and better with each new version!

My brief report on version 0.9.3:

The statistics display is now completely fine. And the screenshot function works correctly.

Regarding multithreading... Using the "PYL Demo" example, in version 0.9.2, with "CPUID Limit" enabled (disabled in parentheses), in the BURNMEM+HIMEM+EMM386+HDPMI32I configuration, the result was:

"2 Core + HT = 4/4 Cores 29.6 FPS (1/2 14.7)"
"4 Core + HT = 5/8 Cores 29.9 FPS (4/4 45.3)"

in version 0.9.3, it became:

"2 Core + HT = 1/2 Cores 14.7 FPS (1/2 14.7)"
"4 Core + HT = 4/4 Cores 45.3 FPS (4/4 45.3)"

Is it possible to add a switch for this behavior here? Something like "GLIDULATOR_HT=0|1". By default, at least at the current stage, it will be "0" (which corresponds to the current behavior of version 0.9.3). In the "2 + HT" configuration, the value "1" will be useful. It will also be useful for other test purposes. This setting doesn’t even need to be displayed in GLIDULAT.CFG right now; it can be mentioned only in MANUAL.TXT until "HT" support is fully implemented.

As for HDPMI32I. Without it, the GLIDULATOR with this game still crashes in this configuration. With it, everything works well, as before (in multithreaded mode).

In Windows, only one core is still available (glide2x.ovl).

After exiting Windows in MS-DOS mode, only one core is still available.

In the "BURNMEM+HIMEM only" configuration, the following differences are observed:

After exiting Windows in MS-DOS mode, multithreading works. The game no longer crashes without "HDPMI32I". But if you still add "HDPMI32I" here, the game starts running only in singlethreaded mode (before launching Windows in multi-threaded mode). It turns out that Glidulator, after exiting Windows, cannot use multi-threading in "paging" mode.

Reply 67 of 75, by ElTentakel

User metadata
Rank Newbie
Rank
Newbie

Thank you for testing. Yes, the latest version supports GLIDULATOR_HT, and I hope it works the way you intended. It also fixes some of the issues you reported, though running EMM386 without HDPMI32I still seems impossible for now. Thanks to the new test machine and a remote testing setup, I was able to fix many other problems like the Half-Life crash, which turned out to be a race condition. Because of this, I haven’t had time to work on performance yet, but those optimizations may come in the next release

Reply 68 of 75, by myne

User metadata
Rank l33t
Rank
l33t

Any word on which are the hottest codepaths?

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 69 of 75, by DoZator

User metadata
Rank Member
Rank
Member
ElTentakel wrote on 2026-11-04, 17:15:

Thank you for testing. Yes, the latest version supports GLIDULATOR_HT, and I hope it works the way you intended. It also fixes some of the issues you reported, though running EMM386 without HDPMI32I still seems impossible for now. Thanks to the new test machine and a remote testing setup, I was able to fix many other problems like the Half-Life crash, which turned out to be a race condition. Because of this, I haven’t had time to work on performance yet, but those optimizations may come in the next release

Everything is fine; I don’t consider this to be a serious problem at all. "HDPMI32I" is a completely safe component that, when loaded into upper memory, doesn’t even interfere with Windows boot. Moreover, it can be unloaded at any time. So, if it’s required for operation in this mode, there’s nothing wrong with that. Otherwise, the behavior seems to match what’s stated in "CHANGES.TXT" and I haven’t noticed any deviations so far. In addition to the fact that "MANUAL.TXT" contains information that I cannot confirm, namely "...and in the DOS box of Windows, the OVL renders on one core with the C path;" but in my case, it’s "JIT" that works - at least, that’s what the statistics show. And this has been the case since the very first version. And yes, congratulations on moving to the new test platform. Thank you for the new release!

Reply 70 of 75, by myne

User metadata
Rank l33t
Rank
l33t

I have some random thoughts for you.

1) have a switch on load eg /cores 0111 or /cores 0011.
Use a binary mask because it's easier for people to understand. 0111 = cores 0 off (should always be off), cores 1,2,3 active.
/glcores 0111 = cores 0 off (should always be off), cores 1,2,3 active.
/glcores 0011 = cores 0,1 off (should always be off), cores 2,3 active.

I suggest this way, because I expect SOMEONE to copy this approach for sound. SBEMU/Picogus. If you can set the "standard" as it were, they are likely to copy.
eg /guscores 0100 would be compatible with glcores 0011.

Picogus is a fairly likely candidate since it has a great deal of functionality. cdroms, network etc.

2) It might be performance wise better to split the rendering a bit. Eg split the core workloads by function.
Example:
core1= render1 (sli? 1)
core2= render2 (sli? 2)
core3= scaling/antialiasing

I have no idea really, but I suspect antialiasing/scaling would be moderately high workload, and therefore run poorly on lower end machines.

3) I had a quick look at your recent changes in vid_voodoo_codegen_x86.h
It looks like you're targeting sse which is good.
It's worth noting that the first AMD64x2/core2 duo had SSE3. Anything before those is likely not performant enough to be worth targeting (Sorry BP6 bros).
In which case... it's worth considering making the worker payload entirely 64bit. I note you mention 16bit alignment in that file. I have no idea what the caveats may be here. I imagine there will be some hurdles, but I can't imagine they're insurmountable.
It may be worth having alternate paths that use AVX too.

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 71 of 75, by ElTentakel

User metadata
Rank Newbie
Rank
Newbie

The approach with the bitmask for cores is very good. I’ve experimented with 8 cores (including HT threads) by balancing the load based on estimated runtimes which improves performance a lot. I’ve also optimized the code and enabled the second TMU for Windows (sadly, DOS doesn’t have enough memory). Filters and FSAA have also been improved and are now distributed across all cores, so their impact is reduced. Overall, it may be possible to run this on a Core 2 Quad or even a very fast Core 2 Duo at the lowest settings, which would be great in my opinion. Since memory access seems to be the bottleneck, I will try to reduce it further. The next release will be dedicated to performance optimization. After that, I will try to test and improve compatibility.

Reply 72 of 75, by myne

User metadata
Rank l33t
Rank
l33t

What's the issue with memory?
Run us through how it allocates and interfaces back and forth
Thoughts on 64bit workers?

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 73 of 75, by ElTentakel

User metadata
Rank Newbie
Rank
Newbie

It’s about the memory that has to be visible to glide2x.ovl. It must be located in an area the overlay can access and that area is very limited. This was also the cause of the crash in Pyr, It simply ran out of memory because glidulator took all of it. There are some MBs I can save, but also two TMUs are not common for DOS games. Maybe I'll come back to that later.

Reply 74 of 75, by myne

User metadata
Rank l33t
Rank
l33t

How do the original cards do it?
Mimo? 12mb x2 in the pci space?
How big is a worker?

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 75 of 75, by myne

User metadata
Rank l33t
Rank
l33t

https://nand2mario.github.io/posts/2026/zsst-voodoo/

Have you read that?

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