Files
filament/libs/geometry/src/Transcoder.cpp
Philip Rideout 6178ba4a6b Add Transcoder API for C++ clients. (#3837)
* Add Transcoder API for C++ clients.

We will add JavaScript and Java support in a later PR.

* Transcoder: use fixed inner loop in some cases.

This lets clang produce better ARM code for common cases.

* Transcoder: stronger typing in Config, etc.

* Transcoder: add UTILS_RESTRICT and noexcept.
2021-04-22 13:29:52 -07:00

197 lines
8.5 KiB
C++

/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <geometry/Transcoder.h>
#include <math/half.h>
using filament::math::half;
namespace filament {
namespace geometry {
// The internal workhorse function of the Transcoder, which takes arbitrary input but always
// produced packed floats. We expose a more readable interface than this to users, who often have
// untyped blobs of interleaved data. Note that this variant takes an arbitrary number of
// components, we also have a fixed-size variant for better compiler output.
template<typename SOURCE_TYPE, int NORMALIZATION_FACTOR>
void convert(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source, size_t count,
int componentCount, int srcStride) noexcept {
constexpr float scale = 1.0f / float(NORMALIZATION_FACTOR);
uint8_t const* srcBytes = (uint8_t const*) source;
for (size_t i = 0; i < count; ++i, target += componentCount, srcBytes += srcStride) {
SOURCE_TYPE const* src = (SOURCE_TYPE const*) srcBytes;
for (int n = 0; n < componentCount; ++n) {
target[n] = float(src[n]) * scale;
}
}
}
template<typename SOURCE_TYPE, int NORMALIZATION_FACTOR, int NUM_COMPONENTS>
void convert(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source, size_t count,
int srcStride) noexcept {
constexpr float scale = 1.0f / float(NORMALIZATION_FACTOR);
uint8_t const* srcBytes = (uint8_t const*) source;
for (size_t i = 0; i < count; ++i, target += NUM_COMPONENTS, srcBytes += srcStride) {
SOURCE_TYPE const* src = (SOURCE_TYPE const*) srcBytes;
for (int n = 0; n < NUM_COMPONENTS; ++n) {
target[n] = float(src[n]) * scale;
}
}
}
// Similar to "convert" but clamps the result to -1, which is required for normalized signed types.
// For example, -128 can be represented in SBYTE but is outside the permitted range and should
// therefore be clamped. For more information, see the Vulkan spec under the section "Conversion
// from Normalized Fixed-Point to Floating-Point".
template<typename SOURCE_TYPE, int NORMALIZATION_FACTOR>
void convertClamped(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source, size_t count,
int componentCount, int srcStride) noexcept {
constexpr float scale = 1.0f / float(NORMALIZATION_FACTOR);
uint8_t const* srcBytes = (uint8_t const*) source;
for (size_t i = 0; i < count; ++i, target += componentCount, srcBytes += srcStride) {
SOURCE_TYPE const* src = (SOURCE_TYPE const*) srcBytes;
for (int n = 0; n < componentCount; ++n) {
const float value = float(src[n]) * scale;
target[n] = value < -1.0f ? -1.0f : value;
}
}
}
template<typename SOURCE_TYPE, int NORMALIZATION_FACTOR, int NUM_COMPONENTS>
void convertClamped(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source, size_t count,
int srcStride) noexcept {
constexpr float scale = 1.0f / float(NORMALIZATION_FACTOR);
uint8_t const* srcBytes = (uint8_t const*) source;
for (size_t i = 0; i < count; ++i, target += NUM_COMPONENTS, srcBytes += srcStride) {
SOURCE_TYPE const* src = (SOURCE_TYPE const*) srcBytes;
for (int n = 0; n < NUM_COMPONENTS; ++n) {
const float value = float(src[n]) * scale;
target[n] = value < -1.0f ? -1.0f : value;
}
}
}
size_t Transcoder::operator()(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source,
size_t count) const noexcept {
const size_t required = count * mConfig.componentCount * sizeof(float);
if (target == nullptr) {
return required;
}
const uint32_t comp = mConfig.componentCount;
switch (mConfig.componentType) {
case ComponentType::BYTE: {
const uint32_t stride = mConfig.inputStrideBytes ? mConfig.inputStrideBytes : comp;
if (mConfig.normalized) {
if (comp == 2) {
convertClamped<int8_t, 127, 2>(target, source, count, stride);
} else if (comp == 3) {
convertClamped<int8_t, 127, 3>(target, source, count, stride);
} else {
convertClamped<int8_t, 127>(target, source, count, comp, stride);
}
} else {
if (comp == 2) {
convert<int8_t, 1, 2>(target, source, count, stride);
} else if (comp == 3) {
convert<int8_t, 1, 3>(target, source, count, stride);
} else {
convert<int8_t, 1>(target, source, count, comp, stride);
}
}
return required;
}
case ComponentType::UBYTE: {
const uint32_t stride = mConfig.inputStrideBytes ? mConfig.inputStrideBytes : comp;
if (mConfig.normalized) {
if (comp == 2) {
convert<uint8_t, 255, 2>(target, source, count, stride);
} else if (comp == 3) {
convert<uint8_t, 255, 3>(target, source, count, stride);
} else {
convert<uint8_t, 255>(target, source, count, comp, stride);
}
} else {
if (comp == 2) {
convert<uint8_t, 1, 2>(target, source, count, stride);
} else if (comp == 3) {
convert<uint8_t, 1, 3>(target, source, count, stride);
} else {
convert<uint8_t, 1>(target, source, count, comp, stride);
}
}
return required;
}
case ComponentType::SHORT: {
const uint32_t stride = mConfig.inputStrideBytes ? mConfig.inputStrideBytes : (2 * comp);
if (mConfig.normalized) {
if (comp == 2) {
convertClamped<int16_t, 32767, 2>(target, source, count, stride);
} else if (comp == 3) {
convertClamped<int16_t, 32767, 3>(target, source, count, stride);
} else {
convertClamped<int16_t, 32767>(target, source, count, comp, stride);
}
} else {
if (comp == 2) {
convert<int16_t, 1, 2>(target, source, count, stride);
} else if (comp == 3) {
convert<int16_t, 1, 3>(target, source, count, stride);
} else {
convert<int16_t, 1>(target, source, count, comp, stride);
}
}
return required;
}
case ComponentType::USHORT: {
const uint32_t stride = mConfig.inputStrideBytes ? mConfig.inputStrideBytes : (2 * comp);
if (mConfig.normalized) {
if (comp == 2) {
convert<uint16_t, 65535, 2>(target, source, count, stride);
} else if (comp == 3) {
convert<uint16_t, 65535, 3>(target, source, count, stride);
} else {
convert<uint16_t, 65535>(target, source, count, comp, stride);
}
} else {
if (comp == 2) {
convert<uint16_t, 1, 2>(target, source, count, stride);
} else if (comp == 3) {
convert<uint16_t, 1, 3>(target, source, count, stride);
} else {
convert<uint16_t, 1>(target, source, count, comp, stride);
}
}
return required;
}
case ComponentType::HALF: {
const uint32_t stride = mConfig.inputStrideBytes ? mConfig.inputStrideBytes : (2 * comp);
uint8_t const* srcBytes = (uint8_t const*) source;
for (size_t i = 0; i < count; ++i, target += comp, srcBytes += stride) {
half const* src = (half const*) srcBytes;
for (int n = 0; n < comp; ++n) {
target[n] = float(src[n]);
}
}
return required;
}
}
return 0;
}
} // namespace geometry
} // namespace filament