Commit 3aece9f7 authored by Emmanuel Gil Peyrot's avatar Emmanuel Gil Peyrot Committed by Emil Velikov

shaders: Add Dolphin’s übershaders.

These shaders have been generated by Dolphin 9649494f67 on Mesa
8c26b52349 for an HD4000 GPU.

They include a lot of uniform branches, mostly on integers, as well as
switch statements branching on small and bounded integers.
Signed-off-by: default avatarEmmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parent 4262876d
[require]
GLSL >= 4.00
[vertex shader]
#version 400
#define FORCE_EARLY_Z layout(early_fragment_tests) in
#extension GL_ARB_shading_language_420pack : enable
#define ATTRIBUTE_LOCATION(x)
#define FRAGMENT_OUTPUT_LOCATION(x)
#define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y)
#define UBO_BINDING(packing, x) layout(packing, binding = x)
#define SAMPLER_BINDING(x) layout(binding = x)
#define SSBO_BINDING(x) layout(binding = x)
#define VARYING_LOCATION(x)
#extension GL_ARB_shader_storage_buffer_object : enable
#extension GL_ARB_shader_image_load_store : enable
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define frac fract
#define lerp mix
// Vertex UberShader
struct Light {
int4 color;
float4 cosatt;
float4 distatt;
float4 pos;
float4 dir;
};
UBO_BINDING(std140, 2) uniform VSBlock {
uint components;
uint xfmem_dualTexInfo;
uint xfmem_numColorChans;
float4 cpnmtx[6];
float4 cproj[4];
int4 cmtrl[4];
Light clights[8];
float4 ctexmtx[24];
float4 ctrmtx[64];
float4 cnmtx[32];
float4 cpostmtx[64];
float4 cpixelcenter;
float2 cviewport;
uint4 xfmem_pack1[8];
#define xfmem_texMtxInfo(i) (xfmem_pack1[(i)].x)
#define xfmem_postMtxInfo(i) (xfmem_pack1[(i)].y)
#define xfmem_color(i) (xfmem_pack1[(i)].z)
#define xfmem_alpha(i) (xfmem_pack1[(i)].w)
};
struct VS_OUTPUT {
float4 pos;
float4 colors_0;
float4 colors_1;
float3 tex0;
float3 tex1;
float3 tex2;
float4 clipPos;
float clipDist0;
float clipDist1;
};
int4 CalculateLighting(uint index, uint attnfunc, uint diffusefunc, float3 pos, float3 normal) {
float3 ldir, h, cosAttn, distAttn;
float dist, dist2, attn;
switch (attnfunc) {
case 0u: // LIGNTATTN_NONE
case 2u: // LIGHTATTN_DIR
ldir = normalize(clights[index].pos.xyz - pos.xyz);
attn = 1.0;
if (length(ldir) == 0.0)
ldir = normal;
break;
case 1u: // LIGHTATTN_SPEC
ldir = normalize(clights[index].pos.xyz - pos.xyz);
attn = (dot(normal, ldir) >= 0.0) ? max(0.0, dot(normal, clights[index].dir.xyz)) : 0.0;
cosAttn = clights[index].cosatt.xyz;
if (diffusefunc == 0u) // LIGHTDIF_NONE
distAttn = clights[index].distatt.xyz;
else
distAttn = normalize(clights[index].distatt.xyz);
attn = max(0.0, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, float3(1.0, attn, attn*attn));
break;
case 3u: // LIGHTATTN_SPOT
ldir = clights[index].pos.xyz - pos.xyz;
dist2 = dot(ldir, ldir);
dist = sqrt(dist2);
ldir = ldir / dist;
attn = max(0.0, dot(ldir, clights[index].dir.xyz));
attn = max(0.0, clights[index].cosatt.x + clights[index].cosatt.y * attn + clights[index].cosatt.z * attn * attn) / dot(clights[index].distatt.xyz, float3(1.0, dist, dist2));
break;
default:
attn = 1.0;
ldir = normal;
break;
}
switch (diffusefunc) {
case 0u: // LIGHTDIF_NONE
return int4(round(attn * float4(clights[index].color)));
case 1u: // LIGHTDIF_SIGN
return int4(round(attn * dot(ldir, normal) * float4(clights[index].color)));
case 2u: // LIGHTDIF_CLAMP
return int4(round(attn * max(0.0, dot(ldir, normal)) * float4(clights[index].color)));
default:
return int4(0, 0, 0, 0);
}
}
ATTRIBUTE_LOCATION(0) in float4 rawpos;
ATTRIBUTE_LOCATION(1) in uint4 posmtx;
ATTRIBUTE_LOCATION(2) in float3 rawnorm0;
ATTRIBUTE_LOCATION(3) in float3 rawnorm1;
ATTRIBUTE_LOCATION(4) in float3 rawnorm2;
ATTRIBUTE_LOCATION(5) in float4 rawcolor0;
ATTRIBUTE_LOCATION(6) in float4 rawcolor1;
ATTRIBUTE_LOCATION(8) in float3 rawtex0;
ATTRIBUTE_LOCATION(9) in float3 rawtex1;
ATTRIBUTE_LOCATION(10) in float3 rawtex2;
ATTRIBUTE_LOCATION(11) in float3 rawtex3;
ATTRIBUTE_LOCATION(12) in float3 rawtex4;
ATTRIBUTE_LOCATION(13) in float3 rawtex5;
ATTRIBUTE_LOCATION(14) in float3 rawtex6;
ATTRIBUTE_LOCATION(15) in float3 rawtex7;
VARYING_LOCATION(0) out VertexData {
float4 pos;
float4 colors_0;
float4 colors_1;
float3 tex0;
float3 tex1;
float3 tex2;
float4 clipPos;
float clipDist0;
float clipDist1;
} vs;
void main()
{
VS_OUTPUT o;
// Position matrix
float4 P0;
float4 P1;
float4 P2;
// Normal matrix
float3 N0;
float3 N1;
float3 N2;
if ((components & 2u) != 0u) {// VB_HAS_POSMTXIDX
// Vertex format has a per-vertex matrix
int posidx = int(posmtx.r);
P0 = ctrmtx[posidx];
P1 = ctrmtx[posidx+1];
P2 = ctrmtx[posidx+2];
int normidx = posidx >= 32 ? (posidx - 32) : posidx;
N0 = cnmtx[normidx].xyz;
N1 = cnmtx[normidx+1].xyz;
N2 = cnmtx[normidx+2].xyz;
} else {
// One shared matrix
P0 = cpnmtx[0];
P1 = cpnmtx[1];
P2 = cpnmtx[2];
N0 = cpnmtx[3].xyz;
N1 = cpnmtx[4].xyz;
N2 = cpnmtx[5].xyz;
}
float4 pos = float4(dot(P0, rawpos), dot(P1, rawpos), dot(P2, rawpos), 1.0);
o.pos = float4(dot(cproj[0], pos), dot(cproj[1], pos), dot(cproj[2], pos), dot(cproj[3], pos));
// Only the first normal gets normalized (TODO: why?)
float3 _norm0 = float3(0.0, 0.0, 0.0);
if ((components & 1024u) != 0u) // VB_HAS_NRM0
_norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0)));
float3 _norm1 = float3(0.0, 0.0, 0.0);
if ((components & 2048u) != 0u) // VB_HAS_NRM1
_norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));
float3 _norm2 = float3(0.0, 0.0, 0.0);
if ((components & 4096u) != 0u) // VB_HAS_NRM2
_norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));
// Lighting
for (uint chan = 0u; chan < xfmem_numColorChans; chan++) {
uint colorreg = xfmem_color(chan);
uint alphareg = xfmem_alpha(chan);
int4 mat = cmtrl[chan + 2u];
int4 lacc = int4(255, 255, 255, 255);
if (bitfieldExtract(colorreg, 0, 1) != 0u) {
if ((components & (8192u << chan)) != 0u) // VB_HAS_COL0
mat.xyz = int3(round(((chan == 0u) ? rawcolor0.xyz : rawcolor1.xyz) * 255.0));
else if ((components & 8192u) != 0u) // VB_HAS_COLO0
mat.xyz = int3(round(rawcolor0.xyz * 255.0));
else
mat.xyz = int3(255, 255, 255);
}
if (bitfieldExtract(alphareg, 0, 1) != 0u) {
if ((components & (8192u << chan)) != 0u) // VB_HAS_COL0
mat.w = int(round(((chan == 0u) ? rawcolor0.w : rawcolor1.w) * 255.0));
else if ((components & 8192u) != 0u) // VB_HAS_COLO0
mat.w = int(round(rawcolor0.w * 255.0));
else
mat.w = 255;
} else {
mat.w = cmtrl [chan + 2u].w;
}
if (bitfieldExtract(colorreg, 1, 1) != 0u) {
if (bitfieldExtract(colorreg, 6, 1) != 0u) {
if ((components & (8192u << chan)) != 0u) // VB_HAS_COL0
lacc.xyz = int3(round(((chan == 0u) ? rawcolor0.xyz : rawcolor1.xyz) * 255.0));
else if ((components & 8192u) != 0u) // VB_HAS_COLO0
lacc.xyz = int3(round(rawcolor0.xyz * 255.0));
else
lacc.xyz = int3(255, 255, 255);
} else {
lacc.xyz = cmtrl [chan].xyz;
}
uint light_mask = bitfieldExtract(colorreg, 2, 4) | (bitfieldExtract(colorreg, 11, 4) << 4u);
uint attnfunc = bitfieldExtract(colorreg, 9, 2);
uint diffusefunc = bitfieldExtract(colorreg, 7, 2);
for (uint light_index = 0u; light_index < 8u; light_index++) {
if ((light_mask & (1u << light_index)) != 0u)
lacc.xyz += CalculateLighting(light_index, attnfunc, diffusefunc, pos.xyz, _norm0).xyz;
}
}
if (bitfieldExtract(alphareg, 1, 1) != 0u) {
if (bitfieldExtract(alphareg, 6, 1) != 0u) {
if ((components & (8192u << chan)) != 0u) // VB_HAS_COL0
lacc.w = int(round(((chan == 0u) ? rawcolor0.w : rawcolor1.w) * 255.0));
else if ((components & 8192u) != 0u) // VB_HAS_COLO0
lacc.w = int(round(rawcolor0.w * 255.0));
else
lacc.w = 255;
} else {
lacc.w = cmtrl [chan].w;
}
uint light_mask = bitfieldExtract(alphareg, 2, 4) | (bitfieldExtract(alphareg, 11, 4) << 4u);
uint attnfunc = bitfieldExtract(alphareg, 9, 2);
uint diffusefunc = bitfieldExtract(alphareg, 7, 2);
for (uint light_index = 0u; light_index < 8u; light_index++) {
if ((light_mask & (1u << light_index)) != 0u)
lacc.w += CalculateLighting(light_index, attnfunc, diffusefunc, pos.xyz, _norm0).w;
}
}
lacc = clamp(lacc, 0, 255);
// Hopefully GPUs that can support dynamic indexing will optimize this.
float4 lit_color = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;
switch (chan) {
case 0u: o.colors_0 = lit_color; break;
case 1u: o.colors_1 = lit_color; break;
}
}
if (xfmem_numColorChans < 2u && (components & 16384u) == 0u)
o.colors_1 = o.colors_0;
o.tex0 = float3(0.0, 0.0, 0.0);
o.tex1 = float3(0.0, 0.0, 0.0);
o.tex2 = float3(0.0, 0.0, 0.0);
// Texture coordinate generation
for (uint texgen = 0u; texgen < 3u; texgen++) {
// Texcoord transforms
float4 coord = float4(0.0, 0.0, 1.0, 1.0);
uint texMtxInfo = xfmem_texMtxInfo(texgen);
switch (bitfieldExtract(texMtxInfo, 7, 5)) {
case 0u: // XF_SRCGEOM_INROW
coord.xyz = rawpos.xyz;
break;
case 1u: // XF_SRCNORMAL_INROW
coord.xyz = ((components & 1024u /* VB_HAS_NRM0 */) != 0u) ? rawnorm0.xyz : coord.xyz; break;
case 3u: // XF_SRCBINORMAL_T_INROW
coord.xyz = ((components & 2048u /* VB_HAS_NRM1 */) != 0u) ? rawnorm1.xyz : coord.xyz; break;
case 4u: // XF_SRCBINORMAL_B_INROW
coord.xyz = ((components & 4096u /* VB_HAS_NRM2 */) != 0u) ? rawnorm2.xyz : coord.xyz; break;
case 5u: // XF_SRCTEX0_INROW
coord = ((components & 32768u /* VB_HAS_UV0 */) != 0u) ? float4(rawtex0.x, rawtex0.y, 1.0, 1.0) : coord;
break;
case 6u: // XF_SRCTEX1_INROW
coord = ((components & 65536u /* VB_HAS_UV1 */) != 0u) ? float4(rawtex1.x, rawtex1.y, 1.0, 1.0) : coord;
break;
case 7u: // XF_SRCTEX2_INROW
coord = ((components & 131072u /* VB_HAS_UV2 */) != 0u) ? float4(rawtex2.x, rawtex2.y, 1.0, 1.0) : coord;
break;
case 8u: // XF_SRCTEX3_INROW
coord = ((components & 262144u /* VB_HAS_UV3 */) != 0u) ? float4(rawtex3.x, rawtex3.y, 1.0, 1.0) : coord;
break;
case 9u: // XF_SRCTEX4_INROW
coord = ((components & 524288u /* VB_HAS_UV4 */) != 0u) ? float4(rawtex4.x, rawtex4.y, 1.0, 1.0) : coord;
break;
case 10u: // XF_SRCTEX5_INROW
coord = ((components & 1048576u /* VB_HAS_UV5 */) != 0u) ? float4(rawtex5.x, rawtex5.y, 1.0, 1.0) : coord;
break;
case 11u: // XF_SRCTEX6_INROW
coord = ((components & 2097152u /* VB_HAS_UV6 */) != 0u) ? float4(rawtex6.x, rawtex6.y, 1.0, 1.0) : coord;
break;
case 12u: // XF_SRCTEX7_INROW
coord = ((components & 4194304u /* VB_HAS_UV7 */) != 0u) ? float4(rawtex7.x, rawtex7.y, 1.0, 1.0) : coord;
break;
}
// Input form of AB11 sets z element to 1.0
if (bitfieldExtract(texMtxInfo, 2, 1) == 0u) // inputform == XF_TEXINPUT_AB11
coord.z = 1.0f;
// first transformation
uint texgentype = bitfieldExtract(texMtxInfo, 4, 3);
float3 output_tex;
switch (texgentype)
{
case 1u: // XF_TEXGEN_EMBOSS_MAP
{
uint light = bitfieldExtract(texMtxInfo, 15, 3);
uint source = bitfieldExtract(texMtxInfo, 12, 3);
switch (source) {
case 0u: output_tex.xyz = o.tex0; break;
case 1u: output_tex.xyz = o.tex1; break;
case 2u: output_tex.xyz = o.tex2; break;
default: output_tex.xyz = float3(0.0, 0.0, 0.0); break;
}
if ((components & 6144u) != 0u) { // VB_HAS_NRM1 | VB_HAS_NRM2
float3 ldir = normalize(clights[light].pos.xyz - pos.xyz);
output_tex.xyz += float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0);
}
}
break;
case 2u: // XF_TEXGEN_COLOR_STRGBC0
output_tex.xyz = float3(o.colors_0.x, o.colors_0.y, 1.0);
break;
case 3u: // XF_TEXGEN_COLOR_STRGBC1
output_tex.xyz = float3(o.colors_1.x, o.colors_1.y, 1.0);
break;
default: // Also XF_TEXGEN_REGULAR
{
if ((components & (4u /* VB_HAS_TEXMTXIDX0 */ << texgen)) != 0u) {
// This is messy, due to dynamic indexing of the input texture coordinates.
// Hopefully the compiler will unroll this whole loop anyway and the switch.
int tmp = 0;
switch (texgen) {
case 0u: tmp = int(rawtex0.z); break;
case 1u: tmp = int(rawtex1.z); break;
case 2u: tmp = int(rawtex2.z); break;
}
if (bitfieldExtract(texMtxInfo, 1, 1) == 1u) {
output_tex.xyz = float3(dot(coord, ctrmtx[tmp]),
dot(coord, ctrmtx[tmp + 1]),
dot(coord, ctrmtx[tmp + 2]));
} else {
output_tex.xyz = float3(dot(coord, ctrmtx[tmp]),
dot(coord, ctrmtx[tmp + 1]),
1.0);
}
} else {
if (bitfieldExtract(texMtxInfo, 1, 1) == 1u) {
output_tex.xyz = float3(dot(coord, ctexmtx[3u * texgen]),
dot(coord, ctexmtx[3u * texgen + 1u]),
dot(coord, ctexmtx[3u * texgen + 2u]));
} else {
output_tex.xyz = float3(dot(coord, ctexmtx[3u * texgen]),
dot(coord, ctexmtx[3u * texgen + 1u]),
1.0);
}
}
}
break;
}
if (xfmem_dualTexInfo != 0u) {
uint postMtxInfo = xfmem_postMtxInfo(texgen); uint base_index = bitfieldExtract(postMtxInfo, 0, 6);
float4 P0 = cpostmtx[base_index & 0x3fu];
float4 P1 = cpostmtx[(base_index + 1u) & 0x3fu];
float4 P2 = cpostmtx[(base_index + 2u) & 0x3fu];
if (bitfieldExtract(postMtxInfo, 8, 1) != 0u)
output_tex.xyz = normalize(output_tex.xyz);
// multiply by postmatrix
output_tex.xyz = float3(dot(P0.xyz, output_tex.xyz) + P0.w,
dot(P1.xyz, output_tex.xyz) + P1.w,
dot(P2.xyz, output_tex.xyz) + P2.w);
}
if (texgentype == 0u && output_tex.z == 0.0) // XF_TEXGEN_REGULAR
output_tex.xy = clamp(output_tex.xy / 2.0f, float2(-1.0f,-1.0f), float2(1.0f,1.0f));
// Hopefully GPUs that can support dynamic indexing will optimize this.
switch (texgen) {
case 0u: o.tex0 = output_tex; break;
case 1u: o.tex1 = output_tex; break;
case 2u: o.tex2 = output_tex; break;
}
}
o.clipPos = o.pos;
float clipDepth = o.pos.z * (1.0 - 1e-7);
o.clipDist0 = clipDepth + o.pos.w;
o.clipDist1 = -clipDepth;
o.pos.z = o.pos.w * cpixelcenter.w - o.pos.z * cpixelcenter.z;
o.pos.xy *= sign(cpixelcenter.xy * float2(1.0, -1.0));
o.pos.xy = o.pos.xy - o.pos.w * cpixelcenter.xy;
vs.pos = o.pos;
vs.colors_0 = o.colors_0;
vs.colors_1 = o.colors_1;
vs.tex0 = o.tex0;
vs.tex1 = o.tex1;
vs.tex2 = o.tex2;
vs.clipPos = o.clipPos;
vs.clipDist0 = o.clipDist0;
vs.clipDist1 = o.clipDist1;
gl_ClipDistance[0] = o.clipDist0;
gl_ClipDistance[1] = o.clipDist1;
gl_Position = o.pos;
}
[fragment shader]
#version 400
#define FORCE_EARLY_Z layout(early_fragment_tests) in
#extension GL_ARB_shading_language_420pack : enable
#define ATTRIBUTE_LOCATION(x)
#define FRAGMENT_OUTPUT_LOCATION(x)
#define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y)
#define UBO_BINDING(packing, x) layout(packing, binding = x)
#define SAMPLER_BINDING(x) layout(binding = x)
#define SSBO_BINDING(x) layout(binding = x)
#define VARYING_LOCATION(x)
#extension GL_ARB_shader_storage_buffer_object : enable
#extension GL_ARB_shader_image_load_store : enable
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define frac fract
#define lerp mix
// Pixel UberShader for 3 texgens, early-depth
int idot(int3 x, int3 y)
{
int3 tmp = x * y;
return tmp.x + tmp.y + tmp.z;
}
int idot(int4 x, int4 y)
{
int4 tmp = x * y;
return tmp.x + tmp.y + tmp.z + tmp.w;
}
int iround(float x) { return int (round(x)); }
int2 iround(float2 x) { return int2(round(x)); }
int3 iround(float3 x) { return int3(round(x)); }
int4 iround(float4 x) { return int4(round(x)); }
SAMPLER_BINDING(0) uniform sampler2DArray samp[8];
UBO_BINDING(std140, 1) uniform PSBlock {
int4 color[4];
int4 k[4];
int4 alphaRef;
float4 texdim[8];
int4 czbias[2];
int4 cindscale[2];
int4 cindmtx[6];
int4 cfogcolor;
int4 cfogi;
float4 cfogf[2];
float4 czslope;
float2 cefbscale;
uint bpmem_genmode;
uint bpmem_alphaTest;
uint bpmem_fogParam3;
uint bpmem_fogRangeBase;
uint bpmem_dstalpha;
uint bpmem_ztex_op;
bool bpmem_late_ztest;
bool bpmem_rgba6_format;
bool bpmem_dither;
bool bpmem_bounding_box;
uint4 bpmem_pack1[16];
uint4 bpmem_pack2[8];
int4 konstLookup[32];
};
#define bpmem_combiners(i) (bpmem_pack1[(i)].xy)
#define bpmem_tevind(i) (bpmem_pack1[(i)].z)
#define bpmem_iref(i) (bpmem_pack1[(i)].w)
#define bpmem_tevorder(i) (bpmem_pack2[(i)].x)
#define bpmem_tevksel(i) (bpmem_pack2[(i)].y)
struct VS_OUTPUT {
float4 pos;
float4 colors_0;
float4 colors_1;
float3 tex0;
float3 tex1;
float3 tex2;
float4 clipPos;
float clipDist0;
float clipDist1;
};
FRAGMENT_OUTPUT_LOCATION_INDEXED(0, 0) out vec4 ocol0;
FRAGMENT_OUTPUT_LOCATION_INDEXED(0, 1) out vec4 ocol1;
VARYING_LOCATION(0) in VertexData {
float4 pos;
float4 colors_0;
float4 colors_1;
float3 tex0;
float3 tex1;
float3 tex2;
float4 clipPos;
float clipDist0;
float clipDist1;
};
float3 selectTexCoord(uint index) {
switch (index) {
case 0u:
return tex0;
case 1u:
return tex1;
case 2u:
return tex2;
default:
return float3(0.0, 0.0, 0.0);
}
}
int4 sampleTexture(uint sampler_num, float2 uv) {
return iround(texture(samp[sampler_num], float3(uv, 0.0)) * 255.0);
}
int4 Swizzle(uint s, int4 color) {
// AKA: Color Channel Swapping
int4 ret;
ret.r = color[bitfieldExtract(bpmem_tevksel(s * 2u), 0, 2)];
ret.g = color[bitfieldExtract(bpmem_tevksel(s * 2u), 2, 2)];
ret.b = color[bitfieldExtract(bpmem_tevksel(s * 2u + 1u), 0, 2)];
ret.a = color[bitfieldExtract(bpmem_tevksel(s * 2u + 1u), 2, 2)];
return ret;
}
int Wrap(int coord, uint mode) {
if (mode == 0u) // ITW_OFF
return coord;
else if (mode < 6u) // ITW_256 to ITW_16
return coord & (0xfffe >> mode);
else // ITW_0
return 0;
}
// TEV's Linear Interpolate, plus bias, add/subtract and scale
int tevLerp(int A, int B, int C, int D, uint bias, bool op, bool alpha, uint shift) {
// Scale C from 0..255 to 0..256
C += C >> 7;
// Add bias to D
if (bias == 1u) D += 128;
else if (bias == 2u) D -= 128;
int lerp = (A << 8) + (B - A)*C;
if (shift != 3u) {
lerp = lerp << shift;
D = D << shift;
}
if ((shift == 3u) == alpha)
lerp = lerp + (op ? 127 : 128);
int result = lerp >> 8;
// Add/Subtract D
if(op) // Subtract
result = D - result;
else // Add
result = D + result;