static void copy_miptrees(struct brw_context *brw, struct intel_mipmap_tree *src_mt, int src_x, int src_y, int src_z, unsigned src_level, struct intel_mipmap_tree *dst_mt, int dst_x, int dst_y, int dst_z, unsigned dst_level, int src_width, int src_height) { unsigned bw, bh; if (brw->gen >= 6) { brw_blorp_copy_miptrees(brw, src_mt, src_level, src_z, dst_mt, dst_level, dst_z, src_x, src_y, dst_x, dst_y, src_width, src_height); return; } /* We are now going to try and copy the texture using the blitter. If * that fails, we will fall back mapping the texture and using memcpy. * In either case, we need to do a full resolve. */ intel_miptree_all_slices_resolve_hiz(brw, src_mt); intel_miptree_all_slices_resolve_depth(brw, src_mt); intel_miptree_resolve_color(brw, src_mt, 0); intel_miptree_all_slices_resolve_hiz(brw, dst_mt); intel_miptree_all_slices_resolve_depth(brw, dst_mt); intel_miptree_resolve_color(brw, dst_mt, 0); _mesa_get_format_block_size(src_mt->format, &bw, &bh); /* It's legal to have a WxH that's smaller than a compressed block. This * happens for example when you are using a higher level LOD. For this case, * we still want to copy the entire block, or else the decompression will be * incorrect. */ if (src_width < bw) src_width = ALIGN_NPOT(src_width, bw); if (src_height < bh) src_height = ALIGN_NPOT(src_height, bh); if (copy_image_with_blitter(brw, src_mt, src_level, src_x, src_y, src_z, dst_mt, dst_level, dst_x, dst_y, dst_z, src_width, src_height)) return; /* This is a worst-case scenario software fallback that maps the two * textures and does a memcpy between them. */ copy_image_with_memcpy(brw, src_mt, src_level, src_x, src_y, src_z, dst_mt, dst_level, dst_x, dst_y, dst_z, src_width, src_height); }
/** * Interface for getting memory for uploading streamed data to the GPU * * In most cases, streamed data (for GPU state structures, for example) is * uploaded through brw_state_batch(), since that interface allows relocations * from the streamed space returned to other BOs. However, that interface has * the restriction that the amount of space allocated has to be "small" (see * estimated_max_prim_size in brw_draw.c). * * This interface, on the other hand, is able to handle arbitrary sized * allocation requests, though it will batch small allocations into the same * BO for efficiency and reduced memory footprint. * * \note The returned pointer is valid only until intel_upload_finish(), which * will happen at batch flush or the next * intel_upload_space()/intel_upload_data(). * * \param out_bo Pointer to a BO, which must point to a valid BO or NULL on * entry, and will have a reference to the new BO containing the state on * return. * * \param out_offset Offset within the buffer object that the data will land. */ void * intel_upload_space(struct brw_context *brw, uint32_t size, uint32_t alignment, drm_intel_bo **out_bo, uint32_t *out_offset) { uint32_t offset; offset = ALIGN_NPOT(brw->upload.next_offset, alignment); if (brw->upload.bo && offset + size > brw->upload.bo->size) { intel_upload_finish(brw); offset = 0; } if (!brw->upload.bo) { brw->upload.bo = drm_intel_bo_alloc(brw->bufmgr, "streamed data", MAX2(INTEL_UPLOAD_SIZE, size), 4096); if (brw->has_llc) drm_intel_bo_map(brw->upload.bo, true); else drm_intel_gem_bo_map_gtt(brw->upload.bo); } brw->upload.next_offset = offset + size; *out_offset = offset; if (*out_bo != brw->upload.bo) { drm_intel_bo_unreference(*out_bo); *out_bo = brw->upload.bo; drm_intel_bo_reference(brw->upload.bo); } return brw->upload.bo->virtual + offset; }
/** * Interface for getting memory for uploading streamed data to the GPU * * In most cases, streamed data (for GPU state structures, for example) is * uploaded through brw_state_batch(), since that interface allows relocations * from the streamed space returned to other BOs. However, that interface has * the restriction that the amount of space allocated has to be "small" (see * estimated_max_prim_size in brw_draw.c). * * This interface, on the other hand, is able to handle arbitrary sized * allocation requests, though it will batch small allocations into the same * BO for efficiency and reduced memory footprint. * * \note The returned pointer is valid only until intel_upload_finish(), which * will happen at batch flush or the next * intel_upload_space()/intel_upload_data(). * * \param out_bo Pointer to a BO, which must point to a valid BO or NULL on * entry, and will have a reference to the new BO containing the state on * return. * * \param out_offset Offset within the buffer object that the data will land. */ void * intel_upload_space(struct brw_context *brw, uint32_t size, uint32_t alignment, struct brw_bo **out_bo, uint32_t *out_offset) { uint32_t offset; offset = ALIGN_NPOT(brw->upload.next_offset, alignment); if (brw->upload.bo && offset + size > brw->upload.bo->size) { intel_upload_finish(brw); offset = 0; } assert((brw->upload.bo == NULL) == (brw->upload.map == NULL)); if (!brw->upload.bo) { brw->upload.bo = brw_bo_alloc(brw->bufmgr, "streamed data", MAX2(INTEL_UPLOAD_SIZE, size), 4096); brw->upload.map = brw_bo_map(brw, brw->upload.bo, MAP_READ | MAP_WRITE); } brw->upload.next_offset = offset + size; *out_offset = offset; if (*out_bo != brw->upload.bo) { brw_bo_unreference(*out_bo); *out_bo = brw->upload.bo; brw_bo_reference(brw->upload.bo); } return brw->upload.map + offset; }
unsigned brw_miptree_get_vertical_slice_pitch(const struct brw_context *brw, const struct intel_mipmap_tree *mt, unsigned level) { if (brw->gen >= 9) { /* ALL_SLICES_AT_EACH_LOD isn't supported on Gen8+ but this code will * effectively end up with a packed qpitch anyway whenever * mt->first_level == mt->last_level. */ assert(mt->array_layout != ALL_SLICES_AT_EACH_LOD); /* On Gen9 we can pick whatever qpitch we like as long as it's aligned * to the vertical alignment so we don't need to add any extra rows. */ unsigned qpitch = mt->total_height; /* If the surface might be used as a stencil buffer or HiZ buffer then * it needs to be a multiple of 8. */ const GLenum base_format = _mesa_get_format_base_format(mt->format); if (_mesa_is_depth_or_stencil_format(base_format)) qpitch = ALIGN(qpitch, 8); /* 3D textures need to be aligned to the tile height. At this point we * don't know which tiling will be used so let's just align it to 32 */ if (mt->target == GL_TEXTURE_3D) qpitch = ALIGN(qpitch, 32); return qpitch; } else if (mt->target == GL_TEXTURE_3D || (brw->gen == 4 && mt->target == GL_TEXTURE_CUBE_MAP) || mt->array_layout == ALL_SLICES_AT_EACH_LOD) { return ALIGN_NPOT(minify(mt->physical_height0, level), mt->align_h); } else { const unsigned h0 = ALIGN_NPOT(mt->physical_height0, mt->align_h); const unsigned h1 = ALIGN_NPOT(minify(mt->physical_height0, 1), mt->align_h); return h0 + h1 + (brw->gen >= 7 ? 12 : 11) * mt->align_h; } }
unsigned brw_miptree_get_horizontal_slice_pitch(const struct brw_context *brw, const struct intel_mipmap_tree *mt, unsigned level) { if ((brw->gen < 9 && mt->target == GL_TEXTURE_3D) || (brw->gen == 4 && mt->target == GL_TEXTURE_CUBE_MAP)) { return ALIGN_NPOT(minify(mt->physical_width0, level), mt->align_w); } else { return 0; } }
static void brw_miptree_layout_texture_3d(struct brw_context *brw, struct intel_mipmap_tree *mt) { mt->total_width = 0; mt->total_height = 0; unsigned ysum = 0; unsigned bh, bw; _mesa_get_format_block_size(mt->format, &bw, &bh); for (unsigned level = mt->first_level; level <= mt->last_level; level++) { unsigned WL = MAX2(mt->physical_width0 >> level, 1); unsigned HL = MAX2(mt->physical_height0 >> level, 1); unsigned DL = MAX2(mt->physical_depth0 >> level, 1); unsigned wL = ALIGN_NPOT(WL, mt->align_w); unsigned hL = ALIGN_NPOT(HL, mt->align_h); if (mt->target == GL_TEXTURE_CUBE_MAP) DL = 6; intel_miptree_set_level_info(mt, level, 0, 0, DL); for (unsigned q = 0; q < DL; q++) { unsigned x = (q % (1 << level)) * wL; unsigned y = ysum + (q >> level) * hL; intel_miptree_set_image_offset(mt, level, q, x / bw, y / bh); mt->total_width = MAX2(mt->total_width, (x + wL) / bw); mt->total_height = MAX2(mt->total_height, (y + hL) / bh); } ysum += ALIGN(DL, 1 << level) / (1 << level) * hL; } align_cube(mt); }
static void brw_miptree_layout_texture_array(struct brw_context *brw, struct intel_mipmap_tree *mt) { unsigned height = mt->physical_height0; bool layout_1d = gen9_use_linear_1d_layout(brw, mt); int physical_qpitch; if (layout_1d) gen9_miptree_layout_1d(mt); else brw_miptree_layout_2d(mt); if (layout_1d) { physical_qpitch = 1; /* When using the horizontal layout the qpitch specifies the distance in * pixels between array slices. The total_width is forced to be a * multiple of the horizontal alignment in brw_miptree_layout_1d (in * this case it's always 64). The vertical alignment is ignored. */ mt->qpitch = mt->total_width; } else { mt->qpitch = brw_miptree_get_vertical_slice_pitch(brw, mt, 0); /* Unlike previous generations the qpitch is a multiple of the * compressed block size on Gen9 so physical_qpitch matches mt->qpitch. */ physical_qpitch = (mt->compressed && brw->gen < 9 ? mt->qpitch / 4 : mt->qpitch); } for (unsigned level = mt->first_level; level <= mt->last_level; level++) { unsigned img_height; img_height = ALIGN_NPOT(height, mt->align_h); if (mt->compressed) img_height /= mt->align_h; for (unsigned q = 0; q < mt->level[level].depth; q++) { if (mt->array_layout == ALL_SLICES_AT_EACH_LOD) { intel_miptree_set_image_offset(mt, level, q, 0, q * img_height); } else { intel_miptree_set_image_offset(mt, level, q, 0, q * physical_qpitch); } } height = minify(height, 1); } if (mt->array_layout == ALL_LOD_IN_EACH_SLICE) mt->total_height = physical_qpitch * mt->physical_depth0; align_cube(mt); }
static void brw_miptree_layout_2d(struct intel_mipmap_tree *mt) { unsigned x = 0; unsigned y = 0; unsigned width = mt->physical_width0; unsigned height = mt->physical_height0; unsigned depth = mt->physical_depth0; /* number of array layers. */ unsigned int bw, bh; _mesa_get_format_block_size(mt->format, &bw, &bh); mt->total_width = mt->physical_width0; if (mt->compressed) mt->total_width = ALIGN_NPOT(mt->total_width, bw); /* May need to adjust width to accommodate the placement of * the 2nd mipmap. This occurs when the alignment * constraints of mipmap placement push the right edge of the * 2nd mipmap out past the width of its parent. */ if (mt->first_level != mt->last_level) { unsigned mip1_width; if (mt->compressed) { mip1_width = ALIGN_NPOT(minify(mt->physical_width0, 1), mt->align_w) + ALIGN_NPOT(minify(mt->physical_width0, 2), bw); } else { mip1_width = ALIGN_NPOT(minify(mt->physical_width0, 1), mt->align_w) + minify(mt->physical_width0, 2); } if (mip1_width > mt->total_width) { mt->total_width = mip1_width; } } mt->total_width /= bw; mt->total_height = 0; for (unsigned level = mt->first_level; level <= mt->last_level; level++) { unsigned img_height; intel_miptree_set_level_info(mt, level, x, y, depth); img_height = ALIGN_NPOT(height, mt->align_h); if (mt->compressed) img_height /= bh; if (mt->array_layout == ALL_SLICES_AT_EACH_LOD) { /* Compact arrays with separated miplevels */ img_height *= depth; } /* Because the images are packed better, the final offset * might not be the maximal one: */ mt->total_height = MAX2(mt->total_height, y + img_height); /* Layout_below: step right after second mipmap. */ if (level == mt->first_level + 1) { x += ALIGN_NPOT(width, mt->align_w) / bw; } else { y += img_height; } width = minify(width, 1); height = minify(height, 1); if (mt->target == GL_TEXTURE_3D) depth = minify(depth, 1); } }