1/**********************************************************
2 * Copyright 2009 VMware, Inc.  All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 **********************************************************/
25
26
27#include "svga_cmd.h"
28
29#include "util/u_debug.h"
30#include "util/u_memory.h"
31#include "util/u_debug_stack.h"
32#include "pipebuffer/pb_buffer.h"
33#include "pipebuffer/pb_validate.h"
34
35#include "svga_winsys.h"
36#include "vmw_context.h"
37#include "vmw_screen.h"
38#include "vmw_buffer.h"
39#include "vmw_surface.h"
40#include "vmw_fence.h"
41
42#define VMW_COMMAND_SIZE (64*1024)
43#define VMW_SURFACE_RELOCS (1024)
44#define VMW_REGION_RELOCS (512)
45
46#define VMW_MUST_FLUSH_STACK 8
47
48struct vmw_region_relocation
49{
50   struct SVGAGuestPtr *where;
51   struct pb_buffer *buffer;
52   /* TODO: put offset info inside where */
53   uint32 offset;
54};
55
56struct vmw_svga_winsys_context
57{
58   struct svga_winsys_context base;
59
60   struct vmw_winsys_screen *vws;
61
62#ifdef DEBUG
63   boolean must_flush;
64   struct debug_stack_frame must_flush_stack[VMW_MUST_FLUSH_STACK];
65#endif
66
67   struct {
68      uint8_t buffer[VMW_COMMAND_SIZE];
69      uint32_t size;
70      uint32_t used;
71      uint32_t reserved;
72   } command;
73
74   struct {
75      struct vmw_svga_winsys_surface *handles[VMW_SURFACE_RELOCS];
76      uint32_t size;
77      uint32_t used;
78      uint32_t staged;
79      uint32_t reserved;
80   } surface;
81
82   struct {
83      struct vmw_region_relocation relocs[VMW_REGION_RELOCS];
84      uint32_t size;
85      uint32_t used;
86      uint32_t staged;
87      uint32_t reserved;
88   } region;
89
90   struct pb_validate *validate;
91
92   /**
93    * The amount of GMR that is referred by the commands currently batched
94    * in the context.
95    */
96   uint32_t seen_regions;
97
98   /**
99    * Whether this context should fail to reserve more commands, not because it
100    * ran out of command space, but because a substantial ammount of GMR was
101    * referred.
102    */
103   boolean preemptive_flush;
104};
105
106
107static INLINE struct vmw_svga_winsys_context *
108vmw_svga_winsys_context(struct svga_winsys_context *swc)
109{
110   assert(swc);
111   return (struct vmw_svga_winsys_context *)swc;
112}
113
114
115static INLINE unsigned
116vmw_translate_to_pb_flags(unsigned flags)
117{
118   unsigned f = 0;
119   if (flags & SVGA_RELOC_READ)
120      f |= PB_USAGE_GPU_READ;
121
122   if (flags & SVGA_RELOC_WRITE)
123      f |= PB_USAGE_GPU_WRITE;
124
125   return f;
126}
127
128static enum pipe_error
129vmw_swc_flush(struct svga_winsys_context *swc,
130              struct pipe_fence_handle **pfence)
131{
132   struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
133   struct pipe_fence_handle *fence = NULL;
134   unsigned i;
135   enum pipe_error ret;
136
137   ret = pb_validate_validate(vswc->validate);
138   assert(ret == PIPE_OK);
139   if(ret == PIPE_OK) {
140
141      /* Apply relocations */
142      for(i = 0; i < vswc->region.used; ++i) {
143         struct vmw_region_relocation *reloc = &vswc->region.relocs[i];
144         struct SVGAGuestPtr ptr;
145
146         if(!vmw_gmr_bufmgr_region_ptr(reloc->buffer, &ptr))
147            assert(0);
148
149         ptr.offset += reloc->offset;
150
151         *reloc->where = ptr;
152      }
153
154      if (vswc->command.used || pfence != NULL)
155         vmw_ioctl_command(vswc->vws,
156			   vswc->base.cid,
157			   0,
158                           vswc->command.buffer,
159                           vswc->command.used,
160                           &fence);
161
162      pb_validate_fence(vswc->validate, fence);
163   }
164
165   vswc->command.used = 0;
166   vswc->command.reserved = 0;
167
168   for(i = 0; i < vswc->surface.used + vswc->surface.staged; ++i) {
169      struct vmw_svga_winsys_surface *vsurf =
170	 vswc->surface.handles[i];
171      p_atomic_dec(&vsurf->validated);
172      vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
173   }
174
175   vswc->surface.used = 0;
176   vswc->surface.reserved = 0;
177
178   for(i = 0; i < vswc->region.used + vswc->region.staged; ++i) {
179      pb_reference(&vswc->region.relocs[i].buffer, NULL);
180   }
181
182   vswc->region.used = 0;
183   vswc->region.reserved = 0;
184
185#ifdef DEBUG
186   vswc->must_flush = FALSE;
187#endif
188   vswc->preemptive_flush = FALSE;
189   vswc->seen_regions = 0;
190
191   if(pfence)
192      vmw_fence_reference(vswc->vws, pfence, fence);
193
194   vmw_fence_reference(vswc->vws, &fence, NULL);
195
196   return ret;
197}
198
199
200static void *
201vmw_swc_reserve(struct svga_winsys_context *swc,
202                uint32_t nr_bytes, uint32_t nr_relocs )
203{
204   struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
205
206#ifdef DEBUG
207   /* Check if somebody forgot to check the previous failure */
208   if(vswc->must_flush) {
209      debug_printf("Forgot to flush:\n");
210      debug_backtrace_dump(vswc->must_flush_stack, VMW_MUST_FLUSH_STACK);
211      assert(!vswc->must_flush);
212   }
213#endif
214
215   assert(nr_bytes <= vswc->command.size);
216   if(nr_bytes > vswc->command.size)
217      return NULL;
218
219   if(vswc->preemptive_flush ||
220      vswc->command.used + nr_bytes > vswc->command.size ||
221      vswc->surface.used + nr_relocs > vswc->surface.size ||
222      vswc->region.used + nr_relocs > vswc->region.size) {
223#ifdef DEBUG
224      vswc->must_flush = TRUE;
225      debug_backtrace_capture(vswc->must_flush_stack, 1,
226                              VMW_MUST_FLUSH_STACK);
227#endif
228      return NULL;
229   }
230
231   assert(vswc->command.used + nr_bytes <= vswc->command.size);
232   assert(vswc->surface.used + nr_relocs <= vswc->surface.size);
233   assert(vswc->region.used + nr_relocs <= vswc->region.size);
234
235   vswc->command.reserved = nr_bytes;
236   vswc->surface.reserved = nr_relocs;
237   vswc->surface.staged = 0;
238   vswc->region.reserved = nr_relocs;
239   vswc->region.staged = 0;
240
241   return vswc->command.buffer + vswc->command.used;
242}
243
244
245static void
246vmw_swc_surface_relocation(struct svga_winsys_context *swc,
247                           uint32 *where,
248                           struct svga_winsys_surface *surface,
249                           unsigned flags)
250{
251   struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
252   struct vmw_svga_winsys_surface *vsurf;
253
254   if(!surface) {
255      *where = SVGA3D_INVALID_ID;
256      return;
257   }
258
259   assert(vswc->surface.staged < vswc->surface.reserved);
260
261   vsurf = vmw_svga_winsys_surface(surface);
262
263   *where = vsurf->sid;
264
265   vmw_svga_winsys_surface_reference(&vswc->surface.handles[vswc->surface.used + vswc->surface.staged], vsurf);
266   p_atomic_inc(&vsurf->validated);
267   ++vswc->surface.staged;
268}
269
270
271static void
272vmw_swc_region_relocation(struct svga_winsys_context *swc,
273                          struct SVGAGuestPtr *where,
274                          struct svga_winsys_buffer *buffer,
275                          uint32 offset,
276                          unsigned flags)
277{
278   struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
279   struct vmw_region_relocation *reloc;
280   unsigned translated_flags;
281   enum pipe_error ret;
282
283   assert(vswc->region.staged < vswc->region.reserved);
284
285   reloc = &vswc->region.relocs[vswc->region.used + vswc->region.staged];
286   reloc->where = where;
287   pb_reference(&reloc->buffer, vmw_pb_buffer(buffer));
288   reloc->offset = offset;
289
290   ++vswc->region.staged;
291
292   translated_flags = vmw_translate_to_pb_flags(flags);
293   ret = pb_validate_add_buffer(vswc->validate, reloc->buffer, translated_flags);
294   /* TODO: Update pipebuffer to reserve buffers and not fail here */
295   assert(ret == PIPE_OK);
296   (void)ret;
297
298   /*
299    * Flush preemptively the FIFO commands to keep the GMR working set within
300    * the GMR pool size.
301    *
302    * This is necessary for applications like SPECviewperf that generate huge
303    * amounts of immediate vertex data, so that we don't pile up too much of
304    * that vertex data neither in the guest nor in the host.
305    *
306    * Note that in the current implementation if a region is referred twice in
307    * a command stream, it will be accounted twice. We could detect repeated
308    * regions and count only once, but there is no incentive to do that, since
309    * regions are typically short-lived; always referred in a single command;
310    * and at the worst we just flush the commands a bit sooner, which for the
311    * SVGA virtual device it's not a performance issue since flushing commands
312    * to the FIFO won't cause flushing in the host.
313    */
314   vswc->seen_regions += reloc->buffer->size;
315   if(vswc->seen_regions >= VMW_GMR_POOL_SIZE/3)
316      vswc->preemptive_flush = TRUE;
317}
318
319
320static void
321vmw_swc_commit(struct svga_winsys_context *swc)
322{
323   struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
324
325   assert(vswc->command.reserved);
326   assert(vswc->command.used + vswc->command.reserved <= vswc->command.size);
327   vswc->command.used += vswc->command.reserved;
328   vswc->command.reserved = 0;
329
330   assert(vswc->surface.staged <= vswc->surface.reserved);
331   assert(vswc->surface.used + vswc->surface.staged <= vswc->surface.size);
332   vswc->surface.used += vswc->surface.staged;
333   vswc->surface.staged = 0;
334   vswc->surface.reserved = 0;
335
336   assert(vswc->region.staged <= vswc->region.reserved);
337   assert(vswc->region.used + vswc->region.staged <= vswc->region.size);
338   vswc->region.used += vswc->region.staged;
339   vswc->region.staged = 0;
340   vswc->region.reserved = 0;
341}
342
343
344static void
345vmw_swc_destroy(struct svga_winsys_context *swc)
346{
347   struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
348   unsigned i;
349
350   for(i = 0; i < vswc->region.used; ++i) {
351      pb_reference(&vswc->region.relocs[i].buffer, NULL);
352   }
353
354   for(i = 0; i < vswc->surface.used; ++i) {
355      p_atomic_dec(&vswc->surface.handles[i]->validated);
356      vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
357   }
358   pb_validate_destroy(vswc->validate);
359   vmw_ioctl_context_destroy(vswc->vws, swc->cid);
360   FREE(vswc);
361}
362
363
364struct svga_winsys_context *
365vmw_svga_winsys_context_create(struct svga_winsys_screen *sws)
366{
367   struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
368   struct vmw_svga_winsys_context *vswc;
369
370   vswc = CALLOC_STRUCT(vmw_svga_winsys_context);
371   if(!vswc)
372      return NULL;
373
374   vswc->base.destroy = vmw_swc_destroy;
375   vswc->base.reserve = vmw_swc_reserve;
376   vswc->base.surface_relocation = vmw_swc_surface_relocation;
377   vswc->base.region_relocation = vmw_swc_region_relocation;
378   vswc->base.commit = vmw_swc_commit;
379   vswc->base.flush = vmw_swc_flush;
380
381   vswc->base.cid = vmw_ioctl_context_create(vws);
382
383   vswc->vws = vws;
384
385   vswc->command.size = VMW_COMMAND_SIZE;
386   vswc->surface.size = VMW_SURFACE_RELOCS;
387   vswc->region.size = VMW_REGION_RELOCS;
388
389   vswc->validate = pb_validate_create();
390   if(!vswc->validate) {
391      FREE(vswc);
392      return NULL;
393   }
394
395   return &vswc->base;
396}
397