Beatmup
linear_mapping.cpp
Go to the documentation of this file.
1 /*
2  Beatmup image and signal processing library
3  Copyright (C) 2020, lnstadrum
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "linear_mapping.h"
20 #include "../utils/fixed_point.h"
21 #include "../utils/string_builder.h"
22 #include "../gpu/bgl.h"
23 #include <cstring>
24 
25 using namespace Beatmup;
26 using namespace GL;
27 
28 #ifdef BEATMUP_OPENGLVERSION_GLES20
30 #else
32 #endif
33 
34 static const char* UNIFORM_MATRIX = "mtrx";
35 static const char* UNIFORM_INPUT = "inp";
36 static const char* UNIFORM_BIAS = "bias";
37 static const char* UNIFORM_DELTA = "dt";
38 
39 static const int
40  VECTOR_TEXTURE_DIMS = 2, // actual number of dimensions in textures storing input and bias vectors
41  VECTOR_MAIN_DIM = 1; // main (non-singleton) dimension in textures containing input and bias
42 
43 
44 static inline bool useFixedPointStorage(bool forceFixed16Storage) {
45 #ifdef BEATMUP_OPENGLVERSION_GLES20
46  return true;
47 #else
48  return forceFixed16Storage;
49 #endif
50 }
51 
52 
53 /**
54  \internal
55  Adds GLSL statement sampling a vector component.
56  \param declaration If declaring a new variable, its datatype (empty string otherwise)
57  \param variable GLSL variable to assign the sampled value to
58  \param uniform The uniform variable to sample
59  \param coordinate Texture coordinate
60  \param delta If non-negative a delta with the corresponding index is added to the texture coordinate
61 */
62 void sampleVectorComponent(String& code, const char* declaration, const char* variable, const char* uniform, const char* coordinate, int delta = -1) {
63  code.printf("%s %s = texture%dD(%s, vec%d(", declaration, variable, VECTOR_TEXTURE_DIMS, uniform, VECTOR_TEXTURE_DIMS);
64  for (int i = 0; i < VECTOR_TEXTURE_DIMS; ++i) {
65  if (i > 0)
66  code(",");
67  if (i == VECTOR_MAIN_DIM) {
68  if (delta >= 0) {
69  code.printf("%s + %s[%d]", coordinate, UNIFORM_DELTA, delta);
70  }
71  else {
72  code(coordinate);
73  }
74  }
75  else
76  code("0");
77  }
78  code.line("));");
79 }
80 
81 
82 /**
83  \brief Generates GLSL code of float to 16-bit fixed point packing/unpacking functions.
84  \param[in,out] code String builder containing the shader source code
85  \param[in] suffix A suffix to add to the function names
86  \param[in] pack If `true`, the packing function code is added
87  \param[in] unpackVec If `true`, the vector unpacking function code is added (producing a vec4 from two vec4)
88  \param[in] unpackScalar If `true`, the scalar unpacking function code is added (producing a float from two floats)
89  \param[in] packPrecision Number of bits storing the fractional part in the resulting packed value
90  \param[in] unpackPrecision Number of bits storing the fractional part in the input value to unpack
91 */
92 static inline void declareGlsl16bitFixedPointFunctions(StringBuilder& code, const char* suffix, bool pack, bool unpackVec, bool unpackScalar, unsigned int packPrecision = 8, unsigned int unpackPrecision = 8) {
93  if (pack) {
94  code.printf("lowp vec2 pack%s(highp float v) {", suffix);
95  if (packPrecision != 8)
96  code.printf("v *= %0.1f;", packPrecision > 8 ? (float)(1 << (packPrecision - 8)) : 1.0f / (1 << (packPrecision - 8)));
97  code.line(
98  " return vec2((256.0 / 255.0) * fract(v), (1.0 / 255.0) * floor(v) + (128.0 / 255.0));"
99  "}"
100  );
101  }
102  const float
103  scaleMsb = unpackPrecision > 8 ? 1.0f / (1 << (unpackPrecision - 8)) : (float)(1 << (unpackPrecision - 8)),
104  scaleLsb = 255.0f * scaleMsb;
105  if (unpackVec)
106  code.printf(
107  "highp vec4 unpack%s(lowp vec4 lsb, lowp vec4 msb) {"
108  " return lsb * (%0.5f / 256.0) + floor(255.0 * msb - 127.5) * %0.5f;"
109  "}",
110  suffix, scaleLsb, scaleMsb
111  );
112  if (unpackScalar)
113  code.printf(
114  "highp float unpack%s(lowp float lsb, lowp float msb) {"
115  " return lsb * (%0.5f / 256.0) + floor(255.0 * msb - 127.5) * %0.5f;"
116  "}",
117  suffix, scaleLsb, scaleMsb
118  );
119 }
120 
121 
122 /**
123  Converts 16-bit fixed-point number into a floating point number.
124  \param[in] lsb The least significant byte of the input fixed-point value
125  \param[in] msb The most significant byte of the input fixed-point value
126  \return the floating-point value.
127 */
128 static inline float unpackFloatFrom16bit(const uint8_t lsb, const int msb) {
129  return (float)Fixed16<8>::interpret( ((msb - 128) << 8) + lsb );
130 }
131 
132 
133 /**
134  \brief Packs a floating point value into a 16-bit fixed-point value.
135  The resulting representation matches the formulas in unpackFloatFrom16bit(..) and packFloatTo16bit(..)
136  A biased clamping-friendly formulation is used: "0.0" on input produces the most significant channel value of "0.5".
137  \param[in] value The input value to pack
138  \param[in] lsb The least significant byte in the output fixed-point value
139  \param[in] msb The most significant byte in the output fixed-point value
140 */
141 static inline void packFloatTo16bit(const float value, uint8_t& lsb, uint8_t& msb) {
142  union {
143  Fixed16<8> val;
144  uint16_t bytes;
145  } ptr{ value };
146  lsb = ptr.bytes & 0xff;
147  msb = (ptr.bytes >> 8) + 128;
148 }
149 
150 
151 static inline void findMinMax(const float* values, const int count, float& minVal, float& maxVal) {
152  minVal = maxVal = values[0];
153  for (int i = 1; i < count; ++i) {
154  minVal = std::min(minVal, values[i]);
155  maxVal = std::max(maxVal, values[i]);
156  }
157 }
158 
159 
160 /**
161  Real-valued matrix usable by GPU
162 */
164  friend class LinearMapping;
165 private:
167  const int texWidth, texHeight; //!< texture size
168  const int width, height; //!< matrix size
169  float mapScale; //!< scaling applied to the matrix coefficients to optimize the fixed-point range use
170  float mapOffset; //!< offset applied to the matrix coefficients after scaling to optimize the fixed-point range use
171 
172  void prepare(GraphicPipeline& gpu, bool);
173 
174 public:
175  Matrix(GraphicPipeline& gpu, int width, int height, const bool floatingPoint);
176 
177  /**
178  \brief Creates a matrix in GPU memory.
179  The memory layout depends on the data format. For floating point data four consecutive rows are packed into color channels.
180  The first texture row looks like this ("x,y" for "col,row" in the input matrix):
181  r 0,0 1,0 2,0 3,0 4,0 ...
182  g 0,1 1,1 2,1 3,1 4,1 ...
183  b 0,2 1,2 2,2 3,2 4,2 ...
184  a 0,3 1,3 2,3 3,3 4,3 ...
185  In 16-bit fixed point case, two consecutive columns are packed into color channels, alternating the least and most significant bytes:
186  r 0,0L 0,0M 0,1L 0,1M ...
187  g 1,0L 1,0M 1,1L 1,1M ...
188  b 2,0L 2,0M 2,1L 2,1M ...
189  a 3,0L 3,0M 3,1L 3,1M ...
190  \param[in] gpu A graphic pipeline instance
191  \param[in] width Matrix width (number of columns)
192  \param[in] height Matrix height (number of rows)
193  \param[in] values Matrix coefficients in scanline order (rows)
194  \param[in] floatingPoint If `true`, the matrix coefficients are stored in floating point format (16-bit fixed point otherwise).
195  */
196  Matrix(GraphicPipeline& gpu, int width, int height, const float* values, const bool floatingPoint);
197 
198  void bind(GraphicPipeline& gpu, int textureUnit) const;
199 
200  inline int getMatrixWidth() const { return width; }
201  inline int getMatrixHeight() const { return height; }
202  inline float getScale() const { return mapScale; }
203  inline float getOffset() const { return mapOffset; }
204 
205  inline const int getWidth() const { return texWidth; }
206  inline const int getHeight() const { return texHeight; }
207  inline const int getDepth() const { return 1; }
208  inline const TextureFormat getTextureFormat() const { return format; }
209 };
210 
211 
212 LinearMapping::Matrix::Matrix(GraphicPipeline& gpu, int width, int height, const bool floatingPoint) :
213  format(floatingPoint ? TextureFormat::RGBAx32f : TextureFormat::RGBAx8),
214  texWidth(width), texHeight(floatingPoint ? height / 4 : height / 2),
215  width(width), height(height), mapScale(1), mapOffset(0)
216 {
217 #ifdef BEATMUP_OPENGLVERSION_GLES20
218  if (floatingPoint)
219  throw RuntimeError("Floating-point linear mapping is not supported in ES 2.0");
220 #endif
221  RuntimeError::check(height % 4 == 0, "Matrix height must be a multiple of four.");
222 
223  // init texture with zeros
224  const int numPix = 4 * getWidth() * getHeight();
225  std::vector<uint8_t> zeros(numPix * TextureHandler::TEXTURE_FORMAT_BYTES_PER_PIXEL[format]);
226  if (floatingPoint)
227  memset(zeros.data(), 0, zeros.size());
228  else
229  for (int i = 0; i < numPix; i += 2)
230  packFloatTo16bit(0, zeros[i], zeros[i + 1]);
231 
232  // setup texture
233  glGenTextures(1, &textureHandle);
234  glActiveTexture(GL_TEXTURE0);
235  glBindTexture(GL_TEXTURE_2D, textureHandle);
236 #ifdef BEATMUP_OPENGLVERSION_GLES20
237  glTexImage2D(GL_TEXTURE_2D,
238  0,
240  getWidth(), getHeight(),
241  0,
244  zeros.data()
245  );
246 #else
247  glTexStorage2D(GL_TEXTURE_2D, 1, BITMAP_INTERNALFORMATS[format], getWidth(), getHeight());
248  glTexSubImage2D(GL_TEXTURE_2D,
249  0, 0, 0, getWidth(), getHeight(),
252  zeros.data()
253  );
254 #endif
255 }
256 
257 
258 LinearMapping::Matrix::Matrix(GraphicPipeline& gpu, int width, int height, const float* values, const bool floatingPoint):
259  format(floatingPoint ? TextureFormat::RGBAx32f : TextureFormat::RGBAx8),
260  texWidth(width),
261  texHeight(floatingPoint ? height / 4 : height / 2),
263 {
264 #ifdef BEATMUP_OPENGLVERSION_GLES20
265  if (floatingPoint)
266  throw RuntimeError("Floating-point linear mapping is not supported in ES 2.0");
267 #endif
268  if (floatingPoint)
269  RuntimeError::check(height % 4 == 0, "Matrix height must be a multiple of four.");
270  else {
271  RuntimeError::check(width % 4 == 0, "Matrix width must be a multiple of four.");
272  RuntimeError::check(height % 2 == 0, "Matrix height must be pair.");
273  }
274 
275  // init texture handler
276  glGenTextures(1, &textureHandle);
277  glActiveTexture(GL_TEXTURE0);
278  glBindTexture(GL_TEXTURE_2D, textureHandle);
279 
280  // floating point compute mode: r, g, b, a store consecutive rows, not columns
281  if (floatingPoint) {
282 #ifndef BEATMUP_OPENGLVERSION_GLES20
283  std::vector<float> textureData(4 * getWidth() * getHeight());
284  for (int y = 0, i = 0; y < height; y += 4)
285  for (int x = 0; x < width; ++x, i += 4) {
286  textureData[i + 0] = values[y * width + x];
287  textureData[i + 1] = values[(y + 1) * width + x];
288  textureData[i + 2] = values[(y + 2) * width + x];
289  textureData[i + 3] = values[(y + 3) * width + x];
290  }
291 
292  glTexStorage2D(GL_TEXTURE_2D, 1, BITMAP_INTERNALFORMATS[format], getWidth(), getHeight());
293  glTexSubImage2D(GL_TEXTURE_2D,
294  0, 0, 0, getWidth(), getHeight(),
297  textureData.data()
298  );
299 #endif
300  }
301 
302  // fixed point compute mode: r, g, b, a store 4 consecutive rows (all LSB or all MSB)
303  else {
304  // measure magnitude
305  float minVal, maxVal;
306  findMinMax(values, width * height, minVal, maxVal);
307  mapScale = maxVal > minVal ? (Fixed16<8>::max() - Fixed16<8>::min()) / (maxVal - minVal) : 1;
308  mapOffset = maxVal > minVal ? Fixed16<8>::min() - minVal * mapScale : 0;
309 
310  // convert to fixed point
311  std::vector<uint8_t> textureData(4 * getWidth() * getHeight());
312  for (int y = 0, i = 0; y < height; y += 2)
313  for (int x = 0; x < width; x += 4, i += 16) {
314  packFloatTo16bit(mapOffset + mapScale * values[y * width + x + 0], textureData[i + 0], textureData[i + 4]);
315  packFloatTo16bit(mapOffset + mapScale * values[y * width + x + 1], textureData[i + 1], textureData[i + 5]);
316  packFloatTo16bit(mapOffset + mapScale * values[y * width + x + 2], textureData[i + 2], textureData[i + 6]);
317  packFloatTo16bit(mapOffset + mapScale * values[y * width + x + 3], textureData[i + 3], textureData[i + 7]);
318  packFloatTo16bit(mapOffset + mapScale * values[(y + 1) * width + x + 0], textureData[i + 8], textureData[i + 12]);
319  packFloatTo16bit(mapOffset + mapScale * values[(y + 1) * width + x + 1], textureData[i + 9], textureData[i + 13]);
320  packFloatTo16bit(mapOffset + mapScale * values[(y + 1) * width + x + 2], textureData[i + 10], textureData[i + 14]);
321  packFloatTo16bit(mapOffset + mapScale * values[(y + 1) * width + x + 3], textureData[i + 11], textureData[i + 15]);
322  }
323 
324 #ifdef BEATMUP_OPENGLVERSION_GLES20
325  glTexImage2D(GL_TEXTURE_2D,
326  0,
328  getWidth(), getHeight(),
329  0,
332  textureData.data()
333  );
334 #else
335  glTexStorage2D(GL_TEXTURE_2D, 1, BITMAP_INTERNALFORMATS[format], getWidth(), getHeight());
336  glTexSubImage2D(GL_TEXTURE_2D,
337  0, 0, 0, getWidth(), getHeight(),
340  textureData.data()
341  );
342 #endif
343  }
344 }
345 
346 
348  glBindTexture(GL_TEXTURE_2D, textureHandle);
349 }
350 
351 
352 void LinearMapping::Matrix::bind(GraphicPipeline& gpu, int textureUnit) const {
353  glActiveTexture(GL_TEXTURE0 + textureUnit);
354  glBindTexture(GL_TEXTURE_2D, textureHandle);
355  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
356  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
357 #ifdef BEATMUP_DEBUG
358  GLException::check("binding matrix");
359 #endif
360 }
361 
362 
363 Vector::Vector(Context& context, GraphicPipeline& gpu, const int size, const Format format) :
364  context(context),
365  texFormat(format == Format::FLOAT ? TextureHandler::TextureFormat::RGBAx32f : TextureHandler::TextureFormat::RGBAx8),
366  format(format),
367  size(size),
368  mapScale(1), mapOffset(0)
369 {
370 #ifdef BEATMUP_OPENGLVERSION_GLES20
371  if (format == Format::FLOAT)
372  throw RuntimeError("Floating-point vectors are not supported in ES 2.0");
373 #endif
374  RuntimeError::check(size % 4 == 0, "Vector size must be a multiple of four.");
375 
376  // init texture handler
377  glGenTextures(1, &textureHandle);
378  glActiveTexture(GL_TEXTURE0);
379  glBindTexture(GL_TEXTURE_2D, textureHandle);
380 
381 #ifdef BEATMUP_OPENGLVERSION_GLES20
382  glTexImage2D(GL_TEXTURE_2D,
383  0,
385  getWidth(), getHeight(),
386  0,
389  nullptr
390  );
391 #else
392  glTexStorage2D(GL_TEXTURE_2D, 1, BITMAP_INTERNALFORMATS[texFormat], getWidth(), getHeight());
393 #endif
394 }
395 
396 
397 Vector::Vector(Context& context, GraphicPipeline& gpu, const int size, const Format format, const float* values, bool remap) :
398  context(context),
399  texFormat(format == Format::FLOAT ? TextureHandler::TextureFormat::RGBAx32f : TextureHandler::TextureFormat::RGBAx8),
400  format(format),
401  size(size),
402  mapScale(1), mapOffset(0)
403 {
404 #ifdef BEATMUP_OPENGLVERSION_GLES20
405  if (format == Format::FLOAT)
406  throw RuntimeError("Floating-point vectors are not supported in ES 2.0");
407 #endif
408  RuntimeError::check(size % 4 == 0, "Vector size must be a multiple of four.");
409 
410  glGenTextures(1, &textureHandle);
411  glActiveTexture(GL_TEXTURE0);
412  glBindTexture(GL_TEXTURE_2D, textureHandle);
413 
414  if (format != Format::FLOAT) {
415  // remap
416  if (remap) {
417  float minVal, maxVal;
418  findMinMax(values, size, minVal, maxVal);
419  if (maxVal > minVal) {
420  if (format == Format::FIXED16) {
421  mapScale = (Fixed16<8>::max() - Fixed16<8>::min()) / (maxVal - minVal);
422  mapOffset = Fixed16<8>::min() - minVal * mapScale;
423  }
424  else if (format == Format::TEXTURE) {
425  mapScale = 1.0f / (maxVal - minVal);
426  mapOffset = - minVal * mapScale;
427  }
428  }
429  }
430 
431  // convert data from floating point
432  std::vector<uint8_t> textureData(4 * getHeight());
433  if (format == Format::FIXED16)
434  for (int i = 0; i < size; ++i)
435  packFloatTo16bit(mapOffset + mapScale * values[i], textureData[2 * i], textureData[2 * i + 1]);
436  else if (format == Format::TEXTURE)
437  for (int i = 0; i < size; ++i) {
438  const float val = mapOffset + mapScale * values[i];
439  textureData[i] = val <= 0.0f ? 0 : val >= 1.0f ? 255 : (uint8_t)roundf_fast(val * 255);
440  }
441  else
442  Insanity::insanity("Invalid vector data format");
443 
444 #ifdef BEATMUP_OPENGLVERSION_GLES20
445  glTexImage2D(GL_TEXTURE_2D,
446  0,
448  getWidth(), getHeight(),
449  0,
452  textureData.data()
453  );
454 #else
455  glTexStorage2D(GL_TEXTURE_2D, 1, BITMAP_INTERNALFORMATS[texFormat], getWidth(), getHeight());
456  glTexSubImage2D(GL_TEXTURE_2D,
457  0, 0, 0, getWidth(), getHeight(),
460  textureData.data()
461  );
462 #endif
463  }
464 
465  else {
466 #ifndef BEATMUP_OPENGLVERSION_GLES20
467  glTexStorage2D(GL_TEXTURE_2D, 1, BITMAP_INTERNALFORMATS[texFormat], getWidth(), getHeight());
468  glTexSubImage2D(GL_TEXTURE_2D,
469  0, 0, 0, getWidth(), getHeight(),
472  values
473  );
474 #endif
475  }
476 }
477 
478 
481 }
482 
483 
485  glBindTexture(GL_TEXTURE_2D, textureHandle);
486 }
487 
488 
489 void Vector::fetch(GraphicPipeline& gpu, std::vector<float>& output) const {
490  output.resize(size);
492 
493  if (format == Format::FLOAT)
494  glReadPixels(0, 0, getWidth(), getHeight(),
495  GL_RGBA, GL_FLOAT,
496  output.data()
497  );
498  else if (format == Format::FIXED16) {
499  glReadPixels(0, 0, getWidth(), getHeight(),
500  GL_RGBA, GL_UNSIGNED_BYTE,
501  (void*)output.data()
502  );
503 
504  // convert values to float; use the same buffer, scan in inverse order
505  const uint8_t* ptr = (const uint8_t*)output.data() + 2 * size;
506  const size_t lastNum = output.size() - 1;
507  for (size_t i = 0; i <= lastNum; ++i) {
508  ptr -= 2;
509  output[lastNum - i] = unpackFloatFrom16bit(ptr[0], ptr[1]);
510  }
511  }
512  else if (format == Format::TEXTURE) {
513  glReadPixels(0, 0, getWidth(), getHeight(),
514  GL_RGBA, GL_UNSIGNED_BYTE,
515  (void*)output.data()
516  );
517 
518  // convert values to float; use the same buffer, scan in inverse order
519  const uint8_t* ptr = (const uint8_t*)output.data() + size;
520  const size_t lastNum = output.size() - 1;
521  for (size_t i = 0; i <= lastNum; ++i) {
522  --ptr;
523  output[lastNum - i] = *ptr / 255.0f;
524  }
525  }
526  else
527  Insanity::insanity("Unsupported vector format");
528 
529  // remap back if needed
530  if (mapScale != 1 || mapOffset != 0)
531  for (auto& _ : output)
532  _ = (_ - mapOffset) / mapScale;
533 }
534 
535 
536 size_t Vector::getMemorySize() const {
537  switch (format) {
538  case Format::FLOAT:
539  return sizeof(float) * (size_t)size;
540  case Format::FIXED16:
541  return 2 * (size_t)size;
542  case Format::TEXTURE:
543  return (size_t)size;
544  }
545  Insanity::insanity("Unsupported vector format");
546  return 0;
547 }
548 
549 
550 LinearMapping::LinearMapping(Context& context, bool forceFixed16Storage) :
551  context(context),
552  buffer{ nullptr, nullptr }, matrix(nullptr), bias(nullptr),
553  multStage(nullptr), sumStage(nullptr), lastSumStage(nullptr), programBank(nullptr),
554  leftPadding(SUM_STAGE_STEPS), forceFixed16Storage(forceFixed16Storage), fixed16Input(false), fixed16Output(false), ready(false)
555 {}
556 
557 
559  if (matrix) {
561  delete matrix;
562  }
563  if (buffer[0]) {
565  delete buffer[0];
566  }
567  if (buffer[1]) {
569  delete buffer[1];
570  }
571  if (!programBank)
573 }
574 
575 
576 void LinearMapping::setMatrix(GraphicPipeline& gpu, const int width, const int height, const float* values) {
577  if (bias)
578  RuntimeError::check(bias->getSize() == height, "Matrix height does not match bias vector length");
579  RuntimeError::check(width % (4 * MULT_STAGE_STEPS) == 0, "Matrix width is not a multiple of " + std::to_string(4 * MULT_STAGE_STEPS));
580  if (matrix) {
582  delete matrix;
583  }
585  ready = false;
586 }
587 
588 
589 void LinearMapping::setBias(GraphicPipeline& gpu, const int height, const float* values) {
590  if (matrix)
591  RuntimeError::check(height == matrix->getMatrixHeight(), "Matrix height does not match bias vector length.");
592  if (bias)
593  delete bias;
595  ready = false;
596 }
597 
598 
600 #ifdef BEATMUP_DEBUG
601  DebugAssertion::check(input.getNumberOfChannels() == 4, "4-channel input texture handler expected");
602  DebugAssertion::check(output.getNumberOfChannels() == 4, "4-channel output texture handler expected");
603 #endif
604 
605  const bool isPlainInput = (4 * input.getHeight() == matrix->getMatrixWidth()); // input samples are just r, g, b, a values in the texture
606  const bool isPackedInput = (2 * input.getHeight() == matrix->getMatrixWidth()); // input samples are packed in (r, g) and (b, a) pairs
607  const bool isPlainOutput = (4 * output.getHeight() == matrix->getMatrixHeight());
608  const bool isPackedOutput = (2 * output.getHeight() == matrix->getMatrixHeight());
609 
610  InvalidArgument::check(isPlainInput || isPackedInput, "Input vector height does not match matrix width");
611  InvalidArgument::check(isPlainOutput || isPackedOutput, "Output vector height does not match matrix height");
612 
613  if (ready && fixed16Input == isPackedInput && fixed16Output == isPackedOutput)
614  // nothing to do, ready to go
615  return;
616 
617  fixed16Input = isPackedInput;
618  fixed16Output = isPackedOutput;
619 
620  if (!matrix)
621  throw RuntimeError("No matrix");
622 
623  // removing old programs
624  if (programBank) {
626  if (sumStage) programBank->release(gpu, sumStage);
628  }
629  else {
630  delete multStage;
631  delete sumStage;
632  delete lastSumStage;
633  }
634 
635  // reseting state
636  programBank = bank;
637 
638  static const unsigned int PRECISION_BOOST = 1; // additional precision bits on intermediate stages (added to 8 fractional bits)
639  const bool fixedPointStorage = useFixedPointStorage(forceFixed16Storage);
640 
641  // make multiplication stage program: this one multiplies small pieces of the matrix by small pieces of the input vector storing the results in a texture
642  {
644  varying highp vec2 texCoord;
645  ));
646 
647  code.printf("uniform sampler%dD %s;", VECTOR_TEXTURE_DIMS, UNIFORM_INPUT);
648  code.printf("uniform sampler2D %s;", UNIFORM_MATRIX);
649  code.printf("uniform highp float %s[%d];", UNIFORM_DELTA, multStageDelta.size());
650  declareGlsl16bitFixedPointFunctions(code, "", fixedPointStorage, fixedPointStorage, false, 8 + PRECISION_BOOST);
651  declareGlsl16bitFixedPointFunctions(code, "In", false, fixed16Input, false);
652  code.nl();
653  code.line("void main() {");
654 
655  // first block: sample the matrix
656  if (!fixedPointStorage) {
657  // read floating-point matrix
658  code.printf("highp mat4 m = mat4("
659  "texture2D(%s, texCoord),"
660  "texture2D(%s, vec2(texCoord.x+%s[0], texCoord.y)),"
661  "texture2D(%s, vec2(texCoord.x+%s[1], texCoord.y)),"
662  "texture2D(%s, vec2(texCoord.x+%s[2], texCoord.y)));",
667  );
668  }
669  else {
670  // read fixed-point packed matrix
671  code.printf("highp vec4 m1 = unpack(texture2D(%s, texCoord), texture2D(%s, vec2(texCoord.x+%s[0], texCoord.y)));",
673  code.printf("highp vec4 m2 = unpack(texture2D(%s, vec2(texCoord.x+%s[1], texCoord.y)), texture2D(%s, vec2(texCoord.x+%s[2], texCoord.y)));",
675  code.printf("m1 = (m1 - %0.8f) * %0.8f;", matrix->getOffset(), 1 / matrix->getScale());
676  code.printf("m2 = (m2 - %0.8f) * %0.8f;", matrix->getOffset(), 1 / matrix->getScale());
677  }
678 
679  // sample the vector
680  if (fixed16Input) {
681  sampleVectorComponent(code, "lowp vec4", "vp1", UNIFORM_INPUT, "texCoord.x");
682  sampleVectorComponent(code, "lowp vec4", "vp2", UNIFORM_INPUT, "texCoord.x", 1);
683  // need to de-multiplex components: LMLM+LMLM to LLLL+MMMM
684  code("highp vec4 v = unpackIn(vec4(vp1.xz, vp2.xz), vec4(vp1.yw, vp2.yw));");
685  }
686  else {
687  sampleVectorComponent(code, "highp vec4", "v", UNIFORM_INPUT, "texCoord.x");
688  }
689 
690  // compute the result
691  if (fixedPointStorage)
692  code.line("highp vec2 r = vec2(dot(m1, v), dot(m2, v));");
693  else
694  code.line("highp vec4 r = m * v;");
695 
696  // further blocks
697  for (int blockIdx = 1; blockIdx < MULT_STAGE_STEPS; ++blockIdx) {
698  // advance the texture coord
699  if (blockIdx == 1)
700  code.printf("highp float x = texCoord.x + %s[3];", UNIFORM_DELTA);
701  else {
702  code.printf("x += %s[3];", UNIFORM_DELTA);
703  }
704 
705  // sample the matrix
706  if (!fixedPointStorage)
707  // read floating-point matrix
708  code.printf("m = mat4("
709  "texture2D(%s, vec2(x, texCoord.y)),"
710  "texture2D(%s, vec2(x+%s[0], texCoord.y)),"
711  "texture2D(%s, vec2(x+%s[1], texCoord.y)),"
712  "texture2D(%s, vec2(x+%s[2], texCoord.y)));",
717  );
718  else {
719  // read fixed-point packed matrix
720  code.printf("m1 = unpack(texture2D(%s, vec2(x, texCoord.y)), texture2D(%s, vec2(x+%s[0], texCoord.y)));",
722  code.printf("m2 = unpack(texture2D(%s, vec2(x+%s[1], texCoord.y)), texture2D(%s, vec2(x+%s[2], texCoord.y)));",
724  code.printf("m1 = (m1 - %0.8f) * %0.8f;", matrix->getOffset(), 1 / matrix->getScale());
725  code.printf("m2 = (m2 - %0.8f) * %0.8f;", matrix->getOffset(), 1 / matrix->getScale());
726  }
727 
728  // sample the vector
729  if (fixed16Input) {
730  sampleVectorComponent(code, "", "vp1", UNIFORM_INPUT, "x");
731  sampleVectorComponent(code, "", "vp2", UNIFORM_INPUT, "x", 1);
732  // need to demultiplex components: LMLM+LMLM to LLLL+MMMM
733  code("v = unpackIn(vec4(vp1.xz, vp2.xz), vec4(vp1.yw, vp2.yw));");
734  }
735  else {
736  sampleVectorComponent(code, "", "v", UNIFORM_INPUT, "x");
737  }
738 
739  // compute the result
740  if (fixedPointStorage)
741  code.line("r += vec2(dot(m1, v), dot(m2, v));");
742  else
743  code.line("r += m * v;");
744  }
745 
746  // store the result
747  if (fixedPointStorage)
748  code("gl_FragColor = vec4(pack(r.x), pack(r.y));");
749  else
750  code.line("gl_FragColor = r;");
751 
752  code("}");
753 
754  if (bank)
755  multStage = (*bank)(gpu, code);
756  else
758  }
759 
760  // set up an intermediate buffer
761  delete buffer[0];
762  buffer[0] = new Matrix(gpu, matrix->getMatrixWidth() / (4 * MULT_STAGE_STEPS) + leftPadding, matrix->getMatrixHeight(), !fixedPointStorage);
763 
764  // compute multiplication stage delta and texture coordinates
765  for (size_t i = 0; i < multStageDelta.size(); ++i)
766  multStageDelta[i] = (float)(i + 1) / matrix->getWidth();
767 
769  Rectangle(0, 0, matrix->getWidth() - 4 * MULT_STAGE_STEPS, matrix->getHeight() - 1),
772  );
773 
774  // determine the number of summation iterations
775  sumStageDelta.clear();
776  sumStageTexCoords.clear();
777  bool isLastIteration = false;
778  int outBufWidth = buffer[0]->getWidth() - leftPadding;
779  const int bufHeightPix = buffer[0]->getHeight();
780  while (!isLastIteration) {
781  const int iterNum = (int)sumStageDelta.size();
782  const int inBufIdx = iterNum % 2; // intermediate buffer index being read at the current iteration
783 
784  // update meaningful output width
785  const int inBufWidth = outBufWidth;
786  outBufWidth = ceili(outBufWidth, SUM_STAGE_STEPS);
787  isLastIteration = (outBufWidth == 1);
788 #ifdef BEATMUP_DEBUG
789  DebugAssertion::check(outBufWidth > 0, "non-positive outBufWidth");
790 #endif
791 
792  // setup second intermediate buffer if not yet
793  if (iterNum == 0 && !isLastIteration) {
794  delete buffer[1];
795  buffer[1] = new Matrix(gpu, outBufWidth + leftPadding, matrix->getMatrixHeight(), !fixedPointStorage);
796  }
797 
798  // compute deltas
799  sumStageDelta.push_back({});
800  auto& deltas = sumStageDelta.back();
801  for (size_t i = 0; i < deltas.size(); ++i)
802  deltas[i] = (float)i / buffer[inBufIdx]->getWidth();
803 
804  // compute texture coords
805  const int rem = inBufWidth - outBufWidth * SUM_STAGE_STEPS;
806  const int ySamplesNum = (isLastIteration && fixedPointStorage && !fixed16Output) ? 2 : 1;
807  // number of samples along Y dimension
808 
809  sumStageTexCoords.emplace_back(gpu.getTextureCoordinates(
810  Rectangle(leftPadding + rem, 0, leftPadding + inBufWidth - SUM_STAGE_STEPS, bufHeightPix - ySamplesNum),
811  IntPoint(buffer[inBufIdx]->getWidth(), bufHeightPix),
812  IntPoint(outBufWidth, bufHeightPix / ySamplesNum)
813  ));
814 
815  // deltas[0] is not used; use it here to pass y step
816  deltas[0] = 1.0f / buffer[inBufIdx]->getHeight();
817  }
818 
819  // make summation stage program
820  {
822  varying highp vec2 texCoord;
823  ));
824 
825  code.printf("uniform sampler2D %s;", UNIFORM_MATRIX);
826  code.printf("uniform highp float %s[%d];", UNIFORM_DELTA, sumStageDelta[0].size());
827  declareGlsl16bitFixedPointFunctions(code, "", fixedPointStorage, false, fixedPointStorage, 8 + PRECISION_BOOST, 8 + PRECISION_BOOST);
828  code.nl();
829  code.line("void main() {");
830  if (!fixedPointStorage) {
831  code("gl_FragColor = ");
832  for (size_t i = 0; i < sumStageDelta[0].size(); ++i)
833  if (i > 0)
834  code.printf(" + texture2D(%s, vec2(texCoord.x + %s[%u], texCoord.y))", UNIFORM_MATRIX, UNIFORM_DELTA, i);
835  else
836  code.printf("texture2D(%s, texCoord)", UNIFORM_MATRIX);
837  code.line(";");
838  }
839  else {
840  for (size_t i = 0; i < sumStageDelta[0].size(); ++i) {
841  if (i == 0) {
842  code.printf("lowp vec4 i = texture2D(%s, texCoord);", UNIFORM_MATRIX);
843  code.printf("highp vec2 s = ");
844  }
845  else {
846  code.printf("i = texture2D(%s, vec2(texCoord.x + %s[%u], texCoord.y));", UNIFORM_MATRIX, UNIFORM_DELTA, i);
847  code("s += ");
848  }
849  code("vec2(unpack(i[0], i[1]), unpack(i[2], i[3]));");
850  }
851  code("gl_FragColor = vec4(pack(s.x), pack(s.y));");
852  }
853  code("}");
854 
855  if (bank)
856  sumStage = (*bank)(gpu, code);
857  else
859  }
860 
861  // make last iteration summation stage program if needed
862  if (fixedPointStorage || bias) {
864  varying highp vec2 texCoord;
865  ));
866 
867  code.printf("uniform sampler2D %s;", UNIFORM_MATRIX);
868  if (bias)
869  code.printf("uniform sampler%dD %s;", VECTOR_TEXTURE_DIMS, UNIFORM_BIAS);
870  code.printf("uniform highp float %s[%d];", UNIFORM_DELTA, sumStageDelta[0].size());
871 
872  // declare packing/unpacking routines
873  declareGlsl16bitFixedPointFunctions(code, "", fixed16Output, false, true, 8, 8 + PRECISION_BOOST);
874  if (bias)
875  declareGlsl16bitFixedPointFunctions(code, "Bias", false, false, true);
876 
877  code.line("void main() {");
878  if (bias)
879  code.line("highp vec2 b;");
880 
881  // floating point case first
882  if (!fixedPointStorage) {
883  if (fixed16Output)
884  throw ImplementationUnsupported("16 bit fixed point output is not supported in floating point computing mode");
885 
886  code("gl_FragColor = ");
887  for (size_t i = 0; i < sumStageDelta[0].size(); ++i)
888  if (i > 0)
889  code.printf(" + texture2D(%s, vec2(texCoord.x + %s[%u], texCoord.y))", UNIFORM_MATRIX, UNIFORM_DELTA, i);
890  else
891  code.printf("texture2D(%s, texCoord)", UNIFORM_MATRIX);
892 
893  if (bias) {
894  code.printf(" + texture%dD(%s, vec%d(", VECTOR_TEXTURE_DIMS, UNIFORM_BIAS, VECTOR_TEXTURE_DIMS);
895  for (int i = 0; i < VECTOR_TEXTURE_DIMS; ++i) {
896  if (i > 0)
897  code(",");
898  code(i == VECTOR_MAIN_DIM ? "texCoord.y" : "0");
899  }
900  code("))");
901  }
902  }
903 
904  else {
905  for (size_t i = 0; i < sumStageDelta[0].size(); ++i) {
906  // sample input
907  if (i == 0) {
908  code.printf("lowp vec4 i = texture2D(%s, texCoord);", UNIFORM_MATRIX);
909  if (!fixed16Output)
910  code.printf("lowp vec4 i2 = texture2D(%s, vec2(texCoord.x, texCoord.y + %s[0]));", UNIFORM_MATRIX, UNIFORM_DELTA);
911  }
912  else {
913  code.printf("i = texture2D(%s, vec2(texCoord.x + %s[%u], texCoord.y));", UNIFORM_MATRIX, UNIFORM_DELTA, i);
914  if (!fixed16Output)
915  code.printf("i2 = texture2D(%s, vec2(texCoord.x + %s[%u], texCoord.y + %s[0]));", UNIFORM_MATRIX, UNIFORM_DELTA, i, UNIFORM_DELTA);
916  }
917 
918  // sum up
919  code(i == 0 ? "highp vec2 s = " : "s += ");
920  code.line("vec2(unpack(i[0], i[1]), unpack(i[2], i[3]));");
921  if (!fixed16Output) {
922  code(i == 0 ? "highp vec2 s2 = " : "s2 += ");
923  code.line("vec2(unpack(i2[0], i2[1]), unpack(i2[2], i2[3]));");
924  }
925  }
926 
927  // add bias
928  if (bias) {
929  sampleVectorComponent(code, "", "i", UNIFORM_BIAS, "texCoord.y");
930  if (!fixed16Output)
931  sampleVectorComponent(code, "", "i2", UNIFORM_BIAS, "texCoord.y", 0);
932 
933  code.line("b = vec2(unpackBias(i[0], i[1]), unpackBias(i[2], i[3]));");
934  if (bias->getMappingScale() != 1 || bias->getMappingOffset() != 0)
935  code.printf("b = %0.8f * (b - %0.8f);", 1 / bias->getMappingScale(), bias->getMappingOffset());
936  code.line("s += b;");
937 
938  if (!fixed16Output) {
939  code.line("b = vec2(unpackBias(i2[0], i2[1]), unpackBias(i2[2], i2[3]));");
940  if (bias->getMappingScale() != 1 || bias->getMappingOffset() != 0)
941  code.printf("b = %0.8f * (b - %0.8f);", 1 / bias->getMappingScale(), bias->getMappingOffset());
942  code.line("s2 += b;");
943  }
944  }
945 
946  code("gl_FragColor = ");
947  if (fixed16Output)
948  code("vec4(pack(s.x), pack(s.y))");
949  else
950  code("vec4(s, s2)");
951  }
952 
953  code.line(";");
954  code("}");
955 
956  if (bank)
957  lastSumStage = (*bank)(gpu, code);
958  else
960  }
961  else
962  lastSumStage = nullptr;
963 
964  ready = true;
965 }
966 
967 
969  static const int MATRIX_UNIT = 1, INPUT_UNIT = 2, BIAS_UNIT = 3;
970 
971  // first stage: compute small 4*4 matrix-by-vector products
972  multStage->enable(gpu);
973  gpu.bindOutput(*buffer[0]);
974 
975  int outBufWidth = buffer[0]->getWidth() - leftPadding;
976  glViewport(leftPadding, 0, outBufWidth, buffer[0]->getHeight());
977 
978  matrix->bind(gpu, MATRIX_UNIT);
979  multStage->setInteger(UNIFORM_MATRIX, MATRIX_UNIT);
980 
981  gpu.bind(input, INPUT_UNIT, TextureParam::INTERP_NEAREST);
982  multStage->setInteger(UNIFORM_INPUT, INPUT_UNIT);
983 
986  multStage->blend();
987 
988  // stage 2: sum them up
989  sumStage->enable(gpu);
990  sumStage->setInteger(UNIFORM_MATRIX, MATRIX_UNIT);
991  for (size_t i = 0; i + 1 < sumStageTexCoords.size(); ++i) {
992  gpu.bindOutput(*buffer[(i + 1) % 2]);
993  outBufWidth = ceili(outBufWidth, SUM_STAGE_STEPS);
994  glViewport(leftPadding, 0, outBufWidth, buffer[0]->getHeight());
995 
996  buffer[i % 2]->bind(gpu, MATRIX_UNIT);
999  sumStage->blend();
1000  }
1001 
1002  // stage 2, last iteration
1004  if (lastProgram != sumStage) {
1005  lastProgram->enable(gpu);
1006  lastProgram->setInteger(UNIFORM_MATRIX, MATRIX_UNIT);
1007  }
1008  gpu.bindOutput(output);
1009  glViewport(0, 0, output.getWidth(), output.getHeight());
1010 
1011  buffer[(sumStageTexCoords.size() - 1) % 2]->bind(gpu, MATRIX_UNIT);
1012  if (bias) {
1013  lastProgram->setInteger(UNIFORM_BIAS, BIAS_UNIT);
1014  gpu.bind(*bias, BIAS_UNIT, TextureParam::INTERP_NEAREST);
1015  }
1016 
1017  lastProgram->setFloatArray(UNIFORM_DELTA, sumStageDelta.back().data(), sumStageDelta.back().size());
1019  lastProgram->blend();
1020 }
1021 
1022 
1025  prepare(gpu, output, input);
1026  process(gpu, output, input);
1027 }
Basic class: task and memory management, any kind of static data.
Definition: context.h:59
GL::RecycleBin * getGpuRecycleBin() const
Definition: context.cpp:340
void setInteger(const std::string &name, const int value, bool safe=false)
Assigns a value to a specific integer variable in the program.
Definition: program.cpp:308
void enable(const GraphicPipeline &gpu)
Definition: program.cpp:250
void setFloatArray(const std::string &name, const float *values, const int length)
Definition: program.cpp:450
GLSL fragment shader.
Definition: program.h:107
Evaluates expression A*x + b = y for a matrix A and vectors x and b using GPU.
Matrix * matrix
the matrix ("A")
RenderingProgram * lastSumStage
last summation stage program: adding bias and clamping output
Vector * bias
optional bias vector ("b")
LinearMapping(Context &context, bool forceFixed16=false)
Instantiates LinearMapping.
const int leftPadding
zero pixels added on the left side in the buffers to sum for whatever the input size is
const bool forceFixed16Storage
if true, 16 bit fixed-point storages are used even if floating point compute is supported by the GPU
bool fixed16Output
if true, the output vector y is stored using 16 bit fixed-point format (float or texture otherwise)
void setBias(GraphicPipeline &gpu, const int height, const float *values)
Matrix * buffer[2]
intermediate buffers used at summation stage in a circular fashion
void prepare(GraphicPipeline &gpu, TextureHandler &output, TextureHandler &input, ProgramBank *bank=nullptr)
Prepares the mapping for application (builds its GPU programs).
RenderingProgram * multStage
multiplication stage program: small pieces of the vector are multiplied by small pieces of the matrix
void process(GraphicPipeline &gpu, TextureHandler &output, TextureHandler &input)
static const int SUM_STAGE_STEPS
number of vectors summed per shader at the summation stage
ProgramBank * programBank
if not null, manages the programs
RenderingProgram * sumStage
summation stage program: the pieces are collected together (repeated multiple times)
bool fixed16Input
if true, the input vector x is stored using 16 bit fixed-point format (float or texture otherwise)
std::vector< std::array< float, SUM_STAGE_STEPS > > sumStageDelta
static const int MULT_STAGE_STEPS
number of 4*4 blocks processed at the multiplication stage
std::vector< Beatmup::Rectangle > sumStageTexCoords
void setMatrix(GraphicPipeline &gpu, const int width, const int height, const float *values)
void operator()(GraphicPipeline &gpu, TextureHandler &result, TextureHandler &input)
Beatmup::Rectangle multStageTexCoords
std::array< float, 4 > multStageDelta
Stores linked GLSL programs and their associated fragment shader codes.
Definition: program_bank.h:31
void release(GraphicPipeline &gpu, GL::RenderingProgram *program)
Marks a program as unused any more.
void put(Item *item)
Puts an item into the recycle bin.
Definition: recycle_bin.cpp:73
GLSL program to render images Makes use of default vertex attributes to pass the texture coordinates ...
Definition: program.h:240
void blend(bool onScreen)
Definition: program.cpp:548
const int getNumberOfChannels() const
Returns number of channels containing in the texture.
virtual const int getHeight() const =0
Height of the texture in pixels.
TextureFormat
Texture format, specifies how the texture should be interpreted on the shader side.
void invalidate(RecycleBin &)
Forces disposing the texture data, e.g.
virtual const int getWidth() const =0
Width of the texture in pixels.
Real-valued vector usable by GPU.
const int getHeight() const
Height of the texture in pixels.
const Format format
data format
const int size
number of samples in the vector
void fetch(GraphicPipeline &gpu, std::vector< float > &output) const
Grabs vector values back from GPU to user memory.
float getMappingOffset() const
Provides the constant offset added to the vector values sent to GPU.
const int getWidth() const
Width of the texture in pixels.
float getMappingScale() const
Provides the scale factor applied to the vector values sent to GPU.
int getSize() const
Returns the length of the vector.
Vector(Context &context, GraphicPipeline &gpu, const int size, const Format format)
Format
Vector data format.
@ FIXED16
16 bit per element
@ TEXTURE
8 bit per element covering [0, 1] range
@ FLOAT
32 bit per element, floating point
const TextureFormat texFormat
texture format of the texture handler
size_t getMemorySize() const
Returns memory size in bytes taken by the vector.
static const Format DEFAULT_FORMAT
void prepare(GraphicPipeline &gpu)
Prepares (eventually uploads) texture data on GPU.
Internal low-level GPU control API.
Definition: pipeline.h:33
static Rectangle getTextureCoordinates(const Rectangle &area, const IntPoint &size, const IntPoint &sampling)
Computes floating-point texture coordinates for pixel-accurate sampling: a texture gets sampled exact...
Definition: pipeline.cpp:976
void switchMode(Mode mode)
Switches GPU mode.
Definition: pipeline.cpp:941
void bindOutput(AbstractBitmap &bitmap)
Binds a bitmap to the pipeline output.
Definition: pipeline.cpp:891
@ INFERENCE
Textures are feature maps computed in fragment shaders.
void setTextureCoordinates(const Rectangle &coords)
Specifies texture coordinates for the next rendering pass.
Definition: pipeline.cpp:966
void bind(GL::TextureHandler &texture, size_t texUnit, const TextureParam param)
Definition: pipeline.cpp:881
Exception thrown when an implementation restriction is encountered.
Definition: exception.h:124
static void insanity(const char *message)
Definition: exception.h:136
static void check(const bool condition, const std::string &message)
Definition: exception.h:75
static void check(const bool condition, const std::string &message)
Definition: exception.h:64
Toolset to build a string content.
StringBuilder & line(const std::string &append)
StringBuilder & printf(const char *format,...)
StringBuilder & nl()
StringBuilder including a string container.
Real-valued matrix usable by GPU.
const TextureFormat getTextureFormat() const
Returns the texture format specifying how the shader must interpret the data.
Matrix(GraphicPipeline &gpu, int width, int height, const bool floatingPoint)
float mapScale
scaling applied to the matrix coefficients to optimize the fixed-point range use
const int getWidth() const
Width of the texture in pixels.
void bind(GraphicPipeline &gpu, int textureUnit) const
void prepare(GraphicPipeline &gpu, bool)
float mapOffset
offset applied to the matrix coefficients after scaling to optimize the fixed-point range use
const int getHeight() const
Height of the texture in pixels.
const int getDepth() const
Depth of the texture in pixels.
static void findMinMax(const float *values, const int count, float &minVal, float &maxVal)
static float unpackFloatFrom16bit(const uint8_t lsb, const int msb)
Converts 16-bit fixed-point number into a floating point number.
static void declareGlsl16bitFixedPointFunctions(StringBuilder &code, const char *suffix, bool pack, bool unpackVec, bool unpackScalar, unsigned int packPrecision=8, unsigned int unpackPrecision=8)
Generates GLSL code of float to 16-bit fixed point packing/unpacking functions.
static const char * UNIFORM_MATRIX
static const int VECTOR_MAIN_DIM
static const char * UNIFORM_DELTA
static const char * UNIFORM_BIAS
static void packFloatTo16bit(const float value, uint8_t &lsb, uint8_t &msb)
Packs a floating point value into a 16-bit fixed-point value.
static bool useFixedPointStorage(bool forceFixed16Storage)
static const int VECTOR_TEXTURE_DIMS
void sampleVectorComponent(String &code, const char *declaration, const char *variable, const char *uniform, const char *coordinate, int delta=-1)
static const char * UNIFORM_INPUT
const GLuint BITMAP_INTERNALFORMATS[]
Definition: bgl.h:147
const GLuint BITMAP_PIXELTYPES[]
Mapping of bitmap pixel formats to GL pixel types.
Definition: bgl.h:160
const GLuint BITMAP_PIXELFORMATS[]
Mapping of bitmap pixel formats to GL pixel formats.
Definition: bgl.h:80
@ BEATMUP_DIALECT
pseudo-extension enabling Beatmup GLSL dialect
Definition: program.h:65
CustomPoint< int > IntPoint
Definition: geometry.h:629
CustomRectangle< float > Rectangle
Definition: geometry.h:627
@ INTERP_NEAREST
nearest neighbor pixel interpolation
std::string to_string(Beatmup::NNets::ActivationFunction function)
CustomPoint< numeric > min(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:724
CustomPoint< numeric > max(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:728
#define BEATMUP_SHADER_CODE(...)
Definition: program.h:26
Fixed-point number.
Definition: fixed_point.h:33
static float max()
Definition: fixed_point.h:111
static float min()
Definition: fixed_point.h:103
static Fixed interpret(const underlying_type value)
Definition: fixed_point.h:93
#define roundf_fast(X)
rounding (nearest integer)
Definition: utils.hpp:22
#define ceili(x, y)
integer division x/y with ceiling
Definition: utils.hpp:21
JNIEnv jobject jint format
jobject jlong jint jint y
JNIEnv jlong jint jint count
jlong jint width
jlong jint jint height
jobject jlong jint x
return(jlong) new Beatmup jlong jstring jint val
jlong jobject size
return bitmap getWidth()
bitmap bind(jenv, jobj)
return bitmap getHeight()
JNIEnv jlong jint jfloat bias