OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "gl/builders/GrGLProgramBuilder.h" | 8 #include "gl/builders/GrGLProgramBuilder.h" |
9 #include "GrGLProgramDesc.h" | 9 #include "GrGLProgramDesc.h" |
10 #include "GrBackendEffectFactory.h" | 10 #include "GrBackendProcessorFactory.h" |
11 #include "GrEffect.h" | 11 #include "GrProcessor.h" |
12 #include "GrGpuGL.h" | 12 #include "GrGpuGL.h" |
13 #include "GrOptDrawState.h" | 13 #include "GrOptDrawState.h" |
14 | 14 |
15 #include "SkChecksum.h" | 15 #include "SkChecksum.h" |
16 | 16 |
17 /** | 17 /** |
18 * The key for an individual coord transform is made up of a matrix type and a b
it that | 18 * The key for an individual coord transform is made up of a matrix type and a b
it that |
19 * indicates the source of the input coords. | 19 * indicates the source of the input coords. |
20 */ | 20 */ |
21 enum { | 21 enum { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
53 } | 53 } |
54 if (kRGB_GrColorComponentFlags & swizzleComponentMask) { | 54 if (kRGB_GrColorComponentFlags & swizzleComponentMask) { |
55 // The 'r', 'g', and/or 'b's must be mapped to 'a' according to our
semantics that | 55 // The 'r', 'g', and/or 'b's must be mapped to 'a' according to our
semantics that |
56 // alpha-only textures smear alpha across all four channels when rea
d. | 56 // alpha-only textures smear alpha across all four channels when rea
d. |
57 return true; | 57 return true; |
58 } | 58 } |
59 } | 59 } |
60 return false; | 60 return false; |
61 } | 61 } |
62 | 62 |
63 static uint32_t gen_attrib_key(const GrEffect* effect) { | 63 static uint32_t gen_attrib_key(const GrGeometryProcessor* effect) { |
64 uint32_t key = 0; | 64 uint32_t key = 0; |
65 | 65 |
66 const GrEffect::VertexAttribArray& vars = effect->getVertexAttribs(); | 66 const GrGeometryProcessor::VertexAttribArray& vars = effect->getVertexAttrib
s(); |
67 int numAttributes = vars.count(); | 67 int numAttributes = vars.count(); |
68 SkASSERT(numAttributes <= 2); | 68 SkASSERT(numAttributes <= 2); |
69 for (int a = 0; a < numAttributes; ++a) { | 69 for (int a = 0; a < numAttributes; ++a) { |
70 uint32_t value = 1 << a; | 70 uint32_t value = 1 << a; |
71 key |= value; | 71 key |= value; |
72 } | 72 } |
73 return key; | 73 return key; |
74 } | 74 } |
75 | 75 |
76 static uint32_t gen_transform_key(const GrEffectStage& effectStage, | 76 static uint32_t gen_transform_key(const GrProcessorStage& effectStage, |
77 bool useExplicitLocalCoords) { | 77 bool useExplicitLocalCoords) { |
78 uint32_t totalKey = 0; | 78 uint32_t totalKey = 0; |
79 int numTransforms = effectStage.getEffect()->numTransforms(); | 79 int numTransforms = effectStage.getProcessor()->numTransforms(); |
80 for (int t = 0; t < numTransforms; ++t) { | 80 for (int t = 0; t < numTransforms; ++t) { |
81 uint32_t key = 0; | 81 uint32_t key = 0; |
82 if (effectStage.isPerspectiveCoordTransform(t, useExplicitLocalCoords))
{ | 82 if (effectStage.isPerspectiveCoordTransform(t, useExplicitLocalCoords))
{ |
83 key |= kGeneral_MatrixType; | 83 key |= kGeneral_MatrixType; |
84 } else { | 84 } else { |
85 key |= kNoPersp_MatrixType; | 85 key |= kNoPersp_MatrixType; |
86 } | 86 } |
87 | 87 |
88 const GrCoordTransform& coordTransform = effectStage.getEffect()->coordT
ransform(t); | 88 const GrCoordTransform& coordTransform = effectStage.getProcessor()->coo
rdTransform(t); |
89 if (kLocal_GrCoordSet != coordTransform.sourceCoords() && useExplicitLoc
alCoords) { | 89 if (kLocal_GrCoordSet != coordTransform.sourceCoords() && useExplicitLoc
alCoords) { |
90 key |= kPositionCoords_Flag; | 90 key |= kPositionCoords_Flag; |
91 } | 91 } |
92 key <<= kTransformKeyBits * t; | 92 key <<= kTransformKeyBits * t; |
93 SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to
overlap | 93 SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to
overlap |
94 totalKey |= key; | 94 totalKey |= key; |
95 } | 95 } |
96 return totalKey; | 96 return totalKey; |
97 } | 97 } |
98 | 98 |
99 static uint32_t gen_texture_key(const GrEffect* effect, const GrGLCaps& caps) { | 99 static uint32_t gen_texture_key(const GrProcessor* effect, const GrGLCaps& caps)
{ |
100 uint32_t key = 0; | 100 uint32_t key = 0; |
101 int numTextures = effect->numTextures(); | 101 int numTextures = effect->numTextures(); |
102 for (int t = 0; t < numTextures; ++t) { | 102 for (int t = 0; t < numTextures; ++t) { |
103 const GrTextureAccess& access = effect->textureAccess(t); | 103 const GrTextureAccess& access = effect->textureAccess(t); |
104 uint32_t configComponentMask = GrPixelConfigComponentMask(access.getText
ure()->config()); | 104 uint32_t configComponentMask = GrPixelConfigComponentMask(access.getText
ure()->config()); |
105 if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.s
wizzleMask())) { | 105 if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.s
wizzleMask())) { |
106 key |= 1 << t; | 106 key |= 1 << t; |
107 } | 107 } |
108 } | 108 } |
109 return key; | 109 return key; |
110 } | 110 } |
111 | 111 |
112 /** | 112 /** |
113 * A function which emits a meta key into the key builder. This is required bec
ause shader code may | 113 * A function which emits a meta key into the key builder. This is required bec
ause shader code may |
114 * be dependent on properties of the effect that the effect itself doesn't use | 114 * be dependent on properties of the effect that the effect itself doesn't use |
115 * in its key (e.g. the pixel format of textures used). So we create a meta-key
for | 115 * in its key (e.g. the pixel format of textures used). So we create a meta-key
for |
116 * every effect using this function. It is also responsible for inserting the ef
fect's class ID | 116 * every effect using this function. It is also responsible for inserting the ef
fect's class ID |
117 * which must be different for every GrEffect subclass. It can fail if an effect
uses too many | 117 * which must be different for every GrProcessor subclass. It can fail if an eff
ect uses too many |
118 * textures, attributes, etc for the space allotted in the meta-key. | 118 * textures, transforms, etc, for the space allotted in the meta-key. |
119 */ | 119 */ |
120 | 120 |
121 static bool gen_effect_meta_key(const GrEffectStage& effectStage, | 121 static uint32_t* get_processor_meta_key(const GrProcessorStage& processorStage, |
122 bool useExplicitLocalCoords, | 122 bool useExplicitLocalCoords, |
123 const GrGLCaps& caps, | 123 const GrGLCaps& caps, |
124 GrEffectKeyBuilder* b) { | 124 GrProcessorKeyBuilder* b) { |
125 | 125 |
126 uint32_t textureKey = gen_texture_key(effectStage.getEffect(), caps); | 126 uint32_t textureKey = gen_texture_key(processorStage.getProcessor(), caps); |
127 uint32_t transformKey = gen_transform_key(effectStage,useExplicitLocalCoords
); | 127 uint32_t transformKey = gen_transform_key(processorStage,useExplicitLocalCoo
rds); |
128 uint32_t attribKey = gen_attrib_key(effectStage.getEffect()); | 128 uint32_t classID = processorStage.getProcessor()->getFactory().effectClassID
(); |
129 uint32_t classID = effectStage.getEffect()->getFactory().effectClassID(); | |
130 | 129 |
131 // Currently we allow 16 bits for each of the above portions of the meta-key
. Fail if they | 130 // Currently we allow 16 bits for each of the above portions of the meta-key
. Fail if they |
132 // don't fit. | 131 // don't fit. |
133 static const uint32_t kMetaKeyInvalidMask = ~((uint32_t) SK_MaxU16); | 132 static const uint32_t kMetaKeyInvalidMask = ~((uint32_t) SK_MaxU16); |
134 if ((textureKey | transformKey | attribKey | classID) & kMetaKeyInvalidMask)
{ | 133 if ((textureKey | transformKey | classID) & kMetaKeyInvalidMask) { |
135 return false; | 134 return NULL; |
136 } | 135 } |
137 | 136 |
138 uint32_t* key = b->add32n(2); | 137 uint32_t* key = b->add32n(2); |
139 key[0] = (textureKey << 16 | transformKey); | 138 key[0] = (textureKey << 16 | transformKey); |
140 key[1] = (classID << 16 | attribKey); | 139 key[1] = (classID << 16); |
141 return true; | 140 return key; |
142 } | 141 } |
143 | 142 |
144 bool GrGLProgramDesc::GetEffectKey(const GrEffectStage& stage, const GrGLCaps& c
aps, | 143 bool GrGLProgramDesc::GetProcessorKey(const GrProcessorStage& stage, |
145 bool useExplicitLocalCoords, GrEffectKeyBuild
er* b, | 144 const GrGLCaps& caps, |
146 uint16_t* effectKeySize) { | 145 bool useExplicitLocalCoords, |
147 const GrBackendEffectFactory& factory = stage.getEffect()->getFactory(); | 146 GrProcessorKeyBuilder* b, |
148 const GrEffect& effect = *stage.getEffect(); | 147 uint16_t* processorKeySize) { |
149 factory.getGLEffectKey(effect, caps, b); | 148 const GrProcessor& effect = *stage.getProcessor(); |
| 149 const GrBackendProcessorFactory& factory = effect.getFactory(); |
| 150 factory.getGLProcessorKey(effect, caps, b); |
150 size_t size = b->size(); | 151 size_t size = b->size(); |
151 if (size > SK_MaxU16) { | 152 if (size > SK_MaxU16) { |
152 *effectKeySize = 0; // suppresses a warning. | 153 *processorKeySize = 0; // suppresses a warning. |
153 return false; | 154 return false; |
154 } | 155 } |
155 *effectKeySize = SkToU16(size); | 156 *processorKeySize = SkToU16(size); |
156 if (!gen_effect_meta_key(stage, useExplicitLocalCoords, caps, b)) { | 157 if (NULL == get_processor_meta_key(stage, useExplicitLocalCoords, caps, b))
{ |
157 return false; | 158 return false; |
158 } | 159 } |
159 return true; | 160 return true; |
160 } | 161 } |
161 | 162 |
| 163 bool GrGLProgramDesc::GetGeometryProcessorKey(const GrGeometryStage& stage, |
| 164 const GrGLCaps& caps, |
| 165 bool useExplicitLocalCoords, |
| 166 GrProcessorKeyBuilder* b, |
| 167 uint16_t* processorKeySize) { |
| 168 const GrProcessor& effect = *stage.getProcessor(); |
| 169 const GrBackendProcessorFactory& factory = effect.getFactory(); |
| 170 factory.getGLProcessorKey(effect, caps, b); |
| 171 size_t size = b->size(); |
| 172 if (size > SK_MaxU16) { |
| 173 *processorKeySize = 0; // suppresses a warning. |
| 174 return false; |
| 175 } |
| 176 *processorKeySize = SkToU16(size); |
| 177 uint32_t* key = get_processor_meta_key(stage, useExplicitLocalCoords, caps,
b); |
| 178 if (NULL == key) { |
| 179 return false; |
| 180 } |
| 181 uint32_t attribKey = gen_attrib_key(stage.getGeometryProcessor()); |
| 182 |
| 183 // Currently we allow 16 bits for each of the above portions of the meta-key
. Fail if they |
| 184 // don't fit. |
| 185 static const uint32_t kMetaKeyInvalidMask = ~((uint32_t) SK_MaxU16); |
| 186 if ((attribKey) & kMetaKeyInvalidMask) { |
| 187 return false; |
| 188 } |
| 189 |
| 190 key[1] |= attribKey; |
| 191 return true; |
| 192 } |
| 193 |
| 194 |
162 bool GrGLProgramDesc::Build(const GrOptDrawState& optState, | 195 bool GrGLProgramDesc::Build(const GrOptDrawState& optState, |
163 GrGpu::DrawType drawType, | 196 GrGpu::DrawType drawType, |
164 GrBlendCoeff srcCoeff, | 197 GrBlendCoeff srcCoeff, |
165 GrBlendCoeff dstCoeff, | 198 GrBlendCoeff dstCoeff, |
166 GrGpuGL* gpu, | 199 GrGpuGL* gpu, |
167 const GrDeviceCoordTexture* dstCopy, | 200 const GrDeviceCoordTexture* dstCopy, |
168 const GrEffectStage** geometryProcessor, | 201 const GrGeometryStage** geometryProcessor, |
169 SkTArray<const GrEffectStage*, true>* colorStages, | 202 SkTArray<const GrFragmentStage*, true>* colorStages, |
170 SkTArray<const GrEffectStage*, true>* coverageStages
, | 203 SkTArray<const GrFragmentStage*, true>* coverageStag
es, |
171 GrGLProgramDesc* desc) { | 204 GrGLProgramDesc* desc) { |
172 colorStages->reset(); | 205 colorStages->reset(); |
173 coverageStages->reset(); | 206 coverageStages->reset(); |
174 | 207 |
175 bool inputColorIsUsed = optState.inputColorIsUsed(); | 208 bool inputColorIsUsed = optState.inputColorIsUsed(); |
176 bool inputCoverageIsUsed = optState.inputCoverageIsUsed(); | 209 bool inputCoverageIsUsed = optState.inputCoverageIsUsed(); |
177 | 210 |
178 // The descriptor is used as a cache key. Thus when a field of the | 211 // The descriptor is used as a cache key. Thus when a field of the |
179 // descriptor will not affect program generation (because of the attribute | 212 // descriptor will not affect program generation (because of the attribute |
180 // bindings in use or other descriptor field settings) it should be set | 213 // bindings in use or other descriptor field settings) it should be set |
181 // to a canonical value to avoid duplicate programs with different keys. | 214 // to a canonical value to avoid duplicate programs with different keys. |
182 | 215 |
183 bool requiresLocalCoordAttrib = optState.requiresLocalCoordAttrib(); | 216 bool requiresLocalCoordAttrib = optState.requiresLocalCoordAttrib(); |
184 | 217 |
185 int numStages = optState.numTotalStages(); | 218 int numStages = optState.numTotalStages(); |
186 | 219 |
187 GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t)); | 220 GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t)); |
188 // Make room for everything up to and including the array of offsets to effe
ct keys. | 221 // Make room for everything up to and including the array of offsets to effe
ct keys. |
189 desc->fKey.reset(); | 222 desc->fKey.reset(); |
190 desc->fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_
t) * numStages); | 223 desc->fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_
t) * numStages); |
191 | 224 |
192 int offsetAndSizeIndex = 0; | 225 int offsetAndSizeIndex = 0; |
193 bool effectKeySuccess = true; | |
194 | 226 |
195 KeyHeader* header = desc->header(); | 227 KeyHeader* header = desc->header(); |
196 // make sure any padding in the header is zeroed. | 228 // make sure any padding in the header is zeroed. |
197 memset(desc->header(), 0, kHeaderSize); | 229 memset(desc->header(), 0, kHeaderSize); |
198 | 230 |
199 // We can only have one effect which touches the vertex shader | 231 // We can only have one effect which touches the vertex shader |
200 if (optState.hasGeometryProcessor()) { | 232 if (optState.hasGeometryProcessor()) { |
201 uint16_t* offsetAndSize = | 233 uint16_t* offsetAndSize = |
202 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffse
tsAndLengthOffset + | 234 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffse
tsAndLengthOffset + |
203 offsetAndSizeIndex * 2 * sizeof(uint
16_t)); | 235 offsetAndSizeIndex * 2 * sizeof(uint
16_t)); |
204 | 236 |
205 GrEffectKeyBuilder b(&desc->fKey); | 237 GrProcessorKeyBuilder b(&desc->fKey); |
206 uint16_t effectKeySize; | 238 uint16_t processorKeySize; |
207 uint32_t effectOffset = desc->fKey.count(); | 239 uint32_t processorOffset = desc->fKey.count(); |
208 effectKeySuccess |= GetEffectKey(*optState.getGeometryProcessor(), g
pu->glCaps(), | 240 const GrGeometryStage& gpStage = *optState.getGeometryProcessor(); |
209 requiresLocalCoordAttrib, &b, &effe
ctKeySize); | 241 if (processorOffset > SK_MaxU16 || |
210 effectKeySuccess |= (effectOffset <= SK_MaxU16); | 242 !GetGeometryProcessorKey(gpStage, gpu->glCaps(), requiresLocalCo
ordAttrib, &b, |
| 243 &processorKeySize)) { |
| 244 desc->fKey.reset(); |
| 245 return false; |
| 246 } |
211 | 247 |
212 offsetAndSize[0] = SkToU16(effectOffset); | 248 offsetAndSize[0] = SkToU16(processorOffset); |
213 offsetAndSize[1] = effectKeySize; | 249 offsetAndSize[1] = processorKeySize; |
214 ++offsetAndSizeIndex; | 250 ++offsetAndSizeIndex; |
215 *geometryProcessor = optState.getGeometryProcessor(); | 251 *geometryProcessor = &gpStage; |
216 header->fHasGeometryProcessor = true; | 252 header->fHasGeometryProcessor = true; |
217 } | 253 } |
218 | 254 |
219 for (int s = 0; s < optState.numColorStages(); ++s) { | 255 for (int s = 0; s < optState.numColorStages(); ++s) { |
220 uint16_t* offsetAndSize = | 256 uint16_t* offsetAndSize = |
221 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAn
dLengthOffset + | 257 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAn
dLengthOffset + |
222 offsetAndSizeIndex * 2 * sizeof(uint16_t
)); | 258 offsetAndSizeIndex * 2 * sizeof(uint16_t
)); |
223 | 259 |
224 GrEffectKeyBuilder b(&desc->fKey); | 260 GrProcessorKeyBuilder b(&desc->fKey); |
225 uint16_t effectKeySize; | 261 uint16_t processorKeySize; |
226 uint32_t effectOffset = desc->fKey.count(); | 262 uint32_t processorOffset = desc->fKey.count(); |
227 effectKeySuccess |= GetEffectKey(optState.getColorStage(s), gpu->glCaps(
), | 263 if (processorOffset > SK_MaxU16 || |
228 requiresLocalCoordAttrib, &b, &effectKe
ySize); | 264 !GetProcessorKey(optState.getColorStage(s), gpu->glCaps(), |
229 effectKeySuccess |= (effectOffset <= SK_MaxU16); | 265 requiresLocalCoordAttrib, &b, &processorKeySize
)) { |
| 266 desc->fKey.reset(); |
| 267 return false; |
| 268 } |
230 | 269 |
231 offsetAndSize[0] = SkToU16(effectOffset); | 270 offsetAndSize[0] = SkToU16(processorOffset); |
232 offsetAndSize[1] = effectKeySize; | 271 offsetAndSize[1] = processorKeySize; |
233 ++offsetAndSizeIndex; | 272 ++offsetAndSizeIndex; |
234 } | 273 } |
235 | 274 |
236 for (int s = 0; s < optState.numCoverageStages(); ++s) { | 275 for (int s = 0; s < optState.numCoverageStages(); ++s) { |
237 uint16_t* offsetAndSize = | 276 uint16_t* offsetAndSize = |
238 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAn
dLengthOffset + | 277 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAn
dLengthOffset + |
239 offsetAndSizeIndex * 2 * sizeof(uint16_t
)); | 278 offsetAndSizeIndex * 2 * sizeof(uint16_t
)); |
240 | 279 |
241 GrEffectKeyBuilder b(&desc->fKey); | 280 GrProcessorKeyBuilder b(&desc->fKey); |
242 uint16_t effectKeySize; | 281 uint16_t processorKeySize; |
243 uint32_t effectOffset = desc->fKey.count(); | 282 uint32_t processorOffset = desc->fKey.count(); |
244 effectKeySuccess |= GetEffectKey(optState.getCoverageStage(s), gpu->glCa
ps(), | 283 if (processorOffset > SK_MaxU16 || |
245 requiresLocalCoordAttrib, &b, &effectKe
ySize); | 284 !GetProcessorKey(optState.getCoverageStage(s), gpu->glCaps(), |
246 effectKeySuccess |= (effectOffset <= SK_MaxU16); | 285 requiresLocalCoordAttrib, &b, &processorKeySize
)) { |
| 286 desc->fKey.reset(); |
| 287 return false; |
| 288 } |
247 | 289 |
248 offsetAndSize[0] = SkToU16(effectOffset); | 290 offsetAndSize[0] = SkToU16(processorOffset); |
249 offsetAndSize[1] = effectKeySize; | 291 offsetAndSize[1] = processorKeySize; |
250 ++offsetAndSizeIndex; | 292 ++offsetAndSizeIndex; |
251 } | 293 } |
252 | 294 |
253 if (!effectKeySuccess) { | |
254 desc->fKey.reset(); | |
255 return false; | |
256 } | |
257 | |
258 // Because header is a pointer into the dynamic array, we can't push any new
data into the key | 295 // Because header is a pointer into the dynamic array, we can't push any new
data into the key |
259 // below here. | 296 // below here. |
260 | 297 |
261 | 298 |
262 header->fEmitsPointSize = GrGpu::kDrawPoints_DrawType == drawType; | 299 header->fEmitsPointSize = GrGpu::kDrawPoints_DrawType == drawType; |
263 | 300 |
264 // Currently the experimental GS will only work with triangle prims (and it
doesn't do anything | 301 // Currently the experimental GS will only work with triangle prims (and it
doesn't do anything |
265 // other than pass through values from the VS to the FS anyway). | 302 // other than pass through values from the VS to the FS anyway). |
266 #if GR_GL_EXPERIMENTAL_GS | 303 #if GR_GL_EXPERIMENTAL_GS |
267 #if 0 | 304 #if 0 |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 *checksum = 0; | 412 *checksum = 0; |
376 *checksum = SkChecksum::Compute(reinterpret_cast<uint32_t*>(fKey.begin()), k
eyLength); | 413 *checksum = SkChecksum::Compute(reinterpret_cast<uint32_t*>(fKey.begin()), k
eyLength); |
377 } | 414 } |
378 | 415 |
379 GrGLProgramDesc& GrGLProgramDesc::operator= (const GrGLProgramDesc& other) { | 416 GrGLProgramDesc& GrGLProgramDesc::operator= (const GrGLProgramDesc& other) { |
380 size_t keyLength = other.keyLength(); | 417 size_t keyLength = other.keyLength(); |
381 fKey.reset(keyLength); | 418 fKey.reset(keyLength); |
382 memcpy(fKey.begin(), other.fKey.begin(), keyLength); | 419 memcpy(fKey.begin(), other.fKey.begin(), keyLength); |
383 return *this; | 420 return *this; |
384 } | 421 } |
OLD | NEW |