1/*
2 * Copyright © 2017 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23#include <stdio.h>
24#include <time.h>
25#include "pipe/p_defines.h"
26#include "pipe/p_state.h"
27#include "util/ralloc.h"
28#include "util/u_inlines.h"
29#include "util/format/u_format.h"
30#include "util/u_upload_mgr.h"
31#include "drm-uapi/i915_drm.h"
32#include "crocus_context.h"
33#include "crocus_resource.h"
34#include "crocus_screen.h"
35#include "common/intel_defines.h"
36#include "common/intel_sample_positions.h"
37
38/**
39 * The pipe->set_debug_callback() driver hook.
40 */
41static void
42crocus_set_debug_callback(struct pipe_context *ctx,
43                          const struct util_debug_callback *cb)
44{
45   struct crocus_context *ice = (struct crocus_context *)ctx;
46
47   if (cb)
48      ice->dbg = *cb;
49   else
50      memset(&ice->dbg, 0, sizeof(ice->dbg));
51}
52
53static bool
54crocus_init_identifier_bo(struct crocus_context *ice)
55{
56   void *bo_map;
57
58   bo_map = crocus_bo_map(NULL, ice->workaround_bo, MAP_READ | MAP_WRITE);
59   if (!bo_map)
60      return false;
61
62   ice->workaround_bo->kflags |= EXEC_OBJECT_CAPTURE;
63   ice->workaround_offset = ALIGN(
64      intel_debug_write_identifiers(bo_map, 4096, "Crocus") + 8, 8);
65
66   crocus_bo_unmap(ice->workaround_bo);
67
68   return true;
69}
70
71/**
72 * Called from the batch module when it detects a GPU hang.
73 *
74 * In this case, we've lost our GEM context, and can't rely on any existing
75 * state on the GPU.  We must mark everything dirty and wipe away any saved
76 * assumptions about the last known state of the GPU.
77 */
78void
79crocus_lost_context_state(struct crocus_batch *batch)
80{
81   /* The batch module doesn't have an crocus_context, because we want to
82    * avoid introducing lots of layering violations.  Unfortunately, here
83    * we do need to inform the context of batch catastrophe.  We know the
84    * batch is one of our context's, so hackily claw our way back.
85    */
86   struct crocus_context *ice = batch->ice;
87   struct crocus_screen *screen = batch->screen;
88   if (batch->name == CROCUS_BATCH_RENDER) {
89      screen->vtbl.init_render_context(batch);
90   } else if (batch->name == CROCUS_BATCH_COMPUTE) {
91      screen->vtbl.init_compute_context(batch);
92   } else {
93      unreachable("unhandled batch reset");
94   }
95
96   ice->state.dirty = ~0ull;
97   memset(ice->state.last_grid, 0, sizeof(ice->state.last_grid));
98   batch->state_base_address_emitted = false;
99   screen->vtbl.lost_genx_state(ice, batch);
100}
101
102static enum pipe_reset_status
103crocus_get_device_reset_status(struct pipe_context *ctx)
104{
105   struct crocus_context *ice = (struct crocus_context *)ctx;
106
107   enum pipe_reset_status worst_reset = PIPE_NO_RESET;
108
109   /* Check the reset status of each batch's hardware context, and take the
110    * worst status (if one was guilty, proclaim guilt).
111    */
112   for (int i = 0; i < ice->batch_count; i++) {
113      /* This will also recreate the hardware contexts as necessary, so any
114       * future queries will show no resets.  We only want to report once.
115       */
116      enum pipe_reset_status batch_reset =
117         crocus_batch_check_for_reset(&ice->batches[i]);
118
119      if (batch_reset == PIPE_NO_RESET)
120         continue;
121
122      if (worst_reset == PIPE_NO_RESET) {
123         worst_reset = batch_reset;
124      } else {
125         /* GUILTY < INNOCENT < UNKNOWN */
126         worst_reset = MIN2(worst_reset, batch_reset);
127      }
128   }
129
130   if (worst_reset != PIPE_NO_RESET && ice->reset.reset)
131      ice->reset.reset(ice->reset.data, worst_reset);
132
133   return worst_reset;
134}
135
136static void
137crocus_set_device_reset_callback(struct pipe_context *ctx,
138                                 const struct pipe_device_reset_callback *cb)
139{
140   struct crocus_context *ice = (struct crocus_context *)ctx;
141
142   if (cb)
143      ice->reset = *cb;
144   else
145      memset(&ice->reset, 0, sizeof(ice->reset));
146}
147
148static void
149crocus_get_sample_position(struct pipe_context *ctx,
150                           unsigned sample_count,
151                           unsigned sample_index,
152                           float *out_value)
153{
154   union {
155      struct {
156         float x[16];
157         float y[16];
158      } a;
159      struct {
160         float  _0XOffset,  _1XOffset,  _2XOffset,  _3XOffset,
161                _4XOffset,  _5XOffset,  _6XOffset,  _7XOffset,
162                _8XOffset,  _9XOffset, _10XOffset, _11XOffset,
163               _12XOffset, _13XOffset, _14XOffset, _15XOffset;
164         float  _0YOffset,  _1YOffset,  _2YOffset,  _3YOffset,
165                _4YOffset,  _5YOffset,  _6YOffset,  _7YOffset,
166                _8YOffset,  _9YOffset, _10YOffset, _11YOffset,
167               _12YOffset, _13YOffset, _14YOffset, _15YOffset;
168      } v;
169   } u;
170   switch (sample_count) {
171   case 1:  INTEL_SAMPLE_POS_1X(u.v._);  break;
172   case 2:  INTEL_SAMPLE_POS_2X(u.v._);  break;
173   case 4:  INTEL_SAMPLE_POS_4X(u.v._);  break;
174   case 8:  INTEL_SAMPLE_POS_8X(u.v._);  break;
175   case 16: INTEL_SAMPLE_POS_16X(u.v._); break;
176   default: unreachable("invalid sample count");
177   }
178
179   out_value[0] = u.a.x[sample_index];
180   out_value[1] = u.a.y[sample_index];
181}
182
183/**
184 * Destroy a context, freeing any associated memory.
185 */
186static void
187crocus_destroy_context(struct pipe_context *ctx)
188{
189   struct crocus_context *ice = (struct crocus_context *)ctx;
190   struct crocus_screen *screen = (struct crocus_screen *)ctx->screen;
191   if (ctx->stream_uploader)
192      u_upload_destroy(ctx->stream_uploader);
193
194   if (ice->blitter)
195      util_blitter_destroy(ice->blitter);
196   screen->vtbl.destroy_state(ice);
197   crocus_destroy_program_cache(ice);
198   u_upload_destroy(ice->query_buffer_uploader);
199
200   crocus_bo_unreference(ice->workaround_bo);
201
202   slab_destroy_child(&ice->transfer_pool);
203   slab_destroy_child(&ice->transfer_pool_unsync);
204
205   crocus_batch_free(&ice->batches[CROCUS_BATCH_RENDER]);
206   if (ice->batches[CROCUS_BATCH_COMPUTE].ice)
207      crocus_batch_free(&ice->batches[CROCUS_BATCH_COMPUTE]);
208
209   ralloc_free(ice);
210}
211
212#define genX_call(devinfo, func, ...)                   \
213   switch ((devinfo)->verx10) {                         \
214   case 80:                                             \
215      gfx8_##func(__VA_ARGS__);                         \
216      break;                                            \
217   case 75:                                             \
218      gfx75_##func(__VA_ARGS__);                        \
219      break;                                            \
220   case 70:                                             \
221      gfx7_##func(__VA_ARGS__);                         \
222      break;                                            \
223   case 60:                                             \
224      gfx6_##func(__VA_ARGS__);                         \
225      break;                                            \
226   case 50:                                             \
227      gfx5_##func(__VA_ARGS__);                         \
228      break;                                            \
229   case 45:                                             \
230      gfx45_##func(__VA_ARGS__);                        \
231      break;                                            \
232   case 40:                                             \
233      gfx4_##func(__VA_ARGS__);                         \
234      break;                                            \
235   default:                                             \
236      unreachable("Unknown hardware generation");       \
237   }
238
239/**
240 * Create a context.
241 *
242 * This is where each context begins.
243 */
244struct pipe_context *
245crocus_create_context(struct pipe_screen *pscreen, void *priv, unsigned flags)
246{
247   struct crocus_screen *screen = (struct crocus_screen*)pscreen;
248   const struct intel_device_info *devinfo = &screen->devinfo;
249   struct crocus_context *ice = rzalloc(NULL, struct crocus_context);
250
251   if (!ice)
252      return NULL;
253
254   struct pipe_context *ctx = &ice->ctx;
255
256   ctx->screen = pscreen;
257   ctx->priv = priv;
258
259   ctx->stream_uploader = u_upload_create_default(ctx);
260   if (!ctx->stream_uploader) {
261      free(ctx);
262      return NULL;
263   }
264   ctx->const_uploader = ctx->stream_uploader;
265
266   ctx->destroy = crocus_destroy_context;
267   ctx->set_debug_callback = crocus_set_debug_callback;
268   ctx->set_device_reset_callback = crocus_set_device_reset_callback;
269   ctx->get_device_reset_status = crocus_get_device_reset_status;
270   ctx->get_sample_position = crocus_get_sample_position;
271
272   ice->shaders.urb_size = devinfo->urb.size;
273
274   crocus_init_context_fence_functions(ctx);
275   crocus_init_blit_functions(ctx);
276   crocus_init_clear_functions(ctx);
277   crocus_init_program_functions(ctx);
278   crocus_init_resource_functions(ctx);
279   crocus_init_flush_functions(ctx);
280
281   crocus_init_program_cache(ice);
282
283   slab_create_child(&ice->transfer_pool, &screen->transfer_pool);
284   slab_create_child(&ice->transfer_pool_unsync, &screen->transfer_pool);
285
286   ice->query_buffer_uploader =
287      u_upload_create(ctx, 4096, PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING,
288                      0);
289
290   ice->workaround_bo =
291      crocus_bo_alloc(screen->bufmgr, "workaround", 4096);
292   if (!ice->workaround_bo)
293      return NULL;
294
295   if (!crocus_init_identifier_bo(ice))
296      return NULL;
297
298   genX_call(devinfo, crocus_init_state, ice);
299   genX_call(devinfo, crocus_init_blorp, ice);
300   genX_call(devinfo, crocus_init_query, ice);
301
302   ice->blitter = util_blitter_create(&ice->ctx);
303   if (ice->blitter == NULL)
304      return NULL;
305   int priority = 0;
306   if (flags & PIPE_CONTEXT_HIGH_PRIORITY)
307      priority = INTEL_CONTEXT_HIGH_PRIORITY;
308   if (flags & PIPE_CONTEXT_LOW_PRIORITY)
309      priority = INTEL_CONTEXT_LOW_PRIORITY;
310
311   ice->batch_count = devinfo->ver >= 7 ? CROCUS_BATCH_COUNT : 1;
312   for (int i = 0; i < ice->batch_count; i++) {
313      crocus_init_batch(ice, (enum crocus_batch_name) i,
314                        priority);
315   }
316
317   ice->urb.size = devinfo->urb.size;
318   screen->vtbl.init_render_context(&ice->batches[CROCUS_BATCH_RENDER]);
319   if (ice->batch_count > 1)
320      screen->vtbl.init_compute_context(&ice->batches[CROCUS_BATCH_COMPUTE]);
321
322   if (!(flags & PIPE_CONTEXT_PREFER_THREADED))
323     return ctx;
324
325   return threaded_context_create(ctx, &screen->transfer_pool,
326                                  crocus_replace_buffer_storage,
327                                  NULL, /* TODO: asynchronous flushes? */
328                                  &ice->thrctx);
329}
330
331bool
332crocus_sw_check_cond_render(struct crocus_context *ice)
333{
334   struct crocus_query *q = ice->condition.query;
335   union pipe_query_result result;
336
337   bool wait = ice->condition.mode == PIPE_RENDER_COND_WAIT ||
338      ice->condition.mode == PIPE_RENDER_COND_BY_REGION_WAIT;
339   if (!q)
340      return true;
341
342   bool ret = ice->ctx.get_query_result(&ice->ctx, (void *)q, wait, &result);
343   if (!ret)
344      return true;
345
346   return ice->condition.condition ? result.u64 == 0 : result.u64 != 0;
347}
348