diff --git a/rejlist.txt b/rejlist.txt index c6230500efe..d678cc52649 100644 --- a/rejlist.txt +++ b/rejlist.txt @@ -1,4 +1,3 @@ -./src/gallium/drivers/panfrost/pan_resource.c.rej ./src/gallium/drivers/panfrost/pan_screen.c.rej ./src/gallium/drivers/panfrost/pan_job.h.rej ./src/gallium/drivers/panfrost/pan_disk_cache.c.rej @@ -49,6 +48,4 @@ ./src/egl/meson.build.rej ./src/mesa/main/shaderapi.c.rej ./src/compiler/glsl/glsl_to_nir.cpp.rej -./rejlist.txt -./meson.build.rej -./docs/features.txt.rej +./meson.build.rej \ No newline at end of file diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c index 5b9e99e0270..7411cf1114f 100644 --- a/src/gallium/drivers/panfrost/pan_resource.c +++ b/src/gallium/drivers/panfrost/pan_resource.c @@ -1277,7 +1277,6 @@ panfrost_ptr_map(struct pipe_context *pctx, struct pipe_resource *resource, panfrost_bo_mem_invalidate(staging->image.data.bo, 0, staging->image.data.bo->size); } - panfrost_bo_mmap(staging->image.data.bo); return staging->image.data.bo->ptr.cpu; } @@ -1313,7 +1312,7 @@ panfrost_ptr_map(struct pipe_context *pctx, struct pipe_resource *resource, if (!create_new_bo && !(usage & PIPE_MAP_UNSYNCHRONIZED) && !(resource->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) && - (usage & PIPE_MAP_WRITE) && panfrost_any_batch_reads_rsrc(ctx, rsrc)) { + (usage & PIPE_MAP_WRITE) && panfrost_any_batch_reads_rsrc(ctx, rsrc)) { // First [breakpoint] /* When a resource to be modified is already being used by a * pending batch, it is often faster to copy the whole BO than * to flush and split the frame in two. @@ -1334,6 +1333,8 @@ panfrost_ptr_map(struct pipe_context *pctx, struct pipe_resource *resource, copy_resource = false; } + bool cache_inval = true; + if (create_new_bo) { /* Make sure we re-emit any descriptors using this resource */ panfrost_dirty_state_all(ctx); @@ -1360,8 +1361,8 @@ panfrost_ptr_map(struct pipe_context *pctx, struct pipe_resource *resource, if (newbo) { if (copy_resource) { - memcpy(newbo->ptr.cpu, rsrc->image.data.bo->ptr.cpu, - panfrost_bo_size(bo)); + panfrost_bo_mem_invalidate(bo, 0, bo->size); + memcpy(newbo->ptr.cpu, bo->ptr.cpu, bo->size); } /* Swap the pointers, dropping a reference to @@ -1391,6 +1392,22 @@ panfrost_ptr_map(struct pipe_context *pctx, struct pipe_resource *resource, } else if (usage & PIPE_MAP_READ) { panfrost_flush_writer(ctx, rsrc, "Synchronized read"); panfrost_bo_wait(bo, INT64_MAX, false); + } else { + /* No flush for writes to uninitialized */ + cache_inval = false; + } + + /* TODO: Only the accessed region for textures */ + if (cache_inval) { + size_t offset = 0; + size_t size = bo->size; + + if (resource->target == PIPE_BUFFER) { + offset = box->x * (size_t) bytes_per_block; + size = box->width * (size_t) bytes_per_block; + } + + panfrost_bo_mem_invalidate(bo, offset, size); } } @@ -1714,9 +1731,16 @@ panfrost_ptr_unmap(struct pipe_context *pctx, struct pipe_transfer *transfer) * fragment job is created; this is deferred to prevent useless surface * reloads that can cascade into DATA_INVALID_FAULTs due to reading * malformed AFBC data if uninitialized */ - - if (trans->staging.rsrc) { + + bool afbc = trans->staging.rsrc; + if (abfc) { if (transfer->usage & PIPE_MAP_WRITE) { + struct panfrost_resource *trans_rsrc = pan_resource(trans->staging.rsrc); + struct panfrost_bo *trans_bo = trans_rsrc->image.data.bo; + + + panfrost_bo_mem_clean(trans_bo, 0, trans_bo->size); + if (panfrost_should_linear_convert(dev, prsrc, transfer)) { panfrost_bo_unreference(prsrc->image.data.bo); @@ -1725,7 +1749,7 @@ panfrost_ptr_unmap(struct pipe_context *pctx, struct pipe_transfer *transfer) prsrc->image.layout.format); prsrc->image.data.bo = - pan_resource(trans->staging.rsrc)->image.data.bo; + trans_bo; panfrost_bo_reference(prsrc->image.data.bo); } else { pan_blit_from_staging(pctx, trans); @@ -1756,10 +1780,11 @@ panfrost_ptr_unmap(struct pipe_context *pctx, struct pipe_transfer *transfer) panfrost_resource_setup(dev, prsrc, DRM_FORMAT_MOD_LINEAR, prsrc->image.layout.format); if (prsrc->image.layout.data_size > panfrost_bo_size(bo)) { + /* We want the BO to be MMAPed. */ + uint32_t flags = bo->flags & ~PAN_BO_DELAY_MMAP; const char *label = bo->label; panfrost_bo_unreference(bo); - bo = prsrc->image.data.bo = panfrost_bo_create( - dev, prsrc->image.layout.data_size, 0, label); + bo = prsrc->image.data.bo = panfrost_bo_create(dev, prsrc->image.layout.data_size, flags, label); assert(bo); } @@ -1774,7 +1799,25 @@ panfrost_ptr_unmap(struct pipe_context *pctx, struct pipe_transfer *transfer) } } } - + /* TODO: Only the accessed region */ + /* It is important to not do this for AFBC resources, or else the + * clean might overwrite the result of the blit. */ + if (!afbc && (transfer->usage & PIPE_MAP_WRITE)) { + size_t offset = 0; + size_t size = prsrc->image.data.bo->size; + + /* TODO: Don't recalculate */ + if (prsrc->base.target == PIPE_BUFFER) { + enum pipe_format format = prsrc->image.layout.format; + int bytes_per_block = util_format_get_blocksize(format); + + offset = transfer->box.x * (size_t) bytes_per_block; + size = transfer->box.width * (size_t) bytes_per_block; + } + + panfrost_bo_mem_clean(prsrc->image.data.bo, + offset, size); + } util_range_add(&prsrc->base, &prsrc->valid_buffer_range, transfer->box.x, transfer->box.x + transfer->box.width); @@ -1927,6 +1970,8 @@ panfrost_resource_context_init(struct pipe_context *pctx) pctx->texture_unmap = u_transfer_helper_transfer_unmap; pctx->create_surface = panfrost_create_surface; pctx->surface_destroy = panfrost_surface_destroy; + pctx->clear_render_target = panfrost_clear_render_target; + pctx->clear_depth_stencil = panfrost_clear_depth_stencil; pctx->resource_copy_region = util_resource_copy_region; pctx->blit = panfrost_blit; pctx->generate_mipmap = panfrost_generate_mipmap; diff --git a/src/gallium/drivers/panfrost/pan_resource.c.rej b/src/gallium/drivers/panfrost/pan_resource.c.rej deleted file mode 100644 index b6c27b14a06..00000000000 --- a/src/gallium/drivers/panfrost/pan_resource.c.rej +++ /dev/null @@ -1,426 +0,0 @@ -diff a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c (rejected hunks) -@@ -33,6 +33,7 @@ - #include - #include - #include "drm-uapi/drm_fourcc.h" -+#include "drm-uapi/drm.h" - - #include "frontend/winsys_handle.h" - #include "util/format/u_format.h" -@@ -51,6 +52,46 @@ - #include "pan_tiling.h" - #include "decode.h" - -+/* The kbase kernel driver always maps imported BOs with caching. When we -+ * don't want that, instead do mmap from the display driver side to get a -+ * write-combine mapping. -+ */ -+static void -+panfrost_bo_mmap_scanout(struct panfrost_bo *bo, -+ struct renderonly *ro, -+ struct renderonly_scanout *scanout) -+{ -+ struct panfrost_device *dev = bo->dev; -+ -+ /* If we are fine with a cached mapping, just return */ -+ if (!(dev->debug & PAN_DBG_UNCACHED_CPU)) -+ return; -+ -+ struct drm_mode_map_dumb map_dumb = { -+ .handle = scanout->handle, -+ }; -+ -+ int err = drmIoctl(ro->kms_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); -+ if (err < 0) { -+ fprintf(stderr, "DRM_IOCTL_MODE_MAP_DUMB failed: %s\n", -+ strerror(errno)); -+ return; -+ } -+ -+ void *addr = mmap(NULL, bo->size, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ ro->kms_fd, map_dumb.offset); -+ if (addr == MAP_FAILED) { -+ fprintf(stderr, "kms_fd mmap failed: %s\n", -+ strerror(errno)); -+ return; -+ } -+ -+ bo->munmap_ptr = bo->ptr.cpu; -+ bo->ptr.cpu = addr; -+ bo->cached = false; -+} -+ - static struct pipe_resource * - panfrost_resource_from_handle(struct pipe_screen *pscreen, - const struct pipe_resource *templat, -@@ -102,15 +143,17 @@ panfrost_resource_from_handle(struct pipe_screen *pscreen, - return NULL; - } - -- rsc->image.data.bo = panfrost_bo_import(dev, whandle->handle); -+ struct panfrost_bo *bo = panfrost_bo_import(dev, whandle->handle); - /* Sometimes an import can fail e.g. on an invalid buffer fd, out of - * memory space to mmap it etc. - */ -- if (!rsc->image.data.bo) { -+ if (!bo) { - FREE(rsc); - return NULL; - } - -+ rsc->image.data.bo = bo; -+ - rsc->modifier_constant = true; - - BITSET_SET(rsc->valid.data, 0); -@@ -122,6 +165,9 @@ panfrost_resource_from_handle(struct pipe_screen *pscreen, - /* failure is expected in some cases.. */ - } - -+ if (rsc->scanout) -+ panfrost_bo_mmap_scanout(bo, dev->ro, rsc->scanout); -+ - return prsc; - } - -@@ -473,7 +519,9 @@ panfrost_resource_setup(struct panfrost_device *dev, - static void - panfrost_resource_init_afbc_headers(struct panfrost_resource *pres) - { -- panfrost_bo_mmap(pres->image.data.bo); -+ struct panfrost_bo *bo = pres->image.data.bo; -+ -+ panfrost_bo_mmap(bo); - - unsigned nr_samples = MAX2(pres->base.nr_samples, 1); - -@@ -482,16 +530,16 @@ panfrost_resource_init_afbc_headers(struct panfrost_resource *pres) - struct pan_image_slice_layout *slice = &pres->image.layout.slices[l]; - - for (unsigned s = 0; s < nr_samples; ++s) { -- void *ptr = pres->image.data.bo->ptr.cpu + -- (i * pres->image.layout.array_stride) + -- slice->offset + -- (s * slice->afbc.surface_stride); -+ size_t offset = (i * pres->image.layout.array_stride) + -+ slice->offset + -+ (s * slice->afbc.surface_stride); - - /* Zero-ed AFBC headers seem to encode a plain - * black. Let's use this pattern to keep the - * initialization simple. - */ -- memset(ptr, 0, slice->afbc.header_size); -+ memset(bo->ptr.cpu + offset, 0, slice->afbc.header_size); -+ panfrost_bo_mem_clean(bo, offset, slice->afbc.header_size); - } - } - } -@@ -643,7 +691,9 @@ panfrost_resource_create_with_modifier(struct pipe_screen *screen, - (bind & PIPE_BIND_SHADER_IMAGE) ? "Shader image" : - "Other resource"; - -- if (dev->ro && (template->bind & PIPE_BIND_SCANOUT)) { -+ /* Revert to doing a kmsro allocation for any shared BO, because kbase -+ * cannot do export */ -+ if (dev->ro && (template->bind & PAN_BIND_SHARED_MASK)) { - struct winsys_handle handle; - struct pan_block_size blocksize = panfrost_block_size(modifier, template->format); - -@@ -702,12 +752,21 @@ panfrost_resource_create_with_modifier(struct pipe_screen *screen, - free(so); - return NULL; - } -+ -+ panfrost_bo_mmap_scanout(so->image.data.bo, dev->ro, so->scanout); - } else { - /* We create a BO immediately but don't bother mapping, since we don't - * care to map e.g. FBOs which the CPU probably won't touch */ - -+ /* For now, don't cache buffers as syncing can be slow when -+ * too much memory is mapped. TODO: dynamically switch, or use -+ * the STREAM_READ etc. hints? */ -+ bool buffer = (template->target == PIPE_BUFFER); -+ unsigned cache_flag = buffer ? 0 : PAN_BO_CACHEABLE; -+ - so->image.data.bo = -- panfrost_bo_create(dev, so->image.layout.data_size, PAN_BO_DELAY_MMAP, label); -+ panfrost_bo_create(dev, so->image.layout.data_size, -+ PAN_BO_DELAY_MMAP | cache_flag, label); - - so->constant_stencil = true; - } -@@ -741,10 +800,22 @@ panfrost_resource_create_with_modifiers(struct pipe_screen *screen, - const struct pipe_resource *template, - const uint64_t *modifiers, int count) - { -+ struct panfrost_device *dev = pan_device(screen); -+ - for (unsigned i = 0; i < PAN_MODIFIER_COUNT; ++i) { -- if (drm_find_modifier(pan_best_modifiers[i], modifiers, count)) { -- return panfrost_resource_create_with_modifier(screen, template, -- pan_best_modifiers[i]); -+ uint64_t mod = pan_best_modifiers[i]; -+ -+ if (drm_is_afbc(mod) && !dev->has_afbc) -+ continue; -+ -+ if (mod != DRM_FORMAT_MOD_LINEAR && (dev->debug & PAN_DBG_LINEAR)) -+ continue; -+ -+ /* TODO: What if mod is an unsupported AFBC variant for this -+ * format? */ -+ -+ if (drm_find_modifier(mod, modifiers, count)) { -+ return panfrost_resource_create_with_modifier(screen, template, mod); - } - } - -@@ -773,6 +844,71 @@ panfrost_resource_destroy(struct pipe_screen *screen, - free(rsrc); - } - -+static void -+panfrost_clear_render_target(struct pipe_context *pipe, -+ struct pipe_surface *dst, -+ const union pipe_color_union *color, -+ unsigned dstx, unsigned dsty, -+ unsigned width, unsigned height, -+ bool render_condition_enabled) -+{ -+ struct panfrost_context *ctx = pan_context(pipe); -+ -+ /* TODO: dstx, etc. */ -+ -+ struct pipe_framebuffer_state tmp = {0}; -+ util_copy_framebuffer_state(&tmp, &ctx->pipe_framebuffer); -+ -+ struct pipe_framebuffer_state fb = { -+ .width = dst->width, -+ .height = dst->height, -+ .layers = 1, -+ .samples = 1, -+ .nr_cbufs = 1, -+ .cbufs[0] = dst, -+ }; -+ pipe->set_framebuffer_state(pipe, &fb); -+ -+ struct panfrost_batch *batch = panfrost_get_fresh_batch_for_fbo(ctx, "Clear render target"); -+ panfrost_batch_clear(batch, PIPE_CLEAR_COLOR0, color, 0, 0); -+ -+ pipe->set_framebuffer_state(pipe, &tmp); -+ util_unreference_framebuffer_state(&tmp); -+} -+ -+static void -+panfrost_clear_depth_stencil(struct pipe_context *pipe, -+ struct pipe_surface *dst, -+ unsigned clear_flags, -+ double depth, unsigned stencil, -+ unsigned dstx, unsigned dsty, -+ unsigned width, unsigned height, -+ bool render_condition_enabled) -+{ -+ struct panfrost_context *ctx = pan_context(pipe); -+ -+ /* TODO: dstx, etc. */ -+ -+ struct pipe_framebuffer_state tmp = {0}; -+ util_copy_framebuffer_state(&tmp, &ctx->pipe_framebuffer); -+ -+ struct pipe_framebuffer_state fb = { -+ .width = dst->width, -+ .height = dst->height, -+ .layers = 1, -+ .samples = 1, -+ .nr_cbufs = 0, -+ .zsbuf = dst, -+ }; -+ pipe->set_framebuffer_state(pipe, &fb); -+ -+ struct panfrost_batch *batch = panfrost_get_fresh_batch_for_fbo(ctx, "Clear depth/stencil"); -+ panfrost_batch_clear(batch, clear_flags, NULL, depth, stencil); -+ -+ pipe->set_framebuffer_state(pipe, &tmp); -+ util_unreference_framebuffer_state(&tmp); -+} -+ - /* Most of the time we can do CPU-side transfers, but sometimes we need to use - * the 3D pipe for this. Let's wrap u_blitter to blit to/from staging textures. - * Code adapted from freedreno */ -@@ -968,6 +1104,8 @@ panfrost_ptr_map(struct pipe_context *pctx, - struct panfrost_resource *staging = pan_alloc_staging(ctx, rsrc, level, box); - assert(staging); - -+ panfrost_bo_mmap(staging->image.data.bo); -+ - /* Staging resources have one LOD: level 0. Query the strides - * on this LOD. - */ -@@ -990,9 +1128,11 @@ panfrost_ptr_map(struct pipe_context *pctx, - pan_blit_to_staging(pctx, transfer); - panfrost_flush_writer(ctx, staging, "AFBC read staging blit"); - panfrost_bo_wait(staging->image.data.bo, INT64_MAX, false); -+ -+ panfrost_bo_mem_invalidate(staging->image.data.bo, 0, -+ staging->image.data.bo->size); - } - -- panfrost_bo_mmap(staging->image.data.bo); - return staging->image.data.bo->ptr.cpu; - } - // we are here -@@ -1029,7 +1169,8 @@ panfrost_ptr_map(struct pipe_context *pctx, - !(usage & PIPE_MAP_UNSYNCHRONIZED) && - !(resource->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) && - (usage & PIPE_MAP_WRITE) && -- rsrc->track.nr_users > 0) { -+ rsrc->track.nr_users > 0 && -+ bo->size < 16 * 1024 * 1024) { - - /* When a resource to be modified is already being used by a - * pending batch, it is often faster to copy the whole BO than -@@ -1051,6 +1192,8 @@ panfrost_ptr_map(struct pipe_context *pctx, - copy_resource = false; - } - -+ bool cache_inval = true; -+ - if (create_new_bo) { - /* Make sure we re-emit any descriptors using this resource */ - panfrost_dirty_state_all(ctx); -@@ -1075,12 +1218,14 @@ panfrost_ptr_map(struct pipe_context *pctx, - flags, bo->label); - - if (newbo) { -- if (copy_resource) -- memcpy(newbo->ptr.cpu, rsrc->image.data.bo->ptr.cpu, bo->size); -+ if (copy_resource) { -+ panfrost_bo_mem_invalidate(bo, 0, bo->size); -+ memcpy(newbo->ptr.cpu, bo->ptr.cpu, bo->size); -+ } - - panfrost_resource_swap_bo(ctx, rsrc, newbo); - -- if (!copy_resource && -+ if (!copy_resource && - drm_is_afbc(rsrc->image.layout.modifier)) - panfrost_resource_init_afbc_headers(rsrc); - -@@ -1102,6 +1247,22 @@ panfrost_ptr_map(struct pipe_context *pctx, - panfrost_flush_writer(ctx, rsrc, "Synchronized read"); - panfrost_bo_wait(bo, INT64_MAX, false); - } -+ } else { -+ /* No flush for writes to uninitialized */ -+ cache_inval = false; -+ } -+ -+ /* TODO: Only the accessed region for textures */ -+ if (cache_inval) { -+ size_t offset = 0; -+ size_t size = bo->size; -+ -+ if (resource->target == PIPE_BUFFER) { -+ offset = box->x * (size_t) bytes_per_block; -+ size = box->width * (size_t) bytes_per_block; -+ } -+ -+ panfrost_bo_mem_invalidate(bo, offset, size); - } - - /* For access to compressed textures, we want the (x, y, w, h) -@@ -1128,6 +1289,8 @@ panfrost_ptr_map(struct pipe_context *pctx, - * caching... I don't know if this is actually possible but we - * should still get it right */ - -+ // TODO: Fix this for cached BOs -+ - unsigned dpw = PIPE_MAP_DIRECTLY | PIPE_MAP_WRITE | PIPE_MAP_PERSISTENT; - - if ((usage & dpw) == dpw && rsrc->index_cache) -@@ -1281,8 +1444,15 @@ panfrost_ptr_unmap(struct pipe_context *pctx, - * reloads that can cascade into DATA_INVALID_FAULTs due to reading - * malformed AFBC data if uninitialized */ - -- if (trans->staging.rsrc) { -+ bool afbc = trans->staging.rsrc; -+ -+ if (afbc) { - if (transfer->usage & PIPE_MAP_WRITE) { -+ struct panfrost_resource *trans_rsrc = pan_resource(trans->staging.rsrc); -+ struct panfrost_bo *trans_bo = trans_rsrc->image.data.bo; -+ -+ panfrost_bo_mem_clean(trans_bo, 0, trans_bo->size); -+ - if (panfrost_should_linear_convert(dev, prsrc, transfer)) { - - panfrost_bo_unreference(prsrc->image.data.bo); -@@ -1290,7 +1460,7 @@ panfrost_ptr_unmap(struct pipe_context *pctx, - panfrost_resource_setup(dev, prsrc, DRM_FORMAT_MOD_LINEAR, - prsrc->image.layout.format); - -- prsrc->image.data.bo = pan_resource(trans->staging.rsrc)->image.data.bo; -+ prsrc->image.data.bo = trans_bo; - panfrost_bo_reference(prsrc->image.data.bo); - } else { - pan_blit_from_staging(pctx, trans); -@@ -1315,10 +1485,13 @@ panfrost_ptr_unmap(struct pipe_context *pctx, - panfrost_resource_setup(dev, prsrc, DRM_FORMAT_MOD_LINEAR, - prsrc->image.layout.format); - if (prsrc->image.layout.data_size > bo->size) { -+ /* We want the BO to be MMAPed. */ -+ uint32_t flags = bo->flags & ~PAN_BO_DELAY_MMAP; - const char *label = bo->label; -+ - panfrost_bo_unreference(bo); - bo = prsrc->image.data.bo = -- panfrost_bo_create(dev, prsrc->image.layout.data_size, 0, label); -+ panfrost_bo_create(dev, prsrc->image.layout.data_size, flags, label); - assert(bo); - } - -@@ -1339,6 +1512,25 @@ panfrost_ptr_unmap(struct pipe_context *pctx, - } - } - -+ /* TODO: Only the accessed region */ -+ /* It is important to not do this for AFBC resources, or else the -+ * clean might overwrite the result of the blit. */ -+ if (!afbc && (transfer->usage & PIPE_MAP_WRITE)) { -+ size_t offset = 0; -+ size_t size = prsrc->image.data.bo->size; -+ -+ /* TODO: Don't recalculate */ -+ if (prsrc->base.target == PIPE_BUFFER) { -+ enum pipe_format format = prsrc->image.layout.format; -+ int bytes_per_block = util_format_get_blocksize(format); -+ -+ offset = transfer->box.x * (size_t) bytes_per_block; -+ size = transfer->box.width * (size_t) bytes_per_block; -+ } -+ -+ panfrost_bo_mem_clean(prsrc->image.data.bo, -+ offset, size); -+ } - - util_range_add(&prsrc->base, &prsrc->valid_buffer_range, - transfer->box.x, -@@ -1353,6 +1545,7 @@ panfrost_ptr_unmap(struct pipe_context *pctx, - ralloc_free(transfer); - } - -+// TODO: does this need to be changed for cached resources? - static void - panfrost_ptr_flush_region(struct pipe_context *pctx, - struct pipe_transfer *transfer, -@@ -1486,6 +1679,8 @@ panfrost_resource_context_init(struct pipe_context *pctx) - pctx->texture_unmap = u_transfer_helper_transfer_unmap; - pctx->create_surface = panfrost_create_surface; - pctx->surface_destroy = panfrost_surface_destroy; -+ pctx->clear_render_target = panfrost_clear_render_target; -+ pctx->clear_depth_stencil = panfrost_clear_depth_stencil; - pctx->resource_copy_region = util_resource_copy_region; - pctx->blit = panfrost_blit; - pctx->generate_mipmap = panfrost_generate_mipmap;