101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2014-2017 Broadcom
301e04c3fSmrg *
401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
501e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
601e04c3fSmrg * to deal in the Software without restriction, including without limitation
701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
901e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1001e04c3fSmrg *
1101e04c3fSmrg * The above copyright notice and this permission notice (including the next
1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the
1301e04c3fSmrg * Software.
1401e04c3fSmrg *
1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2101e04c3fSmrg * IN THE SOFTWARE.
2201e04c3fSmrg */
2301e04c3fSmrg
2401e04c3fSmrg#include <xf86drm.h>
2501e04c3fSmrg#include <err.h>
2601e04c3fSmrg
2701e04c3fSmrg#include "pipe/p_defines.h"
2801e04c3fSmrg#include "util/hash_table.h"
2901e04c3fSmrg#include "util/ralloc.h"
3001e04c3fSmrg#include "util/u_inlines.h"
3101e04c3fSmrg#include "util/u_memory.h"
3201e04c3fSmrg#include "util/u_blitter.h"
3301e04c3fSmrg#include "util/u_upload_mgr.h"
347ec681f3Smrg#include "util/u_prim.h"
3501e04c3fSmrg#include "pipe/p_screen.h"
3601e04c3fSmrg
3701e04c3fSmrg#include "v3d_screen.h"
3801e04c3fSmrg#include "v3d_context.h"
3901e04c3fSmrg#include "v3d_resource.h"
409f464c52Smaya#include "broadcom/compiler/v3d_compiler.h"
4101e04c3fSmrg
4201e04c3fSmrgvoid
4301e04c3fSmrgv3d_flush(struct pipe_context *pctx)
4401e04c3fSmrg{
4501e04c3fSmrg        struct v3d_context *v3d = v3d_context(pctx);
4601e04c3fSmrg
4701e04c3fSmrg        hash_table_foreach(v3d->jobs, entry) {
4801e04c3fSmrg                struct v3d_job *job = entry->data;
4901e04c3fSmrg                v3d_job_submit(v3d, job);
5001e04c3fSmrg        }
5101e04c3fSmrg}
5201e04c3fSmrg
5301e04c3fSmrgstatic void
5401e04c3fSmrgv3d_pipe_flush(struct pipe_context *pctx, struct pipe_fence_handle **fence,
5501e04c3fSmrg               unsigned flags)
5601e04c3fSmrg{
5701e04c3fSmrg        struct v3d_context *v3d = v3d_context(pctx);
5801e04c3fSmrg
5901e04c3fSmrg        v3d_flush(pctx);
6001e04c3fSmrg
6101e04c3fSmrg        if (fence) {
6201e04c3fSmrg                struct pipe_screen *screen = pctx->screen;
6301e04c3fSmrg                struct v3d_fence *f = v3d_fence_create(v3d);
6401e04c3fSmrg                screen->fence_reference(screen, fence, NULL);
6501e04c3fSmrg                *fence = (struct pipe_fence_handle *)f;
6601e04c3fSmrg        }
6701e04c3fSmrg}
6801e04c3fSmrg
699f464c52Smayastatic void
709f464c52Smayav3d_memory_barrier(struct pipe_context *pctx, unsigned int flags)
719f464c52Smaya{
729f464c52Smaya        struct v3d_context *v3d = v3d_context(pctx);
739f464c52Smaya
747ec681f3Smrg        /* We only need to flush for SSBOs and images, because for everything
757ec681f3Smrg         * else we flush the job automatically when we needed.
767ec681f3Smrg         */
777ec681f3Smrg        const unsigned int flush_flags = PIPE_BARRIER_SHADER_BUFFER |
787ec681f3Smrg                                         PIPE_BARRIER_IMAGE;
797ec681f3Smrg
807ec681f3Smrg	if (!(flags & flush_flags))
819f464c52Smaya		return;
829f464c52Smaya
839f464c52Smaya        /* We only need to flush jobs writing to SSBOs/images. */
849f464c52Smaya        perf_debug("Flushing all jobs for glMemoryBarrier(), could do better");
859f464c52Smaya        v3d_flush(pctx);
869f464c52Smaya}
879f464c52Smaya
889f464c52Smayastatic void
899f464c52Smayav3d_set_debug_callback(struct pipe_context *pctx,
909f464c52Smaya                       const struct pipe_debug_callback *cb)
919f464c52Smaya{
929f464c52Smaya        struct v3d_context *v3d = v3d_context(pctx);
939f464c52Smaya
949f464c52Smaya        if (cb)
959f464c52Smaya                v3d->debug = *cb;
969f464c52Smaya        else
979f464c52Smaya                memset(&v3d->debug, 0, sizeof(v3d->debug));
989f464c52Smaya}
999f464c52Smaya
10001e04c3fSmrgstatic void
10101e04c3fSmrgv3d_invalidate_resource(struct pipe_context *pctx, struct pipe_resource *prsc)
10201e04c3fSmrg{
10301e04c3fSmrg        struct v3d_context *v3d = v3d_context(pctx);
10401e04c3fSmrg        struct v3d_resource *rsc = v3d_resource(prsc);
10501e04c3fSmrg
10601e04c3fSmrg        rsc->initialized_buffers = 0;
10701e04c3fSmrg
10801e04c3fSmrg        struct hash_entry *entry = _mesa_hash_table_search(v3d->write_jobs,
10901e04c3fSmrg                                                           prsc);
11001e04c3fSmrg        if (!entry)
11101e04c3fSmrg                return;
11201e04c3fSmrg
11301e04c3fSmrg        struct v3d_job *job = entry->data;
11401e04c3fSmrg        if (job->key.zsbuf && job->key.zsbuf->texture == prsc)
11501e04c3fSmrg                job->store &= ~(PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL);
11601e04c3fSmrg}
11701e04c3fSmrg
1187ec681f3Smrg/**
1197ec681f3Smrg * Flushes the current job to get up-to-date primitive counts written to the
1207ec681f3Smrg * primitive counts BO, then accumulates the transform feedback primitive count
1217ec681f3Smrg * in the context and the corresponding vertex counts in the bound stream
1227ec681f3Smrg * output targets.
1237ec681f3Smrg */
1247ec681f3Smrgvoid
1257ec681f3Smrgv3d_update_primitive_counters(struct v3d_context *v3d)
1267ec681f3Smrg{
1277ec681f3Smrg        struct v3d_job *job = v3d_get_job_for_fbo(v3d);
1287ec681f3Smrg        if (job->draw_calls_queued == 0)
1297ec681f3Smrg                return;
1307ec681f3Smrg
1317ec681f3Smrg        /* In order to get up-to-date primitive counts we need to submit
1327ec681f3Smrg         * the job for execution so we get the counts written to memory.
1337ec681f3Smrg         * Notice that this will require a sync wait for the buffer write.
1347ec681f3Smrg         */
1357ec681f3Smrg        uint32_t prims_before = v3d->tf_prims_generated;
1367ec681f3Smrg        v3d_job_submit(v3d, job);
1377ec681f3Smrg        uint32_t prims_after = v3d->tf_prims_generated;
1387ec681f3Smrg        if (prims_before == prims_after)
1397ec681f3Smrg                return;
1407ec681f3Smrg
1417ec681f3Smrg        enum pipe_prim_type prim_type = u_base_prim_type(v3d->prim_mode);
1427ec681f3Smrg        uint32_t num_verts = u_vertices_for_prims(prim_type,
1437ec681f3Smrg                                                  prims_after - prims_before);
1447ec681f3Smrg        for (int i = 0; i < v3d->streamout.num_targets; i++) {
1457ec681f3Smrg                struct v3d_stream_output_target *so =
1467ec681f3Smrg                        v3d_stream_output_target(v3d->streamout.targets[i]);
1477ec681f3Smrg                so->recorded_vertex_count += num_verts;
1487ec681f3Smrg        }
1497ec681f3Smrg}
1507ec681f3Smrg
1517ec681f3Smrgbool
1527ec681f3Smrgv3d_line_smoothing_enabled(struct v3d_context *v3d)
1537ec681f3Smrg{
1547ec681f3Smrg        if (!v3d->rasterizer->base.line_smooth)
1557ec681f3Smrg                return false;
1567ec681f3Smrg
1577ec681f3Smrg        /* According to the OpenGL docs, line smoothing shouldn’t be applied
1587ec681f3Smrg         * when multisampling
1597ec681f3Smrg         */
1607ec681f3Smrg        if (v3d->job->msaa || v3d->rasterizer->base.multisample)
1617ec681f3Smrg                return false;
1627ec681f3Smrg
1637ec681f3Smrg        if (v3d->framebuffer.nr_cbufs <= 0)
1647ec681f3Smrg                return false;
1657ec681f3Smrg
1667ec681f3Smrg        struct pipe_surface *cbuf = v3d->framebuffer.cbufs[0];
1677ec681f3Smrg        if (!cbuf)
1687ec681f3Smrg                return false;
1697ec681f3Smrg
1707ec681f3Smrg        /* Modifying the alpha for pure integer formats probably
1717ec681f3Smrg         * doesn’t make sense because we don’t know how the application
1727ec681f3Smrg         * uses the alpha value.
1737ec681f3Smrg         */
1747ec681f3Smrg        if (util_format_is_pure_integer(cbuf->format))
1757ec681f3Smrg                return false;
1767ec681f3Smrg
1777ec681f3Smrg        return true;
1787ec681f3Smrg}
1797ec681f3Smrg
1807ec681f3Smrgfloat
1817ec681f3Smrgv3d_get_real_line_width(struct v3d_context *v3d)
1827ec681f3Smrg{
1837ec681f3Smrg        float width = v3d->rasterizer->base.line_width;
1847ec681f3Smrg
1857ec681f3Smrg        if (v3d_line_smoothing_enabled(v3d)) {
1867ec681f3Smrg                /* If line smoothing is enabled then we want to add some extra
1877ec681f3Smrg                 * pixels to the width in order to have some semi-transparent
1887ec681f3Smrg                 * edges.
1897ec681f3Smrg                 */
1907ec681f3Smrg                width = floorf(M_SQRT2 * width) + 3;
1917ec681f3Smrg        }
1927ec681f3Smrg
1937ec681f3Smrg        return width;
1947ec681f3Smrg}
1957ec681f3Smrg
1967ec681f3Smrgvoid
1977ec681f3Smrgv3d_ensure_prim_counts_allocated(struct v3d_context *ctx)
1987ec681f3Smrg{
1997ec681f3Smrg        if (ctx->prim_counts)
2007ec681f3Smrg                return;
2017ec681f3Smrg
2027ec681f3Smrg        /* Init all 7 counters and 1 padding to 0 */
2037ec681f3Smrg        uint32_t zeroes[8] = { 0 };
2047ec681f3Smrg        u_upload_data(ctx->uploader,
2057ec681f3Smrg                      0, sizeof(zeroes), 32, zeroes,
2067ec681f3Smrg                      &ctx->prim_counts_offset,
2077ec681f3Smrg                      &ctx->prim_counts);
2087ec681f3Smrg}
2097ec681f3Smrg
2107ec681f3Smrgvoid
2117ec681f3Smrgv3d_flag_dirty_sampler_state(struct v3d_context *v3d,
2127ec681f3Smrg                             enum pipe_shader_type shader)
2137ec681f3Smrg{
2147ec681f3Smrg        switch (shader) {
2157ec681f3Smrg        case PIPE_SHADER_VERTEX:
2167ec681f3Smrg                v3d->dirty |= V3D_DIRTY_VERTTEX;
2177ec681f3Smrg                break;
2187ec681f3Smrg        case PIPE_SHADER_GEOMETRY:
2197ec681f3Smrg                v3d->dirty |= V3D_DIRTY_GEOMTEX;
2207ec681f3Smrg                break;
2217ec681f3Smrg        case PIPE_SHADER_FRAGMENT:
2227ec681f3Smrg                v3d->dirty |= V3D_DIRTY_FRAGTEX;
2237ec681f3Smrg                break;
2247ec681f3Smrg        case PIPE_SHADER_COMPUTE:
2257ec681f3Smrg                v3d->dirty |= V3D_DIRTY_COMPTEX;
2267ec681f3Smrg                break;
2277ec681f3Smrg        default:
2287ec681f3Smrg                unreachable("Unsupported shader stage");
2297ec681f3Smrg        }
2307ec681f3Smrg}
2317ec681f3Smrg
2327ec681f3Smrgvoid
2337ec681f3Smrgv3d_create_texture_shader_state_bo(struct v3d_context *v3d,
2347ec681f3Smrg                                   struct v3d_sampler_view *so)
2357ec681f3Smrg{
2367ec681f3Smrg        if (v3d->screen->devinfo.ver >= 41)
2377ec681f3Smrg                v3d41_create_texture_shader_state_bo(v3d, so);
2387ec681f3Smrg        else
2397ec681f3Smrg                v3d33_create_texture_shader_state_bo(v3d, so);
2407ec681f3Smrg}
2417ec681f3Smrg
2427ec681f3Smrgvoid
2437ec681f3Smrgv3d_get_tile_buffer_size(bool is_msaa,
2447ec681f3Smrg                         uint32_t nr_cbufs,
2457ec681f3Smrg                         struct pipe_surface **cbufs,
2467ec681f3Smrg                         struct pipe_surface *bbuf,
2477ec681f3Smrg                         uint32_t *tile_width,
2487ec681f3Smrg                         uint32_t *tile_height,
2497ec681f3Smrg                         uint32_t *max_bpp)
2507ec681f3Smrg{
2517ec681f3Smrg        static const uint8_t tile_sizes[] = {
2527ec681f3Smrg                64, 64,
2537ec681f3Smrg                64, 32,
2547ec681f3Smrg                32, 32,
2557ec681f3Smrg                32, 16,
2567ec681f3Smrg                16, 16,
2577ec681f3Smrg        };
2587ec681f3Smrg        int tile_size_index = 0;
2597ec681f3Smrg        if (is_msaa)
2607ec681f3Smrg                tile_size_index += 2;
2617ec681f3Smrg
2627ec681f3Smrg        if (cbufs[3] || cbufs[2])
2637ec681f3Smrg                tile_size_index += 2;
2647ec681f3Smrg        else if (cbufs[1])
2657ec681f3Smrg                tile_size_index++;
2667ec681f3Smrg
2677ec681f3Smrg        *max_bpp = 0;
2687ec681f3Smrg        for (int i = 0; i < nr_cbufs; i++) {
2697ec681f3Smrg                if (cbufs[i]) {
2707ec681f3Smrg                        struct v3d_surface *surf = v3d_surface(cbufs[i]);
2717ec681f3Smrg                        *max_bpp = MAX2(*max_bpp, surf->internal_bpp);
2727ec681f3Smrg                }
2737ec681f3Smrg        }
2747ec681f3Smrg
2757ec681f3Smrg        if (bbuf) {
2767ec681f3Smrg                struct v3d_surface *bsurf = v3d_surface(bbuf);
2777ec681f3Smrg                assert(bbuf->texture->nr_samples <= 1 || is_msaa);
2787ec681f3Smrg                *max_bpp = MAX2(*max_bpp, bsurf->internal_bpp);
2797ec681f3Smrg        }
2807ec681f3Smrg
2817ec681f3Smrg        tile_size_index += *max_bpp;
2827ec681f3Smrg
2837ec681f3Smrg        assert(tile_size_index < ARRAY_SIZE(tile_sizes));
2847ec681f3Smrg        *tile_width = tile_sizes[tile_size_index * 2 + 0];
2857ec681f3Smrg        *tile_height = tile_sizes[tile_size_index * 2 + 1];
2867ec681f3Smrg}
2877ec681f3Smrg
28801e04c3fSmrgstatic void
28901e04c3fSmrgv3d_context_destroy(struct pipe_context *pctx)
29001e04c3fSmrg{
29101e04c3fSmrg        struct v3d_context *v3d = v3d_context(pctx);
29201e04c3fSmrg
29301e04c3fSmrg        v3d_flush(pctx);
29401e04c3fSmrg
29501e04c3fSmrg        if (v3d->blitter)
29601e04c3fSmrg                util_blitter_destroy(v3d->blitter);
29701e04c3fSmrg
29801e04c3fSmrg        if (v3d->uploader)
29901e04c3fSmrg                u_upload_destroy(v3d->uploader);
3009f464c52Smaya        if (v3d->state_uploader)
3019f464c52Smaya                u_upload_destroy(v3d->state_uploader);
30201e04c3fSmrg
3037ec681f3Smrg        if (v3d->prim_counts)
3047ec681f3Smrg                pipe_resource_reference(&v3d->prim_counts, NULL);
3057ec681f3Smrg
30601e04c3fSmrg        slab_destroy_child(&v3d->transfer_pool);
30701e04c3fSmrg
30801e04c3fSmrg        pipe_surface_reference(&v3d->framebuffer.cbufs[0], NULL);
30901e04c3fSmrg        pipe_surface_reference(&v3d->framebuffer.zsbuf, NULL);
31001e04c3fSmrg
3117ec681f3Smrg        if (v3d->sand8_blit_vs)
3127ec681f3Smrg                pctx->delete_vs_state(pctx, v3d->sand8_blit_vs);
3137ec681f3Smrg        if (v3d->sand8_blit_fs_luma)
3147ec681f3Smrg                pctx->delete_fs_state(pctx, v3d->sand8_blit_fs_luma);
3157ec681f3Smrg        if (v3d->sand8_blit_fs_chroma)
3167ec681f3Smrg                pctx->delete_fs_state(pctx, v3d->sand8_blit_fs_chroma);
3177ec681f3Smrg
31801e04c3fSmrg        v3d_program_fini(pctx);
31901e04c3fSmrg
32001e04c3fSmrg        ralloc_free(v3d);
32101e04c3fSmrg}
32201e04c3fSmrg
3239f464c52Smayastatic void
3249f464c52Smayav3d_get_sample_position(struct pipe_context *pctx,
3259f464c52Smaya                        unsigned sample_count, unsigned sample_index,
3269f464c52Smaya                        float *xy)
3279f464c52Smaya{
3289f464c52Smaya        struct v3d_context *v3d = v3d_context(pctx);
3299f464c52Smaya
3309f464c52Smaya        if (sample_count <= 1) {
3319f464c52Smaya                xy[0] = 0.5;
3329f464c52Smaya                xy[1] = 0.5;
3339f464c52Smaya        } else {
3349f464c52Smaya                static const int xoffsets_v33[] = { 1, -3, 3, -1 };
3359f464c52Smaya                static const int xoffsets_v42[] = { -1, 3, -3, 1 };
3369f464c52Smaya                const int *xoffsets = (v3d->screen->devinfo.ver >= 42 ?
3379f464c52Smaya                                       xoffsets_v42 : xoffsets_v33);
3389f464c52Smaya
3399f464c52Smaya                xy[0] = 0.5 + xoffsets[sample_index] * .125;
3409f464c52Smaya                xy[1] = .125 + sample_index * .25;
3419f464c52Smaya        }
3429f464c52Smaya}
3439f464c52Smaya
34401e04c3fSmrgstruct pipe_context *
34501e04c3fSmrgv3d_context_create(struct pipe_screen *pscreen, void *priv, unsigned flags)
34601e04c3fSmrg{
34701e04c3fSmrg        struct v3d_screen *screen = v3d_screen(pscreen);
34801e04c3fSmrg        struct v3d_context *v3d;
34901e04c3fSmrg
35001e04c3fSmrg        /* Prevent dumping of the shaders built during context setup. */
35101e04c3fSmrg        uint32_t saved_shaderdb_flag = V3D_DEBUG & V3D_DEBUG_SHADERDB;
35201e04c3fSmrg        V3D_DEBUG &= ~V3D_DEBUG_SHADERDB;
35301e04c3fSmrg
35401e04c3fSmrg        v3d = rzalloc(NULL, struct v3d_context);
35501e04c3fSmrg        if (!v3d)
35601e04c3fSmrg                return NULL;
35701e04c3fSmrg        struct pipe_context *pctx = &v3d->base;
35801e04c3fSmrg
35901e04c3fSmrg        v3d->screen = screen;
36001e04c3fSmrg
36101e04c3fSmrg        int ret = drmSyncobjCreate(screen->fd, DRM_SYNCOBJ_CREATE_SIGNALED,
36201e04c3fSmrg                                   &v3d->out_sync);
36301e04c3fSmrg        if (ret) {
36401e04c3fSmrg                ralloc_free(v3d);
36501e04c3fSmrg                return NULL;
36601e04c3fSmrg        }
36701e04c3fSmrg
36801e04c3fSmrg        pctx->screen = pscreen;
36901e04c3fSmrg        pctx->priv = priv;
37001e04c3fSmrg        pctx->destroy = v3d_context_destroy;
37101e04c3fSmrg        pctx->flush = v3d_pipe_flush;
3729f464c52Smaya        pctx->memory_barrier = v3d_memory_barrier;
3739f464c52Smaya        pctx->set_debug_callback = v3d_set_debug_callback;
37401e04c3fSmrg        pctx->invalidate_resource = v3d_invalidate_resource;
3759f464c52Smaya        pctx->get_sample_position = v3d_get_sample_position;
37601e04c3fSmrg
37701e04c3fSmrg        if (screen->devinfo.ver >= 41) {
37801e04c3fSmrg                v3d41_draw_init(pctx);
37901e04c3fSmrg                v3d41_state_init(pctx);
38001e04c3fSmrg        } else {
38101e04c3fSmrg                v3d33_draw_init(pctx);
38201e04c3fSmrg                v3d33_state_init(pctx);
38301e04c3fSmrg        }
38401e04c3fSmrg        v3d_program_init(pctx);
38501e04c3fSmrg        v3d_query_init(pctx);
38601e04c3fSmrg        v3d_resource_context_init(pctx);
38701e04c3fSmrg
38801e04c3fSmrg        v3d_job_init(v3d);
38901e04c3fSmrg
39001e04c3fSmrg        v3d->fd = screen->fd;
39101e04c3fSmrg
39201e04c3fSmrg        slab_create_child(&v3d->transfer_pool, &screen->transfer_pool);
39301e04c3fSmrg
39401e04c3fSmrg        v3d->uploader = u_upload_create_default(&v3d->base);
39501e04c3fSmrg        v3d->base.stream_uploader = v3d->uploader;
39601e04c3fSmrg        v3d->base.const_uploader = v3d->uploader;
3979f464c52Smaya        v3d->state_uploader = u_upload_create(&v3d->base,
3989f464c52Smaya                                              4096,
3999f464c52Smaya                                              PIPE_BIND_CONSTANT_BUFFER,
4009f464c52Smaya                                              PIPE_USAGE_STREAM, 0);
40101e04c3fSmrg
40201e04c3fSmrg        v3d->blitter = util_blitter_create(pctx);
40301e04c3fSmrg        if (!v3d->blitter)
40401e04c3fSmrg                goto fail;
40501e04c3fSmrg        v3d->blitter->use_index_buffer = true;
40601e04c3fSmrg
40701e04c3fSmrg        V3D_DEBUG |= saved_shaderdb_flag;
40801e04c3fSmrg
4099f464c52Smaya        v3d->sample_mask = (1 << V3D_MAX_SAMPLES) - 1;
41001e04c3fSmrg        v3d->active_queries = true;
41101e04c3fSmrg
41201e04c3fSmrg        return &v3d->base;
41301e04c3fSmrg
41401e04c3fSmrgfail:
41501e04c3fSmrg        pctx->destroy(pctx);
41601e04c3fSmrg        return NULL;
41701e04c3fSmrg}
418