Skip to main content
Inspiring
April 12, 2024
Answered

Help with AEGP_RenderAndCheckOutLayerFrame_Async?

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 is closed to new replies. Start a new post to keep the conversation going.
shachar carmiCorrect 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...

2 replies

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...

Translate
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!


Translate