1 /**********************************************************
2 * Copyright 2009 VMware, Inc. All rights reserved.
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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
24 **********************************************************/
29 #include "util/u_debug.h"
30 #include "util/u_memory.h"
31 #include "util/u_debug_stack.h"
32 #include "util/u_debug_flush.h"
33 #include "util/u_hash_table.h"
34 #include "pipebuffer/pb_buffer.h"
35 #include "pipebuffer/pb_validate.h"
37 #include "svga_winsys.h"
38 #include "vmw_context.h"
39 #include "vmw_screen.h"
40 #include "vmw_buffer.h"
41 #include "vmw_surface.h"
42 #include "vmw_fence.h"
43 #include "vmw_shader.h"
45 #define VMW_COMMAND_SIZE (64*1024)
46 #define VMW_SURFACE_RELOCS (1024)
47 #define VMW_SHADER_RELOCS (1024)
48 #define VMW_REGION_RELOCS (512)
50 #define VMW_MUST_FLUSH_STACK 8
53 * A factor applied to the maximum mob memory size to determine
54 * the optimial time to preemptively flush the command buffer.
55 * The constant is based on some performance trials with SpecViewperf.
57 #define VMW_MAX_MOB_MEM_FACTOR 2
60 * A factor applied to the maximum surface memory size to determine
61 * the optimial time to preemptively flush the command buffer.
62 * The constant is based on some performance trials with SpecViewperf.
64 #define VMW_MAX_SURF_MEM_FACTOR 2
67 struct vmw_buffer_relocation
69 struct pb_buffer
*buffer
;
75 struct SVGAGuestPtr
*where
;
79 uint32
*offset_into_mob
;
84 struct vmw_ctx_validate_item
{
86 struct vmw_svga_winsys_surface
*vsurf
;
87 struct vmw_svga_winsys_shader
*vshader
;
92 struct vmw_svga_winsys_context
94 struct svga_winsys_context base
;
96 struct vmw_winsys_screen
*vws
;
97 struct util_hash_table
*hash
;
101 struct debug_stack_frame must_flush_stack
[VMW_MUST_FLUSH_STACK
];
102 struct debug_flush_ctx
*fctx
;
106 uint8_t buffer
[VMW_COMMAND_SIZE
];
113 struct vmw_ctx_validate_item items
[VMW_SURFACE_RELOCS
];
121 struct vmw_buffer_relocation relocs
[VMW_REGION_RELOCS
];
129 struct vmw_ctx_validate_item items
[VMW_SHADER_RELOCS
];
136 struct pb_validate
*validate
;
139 * The amount of surface, GMR or MOB memory that is referred by the commands
140 * currently batched in the context command buffer.
142 uint64_t seen_surfaces
;
143 uint64_t seen_regions
;
147 * Whether this context should fail to reserve more commands, not because it
148 * ran out of command space, but because a substantial ammount of GMR was
151 boolean preemptive_flush
;
155 static INLINE
struct vmw_svga_winsys_context
*
156 vmw_svga_winsys_context(struct svga_winsys_context
*swc
)
159 return (struct vmw_svga_winsys_context
*)swc
;
163 static INLINE
unsigned
164 vmw_translate_to_pb_flags(unsigned flags
)
167 if (flags
& SVGA_RELOC_READ
)
168 f
|= PB_USAGE_GPU_READ
;
170 if (flags
& SVGA_RELOC_WRITE
)
171 f
|= PB_USAGE_GPU_WRITE
;
176 static enum pipe_error
177 vmw_swc_flush(struct svga_winsys_context
*swc
,
178 struct pipe_fence_handle
**pfence
)
180 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
181 struct pipe_fence_handle
*fence
= NULL
;
185 ret
= pb_validate_validate(vswc
->validate
);
186 assert(ret
== PIPE_OK
);
189 /* Apply relocations */
190 for(i
= 0; i
< vswc
->region
.used
; ++i
) {
191 struct vmw_buffer_relocation
*reloc
= &vswc
->region
.relocs
[i
];
192 struct SVGAGuestPtr ptr
;
194 if(!vmw_gmr_bufmgr_region_ptr(reloc
->buffer
, &ptr
))
197 ptr
.offset
+= reloc
->offset
;
201 *reloc
->mob
.id
= ptr
.gmrId
;
202 if (reloc
->mob
.offset_into_mob
)
203 *reloc
->mob
.offset_into_mob
= ptr
.offset
;
205 assert(ptr
.offset
== 0);
208 *reloc
->region
.where
= ptr
;
211 if (vswc
->command
.used
|| pfence
!= NULL
)
212 vmw_ioctl_command(vswc
->vws
,
215 vswc
->command
.buffer
,
219 pb_validate_fence(vswc
->validate
, fence
);
222 vswc
->command
.used
= 0;
223 vswc
->command
.reserved
= 0;
225 for(i
= 0; i
< vswc
->surface
.used
+ vswc
->surface
.staged
; ++i
) {
226 struct vmw_ctx_validate_item
*isurf
= &vswc
->surface
.items
[i
];
227 if (isurf
->referenced
)
228 p_atomic_dec(&isurf
->vsurf
->validated
);
229 vmw_svga_winsys_surface_reference(&isurf
->vsurf
, NULL
);
232 util_hash_table_clear(vswc
->hash
);
233 vswc
->surface
.used
= 0;
234 vswc
->surface
.reserved
= 0;
236 for(i
= 0; i
< vswc
->shader
.used
+ vswc
->shader
.staged
; ++i
) {
237 struct vmw_ctx_validate_item
*ishader
= &vswc
->shader
.items
[i
];
238 if (ishader
->referenced
)
239 p_atomic_dec(&ishader
->vshader
->validated
);
240 vmw_svga_winsys_shader_reference(&ishader
->vshader
, NULL
);
243 vswc
->shader
.used
= 0;
244 vswc
->shader
.reserved
= 0;
246 vswc
->region
.used
= 0;
247 vswc
->region
.reserved
= 0;
250 vswc
->must_flush
= FALSE
;
251 debug_flush_flush(vswc
->fctx
);
253 vswc
->preemptive_flush
= FALSE
;
254 vswc
->seen_surfaces
= 0;
255 vswc
->seen_regions
= 0;
259 vmw_fence_reference(vswc
->vws
, pfence
, fence
);
261 vmw_fence_reference(vswc
->vws
, &fence
, NULL
);
268 vmw_swc_reserve(struct svga_winsys_context
*swc
,
269 uint32_t nr_bytes
, uint32_t nr_relocs
)
271 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
274 /* Check if somebody forgot to check the previous failure */
275 if(vswc
->must_flush
) {
276 debug_printf("Forgot to flush:\n");
277 debug_backtrace_dump(vswc
->must_flush_stack
, VMW_MUST_FLUSH_STACK
);
278 assert(!vswc
->must_flush
);
280 debug_flush_might_flush(vswc
->fctx
);
283 assert(nr_bytes
<= vswc
->command
.size
);
284 if(nr_bytes
> vswc
->command
.size
)
287 if(vswc
->preemptive_flush
||
288 vswc
->command
.used
+ nr_bytes
> vswc
->command
.size
||
289 vswc
->surface
.used
+ nr_relocs
> vswc
->surface
.size
||
290 vswc
->shader
.used
+ nr_relocs
> vswc
->shader
.size
||
291 vswc
->region
.used
+ nr_relocs
> vswc
->region
.size
) {
293 vswc
->must_flush
= TRUE
;
294 debug_backtrace_capture(vswc
->must_flush_stack
, 1,
295 VMW_MUST_FLUSH_STACK
);
300 assert(vswc
->command
.used
+ nr_bytes
<= vswc
->command
.size
);
301 assert(vswc
->surface
.used
+ nr_relocs
<= vswc
->surface
.size
);
302 assert(vswc
->shader
.used
+ nr_relocs
<= vswc
->shader
.size
);
303 assert(vswc
->region
.used
+ nr_relocs
<= vswc
->region
.size
);
305 vswc
->command
.reserved
= nr_bytes
;
306 vswc
->surface
.reserved
= nr_relocs
;
307 vswc
->surface
.staged
= 0;
308 vswc
->shader
.reserved
= nr_relocs
;
309 vswc
->shader
.staged
= 0;
310 vswc
->region
.reserved
= nr_relocs
;
311 vswc
->region
.staged
= 0;
313 return vswc
->command
.buffer
+ vswc
->command
.used
;
317 vmw_swc_context_relocation(struct svga_winsys_context
*swc
,
324 vmw_swc_add_validate_buffer(struct vmw_svga_winsys_context
*vswc
,
325 struct pb_buffer
*pb_buf
,
329 unsigned translated_flags
;
332 * TODO: Update pb_validate to provide a similar functionality
333 * (Check buffer already present before adding)
335 if (util_hash_table_get(vswc
->hash
, pb_buf
) != pb_buf
) {
336 translated_flags
= vmw_translate_to_pb_flags(flags
);
337 ret
= pb_validate_add_buffer(vswc
->validate
, pb_buf
, translated_flags
);
338 /* TODO: Update pipebuffer to reserve buffers and not fail here */
339 assert(ret
== PIPE_OK
);
341 (void)util_hash_table_set(vswc
->hash
, pb_buf
, pb_buf
);
349 vmw_swc_region_relocation(struct svga_winsys_context
*swc
,
350 struct SVGAGuestPtr
*where
,
351 struct svga_winsys_buffer
*buffer
,
355 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
356 struct vmw_buffer_relocation
*reloc
;
358 assert(vswc
->region
.staged
< vswc
->region
.reserved
);
360 reloc
= &vswc
->region
.relocs
[vswc
->region
.used
+ vswc
->region
.staged
];
361 reloc
->region
.where
= where
;
364 * pb_validate holds a refcount to the buffer, so no need to
365 * refcount it again in the relocation.
367 reloc
->buffer
= vmw_pb_buffer(buffer
);
368 reloc
->offset
= offset
;
369 reloc
->is_mob
= FALSE
;
370 ++vswc
->region
.staged
;
372 if (vmw_swc_add_validate_buffer(vswc
, reloc
->buffer
, flags
)) {
373 vswc
->seen_regions
+= reloc
->buffer
->size
;
374 if(vswc
->seen_regions
>= VMW_GMR_POOL_SIZE
/5)
375 vswc
->preemptive_flush
= TRUE
;
379 if (!(flags
& SVGA_RELOC_INTERNAL
))
380 debug_flush_cb_reference(vswc
->fctx
, vmw_debug_flush_buf(buffer
));
385 vmw_swc_mob_relocation(struct svga_winsys_context
*swc
,
387 uint32
*offset_into_mob
,
388 struct svga_winsys_buffer
*buffer
,
392 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
393 struct vmw_buffer_relocation
*reloc
;
395 assert(vswc
->region
.staged
< vswc
->region
.reserved
);
397 reloc
= &vswc
->region
.relocs
[vswc
->region
.used
+ vswc
->region
.staged
];
399 reloc
->mob
.offset_into_mob
= offset_into_mob
;
402 * pb_validate holds a refcount to the buffer, so no need to
403 * refcount it again in the relocation.
405 reloc
->buffer
= vmw_pb_buffer(buffer
);
406 reloc
->offset
= offset
;
407 reloc
->is_mob
= TRUE
;
408 ++vswc
->region
.staged
;
410 if (vmw_swc_add_validate_buffer(vswc
, reloc
->buffer
, flags
)) {
411 vswc
->seen_mobs
+= reloc
->buffer
->size
;
412 /* divide by 5, tested for best performance */
413 if (vswc
->seen_mobs
>= vswc
->vws
->ioctl
.max_mob_memory
/ VMW_MAX_MOB_MEM_FACTOR
)
414 vswc
->preemptive_flush
= TRUE
;
418 if (!(flags
& SVGA_RELOC_INTERNAL
))
419 debug_flush_cb_reference(vswc
->fctx
, vmw_debug_flush_buf(buffer
));
425 * vmw_swc_surface_clear_reference - Clear referenced info for a surface
427 * @swc: Pointer to an svga_winsys_context
428 * @vsurf: Pointer to a vmw_svga_winsys_surface, the referenced info of which
431 * This is primarily used by a discard surface map to indicate that the
432 * surface data is no longer referenced by a draw call, and mapping it
433 * should therefore no longer cause a flush.
436 vmw_swc_surface_clear_reference(struct svga_winsys_context
*swc
,
437 struct vmw_svga_winsys_surface
*vsurf
)
439 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
440 struct vmw_ctx_validate_item
*isrf
=
441 util_hash_table_get(vswc
->hash
, vsurf
);
443 if (isrf
&& isrf
->referenced
) {
444 isrf
->referenced
= FALSE
;
445 p_atomic_dec(&vsurf
->validated
);
450 vmw_swc_surface_only_relocation(struct svga_winsys_context
*swc
,
452 struct vmw_svga_winsys_surface
*vsurf
,
455 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
456 struct vmw_ctx_validate_item
*isrf
;
458 assert(vswc
->surface
.staged
< vswc
->surface
.reserved
);
459 isrf
= util_hash_table_get(vswc
->hash
, vsurf
);
462 isrf
= &vswc
->surface
.items
[vswc
->surface
.used
+ vswc
->surface
.staged
];
463 vmw_svga_winsys_surface_reference(&isrf
->vsurf
, vsurf
);
464 isrf
->referenced
= FALSE
;
466 * Note that a failure here may just fall back to unhashed behavior
467 * and potentially cause unnecessary flushing, so ignore the
470 (void) util_hash_table_set(vswc
->hash
, vsurf
, isrf
);
471 ++vswc
->surface
.staged
;
473 vswc
->seen_surfaces
+= vsurf
->size
;
474 /* divide by 5 not well tuned for performance */
475 if (vswc
->seen_surfaces
>= vswc
->vws
->ioctl
.max_surface_memory
/ VMW_MAX_SURF_MEM_FACTOR
)
476 vswc
->preemptive_flush
= TRUE
;
479 if (!(flags
& SVGA_RELOC_INTERNAL
) && !isrf
->referenced
) {
480 isrf
->referenced
= TRUE
;
481 p_atomic_inc(&vsurf
->validated
);
488 vmw_swc_surface_relocation(struct svga_winsys_context
*swc
,
491 struct svga_winsys_surface
*surface
,
494 struct vmw_svga_winsys_surface
*vsurf
;
496 assert(swc
->have_gb_objects
|| mobid
== NULL
);
499 *where
= SVGA3D_INVALID_ID
;
501 *mobid
= SVGA3D_INVALID_ID
;
505 vsurf
= vmw_svga_winsys_surface(surface
);
506 vmw_swc_surface_only_relocation(swc
, where
, vsurf
, flags
);
508 if (swc
->have_gb_objects
&& vsurf
->buf
!= NULL
) {
511 * Make sure backup buffer ends up fenced.
514 pipe_mutex_lock(vsurf
->mutex
);
515 assert(vsurf
->buf
!= NULL
);
517 vmw_swc_mob_relocation(swc
, mobid
, NULL
, (struct svga_winsys_buffer
*)
518 vsurf
->buf
, 0, flags
);
519 pipe_mutex_unlock(vsurf
->mutex
);
524 vmw_swc_shader_relocation(struct svga_winsys_context
*swc
,
528 struct svga_winsys_gb_shader
*shader
)
530 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
531 struct vmw_svga_winsys_shader
*vshader
;
532 struct vmw_ctx_validate_item
*ishader
;
534 *shid
= SVGA3D_INVALID_ID
;
538 assert(vswc
->shader
.staged
< vswc
->shader
.reserved
);
539 vshader
= vmw_svga_winsys_shader(shader
);
540 ishader
= util_hash_table_get(vswc
->hash
, vshader
);
542 if (ishader
== NULL
) {
543 ishader
= &vswc
->shader
.items
[vswc
->shader
.used
+ vswc
->shader
.staged
];
544 vmw_svga_winsys_shader_reference(&ishader
->vshader
, vshader
);
545 ishader
->referenced
= FALSE
;
547 * Note that a failure here may just fall back to unhashed behavior
548 * and potentially cause unnecessary flushing, so ignore the
551 (void) util_hash_table_set(vswc
->hash
, vshader
, ishader
);
552 ++vswc
->shader
.staged
;
555 if (!ishader
->referenced
) {
556 ishader
->referenced
= TRUE
;
557 p_atomic_inc(&vshader
->validated
);
560 *shid
= vshader
->shid
;
562 if (mobid
!= NULL
&& vshader
->buf
)
563 vmw_swc_mob_relocation(swc
, mobid
, offset
, vshader
->buf
,
568 vmw_swc_commit(struct svga_winsys_context
*swc
)
570 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
572 assert(vswc
->command
.reserved
);
573 assert(vswc
->command
.used
+ vswc
->command
.reserved
<= vswc
->command
.size
);
574 vswc
->command
.used
+= vswc
->command
.reserved
;
575 vswc
->command
.reserved
= 0;
577 assert(vswc
->surface
.staged
<= vswc
->surface
.reserved
);
578 assert(vswc
->surface
.used
+ vswc
->surface
.staged
<= vswc
->surface
.size
);
579 vswc
->surface
.used
+= vswc
->surface
.staged
;
580 vswc
->surface
.staged
= 0;
581 vswc
->surface
.reserved
= 0;
583 assert(vswc
->shader
.staged
<= vswc
->shader
.reserved
);
584 assert(vswc
->shader
.used
+ vswc
->shader
.staged
<= vswc
->shader
.size
);
585 vswc
->shader
.used
+= vswc
->shader
.staged
;
586 vswc
->shader
.staged
= 0;
587 vswc
->shader
.reserved
= 0;
589 assert(vswc
->region
.staged
<= vswc
->region
.reserved
);
590 assert(vswc
->region
.used
+ vswc
->region
.staged
<= vswc
->region
.size
);
591 vswc
->region
.used
+= vswc
->region
.staged
;
592 vswc
->region
.staged
= 0;
593 vswc
->region
.reserved
= 0;
598 vmw_swc_destroy(struct svga_winsys_context
*swc
)
600 struct vmw_svga_winsys_context
*vswc
= vmw_svga_winsys_context(swc
);
603 for(i
= 0; i
< vswc
->surface
.used
; ++i
) {
604 struct vmw_ctx_validate_item
*isurf
= &vswc
->surface
.items
[i
];
605 if (isurf
->referenced
)
606 p_atomic_dec(&isurf
->vsurf
->validated
);
607 vmw_svga_winsys_surface_reference(&isurf
->vsurf
, NULL
);
610 for(i
= 0; i
< vswc
->shader
.used
; ++i
) {
611 struct vmw_ctx_validate_item
*ishader
= &vswc
->shader
.items
[i
];
612 if (ishader
->referenced
)
613 p_atomic_dec(&ishader
->vshader
->validated
);
614 vmw_svga_winsys_shader_reference(&ishader
->vshader
, NULL
);
617 util_hash_table_destroy(vswc
->hash
);
618 pb_validate_destroy(vswc
->validate
);
619 vmw_ioctl_context_destroy(vswc
->vws
, swc
->cid
);
621 debug_flush_ctx_destroy(vswc
->fctx
);
626 static unsigned vmw_hash_ptr(void *p
)
628 return (unsigned)(unsigned long)p
;
631 static int vmw_ptr_compare(void *key1
, void *key2
)
633 return (key1
== key2
) ? 0 : 1;
636 struct svga_winsys_context
*
637 vmw_svga_winsys_context_create(struct svga_winsys_screen
*sws
)
639 struct vmw_winsys_screen
*vws
= vmw_winsys_screen(sws
);
640 struct vmw_svga_winsys_context
*vswc
;
642 vswc
= CALLOC_STRUCT(vmw_svga_winsys_context
);
646 vswc
->base
.destroy
= vmw_swc_destroy
;
647 vswc
->base
.reserve
= vmw_swc_reserve
;
648 vswc
->base
.surface_relocation
= vmw_swc_surface_relocation
;
649 vswc
->base
.region_relocation
= vmw_swc_region_relocation
;
650 vswc
->base
.mob_relocation
= vmw_swc_mob_relocation
;
651 vswc
->base
.context_relocation
= vmw_swc_context_relocation
;
652 vswc
->base
.shader_relocation
= vmw_swc_shader_relocation
;
653 vswc
->base
.commit
= vmw_swc_commit
;
654 vswc
->base
.flush
= vmw_swc_flush
;
655 vswc
->base
.surface_map
= vmw_svga_winsys_surface_map
;
656 vswc
->base
.surface_unmap
= vmw_svga_winsys_surface_unmap
;
658 vswc
->base
.cid
= vmw_ioctl_context_create(vws
);
659 vswc
->base
.have_gb_objects
= sws
->have_gb_objects
;
663 vswc
->command
.size
= VMW_COMMAND_SIZE
;
664 vswc
->surface
.size
= VMW_SURFACE_RELOCS
;
665 vswc
->shader
.size
= VMW_SHADER_RELOCS
;
666 vswc
->region
.size
= VMW_REGION_RELOCS
;
668 vswc
->validate
= pb_validate_create();
670 goto out_no_validate
;
672 vswc
->hash
= util_hash_table_create(vmw_hash_ptr
, vmw_ptr_compare
);
677 vswc
->fctx
= debug_flush_ctx_create(TRUE
, VMW_DEBUG_FLUSH_STACK
);
683 pb_validate_destroy(vswc
->validate
);