/************************************************************************** * * Copyright 2009 VMware, Inc. All Rights Reserved. * Copyright 2010 LunarG, 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 "renderer.h" #include "vg_context.h" #include "pipe/p_context.h" #include "pipe/p_state.h" #include "util/u_inlines.h" #include "pipe/p_screen.h" #include "pipe/p_shader_tokens.h" #include "util/u_draw_quad.h" #include "util/u_simple_shaders.h" #include "util/u_memory.h" #include "util/u_sampler.h" #include "util/u_surface.h" #include "util/u_math.h" #include "util/u_format.h" #include "cso_cache/cso_context.h" #include "tgsi/tgsi_ureg.h" typedef enum { RENDERER_STATE_INIT, RENDERER_STATE_COPY, RENDERER_STATE_DRAWTEX, RENDERER_STATE_SCISSOR, RENDERER_STATE_CLEAR, RENDERER_STATE_FILTER, RENDERER_STATE_POLYGON_STENCIL, RENDERER_STATE_POLYGON_FILL, NUM_RENDERER_STATES } RendererState; typedef enum { RENDERER_VS_PLAIN, RENDERER_VS_COLOR, RENDERER_VS_TEXTURE, NUM_RENDERER_VS } RendererVs; typedef enum { RENDERER_FS_COLOR, RENDERER_FS_TEXTURE, RENDERER_FS_SCISSOR, RENDERER_FS_WHITE, NUM_RENDERER_FS } RendererFs; struct renderer { struct pipe_context *pipe; struct cso_context *cso; VGbitfield dirty; struct { struct pipe_rasterizer_state rasterizer; struct pipe_depth_stencil_alpha_state dsa; struct pipe_framebuffer_state fb; } g3d; struct matrix projection; struct matrix mvp; struct pipe_resource *vs_cbuf; struct pipe_resource *fs_cbuf; VGfloat fs_cbuf_data[32]; VGint fs_cbuf_len; struct pipe_vertex_element velems[2]; VGfloat vertices[4][2][4]; void *cached_vs[NUM_RENDERER_VS]; void *cached_fs[NUM_RENDERER_FS]; RendererState state; /* state data */ union { struct { VGint tex_width; VGint tex_height; } copy; struct { VGint tex_width; VGint tex_height; } drawtex; struct { VGboolean restore_dsa; } scissor; struct { VGboolean use_sampler; VGint tex_width, tex_height; } filter; struct { struct pipe_depth_stencil_alpha_state dsa; VGboolean manual_two_sides; VGboolean restore_dsa; } polygon_stencil; } u; }; /** * Return VG_TRUE if the renderer can use the resource as the asked bindings. */ static VGboolean renderer_can_support(struct renderer *renderer, struct pipe_resource *res, unsigned bindings) { struct pipe_screen *screen = renderer->pipe->screen; return screen->is_format_supported(screen, res->format, res->target, 0, bindings); } /** * Set the model-view-projection matrix used by vertex shaders. */ static void renderer_set_mvp(struct renderer *renderer, const struct matrix *mvp) { struct matrix *cur = &renderer->mvp; struct pipe_resource *cbuf; VGfloat consts[3][4]; VGint i; /* projection only */ if (!mvp) mvp = &renderer->projection; /* re-upload only if necessary */ if (memcmp(cur, mvp, sizeof(*mvp)) == 0) return; /* 3x3 matrix to 3 constant vectors (no Z) */ for (i = 0; i < 3; i++) { consts[i][0] = mvp->m[i + 0]; consts[i][1] = mvp->m[i + 3]; consts[i][2] = 0.0f; consts[i][3] = mvp->m[i + 6]; } cbuf = renderer->vs_cbuf; pipe_resource_reference(&cbuf, NULL); cbuf = pipe_buffer_create(renderer->pipe->screen, PIPE_BIND_CONSTANT_BUFFER, PIPE_USAGE_DEFAULT, sizeof(consts)); if (cbuf) { pipe_buffer_write(renderer->pipe, cbuf, 0, sizeof(consts), consts); } pipe_set_constant_buffer(renderer->pipe, PIPE_SHADER_VERTEX, 0, cbuf); memcpy(cur, mvp, sizeof(*mvp)); renderer->vs_cbuf = cbuf; } /** * Create a simple vertex shader that passes through position and the given * attribute. */ static void *create_passthrough_vs(struct pipe_context *pipe, int semantic_name) { struct ureg_program *ureg; struct ureg_src src[2], constants[3]; struct ureg_dst dst[2], tmp; int i; ureg = ureg_create(TGSI_PROCESSOR_VERTEX); if (!ureg) return NULL; /* position is in user coordinates */ src[0] = ureg_DECL_vs_input(ureg, 0); dst[0] = ureg_DECL_output(ureg, TGSI_SEMANTIC_POSITION, 0); tmp = ureg_DECL_temporary(ureg); for (i = 0; i < Elements(constants); i++) constants[i] = ureg_DECL_constant(ureg, i); /* transform to clipped coordinates */ ureg_DP4(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_X), src[0], constants[0]); ureg_DP4(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_Y), src[0], constants[1]); ureg_MOV(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_Z), src[0]); ureg_DP4(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_W), src[0], constants[2]); ureg_MOV(ureg, dst[0], ureg_src(tmp)); if (semantic_name >= 0) { src[1] = ureg_DECL_vs_input(ureg, 1); dst[1] = ureg_DECL_output(ureg, semantic_name, 0); ureg_MOV(ureg, dst[1], src[1]); } ureg_END(ureg); return ureg_create_shader_and_destroy(ureg, pipe); } /** * Set renderer vertex shader. * * This function modifies vertex_shader state. */ static void renderer_set_vs(struct renderer *r, RendererVs id) { /* create as needed */ if (!r->cached_vs[id]) { int semantic_name = -1; switch (id) { case RENDERER_VS_PLAIN: break; case RENDERER_VS_COLOR: semantic_name = TGSI_SEMANTIC_COLOR; break; case RENDERER_VS_TEXTURE: semantic_name = TGSI_SEMANTIC_GENERIC; break; default: assert(!"Unknown renderer vs id"); break; } r->cached_vs[id] = create_passthrough_vs(r->pipe, semantic_name); } cso_set_vertex_shader_handle(r->cso, r->cached_vs[id]); } /** * Create a simple fragment shader that sets the depth to 0.0f. */ static void *create_scissor_fs(struct pipe_context *pipe) { struct ureg_program *ureg; struct ureg_dst out; struct ureg_src imm; ureg = ureg_create(TGSI_PROCESSOR_FRAGMENT); out = ureg_DECL_output(ureg, TGSI_SEMANTIC_POSITION, 0); imm = ureg_imm4f(ureg, 0.0f, 0.0f, 0.0f, 0.0f); ureg_MOV(ureg, ureg_writemask(out, TGSI_WRITEMASK_Z), imm); ureg_END(ureg); return ureg_create_shader_and_destroy(ureg, pipe); } /** * Create a simple fragment shader that sets the color to white. */ static void *create_white_fs(struct pipe_context *pipe) { struct ureg_program *ureg; struct ureg_dst out; struct ureg_src imm; ureg = ureg_create(TGSI_PROCESSOR_FRAGMENT); out = ureg_DECL_output(ureg, TGSI_SEMANTIC_COLOR, 0); imm = ureg_imm4f(ureg, 1.0f, 1.0f, 1.0f, 1.0f); ureg_MOV(ureg, out, imm); ureg_END(ureg); return ureg_create_shader_and_destroy(ureg, pipe); } /** * Set renderer fragment shader. * * This function modifies fragment_shader state. */ static void renderer_set_fs(struct renderer *r, RendererFs id) { /* create as needed */ if (!r->cached_fs[id]) { void *fs = NULL; switch (id) { case RENDERER_FS_COLOR: fs = util_make_fragment_passthrough_shader(r->pipe, TGSI_SEMANTIC_COLOR, TGSI_INTERPOLATE_PERSPECTIVE, TRUE); break; case RENDERER_FS_TEXTURE: fs = util_make_fragment_tex_shader(r->pipe, TGSI_TEXTURE_2D, TGSI_INTERPOLATE_LINEAR); break; case RENDERER_FS_SCISSOR: fs = create_scissor_fs(r->pipe); break; case RENDERER_FS_WHITE: fs = create_white_fs(r->pipe); break; default: assert(!"Unknown renderer fs id"); break; } r->cached_fs[id] = fs; } cso_set_fragment_shader_handle(r->cso, r->cached_fs[id]); } typedef enum { VEGA_Y0_TOP, VEGA_Y0_BOTTOM } VegaOrientation; static void vg_set_viewport(struct renderer *r, VegaOrientation orientation) { const struct pipe_framebuffer_state *fb = &r->g3d.fb; struct pipe_viewport_state viewport; VGfloat y_scale = (orientation == VEGA_Y0_BOTTOM) ? -2.f : 2.f; viewport.scale[0] = fb->width / 2.f; viewport.scale[1] = fb->height / y_scale; viewport.scale[2] = 1.0; viewport.translate[0] = fb->width / 2.f; viewport.translate[1] = fb->height / 2.f; viewport.translate[2] = 0.0; cso_set_viewport(r->cso, &viewport); } /** * Set renderer target. * * This function modifies framebuffer and viewport states. */ static void renderer_set_target(struct renderer *r, struct pipe_surface *cbuf, struct pipe_surface *zsbuf, VGboolean y0_top) { struct pipe_framebuffer_state fb; memset(&fb, 0, sizeof(fb)); fb.width = cbuf->width; fb.height = cbuf->height; fb.cbufs[0] = cbuf; fb.nr_cbufs = 1; fb.zsbuf = zsbuf; cso_set_framebuffer(r->cso, &fb); vg_set_viewport(r, (y0_top) ? VEGA_Y0_TOP : VEGA_Y0_BOTTOM); } /** * Set renderer blend state. Blending is disabled. * * This function modifies blend state. */ static void renderer_set_blend(struct renderer *r, VGbitfield channel_mask) { struct pipe_blend_state blend; memset(&blend, 0, sizeof(blend)); blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; if (channel_mask & VG_RED) blend.rt[0].colormask |= PIPE_MASK_R; if (channel_mask & VG_GREEN) blend.rt[0].colormask |= PIPE_MASK_G; if (channel_mask & VG_BLUE) blend.rt[0].colormask |= PIPE_MASK_B; if (channel_mask & VG_ALPHA) blend.rt[0].colormask |= PIPE_MASK_A; cso_set_blend(r->cso, &blend); } /** * Set renderer sampler and view states. * * This function modifies samplers and fragment_sampler_views states. */ static void renderer_set_samplers(struct renderer *r, uint num_views, struct pipe_sampler_view **views) { struct pipe_sampler_state sampler; unsigned tex_filter = PIPE_TEX_FILTER_NEAREST; unsigned tex_wrap = PIPE_TEX_WRAP_CLAMP_TO_EDGE; uint i; memset(&sampler, 0, sizeof(sampler)); sampler.min_img_filter = tex_filter; sampler.mag_img_filter = tex_filter; sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE; sampler.wrap_s = tex_wrap; sampler.wrap_t = tex_wrap; sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE; sampler.normalized_coords = 1; /* set samplers */ for (i = 0; i < num_views; i++) cso_single_sampler(r->cso, PIPE_SHADER_FRAGMENT, i, &sampler); cso_single_sampler_done(r->cso, PIPE_SHADER_FRAGMENT); /* set views */ cso_set_sampler_views(r->cso, PIPE_SHADER_FRAGMENT, num_views, views); } /** * Set custom renderer fragment shader, and optionally set samplers and views * and upload the fragment constant buffer. * * This function modifies fragment_shader, samplers and fragment_sampler_views * states. */ static void renderer_set_custom_fs(struct renderer *renderer, void *fs, const struct pipe_sampler_state **samplers, struct pipe_sampler_view **views, VGint num_samplers, const void *const_buffer, VGint const_buffer_len) { cso_set_fragment_shader_handle(renderer->cso, fs); /* set samplers and views */ if (num_samplers) { cso_set_samplers(renderer->cso, PIPE_SHADER_FRAGMENT, num_samplers, samplers); cso_set_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT, num_samplers, views); } /* upload fs constant buffer */ if (const_buffer_len) { struct pipe_resource *cbuf = renderer->fs_cbuf; if (!cbuf || renderer->fs_cbuf_len != const_buffer_len || memcmp(renderer->fs_cbuf_data, const_buffer, const_buffer_len)) { pipe_resource_reference(&cbuf, NULL); cbuf = pipe_buffer_create(renderer->pipe->screen, PIPE_BIND_CONSTANT_BUFFER, PIPE_USAGE_DEFAULT, const_buffer_len); pipe_buffer_write(renderer->pipe, cbuf, 0, const_buffer_len, const_buffer); pipe_set_constant_buffer(renderer->pipe, PIPE_SHADER_FRAGMENT, 0, cbuf); renderer->fs_cbuf = cbuf; if (const_buffer_len <= sizeof(renderer->fs_cbuf_data)) { memcpy(renderer->fs_cbuf_data, const_buffer, const_buffer_len); renderer->fs_cbuf_len = const_buffer_len; } else { renderer->fs_cbuf_len = 0; } } } } /** * Setup renderer quad position. */ static void renderer_quad_pos(struct renderer *r, VGfloat x0, VGfloat y0, VGfloat x1, VGfloat y1, VGboolean scissor) { VGfloat z; /* the depth test is used for scissoring */ z = (scissor) ? 0.0f : 1.0f; /* positions */ r->vertices[0][0][0] = x0; r->vertices[0][0][1] = y0; r->vertices[0][0][2] = z; r->vertices[1][0][0] = x1; r->vertices[1][0][1] = y0; r->vertices[1][0][2] = z; r->vertices[2][0][0] = x1; r->vertices[2][0][1] = y1; r->vertices[2][0][2] = z; r->vertices[3][0][0] = x0; r->vertices[3][0][1] = y1; r->vertices[3][0][2] = z; } /** * Setup renderer quad texture coordinates. */ static void renderer_quad_texcoord(struct renderer *r, VGfloat x0, VGfloat y0, VGfloat x1, VGfloat y1, VGint tex_width, VGint tex_height) { VGfloat s0, t0, s1, t1, r0, q0; VGint i; s0 = x0 / tex_width; s1 = x1 / tex_width; t0 = y0 / tex_height; t1 = y1 / tex_height; r0 = 0.0f; q0 = 1.0f; /* texcoords */ r->vertices[0][1][0] = s0; r->vertices[0][1][1] = t0; r->vertices[1][1][0] = s1; r->vertices[1][1][1] = t0; r->vertices[2][1][0] = s1; r->vertices[2][1][1] = t1; r->vertices[3][1][0] = s0; r->vertices[3][1][1] = t1; for (i = 0; i < 4; i++) { r->vertices[i][1][2] = r0; r->vertices[i][1][3] = q0; } } /** * Draw renderer quad. */ static void renderer_quad_draw(struct renderer *r) { util_draw_user_vertex_buffer(r->cso, r->vertices, PIPE_PRIM_TRIANGLE_FAN, Elements(r->vertices), /* verts */ Elements(r->vertices[0])); /* attribs/vert */ } /** * Prepare the renderer for copying. */ VGboolean renderer_copy_begin(struct renderer *renderer, struct pipe_surface *dst, VGboolean y0_top, struct pipe_sampler_view *src) { assert(renderer->state == RENDERER_STATE_INIT); /* sanity check */ if (!renderer_can_support(renderer, dst->texture, PIPE_BIND_RENDER_TARGET) || !renderer_can_support(renderer, src->texture, PIPE_BIND_SAMPLER_VIEW)) return VG_FALSE; cso_save_framebuffer(renderer->cso); cso_save_viewport(renderer->cso); cso_save_blend(renderer->cso); cso_save_samplers(renderer->cso, PIPE_SHADER_FRAGMENT); cso_save_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT); cso_save_fragment_shader(renderer->cso); cso_save_vertex_shader(renderer->cso); renderer_set_target(renderer, dst, NULL, y0_top); renderer_set_blend(renderer, ~0); renderer_set_samplers(renderer, 1, &src); renderer_set_fs(renderer, RENDERER_FS_TEXTURE); renderer_set_vs(renderer, RENDERER_VS_TEXTURE); renderer_set_mvp(renderer, NULL); /* remember the texture size */ renderer->u.copy.tex_width = src->texture->width0; renderer->u.copy.tex_height = src->texture->height0; renderer->state = RENDERER_STATE_COPY; return VG_TRUE; } /** * Draw into the destination rectangle given by (x, y, w, h). The texture is * sampled from within the rectangle given by (sx, sy, sw, sh). * * The coordinates are in surface coordinates. */ void renderer_copy(struct renderer *renderer, VGint x, VGint y, VGint w, VGint h, VGint sx, VGint sy, VGint sw, VGint sh) { assert(renderer->state == RENDERER_STATE_COPY); /* there is no depth buffer for scissoring anyway */ renderer_quad_pos(renderer, x, y, x + w, y + h, VG_FALSE); renderer_quad_texcoord(renderer, sx, sy, sx + sw, sy + sh, renderer->u.copy.tex_width, renderer->u.copy.tex_height); renderer_quad_draw(renderer); } /** * End copying and restore the states. */ void renderer_copy_end(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_COPY); cso_restore_framebuffer(renderer->cso); cso_restore_viewport(renderer->cso); cso_restore_blend(renderer->cso); cso_restore_samplers(renderer->cso, PIPE_SHADER_FRAGMENT); cso_restore_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT); cso_restore_fragment_shader(renderer->cso); cso_restore_vertex_shader(renderer->cso); renderer->state = RENDERER_STATE_INIT; } /** * Prepare the renderer for textured drawing. */ VGboolean renderer_drawtex_begin(struct renderer *renderer, struct pipe_sampler_view *src) { assert(renderer->state == RENDERER_STATE_INIT); if (!renderer_can_support(renderer, src->texture, PIPE_BIND_SAMPLER_VIEW)) return VG_FALSE; cso_save_blend(renderer->cso); cso_save_samplers(renderer->cso, PIPE_SHADER_FRAGMENT); cso_save_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT); cso_save_fragment_shader(renderer->cso); cso_save_vertex_shader(renderer->cso); renderer_set_blend(renderer, ~0); renderer_set_samplers(renderer, 1, &src); renderer_set_fs(renderer, RENDERER_FS_TEXTURE); renderer_set_vs(renderer, RENDERER_VS_TEXTURE); renderer_set_mvp(renderer, NULL); /* remember the texture size */ renderer->u.drawtex.tex_width = src->texture->width0; renderer->u.drawtex.tex_height = src->texture->height0; renderer->state = RENDERER_STATE_DRAWTEX; return VG_TRUE; } /** * Draw into the destination rectangle given by (x, y, w, h). The texture is * sampled from within the rectangle given by (sx, sy, sw, sh). * * The coordinates are in surface coordinates. */ void renderer_drawtex(struct renderer *renderer, VGint x, VGint y, VGint w, VGint h, VGint sx, VGint sy, VGint sw, VGint sh) { assert(renderer->state == RENDERER_STATE_DRAWTEX); /* with scissoring */ renderer_quad_pos(renderer, x, y, x + w, y + h, VG_TRUE); renderer_quad_texcoord(renderer, sx, sy, sx + sw, sy + sh, renderer->u.drawtex.tex_width, renderer->u.drawtex.tex_height); renderer_quad_draw(renderer); } /** * End textured drawing and restore the states. */ void renderer_drawtex_end(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_DRAWTEX); cso_restore_blend(renderer->cso); cso_restore_samplers(renderer->cso, PIPE_SHADER_FRAGMENT); cso_restore_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT); cso_restore_fragment_shader(renderer->cso); cso_restore_vertex_shader(renderer->cso); renderer->state = RENDERER_STATE_INIT; } /** * Prepare the renderer for scissor update. This will reset the depth buffer * to 1.0f. */ VGboolean renderer_scissor_begin(struct renderer *renderer, VGboolean restore_dsa) { struct pipe_depth_stencil_alpha_state dsa; assert(renderer->state == RENDERER_STATE_INIT); if (restore_dsa) cso_save_depth_stencil_alpha(renderer->cso); cso_save_blend(renderer->cso); cso_save_fragment_shader(renderer->cso); /* enable depth writes */ memset(&dsa, 0, sizeof(dsa)); dsa.depth.enabled = 1; dsa.depth.writemask = 1; dsa.depth.func = PIPE_FUNC_ALWAYS; cso_set_depth_stencil_alpha(renderer->cso, &dsa); /* disable color writes */ renderer_set_blend(renderer, 0); renderer_set_fs(renderer, RENDERER_FS_SCISSOR); renderer_set_mvp(renderer, NULL); renderer->u.scissor.restore_dsa = restore_dsa; renderer->state = RENDERER_STATE_SCISSOR; /* clear the depth buffer to 1.0f */ renderer->pipe->clear(renderer->pipe, PIPE_CLEAR_DEPTHSTENCIL, NULL, 1.0f, 0); return VG_TRUE; } /** * Add a scissor rectangle. Depth values inside the rectangle will be set to * 0.0f. */ void renderer_scissor(struct renderer *renderer, VGint x, VGint y, VGint width, VGint height) { assert(renderer->state == RENDERER_STATE_SCISSOR); renderer_quad_pos(renderer, x, y, x + width, y + height, VG_FALSE); renderer_quad_draw(renderer); } /** * End scissor update and restore the states. */ void renderer_scissor_end(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_SCISSOR); if (renderer->u.scissor.restore_dsa) cso_restore_depth_stencil_alpha(renderer->cso); cso_restore_blend(renderer->cso); cso_restore_fragment_shader(renderer->cso); renderer->state = RENDERER_STATE_INIT; } /** * Prepare the renderer for clearing. */ VGboolean renderer_clear_begin(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_INIT); cso_save_blend(renderer->cso); cso_save_fragment_shader(renderer->cso); cso_save_vertex_shader(renderer->cso); renderer_set_blend(renderer, ~0); renderer_set_fs(renderer, RENDERER_FS_COLOR); renderer_set_vs(renderer, RENDERER_VS_COLOR); renderer_set_mvp(renderer, NULL); renderer->state = RENDERER_STATE_CLEAR; return VG_TRUE; } /** * Clear the framebuffer with the specified region and color. * * The coordinates are in surface coordinates. */ void renderer_clear(struct renderer *renderer, VGint x, VGint y, VGint width, VGint height, const VGfloat color[4]) { VGuint i; assert(renderer->state == RENDERER_STATE_CLEAR); renderer_quad_pos(renderer, x, y, x + width, y + height, VG_TRUE); for (i = 0; i < 4; i++) memcpy(renderer->vertices[i][1], color, sizeof(VGfloat) * 4); renderer_quad_draw(renderer); } /** * End clearing and retore the states. */ void renderer_clear_end(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_CLEAR); cso_restore_blend(renderer->cso); cso_restore_fragment_shader(renderer->cso); cso_restore_vertex_shader(renderer->cso); renderer->state = RENDERER_STATE_INIT; } /** * Prepare the renderer for image filtering. */ VGboolean renderer_filter_begin(struct renderer *renderer, struct pipe_resource *dst, VGboolean y0_top, VGbitfield channel_mask, const struct pipe_sampler_state **samplers, struct pipe_sampler_view **views, VGint num_samplers, void *fs, const void *const_buffer, VGint const_buffer_len) { struct pipe_surface *surf, surf_tmpl; assert(renderer->state == RENDERER_STATE_INIT); if (!fs) return VG_FALSE; if (!renderer_can_support(renderer, dst, PIPE_BIND_RENDER_TARGET)) return VG_FALSE; u_surface_default_template(&surf_tmpl, dst); surf = renderer->pipe->create_surface(renderer->pipe, dst, &surf_tmpl); if (!surf) return VG_FALSE; cso_save_framebuffer(renderer->cso); cso_save_viewport(renderer->cso); cso_save_blend(renderer->cso); /* set the image as the target */ renderer_set_target(renderer, surf, NULL, y0_top); pipe_surface_reference(&surf, NULL); renderer_set_blend(renderer, channel_mask); if (num_samplers) { struct pipe_resource *tex; cso_save_samplers(renderer->cso, PIPE_SHADER_FRAGMENT); cso_save_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT); cso_save_fragment_shader(renderer->cso); cso_save_vertex_shader(renderer->cso); renderer_set_custom_fs(renderer, fs, samplers, views, num_samplers, const_buffer, const_buffer_len); renderer_set_vs(renderer, RENDERER_VS_TEXTURE); tex = views[0]->texture; renderer->u.filter.tex_width = tex->width0; renderer->u.filter.tex_height = tex->height0; renderer->u.filter.use_sampler = VG_TRUE; } else { cso_save_fragment_shader(renderer->cso); renderer_set_custom_fs(renderer, fs, NULL, NULL, 0, const_buffer, const_buffer_len); renderer->u.filter.use_sampler = VG_FALSE; } renderer_set_mvp(renderer, NULL); renderer->state = RENDERER_STATE_FILTER; return VG_TRUE; } /** * Draw into a rectangle of the destination with the specified region of the * texture(s). * * The coordinates are in surface coordinates. */ void renderer_filter(struct renderer *renderer, VGint x, VGint y, VGint w, VGint h, VGint sx, VGint sy, VGint sw, VGint sh) { assert(renderer->state == RENDERER_STATE_FILTER); renderer_quad_pos(renderer, x, y, x + w, y + h, VG_FALSE); if (renderer->u.filter.use_sampler) { renderer_quad_texcoord(renderer, sx, sy, sx + sw, sy + sh, renderer->u.filter.tex_width, renderer->u.filter.tex_height); } renderer_quad_draw(renderer); } /** * End image filtering and restore the states. */ void renderer_filter_end(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_FILTER); if (renderer->u.filter.use_sampler) { cso_restore_samplers(renderer->cso, PIPE_SHADER_FRAGMENT); cso_restore_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT); cso_restore_vertex_shader(renderer->cso); } cso_restore_framebuffer(renderer->cso); cso_restore_viewport(renderer->cso); cso_restore_blend(renderer->cso); cso_restore_fragment_shader(renderer->cso); renderer->state = RENDERER_STATE_INIT; } /** * Prepare the renderer for polygon silhouette rendering. */ VGboolean renderer_polygon_stencil_begin(struct renderer *renderer, struct pipe_vertex_element *velem, VGFillRule rule, VGboolean restore_dsa) { struct pipe_depth_stencil_alpha_state *dsa; VGboolean manual_two_sides; assert(renderer->state == RENDERER_STATE_INIT); cso_save_vertex_elements(renderer->cso); cso_save_blend(renderer->cso); cso_save_depth_stencil_alpha(renderer->cso); cso_set_vertex_elements(renderer->cso, 1, velem); /* disable color writes */ renderer_set_blend(renderer, 0); manual_two_sides = VG_FALSE; dsa = &renderer->u.polygon_stencil.dsa; memset(dsa, 0, sizeof(*dsa)); if (rule == VG_EVEN_ODD) { dsa->stencil[0].enabled = 1; dsa->stencil[0].writemask = 1; dsa->stencil[0].fail_op = PIPE_STENCIL_OP_KEEP; dsa->stencil[0].zfail_op = PIPE_STENCIL_OP_KEEP; dsa->stencil[0].zpass_op = PIPE_STENCIL_OP_INVERT; dsa->stencil[0].func = PIPE_FUNC_ALWAYS; dsa->stencil[0].valuemask = ~0; } else { assert(rule == VG_NON_ZERO); /* front face */ dsa->stencil[0].enabled = 1; dsa->stencil[0].writemask = ~0; dsa->stencil[0].fail_op = PIPE_STENCIL_OP_KEEP; dsa->stencil[0].zfail_op = PIPE_STENCIL_OP_KEEP; dsa->stencil[0].zpass_op = PIPE_STENCIL_OP_INCR_WRAP; dsa->stencil[0].func = PIPE_FUNC_ALWAYS; dsa->stencil[0].valuemask = ~0; if (renderer->pipe->screen->get_param(renderer->pipe->screen, PIPE_CAP_TWO_SIDED_STENCIL)) { /* back face */ dsa->stencil[1] = dsa->stencil[0]; dsa->stencil[1].zpass_op = PIPE_STENCIL_OP_DECR_WRAP; } else { manual_two_sides = VG_TRUE; } } cso_set_depth_stencil_alpha(renderer->cso, dsa); if (manual_two_sides) cso_save_rasterizer(renderer->cso); renderer->u.polygon_stencil.manual_two_sides = manual_two_sides; renderer->u.polygon_stencil.restore_dsa = restore_dsa; renderer->state = RENDERER_STATE_POLYGON_STENCIL; return VG_TRUE; } /** * Render a polygon silhouette to stencil buffer. */ void renderer_polygon_stencil(struct renderer *renderer, struct pipe_vertex_buffer *vbuf, VGuint mode, VGuint start, VGuint count) { assert(renderer->state == RENDERER_STATE_POLYGON_STENCIL); cso_set_vertex_buffers(renderer->cso, 0, 1, vbuf); if (!renderer->u.polygon_stencil.manual_two_sides) { cso_draw_arrays(renderer->cso, mode, start, count); } else { struct pipe_rasterizer_state raster; struct pipe_depth_stencil_alpha_state dsa; raster = renderer->g3d.rasterizer; dsa = renderer->u.polygon_stencil.dsa; /* front */ raster.cull_face = PIPE_FACE_BACK; dsa.stencil[0].zpass_op = PIPE_STENCIL_OP_INCR_WRAP; cso_set_rasterizer(renderer->cso, &raster); cso_set_depth_stencil_alpha(renderer->cso, &dsa); cso_draw_arrays(renderer->cso, mode, start, count); /* back */ raster.cull_face = PIPE_FACE_FRONT; dsa.stencil[0].zpass_op = PIPE_STENCIL_OP_DECR_WRAP; cso_set_rasterizer(renderer->cso, &raster); cso_set_depth_stencil_alpha(renderer->cso, &dsa); cso_draw_arrays(renderer->cso, mode, start, count); } } /** * End polygon silhouette rendering. */ void renderer_polygon_stencil_end(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_POLYGON_STENCIL); if (renderer->u.polygon_stencil.manual_two_sides) cso_restore_rasterizer(renderer->cso); cso_restore_vertex_elements(renderer->cso); /* restore color writes */ cso_restore_blend(renderer->cso); if (renderer->u.polygon_stencil.restore_dsa) cso_restore_depth_stencil_alpha(renderer->cso); renderer->state = RENDERER_STATE_INIT; } /** * Prepare the renderer for polygon filling. */ VGboolean renderer_polygon_fill_begin(struct renderer *renderer, VGboolean save_dsa) { struct pipe_depth_stencil_alpha_state dsa; assert(renderer->state == RENDERER_STATE_INIT); if (save_dsa) cso_save_depth_stencil_alpha(renderer->cso); /* setup stencil ops */ memset(&dsa, 0, sizeof(dsa)); dsa.stencil[0].enabled = 1; dsa.stencil[0].func = PIPE_FUNC_NOTEQUAL; dsa.stencil[0].fail_op = PIPE_STENCIL_OP_REPLACE; dsa.stencil[0].zfail_op = PIPE_STENCIL_OP_REPLACE; dsa.stencil[0].zpass_op = PIPE_STENCIL_OP_REPLACE; dsa.stencil[0].valuemask = ~0; dsa.stencil[0].writemask = ~0; dsa.depth = renderer->g3d.dsa.depth; cso_set_depth_stencil_alpha(renderer->cso, &dsa); renderer->state = RENDERER_STATE_POLYGON_FILL; return VG_TRUE; } /** * Fill a polygon. */ void renderer_polygon_fill(struct renderer *renderer, VGfloat min_x, VGfloat min_y, VGfloat max_x, VGfloat max_y) { assert(renderer->state == RENDERER_STATE_POLYGON_FILL); renderer_quad_pos(renderer, min_x, min_y, max_x, max_y, VG_TRUE); renderer_quad_draw(renderer); } /** * End polygon filling. */ void renderer_polygon_fill_end(struct renderer *renderer) { assert(renderer->state == RENDERER_STATE_POLYGON_FILL); cso_restore_depth_stencil_alpha(renderer->cso); renderer->state = RENDERER_STATE_INIT; } struct renderer * renderer_create(struct vg_context *owner) { struct renderer *renderer; struct pipe_rasterizer_state *raster; struct pipe_stencil_ref sr; VGint i; renderer = CALLOC_STRUCT(renderer); if (!renderer) return NULL; renderer->pipe = owner->pipe; renderer->cso = owner->cso_context; /* init vertex data that doesn't change */ for (i = 0; i < 4; i++) renderer->vertices[i][0][3] = 1.0f; /* w */ for (i = 0; i < 2; i++) { renderer->velems[i].src_offset = i * 4 * sizeof(float); renderer->velems[i].instance_divisor = 0; renderer->velems[i].vertex_buffer_index = 0; renderer->velems[i].src_format = PIPE_FORMAT_R32G32B32A32_FLOAT; } cso_set_vertex_elements(renderer->cso, 2, renderer->velems); /* GL rasterization rules */ raster = &renderer->g3d.rasterizer; memset(raster, 0, sizeof(*raster)); raster->half_pixel_center = 1; raster->bottom_edge_rule = 1; raster->depth_clip = 1; cso_set_rasterizer(renderer->cso, raster); /* fixed at 0 */ memset(&sr, 0, sizeof(sr)); cso_set_stencil_ref(renderer->cso, &sr); renderer_set_vs(renderer, RENDERER_VS_PLAIN); renderer->state = RENDERER_STATE_INIT; return renderer; } void renderer_destroy(struct renderer *ctx) { int i; for (i = 0; i < NUM_RENDERER_VS; i++) { if (ctx->cached_vs[i]) cso_delete_vertex_shader(ctx->cso, ctx->cached_vs[i]); } for (i = 0; i < NUM_RENDERER_FS; i++) { if (ctx->cached_fs[i]) cso_delete_fragment_shader(ctx->cso, ctx->cached_fs[i]); } pipe_resource_reference(&ctx->vs_cbuf, NULL); pipe_resource_reference(&ctx->fs_cbuf, NULL); FREE(ctx); } static void update_clip_state(struct renderer *renderer, const struct vg_state *state) { struct pipe_depth_stencil_alpha_state *dsa = &renderer->g3d.dsa; memset(dsa, 0, sizeof(struct pipe_depth_stencil_alpha_state)); if (state->scissoring) { struct pipe_framebuffer_state *fb = &renderer->g3d.fb; int i; renderer_scissor_begin(renderer, VG_FALSE); for (i = 0; i < state->scissor_rects_num; ++i) { const float x = state->scissor_rects[i * 4 + 0].f; const float y = state->scissor_rects[i * 4 + 1].f; const float width = state->scissor_rects[i * 4 + 2].f; const float height = state->scissor_rects[i * 4 + 3].f; VGint x0, y0, x1, y1, iw, ih; x0 = (VGint) x; y0 = (VGint) y; if (x0 < 0) x0 = 0; if (y0 < 0) y0 = 0; /* note that x1 and y1 are exclusive */ x1 = (VGint) ceilf(x + width); y1 = (VGint) ceilf(y + height); if (x1 > fb->width) x1 = fb->width; if (y1 > fb->height) y1 = fb->height; iw = x1 - x0; ih = y1 - y0; if (iw > 0 && ih> 0 ) renderer_scissor(renderer, x0, y0, iw, ih); } renderer_scissor_end(renderer); dsa->depth.enabled = 1; /* glEnable(GL_DEPTH_TEST); */ dsa->depth.writemask = 0;/*glDepthMask(FALSE);*/ dsa->depth.func = PIPE_FUNC_GEQUAL; } } static void renderer_validate_blend(struct renderer *renderer, const struct vg_state *state, enum pipe_format fb_format) { struct pipe_blend_state blend; memset(&blend, 0, sizeof(blend)); blend.rt[0].colormask = PIPE_MASK_RGBA; blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; /* TODO alpha masking happens after blending? */ switch (state->blend_mode) { case VG_BLEND_SRC: blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; break; case VG_BLEND_SRC_OVER: /* use the blend state only when there is no alpha channel */ if (!util_format_has_alpha(fb_format)) { blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_SRC_ALPHA; blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA; blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA; blend.rt[0].blend_enable = 1; } break; case VG_BLEND_SRC_IN: blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_DST_ALPHA; blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; blend.rt[0].blend_enable = 1; break; case VG_BLEND_DST_IN: blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ZERO; blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ZERO; blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ONE; blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_SRC_ALPHA; blend.rt[0].blend_enable = 1; break; case VG_BLEND_DST_OVER: case VG_BLEND_MULTIPLY: case VG_BLEND_SCREEN: case VG_BLEND_DARKEN: case VG_BLEND_LIGHTEN: case VG_BLEND_ADDITIVE: /* need a shader */ break; default: assert(!"not implemented blend mode"); break; } cso_set_blend(renderer->cso, &blend); } /** * Propogate OpenVG state changes to the renderer. Only framebuffer, blending * and scissoring states are relevant here. */ void renderer_validate(struct renderer *renderer, VGbitfield dirty, const struct st_framebuffer *stfb, const struct vg_state *state) { assert(renderer->state == RENDERER_STATE_INIT); dirty |= renderer->dirty; renderer->dirty = 0; if (dirty & FRAMEBUFFER_DIRTY) { struct pipe_framebuffer_state *fb = &renderer->g3d.fb; struct matrix *proj = &renderer->projection; memset(fb, 0, sizeof(struct pipe_framebuffer_state)); fb->width = stfb->width; fb->height = stfb->height; fb->nr_cbufs = 1; fb->cbufs[0] = stfb->strb->surface; fb->zsbuf = stfb->dsrb->surface; cso_set_framebuffer(renderer->cso, fb); vg_set_viewport(renderer, VEGA_Y0_BOTTOM); matrix_load_identity(proj); matrix_translate(proj, -1.0f, -1.0f); matrix_scale(proj, 2.0f / fb->width, 2.0f / fb->height); /* we also got a new depth buffer */ if (dirty & DEPTH_STENCIL_DIRTY) { renderer->pipe->clear(renderer->pipe, PIPE_CLEAR_DEPTHSTENCIL, NULL, 0.0, 0); } } /* must be last because it renders to the depth buffer*/ if (dirty & DEPTH_STENCIL_DIRTY) { update_clip_state(renderer, state); cso_set_depth_stencil_alpha(renderer->cso, &renderer->g3d.dsa); } if (dirty & BLEND_DIRTY) renderer_validate_blend(renderer, state, stfb->strb->format); } /** * Prepare the renderer for OpenVG pipeline. */ void renderer_validate_for_shader(struct renderer *renderer, const struct pipe_sampler_state **samplers, struct pipe_sampler_view **views, VGint num_samplers, const struct matrix *modelview, void *fs, const void *const_buffer, VGint const_buffer_len) { struct matrix mvp = renderer->projection; /* will be used in POLYGON_STENCIL and POLYGON_FILL */ matrix_mult(&mvp, modelview); renderer_set_mvp(renderer, &mvp); renderer_set_custom_fs(renderer, fs, samplers, views, num_samplers, const_buffer, const_buffer_len); } void renderer_validate_for_mask_rendering(struct renderer *renderer, struct pipe_surface *dst, const struct matrix *modelview) { struct matrix mvp = renderer->projection; /* will be used in POLYGON_STENCIL and POLYGON_FILL */ matrix_mult(&mvp, modelview); renderer_set_mvp(renderer, &mvp); renderer_set_target(renderer, dst, renderer->g3d.fb.zsbuf, VG_FALSE); renderer_set_blend(renderer, ~0); renderer_set_fs(renderer, RENDERER_FS_WHITE); /* set internal dirty flags (hacky!) */ renderer->dirty = FRAMEBUFFER_DIRTY | BLEND_DIRTY; } void renderer_copy_surface(struct renderer *ctx, struct pipe_surface *src, int srcX0, int srcY0, int srcX1, int srcY1, struct pipe_surface *dst, int dstX0, int dstY0, int dstX1, int dstY1, float z, unsigned filter) { struct pipe_context *pipe = ctx->pipe; struct pipe_screen *screen = pipe->screen; struct pipe_sampler_view view_templ; struct pipe_sampler_view *view; struct pipe_box src_box; struct pipe_resource texTemp, *tex; const struct pipe_framebuffer_state *fb = &ctx->g3d.fb; const int srcW = abs(srcX1 - srcX0); const int srcH = abs(srcY1 - srcY0); const int srcLeft = MIN2(srcX0, srcX1); const int srcTop = MIN2(srcY0, srcY1); assert(filter == PIPE_TEX_MIPFILTER_NEAREST || filter == PIPE_TEX_MIPFILTER_LINEAR); if (srcLeft != srcX0) { /* left-right flip */ int tmp = dstX0; dstX0 = dstX1; dstX1 = tmp; } if (srcTop != srcY0) { /* up-down flip */ int tmp = dstY0; dstY0 = dstY1; dstY1 = tmp; } assert(screen->is_format_supported(screen, src->format, PIPE_TEXTURE_2D, 0, PIPE_BIND_SAMPLER_VIEW)); assert(screen->is_format_supported(screen, dst->format, PIPE_TEXTURE_2D, 0, PIPE_BIND_SAMPLER_VIEW)); assert(screen->is_format_supported(screen, dst->format, PIPE_TEXTURE_2D, 0, PIPE_BIND_RENDER_TARGET)); /* * XXX for now we're always creating a temporary texture. * Strictly speaking that's not always needed. */ /* create temp texture */ memset(&texTemp, 0, sizeof(texTemp)); texTemp.target = PIPE_TEXTURE_2D; texTemp.format = src->format; texTemp.last_level = 0; texTemp.width0 = srcW; texTemp.height0 = srcH; texTemp.depth0 = 1; texTemp.array_size = 1; texTemp.bind = PIPE_BIND_SAMPLER_VIEW; tex = screen->resource_create(screen, &texTemp); if (!tex) return; u_sampler_view_default_template(&view_templ, tex, tex->format); view = pipe->create_sampler_view(pipe, tex, &view_templ); if (!view) return; u_box_2d_zslice(srcLeft, srcTop, src->u.tex.first_layer, srcW, srcH, &src_box); pipe->resource_copy_region(pipe, tex, 0, 0, 0, 0, /* dest */ src->texture, 0, &src_box); assert(floatsEqual(z, 0.0f)); /* draw */ if (fb->cbufs[0] == dst) { /* transform back to surface coordinates */ dstY0 = dst->height - dstY0; dstY1 = dst->height - dstY1; if (renderer_drawtex_begin(ctx, view)) { renderer_drawtex(ctx, dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0, 0, 0, view->texture->width0, view->texture->height0); renderer_drawtex_end(ctx); } } else { if (renderer_copy_begin(ctx, dst, VG_TRUE, view)) { renderer_copy(ctx, dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0, 0, 0, view->texture->width0, view->texture->height0); renderer_copy_end(ctx); } } } void renderer_texture_quad(struct renderer *r, struct pipe_resource *tex, VGfloat x1offset, VGfloat y1offset, VGfloat x2offset, VGfloat y2offset, VGfloat x1, VGfloat y1, VGfloat x2, VGfloat y2, VGfloat x3, VGfloat y3, VGfloat x4, VGfloat y4) { const VGfloat z = 0.0f; assert(r->state == RENDERER_STATE_INIT); assert(tex->width0 != 0); assert(tex->height0 != 0); cso_save_vertex_shader(r->cso); renderer_set_vs(r, RENDERER_VS_TEXTURE); /* manually set up positions */ r->vertices[0][0][0] = x1; r->vertices[0][0][1] = y1; r->vertices[0][0][2] = z; r->vertices[1][0][0] = x2; r->vertices[1][0][1] = y2; r->vertices[1][0][2] = z; r->vertices[2][0][0] = x3; r->vertices[2][0][1] = y3; r->vertices[2][0][2] = z; r->vertices[3][0][0] = x4; r->vertices[3][0][1] = y4; r->vertices[3][0][2] = z; /* texcoords */ renderer_quad_texcoord(r, x1offset, y1offset, x2offset, y2offset, tex->width0, tex->height0); renderer_quad_draw(r); cso_restore_vertex_shader(r->cso); }