17ec681f3Smrg/*
27ec681f3Smrg * Copyright 2018 Collabora Ltd.
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
87ec681f3Smrg * license, and/or sell copies of the Software, and to permit persons to whom
97ec681f3Smrg * the Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
197ec681f3Smrg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
207ec681f3Smrg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
217ec681f3Smrg * USE OR OTHER DEALINGS IN THE SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg#include "zink_context.h"
257ec681f3Smrg#include "zink_framebuffer.h"
267ec681f3Smrg
277ec681f3Smrg#include "zink_render_pass.h"
287ec681f3Smrg#include "zink_screen.h"
297ec681f3Smrg#include "zink_surface.h"
307ec681f3Smrg
317ec681f3Smrg#include "util/u_framebuffer.h"
327ec681f3Smrg#include "util/u_memory.h"
337ec681f3Smrg#include "util/u_string.h"
347ec681f3Smrg
357ec681f3Smrgvoid
367ec681f3Smrgzink_destroy_framebuffer(struct zink_screen *screen,
377ec681f3Smrg                         struct zink_framebuffer *fb)
387ec681f3Smrg{
397ec681f3Smrg   hash_table_foreach(&fb->objects, he) {
407ec681f3Smrg#if defined(_WIN64) || defined(__x86_64__)
417ec681f3Smrg      VKSCR(DestroyFramebuffer)(screen->dev, he->data, NULL);
427ec681f3Smrg#else
437ec681f3Smrg      VkFramebuffer *ptr = he->data;
447ec681f3Smrg      VKSCR(DestroyFramebuffer)(screen->dev, *ptr, NULL);
457ec681f3Smrg#endif
467ec681f3Smrg   }
477ec681f3Smrg
487ec681f3Smrg   ralloc_free(fb);
497ec681f3Smrg}
507ec681f3Smrg
517ec681f3Smrgvoid
527ec681f3Smrgzink_init_framebuffer_imageless(struct zink_screen *screen, struct zink_framebuffer *fb, struct zink_render_pass *rp)
537ec681f3Smrg{
547ec681f3Smrg   VkFramebuffer ret;
557ec681f3Smrg
567ec681f3Smrg   if (fb->rp == rp)
577ec681f3Smrg      return;
587ec681f3Smrg
597ec681f3Smrg   uint32_t hash = _mesa_hash_pointer(rp);
607ec681f3Smrg
617ec681f3Smrg   struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&fb->objects, hash, rp);
627ec681f3Smrg   if (he) {
637ec681f3Smrg#if defined(_WIN64) || defined(__x86_64__)
647ec681f3Smrg      ret = (VkFramebuffer)he->data;
657ec681f3Smrg#else
667ec681f3Smrg      VkFramebuffer *ptr = he->data;
677ec681f3Smrg      ret = *ptr;
687ec681f3Smrg#endif
697ec681f3Smrg      goto out;
707ec681f3Smrg   }
717ec681f3Smrg
727ec681f3Smrg   assert(rp->state.num_cbufs + rp->state.have_zsbuf + rp->state.num_cresolves + rp->state.num_zsresolves == fb->state.num_attachments);
737ec681f3Smrg
747ec681f3Smrg   VkFramebufferCreateInfo fci;
757ec681f3Smrg   fci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
767ec681f3Smrg   fci.flags = VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT;
777ec681f3Smrg   fci.renderPass = rp->render_pass;
787ec681f3Smrg   fci.attachmentCount = fb->state.num_attachments;
797ec681f3Smrg   fci.pAttachments = NULL;
807ec681f3Smrg   fci.width = fb->state.width;
817ec681f3Smrg   fci.height = fb->state.height;
827ec681f3Smrg   fci.layers = fb->state.layers + 1;
837ec681f3Smrg
847ec681f3Smrg   VkFramebufferAttachmentsCreateInfo attachments;
857ec681f3Smrg   attachments.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO;
867ec681f3Smrg   attachments.pNext = NULL;
877ec681f3Smrg   attachments.attachmentImageInfoCount = fb->state.num_attachments;
887ec681f3Smrg   attachments.pAttachmentImageInfos = fb->infos;
897ec681f3Smrg   fci.pNext = &attachments;
907ec681f3Smrg
917ec681f3Smrg   if (VKSCR(CreateFramebuffer)(screen->dev, &fci, NULL, &ret) != VK_SUCCESS)
927ec681f3Smrg      return;
937ec681f3Smrg#if defined(_WIN64) || defined(__x86_64__)
947ec681f3Smrg   _mesa_hash_table_insert_pre_hashed(&fb->objects, hash, rp, ret);
957ec681f3Smrg#else
967ec681f3Smrg   VkFramebuffer *ptr = ralloc(fb, VkFramebuffer);
977ec681f3Smrg   if (!ptr) {
987ec681f3Smrg      VKSCR(DestroyFramebuffer)(screen->dev, ret, NULL);
997ec681f3Smrg      return;
1007ec681f3Smrg   }
1017ec681f3Smrg   *ptr = ret;
1027ec681f3Smrg   _mesa_hash_table_insert_pre_hashed(&fb->objects, hash, rp, ptr);
1037ec681f3Smrg#endif
1047ec681f3Smrgout:
1057ec681f3Smrg   fb->rp = rp;
1067ec681f3Smrg   fb->fb = ret;
1077ec681f3Smrg}
1087ec681f3Smrg
1097ec681f3Smrgstatic void
1107ec681f3Smrgpopulate_attachment_info(VkFramebufferAttachmentImageInfo *att, struct zink_surface_info *info)
1117ec681f3Smrg{
1127ec681f3Smrg   att->sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO;
1137ec681f3Smrg   att->pNext = NULL;
1147ec681f3Smrg   memcpy(&att->flags, &info->flags, offsetof(struct zink_surface_info, format));
1157ec681f3Smrg   att->viewFormatCount = 1;
1167ec681f3Smrg   att->pViewFormats = &info->format;
1177ec681f3Smrg}
1187ec681f3Smrg
1197ec681f3Smrgstatic struct zink_framebuffer *
1207ec681f3Smrgcreate_framebuffer_imageless(struct zink_context *ctx, struct zink_framebuffer_state *state)
1217ec681f3Smrg{
1227ec681f3Smrg   struct zink_screen *screen = zink_screen(ctx->base.screen);
1237ec681f3Smrg   struct zink_framebuffer *fb = rzalloc(ctx, struct zink_framebuffer);
1247ec681f3Smrg   if (!fb)
1257ec681f3Smrg      return NULL;
1267ec681f3Smrg   pipe_reference_init(&fb->reference, 1);
1277ec681f3Smrg
1287ec681f3Smrg   if (!_mesa_hash_table_init(&fb->objects, fb, _mesa_hash_pointer, _mesa_key_pointer_equal))
1297ec681f3Smrg      goto fail;
1307ec681f3Smrg   memcpy(&fb->state, state, sizeof(struct zink_framebuffer_state));
1317ec681f3Smrg   for (int i = 0; i < state->num_attachments; i++)
1327ec681f3Smrg      populate_attachment_info(&fb->infos[i], &fb->state.infos[i]);
1337ec681f3Smrg
1347ec681f3Smrg   return fb;
1357ec681f3Smrgfail:
1367ec681f3Smrg   zink_destroy_framebuffer(screen, fb);
1377ec681f3Smrg   return NULL;
1387ec681f3Smrg}
1397ec681f3Smrg
1407ec681f3Smrgstruct zink_framebuffer *
1417ec681f3Smrgzink_get_framebuffer_imageless(struct zink_context *ctx)
1427ec681f3Smrg{
1437ec681f3Smrg   assert(zink_screen(ctx->base.screen)->info.have_KHR_imageless_framebuffer);
1447ec681f3Smrg
1457ec681f3Smrg   struct zink_framebuffer_state state;
1467ec681f3Smrg   const unsigned cresolve_offset = ctx->fb_state.nr_cbufs + !!ctx->fb_state.zsbuf;
1477ec681f3Smrg   unsigned num_resolves = 0;
1487ec681f3Smrg   for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
1497ec681f3Smrg      struct pipe_surface *psurf = ctx->fb_state.cbufs[i];
1507ec681f3Smrg      if (!psurf)
1517ec681f3Smrg         psurf = ctx->dummy_surface[util_logbase2_ceil(ctx->gfx_pipeline_state.rast_samples+1)];
1527ec681f3Smrg      struct zink_surface *surface = zink_csurface(psurf);
1537ec681f3Smrg      struct zink_surface *transient = zink_transient_surface(psurf);
1547ec681f3Smrg      if (transient) {
1557ec681f3Smrg         memcpy(&state.infos[i], &transient->info, sizeof(transient->info));
1567ec681f3Smrg         memcpy(&state.infos[cresolve_offset + i], &surface->info, sizeof(surface->info));
1577ec681f3Smrg         num_resolves++;
1587ec681f3Smrg      } else {
1597ec681f3Smrg         memcpy(&state.infos[i], &surface->info, sizeof(surface->info));
1607ec681f3Smrg      }
1617ec681f3Smrg   }
1627ec681f3Smrg
1637ec681f3Smrg   state.num_attachments = ctx->fb_state.nr_cbufs;
1647ec681f3Smrg   const unsigned zsresolve_offset = cresolve_offset + num_resolves;
1657ec681f3Smrg   if (ctx->fb_state.zsbuf) {
1667ec681f3Smrg      struct pipe_surface *psurf = ctx->fb_state.zsbuf;
1677ec681f3Smrg      struct zink_surface *surface = zink_csurface(psurf);
1687ec681f3Smrg      struct zink_surface *transient = zink_transient_surface(psurf);
1697ec681f3Smrg      if (transient) {
1707ec681f3Smrg         memcpy(&state.infos[state.num_attachments], &transient->info, sizeof(transient->info));
1717ec681f3Smrg         memcpy(&state.infos[zsresolve_offset], &surface->info, sizeof(surface->info));
1727ec681f3Smrg         num_resolves++;
1737ec681f3Smrg      } else {
1747ec681f3Smrg         memcpy(&state.infos[state.num_attachments], &surface->info, sizeof(surface->info));
1757ec681f3Smrg      }
1767ec681f3Smrg      state.num_attachments++;
1777ec681f3Smrg   }
1787ec681f3Smrg
1797ec681f3Smrg   /* avoid bitfield explosion */
1807ec681f3Smrg   assert(state.num_attachments + num_resolves < 16);
1817ec681f3Smrg   state.num_attachments += num_resolves;
1827ec681f3Smrg   state.width = MAX2(ctx->fb_state.width, 1);
1837ec681f3Smrg   state.height = MAX2(ctx->fb_state.height, 1);
1847ec681f3Smrg   state.layers = MAX2(util_framebuffer_get_num_layers(&ctx->fb_state), 1) - 1;
1857ec681f3Smrg   state.samples = ctx->fb_state.samples - 1;
1867ec681f3Smrg
1877ec681f3Smrg   struct zink_framebuffer *fb;
1887ec681f3Smrg   struct hash_entry *entry = _mesa_hash_table_search(&ctx->framebuffer_cache, &state);
1897ec681f3Smrg   if (entry)
1907ec681f3Smrg      return entry->data;
1917ec681f3Smrg
1927ec681f3Smrg   fb = create_framebuffer_imageless(ctx, &state);
1937ec681f3Smrg   _mesa_hash_table_insert(&ctx->framebuffer_cache, &fb->state, fb);
1947ec681f3Smrg
1957ec681f3Smrg   return fb;
1967ec681f3Smrg}
1977ec681f3Smrg
1987ec681f3Smrgvoid
1997ec681f3Smrgzink_init_framebuffer(struct zink_screen *screen, struct zink_framebuffer *fb, struct zink_render_pass *rp)
2007ec681f3Smrg{
2017ec681f3Smrg   VkFramebuffer ret;
2027ec681f3Smrg
2037ec681f3Smrg   if (fb->rp == rp)
2047ec681f3Smrg      return;
2057ec681f3Smrg
2067ec681f3Smrg   uint32_t hash = _mesa_hash_pointer(rp);
2077ec681f3Smrg
2087ec681f3Smrg   struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&fb->objects, hash, rp);
2097ec681f3Smrg   if (he) {
2107ec681f3Smrg#if defined(_WIN64) || defined(__x86_64__)
2117ec681f3Smrg      ret = (VkFramebuffer)he->data;
2127ec681f3Smrg#else
2137ec681f3Smrg      VkFramebuffer *ptr = he->data;
2147ec681f3Smrg      ret = *ptr;
2157ec681f3Smrg#endif
2167ec681f3Smrg      goto out;
2177ec681f3Smrg   }
2187ec681f3Smrg
2197ec681f3Smrg   assert(rp->state.num_cbufs + rp->state.have_zsbuf + rp->state.num_cresolves + rp->state.num_zsresolves == fb->state.num_attachments);
2207ec681f3Smrg
2217ec681f3Smrg   VkFramebufferCreateInfo fci = {0};
2227ec681f3Smrg   fci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
2237ec681f3Smrg   fci.renderPass = rp->render_pass;
2247ec681f3Smrg   fci.attachmentCount = fb->state.num_attachments;
2257ec681f3Smrg   fci.pAttachments = fb->state.attachments;
2267ec681f3Smrg   fci.width = fb->state.width;
2277ec681f3Smrg   fci.height = fb->state.height;
2287ec681f3Smrg   fci.layers = fb->state.layers + 1;
2297ec681f3Smrg
2307ec681f3Smrg   if (VKSCR(CreateFramebuffer)(screen->dev, &fci, NULL, &ret) != VK_SUCCESS)
2317ec681f3Smrg      return;
2327ec681f3Smrg#if defined(_WIN64) || defined(__x86_64__)
2337ec681f3Smrg   _mesa_hash_table_insert_pre_hashed(&fb->objects, hash, rp, ret);
2347ec681f3Smrg#else
2357ec681f3Smrg   VkFramebuffer *ptr = ralloc(fb, VkFramebuffer);
2367ec681f3Smrg   if (!ptr) {
2377ec681f3Smrg      VKSCR(DestroyFramebuffer)(screen->dev, ret, NULL);
2387ec681f3Smrg      return;
2397ec681f3Smrg   }
2407ec681f3Smrg   *ptr = ret;
2417ec681f3Smrg   _mesa_hash_table_insert_pre_hashed(&fb->objects, hash, rp, ptr);
2427ec681f3Smrg#endif
2437ec681f3Smrgout:
2447ec681f3Smrg   fb->rp = rp;
2457ec681f3Smrg   fb->fb = ret;
2467ec681f3Smrg}
2477ec681f3Smrg
2487ec681f3Smrgstatic struct zink_framebuffer *
2497ec681f3Smrgcreate_framebuffer(struct zink_context *ctx,
2507ec681f3Smrg                   struct zink_framebuffer_state *state,
2517ec681f3Smrg                   struct pipe_surface **attachments)
2527ec681f3Smrg{
2537ec681f3Smrg   struct zink_screen *screen = zink_screen(ctx->base.screen);
2547ec681f3Smrg   struct zink_framebuffer *fb = rzalloc(NULL, struct zink_framebuffer);
2557ec681f3Smrg   if (!fb)
2567ec681f3Smrg      return NULL;
2577ec681f3Smrg
2587ec681f3Smrg   unsigned num_attachments = 0;
2597ec681f3Smrg   for (int i = 0; i < state->num_attachments; i++) {
2607ec681f3Smrg      struct zink_surface *surf;
2617ec681f3Smrg      if (state->attachments[i]) {
2627ec681f3Smrg         surf = zink_csurface(attachments[i]);
2637ec681f3Smrg         /* no ref! */
2647ec681f3Smrg         fb->surfaces[i] = attachments[i];
2657ec681f3Smrg         num_attachments++;
2667ec681f3Smrg         util_dynarray_append(&surf->framebuffer_refs, struct zink_framebuffer*, fb);
2677ec681f3Smrg      } else {
2687ec681f3Smrg         surf = zink_csurface(ctx->dummy_surface[util_logbase2_ceil(state->samples+1)]);
2697ec681f3Smrg         state->attachments[i] = surf->image_view;
2707ec681f3Smrg      }
2717ec681f3Smrg   }
2727ec681f3Smrg   pipe_reference_init(&fb->reference, 1 + num_attachments);
2737ec681f3Smrg
2747ec681f3Smrg   if (!_mesa_hash_table_init(&fb->objects, fb, _mesa_hash_pointer, _mesa_key_pointer_equal))
2757ec681f3Smrg      goto fail;
2767ec681f3Smrg   memcpy(&fb->state, state, sizeof(struct zink_framebuffer_state));
2777ec681f3Smrg
2787ec681f3Smrg   return fb;
2797ec681f3Smrgfail:
2807ec681f3Smrg   zink_destroy_framebuffer(screen, fb);
2817ec681f3Smrg   return NULL;
2827ec681f3Smrg}
2837ec681f3Smrg
2847ec681f3Smrgvoid
2857ec681f3Smrgdebug_describe_zink_framebuffer(char* buf, const struct zink_framebuffer *ptr)
2867ec681f3Smrg{
2877ec681f3Smrg   sprintf(buf, "zink_framebuffer");
2887ec681f3Smrg}
2897ec681f3Smrg
2907ec681f3Smrgstruct zink_framebuffer *
2917ec681f3Smrgzink_get_framebuffer(struct zink_context *ctx)
2927ec681f3Smrg{
2937ec681f3Smrg   struct zink_screen *screen = zink_screen(ctx->base.screen);
2947ec681f3Smrg
2957ec681f3Smrg   assert(!screen->info.have_KHR_imageless_framebuffer);
2967ec681f3Smrg
2977ec681f3Smrg   struct pipe_surface *attachments[2 * (PIPE_MAX_COLOR_BUFS + 1)] = {0};
2987ec681f3Smrg   const unsigned cresolve_offset = ctx->fb_state.nr_cbufs + !!ctx->fb_state.zsbuf;
2997ec681f3Smrg   unsigned num_resolves = 0;
3007ec681f3Smrg
3017ec681f3Smrg   struct zink_framebuffer_state state = {0};
3027ec681f3Smrg   for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
3037ec681f3Smrg      struct pipe_surface *psurf = ctx->fb_state.cbufs[i];
3047ec681f3Smrg      if (psurf) {
3057ec681f3Smrg         struct zink_surface *surf = zink_csurface(psurf);
3067ec681f3Smrg         struct zink_surface *transient = zink_transient_surface(psurf);
3077ec681f3Smrg         if (transient) {
3087ec681f3Smrg            state.attachments[i] = transient->image_view;
3097ec681f3Smrg            state.attachments[cresolve_offset + i] = surf->image_view;
3107ec681f3Smrg            attachments[cresolve_offset + i] = psurf;
3117ec681f3Smrg            psurf = &transient->base;
3127ec681f3Smrg            num_resolves++;
3137ec681f3Smrg         } else {
3147ec681f3Smrg            state.attachments[i] = surf->image_view;
3157ec681f3Smrg         }
3167ec681f3Smrg      } else {
3177ec681f3Smrg         state.attachments[i] = VK_NULL_HANDLE;
3187ec681f3Smrg      }
3197ec681f3Smrg      attachments[i] = psurf;
3207ec681f3Smrg   }
3217ec681f3Smrg
3227ec681f3Smrg   state.num_attachments = ctx->fb_state.nr_cbufs;
3237ec681f3Smrg   const unsigned zsresolve_offset = cresolve_offset + num_resolves;
3247ec681f3Smrg   if (ctx->fb_state.zsbuf) {
3257ec681f3Smrg      struct pipe_surface *psurf = ctx->fb_state.zsbuf;
3267ec681f3Smrg      if (psurf) {
3277ec681f3Smrg         struct zink_surface *surf = zink_csurface(psurf);
3287ec681f3Smrg         struct zink_surface *transient = zink_transient_surface(psurf);
3297ec681f3Smrg         if (transient) {
3307ec681f3Smrg            state.attachments[state.num_attachments] = transient->image_view;
3317ec681f3Smrg            state.attachments[zsresolve_offset] = surf->image_view;
3327ec681f3Smrg            attachments[zsresolve_offset] = psurf;
3337ec681f3Smrg            psurf = &transient->base;
3347ec681f3Smrg            num_resolves++;
3357ec681f3Smrg         } else {
3367ec681f3Smrg            state.attachments[state.num_attachments] = surf->image_view;
3377ec681f3Smrg         }
3387ec681f3Smrg      } else {
3397ec681f3Smrg         state.attachments[state.num_attachments] = VK_NULL_HANDLE;
3407ec681f3Smrg      }
3417ec681f3Smrg      attachments[state.num_attachments++] = psurf;
3427ec681f3Smrg   }
3437ec681f3Smrg
3447ec681f3Smrg   /* avoid bitfield explosion */
3457ec681f3Smrg   assert(state.num_attachments + num_resolves < 16);
3467ec681f3Smrg   state.num_attachments += num_resolves;
3477ec681f3Smrg   state.width = MAX2(ctx->fb_state.width, 1);
3487ec681f3Smrg   state.height = MAX2(ctx->fb_state.height, 1);
3497ec681f3Smrg   state.layers = MAX2(util_framebuffer_get_num_layers(&ctx->fb_state), 1) - 1;
3507ec681f3Smrg   state.samples = ctx->fb_state.samples - 1;
3517ec681f3Smrg
3527ec681f3Smrg   struct zink_framebuffer *fb;
3537ec681f3Smrg   simple_mtx_lock(&screen->framebuffer_mtx);
3547ec681f3Smrg   struct hash_entry *entry = _mesa_hash_table_search(&screen->framebuffer_cache, &state);
3557ec681f3Smrg   if (entry) {
3567ec681f3Smrg      fb = (void*)entry->data;
3577ec681f3Smrg      struct zink_framebuffer *fb_ref = NULL;
3587ec681f3Smrg      /* this gains 1 ref every time we reuse it */
3597ec681f3Smrg      zink_framebuffer_reference(screen, &fb_ref, fb);
3607ec681f3Smrg   } else {
3617ec681f3Smrg      /* this adds 1 extra ref on creation because all newly-created framebuffers are
3627ec681f3Smrg       * going to be bound; necessary to handle framebuffers which have no "real" attachments
3637ec681f3Smrg       * and are only using null surfaces since the only ref they get is the extra one here
3647ec681f3Smrg       */
3657ec681f3Smrg      fb = create_framebuffer(ctx, &state, attachments);
3667ec681f3Smrg      _mesa_hash_table_insert(&screen->framebuffer_cache, &fb->state, fb);
3677ec681f3Smrg   }
3687ec681f3Smrg   simple_mtx_unlock(&screen->framebuffer_mtx);
3697ec681f3Smrg
3707ec681f3Smrg   return fb;
3717ec681f3Smrg}
372