Cogs.Core
UniformGridSystem.cpp
1#define _USE_MATH_DEFINES
2#ifdef _WIN32
3#include <winsock2.h>
4#include <intrin.h>
5#elif (defined(__GNUC__) || defined(__clang__)) && !defined(EMSCRIPTEN)
6#include <cpuid.h>
7#endif
8
9#include "UniformGridSystem.h"
10
11#include "../Components/UniformGridIsoComponent.h"
12
13#include "Context.h"
14#include "EntityStore.h"
15#include "Resources/ResourceStore.h"
16#include "Resources/MaterialManager.h"
17#include "Resources/MeshManager.h"
18#include "Resources/TextureManager.h"
19#include "Components/Core/SceneComponent.h"
20#include "Components/Core/TransformComponent.h"
21#include "Components/Core/MeshComponent.h"
22#include "Components/Core/MeshRenderComponent.h"
23#include "Components/Core/SubMeshRenderComponent.h"
24
25#include "Resources/Mesh.h"
26#include "Services/Features.h"
27
28#include "../BeamUtils.h"
29
30#include "Foundation/Logging/Logger.h"
31#include "Foundation/Platform/Timer.h"
32
33#include <map>
34#include <queue>
35#include <chrono>
36
37namespace {
38 Cogs::Logging::Log logger = Cogs::Logging::getLogger("UniformGridSystem");
39 struct CpuId
40 {
41 CpuId() = delete;
42 CpuId(uint32_t level)
43 {
44#ifdef _MSC_VER
45 int ret[4];
46 __cpuid(ret, level);
47 eax = ret[0];
48 ebx = ret[1];
49 ecx = ret[2];
50 edx = ret[3];
51#elif defined(__GNUC__) || defined(__clang__)
52 __get_cpuid(level, &eax, &ebx, &ecx, &edx);
53#else
54#error "implement cpuid for this compiler"
55#endif
56 }
57 uint32_t eax, ebx, ecx, edx;
58 };
59
60 float dbToLinear(float v)
61 {
62 float w = powf(10.0f, v*(1.0f/20.0f));
63 if (!isfinite(w)) {
64 w = v < 0.f ? 0.f : std::numeric_limits<float>::max();
65 }
66 return w;
67 }
68}
69
70namespace Cogs::Core::EchoSounder {
71
72 void sampleTile_border_sse41(float * data,
73 const float *v,
74 const glm::vec3 tileIndex,
75 const glm::uvec3 tilePos,
76 const glm::uvec3 dataSize,
77 const glm::uvec3 maxIndices,
78 const glm::vec3 tp,
79 const glm::vec3 scale,
80 const glm::vec3 arrayPositionGlobal,
81 const glm::vec4* frustum,
82 const float minDistanceSquared,
83 const float maxDistanceSquared,
84 const glm::quat inverseOrientation,
85 const uint32_t coordSys,
86 const uint32_t minorCount,
87 const uint32_t sampleCount,
88 const glm::vec3 polarScale,
89 const glm::vec3 polarShift);
90
91 void sampleTile_inner_sse41(float * data,
92 const float *v,
93 const glm::vec3 tileIndex,
94 const glm::uvec3 tilePos,
95 const glm::uvec3 dataSize,
96 const glm::uvec3 maxIndices,
97 const glm::vec3 tp,
98 const glm::vec3 scale,
99 const glm::vec3 arrayPositionGlobal,
100 const glm::vec4* frustum,
101 const float minDistanceSquared,
102 const float maxDistanceSquared,
103 const glm::quat inverseOrientation,
104 const uint32_t coordSys,
105 const uint32_t minorCount,
106 const uint32_t sampleCount,
107 const glm::vec3 polarScale,
108 const glm::vec3 polarShift);
109
110 void sampleTile_inner_fma_gather(float * data,
111 const float *v,
112 const glm::vec3 tileIndex,
113 const glm::uvec3 tilePos,
114 const glm::uvec3 dataSize,
115 const glm::uvec3 maxIndices,
116 const glm::vec3 tp,
117 const glm::vec3 scale,
118 const glm::vec3 arrayPositionGlobal,
119 const glm::vec4* frustum,
120 const float minDistanceSquared,
121 const float maxDistanceSquared,
122 const glm::quat inverseOrientation,
123 const uint32_t coordSys,
124 const uint32_t minorCount,
125 const uint32_t sampleCount,
126 const glm::vec3 polarScale,
127 const glm::vec3 polarShift);
128
129 void sampleTile3_avx2(float * data,
130 float& minVal,
131 float& maxVal,
132 const float *v,
133 const glm::vec3 tileIndex,
134 const glm::uvec3 tilePos,
135 const glm::uvec3 dataSize,
136 const glm::uvec3 maxIndices,
137 const glm::vec3 tp,
138 const glm::vec3 scale,
139 const glm::vec3 arrayPositionGlobal,
140 const glm::vec4* frustum,
141 const float minDistanceSquared,
142 const float maxDistanceSquared,
143 const glm::quat inverseOrientation,
144 const uint32_t coordSys,
145 const uint32_t minorCount,
146 const uint32_t sampleCount,
147 const glm::vec3 polarScale,
148 const glm::vec3 polarShift);
149
150 void sampleTile3_border_avx2(float * data,
151 float& minVal,
152 float& maxVal,
153 const float *v,
154 const glm::vec3 tileIndex,
155 const glm::uvec3 tilePos,
156 const glm::uvec3 dataSize,
157 const glm::uvec3 maxIndices,
158 const glm::vec3 tp,
159 const glm::vec3 scale,
160 const glm::vec3 arrayPositionGlobal,
161 const glm::vec4* frustum,
162 const float minDistanceSquared,
163 const float maxDistanceSquared,
164 const glm::quat inverseOrientation,
165 const uint32_t coordSys,
166 const uint32_t minorCount,
167 const uint32_t sampleCount,
168 const glm::vec3 polarScale,
169 const glm::vec3 polarShift);
170
171 MeshHandle createIsoSurfacesReference(Context* context,
172 uint64_t& analyze,
173 uint64_t& vtx,
174 uint64_t& idx,
176 const float* values,
177 const glm::uvec3 layout,
178 const glm::vec3 minCorner,
179 const glm::vec3 maxCorner,
180 const float *thresholds, size_t count);
181
182 MeshHandle createIsoSurfacesAVX2(Context* context,
183 uint64_t& analyze,
184 uint64_t& vtx,
185 uint64_t& idx,
187 const float* values,
188 const glm::uvec3 gridLayout,
189 const glm::vec3 minCorner,
190 const glm::vec3 maxCorner,
191 const float *thresholds, size_t count);
192
193 MeshHandle createIsoSurfacesSplitAVX2(Context* context,
194 uint64_t& analyze,
195 uint64_t& vtx,
196 uint64_t& idx,
198 const float* values,
199 const glm::uvec3 gridLayout,
200 const glm::vec3 minCorner,
201 const glm::vec3 maxCorner,
202 const float *thresholds, size_t count);
203
204 MeshHandle UniformGridData::createIsoSurfaces(Tile & tile, Cogs::Memory::MemoryBuffer& scratch, const glm::vec3 minCorner)
205 {
206 Context* context = system->context;
207
208 MeshHandle mesh = MeshHandle::NoHandle;
209 float *thresholds = this->thresholds.data();
210 size_t count = this->thresholds.size();
211 std::vector<float> t(count);
212 for(uint32_t i=0; i<count; i++) t[i] = dbToLinear(thresholds[i]);
213 if (!count || tile.maxValue <= t[0] || t[count-1] <= tile.minValue) return mesh;
214
215 const glm::vec3 maxCorner = minCorner + glm::vec3(size)*scale;
216
217 uint64_t analyze = 0;
218 uint64_t vtx = 0;
219 uint64_t idx = 0;
220 switch (compute) {
221 case ComputeType::Reference:
222 case ComputeType::SSE41:
223 case ComputeType::FMA:
224 mesh = createIsoSurfacesReference(context, analyze, vtx, idx,
225 scratch,
226 tile.data, size,
227 minCorner, maxCorner,
228 t.data(), count);
229 break;
230 case ComputeType::AVX2:
231#ifdef USE_SPLIT
232 mesh = createIsoSurfacesSplitAVX2(context, analyze, vtx, idx,
233 scratch,
234 tile.data, size,
235 minCorner, maxCorner,
236 t.data(), count);
237 break;
238#endif
239 mesh = createIsoSurfacesAVX2(context, analyze, vtx, idx,
240 scratch,
241 tile.data, size,
242 minCorner, maxCorner,
243 t.data(), count);
244 break;
245 }
246 isoSurf.analyze.fetch_add(uint32_t(analyze));
247 isoSurf.vtx.fetch_add(uint32_t(vtx));
248 isoSurf.idx.fetch_add(uint32_t(idx));
249 isoSurf.N.fetch_add(1);
250 return mesh;
251 }
252 glm::vec3 getSamplePosRef(const glm::uvec3 /*dataSize*/,
253 const glm::vec3 tilePos,
254 const glm::vec3 scale,
255 const glm::vec3 arrayPositionGlobal,
256 const glm::vec4* frustum,
257 const float minDistanceSquared,
258 const float maxDistanceSquared,
259 const glm::quat inverseOrientation,
260 const uint32_t coordSys,
261 const glm::vec3 polarScale,
262 const glm::vec3 polarShift,
263 const uint32_t x,
264 const uint32_t y,
265 const uint32_t z)
266 {
267 glm::vec3 p = scale * glm::vec3(x, y, z);
268 glm::vec3 q = (tilePos - arrayPositionGlobal) + p; // Sample pos with origin in beam bundle origin.
269 float r2 = glm::dot(q, q);
270
271 bool in_0 = 0.f <= glm::dot(glm::vec3(frustum[0]), q);
272 bool in_1 = 0.f <= glm::dot(glm::vec3(frustum[1]), q);
273 bool in_2 = 0.f <= glm::dot(glm::vec3(frustum[2]), q);
274 bool in_3 = 0.f <= glm::dot(glm::vec3(frustum[3]), q);
275 bool in_4 = minDistanceSquared <= r2;
276 bool in_5 = r2 <= maxDistanceSquared;
277 if (!in_0 || !in_1 || !in_2 || !in_3 || !in_4 || !in_5) return glm::vec3(-1.f);
278
279 glm::vec3 a = inverseOrientation * q;
280 float r = std::sqrt(r2);
281
282 glm::vec3 polarPos;
283 if (coordSys == 0) {
284 float dirX = std::asin(a.x / r);
285 float dirY = std::asin(a.y / r);
286 polarPos = glm::vec3(dirY, dirX, r);
287 }
288 else if (coordSys == 1) {
289 float dirX = std::asin(a.x / r);
290 float dirY = std::atan(a.y / a.z);
291 polarPos = glm::vec3(dirY, dirX, a.z);
292 }
293 glm::vec3 xi = glm::max(glm::vec3(0.f), polarScale*(polarPos - polarShift));
294
295 return xi;
296 }
297}// namespace ...
299{
301
302 CpuId level1(0x00000001u);
303 has_sse41 = level1.ecx & (1u << 19u);
304 bool osxsave = level1.ecx & (1u << 27u);
305 has_avx2 = false;
306 if(osxsave){
307 CpuId level7(0x00000007u);
308 has_avx2 = level7.ebx & (1u << 5u);
309 }
310 if(has_sse41) LOG_INFO(logger, "Has SSE41");
311 if(has_avx2) LOG_INFO(logger, "Has AVX2");
312
313 Queue = context->taskManager->createQueue("EchoSounder", Threads::hardwareConcurrency()-1);
314
315 point_material = context->materialManager->loadMaterial("UniformGridPoint.material");
316 context->materialManager->processLoading();
317 point_instance = context->materialInstanceManager->createMaterialInstance(point_material);
318 float pointSize = 16.0f;
319 point_instance->setProperty("pointSize", &pointSize, sizeof(float));
320 point_instance->setOption("BlendMode", "Blend");
321 point_instance->setupInstance(point_material.resolve());
322}
323
325{
326 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::preUpdate");
327 static std::vector<uint32_t> pointTransferData = {
328 0x00888888, 0xff888888, 0xff888888, 0xff888888,
329 0xffFF3300, 0xffFF3300, 0xffFF3300, 0xffFF3300,
330 0xff00DD00, 0xff00DD00, 0xff00DD00, 0xff00DD00,
331 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF,
332 };
333 for (auto & component : pool) {
334 UniformGridData &data = getData(&component);
335 bool update_all = data.decibelMin != component.decibelMin || data.decibelMax != component.decibelMax;
336 if(!data.visualizer_group){
337 data.visualizer_group = context->store->createChildEntity("Group", component.getContainer(), "VisualizerGroup");
338 }
339 if(update_all){
340 data.decibelMin = component.decibelMin;
341 data.decibelMax = component.decibelMax;
342 }
343 update_outline(context, &component);
344
345 data.compute = component.compute; // TODO
346
347 // UniformGridIsoComponent
349 if(iso_component){
350 if(iso_component->thresholds.size() != data.thresholds.size()){
351 data.thresholds = iso_component->thresholds;
352 update_all = true;
353 }
354 for(size_t i=0; i<iso_component->thresholds.size(); i++){
355 if(iso_component->thresholds[i] != data.thresholds[i]){
356 data.thresholds = iso_component->thresholds;
357 update_all = true;
358 break;
359 }
360 }
361 {
362 MaterialInstanceHandle &instance = iso_component->material;
363 const float fmin = std::numeric_limits<float>::min();
364 glm::vec4 range(iso_component->uTextureRange.x,
365 1.f / std::max(fmin, iso_component->uTextureRange.y - iso_component->uTextureRange.x),
366 iso_component->vTextureRange.x,
367 1.f / std::max(fmin, iso_component->vTextureRange.y - iso_component->vTextureRange.x));
368 instance->setVec4Property(instance->material->getVec4Key("range"), range);
369 instance->setFloatProperty(instance->material->getFloatKey("noiseIntensity"), 0.0f);
370 }
371 size_t count = iso_component->thresholds.size();
372 if(!data.layerMaterialInstances.size()) data.layerMaterialInstances.resize(count);
373 for(uint32_t i=0; i<count; i++){
374 MaterialInstanceHandle &instance = data.layerMaterialInstances[i];
375 if(!instance) instance = context->materialInstanceManager->createMaterialInstance(iso_component->material);
376 instance->clone(iso_component->material.resolve());
377 if(i == count-1 && iso_component->innerLayerOpaque){
378 instance->options.depthWriteEnabled = true;
379 instance->setOpaque();
380 }
381 else{
382 instance->setTransparent();
383 instance->options.depthWriteEnabled = false;
384 }
385 //instance->setTextureProperty(instance->material->getTextureKey("ColorMap"), tex);
386 instance->setTextureAddressMode("ColorMap", "Clamp");
387 const float fmin = std::numeric_limits<float>::min();
388 glm::vec4 range(iso_component->uTextureRange.x,
389 1.f / std::max(fmin, iso_component->uTextureRange.y - iso_component->uTextureRange.x),
390 iso_component->vTextureRange.x,
391 1.f / std::max(fmin, iso_component->vTextureRange.y - iso_component->vTextureRange.x));
392 instance->setVec4Property(instance->material->getVec4Key("range"), range);
393 instance->setFloatProperty(instance->material->getFloatKey("noiseIntensity"), 0.0f);
394 }
395 }
396
397 std::unique_lock<decltype(data.tiles_mu)> lock(data.tiles_mu, std::defer_lock);
398 if(!lock.try_lock()) continue;
399 auto group = context->taskManager->createGroup(Cogs::Core::TaskManager::GlobalQueue);
400 for(auto &tmp : data.destroy)
401 context->store->removeChild(data.visualizer_group.get(), tmp);
402 data.destroy.clear();
403 for(auto &tmp : data.tiles){
404 TileKey key = tmp.first;
405 Tile &tile = tmp.second;
406#if 1
407 if(update_all) data.change_sv = true;
408 if(tile.update && HandleIsValid(tile.mesh) && tile.mesh->isActive() && iso_component){
409 tile.update = false;
410 if (!tile.visualizer) {
411 tile.visualizer = context->store->createChildEntity("EchoUniformGridIsoVisualizer", data.visualizer_group.get(), "UniformGridIsoVisualizer");
412 }
413 auto *tComp = tile.visualizer->getComponent<TransformComponent>();
414 glm::uvec3 tileIndex(decodeTileKey(key));
415 glm::vec3 tileCoord(glm::vec3(tileIndex)-glm::vec3(1<<15));
416 glm::vec3 tilePos = tileCoord*(glm::vec3(data.size-glm::uvec3(1))*data.scale);
417 tComp->position = tilePos;
418 tComp->scale = data.scale;
419 tComp->setChanged();
420 auto *src = tile.visualizer->getComponent<SubMeshRenderComponent>();
421 if (src) src->materials = data.layerMaterialInstances;
422 auto *mc = tile.visualizer->getComponent<MeshComponent>();
423 mc->meshHandle = tile.mesh;
424 mc->setChanged();
425 }
426 if (tile.update && !HandleIsValid(tile.mesh)) {
427 if (tile.visualizer) {
428 auto *mc = tile.visualizer->getComponent<MeshComponent>();
430 mc->setChanged();
431 // FIXME: maybe maintain a pool of inactive entities so we don't have to create & delete so much.
432 context->store->removeChild(data.visualizer_group.get(), tile.visualizer.get());
433 tile.visualizer.reset();
434 }
435 }
436#else
437 if (tile.update || update_all) {
438 tile.update = false;
439 if (!tile.visualizer) {
440 tile.visualizer = context->store->createChildEntity("EchoUniformGridPointVisualizer", data.visualizer_group.get(), "UniformGridPointVisualizer");
441 }
442 auto task = [this, &context, &data, key, &tile]() {
443 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::pointVizTask");
444 float valueMin = dbToLinear(data.decibelMin);
445 float valueMax = dbToLinear(data.decibelMax);
446 float valueScale = 1.0f / (valueMax - valueMin);
447 std::vector<glm::vec3> positions;
448 std::vector<glm::vec4> colors;
449 size_t t_size = pointTransferData.size();
450 float cinv = 1.0f / 255.0f;
451 for (uint32_t z = 0; z < data.size.z; z += viz_z_div) {
452 for (uint32_t y = 0; y < data.size.y; y += viz_y_div) {
453 for (uint32_t x = 0; x < data.size.x; x += viz_x_div) {
454 uint32_t index = z * data.size.y*data.size.x + y * data.size.x + x;
455 float val = glm::clamp<float>(valueScale*tile.data[index], 0.0f, 1.0f);
456 float vali = val * (t_size - 1);
457 float a = vali - floor(vali);
458 uint32_t il = glm::clamp<uint32_t>((uint32_t)floor(vali), 0, t_size);
459 uint32_t iu = glm::clamp<uint32_t>((uint32_t)ceil(vali), 0, t_size);
460 uint32_t cl = pointTransferData[il];
461 uint32_t cu = pointTransferData[iu];
462 glm::vec4 coll(((cl>>0)&0xff)*cinv, ((cl>>8)&0xff)*cinv,
463 ((cl>>16)&0xff)*cinv, ((cl>>24)&0xff)*cinv);
464 glm::vec4 colu(((cu>>0)&0xff)*cinv, ((cu>>8)&0xff)*cinv,
465 ((cu>>16)&0xff)*cinv, ((cu>>24)&0xff)*cinv);
466 glm::vec4 color = glm::mix(coll, colu, a);
467 colors.push_back(color);
468 positions.push_back(glm::vec3(x, y, z)*data.scale);
469 }
470 }
471 }
472 auto *tComp = tile.visualizer->getComponent<TransformComponent>();
473 glm::uvec3 tileIndex(decodeTileKey(key));
474 glm::vec3 tileCoord(glm::vec3(tileIndex)-glm::vec3(1<<15));
475 glm::vec3 tilePos = tileCoord*(glm::vec3(data.size-glm::uvec3(1))*data.scale);
476 tComp->position = tilePos;
477 auto *rc = tile.visualizer->getComponent<MeshRenderComponent>();
478 rc->material = point_instance;
479 auto *mc = tile.visualizer->getComponent<MeshComponent>();
480 auto mesh = context->meshManager->createLocked();
481 mesh->clear();
482 mesh->set(VertexDataType::Positions, &VertexFormats::Pos3f, positions.data(), positions.data() + positions.size());
483 mesh->set(VertexDataType::Colors0, &VertexFormats::Color4f, colors.data(), colors.data() + colors.size());
484 mesh->primitiveType = Cogs::PrimitiveType::PointList;
485 mc->meshHandle = mesh.getHandle();
486 };
487 context->taskManager->enqueueChild(group, task);
488 }
489#endif
490 }
491 context->taskManager->wait(group);
492 }
493}
494void Cogs::Core::EchoSounder::UniformGridSystem::update_outline(Context * context, UniformGridComponent *component)
495{
496 UniformGridData &data = getData(component);
497 if (!component->show_frustum){
498 for(auto &tmp : data.boundingFrustum){
499 EntityPtr entity = tmp.second;
500 auto * meshComp = entity->getComponent<MeshComponent>();
502 meshComp->setChanged();
503 }
504 return;
505 }
506 for(auto &tmp : data.config){
507 uint32_t id = tmp.first;
508 UniformGridConfig &config(*tmp.second);
509
510 if(!data.boundingFrustum.count(id)){
511 Cogs::Core::EntityPtr entity = context->store->createChildEntity("MeshPart", component->getContainer(), "Frustum");
512 data.boundingFrustum[id] = entity;
513
514 auto mat = context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
515 mat->setPermutation("Line");
516 mat->setVariant("LightModel", "BaseColor");
517 mat->setVariant("EnableLighting", false);
518 mat->setVariant("VertexColor", true);
519
520 auto *matRndComp = entity->getComponent<MeshRenderComponent>();
521 matRndComp->material = mat;
522 matRndComp->setRenderFlag(RenderFlags::CustomMaterial);
523 matRndComp->setChanged();
524 }
525 Cogs::Core::EntityPtr entity = data.boundingFrustum[id];
526
527 auto * trComp = entity->getComponent<TransformComponent>();
528 trComp->rotation = data.vesselOrientationGlobal;
529 trComp->position = data.vesselPositionGlobal;
530 trComp->setChanged();
531
532 glm::quat arrayOrientationVessel;
533 glm::vec3 arrayPositionVessel;
534 getArrayToVesselTransform(arrayOrientationVessel,
535 arrayPositionVessel,
536 config.transducerAlpha,
537 config.transducerOffset);
538
539 float minDistance = config.depthOffset;
540 float maxDistance = minDistance + config.sampleCount * config.depthStep;
541
542 uint32_t beamCount = config.majorCount*config.minorCount;
543
544 float minDirectionX = std::numeric_limits<float>::max();
545 float maxDirectionX = -std::numeric_limits<float>::max();
546 float minDirectionY = std::numeric_limits<float>::max();
547 float maxDirectionY = -std::numeric_limits<float>::max();
548 float maxBeamWidthX = -std::numeric_limits<float>::max();
549 float maxBeamWidthY = -std::numeric_limits<float>::max();
550 for(uint32_t i=0; i<beamCount; i++){
551 maxDirectionX = glm::max(maxDirectionX, config.directionX[i]);
552 minDirectionX = glm::min(minDirectionX, config.directionX[i]);
553 maxDirectionY = glm::max(maxDirectionY, config.directionY[i]);
554 minDirectionY = glm::min(minDirectionY, config.directionY[i]);
555 maxBeamWidthX = glm::max(maxBeamWidthX, config.beamWidthX[i]);
556 maxBeamWidthY = glm::max(maxBeamWidthY, config.beamWidthY[i]);
557 }
558
559 if (config.majorCount == 1) {
560 minDirectionX -= 0.5f*maxBeamWidthX;
561 maxDirectionX += 0.5f*maxBeamWidthX;
562 }
563 if (config.minorCount == 1) {
564 minDirectionY -= 0.5f*maxBeamWidthY;
565 maxDirectionY += 0.5f*maxBeamWidthY;
566 }
567
568 auto * meshComp = entity->getComponent<MeshComponent>();
569 glm::vec4 frustum[6];
570 getBoundingFrustum(frustum,
571 arrayOrientationVessel,
572 arrayPositionVessel,
573 config.coordSys,
574 minDirectionX, maxDirectionX,
575 minDirectionY, maxDirectionY,
576 minDistance, maxDistance);
577 meshComp->meshHandle = buildFrustumOutline(context, frustum);
578 meshComp->setChanged();
579 }
580}
581void Cogs::Core::EchoSounder::UniformGridSystem::config(UniformGridComponent *component, UniformGridConfig &config)
582{
583 UniformGridData &data = getData(component);
584 std::unique_lock<decltype(data.mu)> lock(data.mu);
585 uint32_t id = config.configId;
586 data.config[id] = std::make_shared<UniformGridConfig>(std::move(config));
587}
588void Cogs::Core::EchoSounder::UniformGridSystem::clear(UniformGridComponent *component)
589{
590 UniformGridData &data = getData(component);
591 std::unique_lock<decltype(data.mu)> lock(data.mu); // TODO: tiles_mu
592 for(auto &tmp : data.tiles){
593 _aligned_free(tmp.second.data);
594 Entity *ptr = tmp.second.visualizer.get();
595 if(ptr) context->store->removeChild(data.visualizer_group.get(), ptr);
596 }
597 data.tiles.clear();
598}
599float Sinc(float x)
600{
601 constexpr float PI = float(M_PI);
602 if(x < 1e-5) return 1.0f;
603 return sinf(PI*x)/(PI*x);
604}
605float Lanczos(float x, float tau)
606{
607 return Sinc(x) * Sinc(x/tau);
608}
609void Cogs::Core::EchoSounder::UniformGridSampleData::filter_ping()
610{
611 float w = filter_width;
612 float r = w*0.5f;
613 float a = filter_falloff;
614 int32_t iwidth = 1 + int32_t(floor(r))*2;
615 int32_t ihalf = iwidth/2;
616 if(iwidth < 3 || a == 0.0f) return;
617
618 std::vector<float> weights(iwidth, 0.0f);
619 for(int32_t t=0; t<iwidth; t++){
620 int32_t x = t-ihalf;
621 x = abs(x);
622 // Box:
623 // weights[t] = 1.0f;
624 // Triangle:
625 // Gaussian:
626 weights[t] = std::exp(-a*(x*x))-std::exp(-a*(r*r));
627 // Mitchell:
628 // Lanczos:
629 // weights[t] = Lanczos((float)x/r, tau);
630 // TODO: Down/Upsample
631 }
632 double sum = 0.0f;
633 for(int32_t t=0; t<iwidth; t++)
634 sum += weights[t];
635 for(int32_t t=0; t<iwidth; t++)
636 weights[t] = (float)(weights[t]/sum);
637 assert(std::isnormal(sum));
638 Memory::TypedBuffer<float> linear_resampled;
639 uint32_t beamCount = config->majorCount*config->minorCount;
640 uint32_t sampleCount = config->sampleCount;
641 linear_resampled.resize(beamCount*sampleCount, false);
642 for(uint32_t b=0; b<beamCount; b++){
643 uint32_t off = b*sampleCount;
644 for(uint32_t s=0; s<sampleCount; s++){
645 float avg;
646 {
647 int32_t st = glm::clamp((int32_t)s-ihalf, (int32_t)0, (int32_t)sampleCount-1);
648 avg = linear[off+st]*weights[0];
649 }
650 for(int32_t t=1; t<iwidth; t++){
651 int32_t st = glm::clamp((int32_t)s+t-ihalf, (int32_t)0, (int32_t)sampleCount-1);
652 avg += linear[off+st]*weights[t];
653 }
654 linear_resampled[off+s] = avg;
655 }
656 }
657 data.sample_data->linear.swap(linear_resampled);
658}
660{
663 UniformGridData &data = getData(component);
664 data.system = this;
665 data.component = component;
666 Context *con = context;
667 auto task = [this, con, component](){ this->dispatch(con, component); };
668 context->taskManager->enqueue(Queue, task);
669 return handle;
670}
672{
674 UniformGridData &data = getData(component);
675 data.shutdown = true;
676 {
677 std::unique_lock<decltype(data.mu)> lock(data.mu);
678 data.cv.wait(lock);
679 }
681}
682void Cogs::Core::EchoSounder::UniformGridSystem::dispatch(Context *context, UniformGridComponent *component)
683{
684 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::dispatch");
685 UniformGridData &data = getData(component);
686
687 // Update iso-surfaces
688 if(data.change_sv || data.update_iso){
689 std::unique_lock<decltype(data.tiles_mu)> lock(data.tiles_mu);
690 if(data.change_sv){
691 data.change_sv = false;
692 for(auto &tmp1 : data.tiles)
693 tmp1.second.update_iso = true;
694 data.update_iso = true;
695 }
696 data.update_iso = data.update_isosurface();
697 }
698
699 // Add remove tiles
700 if(data.sample_data){
701 using clock = std::chrono::high_resolution_clock;
702 auto t0 = clock::now();
703 {
704 std::unique_lock<decltype(data.tiles_mu)> lock(data.tiles_mu);
705 if((component->size != data.size || component->scale != data.scale) &&
706 component->scale.x > 0.0f && component->scale.y > 0.0f && component->scale.z > 0.0f){
707 data.size = component->size;
708 data.scale = component->scale;
709
710 for(auto &tmp : data.tiles){
711 _aligned_free(tmp.second.data);
712 Entity *ptr = tmp.second.visualizer.get();
713 if(ptr) data.destroy.push_back(ptr);
714 }
715 data.tiles.clear();
716 }
717 else{
718 data.sample_data->evict_tiles();
719 }
720 data.sample_data->cull_tiles();
721 data.sample_data->gen_tiles();
722 }
723
724 // Wait for linear filtering
725 context->taskManager->wait(data.sample_data->linear_group);
726 data.sample_data->filter_ping();
727
728 auto group = context->taskManager->createGroup(Queue);
729 size_t concurrency = Threads::hardwareConcurrency()-1;
730 concurrency*=4;
731 if(data.sample_data->tiles_inside.size()){
732 size_t tile_count = (size_t)data.sample_data->tiles_inside.size();
733 size_t task_count = std::min(tile_count, concurrency);
734 size_t steps = tile_count/task_count+1;
735 for(size_t i=0; i<tile_count; i+=steps){
736 size_t end = std::min(i+steps, tile_count);
737 auto task = [sample_data=data.sample_data, i, end](){ sample_data->sample_inside(i, end); };
738 context->taskManager->enqueueChild(group, task);
739 }
740 }
741 if(data.sample_data->tiles_intersecting.size()){
742 size_t tile_count = (size_t)data.sample_data->tiles_intersecting.size();
743 size_t task_count = std::min(tile_count, concurrency);
744 size_t steps = tile_count/task_count+1;
745 for(size_t i=0; i<tile_count; i+=steps){
746 size_t end = std::min(i+steps, tile_count);
747 auto task = [sample_data=data.sample_data, i, end](){ sample_data->sample_intersecting(i, end); };
748 context->taskManager->enqueueChild(group, task);
749 }
750 }
751 context->taskManager->wait(group);
752
753 {
754 const char* s = "REF";
755 switch (data.sample_data->compute) {
756 case ComputeType::SSE41: s = "SSE41"; break;
757 case ComputeType::FMA: s = "FMA"; break;
758 case ComputeType::AVX2: s = "AVX2"; break;
759 default: break;
760 }
761
762 float N_tot = (float)(data.sample_intersecting.N + data.sample_inside.N);
763 if (data.runs == 9) {
764 auto threadTimeToWallTime = 1.f / (Threads::hardwareConcurrency() - 1);
765
766 LOG_DEBUG(logger, "%s: sample_b %.2fms (%.3fms) sample_i %.2fms (%.3fms) pop=%.1f%% iso %.2fms (%.3fms/%.3fms/%.3fms)",
767 s,
768 threadTimeToWallTime*(1.f / 1000.f) * data.sample_intersecting.us,
769 ((1.f / 1000.f) * data.sample_intersecting.us) / data.sample_intersecting.N,
770 threadTimeToWallTime*(1.f / 1000.f) * data.sample_inside.us,
771 ((1.f / 1000.f) * data.sample_inside.us) / data.sample_inside.N,
772 (100.f*data.isoSurf.N) / N_tot,
773 threadTimeToWallTime*(1.f / 1000.f) *(data.isoSurf.analyze + data.isoSurf.vtx + data.isoSurf.idx),
774 ((1.f / 1000.f) * data.isoSurf.analyze) / data.isoSurf.N,
775 ((1.f / 1000.f) * data.isoSurf.vtx) / data.isoSurf.N,
776 ((1.f / 1000.f) * data.isoSurf.idx) / data.isoSurf.N);
777
778 data.sample_intersecting.us = 0;
779 data.sample_intersecting.N = 0;
780 data.sample_inside.us = 0;
781 data.sample_inside.N = 0;
782 data.isoSurf.analyze = 0;
783 data.isoSurf.vtx = 0;
784 data.isoSurf.idx = 0;
785 data.isoSurf.N = 0;
786 data.runs = 0;
787 }
788 else data.runs++;
789 }
790 auto t1 = clock::now();
791 LOG_DEBUG(logger, "add_point %.4f s (%.2f ms) size (%d, %d, %d) scale (%.3f, %.3f, %.3f)",
792 std::chrono::duration<float>(t1-t0).count(),
793 std::chrono::duration<float, std::milli>(t1-t0).count(),
794 data.size.x, data.size.y, data.size.z,
795 data.scale.x, data.scale.y, data.scale.z);
796
797 std::unique_lock<decltype(data.mu)> lock(data.mu);
798 delete data.sample_data;
799 data.sample_data = nullptr;
800 if(data.sample_data_backbuffer){
801 data.sample_data = data.sample_data_backbuffer;
802 data.vesselOrientationGlobal = data.sample_data->vesselOrientationGlobal;
803 data.vesselPositionGlobal = data.sample_data->vesselPositionGlobal;
804 data.sample_data_backbuffer = nullptr;
805 }
806 }
807
808 if(!data.shutdown){
809 if(!data.update_iso) std::this_thread::sleep_for(std::chrono::milliseconds(8)); // TODO conditon variable
810 auto task = [this, context, component](){ this->dispatch(context, component); };
811 context->taskManager->enqueue(Queue, task);
812 }
813 else{
814 data.cv.notify_all();
815 }
816}
817void Cogs::Core::EchoSounder::UniformGridSystem::addPing(Context * context, UniformGridComponent *component, UniformGridPing &ping)
818{
819 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::addPing");
820 uint64_t ping_number;
821 UniformGridData &data = getData(component);
822
823 std::unique_lock<decltype(data.mu)> lock(data.mu);
824 ping_number = data.ping_number++;
825 if(data.sample_data && data.sample_data_backbuffer){
826 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::drop_ping");
827 data.pings_droped++;
828 LOG_DEBUG(logger, "Dropping ping (Handled: %llu Droped: %llu)", data.pings_handled, data.pings_droped);
829 return;
830 }
831 data.pings_handled++;
832 UniformGridSampleData *sample_data = new UniformGridSampleData(context, this, component, data, ping, ping_number);
833 if(!data.sample_data){
834 data.sample_data = sample_data;
835 data.vesselOrientationGlobal = sample_data->vesselOrientationGlobal;
836 data.vesselPositionGlobal = sample_data->vesselPositionGlobal;
837 }
838 else{
839 data.sample_data_backbuffer = sample_data;
840 }
841}
842Cogs::Core::EchoSounder::UniformGridSampleData::UniformGridSampleData(Context *context, UniformGridSystem *system, UniformGridComponent *component, UniformGridData &data, UniformGridPing &ping, uint64_t ping_number):
843 context(context),
844 system(system),
845 component(component),
846 data(data),
847 config(data.config[ping.configId]),
848 average_alpha(component->average_alpha),
849 filter_width(component->filter_width),
850 filter_falloff(component->filter_falloff),
851 ping_number(ping_number)
852{
853 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::UniformGridSampleData");
854
855 if(component->compute == ComputeType::AVX2 && !system->has_avx2)
856 component->compute = ComputeType::SSE41;
857 if(component->compute == ComputeType::AVX2 && !system->has_sse41)
858 component->compute = ComputeType::Reference;
859 compute = component->compute;
860
861 vesselOrientationGlobal = ping.orientation;
862 vesselPositionGlobal = ping.position;
863 glm::quat arrayOrientationVessel;
864 glm::vec3 arrayPositionVessel;
865 getArrayToVesselTransform(arrayOrientationVessel,
866 arrayPositionVessel,
867 config->transducerAlpha,
868 config->transducerOffset);
869 arrayOrientationGlobal = vesselOrientationGlobal * arrayOrientationVessel;
870 arrayPositionGlobal = vesselPositionGlobal + vesselOrientationGlobal * arrayPositionVessel;
871
872 inverseOrientation = glm::inverse(arrayOrientationGlobal);
873
874 minDistance = config->depthOffset;
875 maxDistance = minDistance + config->sampleCount * config->depthStep;
876
877 minDistanceSquared = minDistance * minDistance;
878 maxDistanceSquared = maxDistance * maxDistance;
879
880 majorCount = config->majorCount;
881 minorCount = config->minorCount;
882 uint32_t beamCount = majorCount * minorCount;
883
884 float minDirectionX = std::numeric_limits<float>::max();
885 float maxDirectionX = -std::numeric_limits<float>::max();
886 float minDirectionY = std::numeric_limits<float>::max();
887 float maxDirectionY = -std::numeric_limits<float>::max();
888 float maxBeamWidthX = -std::numeric_limits<float>::max();
889 float maxBeamWidthY = -std::numeric_limits<float>::max();
890 for(uint32_t i = 0; i<beamCount; i++){
891 maxDirectionX = glm::max(maxDirectionX, config->directionX[i]);
892 minDirectionX = glm::min(minDirectionX, config->directionX[i]);
893 maxDirectionY = glm::max(maxDirectionY, config->directionY[i]);
894 minDirectionY = glm::min(minDirectionY, config->directionY[i]);
895 maxBeamWidthX = glm::max(maxBeamWidthX, config->beamWidthX[i]);
896 maxBeamWidthY = glm::max(maxBeamWidthY, config->beamWidthY[i]);
897 }
898
899 if (majorCount == 1) {
900 minDirectionX -= 0.5f*maxBeamWidthX;
901 maxDirectionX += 0.5f*maxBeamWidthX;
902 }
903 if (minorCount == 1) {
904 minDirectionY -= 0.5f*maxBeamWidthY;
905 maxDirectionY += 0.5f*maxBeamWidthY;
906 }
907
908 // Compute Ping Frustrum
909 getBoundingFrustum(frustum,
910 arrayOrientationGlobal,
911 arrayPositionGlobal,
912 config->coordSys,
913 minDirectionX, maxDirectionX,
914 minDirectionY, maxDirectionY,
915 minDistance, maxDistance);
916
917 maxIndices = glm::uvec3(glm::max(1u, minorCount) - 1u,
918 glm::max(1u, majorCount) - 1u,
919 glm::max(1u, config->sampleCount) - 1u);
920 polarShift = glm::vec3(minDirectionY, minDirectionX, minDistance);
921 polarScale = glm::vec3((glm::max(2u, minorCount) - 2u) / (maxDirectionY - minDirectionY),
922 (glm::max(2u, majorCount) - 2u) / (maxDirectionX - minDirectionX),
923 (glm::max(2u, config->sampleCount) - 2u) / (maxDistance - minDistance));
924
925 // Calculate linear
926 {
927 size_t concurrency = Threads::hardwareConcurrency()-2;
928 uint32_t sampleCount = config->sampleCount;
929 uint32_t steps = (uint32_t)((beamCount*sampleCount)/concurrency+1);
930 linear_group = context->taskManager->createGroup(system->Queue);
931 decibel = std::move(ping.storage);
932 linear.resize(beamCount*sampleCount, false);
933 for(uint32_t i=0; i<beamCount*sampleCount; i+=steps){
934 uint32_t end = std::min(i+steps, beamCount*sampleCount);
935 auto task = [this, start=i, end](){
936 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::dBToLinearTask");
937 float *db = (float*)decibel.data();
938 for(uint32_t i=start; i<end; i++){
939 linear[i] = glm::clamp(dbToLinear(db[i]), 0.0f, 1.0f);
940 }
941 };
942 context->taskManager->enqueueChild(linear_group, task);
943 }
944 }
945}
946enum{
947 INSIDE,
948 OUTSIDE,
949 INTERSECTION
950};
951static int FrustrumAABBIntersect(glm::vec4 *frustum, float minDistanceSquared, float maxDistanceSquared, glm::vec3 &min, glm::vec3 &max, float r2min, float r2max)
952{
953 int ret = INSIDE;
954 for(uint32_t i=0; i<4; i++){
955 glm::vec3 vmin, vmax;
956 if(frustum[i].x < 0) {
957 vmin.x = min.x;
958 vmax.x = max.x;
959 } else {
960 vmin.x = max.x;
961 vmax.x = min.x;
962 }
963 if(frustum[i].y < 0) {
964 vmin.y = min.y;
965 vmax.y = max.y;
966 } else {
967 vmin.y = max.y;
968 vmax.y = min.y;
969 }
970 if(frustum[i].z < 0) {
971 vmin.z = min.z;
972 vmax.z = max.z;
973 } else {
974 vmin.z = max.z;
975 vmax.z = min.z;
976 }
977 if(glm::dot(glm::vec3(frustum[i]), vmin) < 0.0f)
978 return OUTSIDE;
979 if(glm::dot(glm::vec3(frustum[i]), vmax) <= 0.0f)
980 ret = INTERSECTION;
981 }
982 {
983 if(r2min > maxDistanceSquared) return OUTSIDE;
984 else if(r2max >= maxDistanceSquared) ret = INTERSECTION;
985 }
986 {
987 if(r2max < minDistanceSquared) return OUTSIDE;
988 else if(r2min <= minDistanceSquared) ret = INTERSECTION;
989 }
990 return ret;
991}
992void Cogs::Core::EchoSounder::UniformGridSampleData::cull_tiles()
993{
994 // Calculate frustrum AABB
995 glm::vec3 aabb_min(std::numeric_limits<float>::max());
996 glm::vec3 aabb_max(-std::numeric_limits<float>::max());
997 uint32_t beamCount = minorCount * majorCount;
998 for(uint32_t i=0; i<beamCount; i++){
999 glm::vec3 d = arrayOrientationGlobal * getBeamDir(config->coordSys, config->directionX[i], config->directionY[i]);
1000 glm::vec3 min = d * minDistance + arrayPositionGlobal;
1001 glm::vec3 max = d * maxDistance + arrayPositionGlobal;
1002 aabb_min = glm::min(aabb_min, min);
1003 aabb_min = glm::min(aabb_min, max);
1004 aabb_max = glm::max(aabb_max, min);
1005 aabb_max = glm::max(aabb_max, max);
1006 }
1007
1008 glm::vec3 invSize = glm::vec3(1.0f)/glm::vec3(data.size-glm::uvec3(1));
1009 glm::vec3 invScale = glm::vec3(1.0f)/data.scale;
1010
1011 glm::ivec3 imin = glm::floor(aabb_min*invScale*invSize);
1012 glm::ivec3 imax = glm::ceil(aabb_max*invScale*invSize);
1013 imin-=glm::ivec3(2); // Fixme
1014 imax+=glm::ivec3(2);
1015
1016 // Find tiles intersection beam
1017 for(int32_t tx=imin.x; tx<=imax.x; tx++){
1018 for(int32_t ty=imin.y; ty<=imax.y; ty++){
1019 for(int32_t tz=imin.z; tz<=imax.z; tz++){
1020 glm::vec3 tileCoord(tx, ty, tz);
1021 glm::vec3 tilePos = tileCoord*(glm::vec3(data.size-glm::uvec3(1))*data.scale);
1022 glm::vec3 q_a = tilePos - arrayPositionGlobal;
1023 glm::vec3 q_b = q_a + glm::vec3(data.size-glm::uvec3(1)) * data.scale;
1024 glm::vec3 q_min = glm::min(q_a, q_b);
1025 glm::vec3 q_max = glm::max(q_a, q_b);
1026 // TODO r2min/max calculation might not be accurate
1027 float r2min = std::numeric_limits<float>::max();
1028 float r2max = -std::numeric_limits<float>::max();
1029 for(uint32_t i=0; i<8; i++){
1030 glm::vec3 q(i & 1 ? q_min.x : q_max.x,
1031 i & 2 ? q_min.y : q_max.y,
1032 i & 4 ? q_min.z : q_max.z);
1033 float r2 = glm::dot(q, q);
1034 r2min = glm::min(r2min, r2);
1035 r2max = glm::max(r2max, r2);
1036 }
1037 int qi = FrustrumAABBIntersect(frustum, minDistanceSquared, maxDistanceSquared, q_min, q_max, r2min, r2max);
1038 if(qi == OUTSIDE) continue;
1039 if(qi == INSIDE) tiles_inside.push_back(glm::ivec3(tx, ty, tz));
1040 else tiles_intersecting.push_back(glm::ivec3(tx, ty, tz));
1041 }
1042 }
1043 }
1044}
1045void Cogs::Core::EchoSounder::UniformGridSampleData::evict_tiles()
1046{
1047 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::evict_tiles");
1048 if(data.tiles.size() > component->max_tiles){
1049 data.evict_number++;
1050 for(auto it = data.tiles.begin(); it != data.tiles.end(); ){
1051 if(it->second.ping_number < data.evict_number){
1052 _aligned_free(it->second.data);
1053 Entity *ptr = it->second.visualizer.get();
1054 if(ptr) data.destroy.push_back(ptr);
1055 it = data.tiles.erase(it);
1056 }
1057 else{
1058 it++;
1059 }
1060 }
1061 }
1062}
1063Cogs::Core::EchoSounder::Tile Cogs::Core::EchoSounder::UniformGridSampleData::GenTile()
1064{
1065 Tile tile = {};
1066 tile.data = (float*)_aligned_malloc(sizeof(float)*data.size.x*data.size.y*data.size.z, sizeof(float)*8);
1067 if(!tile.data){
1068 evict_tiles();
1069 tile.data = (float*)_aligned_malloc(sizeof(float)*data.size.x*data.size.y*data.size.z, sizeof(float)*8);
1070 }
1071 if(!tile.data){
1072 LOG_ERROR(logger, "UniformGrid: OutOfMemory! Clearing!\n");
1073 system->clear(component);
1074 tile.data = (float*)_aligned_malloc(sizeof(float)*data.size.x*data.size.y*data.size.z, sizeof(float)*8);
1075 }
1076 if(!tile.data){
1077 LOG_ERROR(logger, "UniformGrid: OutOfMemory! Exit!\n");
1078 exit(EXIT_FAILURE);
1079 }
1080 memset(tile.data, 0, sizeof(float)*data.size.x*data.size.y*data.size.z);
1081 tile.minValue = -std::numeric_limits<float>::max();
1082 tile.maxValue = std::numeric_limits<float>::max();
1083 return tile;
1084}
1085void Cogs::Core::EchoSounder::UniformGridSampleData::gen_tiles()
1086{
1087 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::gen_tiles");
1088 for(auto tileIndex: tiles_inside){
1089 glm::uvec3 tilePos = glm::vec3(tileIndex)+glm::vec3(1<<15);
1090 TileKey key = encodeTileKey(tilePos);
1091 if(data.tiles.count(key) == 0){
1092 data.tiles[key] = GenTile();
1093 }
1094 }
1095 for(auto tileIndex: tiles_intersecting){
1096 glm::uvec3 tilePos = glm::vec3(tileIndex)+glm::vec3(1<<15);
1097 TileKey key = encodeTileKey(tilePos);
1098 if(data.tiles.count(key) == 0){
1099 data.tiles[key] = GenTile();
1100 }
1101 }
1102}
1103void Cogs::Core::EchoSounder::UniformGridSampleData::sample_ref(float *d, const float *v, uint32_t z, uint32_t y, uint32_t x, float r2, glm::vec3 q)
1104{
1105 glm::vec3 a = inverseOrientation * q;
1106 float r = std::sqrt(r2);
1107
1108 // x = sin(dirX)
1109 // y = sin(dirY)
1110 // z = r = sqrt(1.0f - x*x - y*y)
1111 float dirX = std::asin(a.x / r);
1112 float dirY = std::asin(a.y / r);
1113 glm::vec3 polarPos = glm::vec3(dirY, dirX, r);
1114 glm::vec3 t;
1115 glm::uvec3 i;
1116 glm::uvec2 j;
1117
1118 if (config->majorCount > 1 && config->minorCount > 1) {
1119 // Discard samples outside of beam grid
1120 glm::vec3 xi = polarScale * (polarPos - polarShift);
1121 if ((xi.x <= 0.0f || xi.y <= 0.0f || xi.z <= 0.0f ||
1122 xi.x > maxIndices.x || xi.y > maxIndices.y || xi.z > maxIndices.z))
1123 return;
1124
1125 // Figure out interpolation parameters.
1126 glm::vec3 tau = glm::floor(xi);
1127 t = xi - tau;
1128 i = glm::uvec3(tau);
1129 j = glm::uvec2(i) + glm::uvec2(1);
1130 }
1131 else {
1132 // Initial float index:
1133 glm::vec3 xi = glm::max(glm::vec3(0.f), polarScale*(polarPos - polarShift));
1134
1135 // Figure out interpolation parameters.
1136 glm::vec3 tau = glm::floor(xi);
1137 t = xi - tau;
1138 i = glm::min(maxIndices, glm::uvec3(tau));
1139 j = glm::min(glm::uvec2(maxIndices), glm::uvec2(i) + glm::uvec2(1));
1140 }
1141
1142 float val00 = v[(i.y*minorCount + i.x)*config->sampleCount + i.z];
1143 float val01 = v[(j.y*minorCount + i.x)*config->sampleCount + i.z];
1144 float val0 = (1.f - t.y)*val00 + t.y*val01;
1145
1146 float val10 = v[(i.y*minorCount + j.x)*config->sampleCount + i.z];
1147 float val11 = v[(j.y*minorCount + j.x)*config->sampleCount + i.z];
1148 float val1 = (1.f - t.y)*val10 + t.y*val11;
1149
1150 float val = (1.f - t.x)*val0 + t.x*val1;
1151
1152 uint32_t index = z * data.size.y*data.size.x + y * data.size.x + x;
1153#if 1
1154 // Exponential moving average
1155 float prev = d[index];
1156 if(prev == 0.0){
1157 d[index] = val;
1158 }
1159 else{
1160 float alpha = average_alpha;
1161 d[index] = val*alpha + prev*(1.0f-alpha);
1162 }
1163#else
1164 d[index] = val;
1165#endif
1166 //tile.age[index] = float(10e-7*((*m)[0].ping->timestamp - misc.ping->timestamp));
1167}
1168void Cogs::Core::EchoSounder::UniformGridSampleData::sample_sn90_ref(float *d, const float *v, uint32_t z, uint32_t y, uint32_t x, float r2, glm::vec3 q)
1169{
1170 glm::vec3 a = inverseOrientation * q;
1171 float r = std::sqrt(r2);
1172
1173 // x = sin(dirX)
1174 // y = cos(dirX) * sin(dirY)
1175 // z = cos(dirX) * cos(dirY)
1176 //
1177 // y/z = sin(dirY)/cos(dirY) = tan(dirY)
1178 //
1179 // dirY = atan(y/z)
1180 float dirX = std::asin(a.x / r);
1181 float dirY = std::atan(a.y / a.z);
1182 glm::vec3 polarPos = glm::vec3(dirY, dirX, a.z);
1183
1184 // Initial float index:
1185 glm::vec3 xi = glm::max(glm::vec3(0.f), polarScale*(polarPos - polarShift));
1186
1187 // Figure out interpolation parameters.
1188 glm::vec3 tau = glm::floor(xi);
1189 glm::vec3 t = xi - tau;
1190
1191#if 0
1192 glm::uvec3 i = glm::min(maxIndices, glm::uvec3(tau));
1193 glm::uvec2 j = glm::min(glm::uvec2(maxIndices), glm::uvec2(i) + glm::uvec2(1));
1194
1195 float val00 = v[(i.y*minorCount + i.x)*config->sampleCount + i.z];
1196 float val01 = v[(j.y*minorCount + i.x)*config->sampleCount + i.z];
1197 float val0 = (1.f - t.y)*val00 + t.y*val01;
1198
1199 float val10 = v[(i.y*minorCount + j.x)*config->sampleCount + i.z];
1200 float val11 = v[(j.y*minorCount + j.x)*config->sampleCount + i.z];
1201 float val1 = (1.f - t.y)*val10 + t.y*val11;
1202
1203 float val = (1.f - t.x)*val0 + t.x*val1;
1204#elif 1
1205 glm::uvec2 i = glm::min(glm::uvec2(maxIndices), glm::uvec2(tau));
1206 glm::uvec2 j = glm::min(glm::uvec2(maxIndices), glm::uvec2(i) + glm::uvec2(1));
1207
1208 float s = t.y;
1209 float scale = -2.0f*cos(dirY)*polarScale.y;
1210
1211 uint32_t z00 = (uint32_t)std::clamp(tau.z + s*scale, 0.0f, (float)maxIndices.z);
1212 uint32_t z01 = (uint32_t)std::clamp(tau.z + (s-1)*scale, 0.0f, (float)maxIndices.z);
1213 uint32_t z10 = (uint32_t)std::clamp(tau.z + s*scale, 0.0f, (float)maxIndices.z);
1214 uint32_t z11 = (uint32_t)std::clamp(tau.z + (s-1)*scale, 0.0f, (float)maxIndices.z);
1215
1216 float val00 = v[(i.y*minorCount + i.x)*config->sampleCount + z00];
1217 float val01 = v[(j.y*minorCount + i.x)*config->sampleCount + z01];
1218 float val0 = (1.f - t.y)*val00 + t.y*val01;
1219
1220 float val10 = v[(i.y*minorCount + j.x)*config->sampleCount + z10];
1221 float val11 = v[(j.y*minorCount + j.x)*config->sampleCount + z11];
1222 float val1 = (1.f - t.y)*val10 + t.y*val11;
1223
1224 float val = (1.f - t.x)*val0 + t.x*val1;
1225#elif 0
1226 glm::uvec2 i = glm::min(glm::uvec2(maxIndices), glm::uvec2(tau));
1227 glm::uvec2 j = glm::min(glm::uvec2(maxIndices), glm::uvec2(i) + glm::uvec2(1));
1228
1229 float s = t.y;
1230 float scale = -2.0f*cos(dirY)*polarScale.y;
1231
1232 uint32_t z000 = std::min<uint32_t>(maxIndices.z, tau.z + s*scale);
1233 uint32_t z010 = std::min<uint32_t>(maxIndices.z, tau.z + (s-1)*scale);
1234 uint32_t z100 = std::min<uint32_t>(maxIndices.z, tau.z + s*scale);
1235 uint32_t z110 = std::min<uint32_t>(maxIndices.z, tau.z + (s-1)*scale);
1236
1237 uint32_t z001 = std::min<uint32_t>(maxIndices.z, tau.z + s*scale + 1);
1238 uint32_t z011 = std::min<uint32_t>(maxIndices.z, tau.z + (s-1)*scale + 1);
1239 uint32_t z101 = std::min<uint32_t>(maxIndices.z, tau.z + s*scale + 1);
1240 uint32_t z111 = std::min<uint32_t>(maxIndices.z, tau.z + (s-1)*scale + 1);
1241
1242 float val000 = v[(i.y*minorCount + i.x)*config->sampleCount + z000];
1243 float val010 = v[(j.y*minorCount + i.x)*config->sampleCount + z010];
1244 float val00 = (1.f - t.y)*val000 + t.y*val010;
1245
1246 float val100 = v[(i.y*minorCount + j.x)*config->sampleCount + z100];
1247 float val110 = v[(j.y*minorCount + j.x)*config->sampleCount + z110];
1248 float val10 = (1.f - t.y)*val100 + t.y*val110;
1249
1250 float val0 = (1.f - t.x)*val00 + t.x*val10;
1251
1252 float val001 = v[(i.y*minorCount + i.x)*config->sampleCount + z001];
1253 float val011 = v[(j.y*minorCount + i.x)*config->sampleCount + z011];
1254 float val01 = (1.f - t.y)*val001 + t.y*val011;
1255
1256 float val101 = v[(i.y*minorCount + j.x)*config->sampleCount + z101];
1257 float val111 = v[(j.y*minorCount + j.x)*config->sampleCount + z111];
1258 float val11 = (1.f - t.y)*val101 + t.y*val111;
1259
1260 float val1 = (1.f - t.x)*val01 + t.x*val11;
1261
1262 float val = (1.f - t.z)*val0 + t.z*val1;
1263#endif
1264
1265 uint32_t index = z * data.size.y*data.size.x + y * data.size.x + x;
1266 d[index] = val;
1267 //age[index] = float(10e-7*((*m)[0].ping->timestamp - misc.ping->timestamp));
1268}
1269void Cogs::Core::EchoSounder::UniformGridSampleData::sample_intersecting(size_t start, size_t end)
1270{
1272
1273 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::sampleIntersectingTask");
1274 float *v = linear.data();
1275 for (size_t ti = start; ti < end; ti++) {
1276 Timer timer = Timer::startNew();
1277 glm::vec3 tileCoord = tiles_intersecting[ti];
1278 glm::vec3 tilePos = tileCoord*(glm::vec3(data.size-glm::uvec3(1))*data.scale);
1279 glm::uvec3 tileIndex = tileCoord+glm::vec3(1<<15);
1280 TileKey key = encodeTileKey(tileIndex);
1281 Tile &tile = data.tiles[key];
1282 if (config->coordSys == 0) {
1283 for (uint32_t z = 0; z < data.size.z; z++) {
1284 for (uint32_t y = 0; y < data.size.y; y++) {
1285 for (uint32_t x = 0; x < data.size.x; x++) {
1286 glm::vec3 p = data.scale * glm::vec3(x, y, z);
1287 glm::vec3 q = (tilePos - arrayPositionGlobal) + p; // Sample pos with origin in beam bundle origin.
1288 float r2 = glm::dot(q, q);
1289
1290 bool in_0 = 0.f <= glm::dot(glm::vec3(frustum[0]), q);
1291 bool in_1 = 0.f <= glm::dot(glm::vec3(frustum[1]), q);
1292 bool in_2 = 0.f <= glm::dot(glm::vec3(frustum[2]), q);
1293 bool in_3 = 0.f <= glm::dot(glm::vec3(frustum[3]), q);
1294 bool in_4 = minDistanceSquared <= r2;
1295 bool in_5 = r2 <= maxDistanceSquared;
1296 if (!in_0 || !in_1 || !in_2 || !in_3 || !in_4 || !in_5) continue;
1297 sample_ref(tile.data, v, z, y, x, r2, q);
1298 }
1299 }
1300 }
1301 }
1302 else if (config->coordSys == 1) {
1303 switch (compute) {
1304 case ComputeType::SSE41:
1305 sampleTile_border_sse41(tile.data, v, tileCoord, tileIndex, data.size, maxIndices, tilePos, data.scale, arrayPositionGlobal, frustum,
1306 minDistanceSquared, maxDistanceSquared, inverseOrientation,
1307 config->coordSys, minorCount, config->sampleCount, polarScale, polarShift);
1308 break;
1309 case ComputeType::FMA:
1310 sampleTile_inner_fma_gather(tile.data, v, tileCoord, tileIndex, data.size, maxIndices, tilePos, data.scale, arrayPositionGlobal, frustum,
1311 minDistanceSquared, maxDistanceSquared, inverseOrientation,
1312 config->coordSys, minorCount, config->sampleCount, polarScale, polarShift);
1313 break;
1314 case ComputeType::AVX2:
1315 sampleTile3_border_avx2(tile.data, tile.minValue, tile.maxValue, v, tileCoord, tileIndex, data.size, maxIndices, tilePos, data.scale, arrayPositionGlobal, frustum,
1316 minDistanceSquared, maxDistanceSquared, inverseOrientation,
1317 config->coordSys, minorCount, config->sampleCount, polarScale, polarShift);
1318 break;
1319 default:
1320 for (uint32_t z = 0; z < data.size.z; z++) {
1321 for (uint32_t y = 0; y < data.size.y; y++) {
1322 for (uint32_t x = 0; x < data.size.x; x++) {
1323 glm::vec3 p = data.scale * glm::vec3(x, y, z);
1324 glm::vec3 q = (tilePos - arrayPositionGlobal) + p; // Sample pos with origin in beam bundle origin.
1325 float r2 = glm::dot(q, q);
1326
1327 bool in_0 = 0.f <= glm::dot(glm::vec3(frustum[0]), q);
1328 bool in_1 = 0.f <= glm::dot(glm::vec3(frustum[1]), q);
1329 bool in_2 = 0.f <= glm::dot(glm::vec3(frustum[2]), q);
1330 bool in_3 = 0.f <= glm::dot(glm::vec3(frustum[3]), q);
1331 bool in_4 = minDistanceSquared <= r2;
1332 bool in_5 = r2 <= maxDistanceSquared;
1333 if (!in_0 || !in_1 || !in_2 || !in_3 || !in_4 || !in_5) continue;
1334 sample_sn90_ref(tile.data, v, z, y, x, r2, q);
1335 }
1336 }
1337 }
1338 break;
1339 }
1340 }
1341 else { assert(false && "Unhandled coordinate system"); }
1342 data.sample_intersecting.us.fetch_add(uint32_t(1e6*timer.elapsedSeconds()));
1343 data.sample_intersecting.N.fetch_add(1);
1344 tile.mesh = data.createIsoSurfaces(tile, scratch, tilePos);
1345 tile.ping_number = ping_number;
1346 tile.update = true;
1347 }
1348}
1349void Cogs::Core::EchoSounder::UniformGridSampleData::sample_inside(size_t start, size_t end)
1350{
1352
1353 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::sampleInsideTask");
1354 float *v = linear.data();
1355 for(size_t ti=start; ti<end; ti++){
1356 Timer timer = Timer::startNew();
1357 glm::vec3 tileCoord = tiles_inside[ti];
1358 glm::vec3 tilePos = tileCoord*(glm::vec3(data.size-glm::uvec3(1))*data.scale);
1359 glm::uvec3 tileIndex = tileCoord+glm::vec3(1<<15);
1360 TileKey key = encodeTileKey(tileIndex);
1361 Tile &tile = data.tiles[key];
1362 if(config->coordSys == 0){
1363 for(uint32_t z=0; z<data.size.z; z++){
1364 for(uint32_t y=0; y<data.size.y; y++){
1365 for(uint32_t x=0; x<data.size.x; x++){
1366 glm::vec3 p = data.scale * glm::vec3(x, y, z);
1367 glm::vec3 q = (tilePos - arrayPositionGlobal) + p; // Sample pos with origin in beam bundle origin.
1368 float r2 = glm::dot(q, q);
1369 sample_ref(tile.data, v, z, y, x, r2, q);
1370 }
1371 }
1372 }
1373 }
1374 else if(config->coordSys == 1){
1375 switch (compute) {
1376 case ComputeType::SSE41:
1377 sampleTile_border_sse41(tile.data, v, tileCoord, tileIndex, data.size, maxIndices, tilePos, data.scale, arrayPositionGlobal, frustum,
1378 minDistanceSquared, maxDistanceSquared, inverseOrientation,
1379 config->coordSys, minorCount, config->sampleCount, polarScale, polarShift);
1380 break;
1381 case ComputeType::FMA:
1382 sampleTile_inner_fma_gather(tile.data, v, tileCoord, tileIndex, data.size, maxIndices, tilePos, data.scale, arrayPositionGlobal, frustum,
1383 minDistanceSquared, maxDistanceSquared, inverseOrientation,
1384 config->coordSys, minorCount, config->sampleCount, polarScale, polarShift);
1385 break;
1386 case ComputeType::AVX2:
1387 sampleTile3_border_avx2(tile.data, tile.minValue, tile.maxValue, v, tileCoord, tileIndex, data.size, maxIndices, tilePos, data.scale, arrayPositionGlobal, frustum,
1388 minDistanceSquared, maxDistanceSquared, inverseOrientation,
1389 config->coordSys, minorCount, config->sampleCount, polarScale, polarShift);
1390 break;
1391 default:
1392 for (uint32_t z = 0; z < data.size.z; z++) {
1393 for (uint32_t y = 0; y < data.size.y; y++) {
1394 for (uint32_t x = 0; x < data.size.x; x++) {
1395 glm::vec3 p = data.scale * glm::vec3(x, y, z);
1396 glm::vec3 q = (tilePos - arrayPositionGlobal) + p; // Sample pos with origin in beam bundle origin.
1397 float r2 = glm::dot(q, q);
1398 sample_sn90_ref(tile.data, v, z, y, x, r2, q);
1399 }
1400 }
1401 }
1402 break;
1403 }
1404 }
1405 else { assert(false && "Unhandled coordinate system"); }
1406 data.sample_inside.us.fetch_add(uint32_t(1e6*timer.elapsedSeconds()));
1407 data.sample_inside.N.fetch_add(1);
1408 tile.mesh = data.createIsoSurfaces(tile, scratch, tilePos);
1409 tile.ping_number = ping_number;
1410 tile.update = true;
1411 }
1412}
1413namespace Cogs::Core::EchoSounder{
1414 typedef std::pair<TileKey, Tile*> Tmp;
1416 bool operator() (const Tmp &lhs, const Tmp &rhs) const
1417 {
1418 return lhs.second->ping_number<rhs.second->ping_number;
1419 }
1420};
1421}// namespace ...
1422bool Cogs::Core::EchoSounder::UniformGridData::update_isosurface()
1423{
1424 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::update_isosurface");
1425 std::priority_queue<Tmp, std::vector<Tmp>, TileOrder> queue; // TODO just sort an array
1426 for(auto &tmp : tiles){
1427 if(tmp.second.update_iso){
1428 queue.push(std::pair<TileKey, Tile*>(tmp.first, &tmp.second));
1429 }
1430 }
1431 if(!queue.size()) return false;
1432
1433 bool ret = false;
1434 std::vector<Tmp> update_tiles;
1435 uint32_t k = 0;
1436 while(!queue.empty()){
1437 update_tiles.push_back(queue.top());
1438 queue.pop();
1439 if(++k >= 512){
1440 ret = true;
1441 break;
1442 }
1443 }
1444
1445 auto group = system->context->taskManager->createGroup(system->Queue);
1446 size_t concurrency = Threads::hardwareConcurrency()-1;
1447 size_t tile_count = update_tiles.size();
1448 size_t task_count = std::min(tile_count, concurrency);
1449 size_t steps = tile_count/task_count+1;
1450 for(size_t i=0; i<tile_count; i+=steps){
1451 size_t end = std::min(i+steps, tile_count);
1452 auto task = [this, &update_tiles, i, end]()
1453 {
1454 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "UniformGrid::update_isosurface_task");
1456 for(size_t j=i; j<end; j++){
1457 Tmp tmp = update_tiles[j];
1458 Tile &tile = *tmp.second;
1459 glm::uvec3 tileIndex(decodeTileKey(tmp.first));
1460 glm::vec3 tileCoord(glm::vec3(tileIndex)-glm::vec3(1<<15));
1461 glm::vec3 tilePos = tileCoord*(glm::vec3(size-glm::uvec3(1))*scale);
1462 tile.mesh = createIsoSurfaces(tile, scratch, tilePos);
1463 tile.update = true;
1464 tile.update_iso = false;
1465 }
1466 };
1467 system->context->taskManager->enqueueChild(group, task);
1468 }
1469 system->context->taskManager->wait(group);
1470 return ret;
1471}
class Entity * getContainer() const
Get the container currently owning this component instance.
Definition: Component.h:151
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
virtual ComponentHandle createComponent()
Create a new component instance.
Context * context
Pointer to the Context instance the system lives in.
virtual void initialize(Context *context)
Initialize the system.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
void preUpdate()
Run the pre-update method of the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
class EntityStore * store
Entity store.
Definition: Context.h:231
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
EntityPtr createChildEntity(const StringView &type, ComponentModel::Entity *parent, const StringView &name=StringView())
Create a new Entity, parenting it to the given parent.
void removeChild(ComponentModel::Entity *parent, const ComponentModel::Entity *entity)
Remove the parent-child relationship between parent and entity.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
Definition: MeshComponent.h:15
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Definition: MeshComponent.h:29
Renders the contents of a MeshComponent using the given materials.
MaterialInstanceHandle material
Material used to render the mesh.
Renders a mesh with flexible submesh usage.
std::vector< MaterialInstanceHandle > materials
Materials used to render individual sub-meshes.
static constexpr TaskQueueId GlobalQueue
Global task queue.
Definition: TaskManager.h:224
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
Log implementation class.
Definition: LogManager.h:139
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
Definition: EntityPtr.h:12
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
@ CustomMaterial
Custom material set. Keeps legacy material component from changing the material instance.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Handle to a Component instance.
Definition: Component.h:67
ComponentType * resolveComponent() const
Definition: Component.h:90
void initialize(Context *context) override
Initialize the system.
void destroyComponent(ComponentHandle component) override
void setFloatProperty(const VariableKey key, float value)
Set the float property with the given key to value.
void setTransparent()
Set the material instance to transparent, indicating to the renderer that blending should be enabled ...
void setTextureAddressMode(const StringView &key, const StringView &addressMode)
Set texture address mode with textual name.
void clone(MaterialInstance *instance)
Clone the the given material instance.
MaterialOptions options
Material rendering options used by this instance.
void setVec4Property(const VariableKey key, glm::vec4 value)
Set the vec4 property with the given key to value.
Material * material
Material resource this MaterialInstance is created from.
void setOpaque()
Set the material instance to opaque, indicating to the renderer that blending should be disabled for ...
bool depthWriteEnabled
If depth writes should be enabled rendering the geometry with the material instance.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
@ PointList
List of points.
Definition: Common.h:124