Imagination PowerVR SDK Blog

Vulkan problem on PowerVR GE8300 - Update push constants multiple times inside Render Pass, constants at GPU side messed up

Hi,
I’m finalizing my Vulkan game engine.
Inside my game engine, I highly rely on push constants to pass some light-weight constants to GPU pipeline, like:

	struct PushConstants		//sizeof is 128 bytes, reached the limits
	{
		// ---------- data for 2D and 3D ---------- 
		float colorConst[4];					//0
		bool32_t textureEnabled;				//4
		bool32_t alphaTestEnabled;				//5
		bool32_t texcoordTransformationEnabled;	//6
		bool32_t imageByLuminance;				//7

		// ---------- data for 3D only ---------- 
		bool32_t shadingEnabled;				//8
		bool32_t fogEnabled;					//9
		float fogStart;							//10
		float fogEnd;							//11
		float fogColor[3];						//12	(Note: alignment!)

		bool32_t flatShading;					//15
		bool32_t specularEnabled;				//16
		bool32_t generateReflectionTexCoord;	//17
		uint32_t mtlSpecularPower;				//18
		float zBias;							//19
		float mtlDiffuse[4];					//20	(Note: alignment!)
		float mtlEmissive[4];					//24	(Note: alignment!)
		float mtlSpecular[4];					//28	(Note: alignment!)
	};

During the rendering of 2D / 3D scenes, I update push constants many times to pass in attributes of each 2D / 3D objects. So the commands recorded on command buffers looks like:

vkCmdBeginRenderPass(...)
	vkCmdPushConstants(...)
	vkCmdBindDescriptorSets(...)
	vkCmdDraw...(...)

	vkCmdPushConstants(...)
	vkCmdBindDescriptorSets(...)
	vkCmdDraw...(...)

	vkCmdPushConstants(...)
	vkCmdBindDescriptorSets(...)
	vkCmdDraw...(...)
	...
vkCmdEndRenderPass(...)

This works well on all Vulkan platforms (nVidia, AMD, Intel, Adreno, Mali, Apple Metal) except PowerVR (My testing device is Acer Iconia 7 with PowerVR GE8300 GPU). Some push constants such as colorConst seem to be messed up between vkCmdDraw(…) calls
For example, If I have code like:

vkCmdPushConstants(...);  //Pass in colorConst as RED
vkCmdDraw(...); 		//Draw a rectangle
vkCmdPushConstants(...);  //Pass in colorConst as BLUE
vkCmdDraw(...); 		//Draw a triangle

The intention of above code is to draw a RED rectangle first, then draw a BLUE triangle. The actual result may be both shape are drawn in RED, and the result can be unpredictable.

I allocate a 128-bytes data block at CPU side and I update its content right before I call vkCmdPushConstants(). So the data block at CPU side is reused:

vkCmdPushConstants(pCommandBuf, vkPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushConstants), &pushConstants);

As far as I know, when Vulkan function vkCmdPushConstants(…) is called, the content of the data block (up to 128 bytes) are copied into GPU pipeline and therefore I can freely update the data right away for the next drawing command. There is no synchronization required by Vulkan specification.

My question, does PowerVR series GPU have extra requirement or steps when sending in push constants to the Gpu pipeline, like sort of synchronization?
Thanks for any suggestions.

Hi @hongkun,

I see no problem in your code, also the size of PushConstant block smaller than VkPhysicalDeviceLimits:: maxPushConstantsSize

  1. Do you see any errors from Vulkan Validation layers ?
  2. Does your VkPipelinelayout contain correctly creation code using VkPushConstantRange ?
  3. did you try analyze capture from RenderDoc ?
  4. What about other data from PushConstant block? Does it work correctly ?
  5. could you share very simple test project? because any developpers want a simple project to detect issue.

Hi there,
Thanks for your suggestions. Here is my answer:

Do you see any errors from Vulkan Validation layers ?
A: No any errors from validation layers.

Does your VkPipelinelayout contain correctly creation code using VkPushConstantRange ?
A: Here is my code to create the pipeline layout:

	std::vector<VkPushConstantRange> pushConstants =
	{
		{
			VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT,	//stageFlags
			0,															//offset
			sizeof(PushConstants)										//size, 128 bytes
		}
	};

	VkPipelineLayoutCreateInfo pipelineLayoutInfo
	{
		VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,		//sType
		nullptr,											//pNext
		0,													//flags
		(uint32_t)descLayouts.size(),						//setLayoutCount (number of DescriptorSet)
		descLayouts.data(),									//pSetLayouts	 (layouts must be in the same sequence as "set = " in shader)
		(uint32_t)pushConstants.size(),						//pushConstantRangeCount
		pushConstants.data()								//pPushConstantRanges
	};
	VK_ASSERT(vkCreatePipelineLayout(device->vkDevice, &pipelineLayoutInfo, nullptr, &vkPipelineLayout));

diy you try analyze capture from RenderDoc ?

A: Unfortunately, the latest version RenderDoc can not generate capture on my Acer Iconia 7 (possibly due to Android version.). Do you have any suggestions for Android phones / tablets with a MediaTek / PowerVR processor / gpu? I can try to get another device to test, particularly those powered by newer PowerVR gpus.

What about other data from PushConstant block? Does it work correctly ?
A: The first several constants have more chance to be messed up. All constants for 3D rendering seems to work properly, they are siting after those 2D members inside the block.

could you share very simple test project? because any developpers want a simple project to detect issue.
A: I can try to create a simple project with the same problem. Meanwhile, If providing the APK package of my app (GameEngineDemo) can help address problem, I can upload the file here.

Thanks!

Hi andreyogld3d,
Not sure if you are from PowerVR team. Do you know if there is a way to update the Vulkan driver on Acer Iconis ONE 7 (model: B1-7A0)? I was told that the Vulkan driver on my device has been updated with bug-fixings when I reported another problem earlier in this forum.
Here is the Vulkan driver info currently on my tablet:
Vulkan Info = Vulkan, Vendor:ImgTec, Driver: 1.0.3, Device: PowerVR Rogue GE8300

No, i’m not from PowerVR team,
I think your test project should has a minimal simple test code without any dependencies, needn’t upload your full GameEngineDemo.

I don’t know how to update drivers.

Hi,

you might be hitting a driver bug.
The vulkan driver on that device is probably quite old.
You could try using a uniform buffer object instead.

bests,
Marton

Hi @MartonTamas, your answer is very interesting :slight_smile:
What should we do in this case ? :slight_smile:
What does driver team think about it ? could you ask them ?
Is it issue in user space or kernel space ?
May be the driver team can provide some *.so library, it can de sended to device using “adb push”.

Hi,

unfortunately we can’t provide driver updates to retail devices, it’s up to the OEM to do so. We’ve probably encountered this before, but I asked the driver team anyways.

I’d suggest trying to work around the issue using uniform buffers.

bests,
Marton

Hi Marton,
Do you have a suggestion on a phone / tablet that has a “newer” Vulkan driver in which this bug has been fixed? If I know the device mode, I would like to get one of such newer devices to test my Vulkan game engine.

Hi @MartonTamas

Thanks you, i will be waiting of repsonce…

Hi @hongkun anyway, i think you should provide minimal test project to testing Vulkan Push constant issue :slight_smile:

@ andreyogld3d
Yes a minimal project can help address problem. It takes too much time to strip down my game engine and reproduce the problem, that’s why I’d like to take the chance to see if it works well on a newer PowerVR Gpu.

Hi,

we do know about the issue, and it should be fixed in newer drivers, however we can’t supply an update as it’s up to the OEM I’m afraid.
I’d suggest implementing the workaround I suggested.
Please let me know how that went.

bests,
Marton

Hi,
Thanks for your info!
Hongkun
www.omnigsoft.com