1287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell/********************************************************** 2287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * Copyright 2008-2009 VMware, Inc. All rights reserved. 3287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * 4287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * Permission is hereby granted, free of charge, to any person 5287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * obtaining a copy of this software and associated documentation 6287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * files (the "Software"), to deal in the Software without 7287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * restriction, including without limitation the rights to use, copy, 8287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * modify, merge, publish, distribute, sublicense, and/or sell copies 9287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * of the Software, and to permit persons to whom the Software is 10287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * furnished to do so, subject to the following conditions: 11287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * 12287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * The above copyright notice and this permission notice shall be 13287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * included in all copies or substantial portions of the Software. 14287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * 15287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * SOFTWARE. 23287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * 24287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell **********************************************************/ 25287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 26287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "svga_cmd.h" 27287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 28287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "pipe/p_state.h" 29287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "pipe/p_defines.h" 30287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "util/u_inlines.h" 31287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "os/os_thread.h" 32287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "util/u_format.h" 33287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "util/u_math.h" 34287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "util/u_memory.h" 35912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz#include "util/u_string.h" 36287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 37974b6413f4540d73c21c092cc0a62abb6d546e21José Fonseca#include "svga_format.h" 38287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "svga_screen.h" 39287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "svga_context.h" 40287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "svga_resource_texture.h" 41287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "svga_sampler_view.h" 42287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "svga_debug.h" 43287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell#include "svga_surface.h" 44287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 45287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 46912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantzvoid 47912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantzsvga_debug_describe_sampler_view(char *buf, const struct svga_sampler_view *sv) 48912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz{ 49912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz char res[128]; 50912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz debug_describe_resource(res, sv->texture); 51912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz util_sprintf(buf, "svga_sampler_view<%s,[%u,%u]>", res, sv->min_lod, sv->max_lod); 52912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz} 53912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz 54287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwellstruct svga_sampler_view * 55287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwellsvga_get_tex_sampler_view(struct pipe_context *pipe, 56287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell struct pipe_resource *pt, 57287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell unsigned min_lod, unsigned max_lod) 58287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell{ 59e16e70610c459721f4344dc6e61a8af1c2ad870dJosé Fonseca struct svga_context *svga = svga_context(pipe); 60e16e70610c459721f4344dc6e61a8af1c2ad870dJosé Fonseca struct svga_screen *ss = svga_screen(pipe->screen); 61287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell struct svga_texture *tex = svga_texture(pt); 62287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell struct svga_sampler_view *sv = NULL; 639305e93114542632384eb38da08018b4b9d1ab96José Fonseca SVGA3dSurfaceFlags flags = SVGA3D_SURFACE_HINT_TEXTURE; 64ffeed5da6e568836867f09f1acb7ce660d091d4aJosé Fonseca SVGA3dSurfaceFormat format = svga_translate_format(ss, pt->format, PIPE_BIND_SAMPLER_VIEW); 65287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell boolean view = TRUE; 66287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 67287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell assert(pt); 68287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell assert(min_lod >= 0); 69287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell assert(min_lod <= max_lod); 70287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell assert(max_lod <= pt->last_level); 71287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 72287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 73287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell /* Is a view needed */ 74287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell { 75287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell /* 76287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * Can't control max lod. For first level views and when we only 77287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * look at one level we disable mip filtering to achive the same 78287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell * results as a view. 79287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell */ 80287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (min_lod == 0 && max_lod >= pt->last_level) 81287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell view = FALSE; 82287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 83287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (ss->debug.no_sampler_view) 84287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell view = FALSE; 85287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 86287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (ss->debug.force_sampler_view) 87287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell view = TRUE; 88287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 89287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 90287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell /* First try the cache */ 91287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (view) { 92287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pipe_mutex_lock(ss->tex_mutex); 93287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (tex->cached_view && 94287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell tex->cached_view->min_lod == min_lod && 95287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell tex->cached_view->max_lod == max_lod) { 96287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell svga_sampler_view_reference(&sv, tex->cached_view); 97287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pipe_mutex_unlock(ss->tex_mutex); 98287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell SVGA_DBG(DEBUG_VIEWS, "svga: Sampler view: reuse %p, %u %u, last %u\n", 99287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt, min_lod, max_lod, pt->last_level); 100287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell svga_validate_sampler_view(svga_context(pipe), sv); 101287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell return sv; 102287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 103287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pipe_mutex_unlock(ss->tex_mutex); 104287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 105287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 106287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv = CALLOC_STRUCT(svga_sampler_view); 107287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pipe_reference_init(&sv->reference, 1); 108dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul 109dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul /* Note: we're not refcounting the texture resource here to avoid 110dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul * a circular dependency. 111dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul */ 112dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul sv->texture = pt; 113dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul 114287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv->min_lod = min_lod; 115287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv->max_lod = max_lod; 116287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 117287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell /* No view needed just use the whole texture */ 118287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (!view) { 119287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell SVGA_DBG(DEBUG_VIEWS, 120287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell "svga: Sampler view: no %p, mips %u..%u, nr %u, size (%ux%ux%u), last %u\n", 121287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt, min_lod, max_lod, 122287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell max_lod - min_lod + 1, 123287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->width0, 124287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->height0, 125287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->depth0, 126287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->last_level); 127287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv->key.cachable = 0; 128287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv->handle = tex->handle; 129912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz debug_reference(&sv->reference, 130912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz (debug_reference_descriptor)svga_debug_describe_sampler_view, 0); 131287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell return sv; 132287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 133287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 134287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell SVGA_DBG(DEBUG_VIEWS, 135287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell "svga: Sampler view: yes %p, mips %u..%u, nr %u, size (%ux%ux%u), last %u\n", 136287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt, min_lod, max_lod, 137287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell max_lod - min_lod + 1, 138287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->width0, 139287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->height0, 140287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->depth0, 141287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pt->last_level); 142287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 143287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv->age = tex->age; 144e16e70610c459721f4344dc6e61a8af1c2ad870dJosé Fonseca sv->handle = svga_texture_view_surface(svga, tex, flags, format, 145287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell min_lod, 146287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell max_lod - min_lod + 1, 147287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell -1, -1, 148287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell &sv->key); 149287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 150287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (!sv->handle) { 151287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell assert(0); 152287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv->key.cachable = 0; 153287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell sv->handle = tex->handle; 154912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz debug_reference(&sv->reference, 155912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz (debug_reference_descriptor)svga_debug_describe_sampler_view, 0); 156287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell return sv; 157287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 158287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 159287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pipe_mutex_lock(ss->tex_mutex); 160287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell svga_sampler_view_reference(&tex->cached_view, sv); 161287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell pipe_mutex_unlock(ss->tex_mutex); 162287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 163912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz debug_reference(&sv->reference, 164912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz (debug_reference_descriptor)svga_debug_describe_sampler_view, 0); 165912ad8874200c0a89bd23663dc0de378f6691140Jakob Bornecrantz 166287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell return sv; 167287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell} 168287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 169287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwellvoid 170287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwellsvga_validate_sampler_view(struct svga_context *svga, struct svga_sampler_view *v) 171287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell{ 172287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell struct svga_texture *tex = svga_texture(v->texture); 173287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell unsigned numFaces; 174287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell unsigned age = 0; 175287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell int i, k; 176287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 177287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell assert(svga); 178287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 179287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (v->handle == tex->handle) 180287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell return; 181287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 182287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell age = tex->age; 183287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 184287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if(tex->b.b.target == PIPE_TEXTURE_CUBE) 185287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell numFaces = 6; 186287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell else 187287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell numFaces = 1; 188287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 189287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell for (i = v->min_lod; i <= v->max_lod; i++) { 190287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell for (k = 0; k < numFaces; k++) { 191287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if (v->age < tex->view_age[i]) 192b84590994c4261d85485357263146d5e3d8827ebJosé Fonseca svga_texture_copy_handle(svga, 193287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell tex->handle, 0, 0, 0, i, k, 194287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell v->handle, 0, 0, 0, i - v->min_lod, k, 195287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell u_minify(tex->b.b.width0, i), 196287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell u_minify(tex->b.b.height0, i), 197287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell u_minify(tex->b.b.depth0, i)); 198287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 199287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 200287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 201287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell v->age = age; 202287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell} 203287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 204287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwellvoid 205287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwellsvga_destroy_sampler_view_priv(struct svga_sampler_view *v) 206287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell{ 207287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell struct svga_texture *tex = svga_texture(v->texture); 208287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell 209287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell if(v->handle != tex->handle) { 210287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell struct svga_screen *ss = svga_screen(v->texture->screen); 211287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell SVGA_DBG(DEBUG_DMA, "unref sid %p (sampler view)\n", v->handle); 212287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell svga_screen_surface_destroy(ss, &v->key, &v->handle); 213287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell } 214dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul 215dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul /* Note: we're not refcounting the texture resource here to avoid 216dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul * a circular dependency. 217dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul */ 218dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul v->texture = NULL; 219dce4c368b5536716770e5cabef0321db8537a6d9Brian Paul 220287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell FREE(v); 221287c94ea4987033f9c99a2f91c5750c9083504caKeith Whitwell} 222