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.");
}
}
