This topic describes how to render drawings in separate threads to improve performance.
ODA BimRv SDK provides a simple API for invoking multithreading functionality for the OdBmMTDrawContext
, OdBmMTDraw
and OdBmMTDrawingMgr
classes.
The OdBmMTDraw
class provides thread-safe rendering functionality for a single OdBmDBDrawing
element via a specified context.
The OdBmMTDrawingMgr
class provides thread-safe rendering functionality for an array of OdBmDBDrawing
elements via a specified context.
It simply starts a number of OdBmMTDraw
instances to render the array of drawings in separate concurrent threads.
You also need an instance of OdBmMTDrawContext
or its descendant.
See the typedefs and the constructor below:
typedef std::function<OdGsDevicePtr(const OdBmMTDrawContext*)> OdBmCreateGsDeviceFuncType;
typedef std::function<void(OdBmObjectId, OdGsDevice*, const OdBmMTDrawContext*)> OdBmPostProcessFuncType;
OdBmMTDrawContext(
OdMutex* pDbAccessMutex,
long picWidth,
long picHeight,
bool bZoomToExtents,
const OdBmCreateGsDeviceFuncType& deviceCreator,
const OdBmPostProcessFuncType& postProcessor)
The last two parameters are references to functions that need to be implemented.
The first function should satisfy the OdBmCreateGsDeviceFuncType
prototype, create an appropriate GS device, and return a pointer to it.
The second function should satisfy the OdBmPostProcessFuncType
prototype and perform post-processing of the rendering results.
The following example renders DBDrawings
via a GLES2
device and stores the results in a bitmap array.
You can find the following code in the BmMTDrawingMgrEx
example.
First, create a descendant of the OdBmMTDrawContext
class.
class OdBmGLES2MTDrawContext : public OdBmMTDrawContext {
public:
OdBmGLES2MTDrawContext(
OdMutex* dbAccessMutex,
long picWidth,
long picHeight,
bool bZoomToExtents,
const OdBmCreateGsDeviceFuncType& createGsDeviceFunc,
const OdBmPostProcessFuncType& postProcessFunc,
bool bDiscardBackFaces,
bool bPlotGeneration,
ODCOLORREF bgColor = ODRGB(255, 255, 255));
/** \details
Returns true if back faces are discarded.
*/
bool discardBackFaces() const;
/** \details
Returns background color of the rendered raster images.
*/
ODCOLORREF bgColor() const;
/** \details
Returns true if plot generation is set.
*/
bool plotGeneration() const;
private:
bool m_discardBackFaces;
ODCOLORREF m_bgColor;
bool m_plotGeneration;
};
Next, define a cover class, for example:.
/** \details
A helper class to render DB in a separate cuncurent threads.
*/
class OdBmMTRenderer {
public:
typedef std::map<OdBmObjectId, OdGiRasterImagePtr> Renditions;
/** \details
Runs rendering queue.
\param drawings [in] An array of drawings to be rendered.
\param picWidth [in] Width of the resulting bitmaps in pixels.
\param picHeight [in] Height of the resulting bitmaps in pixels.
\param bZoomToExtents [in] A flag that states whether to use zoom to extents.
\param bDiscardBackFaces [in] A flag that states whether to discard back faces while rendering.
\param bgColor [in] The resulting bitmaps background color.
*/
Renditions run(OdBmDBDrawingPtrArray& drawings,
long picWidth, long picHeight, bool bZoomToExtents = false,
bool bDiscardBackFaces = false, ODCOLORREF bgColor = ODRGB(255, 255, 255));
};
Now, implement it. First, get all the required include files:
#include "BmMTRenderer.h"
#include "DynamicLinker.h"
#include "RxVariantValue.h"
#include "Gi/GiRasterWrappers.h"
#include "Database/MTDrawingMgr/BmMTDrawingMgr.h"
#include "MTDrawingMgr/BmMTDraw.h"
#include "BmGLES2MTDrawContext.h"
Next, implement the run()
function. It is a specialized process, so the first step is to create a function of the OdBmCreateGsDeviceFuncType
type.
/******************************************************************/
/* GS device creation function */
/******************************************************************/
OdMutex m_createDevice;
auto createDevice = [&m_createDevice](const OdBmMTDrawContext* pContext) {
TD_AUTOLOCK(m_createDevice);
const OdBmGLES2MTDrawContext* pToImgContext =
static_cast<const OdBmGLES2MTDrawContext*>(pContext);
OdGsModulePtr pGsModule = ::odrxDynamicLinker()->loadModule(OdWinGLES2ModuleName);
OdGsDevicePtr pGsDevice = pGsModule->createBitmapDevice();
// Enable HLR for Bitmap and WinGDI vectorizers
OdRxDictionaryPtr pProps = pGsDevice->properties();
if (pProps->has(OD_T("EnableSoftwareHLR"))) // Check if property is supported
pProps->putAt(OD_T("EnableSoftwareHLR"), OdRxVariantValue(true));
if (pProps->has(OD_T("DiscardBackFaces"))) // Check if property is supported
pProps->putAt(OD_T("DiscardBackFaces"), OdRxVariantValue(pToImgContext->discardBackFaces()));
OdRxThreadPoolServicePtr pThreadPool = ::odrxDynamicLinker()->loadApp(OdThreadPoolModuleName);
if (pThreadPool.isNull())
throw(L"ThreadPool.tx not found.\n");
OdUInt16 nCPUs = pThreadPool->numCPUs();
if (pGsDevice->properties()->has(OD_T("MaxRegenThreads")))
pGsDevice->properties()->putAt(OD_T("MaxRegenThreads"), OdRxVariantValue(nCPUs));
if (pGsDevice->properties()->has(OD_T("EnableMultithread")))
pGsDevice->properties()->putAt(OD_T("EnableMultithread"), OdRxVariantValue(true));
pGsDevice->setLogicalPalette(
(ODGETRED(pToImgContext->bgColor()) < 140) && (ODGETGREEN(pToImgContext->bgColor()) < 140) && (ODGETBLUE(pToImgContext->bgColor()) < 140) ?
(::odcmAcadDarkPalette()) : (::odcmAcadLightPalette()), 256);
pGsDevice->setBackgroundColor(pToImgContext->bgColor());
return pGsDevice;
};
Next, create a function of the OdBmPostProcessFuncType
type.
/******************************************************************/
/* Rendering results processing function */
/******************************************************************/
OdMutex m_mutex;
Renditions renditions;
auto postPrecess = [&m_mutex, &renditions](OdBmObjectId id, OdGsDevice* pDevice, const OdBmMTDrawContext* pContext) {
TD_AUTOLOCK(m_mutex);
// Create clone of rendered raster image to correctly release all vectorizer resources in current thread
OdGiRasterImagePtr pRaster = OdGiRasterImageHolder::createObject(
OdGiRasterImagePtr(pDevice->properties().get()->getAt(OD_T("RasterImage"))));
renditions[id] = pRaster;
};
Finally, create an instance of OdBmMTDrawContext
:
/******************************************************************/
/* Instantiate the context and start MT rendering */
/******************************************************************/
OdBmGLES2MTDrawContext m_ctx(&m_mutex, picWidth, picHeight, bZoomToExtents,
(OdBmMTDrawContext::OdBmCreateGsDeviceFuncType)createDevice,
(OdBmMTDrawContext::OdBmPostProcessFuncType)postPrecess,
bDiscardBackFaces, bgColor);
OdBmMTDrawingMgr().draw(drawings, &m_ctx);
You can find the run()
method implemented in the BimRv/Examples/BmMTDrawingMgrEx/BmMTDrawingMgrEx.cpp
file.
Rendering in Separate Threads Example
Copyright © 2002 – 2022. Open Design Alliance. All rights reserved.
|