Skip to content

Commit 8885669

Browse files
committed
backend: enhance data reshaper
- add half float as source type - gray-scaled output when src-channel=1 and dest-channel=3 or 4 - Ensure that per-pixel work is as-constexpr-as-possible
1 parent 58f6d77 commit 8885669

File tree

1 file changed

+180
-83
lines changed

1 file changed

+180
-83
lines changed

filament/backend/src/DataReshaper.h

Lines changed: 180 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,146 @@
1919

2020
#include <backend/PixelBufferDescriptor.h>
2121

22-
#include <cstring>
23-
#include <stddef.h>
24-
#include <stdint.h>
25-
2622
#include <math/scalar.h>
23+
#include <math/half.h>
2724

2825
#include <utils/debug.h>
26+
#include <utils/Logger.h>
27+
28+
#include <cstdint>
29+
#include <cstring>
30+
#include <stddef.h>
31+
#include <stdint.h>
2932

3033
namespace filament {
3134
namespace backend {
3235

36+
namespace {
37+
3338
// Provides an alpha value when expanding 3-channel images to 4-channel.
3439
// Also used as a normalization scale when converting between numeric types.
3540
template<typename componentType> inline componentType getMaxValue();
3641

42+
template<> inline constexpr float getMaxValue() { return 1.0f; }
43+
template<> inline constexpr int32_t getMaxValue() { return 0x7fffffff; }
44+
template<> inline constexpr uint32_t getMaxValue() { return 0xffffffff; }
45+
template<> inline constexpr uint16_t getMaxValue() { return 0x3c00; } // 0x3c00 is 1.0 in half-float.
46+
template<> inline constexpr uint8_t getMaxValue() { return 0xff; }
47+
template<> inline math::half getMaxValue() { return math::half(1.0f); }
48+
49+
template<typename srcComponentType, typename dstComponentType, uint8_t srcChannelCount,
50+
uint8_t dstChannelCount>
51+
void reshapeImageKernel(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
52+
size_t srcBytesPerRow, size_t dstRowOffset, size_t dstColumnOffset, size_t dstBytesPerRow,
53+
size_t width, size_t height, bool swizzle) {
54+
constexpr size_t minChannelCount = math::min(srcChannelCount, dstChannelCount);
55+
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
56+
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
57+
double const mFactor = dstMaxValue / ((double) srcMaxValue);
58+
assert_invariant(minChannelCount <= 4);
59+
UTILS_ASSUME(minChannelCount <= 4);
60+
dest += (dstRowOffset * dstBytesPerRow);
61+
62+
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
63+
for (size_t row = 0; row < height; ++row) {
64+
const srcComponentType* in = (const srcComponentType*) src;
65+
dstComponentType* out = (dstComponentType*) dest + (dstColumnOffset * dstChannelCount);
66+
for (size_t column = 0; column < width; ++column) {
67+
for (size_t channel = 0; channel < minChannelCount; ++channel) {
68+
if constexpr (std::is_same_v<dstComponentType, srcComponentType>) {
69+
out[channel] = in[inds[channel]];
70+
} else {
71+
// convert to double then clamp and cast to dst type.
72+
out[channel] = static_cast<dstComponentType>(std::clamp(
73+
in[inds[channel]] * mFactor, 0.0,
74+
static_cast<double>(std::numeric_limits<dstComponentType>::max())));
75+
}
76+
}
77+
if constexpr (srcChannelCount == 1 && (dstChannelCount == 3 || dstChannelCount == 4)) {
78+
// For the special case of src-channel-count=1, dst-channel-count=3/4, we assume
79+
// the output should be a grayscale image with alpha=1 and the gray value is set
80+
// to the src value. This is useful for producing a grayscale image from a depth
81+
// map.
82+
for (size_t channel = 1; channel < 3; ++channel) {
83+
out[channel] = out[0];
84+
}
85+
if constexpr (dstChannelCount == 4) {
86+
out[3] = dstMaxValue;
87+
}
88+
} else {
89+
for (size_t channel = srcChannelCount; channel < dstChannelCount; ++channel) {
90+
out[channel] = dstMaxValue;
91+
}
92+
}
93+
in += srcChannelCount;
94+
out += dstChannelCount;
95+
}
96+
src += srcBytesPerRow;
97+
dest += dstBytesPerRow;
98+
}
99+
}
100+
101+
// Converts a n-channel image of UBYTE, INT, UINT, HALF, or FLOAT to a different type.
102+
template<typename dstComponentType, typename srcComponentType>
103+
static void reshapeImageImpl(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
104+
size_t srcBytesPerRow, size_t srcChannelCount, size_t dstRowOffset, size_t dstColumnOffset,
105+
size_t dstBytesPerRow, size_t dstChannelCount, size_t width, size_t height, bool swizzle) {
106+
107+
static_assert(!std::is_same_v<dstComponentType, math::half>);
108+
109+
void (*impl)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow, size_t srcRowOffset,
110+
size_t srcColumnOffset, size_t dstBytesPerRow, size_t width, size_t height,
111+
bool swizzle) = nullptr;
112+
113+
auto channelCountError = [srcChannelCount, dstChannelCount]() {
114+
LOG(ERROR) << "DataReshaper: src/dst channel count not supported : " << srcChannelCount
115+
<< "/" << dstChannelCount;
116+
};
117+
118+
if (srcChannelCount == 1) {
119+
switch (dstChannelCount) {
120+
case 1: impl = reshapeImageKernel<srcComponentType, dstComponentType, 1, 1>; break;
121+
case 2: impl = reshapeImageKernel<srcComponentType, dstComponentType, 1, 2>; break;
122+
case 3: impl = reshapeImageKernel<srcComponentType, dstComponentType, 1, 3>; break;
123+
case 4: impl = reshapeImageKernel<srcComponentType, dstComponentType, 1, 4>; break;
124+
default: channelCountError(); break;
125+
}
126+
} else if (srcChannelCount == 2) {
127+
switch (dstChannelCount) {
128+
case 1: impl = reshapeImageKernel<srcComponentType, dstComponentType, 2, 1>; break;
129+
case 2: impl = reshapeImageKernel<srcComponentType, dstComponentType, 2, 2>; break;
130+
case 3: impl = reshapeImageKernel<srcComponentType, dstComponentType, 2, 3>; break;
131+
case 4: impl = reshapeImageKernel<srcComponentType, dstComponentType, 2, 4>; break;
132+
default: channelCountError(); break;
133+
}
134+
} else if (srcChannelCount == 3) {
135+
switch (dstChannelCount) {
136+
case 1: impl = reshapeImageKernel<srcComponentType, dstComponentType, 3, 1>; break;
137+
case 2: impl = reshapeImageKernel<srcComponentType, dstComponentType, 3, 2>; break;
138+
case 3: impl = reshapeImageKernel<srcComponentType, dstComponentType, 3, 3>; break;
139+
case 4: impl = reshapeImageKernel<srcComponentType, dstComponentType, 3, 4>; break;
140+
default: channelCountError(); break;
141+
}
142+
} else if (srcChannelCount == 4) {
143+
switch (dstChannelCount) {
144+
case 1: impl = reshapeImageKernel<srcComponentType, dstComponentType, 4, 1>; break;
145+
case 2: impl = reshapeImageKernel<srcComponentType, dstComponentType, 4, 2>; break;
146+
case 3: impl = reshapeImageKernel<srcComponentType, dstComponentType, 4, 3>; break;
147+
case 4: impl = reshapeImageKernel<srcComponentType, dstComponentType, 4, 4>; break;
148+
default: channelCountError(); break;
149+
}
150+
} else {
151+
channelCountError();
152+
}
153+
154+
if (impl) {
155+
impl(dest, src, srcBytesPerRow, dstRowOffset, dstColumnOffset, dstBytesPerRow, width,
156+
height, swizzle);
157+
}
158+
}
159+
160+
} // anonymous namespace
161+
37162
class DataReshaper {
38163
public:
39164

@@ -76,51 +201,9 @@ class DataReshaper {
76201
}
77202
}
78203

79-
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
80-
template<typename dstComponentType, typename srcComponentType>
81-
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
82-
size_t srcBytesPerRow,
83-
size_t srcChannelCount,
84-
size_t dstRowOffset, size_t dstColumnOffset,
85-
size_t dstBytesPerRow, size_t dstChannelCount,
86-
size_t width, size_t height, bool swizzle) {
87-
// TODO: there's a fast-path where memcpy will work but currently not being taken advantage
88-
// of.
89-
90-
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
91-
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
92-
const size_t minChannelCount = math::min(srcChannelCount, dstChannelCount);
93-
assert_invariant(minChannelCount <= 4);
94-
UTILS_ASSUME(minChannelCount <= 4);
95-
dest += (dstRowOffset * dstBytesPerRow);
96-
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
97-
for (size_t row = 0; row < height; ++row) {
98-
const srcComponentType* in = (const srcComponentType*) src;
99-
dstComponentType* out = (dstComponentType*)dest + (dstColumnOffset * dstChannelCount);
100-
for (size_t column = 0; column < width; ++column) {
101-
for (size_t channel = 0; channel < minChannelCount; ++channel) {
102-
if constexpr (std::is_same_v<dstComponentType, srcComponentType>) {
103-
out[channel] = in[inds[channel]];
104-
} else {
105-
// FIXME: beware of overflows in the multiply
106-
// FIXME: probably not correct for _INTEGER src/dst
107-
out[channel] = in[inds[channel]] * dstMaxValue / srcMaxValue;
108-
}
109-
}
110-
for (size_t channel = srcChannelCount; channel < dstChannelCount; ++channel) {
111-
out[channel] = dstMaxValue;
112-
}
113-
in += srcChannelCount;
114-
out += dstChannelCount;
115-
}
116-
src += srcBytesPerRow;
117-
dest += dstBytesPerRow;
118-
}
119-
}
120-
121204
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
122205
static bool reshapeImage(PixelBufferDescriptor* UTILS_RESTRICT dst, PixelDataType srcType,
123-
uint32_t srcChannelCount, const uint8_t* UTILS_RESTRICT srcBytes, int srcBytesPerRow,
206+
uint32_t srcChannelCount, const uint8_t* UTILS_RESTRICT srcBytes, int srcBytesPerRow,
124207
int width, int height, bool swizzle) {
125208
size_t dstChannelCount;
126209
switch (dst->format) {
@@ -132,13 +215,14 @@ class DataReshaper {
132215
case PixelDataFormat::RG: dstChannelCount = 2; break;
133216
case PixelDataFormat::RGB: dstChannelCount = 3; break;
134217
case PixelDataFormat::RGBA: dstChannelCount = 4; break;
135-
default: return false;
218+
default:
219+
LOG(ERROR) << "DataReshaper: unsupported dst->format: " << (int) dst->format;
220+
return false;
136221
}
137222
void (*reshaper)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
138-
size_t srcChannelCount,
139-
size_t srcRowOffset, size_t srcColumnOffset,
140-
size_t dstBytesPerRow, size_t dstChannelCount,
141-
size_t width, size_t height, bool swizzle) = nullptr;
223+
size_t srcChannelCount, size_t srcRowOffset, size_t srcColumnOffset,
224+
size_t dstBytesPerRow, size_t dstChannelCount, size_t width, size_t height,
225+
bool swizzle) = nullptr;
142226
constexpr auto UBYTE = PixelDataType::UBYTE;
143227
constexpr auto FLOAT = PixelDataType::FLOAT;
144228
constexpr auto UINT = PixelDataType::UINT;
@@ -148,71 +232,84 @@ class DataReshaper {
148232
case UBYTE:
149233
switch (srcType) {
150234
case UBYTE:
151-
reshaper = reshapeImage<uint8_t, uint8_t>;
235+
reshaper = reshapeImageImpl<uint8_t, uint8_t>;
152236
if (dst->format == PixelDataFormat::RGBA &&
153237
dstChannelCount == srcChannelCount && !swizzle && dst->top == 0 &&
154238
dst->left == 0) {
155239
reshaper = copyImage;
156240
}
157241
break;
158-
case FLOAT: reshaper = reshapeImage<uint8_t, float>; break;
159-
case INT: reshaper = reshapeImage<uint8_t, int32_t>; break;
160-
case UINT: reshaper = reshapeImage<uint8_t, uint32_t>; break;
161-
default: return false;
242+
case FLOAT: reshaper = reshapeImageImpl<uint8_t, float>; break;
243+
case INT: reshaper = reshapeImageImpl<uint8_t, int32_t>; break;
244+
case UINT: reshaper = reshapeImageImpl<uint8_t, uint32_t>; break;
245+
case HALF: reshaper = reshapeImageImpl<uint8_t, math::half>; break;
246+
default:
247+
LOG(ERROR) << "DataReshaper: UBYTE dst, unsupported srcType: "
248+
<< (int) srcType;
249+
return false;
162250
}
163251
break;
164252
case FLOAT:
165253
switch (srcType) {
166-
case UBYTE: reshaper = reshapeImage<float, uint8_t>; break;
167-
case FLOAT: reshaper = reshapeImage<float, float>; break;
168-
case INT: reshaper = reshapeImage<float, int32_t>; break;
169-
case UINT: reshaper = reshapeImage<float, uint32_t>; break;
170-
default: return false;
254+
case UBYTE: reshaper = reshapeImageImpl<float, uint8_t>; break;
255+
case FLOAT: reshaper = reshapeImageImpl<float, float>; break;
256+
case INT: reshaper = reshapeImageImpl<float, int32_t>; break;
257+
case UINT: reshaper = reshapeImageImpl<float, uint32_t>; break;
258+
default:
259+
LOG(ERROR) << "DataReshaper: FLOAT dst, unsupported srcType: "
260+
<< (int) srcType;
261+
return false;
171262
}
172263
break;
173264
case INT:
174265
switch (srcType) {
175-
case UBYTE: reshaper = reshapeImage<int32_t, uint8_t>; break;
176-
case FLOAT: reshaper = reshapeImage<int32_t, float>; break;
177-
case INT: reshaper = reshapeImage<int32_t, int32_t>; break;
178-
case UINT: reshaper = reshapeImage<int32_t, uint32_t>; break;
179-
default: return false;
266+
case UBYTE: reshaper = reshapeImageImpl<int32_t, uint8_t>; break;
267+
case FLOAT: reshaper = reshapeImageImpl<int32_t, float>; break;
268+
case INT: reshaper = reshapeImageImpl<int32_t, int32_t>; break;
269+
case UINT: reshaper = reshapeImageImpl<int32_t, uint32_t>; break;
270+
default:
271+
LOG(ERROR)
272+
<< "DataReshaper: INT dst, unsupported srcType: " << (int) srcType;
273+
return false;
180274
}
181275
break;
182276
case UINT:
183277
switch (srcType) {
184-
case UBYTE: reshaper = reshapeImage<uint32_t, uint8_t>; break;
185-
case FLOAT: reshaper = reshapeImage<uint32_t, float>; break;
186-
case INT: reshaper = reshapeImage<uint32_t, int32_t>; break;
187-
case UINT: reshaper = reshapeImage<uint32_t, uint32_t>; break;
188-
default: return false;
278+
case UBYTE: reshaper = reshapeImageImpl<uint32_t, uint8_t>; break;
279+
case FLOAT: reshaper = reshapeImageImpl<uint32_t, float>; break;
280+
case INT: reshaper = reshapeImageImpl<uint32_t, int32_t>; break;
281+
case UINT: reshaper = reshapeImageImpl<uint32_t, uint32_t>; break;
282+
default:
283+
LOG(ERROR)
284+
<< "DataReshaper: UINT dst, unsupported srcType: " << (int) srcType;
285+
return false;
189286
}
190287
break;
191288
case HALF:
192289
switch (srcType) {
193-
case HALF: reshaper = copyImage; break;
194-
default: return false;
290+
case HALF:
291+
reshaper = copyImage;
292+
break;
293+
default:
294+
LOG(ERROR)
295+
<< "DataReshaper: HALF dst, unsupported srcType: " << (int) srcType;
296+
return false;
195297
}
196298
break;
197299
default:
300+
LOG(ERROR) << "DataReshaper: unsupported dst->type: " << (int) dst->type;
198301
return false;
199302
}
200303
uint8_t* dstBytes = (uint8_t*) dst->buffer;
201304
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
202305
dst->stride ? dst->stride : width, 1, dst->alignment);
203-
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount,
204-
dst->top, dst->left, dstBytesPerRow,
205-
dstChannelCount, width, height, swizzle);
306+
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount, dst->top, dst->left,
307+
dstBytesPerRow, dstChannelCount, width, height, swizzle);
308+
206309
return true;
207310
}
208311
};
209312

210-
template<> inline float getMaxValue() { return 1.0f; }
211-
template<> inline int32_t getMaxValue() { return 0x7fffffff; }
212-
template<> inline uint32_t getMaxValue() { return 0xffffffff; }
213-
template<> inline uint16_t getMaxValue() { return 0x3c00; } // 0x3c00 is 1.0 in half-float.
214-
template<> inline uint8_t getMaxValue() { return 0xff; }
215-
216313
} // namespace backend
217314
} // namespace filament
218315

0 commit comments

Comments
 (0)