St0fF St0fF - 3 months ago 30
C++ Question

OpenCL/OpenGL Texture interop on windows: resizing OpenGL textures

Follow-up to this question Problems with OpenCL/OpenGL Texture interop / windows:

We have:


  • rendering to a FBO, with a texture attached as COLOR_ATTACHMENT0, this is
    inTex

  • a separable downscaler in OpenCL, using
    inTex
    , producing
    outTex
    in a three-step-process via
    tempTex



Now a new problem appears, as this is an editor-implementation. If
inTex
's size is changed or
outTex
's size is changed, first the OpenGL textures are resized using
glTexImage2d
. Then the three kernel objects are released and finally the
cl_mem
objects of all three textures are released using
clReleaseMemObj( XTex )
. The last step simply recreates all necessary objects using the same code as when initializing, and the loop continues.

During recreation of the cl_mem objects for those textures using
clCreateFromGLTexture2D
I receive
CL_OUT_OF_RESOURCES
. Accordingly the downscaling is not executed any more, as setting the texture-kernel-parameters yields
CL_INVALID_MEM_OBJECT
.

Maybe some pseudo-code helps understand this problem:

// OBJECTS //
////////////
cl_kernel k_x, k_y, k_clear;
cl_mem textureObjects[3];
int src_width, src_height, dst_width, dst_height;
size_t rngClr[3], offsClr[3], range_x[3], range_y[3];
float clrCol[4]

// MAIN ENTRY POINT //
/////////////////////
if ( ( fbo ) && calculate() )
{
glFinish();

if ( CL_recreate ) recreateCLstuff();

CLmgr->acquireGLObjects( 3, textureObjects );
CLmgr->callKernel( k_clear, rngClr, offsClr, "clear" );
CLmgr->setSimpleKernelArg( k_x, 3, 8, &convertXY, "horizontal::convert" );
CLmgr->setSimpleKernelArg( k_x, 4, 16, &offsets_X, "horizontal::offset" );
CLmgr->callKernel( k_x, range_x, "horizontal downsample" );
CLmgr->setSimpleKernelArg( k_y, 3, 8, &convertXY.m128_f32[ 2 ], "vertical::convert" );
CLmgr->setSimpleKernelArg( k_y, 4, 16, &offsets_Y, "vertical::offset" );
CLmgr->callKernel( k_y, range_y, "vertical downsample" );
CLmgr->releaseGLObjects( 3, textureObjects );
clFinish();
}

bool calculate()
{
// check if a resize of the "inTex" happened or a resize of
// "outTex" or "tempTex" is necessary
int srcw, srch, dstw, dsth;
// acquire above values
if ( ( srcw != src_width ) || ( srch != src_height ) || ( dstw != dst_width ) || ( dsth != dst_height ) )
{
CL_recreate = true;

// rebuild temporary texture
if ( ( dst_width != dstw ) || ( src_height != srch ) )
resizeTemporaryOpenGLTexture();
// rebuild target texture
if ( ( dst_width != dstw ) || ( dst_height != dsth ) )
resizeOutputOpenGLTexture();

// finally copy new values to the non-temporary objects
}
// produce all necessary parameters:
// - ranges (rngClr, offsClr, range_x, range_y)
// - convertXY
// - offsets_X
// - offsets_Y
}

void recreateCLstuff()
{
releaseCLstuff();
cerr << name() << ": recreating CL-stuff...\n";
k_x = CLmgr->newKernelInstance( className(), "separable" );
k_y = CLmgr->newKernelInstance( className(), "separable" );
k_clearEmpty = CLmgr->newKernelInstance( className(), "clearEmpty" );
k_clear = CLmgr->newKernelInstance( className(), "clear" );
// [...]
textureObjects[ 0 ] = CLmgr->createTexture( sourceTexID, WI_CL_TEXTURE_USE::sourceTexture, "source FBO texture" );
CLmgr->setMemKernelArg( k_x, 0, textureObjects[ 0 ], "horizontal::src" );
textureObjects[ 1 ] = CLmgr->createTexture( tempTexID, WI_CL_TEXTURE_USE::tempTexture, "separable downscaler buffer" );
CLmgr->setMemKernelArg( k_x, 1, textureObjects[ 1 ], "horizontal::dst" );
CLmgr->setMemKernelArg( k_y, 0, textureObjects[ 1 ], "vertical::src" );
textureObjects[ 2 ] = CLmgr->createTexture( textureID, WI_CL_TEXTURE_USE::targetTexture, "WI_CLtexture target" );
CLmgr->setMemKernelArg( k_y, 1, textureObjects[ 2 ], "vertical::dst" );
CLmgr->setMemKernelArg( k_clear, 1, textureObjects[ 2 ], "clear::dst" );

CLmgr->setSimpleKernelArg( k_clear, 0, 16, clrCol, "clear::clearColor" );

CL_recreate = false;
}

void releaseCLstuff()
{
cerr << name() << ": releasing CL stuff...\n";
cl_int err;
#define releaseKernel( obj ) if ( obj ) if ( err = clReleaseKernel( obj ) ) cerr << "release kernel object \""##obj##"\" failed! (" << clErrorString( err ) << ")\n"

releaseKernel( k_x );
releaseKernel( k_y );
releaseKernel( k_clear );

for ( int i = 0; i < 3; ++i )
{
if ( textureObjects[ i ] )
if ( err = clReleaseMemObject( textureObjects[ i ] ) )
cerr << "release texture object #" << i << " failed! (" << clErrorString( err ) << ")\n";
else
textureObjects[ i ] = nullptr;
}
}

Answer

Simple answer: obviously OpenCL does not like reusing an OpenGL texture. If the size of a shared texture changes, these steps need to be processed:

glDeleteTextures( 1, &id );
glGenTextures( 1, &id );
glBindTexture( GL_TEXTURE_2D, id );
// set texture parameters, so it becomes complete in the eyes of OpenCL and OpenGL
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
// allocate texture storage in the GPU
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
glBindTexture( GL_TEXTURE_2D, 0 );
glFinish();

object = clCreateFromGLTexture2D( context, flags[ usage ], GL_TEXTURE_2D, 0, &id, nullptr );
clSetKernelArg( kernel, argPos, sizeof( cl_mem ), &object );