glamor_render: Optimize the two pass ca rendering.
For the componentAlpha with PictOpOver, we use two pass rendering to implement it. Previous implementation call two times the glamor_composite_... independently which is very inefficient. Now we change the control flow, and do the two pass internally and avoid duplicate works. For the x11perf -rgb10text, this optimization can get about 30% improvement. Signed-off-by: Zhigang Gong <zhigang.gong@linux.intel.com>
This commit is contained in:
parent
21916cf84f
commit
37d4022f01
|
@ -72,6 +72,7 @@
|
|||
#define xorg_list_init list_init
|
||||
#endif
|
||||
|
||||
struct glamor_pixmap_private;
|
||||
|
||||
typedef struct glamor_composite_shader {
|
||||
GLuint prog;
|
||||
|
@ -84,26 +85,23 @@ typedef struct glamor_composite_shader {
|
|||
GLint mask_wh;
|
||||
GLint source_repeat_mode;
|
||||
GLint mask_repeat_mode;
|
||||
union {
|
||||
float source_solid_color[4];
|
||||
struct {
|
||||
struct glamor_pixmap_private *source_priv;
|
||||
PicturePtr source;
|
||||
};
|
||||
};
|
||||
|
||||
union {
|
||||
float mask_solid_color[4];
|
||||
struct {
|
||||
struct glamor_pixmap_private *mask_priv;
|
||||
PicturePtr mask;
|
||||
};
|
||||
};
|
||||
} glamor_composite_shader;
|
||||
|
||||
typedef struct {
|
||||
INT16 x_src;
|
||||
INT16 y_src;
|
||||
INT16 x_mask;
|
||||
INT16 y_mask;
|
||||
INT16 x_dst;
|
||||
INT16 y_dst;
|
||||
INT16 width;
|
||||
INT16 height;
|
||||
} glamor_composite_rect_t;
|
||||
|
||||
|
||||
enum glamor_vertex_type {
|
||||
GLAMOR_VERTEX_POS,
|
||||
GLAMOR_VERTEX_SOURCE,
|
||||
GLAMOR_VERTEX_MASK
|
||||
};
|
||||
|
||||
enum shader_source {
|
||||
SHADER_SOURCE_SOLID,
|
||||
SHADER_SOURCE_TEXTURE,
|
||||
|
@ -133,6 +131,32 @@ struct shader_key {
|
|||
enum shader_in in;
|
||||
};
|
||||
|
||||
struct blendinfo {
|
||||
Bool dest_alpha;
|
||||
Bool source_alpha;
|
||||
GLenum source_blend;
|
||||
GLenum dest_blend;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
INT16 x_src;
|
||||
INT16 y_src;
|
||||
INT16 x_mask;
|
||||
INT16 y_mask;
|
||||
INT16 x_dst;
|
||||
INT16 y_dst;
|
||||
INT16 width;
|
||||
INT16 height;
|
||||
} glamor_composite_rect_t;
|
||||
|
||||
|
||||
enum glamor_vertex_type {
|
||||
GLAMOR_VERTEX_POS,
|
||||
GLAMOR_VERTEX_SOURCE,
|
||||
GLAMOR_VERTEX_MASK
|
||||
};
|
||||
|
||||
|
||||
enum gradient_shader {
|
||||
SHADER_GRADIENT_LINEAR,
|
||||
SHADER_GRADIENT_RADIAL,
|
||||
|
@ -463,7 +487,6 @@ typedef enum glamor_pixmap_status {
|
|||
GLAMOR_UPLOAD_FAILED
|
||||
} glamor_pixmap_status_t;
|
||||
|
||||
|
||||
extern DevPrivateKey glamor_screen_private_key;
|
||||
extern DevPrivateKey glamor_pixmap_private_key;
|
||||
static inline glamor_screen_private *
|
||||
|
@ -716,6 +739,7 @@ PicturePtr glamor_convert_gradient_picture(ScreenPtr screen,
|
|||
PicturePtr source,
|
||||
int x_source,
|
||||
int y_source, int width, int height);
|
||||
|
||||
Bool glamor_composite_choose_shader(CARD8 op,
|
||||
PicturePtr source,
|
||||
PicturePtr mask,
|
||||
|
@ -724,7 +748,16 @@ Bool glamor_composite_choose_shader(CARD8 op,
|
|||
glamor_pixmap_private *mask_pixmap_priv,
|
||||
glamor_pixmap_private *dest_pixmap_priv,
|
||||
struct shader_key *s_key,
|
||||
glamor_composite_shader **shader,
|
||||
struct blendinfo *op_info,
|
||||
PictFormatShort *psaved_source_format);
|
||||
|
||||
void
|
||||
glamor_composite_set_shader_blend(glamor_pixmap_private *dest_priv,
|
||||
struct shader_key *key,
|
||||
glamor_composite_shader *shader,
|
||||
struct blendinfo *op_info);
|
||||
|
||||
void glamor_setup_composite_vbo(ScreenPtr screen, int n_verts);
|
||||
void glamor_emit_composite_vert(ScreenPtr screen,
|
||||
const float *src_coords,
|
||||
|
|
|
@ -43,14 +43,6 @@
|
|||
//#define DEBUGRegionPrint(x) do {} while (0)
|
||||
#define DEBUGRegionPrint RegionPrint
|
||||
#endif
|
||||
|
||||
struct blendinfo {
|
||||
Bool dest_alpha;
|
||||
Bool source_alpha;
|
||||
GLenum source_blend;
|
||||
GLenum dest_blend;
|
||||
};
|
||||
|
||||
static struct blendinfo composite_op_info[] = {
|
||||
[PictOpClear] = {0, 0, GL_ZERO, GL_ZERO},
|
||||
[PictOpSrc] = {0, 0, GL_ONE, GL_ZERO},
|
||||
|
@ -481,13 +473,11 @@ glamor_fini_composite_shaders(ScreenPtr screen)
|
|||
|
||||
static Bool
|
||||
glamor_set_composite_op(ScreenPtr screen,
|
||||
CARD8 op, PicturePtr dest, PicturePtr mask)
|
||||
CARD8 op, struct blendinfo *op_info_result,
|
||||
PicturePtr dest, PicturePtr mask)
|
||||
{
|
||||
GLenum source_blend, dest_blend;
|
||||
struct blendinfo *op_info;
|
||||
glamor_screen_private *glamor_priv =
|
||||
glamor_get_screen_private(screen);
|
||||
glamor_gl_dispatch *dispatch;
|
||||
|
||||
if (op >= ARRAY_SIZE(composite_op_info)) {
|
||||
glamor_fallback("unsupported render op %d \n", op);
|
||||
|
@ -518,25 +508,20 @@ glamor_set_composite_op(ScreenPtr screen,
|
|||
dest_blend = GL_ONE_MINUS_SRC_COLOR;
|
||||
}
|
||||
|
||||
dispatch = glamor_get_dispatch(glamor_priv);
|
||||
if (source_blend == GL_ONE && dest_blend == GL_ZERO) {
|
||||
dispatch->glDisable(GL_BLEND);
|
||||
} else {
|
||||
dispatch->glEnable(GL_BLEND);
|
||||
dispatch->glBlendFunc(source_blend, dest_blend);
|
||||
}
|
||||
glamor_put_dispatch(glamor_priv);
|
||||
op_info_result->source_blend = source_blend;
|
||||
op_info_result->dest_blend = dest_blend;
|
||||
op_info_result->source_alpha = op_info->source_alpha;
|
||||
op_info_result->dest_alpha = op_info->dest_alpha;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
glamor_set_composite_texture(ScreenPtr screen, int unit,
|
||||
glamor_set_composite_texture(glamor_screen_private *glamor_priv, int unit,
|
||||
PicturePtr picture,
|
||||
glamor_pixmap_private * pixmap_priv,
|
||||
GLuint wh_location, GLuint repeat_location)
|
||||
{
|
||||
glamor_screen_private *glamor_priv =
|
||||
glamor_get_screen_private(screen);
|
||||
glamor_gl_dispatch *dispatch;
|
||||
float wh[4];
|
||||
int repeat_type;
|
||||
|
@ -956,10 +941,11 @@ Bool glamor_composite_choose_shader(CARD8 op,
|
|||
glamor_pixmap_private *mask_pixmap_priv,
|
||||
glamor_pixmap_private *dest_pixmap_priv,
|
||||
struct shader_key *s_key,
|
||||
glamor_composite_shader **shader,
|
||||
struct blendinfo *op_info,
|
||||
PictFormatShort *psaved_source_format)
|
||||
{
|
||||
ScreenPtr screen = dest->pDrawable->pScreen;
|
||||
glamor_screen_private *glamor_priv = dest_pixmap_priv->base.glamor_priv;
|
||||
PixmapPtr dest_pixmap = dest_pixmap_priv->base.pixmap;
|
||||
PixmapPtr source_pixmap = NULL;
|
||||
PixmapPtr mask_pixmap = NULL;
|
||||
|
@ -969,8 +955,6 @@ Bool glamor_composite_choose_shader(CARD8 op,
|
|||
struct shader_key key;
|
||||
GLfloat source_solid_color[4];
|
||||
GLfloat mask_solid_color[4];
|
||||
glamor_composite_shader *shader = NULL;
|
||||
glamor_gl_dispatch *dispatch = NULL;
|
||||
Bool ret = FALSE;
|
||||
|
||||
if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dest_pixmap_priv)) {
|
||||
|
@ -1175,44 +1159,33 @@ Bool glamor_composite_choose_shader(CARD8 op,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
glamor_set_destination_pixmap_priv_nc(dest_pixmap_priv);
|
||||
|
||||
if (!glamor_set_composite_op(screen, op, dest, mask)) {
|
||||
if (!glamor_set_composite_op(screen, op, op_info, dest, mask))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
shader = glamor_lookup_composite_shader(screen, &key);
|
||||
if (shader->prog == 0) {
|
||||
*shader = glamor_lookup_composite_shader(screen, &key);
|
||||
if ((*shader)->prog == 0) {
|
||||
glamor_fallback("no shader program for this"
|
||||
"render acccel mode\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dispatch = glamor_get_dispatch(glamor_priv);
|
||||
dispatch->glUseProgram(shader->prog);
|
||||
|
||||
if (key.source == SHADER_SOURCE_SOLID) {
|
||||
glamor_set_composite_solid(dispatch, source_solid_color,
|
||||
shader->source_uniform_location);
|
||||
} else {
|
||||
glamor_set_composite_texture(screen, 0, source,
|
||||
source_pixmap_priv, shader->source_wh,
|
||||
shader->source_repeat_mode);
|
||||
if (key.source == SHADER_SOURCE_SOLID)
|
||||
memcpy(&(*shader)->source_solid_color[0],
|
||||
source_solid_color, 4*sizeof(float));
|
||||
else {
|
||||
(*shader)->source_priv = source_pixmap_priv;
|
||||
(*shader)->source = source;
|
||||
}
|
||||
|
||||
if (key.mask != SHADER_MASK_NONE) {
|
||||
if (key.mask == SHADER_MASK_SOLID) {
|
||||
glamor_set_composite_solid(dispatch,
|
||||
mask_solid_color,
|
||||
shader->mask_uniform_location);
|
||||
} else {
|
||||
glamor_set_composite_texture(screen, 1, mask,
|
||||
mask_pixmap_priv, shader->mask_wh,
|
||||
shader->mask_repeat_mode);
|
||||
}
|
||||
if (key.mask == SHADER_MASK_SOLID)
|
||||
memcpy(&(*shader)->mask_solid_color[0],
|
||||
mask_solid_color, 4*sizeof(float));
|
||||
else {
|
||||
(*shader)->mask_priv = mask_pixmap_priv;
|
||||
(*shader)->mask = mask;
|
||||
}
|
||||
|
||||
glamor_put_dispatch(glamor_priv);
|
||||
ret = TRUE;
|
||||
memcpy(s_key, &key, sizeof(key));
|
||||
*psaved_source_format = saved_source_format;
|
||||
|
@ -1225,6 +1198,56 @@ done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
glamor_composite_set_shader_blend(glamor_pixmap_private *dest_priv,
|
||||
struct shader_key *key,
|
||||
glamor_composite_shader *shader,
|
||||
struct blendinfo *op_info)
|
||||
{
|
||||
glamor_gl_dispatch *dispatch;
|
||||
glamor_screen_private *glamor_priv;
|
||||
|
||||
glamor_priv = dest_priv->base.glamor_priv;
|
||||
|
||||
dispatch = glamor_get_dispatch(glamor_priv);
|
||||
dispatch->glUseProgram(shader->prog);
|
||||
|
||||
if (key->source == SHADER_SOURCE_SOLID) {
|
||||
glamor_set_composite_solid(dispatch,
|
||||
shader->source_solid_color,
|
||||
shader->source_uniform_location);
|
||||
} else {
|
||||
glamor_set_composite_texture(glamor_priv, 0,
|
||||
shader->source,
|
||||
shader->source_priv, shader->source_wh,
|
||||
shader->source_repeat_mode);
|
||||
}
|
||||
|
||||
if (key->mask != SHADER_MASK_NONE) {
|
||||
if (key->mask == SHADER_MASK_SOLID) {
|
||||
glamor_set_composite_solid(dispatch,
|
||||
shader->mask_solid_color,
|
||||
shader->mask_uniform_location);
|
||||
} else {
|
||||
glamor_set_composite_texture(glamor_priv, 1,
|
||||
shader->mask,
|
||||
shader->mask_priv, shader->mask_wh,
|
||||
shader->mask_repeat_mode);
|
||||
}
|
||||
}
|
||||
|
||||
if (op_info->source_blend == GL_ONE
|
||||
&& op_info->dest_blend == GL_ZERO) {
|
||||
dispatch->glDisable(GL_BLEND);
|
||||
} else {
|
||||
dispatch->glEnable(GL_BLEND);
|
||||
dispatch->glBlendFunc(op_info->source_blend,
|
||||
op_info->dest_blend);
|
||||
}
|
||||
|
||||
glamor_put_dispatch(glamor_priv);
|
||||
}
|
||||
|
||||
static Bool
|
||||
glamor_composite_with_shader(CARD8 op,
|
||||
PicturePtr source,
|
||||
|
@ -1233,7 +1256,8 @@ glamor_composite_with_shader(CARD8 op,
|
|||
glamor_pixmap_private *source_pixmap_priv,
|
||||
glamor_pixmap_private *mask_pixmap_priv,
|
||||
glamor_pixmap_private *dest_pixmap_priv,
|
||||
int nrect, glamor_composite_rect_t * rects)
|
||||
int nrect, glamor_composite_rect_t * rects,
|
||||
Bool two_pass_ca)
|
||||
{
|
||||
ScreenPtr screen = dest->pDrawable->pScreen;
|
||||
glamor_screen_private *glamor_priv = dest_pixmap_priv->base.glamor_priv;
|
||||
|
@ -1244,7 +1268,7 @@ glamor_composite_with_shader(CARD8 op,
|
|||
GLfloat dst_xscale, dst_yscale;
|
||||
GLfloat mask_xscale = 1, mask_yscale = 1,
|
||||
src_xscale = 1, src_yscale = 1;
|
||||
struct shader_key key;
|
||||
struct shader_key key, key_ca;
|
||||
float *vertices;
|
||||
int dest_x_off, dest_y_off;
|
||||
int source_x_off, source_y_off;
|
||||
|
@ -1255,14 +1279,30 @@ glamor_composite_with_shader(CARD8 op,
|
|||
int vert_stride = 4;
|
||||
int nrect_max;
|
||||
Bool ret = FALSE;
|
||||
glamor_composite_shader *shader = NULL, *shader_ca = NULL;
|
||||
struct blendinfo op_info, op_info_ca;
|
||||
|
||||
if(!glamor_composite_choose_shader(op, source, mask, dest,
|
||||
source_pixmap_priv, mask_pixmap_priv,
|
||||
dest_pixmap_priv,
|
||||
&key, &saved_source_format)) {
|
||||
&key, &shader, &op_info,
|
||||
&saved_source_format)) {
|
||||
glamor_fallback("glamor_composite_choose_shader failed\n");
|
||||
return ret;
|
||||
}
|
||||
if (two_pass_ca) {
|
||||
if(!glamor_composite_choose_shader(PictOpAdd, source, mask, dest,
|
||||
source_pixmap_priv, mask_pixmap_priv,
|
||||
dest_pixmap_priv,
|
||||
&key_ca, &shader_ca, &op_info_ca,
|
||||
&saved_source_format)) {
|
||||
glamor_fallback("glamor_composite_choose_shader failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
glamor_set_destination_pixmap_priv_nc(dest_pixmap_priv);
|
||||
glamor_composite_set_shader_blend(dest_pixmap_priv, &key, shader, &op_info);
|
||||
|
||||
dispatch = glamor_get_dispatch(glamor_priv);
|
||||
|
||||
|
@ -1277,8 +1317,7 @@ glamor_composite_with_shader(CARD8 op,
|
|||
pixmap_priv_get_dest_scale(dest_pixmap_priv, &dst_xscale, &dst_yscale);
|
||||
|
||||
if (glamor_priv->has_source_coords) {
|
||||
source_pixmap = glamor_get_drawable_pixmap(source->pDrawable);
|
||||
source_pixmap_priv = glamor_get_pixmap_private(source_pixmap);
|
||||
source_pixmap = source_pixmap_priv->base.pixmap;
|
||||
glamor_get_drawable_deltas(source->pDrawable,
|
||||
source_pixmap, &source_x_off,
|
||||
&source_y_off);
|
||||
|
@ -1292,8 +1331,7 @@ glamor_composite_with_shader(CARD8 op,
|
|||
}
|
||||
|
||||
if (glamor_priv->has_mask_coords) {
|
||||
mask_pixmap = glamor_get_drawable_pixmap(mask->pDrawable);
|
||||
mask_pixmap_priv = glamor_get_pixmap_private(mask_pixmap);
|
||||
mask_pixmap = mask_pixmap_priv->base.pixmap;
|
||||
glamor_get_drawable_deltas(mask->pDrawable, mask_pixmap,
|
||||
&mask_x_off, &mask_y_off);
|
||||
pixmap_priv_get_scale(mask_pixmap_priv, &mask_xscale,
|
||||
|
@ -1369,6 +1407,16 @@ glamor_composite_with_shader(CARD8 op,
|
|||
}
|
||||
glamor_flush_composite_rects(screen);
|
||||
nrect -= rect_processed;
|
||||
if (two_pass_ca) {
|
||||
glamor_composite_set_shader_blend(dest_pixmap_priv,
|
||||
&key_ca, shader_ca,
|
||||
&op_info_ca);
|
||||
glamor_flush_composite_rects(screen);
|
||||
if (nrect)
|
||||
glamor_composite_set_shader_blend(dest_pixmap_priv,
|
||||
&key, shader,
|
||||
&op_info);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
@ -1475,7 +1523,6 @@ glamor_composite_clipped_region(CARD8 op,
|
|||
glamor_pixmap_private *temp_src_priv = source_pixmap_priv;
|
||||
glamor_pixmap_private *temp_mask_priv = mask_pixmap_priv;
|
||||
int x_temp_src, y_temp_src, x_temp_mask, y_temp_mask;
|
||||
|
||||
BoxPtr extent;
|
||||
glamor_composite_rect_t rect[10];
|
||||
glamor_composite_rect_t *prect = rect;
|
||||
|
@ -1486,6 +1533,8 @@ glamor_composite_clipped_region(CARD8 op,
|
|||
int height;
|
||||
BoxPtr box;
|
||||
int nbox;
|
||||
Bool two_pass_ca = FALSE;
|
||||
|
||||
extent = RegionExtents(region);
|
||||
box = RegionRects(region);
|
||||
nbox = RegionNumRects(region);
|
||||
|
@ -1557,23 +1606,8 @@ glamor_composite_clipped_region(CARD8 op,
|
|||
|
||||
if (mask && mask->componentAlpha) {
|
||||
if (op == PictOpOver) {
|
||||
glamor_composite_clipped_region(PictOpOutReverse,
|
||||
temp_src, temp_mask, dest,
|
||||
temp_src_priv, temp_mask_priv, dest_pixmap_priv,
|
||||
region,
|
||||
x_temp_src, y_temp_src,
|
||||
x_temp_mask, y_temp_mask,
|
||||
x_dest, y_dest);
|
||||
|
||||
glamor_composite_clipped_region(PictOpAdd,
|
||||
temp_src, temp_mask, dest,
|
||||
temp_src_priv, temp_mask_priv, dest_pixmap_priv,
|
||||
region,
|
||||
x_temp_src, y_temp_src,
|
||||
x_temp_mask, y_temp_mask,
|
||||
x_dest, y_dest);
|
||||
ok = TRUE;
|
||||
goto out;
|
||||
two_pass_ca = TRUE;
|
||||
op = PictOpOutReverse;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1624,7 +1658,7 @@ glamor_composite_clipped_region(CARD8 op,
|
|||
ok = glamor_composite_with_shader(op, temp_src, temp_mask, dest,
|
||||
temp_src_priv, temp_mask_priv,
|
||||
dest_pixmap_priv,
|
||||
box_cnt, prect);
|
||||
box_cnt, prect, two_pass_ca);
|
||||
if (!ok)
|
||||
break;
|
||||
nbox -= box_cnt;
|
||||
|
@ -1959,7 +1993,6 @@ glamor_composite_glyph_rects(CARD8 op,
|
|||
int n;
|
||||
PicturePtr temp_src = NULL;
|
||||
glamor_composite_rect_t *r;
|
||||
Bool ok;
|
||||
|
||||
ValidatePicture(src);
|
||||
ValidatePicture(dst);
|
||||
|
@ -2004,27 +2037,18 @@ glamor_composite_glyph_rects(CARD8 op,
|
|||
|
||||
if (mask && mask->componentAlpha) {
|
||||
if (op == PictOpOver) {
|
||||
ok = glamor_composite_with_shader(PictOpOutReverse,
|
||||
if (glamor_composite_with_shader(PictOpOutReverse,
|
||||
temp_src, mask, dst, temp_src_priv,
|
||||
mask_pixmap_priv, dst_pixmap_priv, nrect, rects);
|
||||
if (!ok) {
|
||||
goto fallback;
|
||||
}
|
||||
ok |= glamor_composite_with_shader(PictOpAdd,
|
||||
temp_src, mask, dst, temp_src_priv,
|
||||
mask_pixmap_priv, dst_pixmap_priv, nrect, rects);
|
||||
if (ok)
|
||||
mask_pixmap_priv, dst_pixmap_priv, nrect, rects,
|
||||
TRUE))
|
||||
goto done;
|
||||
assert(0);
|
||||
}
|
||||
} else {
|
||||
if (glamor_composite_with_shader(op, temp_src, mask, dst, temp_src_priv,
|
||||
mask_pixmap_priv, dst_pixmap_priv, nrect, rects))
|
||||
mask_pixmap_priv, dst_pixmap_priv, nrect, rects, FALSE))
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fallback:
|
||||
n = nrect;
|
||||
r = rects;
|
||||
|
|
|
@ -701,12 +701,13 @@ _glamor_trapezoids_with_shader(CARD8 op,
|
|||
|
||||
ret = glamor_composite_choose_shader(op, temp_src, NULL, dst,
|
||||
temp_src_priv, NULL, dest_pixmap_priv,
|
||||
&key, &saved_source_format);
|
||||
&key, &shader, &op_info, &saved_source_format);
|
||||
if (ret == FALSE) {
|
||||
DEBUGF("can not set the shader program for composite\n");
|
||||
goto TRAPEZOID_RESET_GL;
|
||||
}
|
||||
|
||||
glamor_set_destination_pixmap_priv_nc(dest_pixmap_priv);
|
||||
glamor_composite_set_shader_blend(dest_pixmap_priv, &key, shader, op_info);
|
||||
glamor_priv->has_source_coords = key.source != SHADER_SOURCE_SOLID;
|
||||
glamor_priv->has_mask_coords = (key.mask != SHADER_MASK_NONE &&
|
||||
key.mask != SHADER_MASK_SOLID);
|
||||
|
|
Loading…
Reference in New Issue
Block a user