st/mesa: Associate a sampler view with an st texture object.
Lazily create a sampler view when the texture is being bound for the first time.
This commit is contained in:
@@ -257,6 +257,8 @@ get_pixel_transfer_program(GLcontext *ctx, const struct state_key *key)
|
|||||||
/* create the colormap/texture now if not already done */
|
/* create the colormap/texture now if not already done */
|
||||||
if (!st->pixel_xfer.pixelmap_texture) {
|
if (!st->pixel_xfer.pixelmap_texture) {
|
||||||
st->pixel_xfer.pixelmap_texture = create_color_map_texture(ctx);
|
st->pixel_xfer.pixelmap_texture = create_color_map_texture(ctx);
|
||||||
|
st->pixel_xfer.pixelmap_sampler_view = st_sampler_view_from_texture(ctx->st->pipe,
|
||||||
|
st->pixel_xfer.pixelmap_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* with a little effort, we can do four pixel map look-ups with
|
/* with a little effort, we can do four pixel map look-ups with
|
||||||
|
@@ -56,7 +56,7 @@ update_textures(struct st_context *st)
|
|||||||
|
|
||||||
/* loop over sampler units (aka tex image units) */
|
/* loop over sampler units (aka tex image units) */
|
||||||
for (su = 0; su < st->ctx->Const.MaxTextureImageUnits; su++) {
|
for (su = 0; su < st->ctx->Const.MaxTextureImageUnits; su++) {
|
||||||
struct pipe_texture *pt = NULL;
|
struct pipe_sampler_view *sampler_view = NULL;
|
||||||
|
|
||||||
if (samplersUsed & (1 << su)) {
|
if (samplersUsed & (1 << su)) {
|
||||||
struct gl_texture_object *texObj;
|
struct gl_texture_object *texObj;
|
||||||
@@ -84,7 +84,7 @@ update_textures(struct st_context *st)
|
|||||||
|
|
||||||
st->state.num_textures = su + 1;
|
st->state.num_textures = su + 1;
|
||||||
|
|
||||||
pt = st_get_stobj_texture(stObj);
|
sampler_view = st_get_stobj_sampler_view(stObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -96,17 +96,17 @@ update_textures(struct st_context *st)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pipe_texture_reference(&st->state.sampler_texture[su], pt);
|
pipe_sampler_view_reference(&st->state.sampler_views[su], sampler_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
cso_set_sampler_textures(st->cso_context,
|
cso_set_fragment_sampler_views(st->cso_context,
|
||||||
st->state.num_textures,
|
st->state.num_textures,
|
||||||
st->state.sampler_texture);
|
st->state.sampler_views);
|
||||||
if (st->ctx->Const.MaxVertexTextureImageUnits > 0) {
|
if (st->ctx->Const.MaxVertexTextureImageUnits > 0) {
|
||||||
cso_set_vertex_sampler_textures(st->cso_context,
|
cso_set_vertex_sampler_views(st->cso_context,
|
||||||
MIN2(st->state.num_textures,
|
MIN2(st->state.num_textures,
|
||||||
st->ctx->Const.MaxVertexTextureImageUnits),
|
st->ctx->Const.MaxVertexTextureImageUnits),
|
||||||
st->state.sampler_texture);
|
st->state.sampler_views);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -398,7 +398,7 @@ setup_bitmap_vertex_data(struct st_context *st,
|
|||||||
static void
|
static void
|
||||||
draw_bitmap_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
draw_bitmap_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
||||||
GLsizei width, GLsizei height,
|
GLsizei width, GLsizei height,
|
||||||
struct pipe_texture *pt,
|
struct pipe_sampler_view *sv,
|
||||||
const GLfloat *color)
|
const GLfloat *color)
|
||||||
{
|
{
|
||||||
struct st_context *st = ctx->st;
|
struct st_context *st = ctx->st;
|
||||||
@@ -436,7 +436,7 @@ draw_bitmap_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
|||||||
|
|
||||||
cso_save_rasterizer(cso);
|
cso_save_rasterizer(cso);
|
||||||
cso_save_samplers(cso);
|
cso_save_samplers(cso);
|
||||||
cso_save_sampler_textures(cso);
|
cso_save_fragment_sampler_views(cso);
|
||||||
cso_save_viewport(cso);
|
cso_save_viewport(cso);
|
||||||
cso_save_fragment_shader(cso);
|
cso_save_fragment_shader(cso);
|
||||||
cso_save_vertex_shader(cso);
|
cso_save_vertex_shader(cso);
|
||||||
@@ -466,11 +466,11 @@ draw_bitmap_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
|||||||
|
|
||||||
/* user textures, plus the bitmap texture */
|
/* user textures, plus the bitmap texture */
|
||||||
{
|
{
|
||||||
struct pipe_texture *textures[PIPE_MAX_SAMPLERS];
|
struct pipe_sampler_view *sampler_views[PIPE_MAX_SAMPLERS];
|
||||||
uint num = MAX2(stfp->bitmap_sampler + 1, st->state.num_textures);
|
uint num = MAX2(stfp->bitmap_sampler + 1, st->state.num_textures);
|
||||||
memcpy(textures, st->state.sampler_texture, sizeof(textures));
|
memcpy(sampler_views, st->state.sampler_views, sizeof(sampler_views));
|
||||||
textures[stfp->bitmap_sampler] = pt;
|
sampler_views[stfp->bitmap_sampler] = sv;
|
||||||
cso_set_sampler_textures(cso, num, textures);
|
cso_set_fragment_sampler_views(cso, num, sampler_views);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* viewport state: viewport matching window dims */
|
/* viewport state: viewport matching window dims */
|
||||||
@@ -508,7 +508,7 @@ draw_bitmap_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
|||||||
/* restore state */
|
/* restore state */
|
||||||
cso_restore_rasterizer(cso);
|
cso_restore_rasterizer(cso);
|
||||||
cso_restore_samplers(cso);
|
cso_restore_samplers(cso);
|
||||||
cso_restore_sampler_textures(cso);
|
cso_restore_fragment_sampler_views(cso);
|
||||||
cso_restore_viewport(cso);
|
cso_restore_viewport(cso);
|
||||||
cso_restore_fragment_shader(cso);
|
cso_restore_fragment_shader(cso);
|
||||||
cso_restore_vertex_shader(cso);
|
cso_restore_vertex_shader(cso);
|
||||||
@@ -602,6 +602,7 @@ st_flush_bitmap_cache(struct st_context *st)
|
|||||||
if (st->ctx->DrawBuffer) {
|
if (st->ctx->DrawBuffer) {
|
||||||
struct pipe_context *pipe = st->pipe;
|
struct pipe_context *pipe = st->pipe;
|
||||||
struct pipe_screen *screen = pipe->screen;
|
struct pipe_screen *screen = pipe->screen;
|
||||||
|
struct pipe_sampler_view *sv;
|
||||||
|
|
||||||
assert(cache->xmin <= cache->xmax);
|
assert(cache->xmin <= cache->xmax);
|
||||||
|
|
||||||
@@ -624,13 +625,18 @@ st_flush_bitmap_cache(struct st_context *st)
|
|||||||
cache->trans = NULL;
|
cache->trans = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sv = st_sampler_view_from_texture(st->pipe, cache->texture);
|
||||||
|
if (sv) {
|
||||||
draw_bitmap_quad(st->ctx,
|
draw_bitmap_quad(st->ctx,
|
||||||
cache->xpos,
|
cache->xpos,
|
||||||
cache->ypos,
|
cache->ypos,
|
||||||
cache->zpos,
|
cache->zpos,
|
||||||
BITMAP_CACHE_WIDTH, BITMAP_CACHE_HEIGHT,
|
BITMAP_CACHE_WIDTH, BITMAP_CACHE_HEIGHT,
|
||||||
cache->texture,
|
sv,
|
||||||
cache->color);
|
cache->color);
|
||||||
|
|
||||||
|
pipe_sampler_view_reference(&sv, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* release/free the texture */
|
/* release/free the texture */
|
||||||
@@ -753,10 +759,18 @@ st_Bitmap(GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height,
|
|||||||
|
|
||||||
pt = make_bitmap_texture(ctx, width, height, unpack, bitmap);
|
pt = make_bitmap_texture(ctx, width, height, unpack, bitmap);
|
||||||
if (pt) {
|
if (pt) {
|
||||||
|
struct pipe_sampler_view *sv = st_sampler_view_from_texture(st->pipe, pt);
|
||||||
|
|
||||||
assert(pt->target == PIPE_TEXTURE_2D);
|
assert(pt->target == PIPE_TEXTURE_2D);
|
||||||
|
|
||||||
|
if (sv) {
|
||||||
draw_bitmap_quad(ctx, x, y, ctx->Current.RasterPos[2],
|
draw_bitmap_quad(ctx, x, y, ctx->Current.RasterPos[2],
|
||||||
width, height, pt,
|
width, height, sv,
|
||||||
st->ctx->Current.RasterColor);
|
st->ctx->Current.RasterColor);
|
||||||
|
|
||||||
|
pipe_sampler_view_reference(&sv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* release/free the texture */
|
/* release/free the texture */
|
||||||
pipe_texture_reference(&pt, NULL);
|
pipe_texture_reference(&pt, NULL);
|
||||||
}
|
}
|
||||||
|
@@ -503,7 +503,7 @@ static void
|
|||||||
draw_textured_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
draw_textured_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
||||||
GLsizei width, GLsizei height,
|
GLsizei width, GLsizei height,
|
||||||
GLfloat zoomX, GLfloat zoomY,
|
GLfloat zoomX, GLfloat zoomY,
|
||||||
struct pipe_texture *pt,
|
struct pipe_sampler_view *sv,
|
||||||
void *driver_vp,
|
void *driver_vp,
|
||||||
void *driver_fp,
|
void *driver_fp,
|
||||||
const GLfloat *color,
|
const GLfloat *color,
|
||||||
@@ -526,7 +526,7 @@ draw_textured_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
|||||||
cso_save_rasterizer(cso);
|
cso_save_rasterizer(cso);
|
||||||
cso_save_viewport(cso);
|
cso_save_viewport(cso);
|
||||||
cso_save_samplers(cso);
|
cso_save_samplers(cso);
|
||||||
cso_save_sampler_textures(cso);
|
cso_save_fragment_sampler_views(cso);
|
||||||
cso_save_fragment_shader(cso);
|
cso_save_fragment_shader(cso);
|
||||||
cso_save_vertex_shader(cso);
|
cso_save_vertex_shader(cso);
|
||||||
cso_save_vertex_elements(cso);
|
cso_save_vertex_elements(cso);
|
||||||
@@ -586,13 +586,13 @@ draw_textured_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
|||||||
|
|
||||||
/* texture state: */
|
/* texture state: */
|
||||||
if (st->pixel_xfer.pixelmap_enabled) {
|
if (st->pixel_xfer.pixelmap_enabled) {
|
||||||
struct pipe_texture *textures[2];
|
struct pipe_sampler_view *sampler_views[2];
|
||||||
textures[0] = pt;
|
sampler_views[0] = sv;
|
||||||
textures[1] = st->pixel_xfer.pixelmap_texture;
|
sampler_views[1] = st->pixel_xfer.pixelmap_sampler_view;
|
||||||
cso_set_sampler_textures(cso, 2, textures);
|
cso_set_fragment_sampler_views(cso, 2, sampler_views);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cso_set_sampler_textures(cso, 1, &pt);
|
cso_set_fragment_sampler_views(cso, 1, &sv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compute window coords (y=0=bottom) with pixel zoom.
|
/* Compute window coords (y=0=bottom) with pixel zoom.
|
||||||
@@ -608,14 +608,14 @@ draw_textured_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
|
|||||||
z = z * 2.0 - 1.0;
|
z = z * 2.0 - 1.0;
|
||||||
|
|
||||||
draw_quad(ctx, x0, y0, z, x1, y1, color, invertTex,
|
draw_quad(ctx, x0, y0, z, x1, y1, color, invertTex,
|
||||||
(GLfloat) width / pt->width0,
|
(GLfloat) width / sv->texture->width0,
|
||||||
(GLfloat) height / pt->height0);
|
(GLfloat) height / sv->texture->height0);
|
||||||
|
|
||||||
/* restore state */
|
/* restore state */
|
||||||
cso_restore_rasterizer(cso);
|
cso_restore_rasterizer(cso);
|
||||||
cso_restore_viewport(cso);
|
cso_restore_viewport(cso);
|
||||||
cso_restore_samplers(cso);
|
cso_restore_samplers(cso);
|
||||||
cso_restore_sampler_textures(cso);
|
cso_restore_fragment_sampler_views(cso);
|
||||||
cso_restore_fragment_shader(cso);
|
cso_restore_fragment_shader(cso);
|
||||||
cso_restore_vertex_shader(cso);
|
cso_restore_vertex_shader(cso);
|
||||||
cso_restore_vertex_elements(cso);
|
cso_restore_vertex_elements(cso);
|
||||||
@@ -809,12 +809,17 @@ st_DrawPixels(GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height,
|
|||||||
struct pipe_texture *pt
|
struct pipe_texture *pt
|
||||||
= make_texture(st, width, height, format, type, unpack, pixels);
|
= make_texture(st, width, height, format, type, unpack, pixels);
|
||||||
if (pt) {
|
if (pt) {
|
||||||
|
struct pipe_sampler_view *sv = st_sampler_view_from_texture(st->pipe, pt);
|
||||||
|
|
||||||
|
if (sv) {
|
||||||
draw_textured_quad(ctx, x, y, ctx->Current.RasterPos[2],
|
draw_textured_quad(ctx, x, y, ctx->Current.RasterPos[2],
|
||||||
width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY,
|
width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY,
|
||||||
pt,
|
sv,
|
||||||
driver_vp,
|
driver_vp,
|
||||||
driver_fp,
|
driver_fp,
|
||||||
color, GL_FALSE);
|
color, GL_FALSE);
|
||||||
|
pipe_sampler_view_reference(&sv, NULL);
|
||||||
|
}
|
||||||
pipe_texture_reference(&pt, NULL);
|
pipe_texture_reference(&pt, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -933,6 +938,7 @@ st_CopyPixels(GLcontext *ctx, GLint srcx, GLint srcy,
|
|||||||
struct st_renderbuffer *rbRead;
|
struct st_renderbuffer *rbRead;
|
||||||
void *driver_vp, *driver_fp;
|
void *driver_vp, *driver_fp;
|
||||||
struct pipe_texture *pt;
|
struct pipe_texture *pt;
|
||||||
|
struct pipe_sampler_view *sv;
|
||||||
GLfloat *color;
|
GLfloat *color;
|
||||||
enum pipe_format srcFormat, texFormat;
|
enum pipe_format srcFormat, texFormat;
|
||||||
int ptw, pth;
|
int ptw, pth;
|
||||||
@@ -1050,6 +1056,11 @@ st_CopyPixels(GLcontext *ctx, GLint srcx, GLint srcy,
|
|||||||
if (!pt)
|
if (!pt)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
sv = st_sampler_view_from_texture(st->pipe, pt);
|
||||||
|
if (!sv) {
|
||||||
|
pipe_texture_reference(&pt, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (srcFormat == texFormat) {
|
if (srcFormat == texFormat) {
|
||||||
/* copy source framebuffer surface into mipmap/texture */
|
/* copy source framebuffer surface into mipmap/texture */
|
||||||
@@ -1118,12 +1129,13 @@ st_CopyPixels(GLcontext *ctx, GLint srcx, GLint srcy,
|
|||||||
/* draw textured quad */
|
/* draw textured quad */
|
||||||
draw_textured_quad(ctx, dstx, dsty, ctx->Current.RasterPos[2],
|
draw_textured_quad(ctx, dstx, dsty, ctx->Current.RasterPos[2],
|
||||||
width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY,
|
width, height, ctx->Pixel.ZoomX, ctx->Pixel.ZoomY,
|
||||||
pt,
|
sv,
|
||||||
driver_vp,
|
driver_vp,
|
||||||
driver_fp,
|
driver_fp,
|
||||||
color, GL_TRUE);
|
color, GL_TRUE);
|
||||||
|
|
||||||
pipe_texture_reference(&pt, NULL);
|
pipe_texture_reference(&pt, NULL);
|
||||||
|
pipe_sampler_view_reference(&sv, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -123,6 +123,8 @@ st_DeleteTextureObject(GLcontext *ctx,
|
|||||||
struct st_texture_object *stObj = st_texture_object(texObj);
|
struct st_texture_object *stObj = st_texture_object(texObj);
|
||||||
if (stObj->pt)
|
if (stObj->pt)
|
||||||
pipe_texture_reference(&stObj->pt, NULL);
|
pipe_texture_reference(&stObj->pt, NULL);
|
||||||
|
if (stObj->sampler_view)
|
||||||
|
pipe_sampler_view_reference(&stObj->sampler_view, NULL);
|
||||||
|
|
||||||
_mesa_delete_texture_object(ctx, texObj);
|
_mesa_delete_texture_object(ctx, texObj);
|
||||||
}
|
}
|
||||||
@@ -312,6 +314,8 @@ guess_and_alloc_texture(struct st_context *st,
|
|||||||
depth,
|
depth,
|
||||||
usage);
|
usage);
|
||||||
|
|
||||||
|
stObj->pipe = st->pipe;
|
||||||
|
|
||||||
DBG("%s - success\n", __FUNCTION__);
|
DBG("%s - success\n", __FUNCTION__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -215,8 +215,8 @@ static void st_destroy_context_priv( struct st_context *st )
|
|||||||
st_destroy_drawtex(st);
|
st_destroy_drawtex(st);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i = 0; i < Elements(st->state.sampler_texture); i++) {
|
for (i = 0; i < Elements(st->state.sampler_views); i++) {
|
||||||
pipe_texture_reference(&st->state.sampler_texture[i], NULL);
|
pipe_sampler_view_reference(&st->state.sampler_views[i], NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < Elements(st->state.constants); i++) {
|
for (i = 0; i < Elements(st->state.constants); i++) {
|
||||||
|
@@ -94,7 +94,7 @@ struct st_context
|
|||||||
struct pipe_clip_state clip;
|
struct pipe_clip_state clip;
|
||||||
struct pipe_buffer *constants[2];
|
struct pipe_buffer *constants[2];
|
||||||
struct pipe_framebuffer_state framebuffer;
|
struct pipe_framebuffer_state framebuffer;
|
||||||
struct pipe_texture *sampler_texture[PIPE_MAX_SAMPLERS];
|
struct pipe_sampler_view *sampler_views[PIPE_MAX_SAMPLERS];
|
||||||
struct pipe_scissor_state scissor;
|
struct pipe_scissor_state scissor;
|
||||||
struct pipe_viewport_state viewport;
|
struct pipe_viewport_state viewport;
|
||||||
|
|
||||||
@@ -141,6 +141,7 @@ struct st_context
|
|||||||
struct st_fragment_program *combined_prog;
|
struct st_fragment_program *combined_prog;
|
||||||
GLuint combined_prog_sn;
|
GLuint combined_prog_sn;
|
||||||
struct pipe_texture *pixelmap_texture;
|
struct pipe_texture *pixelmap_texture;
|
||||||
|
struct pipe_sampler_view *pixelmap_sampler_view;
|
||||||
boolean pixelmap_enabled; /**< use the pixelmap texture? */
|
boolean pixelmap_enabled; /**< use the pixelmap texture? */
|
||||||
} pixel_xfer;
|
} pixel_xfer;
|
||||||
|
|
||||||
|
@@ -29,6 +29,9 @@
|
|||||||
#define ST_TEXTURE_H
|
#define ST_TEXTURE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "pipe/p_context.h"
|
||||||
|
#include "util/u_sampler.h"
|
||||||
|
|
||||||
#include "main/mtypes.h"
|
#include "main/mtypes.h"
|
||||||
|
|
||||||
struct pipe_context;
|
struct pipe_context;
|
||||||
@@ -68,6 +71,13 @@ struct st_texture_object
|
|||||||
*/
|
*/
|
||||||
struct pipe_texture *pt;
|
struct pipe_texture *pt;
|
||||||
|
|
||||||
|
/* Default sampler view attached to this texture object. Created lazily
|
||||||
|
* on first binding.
|
||||||
|
*/
|
||||||
|
struct pipe_sampler_view *sampler_view;
|
||||||
|
|
||||||
|
struct pipe_context *pipe;
|
||||||
|
|
||||||
GLboolean teximage_realloc;
|
GLboolean teximage_realloc;
|
||||||
|
|
||||||
/* True if there is/was a surface bound to this texture object. It helps
|
/* True if there is/was a surface bound to this texture object. It helps
|
||||||
@@ -105,6 +115,35 @@ st_get_stobj_texture(struct st_texture_object *stObj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static INLINE struct pipe_sampler_view *
|
||||||
|
st_sampler_view_from_texture(struct pipe_context *pipe,
|
||||||
|
struct pipe_texture *texture)
|
||||||
|
{
|
||||||
|
struct pipe_sampler_view templ;
|
||||||
|
|
||||||
|
u_sampler_view_default_template(&templ,
|
||||||
|
texture,
|
||||||
|
texture->format);
|
||||||
|
|
||||||
|
return pipe->create_sampler_view(pipe, texture, &templ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static INLINE struct pipe_sampler_view *
|
||||||
|
st_get_stobj_sampler_view(struct st_texture_object *stObj)
|
||||||
|
{
|
||||||
|
if (!stObj || !stObj->pt) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stObj->sampler_view) {
|
||||||
|
stObj->sampler_view = st_sampler_view_from_texture(stObj->pipe, stObj->pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stObj->sampler_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern struct pipe_texture *
|
extern struct pipe_texture *
|
||||||
st_texture_create(struct st_context *st,
|
st_texture_create(struct st_context *st,
|
||||||
enum pipe_texture_target target,
|
enum pipe_texture_target target,
|
||||||
|
Reference in New Issue
Block a user