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