I've noticed some pretty weird render artifacts when using 9Patch objects.
(See the images below)
Questions
Is it possible that this is a bug? (I don't want to jump to conclusions as I've only investigated this on one computer.)
If anyone knows a workaround for the following issues, please let me know.
Image 1: Pixel shape distortion
Shown, several instances of a few test patterns, and a stone block from a game of mine, in which I first began investigating the occasional 9Patch runtime render issues. Both are zoomed to 8x scale. The Editor is at 800% zoom, and the "In game" runtime has the layout scaled by a factor of 8 via events.
(click to enlarge)
Some background
I make pixel-style games that incorporate some custom post-processing effects to keep everything properly pixilated, and for a long time I thought that my processing chain was occasionally causing these weird 9Patch render artifacts.
A day ago, I finally decided to investigate and fix the issue, once and for all, but after fiddling with the math in various stages of my processing chain to no avail, I began disabling stages of the chain, until I had disabled everything, but the artifacts remained.
I then tried reproducing the 9Patch render artifacts in a new blank project. It took a while to figure out, but it turns out it's pretty simple to set up.
Example capx
Here's an example capx.
The capx has two layouts, one shows the effect in "Image 1", and the other layout shows the effect seen in "Image 2".
In the first layout there are also two extra 9Patch objects that should look okay even during runtime as they have their margins set so that the sub-zones of the source texture all have power-of-two dimensions. The power-of-two dimensions does not fix the Z-order issue, explained below.
Image 2: Z-order issue
As far as I can tell, the Z-order artifacts are a totally separate phenomenon from the distorted pixels described above and shown in "Image 1".
Given a set of 9Patch instances, with different margin properties, the properties of the lowest Z-order instance (back-most and first to be rendered) will contaminate the rendering processes of all subsequent instances.
Specifically, any subsequent instance will use its own margin properties to partition its internal space into corner, edge, and fill zones, however it will then use the margin properties of the back-most instance when determining how to sample the source texture to fill those zones. This causes the smearing (UV clamping), and partially missing (UVs run off the edge) edge and fill artifacts seen in the image below.
[edit] Importantly, it is the Z-order at game start that matters here. Once the game is running, the Z-order can be rearranged, but the cross-instance render contamination will still come from whichever instance had the back-most Z-order when the game started. So in my example capx, if you want to see the effect of a different instance acting as the back-most contaminant-instance, you'll need to change the Z-order in the editor and relaunch the game.
(click to enlarge)
Reproducing the issues
- 1. Set the project to use "point sampling".
- 2. Add a "start of layout" event to scale up the layout by 8x or so at runtime (as you might do for pixel/retro-looking games).
- 3. Add a32x32 9Patch object with any detailed per-pixel pattern.
- 4. In the 9Patch properties, set all margins to 4px.
- 5. Set Fill and Edges to "Tile".
- 6. Set Seems to "Exact".
- 7. Resize the 9Patch so that corner, edge, and fill zones are all visible.
- 8. Run the game. (Make sure the 9Patch is centered as you'll be zooming in 8x.)
Result: In the runtime window, you should see distortion artifacts in the 9Patch pixels, similar to those shown in "Image 1". Conversely, if you look at the same 9Patch in the editor, zoomed to 800% (use Ctrl + Shift + MouseWheel), it should look correct, with no distortion.
- 9. Now, create another instance of the same 9Patch object but set all its margins to 8px. (You should now have an 8px-margin instance of the 9Patch object, in addition to the original 4px- margin instance.)
- 10. Run the game.
Result: In the runtime window, you should see distortion, just as before, but you should also see that the new 8px-margin instance is tiling its edges and fill in what looks like a smeared and chopped up jumble.
It took me a while to figure out what exactly was happening. I made a texture to help visualize the UV mapping. It appeared that the 8px-margin instance was correctly partitioning its interior into 9 sub-zones using the 8 pixel margin metrics set earlier. However, it was filling in those sub-zones by sampling the source texture as if it were using the 4 pixel margins set for the back-most instance.
This works the same way for whichever instance is back-most in Z-order. All other instances erroneously use the margin metric set in the properties of the back-most instance when sampling the source texture. (You can see this in "Image 2")
Again, this may be something locally weird on my computer, so if anyone else has encountered this, tries the example capx, or reproduces any of these issues, I'd be interested to hear what you found.
Thanks for enduring my wall of text. :)