mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2025-08-29 10:43:55 +02:00
967 lines
37 KiB
C
967 lines
37 KiB
C
/*
|
|
* Fixed-function pipeline replacement implemented using HLSL shaders
|
|
*
|
|
* Copyright 2006 Jason Green
|
|
* Copyright 2006-2007 Henri Verbeet
|
|
* Copyright 2007-2009,2013 Stefan Dösinger for CodeWeavers
|
|
* Copyright 2009-2011 Henri Verbeet for CodeWeavers
|
|
* Copyright 2022,2024 Elizabeth Figura for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "wined3d_private.h"
|
|
#include <vkd3d_shader.h>
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader);
|
|
|
|
static const char *get_material_colour_source(enum wined3d_material_color_source mcs, const char *material)
|
|
{
|
|
switch (mcs)
|
|
{
|
|
case WINED3D_MCS_MATERIAL:
|
|
return material;
|
|
case WINED3D_MCS_COLOR1:
|
|
return "i.diffuse";
|
|
case WINED3D_MCS_COLOR2:
|
|
return "i.specular";
|
|
default:
|
|
ERR("Invalid material color source %#x.\n", mcs);
|
|
return "<invalid>";
|
|
}
|
|
}
|
|
|
|
static void generate_lighting_footer(struct wined3d_string_buffer *buffer,
|
|
const struct wined3d_ffp_vs_settings *settings, unsigned int idx, bool legacy_lighting)
|
|
{
|
|
shader_addline(buffer, " diffuse += saturate(dot(dir, normal)) * c.lights[%u].diffuse.xyz * att;\n", idx);
|
|
if (settings->localviewer)
|
|
shader_addline(buffer, " t = dot(normal, ffp_normalize(dir - ffp_normalize(ec_pos.xyz)));\n");
|
|
else
|
|
shader_addline(buffer, " t = dot(normal, ffp_normalize(dir + float3(0.0, 0.0, -1.0)));\n");
|
|
if (settings->specular_enable)
|
|
{
|
|
shader_addline(buffer, " if (dot(dir, normal) > 0.0 && t > 0.0");
|
|
if (legacy_lighting)
|
|
shader_addline(buffer, " && c.material.power > 0.0");
|
|
shader_addline(buffer, ")\n");
|
|
shader_addline(buffer, " specular += pow(t, c.material.power) * c.lights[%u].specular * att;\n", idx);
|
|
}
|
|
}
|
|
|
|
static void generate_lighting(struct wined3d_string_buffer *buffer,
|
|
const struct wined3d_ffp_vs_settings *settings, bool legacy_lighting)
|
|
{
|
|
const char *ambient, *diffuse, *specular, *emissive;
|
|
unsigned int idx = 0;
|
|
|
|
if (!settings->lighting)
|
|
{
|
|
if (settings->diffuse)
|
|
shader_addline(buffer, " o.diffuse = i.diffuse;\n");
|
|
else
|
|
shader_addline(buffer, " o.diffuse = 1.0;\n");
|
|
shader_addline(buffer, " o.specular = i.specular;\n");
|
|
return;
|
|
}
|
|
|
|
shader_addline(buffer, " float3 ambient = c.ambient_colour.xyz;\n");
|
|
shader_addline(buffer, " float3 diffuse = 0.0;\n");
|
|
shader_addline(buffer, " float3 dir, dst;\n");
|
|
shader_addline(buffer, " float att, t, range, falloff, cos_htheta, cos_hphi;\n");
|
|
|
|
if (settings->specular_enable)
|
|
shader_addline(buffer, " float4 specular = 0.0;\n");
|
|
|
|
ambient = get_material_colour_source(settings->ambient_source, "c.material.ambient");
|
|
diffuse = get_material_colour_source(settings->diffuse_source, "c.material.diffuse");
|
|
specular = get_material_colour_source(settings->specular_source, "c.material.specular");
|
|
emissive = get_material_colour_source(settings->emissive_source, "c.material.emissive");
|
|
|
|
for (unsigned int i = 0; i < settings->point_light_count; ++i)
|
|
{
|
|
shader_addline(buffer, " dir = c.lights[%u].position.xyz - ec_pos.xyz;\n", idx);
|
|
shader_addline(buffer, " dst.z = dot(dir, dir);\n");
|
|
shader_addline(buffer, " dst.y = sqrt(dst.z);\n");
|
|
shader_addline(buffer, " dst.x = 1.0;\n");
|
|
|
|
shader_addline(buffer, " range = c.lights[%u].packed_params.x;\n", idx);
|
|
|
|
if (legacy_lighting)
|
|
{
|
|
shader_addline(buffer, " dst.y = (range - dst.y) / range;\n");
|
|
shader_addline(buffer, " dst.z = dst.y * dst.y;\n");
|
|
shader_addline(buffer, " if (dst.y > 0.0)\n{\n");
|
|
shader_addline(buffer, " att = dot(dst.xyz, c.lights[%u].attenuation.xyz);\n", idx);
|
|
}
|
|
else
|
|
{
|
|
shader_addline(buffer, " if (dst.y <= range)\n{\n");
|
|
shader_addline(buffer, " att = 1.0 / dot(dst.xyz, c.lights[%u].attenuation.xyz);\n", idx);
|
|
}
|
|
shader_addline(buffer, " ambient += c.lights[%u].ambient.xyz * att;\n", idx);
|
|
if (settings->normal)
|
|
{
|
|
shader_addline(buffer, " dir = ffp_normalize(dir);\n");
|
|
generate_lighting_footer(buffer, settings, idx, legacy_lighting);
|
|
}
|
|
shader_addline(buffer, " }\n");
|
|
|
|
++idx;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < settings->spot_light_count; ++i)
|
|
{
|
|
shader_addline(buffer, " dir = c.lights[%u].position.xyz - ec_pos.xyz;\n", idx);
|
|
shader_addline(buffer, " dst.z = dot(dir, dir);\n");
|
|
shader_addline(buffer, " dst.y = sqrt(dst.z);\n");
|
|
shader_addline(buffer, " dst.x = 1.0;\n");
|
|
|
|
shader_addline(buffer, " range = c.lights[%u].packed_params.x;\n", idx);
|
|
shader_addline(buffer, " falloff = c.lights[%u].packed_params.y;\n", idx);
|
|
shader_addline(buffer, " cos_htheta = c.lights[%u].packed_params.z;\n", idx);
|
|
shader_addline(buffer, " cos_hphi = c.lights[%u].packed_params.w;\n", idx);
|
|
|
|
if (legacy_lighting)
|
|
{
|
|
shader_addline(buffer, " dst.y = (range - dst.y) / range;\n");
|
|
shader_addline(buffer, " dst.z = dst.y * dst.y;\n");
|
|
shader_addline(buffer, " if (dst.y > 0.0)\n{\n");
|
|
}
|
|
else
|
|
{
|
|
shader_addline(buffer, " if (dst.y <= range)\n{\n");
|
|
}
|
|
shader_addline(buffer, " dir = ffp_normalize(dir);\n");
|
|
shader_addline(buffer, " t = dot(-dir, ffp_normalize(c.lights[%u].direction.xyz));\n", idx);
|
|
shader_addline(buffer, " if (t > cos_htheta) att = 1.0;\n");
|
|
shader_addline(buffer, " else if (t <= cos_hphi) att = 0.0;\n");
|
|
shader_addline(buffer, " else att = pow((t - cos_hphi) / (cos_htheta - cos_hphi), falloff);\n");
|
|
if (legacy_lighting)
|
|
shader_addline(buffer, " att *= dot(dst.xyz, c.lights[%u].attenuation.xyz);\n", idx);
|
|
else
|
|
shader_addline(buffer, " att /= dot(dst.xyz, c.lights[%u].attenuation.xyz);\n", idx);
|
|
shader_addline(buffer, " ambient += c.lights[%u].ambient.xyz * att;\n", idx);
|
|
if (settings->normal)
|
|
generate_lighting_footer(buffer, settings, idx, legacy_lighting);
|
|
shader_addline(buffer, " }\n");
|
|
|
|
++idx;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < settings->directional_light_count; ++i)
|
|
{
|
|
shader_addline(buffer, " ambient += c.lights[%u].ambient.xyz;\n", idx);
|
|
if (settings->normal)
|
|
{
|
|
shader_addline(buffer, " att = 1.0;\n");
|
|
shader_addline(buffer, " dir = ffp_normalize(c.lights[%u].direction.xyz);\n", idx);
|
|
generate_lighting_footer(buffer, settings, idx, legacy_lighting);
|
|
}
|
|
|
|
++idx;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < settings->parallel_point_light_count; ++i)
|
|
{
|
|
shader_addline(buffer, " ambient += c.lights[%u].ambient.xyz;\n", idx);
|
|
if (settings->normal)
|
|
{
|
|
shader_addline(buffer, " att = 1.0;\n");
|
|
shader_addline(buffer, " dir = ffp_normalize(c.lights[%u].position.xyz);\n", idx);
|
|
generate_lighting_footer(buffer, settings, idx, legacy_lighting);
|
|
}
|
|
|
|
++idx;
|
|
}
|
|
|
|
shader_addline(buffer, " o.diffuse.xyz = %s.xyz * ambient + %s.xyz * diffuse + %s.xyz;\n",
|
|
ambient, diffuse, emissive);
|
|
shader_addline(buffer, " o.diffuse.w = %s.w;\n", diffuse);
|
|
if (settings->specular_enable)
|
|
shader_addline(buffer, " o.specular = %s * specular;\n", specular);
|
|
else
|
|
shader_addline(buffer, " o.specular = i.specular;\n");
|
|
}
|
|
|
|
static bool ffp_hlsl_generate_vertex_shader(const struct wined3d_ffp_vs_settings *settings,
|
|
struct wined3d_string_buffer *buffer, bool legacy_lighting)
|
|
{
|
|
struct wined3d_string_buffer texcoord;
|
|
|
|
/* This must be kept in sync with struct wined3d_ffp_vs_constants. */
|
|
shader_addline(buffer, "uniform struct\n");
|
|
shader_addline(buffer, "{\n");
|
|
shader_addline(buffer, " float4x4 modelview_matrices[%u];\n", MAX_VERTEX_BLENDS);
|
|
shader_addline(buffer, " float4x4 projection_matrix;\n");
|
|
shader_addline(buffer, " float4x4 texture_matrices[%u];\n", WINED3D_MAX_FFP_TEXTURES);
|
|
shader_addline(buffer, " float4 point_params;\n");
|
|
shader_addline(buffer, " struct\n");
|
|
shader_addline(buffer, " {\n");
|
|
shader_addline(buffer, " float4 diffuse;\n");
|
|
shader_addline(buffer, " float4 ambient;\n");
|
|
shader_addline(buffer, " float4 specular;\n");
|
|
shader_addline(buffer, " float4 emissive;\n");
|
|
shader_addline(buffer, " float power;\n");
|
|
shader_addline(buffer, " } material;\n");
|
|
shader_addline(buffer, " float4 ambient_colour;\n");
|
|
shader_addline(buffer, " struct\n");
|
|
shader_addline(buffer, " {\n");
|
|
shader_addline(buffer, " float4 diffuse, specular, ambient;\n");
|
|
shader_addline(buffer, " float4 position, direction;\n");
|
|
shader_addline(buffer, " float4 packed_params;\n");
|
|
shader_addline(buffer, " float4 attenuation;\n");
|
|
shader_addline(buffer, " } lights[%u];\n", WINED3D_MAX_ACTIVE_LIGHTS);
|
|
shader_addline(buffer, "} c;\n");
|
|
|
|
shader_addline(buffer, "struct input\n");
|
|
shader_addline(buffer, "{\n");
|
|
shader_addline(buffer, " float4 pos : POSITION;\n");
|
|
shader_addline(buffer, " float4 blend_weight : BLENDWEIGHT;\n");
|
|
shader_addline(buffer, " uint blend_indices : BLENDINDICES;\n");
|
|
shader_addline(buffer, " float3 normal : NORMAL;\n");
|
|
shader_addline(buffer, " float point_size : PSIZE;\n");
|
|
shader_addline(buffer, " float4 diffuse : COLOR0;\n");
|
|
shader_addline(buffer, " float4 specular : COLOR1;\n");
|
|
shader_addline(buffer, " float4 texcoord[%u] : TEXCOORD;\n", WINED3D_MAX_FFP_TEXTURES);
|
|
shader_addline(buffer, "};\n\n");
|
|
|
|
shader_addline(buffer, "struct output\n");
|
|
shader_addline(buffer, "{\n");
|
|
shader_addline(buffer, " float4 pos : POSITION;\n");
|
|
shader_addline(buffer, " float4 diffuse : COLOR0;\n");
|
|
shader_addline(buffer, " float4 specular : COLOR1;\n");
|
|
for (unsigned int i = 0; i < WINED3D_MAX_FFP_TEXTURES; ++i)
|
|
{
|
|
if (((settings->texgen[i] & 0xffff0000) != WINED3DTSS_TCI_PASSTHRU) || (settings->texcoords & (1u << i)))
|
|
shader_addline(buffer, " float4 texcoord%u : TEXCOORD%u;\n", i, i);
|
|
}
|
|
if (settings->fog_mode == WINED3D_FFP_VS_FOG_DEPTH || settings->fog_mode == WINED3D_FFP_VS_FOG_RANGE)
|
|
shader_addline(buffer, " float fogcoord : FOG;\n");
|
|
shader_addline(buffer, " float point_size : PSIZE;\n");
|
|
shader_addline(buffer, "};\n\n");
|
|
|
|
shader_addline(buffer, "float3 ffp_normalize(float3 n)\n{\n");
|
|
shader_addline(buffer, " float lensq = dot(n, n);\n");
|
|
shader_addline(buffer, " return lensq == 0.0 ? n : (n * rsqrt(lensq));\n");
|
|
shader_addline(buffer, "}\n\n");
|
|
|
|
shader_addline(buffer, "void main(in struct input i, out struct output o)\n");
|
|
shader_addline(buffer, "{\n");
|
|
|
|
shader_addline(buffer, " i.blend_weight[%u] = 1.0;\n", settings->vertexblends);
|
|
|
|
if (settings->transformed)
|
|
{
|
|
shader_addline(buffer, " float4 ec_pos = float4(i.pos.xyz, 1.0);\n");
|
|
/* We reuse the projection matrix to undo the transformation from clip
|
|
* coordinates to pixel coordinates. */
|
|
shader_addline(buffer, " float4 out_pos = mul(c.projection_matrix, ec_pos);\n\n");
|
|
|
|
/* Use a ternary; this is not the most natural way to write it but is
|
|
* nicer to the compiler. */
|
|
shader_addline(buffer, " o.pos = (i.pos.w == 0.0 ? out_pos : out_pos / i.pos.w);\n");
|
|
}
|
|
else
|
|
{
|
|
for (unsigned int i = 0; i < settings->vertexblends; ++i)
|
|
shader_addline(buffer, " i.blend_weight[%u] -= i.blend_weight[%u];\n", settings->vertexblends, i);
|
|
|
|
shader_addline(buffer, " float4 ec_pos = 0.0;\n\n");
|
|
for (unsigned int i = 0; i < settings->vertexblends + 1; ++i)
|
|
shader_addline(buffer, " ec_pos += i.blend_weight[%u] * mul(c.modelview_matrices[%u], float4(i.pos.xyz, 1.0));\n", i, i);
|
|
|
|
shader_addline(buffer, " o.pos = mul(c.projection_matrix, ec_pos);\n");
|
|
shader_addline(buffer, " ec_pos /= ec_pos.w;\n\n");
|
|
}
|
|
|
|
shader_addline(buffer, " float3 normal = 0.0;\n");
|
|
if (settings->normal)
|
|
{
|
|
if (!settings->vertexblends)
|
|
{
|
|
if (settings->transformed)
|
|
shader_addline(buffer, " normal = i.normal;\n");
|
|
else
|
|
shader_addline(buffer, " normal = mul((float3x3)c.modelview_matrices[1], i.normal);\n");
|
|
}
|
|
else
|
|
{
|
|
for (unsigned int i = 0; i < settings->vertexblends + 1; ++i)
|
|
{
|
|
if (settings->transformed)
|
|
shader_addline(buffer, " normal += i.blend_weight[%u] * i.normal;\n", i);
|
|
else
|
|
shader_addline(buffer, " normal += i.blend_weight[%u]"
|
|
" * mul((float3x3)c.modelview_matrices[%u], i.normal);\n", i, i);
|
|
}
|
|
}
|
|
|
|
if (settings->normalize)
|
|
shader_addline(buffer, " normal = ffp_normalize(normal);\n");
|
|
}
|
|
|
|
generate_lighting(buffer, settings, legacy_lighting);
|
|
|
|
string_buffer_init(&texcoord);
|
|
for (unsigned int i = 0; i < WINED3D_MAX_FFP_TEXTURES; ++i)
|
|
{
|
|
string_buffer_clear(&texcoord);
|
|
|
|
switch (settings->texgen[i] & 0xffff0000)
|
|
{
|
|
case WINED3DTSS_TCI_PASSTHRU:
|
|
if (settings->texcoords & (1u << i))
|
|
shader_addline(&texcoord, "i.texcoord[%u]", settings->texgen[i] & 0x0000ffff);
|
|
else
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
FIXME("Unhandled texgen %#x.\n", settings->texgen[i]);
|
|
break;
|
|
}
|
|
|
|
if (settings->transformed)
|
|
shader_addline(buffer, " o.texcoord%u = %s;\n", i, texcoord.buffer);
|
|
else
|
|
shader_addline(buffer, " o.texcoord%u = mul(c.texture_matrices[%u], %s);\n", i, i, texcoord.buffer);
|
|
}
|
|
string_buffer_free(&texcoord);
|
|
|
|
switch (settings->fog_mode)
|
|
{
|
|
case WINED3D_FFP_VS_FOG_OFF:
|
|
break;
|
|
|
|
case WINED3D_FFP_VS_FOG_FOGCOORD:
|
|
/* This is FOGTABLEMODE == NONE, FOGVERTEXMODE == NONE.
|
|
* The specular W is used in that case, even if we output fogcoord,
|
|
* so there's no point in outputting fogcoord. */
|
|
break;
|
|
|
|
case WINED3D_FFP_VS_FOG_RANGE:
|
|
shader_addline(buffer, " o.fogcoord = length(ec_pos.xyz);\n");
|
|
break;
|
|
|
|
case WINED3D_FFP_VS_FOG_DEPTH:
|
|
if (settings->transformed)
|
|
shader_addline(buffer, " o.fogcoord = ec_pos.z;\n");
|
|
else
|
|
shader_addline(buffer, " o.fogcoord = abs(o.pos.z);\n");
|
|
break;
|
|
}
|
|
|
|
shader_addline(buffer, " o.point_size = %s / sqrt(c.point_params.x"
|
|
" + c.point_params.y * length(ec_pos.xyz)"
|
|
" + c.point_params.z * dot(ec_pos.xyz, ec_pos.xyz));\n",
|
|
settings->per_vertex_point_size ? "i.point_size" : "c.point_params.w");
|
|
|
|
shader_addline(buffer, "}\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
static const char *get_fragment_op_arg(struct wined3d_string_buffer *buffer,
|
|
unsigned int argnum, unsigned int stage, unsigned int arg)
|
|
{
|
|
const char *ret;
|
|
|
|
if (arg == ARG_UNUSED)
|
|
return "<unused arg>";
|
|
|
|
switch (arg & WINED3DTA_SELECTMASK)
|
|
{
|
|
case WINED3DTA_DIFFUSE:
|
|
ret = "i.diffuse";
|
|
break;
|
|
|
|
case WINED3DTA_CURRENT:
|
|
ret = "ret";
|
|
break;
|
|
|
|
case WINED3DTA_TEXTURE:
|
|
switch (stage)
|
|
{
|
|
case 0: ret = "tex0"; break;
|
|
case 1: ret = "tex1"; break;
|
|
case 2: ret = "tex2"; break;
|
|
case 3: ret = "tex3"; break;
|
|
case 4: ret = "tex4"; break;
|
|
case 5: ret = "tex5"; break;
|
|
case 6: ret = "tex6"; break;
|
|
case 7: ret = "tex7"; break;
|
|
default:
|
|
ret = "<invalid texture>";
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WINED3DTA_TFACTOR:
|
|
ret = "c.texture_factor";
|
|
break;
|
|
|
|
case WINED3DTA_SPECULAR:
|
|
ret = "i.specular";
|
|
break;
|
|
|
|
case WINED3DTA_TEMP:
|
|
ret = "temp_reg";
|
|
break;
|
|
|
|
case WINED3DTA_CONSTANT:
|
|
switch (stage)
|
|
{
|
|
case 0: ret = "c.texture_constants[0]"; break;
|
|
case 1: ret = "c.texture_constants[1]"; break;
|
|
case 2: ret = "c.texture_constants[2]"; break;
|
|
case 3: ret = "c.texture_constants[3]"; break;
|
|
case 4: ret = "c.texture_constants[4]"; break;
|
|
case 5: ret = "c.texture_constants[5]"; break;
|
|
case 6: ret = "c.texture_constants[6]"; break;
|
|
case 7: ret = "c.texture_constants[7]"; break;
|
|
default:
|
|
ret = "<invalid constant>";
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return "<unhandled arg>";
|
|
}
|
|
|
|
if (arg & WINED3DTA_COMPLEMENT)
|
|
{
|
|
shader_addline(buffer, " arg%u = 1.0 - %s;\n", argnum, ret);
|
|
if (argnum == 0)
|
|
ret = "arg0";
|
|
else if (argnum == 1)
|
|
ret = "arg1";
|
|
else if (argnum == 2)
|
|
ret = "arg2";
|
|
}
|
|
|
|
if (arg & WINED3DTA_ALPHAREPLICATE)
|
|
{
|
|
shader_addline(buffer, " arg%u = %s.w;\n", argnum, ret);
|
|
if (argnum == 0)
|
|
ret = "arg0";
|
|
else if (argnum == 1)
|
|
ret = "arg1";
|
|
else if (argnum == 2)
|
|
ret = "arg2";
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void generate_fragment_op(struct wined3d_string_buffer *buffer, unsigned int stage, bool color,
|
|
bool alpha, bool tmp_dst, unsigned int op, unsigned int dw_arg0, unsigned int dw_arg1, unsigned int dw_arg2)
|
|
{
|
|
const char *dstmask, *dstreg, *arg0, *arg1, *arg2;
|
|
|
|
if (color && alpha)
|
|
dstmask = "";
|
|
else if (color)
|
|
dstmask = ".xyz";
|
|
else
|
|
dstmask = ".w";
|
|
|
|
dstreg = tmp_dst ? "temp_reg" : "ret";
|
|
|
|
arg0 = get_fragment_op_arg(buffer, 0, stage, dw_arg0);
|
|
arg1 = get_fragment_op_arg(buffer, 1, stage, dw_arg1);
|
|
arg2 = get_fragment_op_arg(buffer, 2, stage, dw_arg2);
|
|
|
|
switch (op)
|
|
{
|
|
case WINED3D_TOP_DISABLE:
|
|
break;
|
|
|
|
case WINED3D_TOP_SELECT_ARG1:
|
|
shader_addline(buffer, " %s%s = %s%s;\n", dstreg, dstmask, arg1, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_SELECT_ARG2:
|
|
shader_addline(buffer, " %s%s = %s%s;\n", dstreg, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_MODULATE:
|
|
shader_addline(buffer, " %s%s = %s%s * %s%s;\n", dstreg, dstmask, arg1, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_MODULATE_4X:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s * %s%s * 4.0);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_MODULATE_2X:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s * %s%s * 2.0);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_ADD:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s + %s%s);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_ADD_SIGNED:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s + (%s - 0.5)%s);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_ADD_SIGNED_2X:
|
|
shader_addline(buffer, " %s%s = saturate((%s%s + (%s - 0.5)%s) * 2.0);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_SUBTRACT:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s - %s%s);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_ADD_SMOOTH:
|
|
shader_addline(buffer, " %s%s = saturate((1.0 - %s)%s * %s%s + %s%s);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg1, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_BLEND_DIFFUSE_ALPHA:
|
|
arg0 = get_fragment_op_arg(buffer, 0, stage, WINED3DTA_DIFFUSE);
|
|
shader_addline(buffer, " %s%s = lerp(%s%s, %s%s, %s.w);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0);
|
|
break;
|
|
|
|
case WINED3D_TOP_BLEND_TEXTURE_ALPHA:
|
|
arg0 = get_fragment_op_arg(buffer, 0, stage, WINED3DTA_TEXTURE);
|
|
shader_addline(buffer, " %s%s = lerp(%s%s, %s%s, %s.w);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0);
|
|
break;
|
|
|
|
case WINED3D_TOP_BLEND_FACTOR_ALPHA:
|
|
arg0 = get_fragment_op_arg(buffer, 0, stage, WINED3DTA_TFACTOR);
|
|
shader_addline(buffer, " %s%s = lerp(%s%s, %s%s, %s.w);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0);
|
|
break;
|
|
|
|
case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM:
|
|
arg0 = get_fragment_op_arg(buffer, 0, stage, WINED3DTA_TEXTURE);
|
|
shader_addline(buffer, " %s%s = saturate(%s%s * (1.0 - %s.w) + %s%s);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg0, arg1, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_BLEND_CURRENT_ALPHA:
|
|
arg0 = get_fragment_op_arg(buffer, 0, stage, WINED3DTA_CURRENT);
|
|
shader_addline(buffer, " %s%s = lerp(%s%s, %s%s, %s.w);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0);
|
|
break;
|
|
|
|
case WINED3D_TOP_MODULATE_ALPHA_ADD_COLOR:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s * %s.w + %s%s);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg1, arg1, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_MODULATE_COLOR_ADD_ALPHA:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s * %s%s + %s.w);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg1);
|
|
break;
|
|
|
|
case WINED3D_TOP_MODULATE_INVALPHA_ADD_COLOR:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s * (1.0 - %s.w) + %s%s);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg1, arg1, dstmask);
|
|
break;
|
|
case WINED3D_TOP_MODULATE_INVCOLOR_ADD_ALPHA:
|
|
shader_addline(buffer, " %s%s = saturate((1.0 - %s)%s * %s%s + %s.w);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg1);
|
|
break;
|
|
|
|
case WINED3D_TOP_BUMPENVMAP:
|
|
case WINED3D_TOP_BUMPENVMAP_LUMINANCE:
|
|
/* These are handled in the first pass, nothing to do. */
|
|
break;
|
|
|
|
case WINED3D_TOP_DOTPRODUCT3:
|
|
shader_addline(buffer, " %s%s = saturate(dot(%s.xyz - 0.5, %s.xyz - 0.5) * 4.0);\n",
|
|
dstreg, dstmask, arg1, arg2);
|
|
break;
|
|
|
|
case WINED3D_TOP_MULTIPLY_ADD:
|
|
shader_addline(buffer, " %s%s = saturate(%s%s * %s%s + %s%s);\n",
|
|
dstreg, dstmask, arg1, dstmask, arg2, dstmask, arg0, dstmask);
|
|
break;
|
|
|
|
case WINED3D_TOP_LERP:
|
|
/* MSDN isn't quite right here. */
|
|
shader_addline(buffer, " %s%s = lerp(%s%s, %s%s, %s%s);\n",
|
|
dstreg, dstmask, arg2, dstmask, arg1, dstmask, arg0, dstmask);
|
|
break;
|
|
|
|
default:
|
|
FIXME("Unhandled operation %#x.\n", op);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool ffp_hlsl_generate_pixel_shader(const struct ffp_frag_settings *settings,
|
|
struct wined3d_string_buffer *buffer)
|
|
{
|
|
uint8_t tex_map = 0;
|
|
unsigned int i;
|
|
|
|
/* Find out which textures are read. */
|
|
for (i = 0; i < WINED3D_MAX_FFP_TEXTURES; ++i)
|
|
{
|
|
unsigned int arg0, arg1, arg2;
|
|
|
|
if (settings->op[i].cop == WINED3D_TOP_DISABLE)
|
|
break;
|
|
|
|
arg0 = settings->op[i].carg0 & WINED3DTA_SELECTMASK;
|
|
arg1 = settings->op[i].carg1 & WINED3DTA_SELECTMASK;
|
|
arg2 = settings->op[i].carg2 & WINED3DTA_SELECTMASK;
|
|
|
|
if (arg0 == WINED3DTA_TEXTURE || arg1 == WINED3DTA_TEXTURE || arg2 == WINED3DTA_TEXTURE
|
|
|| (i == 0 && settings->color_key_enabled))
|
|
tex_map |= 1u << i;
|
|
|
|
switch (settings->op[i].cop)
|
|
{
|
|
case WINED3D_TOP_BUMPENVMAP_LUMINANCE:
|
|
case WINED3D_TOP_BUMPENVMAP:
|
|
case WINED3D_TOP_BLEND_TEXTURE_ALPHA:
|
|
case WINED3D_TOP_BLEND_TEXTURE_ALPHA_PM:
|
|
tex_map |= 1u << i;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (settings->op[i].aop == WINED3D_TOP_DISABLE)
|
|
continue;
|
|
|
|
arg0 = settings->op[i].aarg0 & WINED3DTA_SELECTMASK;
|
|
arg1 = settings->op[i].aarg1 & WINED3DTA_SELECTMASK;
|
|
arg2 = settings->op[i].aarg2 & WINED3DTA_SELECTMASK;
|
|
|
|
if (arg0 == WINED3DTA_TEXTURE || arg1 == WINED3DTA_TEXTURE || arg2 == WINED3DTA_TEXTURE)
|
|
tex_map |= 1u << i;
|
|
}
|
|
|
|
for (i = 0; i < WINED3D_MAX_FFP_TEXTURES; ++i)
|
|
{
|
|
const char *sampler_type;
|
|
|
|
if (!(tex_map & (1u << i)))
|
|
continue;
|
|
|
|
switch (settings->op[i].tex_type)
|
|
{
|
|
case WINED3D_GL_RES_TYPE_TEX_1D:
|
|
sampler_type = "1D";
|
|
break;
|
|
case WINED3D_GL_RES_TYPE_TEX_2D:
|
|
sampler_type = "2D";
|
|
break;
|
|
case WINED3D_GL_RES_TYPE_TEX_3D:
|
|
sampler_type = "3D";
|
|
break;
|
|
case WINED3D_GL_RES_TYPE_TEX_CUBE:
|
|
sampler_type = "CUBE";
|
|
break;
|
|
default:
|
|
FIXME("Unhandled sampler type %#x.\n", settings->op[i].tex_type);
|
|
sampler_type = NULL;
|
|
break;
|
|
}
|
|
if (sampler_type)
|
|
shader_addline(buffer, "sampler%s ps_sampler%u : register(s%u);\n", sampler_type, i, i);
|
|
|
|
shader_addline(buffer, "static float4 tex%u;\n", i);
|
|
}
|
|
|
|
/* This must be kept in sync with struct wined3d_ffp_vs_constants. */
|
|
shader_addline(buffer, "uniform struct\n");
|
|
shader_addline(buffer, "{\n");
|
|
shader_addline(buffer, " float4 texture_constants[%u];\n", WINED3D_MAX_FFP_TEXTURES);
|
|
shader_addline(buffer, " float4 texture_factor;\n");
|
|
shader_addline(buffer, " float4 specular_enable;\n");
|
|
shader_addline(buffer, " float4 color_key[2];\n");
|
|
/* Bumpenv constants are manually packed. */
|
|
shader_addline(buffer, " float4 bumpenv_matrices[%u];\n", WINED3D_MAX_FFP_TEXTURES);
|
|
shader_addline(buffer, " float4 bumpenv_lscale[2];\n");
|
|
shader_addline(buffer, " float4 bumpenv_loffset[2];\n");
|
|
shader_addline(buffer, "} c;\n");
|
|
|
|
shader_addline(buffer, "struct input\n");
|
|
shader_addline(buffer, "{\n");
|
|
shader_addline(buffer, " float4 pos : POSITION;\n");
|
|
shader_addline(buffer, " float4 diffuse : COLOR0;\n");
|
|
shader_addline(buffer, " float4 specular : COLOR1;\n");
|
|
shader_addline(buffer, " float4 texcoord[%u] : TEXCOORD;\n", WINED3D_MAX_FFP_TEXTURES);
|
|
shader_addline(buffer, "};\n\n");
|
|
|
|
shader_addline(buffer, "struct output\n");
|
|
shader_addline(buffer, "{\n");
|
|
shader_addline(buffer, " float4 colour : COLOR;\n");
|
|
shader_addline(buffer, "};\n\n");
|
|
|
|
shader_addline(buffer, "void main(in struct input i, out struct output o)\n");
|
|
shader_addline(buffer, "{\n");
|
|
|
|
shader_addline(buffer, " float4 texcoord[%u];\n", WINED3D_MAX_FFP_TEXTURES);
|
|
shader_addline(buffer, " float4 temp_reg = 0.0;\n\n");
|
|
shader_addline(buffer, " float4 ret, arg0, arg1, arg2;\n\n");
|
|
|
|
for (i = 0; i < WINED3D_MAX_FFP_TEXTURES; ++i)
|
|
{
|
|
if (tex_map & (1u << i))
|
|
{
|
|
if (settings->pointsprite || (settings->texcoords_initialized & (1u << i)))
|
|
shader_addline(buffer, " texcoord[%u] = i.texcoord[%u];\n", i, i);
|
|
else
|
|
shader_addline(buffer, " texcoord[%u] = 0.0;\n", i);
|
|
}
|
|
}
|
|
|
|
/* Texture sampling. */
|
|
|
|
for (i = 0; i < WINED3D_MAX_FFP_TEXTURES && settings->op[i].cop != WINED3D_TOP_DISABLE; ++i)
|
|
{
|
|
const char *texture_function, *coord_mask;
|
|
bool proj = settings->op[i].projected;
|
|
|
|
if (!(tex_map & (1u << i)))
|
|
continue;
|
|
|
|
switch (settings->op[i].tex_type)
|
|
{
|
|
case WINED3D_GL_RES_TYPE_TEX_1D:
|
|
texture_function = "tex1D";
|
|
coord_mask = "x";
|
|
break;
|
|
case WINED3D_GL_RES_TYPE_TEX_2D:
|
|
texture_function = "tex2D";
|
|
coord_mask = "xy";
|
|
break;
|
|
case WINED3D_GL_RES_TYPE_TEX_3D:
|
|
texture_function = "tex3D";
|
|
coord_mask = "xyz";
|
|
break;
|
|
case WINED3D_GL_RES_TYPE_TEX_CUBE:
|
|
texture_function = "texCUBE";
|
|
coord_mask = "xyz";
|
|
break;
|
|
default:
|
|
FIXME("Unhandled texture type %#x.\n", settings->op[i].tex_type);
|
|
texture_function = "";
|
|
coord_mask = "xyzw";
|
|
proj = false;
|
|
break;
|
|
}
|
|
|
|
if (proj)
|
|
coord_mask = "xyzw";
|
|
|
|
if (i > 0
|
|
&& (settings->op[i - 1].cop == WINED3D_TOP_BUMPENVMAP
|
|
|| settings->op[i - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE))
|
|
{
|
|
shader_addline(buffer, "ret.xy = mul(transpose((float2x2)c.bumpenv_matrices[%u]), tex%u.xy);\n", i - 1, i - 1);
|
|
|
|
/* With projective textures, texbem only divides the static texture
|
|
* coordinate, not the displacement, so multiply the displacement
|
|
* with the dividing parameter before sampling. */
|
|
if (settings->op[i].projected)
|
|
{
|
|
shader_addline(buffer, "ret.xy = (ret.xy * texcoord[%u].w) + texcoord[%u].xy;\n", i, i);
|
|
shader_addline(buffer, "ret.zw = texcoord[%u].ww;\n", i);
|
|
}
|
|
else
|
|
{
|
|
shader_addline(buffer, "ret = texcoord[%u] + ret.xyxy;\n", i);
|
|
}
|
|
|
|
shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ret.%s);\n",
|
|
i, texture_function, proj ? "proj" : "", i, coord_mask);
|
|
|
|
if (settings->op[i - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE)
|
|
{
|
|
shader_addline(buffer, "tex%u *= saturate(tex%u.z * c.bumpenv_lscale[%u][%u] + c.bumpenv_loffset[%u][%u]);\n",
|
|
i, i - 1, (i - 1) / 4, (i - 1) % 4, (i - 1) / 4, (i - 1) % 4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shader_addline(buffer, " tex%u = %s%s(ps_sampler%u, texcoord[%u].%s);\n",
|
|
i, texture_function, proj ? "proj" : "", i, i, coord_mask);
|
|
}
|
|
}
|
|
|
|
if (settings->color_key_enabled)
|
|
{
|
|
shader_addline(buffer, " if (all(tex0 >= c.color_key[0]) && all(tex0 < c.color_key[1]))\n");
|
|
shader_addline(buffer, " discard;\n");
|
|
}
|
|
|
|
shader_addline(buffer, " ret = i.diffuse;\n");
|
|
|
|
for (i = 0; i < WINED3D_MAX_FFP_TEXTURES; ++i)
|
|
{
|
|
bool op_equal;
|
|
|
|
if (settings->op[i].cop == WINED3D_TOP_DISABLE)
|
|
break;
|
|
|
|
if (settings->op[i].cop == WINED3D_TOP_SELECT_ARG1
|
|
&& settings->op[i].aop == WINED3D_TOP_SELECT_ARG1)
|
|
op_equal = settings->op[i].carg1 == settings->op[i].aarg1;
|
|
else if (settings->op[i].cop == WINED3D_TOP_SELECT_ARG1
|
|
&& settings->op[i].aop == WINED3D_TOP_SELECT_ARG2)
|
|
op_equal = settings->op[i].carg1 == settings->op[i].aarg2;
|
|
else if (settings->op[i].cop == WINED3D_TOP_SELECT_ARG2
|
|
&& settings->op[i].aop == WINED3D_TOP_SELECT_ARG1)
|
|
op_equal = settings->op[i].carg2 == settings->op[i].aarg1;
|
|
else if (settings->op[i].cop == WINED3D_TOP_SELECT_ARG2
|
|
&& settings->op[i].aop == WINED3D_TOP_SELECT_ARG2)
|
|
op_equal = settings->op[i].carg2 == settings->op[i].aarg2;
|
|
else
|
|
op_equal = settings->op[i].aop == settings->op[i].cop
|
|
&& settings->op[i].carg0 == settings->op[i].aarg0
|
|
&& settings->op[i].carg1 == settings->op[i].aarg1
|
|
&& settings->op[i].carg2 == settings->op[i].aarg2;
|
|
|
|
if (settings->op[i].aop == WINED3D_TOP_DISABLE)
|
|
{
|
|
generate_fragment_op(buffer, i, true, false, settings->op[i].tmp_dst,
|
|
settings->op[i].cop, settings->op[i].carg0,
|
|
settings->op[i].carg1, settings->op[i].carg2);
|
|
}
|
|
else if (op_equal)
|
|
{
|
|
generate_fragment_op(buffer, i, true, true, settings->op[i].tmp_dst,
|
|
settings->op[i].cop, settings->op[i].carg0,
|
|
settings->op[i].carg1, settings->op[i].carg2);
|
|
}
|
|
else if (settings->op[i].cop != WINED3D_TOP_BUMPENVMAP
|
|
&& settings->op[i].cop != WINED3D_TOP_BUMPENVMAP_LUMINANCE)
|
|
{
|
|
generate_fragment_op(buffer, i, true, false, settings->op[i].tmp_dst,
|
|
settings->op[i].cop, settings->op[i].carg0,
|
|
settings->op[i].carg1, settings->op[i].carg2);
|
|
generate_fragment_op(buffer, i, false, true, settings->op[i].tmp_dst,
|
|
settings->op[i].aop, settings->op[i].aarg0,
|
|
settings->op[i].aarg1, settings->op[i].aarg2);
|
|
}
|
|
}
|
|
|
|
shader_addline(buffer, " o.colour = i.specular * c.specular_enable + ret;\n");
|
|
|
|
shader_addline(buffer, "}\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool compile_hlsl_shader(const struct wined3d_string_buffer *hlsl,
|
|
struct vkd3d_shader_code *sm1, const char *profile)
|
|
{
|
|
struct vkd3d_shader_hlsl_source_info hlsl_source_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_HLSL_SOURCE_INFO};
|
|
struct vkd3d_shader_compile_info compile_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO};
|
|
char *messages;
|
|
int ret;
|
|
|
|
compile_info.source.code = hlsl->buffer;
|
|
compile_info.source.size = hlsl->content_size;
|
|
compile_info.source_type = VKD3D_SHADER_SOURCE_HLSL;
|
|
compile_info.target_type = VKD3D_SHADER_TARGET_D3D_BYTECODE;
|
|
compile_info.log_level = VKD3D_SHADER_LOG_WARNING;
|
|
|
|
compile_info.next = &hlsl_source_info;
|
|
hlsl_source_info.profile = profile;
|
|
|
|
ret = vkd3d_shader_compile(&compile_info, sm1, &messages);
|
|
if (messages && *messages && FIXME_ON(d3d_shader))
|
|
{
|
|
const char *ptr, *end, *line;
|
|
|
|
FIXME("Shader log:\n");
|
|
ptr = messages;
|
|
end = ptr + strlen(ptr);
|
|
while ((line = wined3d_get_line(&ptr, end)))
|
|
FIXME(" %.*s", (int)(ptr - line), line);
|
|
FIXME("\n");
|
|
}
|
|
vkd3d_shader_free_messages(messages);
|
|
|
|
if (ret < 0)
|
|
{
|
|
ERR("Failed to compile HLSL, ret %d.\n", ret);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ffp_hlsl_compile_vs(const struct wined3d_ffp_vs_settings *settings,
|
|
struct wined3d_shader_desc *shader_desc, struct wined3d_device *device)
|
|
{
|
|
struct wined3d_string_buffer string;
|
|
struct vkd3d_shader_code sm1;
|
|
|
|
if (!string_buffer_init(&string))
|
|
return false;
|
|
|
|
if (!ffp_hlsl_generate_vertex_shader(settings, &string, device->wined3d->flags & WINED3D_LEGACY_FFP_LIGHTING))
|
|
{
|
|
string_buffer_free(&string);
|
|
return false;
|
|
}
|
|
|
|
if (!compile_hlsl_shader(&string, &sm1, "vs_2_a"))
|
|
{
|
|
string_buffer_free(&string);
|
|
return false;
|
|
}
|
|
string_buffer_free(&string);
|
|
|
|
shader_desc->byte_code = sm1.code;
|
|
shader_desc->byte_code_size = ~(size_t)0;
|
|
return true;
|
|
}
|
|
|
|
bool ffp_hlsl_compile_ps(const struct ffp_frag_settings *settings, struct wined3d_shader_desc *shader_desc)
|
|
{
|
|
struct wined3d_string_buffer string;
|
|
struct vkd3d_shader_code sm1;
|
|
|
|
if (!string_buffer_init(&string))
|
|
return false;
|
|
|
|
if (!ffp_hlsl_generate_pixel_shader(settings, &string))
|
|
{
|
|
string_buffer_free(&string);
|
|
return false;
|
|
}
|
|
|
|
if (!compile_hlsl_shader(&string, &sm1, "ps_2_a"))
|
|
{
|
|
string_buffer_free(&string);
|
|
return false;
|
|
}
|
|
string_buffer_free(&string);
|
|
|
|
shader_desc->byte_code = sm1.code;
|
|
shader_desc->byte_code_size = ~(size_t)0;
|
|
return true;
|
|
}
|