/********************************************************** * Copyright 2008-2009 VMware, Inc. All rights reserved. * * 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. * **********************************************************/ #include "util/u_inlines.h" #include "pipe/p_defines.h" #include "util/u_format.h" #include "util/u_math.h" #include "util/u_memory.h" #include "tgsi/tgsi_parse.h" #include "svga_context.h" #include "svga_resource_texture.h" #include "svga_debug.h" static INLINE unsigned translate_wrap_mode(unsigned wrap) { switch (wrap) { case PIPE_TEX_WRAP_REPEAT: return SVGA3D_TEX_ADDRESS_WRAP; case PIPE_TEX_WRAP_CLAMP: return SVGA3D_TEX_ADDRESS_CLAMP; case PIPE_TEX_WRAP_CLAMP_TO_EDGE: /* Unfortunately SVGA3D_TEX_ADDRESS_EDGE not respected by * hardware. */ return SVGA3D_TEX_ADDRESS_CLAMP; case PIPE_TEX_WRAP_CLAMP_TO_BORDER: return SVGA3D_TEX_ADDRESS_BORDER; case PIPE_TEX_WRAP_MIRROR_REPEAT: return SVGA3D_TEX_ADDRESS_MIRROR; case PIPE_TEX_WRAP_MIRROR_CLAMP: case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE: case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER: return SVGA3D_TEX_ADDRESS_MIRRORONCE; default: assert(0); return SVGA3D_TEX_ADDRESS_WRAP; } } static INLINE unsigned translate_img_filter( unsigned filter ) { switch (filter) { case PIPE_TEX_FILTER_NEAREST: return SVGA3D_TEX_FILTER_NEAREST; case PIPE_TEX_FILTER_LINEAR: return SVGA3D_TEX_FILTER_LINEAR; default: assert(0); return SVGA3D_TEX_FILTER_NEAREST; } } static INLINE unsigned translate_mip_filter( unsigned filter ) { switch (filter) { case PIPE_TEX_MIPFILTER_NONE: return SVGA3D_TEX_FILTER_NONE; case PIPE_TEX_MIPFILTER_NEAREST: return SVGA3D_TEX_FILTER_NEAREST; case PIPE_TEX_MIPFILTER_LINEAR: return SVGA3D_TEX_FILTER_LINEAR; default: assert(0); return SVGA3D_TEX_FILTER_NONE; } } static void * svga_create_sampler_state(struct pipe_context *pipe, const struct pipe_sampler_state *sampler) { struct svga_context *svga = svga_context(pipe); struct svga_sampler_state *cso = CALLOC_STRUCT( svga_sampler_state ); if (!cso) return NULL; cso->mipfilter = translate_mip_filter(sampler->min_mip_filter); cso->magfilter = translate_img_filter( sampler->mag_img_filter ); cso->minfilter = translate_img_filter( sampler->min_img_filter ); cso->aniso_level = MAX2( sampler->max_anisotropy, 1 ); if(sampler->max_anisotropy) cso->magfilter = cso->minfilter = SVGA3D_TEX_FILTER_ANISOTROPIC; cso->lod_bias = sampler->lod_bias; cso->addressu = translate_wrap_mode(sampler->wrap_s); cso->addressv = translate_wrap_mode(sampler->wrap_t); cso->addressw = translate_wrap_mode(sampler->wrap_r); cso->normalized_coords = sampler->normalized_coords; cso->compare_mode = sampler->compare_mode; cso->compare_func = sampler->compare_func; { uint32 r = float_to_ubyte(sampler->border_color.f[0]); uint32 g = float_to_ubyte(sampler->border_color.f[1]); uint32 b = float_to_ubyte(sampler->border_color.f[2]); uint32 a = float_to_ubyte(sampler->border_color.f[3]); cso->bordercolor = (a << 24) | (r << 16) | (g << 8) | b; } /* No SVGA3D support for: * - min/max LOD clamping */ cso->min_lod = 0; cso->view_min_lod = MAX2((int) (sampler->min_lod + 0.5), 0); cso->view_max_lod = MAX2((int) (sampler->max_lod + 0.5), 0); /* Use min_mipmap */ if (svga->debug.use_min_mipmap) { if (cso->view_min_lod == cso->view_max_lod) { cso->min_lod = cso->view_min_lod; cso->view_min_lod = 0; cso->view_max_lod = 1000; /* Just a high number */ cso->mipfilter = SVGA3D_TEX_FILTER_NONE; } } SVGA_DBG(DEBUG_VIEWS, "min %u, view(min %u, max %u) lod, mipfilter %s\n", cso->min_lod, cso->view_min_lod, cso->view_max_lod, cso->mipfilter == SVGA3D_TEX_FILTER_NONE ? "SVGA3D_TEX_FILTER_NONE" : "SOMETHING"); return cso; } static void svga_bind_sampler_states(struct pipe_context *pipe, unsigned shader, unsigned start, unsigned num, void **samplers) { struct svga_context *svga = svga_context(pipe); unsigned i; assert(shader < PIPE_SHADER_TYPES); assert(start + num <= PIPE_MAX_SAMPLERS); /* we only support fragment shader samplers at this time */ if (shader != PIPE_SHADER_FRAGMENT) return; /* Check for no-op */ if (start + num <= svga->curr.num_samplers && !memcmp(svga->curr.sampler + start, samplers, num * sizeof(void *))) { if (0) debug_printf("sampler noop\n"); return; } for (i = 0; i < num; i++) svga->curr.sampler[start + i] = samplers[i]; /* find highest non-null sampler_views[] entry */ { unsigned j = MAX2(svga->curr.num_samplers, start + num); while (j > 0 && svga->curr.sampler[j - 1] == NULL) j--; svga->curr.num_samplers = j; } svga->dirty |= SVGA_NEW_SAMPLER; } static void svga_bind_fragment_sampler_states(struct pipe_context *pipe, unsigned num, void **sampler) { svga_bind_sampler_states(pipe, PIPE_SHADER_FRAGMENT, 0, num, sampler); } static void svga_delete_sampler_state(struct pipe_context *pipe, void *sampler) { FREE(sampler); } static struct pipe_sampler_view * svga_create_sampler_view(struct pipe_context *pipe, struct pipe_resource *texture, const struct pipe_sampler_view *templ) { struct pipe_sampler_view *view = CALLOC_STRUCT(pipe_sampler_view); if (view) { *view = *templ; view->reference.count = 1; view->texture = NULL; pipe_resource_reference(&view->texture, texture); view->context = pipe; } return view; } static void svga_sampler_view_destroy(struct pipe_context *pipe, struct pipe_sampler_view *view) { pipe_resource_reference(&view->texture, NULL); FREE(view); } static void svga_set_sampler_views(struct pipe_context *pipe, unsigned shader, unsigned start, unsigned num, struct pipe_sampler_view **views) { struct svga_context *svga = svga_context(pipe); unsigned flag_1d = 0; unsigned flag_srgb = 0; uint i; assert(shader < PIPE_SHADER_TYPES); assert(start + num <= Elements(svga->curr.sampler_views)); /* we only support fragment shader sampler views at this time */ if (shader != PIPE_SHADER_FRAGMENT) return; /* Check for no-op */ if (start + num <= svga->curr.num_sampler_views && !memcmp(svga->curr.sampler_views + start, views, num * sizeof(struct pipe_sampler_view *))) { if (0) debug_printf("texture noop\n"); return; } for (i = 0; i < num; i++) { if (svga->curr.sampler_views[start + i] != views[i]) { /* Note: we're using pipe_sampler_view_release() here to work around * a possible crash when the old view belongs to another context that * was already destroyed. */ pipe_sampler_view_release(pipe, &svga->curr.sampler_views[start + i]); pipe_sampler_view_reference(&svga->curr.sampler_views[start + i], views[i]); } if (!views[i]) continue; if (util_format_is_srgb(views[i]->format)) flag_srgb |= 1 << (start + i); if (views[i]->texture->target == PIPE_TEXTURE_1D) flag_1d |= 1 << (start + i); } /* find highest non-null sampler_views[] entry */ { unsigned j = MAX2(svga->curr.num_sampler_views, start + num); while (j > 0 && svga->curr.sampler_views[j - 1] == NULL) j--; svga->curr.num_sampler_views = j; } svga->dirty |= SVGA_NEW_TEXTURE_BINDING; if (flag_srgb != svga->curr.tex_flags.flag_srgb || flag_1d != svga->curr.tex_flags.flag_1d) { svga->dirty |= SVGA_NEW_TEXTURE_FLAGS; svga->curr.tex_flags.flag_1d = flag_1d; svga->curr.tex_flags.flag_srgb = flag_srgb; } } static void svga_set_fragment_sampler_views(struct pipe_context *pipe, unsigned num, struct pipe_sampler_view **views) { svga_set_sampler_views(pipe, PIPE_SHADER_FRAGMENT, 0, num, views); } void svga_init_sampler_functions( struct svga_context *svga ) { svga->pipe.create_sampler_state = svga_create_sampler_state; svga->pipe.bind_fragment_sampler_states = svga_bind_fragment_sampler_states; svga->pipe.delete_sampler_state = svga_delete_sampler_state; svga->pipe.set_fragment_sampler_views = svga_set_fragment_sampler_views; svga->pipe.create_sampler_view = svga_create_sampler_view; svga->pipe.sampler_view_destroy = svga_sampler_view_destroy; }