It’s my first time posting, so please let me know if I broke any forum rule or something. I will update/fix.
Recently some users of our app are reporting images not showing correctly. After some digging I found out it was ClippingNode not working as expected on the following devices with Android 9 (Pie).
Moto g(6) Play
Samsung Galaxy J4+
Samsung Galaxy J6+
One thing in common among them is they are all using Qualcomm Adreno 308 GPU.
Attaching two images: same code with different devices: Pixel 2 and Moto g(6) Play.
auto clippingNode = ClippingNode::create();
auto cloud = Sprite::create("cloud.png");
clippingNode->addChild(cloud);
clippingNode->setStencil(Node::create());
clippingNode->setAlphaThreshold(0);
addChild(clippingNode, 10);
clippingNode->setPosition(getContentSize() * 0.5f);
auto brush = Sprite::create("brush.png");
clippingNode->getStencil()->addChild(brush);
I’m wondering if anyone is having similar issues. Any suggestions / guides will be appreciated. Current cocos2d-x we are using is 3.16, but I have tried upgrading it to 3.17.2, which didn’t help.
I tried changing the threshold to 0.1f, but still the same. Alpha range - it is either 1 or 0, please see “brush” below.
If possible, can you try testing on a device with Adreno 308 GPU please? Or one of the devices I listed above. (Moto g6 play, Galaxy j6+, or Galaxy j4+) Looks like Mix2S has Adreno 630.
Regarding the sample, what do you want me to upload? Will attach two images - “cloud” and “brush”. And also apk in case you prefer.
Wonder if it has to do with any OpenGL depth/stencil configuration stuff. Maybe checking in that direction might help find something specific…
edit: Found this:
Wonder if this has any pertinent information for you. The device they had an issue with seems to also be an Adreno 3xx.
The problem was that the stencil buffer wasn't cleared correctly. It is necessary to set stencil mask glStencilMask(0xff) before calling glClear(GL_STENCIL_BUFFER_BIT) to clear all bits of stencil buffer. Some devices might ignore stencil mask and always use 0xff when clearing stencil buffer.
Thank you for your suggestion! Will take a look at it. The issue I posted was fine on Android 8, and only happens on Android 9, so I’m guessing there’s something changed between 8 and 9, and some devices just can’t handle it yet.
Hi @zhangxm, no I have not. Thank you for the reference! Any solutions / work-arounds to this issue? I have tried some myself, but still no luck
Will try more tonight.
Yea, a work around is to use a custom shader instead that uses RGB data from the texture, and multiplies alpha from the mask and the texture, or replaces one with the other. You can find several examples around the net.
One example:
Using a shader will also let you mask with alpha/opacity which the ClippingNode cannot. ClippingNode uses a stencil only to cut out parts based on a threshold.
Hi, @zhangxm, do you mean just trying setOpacity, to see if alpha works on those devices? If so, yes i just tried, and alpha works fine those Adreno 308 GPU devices.
If you are talking about something else, can you please be more specific, please?
Hi @tdebock, thank you for the reference. I found that CCMask needs to be changed a lot to be compiled successfully, will also try making a custom shader as you suggested, it might take some time, though. Will give you an update.
@zhangxm, yes with custom shader, i was able to do simple clippings and show clipped images without any issue on those Adreno 308 devices. Fragment shader is something like the following:
void main()
{
// calculates texture coordinates for mask sprite
float a = (((v_texCoord.x - 0.5) * texture_size.x + texture_pos.x - mask_pos.x) * cos_theta -
((v_texCoord.y - 0.5) * texture_size.y + texture_pos.y - mask_pos.y) * sin_theta) / mask_size.x;
float b = (((v_texCoord.x - 0.5) * texture_size.x + texture_pos.x - mask_pos.x) * sin_theta +
((v_texCoord.y - 0.5) * texture_size.y + texture_pos.y - mask_pos.y) * cos_theta) / mask_size.y;
// i passed bottom left point of the sprite, and calculated with center point of the sprite
// now it moves to the bottom left corner again to fit 0~1 texture coordinates
a = a + 0.5;
b = b + 0.5;
// get mask/masked sprite texture colors
vec4 texColor = texture2D(u_texture, v_texCoord).rgba;
vec4 maskColor = texture2D(u_mask, vec2(a, b)).rgba;
// discards unwanted pixels
if (a > 1.0 || a < 0.0 || b > 1.0 || b < 0.0)
discard;
// if at one pixel mask's alpha is greater than 0, we pick texColor
// used 0.5 just to compare with the original mask sprite
if (maskColor.a > 0.0)
gl_FragColor = vec4(texColor.r, texColor.g, texColor.b, 0.5);
else
discard;
}
But now for me, I need to apply it to our game project, which is using 9-scale images and multiple-sprite clippings. I guess that’s another issue / problem, and currently trying to apply CCMask, which was suggested by @tdebock, to our project.
Thank you everyone for your help! Hopefully soon either cocos2d-x or android can have a patch regarding this issue. Will post another update when all fixes on my side are done, just wondering if I need to close or mark solutions to this post.