/************************************************************************** * * Copyright 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, 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 VMWARE AND/OR ITS SUPPLIERS 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 "vg_context.h" #include "paint.h" #include "renderer.h" #include "shaders_cache.h" #include "shader.h" #include "vg_manager.h" #include "api.h" #include "mask.h" #include "handle.h" #include "pipe/p_context.h" #include "util/u_inlines.h" #include "cso_cache/cso_context.h" #include "util/u_memory.h" #include "util/u_blit.h" #include "util/u_sampler.h" #include "util/u_surface.h" #include "util/u_format.h" struct vg_context *_vg_context = 0; struct vg_context * vg_current_context(void) { return _vg_context; } /** * A depth/stencil rb will be needed regardless of what the visual says. */ static boolean choose_depth_stencil_format(struct vg_context *ctx) { struct pipe_screen *screen = ctx->pipe->screen; enum pipe_format formats[] = { PIPE_FORMAT_Z24_UNORM_S8_UINT, PIPE_FORMAT_S8_UINT_Z24_UNORM, PIPE_FORMAT_NONE }; enum pipe_format *fmt; for (fmt = formats; *fmt != PIPE_FORMAT_NONE; fmt++) { if (screen->is_format_supported(screen, *fmt, PIPE_TEXTURE_2D, 0, PIPE_BIND_DEPTH_STENCIL)) break; } ctx->ds_format = *fmt; return (ctx->ds_format != PIPE_FORMAT_NONE); } void vg_set_current_context(struct vg_context *ctx) { _vg_context = ctx; api_make_dispatch_current((ctx) ? ctx->dispatch : NULL); } struct vg_context * vg_create_context(struct pipe_context *pipe, const void *visual, struct vg_context *share) { struct vg_context *ctx; ctx = CALLOC_STRUCT(vg_context); ctx->pipe = pipe; if (!choose_depth_stencil_format(ctx)) { FREE(ctx); return NULL; } ctx->dispatch = api_create_dispatch(); vg_init_state(&ctx->state.vg); ctx->state.dirty = ALL_DIRTY; ctx->cso_context = cso_create_context(pipe); ctx->default_paint = paint_create(ctx); ctx->state.vg.stroke_paint = ctx->default_paint; ctx->state.vg.fill_paint = ctx->default_paint; ctx->mask.sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; ctx->mask.sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; ctx->mask.sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE; ctx->mask.sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE; ctx->mask.sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST; ctx->mask.sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST; ctx->mask.sampler.normalized_coords = 0; ctx->blend_sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; ctx->blend_sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; ctx->blend_sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE; ctx->blend_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE; ctx->blend_sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST; ctx->blend_sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST; ctx->blend_sampler.normalized_coords = 0; vg_set_error(ctx, VG_NO_ERROR); ctx->owned_objects[VG_OBJECT_PAINT] = cso_hash_create(); ctx->owned_objects[VG_OBJECT_IMAGE] = cso_hash_create(); ctx->owned_objects[VG_OBJECT_MASK] = cso_hash_create(); ctx->owned_objects[VG_OBJECT_FONT] = cso_hash_create(); ctx->owned_objects[VG_OBJECT_PATH] = cso_hash_create(); ctx->renderer = renderer_create(ctx); ctx->sc = shaders_cache_create(ctx); ctx->shader = shader_create(ctx); ctx->blit = util_create_blit(ctx->pipe, ctx->cso_context); return ctx; } void vg_destroy_context(struct vg_context *ctx) { struct pipe_resource **cbuf = &ctx->mask.cbuf; util_destroy_blit(ctx->blit); renderer_destroy(ctx->renderer); shaders_cache_destroy(ctx->sc); shader_destroy(ctx->shader); paint_destroy(ctx->default_paint); if (*cbuf) pipe_resource_reference(cbuf, NULL); if (ctx->mask.union_fs) vg_shader_destroy(ctx, ctx->mask.union_fs); if (ctx->mask.intersect_fs) vg_shader_destroy(ctx, ctx->mask.intersect_fs); if (ctx->mask.subtract_fs) vg_shader_destroy(ctx, ctx->mask.subtract_fs); if (ctx->mask.set_fs) vg_shader_destroy(ctx, ctx->mask.set_fs); cso_release_all(ctx->cso_context); cso_destroy_context(ctx->cso_context); cso_hash_delete(ctx->owned_objects[VG_OBJECT_PAINT]); cso_hash_delete(ctx->owned_objects[VG_OBJECT_IMAGE]); cso_hash_delete(ctx->owned_objects[VG_OBJECT_MASK]); cso_hash_delete(ctx->owned_objects[VG_OBJECT_FONT]); cso_hash_delete(ctx->owned_objects[VG_OBJECT_PATH]); api_destroy_dispatch(ctx->dispatch); FREE(ctx); } void vg_init_object(struct vg_object *obj, struct vg_context *ctx, enum vg_object_type type) { obj->type = type; obj->ctx = ctx; obj->handle = create_handle(obj); } /** free object resources, but not the object itself */ void vg_free_object(struct vg_object *obj) { obj->type = 0; obj->ctx = NULL; destroy_handle(obj->handle); } VGboolean vg_context_is_object_valid(struct vg_context *ctx, enum vg_object_type type, VGHandle handle) { if (ctx) { struct cso_hash *hash = ctx->owned_objects[type]; if (!hash) return VG_FALSE; return cso_hash_contains(hash, (unsigned) handle); } return VG_FALSE; } void vg_context_add_object(struct vg_context *ctx, struct vg_object *obj) { if (ctx) { struct cso_hash *hash = ctx->owned_objects[obj->type]; if (!hash) return; cso_hash_insert(hash, (unsigned) obj->handle, obj); } } void vg_context_remove_object(struct vg_context *ctx, struct vg_object *obj) { if (ctx) { struct cso_hash *hash = ctx->owned_objects[obj->type]; if (!hash) return; cso_hash_take(hash, (unsigned) obj->handle); } } static struct pipe_resource * create_texture(struct pipe_context *pipe, enum pipe_format format, VGint width, VGint height) { struct pipe_resource templ; memset(&templ, 0, sizeof(templ)); if (format != PIPE_FORMAT_NONE) { templ.format = format; } else { templ.format = PIPE_FORMAT_B8G8R8A8_UNORM; } templ.target = PIPE_TEXTURE_2D; templ.width0 = width; templ.height0 = height; templ.depth0 = 1; templ.array_size = 1; templ.last_level = 0; if (util_format_get_component_bits(format, UTIL_FORMAT_COLORSPACE_ZS, 1)) { templ.bind = PIPE_BIND_DEPTH_STENCIL; } else { templ.bind = (PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW); } return pipe->screen->resource_create(pipe->screen, &templ); } static struct pipe_sampler_view * create_tex_and_view(struct pipe_context *pipe, enum pipe_format format, VGint width, VGint height) { struct pipe_resource *texture; struct pipe_sampler_view view_templ; struct pipe_sampler_view *view; texture = create_texture(pipe, format, width, height); if (!texture) return NULL; u_sampler_view_default_template(&view_templ, texture, texture->format); view = pipe->create_sampler_view(pipe, texture, &view_templ); /* want the texture to go away if the view is freed */ pipe_resource_reference(&texture, NULL); return view; } static void vg_context_update_surface_mask_view(struct vg_context *ctx, uint width, uint height) { struct st_framebuffer *stfb = ctx->draw_buffer; struct pipe_sampler_view *old_sampler_view = stfb->surface_mask_view; struct pipe_context *pipe = ctx->pipe; if (old_sampler_view && old_sampler_view->texture->width0 == width && old_sampler_view->texture->height0 == height) return; /* we use PIPE_FORMAT_B8G8R8A8_UNORM because we want to render to this texture and use it as a sampler, so while this wastes some space it makes both of those a lot simpler */ stfb->surface_mask_view = create_tex_and_view(pipe, PIPE_FORMAT_B8G8R8A8_UNORM, width, height); if (!stfb->surface_mask_view) { if (old_sampler_view) pipe_sampler_view_reference(&old_sampler_view, NULL); return; } /* XXX could this call be avoided? */ vg_validate_state(ctx); /* alpha mask starts with 1.f alpha */ mask_fill(0, 0, width, height, 1.f); /* if we had an old surface copy it over */ if (old_sampler_view) { struct pipe_box src_box; u_box_origin_2d(MIN2(old_sampler_view->texture->width0, stfb->surface_mask_view->texture->width0), MIN2(old_sampler_view->texture->height0, stfb->surface_mask_view->texture->height0), &src_box); pipe->resource_copy_region(pipe, stfb->surface_mask_view->texture, 0, 0, 0, 0, old_sampler_view->texture, 0, &src_box); } /* Free the old texture */ if (old_sampler_view) pipe_sampler_view_reference(&old_sampler_view, NULL); } static void vg_context_update_blend_texture_view(struct vg_context *ctx, uint width, uint height) { struct pipe_context *pipe = ctx->pipe; struct st_framebuffer *stfb = ctx->draw_buffer; struct pipe_sampler_view *old = stfb->blend_texture_view; if (old && old->texture->width0 == width && old->texture->height0 == height) return; stfb->blend_texture_view = create_tex_and_view(pipe, PIPE_FORMAT_B8G8R8A8_UNORM, width, height); pipe_sampler_view_reference(&old, NULL); } static boolean vg_context_update_depth_stencil_rb(struct vg_context * ctx, uint width, uint height) { struct st_renderbuffer *dsrb = ctx->draw_buffer->dsrb; struct pipe_context *pipe = ctx->pipe; struct pipe_surface surf_tmpl; if ((dsrb->width == width && dsrb->height == height) && dsrb->texture) return FALSE; /* unreference existing ones */ pipe_surface_reference(&dsrb->surface, NULL); pipe_resource_reference(&dsrb->texture, NULL); dsrb->width = dsrb->height = 0; dsrb->texture = create_texture(pipe, dsrb->format, width, height); if (!dsrb->texture) return TRUE; memset(&surf_tmpl, 0, sizeof(surf_tmpl)); u_surface_default_template(&surf_tmpl, dsrb->texture, PIPE_BIND_DEPTH_STENCIL); dsrb->surface = pipe->create_surface(pipe, dsrb->texture, &surf_tmpl); if (!dsrb->surface) { pipe_resource_reference(&dsrb->texture, NULL); return TRUE; } dsrb->width = width; dsrb->height = height; assert(dsrb->surface->width == width); assert(dsrb->surface->height == height); return TRUE; } void vg_validate_state(struct vg_context *ctx) { struct st_framebuffer *stfb = ctx->draw_buffer; vg_manager_validate_framebuffer(ctx); if (vg_context_update_depth_stencil_rb(ctx, stfb->width, stfb->height)) ctx->state.dirty |= DEPTH_STENCIL_DIRTY; /* blend state depends on fb format and paint color */ if ((ctx->state.dirty & FRAMEBUFFER_DIRTY) || (ctx->state.dirty & PAINT_DIRTY)) ctx->state.dirty |= BLEND_DIRTY; renderer_validate(ctx->renderer, ctx->state.dirty, ctx->draw_buffer, &ctx->state.vg); ctx->state.dirty = 0; shader_set_masking(ctx->shader, ctx->state.vg.masking); shader_set_image_mode(ctx->shader, ctx->state.vg.image_mode); shader_set_color_transform(ctx->shader, ctx->state.vg.color_transform); } VGboolean vg_object_is_valid(VGHandle object, enum vg_object_type type) { struct vg_object *obj = handle_to_object(object); if (obj && is_aligned(obj) && obj->type == type) return VG_TRUE; else return VG_FALSE; } void vg_set_error(struct vg_context *ctx, VGErrorCode code) { /*vgGetError returns the oldest error code provided by * an API call on the current context since the previous * call to vgGetError on that context (or since the creation of the context).*/ if (ctx->_error == VG_NO_ERROR) ctx->_error = code; } static void vg_prepare_blend_texture(struct vg_context *ctx, struct pipe_sampler_view *src) { struct st_framebuffer *stfb = ctx->draw_buffer; struct pipe_surface *surf; struct pipe_surface surf_tmpl; vg_context_update_blend_texture_view(ctx, stfb->width, stfb->height); memset(&surf_tmpl, 0, sizeof(surf_tmpl)); u_surface_default_template(&surf_tmpl, stfb->blend_texture_view->texture, PIPE_BIND_RENDER_TARGET); surf = ctx->pipe->create_surface(ctx->pipe, stfb->blend_texture_view->texture, &surf_tmpl); if (surf) { util_blit_pixels_tex(ctx->blit, src, 0, 0, stfb->width, stfb->height, surf, 0, 0, stfb->width, stfb->height, 0.0, PIPE_TEX_MIPFILTER_NEAREST); pipe_surface_reference(&surf, NULL); } } struct pipe_sampler_view *vg_prepare_blend_surface(struct vg_context *ctx) { struct pipe_context *pipe = ctx->pipe; struct pipe_sampler_view *view; struct pipe_sampler_view view_templ; struct st_framebuffer *stfb = ctx->draw_buffer; struct st_renderbuffer *strb = stfb->strb; vg_validate_state(ctx); u_sampler_view_default_template(&view_templ, strb->texture, strb->texture->format); view = pipe->create_sampler_view(pipe, strb->texture, &view_templ); vg_prepare_blend_texture(ctx, view); pipe_sampler_view_reference(&view, NULL); return stfb->blend_texture_view; } struct pipe_sampler_view *vg_prepare_blend_surface_from_mask(struct vg_context *ctx) { struct st_framebuffer *stfb = ctx->draw_buffer; vg_validate_state(ctx); vg_context_update_surface_mask_view(ctx, stfb->width, stfb->height); vg_prepare_blend_texture(ctx, stfb->surface_mask_view); return stfb->blend_texture_view; } struct pipe_sampler_view *vg_get_surface_mask(struct vg_context *ctx) { struct st_framebuffer *stfb = ctx->draw_buffer; vg_context_update_surface_mask_view(ctx, stfb->width, stfb->height); return stfb->surface_mask_view; } /** * A transformation from window coordinates to paint coordinates. */ VGboolean vg_get_paint_matrix(struct vg_context *ctx, const struct matrix *paint_to_user, const struct matrix *user_to_surface, struct matrix *mat) { struct matrix tmp; /* get user-to-paint matrix */ memcpy(mat, paint_to_user, sizeof(*paint_to_user)); if (!matrix_invert(mat)) return VG_FALSE; /* get surface-to-user matrix */ memcpy(&tmp, user_to_surface, sizeof(*user_to_surface)); if (!matrix_invert(&tmp)) return VG_FALSE; matrix_mult(mat, &tmp); return VG_TRUE; }