VideoCommon/PixelShaderGen: Make arrays immutable

Many of the arrays defined within this file weren't declared as
immutable, which can inhibit the strings being put into the read-only
segment. We can declare them constexpr to make them immutable.

While we're at it, we can use std::array, to allow bounds conditional
bounds checking with standard libraries. The declarations can also be
shortened in the future when all platform toolchain versions we use
support std::array deduction guides. Currently macOS and FreeBSD
builders fail on them.
This commit is contained in:
Lioncash 2019-05-29 20:50:17 -04:00
parent e28d08e973
commit 45934dd0c5
1 changed files with 137 additions and 88 deletions

View File

@ -41,7 +41,7 @@ enum : u32
C_PENVCONST_END = C_EFBSCALE + 1
};
static const char* tevKSelTableC[] = {
constexpr std::array<const char*, 32> tev_ksel_table_c{
"255,255,255", // 1 = 0x00
"223,223,223", // 7_8 = 0x01
"191,191,191", // 3_4 = 0x02
@ -76,7 +76,7 @@ static const char* tevKSelTableC[] = {
I_KCOLORS "[3].aaa", // K3_A = 0x1F
};
static const char* tevKSelTableA[] = {
constexpr std::array<const char*, 32> tev_ksel_table_a{
"255", // 1 = 0x00
"223", // 7_8 = 0x01
"191", // 3_4 = 0x02
@ -111,7 +111,7 @@ static const char* tevKSelTableA[] = {
I_KCOLORS "[3].a", // K3_A = 0x1F
};
static const char* tevCInputTable[] = {
constexpr std::array<const char*, 16> tev_c_input_table{
"prev.rgb", // CPREV,
"prev.aaa", // APREV,
"c0.rgb", // C0,
@ -130,7 +130,7 @@ static const char* tevCInputTable[] = {
"int3(0,0,0)", // ZERO
};
static const char* tevAInputTable[] = {
constexpr std::array<const char*, 8> tev_a_input_table{
"prev.a", // APREV,
"c0.a", // A0,
"c1.a", // A1,
@ -141,7 +141,7 @@ static const char* tevAInputTable[] = {
"0", // ZERO
};
static const char* tevRasTable[] = {
constexpr std::array<const char*, 8> tev_ras_table{
"iround(col0 * 255.0)",
"iround(col1 * 255.0)",
"ERROR13", // 2
@ -152,8 +152,19 @@ static const char* tevRasTable[] = {
"int4(0, 0, 0, 0)", // zero
};
static const char* tevCOutputTable[] = {"prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb"};
static const char* tevAOutputTable[] = {"prev.a", "c0.a", "c1.a", "c2.a"};
constexpr std::array<const char*, 4> tev_c_output_table{
"prev.rgb",
"c0.rgb",
"c1.rgb",
"c2.rgb",
};
constexpr std::array<const char*, 4> tev_a_output_table{
"prev.a",
"c0.a",
"c1.a",
"c2.a",
};
// FIXME: Some of the video card's capabilities (BBox support, EarlyZ support, dstAlpha support)
// leak
@ -817,11 +828,11 @@ ShaderCode GeneratePixelShaderCode(APIType ApiType, const ShaderHostConfig& host
last_ac.hex = uid_data->stagehash[uid_data->genMode_numtevstages].ac;
if (last_cc.dest != 0)
{
out.Write("\tprev.rgb = %s;\n", tevCOutputTable[last_cc.dest]);
out.Write("\tprev.rgb = %s;\n", tev_c_output_table[last_cc.dest]);
}
if (last_ac.dest != 0)
{
out.Write("\tprev.a = %s;\n", tevAOutputTable[last_ac.dest]);
out.Write("\tprev.a = %s;\n", tev_a_output_table[last_ac.dest]);
}
}
out.Write("\tprev = prev & 255;\n");
@ -953,11 +964,23 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
// coords
if (tevind.bs != ITBA_OFF)
{
const char* tevIndAlphaSel[] = {"", "x", "y", "z"};
const char* tevIndAlphaMask[] = {"248", "224", "240",
"248"}; // 0b11111000, 0b11100000, 0b11110000, 0b11111000
out.Write("alphabump = iindtex%d.%s & %s;\n", tevind.bt.Value(), tevIndAlphaSel[tevind.bs],
tevIndAlphaMask[tevind.fmt]);
constexpr std::array<const char*, 4> tev_ind_alpha_sel{
"",
"x",
"y",
"z",
};
// 0b11111000, 0b11100000, 0b11110000, 0b11111000
constexpr std::array<const char*, 4> tev_ind_alpha_mask{
"248",
"224",
"240",
"248",
};
out.Write("alphabump = iindtex%d.%s & %s;\n", tevind.bt.Value(), tev_ind_alpha_sel[tevind.bs],
tev_ind_alpha_mask[tevind.fmt]);
}
else
{
@ -967,31 +990,44 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
if (tevind.mid != 0)
{
// format
const char* tevIndFmtMask[] = {"255", "31", "15", "7"};
constexpr std::array<const char*, 4> tev_ind_fmt_mask{
"255",
"31",
"15",
"7",
};
out.Write("\tint3 iindtevcrd%d = iindtex%d & %s;\n", n, tevind.bt.Value(),
tevIndFmtMask[tevind.fmt]);
tev_ind_fmt_mask[tevind.fmt]);
// bias - TODO: Check if this needs to be this complicated...
// indexed by bias
const char* tevIndBiasField[] = {"", "x", "y", "xy",
"z", "xz", "yz", "xyz"};
constexpr std::array<const char*, 8> tev_ind_bias_field{
"", "x", "y", "xy", "z", "xz", "yz", "xyz",
};
// indexed by fmt
const char* tevIndBiasAdd[] = {"-128", "1", "1", "1"};
constexpr std::array<const char*, 4> tev_ind_bias_add{
"-128",
"1",
"1",
"1",
};
if (tevind.bias == ITB_S || tevind.bias == ITB_T || tevind.bias == ITB_U)
{
out.Write("\tiindtevcrd%d.%s += int(%s);\n", n, tevIndBiasField[tevind.bias],
tevIndBiasAdd[tevind.fmt]);
out.Write("\tiindtevcrd%d.%s += int(%s);\n", n, tev_ind_bias_field[tevind.bias],
tev_ind_bias_add[tevind.fmt]);
}
else if (tevind.bias == ITB_ST || tevind.bias == ITB_SU || tevind.bias == ITB_TU)
{
out.Write("\tiindtevcrd%d.%s += int2(%s, %s);\n", n, tevIndBiasField[tevind.bias],
tevIndBiasAdd[tevind.fmt], tevIndBiasAdd[tevind.fmt]);
out.Write("\tiindtevcrd%d.%s += int2(%s, %s);\n", n, tev_ind_bias_field[tevind.bias],
tev_ind_bias_add[tevind.fmt], tev_ind_bias_add[tevind.fmt]);
}
else if (tevind.bias == ITB_STU)
{
out.Write("\tiindtevcrd%d.%s += int3(%s, %s, %s);\n", n, tevIndBiasField[tevind.bias],
tevIndBiasAdd[tevind.fmt], tevIndBiasAdd[tevind.fmt], tevIndBiasAdd[tevind.fmt]);
out.Write("\tiindtevcrd%d.%s += int3(%s, %s, %s);\n", n, tev_ind_bias_field[tevind.bias],
tev_ind_bias_add[tevind.fmt], tev_ind_bias_add[tevind.fmt],
tev_ind_bias_add[tevind.fmt]);
}
// multiply by offset matrix and scale - calculations are likely to overflow badly,
@ -1078,9 +1114,11 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
// ---------
// Wrapping
// ---------
const char* tevIndWrapStart[] = {
"0", "(256<<7)", "(128<<7)", "(64<<7)",
"(32<<7)", "(16<<7)", "1"}; // TODO: Should the last one be 1 or (1<<7)?
// TODO: Should the last element be 1 or (1<<7)?
constexpr std::array<const char*, 7> tev_ind_wrap_start{
"0", "(256<<7)", "(128<<7)", "(64<<7)", "(32<<7)", "(16<<7)", "1",
};
// wrap S
if (tevind.sw == ITW_OFF)
@ -1094,7 +1132,7 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
else
{
out.Write("\twrappedcoord.x = fixpoint_uv%d.x & (%s - 1);\n", texcoord,
tevIndWrapStart[tevind.sw]);
tev_ind_wrap_start[tevind.sw]);
}
// wrap T
@ -1109,7 +1147,7 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
else
{
out.Write("\twrappedcoord.y = fixpoint_uv%d.y & (%s - 1);\n", texcoord,
tevIndWrapStart[tevind.tw]);
tev_ind_wrap_start[tevind.tw]);
}
if (tevind.fb_addprev) // add previous tevcoord
@ -1132,17 +1170,27 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
ac.b == TEVALPHAARG_RASA || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA)
{
// Generate swizzle string to represent the Ras color channel swapping
char rasswap[5] = {"rgba"[stage.tevksel_swap1a], "rgba"[stage.tevksel_swap2a],
"rgba"[stage.tevksel_swap1b], "rgba"[stage.tevksel_swap2b], '\0'};
const char rasswap[5] = {
"rgba"[stage.tevksel_swap1a],
"rgba"[stage.tevksel_swap2a],
"rgba"[stage.tevksel_swap1b],
"rgba"[stage.tevksel_swap2b],
'\0',
};
out.Write("\trastemp = %s.%s;\n", tevRasTable[stage.tevorders_colorchan], rasswap);
out.Write("\trastemp = %s.%s;\n", tev_ras_table[stage.tevorders_colorchan], rasswap);
}
if (stage.tevorders_enable)
{
// Generate swizzle string to represent the texture color channel swapping
char texswap[5] = {"rgba"[stage.tevksel_swap1c], "rgba"[stage.tevksel_swap2c],
"rgba"[stage.tevksel_swap1d], "rgba"[stage.tevksel_swap2d], '\0'};
const char texswap[5] = {
"rgba"[stage.tevksel_swap1c],
"rgba"[stage.tevksel_swap2c],
"rgba"[stage.tevksel_swap1d],
"rgba"[stage.tevksel_swap2d],
'\0',
};
if (!stage.hasindstage)
{
@ -1164,8 +1212,8 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
cc.d == TEVCOLORARG_KONST || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST ||
ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST)
{
out.Write("\tkonsttemp = int4(%s, %s);\n", tevKSelTableC[stage.tevksel_kc],
tevKSelTableA[stage.tevksel_ka]);
out.Write("\tkonsttemp = int4(%s, %s);\n", tev_ksel_table_c[stage.tevksel_kc],
tev_ksel_table_a[stage.tevksel_ka]);
if (stage.tevksel_kc > 7)
{
@ -1194,23 +1242,23 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
if (ac.dest >= GX_TEVREG0)
out.SetConstantsUsed(C_COLORS + ac.dest, C_COLORS + ac.dest);
out.Write("\ttevin_a = int4(%s, %s)&int4(255, 255, 255, 255);\n", tevCInputTable[cc.a],
tevAInputTable[ac.a]);
out.Write("\ttevin_b = int4(%s, %s)&int4(255, 255, 255, 255);\n", tevCInputTable[cc.b],
tevAInputTable[ac.b]);
out.Write("\ttevin_c = int4(%s, %s)&int4(255, 255, 255, 255);\n", tevCInputTable[cc.c],
tevAInputTable[ac.c]);
out.Write("\ttevin_d = int4(%s, %s);\n", tevCInputTable[cc.d], tevAInputTable[ac.d]);
out.Write("\ttevin_a = int4(%s, %s)&int4(255, 255, 255, 255);\n", tev_c_input_table[cc.a],
tev_a_input_table[ac.a]);
out.Write("\ttevin_b = int4(%s, %s)&int4(255, 255, 255, 255);\n", tev_c_input_table[cc.b],
tev_a_input_table[ac.b]);
out.Write("\ttevin_c = int4(%s, %s)&int4(255, 255, 255, 255);\n", tev_c_input_table[cc.c],
tev_a_input_table[ac.c]);
out.Write("\ttevin_d = int4(%s, %s);\n", tev_c_input_table[cc.d], tev_a_input_table[ac.d]);
out.Write("\t// color combine\n");
out.Write("\t%s = clamp(", tevCOutputTable[cc.dest]);
out.Write("\t%s = clamp(", tev_c_output_table[cc.dest]);
if (cc.bias != TEVBIAS_COMPARE)
{
WriteTevRegular(out, "rgb", cc.bias, cc.op, cc.clamp, cc.shift, false);
}
else
{
const char* function_table[] = {
constexpr std::array<const char*, 8> function_table{
"((tevin_a.r > tevin_b.r) ? tevin_c.rgb : int3(0,0,0))", // TEVCMP_R8_GT
"((tevin_a.r == tevin_b.r) ? tevin_c.rgb : int3(0,0,0))", // TEVCMP_R8_EQ
"((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.rgb : "
@ -1225,7 +1273,7 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
"((int3(1,1,1) - sign(abs(tevin_a.rgb - tevin_b.rgb))) * tevin_c.rgb)" // TEVCMP_RGB8_EQ
};
int mode = (cc.shift << 1) | cc.op;
const int mode = (cc.shift << 1) | cc.op;
out.Write(" tevin_d.rgb + ");
out.Write("%s", function_table[mode]);
}
@ -1236,14 +1284,14 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
out.Write(";\n");
out.Write("\t// alpha combine\n");
out.Write("\t%s = clamp(", tevAOutputTable[ac.dest]);
out.Write("\t%s = clamp(", tev_a_output_table[ac.dest]);
if (ac.bias != TEVBIAS_COMPARE)
{
WriteTevRegular(out, "a", ac.bias, ac.op, ac.clamp, ac.shift, true);
}
else
{
const char* function_table[] = {
constexpr std::array<const char*, 8> function_table{
"((tevin_a.r > tevin_b.r) ? tevin_c.a : 0)", // TEVCMP_R8_GT
"((tevin_a.r == tevin_b.r) ? tevin_c.a : 0)", // TEVCMP_R8_EQ
"((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.a : 0)", // TEVCMP_GR16_GT
@ -1254,7 +1302,7 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
"((tevin_a.a == tevin_b.a) ? tevin_c.a : 0)" // TEVCMP_A8_EQ
};
int mode = (ac.shift << 1) | ac.op;
const int mode = (ac.shift << 1) | ac.op;
out.Write(" tevin_d.a + ");
out.Write("%s", function_table[mode]);
}
@ -1269,38 +1317,38 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
static void WriteTevRegular(ShaderCode& out, const char* components, int bias, int op, int clamp,
int shift, bool alpha)
{
const char* tevScaleTableLeft[] = {
constexpr std::array<const char*, 4> tev_scale_table_left{
"", // SCALE_1
" << 1", // SCALE_2
" << 2", // SCALE_4
"", // DIVIDE_2
};
const char* tevScaleTableRight[] = {
constexpr std::array<const char*, 4> tev_scale_table_right{
"", // SCALE_1
"", // SCALE_2
"", // SCALE_4
" >> 1", // DIVIDE_2
};
const char* tevLerpBias[] = // indexed by 2*op+(shift==3)
{
"",
" + 128",
"",
" + 127",
};
// indexed by 2*op+(shift==3)
constexpr std::array<const char*, 4> tev_lerp_bias{
"",
" + 128",
"",
" + 127",
};
const char* tevBiasTable[] = {
constexpr std::array<const char*, 4> tev_bias_table{
"", // ZERO,
" + 128", // ADDHALF,
" - 128", // SUBHALF,
"",
};
const char* tevOpTable[] = {
"+", // TEVOP_ADD = 0,
"-", // TEVOP_SUB = 1,
constexpr std::array<char, 2> tev_op_table{
'+', // TEVOP_ADD = 0,
'-', // TEVOP_SUB = 1,
};
// Regular TEV stage: (d + bias + lerp(a,b,c)) * scale
@ -1308,12 +1356,12 @@ static void WriteTevRegular(ShaderCode& out, const char* components, int bias, i
// - c is scaled from 0..255 to 0..256, which allows dividing the result by 256 instead of 255
// - if scale is bigger than one, it is moved inside the lerp calculation for increased accuracy
// - a rounding bias is added before dividing by 256
out.Write("(((tevin_d.%s%s)%s)", components, tevBiasTable[bias], tevScaleTableLeft[shift]);
out.Write(" %s ", tevOpTable[op]);
out.Write("(((tevin_d.%s%s)%s)", components, tev_bias_table[bias], tev_scale_table_left[shift]);
out.Write(" %c ", tev_op_table[op]);
out.Write("(((((tevin_a.%s<<8) + (tevin_b.%s-tevin_a.%s)*(tevin_c.%s+(tevin_c.%s>>7)))%s)%s)>>8)",
components, components, components, components, components, tevScaleTableLeft[shift],
tevLerpBias[2 * op + ((shift == 3) == alpha)]);
out.Write(")%s", tevScaleTableRight[shift]);
components, components, components, components, components, tev_scale_table_left[shift],
tev_lerp_bias[2 * op + ((shift == 3) == alpha)]);
out.Write(")%s", tev_scale_table_right[shift]);
}
static void SampleTexture(ShaderCode& out, const char* texcoords, const char* texswap, int texmap,
@ -1334,7 +1382,7 @@ static void SampleTexture(ShaderCode& out, const char* texcoords, const char* te
}
}
static const char* tevAlphaFuncsTable[] = {
constexpr std::array<const char*, 8> tev_alpha_funcs_table{
"(false)", // NEVER
"(prev.a < %s)", // LESS
"(prev.a == %s)", // EQUAL
@ -1345,7 +1393,7 @@ static const char* tevAlphaFuncsTable[] = {
"(true)" // ALWAYS
};
static const char* tevAlphaFunclogicTable[] = {
constexpr std::array<const char*, 4> tev_alpha_funclogic_table{
" && ", // and
" || ", // or
" != ", // xor
@ -1355,7 +1403,7 @@ static const char* tevAlphaFunclogicTable[] = {
static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_data, APIType ApiType,
bool per_pixel_depth, bool use_dual_source)
{
static const char* alphaRef[2] = {I_ALPHA ".r", I_ALPHA ".g"};
static constexpr std::array<const char*, 2> alpha_ref{I_ALPHA ".r", I_ALPHA ".g"};
out.SetConstantsUsed(C_ALPHA, C_ALPHA);
@ -1366,13 +1414,14 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat
// Lookup the first component from the alpha function table
int compindex = uid_data->alpha_test_comp0;
out.Write(tevAlphaFuncsTable[compindex], alphaRef[0]);
out.Write(tev_alpha_funcs_table[compindex], alpha_ref[0]);
out.Write("%s", tevAlphaFunclogicTable[uid_data->alpha_test_logic]); // lookup the logic op
// Lookup the logic op
out.Write("%s", tev_alpha_funclogic_table[uid_data->alpha_test_logic]);
// Lookup the second component from the alpha function table
compindex = uid_data->alpha_test_comp1;
out.Write(tevAlphaFuncsTable[compindex], alphaRef[1]);
out.Write(tev_alpha_funcs_table[compindex], alpha_ref[1]);
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_NEGATED_BOOLEAN))
out.Write(") == false) {\n");
@ -1399,7 +1448,7 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat
out.Write("\t}\n");
}
static const char* tevFogFuncsTable[] = {
constexpr std::array<const char*, 8> tev_fog_funcs_table{
"", // No Fog
"", // ?
"", // Linear
@ -1456,7 +1505,7 @@ static void WriteFog(ShaderCode& out, const pixel_shader_uid_data* uid_data)
if (uid_data->fog_fsel > 3)
{
out.Write("%s", tevFogFuncsTable[uid_data->fog_fsel]);
out.Write("%s", tev_fog_funcs_table[uid_data->fog_fsel]);
}
else
{
@ -1509,7 +1558,7 @@ static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data)
{
if (uid_data->blend_enable)
{
static const std::array<const char*, 8> blendSrcFactor{{
static constexpr std::array<const char*, 8> blend_src_factor{
"float3(0,0,0);", // ZERO
"float3(1,1,1);", // ONE
"initial_ocol0.rgb;", // DSTCLR
@ -1518,8 +1567,8 @@ static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data)
"float3(1,1,1) - ocol1.aaa;", // INVSRCALPHA
"initial_ocol0.aaa;", // DSTALPHA
"float3(1,1,1) - initial_ocol0.aaa;", // INVDSTALPHA
}};
static const std::array<const char*, 8> blendSrcFactorAlpha{{
};
static constexpr std::array<const char*, 8> blend_src_factor_alpha{
"0.0;", // ZERO
"1.0;", // ONE
"initial_ocol0.a;", // DSTCLR
@ -1528,8 +1577,8 @@ static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data)
"1.0 - ocol1.a;", // INVSRCALPHA
"initial_ocol0.a;", // DSTALPHA
"1.0 - initial_ocol0.a;", // INVDSTALPHA
}};
static const std::array<const char*, 8> blendDstFactor{{
};
static constexpr std::array<const char*, 8> blend_dst_factor{
"float3(0,0,0);", // ZERO
"float3(1,1,1);", // ONE
"ocol0.rgb;", // SRCCLR
@ -1538,8 +1587,8 @@ static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data)
"float3(1,1,1) - ocol1.aaa;", // INVSRCALPHA
"initial_ocol0.aaa;", // DSTALPHA
"float3(1,1,1) - initial_ocol0.aaa;", // INVDSTALPHA
}};
static const std::array<const char*, 8> blendDstFactorAlpha{{
};
static constexpr std::array<const char*, 8> blend_dst_factor_alpha{
"0.0;", // ZERO
"1.0;", // ONE
"ocol0.a;", // SRCCLR
@ -1548,13 +1597,13 @@ static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data)
"1.0 - ocol1.a;", // INVSRCALPHA
"initial_ocol0.a;", // DSTALPHA
"1.0 - initial_ocol0.a;", // INVDSTALPHA
}};
};
out.Write("\tfloat4 blend_src;\n");
out.Write("\tblend_src.rgb = %s\n", blendSrcFactor[uid_data->blend_src_factor]);
out.Write("\tblend_src.a = %s\n", blendSrcFactorAlpha[uid_data->blend_src_factor_alpha]);
out.Write("\tblend_src.rgb = %s\n", blend_src_factor[uid_data->blend_src_factor]);
out.Write("\tblend_src.a = %s\n", blend_src_factor_alpha[uid_data->blend_src_factor_alpha]);
out.Write("\tfloat4 blend_dst;\n");
out.Write("\tblend_dst.rgb = %s\n", blendDstFactor[uid_data->blend_dst_factor]);
out.Write("\tblend_dst.a = %s\n", blendDstFactorAlpha[uid_data->blend_dst_factor_alpha]);
out.Write("\tblend_dst.rgb = %s\n", blend_dst_factor[uid_data->blend_dst_factor]);
out.Write("\tblend_dst.a = %s\n", blend_dst_factor_alpha[uid_data->blend_dst_factor_alpha]);
out.Write("\tfloat4 blend_result;\n");
if (uid_data->blend_subtract)