1857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs/*
2f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller * Copyright 2011 Nouveau Project
3857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs *
4857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * copy of this software and associated documentation files (the "Software"),
6857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * to deal in the Software without restriction, including without limitation
7857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * Software is furnished to do so, subject to the following conditions:
10857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs *
11857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * The above copyright notice and this permission notice shall be included in
12857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * all copies or substantial portions of the Software.
13857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs *
14857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs * SOFTWARE.
21f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller *
22f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller * Authors: Christoph Bumiller
23857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs */
24857a3294a959015bf893241199f7fd7f7882a6abBen Skeggs
256d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller#define NV50_PUSH_EXPLICIT_SPACE_CHECKING
266d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller
2747b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs#include "nv50_context.h"
28f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller#include "nouveau/nv_object.xml.h"
29f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
30f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller/* XXX: Nested queries, and simultaneous queries on multiple gallium contexts
31f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller * (since we use only a single GPU channel per screen) will not work properly.
32f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller *
33f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller * The first is not that big of an issue because OpenGL does not allow nested
34f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller * queries anyway.
35f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller */
3647b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs
37918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggsstruct nv50_query {
38f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   uint32_t *data;
3902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   uint16_t type;
4002fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   uint16_t index;
41f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   uint32_t sequence;
42f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nouveau_bo *bo;
43f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   uint32_t base;
44f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   uint32_t offset; /* base + i * 16 */
45f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   boolean ready;
46939b26639fdc0913c5670b7fa20aab1f41b2b155Christoph Bumiller   boolean flushed;
47f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   boolean is64bit;
482f30a5bdaad480118e23ac4243de3b4a11ba62a8Ben Skeggs   struct nouveau_mm_allocation *mm;
49918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs};
50918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
51f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller#define NV50_QUERY_ALLOC_SPACE 128
52f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
53918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggsstatic INLINE struct nv50_query *
54918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggsnv50_query(struct pipe_query *pipe)
55918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs{
56f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   return (struct nv50_query *)pipe;
57f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller}
58f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
59f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumillerstatic boolean
60f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumillernv50_query_allocate(struct nv50_context *nv50, struct nv50_query *q, int size)
61f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller{
62f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_screen *screen = nv50->screen;
63f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   int ret;
64f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
65f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (q->bo) {
66f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      nouveau_bo_ref(NULL, &q->bo);
67f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      if (q->mm) {
68f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         if (q->ready)
692f30a5bdaad480118e23ac4243de3b4a11ba62a8Ben Skeggs            nouveau_mm_free(q->mm);
70f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         else
716d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller            nouveau_fence_work(screen->base.fence.current, nouveau_mm_free_work,
726d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller                               q->mm);
73f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      }
74f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
75f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (size) {
76cd24fcedecfc41d77047fb827a88db528ed292caBen Skeggs      q->mm = nouveau_mm_allocate(screen->base.mm_GART, size, &q->bo, &q->base);
77f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      if (!q->bo)
78f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         return FALSE;
79f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->offset = q->base;
80f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
816d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      ret = nouveau_bo_map(q->bo, 0, screen->base.client);
82f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      if (ret) {
83f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         nv50_query_allocate(nv50, q, 0);
84f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         return FALSE;
85f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      }
866d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      q->data = (uint32_t *)((uint8_t *)q->bo->map + q->base);
87f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
88f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   return TRUE;
89f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller}
90f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
91f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumillerstatic void
92f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumillernv50_query_destroy(struct pipe_context *pipe, struct pipe_query *pq)
93f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller{
94f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   nv50_query_allocate(nv50_context(pipe), nv50_query(pq), 0);
95f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   FREE(nv50_query(pq));
96918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs}
97918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
9879bca7dd884da33c06ecd3dabb893f9cfed1aaedBen Skeggsstatic struct pipe_query *
9979bca7dd884da33c06ecd3dabb893f9cfed1aaedBen Skeggsnv50_query_create(struct pipe_context *pipe, unsigned type)
10079bca7dd884da33c06ecd3dabb893f9cfed1aaedBen Skeggs{
101f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_context *nv50 = nv50_context(pipe);
102f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_query *q;
103f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
104f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   q = CALLOC_STRUCT(nv50_query);
105f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (!q)
106f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      return NULL;
107918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
108f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (!nv50_query_allocate(nv50, q, NV50_QUERY_ALLOC_SPACE)) {
109f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      FREE(q);
110f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      return NULL;
111f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
112918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
113f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   q->is64bit = (type == PIPE_QUERY_PRIMITIVES_GENERATED ||
114f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller                 type == PIPE_QUERY_PRIMITIVES_EMITTED ||
115f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller                 type == PIPE_QUERY_SO_STATISTICS);
116f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   q->type = type;
117918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
118f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (q->type == PIPE_QUERY_OCCLUSION_COUNTER) {
119f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->offset -= 16;
120f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->data -= 16 / sizeof(*q->data); /* we advance before query_begin ! */
121f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
122f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
123f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   return (struct pipe_query *)q;
12479bca7dd884da33c06ecd3dabb893f9cfed1aaedBen Skeggs}
12579bca7dd884da33c06ecd3dabb893f9cfed1aaedBen Skeggs
126b8965bee404cb36ccd97ac089fbd3ffc63268080Ben Skeggsstatic void
1276d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumillernv50_query_get(struct nouveau_pushbuf *push, struct nv50_query *q,
128f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller               unsigned offset, uint32_t get)
12947b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs{
130f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   offset += q->offset;
131918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
1326d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_SPACE(push, 5);
1336d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_REFN (push, q->bo, NOUVEAU_BO_GART | NOUVEAU_BO_WR);
1346d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   BEGIN_NV04(push, NV50_3D(QUERY_ADDRESS_HIGH), 4);
1356d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_DATAh(push, q->bo->offset + offset);
1366d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_DATA (push, q->bo->offset + offset);
1376d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_DATA (push, q->sequence);
1386d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_DATA (push, get);
13947b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs}
14047b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs
141b8965bee404cb36ccd97ac089fbd3ffc63268080Ben Skeggsstatic void
142918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggsnv50_query_begin(struct pipe_context *pipe, struct pipe_query *pq)
14347b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs{
144f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_context *nv50 = nv50_context(pipe);
1456d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   struct nouveau_pushbuf *push = nv50->base.pushbuf;
146f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_query *q = nv50_query(pq);
147f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
148f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   /* For occlusion queries we have to change the storage, because a previous
149f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller    * query might set the initial render conition to FALSE even *after* we re-
150f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller    * initialized it to TRUE.
151f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller    */
152f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (q->type == PIPE_QUERY_OCCLUSION_COUNTER) {
153f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->offset += 16;
154f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->data += 16 / sizeof(*q->data);
155f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      if (q->offset - q->base == NV50_QUERY_ALLOC_SPACE)
156f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         nv50_query_allocate(nv50, q, NV50_QUERY_ALLOC_SPACE);
157918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
158f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      /* XXX: can we do this with the GPU, and sync with respect to a previous
159f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller       *  query ?
160f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller       */
161f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->data[1] = 1; /* initial render condition = TRUE */
162f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
163f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (!q->is64bit)
164f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->data[0] = q->sequence++; /* the previously used one */
165918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggs
166f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   switch (q->type) {
167f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_OCCLUSION_COUNTER:
1686d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      PUSH_SPACE(push, 4);
1696d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      BEGIN_NV04(push, NV50_3D(COUNTER_RESET), 1);
1706d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      PUSH_DATA (push, NV50_3D_COUNTER_RESET_SAMPLECNT);
1716d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      BEGIN_NV04(push, NV50_3D(SAMPLECNT_ENABLE), 1);
1726d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      PUSH_DATA (push, 1);
173f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
17402fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   case PIPE_QUERY_PRIMITIVES_GENERATED:
17502fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      nv50_query_get(push, q, 0x10, 0x06805002);
176f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
177f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_PRIMITIVES_EMITTED:
17802fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      nv50_query_get(push, q, 0x10, 0x05805002);
179f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
180f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_SO_STATISTICS:
18102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      nv50_query_get(push, q, 0x20, 0x05805002);
18202fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      nv50_query_get(push, q, 0x30, 0x06805002);
183f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
184f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_TIMESTAMP_DISJOINT:
185f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_TIME_ELAPSED:
1866d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0x10, 0x00005002);
187f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
188f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   default:
189f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
190f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
191f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   q->ready = FALSE;
19247b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs}
19347b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs
194b8965bee404cb36ccd97ac089fbd3ffc63268080Ben Skeggsstatic void
195918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggsnv50_query_end(struct pipe_context *pipe, struct pipe_query *pq)
19679bca7dd884da33c06ecd3dabb893f9cfed1aaedBen Skeggs{
197f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_context *nv50 = nv50_context(pipe);
1986d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   struct nouveau_pushbuf *push = nv50->base.pushbuf;
199f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_query *q = nv50_query(pq);
200f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
201f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   switch (q->type) {
202f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_OCCLUSION_COUNTER:
2036d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0, 0x0100f002);
2046d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      PUSH_SPACE(push, 2);
2056d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      BEGIN_NV04(push, NV50_3D(SAMPLECNT_ENABLE), 1);
2066d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      PUSH_DATA (push, 0);
207f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
208f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_PRIMITIVES_GENERATED:
2096d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0, 0x06805002);
210f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
211f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_PRIMITIVES_EMITTED:
2126d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0, 0x05805002);
213f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
214f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_SO_STATISTICS:
2156d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0x00, 0x05805002);
2166d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0x10, 0x06805002);
217f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
2189ed65301e044711de0db51b4986085fca170d764Christoph Bumiller   case PIPE_QUERY_TIMESTAMP:
2199ed65301e044711de0db51b4986085fca170d764Christoph Bumiller      q->sequence++;
2209ed65301e044711de0db51b4986085fca170d764Christoph Bumiller      /* fall through */
221f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_TIMESTAMP_DISJOINT:
222f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_TIME_ELAPSED:
2236d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0, 0x00005002);
224f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
225f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_GPU_FINISHED:
2269ed65301e044711de0db51b4986085fca170d764Christoph Bumiller      q->sequence++;
2276d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      nv50_query_get(push, q, 0, 0x1000f010);
228f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
22902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   case NVA0_QUERY_STREAM_OUTPUT_BUFFER_OFFSET:
23002fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      nv50_query_get(push, q, 0, 0x0d005002 | (q->index << 5));
23102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      break;
232f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   default:
233f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      assert(0);
234f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
235f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
2369ed65301e044711de0db51b4986085fca170d764Christoph Bumiller   q->ready = q->flushed = FALSE;
237f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller}
238f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
239f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumillerstatic INLINE boolean
240f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumillernv50_query_ready(struct nv50_query *q)
241f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller{
242f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   return q->ready || (!q->is64bit && (q->data[0] == q->sequence));
243f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller}
244f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
24579bca7dd884da33c06ecd3dabb893f9cfed1aaedBen Skeggsstatic boolean
246918fc55e5f5cbedd3ab8fb3e02b225106c059fa6Ben Skeggsnv50_query_result(struct pipe_context *pipe, struct pipe_query *pq,
2474445e170bee23a3607ece0e010adef7058ac6a11Marek Olšák                  boolean wait, union pipe_query_result *result)
24847b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs{
2496d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   struct nv50_context *nv50 = nv50_context(pipe);
250f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_query *q = nv50_query(pq);
2516d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   uint64_t *res64 = (uint64_t *)result;
25202fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   uint32_t *res32 = (uint32_t *)result;
2536d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   boolean *res8 = (boolean *)result;
254f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   uint64_t *data64 = (uint64_t *)q->data;
255f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
256f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (!q->ready) /* update ? */
257f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      q->ready = nv50_query_ready(q);
258f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (!q->ready) {
259f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      if (!wait) {
2606d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller         /* for broken apps that spin on GL_QUERY_RESULT_AVAILABLE */
261939b26639fdc0913c5670b7fa20aab1f41b2b155Christoph Bumiller         if (!q->flushed) {
262939b26639fdc0913c5670b7fa20aab1f41b2b155Christoph Bumiller            q->flushed = TRUE;
263939b26639fdc0913c5670b7fa20aab1f41b2b155Christoph Bumiller            PUSH_KICK(nv50->base.pushbuf);
264939b26639fdc0913c5670b7fa20aab1f41b2b155Christoph Bumiller         }
265f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         return FALSE;
266f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      }
2676d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      if (nouveau_bo_wait(q->bo, NOUVEAU_BO_RD, nv50->screen->base.client))
268f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller         return FALSE;
269f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
270f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   q->ready = TRUE;
271f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
272f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   switch (q->type) {
2736d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   case PIPE_QUERY_GPU_FINISHED:
2746d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      res8[0] = TRUE;
2756d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      break;
276f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_OCCLUSION_COUNTER: /* u32 sequence, u32 count, u64 time */
277f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      res64[0] = q->data[1];
278f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
279f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_PRIMITIVES_GENERATED: /* u64 count, u64 time */
280f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_PRIMITIVES_EMITTED: /* u64 count, u64 time */
28102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      res64[0] = data64[0] - data64[2];
282f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
283f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_SO_STATISTICS:
28402fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      res64[0] = data64[0] - data64[4];
28502fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      res64[1] = data64[2] - data64[6];
286f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
2879ed65301e044711de0db51b4986085fca170d764Christoph Bumiller   case PIPE_QUERY_TIMESTAMP:
2889ed65301e044711de0db51b4986085fca170d764Christoph Bumiller      res64[0] = data64[1];
2899ed65301e044711de0db51b4986085fca170d764Christoph Bumiller      break;
290f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_TIMESTAMP_DISJOINT: /* u32 sequence, u32 0, u64 time */
291f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      res64[0] = 1000000000;
2929ed65301e044711de0db51b4986085fca170d764Christoph Bumiller      res8[8] = (data64[1] == data64[3]) ? FALSE : TRUE;
293f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
294f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   case PIPE_QUERY_TIME_ELAPSED:
295f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      res64[0] = data64[1] - data64[3];
296f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      break;
29702fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   case NVA0_QUERY_STREAM_OUTPUT_BUFFER_OFFSET:
29802fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      res32[0] = q->data[1];
29902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      break;
300f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   default:
301f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      return FALSE;
302f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
303f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
304f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   return TRUE;
30547b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs}
30647b418b8fa5fd242e9021503d6ec329ac3d56fb0Ben Skeggs
30702fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumillervoid
30802fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumillernv84_query_fifo_wait(struct nouveau_pushbuf *push, struct pipe_query *pq)
30902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller{
31002fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   struct nv50_query *q = nv50_query(pq);
31102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   unsigned offset = q->offset;
31202fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
31302fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   PUSH_SPACE(push, 5);
31402fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   PUSH_REFN (push, q->bo, NOUVEAU_BO_GART | NOUVEAU_BO_RD);
31502fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   BEGIN_NV04(push, SUBC_3D(NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH), 4);
31602fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   PUSH_DATAh(push, q->bo->offset + offset);
31702fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   PUSH_DATA (push, q->bo->offset + offset);
31802fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   PUSH_DATA (push, q->sequence);
31902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   PUSH_DATA (push, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL);
32002fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller}
32102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
322d6fd31a8d47b6cc70b5e64757e1272ccc6594350Christoph Bumillerstatic void
323d6fd31a8d47b6cc70b5e64757e1272ccc6594350Christoph Bumillernv50_render_condition(struct pipe_context *pipe,
324f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller                      struct pipe_query *pq, uint mode)
325d6fd31a8d47b6cc70b5e64757e1272ccc6594350Christoph Bumiller{
326f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_context *nv50 = nv50_context(pipe);
3276d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   struct nouveau_pushbuf *push = nv50->base.pushbuf;
328f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   struct nv50_query *q;
329f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
3306d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_SPACE(push, 6);
3316d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller
332f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (!pq) {
3336d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      BEGIN_NV04(push, NV50_3D(COND_MODE), 1);
3346d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      PUSH_DATA (push, NV50_3D_COND_MODE_ALWAYS);
335f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller      return;
336f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
337f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   q = nv50_query(pq);
338f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
339f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   if (mode == PIPE_RENDER_COND_WAIT ||
340f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller       mode == PIPE_RENDER_COND_BY_REGION_WAIT) {
3416d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      BEGIN_NV04(push, SUBC_3D(NV50_GRAPH_SERIALIZE), 1);
3426d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller      PUSH_DATA (push, 0);
343f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller   }
344f80c03e1875fe96ff2f4c022e3cb76357828140dChristoph Bumiller
3456d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   BEGIN_NV04(push, NV50_3D(COND_ADDRESS_HIGH), 3);
3466d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_DATAh(push, q->bo->offset + q->offset);
3476d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_DATA (push, q->bo->offset + q->offset);
3486d1cdec3ba151168bfc3aef222fba6265dfb41fbChristoph Bumiller   PUSH_DATA (push, NV50_3D_COND_MODE_RES_NON_ZERO);
349d6fd31a8d47b6cc70b5e64757e1272ccc6594350Christoph Bumiller}
350d6fd31a8d47b6cc70b5e64757e1272ccc6594350Christoph Bumiller
351b8965bee404cb36ccd97ac089fbd3ffc63268080Ben Skeggsvoid
35202fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumillernv50_query_pushbuf_submit(struct nouveau_pushbuf *push,
35302fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller                          struct pipe_query *pq, unsigned result_offset)
35402fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller{
35502fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   struct nv50_query *q = nv50_query(pq);
35602fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
35702fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   /* XXX: does this exist ? */
35802fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller#define NV50_IB_ENTRY_1_NO_PREFETCH (0 << (31 - 8))
35902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
36002fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   nouveau_pushbuf_space(push, 0, 0, 1);
36102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   nouveau_pushbuf_data(push, q->bo, q->offset + result_offset, 4 |
36202fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller                        NV50_IB_ENTRY_1_NO_PREFETCH);
36302fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller}
36402fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
36502fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumillervoid
36602fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumillernva0_so_target_save_offset(struct pipe_context *pipe,
36702fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller                           struct pipe_stream_output_target *ptarg,
36802fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller                           unsigned index, boolean serialize)
36902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller{
37002fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   struct nv50_so_target *targ = nv50_so_target(ptarg);
37102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
37202fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   if (serialize) {
37302fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      struct nouveau_pushbuf *push = nv50_context(pipe)->base.pushbuf;
37402fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      PUSH_SPACE(push, 2);
37502fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      BEGIN_NV04(push, SUBC_3D(NV50_GRAPH_SERIALIZE), 1);
37602fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller      PUSH_DATA (push, 0);
37702fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   }
37802fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
37902fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   nv50_query(targ->pq)->index = index;
38002fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller   nv50_query_end(pipe, targ->pq);
38102fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller}
38202fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumiller
38302fac2930581b9bea9f6d221eb6d6b471fc3b9c6Christoph Bumillervoid
384b8965bee404cb36ccd97ac089fbd3ffc63268080Ben Skeggsnv50_init_query_functions(struct nv50_context *nv50)
385b8965bee404cb36ccd97ac089fbd3ffc63268080Ben Skeggs{
3861ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs   struct pipe_context *pipe = &nv50->base.pipe;
3871ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs
3881ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs   pipe->create_query = nv50_query_create;
3891ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs   pipe->destroy_query = nv50_query_destroy;
3901ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs   pipe->begin_query = nv50_query_begin;
3911ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs   pipe->end_query = nv50_query_end;
3921ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs   pipe->get_query_result = nv50_query_result;
3931ba8e9510812f155359d380bda6876cdee5ba21eBen Skeggs   pipe->render_condition = nv50_render_condition;
394b8965bee404cb36ccd97ac089fbd3ffc63268080Ben Skeggs}
395