Skip to content

Commit 5567c5e

Browse files
committed
feat(mesh): speedup vertex data unpacking
1 parent e5c30db commit 5567c5e

2 files changed

Lines changed: 101 additions & 145 deletions

File tree

‎UnityPy/helpers/MeshHelper.py‎

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -375,19 +375,26 @@ def read_vertex_data(self, m_Channels: list[ChannelInfo], m_Streams: list[Stream
375375
swap,
376376
)
377377
else:
378-
componentBytes = bytearray(m_VertexCount * channel_dimension * component_byte_size)
378+
channelSize = channel_dimension * component_byte_size
379379

380-
vertexBaseOffset = m_Stream.offset + m_Channel.offset
381-
for v in range(m_VertexCount):
382-
vertexOffset = vertexBaseOffset + m_Stream.stride * v
383-
for d in range(channel_dimension):
384-
componentOffset = vertexOffset + component_byte_size * d
385-
vertexDataSrc = componentOffset
386-
componentDataSrc = component_byte_size * (v * channel_dimension + d)
387-
buff = m_VertexData.m_DataSize[vertexDataSrc : vertexDataSrc + component_byte_size]
388-
if swap: # swap bytes
389-
buff = buff[::-1]
390-
componentBytes[componentDataSrc : componentDataSrc + component_byte_size] = buff
380+
componentBytes = bytearray(m_VertexCount * channel_dimension * component_byte_size)
381+
vertexData = m_VertexData.m_DataSize
382+
383+
componentOffset = 0
384+
vertexOffset = m_Stream.offset + m_Channel.offset
385+
386+
for _ in range(m_VertexCount):
387+
componentBytes[componentOffset : componentOffset + channelSize] = vertexData[
388+
vertexOffset : vertexOffset + channelSize
389+
]
390+
componentOffset += channelSize
391+
vertexOffset += m_Stream.stride
392+
393+
if swap:
394+
for offset in range(0, len(componentBytes), component_byte_size):
395+
item = componentBytes[offset : offset + component_byte_size]
396+
item.reverse()
397+
componentBytes[offset : offset + component_byte_size] = item
391398

392399
component_data = list(struct.iter_unpack(f">{channel_dimension}{component_dtype}", componentBytes))
393400
self.assign_channel_vertex_data(chn, component_data)

‎UnityPyBoost/Mesh.cpp‎

Lines changed: 82 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Mesh.hpp"
22
#include <Python.h>
3+
#include <string>
34
#include <swap.hpp>
45

56
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
@@ -20,6 +21,64 @@ enum VertexFormat
2021
kVertexFormatSInt32
2122
};
2223

24+
template <uint8_t componentByteSize>
25+
void unpack_vertexdata_template(uint8_t *componentBytes, uint8_t *vertexData, uint32_t m_VertexCount, uint32_t m_StreamOffset, uint32_t m_StreamStride, uint32_t m_ChannelOffset, uint32_t m_ChannelDimension)
26+
{
27+
const auto channelSize = componentByteSize * m_ChannelDimension;
28+
29+
uint8_t *componentCur = componentBytes;
30+
uint8_t *vertexCur = vertexData;
31+
32+
// move vertexCur to the first vertex
33+
vertexCur += m_StreamOffset + m_ChannelOffset;
34+
35+
for (uint32_t v = 0; v < m_VertexCount; v++)
36+
{
37+
memcpy(componentCur, vertexCur, channelSize);
38+
componentCur += channelSize;
39+
vertexCur += m_StreamStride;
40+
}
41+
}
42+
43+
template <uint8_t componentByteSize>
44+
void swap_vertexdata(uint8_t *componentBytes, uint32_t m_VertexCount, uint32_t m_ChannelDimension)
45+
{
46+
if constexpr (componentByteSize == 1)
47+
{
48+
// do nothing
49+
}
50+
else if constexpr (componentByteSize == 2)
51+
{
52+
uint16_t *componentUints = (uint16_t *)componentBytes;
53+
for (uint32_t i = 0; i < m_VertexCount * m_ChannelDimension; i++)
54+
{
55+
swap_any_inplace(componentUints++);
56+
}
57+
}
58+
else if constexpr (componentByteSize == 4)
59+
{
60+
uint32_t *componentUints = (uint32_t *)componentBytes;
61+
for (uint32_t i = 0; i < m_VertexCount * m_ChannelDimension; i++)
62+
{
63+
swap_any_inplace(componentUints++);
64+
}
65+
}
66+
else if constexpr (componentByteSize == 8)
67+
{
68+
uint64_t *componentUints = (uint64_t *)componentBytes;
69+
for (uint32_t i = 0; i < m_VertexCount * m_ChannelDimension; i++)
70+
{
71+
swap_any_inplace(componentUints++);
72+
}
73+
}
74+
else
75+
{
76+
const auto compoentByteSizeStr = std::to_string(componentByteSize);
77+
const auto error_message = "Swap not implemented for this size: " + compoentByteSizeStr;
78+
PyErr_SetString(PyExc_ValueError, error_message.c_str());
79+
}
80+
}
81+
2382
PyObject *unpack_vertexdata(PyObject *self, PyObject *args)
2483
{
2584
// define vars
@@ -63,148 +122,38 @@ PyObject *unpack_vertexdata(PyObject *self, PyObject *args)
63122
}
64123
uint8_t *componentBytes = (uint8_t *)PyBytes_AS_STRING(res);
65124

66-
for (uint32_t v = 0; v < m_VertexCount; v++)
125+
switch (componentByteSize)
67126
{
68-
uint32_t vertexOffset = m_StreamOffset + m_ChannelOffset + m_StreamStride * v;
69-
for (uint32_t d = 0; d < m_ChannelDimension; d++)
127+
case 1:
128+
unpack_vertexdata_template<1>(componentBytes, vertexData, m_VertexCount, m_StreamOffset, m_StreamStride, m_ChannelOffset, m_ChannelDimension);
129+
break;
130+
case 2:
131+
unpack_vertexdata_template<2>(componentBytes, vertexData, m_VertexCount, m_StreamOffset, m_StreamStride, m_ChannelOffset, m_ChannelDimension);
132+
if (swap)
70133
{
71-
uint32_t vertexDataOffset = vertexOffset + componentByteSize * d;
72-
uint32_t componentOffset = componentByteSize * (v * m_ChannelDimension + d);
73-
memcpy(componentBytes + componentOffset, vertexData + vertexDataOffset, componentByteSize);
134+
swap_vertexdata<2>(componentBytes, m_VertexCount, m_ChannelDimension);
74135
}
75-
}
76-
77-
if (swap) // swap bytes
78-
{
79-
if (componentByteSize == 2)
136+
break;
137+
case 4:
138+
unpack_vertexdata_template<4>(componentBytes, vertexData, m_VertexCount, m_StreamOffset, m_StreamStride, m_ChannelOffset, m_ChannelDimension);
139+
if (swap)
80140
{
81-
uint16_t *componentUints = (uint16_t *)componentBytes;
82-
for (uint32_t i = 0; i < componentBytesLength; i += 2)
83-
{
84-
swap_any_inplace(componentUints++);
85-
}
141+
swap_vertexdata<4>(componentBytes, m_VertexCount, m_ChannelDimension);
86142
}
87-
else if (componentByteSize == 4)
143+
break;
144+
case 8:
145+
unpack_vertexdata_template<8>(componentBytes, vertexData, m_VertexCount, m_StreamOffset, m_StreamStride, m_ChannelOffset, m_ChannelDimension);
146+
if (swap)
88147
{
89-
90-
uint32_t *componentUints = (uint32_t *)componentBytes;
91-
for (uint32_t i = 0; i < componentBytesLength; i += 4)
92-
{
93-
swap_any_inplace(componentUints++);
94-
}
148+
swap_vertexdata<8>(componentBytes, m_VertexCount, m_ChannelDimension);
95149
}
150+
break;
151+
default:
152+
PyBuffer_Release(&vertexDataView);
153+
PyErr_SetString(PyExc_ValueError, "Unsupported component byte size");
154+
return nullptr;
96155
}
97156

98157
PyBuffer_Release(&vertexDataView);
99158
return res;
100-
101-
// fast enough in Python
102-
// uint32_t itemCount = componentBytesLength / componentByteSize;
103-
// PyObject *lst = PyList_New(itemCount);
104-
// if (!lst)
105-
// return nullptr;
106-
107-
// switch (format)
108-
// {
109-
// case kVertexFormatFloat:
110-
// {
111-
// float *items = (float *)componentBytes;
112-
// for (uint32_t i = 0; i < itemCount; i++)
113-
// {
114-
// PyList_SetItem(lst, i, PyFloat_FromDouble((double)*items++));
115-
// }
116-
// // result[i] = BitConverter.ToSingle(inputBytes, i * 4);
117-
// break;
118-
// }
119-
// case kVertexFormatFloat16:
120-
// {
121-
// uint16_t *items = (uint16_t *)componentBytes;
122-
// for (uint32_t i = 0; i < itemCount; i++)
123-
// {
124-
// double x = _PyFloat_Unpack2(items++, 0);
125-
// if (x == -1.0 && PyErr_Occurred())
126-
// {
127-
// return nullptr;
128-
// }
129-
// PyList_SetItem(lst, i, PyFloat_FromDouble(x));
130-
// }
131-
// // result[i] = Half.ToHalf(inputBytes, i * 2);
132-
// break;
133-
// }
134-
// case kVertexFormatUNorm8:
135-
// {
136-
// uint8_t *items = componentBytes;
137-
// for (uint32_t i = 0; i < itemCount; i++)
138-
// {
139-
// PyList_SetItem(lst, i, PyFloat_FromDouble((double)(*items++ / 255.0f)));
140-
// }
141-
// // result[i] = inputBytes[i] / 255f;
142-
// break;
143-
// }
144-
// case kVertexFormatSNorm8:
145-
// {
146-
// int8_t *items = (int8_t *)componentBytes;
147-
// for (uint32_t i = 0; i < itemCount; i++)
148-
// {
149-
// PyList_SetItem(lst, i, PyFloat_FromDouble((double)MAX((*items++ / 127.0f), -1.0f)));
150-
// }
151-
// // result[i] = Math.Max((sbyte)inputBytes[i] / 127f, -1f);
152-
// break;
153-
// }
154-
// case kVertexFormatUNorm16:
155-
// {
156-
// uint16_t *items = (uint16_t *)componentBytes;
157-
// for (uint32_t i = 0; i < itemCount; i++)
158-
// {
159-
// PyList_SetItem(lst, i, PyFloat_FromDouble((double)(*items++ / 65535.0f)));
160-
// }
161-
// // result[i] = BitConverter.ToUInt16(inputBytes, i * 2) / 65535f;
162-
// break;
163-
// }
164-
// case kVertexFormatSNorm16:
165-
// {
166-
// int16_t *items = (int16_t *)componentBytes;
167-
// for (uint32_t i = 0; i < itemCount; i++)
168-
// {
169-
// PyList_SetItem(lst, i, PyFloat_FromDouble((double)MAX((*items++ / 32767.0f), -1.0f)));
170-
// }
171-
// // result[i] = Math.Max(BitConverter.ToInt16(inputBytes, i * 2) / 32767f, -1f);
172-
// break;
173-
// }
174-
// case kVertexFormatUInt8:
175-
// case kVertexFormatSInt8:
176-
// {
177-
// uint8_t *items = componentBytes;
178-
// for (uint32_t i = 0; i < itemCount; i++)
179-
// {
180-
// PyList_SetItem(lst, i, PyLong_FromUnsignedLong((uint32_t)*items++));
181-
// }
182-
// // result[i] = inputBytes[i];
183-
// break;
184-
// }
185-
// case kVertexFormatUInt16:
186-
// case kVertexFormatSInt16:
187-
// {
188-
// uint16_t *items = (uint16_t *)componentBytes;
189-
// for (uint32_t i = 0; i < itemCount; i++)
190-
// {
191-
// PyList_SetItem(lst, i, PyLong_FromUnsignedLong((uint32_t)*items++));
192-
// }
193-
// // result[i] = BitConverter.ToInt16(inputBytes, i * 2);
194-
// break;
195-
// }
196-
// case kVertexFormatUInt32:
197-
// case kVertexFormatSInt32:
198-
// {
199-
// uint32_t *items = (uint32_t *)componentBytes;
200-
// for (uint32_t i = 0; i < itemCount; i++)
201-
// {
202-
// PyList_SetItem(lst, i, PyLong_FromUnsignedLong(*items++));
203-
// }
204-
// // result[i] = BitConverter.ToInt32(inputBytes, i * 4);
205-
// break;
206-
// }
207-
// }
208-
// free(componentBytes);
209-
// return lst;
210159
}

0 commit comments

Comments
 (0)