/**************************************************************************
*
* 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_STATIC,
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);
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.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(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_STATIC,
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,
PIPE_BIND_RENDER_TARGET);
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, 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->gl_rasterization_rules = 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);
}