xref: /linux/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h (revision 29b4a6996c244f0d360537d6a4a0996468372c17)
1 /* SPDX-License-Identifier: GPL-2.0 OR MIT */
2 /**********************************************************
3  *
4  * Copyright (c) 2021-2024 Broadcom. All Rights Reserved. The term
5  * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
6  *
7  * Permission is hereby granted, free of charge, to any person
8  * obtaining a copy of this software and associated documentation
9  * files (the "Software"), to deal in the Software without
10  * restriction, including without limitation the rights to use, copy,
11  * modify, merge, publish, distribute, sublicense, and/or sell copies
12  * of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  *
27  **********************************************************/
28 
29 #ifndef VMW_SURFACE_CACHE_H
30 #define VMW_SURFACE_CACHE_H
31 
32 #include "device_include/svga3d_surfacedefs.h"
33 
34 #include <drm/vmwgfx_drm.h>
35 
36 #define SVGA3D_FLAGS_UPPER_32(svga3d_flags) ((svga3d_flags) >> 32)
37 #define SVGA3D_FLAGS_LOWER_32(svga3d_flags) \
38 	((svga3d_flags) & ((uint64_t)U32_MAX))
39 
clamped_umul32(u32 a,u32 b)40 static inline u32 clamped_umul32(u32 a, u32 b)
41 {
42 	uint64_t tmp = (uint64_t) a*b;
43 	return (tmp > (uint64_t) ((u32) -1)) ? (u32) -1 : tmp;
44 }
45 
46 /**
47  * vmw_surface_get_desc - Look up the appropriate SVGA3dSurfaceDesc for the
48  * given format.
49  */
50 static inline const SVGA3dSurfaceDesc *
vmw_surface_get_desc(SVGA3dSurfaceFormat format)51 vmw_surface_get_desc(SVGA3dSurfaceFormat format)
52 {
53 	if (format < ARRAY_SIZE(g_SVGA3dSurfaceDescs))
54 		return &g_SVGA3dSurfaceDescs[format];
55 
56 	return &g_SVGA3dSurfaceDescs[SVGA3D_FORMAT_INVALID];
57 }
58 
59 /**
60  * vmw_surface_get_mip_size -  Given a base level size and the mip level,
61  * compute the size of the mip level.
62  */
63 static inline struct drm_vmw_size
vmw_surface_get_mip_size(struct drm_vmw_size base_level,u32 mip_level)64 vmw_surface_get_mip_size(struct drm_vmw_size base_level, u32 mip_level)
65 {
66 	struct drm_vmw_size size = {
67 		.width = max_t(u32, base_level.width >> mip_level, 1),
68 		.height = max_t(u32, base_level.height >> mip_level, 1),
69 		.depth = max_t(u32, base_level.depth >> mip_level, 1)
70 	};
71 
72 	return size;
73 }
74 
75 static inline void
vmw_surface_get_size_in_blocks(const SVGA3dSurfaceDesc * desc,const struct drm_vmw_size * pixel_size,SVGA3dSize * block_size)76 vmw_surface_get_size_in_blocks(const SVGA3dSurfaceDesc *desc,
77 				 const struct drm_vmw_size *pixel_size,
78 				 SVGA3dSize *block_size)
79 {
80 	block_size->width = __KERNEL_DIV_ROUND_UP(pixel_size->width,
81 						  desc->blockSize.width);
82 	block_size->height = __KERNEL_DIV_ROUND_UP(pixel_size->height,
83 						   desc->blockSize.height);
84 	block_size->depth = __KERNEL_DIV_ROUND_UP(pixel_size->depth,
85 						  desc->blockSize.depth);
86 }
87 
88 static inline bool
vmw_surface_is_planar_surface(const SVGA3dSurfaceDesc * desc)89 vmw_surface_is_planar_surface(const SVGA3dSurfaceDesc *desc)
90 {
91 	return (desc->blockDesc & SVGA3DBLOCKDESC_PLANAR_YUV) != 0;
92 }
93 
94 static inline u32
vmw_surface_calculate_pitch(const SVGA3dSurfaceDesc * desc,const struct drm_vmw_size * size)95 vmw_surface_calculate_pitch(const SVGA3dSurfaceDesc *desc,
96 			      const struct drm_vmw_size *size)
97 {
98 	u32 pitch;
99 	SVGA3dSize blocks;
100 
101 	vmw_surface_get_size_in_blocks(desc, size, &blocks);
102 
103 	pitch = blocks.width * desc->pitchBytesPerBlock;
104 
105 	return pitch;
106 }
107 
108 /**
109  * vmw_surface_get_image_buffer_size - Calculates image buffer size.
110  *
111  * Return the number of bytes of buffer space required to store one image of a
112  * surface, optionally using the specified pitch.
113  *
114  * If pitch is zero, it is assumed that rows are tightly packed.
115  *
116  * This function is overflow-safe. If the result would have overflowed, instead
117  * we return MAX_UINT32.
118  */
119 static inline u32
vmw_surface_get_image_buffer_size(const SVGA3dSurfaceDesc * desc,const struct drm_vmw_size * size,u32 pitch)120 vmw_surface_get_image_buffer_size(const SVGA3dSurfaceDesc *desc,
121 				    const struct drm_vmw_size *size,
122 				    u32 pitch)
123 {
124 	SVGA3dSize image_blocks;
125 	u32 slice_size, total_size;
126 
127 	vmw_surface_get_size_in_blocks(desc, size, &image_blocks);
128 
129 	if (vmw_surface_is_planar_surface(desc)) {
130 		total_size = clamped_umul32(image_blocks.width,
131 					    image_blocks.height);
132 		total_size = clamped_umul32(total_size, image_blocks.depth);
133 		total_size = clamped_umul32(total_size, desc->bytesPerBlock);
134 		return total_size;
135 	}
136 
137 	if (pitch == 0)
138 		pitch = vmw_surface_calculate_pitch(desc, size);
139 
140 	slice_size = clamped_umul32(image_blocks.height, pitch);
141 	total_size = clamped_umul32(slice_size, image_blocks.depth);
142 
143 	return total_size;
144 }
145 
146 /**
147  * vmw_surface_get_serialized_size - Get the serialized size for the image.
148  */
149 static inline u32
vmw_surface_get_serialized_size(SVGA3dSurfaceFormat format,struct drm_vmw_size base_level_size,u32 num_mip_levels,u32 num_layers)150 vmw_surface_get_serialized_size(SVGA3dSurfaceFormat format,
151 				  struct drm_vmw_size base_level_size,
152 				  u32 num_mip_levels,
153 				  u32 num_layers)
154 {
155 	const SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(format);
156 	u32 total_size = 0;
157 	u32 mip;
158 
159 	for (mip = 0; mip < num_mip_levels; mip++) {
160 		struct drm_vmw_size size =
161 			vmw_surface_get_mip_size(base_level_size, mip);
162 		total_size += vmw_surface_get_image_buffer_size(desc,
163 								  &size, 0);
164 	}
165 
166 	return total_size * num_layers;
167 }
168 
169 /**
170  * vmw_surface_get_serialized_size_extended - Returns the number of bytes
171  * required for a surface with given parameters. Support for sample count.
172  */
173 static inline u32
vmw_surface_get_serialized_size_extended(SVGA3dSurfaceFormat format,struct drm_vmw_size base_level_size,u32 num_mip_levels,u32 num_layers,u32 num_samples)174 vmw_surface_get_serialized_size_extended(SVGA3dSurfaceFormat format,
175 					   struct drm_vmw_size base_level_size,
176 					   u32 num_mip_levels,
177 					   u32 num_layers,
178 					   u32 num_samples)
179 {
180 	uint64_t total_size =
181 		vmw_surface_get_serialized_size(format,
182 						  base_level_size,
183 						  num_mip_levels,
184 						  num_layers);
185 	total_size *= max_t(u32, 1, num_samples);
186 
187 	return min_t(uint64_t, total_size, (uint64_t)U32_MAX);
188 }
189 
190 /**
191  * vmw_surface_get_pixel_offset - Compute the offset (in bytes) to a pixel
192  * in an image (or volume).
193  *
194  * @width: The image width in pixels.
195  * @height: The image height in pixels
196  */
197 static inline u32
vmw_surface_get_pixel_offset(SVGA3dSurfaceFormat format,u32 width,u32 height,u32 x,u32 y,u32 z)198 vmw_surface_get_pixel_offset(SVGA3dSurfaceFormat format,
199 			       u32 width, u32 height,
200 			       u32 x, u32 y, u32 z)
201 {
202 	const SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(format);
203 	const u32 bw = desc->blockSize.width, bh = desc->blockSize.height;
204 	const u32 bd = desc->blockSize.depth;
205 	const u32 rowstride = __KERNEL_DIV_ROUND_UP(width, bw) *
206 			      desc->bytesPerBlock;
207 	const u32 imgstride = __KERNEL_DIV_ROUND_UP(height, bh) * rowstride;
208 	const u32 offset = (z / bd * imgstride +
209 			    y / bh * rowstride +
210 			    x / bw * desc->bytesPerBlock);
211 	return offset;
212 }
213 
214 static inline u32
vmw_surface_get_image_offset(SVGA3dSurfaceFormat format,struct drm_vmw_size baseLevelSize,u32 numMipLevels,u32 face,u32 mip)215 vmw_surface_get_image_offset(SVGA3dSurfaceFormat format,
216 			       struct drm_vmw_size baseLevelSize,
217 			       u32 numMipLevels,
218 			       u32 face,
219 			       u32 mip)
220 
221 {
222 	u32 offset;
223 	u32 mipChainBytes;
224 	u32 mipChainBytesToLevel;
225 	u32 i;
226 	const SVGA3dSurfaceDesc *desc;
227 	struct drm_vmw_size mipSize;
228 	u32 bytes;
229 
230 	desc = vmw_surface_get_desc(format);
231 
232 	mipChainBytes = 0;
233 	mipChainBytesToLevel = 0;
234 	for (i = 0; i < numMipLevels; i++) {
235 		mipSize = vmw_surface_get_mip_size(baseLevelSize, i);
236 		bytes = vmw_surface_get_image_buffer_size(desc, &mipSize, 0);
237 		mipChainBytes += bytes;
238 		if (i < mip)
239 			mipChainBytesToLevel += bytes;
240 	}
241 
242 	offset = mipChainBytes * face + mipChainBytesToLevel;
243 
244 	return offset;
245 }
246 
247 
248 /**
249  * vmw_surface_is_gb_screen_target_format - Is the specified format usable as
250  *                                            a ScreenTarget?
251  *                                            (with just the GBObjects cap-bit
252  *                                             set)
253  * @format: format to queried
254  *
255  * RETURNS:
256  * true if queried format is valid for screen targets
257  */
258 static inline bool
vmw_surface_is_gb_screen_target_format(SVGA3dSurfaceFormat format)259 vmw_surface_is_gb_screen_target_format(SVGA3dSurfaceFormat format)
260 {
261 	return (format == SVGA3D_X8R8G8B8 ||
262 		format == SVGA3D_A8R8G8B8 ||
263 		format == SVGA3D_R5G6B5   ||
264 		format == SVGA3D_X1R5G5B5 ||
265 		format == SVGA3D_A1R5G5B5 ||
266 		format == SVGA3D_P8);
267 }
268 
269 
270 /**
271  * vmw_surface_is_dx_screen_target_format - Is the specified format usable as
272  *                                            a ScreenTarget?
273  *                                            (with DX10 enabled)
274  *
275  * @format: format to queried
276  *
277  * Results:
278  * true if queried format is valid for screen targets
279  */
280 static inline bool
vmw_surface_is_dx_screen_target_format(SVGA3dSurfaceFormat format)281 vmw_surface_is_dx_screen_target_format(SVGA3dSurfaceFormat format)
282 {
283 	return (format == SVGA3D_R8G8B8A8_UNORM ||
284 		format == SVGA3D_B8G8R8A8_UNORM ||
285 		format == SVGA3D_B8G8R8X8_UNORM);
286 }
287 
288 
289 /**
290  * vmw_surface_is_screen_target_format - Is the specified format usable as a
291  *                                         ScreenTarget?
292  *                                         (for some combination of caps)
293  *
294  * @format: format to queried
295  *
296  * Results:
297  * true if queried format is valid for screen targets
298  */
299 static inline bool
vmw_surface_is_screen_target_format(SVGA3dSurfaceFormat format)300 vmw_surface_is_screen_target_format(SVGA3dSurfaceFormat format)
301 {
302 	if (vmw_surface_is_gb_screen_target_format(format)) {
303 		return true;
304 	}
305 	return vmw_surface_is_dx_screen_target_format(format);
306 }
307 
308 /**
309  * struct vmw_surface_mip - Mimpmap level information
310  * @bytes: Bytes required in the backing store of this mipmap level.
311  * @img_stride: Byte stride per image.
312  * @row_stride: Byte stride per block row.
313  * @size: The size of the mipmap.
314  */
315 struct vmw_surface_mip {
316 	size_t bytes;
317 	size_t img_stride;
318 	size_t row_stride;
319 	struct drm_vmw_size size;
320 
321 };
322 
323 /**
324  * struct vmw_surface_cache - Cached surface information
325  * @desc: Pointer to the surface descriptor
326  * @mip: Array of mipmap level information. Valid size is @num_mip_levels.
327  * @mip_chain_bytes: Bytes required in the backing store for the whole chain
328  * of mip levels.
329  * @sheet_bytes: Bytes required in the backing store for a sheet
330  * representing a single sample.
331  * @num_mip_levels: Valid size of the @mip array. Number of mipmap levels in
332  * a chain.
333  * @num_layers: Number of slices in an array texture or number of faces in
334  * a cubemap texture.
335  */
336 struct vmw_surface_cache {
337 	const SVGA3dSurfaceDesc *desc;
338 	struct vmw_surface_mip mip[DRM_VMW_MAX_MIP_LEVELS];
339 	size_t mip_chain_bytes;
340 	size_t sheet_bytes;
341 	u32 num_mip_levels;
342 	u32 num_layers;
343 };
344 
345 /**
346  * struct vmw_surface_loc - Surface location
347  * @sheet: The multisample sheet.
348  * @sub_resource: Surface subresource. Defined as layer * num_mip_levels +
349  * mip_level.
350  * @x: X coordinate.
351  * @y: Y coordinate.
352  * @z: Z coordinate.
353  */
354 struct vmw_surface_loc {
355 	u32 sheet;
356 	u32 sub_resource;
357 	u32 x, y, z;
358 };
359 
360 /**
361  * vmw_surface_subres - Compute the subresource from layer and mipmap.
362  * @cache: Surface layout data.
363  * @mip_level: The mipmap level.
364  * @layer: The surface layer (face or array slice).
365  *
366  * Return: The subresource.
367  */
vmw_surface_subres(const struct vmw_surface_cache * cache,u32 mip_level,u32 layer)368 static inline u32 vmw_surface_subres(const struct vmw_surface_cache *cache,
369 				       u32 mip_level, u32 layer)
370 {
371 	return cache->num_mip_levels * layer + mip_level;
372 }
373 
374 /**
375  * vmw_surface_setup_cache - Build a surface cache entry
376  * @size: The surface base level dimensions.
377  * @format: The surface format.
378  * @num_mip_levels: Number of mipmap levels.
379  * @num_layers: Number of layers.
380  * @cache: Pointer to a struct vmw_surface_cach object to be filled in.
381  *
382  * Return: Zero on success, -EINVAL on invalid surface layout.
383  */
vmw_surface_setup_cache(const struct drm_vmw_size * size,SVGA3dSurfaceFormat format,u32 num_mip_levels,u32 num_layers,u32 num_samples,struct vmw_surface_cache * cache)384 static inline int vmw_surface_setup_cache(const struct drm_vmw_size *size,
385 					    SVGA3dSurfaceFormat format,
386 					    u32 num_mip_levels,
387 					    u32 num_layers,
388 					    u32 num_samples,
389 					    struct vmw_surface_cache *cache)
390 {
391 	const SVGA3dSurfaceDesc *desc;
392 	u32 i;
393 
394 	memset(cache, 0, sizeof(*cache));
395 	cache->desc = desc = vmw_surface_get_desc(format);
396 	cache->num_mip_levels = num_mip_levels;
397 	cache->num_layers = num_layers;
398 	for (i = 0; i < cache->num_mip_levels; i++) {
399 		struct vmw_surface_mip *mip = &cache->mip[i];
400 
401 		mip->size = vmw_surface_get_mip_size(*size, i);
402 		mip->bytes = vmw_surface_get_image_buffer_size
403 			(desc, &mip->size, 0);
404 		mip->row_stride =
405 			__KERNEL_DIV_ROUND_UP(mip->size.width,
406 					      desc->blockSize.width) *
407 			desc->bytesPerBlock * num_samples;
408 		if (!mip->row_stride)
409 			goto invalid_dim;
410 
411 		mip->img_stride =
412 			__KERNEL_DIV_ROUND_UP(mip->size.height,
413 					      desc->blockSize.height) *
414 			mip->row_stride;
415 		if (!mip->img_stride)
416 			goto invalid_dim;
417 
418 		cache->mip_chain_bytes += mip->bytes;
419 	}
420 	cache->sheet_bytes = cache->mip_chain_bytes * num_layers;
421 	if (!cache->sheet_bytes)
422 		goto invalid_dim;
423 
424 	return 0;
425 
426 invalid_dim:
427 	VMW_DEBUG_USER("Invalid surface layout for dirty tracking.\n");
428 	return -EINVAL;
429 }
430 
431 /**
432  * vmw_surface_get_loc - Get a surface location from an offset into the
433  * backing store
434  * @cache: Surface layout data.
435  * @loc: Pointer to a struct vmw_surface_loc to be filled in.
436  * @offset: Offset into the surface backing store.
437  */
438 static inline void
vmw_surface_get_loc(const struct vmw_surface_cache * cache,struct vmw_surface_loc * loc,size_t offset)439 vmw_surface_get_loc(const struct vmw_surface_cache *cache,
440 		      struct vmw_surface_loc *loc,
441 		      size_t offset)
442 {
443 	const struct vmw_surface_mip *mip = &cache->mip[0];
444 	const SVGA3dSurfaceDesc *desc = cache->desc;
445 	u32 layer;
446 	int i;
447 
448 	loc->sheet = offset / cache->sheet_bytes;
449 	offset -= loc->sheet * cache->sheet_bytes;
450 
451 	layer = offset / cache->mip_chain_bytes;
452 	offset -= layer * cache->mip_chain_bytes;
453 	for (i = 0; i < cache->num_mip_levels; ++i, ++mip) {
454 		if (mip->bytes > offset)
455 			break;
456 		offset -= mip->bytes;
457 	}
458 
459 	loc->sub_resource = vmw_surface_subres(cache, i, layer);
460 	loc->z = offset / mip->img_stride;
461 	offset -= loc->z * mip->img_stride;
462 	loc->z *= desc->blockSize.depth;
463 	loc->y = offset / mip->row_stride;
464 	offset -= loc->y * mip->row_stride;
465 	loc->y *= desc->blockSize.height;
466 	loc->x = offset / desc->bytesPerBlock;
467 	loc->x *= desc->blockSize.width;
468 }
469 
470 /**
471  * vmw_surface_inc_loc - Clamp increment a surface location with one block
472  * size
473  * in each dimension.
474  * @loc: Pointer to a struct vmw_surface_loc to be incremented.
475  *
476  * When computing the size of a range as size = end - start, the range does not
477  * include the end element. However a location representing the last byte
478  * of a touched region in the backing store *is* included in the range.
479  * This function modifies such a location to match the end definition
480  * given as start + size which is the one used in a SVGA3dBox.
481  */
482 static inline void
vmw_surface_inc_loc(const struct vmw_surface_cache * cache,struct vmw_surface_loc * loc)483 vmw_surface_inc_loc(const struct vmw_surface_cache *cache,
484 		      struct vmw_surface_loc *loc)
485 {
486 	const SVGA3dSurfaceDesc *desc = cache->desc;
487 	u32 mip = loc->sub_resource % cache->num_mip_levels;
488 	const struct drm_vmw_size *size = &cache->mip[mip].size;
489 
490 	loc->sub_resource++;
491 	loc->x += desc->blockSize.width;
492 	if (loc->x > size->width)
493 		loc->x = size->width;
494 	loc->y += desc->blockSize.height;
495 	if (loc->y > size->height)
496 		loc->y = size->height;
497 	loc->z += desc->blockSize.depth;
498 	if (loc->z > size->depth)
499 		loc->z = size->depth;
500 }
501 
502 /**
503  * vmw_surface_min_loc - The start location in a subresource
504  * @cache: Surface layout data.
505  * @sub_resource: The subresource.
506  * @loc: Pointer to a struct vmw_surface_loc to be filled in.
507  */
508 static inline void
vmw_surface_min_loc(const struct vmw_surface_cache * cache,u32 sub_resource,struct vmw_surface_loc * loc)509 vmw_surface_min_loc(const struct vmw_surface_cache *cache,
510 		      u32 sub_resource,
511 		      struct vmw_surface_loc *loc)
512 {
513 	loc->sheet = 0;
514 	loc->sub_resource = sub_resource;
515 	loc->x = loc->y = loc->z = 0;
516 }
517 
518 /**
519  * vmw_surface_min_loc - The end location in a subresource
520  * @cache: Surface layout data.
521  * @sub_resource: The subresource.
522  * @loc: Pointer to a struct vmw_surface_loc to be filled in.
523  *
524  * Following the end definition given in vmw_surface_inc_loc(),
525  * Compute the end location of a surface subresource.
526  */
527 static inline void
vmw_surface_max_loc(const struct vmw_surface_cache * cache,u32 sub_resource,struct vmw_surface_loc * loc)528 vmw_surface_max_loc(const struct vmw_surface_cache *cache,
529 		      u32 sub_resource,
530 		      struct vmw_surface_loc *loc)
531 {
532 	const struct drm_vmw_size *size;
533 	u32 mip;
534 
535 	loc->sheet = 0;
536 	loc->sub_resource = sub_resource + 1;
537 	mip = sub_resource % cache->num_mip_levels;
538 	size = &cache->mip[mip].size;
539 	loc->x = size->width;
540 	loc->y = size->height;
541 	loc->z = size->depth;
542 }
543 
544 
545 #endif /* VMW_SURFACE_CACHE_H */
546