Search code examples
javamultithreadinglibgdx

AsyncExecutor doesn’t seem asynchronous


This question has been edited to provide as much information about the problem as possible, as someone suggested I should do. Now that there’s bounty on it—that only seems like the right thing to do. Good luck, people!

Don’t forget you can always ask additional questions if you feel there’s something I didn’t include!

Introduction

I’m working on a system for procedurally generating terrain as a character walks around using various different noises—mostly perlin- and simplex-noise. The world itself has three dimensions, althought it’s viewed as top-down in two dimensions inside the game.

For those of you used to procedurally generating terrain, you’ll be aware of the fact that it’s necessary to use multithreading in order for it not to cause lag spikes within the renderer thread. That’s exactly what I’ve been trying to do, with a little help from LibGDX’s AsyncExecutor, as well as the same library’s AsyncTask, which is a task running on the thread offered by the AsyncExecutor.

Problem

My problem is that the AsyncExecutor doesn’t seem asynchronous at all. It causes lag spikes within my renderer thread, which in theory could be from it using a resource that also is used by the renderer thread. The thing is, however, that the renderer thread generates in new “chunks”—I call them regions—that the AsyncExecutor then work on. The renderer thread isn’t allowed to render that resource until it has been fully generated by the AsyncExecutor.

RegionHandler

I have a class called RegionHandler which places new regions in the direction the player is moving and removes regions from the opposite directions—in order for the program not having to be aware of more than 13 x 9 regions at one time.

Here’s a brief piece of code explaining how it works—the comments are there to simplify discussing different pieces of the code:

// These ‘if’s are triggered when the player moves
// to a certain position
if(addLeft)
{
    // Adds new regions to the left and removes to the right
    // ...

    // Submit ‘RegionLoader’ task to ‘asyncExecutor’
    // with regions that needs be generated as well as ‘toSends’ which is
    // also part of that generation
    asyncExecutor.submit(new RegionLoader(toGenRegions, toSends));      
}
else if(addRight)
{
    // Adds new regions to the right and removes to the left
    // ...

    // Same as previous ‘submit’ to the ‘asyncExecutor’
    asyncExecutor.submit(new RegionLoader(toGenRegions, toSends));
}

if(addBottom)
{
    // Adds new regions to the bottom and removes to the top
    // ...

    // Same as previous ‘submit’ to the ‘asyncExecutor’
    asyncExecutor.submit(new RegionLoader(toSend, toSends));
}
else if(addTop)
{
    // Adds new regions to the top and removes from the bottom
    // ...

    // Same as previous ‘submit’ to the ‘asyncExecutor’
    asyncExecutor.submit(new RegionLoader(toSend, toSends));
}

The asyncExecutor is in fact and AsyncExecutor and the RegionLoader implements the AsyncTask interface. I’ve tested to see how long this piece of code takes to run through—this piece of code never takes over a single millisecond to run through.

The Regions are handled inside a list:

List<List<Region>> regions;

RegionLoader

This class is a task with the capability of being run by an AsyncExecutor.

private class RegionLoader implements AsyncTask<Object>
{
    private List<Region> regions;
    private List<ToSendInner> toSends;

    public RegionLoader(
            List<Region> regions, 
            List<ToSendInner> toSends)
    {
        this.regions = regions;
        this.toSends = toSends;
    }

    @Override
    public Object call() throws Exception
    {
        // Generates the ‘Region’s it has been given inside the constructor
        // ...

        return null;
    }
}

Once call() has finished it’ll set a boolean to true that allows for the rendering of that specific Region.

Furthermore

Although the AsyncExecutor is meant to be asynchronous, it causes lag spikes every once in a while inside the renderer thread. As mentioned earlier, this could be caused by the AsyncExecutor using the same resources as the renderer thread, but the renderer thread merely renders the Regions when it’s allowed to—for that reason I can’t see why that’d be what’s causing the lag spikes.

What I’ve tried so far

I’ve tried using regular Java threads, but I read somewhere that these might not work due to LibGDX supporting Android development and Android doesn’t have the same capabilities when it comes to multithreading—but I could truly be wrong!

Furthermore

Perhaps someone has experienced a similar problem over their game development careers? If so, maybe someone can point me in the right direction?


Solution

  • I'd take a look into the effects of your garbage collection, as you seem to be passing new Region objects to the tasks. Try reusing/repopulating existing Regions instead.