Skip to content

Commit c0b8d71

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 c0b8d71

File tree

1 file changed

+236
-91
lines changed

1 file changed

+236
-91
lines changed

filament/backend/src/DataReshaper.h

Lines changed: 236 additions & 91 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,69 +201,44 @@ 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) {
127-
case PixelDataFormat::R_INTEGER: dstChannelCount = 1; break;
128-
case PixelDataFormat::RG_INTEGER: dstChannelCount = 2; break;
129-
case PixelDataFormat::RGB_INTEGER: dstChannelCount = 3; break;
130-
case PixelDataFormat::RGBA_INTEGER: dstChannelCount = 4; break;
131-
case PixelDataFormat::R: dstChannelCount = 1; break;
132-
case PixelDataFormat::RG: dstChannelCount = 2; break;
133-
case PixelDataFormat::RGB: dstChannelCount = 3; break;
134-
case PixelDataFormat::RGBA: dstChannelCount = 4; break;
135-
default: return false;
210+
case PixelDataFormat::R_INTEGER:
211+
dstChannelCount = 1;
212+
break;
213+
case PixelDataFormat::RG_INTEGER:
214+
dstChannelCount = 2;
215+
break;
216+
case PixelDataFormat::RGB_INTEGER:
217+
dstChannelCount = 3;
218+
break;
219+
case PixelDataFormat::RGBA_INTEGER:
220+
dstChannelCount = 4;
221+
break;
222+
case PixelDataFormat::R:
223+
dstChannelCount = 1;
224+
break;
225+
case PixelDataFormat::RG:
226+
dstChannelCount = 2;
227+
break;
228+
case PixelDataFormat::RGB:
229+
dstChannelCount = 3;
230+
break;
231+
case PixelDataFormat::RGBA:
232+
dstChannelCount = 4;
233+
break;
234+
default:
235+
LOG(ERROR) << "DataReshaper: unsupported dst->format: " << (int) dst->format;
236+
return false;
136237
}
137238
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;
239+
size_t srcChannelCount, size_t srcRowOffset, size_t srcColumnOffset,
240+
size_t dstBytesPerRow, size_t dstChannelCount, size_t width, size_t height,
241+
bool swizzle) = nullptr;
142242
constexpr auto UBYTE = PixelDataType::UBYTE;
143243
constexpr auto FLOAT = PixelDataType::FLOAT;
144244
constexpr auto UINT = PixelDataType::UINT;
@@ -148,71 +248,116 @@ class DataReshaper {
148248
case UBYTE:
149249
switch (srcType) {
150250
case UBYTE:
151-
reshaper = reshapeImage<uint8_t, uint8_t>;
251+
reshaper = reshapeImageImpl<uint8_t, uint8_t>;
152252
if (dst->format == PixelDataFormat::RGBA &&
153253
dstChannelCount == srcChannelCount && !swizzle && dst->top == 0 &&
154254
dst->left == 0) {
155255
reshaper = copyImage;
156256
}
157257
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;
258+
case FLOAT:
259+
reshaper = reshapeImageImpl<uint8_t, float>;
260+
break;
261+
case INT:
262+
reshaper = reshapeImageImpl<uint8_t, int32_t>;
263+
break;
264+
case UINT:
265+
reshaper = reshapeImageImpl<uint8_t, uint32_t>;
266+
break;
267+
case HALF:
268+
reshaper = reshapeImageImpl<uint8_t, math::half>;
269+
break;
270+
default:
271+
LOG(ERROR) << "DataReshaper: UBYTE dst, unsupported srcType: "
272+
<< (int) srcType;
273+
return false;
162274
}
163275
break;
164276
case FLOAT:
165277
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;
278+
case UBYTE:
279+
reshaper = reshapeImageImpl<float, uint8_t>;
280+
break;
281+
case FLOAT:
282+
reshaper = reshapeImageImpl<float, float>;
283+
break;
284+
case INT:
285+
reshaper = reshapeImageImpl<float, int32_t>;
286+
break;
287+
case UINT:
288+
reshaper = reshapeImageImpl<float, uint32_t>;
289+
break;
290+
default:
291+
LOG(ERROR) << "DataReshaper: FLOAT dst, unsupported srcType: "
292+
<< (int) srcType;
293+
return false;
171294
}
172295
break;
173296
case INT:
174297
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;
298+
case UBYTE:
299+
reshaper = reshapeImageImpl<int32_t, uint8_t>;
300+
break;
301+
case FLOAT:
302+
reshaper = reshapeImageImpl<int32_t, float>;
303+
break;
304+
case INT:
305+
reshaper = reshapeImageImpl<int32_t, int32_t>;
306+
break;
307+
case UINT:
308+
reshaper = reshapeImageImpl<int32_t, uint32_t>;
309+
break;
310+
default:
311+
LOG(ERROR)
312+
<< "DataReshaper: INT dst, unsupported srcType: " << (int) srcType;
313+
return false;
180314
}
181315
break;
182316
case UINT:
183317
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;
318+
case UBYTE:
319+
reshaper = reshapeImageImpl<uint32_t, uint8_t>;
320+
break;
321+
case FLOAT:
322+
reshaper = reshapeImageImpl<uint32_t, float>;
323+
break;
324+
case INT:
325+
reshaper = reshapeImageImpl<uint32_t, int32_t>;
326+
break;
327+
case UINT:
328+
reshaper = reshapeImageImpl<uint32_t, uint32_t>;
329+
break;
330+
default:
331+
LOG(ERROR)
332+
<< "DataReshaper: UINT dst, unsupported srcType: " << (int) srcType;
333+
return false;
189334
}
190335
break;
191336
case HALF:
192337
switch (srcType) {
193-
case HALF: reshaper = copyImage; break;
194-
default: return false;
338+
case HALF:
339+
reshaper = copyImage;
340+
break;
341+
default:
342+
LOG(ERROR)
343+
<< "DataReshaper: HALF dst, unsupported srcType: " << (int) srcType;
344+
return false;
195345
}
196346
break;
197347
default:
348+
LOG(ERROR) << "DataReshaper: unsupported dst->type: " << (int) dst->type;
198349
return false;
199350
}
200351
uint8_t* dstBytes = (uint8_t*) dst->buffer;
201352
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
202353
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);
354+
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount, dst->top, dst->left,
355+
dstBytesPerRow, dstChannelCount, width, height, swizzle);
356+
206357
return true;
207358
}
208359
};
209360

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-
216361
} // namespace backend
217362
} // namespace filament
218363

0 commit comments

Comments
 (0)