Blue Brain BioExplorer
DeflectPixelOp.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2024, EPFL/Blue Brain Project
3  * All rights reserved. Do not distribute without permission.
4  * Responsible Author: Daniel Nachbaur <daniel.nachbaur@epfl.ch>
5  *
6  * This file is part of https://github.com/BlueBrain/Core
7  *
8  * This library is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License version 3.0 as published
10  * by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include "DeflectPixelOp.h"
23 #include "utils.h"
24 
25 #include <ospray/SDK/fb/FrameBuffer.h>
26 
27 #include <platform/engines/ospray/Utils.h>
28 
29 namespace
30 {
31 std::future<bool> make_ready_future()
32 {
33  std::promise<bool> promise;
34  promise.set_value(true);
35  return promise.get_future();
36 }
37 
38 #pragma omp declare simd
39 inline unsigned char clampCvt(float f)
40 {
41  if (f < 0.f)
42  f = 0.f;
43  if (f > 1.f)
44  f = 1.f;
45  return f * 255.f + .5f;
46 }
47 
48 const size_t ALIGNMENT = 64;
49 } // namespace
50 
51 namespace core
52 {
53 DeflectPixelOp::Instance::Instance(::ospray::FrameBuffer* fb_, DeflectPixelOp& parent)
54  : _parent(parent)
55 {
56  fb = fb_;
57 }
58 
60 
62 {
63  if (!_parent._deflectStream)
64  return;
65  const size_t numTiles = fb->getTotalTiles();
66  if (_pixels.size() < numTiles)
67  {
68  _pixels.resize(numTiles);
69 
70  for (auto& i : _pixels)
71  {
72  if (i)
73  continue;
74 
75  i.reset((unsigned char*)_mm_malloc(TILE_SIZE * TILE_SIZE * 4, ALIGNMENT));
76  memset(i.get(), 255, TILE_SIZE * TILE_SIZE * 4);
77  }
78  }
79 }
80 
82 {
83  if (!_parent._deflectStream)
84  return;
85 
86  auto sharedFuture = _parent._deflectStream->finishFrame().share();
87  for (auto& i : _parent._finishFutures)
88  i.second = sharedFuture;
89 }
90 
91 void DeflectPixelOp::Instance::postAccum(::ospray::Tile& tile)
92 {
93  if (!_parent._deflectStream)
94  return;
95 
96  const auto& fbSize = fb->getNumPixels();
97  ::ospray::vec2i tileSize{TILE_SIZE, TILE_SIZE};
98 
99  if (tile.region.lower.x + TILE_SIZE > fbSize.x)
100  tileSize.x = fbSize.x % TILE_SIZE;
101  if (tile.region.lower.y + TILE_SIZE > fbSize.y)
102  tileSize.y = fbSize.y % TILE_SIZE;
103 
104  deflect::ImageWrapper image(_copyPixels(tile, tileSize), tileSize.x, tileSize.y, deflect::RGBA, tile.region.lower.x,
105  tile.region.lower.y);
106  image.compressionPolicy = _parent._params.getCompression() ? deflect::COMPRESSION_ON : deflect::COMPRESSION_OFF;
107  image.compressionQuality = _parent._params.getQuality();
108  image.subsampling = _parent._params.getChromaSubsampling();
109  image.rowOrder = _parent._params.isTopDown() ? deflect::RowOrder::top_down : deflect::RowOrder::bottom_up;
110 
111  const auto name = fb->getParamString("name");
112  image.view = utils::getView(name);
113  image.channel = utils::getChannel(name);
114 
115  try
116  {
117  auto i = _parent._finishFutures.find(pthread_self());
118  if (i == _parent._finishFutures.end())
119  {
120  // only for the first frame
121  std::lock_guard<std::mutex> _lock(_parent._mutex);
122  _parent._finishFutures.insert({pthread_self(), make_ready_future()});
123  }
124  else
125  i->second.wait(); // complete previous frame
126 
127  _parent._deflectStream->send(image).get();
128  }
129  catch (const std::exception& exc)
130  {
131  std::cerr << "Encountered error during sendImage: " << exc.what() << std::endl;
132  }
133 }
134 
135 unsigned char* DeflectPixelOp::Instance::_copyPixels(::ospray::Tile& tile, const ::ospray::vec2i& tileSize)
136 {
137  const size_t tileID = tile.region.lower.y / TILE_SIZE * fb->getNumTiles().x + tile.region.lower.x / TILE_SIZE;
138 
139 #ifdef __GNUC__
140  unsigned char* __restrict__ pixels = (unsigned char*)__builtin_assume_aligned(_pixels[tileID].get(), ALIGNMENT);
141 #else
142  unsigned char* __restrict__ pixels = _pixels[tileID].get();
143 #endif
144  float* __restrict__ red = tile.r;
145  float* __restrict__ green = tile.g;
146  float* __restrict__ blue = tile.b;
147 
148  if (tileSize.x < TILE_SIZE)
149  {
150  int index = 0;
151  for (int i = 0; i < tileSize.y; ++i)
152  {
153  for (int j = 0; j < tileSize.x; ++j)
154  {
155  pixels[index + 0] = clampCvt(red[i * TILE_SIZE + j]);
156  pixels[index + 1] = clampCvt(green[i * TILE_SIZE + j]);
157  pixels[index + 2] = clampCvt(blue[i * TILE_SIZE + j]);
158  index += 4;
159  }
160  }
161  return pixels;
162  }
163 
164 // clang-format off
165  // AVX vectorization with icc 17 reports (create with -qopt-report=5)
166  //LOOP BEGIN at ../plugins/engines/ospray/modules/deflect/DeflectPixelOp.cpp(130,16)
167  // remark #15388: vectorization support: reference red[i] has aligned access
168  // remark #15388: vectorization support: reference green[i] has aligned access
169  // remark #15388: vectorization support: reference blue[i] has aligned access
170  // remark #15329: vectorization support: non-unit strided store was emulated for the variable <U137_V[i*4]>, stride is 4
171  // remark #15329: vectorization support: non-unit strided store was emulated for the variable <U137_V[i*4+1]>, stride is 4
172  // remark #15329: vectorization support: non-unit strided store was emulated for the variable <U137_V[i*4+2]>, stride is 4
173  // remark #15305: vectorization support: vector length 32
174  // remark #15301: OpenMP SIMD LOOP WAS VECTORIZED
175  // remark #15448: unmasked aligned unit stride loads: 3
176  // remark #15453: unmasked strided stores: 3
177  // remark #15475: --- begin vector cost summary ---
178  // remark #15476: scalar cost: 56
179  // remark #15477: vector cost: 9.460
180  // remark #15478: estimated potential speedup: 5.910
181  // remark #15487: type converts: 3
182  // remark #15488: --- end vector cost summary ---
183  // remark #25015: Estimate of max trip count of loop=128
184  //LOOP END
185 // clang-format on
186 #ifdef __INTEL_COMPILER
187 #pragma vector aligned
188 #endif
189 #pragma omp simd
190  for (int i = 0; i < TILE_SIZE * TILE_SIZE; ++i)
191  {
192  pixels[i * 4 + 0] = clampCvt(red[i]);
193  pixels[i * 4 + 1] = clampCvt(green[i]);
194  pixels[i * 4 + 2] = clampCvt(blue[i]);
195  }
196  return pixels;
197 }
198 
200 {
201  core::fromOSPRayProperties(_params.getPropertyMap(), *this);
202  if (_deflectStream && utils::needsReset(*_deflectStream, _params))
203  {
204  _finish();
205  _deflectStream.reset();
206  }
207 
208  if (!_deflectStream && _params.getEnabled())
209  {
210  try
211  {
212  _deflectStream.reset(new deflect::Stream(_params.getId(), _params.getHostname(), _params.getPort()));
213  }
214  catch (const std::runtime_error& ex)
215  {
216  std::cerr << "Deflect failed to initialize. " << ex.what() << std::endl;
217  _params.setEnabled(false);
218  }
219  }
220 }
221 
222 ospray::PixelOp::Instance* DeflectPixelOp::createInstance(::ospray::FrameBuffer* fb, PixelOp::Instance* /*prev*/)
223 {
224  return new Instance(fb, *this);
225 }
226 
227 void DeflectPixelOp::_finish()
228 {
229  for (auto& i : _finishFutures)
230  i.second.wait();
231  _finishFutures.clear();
232 }
233 } // namespace core
234 
235 namespace ospray
236 {
238 }
std::string getHostname() const
const PropertyMap & getPropertyMap() const
std::string getId() const
unsigned getPort() const
void setEnabled(const bool enabled)
::ospray::PixelOp::Instance * createInstance(::ospray::FrameBuffer *fb, PixelOp::Instance *prev) final
void fromOSPRayProperties(PropertyMap &object, ::ospray::ManagedObject &ospObject)
Definition: OSPRayUtils.cpp:94
deflect::View getView(const std::string &name)
Definition: utils.h:46
uint8_t getChannel(const std::string &name)
Definition: utils.h:63
bool needsReset(const deflect::Observer &stream, const DeflectParameters &params)
Definition: utils.h:70
OSP_REGISTER_PIXEL_OP(core::DeflectPixelOp, deflectPixelOp)
Instance(::ospray::FrameBuffer *fb_, DeflectPixelOp &parent)
void postAccum(::ospray::Tile &tile) final
#define deflectPixelOp
Definition: utils.h:28