/* * Mesa 3-D graphics library * * Copyright (C) 2012-2013 LunarG, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Chia-I Wu */ #include "ilo_screen.h" #include "ilo_resource.h" /* use PIPE_BIND_CUSTOM to indicate MCS */ #define ILO_BIND_MCS PIPE_BIND_CUSTOM struct tex_layout { const struct ilo_dev_info *dev; const struct pipe_resource *templ; enum pipe_format format; unsigned block_width, block_height, block_size; bool compressed; bool has_depth, has_stencil; enum intel_tiling_mode tiling; bool can_be_linear; bool array_spacing_full; bool interleaved; struct { int w, h, d; struct ilo_texture_slice *slices; } levels[PIPE_MAX_TEXTURE_LEVELS]; int align_i, align_j; int qpitch; int width, height; }; static void tex_layout_init_qpitch(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; int h0, h1; if (templ->array_size <= 1) return; h0 = align(layout->levels[0].h, layout->align_j); if (!layout->array_spacing_full) { layout->qpitch = h0; return; } h1 = align(layout->levels[1].h, layout->align_j); /* * From the Sandy Bridge PRM, volume 1 part 1, page 115: * * "The following equation is used for surface formats other than * compressed textures: * * QPitch = (h0 + h1 + 11j)" * * "The equation for compressed textures (BC* and FXT1 surface formats) * follows: * * QPitch = (h0 + h1 + 11j) / 4" * * "[DevSNB] Errata: Sampler MSAA Qpitch will be 4 greater than the * value calculated in the equation above, for every other odd Surface * Height starting from 1 i.e. 1,5,9,13" * * From the Ivy Bridge PRM, volume 1 part 1, page 111-112: * * "If Surface Array Spacing is set to ARYSPC_FULL (note that the depth * buffer and stencil buffer have an implied value of ARYSPC_FULL): * * QPitch = (h0 + h1 + 12j) * QPitch = (h0 + h1 + 12j) / 4 (compressed) * * (There are many typos or missing words here...)" * * To access the N-th slice, an offset of (Stride * QPitch * N) is added to * the base address. The PRM divides QPitch by 4 for compressed formats * because the block height for those formats are 4, and it wants QPitch to * mean the number of memory rows, as opposed to texel rows, between * slices. Since we use texel rows in tex->slice_offsets, we do not need * to divide QPitch by 4. */ layout->qpitch = h0 + h1 + ((layout->dev->gen >= ILO_GEN(7)) ? 12 : 11) * layout->align_j; if (layout->dev->gen == ILO_GEN(6) && templ->nr_samples > 1 && templ->height0 % 4 == 1) layout->qpitch += 4; } static void tex_layout_init_alignments(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; /* * From the Sandy Bridge PRM, volume 1 part 1, page 113: * * "surface format align_i align_j * YUV 4:2:2 formats 4 *see below * BC1-5 4 4 * FXT1 8 4 * all other formats 4 *see below" * * "- align_j = 4 for any depth buffer * - align_j = 2 for separate stencil buffer * - align_j = 4 for any render target surface is multisampled (4x) * - align_j = 4 for any render target surface with Surface Vertical * Alignment = VALIGN_4 * - align_j = 2 for any render target surface with Surface Vertical * Alignment = VALIGN_2 * - align_j = 2 for all other render target surface * - align_j = 2 for any sampling engine surface with Surface Vertical * Alignment = VALIGN_2 * - align_j = 4 for any sampling engine surface with Surface Vertical * Alignment = VALIGN_4" * * From the Sandy Bridge PRM, volume 4 part 1, page 86: * * "This field (Surface Vertical Alignment) must be set to VALIGN_2 if * the Surface Format is 96 bits per element (BPE)." * * They can be rephrased as * * align_i align_j * compressed formats block width block height * PIPE_FORMAT_S8_UINT 4 2 * other depth/stencil formats 4 4 * 4x multisampled 4 4 * bpp 96 4 2 * others 4 2 or 4 */ /* * From the Ivy Bridge PRM, volume 1 part 1, page 110: * * "surface defined by surface format align_i align_j * 3DSTATE_DEPTH_BUFFER D16_UNORM 8 4 * not D16_UNORM 4 4 * 3DSTATE_STENCIL_BUFFER N/A 8 8 * SURFACE_STATE BC*, ETC*, EAC* 4 4 * FXT1 8 4 * all others (set by SURFACE_STATE)" * * From the Ivy Bridge PRM, volume 4 part 1, page 63: * * "- This field (Surface Vertical Aligment) is intended to be set to * VALIGN_4 if the surface was rendered as a depth buffer, for a * multisampled (4x) render target, or for a multisampled (8x) * render target, since these surfaces support only alignment of 4. * - Use of VALIGN_4 for other surfaces is supported, but uses more * memory. * - This field must be set to VALIGN_4 for all tiled Y Render Target * surfaces. * - Value of 1 is not supported for format YCRCB_NORMAL (0x182), * YCRCB_SWAPUVY (0x183), YCRCB_SWAPUV (0x18f), YCRCB_SWAPY (0x190) * - If Number of Multisamples is not MULTISAMPLECOUNT_1, this field * must be set to VALIGN_4." * - VALIGN_4 is not supported for surface format R32G32B32_FLOAT." * * "- This field (Surface Horizontal Aligment) is intended to be set to * HALIGN_8 only if the surface was rendered as a depth buffer with * Z16 format or a stencil buffer, since these surfaces support only * alignment of 8. * - Use of HALIGN_8 for other surfaces is supported, but uses more * memory. * - This field must be set to HALIGN_4 if the Surface Format is BC*. * - This field must be set to HALIGN_8 if the Surface Format is * FXT1." * * They can be rephrased as * * align_i align_j * compressed formats block width block height * PIPE_FORMAT_Z16_UNORM 8 4 * PIPE_FORMAT_S8_UINT 8 8 * other depth/stencil formats 4 or 8 4 * 2x or 4x multisampled 4 or 8 4 * tiled Y 4 or 8 4 (if rt) * PIPE_FORMAT_R32G32B32_FLOAT 4 or 8 2 * others 4 or 8 2 or 4 */ if (layout->compressed) { /* this happens to be the case */ layout->align_i = layout->block_width; layout->align_j = layout->block_height; } else if (layout->has_depth || layout->has_stencil) { if (layout->dev->gen >= ILO_GEN(7)) { switch (layout->format) { case PIPE_FORMAT_Z16_UNORM: layout->align_i = 8; layout->align_j = 4; break; case PIPE_FORMAT_S8_UINT: layout->align_i = 8; layout->align_j = 8; break; default: /* * From the Ivy Bridge PRM, volume 2 part 1, page 319: * * "The 3 LSBs of both offsets (Depth Coordinate Offset Y and * Depth Coordinate Offset X) must be zero to ensure correct * alignment" * * We will make use of them and setting align_i to 8 help us meet * the requirement. */ layout->align_i = (templ->last_level > 0) ? 8 : 4; layout->align_j = 4; break; } } else { switch (layout->format) { case PIPE_FORMAT_S8_UINT: layout->align_i = 4; layout->align_j = 2; break; default: layout->align_i = 4; layout->align_j = 4; break; } } } else { const bool valign_4 = (templ->nr_samples > 1) || (layout->dev->gen >= ILO_GEN(7) && layout->tiling == INTEL_TILING_Y && (templ->bind & PIPE_BIND_RENDER_TARGET)); if (valign_4) assert(layout->block_size != 12); layout->align_i = 4; layout->align_j = (valign_4) ? 4 : 2; } /* * the fact that align i and j are multiples of block width and height * respectively is what makes the size of the bo a multiple of the block * size, slices start at block boundaries, and many of the computations * work. */ assert(layout->align_i % layout->block_width == 0); assert(layout->align_j % layout->block_height == 0); /* make sure align() works */ assert(util_is_power_of_two(layout->align_i) && util_is_power_of_two(layout->align_j)); assert(util_is_power_of_two(layout->block_width) && util_is_power_of_two(layout->block_height)); } static void tex_layout_init_levels(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; int last_level, lv; last_level = templ->last_level; /* need at least 2 levels to compute full qpitch */ if (last_level == 0 && templ->array_size > 1 && layout->array_spacing_full) last_level++; /* compute mip level sizes */ for (lv = 0; lv <= last_level; lv++) { int w, h, d; w = u_minify(templ->width0, lv); h = u_minify(templ->height0, lv); d = u_minify(templ->depth0, lv); /* * From the Sandy Bridge PRM, volume 1 part 1, page 114: * * "The dimensions of the mip maps are first determined by applying * the sizing algorithm presented in Non-Power-of-Two Mipmaps * above. Then, if necessary, they are padded out to compression * block boundaries." */ w = align(w, layout->block_width); h = align(h, layout->block_height); /* * From the Sandy Bridge PRM, volume 1 part 1, page 111: * * "If the surface is multisampled (4x), these values must be * adjusted as follows before proceeding: * * W_L = ceiling(W_L / 2) * 4 * H_L = ceiling(H_L / 2) * 4" * * From the Ivy Bridge PRM, volume 1 part 1, page 108: * * "If the surface is multisampled and it is a depth or stencil * surface or Multisampled Surface StorageFormat in SURFACE_STATE * is MSFMT_DEPTH_STENCIL, W_L and H_L must be adjusted as follows * before proceeding: * * #samples W_L = H_L = * 2 ceiling(W_L / 2) * 4 HL [no adjustment] * 4 ceiling(W_L / 2) * 4 ceiling(H_L / 2) * 4 * 8 ceiling(W_L / 2) * 8 ceiling(H_L / 2) * 4 * 16 ceiling(W_L / 2) * 8 ceiling(H_L / 2) * 8" * * For interleaved samples (4x), where pixels * * (x, y ) (x+1, y ) * (x, y+1) (x+1, y+1) * * would be is occupied by * * (x, y , si0) (x+1, y , si0) (x, y , si1) (x+1, y , si1) * (x, y+1, si0) (x+1, y+1, si0) (x, y+1, si1) (x+1, y+1, si1) * (x, y , si2) (x+1, y , si2) (x, y , si3) (x+1, y , si3) * (x, y+1, si2) (x+1, y+1, si2) (x, y+1, si3) (x+1, y+1, si3) * * Thus the need to * * w = align(w, 2) * 2; * y = align(y, 2) * 2; */ if (layout->interleaved) { switch (templ->nr_samples) { case 0: case 1: break; case 2: w = align(w, 2) * 2; break; case 4: w = align(w, 2) * 2; h = align(h, 2) * 2; break; case 8: w = align(w, 2) * 4; h = align(h, 2) * 2; break; case 16: w = align(w, 2) * 4; h = align(h, 2) * 4; break; default: assert(!"unsupported sample count"); break; } } layout->levels[lv].w = w; layout->levels[lv].h = h; layout->levels[lv].d = d; } } static void tex_layout_init_spacing(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; if (layout->dev->gen >= ILO_GEN(7)) { /* * It is not explicitly states, but render targets are expected to be * UMS/CMS (samples non-interleaved) and depth/stencil buffers are * expected to be IMS (samples interleaved). * * See "Multisampled Surface Storage Format" field of SURFACE_STATE. */ if (layout->has_depth || layout->has_stencil) { layout->interleaved = true; /* * From the Ivy Bridge PRM, volume 1 part 1, page 111: * * "note that the depth buffer and stencil buffer have an implied * value of ARYSPC_FULL" */ layout->array_spacing_full = true; } else { layout->interleaved = false; /* * From the Ivy Bridge PRM, volume 4 part 1, page 66: * * "If Multisampled Surface Storage Format is MSFMT_MSS and * Number of Multisamples is not MULTISAMPLECOUNT_1, this field * (Surface Array Spacing) must be set to ARYSPC_LOD0." * * As multisampled resources are not mipmapped, we never use * ARYSPC_FULL for them. */ if (templ->nr_samples > 1) assert(templ->last_level == 0); layout->array_spacing_full = (templ->last_level > 0); } } else { /* GEN6 supports only interleaved samples */ layout->interleaved = true; /* * From the Sandy Bridge PRM, volume 1 part 1, page 115: * * "The separate stencil buffer does not support mip mapping, thus * the storage for LODs other than LOD 0 is not needed. The * following QPitch equation applies only to the separate stencil * buffer: * * QPitch = h_0" * * GEN6 does not support compact spacing otherwise. */ layout->array_spacing_full = (layout->format != PIPE_FORMAT_S8_UINT); } } static void tex_layout_init_tiling(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; const enum pipe_format format = layout->format; const unsigned tile_none = 1 << INTEL_TILING_NONE; const unsigned tile_x = 1 << INTEL_TILING_X; const unsigned tile_y = 1 << INTEL_TILING_Y; unsigned valid_tilings = tile_none | tile_x | tile_y; /* * From the Sandy Bridge PRM, volume 1 part 2, page 32: * * "Display/Overlay Y-Major not supported. * X-Major required for Async Flips" */ if (unlikely(templ->bind & PIPE_BIND_SCANOUT)) valid_tilings &= tile_x; /* * From the Sandy Bridge PRM, volume 3 part 2, page 158: * * "The cursor surface address must be 4K byte aligned. The cursor must * be in linear memory, it cannot be tiled." */ if (unlikely(templ->bind & PIPE_BIND_CURSOR)) valid_tilings &= tile_none; /* * From the Ivy Bridge PRM, volume 4 part 1, page 76: * * "The MCS surface must be stored as Tile Y." */ if (templ->bind & ILO_BIND_MCS) valid_tilings &= tile_y; /* * From the Sandy Bridge PRM, volume 2 part 1, page 318: * * "[DevSNB+]: This field (Tiled Surface) must be set to TRUE. Linear * Depth Buffer is not supported." * * "The Depth Buffer, if tiled, must use Y-Major tiling." * * From the Sandy Bridge PRM, volume 1 part 2, page 22: * * "W-Major Tile Format is used for separate stencil." * * Since the HW does not support W-tiled fencing, we have to do it in the * driver. */ if (templ->bind & PIPE_BIND_DEPTH_STENCIL) { switch (format) { case PIPE_FORMAT_S8_UINT: valid_tilings &= tile_none; break; default: valid_tilings &= tile_y; break; } } if (templ->bind & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW)) { if (templ->bind & PIPE_BIND_RENDER_TARGET) { /* * From the Sandy Bridge PRM, volume 1 part 2, page 32: * * "NOTE: 128BPE Format Color buffer ( render target ) MUST be * either TileX or Linear." */ if (layout->block_size == 16) valid_tilings &= ~tile_y; /* * From the Ivy Bridge PRM, volume 4 part 1, page 63: * * "This field (Surface Vertical Aligment) must be set to * VALIGN_4 for all tiled Y Render Target surfaces." * * "VALIGN_4 is not supported for surface format * R32G32B32_FLOAT." */ if (layout->dev->gen >= ILO_GEN(7) && layout->block_size == 12) valid_tilings &= ~tile_y; } /* * Also, heuristically set a minimum width/height for enabling tiling. */ if (templ->width0 < 64 && (valid_tilings & ~tile_x)) valid_tilings &= ~tile_x; if ((templ->width0 < 32 || templ->height0 < 16) && (templ->width0 < 16 || templ->height0 < 32) && (valid_tilings & ~tile_y)) valid_tilings &= ~tile_y; } else { /* force linear if we are not sure where the texture is bound to */ if (valid_tilings & tile_none) valid_tilings &= tile_none; } /* no conflicting binding flags */ assert(valid_tilings); /* prefer tiled than linear */ if (valid_tilings & tile_y) layout->tiling = INTEL_TILING_Y; else if (valid_tilings & tile_x) layout->tiling = INTEL_TILING_X; else layout->tiling = INTEL_TILING_NONE; layout->can_be_linear = valid_tilings & tile_none; } static void tex_layout_init_format(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; enum pipe_format format; const struct util_format_description *desc; switch (templ->format) { case PIPE_FORMAT_ETC1_RGB8: format = PIPE_FORMAT_R8G8B8X8_UNORM; break; default: format = templ->format; break; } layout->format = format; layout->block_width = util_format_get_blockwidth(format); layout->block_height = util_format_get_blockheight(format); layout->block_size = util_format_get_blocksize(format); layout->compressed = util_format_is_compressed(format); desc = util_format_description(format); layout->has_depth = util_format_has_depth(desc); layout->has_stencil = util_format_has_stencil(desc); } static void tex_layout_init(struct tex_layout *layout, struct pipe_screen *screen, const struct pipe_resource *templ, struct ilo_texture_slice **slices) { struct ilo_screen *is = ilo_screen(screen); memset(layout, 0, sizeof(*layout)); layout->dev = &is->dev; layout->templ = templ; /* note that there are dependencies between these functions */ tex_layout_init_format(layout); tex_layout_init_tiling(layout); tex_layout_init_spacing(layout); tex_layout_init_levels(layout); tex_layout_init_alignments(layout); tex_layout_init_qpitch(layout); if (slices) { int lv; for (lv = 0; lv <= templ->last_level; lv++) layout->levels[lv].slices = slices[lv]; } } static bool tex_layout_force_linear(struct tex_layout *layout) { if (!layout->can_be_linear) return false; /* * we may be able to switch from VALIGN_4 to VALIGN_2 when the layout was * Y-tiled, but let's keep it simple */ layout->tiling = INTEL_TILING_NONE; return true; } /** * Layout a 2D texture. */ static void tex_layout_2d(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; unsigned int level_x, level_y, num_slices; int lv; level_x = 0; level_y = 0; for (lv = 0; lv <= templ->last_level; lv++) { const unsigned int level_w = layout->levels[lv].w; const unsigned int level_h = layout->levels[lv].h; int slice; /* set slice offsets */ if (layout->levels[lv].slices) { for (slice = 0; slice < templ->array_size; slice++) { layout->levels[lv].slices[slice].x = level_x; /* slices are qpitch apart in Y-direction */ layout->levels[lv].slices[slice].y = level_y + layout->qpitch * slice; } } /* extend the size of the monolithic bo to cover this mip level */ if (layout->width < level_x + level_w) layout->width = level_x + level_w; if (layout->height < level_y + level_h) layout->height = level_y + level_h; /* MIPLAYOUT_BELOW */ if (lv == 1) level_x += align(level_w, layout->align_i); else level_y += align(level_h, layout->align_j); } num_slices = templ->array_size; /* samples of the same index are stored in a slice */ if (templ->nr_samples > 1 && !layout->interleaved) num_slices *= templ->nr_samples; /* we did not take slices into consideration in the computation above */ layout->height += layout->qpitch * (num_slices - 1); } /** * Layout a 3D texture. */ static void tex_layout_3d(struct tex_layout *layout) { const struct pipe_resource *templ = layout->templ; unsigned int level_y; int lv; level_y = 0; for (lv = 0; lv <= templ->last_level; lv++) { const unsigned int level_w = layout->levels[lv].w; const unsigned int level_h = layout->levels[lv].h; const unsigned int level_d = layout->levels[lv].d; const unsigned int slice_pitch = align(level_w, layout->align_i); const unsigned int slice_qpitch = align(level_h, layout->align_j); const unsigned int num_slices_per_row = 1 << lv; int slice; for (slice = 0; slice < level_d; slice += num_slices_per_row) { int i; /* set slice offsets */ if (layout->levels[lv].slices) { for (i = 0; i < num_slices_per_row && slice + i < level_d; i++) { layout->levels[lv].slices[slice + i].x = slice_pitch * i; layout->levels[lv].slices[slice + i].y = level_y; } } /* move on to the next slice row */ level_y += slice_qpitch; } /* rightmost slice */ slice = MIN2(num_slices_per_row, level_d) - 1; /* extend the size of the monolithic bo to cover this slice */ if (layout->width < slice_pitch * slice + level_w) layout->width = slice_pitch * slice + level_w; if (lv == templ->last_level) layout->height = (level_y - slice_qpitch) + level_h; } } static void tex_layout_validate(struct tex_layout *layout) { /* * From the Sandy Bridge PRM, volume 1 part 2, page 22: * * "A 4KB tile is subdivided into 8-high by 8-wide array of Blocks for * W-Major Tiles (W Tiles). Each Block is 8 rows by 8 bytes." * * Since we ask for INTEL_TILING_NONE instead of the non-existent * INTEL_TILING_W, we need to manually align the width and height to the * tile boundaries. */ if (layout->templ->format == PIPE_FORMAT_S8_UINT) { layout->width = align(layout->width, 64); layout->height = align(layout->height, 64); } assert(layout->width % layout->block_width == 0); assert(layout->height % layout->block_height == 0); assert(layout->qpitch % layout->block_height == 0); } static size_t tex_layout_estimate_size(const struct tex_layout *layout) { unsigned stride, height; stride = (layout->width / layout->block_width) * layout->block_size; height = layout->height / layout->block_height; switch (layout->tiling) { case INTEL_TILING_X: stride = align(stride, 512); height = align(height, 8); break; case INTEL_TILING_Y: stride = align(stride, 128); height = align(height, 32); break; default: height = align(height, 2); break; } return stride * height; } static void tex_layout_apply(const struct tex_layout *layout, struct ilo_texture *tex) { tex->bo_format = layout->format; /* in blocks */ tex->bo_width = layout->width / layout->block_width; tex->bo_height = layout->height / layout->block_height; tex->bo_cpp = layout->block_size; tex->tiling = layout->tiling; tex->compressed = layout->compressed; tex->block_width = layout->block_width; tex->block_height = layout->block_height; tex->halign_8 = (layout->align_i == 8); tex->valign_4 = (layout->align_j == 4); tex->array_spacing_full = layout->array_spacing_full; tex->interleaved = layout->interleaved; } static void tex_free_slices(struct ilo_texture *tex) { FREE(tex->slice_offsets[0]); } static bool tex_alloc_slices(struct ilo_texture *tex) { const struct pipe_resource *templ = &tex->base; struct ilo_texture_slice *slices; int depth, lv; /* sum the depths of all levels */ depth = 0; for (lv = 0; lv <= templ->last_level; lv++) depth += u_minify(templ->depth0, lv); /* * There are (depth * tex->base.array_size) slices in total. Either depth * is one (non-3D) or templ->array_size is one (non-array), but it does * not matter. */ slices = CALLOC(depth * templ->array_size, sizeof(*slices)); if (!slices) return false; tex->slice_offsets[0] = slices; /* point to the respective positions in the buffer */ for (lv = 1; lv <= templ->last_level; lv++) { tex->slice_offsets[lv] = tex->slice_offsets[lv - 1] + u_minify(templ->depth0, lv - 1) * templ->array_size; } return true; } static struct intel_bo * tex_create_bo(const struct ilo_texture *tex, const struct winsys_handle *handle) { struct ilo_screen *is = ilo_screen(tex->base.screen); const char *name; struct intel_bo *bo; switch (tex->base.target) { case PIPE_TEXTURE_1D: name = "1D texture"; break; case PIPE_TEXTURE_2D: name = "2D texture"; break; case PIPE_TEXTURE_3D: name = "3D texture"; break; case PIPE_TEXTURE_CUBE: name = "cube texture"; break; case PIPE_TEXTURE_RECT: name = "rectangle texture"; break; case PIPE_TEXTURE_1D_ARRAY: name = "1D array texture"; break; case PIPE_TEXTURE_2D_ARRAY: name = "2D array texture"; break; case PIPE_TEXTURE_CUBE_ARRAY: name = "cube array texture"; break; default: name ="unknown texture"; break; } if (handle) { bo = is->winsys->import_handle(is->winsys, name, tex->bo_width, tex->bo_height, tex->bo_cpp, handle); } else { bo = is->winsys->alloc(is->winsys, name, tex->bo_width, tex->bo_height, tex->bo_cpp, tex->tiling, tex->bo_flags); } return bo; } static void tex_set_bo(struct ilo_texture *tex, struct intel_bo *bo) { if (tex->bo) tex->bo->unreference(tex->bo); tex->bo = bo; /* winsys may decide to use a different tiling */ tex->tiling = tex->bo->get_tiling(tex->bo); tex->bo_stride = tex->bo->get_pitch(tex->bo); } static void tex_destroy(struct ilo_texture *tex) { tex->bo->unreference(tex->bo); tex_free_slices(tex); FREE(tex); } static struct pipe_resource * tex_create(struct pipe_screen *screen, const struct pipe_resource *templ, const struct winsys_handle *handle) { struct tex_layout layout; struct ilo_texture *tex; struct intel_bo *bo; tex = CALLOC_STRUCT(ilo_texture); if (!tex) return NULL; tex->base = *templ; tex->base.screen = screen; pipe_reference_init(&tex->base.reference, 1); if (!tex_alloc_slices(tex)) { FREE(tex); return NULL; } tex->imported = (handle != NULL); if (tex->base.bind & (PIPE_BIND_DEPTH_STENCIL | PIPE_BIND_RENDER_TARGET)) tex->bo_flags |= INTEL_ALLOC_FOR_RENDER; tex_layout_init(&layout, screen, templ, tex->slice_offsets); switch (templ->target) { case PIPE_TEXTURE_1D: case PIPE_TEXTURE_2D: case PIPE_TEXTURE_CUBE: case PIPE_TEXTURE_RECT: case PIPE_TEXTURE_1D_ARRAY: case PIPE_TEXTURE_2D_ARRAY: case PIPE_TEXTURE_CUBE_ARRAY: tex_layout_2d(&layout); break; case PIPE_TEXTURE_3D: tex_layout_3d(&layout); break; default: assert(!"unknown resource target"); break; } tex_layout_validate(&layout); /* make sure the bo can be mapped through GTT if tiled */ if (layout.tiling != INTEL_TILING_NONE) { /* * Usually only the first 256MB of the GTT is mappable. * * See also how intel_context::max_gtt_map_object_size is calculated. */ const size_t mappable_gtt_size = 256 * 1024 * 1024; const size_t size = tex_layout_estimate_size(&layout); /* be conservative */ if (size > mappable_gtt_size / 4) tex_layout_force_linear(&layout); } tex_layout_apply(&layout, tex); bo = tex_create_bo(tex, handle); if (!bo) { tex_free_slices(tex); FREE(tex); return NULL; } tex_set_bo(tex, bo); return &tex->base; } static bool tex_get_handle(struct ilo_texture *tex, struct winsys_handle *handle) { int err; err = tex->bo->export_handle(tex->bo, handle); return !err; } /** * Estimate the texture size. For large textures, the errors should be pretty * small. */ static size_t tex_estimate_size(struct pipe_screen *screen, const struct pipe_resource *templ) { struct tex_layout layout; tex_layout_init(&layout, screen, templ, NULL); switch (templ->target) { case PIPE_TEXTURE_3D: tex_layout_3d(&layout); break; default: tex_layout_2d(&layout); break; } tex_layout_validate(&layout); return tex_layout_estimate_size(&layout); } static struct intel_bo * buf_create_bo(const struct ilo_buffer *buf) { struct ilo_screen *is = ilo_screen(buf->base.screen); const char *name; switch (buf->base.bind) { case PIPE_BIND_VERTEX_BUFFER: name = "vertex buffer"; break; case PIPE_BIND_INDEX_BUFFER: name = "index buffer"; break; case PIPE_BIND_CONSTANT_BUFFER: name = "constant buffer"; break; case PIPE_BIND_STREAM_OUTPUT: name = "stream output"; break; default: name = "unknown buffer"; break; } return is->winsys->alloc_buffer(is->winsys, name, buf->bo_size, buf->bo_flags); } static void buf_set_bo(struct ilo_buffer *buf, struct intel_bo *bo) { if (buf->bo) buf->bo->unreference(buf->bo); buf->bo = bo; } static void buf_destroy(struct ilo_buffer *buf) { buf->bo->unreference(buf->bo); FREE(buf); } static struct pipe_resource * buf_create(struct pipe_screen *screen, const struct pipe_resource *templ) { struct ilo_buffer *buf; struct intel_bo *bo; buf = CALLOC_STRUCT(ilo_buffer); if (!buf) return NULL; buf->base = *templ; buf->base.screen = screen; pipe_reference_init(&buf->base.reference, 1); buf->bo_size = templ->width0; buf->bo_flags = 0; bo = buf_create_bo(buf); if (!bo) { FREE(buf); return NULL; } buf_set_bo(buf, bo); return &buf->base; } static boolean ilo_can_create_resource(struct pipe_screen *screen, const struct pipe_resource *templ) { /* * We do not know if we will fail until we try to allocate the bo. * So just set a limit on the texture size. */ const size_t max_size = 1 * 1024 * 1024 * 1024; size_t size; if (templ->target == PIPE_BUFFER) size = templ->width0; else size = tex_estimate_size(screen, templ); return (size <= max_size); } static struct pipe_resource * ilo_resource_create(struct pipe_screen *screen, const struct pipe_resource *templ) { if (templ->target == PIPE_BUFFER) return buf_create(screen, templ); else return tex_create(screen, templ, NULL); } static struct pipe_resource * ilo_resource_from_handle(struct pipe_screen *screen, const struct pipe_resource *templ, struct winsys_handle *handle) { if (templ->target == PIPE_BUFFER) return NULL; else return tex_create(screen, templ, handle); } static boolean ilo_resource_get_handle(struct pipe_screen *screen, struct pipe_resource *res, struct winsys_handle *handle) { if (res->target == PIPE_BUFFER) return false; else return tex_get_handle(ilo_texture(res), handle); } static void ilo_resource_destroy(struct pipe_screen *screen, struct pipe_resource *res) { if (res->target == PIPE_BUFFER) buf_destroy(ilo_buffer(res)); else tex_destroy(ilo_texture(res)); } /** * Initialize resource-related functions. */ void ilo_init_resource_functions(struct ilo_screen *is) { is->base.can_create_resource = ilo_can_create_resource; is->base.resource_create = ilo_resource_create; is->base.resource_from_handle = ilo_resource_from_handle; is->base.resource_get_handle = ilo_resource_get_handle; is->base.resource_destroy = ilo_resource_destroy; } bool ilo_buffer_alloc_bo(struct ilo_buffer *buf) { struct intel_bo *bo; bo = buf_create_bo(buf); if (!bo) return false; buf_set_bo(buf, bo); return true; } bool ilo_texture_alloc_bo(struct ilo_texture *tex) { struct intel_bo *bo; /* a shared bo cannot be reallocated */ if (tex->imported) return false; bo = tex_create_bo(tex, NULL); if (!bo) return false; tex_set_bo(tex, bo); return true; } /** * Return the offset (in bytes) to a slice within the bo. * * When tile_aligned is true, the offset is to the tile containing the start * address of the slice. x_offset and y_offset are offsets (in pixels) from * the tile start to slice start. x_offset is always a multiple of 4 and * y_offset is always a multiple of 2. */ unsigned ilo_texture_get_slice_offset(const struct ilo_texture *tex, int level, int slice, bool tile_aligned, unsigned *x_offset, unsigned *y_offset) { const unsigned x = tex->slice_offsets[level][slice].x / tex->block_width; const unsigned y = tex->slice_offsets[level][slice].y / tex->block_height; unsigned tile_w, tile_h, tile_size, row_size; unsigned slice_offset; /* see the Sandy Bridge PRM, volume 1 part 2, page 24 */ switch (tex->tiling) { case INTEL_TILING_NONE: tile_w = tex->bo_cpp; tile_h = 1; break; case INTEL_TILING_X: tile_w = 512; tile_h = 8; break; case INTEL_TILING_Y: tile_w = 128; tile_h = 32; break; default: assert(!"unknown tiling"); tile_w = tex->bo_cpp; tile_h = 1; break; } tile_size = tile_w * tile_h; row_size = tex->bo_stride * tile_h; /* * for non-tiled resources, this is equivalent to * * slice_offset = y * tex->bo_stride + x * tex->bo_cpp; */ slice_offset = row_size * (y / tile_h) + tile_size * (x * tex->bo_cpp / tile_w); /* * Since tex->bo_stride is a multiple of tile_w, slice_offset should be * aligned at this point. */ assert(slice_offset % tile_size == 0); if (tile_aligned) { /* * because of the possible values of align_i and align_j in * layout_tex_init(), x_offset must be a multiple of 4 and y_offset must * be a multiple of 2. */ if (x_offset) { assert(tile_w % tex->bo_cpp == 0); *x_offset = (x % (tile_w / tex->bo_cpp)) * tex->block_width; assert(*x_offset % 4 == 0); } if (y_offset) { *y_offset = (y % tile_h) * tex->block_height; assert(*y_offset % 2 == 0); } } else { const unsigned tx = (x * tex->bo_cpp) % tile_w; const unsigned ty = y % tile_h; switch (tex->tiling) { case INTEL_TILING_NONE: assert(tx == 0 && ty == 0); break; case INTEL_TILING_X: slice_offset += tile_w * ty + tx; break; case INTEL_TILING_Y: slice_offset += tile_h * 16 * (tx / 16) + ty * 16 + (tx % 16); break; } if (x_offset) *x_offset = 0; if (y_offset) *y_offset = 0; } return slice_offset; }