half_float_maker.cc 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // Copyright (c) 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "media/video/half_float_maker.h"
  5. #include "third_party/libyuv/include/libyuv.h"
  6. namespace media {
  7. HalfFloatMaker::~HalfFloatMaker() = default;
  8. // By OR-ing with 0x3800, 10-bit numbers become half-floats in the
  9. // range [0.5..1) and 9-bit numbers get the range [0.5..0.75).
  10. //
  11. // Half-floats are evaluated as:
  12. // float value = pow(2.0, exponent - 25) * (0x400 + fraction);
  13. //
  14. // In our case the exponent is 14 (since we or with 0x3800) and
  15. // pow(2.0, 14-25) * 0x400 evaluates to 0.5 (our offset) and
  16. // pow(2.0, 14-25) * fraction is [0..0.49951171875] for 10-bit and
  17. // [0..0.24951171875] for 9-bit.
  18. //
  19. // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
  20. class HalfFloatMaker_xor : public HalfFloatMaker {
  21. public:
  22. explicit HalfFloatMaker_xor(int bits_per_channel)
  23. : bits_per_channel_(bits_per_channel) {}
  24. float Offset() const override { return 0.5; }
  25. float Multiplier() const override {
  26. int max_input_value = (1 << bits_per_channel_) - 1;
  27. // 2 << 11 = 2048 would be 1.0 with our exponent.
  28. return 2048.0 / max_input_value;
  29. }
  30. void MakeHalfFloats(const uint16_t* src, size_t num, uint16_t* dst) override {
  31. // Micro-benchmarking indicates that the compiler does
  32. // a good enough job of optimizing this loop that trying
  33. // to manually operate on one uint64 at a time is not
  34. // actually helpful.
  35. // Note to future optimizers: Benchmark your optimizations!
  36. for (size_t i = 0; i < num; i++)
  37. dst[i] = src[i] | 0x3800;
  38. }
  39. private:
  40. int bits_per_channel_;
  41. };
  42. // Convert plane of 16 bit shorts to half floats using libyuv.
  43. class HalfFloatMaker_libyuv : public HalfFloatMaker {
  44. public:
  45. explicit HalfFloatMaker_libyuv(int bits_per_channel) {
  46. int max_value = (1 << bits_per_channel) - 1;
  47. // For less than 15 bits, we can give libyuv a multiplier of
  48. // 1.0, which is faster on some platforms. If bits is 16 or larger,
  49. // a multiplier of 1.0 would cause overflows. However, a multiplier
  50. // of 1/max_value would cause subnormal floats, which perform
  51. // very poorly on some platforms.
  52. if (bits_per_channel <= 15) {
  53. libyuv_multiplier_ = 1.0f;
  54. } else {
  55. // This multiplier makes sure that we avoid subnormal values.
  56. libyuv_multiplier_ = 1.0f / 4096.0f;
  57. }
  58. resource_multiplier_ = 1.0f / libyuv_multiplier_ / max_value;
  59. }
  60. float Offset() const override { return 0.0f; }
  61. float Multiplier() const override { return resource_multiplier_; }
  62. void MakeHalfFloats(const uint16_t* src, size_t num, uint16_t* dst) override {
  63. // Source and dest stride can be zero since we're only copying
  64. // one row at a time.
  65. int stride = 0;
  66. int rows = 1;
  67. libyuv::HalfFloatPlane(src, stride, dst, stride, libyuv_multiplier_, num,
  68. rows);
  69. }
  70. private:
  71. float libyuv_multiplier_;
  72. float resource_multiplier_;
  73. };
  74. std::unique_ptr<HalfFloatMaker> HalfFloatMaker::NewHalfFloatMaker(
  75. int bits_per_channel) {
  76. if (bits_per_channel < 11) {
  77. return std::unique_ptr<HalfFloatMaker>(
  78. new HalfFloatMaker_xor(bits_per_channel));
  79. } else {
  80. return std::unique_ptr<HalfFloatMaker>(
  81. new HalfFloatMaker_libyuv(bits_per_channel));
  82. }
  83. }
  84. } // namespace media