DDRKirby(ISQ) had problems with the HLSL code for "RGBtoHSV" in my original posting on this subject. I've had a chance to look at this in more detail and believe I know what the problem is/was. Some versions of the HLSL compiler have difficulty generating correct code for component swizzling under very specific circumstances. A minor refactoring seems to solve the problem:
float3 Hue(float H)
{
float R = abs(H * 6 - 3) - 1;
float G = 2 - abs(H * 6 - 2);
float B = 2 - abs(H * 6 - 4);
return saturate(float3(R,G,B));
}
float3 HSVtoRGB(in float3 HSV)
{
return ((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z;
}
float3 RGBtoHSV(in float3 RGB)
{
float3 HSV = 0;
#if NO_ASM
HSV.z = max(RGB.r, max(RGB.g, RGB.b));
float M = min(RGB.r, min(RGB.g, RGB.b));
float C = HSV.z - M;
#else
float4 RGB4 = RGB.rgbr;
asm { max4 HSV.z, RGB4 };
asm { max4 RGB4.w, -RGB4 };
float C = HSV.z + RGB4.w;
#endif
if (C != 0)
{
float4 RGB0 = float4(RGB, 0);
float4 Delta = (HSV.z - RGB0) / C;
Delta.rgb -= Delta.brg;
Delta.rgb += float3(2,4,6);
Delta.brg = step(HSV.z, RGB) * Delta.brg;
#if NO_ASM
HSV.x = max(Delta.r, max(Delta.g, Delta.b));
#else
float4 Delta4 = Delta.rgbr;
asm { max4 HSV.x, Delta4 };
#endif
HSV.x = frac(HSV.x / 6);
HSV.y = 1 / Delta.w;
}
return HSV;
}
The major change (which produces the same optimized code as I expected from the original version) is highlighted. I'm not saying this will fix everyone's woes, but it certainly corrects the problem with the one situation I came across.
Thursday, 24 March 2011
Saturday, 19 March 2011
Slacker By Nature
Having a blogger motto of "Software engineer by trade; slacker by nature" may not seem like a good advert for myself, but I believe I'm incredibly effective. That's subtly difference from being efficient, and vastly different from being a fast coder; I probably wouldn't fair well in demoscene 48-hour coding challenges.
But being a slacker by nature means I'm more likely to look for a simple, path-of-least-resistance solution, which, in many software engineering domains is the "right" solution. Don't reinvent the wheel ... unless you're looking for a competitive advantage by engineering a racing wheel that is 1% more efficient for 100% extra work. In which case, reimplement your string class and XML parser while you're at it!
But being a slacker by nature means I'm more likely to look for a simple, path-of-least-resistance solution, which, in many software engineering domains is the "right" solution. Don't reinvent the wheel ... unless you're looking for a competitive advantage by engineering a racing wheel that is 1% more efficient for 100% extra work. In which case, reimplement your string class and XML parser while you're at it!
Saturday, 19 February 2011
Sunday, 6 February 2011
Monday, 31 January 2011
RGB/HSV in HLSL 2
Petr Kobalíček has spurred me on to look more closely at the the HLSL conversion between RGB and HSV colour spaces. With a bit of refactoring, I've managed to shave a few more GPU cycles off:
float3 RGBtoHSV(in float3 RGB)
{
float3 HSV = 0;
#if NO_ASM
HSV.z = max(RGB.r, max(RGB.g, RGB.b));
float M = min(RGB.r, min(RGB.g, RGB.b));
float C = HSV.z - M;
#else
float4 RGB4 = RGB.rgbr;
asm { max4 HSV.z, RGB4 };
asm { max4 RGB4.w, -RGB4 };
float C = HSV.z + RGB4.w;
#endif
if (C != 0)
{
float RGB0 = float4(RGB, 0);
float4 Delta = (HSV.z - RGB0) / C;
Delta.rgb -= Delta.brg;
Delta.rgb += float3(2,4,6);
Delta.rgb *= step(HSV.z, RGB.gbr);
#if NO_ASM
HSV.x = max(Delta.r, max(Delta.g, Delta.b));
#else
float4 Delta4 = Delta.rgbr;
asm { max4 HSV.x, Delta4 };
#endif
HSV.x = frac(HSV.x / 6);
HSV.y = 1 / Delta.w;
}
return HSV;
}
float3 RGBtoHSV(in float3 RGB)
{
float3 HSV = 0;
#if NO_ASM
HSV.z = max(RGB.r, max(RGB.g, RGB.b));
float M = min(RGB.r, min(RGB.g, RGB.b));
float C = HSV.z - M;
#else
float4 RGB4 = RGB.rgbr;
asm { max4 HSV.z, RGB4 };
asm { max4 RGB4.w, -RGB4 };
float C = HSV.z + RGB4.w;
#endif
if (C != 0)
{
float RGB0 = float4(RGB, 0);
float4 Delta = (HSV.z - RGB0) / C;
Delta.rgb -= Delta.brg;
Delta.rgb += float3(2,4,6);
Delta.rgb *= step(HSV.z, RGB.gbr);
#if NO_ASM
HSV.x = max(Delta.r, max(Delta.g, Delta.b));
#else
float4 Delta4 = Delta.rgbr;
asm { max4 HSV.x, Delta4 };
#endif
HSV.x = frac(HSV.x / 6);
HSV.y = 1 / Delta.w;
}
return HSV;
}
Sunday, 30 January 2011
16-bit Shifts on Z80
One would think that 16-bit, unsigned, binary shifts on a Z80 microprocessor would be as trivial as it comes. But I've only recently realised just how little has been actually written down about optimising Z80 code for even these simplest of cases. So here's a quick guide; alas, probably thirty years too late!
The reason these shifts aren't obvious is because of the inherent asymmetry in the Z80 processor. Although it has a fairly orthogonal 8-bit ALU, its 16-bit (address) pipeline only has a simple adder. Multiplying HL by two (a left shift) is trivial: ADD HL, HL. Shifting right is a whole can of worms; to shift a 16-bit quantity to the right, it is sometimes quicker (and/or shorter) to shift/rotate left and adjust.
I've written a library of routines to perform the shifts, including some that use self-modifying code to eke out a few more T-cycles. I believe they're optimal. Anyone out there know different?
The reason these shifts aren't obvious is because of the inherent asymmetry in the Z80 processor. Although it has a fairly orthogonal 8-bit ALU, its 16-bit (address) pipeline only has a simple adder. Multiplying HL by two (a left shift) is trivial: ADD HL, HL. Shifting right is a whole can of worms; to shift a 16-bit quantity to the right, it is sometimes quicker (and/or shorter) to shift/rotate left and adjust.
I've written a library of routines to perform the shifts, including some that use self-modifying code to eke out a few more T-cycles. I believe they're optimal. Anyone out there know different?
Friday, 31 December 2010
Computus 3
After reminiscing with Dr J R Stockton about old computers, it got to converting my 16-bit algorithm for Easter to 8-bit Z80 assembly language. I spent quite some time hand-optimising it (which is an enjoyable past-time in itself, if there's no deadline involved!) and posted the code with a short commentary.
I had thought about writing a bespoke super-optimiser to try to find the "ultimate" Z80 code, but with just under 500 bytes of machine code, that's over 101000 permutations. I think I might have to divide and conquer!
I had thought about writing a bespoke super-optimiser to try to find the "ultimate" Z80 code, but with just under 500 bytes of machine code, that's over 101000 permutations. I think I might have to divide and conquer!
Subscribe to:
Posts (Atom)
