OpenCL POC 1

This commit is contained in:
Alessandro Pasotti 2018-04-10 08:50:12 +02:00
parent a1a09d75e6
commit 51d1bb36b8
3 changed files with 69 additions and 35 deletions

View File

@ -34,9 +34,16 @@ option(USE_OPENCL "Use OpenCL" ON)
if (USE_OPENCL) if (USE_OPENCL)
FIND_PACKAGE(OpenCL) FIND_PACKAGE(OpenCL)
if(${OpenCL_FOUND}) if(${OpenCL_FOUND})
<<<<<<< a1a09d75e627b0f085c5a1177db5d58374ca6f44
SET(HAVE_OPENCL TRUE) SET(HAVE_OPENCL TRUE)
else(${OpenCL_FOUND}) else(${OpenCL_FOUND})
MESSAGE(STATUS "Couldn't find OpenCL: support DISABLED") MESSAGE(STATUS "Couldn't find OpenCL: support DISABLED")
=======
SET (USE_OPENCL TRUE CACHE BOOL "Use OpenCL")
IF(USE_OPENCL)
SET(HAVE_OPENCL TRUE)
ENDIF(USE_OPENCL)
>>>>>>> OpenCL POC 1
endif(${OpenCL_FOUND}) endif(${OpenCL_FOUND})
endif(USE_OPENCL) endif(USE_OPENCL)

View File

@ -41,6 +41,7 @@ QgsNineCellFilter::QgsNineCellFilter( const QString &inputFile, const QString &o
} }
// TODO: return an anum instead of an int
int QgsNineCellFilter::processRaster( QgsFeedback *feedback ) int QgsNineCellFilter::processRaster( QgsFeedback *feedback )
{ {
#ifdef HAVE_OPENCL #ifdef HAVE_OPENCL
@ -52,8 +53,8 @@ int QgsNineCellFilter::processRaster( QgsFeedback *feedback )
{ {
try try
{ {
QgsMessageLog::logMessage( QObject::tr( "Running OpenCL program: %1" ) QgsDebugMsg( QObject::tr( "Running OpenCL program: %1" )
.arg( openClProgramBaseName( ) ), QgsOpenClUtils::LOGMESSAGE_TAG, Qgis::Info ); .arg( openClProgramBaseName( ) ), QgsOpenClUtils::LOGMESSAGE_TAG, Qgis::Info );
return processRasterGPU( source, feedback ); return processRasterGPU( source, feedback );
} }
catch ( cl::Error &e ) catch ( cl::Error &e )
@ -165,6 +166,7 @@ gdal::dataset_unique_ptr QgsNineCellFilter::openOutputFile( GDALDatasetH inputDa
#ifdef HAVE_OPENCL #ifdef HAVE_OPENCL
// TODO: return an anum instead of an int
int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *feedback ) int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *feedback )
{ {
@ -224,21 +226,25 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
// Cast to float (because double just crashes on some GPUs) // Cast to float (because double just crashes on some GPUs)
std::vector<float> rasterParams; std::vector<float> rasterParams;
rasterParams.push_back( mInputNodataValue ); rasterParams.push_back( mInputNodataValue ); // 0
rasterParams.push_back( mOutputNodataValue ); rasterParams.push_back( mOutputNodataValue ); // 1
rasterParams.push_back( mZFactor ); rasterParams.push_back( mZFactor ); // 2
rasterParams.push_back( mCellSizeX ); rasterParams.push_back( mCellSizeX ); // 3
rasterParams.push_back( mCellSizeY ); rasterParams.push_back( mCellSizeY ); // 4
// Allow subclasses to add extra params needed for computation:
// used to pass additional args to opencl program
addExtraRasterParams( rasterParams ); addExtraRasterParams( rasterParams );
std::size_t bufferSize( sizeof( float ) * ( xSize + 2 ) ); std::size_t bufferSize( sizeof( float ) * ( xSize + 2 ) );
std::size_t inputSize( sizeof( float ) * ( xSize ) );
cl::Buffer rasterParamsBuffer( queue, rasterParams.begin(), rasterParams.end(), true, false, nullptr ); cl::Buffer rasterParamsBuffer( queue, rasterParams.begin(), rasterParams.end(), true, false, nullptr );
cl::Buffer scanLine1Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr ); cl::Buffer scanLine1Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr );
cl::Buffer scanLine2Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr ); cl::Buffer scanLine2Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr );
cl::Buffer scanLine3Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr ); cl::Buffer scanLine3Buffer( ctx, CL_MEM_READ_ONLY, bufferSize, nullptr, nullptr );
cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, sizeof( float ) * xSize, nullptr, nullptr ); cl::Buffer *scanLineBuffer[3] = {&scanLine1Buffer, &scanLine2Buffer, &scanLine3Buffer};
cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, inputSize, nullptr, nullptr );
// Create a program from the kernel source // Create a program from the kernel source
cl::Program program( QgsOpenClUtils::buildProgram( ctx, source, QgsOpenClUtils::ExceptionBehavior::Throw ) ); cl::Program program( QgsOpenClUtils::buildProgram( ctx, source, QgsOpenClUtils::ExceptionBehavior::Throw ) );
@ -252,6 +258,9 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
cl::Buffer & cl::Buffer &
> ( program, "processNineCellWindow" ); > ( program, "processNineCellWindow" );
// Rotate buffer index
std::vector<int> rowIndex = {0, 1, 2};
// values outside the layer extent (if the 3x3 window is on the border) are sent to the processing method as (input) nodata values // values outside the layer extent (if the 3x3 window is on the border) are sent to the processing method as (input) nodata values
for ( int i = 0; i < ySize; ++i ) for ( int i = 0; i < ySize; ++i )
{ {
@ -267,22 +276,23 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
if ( i == 0 ) if ( i == 0 )
{ {
// Fill scanline 1 with (input) nodata for the values above the first row and feed scanline2 with the first row // Fill scanline 1 with (input) nodata for the values above the first row and
// feed scanline2 with the first actual data row
for ( int a = 0; a < xSize + 2 ; ++a ) for ( int a = 0; a < xSize + 2 ; ++a )
{ {
scanLine[a] = mInputNodataValue; scanLine[a] = mInputNodataValue;
} }
queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine.get() ); queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine.get() );
// Read scanline2 // Read scanline2: first real raster row
if ( GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, &scanLine[1], xSize, 1, GDT_Float32, 0, 0 ) != CE_None ) if ( GDALRasterIO( rasterBand, GF_Read, 0, i, xSize, 1, &scanLine[1], xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
{ {
QgsDebugMsg( "Raster IO Error" ); QgsDebugMsg( "Raster IO Error" );
} }
queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine.get() ); queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine.get() );
// Read scanline3 // Read scanline3: second real raster row
if ( GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, &scanLine[1], xSize, 1, GDT_Float32, 0, 0 ) != CE_None ) if ( GDALRasterIO( rasterBand, GF_Read, 0, i + 1, xSize, 1, &scanLine[1], xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
{ {
QgsDebugMsg( "Raster IO Error" ); QgsDebugMsg( "Raster IO Error" );
} }
@ -291,17 +301,14 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
else else
{ {
// Normally fetch only scanLine3 and move forward one row // Normally fetch only scanLine3 and move forward one row
queue.enqueueCopyBuffer( scanLine2Buffer, scanLine1Buffer, 0, 0, bufferSize, nullptr, nullptr ); // Read scanline 3, fill the last row with nodata values if it's the last iteration
queue.enqueueCopyBuffer( scanLine3Buffer, scanLine2Buffer, 0, 0, bufferSize, nullptr, nullptr );
// Read scanline 3
if ( i == ySize - 1 ) //fill the row below the bottom with nodata values if ( i == ySize - 1 ) //fill the row below the bottom with nodata values
{ {
for ( int a = 0; a < xSize + 2; ++a ) for ( int a = 0; a < xSize + 2; ++a )
{ {
scanLine[a] = mInputNodataValue; scanLine[a] = mInputNodataValue;
} }
queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine.get() ); // row 0 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine.get() ); // row 0
} }
else // Read line i + 1 and put it into scanline 3 else // Read line i + 1 and put it into scanline 3
// Overwrite from input, skip first and last // Overwrite from input, skip first and last
@ -310,7 +317,7 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
{ {
QgsDebugMsg( "Raster IO Error" ); QgsDebugMsg( "Raster IO Error" );
} }
queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine.get() ); // row 0 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine.get() ); // row 0
} }
} }
@ -318,22 +325,22 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
queue, queue,
cl::NDRange( xSize ) cl::NDRange( xSize )
), ),
scanLine1Buffer, *scanLineBuffer[rowIndex[0]],
scanLine2Buffer, *scanLineBuffer[rowIndex[1]],
scanLine3Buffer, *scanLineBuffer[rowIndex[2]],
resultLineBuffer, resultLineBuffer,
rasterParamsBuffer rasterParamsBuffer
); );
queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, xSize * sizeof( float ), resultLine.get() ); queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, inputSize, resultLine.get() );
if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, xSize, 1, resultLine.get(), xSize, 1, GDT_Float32, 0, 0 ) != CE_None ) if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, xSize, 1, resultLine.get(), xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
{ {
QgsDebugMsg( "Raster IO Error" ); QgsDebugMsg( "Raster IO Error" );
} }
std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
} }
if ( feedback && feedback->isCanceled() ) if ( feedback && feedback->isCanceled() )
{ {
//delete the dataset without closing (because it is faster) //delete the dataset without closing (because it is faster)
@ -344,6 +351,8 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
} }
#endif #endif
// TODO: return an anum instead of an int
int QgsNineCellFilter::processRasterCPU( QgsFeedback *feedback ) int QgsNineCellFilter::processRasterCPU( QgsFeedback *feedback )
{ {
@ -450,7 +459,7 @@ int QgsNineCellFilter::processRasterCPU( QgsFeedback *feedback )
QgsDebugMsg( "Raster IO Error" ); QgsDebugMsg( "Raster IO Error" );
} }
} }
// Set first and last extra colums to nodata // Set first and last extra columns to nodata
scanLine1[0] = scanLine1[xSize + 1] = mInputNodataValue; scanLine1[0] = scanLine1[xSize + 1] = mInputNodataValue;
scanLine2[0] = scanLine2[xSize + 1] = mInputNodataValue; scanLine2[0] = scanLine2[xSize + 1] = mInputNodataValue;
scanLine3[0] = scanLine3[xSize + 1] = mInputNodataValue; scanLine3[0] = scanLine3[xSize + 1] = mInputNodataValue;

View File

@ -40,10 +40,9 @@ class ANALYSIS_EXPORT QgsNineCellFilter
/** /**
* Starts the calculation, reads from mInputFile and stores the result in mOutputFile * Starts the calculation, reads from mInputFile and stores the result in mOutputFile
\param feedback feedback object that receives update and that is checked for cancelation. * \param feedback feedback object that receives update and that is checked for cancelation.
\returns 0 in case of success * \returns 0 in case of success
TODO: return an enum */
*/
int processRaster( QgsFeedback *feedback = nullptr ); int processRaster( QgsFeedback *feedback = nullptr );
double cellSizeX() const { return mCellSizeX; } double cellSizeX() const { return mCellSizeX; }
@ -60,8 +59,23 @@ class ANALYSIS_EXPORT QgsNineCellFilter
void setOutputNodataValue( double value ) { mOutputNodataValue = value; } void setOutputNodataValue( double value ) { mOutputNodataValue = value; }
/** /**
* Calculates output value from nine input values. The input values and the output value can be equal to the * Calculates output value from nine input values. The input values and the output
nodata value if not present or outside of the border. Must be implemented by subclasses*/ * value can be equal to the nodata value if not present or outside of the border.
* Must be implemented by subclasses.
*
* First index of the input cell is the row, second index is the column
*
* @param x11 surrounding cell top left
* @param x21 surrounding cell central left
* @param x31 surrounding cell bottom left
* @param x12 surrounding cell top central
* @param x22 the central cell for which the value will be calculated
* @param x32 surrounding cell bottom central
* @param x13 surrounding cell top right
* @param x23 surrounding cell central right
* @param x33 surrounding cell bottom right
* @return the calculated cell value for the central cell x22
*/
virtual float processNineCellWindow( float *x11, float *x21, float *x31, virtual float processNineCellWindow( float *x11, float *x21, float *x31,
float *x12, float *x22, float *x32, float *x12, float *x22, float *x32,
float *x13, float *x23, float *x33 ) = 0; float *x13, float *x23, float *x33 ) = 0;
@ -85,9 +99,8 @@ class ANALYSIS_EXPORT QgsNineCellFilter
/** /**
* \brief processRasterCPU executes the computation on the CPU * \brief processRasterCPU executes the computation on the CPU
* \param feedback * \param feedback instance of QgsFeedback, to allow for progress monitoring and cancelation
* \return an opaque integer for error codes: 0 in case of success * \return an opaque integer for error codes: 0 in case of success
* TODO: return an enum
*/ */
int processRasterCPU( QgsFeedback *feedback = nullptr ); int processRasterCPU( QgsFeedback *feedback = nullptr );
@ -95,12 +108,17 @@ class ANALYSIS_EXPORT QgsNineCellFilter
/** /**
* \brief processRasterGPU executes the computation on the GPU * \brief processRasterGPU executes the computation on the GPU
* \param feedback * \param source path to the OpenCL source file
* \param feedback instance of QgsFeedback, to allow for progress monitoring and cancelation
* \return an opaque integer for error codes: 0 in case of success * \return an opaque integer for error codes: 0 in case of success
* TODO: return an enum
*/ */
int processRasterGPU( const QString &source, QgsFeedback *feedback = nullptr ); int processRasterGPU( const QString &source, QgsFeedback *feedback = nullptr );
/**
* \brief addExtraRasterParams allow derived classes to add parameters needed
* by OpenCL program
* \param params vector of parameters passed to OpenCL algorithm
*/
virtual void addExtraRasterParams( std::vector<float> &params ) virtual void addExtraRasterParams( std::vector<float> &params )
{ {
Q_UNUSED( params ); Q_UNUSED( params );