aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/ccpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/ccpr')
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageOp.cpp28
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageOp.h9
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageProcessor.cpp26
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageProcessor.h145
-rw-r--r--src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp48
5 files changed, 124 insertions, 132 deletions
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.cpp b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
index 5775c5aa86..57b73bf935 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.cpp
@@ -393,32 +393,28 @@ void GrCCPRCoverageOp::onExecute(GrOpFlushState* flushState) {
fDynamicStatesScratchBuffer.reserve(1 + fScissorBatches.count());
// Triangles.
- auto constexpr kTrianglesGrPrimitiveType = GrCCPRCoverageProcessor::kTrianglesGrPrimitiveType;
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleHulls,
- kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
+ &PrimitiveTallies::fTriangles);
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleEdges,
- kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
+ &PrimitiveTallies::fTriangles);
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kTriangleCorners,
- kTrianglesGrPrimitiveType, 3, &PrimitiveTallies::fTriangles);
+ &PrimitiveTallies::fTriangles);
// Quadratics.
- auto constexpr kQuadraticsGrPrimitiveType = GrCCPRCoverageProcessor::kQuadraticsGrPrimitiveType;
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticHulls,
- kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
+ &PrimitiveTallies::fQuadratics);
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kQuadraticCorners,
- kQuadraticsGrPrimitiveType, 3, &PrimitiveTallies::fQuadratics);
+ &PrimitiveTallies::fQuadratics);
// Cubics.
- auto constexpr kCubicsGrPrimitiveType = GrCCPRCoverageProcessor::kCubicsGrPrimitiveType;
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kCubicHulls,
- kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fCubics);
+ &PrimitiveTallies::fCubics);
this->drawMaskPrimitives(flushState, pipeline, RenderPass::kCubicCorners,
- kCubicsGrPrimitiveType, 4, &PrimitiveTallies::fCubics);
+ &PrimitiveTallies::fCubics);
}
void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
GrCCPRCoverageProcessor::RenderPass renderPass,
- GrPrimitiveType primType, int vertexCount,
int PrimitiveTallies::* instanceType) const {
using ScissorMode = GrCCPRCoverageOpsBuilder::ScissorMode;
SkASSERT(pipeline.getScissorState().enabled());
@@ -426,11 +422,12 @@ void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPi
fMeshesScratchBuffer.reset();
fDynamicStatesScratchBuffer.reset();
+ GrCCPRCoverageProcessor proc(renderPass);
+
if (const int instanceCount = fInstanceCounts[(int)ScissorMode::kNonScissored].*instanceType) {
SkASSERT(instanceCount > 0);
const int baseInstance = fBaseInstances[(int)ScissorMode::kNonScissored].*instanceType;
- GrMesh& mesh = fMeshesScratchBuffer.emplace_back(primType);
- mesh.setInstanced(fInstanceBuffer.get(), instanceCount, baseInstance, vertexCount);
+ proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance, &fMeshesScratchBuffer);
fDynamicStatesScratchBuffer.push_back().fScissorRect.setXYWH(0, 0, fDrawBounds.width(),
fDrawBounds.height());
}
@@ -444,8 +441,8 @@ void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPi
continue;
}
SkASSERT(instanceCount > 0);
- GrMesh& mesh = fMeshesScratchBuffer.emplace_back(primType);
- mesh.setInstanced(fInstanceBuffer.get(), instanceCount, baseInstance, vertexCount);
+ proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance,
+ &fMeshesScratchBuffer);
fDynamicStatesScratchBuffer.push_back().fScissorRect = batch.fScissor;
baseInstance += instanceCount;
}
@@ -454,7 +451,6 @@ void GrCCPRCoverageOp::drawMaskPrimitives(GrOpFlushState* flushState, const GrPi
SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
if (!fMeshesScratchBuffer.empty()) {
- GrCCPRCoverageProcessor proc(renderPass);
SkASSERT(flushState->rtCommandBuffer());
flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
fDynamicStatesScratchBuffer.begin(),
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.h b/src/gpu/ccpr/GrCCPRCoverageOp.h
index 571e29a8cf..7957586958 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOp.h
+++ b/src/gpu/ccpr/GrCCPRCoverageOp.h
@@ -158,9 +158,8 @@ private:
const PrimitiveTallies baseInstances[kNumScissorModes],
const PrimitiveTallies endInstances[kNumScissorModes]);
- void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&,
- const GrCCPRCoverageProcessor::RenderPass, GrPrimitiveType,
- int vertexCount, int PrimitiveTallies::* instanceType) const;
+ void drawMaskPrimitives(GrOpFlushState*, const GrPipeline&, GrCCPRCoverageProcessor::RenderPass,
+ int PrimitiveTallies::* instanceType) const;
sk_sp<GrBuffer> fInstanceBuffer;
PrimitiveTallies fBaseInstances[kNumScissorModes];
@@ -168,8 +167,8 @@ private:
const SkTArray<ScissorBatch, true> fScissorBatches;
const SkISize fDrawBounds;
- mutable SkTArray<GrMesh> fMeshesScratchBuffer;
- mutable SkTArray<GrPipeline::DynamicState> fDynamicStatesScratchBuffer;
+ mutable SkTArray<GrMesh, true> fMeshesScratchBuffer;
+ mutable SkTArray<GrPipeline::DynamicState, true> fDynamicStatesScratchBuffer;
friend class GrCCPRCoverageOpsBuilder;
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
index 61e431a2c9..c7b80390e9 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.cpp
@@ -13,32 +13,6 @@
#include "ccpr/GrCCPRTriangleShader.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
-GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(RenderPass renderPass)
- : INHERITED(kGrCCPRCoverageProcessor_ClassID)
- , fRenderPass(renderPass) {
- if (RenderPassIsCubic(fRenderPass)) {
- this->addInstanceAttrib("X", kFloat4_GrVertexAttribType);
- this->addInstanceAttrib("Y", kFloat4_GrVertexAttribType);
-
- SkASSERT(offsetof(CubicInstance, fX) ==
- this->getInstanceAttrib(InstanceAttribs::kX).fOffsetInRecord);
- SkASSERT(offsetof(CubicInstance, fY) ==
- this->getInstanceAttrib(InstanceAttribs::kY).fOffsetInRecord);
- SkASSERT(sizeof(CubicInstance) == this->getInstanceStride());
- } else {
- this->addInstanceAttrib("X", kFloat3_GrVertexAttribType);
- this->addInstanceAttrib("Y", kFloat3_GrVertexAttribType);
-
- SkASSERT(offsetof(TriangleInstance, fX) ==
- this->getInstanceAttrib(InstanceAttribs::kX).fOffsetInRecord);
- SkASSERT(offsetof(TriangleInstance, fY) ==
- this->getInstanceAttrib(InstanceAttribs::kY).fOffsetInRecord);
- SkASSERT(sizeof(TriangleInstance) == this->getInstanceStride());
- }
-
- this->setWillUseGeoShader();
-}
-
void GrCCPRCoverageProcessor::Shader::emitVaryings(GrGLSLVaryingHandler* varyingHandler,
SkString* code, const char* position,
const char* coverage, const char* wind) {
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor.h b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
index 7ecb888005..dfd2e249c4 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor.h
@@ -15,6 +15,7 @@
class GrGLSLPPFragmentBuilder;
class GrGLSLShaderBuilder;
+class GrMesh;
/**
* This is the geometry processor for the simple convex primitive shapes (triangles and closed curve
@@ -26,22 +27,12 @@ class GrGLSLShaderBuilder;
* below). Once all of a path's primitives have been drawn, the render target contains a composite
* coverage count that can then be used to draw the path (see GrCCPRPathProcessor).
*
- * Draw calls are instanced. They use use the corresponding GrPrimitiveTypes as defined below.
- * Caller fills out the primitives' atlas-space vertices and control points in instance arrays
- * using the provided structs below. There are no vertex attribs.
+ * To draw a renderer pass, see appendMesh below.
*/
class GrCCPRCoverageProcessor : public GrGeometryProcessor {
public:
- static constexpr GrPrimitiveType kTrianglesGrPrimitiveType = GrPrimitiveType::kTriangles;
- static constexpr GrPrimitiveType kQuadraticsGrPrimitiveType = GrPrimitiveType::kTriangles;
- static constexpr GrPrimitiveType kCubicsGrPrimitiveType = GrPrimitiveType::kLinesAdjacency;
-
- enum class InstanceAttribs : int {
- kX,
- kY
- };
-
- struct TriangleInstance { // Also used by quadratics.
+ // Defines a single triangle or closed quadratic bezier, with transposed x,y point values.
+ struct TriangleInstance {
float fX[3];
float fY[3];
@@ -49,6 +40,7 @@ public:
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans);
};
+ // Defines a single closed cubic bezier, with transposed x,y point values.
struct CubicInstance {
float fX[4];
float fY[4];
@@ -56,11 +48,9 @@ public:
void set(const SkPoint[4], float dx, float dy);
};
- /**
- * All primitive shapes (triangles and convex closed curve segments) require more than one
- * render pass. Here we enumerate every render pass needed in order to produce a complete
- * coverage count mask. This is an exhaustive list of all ccpr coverage shaders.
- */
+ // All primitive shapes (triangles and convex closed curve segments) require more than one
+ // render pass. Here we enumerate every render pass needed in order to produce a complete
+ // coverage count mask. This is an exhaustive list of all ccpr coverage shaders.
enum class RenderPass {
// Triangles.
kTriangleHulls,
@@ -75,42 +65,44 @@ public:
kCubicHulls,
kCubicCorners
};
+ static bool RenderPassIsCubic(RenderPass);
+ static const char* RenderPassName(RenderPass);
- static inline bool RenderPassIsCubic(RenderPass pass) {
- switch (pass) {
- case RenderPass::kTriangleHulls:
- case RenderPass::kTriangleEdges:
- case RenderPass::kTriangleCorners:
- case RenderPass::kQuadraticHulls:
- case RenderPass::kQuadraticCorners:
- return false;
- case RenderPass::kCubicHulls:
- case RenderPass::kCubicCorners:
- return true;
- }
- SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
- return false;
+ GrCCPRCoverageProcessor(RenderPass pass)
+ : INHERITED(kGrCCPRCoverageProcessor_ClassID)
+ , fRenderPass(pass) {
+ this->initGS();
}
- static inline const char* RenderPassName(RenderPass pass) {
- switch (pass) {
- case RenderPass::kTriangleHulls: return "kTriangleHulls";
- case RenderPass::kTriangleEdges: return "kTriangleEdges";
- case RenderPass::kTriangleCorners: return "kTriangleCorners";
- case RenderPass::kQuadraticHulls: return "kQuadraticHulls";
- case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
- case RenderPass::kCubicHulls: return "kCubicHulls";
- case RenderPass::kCubicCorners: return "kCubicCorners";
- }
- SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
- return "";
+ // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array
+ // of either TriangleInstance or CubicInstance, depending on this processor's RendererPass, with
+ // coordinates in the desired shape's final atlas-space position.
+ //
+ // NOTE: Quadratics use TriangleInstance since both have 3 points.
+ void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+ SkTArray<GrMesh, true>* out) {
+ this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out);
}
- /**
- * This serves as the base class for each RenderPass's Shader. It indicates what type of
- * geometry the Impl should generate and provides implementation-independent code to process
- * the inputs and calculate coverage in the fragment Shader.
- */
+ // GrPrimitiveProcessor overrides.
+ const char* name() const override { return RenderPassName(fRenderPass); }
+ SkString dumpInfo() const override {
+ return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
+ }
+ void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+ GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
+
+#ifdef SK_DEBUG
+ // Increases the 1/2 pixel AA bloat by a factor of debugBloat and outputs color instead of
+ // coverage (coverage=+1 -> green, coverage=0 -> black, coverage=-1 -> red).
+ void enableDebugVisualizations(float debugBloat) { fDebugBloat = debugBloat; }
+ bool debugVisualizationsEnabled() const { return fDebugBloat > 0; }
+ float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; }
+#endif
+
+ // This serves as the base class for each RenderPass's Shader. It indicates what type of
+ // geometry the Impl should generate and provides implementation-independent code to process the
+ // inputs and calculate coverage in the fragment Shader.
class Shader {
public:
using TexelBufferHandle = GrGLSLGeometryProcessor::TexelBufferHandle;
@@ -211,27 +203,6 @@ public:
GrGLSLVarying fWind{kHalf_GrSLType, GrGLSLVarying::Scope::kGeoToFrag};
};
- GrCCPRCoverageProcessor(RenderPass);
-
- const char* name() const override { return RenderPassName(fRenderPass); }
- SkString dumpInfo() const override {
- return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
- }
- const Attribute& getInstanceAttrib(InstanceAttribs attribID) const {
- return this->getAttrib((int)attribID);
- }
-
- void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
- GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
-
-#ifdef SK_DEBUG
- // Increases the 1/2 pixel AA bloat by a factor of debugBloat and outputs color instead of
- // coverage (coverage=+1 -> green, coverage=0 -> black, coverage=-1 -> red).
- void enableDebugVisualizations(float debugBloat) { fDebugBloat = debugBloat; }
- bool debugVisualizationsEnabled() const { return fDebugBloat > 0; }
- float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; }
-#endif
-
class GSImpl;
private:
@@ -239,6 +210,10 @@ private:
// accidentally bleed into neighbor pixels.
static constexpr float kAABloatRadius = 0.491111f;
+ void initGS();
+ void appendGSMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+ SkTArray<GrMesh, true>* out);
+
int numInputPoints() const {
return RenderPassIsCubic(fRenderPass) ? 4 : 3;
}
@@ -270,4 +245,34 @@ inline void GrCCPRCoverageProcessor::CubicInstance::set(const SkPoint p[4], floa
(Y + dy).store(&fY);
}
+inline bool GrCCPRCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
+ switch (pass) {
+ case RenderPass::kTriangleHulls:
+ case RenderPass::kTriangleEdges:
+ case RenderPass::kTriangleCorners:
+ case RenderPass::kQuadraticHulls:
+ case RenderPass::kQuadraticCorners:
+ return false;
+ case RenderPass::kCubicHulls:
+ case RenderPass::kCubicCorners:
+ return true;
+ }
+ SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
+ return false;
+}
+
+inline const char* GrCCPRCoverageProcessor::RenderPassName(RenderPass pass) {
+ switch (pass) {
+ case RenderPass::kTriangleHulls: return "kTriangleHulls";
+ case RenderPass::kTriangleEdges: return "kTriangleEdges";
+ case RenderPass::kTriangleCorners: return "kTriangleCorners";
+ case RenderPass::kQuadraticHulls: return "kQuadraticHulls";
+ case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
+ case RenderPass::kCubicHulls: return "kCubicHulls";
+ case RenderPass::kCubicCorners: return "kCubicCorners";
+ }
+ SK_ABORT("Invalid GrCCPRCoverageProcessor::RenderPass");
+ return "";
+}
+
#endif
diff --git a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
index 5c9ba3bfc2..60928b6afc 100644
--- a/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageProcessor_GSImpl.cpp
@@ -7,6 +7,7 @@
#include "GrCCPRCoverageProcessor.h"
+#include "GrMesh.h"
#include "glsl/GrGLSLVertexGeoBuilder.h"
using Shader = GrCCPRCoverageProcessor::Shader;
@@ -26,17 +27,16 @@ protected:
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
- // Vertex shader.
- GrGLSLVertexBuilder* v = args.fVertBuilder;
- v->codeAppendf("float2 self = float2(%s[sk_VertexID], %s[sk_VertexID]);",
- proc.getInstanceAttrib(InstanceAttribs::kX).fName,
- proc.getInstanceAttrib(InstanceAttribs::kY).fName);
- gpArgs->fPositionVar.set(kFloat2_GrSLType, "self");
+ // The vertex shader simply forwards transposed x or y values to the geometry shader.
+ SkASSERT(1 == proc.numAttribs());
+ gpArgs->fPositionVar.set(4 == proc.numInputPoints() ? kFloat4_GrSLType : kFloat3_GrSLType,
+ proc.getAttrib(0).fName);
// Geometry shader.
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName);
varyingHandler->emitAttributes(proc);
+ varyingHandler->setNoPerspective();
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
// Fragment shader.
@@ -52,12 +52,9 @@ protected:
int numInputPoints = proc.numInputPoints();
SkASSERT(3 == numInputPoints || 4 == numInputPoints);
- g->codeAppendf("float%ix2 pts = float%ix2(", numInputPoints, numInputPoints);
- for (int i = 0; i < numInputPoints; ++i) {
- g->codeAppend (i ? ", " : "");
- g->codeAppendf("sk_in[%i].sk_Position.xy", i);
- }
- g->codeAppend (");");
+ const char* posValues = (4 == numInputPoints) ? "sk_Position" : "sk_Position.xyz";
+ g->codeAppendf("float%ix2 pts = transpose(float2x%i(sk_in[0].%s, sk_in[1].%s));",
+ numInputPoints, numInputPoints, posValues, posValues);
GrShaderVar wind("wind", kHalf_GrSLType);
g->declareGlobal(wind);
@@ -89,9 +86,8 @@ protected:
Shader::GeometryVars vars;
fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars);
int maxPoints = this->onEmitGeometryShader(g, wind, emitVertexFn.c_str(), vars);
- InputType inputType = (3 == numInputPoints) ? InputType::kTriangles
- : InputType::kLinesAdjacency;
- g->configure(inputType, OutputType::kTriangleStrip, maxPoints, fShader->getNumSegments());
+ g->configure(InputType::kLines, OutputType::kTriangleStrip, maxPoints,
+ fShader->getNumSegments());
}
virtual int onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind,
@@ -255,6 +251,17 @@ public:
}
};
+void GrCCPRCoverageProcessor::initGS() {
+ if (RenderPassIsCubic(fRenderPass)) {
+ this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); // (See appendMesh.)
+ SkASSERT(sizeof(CubicInstance) == this->getVertexStride() * 2);
+ } else {
+ this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); // (See appendMesh.)
+ SkASSERT(sizeof(TriangleInstance) == this->getVertexStride() * 2);
+ }
+ this->setWillUseGeoShader();
+}
+
GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::CreateGSImpl(std::unique_ptr<Shader> shader) {
switch (shader->getGeometryType()) {
case Shader::GeometryType::kHull:
@@ -267,3 +274,14 @@ GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::CreateGSImpl(std::unique_ptr<
SK_ABORT("Unexpected Shader::GeometryType.");
return nullptr;
}
+
+void GrCCPRCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceCount,
+ int baseInstance, SkTArray<GrMesh, true>* out) {
+ // GSImpl doesn't actually make instanced draw calls. Instead, we feed transposed x,y point
+ // values to the GPU in a regular vertex array and draw kLines (see initGS). Then, each vertex
+ // invocation receives either the shape's x or y values as inputs, which it forwards to the
+ // geometry shader.
+ GrMesh& mesh = out->emplace_back(GrPrimitiveType::kLines);
+ mesh.setNonIndexedNonInstanced(instanceCount * 2);
+ mesh.setVertexData(instanceBuffer, baseInstance * 2);
+}