st_api: Remove st_context::is_visual_supported.
[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 #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
49 struct vg_context *_vg_context = 0;
50
51 struct vg_context * vg_current_context(void)
52 {
53 return _vg_context;
54 }
55
56 static 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 */
72 static boolean
73 choose_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
94 void vg_set_current_context(struct vg_context *ctx)
95 {
96 _vg_context = ctx;
97 api_make_dispatch_current((ctx) ? ctx->dispatch : NULL);
98 }
99
100 struct 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
167 void 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
227 void 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
233 VGboolean 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
246 void 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
258 void 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
270 static 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
344 void 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
452 VGboolean 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
461 void 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
472 void 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
511 void 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
545 void * 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
558 void * 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
570 void * 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
582 void 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 }