cancel
Showing results for 
Search instead for 
Did you mean: 

Archives Discussions

GLSL tessellation shader does not want link

Vertex, control, evaluation and fragment shader objects compiled succussfuly, but program does not want link, it log containe only four "unexpected error."

 Catalist 11.2 WinXP SP3 Radeon HD5450

// Vertex shader

#version 400 compatibility

#define gl_Tangent gl_MultiTexCoord1.xyz
#define gl_Binormal gl_MultiTexCoord2.xyz

out vec3 tcWorldPos;
out vec3 tcNormal;
out float tcVertexDistanceFactor;
out vec2 tcTexCoord;
out vec3 tcLightTS;
out vec3 tcViewTS;

uniform MainConstants {
mat4 WorldMatrix; // World matrix
mat4 ViewMatrix; // View matrix
mat4 ProjectionMatrix; // Projection matrix
mat4 WorldViewProjectionMatrix; // WVP matrix
mat4 ViewProjectionMatrix; // VP matrix
mat4 InvViewMatrix; // Inverse of view matrix
vec4 ScreenResolution; // Screen resolution

vec4 MeshColor; // Mesh color
vec4 TessellationFactor; // Edge, inside, minimum tessellation factor and 1/desired triangle size
vec4 DetailTessellationHeightScale; // Height scale for detail tessellation of grid surface
vec4 GridSize; // Grid size
};

uniform Material {
vec4 MaterialAmbientColor; // Material's ambient color
vec4 MaterialDiffuseColor; // Material's diffuse color
vec4 MaterialSpecularColor; // Material's specular color
vec4 SpecularExponent; // Material's specular exponent

vec4 LightPosition; // Light's position in world space
vec4 LightDiffuse; // Light's diffuse color
vec4 LightAmbient; // Light's ambient color

vec4 Eye; // Camera's location
vec4 BaseTextureRepeat; // The tiling factor for base and normal map textures
vec4 POMHeightMapScale; // Describes the useful range of values for the height field

vec4 ShadowSoftening; // Blurring factor for the soft shadows computation

int MinSamples; // The minimum number of samples for sampling the height field profile
int MaxSamples; // The maximum number of samples for sampling the height field profile
};

void main()
{
// Compute position in world space
vec4 vPositionWS = WorldMatrix * gl_Vertex;

// Compute denormalized light vector in world space
vec3 vLightWS = LightPosition.xyz - vPositionWS.xyz;
// Need to invert Z for correct lighting
vLightWS.z = -vLightWS.z;

// Propagate texture coordinate through:
tcTexCoord = gl_MultiTexCoord0.xy * BaseTextureRepeat.x;

// Transform normal, tangent and binormal vectors from object
// space to homogeneous projection space and normalize them
vec3 vNormalWS = mat3(WorldMatrix) * gl_Normal;
vec3 vTangentWS = mat3(WorldMatrix) * gl_Tangent;
vec3 vBinormalWS = mat3(WorldMatrix) * gl_Binormal;

// Normalize tangent space vectors
vNormalWS = normalize( vNormalWS );
vTangentWS = normalize( vTangentWS );
vBinormalWS = normalize( vBinormalWS );

// Output normal
tcNormal = vNormalWS;

// Calculate tangent basis
mat3 WorldToTangent = mat3( vTangentWS, vBinormalWS, vNormalWS );

// Propagate the light vector (in tangent space)
tcLightTS = vLightWS * WorldToTangent;

// Compute and output the world view vector (unnormalized)
vec3 vViewWS = Eye.xyz - vPositionWS.xyz;
tcViewTS = vViewWS * WorldToTangent;

// Write world position
tcWorldPos = vec3( vPositionWS.xyz );

// Min and max distance should be chosen according to scene quality requirements
const float MinDistance = 20.0;
const float MaxDistance = 250.0;

// Calculate distance between vertex and camera, and a vertex distance factor issued from it
float Distance = distance( vPositionWS.xyz, Eye.xyz );
tcVertexDistanceFactor = 1.0 - clamp( ( ( Distance - MinDistance ) / ( MaxDistance - MinDistance ) ),
0.0, 1.0 - TessellationFactor.z/TessellationFactor.x);
}

// Control shader

#version 400
layout(vertices = 3) out;

in vec3 tcWorldPos[];
in vec3 tcNormal[];
in float tcVertexDistanceFactor[];
in vec2 tcTexCoord[];
in vec3 tcLightTS[];
in vec3 tcViewTS[];

out vec3 teWorldPos[];
out vec3 teNormal[];
out float teVertexDistanceFactor[];
out vec2 teTexCoord[];
out vec3 teLightTS[];
out vec3 teViewTS[];

uniform MainConstants {
mat4 WorldMatrix; // World matrix
mat4 ViewMatrix; // View matrix
mat4 ProjectionMatrix; // Projection matrix
mat4 WorldViewProjectionMatrix; // WVP matrix
mat4 ViewProjectionMatrix; // VP matrix
mat4 InvViewMatrix; // Inverse of view matrix
vec4 ScreenResolution; // Screen resolution

vec4 MeshColor; // Mesh color
vec4 TessellationFactor; // Edge, inside, minimum tessellation factor and 1/desired triangle size
vec4 DetailTessellationHeightScale; // Height scale for detail tessellation of grid surface
vec4 GridSize; // Grid size
};

void main()
{
// Copy inputs to outputs
teWorldPos[gl_InvocationID] = tcWorldPos[gl_InvocationID];
teNormal[gl_InvocationID] = tcNormal[gl_InvocationID];
teVertexDistanceFactor[gl_InvocationID] = tcVertexDistanceFactor[gl_InvocationID];
teTexCoord[gl_InvocationID] = tcTexCoord[gl_InvocationID];
teLightTS[gl_InvocationID] = tcLightTS[gl_InvocationID];
teViewTS[gl_InvocationID] = tcViewTS[gl_InvocationID];

vec4 vEdgeTessellationFactors = TessellationFactor.xxxy;

// Calculate edge scale factor from vertex scale factor: simply compute
// average tess factor between the two vertices making up an edge
vEdgeTessellationFactors.x = 0.5 * ( tcVertexDistanceFactor[1] + tcVertexDistanceFactor[2] );
vEdgeTessellationFactors.y = 0.5 * ( tcVertexDistanceFactor[2] + tcVertexDistanceFactor[0] );
vEdgeTessellationFactors.z = 0.5 * ( tcVertexDistanceFactor[0] + tcVertexDistanceFactor[1] );
vEdgeTessellationFactors.w = vEdgeTessellationFactors.x;

// Multiply them by global tessellation factor
vEdgeTessellationFactors *= TessellationFactor.xxxy;

// Assign tessellation levels
gl_TessLevelOuter[0] = vEdgeTessellationFactors.x;
gl_TessLevelOuter[1] = vEdgeTessellationFactors.y;
gl_TessLevelOuter[2] = vEdgeTessellationFactors.z;
gl_TessLevelInner[0] = vEdgeTessellationFactors.w;
}

// Evaluation shader

#version 400
layout(triangles, fractional_odd_spacing, cw) in;

in vec3 tcWorldPos[];
in vec3 tcNormal[];
in float tcVertexDistanceFactor[];
in vec2 tcTexCoord[];
in vec3 tcLightTS[];
in vec3 tcViewTS[];

out vec2 fTexCoord;
out vec3 fLightTS;
out vec3 fViewTS;

uniform MainConstants {
mat4 WorldMatrix; // World matrix
mat4 ViewMatrix; // View matrix
mat4 ProjectionMatrix; // Projection matrix
mat4 WorldViewProjectionMatrix; // WVP matrix
mat4 ViewProjectionMatrix; // VP matrix
mat4 InvViewMatrix; // Inverse of view matrix
vec4 ScreenResolution; // Screen resolution

vec4 MeshColor; // Mesh color
vec4 TessellationFactor; // Edge, inside, minimum tessellation factor and 1/desired triangle size
vec4 DetailTessellationHeightScale; // Height scale for detail tessellation of grid surface
vec4 GridSize; // Grid size
};

uniform Material {
vec4 MaterialAmbientColor; // Material's ambient color
vec4 MaterialDiffuseColor; // Material's diffuse color
vec4 MaterialSpecularColor; // Material's specular color
vec4 SpecularExponent; // Material's specular exponent

vec4 LightPosition; // Light's position in world space
vec4 LightDiffuse; // Light's diffuse color
vec4 LightAmbient; // Light's ambient color

vec4 Eye; // Camera's location
vec4 BaseTextureRepeat; // The tiling factor for base and normal map textures
vec4 POMHeightMapScale; // Describes the useful range of values for the height field

vec4 ShadowSoftening; // Blurring factor for the soft shadows computation

int MinSamples; // The minimum number of samples for sampling the height field profile
int MaxSamples; // The maximum number of samples for sampling the height field profile
};

uniform sampler2D HeightTexture; // Normal map and height map texture pair

void main()
{
// Interpolate world space position with barycentric coordinates
vec3 vWorldPos = gl_TessCoord.x * tcWorldPos[0] +
gl_TessCoord.y * tcWorldPos[1] +
gl_TessCoord.z * tcWorldPos[2];

// Interpolate world space normal and renormalize it
vec3 vNormal = gl_TessCoord.x * tcNormal[0] +
gl_TessCoord.y * tcNormal[1] +
gl_TessCoord.z * tcNormal[2];
vNormal = normalize( vNormal );

// Interpolate other inputs with barycentric coordinates
fTexCoord = gl_TessCoord.x * tcTexCoord[0] +
gl_TessCoord.y * tcTexCoord[1] +
gl_TessCoord.z * tcTexCoord[2];
vec3 vLightTS = gl_TessCoord.x * tcLightTS[0] +
gl_TessCoord.y * tcLightTS[1] +
gl_TessCoord.z * tcLightTS[2];

// Calculate MIP level to fetch normal from
float HeightMapMIPLevel = clamp( ( distance( vWorldPos, Eye.xyz ) - 100.0 ) / 100.0, 0.0, 3.0);

// Sample normal and height map
vec4 vNormalHeight = textureLod(HeightTexture, fTexCoord, HeightMapMIPLevel );

// Displace vertex along normal
vWorldPos += vNormal * ( DetailTessellationHeightScale.x * ( vNormalHeight.w-1.0 ) );

// Transform world position with viewprojection matrix
gl_Position = ViewProjectionMatrix * vec4( vWorldPos.xyz, 1.0 );

// Per-pixel lighting: pass tangent space light vector to pixel shader
fLightTS = vLightTS;

// Also pass tangent space view vector
fViewTS = gl_TessCoord.x * tcViewTS[0] +
gl_TessCoord.y * tcViewTS[1] +
gl_TessCoord.z * tcViewTS[2];
}

// Fragment shader

#version 400
in vec2 fTexCoord;
in vec3 fLightTS;
in vec3 fViewTS;
out vec4 FragColor;

uniform Material {
vec4 MaterialAmbientColor; // Material's ambient color
vec4 MaterialDiffuseColor; // Material's diffuse color
vec4 MaterialSpecularColor; // Material's specular color
vec4 SpecularExponent; // Material's specular exponent

vec4 LightPosition; // Light's position in world space
vec4 LightDiffuse; // Light's diffuse color
vec4 LightAmbient; // Light's ambient color

vec4 Eye; // Camera's location
vec4 BaseTextureRepeat; // The tiling factor for base and normal map textures
vec4 POMHeightMapScale; // Describes the useful range of values for the height field

vec4 ShadowSoftening; // Blurring factor for the soft shadows computation

int MinSamples; // The minimum number of samples for sampling the height field profile
int MaxSamples; // The maximum number of samples for sampling the height field profile
};

uniform sampler2D ColorTexture; // Base color texture
uniform sampler2D HeightTexture; // Normal map and height map texture pair

//--------------------------------------------------------------------------------------
// Function: ComputeIllumination
//
// Description: Computes phong illumination for the given pixel using its attribute
// textures and a light vector.
//--------------------------------------------------------------------------------------
vec4 ComputeIllumination( vec2 texCoord, vec3 vLightTS, vec3 vViewTS )
{
// Sample the normal from the normal map for the given texture sample:
vec3 vNormalTS = normalize( texture(HeightTexture, texCoord).xyz * 2.0 - 1.0 );

// Sample base map
vec4 cBaseColor = texture( ColorTexture, texCoord );

// Compute diffuse color component:
vec4 cDiffuse = clamp( dot( vNormalTS, vLightTS ), 0.0, 1.0 ) * MaterialDiffuseColor;

// Compute the specular component if desired:
vec4 cSpecular = vec4(0.0);

vec3 vReflectionTS = normalize( 2 * dot( vViewTS, vNormalTS ) * vNormalTS - vViewTS );
float fRdotL = clamp( dot( vReflectionTS, vLightTS ), 0.0, 1.0);
cSpecular = pow( fRdotL, SpecularExponent.x ) * MaterialSpecularColor;

// Composite the final color:
vec4 cFinalColor = ( MaterialAmbientColor + cDiffuse ) * cBaseColor + cSpecular;

return cFinalColor;
}

void main()
{
// Normalize tangent space light vector
vec3 vLightTS = normalize( fLightTS );

// Normalize tangent space view vector
vec3 vViewTS = normalize( fViewTS );

// Compute resulting color for the pixel:
FragColor = ComputeIllumination( fTexCoord, vLightTS, vViewTS );
}

 

0 Likes
5 Replies
frali
Staff

It's a bug in the driver. We will fix it soon. 

Thanks for your feedback
Frank 

0 Likes

Thanks, we will waiting (I guess half year ).

Is there a way to avoid such error, and what cause of this?

0 Likes

Next program does not want link too.

Log containe

Tessellation control shader(s) failed to link, Vertex shader(s) linked, fragment shader(s) linked, tessellation evaluation shader(s) linked.
Hull Shader not supported by HW.

// Vertex shader

#version 400 compatibility

uniform mat4 ModelMatrix;
uniform mat3 NormalMatrix;

out vec3 v2c_Position;
out vec3 v2c_Normal;
out vec3 v2c_TexCoord;

void main()
{
// Pass through world space position
v2c_Position = (ModelMatrix * gl_Vertex).xyz;

// Pass through normalized world space normal
v2c_Normal = NormalMatrix * gl_Normal;

// Pass through texture coordinates
v2c_TexCoord = gl_MultiTexCoord0.xyz;
}

// Control shader 

#version 400
layout(vertices = 3) out;

in vec3 v2c_Position[];
in vec3 v2c_Normal[];
in vec3 v2c_TexCoord[];
out vec3 c2e_Position[];
out vec3 c2e_Normal[];
out vec3 c2e_TexCoord[];

// Geometry cubic generated control points
patch out vec3 B210;
patch out vec3 B120;
patch out vec3 B021;
patch out vec3 B012;
patch out vec3 B102;
patch out vec3 B201;
patch out vec3 B111;

// Normal quadratic generated control points
patch out vec3 N110;
patch out vec3 N011;
patch out vec3 N101;

uniform vec4 TessFactors; // Tessellation factors ( x=Edge, y=Inside, z=MinDistance, w=Range )
uniform vec4 ViewVector; // View Vector
uniform float SilhouetteEpsilon;

//--------------------------------------------------------------------------------------
// Returns the dot product between the viewing vector and the patch edge
//--------------------------------------------------------------------------------------
float GetEdgeDotProduct (
vec3 EdgeNormal0, // Normalized normal of the first control point of the given patch edge
vec3 EdgeNormal1, // Normalized normal of the second control point of the given patch edge
vec3 ViewVector // Normalized viewing vector
)
{
vec3 EdgeNormal = normalize( ( EdgeNormal0 + EdgeNormal1 ) * 0.5 );

float EdgeDotProduct = dot( EdgeNormal, ViewVector );

return EdgeDotProduct;
}

//--------------------------------------------------------------------------------------
// Returns the orientation adaptive tessellation factor (0.0 -> 1.0)
//--------------------------------------------------------------------------------------
float GetOrientationAdaptiveScaleFactor (
float EdgeDotProduct, // Dot product of edge normal with view vector
float SilhouetteEpsilon // Epsilon to determine the range of values considered to be silhoutte
)
{
float Scale = 1.0 - abs( EdgeDotProduct );

Scale = clamp( ( Scale - SilhouetteEpsilon ) / ( 1.0 - SilhouetteEpsilon ), 0.0, 1.0 );

return Scale;
}

void main()
{
int id = gl_InvocationID;
v2c_Position[id] = c2e_Position[id];
v2c_Normal[id] = c2e_Normal[id];
v2c_TexCoord[id] = c2e_TexCoord[id];

// Use the tessellation factors as defined in constant space
gl_TessLevelOuter[0] = TessFactors.x;
gl_TessLevelOuter[1] = TessFactors.x;
gl_TessLevelOuter[2] = TessFactors.x;


// Orientation adaptive tessellation
float fAdaptiveScaleFactor;
float fEdgeDot[3];

// Aquire patch edge dot product
// between patch edge normal and view vector
fEdgeDot[0] = GetEdgeDotProduct( v2c_Normal[2], v2c_Normal[0], ViewVector.xyz );
fEdgeDot[1] = GetEdgeDotProduct( v2c_Normal[0], v2c_Normal[1], ViewVector.xyz );
fEdgeDot[2] = GetEdgeDotProduct( v2c_Normal[1], v2c_Normal[2], ViewVector.xyz );


// Scale the tessellation factors based on patch orientation with respect to the viewing
// vector
// Edge 0
fAdaptiveScaleFactor = GetOrientationAdaptiveScaleFactor( fEdgeDot[0], SilhouetteEpsilon );
float fTessFactor0 = mix( 1.0, TessFactors.x, fAdaptiveScaleFactor );
// Edge 1
fAdaptiveScaleFactor = GetOrientationAdaptiveScaleFactor( fEdgeDot[1], SilhouetteEpsilon );
float fTessFactor1 = mix( 1.0, TessFactors.x, fAdaptiveScaleFactor );
// Edge 2
fAdaptiveScaleFactor = GetOrientationAdaptiveScaleFactor( fEdgeDot[2], SilhouetteEpsilon );
float fTessFactor2 = mix( 1.0, TessFactors.x, fAdaptiveScaleFactor );

gl_TessLevelOuter[0] = fTessFactor0;
gl_TessLevelOuter[1] = fTessFactor1;
gl_TessLevelOuter[2] = fTessFactor2;

// Now setup the PNTriangle control points...

// Assign Positions
vec3 B003 = v2c_Position[0];
vec3 B030 = v2c_Position[1];
vec3 B300 = v2c_Position[2];
// And Normals
vec3 N002 = v2c_Normal[0];
vec3 N020 = v2c_Normal[1];
vec3 N200 = v2c_Normal[2];

// Compute the cubic geometry control points
// Edge control points
B210 = ( ( 2.0 * B003 ) + B030 - ( dot( ( B030 - B003 ), N002 ) * N002 ) ) / 3.0;
B120 = ( ( 2.0 * B030 ) + B003 - ( dot( ( B003 - B030 ), N020 ) * N020 ) ) / 3.0;
B021 = ( ( 2.0 * B030 ) + B300 - ( dot( ( B300 - B030 ), N020 ) * N020 ) ) / 3.0;
B012 = ( ( 2.0 * B300 ) + B030 - ( dot( ( B030 - B300 ), N200 ) * N200 ) ) / 3.0;
B102 = ( ( 2.0 * B300 ) + B003 - ( dot( ( B003 - B300 ), N200 ) * N200 ) ) / 3.0;
B201 = ( ( 2.0 * B003 ) + B300 - ( dot( ( B300 - B003 ), N002 ) * N002 ) ) / 3.0;
// Center control point
vec3 E = ( B210 + B120 + B021 + B012 + B102 + B201 ) / 6.0;
vec3 V = ( B003 + B030 + B300 ) / 3.0;
B111 = E + ( ( E - V ) / 2.0 );

// Compute the quadratic normal control points, and rotate into world space
float fV12 = 2.0 * dot( B030 - B003, N002 + N020 ) / dot( B030 - B003, B030 - B003 );
N110 = normalize( N002 + N020 - fV12 * ( B030 - B003 ) );
float fV23 = 2.0 * dot( B300 - B030, N020 + N200 ) / dot( B300 - B030, B300 - B030 );
N011 = normalize( N020 + N200 - fV23 * ( B300 - B030 ) );
float fV31 = 2.0 * dot( B003 - B300, N200 + N002 ) / dot( B003 - B300, B003 - B300 );
N101 = normalize( N200 + N002 - fV31 * ( B003 - B300 ) );

// Inside tess factor is just the average of the edge factors
gl_TessLevelInner[0] = ( gl_TessLevelOuter[0] + gl_TessLevelOuter[1] + gl_TessLevelOuter[2] ) / 3.0;
}

// Evaluation shader


#version 400 core
layout(triangles, fractional_odd_spacing, cw) in;

in vec3 c2e_Position[];
in vec3 c2e_Normal[];
in vec3 c2e_TexCoord[];
out vec3 e2f_Position;
out vec3 e2f_Normal;
out vec3 e2f_TexCoord;
out vec4 e2f_Diffuse;

// Geometry cubic generated control points
patch in vec3 B210;
patch in vec3 B120;
patch in vec3 B021;
patch in vec3 B012;
patch in vec3 B102;
patch in vec3 B201;
patch in vec3 B111;

// Normal quadratic generated control points
patch in vec3 N110;
patch in vec3 N011;
patch in vec3 N101;

uniform vec4 LightDir;
uniform vec3 MaterialAmbientColor;
uniform vec3 MaterialDiffuseColor;
uniform vec3 LightDiffuse;
uniform mat4 ViewProjectionMatrix;

void main()
{
// The barycentric coordinates
float fU = gl_TessCoord.x;
float fV = gl_TessCoord.y;
float fW = gl_TessCoord.z;

// Precompute squares and squares * 3
float fUU = fU * fU;
float fVV = fV * fV;
float fWW = fW * fW;
float fUU3 = fUU * 3.0;
float fVV3 = fVV * 3.0;
float fWW3 = fWW * 3.0;

// Compute position from cubic control points and barycentric coords
vec3 f3Position = c2e_Position[0] * fWW * fW +
c2e_Position[1] * fUU * fU +
c2e_Position[2] * fVV * fV +
B210 * fWW3 * fU +
B120 * fW * fUU3 +
B201 * fWW3 * fV +
B021 * fUU3 * fV +
B102 * fW * fVV3 +
B012 * fU * fVV3 +
B111 * 6.0 * fW * fU * fV;

// Compute normal from quadratic control points and barycentric coords
vec3 f3Normal = c2e_Normal[0] * fWW +
c2e_Normal[1] * fUU +
c2e_Normal[2] * fVV +
N110 * fW * fU +
N011 * fU * fV +
N101 * fW * fV;

// Normalize the interpolated normal
f3Normal = normalize( f3Normal );

// Linearly interpolate the texture coords
e2f_TexCoord = c2e_TexCoord[0] * fW + c2e_TexCoord[1] * fU + c2e_TexCoord[2] * fV;

// Calc diffuse color
e2f_Diffuse.rgb = MaterialDiffuseColor * LightDiffuse * max( 0.0, dot( f3Normal, LightDir.xyz ) ) + MaterialAmbientColor;
e2f_Diffuse.a = 1.0;

// Transform model position with view-projection matrix
gl_Position = ViewProjectionMatrix * vec4( f3Position.xyz, 1.0 );
}

// Fragment shader

 #version 400 core

in vec3 e2f_Position;
in vec3 e2f_Normal;
in vec3 e2f_TexCoord;
in vec4 e2f_Diffuse;
out vec4 FragColor;

uniform sampler2D DiffuseTexture;

void main()
{
FragColor = texture(DiffuseTexture, e2f_TexCoord.st) * e2f_Diffuse;
FragColor.a = 1.0;
}

0 Likes

After further investigation, you could avoid the errors by writing the correct shaders.

In your first example,
in Evaluation shader,

in vec3 tcWorldPos[];
in vec3 tcNormal[];
in float tcVertexDistanceFactor[];
in vec2 tcTexCoord[];
in vec3 tcLightTS[];
in vec3 tcViewTS[];

should be

in vec3 teWorldPos[];
in vec3 teNormal[];
in float teVertexDistanceFactor[];
in vec2 teTexCoord[];
in vec3 teLightTS[];
in vec3 teViewTS[];

And you should get other symbols in evaluation shader changed from "tc" to "te".

 

For your second example,

in control shader,

in vec3 v2c_Position[]; 
in vec3 v2c_Normal[];
in vec3 v2c_TexCoord[];
out vec3 c2e_Position[]; 
out vec3 c2e_Normal[];
out vec3 c2e_TexCoord[];

void main()
{
v2c_Position[id] = c2e_Position[id]; 
v2c_Normal[id] = c2e_Normal[id];
v2c_TexCoord[id] = c2e_TexCoord[id];
}

It's an obvious bug in the shader that setting the output to the input. After I invert the dest and source position, the shaders could be linked.

 

It's a pity that the driver doesn't detect such error and confuse you, we will improve the driver quality. Thanks. 

0 Likes

Oh, thank you. Now all links. I apologize for my carelessness.

0 Likes