VVISF-GL
VVISF-GL copied to clipboard
Issues with Multi-Frame-Rendering in AfterEffects Plugin on Windows
Hi!
We have a repository for an AfterEffects plugin that was originally developed for Mac, working seamlessly with VVISF-GL. However, when we ported it to Windows, we encountered issues with the Multi-Frame-Rendering mode, which implies multithreading. When rendering occurs in the background, the application crashes due to the plugin since cpuBackingPtr
becomes nullptr
after GLTexToCPUCopier::downloadTexToCPU
. Checking the GLERRLOG, I noticed that even before this, within the same cycle, there's an issue with glBindBuffer(inPBOBuffer->desc.target, inPBOBuffer->name);
here inside the GLCPUToTexCopier::_beginProcessing
function. I'm currently uncertain about the root cause. Can you take a look at our code?
We're using the following approach:
In the GlobalSetup function (initialized once during the effect's initial setup), we create a context, BufferPool, and scenes:
#ifdef _WIN32
GLContext::bootstrapGLEnvironmentIfNecessary();
// VVGL on Windows Doesn't have pixel format functions
globalData->context = VVGL::CreateNewGLContextRef(nullptr, nullptr);
#else
globalData->context = VVGL::CreateNewGLContextRef(NULL, CreateCompatibilityGLPixelFormat());
#endif
VVGL::CreateGlobalBufferPool(globalData->context->newContextSharingMe());
globalData->downloader = VVGL::CreateGLTexToCPUCopierRefUsing(globalData->context->newContextSharingMe());
globalData->uploader = VVGL::CreateGLCPUToTexCopierRefUsing(globalData->context->newContextSharingMe());
globalData->defaultScene = VVISF::CreateISF4AESceneRefUsing(globalData->context->newContextSharingMe());
globalData->defaultScene->useCode(SystemUtil::readResourceShader(IDR_DEFAULT_FS), "");
The main work then occurs in SmartRender, which is executed on every render frame, regardless of whether the threads are background or main.
We take the primary layer (essentially an RGBA image) and convert it into a buffer based on color bit depth (VVGL::CreateRGBACPUBufferUsing
, VVGL::CreateRGBAShortCPUBufferUsing
or VVGL::CreateRGBAFloatCPUBufferUsing
) through the createRGBACPUBufferWithBitdepthUsing function using GlobalBufferPool: VVGL::GLBufferPoolRef bp = VVGL::GetGlobalBufferPool();
. Then, we upload the image via the uploader to the texture:
VVGL::GLBufferRef imageAECPU = createRGBACPUBufferWithBitdepthUsing(bufferSizeInPixel, layerDef->data, imageSize, bitdepth);
auto imageAE = globalData->uploader->uploadCPUToTex(imageAECPU);
// Note that AE's inputImage is cropped by mask's region and smaller than ISF resolution.
glBindTexture(GL_TEXTURE_2D, imageAE->name);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glBindTexture(GL_TEXTURE_2D, 0);
auto origin = VVISF::ISFVal(VVISF::ISFValType_Point2D, layerDef->origin_x, layerDef->origin_y);
globalData->ae2glScene->setBufferForInputNamed(imageAE, "inputImage");
globalData->ae2glScene->setValueForInputNamed(origin, "origin");
outImage = createRGBATexWithBitdepth(outImageSize, globalData->context, bitdepth);
globalData->ae2glScene->renderToBuffer(outImage);
// Though ISF specs does not specify the wrap mode of texture, set it to CLAMP_TO_EDGE to match with online ISF
// editor's behavior.
glBindTexture(GL_TEXTURE_2D, outImage->name);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
// ...
input->setCurrentImageBuffer(outImage);
At this point, if the render is on a background thread, there will be an error with glBindBuffer in the void GLCPUToTexCopier::_beginProcessing
function. If we set an assert, it will trigger only in these scenarios:
glBindBuffer(inPBOBuffer->desc.target, inPBOBuffer->name);
if (glGetError() != 0) {
assert("Cannot Bind to Buffer" && false);
}
Following the renderISFToCPUBuffer code which executed from SmartRender, we then attempt to retrieve the OpenGL execution result from the texture, download the result, and free the resources:
// Render ISF
auto isfImage = createRGBATexWithBitdepth(outSize, globalData->context, bitdepth);
// ...
scene.renderToBuffer(isfImage, outSize, time);
// ...
// Download the result of ISF
auto& gl2aeScene = *globalData->gl2aeScene;
gl2aeScene.setBufferForInputNamed(isfImage, "inputImage");
auto outputImage = createRGBATexWithBitdepth(outSize, globalData->context, bitdepth);
gl2aeScene.renderToBuffer(outputImage);
(*outBuffer) = globalData->downloader->downloadTexToCPU(outputImage);
// Release resources
VVGL::GetGlobalBufferPool()->housekeeping();
Are we handling everything correctly? Have we overlooked anything? How should the VVGL lifecycle proceed in a multi-thread rendering mode?