Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/auxiliary/util/u_helpers.c
4561 views
1
/**************************************************************************
2
*
3
* Copyright 2012 Marek Olšák <[email protected]>
4
* All Rights Reserved.
5
*
6
* Permission is hereby granted, free of charge, to any person obtaining a
7
* copy of this software and associated documentation files (the
8
* "Software"), to deal in the Software without restriction, including
9
* without limitation the rights to use, copy, modify, merge, publish,
10
* distribute, sub license, and/or sell copies of the Software, and to
11
* permit persons to whom the Software is furnished to do so, subject to
12
* the following conditions:
13
*
14
* The above copyright notice and this permission notice (including the
15
* next paragraph) shall be included in all copies or substantial portions
16
* of the Software.
17
*
18
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21
* IN NO EVENT SHALL THE AUTHORS AND/OR THEIR SUPPLIERS BE LIABLE FOR
22
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
*
26
**************************************************************************/
27
28
#include "util/u_cpu_detect.h"
29
#include "util/u_helpers.h"
30
#include "util/u_inlines.h"
31
#include "util/u_upload_mgr.h"
32
#include "util/u_thread.h"
33
#include "util/os_time.h"
34
#include <inttypes.h>
35
36
/**
37
* This function is used to copy an array of pipe_vertex_buffer structures,
38
* while properly referencing the pipe_vertex_buffer::buffer member.
39
*
40
* enabled_buffers is updated such that the bits corresponding to the indices
41
* of disabled buffers are set to 0 and the enabled ones are set to 1.
42
*
43
* \sa util_copy_framebuffer_state
44
*/
45
void util_set_vertex_buffers_mask(struct pipe_vertex_buffer *dst,
46
uint32_t *enabled_buffers,
47
const struct pipe_vertex_buffer *src,
48
unsigned start_slot, unsigned count,
49
unsigned unbind_num_trailing_slots,
50
bool take_ownership)
51
{
52
unsigned i;
53
uint32_t bitmask = 0;
54
55
dst += start_slot;
56
57
*enabled_buffers &= ~u_bit_consecutive(start_slot, count);
58
59
if (src) {
60
for (i = 0; i < count; i++) {
61
if (src[i].buffer.resource)
62
bitmask |= 1 << i;
63
64
pipe_vertex_buffer_unreference(&dst[i]);
65
66
if (!take_ownership && !src[i].is_user_buffer)
67
pipe_resource_reference(&dst[i].buffer.resource, src[i].buffer.resource);
68
}
69
70
/* Copy over the other members of pipe_vertex_buffer. */
71
memcpy(dst, src, count * sizeof(struct pipe_vertex_buffer));
72
73
*enabled_buffers |= bitmask << start_slot;
74
}
75
else {
76
/* Unreference the buffers. */
77
for (i = 0; i < count; i++)
78
pipe_vertex_buffer_unreference(&dst[i]);
79
}
80
81
for (i = 0; i < unbind_num_trailing_slots; i++)
82
pipe_vertex_buffer_unreference(&dst[count + i]);
83
}
84
85
/**
86
* Same as util_set_vertex_buffers_mask, but it only returns the number
87
* of bound buffers.
88
*/
89
void util_set_vertex_buffers_count(struct pipe_vertex_buffer *dst,
90
unsigned *dst_count,
91
const struct pipe_vertex_buffer *src,
92
unsigned start_slot, unsigned count,
93
unsigned unbind_num_trailing_slots,
94
bool take_ownership)
95
{
96
unsigned i;
97
uint32_t enabled_buffers = 0;
98
99
for (i = 0; i < *dst_count; i++) {
100
if (dst[i].buffer.resource)
101
enabled_buffers |= (1ull << i);
102
}
103
104
util_set_vertex_buffers_mask(dst, &enabled_buffers, src, start_slot,
105
count, unbind_num_trailing_slots,
106
take_ownership);
107
108
*dst_count = util_last_bit(enabled_buffers);
109
}
110
111
/**
112
* This function is used to copy an array of pipe_shader_buffer structures,
113
* while properly referencing the pipe_shader_buffer::buffer member.
114
*
115
* \sa util_set_vertex_buffer_mask
116
*/
117
void util_set_shader_buffers_mask(struct pipe_shader_buffer *dst,
118
uint32_t *enabled_buffers,
119
const struct pipe_shader_buffer *src,
120
unsigned start_slot, unsigned count)
121
{
122
unsigned i;
123
124
dst += start_slot;
125
126
if (src) {
127
for (i = 0; i < count; i++) {
128
pipe_resource_reference(&dst[i].buffer, src[i].buffer);
129
130
if (src[i].buffer)
131
*enabled_buffers |= (1ull << (start_slot + i));
132
else
133
*enabled_buffers &= ~(1ull << (start_slot + i));
134
}
135
136
/* Copy over the other members of pipe_shader_buffer. */
137
memcpy(dst, src, count * sizeof(struct pipe_shader_buffer));
138
}
139
else {
140
/* Unreference the buffers. */
141
for (i = 0; i < count; i++)
142
pipe_resource_reference(&dst[i].buffer, NULL);
143
144
*enabled_buffers &= ~(((1ull << count) - 1) << start_slot);
145
}
146
}
147
148
/**
149
* Given a user index buffer, save the structure to "saved", and upload it.
150
*/
151
bool
152
util_upload_index_buffer(struct pipe_context *pipe,
153
const struct pipe_draw_info *info,
154
const struct pipe_draw_start_count_bias *draw,
155
struct pipe_resource **out_buffer,
156
unsigned *out_offset, unsigned alignment)
157
{
158
unsigned start_offset = draw->start * info->index_size;
159
160
u_upload_data(pipe->stream_uploader, start_offset,
161
draw->count * info->index_size, alignment,
162
(char*)info->index.user + start_offset,
163
out_offset, out_buffer);
164
u_upload_unmap(pipe->stream_uploader);
165
*out_offset -= start_offset;
166
return *out_buffer != NULL;
167
}
168
169
/* This is a helper for hardware bring-up. Don't remove. */
170
struct pipe_query *
171
util_begin_pipestat_query(struct pipe_context *ctx)
172
{
173
struct pipe_query *q =
174
ctx->create_query(ctx, PIPE_QUERY_PIPELINE_STATISTICS, 0);
175
if (!q)
176
return NULL;
177
178
ctx->begin_query(ctx, q);
179
return q;
180
}
181
182
/* This is a helper for hardware bring-up. Don't remove. */
183
void
184
util_end_pipestat_query(struct pipe_context *ctx, struct pipe_query *q,
185
FILE *f)
186
{
187
static unsigned counter;
188
struct pipe_query_data_pipeline_statistics stats;
189
190
ctx->end_query(ctx, q);
191
ctx->get_query_result(ctx, q, true, (void*)&stats);
192
ctx->destroy_query(ctx, q);
193
194
fprintf(f,
195
"Draw call %u:\n"
196
" ia_vertices = %"PRIu64"\n"
197
" ia_primitives = %"PRIu64"\n"
198
" vs_invocations = %"PRIu64"\n"
199
" gs_invocations = %"PRIu64"\n"
200
" gs_primitives = %"PRIu64"\n"
201
" c_invocations = %"PRIu64"\n"
202
" c_primitives = %"PRIu64"\n"
203
" ps_invocations = %"PRIu64"\n"
204
" hs_invocations = %"PRIu64"\n"
205
" ds_invocations = %"PRIu64"\n"
206
" cs_invocations = %"PRIu64"\n",
207
(unsigned)p_atomic_inc_return(&counter),
208
stats.ia_vertices,
209
stats.ia_primitives,
210
stats.vs_invocations,
211
stats.gs_invocations,
212
stats.gs_primitives,
213
stats.c_invocations,
214
stats.c_primitives,
215
stats.ps_invocations,
216
stats.hs_invocations,
217
stats.ds_invocations,
218
stats.cs_invocations);
219
}
220
221
/* This is a helper for profiling. Don't remove. */
222
struct pipe_query *
223
util_begin_time_query(struct pipe_context *ctx)
224
{
225
struct pipe_query *q =
226
ctx->create_query(ctx, PIPE_QUERY_TIME_ELAPSED, 0);
227
if (!q)
228
return NULL;
229
230
ctx->begin_query(ctx, q);
231
return q;
232
}
233
234
/* This is a helper for profiling. Don't remove. */
235
void
236
util_end_time_query(struct pipe_context *ctx, struct pipe_query *q, FILE *f,
237
const char *name)
238
{
239
union pipe_query_result result;
240
241
ctx->end_query(ctx, q);
242
ctx->get_query_result(ctx, q, true, &result);
243
ctx->destroy_query(ctx, q);
244
245
fprintf(f, "Time elapsed: %s - %"PRIu64".%u us\n", name, result.u64 / 1000, (unsigned)(result.u64 % 1000) / 100);
246
}
247
248
/* This is a helper for hardware bring-up. Don't remove. */
249
void
250
util_wait_for_idle(struct pipe_context *ctx)
251
{
252
struct pipe_fence_handle *fence = NULL;
253
254
ctx->flush(ctx, &fence, 0);
255
ctx->screen->fence_finish(ctx->screen, NULL, fence, PIPE_TIMEOUT_INFINITE);
256
}
257
258
void
259
util_throttle_init(struct util_throttle *t, uint64_t max_mem_usage)
260
{
261
t->max_mem_usage = max_mem_usage;
262
}
263
264
void
265
util_throttle_deinit(struct pipe_screen *screen, struct util_throttle *t)
266
{
267
for (unsigned i = 0; i < ARRAY_SIZE(t->ring); i++)
268
screen->fence_reference(screen, &t->ring[i].fence, NULL);
269
}
270
271
static uint64_t
272
util_get_throttle_total_memory_usage(struct util_throttle *t)
273
{
274
uint64_t total_usage = 0;
275
276
for (unsigned i = 0; i < ARRAY_SIZE(t->ring); i++)
277
total_usage += t->ring[i].mem_usage;
278
return total_usage;
279
}
280
281
static void util_dump_throttle_ring(struct util_throttle *t)
282
{
283
printf("Throttle:\n");
284
for (unsigned i = 0; i < ARRAY_SIZE(t->ring); i++) {
285
printf(" ring[%u]: fence = %s, mem_usage = %"PRIu64"%s%s\n",
286
i, t->ring[i].fence ? "yes" : " no",
287
t->ring[i].mem_usage,
288
t->flush_index == i ? " [flush]" : "",
289
t->wait_index == i ? " [wait]" : "");
290
}
291
}
292
293
/**
294
* Notify util_throttle that the next operation allocates memory.
295
* util_throttle tracks memory usage and waits for fences until its tracked
296
* memory usage decreases.
297
*
298
* Example:
299
* util_throttle_memory_usage(..., w*h*d*Bpp);
300
* TexSubImage(..., w, h, d, ...);
301
*
302
* This means that TexSubImage can't allocate more memory its maximum limit
303
* set during initialization.
304
*/
305
void
306
util_throttle_memory_usage(struct pipe_context *pipe,
307
struct util_throttle *t, uint64_t memory_size)
308
{
309
(void)util_dump_throttle_ring; /* silence warning */
310
311
if (!t->max_mem_usage)
312
return;
313
314
struct pipe_screen *screen = pipe->screen;
315
struct pipe_fence_handle **fence = NULL;
316
unsigned ring_size = ARRAY_SIZE(t->ring);
317
uint64_t total = util_get_throttle_total_memory_usage(t);
318
319
/* If there is not enough memory, walk the list of fences and find
320
* the latest one that we need to wait for.
321
*/
322
while (t->wait_index != t->flush_index &&
323
total && total + memory_size > t->max_mem_usage) {
324
assert(t->ring[t->wait_index].fence);
325
326
/* Release an older fence if we need to wait for a newer one. */
327
if (fence)
328
screen->fence_reference(screen, fence, NULL);
329
330
fence = &t->ring[t->wait_index].fence;
331
t->ring[t->wait_index].mem_usage = 0;
332
t->wait_index = (t->wait_index + 1) % ring_size;
333
334
total = util_get_throttle_total_memory_usage(t);
335
}
336
337
/* Wait for the fence to decrease memory usage. */
338
if (fence) {
339
screen->fence_finish(screen, pipe, *fence, PIPE_TIMEOUT_INFINITE);
340
screen->fence_reference(screen, fence, NULL);
341
}
342
343
/* Flush and get a fence if we've exhausted memory usage for the current
344
* slot.
345
*/
346
if (t->ring[t->flush_index].mem_usage &&
347
t->ring[t->flush_index].mem_usage + memory_size >
348
t->max_mem_usage / (ring_size / 2)) {
349
struct pipe_fence_handle **fence =
350
&t->ring[t->flush_index].fence;
351
352
/* Expect that the current flush slot doesn't have a fence yet. */
353
assert(!*fence);
354
355
pipe->flush(pipe, fence, PIPE_FLUSH_ASYNC);
356
t->flush_index = (t->flush_index + 1) % ring_size;
357
358
/* Vacate the next slot if it's occupied. This should be rare. */
359
if (t->flush_index == t->wait_index) {
360
struct pipe_fence_handle **fence =
361
&t->ring[t->wait_index].fence;
362
363
t->ring[t->wait_index].mem_usage = 0;
364
t->wait_index = (t->wait_index + 1) % ring_size;
365
366
assert(*fence);
367
screen->fence_finish(screen, pipe, *fence, PIPE_TIMEOUT_INFINITE);
368
screen->fence_reference(screen, fence, NULL);
369
}
370
371
assert(!t->ring[t->flush_index].mem_usage);
372
assert(!t->ring[t->flush_index].fence);
373
}
374
375
t->ring[t->flush_index].mem_usage += memory_size;
376
}
377
378
bool
379
util_lower_clearsize_to_dword(const void *clearValue, int *clearValueSize, uint32_t *clamped)
380
{
381
/* Reduce a large clear value size if possible. */
382
if (*clearValueSize > 4) {
383
bool clear_dword_duplicated = true;
384
const uint32_t *clear_value = clearValue;
385
386
/* See if we can lower large fills to dword fills. */
387
for (unsigned i = 1; i < *clearValueSize / 4; i++) {
388
if (clear_value[0] != clear_value[i]) {
389
clear_dword_duplicated = false;
390
break;
391
}
392
}
393
if (clear_dword_duplicated) {
394
*clamped = *clear_value;
395
*clearValueSize = 4;
396
}
397
return clear_dword_duplicated;
398
}
399
400
/* Expand a small clear value size. */
401
if (*clearValueSize <= 2) {
402
if (*clearValueSize == 1) {
403
*clamped = *(uint8_t *)clearValue;
404
*clamped |=
405
(*clamped << 8) | (*clamped << 16) | (*clamped << 24);
406
} else {
407
*clamped = *(uint16_t *)clearValue;
408
*clamped |= *clamped << 16;
409
}
410
*clearValueSize = 4;
411
return true;
412
}
413
return false;
414
}
415
416