/* * Copyright (c) 2017 Etnaviv Project * Copyright (C) 2017 Zodiac Inflight Innovations * * 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, sub license, * 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 (including the * next paragraph) 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 NON-INFRINGEMENT. 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: * Wladimir J. van der Laan */ #include "etnaviv_texture_desc.h" #include "hw/common.xml.h" #include "hw/texdesc_3d.xml.h" #include "etnaviv_clear_blit.h" #include "etnaviv_context.h" #include "etnaviv_emit.h" #include "etnaviv_format.h" #include "etnaviv_translate.h" #include "etnaviv_texture.h" #include "util/u_inlines.h" #include "util/u_memory.h" #include static void * etna_create_sampler_state_desc(struct pipe_context *pipe, const struct pipe_sampler_state *ss) { struct etna_sampler_state_desc *cs = CALLOC_STRUCT(etna_sampler_state_desc); if (!cs) return NULL; cs->SAMP_CTRL0 = VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_UWRAP(translate_texture_wrapmode(ss->wrap_s)) | VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_VWRAP(translate_texture_wrapmode(ss->wrap_t)) | VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_WWRAP(translate_texture_wrapmode(ss->wrap_r)) | VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_MIN(translate_texture_filter(ss->min_img_filter)) | VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_MIP(translate_texture_mipfilter(ss->min_mip_filter)) | VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_MAG(translate_texture_filter(ss->mag_img_filter)) | VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_UNK21; /* no ROUND_UV bit? */ cs->SAMP_CTRL1 = VIVS_NTE_DESCRIPTOR_SAMP_CTRL1_UNK1; uint32_t min_lod_fp8 = MIN2(etna_float_to_fixp88(ss->min_lod), 0xfff); uint32_t max_lod_fp8 = MIN2(etna_float_to_fixp88(ss->max_lod), 0xfff); uint32_t max_lod_min = ss->min_img_filter != ss->mag_img_filter ? 4 : 0; if (ss->min_mip_filter != PIPE_TEX_MIPFILTER_NONE) { cs->SAMP_LOD_MINMAX = VIVS_NTE_DESCRIPTOR_SAMP_LOD_MINMAX_MAX(MAX2(max_lod_fp8, max_lod_min)) | VIVS_NTE_DESCRIPTOR_SAMP_LOD_MINMAX_MIN(min_lod_fp8); } else { cs->SAMP_LOD_MINMAX = VIVS_NTE_DESCRIPTOR_SAMP_LOD_MINMAX_MAX(MAX2(max_lod_fp8, max_lod_min)) | VIVS_NTE_DESCRIPTOR_SAMP_LOD_MINMAX_MIN(min_lod_fp8); } cs->SAMP_LOD_BIAS = VIVS_NTE_DESCRIPTOR_SAMP_LOD_BIAS_BIAS(etna_float_to_fixp88(ss->lod_bias)) | COND(ss->lod_bias != 0.0, VIVS_NTE_DESCRIPTOR_SAMP_LOD_BIAS_ENABLE); return cs; } static void etna_delete_sampler_state_desc(struct pipe_context *pctx, void *ss) { FREE(ss); } static struct pipe_sampler_view * etna_create_sampler_view_desc(struct pipe_context *pctx, struct pipe_resource *prsc, const struct pipe_sampler_view *so) { struct etna_sampler_view_desc *sv = CALLOC_STRUCT(etna_sampler_view_desc); struct etna_context *ctx = etna_context(pctx); const uint32_t format = translate_texture_format(so->format); const bool ext = !!(format & EXT_FORMAT); const bool astc = !!(format & ASTC_FORMAT); const uint32_t swiz = get_texture_swiz(so->format, so->swizzle_r, so->swizzle_g, so->swizzle_b, so->swizzle_a); if (!sv) return NULL; struct etna_resource *res = etna_texture_handle_incompatible(pctx, prsc); if (!res) { free(sv); return NULL; } sv->base = *so; pipe_reference_init(&sv->base.reference, 1); sv->base.texture = NULL; pipe_resource_reference(&sv->base.texture, prsc); sv->base.context = pctx; /* Determine whether target supported */ uint32_t target_hw = translate_texture_target(sv->base.target); if (target_hw == ETNA_NO_MATCH) { BUG("Unhandled texture target"); free(sv); return NULL; } /* Texture descriptor sampler bits */ if (util_format_is_srgb(so->format)) sv->SAMP_CTRL1 |= VIVS_NTE_DESCRIPTOR_SAMP_CTRL1_SRGB; if (!util_format_is_float(so->format) && so->target != PIPE_TEXTURE_3D) sv->SAMP_CTRL0 |= VIVS_NTE_DESCRIPTOR_SAMP_CTRL0_INT_FILTER; /* Create texture descriptor */ sv->bo = etna_bo_new(ctx->screen->dev, 0x100, DRM_ETNA_GEM_CACHE_WC); if (!sv->bo) goto error; uint32_t *buf = etna_bo_map(sv->bo); etna_bo_cpu_prep(sv->bo, DRM_ETNA_PREP_WRITE); memset(buf, 0, 0x100); /** GC7000 needs the size of the BASELOD level */ uint32_t base_width = u_minify(res->base.width0, sv->base.u.tex.first_level); uint32_t base_height = u_minify(res->base.height0, sv->base.u.tex.first_level); #define DESC_SET(x, y) buf[(TEXDESC_##x)>>2] = (y) DESC_SET(CONFIG0, COND(!ext && !astc, VIVS_TE_SAMPLER_CONFIG0_FORMAT(format)) | VIVS_TE_SAMPLER_CONFIG0_TYPE(target_hw) | COND(res->layout == ETNA_LAYOUT_LINEAR && !util_format_is_compressed(so->format), VIVS_TE_SAMPLER_CONFIG0_ADDRESSING_MODE(TEXTURE_ADDRESSING_MODE_LINEAR))); DESC_SET(CONFIG1, COND(ext, VIVS_TE_SAMPLER_CONFIG1_FORMAT_EXT(format)) | COND(astc, VIVS_TE_SAMPLER_CONFIG1_FORMAT_EXT(TEXTURE_FORMAT_EXT_ASTC)) | VIVS_TE_SAMPLER_CONFIG1_HALIGN(res->halign) | swiz); DESC_SET(CONFIG2, 0x00030000); DESC_SET(LINEAR_STRIDE, res->levels[0].stride); DESC_SET(SLICE, res->levels[0].layer_stride); DESC_SET(3D_CONFIG, 0x00000001); DESC_SET(ASTC0, COND(astc, VIVS_NTE_SAMPLER_ASTC0_ASTC_FORMAT(format)) | VIVS_NTE_SAMPLER_ASTC0_UNK8(0xc) | VIVS_NTE_SAMPLER_ASTC0_UNK16(0xc) | VIVS_NTE_SAMPLER_ASTC0_UNK24(0xc)); DESC_SET(BASELOD, TEXDESC_BASELOD_BASELOD(sv->base.u.tex.first_level) | TEXDESC_BASELOD_MAXLOD(MIN2(sv->base.u.tex.last_level, res->base.last_level))); DESC_SET(LOG_SIZE_EXT, TEXDESC_LOG_SIZE_EXT_WIDTH(etna_log2_fixp88(base_width)) | TEXDESC_LOG_SIZE_EXT_HEIGHT(etna_log2_fixp88(base_height))); DESC_SET(SIZE, VIVS_TE_SAMPLER_SIZE_WIDTH(base_width) | VIVS_TE_SAMPLER_SIZE_HEIGHT(base_height)); for (int lod = 0; lod <= res->base.last_level; ++lod) DESC_SET(LOD_ADDR(lod), etna_bo_gpu_va(res->bo) + res->levels[lod].offset); #undef DESC_SET etna_bo_cpu_fini(sv->bo); sv->DESC_ADDR.bo = sv->bo; sv->DESC_ADDR.offset = 0; sv->DESC_ADDR.flags = ETNA_RELOC_READ; return &sv->base; error: free(sv); return NULL; } static void etna_sampler_view_update_descriptor(struct etna_context *ctx, struct etna_cmd_stream *stream, struct etna_sampler_view_desc *sv) { /* TODO: this should instruct the kernel to update the descriptor when the * bo is submitted. For now, just prevent the bo from being freed * while it is in use indirectly. */ struct etna_resource *res = etna_resource(sv->base.texture); if (res->texture) { res = etna_resource(res->texture); } /* No need to ref LOD levels individually as they'll always come from the same bo */ etna_cmd_stream_ref_bo(stream, res->bo, ETNA_RELOC_READ); } static void etna_sampler_view_desc_destroy(struct pipe_context *pctx, struct pipe_sampler_view *so) { struct etna_sampler_view_desc *sv = etna_sampler_view_desc(so); pipe_resource_reference(&sv->base.texture, NULL); etna_bo_del(sv->bo); FREE(sv); } static void etna_emit_texture_desc(struct etna_context *ctx) { struct etna_cmd_stream *stream = ctx->stream; uint32_t active_samplers = active_samplers_bits(ctx); uint32_t dirty = ctx->dirty; if (unlikely(dirty & ETNA_DIRTY_SAMPLER_VIEWS)) { for (int x = 0; x < VIVS_TS_SAMPLER__LEN; ++x) { if ((1 << x) & active_samplers) { struct etna_sampler_view_desc *sv = etna_sampler_view_desc(ctx->sampler_view[x]); struct etna_resource *res = etna_resource(sv->base.texture); struct etna_reloc LOD_ADDR_0; if (!sv->ts.enable) continue; etna_set_state(stream, VIVS_TS_SAMPLER_CONFIG(x), sv->ts.TS_SAMPLER_CONFIG); etna_set_state_reloc(stream, VIVS_TS_SAMPLER_STATUS_BASE(x), &sv->ts.TS_SAMPLER_STATUS_BASE); etna_set_state(stream, VIVS_TS_SAMPLER_CLEAR_VALUE(x), sv->ts.TS_SAMPLER_CLEAR_VALUE); etna_set_state(stream, VIVS_TS_SAMPLER_CLEAR_VALUE2(x), sv->ts.TS_SAMPLER_CLEAR_VALUE2); LOD_ADDR_0.bo = res->bo; LOD_ADDR_0.offset = res->levels[0].offset; LOD_ADDR_0.flags = ETNA_RELOC_READ; etna_set_state_reloc(stream, VIVS_TS_SAMPLER_SURFACE_BASE(x), &LOD_ADDR_0); } } } if (unlikely(dirty & (ETNA_DIRTY_SAMPLERS | ETNA_DIRTY_SAMPLER_VIEWS))) { for (int x = 0; x < PIPE_MAX_SAMPLERS; ++x) { if ((1 << x) & active_samplers) { struct etna_sampler_state_desc *ss = etna_sampler_state_desc(ctx->sampler[x]); struct etna_sampler_view_desc *sv = etna_sampler_view_desc(ctx->sampler_view[x]); etna_set_state(stream, VIVS_NTE_DESCRIPTOR_TX_CTRL(x), COND(sv->ts.enable, VIVS_NTE_DESCRIPTOR_TX_CTRL_TS_ENABLE) | VIVS_NTE_DESCRIPTOR_TX_CTRL_TS_MODE(sv->ts.mode) | VIVS_NTE_DESCRIPTOR_TX_CTRL_TS_INDEX(x)); etna_set_state(stream, VIVS_NTE_DESCRIPTOR_SAMP_CTRL0(x), ss->SAMP_CTRL0 | sv->SAMP_CTRL0); etna_set_state(stream, VIVS_NTE_DESCRIPTOR_SAMP_CTRL1(x), ss->SAMP_CTRL1 | sv->SAMP_CTRL1); etna_set_state(stream, VIVS_NTE_DESCRIPTOR_SAMP_LOD_MINMAX(x), ss->SAMP_LOD_MINMAX); etna_set_state(stream, VIVS_NTE_DESCRIPTOR_SAMP_LOD_BIAS(x), ss->SAMP_LOD_BIAS); } } } if (unlikely(dirty & ETNA_DIRTY_SAMPLER_VIEWS)) { /* Set texture descriptors */ for (int x = 0; x < PIPE_MAX_SAMPLERS; ++x) { if ((1 << x) & ctx->dirty_sampler_views) { if ((1 << x) & active_samplers) { struct etna_sampler_view_desc *sv = etna_sampler_view_desc(ctx->sampler_view[x]); etna_sampler_view_update_descriptor(ctx, stream, sv); etna_set_state_reloc(stream, VIVS_NTE_DESCRIPTOR_ADDR(x), &sv->DESC_ADDR); } else { /* dummy texture descriptors for unused samplers */ etna_set_state_reloc(stream, VIVS_NTE_DESCRIPTOR_ADDR(x), &ctx->DUMMY_DESC_ADDR); } } } } if (unlikely(dirty & ETNA_DIRTY_SAMPLER_VIEWS)) { /* Invalidate all dirty sampler views. */ for (int x = 0; x < PIPE_MAX_SAMPLERS; ++x) { if ((1 << x) & ctx->dirty_sampler_views) { etna_set_state(stream, VIVS_NTE_DESCRIPTOR_INVALIDATE, VIVS_NTE_DESCRIPTOR_INVALIDATE_UNK29 | VIVS_NTE_DESCRIPTOR_INVALIDATE_IDX(x)); } } } } static struct etna_sampler_ts* etna_ts_for_sampler_view_state(struct pipe_sampler_view *pview) { struct etna_sampler_view_desc *sv = etna_sampler_view_desc(pview); return &sv->ts; } void etna_texture_desc_init(struct pipe_context *pctx) { struct etna_context *ctx = etna_context(pctx); DBG("etnaviv: Using descriptor-based texturing\n"); ctx->base.create_sampler_state = etna_create_sampler_state_desc; ctx->base.delete_sampler_state = etna_delete_sampler_state_desc; ctx->base.create_sampler_view = etna_create_sampler_view_desc; ctx->base.sampler_view_destroy = etna_sampler_view_desc_destroy; ctx->emit_texture_state = etna_emit_texture_desc; ctx->ts_for_sampler_view = etna_ts_for_sampler_view_state; }