1#include "GeometryProcessing.h"
4#include "Services/TaskManager.h"
5#include "Services/Features.h"
6#include "Platform/Instrumentation.h"
8#include "Foundation/Memory/MemoryBuffer.h"
9#include "Foundation/Platform/Timer.h"
14#include <glm/gtc/type_ptr.hpp>
16#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
17 #include <xmmintrin.h>
32 inline T* alignUpwards(
void* ptr,
size_t alignment=64)
34 auto a = alignment - 1;
35 return reinterpret_cast<T*
>(((size_t)ptr + a)&(~a));
38#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
39 inline __m128 loadVec3(
const float* p)
43 return _mm_loadu_ps(p);
46 __m128 t0 = _mm_castpd_ps(_mm_load_sd((
const double*)p));
47 __m128 t1 = _mm_load_ss(p + 2);
48 return _mm_shuffle_ps(t0, t1, _MM_SHUFFLE(1, 0, 1, 0));
52 inline void storeVec3(
float* p, __m128 v)
54 _mm_store_sd((
double*)p, _mm_castps_pd(v));
55 _mm_store_ss(p + 2, _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)));
59 struct ClearStridedVec3Task
62 uint32_t normalStride;
64 std::atomic<uint64_t>* elapsed_us;
66 ClearStridedVec3Task(
float* normals,
67 uint32_t normalStride,
68 uint32_t ia, uint32_t ib,
69 std::atomic<uint64_t>* elapsed_us):
71 normalStride(normalStride),
73 elapsed_us(elapsed_us)
78 CpuInstrumentationScope(SCOPE_GEOMETRY,
"StrClr");
79 auto timer = Cogs::Timer::startNew();
81 for (uint32_t i = ia; i < ib; i++) {
82 normals[normalStride*i + 0] = 0.0f;
83 normals[normalStride*i + 1] = 0.0f;
84 normals[normalStride*i + 2] = 0.0f;
88 elapsed_us->fetch_add(timer.elapsedMicroseconds());
93 struct TriangleNormalsTask
95 const uint32_t* indices;
97 const float* vertices;
98 uint32_t vertexStride;
100 std::atomic<uint64_t>* elapsed_us;
102 TriangleNormalsTask(
const uint32_t* indices,
104 const float* vertices,
105 uint32_t vertexStride,
106 uint32_t ia, uint32_t ib,
107 std::atomic<uint64_t>* elapsed_us =
nullptr):
111 vertexStride(vertexStride),
113 elapsed_us(elapsed_us)
118 CpuInstrumentationScope(SCOPE_GEOMETRY,
"TriNrm");
119 auto timer = Cogs::Timer::startNew();
120 for (uint32_t i = ia; i < ib; i++) {
121 const auto i0 = indices[3*i + 0];
122 const auto i1 = indices[3*i + 1];
123 const auto i2 = indices[3*i + 2];
124 const auto & a = glm::make_vec3(vertices + vertexStride*i0);
125 const auto & b = glm::make_vec3(vertices + vertexStride*i1);
126 const auto & c = glm::make_vec3(vertices + vertexStride*i2);
127 const auto n = glm::cross(c - a, b - a);
129 *
reinterpret_cast<glm::vec3*
>(normals + 4 * i) = n;
132 elapsed_us->fetch_add(timer.elapsedMicroseconds());
137#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
138 struct TriangleNormalsTaskSSE2a
140 const uint32_t* indices;
141 float* triangleNormals;
142 const float* vertices;
143 uint32_t vertexStride;
145 std::atomic<uint64_t>* elapsed_us;
147 TriangleNormalsTaskSSE2a(
const uint32_t* indices,
148 float* triangleNormals,
149 const float* vertices,
150 uint32_t vertexStride,
151 uint32_t ia, uint32_t ib,
152 std::atomic<uint64_t>* elapsed_us):
154 triangleNormals(triangleNormals),
156 vertexStride(vertexStride),
158 elapsed_us(elapsed_us)
163 CpuInstrumentationScope(SCOPE_GEOMETRY,
"TriNrmSSE2");
164 auto timer = Cogs::Timer::startNew();
167 auto moo = indices + 3 * ia;
168 for (; i + 1 < ib; i+=2) {
169 const auto ix0_a = *moo++;
170 const auto ix1_a = *moo++;
171 const auto ix2_a = *moo++;
173 const auto ix0_b = *moo++;
174 const auto ix1_b = *moo++;
175 const auto ix2_b = *moo++;
177 __m128 a_a = loadVec3(vertices + vertexStride*ix0_a);
178 __m128 a_b = loadVec3(vertices + vertexStride*ix0_b);
179 __m128 b_a = loadVec3(vertices + vertexStride*ix1_a);
180 __m128 b_b = loadVec3(vertices + vertexStride*ix1_b);
181 __m128 c_a = loadVec3(vertices + vertexStride*ix2_a);
182 __m128 c_b = loadVec3(vertices + vertexStride*ix2_b);
184 __m128 ca_xyz_a = _mm_sub_ps(c_a, a_a);
185 __m128 ca_xyz_b = _mm_sub_ps(c_b, a_b);
186 __m128 ca_yzx_a = _mm_shuffle_ps(ca_xyz_a, ca_xyz_a, _MM_SHUFFLE(3, 0, 2, 1));
187 __m128 ca_yzx_b = _mm_shuffle_ps(ca_xyz_b, ca_xyz_b, _MM_SHUFFLE(3, 0, 2, 1));
189 __m128 ba_xyz_a = _mm_sub_ps(b_a, a_a);
190 __m128 ba_xyz_b = _mm_sub_ps(b_b, a_b);
191 __m128 ba_yzx_a = _mm_shuffle_ps(ba_xyz_a, ba_xyz_a, _MM_SHUFFLE(3, 0, 2, 1));
192 __m128 ba_yzx_b = _mm_shuffle_ps(ba_xyz_b, ba_xyz_b, _MM_SHUFFLE(3, 0, 2, 1));
194 __m128 f_zxy_a = _mm_mul_ps(ca_xyz_a, ba_yzx_a);
195 __m128 f_zxy_b = _mm_mul_ps(ca_xyz_b, ba_yzx_b);
196 __m128 g_zxy_a = _mm_mul_ps(ba_xyz_a, ca_yzx_a);
197 __m128 g_zxy_b = _mm_mul_ps(ba_xyz_b, ca_yzx_b);
199 __m128 n_zxy_a = _mm_sub_ps(f_zxy_a, g_zxy_a);
200 __m128 n_zxy_b = _mm_sub_ps(f_zxy_b, g_zxy_b);
201 __m128 n_xyz_a = _mm_shuffle_ps(n_zxy_a, n_zxy_a, _MM_SHUFFLE(3, 0, 2, 1));
202 __m128 n_xyz_b = _mm_shuffle_ps(n_zxy_b, n_zxy_b, _MM_SHUFFLE(3, 0, 2, 1));
204 _mm_store_ps(triangleNormals + 4 * (i + 0), n_xyz_a);
205 _mm_store_ps(triangleNormals + 4 * (i + 1), n_xyz_b);
208 for (; i < ib; i++) {
209 const auto ix0 = indices[3 * i + 0];
210 const auto ix1 = indices[3 * i + 1];
211 const auto ix2 = indices[3 * i + 2];
213 __m128 a = loadVec3(vertices + vertexStride*ix0);
214 __m128 b = loadVec3(vertices + vertexStride*ix1);
215 __m128 c = loadVec3(vertices + vertexStride*ix2);
216 __m128 ca_xyz = _mm_sub_ps(c, a);
217 __m128 ca_yzx = _mm_shuffle_ps(ca_xyz, ca_xyz, _MM_SHUFFLE(3, 0, 2, 1));
219 __m128 ba_xyz = _mm_sub_ps(b, a);
220 __m128 ba_yzx = _mm_shuffle_ps(ba_xyz, ba_xyz, _MM_SHUFFLE(3, 0, 2, 1));
222 __m128 f_zxy = _mm_mul_ps(ca_xyz, ba_yzx);
223 __m128 g_zxy = _mm_mul_ps(ba_xyz, ca_yzx);
225 __m128 n_zxy = _mm_sub_ps(f_zxy, g_zxy);
226 __m128 n_xyz = _mm_shuffle_ps(n_zxy, n_zxy, _MM_SHUFFLE(3, 0, 2, 1));
228 _mm_store_ps(triangleNormals + 4 * i, n_xyz);
231 elapsed_us->fetch_add(timer.elapsedMicroseconds());
236 struct TriangleNormalsTaskSSE2b
238 const uint32_t* indices;
239 float* triangleNormals;
240 const float* vertices;
241 uint32_t vertexStride;
243 std::atomic<uint64_t>* elapsed_us;
245 TriangleNormalsTaskSSE2b(
const uint32_t* indices,
246 float* triangleNormals,
247 const float* vertices,
248 uint32_t vertexStride,
249 uint32_t ia, uint32_t ib,
250 std::atomic<uint64_t>* elapsed_us =
nullptr):
252 triangleNormals(triangleNormals),
254 vertexStride(vertexStride),
256 elapsed_us(elapsed_us)
261 CpuInstrumentationScope(SCOPE_GEOMETRY,
"NrmAddSSE2");
262 auto timer = Cogs::Timer::startNew();
264 for (uint32_t i = ia; i < ib; i += 4) {
268 for (uint32_t j = 0; j < 4; j++) {
269 ii[j] = std::min(ib - 1, i + j);
270 for (uint32_t k = 0; k < 3; k++) {
271 ix[j][k] = indices[3 * ii[j] + k];
275 __m128 a_x = _mm_loadu_ps(vertices + vertexStride*ix[0][0]);
276 __m128 a_y = _mm_loadu_ps(vertices + vertexStride*ix[1][0]);
277 __m128 a_z = _mm_loadu_ps(vertices + vertexStride*ix[2][0]);
278 __m128 a_w = _mm_loadu_ps(vertices + vertexStride*ix[3][0]);
279 _MM_TRANSPOSE4_PS(a_x, a_y, a_z, a_w);
281 __m128 b_x = _mm_loadu_ps(vertices + vertexStride*ix[0][1]);
282 __m128 b_y = _mm_loadu_ps(vertices + vertexStride*ix[1][1]);
283 __m128 b_z = _mm_loadu_ps(vertices + vertexStride*ix[2][1]);
284 __m128 b_w = _mm_loadu_ps(vertices + vertexStride*ix[3][1]);
285 _MM_TRANSPOSE4_PS(b_x, b_y, b_z, b_w);
287 __m128 c_x = _mm_loadu_ps(vertices + vertexStride*ix[0][2]);
288 __m128 c_y = _mm_loadu_ps(vertices + vertexStride*ix[1][2]);
289 __m128 c_z = _mm_loadu_ps(vertices + vertexStride*ix[2][2]);
290 __m128 c_w = _mm_loadu_ps(vertices + vertexStride*ix[3][2]);
291 _MM_TRANSPOSE4_PS(c_x, c_y, c_z, c_w);
293 __m128 ba_x = _mm_sub_ps(b_x, a_x);
294 __m128 ba_y = _mm_sub_ps(b_y, a_y);
295 __m128 ba_z = _mm_sub_ps(b_z, a_z);
297 __m128 ca_x = _mm_sub_ps(c_x, a_x);
298 __m128 ca_y = _mm_sub_ps(c_y, a_y);
299 __m128 ca_z = _mm_sub_ps(c_z, a_z);
301 __m128 n_x = _mm_sub_ps(_mm_mul_ps(ca_y, ba_z), _mm_mul_ps(ba_y, ca_z));
302 __m128 n_y = _mm_sub_ps(_mm_mul_ps(ca_z, ba_x), _mm_mul_ps(ba_z, ca_x));
303 __m128 n_z = _mm_sub_ps(_mm_mul_ps(ca_x, ba_y), _mm_mul_ps(ba_x, ca_y));
304 __m128 n_w = _mm_setzero_ps();
305 _MM_TRANSPOSE4_PS(n_x, n_y, n_z, n_w);
307 _mm_store_ps(triangleNormals + 4 * ii[0], n_x);
308 _mm_store_ps(triangleNormals + 4 * ii[1], n_y);
309 _mm_store_ps(triangleNormals + 4 * ii[2], n_z);
310 _mm_store_ps(triangleNormals + 4 * ii[3], n_w);
313 elapsed_us->fetch_add(timer.elapsedMicroseconds());
319 struct VertexNormalsTask
321 float* vertexNormals;
322 uint32_t vertexNormalStride;
323 const float * triangleNormals;
324 const uint32_t* inverseIndexHead;
325 const uint32_t* inverseIndexNext;
327 std::atomic<uint64_t>* elapsed_us;
329 VertexNormalsTask(
float* vertexNormals,
330 uint32_t vertexNormalStride,
331 const float * triangleNormals,
332 const uint32_t* inverseIndexHead,
333 const uint32_t* inverseIndexNext,
334 uint32_t ia, uint32_t ib,
335 std::atomic<uint64_t>* elapsed_us):
336 vertexNormals(vertexNormals),
337 vertexNormalStride(vertexNormalStride),
338 triangleNormals(triangleNormals),
339 inverseIndexHead(inverseIndexHead),
340 inverseIndexNext(inverseIndexNext),
342 elapsed_us(elapsed_us)
347 CpuInstrumentationScope(SCOPE_GEOMETRY,
"VtxNrm");
348 auto timer = Cogs::Timer::startNew();
350 for (uint32_t i = ia; i < ib; i++) {
353 auto ix = inverseIndexHead[i];
355 n += *
reinterpret_cast<const glm::vec3*
>(triangleNormals + 4 * (ix/3));
356 ix = inverseIndexNext[ix];
358 n = glm::normalize(n);
360 *
reinterpret_cast<glm::vec3*
>(vertexNormals + vertexNormalStride*i) = n;
363 elapsed_us->fetch_add(timer.elapsedMicroseconds());
368#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
369 struct VertexNormalsTaskSSE2
371 float* vertexNormals;
372 uint32_t vertexNormalStride;
373 const float * triangleNormals;
374 const uint32_t* inverseIndexHead;
375 const uint32_t* inverseIndexNext;
377 std::atomic<uint64_t>* elapsed_us;
379 VertexNormalsTaskSSE2(
float* vertexNormals,
380 uint32_t vertexNormalStride,
381 const float * triangleNormals,
382 const uint32_t* inverseIndexHead,
383 const uint32_t* inverseIndexNext,
384 uint32_t ia, uint32_t ib,
385 std::atomic<uint64_t>* elapsed_us):
386 vertexNormals(vertexNormals),
387 vertexNormalStride(vertexNormalStride),
388 triangleNormals(triangleNormals),
389 inverseIndexHead(inverseIndexHead),
390 inverseIndexNext(inverseIndexNext),
392 elapsed_us(elapsed_us)
397 CpuInstrumentationScope(SCOPE_GEOMETRY,
"VtxNrmSSE2");
398 auto timer = Cogs::Timer::startNew();
400 for (uint32_t i = ia; i < ib; i += 4) {
404 for (uint32_t l = 0; l < 4; l++) {
405 auto j = std::min(ib - 1, i + l);
408 __m128 m = _mm_setzero_ps();
409 auto ix = inverseIndexHead[j];
411 m = _mm_add_ps(m, _mm_load_ps(triangleNormals + 4 * (ix / 3)));
412 ix = inverseIndexNext[ix];
421 _MM_TRANSPOSE4_PS(x, y, z, w);
422 __m128 xx = _mm_mul_ps(x, x);
423 __m128 yy = _mm_mul_ps(y, y);
424 __m128 zz = _mm_mul_ps(z, z);
425 __m128 s0 = _mm_add_ps(xx, yy);
426 __m128 s1 = _mm_add_ps(s0, zz);
428 __m128 r = _mm_rsqrt_ps(s1);
430 storeVec3(vertexNormals + vertexNormalStride*ii[0], _mm_mul_ps(_mm_shuffle_ps(r, r, _MM_SHUFFLE(0, 0, 0, 0)), n[0]));
431 storeVec3(vertexNormals + vertexNormalStride*ii[1], _mm_mul_ps(_mm_shuffle_ps(r, r, _MM_SHUFFLE(1, 1, 1, 1)), n[1]));
432 storeVec3(vertexNormals + vertexNormalStride*ii[2], _mm_mul_ps(_mm_shuffle_ps(r, r, _MM_SHUFFLE(2, 2, 2, 2)), n[2]));
433 storeVec3(vertexNormals + vertexNormalStride*ii[3], _mm_mul_ps(_mm_shuffle_ps(r, r, _MM_SHUFFLE(3, 3, 3, 3)), n[3]));
436 elapsed_us->fetch_add(timer.elapsedMicroseconds());
443void Cogs::Core::GeometryProcessing::normalsFromIndexedTriangles(
Context* ,
444 std::vector<glm::vec3>& N,
445 std::vector<uint32_t>& remap,
446 std::vector<uint32_t>& newIndices,
450 const uint32_t* indices,
452 const float featureAngle,
453 const float protrusionAngle,
457 const auto P_element_stride = uint32_t(P_stride /
sizeof(
float));
459 auto * triangleNormalsPtr =
reinterpret_cast<float*
>(((size_t)triangleNormals.data() + 63)&(~63));
462 const uint32_t taskSize = 10000;
466 TaskId group = context->taskManager->createGroup();
467 const bool useSSE = context->features->supported(CPUFeature::SSE2);
468 for (uint32_t i = 0; i < Nt; i += taskSize) {
470 tm->enqueueChild(group, TriangleNormalsTaskSSE2b(indices,
474 i, glm::min(Nt, i + taskSize)));
477 tm->enqueueChild(group, TriangleNormalsTask(indices,
481 i, glm::min(Nt, i + taskSize)));
486 for (
size_t i = 0; i < Nt; i++) {
487 const auto & a = glm::make_vec3(P + P_element_stride * indices[3 * i + 0]);
488 const auto & b = glm::make_vec3(P + P_element_stride * indices[3 * i + 1]);
489 const auto & c = glm::make_vec3(P + P_element_stride * indices[3 * i + 2]);
490 const auto n = glm::cross(c - a, b - a);
491 *
reinterpret_cast<glm::vec3*
>(triangleNormalsPtr + 4 * i) = glm::normalize(n);
495 for (
size_t i = 0; i < Nt; i++) {
496 const auto & a = glm::make_vec3(P + P_element_stride * indices[3 * i + 0]);
497 const auto & b = glm::make_vec3(P + P_element_stride * indices[3 * i + 2]);
498 const auto & c = glm::make_vec3(P + P_element_stride * indices[3 * i + 1]);
499 const auto n = glm::cross(c - a, b - a);
500 *
reinterpret_cast<glm::vec3*
>(triangleNormalsPtr + 4 * i) = glm::normalize(n);
505 std::vector<uint32_t> head(Nv, ~0u);
506 std::vector<Corner> corner(Ni);
507 for (uint32_t t = 0; t < Nt; t++) {
508 const auto a = indices[3 * t + 0];
509 const auto b = indices[3 * t + 1];
510 const auto c = indices[3 * t + 2];
512 corner[3 * t + 0].Vp = c;
513 corner[3 * t + 0].Vn = b;
514 corner[3 * t + 0].triangle = (t<<2) + 0;
515 corner[3 * t + 0].next = head[a];
518 corner[3 * t + 1].Vp = a;
519 corner[3 * t + 1].Vn = c;
520 corner[3 * t + 1].triangle = (t<<2) + 1;
521 corner[3 * t + 1].next = head[b];
524 corner[3 * t + 2].Vp = b;
525 corner[3 * t + 2].Vn = a;
526 corner[3 * t + 2].triangle = (t<<2) + 2;
527 corner[3 * t + 2].next = head[c];
531 const float protrusionCos = glm::cos(protrusionAngle);
532 const float featureCos = glm::cos(featureAngle);
534 std::vector<Corner> vertexCorners;
535 std::vector<Corner> pie;
541 newIndices.resize(Ni);
544 for (uint32_t v = 0; v < Nv; v++) {
547 vertexCorners.clear();
548 for (
auto n = head[v]; n != ~0u; n = corner[n].next) {
549 vertexCorners.push_back(corner[n]);
550 n_avg += glm::make_vec3(triangleNormalsPtr + 4 * (vertexCorners.back().triangle >> 2));
552 n_avg = glm::normalize(n_avg);
555 float minProtrusion = 1.f;
556 while (!vertexCorners.empty()) {
559 pie.push_back(vertexCorners.back());
560 vertexCorners.pop_back();
564 for (
size_t l = 0; l < vertexCorners.size(); l++) {
565 if (pie.back().Vn == vertexCorners[l].Vp) {
567 const auto q = glm::make_vec3(triangleNormalsPtr + 4 * (vertexCorners[l].triangle >> 2));
568 if (featureCos <= glm::dot(glm::make_vec3(triangleNormalsPtr + 4 * (pie.back().triangle >> 2)), q))
570 minProtrusion = glm::min(minProtrusion, glm::dot(n_avg, q));
571 pie.push_back(vertexCorners[l]);
572 vertexCorners[l] = vertexCorners.back();
573 vertexCorners.pop_back();
579 if (!vertexCorners.empty()) {
582 std::swap(pie.front(), pie.back());
585 for (
size_t l = 0; l < vertexCorners.size(); l++) {
586 if (pie.back().Vp == vertexCorners[l].Vn) {
588 const auto q = glm::make_vec3(triangleNormalsPtr + 4 * (vertexCorners[l].triangle >> 2));
589 if (featureCos <= glm::dot(glm::make_vec3(triangleNormalsPtr + 4 * (pie.back().triangle>>2)), q))
591 minProtrusion = glm::min(minProtrusion, glm::dot(n_avg, q));
592 pie.push_back(vertexCorners[l]);
593 vertexCorners[l] = vertexCorners.back();
594 vertexCorners.pop_back();
602 if (first && vertexCorners.empty() && minProtrusion < protrusionCos) {
604 for (
auto & slice : pie) {
605 auto ix = uint32_t(N.size());
606 newIndices[3 * (slice.triangle >> 2) + (slice.triangle & 3)] = ix;
608 N.push_back(glm::make_vec3(triangleNormalsPtr + 4 * (slice.triangle >> 2)));
612 auto ix = uint32_t(N.size());
614 for (
auto & slice : pie) {
615 n += glm::make_vec3(triangleNormalsPtr + 4 * (slice.triangle >> 2));
616 newIndices[3 * (slice.triangle >> 2) + (slice.triangle & 3)] = ix;
619 N.push_back(glm::normalize(n));
627void Cogs::Core::GeometryProcessing::normalsFromIndexedTriangles(
Context* context,
629 uint32_t normalStride,
630 const float* vertices,
631 uint32_t vertexStride,
632 const uint32_t numVertices,
633 const uint32_t* indices,
634 const uint32_t numIndices,
635 const uint32_t taskSize_,
636 std::atomic<uint64_t>* elapsed_us)
639 auto taskSize = 4 * taskSize_;
640 auto numTriangles = numIndices / 3;
648 std::memset(inverseHead.data(), ~0, inverseHead.size());
650 auto * inverseHeadPtr =
reinterpret_cast<uint32_t*
>(((size_t)inverseHead.data() + 63)&(~63));
651 auto * inverseNextPtr =
reinterpret_cast<uint32_t*
>(((size_t)inverseNext.data() + 63)&(~63));
656 for (uint32_t ia = 0; ia < numIndices; ia += taskSize) {
657 auto ib = std::min(numIndices, ia + taskSize);
658 tm->enqueueChild(group, [ia, ib, inverseHeadPtr, inverseNextPtr, indices]
660 CpuInstrumentationScope(SCOPE_GEOMETRY,
"NrmInvIx");
661 for (uint32_t i = ia; i < ib; i++) {
663 inverseNextPtr[i] = _InterlockedExchange((
volatile long*)(inverseHeadPtr + indices[i]), i);
665 inverseNextPtr[i] = __atomic_exchange_n(inverseHeadPtr + indices[i], i, __ATOMIC_ACQ_REL);
674 auto * triangleNormalsPtr =
reinterpret_cast<float*
>(((size_t)triangleNormals.data() + 63)&(~63));
679#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
680 if (context->
features->supported(CPUFeature::SSE2)) {
681 for (uint32_t i = 0; i < numTriangles; i += taskSize) {
682 tm->enqueueChild(group, TriangleNormalsTaskSSE2b(indices,
686 i, glm::min(numTriangles, i + taskSize),
693 for (uint32_t i = 0; i < numTriangles; i += taskSize) {
694 tm->enqueueChild(group, TriangleNormalsTask(indices,
698 i, glm::min(numTriangles, i + taskSize),
710#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
711 if (context->
features->supported(CPUFeature::SSE2)) {
712 for (uint32_t i = 0; i < numVertices; i += taskSize) {
713 tm->enqueueChild(group, VertexNormalsTaskSSE2(normals,
718 i, std::min(numVertices, i + taskSize),
725 for (uint32_t i = 0; i < numVertices; i += taskSize) {
726 tm->enqueueChild(group,
727 VertexNormalsTask(normals,
732 i, std::min(numVertices, i + taskSize),
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class Features > features
Features service instance.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
Task id struct used to identify unique Task instances.