1 /* 2 * Copyright (C) 2016 Rob Clark <robclark (at) freedesktop.org> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 * 23 * Authors: 24 * Rob Clark <robclark (at) freedesktop.org> 25 */ 26 27 #include "pipe/p_state.h" 28 #include "util/u_string.h" 29 #include "util/u_memory.h" 30 #include "util/u_inlines.h" 31 #include "util/u_format.h" 32 33 #include "fd5_texture.h" 34 #include "fd5_format.h" 35 36 static enum a5xx_tex_clamp 37 tex_clamp(unsigned wrap, bool clamp_to_edge, bool *needs_border) 38 { 39 /* Hardware does not support _CLAMP, but we emulate it: */ 40 if (wrap == PIPE_TEX_WRAP_CLAMP) { 41 wrap = (clamp_to_edge) ? 42 PIPE_TEX_WRAP_CLAMP_TO_EDGE : PIPE_TEX_WRAP_CLAMP_TO_BORDER; 43 } 44 45 switch (wrap) { 46 case PIPE_TEX_WRAP_REPEAT: 47 return A5XX_TEX_REPEAT; 48 case PIPE_TEX_WRAP_CLAMP_TO_EDGE: 49 return A5XX_TEX_CLAMP_TO_EDGE; 50 case PIPE_TEX_WRAP_CLAMP_TO_BORDER: 51 *needs_border = true; 52 return A5XX_TEX_CLAMP_TO_BORDER; 53 case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE: 54 /* only works for PoT.. need to emulate otherwise! */ 55 return A5XX_TEX_MIRROR_CLAMP; 56 case PIPE_TEX_WRAP_MIRROR_REPEAT: 57 return A5XX_TEX_MIRROR_REPEAT; 58 case PIPE_TEX_WRAP_MIRROR_CLAMP: 59 case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER: 60 /* these two we could perhaps emulate, but we currently 61 * just don't advertise PIPE_CAP_TEXTURE_MIRROR_CLAMP 62 */ 63 default: 64 DBG("invalid wrap: %u", wrap); 65 return 0; 66 } 67 } 68 69 static enum a5xx_tex_filter 70 tex_filter(unsigned filter, bool aniso) 71 { 72 switch (filter) { 73 case PIPE_TEX_FILTER_NEAREST: 74 return A5XX_TEX_NEAREST; 75 case PIPE_TEX_FILTER_LINEAR: 76 return aniso ? A5XX_TEX_ANISO : A5XX_TEX_LINEAR; 77 default: 78 DBG("invalid filter: %u", filter); 79 return 0; 80 } 81 } 82 83 static void * 84 fd5_sampler_state_create(struct pipe_context *pctx, 85 const struct pipe_sampler_state *cso) 86 { 87 struct fd5_sampler_stateobj *so = CALLOC_STRUCT(fd5_sampler_stateobj); 88 unsigned aniso = util_last_bit(MIN2(cso->max_anisotropy >> 1, 8)); 89 bool miplinear = false; 90 bool clamp_to_edge; 91 92 if (!so) 93 return NULL; 94 95 so->base = *cso; 96 97 if (cso->min_mip_filter == PIPE_TEX_MIPFILTER_LINEAR) 98 miplinear = true; 99 100 /* 101 * For nearest filtering, _CLAMP means _CLAMP_TO_EDGE; for linear 102 * filtering, _CLAMP means _CLAMP_TO_BORDER while additionally 103 * clamping the texture coordinates to [0.0, 1.0]. 104 * 105 * The clamping will be taken care of in the shaders. There are two 106 * filters here, but let the minification one has a say. 107 */ 108 clamp_to_edge = (cso->min_img_filter == PIPE_TEX_FILTER_NEAREST); 109 if (!clamp_to_edge) { 110 so->saturate_s = (cso->wrap_s == PIPE_TEX_WRAP_CLAMP); 111 so->saturate_t = (cso->wrap_t == PIPE_TEX_WRAP_CLAMP); 112 so->saturate_r = (cso->wrap_r == PIPE_TEX_WRAP_CLAMP); 113 } 114 115 so->needs_border = false; 116 so->texsamp0 = 117 COND(miplinear, A5XX_TEX_SAMP_0_MIPFILTER_LINEAR_NEAR) | 118 A5XX_TEX_SAMP_0_XY_MAG(tex_filter(cso->mag_img_filter, aniso)) | 119 A5XX_TEX_SAMP_0_XY_MIN(tex_filter(cso->min_img_filter, aniso)) | 120 A5XX_TEX_SAMP_0_ANISO(aniso) | 121 A5XX_TEX_SAMP_0_WRAP_S(tex_clamp(cso->wrap_s, clamp_to_edge, &so->needs_border)) | 122 A5XX_TEX_SAMP_0_WRAP_T(tex_clamp(cso->wrap_t, clamp_to_edge, &so->needs_border)) | 123 A5XX_TEX_SAMP_0_WRAP_R(tex_clamp(cso->wrap_r, clamp_to_edge, &so->needs_border)); 124 125 so->texsamp1 = 126 COND(miplinear, A5XX_TEX_SAMP_1_MIPFILTER_LINEAR_FAR) | 127 COND(!cso->seamless_cube_map, A5XX_TEX_SAMP_1_CUBEMAPSEAMLESSFILTOFF) | 128 COND(!cso->normalized_coords, A5XX_TEX_SAMP_1_UNNORM_COORDS); 129 130 if (cso->min_mip_filter != PIPE_TEX_MIPFILTER_NONE) { 131 so->texsamp0 |= A5XX_TEX_SAMP_0_LOD_BIAS(cso->lod_bias); 132 so->texsamp1 |= 133 A5XX_TEX_SAMP_1_MIN_LOD(cso->min_lod) | 134 A5XX_TEX_SAMP_1_MAX_LOD(cso->max_lod); 135 } 136 137 if (cso->compare_mode) 138 so->texsamp1 |= A5XX_TEX_SAMP_1_COMPARE_FUNC(cso->compare_func); /* maps 1:1 */ 139 140 return so; 141 } 142 143 static void 144 fd5_sampler_states_bind(struct pipe_context *pctx, 145 enum pipe_shader_type shader, unsigned start, 146 unsigned nr, void **hwcso) 147 { 148 struct fd_context *ctx = fd_context(pctx); 149 struct fd5_context *fd5_ctx = fd5_context(ctx); 150 uint16_t saturate_s = 0, saturate_t = 0, saturate_r = 0; 151 unsigned i; 152 153 if (!hwcso) 154 nr = 0; 155 156 for (i = 0; i < nr; i++) { 157 if (hwcso[i]) { 158 struct fd5_sampler_stateobj *sampler = 159 fd5_sampler_stateobj(hwcso[i]); 160 if (sampler->saturate_s) 161 saturate_s |= (1 << i); 162 if (sampler->saturate_t) 163 saturate_t |= (1 << i); 164 if (sampler->saturate_r) 165 saturate_r |= (1 << i); 166 } 167 } 168 169 fd_sampler_states_bind(pctx, shader, start, nr, hwcso); 170 171 if (shader == PIPE_SHADER_FRAGMENT) { 172 fd5_ctx->fsaturate = 173 (saturate_s != 0) || 174 (saturate_t != 0) || 175 (saturate_r != 0); 176 fd5_ctx->fsaturate_s = saturate_s; 177 fd5_ctx->fsaturate_t = saturate_t; 178 fd5_ctx->fsaturate_r = saturate_r; 179 } else if (shader == PIPE_SHADER_VERTEX) { 180 fd5_ctx->vsaturate = 181 (saturate_s != 0) || 182 (saturate_t != 0) || 183 (saturate_r != 0); 184 fd5_ctx->vsaturate_s = saturate_s; 185 fd5_ctx->vsaturate_t = saturate_t; 186 fd5_ctx->vsaturate_r = saturate_r; 187 } 188 } 189 190 static enum a5xx_tex_type 191 tex_type(unsigned target) 192 { 193 switch (target) { 194 default: 195 assert(0); 196 case PIPE_BUFFER: 197 case PIPE_TEXTURE_1D: 198 case PIPE_TEXTURE_1D_ARRAY: 199 return A5XX_TEX_1D; 200 case PIPE_TEXTURE_RECT: 201 case PIPE_TEXTURE_2D: 202 case PIPE_TEXTURE_2D_ARRAY: 203 return A5XX_TEX_2D; 204 case PIPE_TEXTURE_3D: 205 return A5XX_TEX_3D; 206 case PIPE_TEXTURE_CUBE: 207 case PIPE_TEXTURE_CUBE_ARRAY: 208 return A5XX_TEX_CUBE; 209 } 210 } 211 212 static bool 213 use_astc_srgb_workaround(struct pipe_context *pctx, enum pipe_format format) 214 { 215 return (fd_screen(pctx->screen)->gpu_id == 420) && 216 (util_format_description(format)->layout == UTIL_FORMAT_LAYOUT_ASTC); 217 } 218 219 static struct pipe_sampler_view * 220 fd5_sampler_view_create(struct pipe_context *pctx, struct pipe_resource *prsc, 221 const struct pipe_sampler_view *cso) 222 { 223 struct fd5_pipe_sampler_view *so = CALLOC_STRUCT(fd5_pipe_sampler_view); 224 struct fd_resource *rsc = fd_resource(prsc); 225 unsigned lvl, layers; 226 uint32_t sz2 = 0; 227 228 if (!so) 229 return NULL; 230 231 so->base = *cso; 232 pipe_reference(NULL, &prsc->reference); 233 so->base.texture = prsc; 234 so->base.reference.count = 1; 235 so->base.context = pctx; 236 237 so->texconst0 = 238 A5XX_TEX_CONST_0_FMT(fd5_pipe2tex(cso->format)) | 239 fd5_tex_swiz(cso->format, cso->swizzle_r, cso->swizzle_g, 240 cso->swizzle_b, cso->swizzle_a); 241 242 if (util_format_is_srgb(cso->format)) { 243 if (use_astc_srgb_workaround(pctx, cso->format)) 244 so->astc_srgb = true; 245 so->texconst0 |= A5XX_TEX_CONST_0_SRGB; 246 } 247 248 if (cso->target == PIPE_BUFFER) { 249 unsigned elements = cso->u.buf.size / util_format_get_blocksize(cso->format); 250 251 lvl = 0; 252 so->texconst1 = 253 A5XX_TEX_CONST_1_WIDTH(elements) | 254 A5XX_TEX_CONST_1_HEIGHT(1); 255 so->texconst2 = 256 A5XX_TEX_CONST_2_FETCHSIZE(fd5_pipe2fetchsize(cso->format)) | 257 A5XX_TEX_CONST_2_PITCH(elements * rsc->cpp); 258 so->offset = cso->u.buf.offset; 259 } else { 260 unsigned miplevels; 261 262 lvl = fd_sampler_first_level(cso); 263 miplevels = fd_sampler_last_level(cso) - lvl; 264 layers = cso->u.tex.last_layer - cso->u.tex.first_layer + 1; 265 266 so->texconst0 |= A5XX_TEX_CONST_0_MIPLVLS(miplevels); 267 so->texconst1 = 268 A5XX_TEX_CONST_1_WIDTH(u_minify(prsc->width0, lvl)) | 269 A5XX_TEX_CONST_1_HEIGHT(u_minify(prsc->height0, lvl)); 270 so->texconst2 = 271 A5XX_TEX_CONST_2_FETCHSIZE(fd5_pipe2fetchsize(cso->format)) | 272 A5XX_TEX_CONST_2_PITCH( 273 util_format_get_nblocksx( 274 cso->format, rsc->slices[lvl].pitch) * rsc->cpp); 275 so->offset = fd_resource_offset(rsc, lvl, cso->u.tex.first_layer); 276 } 277 278 so->texconst2 |= A5XX_TEX_CONST_2_TYPE(tex_type(cso->target)); 279 280 switch (cso->target) { 281 case PIPE_TEXTURE_1D: 282 case PIPE_TEXTURE_2D: 283 so->texconst3 = 284 A5XX_TEX_CONST_3_ARRAY_PITCH(rsc->layer_size); 285 so->texconst5 = 286 A5XX_TEX_CONST_5_DEPTH(1); 287 break; 288 case PIPE_TEXTURE_1D_ARRAY: 289 case PIPE_TEXTURE_2D_ARRAY: 290 so->texconst3 = 291 A5XX_TEX_CONST_3_ARRAY_PITCH(rsc->layer_size); 292 so->texconst5 = 293 A5XX_TEX_CONST_5_DEPTH(layers); 294 break; 295 case PIPE_TEXTURE_CUBE: 296 case PIPE_TEXTURE_CUBE_ARRAY: 297 so->texconst3 = 298 A5XX_TEX_CONST_3_ARRAY_PITCH(rsc->layer_size); 299 so->texconst5 = 300 A5XX_TEX_CONST_5_DEPTH(layers / 6); 301 break; 302 case PIPE_TEXTURE_3D: 303 while (lvl < cso->u.tex.last_level && sz2 != rsc->slices[lvl+1].size0) 304 sz2 = rsc->slices[++lvl].size0; 305 so->texconst3 = 306 A5XX_TEX_CONST_3_ARRAY_PITCH(rsc->slices[lvl].size0); 307 so->texconst5 = 308 A5XX_TEX_CONST_5_DEPTH(u_minify(prsc->depth0, lvl)); 309 break; 310 default: 311 so->texconst3 = 0x00000000; 312 break; 313 } 314 315 return &so->base; 316 } 317 318 static void 319 fd5_set_sampler_views(struct pipe_context *pctx, enum pipe_shader_type shader, 320 unsigned start, unsigned nr, 321 struct pipe_sampler_view **views) 322 { 323 struct fd_context *ctx = fd_context(pctx); 324 struct fd5_context *fd5_ctx = fd5_context(ctx); 325 uint16_t astc_srgb = 0; 326 unsigned i; 327 328 for (i = 0; i < nr; i++) { 329 if (views[i]) { 330 struct fd5_pipe_sampler_view *view = 331 fd5_pipe_sampler_view(views[i]); 332 if (view->astc_srgb) 333 astc_srgb |= (1 << i); 334 } 335 } 336 337 fd_set_sampler_views(pctx, shader, start, nr, views); 338 339 if (shader == PIPE_SHADER_FRAGMENT) { 340 fd5_ctx->fastc_srgb = astc_srgb; 341 } else if (shader == PIPE_SHADER_VERTEX) { 342 fd5_ctx->vastc_srgb = astc_srgb; 343 } 344 } 345 346 void 347 fd5_texture_init(struct pipe_context *pctx) 348 { 349 pctx->create_sampler_state = fd5_sampler_state_create; 350 pctx->bind_sampler_states = fd5_sampler_states_bind; 351 pctx->create_sampler_view = fd5_sampler_view_create; 352 pctx->set_sampler_views = fd5_set_sampler_views; 353 } 354