Skip to content

Commit

Permalink
[v3.8.5] Fix Model.setInstancedAttribute doesn't support attribute of…
Browse files Browse the repository at this point in the history
… INT format. (cocos#17858)

* setInstancedAttribute

* Add copyTypedArray

* Fix Model::setInstancedAttribute and SubModule::setInstancedAttribute.

---------

Co-authored-by: James Chen <[email protected]>
  • Loading branch information
2 people authored and qiuguohua committed Nov 26, 2024
1 parent 4285e63 commit dd9b92f
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 45 deletions.
40 changes: 9 additions & 31 deletions native/cocos/bindings/manual/jsb_scene_manual.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<float, 64> stackData;
float *pData = nullptr;
bool needFree = false;

if (len <= static_cast<uint32_t>(stackData.size())) {
pData = stackData.data();
} else {
pData = static_cast<float *>(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 * sizeof(float));

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 byteLength = 0;
if (val.toObject()->getTypedArrayData(&data, &byteLength) && data != nullptr && byteLength > 0) {
cobj->setInstancedAttribute(name, reinterpret_cast<const float *>(data), static_cast<uint32_t>(byteLength));
}
} 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;
}
Expand Down
59 changes: 59 additions & 0 deletions native/cocos/core/TypedArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<type>(&src); \
auto *dstArray = ccstd::get_if<type>(&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<type>(&dst); \
if (dstArray != nullptr) { \
for (uint32_t i = 0; i < srcLength; ++i) { \
(*dstArray)[dstOffset + i] = getTypedArrayValue<type::value_type>(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
2 changes: 2 additions & 0 deletions native/cocos/core/TypedArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,6 @@ T getTypedArrayElementValue(const TypedArrayElementType &element) {
return 0;
}

void copyTypedArray(TypedArray &dst, uint32_t dstOffset, const TypedArray &src);

} // namespace cc
4 changes: 2 additions & 2 deletions native/cocos/scene/Model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,9 +707,9 @@ void Model::updateReflectionProbeId() {
_localDataUpdated = true;
}

void Model::setInstancedAttribute(const ccstd::string &name, const float *value, uint32_t byteLength) {
void Model::setInstancedAttribute(const ccstd::string &name, const TypedArray &value) {
for (const auto &subModel : _subModels) {
subModel->setInstancedAttribute(name, value, byteLength);
subModel->setInstancedAttribute(name, value);
}
}
void Model::setReflectionProbeType(UseReflectionProbeType val) {
Expand Down
2 changes: 1 addition & 1 deletion native/cocos/scene/Model.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 byteLength);
void setInstancedAttribute(const ccstd::string &name, const TypedArray &value);
void updateWorldBound();
void updateWorldBoundsForJSSkinningModel(const Vec3 &min, const Vec3 &max);
void updateWorldBoundsForJSBakedSkinningModel(geometry::AABB *aabb);
Expand Down
15 changes: 5 additions & 10 deletions native/cocos/scene/SubModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,30 +362,25 @@ void SubModel::setSubMesh(RenderingSubMesh *subMesh) {
_subMesh = subMesh;
}

void SubModel::setInstancedAttribute(const ccstd::string &name, const float *value, uint32_t byteLength) {
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) {
const auto &attribute = attributes[i];
if (attribute.name == name) {
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attribute.format)];
switch (info.type) {
case gfx::FormatType::NONE:
case gfx::FormatType::UNORM:
case gfx::FormatType::SNORM:
case gfx::FormatType::UINT:
case gfx::FormatType::INT: {
CC_ABORT();
} break;
case gfx::FormatType::INT:
case gfx::FormatType::FLOAT:
case gfx::FormatType::UFLOAT: {
CC_ASSERT(ccstd::holds_alternative<Float32Array>(views[i]));
auto &view = ccstd::get<Float32Array>(views[i]);
auto *dstData = reinterpret_cast<float *>(view.buffer()->getData() + view.byteOffset());
CC_ASSERT(byteLength <= view.byteLength());
memcpy(dstData, value, byteLength);
copyTypedArray(views[i], 0, value);
} break;
case gfx::FormatType::NONE:
default:
CC_ABORT();
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion native/cocos/scene/SubModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 byteLength);
void setInstancedAttribute(const ccstd::string &name, const TypedArray &value);

inline gfx::DescriptorSet *getDescriptorSet() const { return _descriptorSet; }
inline gfx::DescriptorSet *getWorldBoundDescriptorSet() const { return _worldBoundDescriptorSet; }
Expand Down

0 comments on commit dd9b92f

Please sign in to comment.