vg_context.c revision f914cd1796845164109c837a111c39ba64852ad4
1/************************************************************************** 2 * 3 * Copyright 2009 VMware, Inc. All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 **************************************************************************/ 26 27#include "vg_context.h" 28 29#include "paint.h" 30#include "renderer.h" 31#include "shaders_cache.h" 32#include "shader.h" 33#include "asm_util.h" 34#include "st_inlines.h" 35#include "vg_manager.h" 36#include "api.h" 37 38#include "pipe/p_context.h" 39#include "util/u_inlines.h" 40#include "pipe/p_shader_tokens.h" 41 42#include "cso_cache/cso_context.h" 43 44#include "util/u_simple_shaders.h" 45#include "util/u_memory.h" 46#include "util/u_blit.h" 47#include "util/u_sampler.h" 48 49struct vg_context *_vg_context = 0; 50 51struct vg_context * vg_current_context(void) 52{ 53 return _vg_context; 54} 55 56static void init_clear(struct vg_context *st) 57{ 58 struct pipe_context *pipe = st->pipe; 59 60 /* rasterizer state: bypass clipping */ 61 memset(&st->clear.raster, 0, sizeof(st->clear.raster)); 62 st->clear.raster.gl_rasterization_rules = 1; 63 64 /* fragment shader state: color pass-through program */ 65 st->clear.fs = 66 util_make_fragment_passthrough_shader(pipe); 67} 68 69/** 70 * A depth/stencil rb will be needed regardless of what the visual says. 71 */ 72static boolean 73choose_depth_stencil_format(struct vg_context *ctx) 74{ 75 struct pipe_screen *screen = ctx->pipe->screen; 76 enum pipe_format formats[] = { 77 PIPE_FORMAT_Z24_UNORM_S8_USCALED, 78 PIPE_FORMAT_S8_USCALED_Z24_UNORM, 79 PIPE_FORMAT_NONE 80 }; 81 enum pipe_format *fmt; 82 83 for (fmt = formats; *fmt != PIPE_FORMAT_NONE; fmt++) { 84 if (screen->is_format_supported(screen, *fmt, 85 PIPE_TEXTURE_2D, 0, PIPE_BIND_DEPTH_STENCIL, 0)) 86 break; 87 } 88 89 ctx->ds_format = *fmt; 90 91 return (ctx->ds_format != PIPE_FORMAT_NONE); 92} 93 94void vg_set_current_context(struct vg_context *ctx) 95{ 96 _vg_context = ctx; 97 api_make_dispatch_current((ctx) ? ctx->dispatch : NULL); 98} 99 100struct vg_context * vg_create_context(struct pipe_context *pipe, 101 const void *visual, 102 struct vg_context *share) 103{ 104 struct vg_context *ctx; 105 unsigned i; 106 107 ctx = CALLOC_STRUCT(vg_context); 108 109 ctx->pipe = pipe; 110 if (!choose_depth_stencil_format(ctx)) { 111 FREE(ctx); 112 return NULL; 113 } 114 115 ctx->dispatch = api_create_dispatch(); 116 117 vg_init_state(&ctx->state.vg); 118 ctx->state.dirty = ALL_DIRTY; 119 120 ctx->cso_context = cso_create_context(pipe); 121 122 init_clear(ctx); 123 124 ctx->default_paint = paint_create(ctx); 125 ctx->state.vg.stroke_paint = ctx->default_paint; 126 ctx->state.vg.fill_paint = ctx->default_paint; 127 128 129 ctx->mask.sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 130 ctx->mask.sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 131 ctx->mask.sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE; 132 ctx->mask.sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST; 133 ctx->mask.sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST; 134 ctx->mask.sampler.normalized_coords = 0; 135 136 ctx->blend_sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 137 ctx->blend_sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 138 ctx->blend_sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE; 139 ctx->blend_sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST; 140 ctx->blend_sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST; 141 ctx->blend_sampler.normalized_coords = 0; 142 143 for (i = 0; i < 2; i++) { 144 ctx->velems[i].src_offset = i * 4 * sizeof(float); 145 ctx->velems[i].instance_divisor = 0; 146 ctx->velems[i].vertex_buffer_index = 0; 147 ctx->velems[i].src_format = PIPE_FORMAT_R32G32B32A32_FLOAT; 148 } 149 150 vg_set_error(ctx, VG_NO_ERROR); 151 152 ctx->owned_objects[VG_OBJECT_PAINT] = cso_hash_create(); 153 ctx->owned_objects[VG_OBJECT_IMAGE] = cso_hash_create(); 154 ctx->owned_objects[VG_OBJECT_MASK] = cso_hash_create(); 155 ctx->owned_objects[VG_OBJECT_FONT] = cso_hash_create(); 156 ctx->owned_objects[VG_OBJECT_PATH] = cso_hash_create(); 157 158 ctx->renderer = renderer_create(ctx); 159 ctx->sc = shaders_cache_create(ctx); 160 ctx->shader = shader_create(ctx); 161 162 ctx->blit = util_create_blit(ctx->pipe, ctx->cso_context); 163 164 return ctx; 165} 166 167void vg_destroy_context(struct vg_context *ctx) 168{ 169 struct pipe_resource **cbuf = &ctx->mask.cbuf; 170 struct pipe_resource **vsbuf = &ctx->vs_const_buffer; 171 172 util_destroy_blit(ctx->blit); 173 renderer_destroy(ctx->renderer); 174 shaders_cache_destroy(ctx->sc); 175 shader_destroy(ctx->shader); 176 paint_destroy(ctx->default_paint); 177 178 if (*cbuf) 179 pipe_resource_reference(cbuf, NULL); 180 181 if (*vsbuf) 182 pipe_resource_reference(vsbuf, NULL); 183 184 if (ctx->clear.fs) { 185 cso_delete_fragment_shader(ctx->cso_context, ctx->clear.fs); 186 ctx->clear.fs = NULL; 187 } 188 189 if (ctx->plain_vs) { 190 vg_shader_destroy(ctx, ctx->plain_vs); 191 ctx->plain_vs = NULL; 192 } 193 if (ctx->clear_vs) { 194 vg_shader_destroy(ctx, ctx->clear_vs); 195 ctx->clear_vs = NULL; 196 } 197 if (ctx->texture_vs) { 198 vg_shader_destroy(ctx, ctx->texture_vs); 199 ctx->texture_vs = NULL; 200 } 201 202 if (ctx->pass_through_depth_fs) 203 vg_shader_destroy(ctx, ctx->pass_through_depth_fs); 204 if (ctx->mask.union_fs) 205 vg_shader_destroy(ctx, ctx->mask.union_fs); 206 if (ctx->mask.intersect_fs) 207 vg_shader_destroy(ctx, ctx->mask.intersect_fs); 208 if (ctx->mask.subtract_fs) 209 vg_shader_destroy(ctx, ctx->mask.subtract_fs); 210 if (ctx->mask.set_fs) 211 vg_shader_destroy(ctx, ctx->mask.set_fs); 212 213 cso_release_all(ctx->cso_context); 214 cso_destroy_context(ctx->cso_context); 215 216 cso_hash_delete(ctx->owned_objects[VG_OBJECT_PAINT]); 217 cso_hash_delete(ctx->owned_objects[VG_OBJECT_IMAGE]); 218 cso_hash_delete(ctx->owned_objects[VG_OBJECT_MASK]); 219 cso_hash_delete(ctx->owned_objects[VG_OBJECT_FONT]); 220 cso_hash_delete(ctx->owned_objects[VG_OBJECT_PATH]); 221 222 api_destroy_dispatch(ctx->dispatch); 223 224 FREE(ctx); 225} 226 227void vg_init_object(struct vg_object *obj, struct vg_context *ctx, enum vg_object_type type) 228{ 229 obj->type = type; 230 obj->ctx = ctx; 231} 232 233VGboolean vg_context_is_object_valid(struct vg_context *ctx, 234 enum vg_object_type type, 235 void *ptr) 236{ 237 if (ctx) { 238 struct cso_hash *hash = ctx->owned_objects[type]; 239 if (!hash) 240 return VG_FALSE; 241 return cso_hash_contains(hash, (unsigned)(long)ptr); 242 } 243 return VG_FALSE; 244} 245 246void vg_context_add_object(struct vg_context *ctx, 247 enum vg_object_type type, 248 void *ptr) 249{ 250 if (ctx) { 251 struct cso_hash *hash = ctx->owned_objects[type]; 252 if (!hash) 253 return; 254 cso_hash_insert(hash, (unsigned)(long)ptr, ptr); 255 } 256} 257 258void vg_context_remove_object(struct vg_context *ctx, 259 enum vg_object_type type, 260 void *ptr) 261{ 262 if (ctx) { 263 struct cso_hash *hash = ctx->owned_objects[type]; 264 if (!hash) 265 return; 266 cso_hash_take(hash, (unsigned)(long)ptr); 267 } 268} 269 270static void update_clip_state(struct vg_context *ctx) 271{ 272 struct pipe_depth_stencil_alpha_state *dsa = &ctx->state.g3d.dsa; 273 struct vg_state *state = &ctx->state.vg; 274 275 memset(dsa, 0, sizeof(struct pipe_depth_stencil_alpha_state)); 276 277 if (state->scissoring) { 278 struct pipe_blend_state *blend = &ctx->state.g3d.blend; 279 struct pipe_framebuffer_state *fb = &ctx->state.g3d.fb; 280 int i; 281 282 dsa->depth.writemask = 1;/*glDepthMask(TRUE);*/ 283 dsa->depth.func = PIPE_FUNC_ALWAYS; 284 dsa->depth.enabled = 1; 285 286 cso_save_blend(ctx->cso_context); 287 cso_save_fragment_shader(ctx->cso_context); 288 /* set a passthrough shader */ 289 if (!ctx->pass_through_depth_fs) 290 ctx->pass_through_depth_fs = shader_create_from_text(ctx->pipe, 291 pass_through_depth_asm, 292 40, 293 PIPE_SHADER_FRAGMENT); 294 cso_set_fragment_shader_handle(ctx->cso_context, 295 ctx->pass_through_depth_fs->driver); 296 cso_set_depth_stencil_alpha(ctx->cso_context, dsa); 297 298 ctx->pipe->clear(ctx->pipe, PIPE_CLEAR_DEPTHSTENCIL, NULL, 1.0, 0); 299 300 /* disable color writes */ 301 blend->rt[0].colormask = 0; /*disable colorwrites*/ 302 cso_set_blend(ctx->cso_context, blend); 303 304 /* enable scissoring */ 305 for (i = 0; i < state->scissor_rects_num; ++i) { 306 const float x = state->scissor_rects[i * 4 + 0].f; 307 const float y = state->scissor_rects[i * 4 + 1].f; 308 const float width = state->scissor_rects[i * 4 + 2].f; 309 const float height = state->scissor_rects[i * 4 + 3].f; 310 VGfloat minx, miny, maxx, maxy; 311 312 minx = 0; 313 miny = 0; 314 maxx = fb->width; 315 maxy = fb->height; 316 317 if (x > minx) 318 minx = x; 319 if (y > miny) 320 miny = y; 321 322 if (x + width < maxx) 323 maxx = x + width; 324 if (y + height < maxy) 325 maxy = y + height; 326 327 /* check for null space */ 328 if (minx >= maxx || miny >= maxy) 329 minx = miny = maxx = maxy = 0; 330 331 /*glClear(GL_DEPTH_BUFFER_BIT);*/ 332 renderer_draw_quad(ctx->renderer, minx, miny, maxx, maxy, 0.0f); 333 } 334 335 cso_restore_blend(ctx->cso_context); 336 cso_restore_fragment_shader(ctx->cso_context); 337 338 dsa->depth.enabled = 1; /* glEnable(GL_DEPTH_TEST); */ 339 dsa->depth.writemask = 0;/*glDepthMask(FALSE);*/ 340 dsa->depth.func = PIPE_FUNC_GEQUAL; 341 } 342} 343 344void vg_validate_state(struct vg_context *ctx) 345{ 346 vg_manager_validate_framebuffer(ctx); 347 348 if ((ctx->state.dirty & BLEND_DIRTY)) { 349 struct pipe_blend_state *blend = &ctx->state.g3d.blend; 350 memset(blend, 0, sizeof(struct pipe_blend_state)); 351 blend->rt[0].blend_enable = 1; 352 blend->rt[0].colormask = PIPE_MASK_RGBA; 353 354 switch (ctx->state.vg.blend_mode) { 355 case VG_BLEND_SRC: 356 blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE; 357 blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; 358 blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; 359 blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; 360 blend->rt[0].blend_enable = 0; 361 break; 362 case VG_BLEND_SRC_OVER: 363 blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_SRC_ALPHA; 364 blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; 365 blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA; 366 blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA; 367 break; 368 case VG_BLEND_DST_OVER: 369 blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_INV_DST_ALPHA; 370 blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_INV_DST_ALPHA; 371 blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_DST_ALPHA; 372 blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_DST_ALPHA; 373 break; 374 case VG_BLEND_SRC_IN: 375 blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_DST_ALPHA; 376 blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_DST_ALPHA; 377 blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; 378 blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; 379 break; 380 case VG_BLEND_DST_IN: 381 blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ZERO; 382 blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ZERO; 383 blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_SRC_ALPHA; 384 blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_SRC_ALPHA; 385 break; 386 case VG_BLEND_MULTIPLY: 387 case VG_BLEND_SCREEN: 388 case VG_BLEND_DARKEN: 389 case VG_BLEND_LIGHTEN: 390 blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE; 391 blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; 392 blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; 393 blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; 394 blend->rt[0].blend_enable = 0; 395 break; 396 case VG_BLEND_ADDITIVE: 397 blend->rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE; 398 blend->rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE; 399 blend->rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ONE; 400 blend->rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE; 401 break; 402 default: 403 assert(!"not implemented blend mode"); 404 } 405 cso_set_blend(ctx->cso_context, &ctx->state.g3d.blend); 406 } 407 if ((ctx->state.dirty & RASTERIZER_DIRTY)) { 408 struct pipe_rasterizer_state *raster = &ctx->state.g3d.rasterizer; 409 memset(raster, 0, sizeof(struct pipe_rasterizer_state)); 410 raster->gl_rasterization_rules = 1; 411 cso_set_rasterizer(ctx->cso_context, &ctx->state.g3d.rasterizer); 412 } 413 if ((ctx->state.dirty & VIEWPORT_DIRTY)) { 414 struct pipe_framebuffer_state *fb = &ctx->state.g3d.fb; 415 const VGint param_bytes = 8 * sizeof(VGfloat); 416 VGfloat vs_consts[8] = { 417 2.f/fb->width, 2.f/fb->height, 1, 1, 418 -1, -1, 0, 0 419 }; 420 struct pipe_resource **cbuf = &ctx->vs_const_buffer; 421 422 vg_set_viewport(ctx, VEGA_Y0_BOTTOM); 423 424 pipe_resource_reference(cbuf, NULL); 425 *cbuf = pipe_buffer_create(ctx->pipe->screen, 426 PIPE_BIND_CONSTANT_BUFFER, 427 param_bytes); 428 429 if (*cbuf) { 430 st_no_flush_pipe_buffer_write(ctx, *cbuf, 431 0, param_bytes, vs_consts); 432 } 433 ctx->pipe->set_constant_buffer(ctx->pipe, PIPE_SHADER_VERTEX, 0, *cbuf); 434 } 435 if ((ctx->state.dirty & VS_DIRTY)) { 436 cso_set_vertex_shader_handle(ctx->cso_context, 437 vg_plain_vs(ctx)); 438 } 439 440 /* must be last because it renders to the depth buffer*/ 441 if ((ctx->state.dirty & DEPTH_STENCIL_DIRTY)) { 442 update_clip_state(ctx); 443 cso_set_depth_stencil_alpha(ctx->cso_context, &ctx->state.g3d.dsa); 444 } 445 446 shader_set_masking(ctx->shader, ctx->state.vg.masking); 447 shader_set_image_mode(ctx->shader, ctx->state.vg.image_mode); 448 449 ctx->state.dirty = NONE_DIRTY; 450} 451 452VGboolean vg_object_is_valid(void *ptr, enum vg_object_type type) 453{ 454 struct vg_object *obj = ptr; 455 if (ptr && is_aligned(obj) && obj->type == type) 456 return VG_TRUE; 457 else 458 return VG_FALSE; 459} 460 461void vg_set_error(struct vg_context *ctx, 462 VGErrorCode code) 463{ 464 /*vgGetError returns the oldest error code provided by 465 * an API call on the current context since the previous 466 * call to vgGetError on that context (or since the creation 467 of the context).*/ 468 if (ctx->_error == VG_NO_ERROR) 469 ctx->_error = code; 470} 471 472void vg_prepare_blend_surface(struct vg_context *ctx) 473{ 474 struct pipe_surface *dest_surface = NULL; 475 struct pipe_context *pipe = ctx->pipe; 476 struct pipe_sampler_view *view; 477 struct pipe_sampler_view view_templ; 478 struct st_framebuffer *stfb = ctx->draw_buffer; 479 struct st_renderbuffer *strb = stfb->strb; 480 481 /* first finish all pending rendering */ 482 vgFinish(); 483 484 u_sampler_view_default_template(&view_templ, strb->texture, strb->texture->format); 485 view = pipe->create_sampler_view(pipe, strb->texture, &view_templ); 486 487 dest_surface = pipe->screen->get_tex_surface(pipe->screen, 488 stfb->blend_texture_view->texture, 489 0, 0, 0, 490 PIPE_BIND_RENDER_TARGET); 491 /* flip it, because we want to use it as a sampler */ 492 util_blit_pixels_tex(ctx->blit, 493 view, 494 0, strb->height, 495 strb->width, 0, 496 dest_surface, 497 0, 0, 498 strb->width, strb->height, 499 0.0, PIPE_TEX_MIPFILTER_NEAREST); 500 501 if (dest_surface) 502 pipe_surface_reference(&dest_surface, NULL); 503 504 /* make sure it's complete */ 505 vgFinish(); 506 507 pipe_sampler_view_reference(&view, NULL); 508} 509 510 511void vg_prepare_blend_surface_from_mask(struct vg_context *ctx) 512{ 513 struct pipe_surface *dest_surface = NULL; 514 struct pipe_context *pipe = ctx->pipe; 515 struct st_framebuffer *stfb = ctx->draw_buffer; 516 struct st_renderbuffer *strb = stfb->strb; 517 518 vg_validate_state(ctx); 519 520 /* first finish all pending rendering */ 521 vgFinish(); 522 523 dest_surface = pipe->screen->get_tex_surface(pipe->screen, 524 stfb->blend_texture_view->texture, 525 0, 0, 0, 526 PIPE_BIND_RENDER_TARGET); 527 528 /* flip it, because we want to use it as a sampler */ 529 util_blit_pixels_tex(ctx->blit, 530 stfb->alpha_mask_view, 531 0, strb->height, 532 strb->width, 0, 533 dest_surface, 534 0, 0, 535 strb->width, strb->height, 536 0.0, PIPE_TEX_MIPFILTER_NEAREST); 537 538 /* make sure it's complete */ 539 vgFinish(); 540 541 if (dest_surface) 542 pipe_surface_reference(&dest_surface, NULL); 543} 544 545void * vg_plain_vs(struct vg_context *ctx) 546{ 547 if (!ctx->plain_vs) { 548 ctx->plain_vs = shader_create_from_text(ctx->pipe, 549 vs_plain_asm, 550 200, 551 PIPE_SHADER_VERTEX); 552 } 553 554 return ctx->plain_vs->driver; 555} 556 557 558void * vg_clear_vs(struct vg_context *ctx) 559{ 560 if (!ctx->clear_vs) { 561 ctx->clear_vs = shader_create_from_text(ctx->pipe, 562 vs_clear_asm, 563 200, 564 PIPE_SHADER_VERTEX); 565 } 566 567 return ctx->clear_vs->driver; 568} 569 570void * vg_texture_vs(struct vg_context *ctx) 571{ 572 if (!ctx->texture_vs) { 573 ctx->texture_vs = shader_create_from_text(ctx->pipe, 574 vs_texture_asm, 575 200, 576 PIPE_SHADER_VERTEX); 577 } 578 579 return ctx->texture_vs->driver; 580} 581 582void vg_set_viewport(struct vg_context *ctx, VegaOrientation orientation) 583{ 584 struct pipe_viewport_state viewport; 585 struct pipe_framebuffer_state *fb = &ctx->state.g3d.fb; 586 VGfloat y_scale = (orientation == VEGA_Y0_BOTTOM) ? -2.f : 2.f; 587 588 viewport.scale[0] = fb->width / 2.f; 589 viewport.scale[1] = fb->height / y_scale; 590 viewport.scale[2] = 1.0; 591 viewport.scale[3] = 1.0; 592 viewport.translate[0] = fb->width / 2.f; 593 viewport.translate[1] = fb->height / 2.f; 594 viewport.translate[2] = 0.0; 595 viewport.translate[3] = 0.0; 596 597 cso_set_viewport(ctx->cso_context, &viewport); 598} 599