Files
LibretroAdmin f2f126da96 Video: build the vertex ubershaders by calls
The vertex ubershader is built through rslc's builder, as the
specialized vertex shader already is, so no GLSL is generated or
parsed for it on the compile workers.

The lighting function and the per-channel vertex lighting it shares
with the pixel ubershader, and bitfieldExtract for backends without it,
are builder pieces of their own. The text writers nothing uses any
more go: the vertex ubershader's, the line and point expansion, the
line offset, dolphin_isnan's and the output member assignment.

Metal's built shaders now get the same MSL header and fixups its
parsed ones do, so the ubershader lighting loop stays unrolled.

Built stages compile to the same words the text did for Vulkan and
D3D for every texgen count across random host configurations.
2026-11-10 08:19:30 +01:00

726 lines
28 KiB
C++

// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/ShaderGenCommon.h"
#include <stdarg.h>
#include <stdio.h>
#include <fmt/format.h>
#include "Common/FileUtil.h"
#include "Core/ConfigManager.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
void ShaderCode::Printf(const char* format, ...)
{
char line[512];
va_list ap;
int n;
/* a line formats on the stack; longer goes through the text's own */
va_start(ap, format);
n = vsnprintf(line, sizeof(line), format, ap);
va_end(ap);
if (n >= 0 && static_cast<size_t>(n) < sizeof(line))
{
shader_text_putn(&m_text, line, static_cast<size_t>(n));
return;
}
va_start(ap, format);
n = vsnprintf(nullptr, 0, format, ap);
va_end(ap);
if (n < 0)
return;
{
char* const big = static_cast<char*>(malloc(static_cast<size_t>(n) + 1));
if (!big)
return;
va_start(ap, format);
vsnprintf(big, static_cast<size_t>(n) + 1, format, ap);
va_end(ap);
shader_text_putn(&m_text, big, static_cast<size_t>(n));
free(big);
}
}
void WriteSwitchTree(ShaderCode& out, const char* variable, const char* const* values,
const char* const* names, u32 indent, u32 low, u32 high)
{
u32 mid;
// Each generated statement is for low <= x < high
if (high == low + 1)
{
// Down to 1 case (low <= x < low + 1 means x == low)
// Note that this indentation behaves poorly for multi-line code
out.Indent(indent);
out.Printf("%s // %s (%u)\n", values[low], names[low], low);
return;
}
mid = low + ((high - low) / 2);
out.Indent(indent);
out.Printf("if (%s < %uu) {\n", variable, mid);
WriteSwitchTree(out, variable, values, names, indent + 2, low, mid);
out.Indent(indent);
out.Puts("} else {\n");
WriteSwitchTree(out, variable, values, names, indent + 2, mid, high);
out.Indent(indent);
out.Puts("}\n");
}
ShaderHostConfig ShaderHostConfig::GetCurrent()
{
ShaderHostConfig bits = {};
bits.msaa = g_ActiveConfig.iMultisamples > 1;
bits.ssaa =
g_ActiveConfig.iMultisamples > 1 && g_ActiveConfig.bSSAA && g_backend_info.bSupportsSSAA;
bits.stereo = g_ActiveConfig.stereo_mode != StereoMode::Off;
bits.wireframe = g_ActiveConfig.bWireFrame;
bits.per_pixel_lighting = g_ActiveConfig.bEnablePixelLighting;
bits.vertex_rounding = g_ActiveConfig.UseVertexRounding();
bits.fast_depth_calc = g_ActiveConfig.bFastDepthCalc;
bits.bounding_box = g_ActiveConfig.bBBoxEnable;
bits.backend_dual_source_blend = g_backend_info.bSupportsDualSourceBlend;
bits.backend_geometry_shaders = g_backend_info.bSupportsGeometryShaders;
bits.backend_early_z = g_backend_info.bSupportsEarlyZ;
bits.backend_bbox = g_backend_info.bSupportsBBox;
bits.backend_gs_instancing = g_backend_info.bSupportsGSInstancing;
bits.backend_clip_control = g_backend_info.bSupportsClipControl;
bits.backend_ssaa = g_backend_info.bSupportsSSAA;
bits.backend_atomics = g_backend_info.bSupportsFragmentStoresAndAtomics;
bits.backend_depth_clamp = g_backend_info.bSupportsDepthClamp;
bits.backend_reversed_depth_range = g_backend_info.bSupportsReversedDepthRange;
bits.backend_bitfield = g_backend_info.bSupportsBitfield;
bits.backend_dynamic_sampler_indexing = g_backend_info.bSupportsDynamicSamplerIndexing;
bits.backend_shader_framebuffer_fetch = g_backend_info.bSupportsFramebufferFetch;
bits.backend_logic_op = g_backend_info.bSupportsLogicOp;
bits.backend_palette_conversion = g_backend_info.bSupportsPaletteConversion;
bits.enable_validation_layer = g_ActiveConfig.bEnableValidationLayer;
bits.manual_texture_sampling = !g_ActiveConfig.bFastTextureSampling;
bits.manual_texture_sampling_custom_texture_sizes =
g_ActiveConfig.ManualTextureSamplingWithCustomTextureSizes();
bits.backend_sampler_lod_bias = g_backend_info.bSupportsLodBiasInSampler;
bits.backend_dynamic_vertex_loader = g_backend_info.bSupportsDynamicVertexLoader;
bits.backend_vs_point_line_expand = g_ActiveConfig.UseVSForLinePointExpand();
bits.backend_gl_layer_in_fs = g_backend_info.bSupportsGLLayerInFS;
return bits;
}
std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid,
bool include_host_config, bool include_api)
{
if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
std::string filename = File::GetUserPath(D_SHADERCACHE_IDX);
if (include_api)
{
switch (api_type)
{
case APIType::D3D:
filename += "D3D";
break;
case APIType::Metal:
filename += "Metal";
break;
case APIType::OpenGL:
filename += "OpenGL";
break;
case APIType::Vulkan:
filename += "Vulkan";
break;
default:
break;
}
filename += '-';
}
filename += type;
if (include_gameid)
{
filename += '-';
filename += SConfig::GetInstance().GetGameID();
}
if (include_host_config)
{
// We're using 21 bits, so 6 hex characters.
ShaderHostConfig host_config = ShaderHostConfig::GetCurrent();
filename += fmt::format("-{:06X}", host_config.bits);
#if defined(__WEBOS__)
// The webOS core option toggles binding layout, which changes the
// generated GLSL
if (g_backend_info.bSupportsBindingLayout)
filename += "-bl";
#endif
}
filename += ".cache";
return filename;
}
void WriteBitfieldExtractHeader(ShaderCode& out, APIType api_type,
const ShaderHostConfig& host_config)
{
// ==============================================
// BitfieldExtract for APIs which don't have it
// ==============================================
if (!host_config.backend_bitfield)
{
out.Puts("uint bitfieldExtract(uint val, int off, int size) {\n"
" // This built-in function is only supported in OpenGL 4.0+ and ES 3.1+\n"
" // Microsoft's HLSL compiler automatically optimises this to a bitfield extract instruction.\n"
" uint mask = uint((1 << size) - 1);\n"
" return uint(val >> off) & mask;\n"
"}\n"
"\n");
out.Puts("int bitfieldExtract(int val, int off, int size) {\n"
" // This built-in function is only supported in OpenGL 4.0+ and ES 3.1+\n"
" // Microsoft's HLSL compiler automatically optimises this to a bitfield extract instruction.\n"
" return ((val << (32 - size - off)) >> (32 - size));\n"
"}\n"
"\n");
}
}
static void DefineOutputMember(ShaderCode& object, const char* qualifier,
const char* type, const char* name, int var_index)
{
object.Printf("\t%s %s %s", qualifier, type, name);
if (var_index != -1)
object.Printf("%d", var_index);
object.Puts(";\n");
}
void GenerateVSOutputMembers(ShaderCode& object, u32 texgens, const ShaderHostConfig& host_config,
const char* qualifier)
{
DefineOutputMember(object, qualifier, "float4", "pos", -1);
DefineOutputMember(object, qualifier, "float4", "colors_", 0);
DefineOutputMember(object, qualifier, "float4", "colors_", 1);
if (host_config.backend_geometry_shaders)
{
DefineOutputMember(object, qualifier, "float", "clipDist", 0);
DefineOutputMember(object, qualifier, "float", "clipDist", 1);
}
for (unsigned int i = 0; i < texgens; ++i)
DefineOutputMember(object, qualifier, "float3", "tex", i);
if (!host_config.fast_depth_calc)
DefineOutputMember(object, qualifier, "float4", "clipPos", -1);
if (host_config.per_pixel_lighting)
{
DefineOutputMember(object, qualifier, "float3", "Normal", -1);
DefineOutputMember(object, qualifier, "float3", "WorldPos", -1);
}
}
uint32_t sb_place_ref(const struct shader_build* sb, const struct sb_place* p)
{
uint32_t e = sb_ref(sb, p->var);
if (p->index_var)
return sb_idx(sb, e, sb_ref(sb, p->index_var));
if (p->index >= 0)
return sb_idx(sb, e, sb_i(sb, p->index));
return e;
}
void BuildVSOutputLayout(const struct shader_build* sb, u32 texgens,
const ShaderHostConfig& host_config, unsigned quals,
struct vs_output_layout* l)
{
static const char* const tex_names[8] = {"tex0", "tex1", "tex2", "tex3",
"tex4", "tex5", "tex6", "tex7"};
u32 n = 0, i;
#define VS_OUT_MEMBER(idx, type, name) \
do \
{ \
l->idx = static_cast<int>(n); \
l->types[n] = (type); \
l->names[n] = (name); \
l->quals[n++] = quals; \
} while (0)
l->clipDist0 = l->clipDist1 = l->clipPos = l->Normal = l->WorldPos = -1;
l->texgens = texgens;
VS_OUT_MEMBER(pos, sb->f[4], "pos");
VS_OUT_MEMBER(colors_0, sb->f[4], "colors_0");
VS_OUT_MEMBER(colors_1, sb->f[4], "colors_1");
if (host_config.backend_geometry_shaders)
{
VS_OUT_MEMBER(clipDist0, sb->f[1], "clipDist0");
VS_OUT_MEMBER(clipDist1, sb->f[1], "clipDist1");
}
l->tex0 = static_cast<int>(n);
for (i = 0; i < texgens; i++)
{
l->types[n] = sb->f[3];
l->names[n] = tex_names[i];
l->quals[n++] = quals;
}
if (!host_config.fast_depth_calc)
VS_OUT_MEMBER(clipPos, sb->f[4], "clipPos");
if (host_config.per_pixel_lighting)
{
VS_OUT_MEMBER(Normal, sb->f[3], "Normal");
VS_OUT_MEMBER(WorldPos, sb->f[3], "WorldPos");
}
#undef VS_OUT_MEMBER
l->n = n;
}
unsigned BuildInterpolationQuals(bool msaa, bool ssaa)
{
if (!msaa)
return 0;
return ssaa ? RSLC_B_SAMPLE : RSLC_B_CENTROID;
}
void BuildAssignVSOutputMembers(const struct shader_build* sb, const struct vs_output_layout* l,
const struct sb_place* a, const struct sb_place* b,
const ShaderHostConfig& host_config)
{
u32 i;
#define VS_OUT_ASSIGN(idx) \
sb_set(sb, sb_mem(sb, sb_place_ref(sb, a), static_cast<uint32_t>(idx)), \
sb_mem(sb, sb_place_ref(sb, b), static_cast<uint32_t>(idx)))
VS_OUT_ASSIGN(l->pos);
VS_OUT_ASSIGN(l->colors_0);
VS_OUT_ASSIGN(l->colors_1);
for (i = 0; i < l->texgens; i++)
VS_OUT_ASSIGN(l->tex0 + static_cast<int>(i));
if (!host_config.fast_depth_calc)
VS_OUT_ASSIGN(l->clipPos);
if (host_config.per_pixel_lighting)
{
VS_OUT_ASSIGN(l->Normal);
VS_OUT_ASSIGN(l->WorldPos);
}
if (host_config.backend_geometry_shaders)
{
VS_OUT_ASSIGN(l->clipDist0);
VS_OUT_ASSIGN(l->clipDist1);
}
#undef VS_OUT_ASSIGN
}
const struct rslc_type* BuildLightStruct(const struct shader_build* sb)
{
static const char* const names[5] = {"color", "cosatt", "distatt", "pos", "dir"};
const struct rslc_type* types[5];
types[0] = sb->i[4];
types[1] = sb->f[4];
types[2] = sb->f[4];
types[3] = sb->f[4];
types[4] = sb->f[4];
return rslc_b_struct(sb->b, "Light", types, names, 5);
}
u32 BuildGeometryShaderUniforms(const struct shader_build* sb, const struct rslc_type** types,
const char** names, u32 n)
{
types[n] = sb->f[4];
names[n++] = I_STEREOPARAMS;
types[n] = sb->f[4];
names[n++] = I_LINEPTPARAMS;
types[n] = sb->i[4];
names[n++] = I_TEXOFFSET;
types[n] = sb->u[1];
names[n++] = "vs_expand";
return n;
}
struct rslc_var* BuildLineOffset(const struct shader_build* sb, struct rslc_var* clinept,
sb_expr_fn pos_a, const void* a_ctx, sb_expr_fn pos_b,
const void* b_ctx, struct rslc_var* sign)
{
// GameCube/Wii's line drawing algorithm is a little quirky. It does not
// use the correct line caps. Instead, the line caps are vertical or
// horizontal depending the slope of the line.
struct rslc_var* offset = rslc_b_local(sb->b, "offset", sb->f[2], 0, false);
struct rslc_var* to;
uint32_t then_s, else_s, e;
#define LP(c) sb_swz(sb, sb_ref(sb, clinept), c)
to = rslc_b_local(
sb->b, "to", sb->f[2],
sb_call(sb, "abs", 1,
sb_bin(sb, RSLC_B_SUB,
sb_bin(sb, RSLC_B_DIV, sb_swz(sb, pos_a(sb, a_ctx), "xy"),
sb_swz(sb, pos_a(sb, a_ctx), "w")),
sb_bin(sb, RSLC_B_DIV, sb_swz(sb, pos_b(sb, b_ctx), "xy"),
sb_swz(sb, pos_b(sb, b_ctx), "w")))),
false);
// FIXME: What does real hardware do when line is at a 45-degree angle?
// FIXME: Lines aren't drawn at the correct width. See Twilight Princess map.
rslc_b_begin_block(sb->b);
// Line is more tall. Extend geometry left and right.
// Lerp LineWidth/2 from [0..VpWidth] to [-1..1]
e = sign ? sb_bin(sb, RSLC_B_MUL, sb_ref(sb, sign), LP("z")) : LP("z");
e = sb_bin(sb, RSLC_B_DIV, e, LP("x"));
sb_set(sb, sb_ref(sb, offset), sb_con(sb, sb->f[2], 2, e, sb_i(sb, 0)));
then_s = rslc_b_end_block(sb->b);
rslc_b_begin_block(sb->b);
// Line is more wide. Extend geometry up and down.
// Lerp LineWidth/2 from [0..VpHeight] to [1..-1]
e = sb_un(sb, RSLC_B_NEG, LP("z"));
if (sign)
e = sb_bin(sb, RSLC_B_MUL, sb_ref(sb, sign), e);
e = sb_bin(sb, RSLC_B_DIV, e, LP("y"));
sb_set(sb, sb_ref(sb, offset), sb_con(sb, sb->f[2], 2, sb_i(sb, 0), e));
else_s = rslc_b_end_block(sb->b);
rslc_b_stmt(sb->b, rslc_b_if(sb->b,
sb_bin(sb, RSLC_B_GT,
sb_bin(sb, RSLC_B_MUL, LP("y"),
sb_swz(sb, sb_ref(sb, to), "y")),
sb_bin(sb, RSLC_B_MUL, LP("x"),
sb_swz(sb, sb_ref(sb, to), "x"))),
then_s, else_s));
#undef LP
return offset;
}
void BuildBitfieldExtractHeader(const struct shader_build* sb, const ShaderHostConfig& host_config,
struct sb_bitfield* bf)
{
static const char* const names[3] = {"val", "off", "size"};
static const unsigned dirs[3] = {RSLC_B_IN, RSLC_B_IN, RSLC_B_IN};
const struct rslc_type* types[3];
struct rslc_var *mask, *val, *off, *size;
bf->u = bf->i = nullptr;
// ==============================================
// BitfieldExtract for APIs which don't have it
// ==============================================
if (host_config.backend_bitfield)
return;
// This built-in function is only supported in OpenGL 4.0+ and ES 3.1+
// Microsoft's HLSL compiler automatically optimises this to a bitfield extract instruction.
types[0] = sb->u[1];
types[1] = types[2] = sb->i[1];
bf->u = rslc_b_func(sb->b, "bitfieldExtract", sb->u[1], 3, types, dirs, names);
rslc_b_func_begin(sb->b, bf->u);
val = rslc_b_param(bf->u, 0);
off = rslc_b_param(bf->u, 1);
size = rslc_b_param(bf->u, 2);
mask = rslc_b_local(
sb->b, "mask", sb->u[1],
sb_con(sb, sb->u[1], 1,
sb_bin(sb, RSLC_B_SUB, sb_bin(sb, RSLC_B_SHL, sb_i(sb, 1), sb_ref(sb, size)),
sb_i(sb, 1))),
false);
rslc_b_stmt(sb->b,
rslc_b_return(sb->b, sb_bin(sb, RSLC_B_BAND,
sb_con(sb, sb->u[1], 1,
sb_bin(sb, RSLC_B_SHR, sb_ref(sb, val),
sb_ref(sb, off))),
sb_ref(sb, mask))));
rslc_b_func_end(sb->b);
types[0] = sb->i[1];
bf->i = rslc_b_func(sb->b, "bitfieldExtract", sb->i[1], 3, types, dirs, names);
rslc_b_func_begin(sb->b, bf->i);
val = rslc_b_param(bf->i, 0);
off = rslc_b_param(bf->i, 1);
size = rslc_b_param(bf->i, 2);
rslc_b_stmt(
sb->b,
rslc_b_return(
sb->b,
sb_bin(sb, RSLC_B_SHR,
sb_bin(sb, RSLC_B_SHL, sb_ref(sb, val),
sb_bin(sb, RSLC_B_SUB,
sb_bin(sb, RSLC_B_SUB, sb_i(sb, 32), sb_ref(sb, size)),
sb_ref(sb, off))),
sb_bin(sb, RSLC_B_SUB, sb_i(sb, 32), sb_ref(sb, size)))));
rslc_b_func_end(sb->b);
}
uint32_t sb_bitfield_extract(const struct shader_build* sb, const struct sb_bitfield* bf,
bool is_signed, uint32_t src, u32 start, u32 bits)
{
uint32_t a[3];
const struct rslc_func* f = is_signed ? bf->i : bf->u;
a[0] = sb_con(sb, is_signed ? sb->i[1] : sb->u[1], 1, src);
a[1] = sb_i(sb, static_cast<int32_t>(start));
a[2] = sb_i(sb, static_cast<int32_t>(bits));
if (f)
return rslc_b_call(sb->b, f, a, 3);
return rslc_b_builtin(sb->b, "bitfieldExtract", a, 3);
}
struct rslc_func* BuildIsNanHeader(const struct shader_build* sb, APIType api_type)
{
static const char* const names[1] = {"f"};
static const unsigned dirs[1] = {RSLC_B_IN};
const struct rslc_type* types[1];
struct rslc_func* f;
if (api_type != APIType::D3D)
return nullptr;
// Workaround for the HLSL compiler deciding that isnan can never be true and
// optimising away the call, even though the value can actually be NaN
// Just look for the bit pattern that indicates NaN instead
types[0] = sb->f[1];
f = rslc_b_func(sb->b, "dolphin_isnan", sb->bl, 1, types, dirs, names);
rslc_b_func_begin(sb->b, f);
rslc_b_stmt(sb->b,
rslc_b_return(sb->b, sb_bin(sb, RSLC_B_GT,
sb_bin(sb, RSLC_B_BAND,
sb_call(sb, "floatBitsToInt", 1,
sb_ref(sb, rslc_b_param(f, 0))),
sb_i(sb, 0x7FFFFFFF)),
sb_i(sb, 0x7F800000))));
rslc_b_func_end(sb->b);
return f;
}
uint32_t sb_isnan(const struct shader_build* sb, const struct rslc_func* isnan_fn, uint32_t e)
{
if (isnan_fn)
return rslc_b_call(sb->b, isnan_fn, &e, 1);
return sb_call(sb, "isnan", 1, e);
}
u32 BuildShaderUniforms(const struct shader_build* sb, const struct rslc_type* light,
const struct rslc_type** types, const char** names, u32 n)
{
static const char* const su_names[SU_COUNT] = {"components",
"xfmem_dualTexInfo",
"xfmem_numColorChans",
"missing_color_hex",
"missing_color_value",
I_POSNORMALMATRIX,
I_PROJECTION,
I_MATERIALS,
I_LIGHTS,
I_TEXMATRICES,
I_TRANSFORMMATRICES,
I_NORMALMATRICES,
I_POSTTRANSFORMMATRICES,
I_PIXELCENTERCORRECTION,
I_VIEWPORT_SIZE,
"xfmem_pack1",
I_CACHED_NORMAL,
I_CACHED_TANGENT,
I_CACHED_BINORMAL,
"vertex_stride",
"vertex_offset_rawnormal",
"vertex_offset_rawtangent",
"vertex_offset_rawbinormal",
"vertex_offset_rawpos",
"vertex_offset_posmtx",
"vertex_offset_rawcolor0",
"vertex_offset_rawcolor1",
"vertex_offset_rawtex"};
u32 k;
for (k = 0; k < SU_COUNT; k++)
names[n + k] = su_names[k];
types[n + SU_COMPONENTS] = sb->u[1];
types[n + SU_DUALTEXINFO] = sb->u[1];
types[n + SU_NUMCOLORCHANS] = sb->u[1];
types[n + SU_MISSING_COLOR_HEX] = sb->u[1];
types[n + SU_MISSING_COLOR_VALUE] = sb->f[4];
types[n + SU_POSNORMALMATRIX] = rslc_b_array(sb->b, sb->f[4], 6);
types[n + SU_PROJECTION] = rslc_b_array(sb->b, sb->f[4], 4);
types[n + SU_MATERIALS] = rslc_b_array(sb->b, sb->i[4], 4);
types[n + SU_LIGHTS] = rslc_b_array(sb->b, light, 8);
types[n + SU_TEXMATRICES] = rslc_b_array(sb->b, sb->f[4], 24);
types[n + SU_TRANSFORMMATRICES] = rslc_b_array(sb->b, sb->f[4], 64);
types[n + SU_NORMALMATRICES] = rslc_b_array(sb->b, sb->f[4], 32);
types[n + SU_POSTTRANSFORMMATRICES] = rslc_b_array(sb->b, sb->f[4], 64);
types[n + SU_PIXELCENTERCORRECTION] = sb->f[4];
types[n + SU_VIEWPORT_SIZE] = sb->f[2];
types[n + SU_PACK1] = rslc_b_array(sb->b, sb->u[4], 8);
types[n + SU_CACHED_NORMAL] = sb->f[4];
types[n + SU_CACHED_TANGENT] = sb->f[4];
types[n + SU_CACHED_BINORMAL] = sb->f[4];
for (k = SU_VERTEX_STRIDE; k < SU_OFFSET_RAWTEX; k++)
types[n + k] = sb->u[1];
types[n + SU_OFFSET_RAWTEX] = rslc_b_array(sb->b, sb->u[4], 2);
return n + SU_COUNT;
}
namespace
{
/* o.member */
uint32_t vsx_o(const struct shader_build* sb, const struct vs_expansion* x, int member)
{
return sb_mem(sb, sb_ref(sb, x->o), static_cast<uint32_t>(member));
}
uint32_t vsx_o_pos(const struct shader_build* sb, const void* ctx)
{
const struct vs_expansion* x = static_cast<const struct vs_expansion*>(ctx);
return vsx_o(sb, x, x->layout->pos);
}
uint32_t vsx_other_pos(const struct shader_build* sb, const void* ctx)
{
return sb_ref(sb, static_cast<const struct vs_expansion*>(ctx)->other_pos);
}
/* o.pos.xy += offset * o.pos.w; */
void vsx_offset_pos(const struct shader_build* sb, const struct vs_expansion* x,
struct rslc_var* offset)
{
sb_op_set(sb, RSLC_B_ADD, sb_swz(sb, vsx_o(sb, x, x->layout->pos), "xy"),
sb_bin(sb, RSLC_B_MUL, sb_ref(sb, offset),
sb_swz(sb, vsx_o(sb, x, x->layout->pos), "w")));
}
/* (((ctexoffset[k] >> i) & 0x1) != 0) */
uint32_t vsx_tex_bit(const struct shader_build* sb, const struct vs_expansion* x, int k, u32 i)
{
return sb_bin(sb, RSLC_B_NE,
sb_bin(sb, RSLC_B_BAND,
sb_bin(sb, RSLC_B_SHR, sb_idx(sb, sb_ref(sb, x->texoffset), sb_i(sb, k)),
sb_i(sb, static_cast<int32_t>(i))),
sb_i(sb, 0x1)),
sb_i(sb, 0));
}
/* is ? a : b, of float literals */
uint32_t vsx_pick(const struct shader_build* sb, struct rslc_var* is, double a, double b)
{
return rslc_b_select(sb->b, sb_ref(sb, is),
a < 0 ? sb_un(sb, RSLC_B_NEG, sb_f(sb, -a)) : sb_f(sb, a),
b < 0 ? sb_un(sb, RSLC_B_NEG, sb_f(sb, -b)) : sb_f(sb, b));
}
} // namespace
void BuildVSLineExpansion(const struct shader_build* sb, const struct vs_expansion* x)
{
const u32 texgens = x->layout->texgens;
struct rslc_var *expand_sign, *offset;
uint32_t d[4];
int k;
for (k = 0; k < 4; k++)
d[k] = sb_call(sb, "dot", 2, sb_idx(sb, sb_ref(sb, x->projection), sb_i(sb, k)),
sb_ref(sb, x->other_pos));
sb_set(sb, sb_ref(sb, x->other_pos), sb_con(sb, sb->f[4], 4, d[0], d[1], d[2], d[3]));
expand_sign =
rslc_b_local(sb->b, "expand_sign", sb->f[1], vsx_pick(sb, x->is_right, 1.0, -1.0), false);
offset = BuildLineOffset(sb, x->clinept, vsx_o_pos, x, vsx_other_pos, x, expand_sign);
vsx_offset_pos(sb, x, offset);
if (texgens > 0)
{
struct rslc_var* tex_offset;
uint32_t then_s;
u32 i;
rslc_b_begin_block(sb->b);
tex_offset = rslc_b_local(
sb->b, "texOffset", sb->f[1],
sb_bin(sb, RSLC_B_DIV, sb_f(sb, 1.0),
sb_con(sb, sb->f[1], 1, sb_idx(sb, sb_ref(sb, x->texoffset), sb_i(sb, 2)))),
false);
for (i = 0; i < texgens; i++)
{
rslc_b_stmt(sb->b,
rslc_b_if(sb->b, vsx_tex_bit(sb, x, 0, i),
rslc_b_expr(sb->b, rslc_b_assign(
sb->b, RSLC_B_ADD,
sb_swz(sb,
vsx_o(sb, x,
x->layout->tex0 +
static_cast<int>(i)),
"x"),
sb_ref(sb, tex_offset))),
0));
}
then_s = rslc_b_end_block(sb->b);
rslc_b_stmt(sb->b,
rslc_b_if(sb->b,
sb_bin(sb, RSLC_B_AND,
sb_bin(sb, RSLC_B_NE,
sb_idx(sb, sb_ref(sb, x->texoffset), sb_i(sb, 2)),
sb_i(sb, 0)),
sb_ref(sb, x->is_right)),
then_s, 0));
}
}
void BuildVSPointExpansion(const struct shader_build* sb, const struct vs_expansion* x)
{
const u32 texgens = x->layout->texgens;
struct rslc_var *expand_sign, *offset;
expand_sign = rslc_b_local(sb->b, "expand_sign", sb->f[2],
sb_con(sb, sb->f[2], 2, vsx_pick(sb, x->is_right, 1.0, -1.0),
vsx_pick(sb, x->is_bottom, -1.0, 1.0)),
false);
offset = rslc_b_local(sb->b, "offset", sb->f[2],
sb_bin(sb, RSLC_B_DIV,
sb_bin(sb, RSLC_B_MUL, sb_ref(sb, expand_sign),
sb_swz(sb, sb_ref(sb, x->clinept), "ww")),
sb_swz(sb, sb_ref(sb, x->clinept), "xy")),
false);
vsx_offset_pos(sb, x, offset);
if (texgens > 0)
{
struct rslc_var *magnitude, *tex_offset;
uint32_t then_s;
u32 i;
rslc_b_begin_block(sb->b);
magnitude = rslc_b_local(
sb->b, "texOffsetMagnitude", sb->f[1],
sb_bin(sb, RSLC_B_DIV, sb_f(sb, 1.0),
sb_con(sb, sb->f[1], 1, sb_idx(sb, sb_ref(sb, x->texoffset), sb_i(sb, 3)))),
false);
tex_offset = rslc_b_local(
sb->b, "texOffset", sb->f[2],
sb_con(sb, sb->f[2], 2,
rslc_b_select(sb->b, sb_ref(sb, x->is_right), sb_ref(sb, magnitude), sb_f(sb, 0.0)),
rslc_b_select(sb->b, sb_ref(sb, x->is_bottom), sb_ref(sb, magnitude),
sb_f(sb, 0.0))),
false);
for (i = 0; i < texgens; i++)
{
rslc_b_stmt(sb->b,
rslc_b_if(sb->b, vsx_tex_bit(sb, x, 1, i),
rslc_b_expr(sb->b, rslc_b_assign(
sb->b, RSLC_B_ADD,
sb_swz(sb,
vsx_o(sb, x,
x->layout->tex0 +
static_cast<int>(i)),
"xy"),
sb_ref(sb, tex_offset))),
0));
}
then_s = rslc_b_end_block(sb->b);
rslc_b_stmt(sb->b,
rslc_b_if(sb->b,
sb_bin(sb, RSLC_B_NE, sb_idx(sb, sb_ref(sb, x->texoffset), sb_i(sb, 3)),
sb_i(sb, 0)),
then_s, 0));
}
}
const char* GetInterpolationQualifier(bool msaa, bool ssaa, bool in_glsl_interface_block, bool in)
{
if (!msaa)
return "";
// Without GL_ARB_shading_language_420pack support, the interpolation qualifier must be
// "centroid in" and not "centroid", even within an interface block.
if (in_glsl_interface_block && !g_backend_info.bSupportsBindingLayout)
{
if (!ssaa)
return in ? "centroid in" : "centroid out";
else
return in ? "sample in" : "sample out";
}
else
{
if (!ssaa)
return "centroid";
else
return "sample";
}
}