One of my game engine demo (Android) app runs well on devices with Adreno and PowerVR Gpus. However, when I test it on devices with Mali Gpus, I can not get any lighting effect on 3D objects, See the attached device screenshot (left) and RenderDoc snapshot (right):
I narrowed down the problem and found that it cause by accessing array elements by an variable index inside an Uniform buffer.
My shading model is a simple Blinn-Phong shading model. All light sources are passed in by a per-scene uniform buffer. There is 1 ambient light and 5 (up to) direction lights defined in this uniform buffer:
struct DirectionalLight{ vec4 color; vec3 direction; bool isActive; //Is this light in use?};
struct DirectionalLight
{
vec4 color;
vec3 direction;
bool isActive; //Is this light in use?
};
#define NUM_DIR_LIGHT 5layout (std140, set = 1, binding = 0) uniform ProjMatrixBuffer{ mat4 projMat; // Scene projection matrix
#define NUM_DIR_LIGHT 5
layout (std140, set = 1, binding = 0) uniform ProjMatrixBuffer
mat4 projMat; // Scene projection matrix
vec4 ambientColor; // Ambient color DirectionalLight lights[NUM_DIR_LIGHT]; // 5 directional light uint activeLightNum; // Num of active light};
vec4 ambientColor; // Ambient color
DirectionalLight lights[NUM_DIR_LIGHT]; // 5 directional light
uint activeLightNum; // Num of active light
The light effect is calculated by the following function:
LightingColorOutput blinnPhongShading(vec3 normal){
LightingColorOutput blinnPhongShading(vec3 normal)
... for(int i=0; i<activeLightNum; i++) // Loop through all directional lightings { DirectionalLight dirLight = lights[i]; <<<<======== Problem! using [i] makes dirLight has value of all-zero! vec3 lightVec = dirLight.direction; vec4 lightCol = dirLight.color;
for(int i=0; i<activeLightNum; i++) // Loop through all directional lightings
DirectionalLight dirLight = lights[i]; <<<<======== Problem! using [i] makes dirLight has value of all-zero!
vec3 lightVec = dirLight.direction;
vec4 lightCol = dirLight.color;
calculate the light effect generate by this direction light ...
...
}
The line "DirectionalLight dirLight = lights[ i ];" cause the problem. When I use variable "i" to access array element, the structure has all 0 values, and therefore 3D objects appear to be black or very dark.
When I hard code the array index by 0, 1, 2, ..., then I got the correct values that I passed in by Vulkan code.
So the workaround is that I can do the loop unrolling like:
if(lights[0].isActive) calculateLightEffect( lights[0]);if(lights[1].isActive) calculateLightEffect( lights[1]);if(lights[2].isActive) calculateLightEffect( lights[2]);if(lights[3].isActive) calculateLightEffect( lights[3]); ...
if(lights[0].isActive)
calculateLightEffect( lights[0]);
if(lights[1].isActive)
calculateLightEffect( lights[1]);
if(lights[2].isActive)
calculateLightEffect( lights[2]);
if(lights[3].isActive)
calculateLightEffect( lights[3]);
Certainly this is not a nice solution.
Note that my Vulkan shader works well on nVidia, AMD, Intel, PowerVR and Adreno Gpus and I think it complies with Spir-V specs. Is there any thing wrong when I use variable "i" as array index to access array elements? Or is there a special requirement for Mali Gpu? Thanks for any suggestions.
Hi Pete,
Following is the device info I collected by Vulkan API:
******* Device = HUAWEI P30******* Vulkan Info = Vulkan, Vendor:ARM, Driver: 1.1.82, Device: Mali-G76******* Vulkan Surface size = 1080 X 2265
*************************************************************** Device = HUAWEI Mate8******* Vulkan Info = Vulkan, Vendor:ARM, Driver: 1.0.53, Device: Mali-T880******* Vulkan Surface size = 1080 X 1920
Attached are GLSL source code and the Windows script to compile them into Spir-V. I use "glslangValidator.exe" bundled in LunarG Vulkan SDK (version: 1.1.82.1), so if you have LunarG Vulkan SDK installed, you can run "call %VULKAN_SDK%\Bin\glslangValidator.exe -V Vulkan3D.vert -o Vulkan3D.vert.spv -S vert" to get Spir-V code.
#version 450 struct DirectionalLight //32-bytes { vec4 color; vec3 direction; bool isActive; }; //....................................................... layout (std140, set = 0, binding = 0) uniform PerObjMatrixBuffer //Per-obj dynamic uniform buffer { mat4 modelViewMat; //64-bytes mat4 texcoordMat; //64-bytes }; //....................................................... #define NUM_DIR_LIGHT 5 layout (std140, set = 1, binding = 0) uniform ProjMatrixBuffer //Per-scene dynamic uniform buffer { mat4 projMat; //64-bytes vec4 ambientColor; //16-bytes DirectionalLight lights[NUM_DIR_LIGHT]; //32xN-bytes uint activeLightNum; //4-bytes }; //....................................................... layout(push_constant, std140) uniform per_frame_PC //Push constants { uniform vec4 colorConst; //0 bool textureEnabled; //4 bool alphaTestEnabled; //5 bool texcoordTransformationEnabled; //6 bool imageByLuminance; //7 bool shadingEnabled; //8 bool fogEnabled; //9 float fogStart; //10 float fogEnd; //11 vec3 fogColor; //12 bool flatShading; //15 bool specularEnabled; //16 bool generateReflectionTexCoord; //17 uint mtlSpecularPower; //18 float zBias; //19 vec4 mtlDiffuse; //20 (Note: alignment!) vec4 mtlEmissive; //24 (Note: alignment!) vec4 mtlSpecular; //28 (Note: alignment!) }; //....................................................... // Vertex attributes layout (location = 0) in vec3 in_pos; //Vertex position layout(location = 1) in vec3 in_normal; //Vertex normal layout (location = 2) in vec2 in_texcoord; //Vertex tex-coord //....................................................... layout (location = 0) out vec2 tex0; layout (location = 1) flat out vec4 colFlatDiffuse; layout (location = 2) flat out vec4 colFlatSpecular; layout (location = 3) out vec4 colDiffuse; layout (location = 4) out vec4 colSpecular; layout (location = 5) out float fogDist; // ============================================================================ struct LightingColorOutput { vec4 Diffuse; vec4 Specular; }; const vec3 VIEW_VEC = vec3(0.f, 0.f, +1.f); //We always do shading in the view coordinate LightingColorOutput blinnPhongShading(vec3 normal) { LightingColorOutput outputCol; outputCol.Diffuse = ambientColor; // Diffuse initial value set to ambient light outputCol.Specular = vec4(0.f); // Specular initial value set to BLACK normal = normal * mat3(modelViewMat); // Transform normal (note: muste be normalized) for(int i=0; i<activeLightNum; i++) // Calculate lighting effect by directional lights { DirectionalLight dirLight = lights[i]; vec3 lightVec = dirLight.direction; vec4 lightCol = dirLight.color; //Accumulate diffuse color float diffuseIntensity = clamp( dot(normal, lightVec), 0.f, 1.f); outputCol.Diffuse += lightCol * diffuseIntensity; //Accumulate speular color if(specularEnabled) { vec3 halfVec = normalize( lightVec + VIEW_VEC ); float specularIntensity = pow( clamp(dot(normal, halfVec), 0.f, 1.f), float(mtlSpecularPower) ); outputCol.Specular += lightCol * mtlSpecular * specularIntensity; } } return outputCol; } // ============================================================================ void main() { vec4 posInView = vec4(in_pos, 1.f) * modelViewMat; //Got the vertex position in view coordinate gl_Position = posInView * projMat; fogDist = posInView.z; //................................. if(generateReflectionTexCoord) tex0 = (in_normal * mat3(modelViewMat)).xy; else tex0 = texcoordTransformationEnabled? (vec4(in_texcoord, 0.f, 1.f) * texcoordMat).xy : in_texcoord; if(shadingEnabled) //Shading is enabled, calculate directional light intensity by Blinn-Phong model { LightingColorOutput colLight = blinnPhongShading(in_normal); colDiffuse = colFlatDiffuse = colLight.Diffuse; //Save diffuse light as vertex shader output colSpecular = colFlatSpecular = colLight.Specular; //Save specular light as vertex shader output } gl_Position.z += zBias; //Add the Z-Bias }
One more thing: My apps have dual graphics back end, OpenGL ES and Vulkan, I can switch between OpenGL and Vulkan.
The GLSL shader used for two back ends are almost same, which means I access light sources in vertex shader by the same code like "DirectionalLight dirLight = lights[ i ];" at OpenGL side and there is no any problem.
Thanks for your help.
Hi Hongkun,
Thanks for the bug report.
First question: What device, Mali GPU, and Mali driver version are you trying to use this on? The quickest means to find the GPU information is to open the URL "chrome://gpu" in the Chrome browser, and look for the GL_RENDERER and GL_VERSION strings in the resulting page.
Second question: Can you share the complete shader (GLSL and SPIRV) that causes the problem so we can try to reproduce the issue you are seeing.
Cheers, Pete
View all questions in Graphics and Gaming forum