diff --git a/native/cocos/bindings/manual/jsb_scene_manual.cpp b/native/cocos/bindings/manual/jsb_scene_manual.cpp index 4dba7ef5ffc..a3474c44a3b 100644 --- a/native/cocos/bindings/manual/jsb_scene_manual.cpp +++ b/native/cocos/bindings/manual/jsb_scene_manual.cpp @@ -677,48 +677,26 @@ static bool js_Model_setInstancedAttribute(se::State &s) // NOLINT(readability-i if (val.toObject()->isArray()) { uint32_t len = 0; val.toObject()->getArrayLength(&len); + + cc::Float32Array value(len); se::Value dataVal; - ccstd::array stackData; - float *pData = nullptr; - bool needFree = false; - - if (len <= static_cast(stackData.size())) { - pData = stackData.data(); - } else { - pData = static_cast(CC_MALLOC(len)); - needFree = true; - } - for (uint32_t i = 0; i < len; ++i) { ok = val.toObject()->getArrayElement(i, &dataVal); CC_ASSERT(ok && dataVal.isNumber()); - pData[i] = dataVal.toFloat(); + value[i] = dataVal.toFloat(); } - cobj->setInstancedAttribute(name, pData, len); - - if (needFree) { - CC_FREE(pData); - } + cobj->setInstancedAttribute(name, value); return true; } if (val.toObject()->isTypedArray()) { - se::Object::TypedArrayType type = val.toObject()->getTypedArrayType(); - switch (type) { - case se::Object::TypedArrayType::FLOAT32: { - uint8_t *data = nullptr; - size_t byteCount = 0; - if (val.toObject()->getTypedArrayData(&data, &byteCount) && data != nullptr && byteCount > 0) { - cobj->setInstancedAttribute(name, reinterpret_cast(data), static_cast(byteCount)); - } - } break; - - default: - // FIXME: - CC_ABORT(); - break; + cc::TypedArray arr; + ok = sevalue_to_native(val, &arr); + SE_PRECONDITION2(ok, false, "Error processing arguments"); + if (ok) { + cobj->setInstancedAttribute(name, arr); } return true; } diff --git a/native/cocos/core/TypedArray.cpp b/native/cocos/core/TypedArray.cpp index 4cf764919af..c9a8f1cd51e 100644 --- a/native/cocos/core/TypedArray.cpp +++ b/native/cocos/core/TypedArray.cpp @@ -121,4 +121,63 @@ void setTypedArrayValue(TypedArray &arr, uint32_t idx, const TypedArrayElementTy #undef TYPEDARRAY_SET_VALUE } +void copyTypedArray(TypedArray &dst, uint32_t dstOffset, const TypedArray &src) { + uint32_t srcLength = getTypedArrayLength(src); + uint32_t dstLength = getTypedArrayLength(dst); + uint32_t srcBytesPerElement = getTypedArrayBytesPerElement(src); + uint32_t dstBytesPerElement = getTypedArrayBytesPerElement(dst); + + // Ensure the destination array can fit the data starting from the given offset + CC_ASSERT(dstOffset + srcLength <= dstLength); + + // Optimization: If src and dst are of the same type, use memcpy for efficiency +#define COPY_TYPED_ARRAY_MEMCPY(type) \ + do { \ + auto *srcArray = ccstd::get_if(&src); \ + auto *dstArray = ccstd::get_if(&dst); \ + if (srcArray != nullptr && dstArray != nullptr) { \ + memcpy(&(*dstArray)[dstOffset], &(*srcArray)[0], \ + srcLength * srcBytesPerElement); \ + return; \ + } \ + } while (false) + + COPY_TYPED_ARRAY_MEMCPY(Float32Array); + COPY_TYPED_ARRAY_MEMCPY(Uint32Array); + COPY_TYPED_ARRAY_MEMCPY(Uint16Array); + COPY_TYPED_ARRAY_MEMCPY(Uint8Array); + COPY_TYPED_ARRAY_MEMCPY(Int32Array); + COPY_TYPED_ARRAY_MEMCPY(Int16Array); + COPY_TYPED_ARRAY_MEMCPY(Int8Array); + COPY_TYPED_ARRAY_MEMCPY(Float64Array); + +#undef COPY_TYPED_ARRAY_MEMCPY + + // Cross-type copy: Use a loop for element-wise copying with type conversion +#define COPY_TYPED_ARRAY_TO_DEST(type) \ + do { \ + auto *dstArray = ccstd::get_if(&dst); \ + if (dstArray != nullptr) { \ + for (uint32_t i = 0; i < srcLength; ++i) { \ + (*dstArray)[dstOffset + i] = getTypedArrayValue(src, i); \ + } \ + return; \ + } \ + } while (false) + + COPY_TYPED_ARRAY_TO_DEST(Float32Array); + COPY_TYPED_ARRAY_TO_DEST(Uint32Array); + COPY_TYPED_ARRAY_TO_DEST(Uint16Array); + COPY_TYPED_ARRAY_TO_DEST(Uint8Array); + COPY_TYPED_ARRAY_TO_DEST(Int32Array); + COPY_TYPED_ARRAY_TO_DEST(Int16Array); + COPY_TYPED_ARRAY_TO_DEST(Int8Array); + COPY_TYPED_ARRAY_TO_DEST(Float64Array); + +#undef COPY_TYPED_ARRAY_TO_DEST + + // If this point is reached, the destination type is unsupported + CC_ASSERTF(false, "Unsupported TypedArray type for destination"); +} + } // namespace cc diff --git a/native/cocos/core/TypedArray.h b/native/cocos/core/TypedArray.h index ced002aa023..99d29d52cfd 100644 --- a/native/cocos/core/TypedArray.h +++ b/native/cocos/core/TypedArray.h @@ -395,4 +395,6 @@ T getTypedArrayElementValue(const TypedArrayElementType &element) { return 0; } +void copyTypedArray(TypedArray &dst, uint32_t dstOffset, const TypedArray &src); + } // namespace cc diff --git a/native/cocos/scene/Model.cpp b/native/cocos/scene/Model.cpp index 1d88206711e..4f9ef50393f 100644 --- a/native/cocos/scene/Model.cpp +++ b/native/cocos/scene/Model.cpp @@ -707,9 +707,9 @@ void Model::updateReflectionProbeId() { _localDataUpdated = true; } -void Model::setInstancedAttribute(const ccstd::string &name, const float *value, uint32_t byteCount) { +void Model::setInstancedAttribute(const ccstd::string &name, const TypedArray &value) { for (const auto &subModel : _subModels) { - subModel->setInstancedAttribute(name, value, byteCount); + subModel->setInstancedAttribute(name, value); } } void Model::setReflectionProbeType(UseReflectionProbeType val) { diff --git a/native/cocos/scene/Model.h b/native/cocos/scene/Model.h index 015a3ace526..ec334533051 100644 --- a/native/cocos/scene/Model.h +++ b/native/cocos/scene/Model.h @@ -135,7 +135,7 @@ class Model : public RefCounted { void onMacroPatchesStateChanged(); void onGeometryChanged(); void setSubModelMesh(index_t idx, RenderingSubMesh *subMesh) const; - void setInstancedAttribute(const ccstd::string &name, const float *value, uint32_t byteCount); + void setInstancedAttribute(const ccstd::string &name, const TypedArray &value); void updateWorldBound(); void updateWorldBoundsForJSSkinningModel(const Vec3 &min, const Vec3 &max); void updateWorldBoundsForJSBakedSkinningModel(geometry::AABB *aabb); diff --git a/native/cocos/scene/SubModel.cpp b/native/cocos/scene/SubModel.cpp index fab9bb1961e..652687d2d2d 100644 --- a/native/cocos/scene/SubModel.cpp +++ b/native/cocos/scene/SubModel.cpp @@ -362,48 +362,7 @@ void SubModel::setSubMesh(RenderingSubMesh *subMesh) { _subMesh = subMesh; } -template -void copyInstancedData(const T& view, const float* value, uint32_t byteCount) { - auto* dstData = view.buffer()->getData() + view.byteOffset(); - uint32_t bLength; - if constexpr (std::is_same_v) { - bLength = byteCount * sizeof(float_t); - } else if constexpr (std::is_same_v) { - bLength = byteCount * sizeof(uint16_t); - } else if constexpr (std::is_same_v) { - bLength = byteCount * sizeof(int16_t); - } else if constexpr (std::is_same_v) { - bLength = byteCount * sizeof(uint32_t); - } else if constexpr (std::is_same_v) { - bLength = byteCount * sizeof(int32_t); - } else if constexpr (std::is_same_v) { - bLength = byteCount * sizeof(uint8_t); - } else if constexpr (std::is_same_v) { - bLength = byteCount * sizeof(int8_t); - } - CC_ASSERT(bLength <= view.byteLength()); - memcpy(dstData, value, bLength); -} - -void handleTypedArray(const cc::TypedArray& view, const float* value, uint32_t byteCount) { - if (ccstd::holds_alternative(view)) { - copyInstancedData(ccstd::get(view), value, byteCount); - } else if (ccstd::holds_alternative(view)) { - copyInstancedData(ccstd::get(view), value, byteCount); - } else if (ccstd::holds_alternative(view)) { - copyInstancedData(ccstd::get(view), value, byteCount); - } else if (ccstd::holds_alternative(view)) { - copyInstancedData(ccstd::get(view), value, byteCount); - } else if (ccstd::holds_alternative(view)) { - copyInstancedData(ccstd::get(view), value, byteCount); - } else if (ccstd::holds_alternative(view)) { - copyInstancedData(ccstd::get(view), value, byteCount); - } else if (ccstd::holds_alternative(view)) { - copyInstancedData(ccstd::get(view), value, byteCount); - } -} - -void SubModel::setInstancedAttribute(const ccstd::string &name, const float *value, uint32_t byteCount) { +void SubModel::setInstancedAttribute(const ccstd::string &name, const TypedArray &value) { const auto &attributes = _instancedAttributeBlock.attributes; auto &views = _instancedAttributeBlock.views; for (size_t i = 0, len = attributes.size(); i < len; ++i) { @@ -417,7 +376,7 @@ void SubModel::setInstancedAttribute(const ccstd::string &name, const float *val case gfx::FormatType::INT: case gfx::FormatType::FLOAT: case gfx::FormatType::UFLOAT: { - handleTypedArray(views[i], value, byteCount); + copyTypedArray(views[i], 0, value); } break; case gfx::FormatType::NONE: default: diff --git a/native/cocos/scene/SubModel.h b/native/cocos/scene/SubModel.h index a8e595f4d3b..780ff53ded7 100644 --- a/native/cocos/scene/SubModel.h +++ b/native/cocos/scene/SubModel.h @@ -66,7 +66,7 @@ class SubModel : public RefCounted { void setSubMesh(RenderingSubMesh *subMesh); inline void setInstancedWorldMatrixIndex(int32_t worldMatrixIndex) { _instancedWorldMatrixIndex = worldMatrixIndex; } inline void setInstancedSHIndex(int32_t index) { _instancedSHIndex = index; } - void setInstancedAttribute(const ccstd::string &name, const float *value, uint32_t byteCount); + void setInstancedAttribute(const ccstd::string &name, const TypedArray &value); inline gfx::DescriptorSet *getDescriptorSet() const { return _descriptorSet; } inline gfx::DescriptorSet *getWorldBoundDescriptorSet() const { return _worldBoundDescriptorSet; }