1bf215546Sopenharmony_ci/**************************************************************************
2bf215546Sopenharmony_ci *
3bf215546Sopenharmony_ci * Copyright 2007 VMware, Inc.
4bf215546Sopenharmony_ci * All Rights Reserved.
5bf215546Sopenharmony_ci *
6bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
7bf215546Sopenharmony_ci * copy of this software and associated documentation files (the
8bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
9bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
10bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
11bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
12bf215546Sopenharmony_ci * the following conditions:
13bf215546Sopenharmony_ci *
14bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the
15bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
16bf215546Sopenharmony_ci * of the Software.
17bf215546Sopenharmony_ci *
18bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21bf215546Sopenharmony_ci * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25bf215546Sopenharmony_ci *
26bf215546Sopenharmony_ci **************************************************************************/
27bf215546Sopenharmony_ci
28bf215546Sopenharmony_ci /*
29bf215546Sopenharmony_ci  * Authors:
30bf215546Sopenharmony_ci  *   Keith Whitwell <keithw@vmware.com>
31bf215546Sopenharmony_ci  */
32bf215546Sopenharmony_ci
33bf215546Sopenharmony_ci#include "draw/draw_private.h"
34bf215546Sopenharmony_ci#include "draw/draw_pipe.h"
35bf215546Sopenharmony_ci#include "util/u_debug.h"
36bf215546Sopenharmony_ci#include "util/u_math.h"
37bf215546Sopenharmony_ci
38bf215546Sopenharmony_ci
39bf215546Sopenharmony_ciboolean
40bf215546Sopenharmony_cidraw_pipeline_init(struct draw_context *draw)
41bf215546Sopenharmony_ci{
42bf215546Sopenharmony_ci   /* create pipeline stages */
43bf215546Sopenharmony_ci   draw->pipeline.wide_line  = draw_wide_line_stage(draw);
44bf215546Sopenharmony_ci   draw->pipeline.wide_point = draw_wide_point_stage(draw);
45bf215546Sopenharmony_ci   draw->pipeline.stipple   = draw_stipple_stage(draw);
46bf215546Sopenharmony_ci   draw->pipeline.unfilled  = draw_unfilled_stage(draw);
47bf215546Sopenharmony_ci   draw->pipeline.twoside   = draw_twoside_stage(draw);
48bf215546Sopenharmony_ci   draw->pipeline.offset    = draw_offset_stage(draw);
49bf215546Sopenharmony_ci   draw->pipeline.clip      = draw_clip_stage(draw);
50bf215546Sopenharmony_ci   draw->pipeline.flatshade = draw_flatshade_stage(draw);
51bf215546Sopenharmony_ci   draw->pipeline.cull      = draw_cull_stage(draw);
52bf215546Sopenharmony_ci   draw->pipeline.user_cull = draw_user_cull_stage(draw);
53bf215546Sopenharmony_ci   draw->pipeline.validate  = draw_validate_stage(draw);
54bf215546Sopenharmony_ci   draw->pipeline.first     = draw->pipeline.validate;
55bf215546Sopenharmony_ci
56bf215546Sopenharmony_ci   if (!draw->pipeline.wide_line ||
57bf215546Sopenharmony_ci       !draw->pipeline.wide_point ||
58bf215546Sopenharmony_ci       !draw->pipeline.stipple ||
59bf215546Sopenharmony_ci       !draw->pipeline.unfilled ||
60bf215546Sopenharmony_ci       !draw->pipeline.twoside ||
61bf215546Sopenharmony_ci       !draw->pipeline.offset ||
62bf215546Sopenharmony_ci       !draw->pipeline.clip ||
63bf215546Sopenharmony_ci       !draw->pipeline.flatshade ||
64bf215546Sopenharmony_ci       !draw->pipeline.cull ||
65bf215546Sopenharmony_ci       !draw->pipeline.user_cull ||
66bf215546Sopenharmony_ci       !draw->pipeline.validate)
67bf215546Sopenharmony_ci      return FALSE;
68bf215546Sopenharmony_ci
69bf215546Sopenharmony_ci   /* these defaults are oriented toward the needs of softpipe */
70bf215546Sopenharmony_ci   draw->pipeline.wide_point_threshold = 1000000.0f; /* infinity */
71bf215546Sopenharmony_ci   draw->pipeline.wide_line_threshold = 1.0f;
72bf215546Sopenharmony_ci   draw->pipeline.wide_point_sprites = FALSE;
73bf215546Sopenharmony_ci   draw->pipeline.line_stipple = TRUE;
74bf215546Sopenharmony_ci   draw->pipeline.point_sprite = TRUE;
75bf215546Sopenharmony_ci
76bf215546Sopenharmony_ci   return TRUE;
77bf215546Sopenharmony_ci}
78bf215546Sopenharmony_ci
79bf215546Sopenharmony_ci
80bf215546Sopenharmony_civoid
81bf215546Sopenharmony_cidraw_pipeline_destroy(struct draw_context *draw)
82bf215546Sopenharmony_ci{
83bf215546Sopenharmony_ci   if (draw->pipeline.wide_line)
84bf215546Sopenharmony_ci      draw->pipeline.wide_line->destroy(draw->pipeline.wide_line);
85bf215546Sopenharmony_ci   if (draw->pipeline.wide_point)
86bf215546Sopenharmony_ci      draw->pipeline.wide_point->destroy(draw->pipeline.wide_point);
87bf215546Sopenharmony_ci   if (draw->pipeline.stipple)
88bf215546Sopenharmony_ci      draw->pipeline.stipple->destroy(draw->pipeline.stipple);
89bf215546Sopenharmony_ci   if (draw->pipeline.unfilled)
90bf215546Sopenharmony_ci      draw->pipeline.unfilled->destroy(draw->pipeline.unfilled);
91bf215546Sopenharmony_ci   if (draw->pipeline.twoside)
92bf215546Sopenharmony_ci      draw->pipeline.twoside->destroy(draw->pipeline.twoside);
93bf215546Sopenharmony_ci   if (draw->pipeline.offset)
94bf215546Sopenharmony_ci      draw->pipeline.offset->destroy(draw->pipeline.offset);
95bf215546Sopenharmony_ci   if (draw->pipeline.clip)
96bf215546Sopenharmony_ci      draw->pipeline.clip->destroy(draw->pipeline.clip);
97bf215546Sopenharmony_ci   if (draw->pipeline.flatshade)
98bf215546Sopenharmony_ci      draw->pipeline.flatshade->destroy(draw->pipeline.flatshade);
99bf215546Sopenharmony_ci   if (draw->pipeline.cull)
100bf215546Sopenharmony_ci      draw->pipeline.cull->destroy(draw->pipeline.cull);
101bf215546Sopenharmony_ci   if (draw->pipeline.user_cull)
102bf215546Sopenharmony_ci      draw->pipeline.user_cull->destroy(draw->pipeline.user_cull);
103bf215546Sopenharmony_ci   if (draw->pipeline.validate)
104bf215546Sopenharmony_ci      draw->pipeline.validate->destroy(draw->pipeline.validate);
105bf215546Sopenharmony_ci   if (draw->pipeline.aaline)
106bf215546Sopenharmony_ci      draw->pipeline.aaline->destroy(draw->pipeline.aaline);
107bf215546Sopenharmony_ci   if (draw->pipeline.aapoint)
108bf215546Sopenharmony_ci      draw->pipeline.aapoint->destroy(draw->pipeline.aapoint);
109bf215546Sopenharmony_ci   if (draw->pipeline.pstipple)
110bf215546Sopenharmony_ci      draw->pipeline.pstipple->destroy(draw->pipeline.pstipple);
111bf215546Sopenharmony_ci   if (draw->pipeline.rasterize)
112bf215546Sopenharmony_ci      draw->pipeline.rasterize->destroy(draw->pipeline.rasterize);
113bf215546Sopenharmony_ci}
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_ci
116bf215546Sopenharmony_ci/**
117bf215546Sopenharmony_ci * Build primitive to render a point with vertex at v0.
118bf215546Sopenharmony_ci */
119bf215546Sopenharmony_cistatic void
120bf215546Sopenharmony_cido_point(struct draw_context *draw,
121bf215546Sopenharmony_ci         const char *v0)
122bf215546Sopenharmony_ci{
123bf215546Sopenharmony_ci   struct prim_header prim;
124bf215546Sopenharmony_ci
125bf215546Sopenharmony_ci   prim.flags = 0;
126bf215546Sopenharmony_ci   prim.pad = 0;
127bf215546Sopenharmony_ci   prim.v[0] = (struct vertex_header *)v0;
128bf215546Sopenharmony_ci
129bf215546Sopenharmony_ci   draw->pipeline.first->point(draw->pipeline.first, &prim);
130bf215546Sopenharmony_ci}
131bf215546Sopenharmony_ci
132bf215546Sopenharmony_ci
133bf215546Sopenharmony_ci/**
134bf215546Sopenharmony_ci * Build primitive to render a line with vertices at v0, v1.
135bf215546Sopenharmony_ci * \param flags  bitmask of DRAW_PIPE_EDGE_x, DRAW_PIPE_RESET_STIPPLE
136bf215546Sopenharmony_ci */
137bf215546Sopenharmony_cistatic void
138bf215546Sopenharmony_cido_line(struct draw_context *draw,
139bf215546Sopenharmony_ci        ushort flags,
140bf215546Sopenharmony_ci        const char *v0,
141bf215546Sopenharmony_ci        const char *v1)
142bf215546Sopenharmony_ci{
143bf215546Sopenharmony_ci   struct prim_header prim;
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_ci   prim.flags = flags;
146bf215546Sopenharmony_ci   prim.pad = 0;
147bf215546Sopenharmony_ci   prim.v[0] = (struct vertex_header *)v0;
148bf215546Sopenharmony_ci   prim.v[1] = (struct vertex_header *)v1;
149bf215546Sopenharmony_ci
150bf215546Sopenharmony_ci   draw->pipeline.first->line(draw->pipeline.first, &prim);
151bf215546Sopenharmony_ci}
152bf215546Sopenharmony_ci
153bf215546Sopenharmony_ci
154bf215546Sopenharmony_ci/**
155bf215546Sopenharmony_ci * Build primitive to render a triangle with vertices at v0, v1, v2.
156bf215546Sopenharmony_ci * \param flags  bitmask of DRAW_PIPE_EDGE_x, DRAW_PIPE_RESET_STIPPLE
157bf215546Sopenharmony_ci */
158bf215546Sopenharmony_cistatic void
159bf215546Sopenharmony_cido_triangle(struct draw_context *draw,
160bf215546Sopenharmony_ci            ushort flags,
161bf215546Sopenharmony_ci            char *v0,
162bf215546Sopenharmony_ci            char *v1,
163bf215546Sopenharmony_ci            char *v2)
164bf215546Sopenharmony_ci{
165bf215546Sopenharmony_ci   struct prim_header prim;
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci   prim.v[0] = (struct vertex_header *)v0;
168bf215546Sopenharmony_ci   prim.v[1] = (struct vertex_header *)v1;
169bf215546Sopenharmony_ci   prim.v[2] = (struct vertex_header *)v2;
170bf215546Sopenharmony_ci   prim.flags = flags;
171bf215546Sopenharmony_ci   prim.pad = 0;
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_ci   draw->pipeline.first->tri(draw->pipeline.first, &prim);
174bf215546Sopenharmony_ci}
175bf215546Sopenharmony_ci
176bf215546Sopenharmony_ci
177bf215546Sopenharmony_ci/*
178bf215546Sopenharmony_ci * Set up macros for draw_pt_decompose.h template code.
179bf215546Sopenharmony_ci * This code uses vertex indexes / elements.
180bf215546Sopenharmony_ci */
181bf215546Sopenharmony_ci
182bf215546Sopenharmony_ci#define TRIANGLE(flags,i0,i1,i2)                                 \
183bf215546Sopenharmony_ci   do {                                                          \
184bf215546Sopenharmony_ci      do_triangle(draw,                                          \
185bf215546Sopenharmony_ci                  flags,                                         \
186bf215546Sopenharmony_ci                  verts + stride * (i0),                         \
187bf215546Sopenharmony_ci                  verts + stride * (i1),                         \
188bf215546Sopenharmony_ci                  verts + stride * (i2));                        \
189bf215546Sopenharmony_ci   } while (0)
190bf215546Sopenharmony_ci
191bf215546Sopenharmony_ci#define LINE(flags,i0,i1)                                         \
192bf215546Sopenharmony_ci   do {                                                           \
193bf215546Sopenharmony_ci      do_line(draw,                                               \
194bf215546Sopenharmony_ci              flags,                                              \
195bf215546Sopenharmony_ci              verts + stride * (i0),                              \
196bf215546Sopenharmony_ci              verts + stride * (i1));                             \
197bf215546Sopenharmony_ci   } while (0)
198bf215546Sopenharmony_ci
199bf215546Sopenharmony_ci#define POINT(i0)                              \
200bf215546Sopenharmony_ci   do {                                        \
201bf215546Sopenharmony_ci      do_point(draw, verts + stride * (i0));   \
202bf215546Sopenharmony_ci   } while (0)
203bf215546Sopenharmony_ci
204bf215546Sopenharmony_ci#define GET_ELT(idx) (MIN2(elts[idx], max_index))
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_ci#define FUNC pipe_run_elts
207bf215546Sopenharmony_ci#define FUNC_VARS                               \
208bf215546Sopenharmony_ci    struct draw_context *draw,                  \
209bf215546Sopenharmony_ci    enum pipe_prim_type prim,                   \
210bf215546Sopenharmony_ci    unsigned prim_flags,                        \
211bf215546Sopenharmony_ci    struct vertex_header *vertices,             \
212bf215546Sopenharmony_ci    unsigned stride,                            \
213bf215546Sopenharmony_ci    const ushort *elts,                         \
214bf215546Sopenharmony_ci    unsigned count,                             \
215bf215546Sopenharmony_ci    unsigned max_index
216bf215546Sopenharmony_ci
217bf215546Sopenharmony_ci#include "draw_pt_decompose.h"
218bf215546Sopenharmony_ci
219bf215546Sopenharmony_ci
220bf215546Sopenharmony_ci
221bf215546Sopenharmony_ci/**
222bf215546Sopenharmony_ci * Code to run the pipeline on a fairly arbitrary collection of vertices.
223bf215546Sopenharmony_ci * For drawing indexed primitives.
224bf215546Sopenharmony_ci *
225bf215546Sopenharmony_ci * Vertex headers must be pre-initialized with the
226bf215546Sopenharmony_ci * UNDEFINED_VERTEX_ID, this code will cause that id to become
227bf215546Sopenharmony_ci * overwritten, so it may have to be reset if there is the intention
228bf215546Sopenharmony_ci * to reuse the vertices.
229bf215546Sopenharmony_ci *
230bf215546Sopenharmony_ci * This code provides a callback to reset the vertex id's which the
231bf215546Sopenharmony_ci * draw_vbuf.c code uses when it has to perform a flush.
232bf215546Sopenharmony_ci */
233bf215546Sopenharmony_civoid
234bf215546Sopenharmony_cidraw_pipeline_run(struct draw_context *draw,
235bf215546Sopenharmony_ci                  const struct draw_vertex_info *vert_info,
236bf215546Sopenharmony_ci                  const struct draw_prim_info *prim_info)
237bf215546Sopenharmony_ci{
238bf215546Sopenharmony_ci   draw->pipeline.verts = (char *)vert_info->verts;
239bf215546Sopenharmony_ci   draw->pipeline.vertex_stride = vert_info->stride;
240bf215546Sopenharmony_ci   draw->pipeline.vertex_count = vert_info->count;
241bf215546Sopenharmony_ci
242bf215546Sopenharmony_ci   unsigned i, start;
243bf215546Sopenharmony_ci   for (start = i = 0;
244bf215546Sopenharmony_ci        i < prim_info->primitive_count;
245bf215546Sopenharmony_ci        start += prim_info->primitive_lengths[i], i++) {
246bf215546Sopenharmony_ci      const unsigned count = prim_info->primitive_lengths[i];
247bf215546Sopenharmony_ci
248bf215546Sopenharmony_ci#if DEBUG
249bf215546Sopenharmony_ci      /* Warn if one of the element indexes go outside the vertex buffer */
250bf215546Sopenharmony_ci      {
251bf215546Sopenharmony_ci         unsigned max_index = 0x0;
252bf215546Sopenharmony_ci         /* find the largest element index */
253bf215546Sopenharmony_ci         for (unsigned i = 0; i < count; i++) {
254bf215546Sopenharmony_ci            unsigned int index = prim_info->elts[start + i];
255bf215546Sopenharmony_ci            if (index > max_index)
256bf215546Sopenharmony_ci               max_index = index;
257bf215546Sopenharmony_ci         }
258bf215546Sopenharmony_ci         if (max_index >= vert_info->count) {
259bf215546Sopenharmony_ci            debug_printf("%s: max_index (%u) outside vertex buffer (%u)\n",
260bf215546Sopenharmony_ci                         __FUNCTION__,
261bf215546Sopenharmony_ci                         max_index,
262bf215546Sopenharmony_ci                         vert_info->count);
263bf215546Sopenharmony_ci         }
264bf215546Sopenharmony_ci      }
265bf215546Sopenharmony_ci#endif
266bf215546Sopenharmony_ci
267bf215546Sopenharmony_ci      pipe_run_elts(draw,
268bf215546Sopenharmony_ci                    prim_info->prim,
269bf215546Sopenharmony_ci                    prim_info->flags,
270bf215546Sopenharmony_ci                    vert_info->verts,
271bf215546Sopenharmony_ci                    vert_info->stride,
272bf215546Sopenharmony_ci                    prim_info->elts + start,
273bf215546Sopenharmony_ci                    count,
274bf215546Sopenharmony_ci                    vert_info->count - 1);
275bf215546Sopenharmony_ci   }
276bf215546Sopenharmony_ci
277bf215546Sopenharmony_ci   draw->pipeline.verts = NULL;
278bf215546Sopenharmony_ci   draw->pipeline.vertex_count = 0;
279bf215546Sopenharmony_ci}
280bf215546Sopenharmony_ci
281bf215546Sopenharmony_ci
282bf215546Sopenharmony_ci/*
283bf215546Sopenharmony_ci * Set up macros for draw_pt_decompose.h template code.
284bf215546Sopenharmony_ci * This code is for non-indexed (aka linear) rendering (no elts).
285bf215546Sopenharmony_ci */
286bf215546Sopenharmony_ci
287bf215546Sopenharmony_ci#define TRIANGLE(flags,i0,i1,i2)       \
288bf215546Sopenharmony_ci   do_triangle(draw, flags,            \
289bf215546Sopenharmony_ci                verts + stride * (i0), \
290bf215546Sopenharmony_ci                verts + stride * (i1), \
291bf215546Sopenharmony_ci                verts + stride * (i2))
292bf215546Sopenharmony_ci
293bf215546Sopenharmony_ci#define LINE(flags,i0,i1)              \
294bf215546Sopenharmony_ci   do_line(draw, flags,                \
295bf215546Sopenharmony_ci            verts + stride * (i0),     \
296bf215546Sopenharmony_ci            verts + stride * (i1))
297bf215546Sopenharmony_ci
298bf215546Sopenharmony_ci#define POINT(i0)                      \
299bf215546Sopenharmony_ci   do_point(draw, verts + stride * (i0))
300bf215546Sopenharmony_ci
301bf215546Sopenharmony_ci
302bf215546Sopenharmony_ci#define GET_ELT(idx) (idx)
303bf215546Sopenharmony_ci
304bf215546Sopenharmony_ci#define FUNC pipe_run_linear
305bf215546Sopenharmony_ci#define FUNC_VARS                      \
306bf215546Sopenharmony_ci    struct draw_context *draw,         \
307bf215546Sopenharmony_ci    enum pipe_prim_type prim,          \
308bf215546Sopenharmony_ci    unsigned prim_flags,               \
309bf215546Sopenharmony_ci    struct vertex_header *vertices,    \
310bf215546Sopenharmony_ci    unsigned stride,                   \
311bf215546Sopenharmony_ci    unsigned count
312bf215546Sopenharmony_ci
313bf215546Sopenharmony_ci#include "draw_pt_decompose.h"
314bf215546Sopenharmony_ci
315bf215546Sopenharmony_ci
316bf215546Sopenharmony_ci/*
317bf215546Sopenharmony_ci * For drawing non-indexed primitives.
318bf215546Sopenharmony_ci */
319bf215546Sopenharmony_civoid
320bf215546Sopenharmony_cidraw_pipeline_run_linear(struct draw_context *draw,
321bf215546Sopenharmony_ci                         const struct draw_vertex_info *vert_info,
322bf215546Sopenharmony_ci                         const struct draw_prim_info *prim_info)
323bf215546Sopenharmony_ci{
324bf215546Sopenharmony_ci   unsigned i, start;
325bf215546Sopenharmony_ci
326bf215546Sopenharmony_ci   for (start = i = 0;
327bf215546Sopenharmony_ci        i < prim_info->primitive_count;
328bf215546Sopenharmony_ci        start += prim_info->primitive_lengths[i], i++) {
329bf215546Sopenharmony_ci      unsigned count = prim_info->primitive_lengths[i];
330bf215546Sopenharmony_ci      char *verts = ((char*)vert_info->verts) +
331bf215546Sopenharmony_ci                    (start * vert_info->stride);
332bf215546Sopenharmony_ci
333bf215546Sopenharmony_ci      draw->pipeline.verts = verts;
334bf215546Sopenharmony_ci      draw->pipeline.vertex_stride = vert_info->stride;
335bf215546Sopenharmony_ci      draw->pipeline.vertex_count = count;
336bf215546Sopenharmony_ci
337bf215546Sopenharmony_ci      assert(count <= vert_info->count);
338bf215546Sopenharmony_ci
339bf215546Sopenharmony_ci      pipe_run_linear(draw,
340bf215546Sopenharmony_ci                      prim_info->prim,
341bf215546Sopenharmony_ci                      prim_info->flags,
342bf215546Sopenharmony_ci                      (struct vertex_header*)verts,
343bf215546Sopenharmony_ci                      vert_info->stride,
344bf215546Sopenharmony_ci                      count);
345bf215546Sopenharmony_ci   }
346bf215546Sopenharmony_ci
347bf215546Sopenharmony_ci   draw->pipeline.verts = NULL;
348bf215546Sopenharmony_ci   draw->pipeline.vertex_count = 0;
349bf215546Sopenharmony_ci}
350bf215546Sopenharmony_ci
351bf215546Sopenharmony_ci
352bf215546Sopenharmony_civoid
353bf215546Sopenharmony_cidraw_pipeline_flush(struct draw_context *draw,
354bf215546Sopenharmony_ci                    unsigned flags)
355bf215546Sopenharmony_ci{
356bf215546Sopenharmony_ci   draw->pipeline.first->flush(draw->pipeline.first, flags);
357bf215546Sopenharmony_ci   if (flags & DRAW_FLUSH_STATE_CHANGE)
358bf215546Sopenharmony_ci      draw->pipeline.first = draw->pipeline.validate;
359bf215546Sopenharmony_ci}
360