mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 21:53:41 +00:00
sw_engine: fix invalid data sharing at multi-threading.
We have encountered that multi-threading usage that user creates, multiple canvases owned by multiple user threads. Current sw_engine memory pool has been considered only for multi-threads, spawned by tvg task scheduler. In this case it's safe but when user threads introduced, it can occur race-condition. Thus, Here is a renewal policy that non-threading tvg(initialized threads with zero), takes care of multiple user threads bu changing its policy, each of canvases should have individual memory pool to guarantee mutual-exclusion. @API additions enum MempoolPolicy { Default = 0, ///< Default behavior that ThorVG is designed to. Shareable, ///< Memory Pool is shared among the SwCanvases. Individual ///< Allocate designated memory pool that is only used by current instance. }; Result SwCanvas::mempool(MempoolPolicy policy) noexcept; All in all, if user calls multiple threads, set memory pool policy to Individual.
This commit is contained in:
parent
f676d6a25c
commit
6dd414ee3b
9 changed files with 280 additions and 180 deletions
37
inc/thorvg.h
37
inc/thorvg.h
|
@ -1114,6 +1114,18 @@ public:
|
||||||
ARGB8888 ///< The channels are joined in the order: alpha, red, green, blue.
|
ARGB8888 ///< The channels are joined in the order: alpha, red, green, blue.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enumeration specifying the methods of Memory Pool behavior policy.
|
||||||
|
*
|
||||||
|
* @BETA_API
|
||||||
|
*/
|
||||||
|
enum MempoolPolicy
|
||||||
|
{
|
||||||
|
Default = 0, ///< Default behavior that ThorVG is designed to.
|
||||||
|
Shareable, ///< Memory Pool is shared among the SwCanvases.
|
||||||
|
Individual ///< Allocate designated memory pool that is only used by current instance.
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the target buffer for the rasterization.
|
* @brief Sets the target buffer for the rasterization.
|
||||||
*
|
*
|
||||||
|
@ -1134,6 +1146,31 @@ public:
|
||||||
*/
|
*/
|
||||||
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept;
|
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set sw engine memory pool behavior policy.
|
||||||
|
*
|
||||||
|
* Basically ThorVG draws a lot of shapes, it allocates/deallocates a few chunk of memory
|
||||||
|
* while processing rendering. It internally uses one shared memory pool
|
||||||
|
* which can be reused among the canvases in order to avoid memory overhead.
|
||||||
|
*
|
||||||
|
* Thus ThorVG suggests memory pool policy to satisfy user demands,
|
||||||
|
* if it needs to guarantee the thread-safety of the internal data access.
|
||||||
|
*
|
||||||
|
* @param[in] policy Use the shared cache memory. The default value is @c true
|
||||||
|
*
|
||||||
|
* @retval Result::Success When succeed.
|
||||||
|
* @retval Result::InsufficientCondition If the canvas has any paints.
|
||||||
|
* @retval Result::NonSupport In case the software engine is not supported.
|
||||||
|
*
|
||||||
|
* @note When @c policy is set as @c MempoolPolicy::Individual, current instance of canvas uses its own individual
|
||||||
|
* memory data that is not shared with others. This is necessary when the canvas is accessed on a worker-thread.
|
||||||
|
*
|
||||||
|
* @warning It's not allowed after pushing any paints.
|
||||||
|
*
|
||||||
|
* @BETA_API
|
||||||
|
*/
|
||||||
|
Result mempool(MempoolPolicy policy) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new SwCanvas object.
|
* @brief Creates a new SwCanvas object.
|
||||||
* @return A new SwCanvas object when succeed, otherwise @c nullptr
|
* @return A new SwCanvas object when succeed, otherwise @c nullptr
|
||||||
|
|
|
@ -254,6 +254,13 @@ struct SwCompositor : Compositor
|
||||||
bool valid;
|
bool valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SwMpool
|
||||||
|
{
|
||||||
|
SwOutline* outline = nullptr;
|
||||||
|
SwOutline* strokeOutline = nullptr;
|
||||||
|
unsigned allocSize = 0;
|
||||||
|
};
|
||||||
|
|
||||||
static inline SwCoord TO_SWCOORD(float val)
|
static inline SwCoord TO_SWCOORD(float val)
|
||||||
{
|
{
|
||||||
return SwCoord(val * 64);
|
return SwCoord(val * 64);
|
||||||
|
@ -299,13 +306,12 @@ SwPoint mathTransform(const Point* to, const Matrix* transform);
|
||||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion);
|
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion);
|
||||||
|
|
||||||
void shapeReset(SwShape* shape);
|
void shapeReset(SwShape* shape);
|
||||||
bool shapeGenOutline(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform);
|
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||||
bool shapePrepare(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion);
|
|
||||||
bool shapePrepared(const SwShape* shape);
|
bool shapePrepared(const SwShape* shape);
|
||||||
bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias, bool hasComposite);
|
bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias, bool hasComposite);
|
||||||
void shapeDelOutline(SwShape* shape, uint32_t tid);
|
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
|
||||||
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
|
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
|
||||||
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion);
|
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||||
void shapeFree(SwShape* shape);
|
void shapeFree(SwShape* shape);
|
||||||
void shapeDelStroke(SwShape* shape);
|
void shapeDelStroke(SwShape* shape);
|
||||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||||
|
@ -317,15 +323,14 @@ void shapeDelStrokeFill(SwShape* shape);
|
||||||
|
|
||||||
void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
|
void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
|
||||||
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
||||||
SwOutline* strokeExportOutline(SwStroke* stroke, unsigned tid);
|
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
|
||||||
void strokeFree(SwStroke* stroke);
|
void strokeFree(SwStroke* stroke);
|
||||||
|
|
||||||
bool imagePrepare(SwImage* image, const Picture* pdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion);
|
bool imagePrepare(SwImage* image, const Picture* pdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||||
bool imagePrepared(const SwImage* image);
|
bool imagePrepared(const SwImage* image);
|
||||||
bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias);
|
bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias);
|
||||||
void imageDelOutline(SwImage* image, uint32_t tid);
|
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
|
||||||
void imageReset(SwImage* image);
|
void imageReset(SwImage* image);
|
||||||
bool imageGenOutline(SwImage* image, const Picture* pdata, unsigned tid, const Matrix* transform);
|
|
||||||
void imageFree(SwImage* image);
|
void imageFree(SwImage* image);
|
||||||
|
|
||||||
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||||
|
@ -341,13 +346,13 @@ void rleClipPath(SwRleData *rle, const SwRleData *clip);
|
||||||
void rleClipRect(SwRleData *rle, const SwBBox* clip);
|
void rleClipRect(SwRleData *rle, const SwBBox* clip);
|
||||||
void rleAlphaMask(SwRleData *rle, const SwRleData *clip);
|
void rleAlphaMask(SwRleData *rle, const SwRleData *clip);
|
||||||
|
|
||||||
bool mpoolInit(uint32_t threads);
|
SwMpool* mpoolInit(uint32_t threads);
|
||||||
bool mpoolTerm();
|
bool mpoolTerm(SwMpool* mpool);
|
||||||
bool mpoolClear();
|
bool mpoolClear(SwMpool* mpool);
|
||||||
SwOutline* mpoolReqOutline(unsigned idx);
|
SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx);
|
||||||
void mpoolRetOutline(unsigned idx);
|
void mpoolRetOutline(SwMpool* mpool, unsigned idx);
|
||||||
SwOutline* mpoolReqStrokeOutline(unsigned idx);
|
SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||||
void mpoolRetStrokeOutline(unsigned idx);
|
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||||
|
|
||||||
bool rasterCompositor(SwSurface* surface);
|
bool rasterCompositor(SwSurface* surface);
|
||||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
|
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
|
||||||
|
|
|
@ -26,55 +26,13 @@
|
||||||
/* Internal Class Implementation */
|
/* Internal Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
|
static bool _genOutline(SwImage* image, const Picture* pdata, const Matrix* transform, SwMpool* mpool, unsigned tid)
|
||||||
|
|
||||||
/************************************************************************/
|
|
||||||
/* External Class Implementation */
|
|
||||||
/************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
bool imagePrepare(SwImage* image, const Picture* pdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion)
|
|
||||||
{
|
|
||||||
if (!imageGenOutline(image, pdata, tid, transform)) return false;
|
|
||||||
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool imagePrepared(const SwImage* image)
|
|
||||||
{
|
|
||||||
return image->rle ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias)
|
|
||||||
{
|
|
||||||
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void imageDelOutline(SwImage* image, uint32_t tid)
|
|
||||||
{
|
|
||||||
mpoolRetOutline(tid);
|
|
||||||
image->outline = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void imageReset(SwImage* image)
|
|
||||||
{
|
|
||||||
rleReset(image->rle);
|
|
||||||
image->rle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool imageGenOutline(SwImage* image, const Picture* pdata, unsigned tid, const Matrix* transform)
|
|
||||||
{
|
{
|
||||||
float w, h;
|
float w, h;
|
||||||
pdata->viewbox(nullptr, nullptr, &w, &h);
|
pdata->viewbox(nullptr, nullptr, &w, &h);
|
||||||
if (w == 0 || h == 0) return false;
|
if (w == 0 || h == 0) return false;
|
||||||
|
|
||||||
image->outline = mpoolReqOutline(tid);
|
image->outline = mpoolReqOutline(mpool, tid);
|
||||||
auto outline = image->outline;
|
auto outline = image->outline;
|
||||||
|
|
||||||
outline->reservedPtsCnt = 5;
|
outline->reservedPtsCnt = 5;
|
||||||
|
@ -107,6 +65,47 @@ bool imageGenOutline(SwImage* image, const Picture* pdata, unsigned tid, const M
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* External Class Implementation */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
bool imagePrepare(SwImage* image, const Picture* pdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||||
|
{
|
||||||
|
if (!_genOutline(image, pdata, transform, mpool, tid)) return false;
|
||||||
|
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool imagePrepared(const SwImage* image)
|
||||||
|
{
|
||||||
|
return image->rle ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias)
|
||||||
|
{
|
||||||
|
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid)
|
||||||
|
{
|
||||||
|
mpoolRetOutline(mpool, tid);
|
||||||
|
image->outline = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void imageReset(SwImage* image)
|
||||||
|
{
|
||||||
|
rleReset(image->rle);
|
||||||
|
image->rle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void imageFree(SwImage* image)
|
void imageFree(SwImage* image)
|
||||||
{
|
{
|
||||||
rleFree(image->rle);
|
rleFree(image->rle);
|
||||||
|
|
|
@ -26,77 +26,76 @@
|
||||||
/* Internal Class Implementation */
|
/* Internal Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
static SwOutline* outline = nullptr;
|
|
||||||
static SwOutline* strokeOutline = nullptr;
|
|
||||||
static unsigned allocSize = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* External Class Implementation */
|
/* External Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
SwOutline* mpoolReqOutline(unsigned idx)
|
SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx)
|
||||||
{
|
{
|
||||||
return &outline[idx];
|
return &mpool->outline[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void mpoolRetOutline(unsigned idx)
|
void mpoolRetOutline(SwMpool* mpool, unsigned idx)
|
||||||
{
|
{
|
||||||
outline[idx].cntrsCnt = 0;
|
mpool->outline[idx].cntrsCnt = 0;
|
||||||
outline[idx].ptsCnt = 0;
|
mpool->outline[idx].ptsCnt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SwOutline* mpoolReqStrokeOutline(unsigned idx)
|
SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx)
|
||||||
{
|
{
|
||||||
return &strokeOutline[idx];
|
return &mpool->strokeOutline[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void mpoolRetStrokeOutline(unsigned idx)
|
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
|
||||||
{
|
{
|
||||||
strokeOutline[idx].cntrsCnt = 0;
|
mpool->strokeOutline[idx].cntrsCnt = 0;
|
||||||
strokeOutline[idx].ptsCnt = 0;
|
mpool->strokeOutline[idx].ptsCnt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool mpoolInit(unsigned threads)
|
SwMpool* mpoolInit(unsigned threads)
|
||||||
{
|
{
|
||||||
if (outline || strokeOutline) return false;
|
auto mpool = new SwMpool;
|
||||||
|
if (!mpool) return nullptr;
|
||||||
|
|
||||||
if (threads == 0) threads = 1;
|
if (threads == 0) threads = 1;
|
||||||
|
|
||||||
outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
|
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
|
||||||
if (!outline) goto err;
|
if (!mpool->outline) goto err;
|
||||||
|
|
||||||
strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
|
mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
|
||||||
if (!strokeOutline) goto err;
|
if (!mpool->strokeOutline) goto err;
|
||||||
|
|
||||||
allocSize = threads;
|
mpool->allocSize = threads;
|
||||||
|
|
||||||
return true;
|
return mpool;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (outline) {
|
if (mpool->outline) {
|
||||||
free(outline);
|
free(mpool->outline);
|
||||||
outline = nullptr;
|
mpool->outline = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strokeOutline) {
|
if (mpool->strokeOutline) {
|
||||||
free(strokeOutline);
|
free(mpool->strokeOutline);
|
||||||
strokeOutline = nullptr;
|
mpool->strokeOutline = nullptr;
|
||||||
}
|
}
|
||||||
return false;
|
delete(mpool);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool mpoolClear()
|
bool mpoolClear(SwMpool* mpool)
|
||||||
{
|
{
|
||||||
SwOutline* p;
|
SwOutline* p;
|
||||||
|
|
||||||
for (unsigned i = 0; i < allocSize; ++i) {
|
for (unsigned i = 0; i < mpool->allocSize; ++i) {
|
||||||
|
|
||||||
p = &outline[i];
|
p = &mpool->outline[i];
|
||||||
|
|
||||||
if (p->cntrs) {
|
if (p->cntrs) {
|
||||||
free(p->cntrs);
|
free(p->cntrs);
|
||||||
|
@ -113,7 +112,7 @@ bool mpoolClear()
|
||||||
p->cntrsCnt = p->reservedCntrsCnt = 0;
|
p->cntrsCnt = p->reservedCntrsCnt = 0;
|
||||||
p->ptsCnt = p->reservedPtsCnt = 0;
|
p->ptsCnt = p->reservedPtsCnt = 0;
|
||||||
|
|
||||||
p = &strokeOutline[i];
|
p = &mpool->strokeOutline[i];
|
||||||
|
|
||||||
if (p->cntrs) {
|
if (p->cntrs) {
|
||||||
free(p->cntrs);
|
free(p->cntrs);
|
||||||
|
@ -135,21 +134,23 @@ bool mpoolClear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool mpoolTerm()
|
bool mpoolTerm(SwMpool* mpool)
|
||||||
{
|
{
|
||||||
mpoolClear();
|
if (!mpool) return false;
|
||||||
|
|
||||||
if (outline) {
|
mpoolClear(mpool);
|
||||||
free(outline);
|
|
||||||
outline = nullptr;
|
if (mpool->outline) {
|
||||||
|
free(mpool->outline);
|
||||||
|
mpool->outline = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strokeOutline) {
|
if (mpool->strokeOutline) {
|
||||||
free(strokeOutline);
|
free(mpool->strokeOutline);
|
||||||
strokeOutline = nullptr;
|
mpool->strokeOutline = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
allocSize = 0;
|
delete(mpool);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
|
@ -29,12 +29,14 @@
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
static int32_t initEngineCnt = false;
|
static int32_t initEngineCnt = false;
|
||||||
static int32_t rendererCnt = 0;
|
static int32_t rendererCnt = 0;
|
||||||
|
static SwMpool* globalMpool = nullptr;
|
||||||
|
static uint32_t threadsCnt = 0;
|
||||||
|
|
||||||
struct SwTask : Task
|
struct SwTask : Task
|
||||||
{
|
{
|
||||||
Matrix* transform = nullptr;
|
Matrix* transform = nullptr;
|
||||||
SwSurface* surface = nullptr;
|
SwSurface* surface = nullptr;
|
||||||
|
SwMpool* mpool = nullptr;
|
||||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||||
Array<RenderData> clips;
|
Array<RenderData> clips;
|
||||||
uint32_t opacity;
|
uint32_t opacity;
|
||||||
|
@ -92,7 +94,7 @@ struct SwShapeTask : SwTask
|
||||||
bool renderShape = (alpha > 0 || sdata->fill());
|
bool renderShape = (alpha > 0 || sdata->fill());
|
||||||
if (renderShape || validStroke) {
|
if (renderShape || validStroke) {
|
||||||
shapeReset(&shape);
|
shapeReset(&shape);
|
||||||
if (!shapePrepare(&shape, sdata, tid, transform, clipRegion, bbox)) goto err;
|
if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
|
||||||
if (renderShape) {
|
if (renderShape) {
|
||||||
/* We assume that if stroke width is bigger than 2,
|
/* We assume that if stroke width is bigger than 2,
|
||||||
shape outline below stroke could be full covered by stroke drawing.
|
shape outline below stroke could be full covered by stroke drawing.
|
||||||
|
@ -121,7 +123,7 @@ struct SwShapeTask : SwTask
|
||||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
||||||
if (validStroke) {
|
if (validStroke) {
|
||||||
shapeResetStroke(&shape, sdata, transform);
|
shapeResetStroke(&shape, sdata, transform);
|
||||||
if (!shapeGenStrokeRle(&shape, sdata, tid, transform, clipRegion, bbox)) goto err;
|
if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
|
||||||
++addStroking;
|
++addStroking;
|
||||||
|
|
||||||
if (auto fill = sdata->strokeFill()) {
|
if (auto fill = sdata->strokeFill()) {
|
||||||
|
@ -155,7 +157,7 @@ struct SwShapeTask : SwTask
|
||||||
err:
|
err:
|
||||||
shapeReset(&shape);
|
shapeReset(&shape);
|
||||||
end:
|
end:
|
||||||
shapeDelOutline(&shape, tid);
|
shapeDelOutline(&shape, mpool, tid);
|
||||||
if (addStroking > 1 && opacity < 255) cmpStroking = true;
|
if (addStroking > 1 && opacity < 255) cmpStroking = true;
|
||||||
else cmpStroking = false;
|
else cmpStroking = false;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +185,7 @@ struct SwImageTask : SwTask
|
||||||
|
|
||||||
if (prepareImage) {
|
if (prepareImage) {
|
||||||
imageReset(&image);
|
imageReset(&image);
|
||||||
if (!imagePrepare(&image, pdata, tid, transform, clipRegion, bbox)) goto end;
|
if (!imagePrepare(&image, pdata, transform, clipRegion, bbox, mpool, tid)) goto end;
|
||||||
|
|
||||||
//Clip Path?
|
//Clip Path?
|
||||||
if (clips.count > 0) {
|
if (clips.count > 0) {
|
||||||
|
@ -199,7 +201,7 @@ struct SwImageTask : SwTask
|
||||||
}
|
}
|
||||||
image.data = const_cast<uint32_t*>(pdata->data());
|
image.data = const_cast<uint32_t*>(pdata->data());
|
||||||
end:
|
end:
|
||||||
imageDelOutline(&image, tid);
|
imageDelOutline(&image, mpool, tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dispose() override
|
bool dispose() override
|
||||||
|
@ -214,7 +216,8 @@ static void _termEngine()
|
||||||
{
|
{
|
||||||
if (rendererCnt > 0) return;
|
if (rendererCnt > 0) return;
|
||||||
|
|
||||||
mpoolTerm();
|
mpoolTerm(globalMpool);
|
||||||
|
globalMpool = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -228,6 +231,8 @@ SwRenderer::~SwRenderer()
|
||||||
|
|
||||||
if (surface) delete(surface);
|
if (surface) delete(surface);
|
||||||
|
|
||||||
|
if (!sharedMpool) mpoolTerm(mpool);
|
||||||
|
|
||||||
--rendererCnt;
|
--rendererCnt;
|
||||||
|
|
||||||
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
|
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
|
||||||
|
@ -397,6 +402,24 @@ bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint32_
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SwRenderer::mempool(bool shared)
|
||||||
|
{
|
||||||
|
if (shared) {
|
||||||
|
if (!sharedMpool) {
|
||||||
|
if (!mpoolTerm(mpool)) return false;
|
||||||
|
mpool = globalMpool;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sharedMpool) mpool = mpoolInit(threadsCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedMpool = shared;
|
||||||
|
|
||||||
|
if (mpool) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Compositor* SwRenderer::target(const RenderRegion& region)
|
Compositor* SwRenderer::target(const RenderRegion& region)
|
||||||
{
|
{
|
||||||
auto x = region.x;
|
auto x = region.x;
|
||||||
|
@ -539,6 +562,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
|
||||||
|
|
||||||
task->opacity = opacity;
|
task->opacity = opacity;
|
||||||
task->surface = surface;
|
task->surface = surface;
|
||||||
|
task->mpool = mpool;
|
||||||
task->flags = flags;
|
task->flags = flags;
|
||||||
task->bbox.min.x = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
|
task->bbox.min.x = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
|
||||||
task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
|
task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
|
||||||
|
@ -578,11 +602,20 @@ RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const Render
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SwRenderer::SwRenderer():mpool(globalMpool)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SwRenderer::init(uint32_t threads)
|
bool SwRenderer::init(uint32_t threads)
|
||||||
{
|
{
|
||||||
if ((initEngineCnt++) > 0) return true;
|
if ((initEngineCnt++) > 0) return true;
|
||||||
|
|
||||||
if (!mpoolInit(threads)) return false;
|
threadsCnt = threads;
|
||||||
|
|
||||||
|
//Share the memory pool among the renderer
|
||||||
|
globalMpool = mpoolInit(threads);
|
||||||
|
if (!globalMpool) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
struct SwSurface;
|
struct SwSurface;
|
||||||
struct SwTask;
|
struct SwTask;
|
||||||
struct SwCompositor;
|
struct SwCompositor;
|
||||||
|
struct SwMpool;
|
||||||
|
|
||||||
namespace tvg
|
namespace tvg
|
||||||
{
|
{
|
||||||
|
@ -48,6 +49,7 @@ public:
|
||||||
bool clear() override;
|
bool clear() override;
|
||||||
bool sync() override;
|
bool sync() override;
|
||||||
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
|
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
|
||||||
|
bool mempool(bool shared);
|
||||||
|
|
||||||
Compositor* target(const RenderRegion& region) override;
|
Compositor* target(const RenderRegion& region) override;
|
||||||
bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override;
|
bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override;
|
||||||
|
@ -61,9 +63,12 @@ private:
|
||||||
SwSurface* surface = nullptr; //active surface
|
SwSurface* surface = nullptr; //active surface
|
||||||
Array<SwTask*> tasks; //async task list
|
Array<SwTask*> tasks; //async task list
|
||||||
Array<SwSurface*> compositors; //render targets cache list
|
Array<SwSurface*> compositors; //render targets cache list
|
||||||
|
SwMpool* mpool; //private memory pool
|
||||||
RenderRegion vport; //viewport
|
RenderRegion vport; //viewport
|
||||||
|
|
||||||
SwRenderer(){};
|
bool sharedMpool = true; //memory-pool behavior policy
|
||||||
|
|
||||||
|
SwRenderer();
|
||||||
~SwRenderer();
|
~SwRenderer();
|
||||||
|
|
||||||
RenderData prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags);
|
RenderData prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags);
|
||||||
|
|
|
@ -249,7 +249,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
|
static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
|
||||||
{
|
{
|
||||||
const PathCommand* cmds = nullptr;
|
const PathCommand* cmds = nullptr;
|
||||||
auto cmdCnt = sdata->pathCommands(&cmds);
|
auto cmdCnt = sdata->pathCommands(&cmds);
|
||||||
|
@ -344,7 +344,7 @@ SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool _fastTrack(const SwOutline* outline)
|
static bool _fastTrack(const SwOutline* outline)
|
||||||
{
|
{
|
||||||
//Fast Track: Othogonal rectangle?
|
//Fast Track: Othogonal rectangle?
|
||||||
if (outline->ptsCnt != 5) return false;
|
if (outline->ptsCnt != 5) return false;
|
||||||
|
@ -363,67 +363,8 @@ bool _fastTrack(const SwOutline* outline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
|
||||||
/* External Class Implementation */
|
|
||||||
/************************************************************************/
|
|
||||||
|
|
||||||
bool shapePrepare(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion)
|
static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid)
|
||||||
{
|
|
||||||
if (!shapeGenOutline(shape, sdata, tid, transform)) return false;
|
|
||||||
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion)) return false;
|
|
||||||
|
|
||||||
//Keep it for Rasterization Region
|
|
||||||
shape->bbox = renderRegion;
|
|
||||||
|
|
||||||
//Check valid region
|
|
||||||
if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false;
|
|
||||||
|
|
||||||
//Check boundary
|
|
||||||
if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y ||
|
|
||||||
renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool shapePrepared(const SwShape* shape)
|
|
||||||
{
|
|
||||||
return shape->rle ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias, bool hasComposite)
|
|
||||||
{
|
|
||||||
//FIXME: Should we draw it?
|
|
||||||
//Case: Stroke Line
|
|
||||||
//if (shape.outline->opened) return true;
|
|
||||||
|
|
||||||
//Case A: Fast Track Rectangle Drawing
|
|
||||||
if (!hasComposite && (shape->rect = _fastTrack(shape->outline))) return true;
|
|
||||||
//Case B: Normale Shape RLE Drawing
|
|
||||||
if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void shapeDelOutline(SwShape* shape, uint32_t tid)
|
|
||||||
{
|
|
||||||
mpoolRetOutline(tid);
|
|
||||||
shape->outline = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void shapeReset(SwShape* shape)
|
|
||||||
{
|
|
||||||
rleReset(shape->rle);
|
|
||||||
rleReset(shape->strokeRle);
|
|
||||||
shape->rect = false;
|
|
||||||
shape->bbox.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool shapeGenOutline(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform)
|
|
||||||
{
|
{
|
||||||
const PathCommand* cmds = nullptr;
|
const PathCommand* cmds = nullptr;
|
||||||
auto cmdCnt = sdata->pathCommands(&cmds);
|
auto cmdCnt = sdata->pathCommands(&cmds);
|
||||||
|
@ -463,7 +404,7 @@ bool shapeGenOutline(SwShape* shape, const Shape* sdata, unsigned tid, const Mat
|
||||||
++outlinePtsCnt; //for close
|
++outlinePtsCnt; //for close
|
||||||
++outlineCntrsCnt; //for end
|
++outlineCntrsCnt; //for end
|
||||||
|
|
||||||
shape->outline = mpoolReqOutline(tid);
|
shape->outline = mpoolReqOutline(mpool, tid);
|
||||||
auto outline = shape->outline;
|
auto outline = shape->outline;
|
||||||
outline->opened = true;
|
outline->opened = true;
|
||||||
|
|
||||||
|
@ -510,6 +451,66 @@ bool shapeGenOutline(SwShape* shape, const Shape* sdata, unsigned tid, const Mat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* External Class Implementation */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||||
|
{
|
||||||
|
if (!_genOutline(shape, sdata, transform, mpool, tid)) return false;
|
||||||
|
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion)) return false;
|
||||||
|
|
||||||
|
//Keep it for Rasterization Region
|
||||||
|
shape->bbox = renderRegion;
|
||||||
|
|
||||||
|
//Check valid region
|
||||||
|
if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false;
|
||||||
|
|
||||||
|
//Check boundary
|
||||||
|
if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y ||
|
||||||
|
renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool shapePrepared(const SwShape* shape)
|
||||||
|
{
|
||||||
|
return shape->rle ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias, bool hasComposite)
|
||||||
|
{
|
||||||
|
//FIXME: Should we draw it?
|
||||||
|
//Case: Stroke Line
|
||||||
|
//if (shape.outline->opened) return true;
|
||||||
|
|
||||||
|
//Case A: Fast Track Rectangle Drawing
|
||||||
|
if (!hasComposite && (shape->rect = _fastTrack(shape->outline))) return true;
|
||||||
|
//Case B: Normale Shape RLE Drawing
|
||||||
|
if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid)
|
||||||
|
{
|
||||||
|
mpoolRetOutline(mpool, tid);
|
||||||
|
shape->outline = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void shapeReset(SwShape* shape)
|
||||||
|
{
|
||||||
|
rleReset(shape->rle);
|
||||||
|
rleReset(shape->strokeRle);
|
||||||
|
shape->rect = false;
|
||||||
|
shape->bbox.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void shapeFree(SwShape* shape)
|
void shapeFree(SwShape* shape)
|
||||||
{
|
{
|
||||||
rleFree(shape->rle);
|
rleFree(shape->rle);
|
||||||
|
@ -543,7 +544,7 @@ void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transfor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion)
|
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||||
{
|
{
|
||||||
SwOutline* shapeOutline = nullptr;
|
SwOutline* shapeOutline = nullptr;
|
||||||
SwOutline* strokeOutline = nullptr;
|
SwOutline* strokeOutline = nullptr;
|
||||||
|
@ -558,7 +559,7 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const M
|
||||||
//Normal Style stroke
|
//Normal Style stroke
|
||||||
} else {
|
} else {
|
||||||
if (!shape->outline) {
|
if (!shape->outline) {
|
||||||
if (!shapeGenOutline(shape, sdata, tid, transform)) return false;
|
if (!_genOutline(shape, sdata, transform, mpool, tid)) return false;
|
||||||
}
|
}
|
||||||
shapeOutline = shape->outline;
|
shapeOutline = shape->outline;
|
||||||
}
|
}
|
||||||
|
@ -568,7 +569,7 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const M
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
strokeOutline = strokeExportOutline(shape->stroke, tid);
|
strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
|
||||||
if (!strokeOutline) {
|
if (!strokeOutline) {
|
||||||
ret = false;
|
ret = false;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -588,7 +589,7 @@ fail:
|
||||||
if (shapeOutline->types) free(shapeOutline->types);
|
if (shapeOutline->types) free(shapeOutline->types);
|
||||||
free(shapeOutline);
|
free(shapeOutline);
|
||||||
}
|
}
|
||||||
mpoolRetStrokeOutline(tid);
|
mpoolRetStrokeOutline(mpool, tid);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -907,7 +907,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SwOutline* strokeExportOutline(SwStroke* stroke, unsigned tid)
|
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid)
|
||||||
{
|
{
|
||||||
uint32_t count1, count2, count3, count4;
|
uint32_t count1, count2, count3, count4;
|
||||||
|
|
||||||
|
@ -917,7 +917,7 @@ SwOutline* strokeExportOutline(SwStroke* stroke, unsigned tid)
|
||||||
auto ptsCnt = count1 + count3;
|
auto ptsCnt = count1 + count3;
|
||||||
auto cntrsCnt = count2 + count4;
|
auto cntrsCnt = count2 + count4;
|
||||||
|
|
||||||
auto outline = mpoolReqStrokeOutline(tid);
|
auto outline = mpoolReqStrokeOutline(mpool, tid);
|
||||||
if (outline->reservedPtsCnt < ptsCnt) {
|
if (outline->reservedPtsCnt < ptsCnt) {
|
||||||
outline->pts = static_cast<SwPoint*>(realloc(outline->pts, sizeof(SwPoint) * ptsCnt));
|
outline->pts = static_cast<SwPoint*>(realloc(outline->pts, sizeof(SwPoint) * ptsCnt));
|
||||||
outline->types = static_cast<uint8_t*>(realloc(outline->types, sizeof(uint8_t) * ptsCnt));
|
outline->types = static_cast<uint8_t*>(realloc(outline->types, sizeof(uint8_t) * ptsCnt));
|
||||||
|
|
|
@ -58,6 +58,25 @@ SwCanvas::~SwCanvas()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Result SwCanvas::mempool(MempoolPolicy policy) noexcept
|
||||||
|
{
|
||||||
|
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||||
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
|
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
|
||||||
|
if (!renderer) return Result::MemoryCorruption;
|
||||||
|
|
||||||
|
//It can't change the policy during the running.
|
||||||
|
if (Canvas::pImpl->paints.count > 0) return Result::InsufficientCondition;
|
||||||
|
|
||||||
|
if (policy == MempoolPolicy::Individual) renderer->mempool(false);
|
||||||
|
else renderer->mempool(true);
|
||||||
|
|
||||||
|
return Result::Success;
|
||||||
|
#endif
|
||||||
|
return Result::NonSupport;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
|
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
|
||||||
{
|
{
|
||||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||||
|
|
Loading…
Add table
Reference in a new issue