Vulkan Tessellation: Black terrain on nVidia GT 1030

Hi! I have black screen using tessellation shaders on nVidia GT 1030.

Windows 10 X64, last drivers 496.49

AMD RX 560:

I attached full demo with source HLSL files and spir-v result: VulkanDemoTerrainTesselation

Shader from VulkanDemoTerrainTesselation:

vs.vert - Vertex shader

#define location(x) [[vk::location(x)]]
struct Output {
	float4 pos : SV_POSITION;
	location(0) float2 texCoord : TEXCOORD0;
	location(1) float3 Normal : NORMAL;
};

struct Input {
	location(0) float4 pos: SV_POSITION;
	location(8) float2 uv	: TEXCOORD0;
	location(2) float3 Normal	: NORMAL;
};

Output vsMain(in Input input) {
	Output output;
	output.pos = float4(input.pos.xyz, 1.0f);
	output.Normal = input.Normal;	
	output.texCoord = input.uv;
	return output;
}

hs.tesc - Hull shader

#define location(x) [[vk::location(x)]]
#define binding(x) [[vk::binding(x)]]
#define MAX_TF 20.0f
struct ConstantsHSOutput {
		float Edges[4]	: SV_TessFactor;
		float Inside[2] : SV_InsideTessFactor;
	};
struct Input{
		float4 pos : SV_POSITION;
		location(0) float2 uv : TEXCOORD0;
		location(1) float3 Normal : NORMAL;
};
#define MAX_TF 20.0f
binding(0) cbuffer b0: register(b0) {
	row_major float4x4 view;
	row_major float4x4 proj;
	float4 cameraPosition;
	float LodDistance;
	float tessellationFactor;
	float tessellatedEdgeSize;
	float2 viewportDim;
	uint numLods;
};
float distSq(float3 p0, float3 p1) {
	float3 d = p0 - p1;
	return dot(d, d);
}
float screenSpaceTessFactor(float4 p0, float4 p1) {
	float4 midPoint = 0.5f * (p0 + p1);
	float radius = distance(p0, p1) * 0.5f;
	float4 v0 = mul(midPoint, view);	float4 clip0 = mul(float4(v0.x - radius, 
v0.yzw), proj);
	float4 clip1 = mul(float4(v0.x + radius, v0.yzw), proj);
	clip0 /= clip0.w;
	clip1 /= clip1.w;
	clip0.xy *= viewportDim;
	clip1.xy *= viewportDim;
#ifdef D3D12
	return max(distance(clip0, clip1) / tessellatedEdgeSize * tessellationFactor, 1.0f);
#else
	return clamp(distance(clip0, clip1) / tessellatedEdgeSize * tessellationFactor, 1.0f, MAX_TF);
#endif
}
float CalculateTessFactor(float4 p0, float4 p1) {
#ifdef USE_SS_TESS_FACTOR_CALC
	return screenSpaceTessFactor(p0, p1);
#else
	float3 center = (p0 + p1).xyz * 0.5f;
	float factor = LodDistance / distSq(center, cameraPosition);
	return clamp(factor, 1.0f, numLods);
#endif
}
ConstantsHSOutput ConstantsHS(InputPatch<Input, 4> ip, uint PatchID : SV_PrimitiveID) {
	ConstantsHSOutput hsOut;
	hsOut.Edges[0] = CalculateTessFactor(ip[0].pos, ip[3].pos);
	hsOut.Edges[1] = CalculateTessFactor(ip[1].pos, ip[0].pos);
	hsOut.Edges[2] = CalculateTessFactor(ip[2].pos, ip[1].pos);
	hsOut.Edges[3] = CalculateTessFactor(ip[3].pos, ip[2].pos);
	hsOut.Inside[0] = lerp(hsOut.Edges[0], hsOut.Edges[3], 0.5f);
	hsOut.Inside[1] = lerp(hsOut.Edges[1], hsOut.Edges[2], 0.5f);
	return hsOut;
}
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("ConstantsHS")]
[maxtessfactor(MAX_TF)]
Input hMain(InputPatch<Input, 4> ip, uint i : SV_OutputControlPointID, uint PatchID : SV_PrimitiveID) {
	Input output;
	output.pos = float4(ip[i].pos.xyz, 1.0f);
	output.uv = ip[i].uv;
	output.Normal = ip[i].Normal;
	return output;
}

ds.tese - Domain shader

#define location(x) [[vk::location(x)]]
#define binding(x) [[vk::binding(x)]]
#define MULTIPLE_LIGHTS 1

struct ConstantsHSOutput {
		float Edges[4]	: SV_TessFactor;
		float Inside[2] : SV_InsideTessFactor;
	};
struct Output{
		location(0) float4 pos : POSITION;
		location(1) float2 texCoord : TEXCOORD0;
		location(2) float3 Normal : NORMAL;
		location(3) float3 lightDir : TEXCOORD1;
		location(4) float4 shadowCrd : TEXCOORD2;
		location(5) float4 lightProjSpaceLokup : TEXCOORD3;
		location(6) float3 vVec : TEXCOORD4;
		location(7) float3 viewDir : TEXCOORD5;
};

binding(1) cbuffer b0 : register(b0) {
	row_major float4x4 ModelViewProjection;
	row_major float4x4 proj;
	float4 cameraPosition;
	float4 LightDir;
	row_major float4x4 MatTexture;
	row_major float4x4 ViewMatrix;
	float scaleHeight;
	float2 uvScale;
};
binding(2) Texture2D HeightMap : register(t0);
binding(2) SamplerState Sampler : register(s0);
struct Input {
	location(0) float4 pos: SV_POSITION;
	location(8) float2 uv	: TEXCOORD0;
	location(2) float3 Normal	: NORMAL;
};
float4 CalcLightProjSpaceLookup(float4 projectSpace) {	projectSpace.xy = (projectSpace.xy + float2(projectSpace.w, projectSpace.w)) * 0.5;	return projectSpace;}

Output vsMain(in Input input) {
	Output output;
	float4 Pos = float4(input.pos.xyz, 1.0f); 
	output.pos = mul(Pos, ModelViewProjection);
#ifdef MULTIPLE_LIGHTS
	output.lightProjSpaceLokup = CalcLightProjSpaceLookup(output.pos);
#ifdef NO_LIGHT_VIEW_SPACE
	output.vVec = pos.xyz;
#else
	output.vVec = mul(Pos, ViewMatrix).xyz;
	output.viewDir = cameraPosition.xyz - Pos.xyz;
#endif
#endif
	output.shadowCrd = mul(Pos, MatTexture);
	output.texCoord = input.uv;
	output.Normal = mul(input.Normal, (float3x3)ViewMatrix);
	output.lightDir = -LightDir.xyz;
    return output;
}
[domain("quad")]
Output dMain(in ConstantsHSOutput input, float2 domain : SV_DomainLocation, const OutputPatch<Input, 4> patch) {
	Output dsOutput;
	Input vsIn;
	vsIn.pos = lerp(lerp(patch[0].pos, patch[1].pos, domain.x), lerp(patch[3].pos, patch[2].pos, domain.x), domain.y);
	float2 uv = vsIn.pos.xz * uvScale;
	vsIn.pos.y = HeightMap.SampleLevel(Sampler, uv, 0.0).r * scaleHeight;
	vsIn.uv = lerp(lerp(patch[0].uv, patch[1].uv, domain.x), lerp(patch[3].uv, patch[2].uv, domain.x), domain.y);
	vsIn.Normal = lerp(lerp(patch[0].Normal, patch[1].Normal, domain.x), lerp(patch[3].Normal, patch[2].Normal, domain.x), domain.y);
	dsOutput = vsMain(vsIn);
	return dsOutput;
}

ps.frag - Pixel shader

//
#define location(x) layout(location = x)
#define binding(x) [[vk::binding(x)]]
#define SIMPLE_POINT_LIGHT
#defne MULTIPLE_LIGHTS

//
struct PSOutput {
	location(0) float4 pos : SV_POSITION;
	location(1) float2 texCoord : TEXCOORD0;
	location(2) float3 Normal : NORMAL;
	location(3) float3 lightDir : TEXCOORD1;
	location(4) float4 shadowCrd: TEXCOORD2;
#ifdef MULTIPLE_LIGHTS
	location(5) float4 lightProjSpaceLokup : TEXCOORD3;
	location(6) float3 vVec : TEXCOORD4;
	location(7) float3 viewDir : TEXCOORD5;
#endif
};

binding(3) Texture2D tex1 : register(t0);
binding(3) SamplerState samLinear1 : register(s0);

binding(4) Texture2D tex2 : register(t1);
binding(4) SamplerState samLinear2 : register(s1);

binding(5) Texture2D ShadowMap : register(t2);
binding(5) SamplerState sShadowMap : register(s2);

#ifdef MULTIPLE_LIGHTS
binding(6) Texture2D BitPlane : register(t3);
binding(6) SamplerState sBitPlane : register(s3);
#endif

struct Light {
	float4 posRange;
	float4 colorLightType;
#ifndef SIMPLE_POINT_LIGHT
	float4 directionFalloff;
	float4 lighTypeAttenuation;
	float4 ThetaPhi;
#endif
};

#ifdef MULTIPLE_LIGHTS

binding(7) cbuffer b0: register(b0) {
	Light lights[NUM_LIGHTS];
};

#define NEW_MULTI_LIGHTING

#ifdef NEW_MULTI_LIGHTING

float4 CalculateLighting(float4 Color, float3 worldPos, float3 Normal, float3 viewDir, float4 lightIndex)
{
	float3 ambient_color = float3(0.0f, 0.0f, 0.0f);
    float3 color = float3(0.0f, 0.0f, 0.0f);
    
    float3 n = normalize(Normal);
    float3 v = normalize(viewDir);
#ifndef NO_LIGHT_BUFFER
    for (int i = 0; i < 4; ++i)
    {                   
		float lIndex = 256.0f * lightIndex[i];
		Light light = lights[int(lIndex)]; 
#else
    for (int i = 0; i < 255; ++i)
    {        		
		Light light = lights[i]; 
#endif
        float3 l = (light.posRange.xyz - worldPos) * light.posRange.w;
        float atten = saturate(1.0f - dot(l, l));
        l = normalize(l);
        float3 h = normalize(l + v);
        
        float nDotL = saturate(dot(n, l));
        float nDotH = saturate(dot(n, h));
        float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, 16.0f);
        
        color += (light.colorLightType.xyz * nDotL * atten) + power * atten;
    }
	color += ambient_color;
	return float4(color.xyz, Color.a);
}

#else

float4 CalculateLighting(float4 color, float3 vVec, float3 Normal, float4 lightIndex)
{
	float3 ambient_color = float3(0.2f, 0.2f, 0.2f);
	float3 lighting = float3(0.0f, 0.0f, 0.0f);
	for (int i = 0; i < 4; ++i) {
		float lIndex = 255.0f * lightIndex[i];
		Light light = lights[int(lIndex)]; 
		if (!light.posRange.w) {
	    	//return float4(0.0f, 0.0f, 0.0f, 0.0f);
			//continue;
		}
	    // Get the vector from the light center to the surface
		float3 lightVec = light.posRange.xyz - vVec;
#if 1
#if 1
 		// Scale based on the light radius
		float3 lVec = lightVec * light.posRange.a;
		float atten = 1.0f - saturate(dot(lVec, lVec));
#else
		float d = length(lightVec) / light.posRange.a;
		float atten = 1.0f - saturate(d);
#endif
#else
		float d = length(lightVec) * light.posRange.a;
		const float3 ConstantAtten = float3(0.4f, 0.01f, 0.01f);
		float atten = 1.0f / (ConstantAtten.x + ConstantAtten.y * d + ConstantAtten.z * d * d);
#endif	
		lightVec = normalize(lightVec);
		float3 H = normalize(lightVec + vVec);
		float diffuse = saturate(dot(lightVec, Normal));
		float specular = pow(saturate(dot(lightVec, H)), 16.0);
		lighting += atten * (diffuse * light.colorLightType.xyz * color.xyz + color.xyz * ambient_color + light.colorLightType.xyz * specular);
	}
	//lighting = max(lighting, 0.0f);
	return float4(lighting.xyz, color.a);
	//return float4(color.xyz, color.a);
}
#endif // NEW_MULTI_LIGHTING 
#endif // MULTIPLE_LIGHTS

float4 psMain(in PSOutput In) : SV_Target {
	
	float4 color = tex1.Sample(samLinear1, In.texCoord);
	float4 color2 = tex2.Sample(samLinear2, In.texCoord);
	color *= color2;
	float4 shadowCrd = In.shadowCrd;
	float shadowColor = 0.5f;
#ifdef PCF
	float sh = PCFFilter(4.0, ShadowMap, shadowCrd, shadowColor);
#else
	shadowCrd.xyz /= shadowCrd.w;
	float shadow = ShadowMap.Sample(sShadowMap, shadowCrd.xy).r;
	float sh = shadow < shadowCrd.z - 0.001f ? shadowColor : 1.0f;
#endif
	float3 normal = normalize(In.Normal);
#ifdef MULTIPLE_LIGHTS
	float4 lightIndex = GetLightIndex(BitPlane, In.lightProjSpaceLokup);
#ifdef NEW_MULTI_LIGHTING
	float3 viewDir = normalize(In.viewDir);
	float4 Albedo = CalculateLighting(color, In.vVec, normal, viewDir, lightIndex);
#else
	float4 Albedo = CalculateLighting(color, In.vVec, normal, lightIndex);
#endif
#endif	
	float3 lightDir = normalize(In.lightDir);
	float I = saturate(dot(lightDir, normal));
	const float3 ambient = float3(0.2f, 0.2f, 0.2f);
#ifdef MULTIPLE_LIGHTS
	color.xyz += Albedo.zyz;
#endif
	color.xyz += ambient;
	color.xyz *= I;
	color.xyz *= sh;
	return color;
}

The Direct3D12 version of the same shaders shows correct result.
Do we have nVidia driver bug or DXC/glslang translation from HLSL to SPIR-V bug ?

Hello @AndreyOGL_D3D , welcome back.

Did this issue appear only with the new driver version, or did you see this before?

Also, how can I reproduce? I did download from your link, but running either EXE only opens a shell window and closes it again after showing Vulkan driver and device status. Do I need some specific command line parameter?

1 Like

Hi @MarkusHoHo , Thank you for your answer!

I see problem on last driver.

You just need to run any exe file: . Could you show/attach a log file(log.log)?, May be problem during using full screen Mode
VK_EXT_full_screen_exclusive(3) Try to switch off Full screen mode:

setup.cfg:

[render]
..
fullscreen 0

Disabling Fullscreen shows an empty window whch closes after 2 seconds.
Could I be missing some necessary requirements?

Why I am asking all this is simply that before filing a bug with our engineers, I need to have some form of reliable reproduction available. Otherwise the issue here might be related to something completely different that is very specific to your personal setup. I hope you can understand that.

Thanks!
log.log (3.0 KB)

1 Like

Hi @MarkusHoHo , according info from log file, I guess my demo crashing, I have no NVIDIA GeForce RTX 3060 Ti for testing… The same bug on Nvidia 2070 Super

I attached loglog_Nvidia_2070.txt (72.0 KB)

Could you test on Nvidia 10xx/20xx series?

Possibly, but please be patient, I might not get to it very soon.

1 Like

Hi, @MarkusHoHo you can test on NVIDIA GeForce RTX 3060 Ti, I have fixed crash, please use the same link.

Thank you for the updated package! I can reproduce now.

Since this is beyond my Vulkan experience I will reach out to our experts to see if we can get some insight into what might be the root cause.

Thanks again for bringing this up.

1 Like

Hi @MarkusHoHo , do you have any update ?

I have researched problem using RenderDoc.

nVidia doesn’t calculate correctly texture coords and Normal using Hull shader, see screenshot nVidia GT 1650:

The same from AMD RX 560:

I have attached RenderDoc’s captures

vk_amd_tessellation.rdc.7z (8.1 MB)
vk_nv_tessellation.rdc.7z (3.4 MB)

@MarkusHoHo problem has been fixed!
pixel shader received invalid normal ans texture’s coords.

1 Like

Good that you figured this out!

I had not received internal feedback yet on this.

Was the issue now on NVIDIA driver side? Then I would pass this on as a bug if necessary.

1 Like

Problem on my side. Ugly OpenGL’s legacy for Vulkan API!

The Input layout must be use “string” description for Vertex Attributes like as HLSL semantics Direct3D instead of end-to-end numbering of vertex’s attributes’s location. I hope Khronos will create some Vulkan extensions for resolve this problem.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.