| /************************************************************************** |
| * |
| * 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 "asm_util.h" |
| #include "st_inlines.h" |
| #include "vg_manager.h" |
| #include "api.h" |
| |
| #include "pipe/p_context.h" |
| #include "util/u_inlines.h" |
| #include "pipe/p_shader_tokens.h" |
| |
| #include "cso_cache/cso_context.h" |
| |
| #include "util/u_simple_shaders.h" |
| #include "util/u_memory.h" |
| #include "util/u_blit.h" |
| #include "util/u_sampler.h" |
| |
| struct vg_context *_vg_context = 0; |
| |
| struct vg_context * vg_current_context(void) |
| { |
| return _vg_context; |
| } |
| |
| static void init_clear(struct vg_context *st) |
| { |
| struct pipe_context *pipe = st->pipe; |
| |
| /* rasterizer state: bypass clipping */ |
| memset(&st->clear.raster, 0, sizeof(st->clear.raster)); |
| st->clear.raster.gl_rasterization_rules = 1; |
| |
| /* fragment shader state: color pass-through program */ |
| st->clear.fs = |
| util_make_fragment_passthrough_shader(pipe); |
| } |
| |
| /** |
| * 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_USCALED, |
| PIPE_FORMAT_S8_USCALED_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, 0)) |
| 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; |
| unsigned i; |
| |
| 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); |
| |
| init_clear(ctx); |
| |
| 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.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.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; |
| |
| for (i = 0; i < 2; i++) { |
| ctx->velems[i].src_offset = i * 4 * sizeof(float); |
| ctx->velems[i].instance_divisor = 0; |
| ctx->velems[i].vertex_buffer_index = 0; |
| ctx->velems[i].src_format = PIPE_FORMAT_R32G32B32A32_FLOAT; |
| } |
| |
| 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; |
| struct pipe_resource **vsbuf = &ctx->vs_const_buffer; |
| |
| 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 (*vsbuf) |
| pipe_resource_reference(vsbuf, NULL); |
| |
| if (ctx->clear.fs) { |
| cso_delete_fragment_shader(ctx->cso_context, ctx->clear.fs); |
| ctx->clear.fs = NULL; |
| } |
| |
| if (ctx->plain_vs) { |
| vg_shader_destroy(ctx, ctx->plain_vs); |
| ctx->plain_vs = NULL; |
| } |
| if (ctx->clear_vs) { |
| vg_shader_destroy(ctx, ctx->clear_vs); |
| ctx->clear_vs = NULL; |
| } |
| if (ctx->texture_vs) { |
| vg_shader_destroy(ctx, ctx->texture_vs); |
| ctx->texture_vs = NULL; |
| } |
| |
| if (ctx->pass_through_depth_fs) |
| vg_shader_destroy(ctx, ctx->pass_through_depth_fs); |
| 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; |
| } |
| |
| VGboolean vg_context_is_object_valid(struct vg_context *ctx, |
| enum vg_object_type type, |
| void *ptr) |
| { |
| if (ctx) { |
| struct cso_hash *hash = ctx->owned_objects[type]; |
| if (!hash) |
| return VG_FALSE; |
| return cso_hash_contains(hash, (unsigned)(long)ptr); |
| } |
| return VG_FALSE; |
| } |
| |
| void vg_context_add_object(struct vg_context *ctx, |
| enum vg_object_type type, |
| void *ptr) |
| { |
| if (ctx) { |
| struct cso_hash *hash = ctx->owned_objects[type]; |
| if (!hash) |
| return; |
| cso_hash_insert(hash, (unsigned)(long)ptr, ptr); |
| } |
| } |
| |
| void vg_context_remove_object(struct vg_context *ctx, |
| enum vg_object_type type, |
| void *ptr) |
| { |
| if (ctx) { |
| struct cso_hash *hash = ctx->owned_objects[type]; |
| if (!hash) |
| return; |
| cso_hash_take(hash, (unsigned)(long)ptr); |
| } |
| } |
| |
| static void update_clip_state(struct vg_context *ctx) |
| { |
| struct pipe_depth_stencil_alpha_state *dsa = &ctx->state.g3d.dsa; |
| struct vg_state *state = &ctx->state.vg; |
| |
| memset(dsa, 0, sizeof(struct pipe_depth_stencil_alpha_state)); |
| |
| if (state->scissoring) { |
| struct pipe_blend_state *blend = &ctx->state.g3d.blend; |
| struct pipe_framebuffer_state *fb = &ctx->state.g3d.fb; |
| int i; |
| |
| dsa->depth.writemask = 1;/*glDepthMask(TRUE);*/ |
| dsa->depth.func = PIPE_FUNC_ALWAYS; |
| dsa->depth.enabled = 1; |
| |
| cso_save_blend(ctx->cso_context); |
| cso_save_fragment_shader(ctx->cso_context); |
| /* set a passthrough shader */ |
| if (!ctx->pass_through_depth_fs) |
| ctx->pass_through_depth_fs = shader_create_from_text(ctx->pipe, |
| pass_through_depth_asm, |
| 40, |
| PIPE_SHADER_FRAGMENT); |
| cso_set_fragment_shader_handle(ctx->cso_context, |
| ctx->pass_through_depth_fs->driver); |
| cso_set_depth_stencil_alpha(ctx->cso_context, dsa); |
| |
| ctx->pipe->clear(ctx->pipe, PIPE_CLEAR_DEPTHSTENCIL, NULL, 1.0, 0); |
| |
| /* disable color writes */ |
| blend->rt[0].colormask = 0; /*disable colorwrites*/ |
| cso_set_blend(ctx->cso_context, blend); |
| |
| /* enable scissoring */ |
| 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; |
| VGfloat minx, miny, maxx, maxy; |
| |
| minx = 0; |
| miny = 0; |
| maxx = fb->width; |
| maxy = fb->height; |
| |
| if (x > minx) |
| minx = x; |
| if (y > miny) |
| miny = y; |
| |
| if (x + width < maxx) |
| maxx = x + width; |
| if (y + height < maxy) |
| maxy = y + height; |
| |
| /* check for null space */ |
| if (minx >= maxx || miny >= maxy) |
| minx = miny = maxx = maxy = 0; |
| |
| /*glClear(GL_DEPTH_BUFFER_BIT);*/ |
| renderer_draw_quad(ctx->renderer, minx, miny, maxx, maxy, 0.0f); |
| } |
| |
| cso_restore_blend(ctx->cso_context); |
| cso_restore_fragment_shader(ctx->cso_context); |
| |
| dsa->depth.enabled = 1; /* glEnable(GL_DEPTH_TEST); */ |
| dsa->depth.writemask = 0;/*glDepthMask(FALSE);*/ |
| dsa->depth.func = PIPE_FUNC_GEQUAL; |
| } |
| } |
| |
| void vg_validate_state(struct vg_context *ctx) |
| { |
| vg_manager_validate_framebuffer(ctx); |
| |
| if ((ctx->state.dirty & BLEND_DIRTY)) { |
| struct pipe_blend_state *blend = &ctx->state.g3d.blend; |
| memset(blend, 0, sizeof(struct pipe_blend_state)); |
| blend->rt[0].blend_enable = 1; |
| blend->rt[0].colormask = PIPE_MASK_RGBA; |
| |
| switch (ctx->state.vg.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; |
| blend->rt[0].blend_enable = 0; |
| break; |
| case VG_BLEND_SRC_OVER: |
| 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; |
| break; |
| case VG_BLEND_DST_OVER: |
| blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_INV_DST_ALPHA; |
| blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_INV_DST_ALPHA; |
| blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_DST_ALPHA; |
| blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_DST_ALPHA; |
| break; |
| case VG_BLEND_SRC_IN: |
| blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_DST_ALPHA; |
| 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; |
| 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_SRC_ALPHA; |
| blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_SRC_ALPHA; |
| break; |
| case VG_BLEND_MULTIPLY: |
| case VG_BLEND_SCREEN: |
| case VG_BLEND_DARKEN: |
| case VG_BLEND_LIGHTEN: |
| 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; |
| blend->rt[0].blend_enable = 0; |
| break; |
| case VG_BLEND_ADDITIVE: |
| 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_ONE; |
| blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE; |
| break; |
| default: |
| assert(!"not implemented blend mode"); |
| } |
| cso_set_blend(ctx->cso_context, &ctx->state.g3d.blend); |
| } |
| if ((ctx->state.dirty & RASTERIZER_DIRTY)) { |
| struct pipe_rasterizer_state *raster = &ctx->state.g3d.rasterizer; |
| memset(raster, 0, sizeof(struct pipe_rasterizer_state)); |
| raster->gl_rasterization_rules = 1; |
| cso_set_rasterizer(ctx->cso_context, &ctx->state.g3d.rasterizer); |
| } |
| if ((ctx->state.dirty & VIEWPORT_DIRTY)) { |
| struct pipe_framebuffer_state *fb = &ctx->state.g3d.fb; |
| const VGint param_bytes = 8 * sizeof(VGfloat); |
| VGfloat vs_consts[8] = { |
| 2.f/fb->width, 2.f/fb->height, 1, 1, |
| -1, -1, 0, 0 |
| }; |
| struct pipe_resource **cbuf = &ctx->vs_const_buffer; |
| |
| vg_set_viewport(ctx, VEGA_Y0_BOTTOM); |
| |
| pipe_resource_reference(cbuf, NULL); |
| *cbuf = pipe_buffer_create(ctx->pipe->screen, |
| PIPE_BIND_CONSTANT_BUFFER, |
| param_bytes); |
| |
| if (*cbuf) { |
| st_no_flush_pipe_buffer_write(ctx, *cbuf, |
| 0, param_bytes, vs_consts); |
| } |
| ctx->pipe->set_constant_buffer(ctx->pipe, PIPE_SHADER_VERTEX, 0, *cbuf); |
| } |
| if ((ctx->state.dirty & VS_DIRTY)) { |
| cso_set_vertex_shader_handle(ctx->cso_context, |
| vg_plain_vs(ctx)); |
| } |
| |
| /* must be last because it renders to the depth buffer*/ |
| if ((ctx->state.dirty & DEPTH_STENCIL_DIRTY)) { |
| update_clip_state(ctx); |
| cso_set_depth_stencil_alpha(ctx->cso_context, &ctx->state.g3d.dsa); |
| } |
| |
| shader_set_masking(ctx->shader, ctx->state.vg.masking); |
| shader_set_image_mode(ctx->shader, ctx->state.vg.image_mode); |
| |
| ctx->state.dirty = NONE_DIRTY; |
| } |
| |
| VGboolean vg_object_is_valid(void *ptr, enum vg_object_type type) |
| { |
| struct vg_object *obj = ptr; |
| if (ptr && 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; |
| } |
| |
| void vg_prepare_blend_surface(struct vg_context *ctx) |
| { |
| struct pipe_surface *dest_surface = NULL; |
| 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; |
| |
| /* first finish all pending rendering */ |
| vgFinish(); |
| |
| u_sampler_view_default_template(&view_templ, strb->texture, strb->texture->format); |
| view = pipe->create_sampler_view(pipe, strb->texture, &view_templ); |
| |
| dest_surface = pipe->screen->get_tex_surface(pipe->screen, |
| stfb->blend_texture_view->texture, |
| 0, 0, 0, |
| PIPE_BIND_RENDER_TARGET); |
| /* flip it, because we want to use it as a sampler */ |
| util_blit_pixels_tex(ctx->blit, |
| view, |
| 0, strb->height, |
| strb->width, 0, |
| dest_surface, |
| 0, 0, |
| strb->width, strb->height, |
| 0.0, PIPE_TEX_MIPFILTER_NEAREST); |
| |
| if (dest_surface) |
| pipe_surface_reference(&dest_surface, NULL); |
| |
| /* make sure it's complete */ |
| vgFinish(); |
| |
| pipe_sampler_view_reference(&view, NULL); |
| } |
| |
| |
| void vg_prepare_blend_surface_from_mask(struct vg_context *ctx) |
| { |
| struct pipe_surface *dest_surface = NULL; |
| struct pipe_context *pipe = ctx->pipe; |
| struct st_framebuffer *stfb = ctx->draw_buffer; |
| struct st_renderbuffer *strb = stfb->strb; |
| |
| vg_validate_state(ctx); |
| |
| /* first finish all pending rendering */ |
| vgFinish(); |
| |
| dest_surface = pipe->screen->get_tex_surface(pipe->screen, |
| stfb->blend_texture_view->texture, |
| 0, 0, 0, |
| PIPE_BIND_RENDER_TARGET); |
| |
| /* flip it, because we want to use it as a sampler */ |
| util_blit_pixels_tex(ctx->blit, |
| stfb->alpha_mask_view, |
| 0, strb->height, |
| strb->width, 0, |
| dest_surface, |
| 0, 0, |
| strb->width, strb->height, |
| 0.0, PIPE_TEX_MIPFILTER_NEAREST); |
| |
| /* make sure it's complete */ |
| vgFinish(); |
| |
| if (dest_surface) |
| pipe_surface_reference(&dest_surface, NULL); |
| } |
| |
| void * vg_plain_vs(struct vg_context *ctx) |
| { |
| if (!ctx->plain_vs) { |
| ctx->plain_vs = shader_create_from_text(ctx->pipe, |
| vs_plain_asm, |
| 200, |
| PIPE_SHADER_VERTEX); |
| } |
| |
| return ctx->plain_vs->driver; |
| } |
| |
| |
| void * vg_clear_vs(struct vg_context *ctx) |
| { |
| if (!ctx->clear_vs) { |
| ctx->clear_vs = shader_create_from_text(ctx->pipe, |
| vs_clear_asm, |
| 200, |
| PIPE_SHADER_VERTEX); |
| } |
| |
| return ctx->clear_vs->driver; |
| } |
| |
| void * vg_texture_vs(struct vg_context *ctx) |
| { |
| if (!ctx->texture_vs) { |
| ctx->texture_vs = shader_create_from_text(ctx->pipe, |
| vs_texture_asm, |
| 200, |
| PIPE_SHADER_VERTEX); |
| } |
| |
| return ctx->texture_vs->driver; |
| } |
| |
| void vg_set_viewport(struct vg_context *ctx, VegaOrientation orientation) |
| { |
| struct pipe_viewport_state viewport; |
| struct pipe_framebuffer_state *fb = &ctx->state.g3d.fb; |
| 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.scale[3] = 1.0; |
| viewport.translate[0] = fb->width / 2.f; |
| viewport.translate[1] = fb->height / 2.f; |
| viewport.translate[2] = 0.0; |
| viewport.translate[3] = 0.0; |
| |
| cso_set_viewport(ctx->cso_context, &viewport); |
| } |