Add GrProgramElement base class for GrEffect with deferred exec ref.
BUG=skia:2889 R=robertphillips@google.com Author: bsalomon@google.com Review URL: https://codereview.chromium.org/537773004
This commit is contained in:
parent
8f2e791baa
commit
95740981c3
@ -24,6 +24,9 @@
|
|||||||
'<(skia_include_path)/gpu/GrGpuResource.h',
|
'<(skia_include_path)/gpu/GrGpuResource.h',
|
||||||
'<(skia_include_path)/gpu/GrPaint.h',
|
'<(skia_include_path)/gpu/GrPaint.h',
|
||||||
'<(skia_include_path)/gpu/GrPathRendererChain.h',
|
'<(skia_include_path)/gpu/GrPathRendererChain.h',
|
||||||
|
'<(skia_include_path)/gpu/GrProgramElement.h',
|
||||||
|
'<(skia_include_path)/gpu/GrProgramElementRef.h',
|
||||||
|
'<(skia_include_path)/gpu/GrProgramResource.h',
|
||||||
'<(skia_include_path)/gpu/GrRect.h',
|
'<(skia_include_path)/gpu/GrRect.h',
|
||||||
'<(skia_include_path)/gpu/GrRenderTarget.h',
|
'<(skia_include_path)/gpu/GrRenderTarget.h',
|
||||||
'<(skia_include_path)/gpu/GrResourceKey.h',
|
'<(skia_include_path)/gpu/GrResourceKey.h',
|
||||||
@ -104,6 +107,8 @@
|
|||||||
'<(skia_src_path)/gpu/GrPathRendering.h',
|
'<(skia_src_path)/gpu/GrPathRendering.h',
|
||||||
'<(skia_src_path)/gpu/GrPathUtils.cpp',
|
'<(skia_src_path)/gpu/GrPathUtils.cpp',
|
||||||
'<(skia_src_path)/gpu/GrPathUtils.h',
|
'<(skia_src_path)/gpu/GrPathUtils.h',
|
||||||
|
'<(skia_src_path)/gpu/GrProgramElement.cpp',
|
||||||
|
'<(skia_src_path)/gpu/GrProgramResource.cpp',
|
||||||
'<(skia_src_path)/gpu/GrPictureUtils.h',
|
'<(skia_src_path)/gpu/GrPictureUtils.h',
|
||||||
'<(skia_src_path)/gpu/GrPictureUtils.cpp',
|
'<(skia_src_path)/gpu/GrPictureUtils.cpp',
|
||||||
'<(skia_src_path)/gpu/GrPlotMgr.h',
|
'<(skia_src_path)/gpu/GrPlotMgr.h',
|
||||||
|
@ -10,16 +10,14 @@
|
|||||||
|
|
||||||
#include "GrColor.h"
|
#include "GrColor.h"
|
||||||
#include "GrEffectUnitTest.h"
|
#include "GrEffectUnitTest.h"
|
||||||
#include "GrTexture.h"
|
#include "GrProgramElement.h"
|
||||||
#include "GrTextureAccess.h"
|
#include "GrTextureAccess.h"
|
||||||
#include "GrTypesPriv.h"
|
#include "GrTypesPriv.h"
|
||||||
|
|
||||||
class GrBackendEffectFactory;
|
class GrBackendEffectFactory;
|
||||||
class GrContext;
|
class GrContext;
|
||||||
class GrCoordTransform;
|
class GrCoordTransform;
|
||||||
class GrEffect;
|
|
||||||
class GrVertexEffect;
|
|
||||||
class SkString;
|
|
||||||
|
|
||||||
/** Provides custom vertex shader, fragment shader, uniform data for a particular stage of the
|
/** Provides custom vertex shader, fragment shader, uniform data for a particular stage of the
|
||||||
Ganesh shading pipeline.
|
Ganesh shading pipeline.
|
||||||
@ -31,7 +29,7 @@ class SkString;
|
|||||||
effect must reach 0 before the thread terminates and the pool is destroyed. To create a static
|
effect must reach 0 before the thread terminates and the pool is destroyed. To create a static
|
||||||
effect use the macro GR_CREATE_STATIC_EFFECT declared below.
|
effect use the macro GR_CREATE_STATIC_EFFECT declared below.
|
||||||
*/
|
*/
|
||||||
class GrEffect : public SkRefCnt {
|
class GrEffect : public GrProgramElement {
|
||||||
public:
|
public:
|
||||||
SK_DECLARE_INST_COUNT(GrEffect)
|
SK_DECLARE_INST_COUNT(GrEffect)
|
||||||
|
|
||||||
@ -205,7 +203,7 @@ private:
|
|||||||
bool fWillUseInputColor;
|
bool fWillUseInputColor;
|
||||||
bool fRequiresVertexShader;
|
bool fRequiresVertexShader;
|
||||||
|
|
||||||
typedef SkRefCnt INHERITED;
|
typedef GrProgramElement INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,5 +215,4 @@ static SkAlignedSStorage<sizeof(EFFECT_CLASS)> g_##NAME##_Storage;
|
|||||||
static GrEffect* NAME SkNEW_PLACEMENT_ARGS(g_##NAME##_Storage.get(), EFFECT_CLASS, ARGS); \
|
static GrEffect* NAME SkNEW_PLACEMENT_ARGS(g_##NAME##_Storage.get(), EFFECT_CLASS, ARGS); \
|
||||||
static SkAutoTDestroy<GrEffect> NAME##_ad(NAME);
|
static SkAutoTDestroy<GrEffect> NAME##_ad(NAME);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -110,7 +110,7 @@ private:
|
|||||||
mutable int32_t fPendingWrites;
|
mutable int32_t fPendingWrites;
|
||||||
|
|
||||||
// These functions need access to the pending read/write member functions.
|
// These functions need access to the pending read/write member functions.
|
||||||
friend class GrDrawState;
|
friend class GrRODrawState;
|
||||||
friend class GrProgramResource;
|
friend class GrProgramResource;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
94
include/gpu/GrProgramElement.h
Normal file
94
include/gpu/GrProgramElement.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GrProgramElement_DEFINED
|
||||||
|
#define GrProgramElement_DEFINED
|
||||||
|
|
||||||
|
#include "SkRefCnt.h"
|
||||||
|
#include "SkTArray.h"
|
||||||
|
|
||||||
|
class GrProgramResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for GrEffect (and future GrGeometryProcessor). GrDrawState uses this to manage
|
||||||
|
* transitioning a GrEffect from being owned by a client to being scheduled for execution. It
|
||||||
|
* converts resources owned by the effect from being ref'ed to having pending reads/writes.
|
||||||
|
*
|
||||||
|
* All GrGpuResource objects owned by a GrProgramElement or derived classes (either directly or
|
||||||
|
* indirectly) must be wrapped in a GrProgramResource and registered with the GrProgramElement using
|
||||||
|
* addGrProgramResource(). This allows the regular refs to be converted to pending IO events
|
||||||
|
* when the program element is scheduled for deferred execution.
|
||||||
|
*/
|
||||||
|
class GrProgramElement : public SkNoncopyable {
|
||||||
|
public:
|
||||||
|
SK_DECLARE_INST_COUNT_ROOT(GrProgramElement)
|
||||||
|
|
||||||
|
virtual ~GrProgramElement() {
|
||||||
|
// fRefCnt can be one when an effect is created statically using GR_CREATE_STATIC_EFFECT
|
||||||
|
SkASSERT((0 == fRefCnt || 1 == fRefCnt) && 0 == fPendingExecutions);
|
||||||
|
// Set to invalid values.
|
||||||
|
SkDEBUGCODE(fRefCnt = fPendingExecutions = -10;)
|
||||||
|
}
|
||||||
|
|
||||||
|
void ref() const {
|
||||||
|
// Once the ref cnt reaches zero it should never be ref'ed again.
|
||||||
|
SkASSERT(fRefCnt > 0);
|
||||||
|
this->validate();
|
||||||
|
++fRefCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unref() const {
|
||||||
|
this->validate();
|
||||||
|
--fRefCnt;
|
||||||
|
if (0 == fRefCnt && 0 == fPendingExecutions) {
|
||||||
|
SkDELETE(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate() const {
|
||||||
|
#ifdef SK_DEBUG
|
||||||
|
SkASSERT(fRefCnt >= 0);
|
||||||
|
SkASSERT(fPendingExecutions >= 0);
|
||||||
|
SkASSERT(fRefCnt + fPendingExecutions > 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
GrProgramElement() : fRefCnt(1), fPendingExecutions(0) {}
|
||||||
|
|
||||||
|
/** Subclasses registers their resources using this function. It is assumed the GrProgramResouce
|
||||||
|
is and will remain owned by the subclass and this function will retain a raw ptr. Once a
|
||||||
|
GrProgramResource is registered its setResource must not be called.
|
||||||
|
*/
|
||||||
|
void addProgramResource(const GrProgramResource* res) {
|
||||||
|
fProgramResources.push_back(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void convertRefToPendingExecution() const;
|
||||||
|
|
||||||
|
void completedExecution() const {
|
||||||
|
this->validate();
|
||||||
|
--fPendingExecutions;
|
||||||
|
if (0 == fRefCnt && 0 == fPendingExecutions) {
|
||||||
|
SkDELETE(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable int32_t fRefCnt;
|
||||||
|
// Count of deferred executions not yet issued to the 3D API.
|
||||||
|
mutable int32_t fPendingExecutions;
|
||||||
|
|
||||||
|
SkSTArray<4, const GrProgramResource*, true> fProgramResources;
|
||||||
|
|
||||||
|
// Only this class can access convertRefToPendingExecution() and completedExecution().
|
||||||
|
template <typename T> friend class GrProgramElementRef;
|
||||||
|
|
||||||
|
typedef SkNoncopyable INHERITED;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
73
include/gpu/GrProgramElementRef.h
Normal file
73
include/gpu/GrProgramElementRef.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GrProgramElementRef_DEFINED
|
||||||
|
#define GrProgramElementRef_DEFINED
|
||||||
|
|
||||||
|
#include "SkRefCnt.h"
|
||||||
|
#include "GrTypes.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for owning a GrProgramElement subclass and being able to convert a ref to pending
|
||||||
|
* execution. It is like an SkAutoTUnref for program elements whose execution can be deferred. Once
|
||||||
|
* in the pending execution state it is illegal to change the object that is owned by the
|
||||||
|
* GrProgramElementRef. Its destructor will either unref the GrProgramElement or signal that
|
||||||
|
* the pending execution has completed, depending on whether convertToPendingExec() was called.
|
||||||
|
*/
|
||||||
|
template <typename T> class GrProgramElementRef : SkNoncopyable {
|
||||||
|
public:
|
||||||
|
GrProgramElementRef() : fOwnPendingExec(false), fObj(NULL) {};
|
||||||
|
|
||||||
|
// Adopts a ref from the caller.
|
||||||
|
explicit GrProgramElementRef(T* obj) : fOwnPendingExec(false), fObj(obj) {}
|
||||||
|
|
||||||
|
// Adopts a ref from the caller. Do not call after convertToPendingExec.
|
||||||
|
void reset(T* obj) {
|
||||||
|
SkASSERT(!fOwnPendingExec);
|
||||||
|
SkSafeUnref(fObj);
|
||||||
|
fObj = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void convertToPendingExec() {
|
||||||
|
SkASSERT(!fOwnPendingExec);
|
||||||
|
fObj->convertRefToPendingExecution();
|
||||||
|
fOwnPendingExec = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* get() const { return fObj; }
|
||||||
|
operator T*() { return fObj; }
|
||||||
|
|
||||||
|
/** If T is const, the type returned from operator-> will also be const. */
|
||||||
|
typedef typename SkTConstType<typename SkAutoTUnref<T>::BlockRef<T>,
|
||||||
|
SkTIsConst<T>::value>::type BlockRefType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GrProgramElementRef assumes ownership of the ref and manages converting the ref to a
|
||||||
|
* pending execution. As a result, it is an error for the user to ref or unref through
|
||||||
|
* GrProgramElementRef. Therefore operator-> returns BlockRef<T>*.
|
||||||
|
*/
|
||||||
|
BlockRefType *operator->() const {
|
||||||
|
return static_cast<BlockRefType*>(fObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
~GrProgramElementRef() {
|
||||||
|
if (NULL != fObj) {
|
||||||
|
if (fOwnPendingExec) {
|
||||||
|
fObj->completedExecution();
|
||||||
|
} else {
|
||||||
|
fObj->unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool fOwnPendingExec;
|
||||||
|
T* fObj;
|
||||||
|
|
||||||
|
typedef SkNoncopyable INHERITED;
|
||||||
|
};
|
||||||
|
#endif
|
73
include/gpu/GrProgramResource.h
Normal file
73
include/gpu/GrProgramResource.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GrProgramResource_DEFINED
|
||||||
|
#define GrProgramResource_DEFINED
|
||||||
|
|
||||||
|
#include "SkRefCnt.h"
|
||||||
|
|
||||||
|
class GrGpuResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that wraps a resource referenced by a GrProgramElement or GrDrawState. It manages
|
||||||
|
* converting refs to pending io operations. Like SkAutoTUnref, its constructor and setter adopt
|
||||||
|
* a ref from their caller. This class is intended only for internal use in core Gr code.
|
||||||
|
*/
|
||||||
|
class GrProgramResource : SkNoncopyable {
|
||||||
|
public:
|
||||||
|
enum IOType {
|
||||||
|
kRead_IOType,
|
||||||
|
kWrite_IOType,
|
||||||
|
kRW_IOType,
|
||||||
|
|
||||||
|
kNone_IOType, // For internal use only, don't specify to constructor or setResource().
|
||||||
|
};
|
||||||
|
|
||||||
|
SK_DECLARE_INST_COUNT_ROOT(GrProgramResource);
|
||||||
|
GrProgramResource();
|
||||||
|
|
||||||
|
/** Adopts a ref from the caller. ioType expresses what type of IO operations will be marked as
|
||||||
|
pending on the resource when markPendingIO is called. */
|
||||||
|
explicit GrProgramResource(GrGpuResource*, IOType ioType);
|
||||||
|
|
||||||
|
~GrProgramResource();
|
||||||
|
|
||||||
|
GrGpuResource* getResource() const { return fResource; }
|
||||||
|
|
||||||
|
/** Adopts a ref from the caller. ioType expresses what type of IO operations will be marked as
|
||||||
|
pending on the resource when markPendingIO is called. */
|
||||||
|
void setResource(GrGpuResource*, IOType ioType);
|
||||||
|
|
||||||
|
/** Does this object own a pending read or write on the resource it is wrapping. */
|
||||||
|
bool ownsPendingIO() const { return fPendingIO; }
|
||||||
|
|
||||||
|
/** Shortcut for calling setResource() with NULL. It cannot be called after markingPendingIO
|
||||||
|
is called. */
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Called by owning GrProgramElement when the program element is first scheduled for
|
||||||
|
execution. */
|
||||||
|
void markPendingIO() const;
|
||||||
|
|
||||||
|
/** Called when the program element/draw state is no longer owned by GrDrawTarget-client code.
|
||||||
|
This lets the cache know that the drawing code will no longer schedule additional reads or
|
||||||
|
writes to the resource using the program element or draw state. */
|
||||||
|
void removeRef() const;
|
||||||
|
|
||||||
|
friend class GrDrawState;
|
||||||
|
friend class GrProgramElement;
|
||||||
|
|
||||||
|
GrGpuResource* fResource;
|
||||||
|
mutable bool fOwnRef;
|
||||||
|
mutable bool fPendingIO;
|
||||||
|
IOType fIOType;
|
||||||
|
|
||||||
|
typedef SkNoncopyable INHERITED;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
30
src/gpu/GrProgramElement.cpp
Normal file
30
src/gpu/GrProgramElement.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "GrProgramElement.h"
|
||||||
|
#include "GrProgramResource.h"
|
||||||
|
|
||||||
|
void GrProgramElement::convertRefToPendingExecution() const {
|
||||||
|
// This function makes it so that all the GrProgramResources own a single ref to their
|
||||||
|
// underlying GrGpuResource if there are any refs to the GrProgramElement and a single
|
||||||
|
// pending read/write if there are any pending executions of the GrProgramElement. The
|
||||||
|
// GrProgramResource will give up its single ref and/or pending read/write in its destructor.
|
||||||
|
SkASSERT(fRefCnt > 0);
|
||||||
|
if (0 == fPendingExecutions) {
|
||||||
|
for (int i = 0; i < fProgramResources.count(); ++i) {
|
||||||
|
fProgramResources[i]->markPendingIO();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++fPendingExecutions;
|
||||||
|
this->unref();
|
||||||
|
if (0 == fRefCnt) {
|
||||||
|
for (int i = 0; i < fProgramResources.count(); ++i) {
|
||||||
|
fProgramResources[i]->removeRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
src/gpu/GrProgramResource.cpp
Normal file
109
src/gpu/GrProgramResource.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GrProgramResource.h"
|
||||||
|
#include "GrGpuResource.h"
|
||||||
|
|
||||||
|
GrProgramResource::GrProgramResource() {
|
||||||
|
fResource = NULL;
|
||||||
|
fOwnRef = false;
|
||||||
|
fPendingIO = false;
|
||||||
|
fIOType = kNone_IOType;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrProgramResource::GrProgramResource(GrGpuResource* resource, IOType ioType) {
|
||||||
|
fResource = NULL;
|
||||||
|
fOwnRef = false;
|
||||||
|
fPendingIO = false;
|
||||||
|
this->setResource(resource, ioType);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrProgramResource::~GrProgramResource() {
|
||||||
|
if (fOwnRef) {
|
||||||
|
SkASSERT(NULL != fResource);
|
||||||
|
fResource->unref();
|
||||||
|
}
|
||||||
|
if (fPendingIO) {
|
||||||
|
switch (fIOType) {
|
||||||
|
case kNone_IOType:
|
||||||
|
SkFAIL("Shouldn't get here if fIOType is kNone.");
|
||||||
|
break;
|
||||||
|
case kRead_IOType:
|
||||||
|
fResource->completedRead();
|
||||||
|
break;
|
||||||
|
case kWrite_IOType:
|
||||||
|
fResource->completedWrite();
|
||||||
|
break;
|
||||||
|
case kRW_IOType:
|
||||||
|
fResource->completedRead();
|
||||||
|
fResource->completedWrite();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrProgramResource::reset() {
|
||||||
|
SkASSERT(!fPendingIO);
|
||||||
|
SkASSERT((NULL != fResource) == fOwnRef);
|
||||||
|
if (fOwnRef) {
|
||||||
|
fResource->unref();
|
||||||
|
fOwnRef = false;
|
||||||
|
fResource = NULL;
|
||||||
|
fIOType = kNone_IOType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrProgramResource::setResource(GrGpuResource* resource, IOType ioType) {
|
||||||
|
SkASSERT(!fPendingIO);
|
||||||
|
SkASSERT((NULL != fResource) == fOwnRef);
|
||||||
|
SkSafeUnref(fResource);
|
||||||
|
if (NULL == resource) {
|
||||||
|
fResource = NULL;
|
||||||
|
fOwnRef = false;
|
||||||
|
fIOType = kNone_IOType;
|
||||||
|
} else {
|
||||||
|
SkASSERT(kNone_IOType != ioType);
|
||||||
|
fResource = resource;
|
||||||
|
fOwnRef = true;
|
||||||
|
fIOType = ioType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrProgramResource::markPendingIO() const {
|
||||||
|
// This should only be called once, when the owning GrProgramElement gets its first
|
||||||
|
// pendingExecution ref.
|
||||||
|
SkASSERT(!fPendingIO);
|
||||||
|
SkASSERT(NULL != fResource);
|
||||||
|
fPendingIO = true;
|
||||||
|
switch (fIOType) {
|
||||||
|
case kNone_IOType:
|
||||||
|
SkFAIL("GrProgramResource with neither reads nor writes?");
|
||||||
|
break;
|
||||||
|
case kRead_IOType:
|
||||||
|
fResource->addPendingRead();
|
||||||
|
break;
|
||||||
|
case kWrite_IOType:
|
||||||
|
fResource->addPendingWrite();
|
||||||
|
break;
|
||||||
|
case kRW_IOType:
|
||||||
|
fResource->addPendingRead();
|
||||||
|
fResource->addPendingWrite();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrProgramResource::removeRef() const {
|
||||||
|
// This should only be called once, when the owners last ref goes away and
|
||||||
|
// there is a pending execution.
|
||||||
|
SkASSERT(fOwnRef);
|
||||||
|
SkASSERT(fPendingIO);
|
||||||
|
SkASSERT(kNone_IOType != fIOType);
|
||||||
|
SkASSERT(NULL != fResource);
|
||||||
|
fResource->unref();
|
||||||
|
fOwnRef = false;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user