sahi

  • 26 mag 2019
  • Si è iscritto 15 giu 2017

    I think I came up with a solution.

    Every time I changed the Skeleton object, I loaded a new .atlas file. For some reason libgdx or OpenGL does not like that and the textures didn't get loaded. I also got the following error:

    E/libEGL: call to OpenGL ES API with no current context (logged once per thread)
    

    The trick was to load all the atlases in the create()-function, before anything gets rendered. I'm storing them in a Map and getting them when needed.

    I'm not sure how efficient that is memorywise in the long run if the atlases get big and plenty, but it'll do for now.

    Here's a sample project demonstrating the issue: https://github.com/Woncler/spine-libgdx-test (press back button to swap skeletons).

     Loading Image

    Currently I'm just recreating the fragment (and the GLSurfaceView) over and over again, which works but is quite slow especially on older devices. So if swapping only the Skeleton object should work, I would prefer to do it that way.

    I need to be able to change Spine characters on the fly. User can select which character to use and the json and atlas are updated accordingly.

    I figured it would be as simple as to replace the SkeletonData, Atlas and related objects in my ApplicationAdapter and it almost was, but the result is just black meshes. For some reason the textures are not updated. The shape is there because the meshes are loaded correctly, but it's just filled black.

    I did not change or recreate OrthographicCamera, SkeletonRenderer or PolygonSpriteBatch objects. I tried to dispose PolygonSpriteBatch and create a new one, but I got the following error: java.lang.IllegalArgumentException: Error compiling shader.

    I can't use skins because the skeletons and animations are so different. Is there something that need to be disposed that I didn't? I assume I'm just missing something obvious. What would be the recommended way to do something like this?

    EDIT:
    Turns out replacing the whole fragment (extends AndroidFragmentApplication) works.

    • Modificato

    I managed to hack it.

    Turned out that the issue is so random and weird, when I used the Android Studio debugger it didn't occur at all. Therefore I couldn't use the built in debugger to debug this issue. Without the debugger I couldn't use breakpoints and without breakpoints I couldn't inspect the local variables. So I had to just print the vars I was interested in and then filter the console chaos. It was a complete nightmare, but I guess that's software development for you.

    Anyway, what I ultimately found out was that there are essentially two problematic variables: pathLength and curveLength. Both eventually get assigned as Infinity and that's when it breaks. When pathLength hits Infinity, the path constraint retraces so that the path constraint doesn't have a length. When curveLength hits Infinity, the path constraint spazzes out.

    How these variables end up as Infinity is still a mystery to me, because the debugging process is so cumbersome. But my wild guess would be that when they are calculated with Math.sqrt() and casted to float in a for loop, something, somewhere goes wrong. And that something has something to do with old Android devices.

    The solution? Create random arrays! The first one you need to create is placed here, right after the for statement. After insertion the loop will look something like this:

    for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
        float[] hackArray = new float[20]; // <
    
    ---
    
     INSERT THIS LINE
        cx1 = world[w];
        cy1 = world[w + 1];
        ...

    That array fixes the pathLength variable issue. The second array you need to create is placed here, right after the if statement. After insertion the condition will look something like this:

    if (curve != prevCurve) {
        float[] hackArray = new float[20]; // <
    
    ---
    
     INSERT THIS LINE
        prevCurve = curve;
        int ii = curve * 6;
        ...

    That array fixes the curveLength variable issue. And that's it. From now on the path constraint will behave as expected.

    During my debugging I noticed that other operations, such as System.out.printlns also worked the same way as my arrays. I'm not sure which operations prevent the issue but the array creation seemed like the most sensible at the time.

    And I still have no idea what actually is going on behind the curtain. It seems so low level and random that it's impossible to debug properly. Anyway, I hope that this hack is useful if someone else is battling with the same issue. I'm not sure if it's advisable to include it in the official runtime release because the issue is weird and the logic behind the fix is completely nuts. Though the arrays didn't seem to affect performance or behaviour on emulators or newer devices.

    Thanks for pointing me to the right direction. I managed to get some debugging done.

    First of all, both these devices use Dalvik and not ART (Java VM version 1.6.0). So it might be a Dalvik bug as you had guessed three months ago. Furthermore, whenever something weird starts to happen (e.g. path constraint starts to spazz out as demonstrated here), dalvikvm logs this:

    I/dalvikvm: Total arena pages for JIT: 11
    I/dalvikvm: Total arena pages for JIT: 12

    Though I'm not completely certain they are connected.

    The retraction of the path is caused by all the path bones ending up with the same position. I logged values in PathConstraint.java and the positions array starts to get the same values over and over again. In computeWorldPositions I saw Infinity values in the curves array, which originate (I assume) from this for loop. Now, weirdly enough I was able to get rid of the retraction and Infinity values by creating a new array in that for loop. Not sure if it has something to do with memory allocation.

    I also tried the fix described in the previous GitHub issue, but it didn't seem to have any effect.

    I'm still not sure why the path constraint starts to spazz out in the first place, but I'll continue debugging and report any further findings.

    EDIT:
    Here's a GIF demonstrating the issue
     Loading Image


    Right, I think I'm onto something. There are equal X and Y values in the positions array, for example:

    [-12.075218, 186.22685, 0.0, 10.506663, 159.59001, 0.0, 38.70494, 133.08276, 0.0, 61.72608, 107.631035, 0.0, 62.584953, 81.71552, 0.0, 62.584953, 81.71552, 0.0, 62.584953, 81.71552, 0.0, 62.584953, 81.71552, 0.0, -92.54314, 49.60201, -2.9448001, 0.0, 0.0]

    In this array there are multiple values of x = 62.584953 and y = 81.71552.

    Now when boneX and boneY get reassigned, in the next loop we start getting 0.0 values. The calculations get screwed up and the result is that bone properties a and c get values 0.0. This is exactly when the path constraint twitches and goes nuts.

    So the question is why there are equal values in the positions array. The computeWorldPositions calculations look pretty complex but I'll try to get something out of them tomorrow.

    • Modificato

    Hi,

    There seems to be a problem with older Android devices and animating path constraints. After running into the problem with my own skeletons, I recreated it with the stretchyman example. For the first second or two everything is running fine. Then the path starts spazzing out a bit and finally it retraces so that its length is virtually 0.

    I succeeded to recreate the issue with Samsung GT-I8190 (Android 4.1.2) and HTC Sensation Z710e (Android 4.0.3). What these devices have in common is that they both have the ARM Mali-400 MP GPU. I used the latest libgdx (1.9.6) and the latest libgdx runtime (3.6.34.0).

    My educated guess would be that the issue is with Mali-400 GPU and not the runtime directly. Maybe the path constraint implementation in libgdx runtime uses certain features that are not fully functional with this particular GPU? Other advanced features, such as meshes and weights are working correctly though.

    Any thoughts? Thanks!

    The example stretchyman looks something like this:
     Loading Image

    • Modificato

    Has anyone successfully implemented the Cocos2D Objective-C Spine runtime in a Swift project? If so, is there an example project? And if not, would it be difficult to implement?

    We are using Spine in a cross platform mobile project, but the iOS app is and will be written in Swift 3.0. There are a few unofficial Swift runtimes, but they are using Apple's SpriteKit and don't support meshes and possibly other major Spine features.

    Thanks!