Skip to main content
Inspiring
April 12, 2024
Answered

Help with AEGP_RenderAndCheckOutLayerFrame_Async?

  • April 12, 2024
  • 1 reply
  • 408 views
I've been experimenting with AEGP_RenderAndCheckoutLayerFrame_Async lately, and came across some interesting behavior.
I believe further experimentation could result in some pretty interesting usage from within AEGPs.

Starting with my version of the `Grabba` sample project, I created an AsyncManager class to handle calling AEGP_RenderAndCheckoutLayerFrame_Async.

Couple of interesting tidbits I've noticed:
- This call IS safe to use on other threads, as long as only one thread is calling it at a time.
- The Callback Function comes from `BEE.dll`, and works with (I haven't tried all, but I'd assume) all standard AEGP calls.


Main pain points/questions-
I'm looking for recommendations on capturing and safely returning the AEGP_WorldH (referred to as WorldPtr in my library) back to the main thread from the callback function. Currently,
I've tried captureing by reference in the lambda and directly returning as a future, but I'm seeing some memory issues in doing so ( I believe corruption related).
 Has anyone dealt with similar challenges?

- In its current state, things work out properly-- From an AEGP Plugin, I can render 96 [1920x1080] frames, async, in 0.06s.
    - This being said, I could totally see this expanding into AEGP's being able to do more "effect-like" processing.

Here's the classes being used for reference;

AsyncManager:
 

 

class AsyncRenderManager
    {
  public:
    AsyncRenderManager() {}

    // Method to initiate async rendering with an optional custom callback
    void renderAsync(LayerRenderOptionsPtr optionsH, std::function<void(WorldPtr)> callbackF)
    {
        auto callbackPtr = new std::function<void(WorldPtr)>(callbackF);

        auto ret = std::async(std::launch::async, [optionsH, callbackPtr]() {
            RenderSuite().renderAndCheckoutLayerFrameAsync(optionsH, callback,
                                                           reinterpret_cast<AEGP_AsyncFrameRequestRefcon>(callbackPtr));
        });
    }


    // Existing static callback method remains unchanged
    static A_Err callback(AEGP_AsyncRequestId request_id, A_Boolean was_canceled, A_Err error,
                          AEGP_FrameReceiptH receiptH, AEGP_AsyncFrameRequestRefcon refconP0)
    {
        auto callbackPtr = reinterpret_cast<std::function<void(WorldPtr)> *>(refconP0);

        if (callbackPtr && *callbackPtr)
        {
            FrameReceiptPtr ptr = std::make_shared<AEGP_FrameReceiptH>(receiptH);
            auto world = RenderSuite().getReceiptWorld(ptr);
            (*callbackPtr)(world);
            delete callbackPtr;
        }
        return error;
    }
};

 

 

And my version of Grabba:

 

void saveFrame(int i, LayerRenderOptionsPtr layerRenderOptions) {
    LayerRenderOptionsSuite().setTime(layerRenderOptions, FramesToTime(i)); // Set the time of the layer render options
    AsyncRenderManager rmanager; // Create an instance of the AsyncRenderManager class
    rmanager.renderAsync(layerRenderOptions, [i](WorldPtr world) { // Call the renderAsync method with a lambda function
        if (world) { 
            std::string folder = "C:\\Users\\tjerf\\Downloads\\pdf_output\\New folder\\";
            auto data = Image::data(world); // Get the image data from the world
            Image::saveImage(folder + "\\frame" + std::to_string(i) + ".png", "png", data); // Save the image to disk
        }
        });
}

void GrabbaCommand::execute() {
    try {
        auto layer = Layer::activeLayer(); // Get the active layer
        auto duration = layer->duration(); // Get the duration of the layer
        auto framerate = layer->parentComp()->frameRate(); // Get the frame rate of the composition
        int numframes = static_cast<int>(std::round(duration * framerate)); // Calculate the number of frames

        auto layerRenderOptions = LayerRenderOptionsSuite().newFromLayer(layer->getLayer()); // Create a new LayerRenderOptions object
        // Prepare a vector of futures to hold the results of async operations
        std::vector<std::future<void>> futures; 

        for (int i = 0; i < numframes; ++i) { // Loop through each frame
            // Capture 'i' by value to ensure each thread gets the correct frame number
            auto future = std::async(std::launch::async, [i, &layerRenderOptions]() {
                saveFrame(i, layerRenderOptions);
                });

            futures.push_back(std::move(future)); // Move the future into the vector
        }

        // Wait for all async operations to complete
        for (auto& future : futures) {
            future.wait(); // Wait for the future to complete
        }

    }
    catch (const std::exception& e) {
        App::Alert(e.what()); // Display the exception message
    }
    catch (...) {
        App::Alert("An unknown error occurred.");
    }
}

 



This topic has been closed for replies.
Correct answer shachar carmi

actually, this issue has recently been dicussed between several devs and the AE team. apprently, there's a problem with the memory realsing when using the async version of renderAndCheckout. all devs reverted back to the synchronous version, and just do their best to render a frame at a time on each idle cycle as not to make the ui laggy...

1 reply

shachar carmiCommunity ExpertCorrect answer
Community Expert
April 12, 2024

actually, this issue has recently been dicussed between several devs and the AE team. apprently, there's a problem with the memory realsing when using the async version of renderAndCheckout. all devs reverted back to the synchronous version, and just do their best to render a frame at a time on each idle cycle as not to make the ui laggy...

Inspiring
April 13, 2024

Ahhh, thank you!

That explains a lot. I noticed checking in the frame receipt wasn't possible-- I figured that was just because you weren't supposed to, but there being a bug makes more sense!