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