Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/drivers/svga/svga_resource_buffer.c
4570 views
1
/**********************************************************
2
* Copyright 2008-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
#include "svga_cmd.h"
27
28
#include "pipe/p_state.h"
29
#include "pipe/p_defines.h"
30
#include "util/u_inlines.h"
31
#include "os/os_thread.h"
32
#include "util/u_math.h"
33
#include "util/u_memory.h"
34
#include "util/u_resource.h"
35
36
#include "svga_context.h"
37
#include "svga_screen.h"
38
#include "svga_resource_buffer.h"
39
#include "svga_resource_buffer_upload.h"
40
#include "svga_resource_texture.h"
41
#include "svga_sampler_view.h"
42
#include "svga_winsys.h"
43
#include "svga_debug.h"
44
45
46
/**
47
* Determine what buffers eventually need hardware backing.
48
*
49
* Vertex- and index buffers need hardware backing. Constant buffers
50
* do on vgpu10. Staging texture-upload buffers do when they are
51
* supported.
52
*/
53
static inline boolean
54
svga_buffer_needs_hw_storage(const struct svga_screen *ss,
55
const struct pipe_resource *template)
56
{
57
unsigned bind_mask = (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER |
58
PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_STREAM_OUTPUT |
59
PIPE_BIND_SHADER_BUFFER | PIPE_BIND_COMMAND_ARGS_BUFFER);
60
61
if (ss->sws->have_vgpu10) {
62
/*
63
* Driver-created upload const0- and staging texture upload buffers
64
* tagged with PIPE_BIND_CUSTOM
65
*/
66
bind_mask |= PIPE_BIND_CUSTOM;
67
/**
68
* Uniform buffer objects.
69
* Don't create hardware storage for state-tracker constant buffers,
70
* because we frequently map them for reading and writing, and
71
* the length of those buffers are always small, so it is better
72
* to just use system memory.
73
*/
74
}
75
76
if (template->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT)
77
return TRUE;
78
79
return !!(template->bind & bind_mask);
80
}
81
82
/**
83
* Create a buffer transfer.
84
*
85
* Unlike texture DMAs (which are written immediately to the command buffer and
86
* therefore inherently serialized with other context operations), for buffers
87
* we try to coalesce multiple range mappings (i.e, multiple calls to this
88
* function) into a single DMA command, for better efficiency in command
89
* processing. This means we need to exercise extra care here to ensure that
90
* the end result is exactly the same as if one DMA was used for every mapped
91
* range.
92
*/
93
void *
94
svga_buffer_transfer_map(struct pipe_context *pipe,
95
struct pipe_resource *resource,
96
unsigned level,
97
unsigned usage,
98
const struct pipe_box *box,
99
struct pipe_transfer **ptransfer)
100
{
101
struct svga_context *svga = svga_context(pipe);
102
struct svga_screen *ss = svga_screen(pipe->screen);
103
struct svga_buffer *sbuf = svga_buffer(resource);
104
struct pipe_transfer *transfer;
105
uint8_t *map = NULL;
106
int64_t begin = svga_get_time(svga);
107
108
SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_BUFFERTRANSFERMAP);
109
110
assert(box->y == 0);
111
assert(box->z == 0);
112
assert(box->height == 1);
113
assert(box->depth == 1);
114
115
transfer = MALLOC_STRUCT(pipe_transfer);
116
if (!transfer) {
117
goto done;
118
}
119
120
transfer->resource = resource;
121
transfer->level = level;
122
transfer->usage = usage;
123
transfer->box = *box;
124
transfer->stride = 0;
125
transfer->layer_stride = 0;
126
127
if (usage & PIPE_MAP_WRITE) {
128
/* If we write to the buffer for any reason, free any saved translated
129
* vertices.
130
*/
131
pipe_resource_reference(&sbuf->translated_indices.buffer, NULL);
132
}
133
134
if ((usage & PIPE_MAP_READ) && sbuf->dirty &&
135
!sbuf->key.coherent && !svga->swc->force_coherent) {
136
137
/* Host-side buffers can only be dirtied with vgpu10 features
138
* (streamout and buffer copy).
139
*/
140
assert(svga_have_vgpu10(svga));
141
142
if (!sbuf->user) {
143
(void) svga_buffer_handle(svga, resource, sbuf->bind_flags);
144
}
145
146
if (sbuf->dma.pending) {
147
svga_buffer_upload_flush(svga, sbuf);
148
svga_context_finish(svga);
149
}
150
151
assert(sbuf->handle);
152
153
SVGA_RETRY(svga, SVGA3D_vgpu10_ReadbackSubResource(svga->swc,
154
sbuf->handle, 0));
155
svga->hud.num_readbacks++;
156
157
svga_context_finish(svga);
158
159
sbuf->dirty = FALSE;
160
}
161
162
if (usage & PIPE_MAP_WRITE) {
163
if ((usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) &&
164
!(resource->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT)) {
165
/*
166
* Flush any pending primitives, finish writing any pending DMA
167
* commands, and tell the host to discard the buffer contents on
168
* the next DMA operation.
169
*/
170
171
svga_hwtnl_flush_buffer(svga, resource);
172
173
if (sbuf->dma.pending) {
174
svga_buffer_upload_flush(svga, sbuf);
175
176
/*
177
* Instead of flushing the context command buffer, simply discard
178
* the current hwbuf, and start a new one.
179
* With GB objects, the map operation takes care of this
180
* if passed the PIPE_MAP_DISCARD_WHOLE_RESOURCE flag,
181
* and the old backing store is busy.
182
*/
183
184
if (!svga_have_gb_objects(svga))
185
svga_buffer_destroy_hw_storage(ss, sbuf);
186
}
187
188
sbuf->map.num_ranges = 0;
189
sbuf->dma.flags.discard = TRUE;
190
}
191
192
if (usage & PIPE_MAP_UNSYNCHRONIZED) {
193
if (!sbuf->map.num_ranges) {
194
/*
195
* No pending ranges to upload so far, so we can tell the host to
196
* not synchronize on the next DMA command.
197
*/
198
199
sbuf->dma.flags.unsynchronized = TRUE;
200
}
201
} else {
202
/*
203
* Synchronizing, so flush any pending primitives, finish writing any
204
* pending DMA command, and ensure the next DMA will be done in order.
205
*/
206
207
svga_hwtnl_flush_buffer(svga, resource);
208
209
if (sbuf->dma.pending) {
210
svga_buffer_upload_flush(svga, sbuf);
211
212
if (svga_buffer_has_hw_storage(sbuf)) {
213
/*
214
* We have a pending DMA upload from a hardware buffer, therefore
215
* we need to ensure that the host finishes processing that DMA
216
* command before the gallium frontend can start overwriting the
217
* hardware buffer.
218
*
219
* XXX: This could be avoided by tying the hardware buffer to
220
* the transfer (just as done with textures), which would allow
221
* overlapping DMAs commands to be queued on the same context
222
* buffer. However, due to the likelihood of software vertex
223
* processing, it is more convenient to hold on to the hardware
224
* buffer, allowing to quickly access the contents from the CPU
225
* without having to do a DMA download from the host.
226
*/
227
228
if (usage & PIPE_MAP_DONTBLOCK) {
229
/*
230
* Flushing the command buffer here will most likely cause
231
* the map of the hwbuf below to block, so preemptively
232
* return NULL here if DONTBLOCK is set to prevent unnecessary
233
* command buffer flushes.
234
*/
235
236
FREE(transfer);
237
goto done;
238
}
239
240
svga_context_flush(svga, NULL);
241
}
242
}
243
244
sbuf->dma.flags.unsynchronized = FALSE;
245
}
246
}
247
248
if (!sbuf->swbuf && !svga_buffer_has_hw_storage(sbuf)) {
249
if (svga_buffer_create_hw_storage(ss, sbuf, sbuf->bind_flags) != PIPE_OK) {
250
/*
251
* We can't create a hardware buffer big enough, so create a malloc
252
* buffer instead.
253
*/
254
if (0) {
255
debug_printf("%s: failed to allocate %u KB of DMA, "
256
"splitting DMA transfers\n",
257
__FUNCTION__,
258
(sbuf->b.width0 + 1023)/1024);
259
}
260
261
sbuf->swbuf = align_malloc(sbuf->b.width0, 16);
262
if (!sbuf->swbuf) {
263
FREE(transfer);
264
goto done;
265
}
266
}
267
}
268
269
if (sbuf->swbuf) {
270
/* User/malloc buffer */
271
map = sbuf->swbuf;
272
}
273
else if (svga_buffer_has_hw_storage(sbuf)) {
274
boolean retry;
275
276
map = SVGA_TRY_MAP(svga_buffer_hw_storage_map
277
(svga, sbuf, transfer->usage, &retry), retry);
278
if (map == NULL && retry) {
279
/*
280
* At this point, svga_buffer_get_transfer() has already
281
* hit the DISCARD_WHOLE_RESOURCE path and flushed HWTNL
282
* for this buffer.
283
*/
284
svga_retry_enter(svga);
285
svga_context_flush(svga, NULL);
286
map = svga_buffer_hw_storage_map(svga, sbuf, transfer->usage, &retry);
287
svga_retry_exit(svga);
288
}
289
}
290
else {
291
map = NULL;
292
}
293
294
if (map) {
295
++sbuf->map.count;
296
map += transfer->box.x;
297
*ptransfer = transfer;
298
} else {
299
FREE(transfer);
300
}
301
302
svga->hud.map_buffer_time += (svga_get_time(svga) - begin);
303
304
done:
305
SVGA_STATS_TIME_POP(svga_sws(svga));
306
return map;
307
}
308
309
310
void
311
svga_buffer_transfer_flush_region(struct pipe_context *pipe,
312
struct pipe_transfer *transfer,
313
const struct pipe_box *box)
314
{
315
struct svga_screen *ss = svga_screen(pipe->screen);
316
struct svga_buffer *sbuf = svga_buffer(transfer->resource);
317
struct svga_context *svga = svga_context(pipe);
318
unsigned offset = transfer->box.x + box->x;
319
unsigned length = box->width;
320
321
assert(transfer->usage & PIPE_MAP_WRITE);
322
assert(transfer->usage & PIPE_MAP_FLUSH_EXPLICIT);
323
324
if (!(svga->swc->force_coherent || sbuf->key.coherent) || sbuf->swbuf) {
325
mtx_lock(&ss->swc_mutex);
326
svga_buffer_add_range(sbuf, offset, offset + length);
327
mtx_unlock(&ss->swc_mutex);
328
}
329
}
330
331
332
void
333
svga_buffer_transfer_unmap(struct pipe_context *pipe,
334
struct pipe_transfer *transfer)
335
{
336
struct svga_screen *ss = svga_screen(pipe->screen);
337
struct svga_context *svga = svga_context(pipe);
338
struct svga_buffer *sbuf = svga_buffer(transfer->resource);
339
340
SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_BUFFERTRANSFERUNMAP);
341
342
mtx_lock(&ss->swc_mutex);
343
344
assert(sbuf->map.count);
345
if (sbuf->map.count) {
346
--sbuf->map.count;
347
}
348
349
if (svga_buffer_has_hw_storage(sbuf)) {
350
351
/* Note: we may wind up flushing here and unmapping other buffers
352
* which leads to recursively locking ss->swc_mutex.
353
*/
354
svga_buffer_hw_storage_unmap(svga, sbuf);
355
}
356
357
if (transfer->usage & PIPE_MAP_WRITE) {
358
if (!(transfer->usage & PIPE_MAP_FLUSH_EXPLICIT)) {
359
/*
360
* Mapped range not flushed explicitly, so flush the whole buffer,
361
* and tell the host to discard the contents when processing the DMA
362
* command.
363
*/
364
365
SVGA_DBG(DEBUG_DMA, "flushing the whole buffer\n");
366
367
sbuf->dma.flags.discard = TRUE;
368
369
if (!(svga->swc->force_coherent || sbuf->key.coherent) || sbuf->swbuf)
370
svga_buffer_add_range(sbuf, 0, sbuf->b.width0);
371
}
372
373
if (sbuf->swbuf &&
374
(!sbuf->bind_flags || (sbuf->bind_flags & PIPE_BIND_CONSTANT_BUFFER))) {
375
/*
376
* Since the constant buffer is in system buffer, we need
377
* to set the constant buffer dirty bits, so that the context
378
* can update the changes in the device.
379
* According to the GL spec, buffer bound to other contexts will
380
* have to be explicitly rebound by the user to have the changes take
381
* into effect.
382
*/
383
svga->dirty |= SVGA_NEW_CONST_BUFFER;
384
}
385
}
386
387
mtx_unlock(&ss->swc_mutex);
388
FREE(transfer);
389
SVGA_STATS_TIME_POP(svga_sws(svga));
390
}
391
392
393
void
394
svga_resource_destroy(struct pipe_screen *screen,
395
struct pipe_resource *buf)
396
{
397
if (buf->target == PIPE_BUFFER) {
398
struct svga_screen *ss = svga_screen(screen);
399
struct svga_buffer *sbuf = svga_buffer(buf);
400
401
assert(!p_atomic_read(&buf->reference.count));
402
403
assert(!sbuf->dma.pending);
404
405
if (sbuf->handle)
406
svga_buffer_destroy_host_surface(ss, sbuf);
407
408
if (sbuf->uploaded.buffer)
409
pipe_resource_reference(&sbuf->uploaded.buffer, NULL);
410
411
if (sbuf->hwbuf)
412
svga_buffer_destroy_hw_storage(ss, sbuf);
413
414
if (sbuf->swbuf && !sbuf->user)
415
align_free(sbuf->swbuf);
416
417
pipe_resource_reference(&sbuf->translated_indices.buffer, NULL);
418
419
ss->hud.total_resource_bytes -= sbuf->size;
420
assert(ss->hud.num_resources > 0);
421
if (ss->hud.num_resources > 0)
422
ss->hud.num_resources--;
423
424
FREE(sbuf);
425
} else {
426
struct svga_screen *ss = svga_screen(screen);
427
struct svga_texture *tex = svga_texture(buf);
428
429
ss->texture_timestamp++;
430
431
svga_sampler_view_reference(&tex->cached_view, NULL);
432
433
/*
434
DBG("%s deleting %p\n", __FUNCTION__, (void *) tex);
435
*/
436
SVGA_DBG(DEBUG_DMA, "unref sid %p (texture)\n", tex->handle);
437
svga_screen_surface_destroy(ss, &tex->key, &tex->handle);
438
439
/* Destroy the backed surface handle if exists */
440
if (tex->backed_handle)
441
svga_screen_surface_destroy(ss, &tex->backed_key, &tex->backed_handle);
442
443
ss->hud.total_resource_bytes -= tex->size;
444
445
FREE(tex->defined);
446
FREE(tex->rendered_to);
447
FREE(tex->dirty);
448
FREE(tex);
449
450
assert(ss->hud.num_resources > 0);
451
if (ss->hud.num_resources > 0)
452
ss->hud.num_resources--;
453
}
454
}
455
456
struct pipe_resource *
457
svga_buffer_create(struct pipe_screen *screen,
458
const struct pipe_resource *template)
459
{
460
struct svga_screen *ss = svga_screen(screen);
461
struct svga_buffer *sbuf;
462
unsigned bind_flags;
463
464
SVGA_STATS_TIME_PUSH(ss->sws, SVGA_STATS_TIME_CREATEBUFFER);
465
466
sbuf = CALLOC_STRUCT(svga_buffer);
467
if (!sbuf)
468
goto error1;
469
470
sbuf->b = *template;
471
pipe_reference_init(&sbuf->b.reference, 1);
472
sbuf->b.screen = screen;
473
bind_flags = template->bind & ~PIPE_BIND_CUSTOM;
474
475
list_inithead(&sbuf->surfaces);
476
477
if (bind_flags & PIPE_BIND_CONSTANT_BUFFER) {
478
/* Constant buffers can only have the PIPE_BIND_CONSTANT_BUFFER
479
* flag set.
480
*/
481
if (ss->sws->have_vgpu10) {
482
bind_flags = PIPE_BIND_CONSTANT_BUFFER;
483
}
484
}
485
486
/* Although svga device only requires constant buffer size to be
487
* in multiples of 16, in order to allow bind_flags promotion,
488
* we are mandating all buffer size to be in multiples of 16.
489
*/
490
sbuf->b.width0 = align(sbuf->b.width0, 16);
491
492
if (svga_buffer_needs_hw_storage(ss, template)) {
493
494
/* If the buffer is not used for constant buffer, set
495
* the vertex/index bind flags as well so that the buffer will be
496
* accepted for those uses.
497
* Note that the PIPE_BIND_ flags we get from the gallium frontend are
498
* just a hint about how the buffer may be used. And OpenGL buffer
499
* object may be used for many different things.
500
* Also note that we do not unconditionally set the streamout
501
* bind flag since streamout buffer is an output buffer and
502
* might have performance implication.
503
*/
504
if (!(template->bind & PIPE_BIND_CONSTANT_BUFFER) &&
505
!(template->bind & PIPE_BIND_CUSTOM)) {
506
/* Not a constant- or staging buffer.
507
* The buffer may be used for vertex data or indexes.
508
*/
509
bind_flags |= (PIPE_BIND_VERTEX_BUFFER |
510
PIPE_BIND_INDEX_BUFFER);
511
512
/* It may be used for shader resource as well. */
513
bind_flags |= PIPE_BIND_SAMPLER_VIEW;
514
}
515
516
if (svga_buffer_create_host_surface(ss, sbuf, bind_flags) != PIPE_OK)
517
goto error2;
518
}
519
else {
520
sbuf->swbuf = align_malloc(sbuf->b.width0, 64);
521
if (!sbuf->swbuf)
522
goto error2;
523
524
/* Since constant buffer is usually small, it is much cheaper to
525
* use system memory for the data just as it is being done for
526
* the default constant buffer.
527
*/
528
if ((bind_flags & PIPE_BIND_CONSTANT_BUFFER) || !bind_flags)
529
sbuf->use_swbuf = TRUE;
530
}
531
532
debug_reference(&sbuf->b.reference,
533
(debug_reference_descriptor)debug_describe_resource, 0);
534
535
sbuf->bind_flags = bind_flags;
536
sbuf->size = util_resource_size(&sbuf->b);
537
ss->hud.total_resource_bytes += sbuf->size;
538
539
ss->hud.num_resources++;
540
SVGA_STATS_TIME_POP(ss->sws);
541
542
return &sbuf->b;
543
544
error2:
545
FREE(sbuf);
546
error1:
547
SVGA_STATS_TIME_POP(ss->sws);
548
return NULL;
549
}
550
551
552
struct pipe_resource *
553
svga_user_buffer_create(struct pipe_screen *screen,
554
void *ptr,
555
unsigned bytes,
556
unsigned bind)
557
{
558
struct svga_buffer *sbuf;
559
struct svga_screen *ss = svga_screen(screen);
560
561
sbuf = CALLOC_STRUCT(svga_buffer);
562
if (!sbuf)
563
goto no_sbuf;
564
565
pipe_reference_init(&sbuf->b.reference, 1);
566
sbuf->b.screen = screen;
567
sbuf->b.format = PIPE_FORMAT_R8_UNORM; /* ?? */
568
sbuf->b.usage = PIPE_USAGE_IMMUTABLE;
569
sbuf->b.bind = bind;
570
sbuf->b.width0 = bytes;
571
sbuf->b.height0 = 1;
572
sbuf->b.depth0 = 1;
573
sbuf->b.array_size = 1;
574
575
sbuf->bind_flags = bind;
576
sbuf->swbuf = ptr;
577
sbuf->user = TRUE;
578
579
debug_reference(&sbuf->b.reference,
580
(debug_reference_descriptor)debug_describe_resource, 0);
581
582
ss->hud.num_resources++;
583
584
return &sbuf->b;
585
586
no_sbuf:
587
return NULL;
588
}
589
590