Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 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 | |||
48 | struct 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 | |||
56 | struct 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 | |||
107 | static INLINE struct vmw_svga_winsys_context * |
||
108 | vmw_svga_winsys_context(struct svga_winsys_context *swc) |
||
109 | { |
||
110 | assert(swc); |
||
111 | return (struct vmw_svga_winsys_context *)swc; |
||
112 | } |
||
113 | |||
114 | |||
115 | static INLINE unsigned |
||
116 | vmw_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 | |||
128 | static enum pipe_error |
||
129 | vmw_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 | |||
200 | static void * |
||
201 | vmw_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 | |||
245 | static void |
||
246 | vmw_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 | |||
271 | static void |
||
272 | vmw_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 | |||
320 | static void |
||
321 | vmw_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 | |||
344 | static void |
||
345 | vmw_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 | |||
364 | struct svga_winsys_context * |
||
365 | vmw_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 | }>>=>=>=>=>=>>>=>=>=>=>>>> |