1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "RenderScript.h" 18#include "rsCppInternal.h" 19 20using namespace android; 21using namespace RSC; 22 23 24void * Allocation::getIDSafe() const { 25 return getID(); 26} 27 28void Allocation::updateCacheInfo(sp<const Type> t) { 29 mCurrentDimX = t->getX(); 30 mCurrentDimY = t->getY(); 31 mCurrentDimZ = t->getZ(); 32 mCurrentCount = mCurrentDimX; 33 if (mCurrentDimY > 1) { 34 mCurrentCount *= mCurrentDimY; 35 } 36 if (mCurrentDimZ > 1) { 37 mCurrentCount *= mCurrentDimZ; 38 } 39} 40 41Allocation::Allocation(void *id, sp<RS> rs, sp<const Type> t, uint32_t usage) : 42 BaseObj(id, rs), mSelectedY(0), mSelectedZ(0), mSelectedLOD(0), 43 mSelectedFace(RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X) { 44 45 if ((usage & ~(RS_ALLOCATION_USAGE_SCRIPT | 46 RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE | 47 RS_ALLOCATION_USAGE_GRAPHICS_VERTEX | 48 RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS | 49 RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET | 50 RS_ALLOCATION_USAGE_IO_INPUT | 51 RS_ALLOCATION_USAGE_IO_OUTPUT | 52 RS_ALLOCATION_USAGE_OEM | 53 RS_ALLOCATION_USAGE_SHARED)) != 0) { 54 ALOGE("Unknown usage specified."); 55 } 56 57 if ((usage & RS_ALLOCATION_USAGE_IO_INPUT) != 0) { 58 mWriteAllowed = false; 59 if ((usage & ~(RS_ALLOCATION_USAGE_IO_INPUT | 60 RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE | 61 RS_ALLOCATION_USAGE_SCRIPT)) != 0) { 62 ALOGE("Invalid usage combination."); 63 } 64 } 65 66 mType = t; 67 mUsage = usage; 68 mAutoPadding = false; 69 if (t != nullptr) { 70 updateCacheInfo(t); 71 } 72 73} 74 75 76void Allocation::validateIsInt64() { 77 RsDataType dt = mType->getElement()->getDataType(); 78 if ((dt == RS_TYPE_SIGNED_64) || (dt == RS_TYPE_UNSIGNED_64)) { 79 return; 80 } 81 ALOGE("64 bit integer source does not match allocation type %i", dt); 82} 83 84void Allocation::validateIsInt32() { 85 RsDataType dt = mType->getElement()->getDataType(); 86 if ((dt == RS_TYPE_SIGNED_32) || (dt == RS_TYPE_UNSIGNED_32)) { 87 return; 88 } 89 ALOGE("32 bit integer source does not match allocation type %i", dt); 90} 91 92void Allocation::validateIsInt16() { 93 RsDataType dt = mType->getElement()->getDataType(); 94 if ((dt == RS_TYPE_SIGNED_16) || (dt == RS_TYPE_UNSIGNED_16)) { 95 return; 96 } 97 ALOGE("16 bit integer source does not match allocation type %i", dt); 98} 99 100void Allocation::validateIsInt8() { 101 RsDataType dt = mType->getElement()->getDataType(); 102 if ((dt == RS_TYPE_SIGNED_8) || (dt == RS_TYPE_UNSIGNED_8)) { 103 return; 104 } 105 ALOGE("8 bit integer source does not match allocation type %i", dt); 106} 107 108void Allocation::validateIsFloat32() { 109 RsDataType dt = mType->getElement()->getDataType(); 110 if (dt == RS_TYPE_FLOAT_32) { 111 return; 112 } 113 ALOGE("32 bit float source does not match allocation type %i", dt); 114} 115 116void Allocation::validateIsFloat64() { 117 RsDataType dt = mType->getElement()->getDataType(); 118 if (dt == RS_TYPE_FLOAT_64) { 119 return; 120 } 121 ALOGE("64 bit float source does not match allocation type %i", dt); 122} 123 124void Allocation::validateIsObject() { 125 RsDataType dt = mType->getElement()->getDataType(); 126 if ((dt == RS_TYPE_ELEMENT) || 127 (dt == RS_TYPE_TYPE) || 128 (dt == RS_TYPE_ALLOCATION) || 129 (dt == RS_TYPE_SAMPLER) || 130 (dt == RS_TYPE_SCRIPT) || 131 (dt == RS_TYPE_MESH) || 132 (dt == RS_TYPE_PROGRAM_FRAGMENT) || 133 (dt == RS_TYPE_PROGRAM_VERTEX) || 134 (dt == RS_TYPE_PROGRAM_RASTER) || 135 (dt == RS_TYPE_PROGRAM_STORE)) { 136 return; 137 } 138 ALOGE("Object source does not match allocation type %i", dt); 139} 140 141void Allocation::updateFromNative() { 142 BaseObj::updateFromNative(); 143 144 const void *typeID = RS::dispatch->AllocationGetType(mRS->getContext(), getID()); 145 if(typeID != nullptr) { 146 sp<const Type> old = mType; 147 sp<Type> t = new Type((void *)typeID, mRS); 148 t->updateFromNative(); 149 updateCacheInfo(t); 150 mType = t; 151 } 152} 153 154void Allocation::syncAll(RsAllocationUsageType srcLocation) { 155 switch (srcLocation) { 156 case RS_ALLOCATION_USAGE_SCRIPT: 157 case RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS: 158 case RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE: 159 case RS_ALLOCATION_USAGE_GRAPHICS_VERTEX: 160 case RS_ALLOCATION_USAGE_SHARED: 161 break; 162 default: 163 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Source must be exactly one usage type."); 164 return; 165 } 166 tryDispatch(mRS, RS::dispatch->AllocationSyncAll(mRS->getContext(), getIDSafe(), srcLocation)); 167} 168 169void * Allocation::getPointer(size_t *stride) { 170 void *p = nullptr; 171 if (!(mUsage & RS_ALLOCATION_USAGE_SHARED)) { 172 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Allocation does not support USAGE_SHARED."); 173 return nullptr; 174 } 175 176 // FIXME: decide if lack of getPointer should cause compat mode 177 if (RS::dispatch->AllocationGetPointer == nullptr) { 178 mRS->throwError(RS_ERROR_RUNTIME_ERROR, "Can't use getPointer on older APIs"); 179 return nullptr; 180 } 181 182 p = RS::dispatch->AllocationGetPointer(mRS->getContext(), getIDSafe(), 0, 183 RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0, stride, sizeof(size_t)); 184 if (mRS->getError() != RS_SUCCESS) { 185 mRS->throwError(RS_ERROR_RUNTIME_ERROR, "Allocation lock failed"); 186 p = nullptr; 187 } 188 return p; 189} 190 191// --------------------------------------------------------------------------- 192//Functions needed for autopadding & unpadding 193static void copyWithPadding(void* ptr, const void* srcPtr, int mSize, int count) { 194 int sizeBytesPad = mSize * 4; 195 int sizeBytes = mSize * 3; 196 uint8_t *dst = static_cast<uint8_t *>(ptr); 197 const uint8_t *src = static_cast<const uint8_t *>(srcPtr); 198 for (int i = 0; i < count; i++) { 199 memcpy(dst, src, sizeBytes); 200 dst += sizeBytesPad; 201 src += sizeBytes; 202 } 203} 204 205static void copyWithUnPadding(void* ptr, const void* srcPtr, int mSize, int count) { 206 int sizeBytesPad = mSize * 4; 207 int sizeBytes = mSize * 3; 208 uint8_t *dst = static_cast<uint8_t *>(ptr); 209 const uint8_t *src = static_cast<const uint8_t *>(srcPtr); 210 for (int i = 0; i < count; i++) { 211 memcpy(dst, src, sizeBytes); 212 dst += sizeBytes; 213 src += sizeBytesPad; 214 } 215} 216// --------------------------------------------------------------------------- 217 218void Allocation::copy1DRangeFrom(uint32_t off, size_t count, const void *data) { 219 220 if(count < 1) { 221 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Count must be >= 1."); 222 return; 223 } 224 if((off + count) > mCurrentCount) { 225 ALOGE("Overflow, Available count %u, got %zu at offset %u.", mCurrentCount, count, off); 226 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Invalid copy specified"); 227 return; 228 } 229 if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) { 230 size_t eSize = mType->getElement()->getSizeBytes(); 231 void *ptr = malloc(eSize * count); 232 copyWithPadding(ptr, data, eSize / 4, count); 233 tryDispatch(mRS, RS::dispatch->Allocation1DData(mRS->getContext(), getIDSafe(), off, mSelectedLOD, 234 count, ptr, count * mType->getElement()->getSizeBytes())); 235 free(ptr); 236 } else { 237 tryDispatch(mRS, RS::dispatch->Allocation1DData(mRS->getContext(), getIDSafe(), off, mSelectedLOD, 238 count, data, count * mType->getElement()->getSizeBytes())); 239 } 240} 241 242void Allocation::copy1DRangeTo(uint32_t off, size_t count, void *data) { 243 if(count < 1) { 244 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Count must be >= 1."); 245 return; 246 } 247 if((off + count) > mCurrentCount) { 248 ALOGE("Overflow, Available count %u, got %zu at offset %u.", mCurrentCount, count, off); 249 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Invalid copy specified"); 250 return; 251 } 252 if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) { 253 size_t eSize = mType->getElement()->getSizeBytes(); 254 void *ptr = malloc(eSize * count); 255 tryDispatch(mRS, RS::dispatch->Allocation1DRead(mRS->getContext(), getIDSafe(), off, mSelectedLOD, 256 count, ptr, count * mType->getElement()->getSizeBytes())); 257 copyWithUnPadding(data, ptr, eSize / 4, count); 258 free(ptr); 259 } else { 260 tryDispatch(mRS, RS::dispatch->Allocation1DRead(mRS->getContext(), getIDSafe(), off, mSelectedLOD, 261 count, data, count * mType->getElement()->getSizeBytes())); 262 } 263} 264 265void Allocation::copy1DRangeFrom(uint32_t off, size_t count, sp<const Allocation> data, 266 uint32_t dataOff) { 267 268 tryDispatch(mRS, RS::dispatch->AllocationCopy2DRange(mRS->getContext(), getIDSafe(), off, 0, 269 mSelectedLOD, mSelectedFace, 270 count, 1, data->getIDSafe(), dataOff, 0, 271 data->mSelectedLOD, data->mSelectedFace)); 272} 273 274void Allocation::copy1DFrom(const void* data) { 275 copy1DRangeFrom(0, mCurrentCount, data); 276} 277 278void Allocation::copy1DTo(void* data) { 279 copy1DRangeTo(0, mCurrentCount, data); 280} 281 282 283void Allocation::validate2DRange(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h) { 284 if (mAdaptedAllocation != nullptr) { 285 286 } else { 287 if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY)) { 288 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Updated region larger than allocation."); 289 } 290 } 291} 292 293void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, 294 const void *data) { 295 validate2DRange(xoff, yoff, w, h); 296 if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) { 297 size_t eSize = mType->getElement()->getSizeBytes(); 298 void *ptr = malloc(eSize * w * h); 299 copyWithPadding(ptr, data, eSize / 4, w * h); 300 tryDispatch(mRS, RS::dispatch->Allocation2DData(mRS->getContext(), getIDSafe(), xoff, 301 yoff, mSelectedLOD, mSelectedFace, 302 w, h, ptr, w * h * mType->getElement()->getSizeBytes(), 303 w * mType->getElement()->getSizeBytes())); 304 free(ptr); 305 } else { 306 tryDispatch(mRS, RS::dispatch->Allocation2DData(mRS->getContext(), getIDSafe(), xoff, 307 yoff, mSelectedLOD, mSelectedFace, 308 w, h, data, w * h * mType->getElement()->getSizeBytes(), 309 w * mType->getElement()->getSizeBytes())); 310 } 311} 312 313void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, 314 sp<const Allocation> data, uint32_t dataXoff, uint32_t dataYoff) { 315 validate2DRange(xoff, yoff, w, h); 316 tryDispatch(mRS, RS::dispatch->AllocationCopy2DRange(mRS->getContext(), getIDSafe(), xoff, yoff, 317 mSelectedLOD, mSelectedFace, 318 w, h, data->getIDSafe(), dataXoff, dataYoff, 319 data->mSelectedLOD, data->mSelectedFace)); 320} 321 322void Allocation::copy2DRangeTo(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, 323 void* data) { 324 validate2DRange(xoff, yoff, w, h); 325 if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) { 326 size_t eSize = mType->getElement()->getSizeBytes(); 327 void *ptr = malloc(eSize * w * h); 328 tryDispatch(mRS, RS::dispatch->Allocation2DRead(mRS->getContext(), getIDSafe(), xoff, yoff, 329 mSelectedLOD, mSelectedFace, w, h, ptr, 330 w * h * mType->getElement()->getSizeBytes(), 331 w * mType->getElement()->getSizeBytes())); 332 copyWithUnPadding(data, ptr, eSize / 4, w * h); 333 free(ptr); 334 } else { 335 tryDispatch(mRS, RS::dispatch->Allocation2DRead(mRS->getContext(), getIDSafe(), xoff, yoff, 336 mSelectedLOD, mSelectedFace, w, h, data, 337 w * h * mType->getElement()->getSizeBytes(), 338 w * mType->getElement()->getSizeBytes())); 339 } 340} 341 342void Allocation::copy2DStridedFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, 343 const void *data, size_t stride) { 344 validate2DRange(xoff, yoff, w, h); 345 tryDispatch(mRS, RS::dispatch->Allocation2DData(mRS->getContext(), getIDSafe(), xoff, yoff, 346 mSelectedLOD, mSelectedFace, w, h, data, 347 w * h * mType->getElement()->getSizeBytes(), stride)); 348} 349 350void Allocation::copy2DStridedFrom(const void* data, size_t stride) { 351 copy2DStridedFrom(0, 0, mCurrentDimX, mCurrentDimY, data, stride); 352} 353 354void Allocation::copy2DStridedTo(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, 355 void *data, size_t stride) { 356 validate2DRange(xoff, yoff, w, h); 357 tryDispatch(mRS, RS::dispatch->Allocation2DRead(mRS->getContext(), getIDSafe(), xoff, yoff, 358 mSelectedLOD, mSelectedFace, w, h, data, 359 w * h * mType->getElement()->getSizeBytes(), stride)); 360} 361 362void Allocation::copy2DStridedTo(void* data, size_t stride) { 363 copy2DStridedTo(0, 0, mCurrentDimX, mCurrentDimY, data, stride); 364} 365 366void Allocation::validate3DRange(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w, 367 uint32_t h, uint32_t d) { 368 if (mAdaptedAllocation != nullptr) { 369 370 } else { 371 if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY) || ((zoff + d) > mCurrentDimZ)) { 372 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Updated region larger than allocation."); 373 } 374 } 375} 376 377void Allocation::copy3DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w, 378 uint32_t h, uint32_t d, const void* data) { 379 validate3DRange(xoff, yoff, zoff, w, h, d); 380 if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) { 381 size_t eSize = mType->getElement()->getSizeBytes(); 382 void *ptr = malloc(eSize * w * h * d); 383 copyWithPadding(ptr, data, eSize / 4, w * h * d); 384 tryDispatch(mRS, RS::dispatch->Allocation3DData(mRS->getContext(), getIDSafe(), xoff, yoff, zoff, 385 mSelectedLOD, w, h, d, ptr, 386 w * h * d * mType->getElement()->getSizeBytes(), 387 w * mType->getElement()->getSizeBytes())); 388 free(ptr); 389 } else { 390 tryDispatch(mRS, RS::dispatch->Allocation3DData(mRS->getContext(), getIDSafe(), xoff, yoff, zoff, 391 mSelectedLOD, w, h, d, data, 392 w * h * d * mType->getElement()->getSizeBytes(), 393 w * mType->getElement()->getSizeBytes())); 394 } 395} 396 397void Allocation::copy3DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w, uint32_t h, uint32_t d, 398 sp<const Allocation> data, uint32_t dataXoff, uint32_t dataYoff, uint32_t dataZoff) { 399 validate3DRange(xoff, yoff, zoff, w, h, d); 400 tryDispatch(mRS, RS::dispatch->AllocationCopy3DRange(mRS->getContext(), getIDSafe(), xoff, yoff, zoff, 401 mSelectedLOD, w, h, d, data->getIDSafe(), 402 dataXoff, dataYoff, dataZoff, data->mSelectedLOD)); 403} 404 405void Allocation::copy3DRangeTo(uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t w, 406 uint32_t h, uint32_t d, void* data) { 407 validate3DRange(xoff, yoff, zoff, w, h, d); 408 if (mAutoPadding && (mType->getElement()->getVectorSize() == 3)) { 409 size_t eSize = mType->getElement()->getSizeBytes(); 410 void *ptr = malloc(eSize * w * h * d); 411 tryDispatch(mRS, RS::dispatch->Allocation3DRead(mRS->getContext(), getIDSafe(), xoff, yoff, zoff, 412 mSelectedLOD, w, h, d, ptr, 413 w * h * d * mType->getElement()->getSizeBytes(), 414 w * mType->getElement()->getSizeBytes())); 415 copyWithUnPadding(data, ptr, eSize / 4, w * h * d); 416 free(ptr); 417 } else { 418 tryDispatch(mRS, RS::dispatch->Allocation3DRead(mRS->getContext(), getIDSafe(), xoff, yoff, zoff, 419 mSelectedLOD, w, h, d, data, 420 w * h * d * mType->getElement()->getSizeBytes(), 421 w * mType->getElement()->getSizeBytes())); 422 } 423} 424 425sp<Allocation> Allocation::createTyped(sp<RS> rs, sp<const Type> type, 426 RsAllocationMipmapControl mipmaps, uint32_t usage) { 427 void *id = 0; 428 if (rs->getError() == RS_SUCCESS) { 429 id = RS::dispatch->AllocationCreateTyped(rs->getContext(), type->getID(), mipmaps, usage, 0); 430 } 431 if (id == 0) { 432 rs->throwError(RS_ERROR_RUNTIME_ERROR, "Allocation creation failed"); 433 return nullptr; 434 } 435 return new Allocation(id, rs, type, usage); 436} 437 438sp<Allocation> Allocation::createTyped(sp<RS> rs, sp<const Type> type, 439 RsAllocationMipmapControl mipmaps, uint32_t usage, 440 void *pointer) { 441 void *id = 0; 442 if (rs->getError() == RS_SUCCESS) { 443 id = RS::dispatch->AllocationCreateTyped(rs->getContext(), type->getID(), mipmaps, usage, 444 (uintptr_t)pointer); 445 } 446 if (id == 0) { 447 rs->throwError(RS_ERROR_RUNTIME_ERROR, "Allocation creation failed"); 448 return nullptr; 449 } 450 return new Allocation(id, rs, type, usage); 451} 452 453sp<Allocation> Allocation::createTyped(sp<RS> rs, sp<const Type> type, 454 uint32_t usage) { 455 return createTyped(rs, type, RS_ALLOCATION_MIPMAP_NONE, usage); 456} 457 458sp<Allocation> Allocation::createSized(sp<RS> rs, sp<const Element> e, 459 size_t count, uint32_t usage) { 460 Type::Builder b(rs, e); 461 b.setX(count); 462 sp<const Type> t = b.create(); 463 464 return createTyped(rs, t, usage); 465} 466 467sp<Allocation> Allocation::createSized2D(sp<RS> rs, sp<const Element> e, 468 size_t x, size_t y, uint32_t usage) { 469 Type::Builder b(rs, e); 470 b.setX(x); 471 b.setY(y); 472 sp<const Type> t = b.create(); 473 474 return createTyped(rs, t, usage); 475} 476 477void Allocation::ioSendOutput() { 478#ifndef RS_COMPATIBILITY_LIB 479 if ((mUsage & RS_ALLOCATION_USAGE_IO_OUTPUT) == 0) { 480 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Can only send buffer if IO_OUTPUT usage specified."); 481 return; 482 } 483 tryDispatch(mRS, RS::dispatch->AllocationIoSend(mRS->getContext(), getID())); 484#endif 485} 486 487void Allocation::ioGetInput() { 488#ifndef RS_COMPATIBILITY_LIB 489 if ((mUsage & RS_ALLOCATION_USAGE_IO_INPUT) == 0) { 490 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Can only get buffer if IO_INPUT usage specified."); 491 return; 492 } 493 tryDispatch(mRS, RS::dispatch->AllocationIoReceive(mRS->getContext(), getID())); 494#endif 495} 496 497#if !defined(RS_SERVER) && !defined(RS_COMPATIBILITY_LIB) 498#include <gui/Surface.h> 499 500RSC::sp<Surface> Allocation::getSurface() { 501 if ((mUsage & RS_ALLOCATION_USAGE_IO_INPUT) == 0) { 502 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Can only get Surface if IO_INPUT usage specified."); 503 return nullptr; 504 } 505 IGraphicBufferProducer *v = (IGraphicBufferProducer *)RS::dispatch->AllocationGetSurface(mRS->getContext(), 506 getID()); 507 android::sp<IGraphicBufferProducer> bp = v; 508 v->decStrong(nullptr); 509 510 return new Surface(bp, true);; 511} 512 513void Allocation::setSurface(RSC::sp<Surface> s) { 514 if ((mUsage & RS_ALLOCATION_USAGE_IO_OUTPUT) == 0) { 515 mRS->throwError(RS_ERROR_INVALID_PARAMETER, "Can only set Surface if IO_OUTPUT usage specified."); 516 return; 517 } 518 tryDispatch(mRS, RS::dispatch->AllocationSetSurface(mRS->getContext(), getID(), 519 static_cast<ANativeWindow *>(s.get()))); 520} 521 522#endif 523 524