• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 /*
19  * Hardware Composer stress test
20  *
21  * Performs a pseudo-random (prandom) sequence of operations to the
22  * Hardware Composer (HWC), for a specified number of passes or for
23  * a specified period of time.  By default the period of time is FLT_MAX,
24  * so that the number of passes will take precedence.
25  *
26  * The passes are grouped together, where (pass / passesPerGroup) specifies
27  * which group a particular pass is in.  This causes every passesPerGroup
28  * worth of sequential passes to be within the same group.  Computationally
29  * intensive operations are performed just once at the beginning of a group
30  * of passes and then used by all the passes in that group.  This is done
31  * so as to increase both the average and peak rate of graphic operations,
32  * by moving computationally intensive operations to the beginning of a group.
33  * In particular, at the start of each group of passes a set of
34  * graphic buffers are created, then used by the first and remaining
35  * passes of that group of passes.
36  *
37  * The per-group initialization of the graphic buffers is performed
38  * by a function called initFrames.  This function creates an array
39  * of smart pointers to the graphic buffers, in the form of a vector
40  * of vectors.  The array is accessed in row major order, so each
41  * row is a vector of smart pointers.  All the pointers of a single
42  * row point to graphic buffers which use the same pixel format and
43  * have the same dimension, although it is likely that each one is
44  * filled with a different color.  This is done so that after doing
45  * the first HWC prepare then set call, subsequent set calls can
46  * be made with each of the layer handles changed to a different
47  * graphic buffer within the same row.  Since the graphic buffers
48  * in a particular row have the same pixel format and dimension,
49  * additional HWC set calls can be made, without having to perform
50  * an HWC prepare call.
51  *
52  * This test supports the following command-line options:
53  *
54  *   -v        Verbose
55  *   -s num    Starting pass
56  *   -e num    Ending pass
57  *   -p num    Execute the single pass specified by num
58  *   -n num    Number of set operations to perform after each prepare operation
59  *   -t float  Maximum time in seconds to execute the test
60  *   -d float  Delay in seconds performed after each set operation
61  *   -D float  Delay in seconds performed after the last pass is executed
62  *
63  * Typically the test is executed for a large range of passes.  By default
64  * passes 0 through 99999 (100,000 passes) are executed.  Although this test
65  * does not validate the generated image, at times it is useful to reexecute
66  * a particular pass and leave the displayed image on the screen for an
67  * extended period of time.  This can be done either by setting the -s
68  * and -e options to the desired pass, along with a large value for -D.
69  * This can also be done via the -p option, again with a large value for
70  * the -D options.
71  *
72  * So far this test only contains code to create graphic buffers with
73  * a continuous solid color.  Although this test is unable to validate the
74  * image produced, any image that contains other than rectangles of a solid
75  * color are incorrect.  Note that the rectangles may use a transparent
76  * color and have a blending operation that causes the color in overlapping
77  * rectangles to be mixed.  In such cases the overlapping portions may have
78  * a different color from the rest of the rectangle.
79  */
80 
81 #include <algorithm>
82 #include <assert.h>
83 #include <cerrno>
84 #include <cmath>
85 #include <cstdlib>
86 #include <ctime>
87 #include <libgen.h>
88 #include <sched.h>
89 #include <sstream>
90 #include <stdint.h>
91 #include <string.h>
92 #include <unistd.h>
93 #include <vector>
94 
95 #include <sys/syscall.h>
96 #include <sys/types.h>
97 #include <sys/wait.h>
98 
99 #include <EGL/egl.h>
100 #include <EGL/eglext.h>
101 #include <GLES2/gl2.h>
102 #include <GLES2/gl2ext.h>
103 
104 #include <ui/GraphicBuffer.h>
105 
106 #define LOG_TAG "hwcStressTest"
107 #include <utils/Log.h>
108 #include <testUtil.h>
109 
110 #include <hardware/hwcomposer.h>
111 
112 #include <glTestLib.h>
113 #include "hwcTestLib.h"
114 
115 using namespace std;
116 using namespace android;
117 
118 const float maxSizeRatio = 1.3;  // Graphic buffers can be upto this munch
119                                  // larger than the default screen size
120 const unsigned int passesPerGroup = 10; // A group of passes all use the same
121                                         // graphic buffers
122 
123 // Ratios at which rare and frequent conditions should be produced
124 const float rareRatio = 0.1;
125 const float freqRatio = 0.9;
126 
127 // Defaults for command-line options
128 const bool defaultVerbose = false;
129 const unsigned int defaultStartPass = 0;
130 const unsigned int defaultEndPass = 99999;
131 const unsigned int defaultPerPassNumSet = 10;
132 const float defaultPerSetDelay = 0.0; // Default delay after each set
133                                       // operation.  Default delay of
134                                       // zero used so as to perform the
135                                       // the set operations as quickly
136                                       // as possible.
137 const float defaultEndDelay = 2.0; // Default delay between completion of
138                                    // final pass and restart of framework
139 const float defaultDuration = FLT_MAX; // A fairly long time, so that
140                                        // range of passes will have
141                                        // precedence
142 
143 // Command-line option settings
144 static bool verbose = defaultVerbose;
145 static unsigned int startPass = defaultStartPass;
146 static unsigned int endPass = defaultEndPass;
147 static unsigned int numSet = defaultPerPassNumSet;
148 static float perSetDelay = defaultPerSetDelay;
149 static float endDelay = defaultEndDelay;
150 static float duration = defaultDuration;
151 
152 // Command-line mutual exclusion detection flags.
153 // Corresponding flag set true once an option is used.
154 bool eFlag, sFlag, pFlag;
155 
156 #define MAXSTR               100
157 #define MAXCMD               200
158 #define BITSPERBYTE            8 // TODO: Obtain from <values.h>, once
159                                  // it has been added
160 
161 #define CMD_STOP_FRAMEWORK   "stop 2>&1"
162 #define CMD_START_FRAMEWORK  "start 2>&1"
163 
164 #define NUMA(a) (sizeof(a) / sizeof(a [0]))
165 #define MEMCLR(addr, size) do { \
166         memset((addr), 0, (size)); \
167     } while (0)
168 
169 // File scope constants
170 const unsigned int blendingOps[] = {
171     HWC_BLENDING_NONE,
172     HWC_BLENDING_PREMULT,
173     HWC_BLENDING_COVERAGE,
174 };
175 const unsigned int layerFlags[] = {
176     HWC_SKIP_LAYER,
177 };
178 const vector<unsigned int> vecLayerFlags(layerFlags,
179     layerFlags + NUMA(layerFlags));
180 
181 const unsigned int transformFlags[] = {
182     HWC_TRANSFORM_FLIP_H,
183     HWC_TRANSFORM_FLIP_V,
184     HWC_TRANSFORM_ROT_90,
185     // ROT_180 & ROT_270 intentionally not listed, because they
186     // they are formed from combinations of the flags already listed.
187 };
188 const vector<unsigned int> vecTransformFlags(transformFlags,
189     transformFlags + NUMA(transformFlags));
190 
191 // File scope globals
192 static const int texUsage = GraphicBuffer::USAGE_HW_TEXTURE |
193         GraphicBuffer::USAGE_SW_WRITE_RARELY;
194 static hwc_composer_device_1_t *hwcDevice;
195 static EGLDisplay dpy;
196 static EGLSurface surface;
197 static EGLint width, height;
198 static vector <vector <sp<GraphicBuffer> > > frames;
199 
200 // File scope prototypes
201 void init(void);
202 void initFrames(unsigned int seed);
203 template <class T> vector<T> vectorRandSelect(const vector<T>& vec, size_t num);
204 template <class T> T vectorOr(const vector<T>& vec);
205 
206 /*
207  * Main
208  *
209  * Performs the following high-level sequence of operations:
210  *
211  *   1. Command-line parsing
212  *
213  *   2. Initialization
214  *
215  *   3. For each pass:
216  *
217  *        a. If pass is first pass or in a different group from the
218  *           previous pass, initialize the array of graphic buffers.
219  *
220  *        b. Create a HWC list with room to specify a prandomly
221  *           selected number of layers.
222  *
223  *        c. Select a subset of the rows from the graphic buffer array,
224  *           such that there is a unique row to be used for each
225  *           of the layers in the HWC list.
226  *
227  *        d. Prandomly fill in the HWC list with handles
228  *           selected from any of the columns of the selected row.
229  *
230  *        e. Pass the populated list to the HWC prepare call.
231  *
232  *        f. Pass the populated list to the HWC set call.
233  *
234  *        g. If additional set calls are to be made, then for each
235  *           additional set call, select a new set of handles and
236  *           perform the set call.
237  */
238 int
main(int argc,char * argv[])239 main(int argc, char *argv[])
240 {
241     int rv, opt;
242     char *chptr;
243     unsigned int pass;
244     char cmd[MAXCMD];
245     struct timeval startTime, currentTime, delta;
246 
247     testSetLogCatTag(LOG_TAG);
248 
249     // Parse command line arguments
250     while ((opt = getopt(argc, argv, "vp:d:D:n:s:e:t:?h")) != -1) {
251         switch (opt) {
252           case 'd': // Delay after each set operation
253             perSetDelay = strtod(optarg, &chptr);
254             if ((*chptr != '\0') || (perSetDelay < 0.0)) {
255                 testPrintE("Invalid command-line specified per pass delay of: "
256                            "%s", optarg);
257                 exit(1);
258             }
259             break;
260 
261           case 'D': // End of test delay
262                     // Delay between completion of final pass and restart
263                     // of framework
264             endDelay = strtod(optarg, &chptr);
265             if ((*chptr != '\0') || (endDelay < 0.0)) {
266                 testPrintE("Invalid command-line specified end of test delay "
267                            "of: %s", optarg);
268                 exit(2);
269             }
270             break;
271 
272           case 't': // Duration
273             duration = strtod(optarg, &chptr);
274             if ((*chptr != '\0') || (duration < 0.0)) {
275                 testPrintE("Invalid command-line specified duration of: %s",
276                            optarg);
277                 exit(3);
278             }
279             break;
280 
281           case 'n': // Num set operations per pass
282             numSet = strtoul(optarg, &chptr, 10);
283             if (*chptr != '\0') {
284                 testPrintE("Invalid command-line specified num set per pass "
285                            "of: %s", optarg);
286                 exit(4);
287             }
288             break;
289 
290           case 's': // Starting Pass
291             sFlag = true;
292             if (pFlag) {
293                 testPrintE("Invalid combination of command-line options.");
294                 testPrintE("  The -p option is mutually exclusive from the");
295                 testPrintE("  -s and -e options.");
296                 exit(5);
297             }
298             startPass = strtoul(optarg, &chptr, 10);
299             if (*chptr != '\0') {
300                 testPrintE("Invalid command-line specified starting pass "
301                            "of: %s", optarg);
302                 exit(6);
303             }
304             break;
305 
306           case 'e': // Ending Pass
307             eFlag = true;
308             if (pFlag) {
309                 testPrintE("Invalid combination of command-line options.");
310                 testPrintE("  The -p option is mutually exclusive from the");
311                 testPrintE("  -s and -e options.");
312                 exit(7);
313             }
314             endPass = strtoul(optarg, &chptr, 10);
315             if (*chptr != '\0') {
316                 testPrintE("Invalid command-line specified ending pass "
317                            "of: %s", optarg);
318                 exit(8);
319             }
320             break;
321 
322           case 'p': // Run a single specified pass
323             pFlag = true;
324             if (sFlag || eFlag) {
325                 testPrintE("Invalid combination of command-line options.");
326                 testPrintE("  The -p option is mutually exclusive from the");
327                 testPrintE("  -s and -e options.");
328                 exit(9);
329             }
330             startPass = endPass = strtoul(optarg, &chptr, 10);
331             if (*chptr != '\0') {
332                 testPrintE("Invalid command-line specified pass of: %s",
333                            optarg);
334                 exit(10);
335             }
336             break;
337 
338           case 'v': // Verbose
339             verbose = true;
340             break;
341 
342           case 'h': // Help
343           case '?':
344           default:
345             testPrintE("  %s [options]", basename(argv[0]));
346             testPrintE("    options:");
347             testPrintE("      -p Execute specified pass");
348             testPrintE("      -s Starting pass");
349             testPrintE("      -e Ending pass");
350             testPrintE("      -t Duration");
351             testPrintE("      -d Delay after each set operation");
352             testPrintE("      -D End of test delay");
353             testPrintE("      -n Num set operations per pass");
354             testPrintE("      -v Verbose");
355             exit(((optopt == 0) || (optopt == '?')) ? 0 : 11);
356         }
357     }
358     if (endPass < startPass) {
359         testPrintE("Unexpected ending pass before starting pass");
360         testPrintE("  startPass: %u endPass: %u", startPass, endPass);
361         exit(12);
362     }
363     if (argc != optind) {
364         testPrintE("Unexpected command-line postional argument");
365         testPrintE("  %s [-s start_pass] [-e end_pass] [-t duration]",
366             basename(argv[0]));
367         exit(13);
368     }
369     testPrintI("duration: %g", duration);
370     testPrintI("startPass: %u", startPass);
371     testPrintI("endPass: %u", endPass);
372     testPrintI("numSet: %u", numSet);
373 
374     // Stop framework
375     rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK);
376     if (rv >= (signed) sizeof(cmd) - 1) {
377         testPrintE("Command too long for: %s", CMD_STOP_FRAMEWORK);
378         exit(14);
379     }
380     testExecCmd(cmd);
381     testDelay(1.0); // TODO - need means to query whether asyncronous stop
382                     // framework operation has completed.  For now, just wait
383                     // a long time.
384 
385     init();
386 
387     // For each pass
388     gettimeofday(&startTime, NULL);
389     for (pass = startPass; pass <= endPass; pass++) {
390         // Stop if duration of work has already been performed
391         gettimeofday(&currentTime, NULL);
392         delta = tvDelta(&startTime, &currentTime);
393         if (tv2double(&delta) > duration) { break; }
394 
395         // Regenerate a new set of test frames when this pass is
396         // either the first pass or is in a different group then
397         // the previous pass.  A group of passes are passes that
398         // all have the same quotient when their pass number is
399         // divided by passesPerGroup.
400         if ((pass == startPass)
401             || ((pass / passesPerGroup) != ((pass - 1) / passesPerGroup))) {
402             initFrames(pass / passesPerGroup);
403         }
404 
405         testPrintI("==== Starting pass: %u", pass);
406 
407         // Cause deterministic sequence of prandom numbers to be
408         // generated for this pass.
409         srand48(pass);
410 
411         hwc_display_contents_1_t *list;
412         list = hwcTestCreateLayerList(testRandMod(frames.size()) + 1);
413         if (list == NULL) {
414             testPrintE("hwcTestCreateLayerList failed");
415             exit(20);
416         }
417 
418         // Prandomly select a subset of frames to be used by this pass.
419         vector <vector <sp<GraphicBuffer> > > selectedFrames;
420         selectedFrames = vectorRandSelect(frames, list->numHwLayers);
421 
422         // Any transform tends to create a layer that the hardware
423         // composer is unable to support and thus has to leave for
424         // SurfaceFlinger.  Place heavy bias on specifying no transforms.
425         bool noTransform = testRandFract() > rareRatio;
426 
427         for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) {
428             unsigned int idx = testRandMod(selectedFrames[n1].size());
429             sp<GraphicBuffer> gBuf = selectedFrames[n1][idx];
430             hwc_layer_1_t *layer = &list->hwLayers[n1];
431             layer->handle = gBuf->handle;
432 
433             layer->blending = blendingOps[testRandMod(NUMA(blendingOps))];
434             layer->flags = (testRandFract() > rareRatio) ? 0
435                 : vectorOr(vectorRandSelect(vecLayerFlags,
436                            testRandMod(vecLayerFlags.size() + 1)));
437             layer->transform = (noTransform || testRandFract() > rareRatio) ? 0
438                 : vectorOr(vectorRandSelect(vecTransformFlags,
439                            testRandMod(vecTransformFlags.size() + 1)));
440             layer->sourceCrop.left = testRandMod(gBuf->getWidth());
441             layer->sourceCrop.top = testRandMod(gBuf->getHeight());
442             layer->sourceCrop.right = layer->sourceCrop.left
443                 + testRandMod(gBuf->getWidth() - layer->sourceCrop.left) + 1;
444             layer->sourceCrop.bottom = layer->sourceCrop.top
445                 + testRandMod(gBuf->getHeight() - layer->sourceCrop.top) + 1;
446             layer->displayFrame.left = testRandMod(width);
447             layer->displayFrame.top = testRandMod(height);
448             layer->displayFrame.right = layer->displayFrame.left
449                 + testRandMod(width - layer->displayFrame.left) + 1;
450             layer->displayFrame.bottom = layer->displayFrame.top
451                 + testRandMod(height - layer->displayFrame.top) + 1;
452 
453             // Increase the frequency that a scale factor of 1.0 from
454             // the sourceCrop to displayFrame occurs.  This is the
455             // most common scale factor used by applications and would
456             // be rarely produced by this stress test without this
457             // logic.
458             if (testRandFract() <= freqRatio) {
459                 // Only change to scale factor to 1.0 if both the
460                 // width and height will fit.
461                 int sourceWidth = layer->sourceCrop.right
462                                   - layer->sourceCrop.left;
463                 int sourceHeight = layer->sourceCrop.bottom
464                                    - layer->sourceCrop.top;
465                 if (((layer->displayFrame.left + sourceWidth) <= width)
466                     && ((layer->displayFrame.top + sourceHeight) <= height)) {
467                     layer->displayFrame.right = layer->displayFrame.left
468                                                 + sourceWidth;
469                     layer->displayFrame.bottom = layer->displayFrame.top
470                                                  + sourceHeight;
471                 }
472             }
473 
474             layer->visibleRegionScreen.numRects = 1;
475             layer->visibleRegionScreen.rects = &layer->displayFrame;
476         }
477 
478         // Perform prepare operation
479         if (verbose) { testPrintI("Prepare:"); hwcTestDisplayList(list); }
480         hwcDevice->prepare(hwcDevice, 1, &list);
481         if (verbose) {
482             testPrintI("Post Prepare:");
483             hwcTestDisplayListPrepareModifiable(list);
484         }
485 
486         // Turn off the geometry changed flag
487         list->flags &= ~HWC_GEOMETRY_CHANGED;
488 
489         // Perform the set operation(s)
490         if (verbose) {testPrintI("Set:"); }
491         for (unsigned int n1 = 0; n1 < numSet; n1++) {
492             if (verbose) { hwcTestDisplayListHandles(list); }
493             list->dpy = dpy;
494             list->sur = surface;
495             hwcDevice->set(hwcDevice, 1, &list);
496 
497             // Prandomly select a new set of handles
498             for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) {
499                 unsigned int idx = testRandMod(selectedFrames[n1].size());
500                 sp<GraphicBuffer> gBuf = selectedFrames[n1][idx];
501                 hwc_layer_1_t *layer = &list->hwLayers[n1];
502                 layer->handle = (native_handle_t *) gBuf->handle;
503             }
504 
505             testDelay(perSetDelay);
506         }
507 
508         hwcTestFreeLayerList(list);
509         testPrintI("==== Completed pass: %u", pass);
510     }
511 
512     testDelay(endDelay);
513 
514     // Start framework
515     rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK);
516     if (rv >= (signed) sizeof(cmd) - 1) {
517         testPrintE("Command too long for: %s", CMD_START_FRAMEWORK);
518         exit(21);
519     }
520     testExecCmd(cmd);
521 
522     testPrintI("Successfully completed %u passes", pass - startPass);
523 
524     return 0;
525 }
526 
init(void)527 void init(void)
528 {
529     srand48(0); // Defensively set pseudo random number generator.
530                 // Should not need to set this, because a stress test
531                 // sets the seed on each pass.  Defensively set it here
532                 // so that future code that uses pseudo random numbers
533                 // before the first pass will be deterministic.
534 
535     hwcTestInitDisplay(verbose, &dpy, &surface, &width, &height);
536 
537     hwcTestOpenHwc(&hwcDevice);
538 }
539 
540 /*
541  * Initialize Frames
542  *
543  * Creates an array of graphic buffers, within the global variable
544  * named frames.  The graphic buffers are contained within a vector of
545  * vectors.  All the graphic buffers in a particular row are of the same
546  * format and dimension.  Each graphic buffer is uniformly filled with a
547  * prandomly selected color.  It is likely that each buffer, even
548  * in the same row, will be filled with a unique color.
549  */
initFrames(unsigned int seed)550 void initFrames(unsigned int seed)
551 {
552     int rv;
553     const size_t maxRows = 5;
554     const size_t minCols = 2;  // Need at least double buffering
555     const size_t maxCols = 4;  // One more than triple buffering
556 
557     if (verbose) { testPrintI("initFrames seed: %u", seed); }
558     srand48(seed);
559     size_t rows = testRandMod(maxRows) + 1;
560 
561     frames.clear();
562     frames.resize(rows);
563 
564     for (unsigned int row = 0; row < rows; row++) {
565         // All frames within a row have to have the same format and
566         // dimensions.  Width and height need to be >= 1.
567         unsigned int formatIdx = testRandMod(NUMA(hwcTestGraphicFormat));
568         const struct hwcTestGraphicFormat *formatPtr
569             = &hwcTestGraphicFormat[formatIdx];
570         int format = formatPtr->format;
571 
572         // Pick width and height, which must be >= 1 and the size
573         // mod the wMod/hMod value must be equal to 0.
574         size_t w = (width * maxSizeRatio) * testRandFract();
575         size_t h = (height * maxSizeRatio) * testRandFract();
576         w = max(size_t(1u), w);
577         h = max(size_t(1u), h);
578         if ((w % formatPtr->wMod) != 0) {
579             w += formatPtr->wMod - (w % formatPtr->wMod);
580         }
581         if ((h % formatPtr->hMod) != 0) {
582             h += formatPtr->hMod - (h % formatPtr->hMod);
583         }
584         if (verbose) {
585             testPrintI("  frame %u width: %u height: %u format: %u %s",
586                        row, w, h, format, hwcTestGraphicFormat2str(format));
587         }
588 
589         size_t cols = testRandMod((maxCols + 1) - minCols) + minCols;
590         frames[row].resize(cols);
591         for (unsigned int col = 0; col < cols; col++) {
592             ColorFract color(testRandFract(), testRandFract(), testRandFract());
593             float alpha = testRandFract();
594 
595             frames[row][col] = new GraphicBuffer(w, h, format, texUsage);
596             if ((rv = frames[row][col]->initCheck()) != NO_ERROR) {
597                 testPrintE("GraphicBuffer initCheck failed, rv: %i", rv);
598                 testPrintE("  frame %u width: %u height: %u format: %u %s",
599                            row, w, h, format, hwcTestGraphicFormat2str(format));
600                 exit(80);
601             }
602 
603             hwcTestFillColor(frames[row][col].get(), color, alpha);
604             if (verbose) {
605                 testPrintI("    buf: %p handle: %p color: %s alpha: %f",
606                            frames[row][col].get(), frames[row][col]->handle,
607                            string(color).c_str(), alpha);
608             }
609         }
610     }
611 }
612 
613 /*
614  * Vector Random Select
615  *
616  * Prandomly selects and returns num elements from vec.
617  */
618 template <class T>
vectorRandSelect(const vector<T> & vec,size_t num)619 vector<T> vectorRandSelect(const vector<T>& vec, size_t num)
620 {
621     vector<T> rv = vec;
622 
623     while (rv.size() > num) {
624         rv.erase(rv.begin() + testRandMod(rv.size()));
625     }
626 
627     return rv;
628 }
629 
630 /*
631  * Vector Or
632  *
633  * Or's togethen the values of each element of vec and returns the result.
634  */
635 template <class T>
vectorOr(const vector<T> & vec)636 T vectorOr(const vector<T>& vec)
637 {
638     T rv = 0;
639 
640     for (size_t n1 = 0; n1 < vec.size(); n1++) {
641         rv |= vec[n1];
642     }
643 
644     return rv;
645 }
646