lots of corrections to VEFontCache, still no letters on screen
Something is either wrong with the sokol_gfx rendering setup or its a really dumb checkbox/uv value
This commit is contained in:
parent
3b9e08794a
commit
87bc31636e
@ -179,8 +179,8 @@ InitAtlasParams :: struct {
|
||||
}
|
||||
|
||||
InitAtlasParams_Default :: InitAtlasParams {
|
||||
width = 4 * Kilobyte,
|
||||
height = 2 * Kilobyte,
|
||||
width = 4096,
|
||||
height = 2048,
|
||||
glyph_padding = 1,
|
||||
|
||||
region_a = {
|
||||
@ -265,38 +265,43 @@ init :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
draw_list.calls, error = make( Array(DrawCall), 512 )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.calls")
|
||||
|
||||
init_atlas_region :: proc( region : ^AtlasRegion, params : InitAtlasParams, region_params : InitAtlasRegionParams ) {
|
||||
init_atlas_region :: proc( region : ^AtlasRegion, params : InitAtlasParams, region_params : InitAtlasRegionParams, factor : Vec2i, expected_cap : u32 ) {
|
||||
using region
|
||||
|
||||
next_idx = 0;
|
||||
width = region_params.width
|
||||
height = region_params.height
|
||||
size = {
|
||||
params.width / 4,
|
||||
params.height / 2,
|
||||
params.width / factor.x,
|
||||
params.height / factor.y,
|
||||
}
|
||||
capacity = {
|
||||
size.x / width,
|
||||
size.y / height,
|
||||
}
|
||||
assert( capacity.x * capacity.y == expected_cap )
|
||||
|
||||
error : AllocatorError
|
||||
// state.cache, error = make( HMapChained(LRU_Link), uint(capacity.x * capacity.y) )
|
||||
// assert( error == .None, "VEFontCache.init_atlas_region : Failed to allocate state.cache")
|
||||
LRU_init( & state, capacity.x * capacity.y )
|
||||
}
|
||||
init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a )
|
||||
init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b )
|
||||
init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c )
|
||||
init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d )
|
||||
init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a, { 4, 2}, 1024 )
|
||||
init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b, { 4, 2}, 512 )
|
||||
init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c, { 4, 1}, 512 )
|
||||
init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d, { 2, 1}, 256 )
|
||||
|
||||
atlas.width = atlas_params.width
|
||||
atlas.height = atlas_params.height
|
||||
atlas.glyph_padding = atlas_params.glyph_padding
|
||||
|
||||
atlas.region_a.offset = {0, 0}
|
||||
atlas.region_b.offset.x = 0
|
||||
atlas.region_b.offset.y = atlas.region_a.size.y
|
||||
atlas.region_c.offset.x = atlas.region_a.size.x
|
||||
atlas.region_c.offset.y = 0
|
||||
atlas.region_d.offset.x = atlas.width / 2
|
||||
atlas.region_d.offset.y = 0
|
||||
|
||||
LRU_init( & shape_cache.state, shape_cache_params.capacity )
|
||||
|
||||
@ -476,7 +481,7 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale,
|
||||
f32(bounds_0.x) - 21,
|
||||
f32(bounds_0.y) - 33,
|
||||
}
|
||||
|
||||
|
||||
// Note(Original Author): Figure out scaling so it fits within our box.
|
||||
draw := DrawCall_Default
|
||||
draw.pass = FrameBufferPass.Glyph
|
||||
@ -601,7 +606,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
|
||||
|
||||
// Draw oversized glyph to update FBO
|
||||
glyph_draw_scale := over_sample * entry.size_scale
|
||||
glyph_draw_translate := Vec2 { f32(bounds_0.x), f32(bounds_0.y) } * glyph_draw_scale + Vec2{ glyph_padding, glyph_padding }
|
||||
glyph_draw_translate := Vec2 { f32(-bounds_0.x), f32(-bounds_0.y) } * glyph_draw_scale + Vec2{ glyph_padding, glyph_padding }
|
||||
glyph_draw_translate.x = cast(f32) (i32(glyph_draw_translate.x + 0.9999999))
|
||||
glyph_draw_translate.y = cast(f32) (i32(glyph_draw_translate.y + 0.9999999))
|
||||
|
||||
@ -613,16 +618,16 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
|
||||
|
||||
// Calculate the src and destination regions
|
||||
dst_position, dst_width, dst_height := atlas_bbox( atlas, region_kind, u32(atlas_index) )
|
||||
dst_glyph_position := dst_position //+ { glyph_padding, glyph_padding }
|
||||
dst_glyph_position := dst_position + { glyph_padding, glyph_padding }
|
||||
dst_glyph_width := f32(bounds_width) * entry.size_scale
|
||||
dst_glyph_height := f32(bounds_height) * entry.size_scale
|
||||
// dst_glyph_position -= { glyph_padding, glyph_padding }
|
||||
dst_glyph_position -= { glyph_padding, glyph_padding }
|
||||
dst_glyph_width += 2 * glyph_padding
|
||||
dst_glyph_height += 2 * glyph_padding
|
||||
|
||||
dst_size := Vec2 { dst_width, dst_height }
|
||||
dst_glyph_size := Vec2 { dst_glyph_width, dst_glyph_height }
|
||||
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, f32(atlas.width), f32(atlas.height) )
|
||||
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, f32(atlas.width), f32(atlas.height) )
|
||||
screenspace_x_form( & dst_position, & dst_size, f32(atlas.width), f32(atlas.height) )
|
||||
|
||||
src_position := Vec2 { f32(atlas.update_batch_x), 0 }
|
||||
@ -652,7 +657,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
|
||||
// Queue up a blit from glyph_update_FBO to the atlas
|
||||
region = .None
|
||||
start_index = u32(atlas.draw_list.indices.num)
|
||||
blit_quad( & atlas.draw_list, dst_glyph_position, dst_glyph_position + dst_glyph_size, src_position, src_position + src_size )
|
||||
blit_quad( & atlas.draw_list, dst_glyph_position, dst_position + dst_glyph_size, src_position, src_position + src_size )
|
||||
end_index = u32(atlas.draw_list.indices.num)
|
||||
append( & atlas.draw_list.calls, call )
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : u32 )
|
||||
height = f32(atlas.region_b.height)
|
||||
|
||||
position.x = cast(f32) (( local_idx % atlas.region_a.capacity.x ) * atlas.region_a.width)
|
||||
position.y = cast(f32) (( local_idx % atlas.region_a.capacity.x ) * atlas.region_a.height)
|
||||
position.y = cast(f32) (( local_idx / atlas.region_a.capacity.x ) * atlas.region_a.height)
|
||||
|
||||
position.x += f32(atlas.region_a.offset.x)
|
||||
position.y += f32(atlas.region_a.offset.y)
|
||||
@ -46,7 +46,7 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : u32 )
|
||||
height = f32(atlas.region_b.height)
|
||||
|
||||
position.x = cast(f32) (( local_idx % atlas.region_b.capacity.x ) * atlas.region_b.width)
|
||||
position.y = cast(f32) (( local_idx % atlas.region_b.capacity.x ) * atlas.region_b.height)
|
||||
position.y = cast(f32) (( local_idx / atlas.region_b.capacity.x ) * atlas.region_b.height)
|
||||
|
||||
position.x += f32(atlas.region_b.offset.x)
|
||||
position.y += f32(atlas.region_b.offset.y)
|
||||
@ -56,7 +56,7 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : u32 )
|
||||
height = f32(atlas.region_c.height)
|
||||
|
||||
position.x = cast(f32) (( local_idx % atlas.region_c.capacity.x ) * atlas.region_c.width)
|
||||
position.y = cast(f32) (( local_idx % atlas.region_c.capacity.x ) * atlas.region_c.height)
|
||||
position.y = cast(f32) (( local_idx / atlas.region_c.capacity.x ) * atlas.region_c.height)
|
||||
|
||||
position.x += f32(atlas.region_c.offset.x)
|
||||
position.y += f32(atlas.region_c.offset.y)
|
||||
@ -66,7 +66,7 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : u32 )
|
||||
height = f32(atlas.region_d.height)
|
||||
|
||||
position.x = cast(f32) (( local_idx % atlas.region_d.capacity.x ) * atlas.region_d.width)
|
||||
position.y = cast(f32) (( local_idx % atlas.region_d.capacity.x ) * atlas.region_d.height)
|
||||
position.y = cast(f32) (( local_idx / atlas.region_d.capacity.x ) * atlas.region_d.height)
|
||||
|
||||
position.x += f32(atlas.region_d.offset.x)
|
||||
position.y += f32(atlas.region_d.offset.y)
|
||||
@ -140,7 +140,7 @@ decide_codepoint_region :: proc( ctx : ^Context, entry : ^Entry, glyph_index : G
|
||||
region_kind = .A
|
||||
region = & atlas.region_a
|
||||
}
|
||||
else if bounds_width_scaled <= atlas.region_b.width && bounds_height_scaled <= atlas.region_b.height
|
||||
else if bounds_width_scaled <= atlas.region_a.width && bounds_height_scaled <= atlas.region_a.height
|
||||
{
|
||||
// Region B for tall glyphs. These are good for things such as european alphabets.
|
||||
region_kind = .B
|
||||
|
@ -44,32 +44,33 @@ GlyphDrawBuffer :: struct {
|
||||
draw_list : DrawList,
|
||||
}
|
||||
|
||||
blit_quad :: proc( draw_list : ^DrawList, p0, p1 : Vec2, uv0, uv1 : Vec2 )
|
||||
blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} )
|
||||
{
|
||||
// logf("Blitting: xy0: %0.2f, %0.2f xy1: %0.2f, %0.2f uv0: %0.2f, %0.2f uv1: %0.2f, %0.2f",
|
||||
// p0.x, p0.y, p1.x, p1.y, uv0.x, uv0.y, uv1.x, uv1.y);
|
||||
v_offset := cast(u32) draw_list.vertices.num
|
||||
|
||||
vertex := Vertex {
|
||||
{p0.x, p0.y},
|
||||
uv0.x,
|
||||
uv0.y
|
||||
uv0.x, uv0.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
|
||||
vertex = Vertex {
|
||||
{p0.x, p1.y},
|
||||
uv0.x,
|
||||
uv1.y
|
||||
uv0.x, uv1.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
|
||||
vertex = Vertex {
|
||||
{p1.x, p0.y},
|
||||
uv1.x,
|
||||
uv0.y
|
||||
uv1.x, uv0.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
|
||||
vertex = Vertex {
|
||||
{p1.x, p1.y},
|
||||
uv1.x,
|
||||
uv1.y
|
||||
uv1.x, uv1.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
|
||||
@ -112,6 +113,8 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Gly
|
||||
glyph_dst_height := f32(bounds_height) * entry.size_scale
|
||||
glyph_width += f32(2 * ctx.atlas.glyph_padding)
|
||||
glyph_height += f32(2 * ctx.atlas.glyph_padding)
|
||||
glyph_dst_width += f32(2 * ctx.atlas.glyph_padding)
|
||||
glyph_dst_height += f32(2 * ctx.atlas.glyph_padding)
|
||||
|
||||
// Figure out the destination rect.
|
||||
bounds_scaled := Vec2 {
|
||||
@ -150,7 +153,7 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Gly
|
||||
draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, position, scale : Vec2 ) -> b32
|
||||
{
|
||||
// Glyph not in current font
|
||||
if glyph_index == 0 do return true
|
||||
if glyph_index == 0 do return true
|
||||
if parser_is_glyph_empty( & entry.parser_info, glyph_index ) do return true
|
||||
|
||||
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
||||
@ -190,7 +193,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
|
||||
glyph_scale := Vec2 { glyph_width, glyph_height }
|
||||
|
||||
bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale - { 0.5, 0.5 }
|
||||
bounds_0_scaled = {
|
||||
bounds_0_scaled = {
|
||||
cast(f32) cast(i32) bounds_0_scaled.x,
|
||||
cast(f32) cast(i32) bounds_0_scaled.y,
|
||||
}
|
||||
@ -208,7 +211,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
|
||||
call := DrawCall_Default
|
||||
{
|
||||
using call
|
||||
pass = .Target_Uncached
|
||||
pass = .Target
|
||||
colour = ctx.colour
|
||||
start_index = cast(u32) ctx.draw_list.indices.num
|
||||
|
||||
@ -305,7 +308,6 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position :
|
||||
|
||||
cache_glyph_to_atlas( ctx, font, glyph_index )
|
||||
|
||||
// lru_code := u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
|
||||
lru_code := font_glyph_lru_code(font, glyph_index)
|
||||
set( ctx.temp_codepoint_seen, lru_code, true )
|
||||
ctx.temp_codepoint_seen_num += 1
|
||||
@ -315,8 +317,7 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position :
|
||||
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, i32(shaped.glyphs.num), position, scale )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
ctx.cursor_pos.x = position.x + shaped.end_cursor_pos.x * scale.x
|
||||
ctx.cursor_pos.y = position.y + shaped.end_cursor_pos.y * scale.y
|
||||
ctx.cursor_pos = position + shaped.end_cursor_pos * scale
|
||||
|
||||
return true
|
||||
}
|
||||
@ -328,7 +329,6 @@ draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, b
|
||||
{
|
||||
glyph_index := shaped.glyphs.data[ index ]
|
||||
shaped_position := shaped.positions.data[index]
|
||||
// glyph_translate_x := position.x + shaped_position.x * scale.x
|
||||
glyph_translate := position + shaped_position * scale
|
||||
glyph_cached := draw_cached_glyph( ctx, entry, glyph_index, glyph_translate, scale)
|
||||
assert( glyph_cached == true )
|
||||
@ -375,12 +375,12 @@ merge_draw_list :: proc( dst, src : ^DrawList )
|
||||
error : AllocatorError
|
||||
|
||||
v_offset := cast(u32) dst.vertices.num
|
||||
// for index : u32 = 0; index < cast(u32) src.vertices.num; index += 1 {
|
||||
// error = append( & dst.vertices, src.vertices.data[index] )
|
||||
// assert( error == .None )
|
||||
// }
|
||||
error = append( & dst.vertices, src.vertices )
|
||||
assert( error == .None )
|
||||
for index : u32 = 0; index < cast(u32) src.vertices.num; index += 1 {
|
||||
error = append( & dst.vertices, src.vertices.data[index] )
|
||||
assert( error == .None )
|
||||
}
|
||||
// error = append( & dst.vertices, src.vertices )
|
||||
// assert( error == .None )
|
||||
|
||||
i_offset := cast(u32) dst.indices.num
|
||||
for index : u32 = 0; index < cast(u32) src.indices.num; index += 1 {
|
||||
|
@ -210,10 +210,18 @@ render :: proc()
|
||||
|
||||
sokol_gfx.apply_pipeline( screen_pipeline )
|
||||
|
||||
fs_uniform := Ve_Draw_Text_Fs_Params { down_sample = 0, colour = {1, 1, 1, 1} }
|
||||
sokol_gfx.apply_uniforms( ShaderStage.FS, SLOT_ve_blit_atlas_fs_params, Range { & fs_uniform, size_of(fs_uniform) })
|
||||
src_rt := atlas_rt_color
|
||||
|
||||
src_rt := draw_call.pass == .Target_Uncached ? glyph_rt_color : atlas_rt_color
|
||||
fs_uniform := Ve_Draw_Text_Fs_Params {
|
||||
// down_sample = draw_call.pass == .Target_Uncached ? 1 : 0,
|
||||
colour = {1, 1, 1, 1},
|
||||
}
|
||||
|
||||
if draw_call.pass == .Target_Uncached {
|
||||
fs_uniform.down_sample = 1
|
||||
src_rt = glyph_rt_color
|
||||
}
|
||||
sokol_gfx.apply_uniforms( ShaderStage.FS, SLOT_ve_blit_atlas_fs_params, Range { & fs_uniform, size_of(fs_uniform) })
|
||||
|
||||
sokol_gfx.apply_bindings(Bindings {
|
||||
vertex_buffers = {
|
||||
|
@ -183,6 +183,7 @@ font_provider_startup :: proc()
|
||||
pixel_format = .DEPTH,
|
||||
// compare = .ALWAYS,
|
||||
},
|
||||
cull_mode = .NONE,
|
||||
// sample_count = 1,
|
||||
// label =
|
||||
})
|
||||
@ -200,7 +201,7 @@ font_provider_startup :: proc()
|
||||
num_mipmaps = 1,
|
||||
usage = .IMMUTABLE,
|
||||
pixel_format = .R8,
|
||||
sample_count = app_env.defaults.sample_count,
|
||||
sample_count = 1,
|
||||
// TODO(Ed): Setup labels for debug tracing/logging
|
||||
// label =
|
||||
})
|
||||
@ -215,7 +216,7 @@ font_provider_startup :: proc()
|
||||
num_mipmaps = 1,
|
||||
usage = .IMMUTABLE,
|
||||
pixel_format = .DEPTH,
|
||||
sample_count = app_env.defaults.sample_count,
|
||||
sample_count = 1,
|
||||
})
|
||||
|
||||
color_attach := AttachmentDesc {
|
||||
@ -236,10 +237,10 @@ font_provider_startup :: proc()
|
||||
glyph_action := PassAction {
|
||||
colors = {
|
||||
0 = {
|
||||
load_action = .CLEAR,
|
||||
load_action = .LOAD,
|
||||
store_action = .STORE,
|
||||
clear_value = {0.01,0.01,0.01,1},
|
||||
// clear_value = {0.00, 0.00, 0.00, 0.00},
|
||||
// clear_value = {0.01,0.01,0.01,1},
|
||||
clear_value = {0.00, 0.00, 0.00, 0.00},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,6 +299,7 @@ font_provider_startup :: proc()
|
||||
pixel_format = .DEPTH,
|
||||
compare = .ALWAYS,
|
||||
},
|
||||
cull_mode = .NONE,
|
||||
// sample_count = 1,
|
||||
})
|
||||
}
|
||||
@ -307,13 +309,13 @@ font_provider_startup :: proc()
|
||||
atlas_rt_color = sokol_gfx.make_image( ImageDesc {
|
||||
type = ._2D,
|
||||
render_target = true,
|
||||
width = i32(ve_font_cache.atlas.buffer_width),
|
||||
height = i32(ve_font_cache.atlas.buffer_height),
|
||||
width = i32(ve_font_cache.atlas.width),
|
||||
height = i32(ve_font_cache.atlas.height),
|
||||
num_slices = 1,
|
||||
num_mipmaps = 1,
|
||||
usage = .IMMUTABLE,
|
||||
pixel_format = .R8,
|
||||
sample_count = app_env.defaults.sample_count,
|
||||
sample_count = 1,
|
||||
// TODO(Ed): Setup labels for debug tracing/logging
|
||||
// label =
|
||||
})
|
||||
@ -322,13 +324,13 @@ font_provider_startup :: proc()
|
||||
atlas_rt_depth = sokol_gfx.make_image( ImageDesc {
|
||||
type = ._2D,
|
||||
render_target = true,
|
||||
width = i32(ve_font_cache.atlas.buffer_width),
|
||||
height = i32(ve_font_cache.atlas.buffer_height),
|
||||
width = i32(ve_font_cache.atlas.width),
|
||||
height = i32(ve_font_cache.atlas.height),
|
||||
num_slices = 1,
|
||||
num_mipmaps = 1,
|
||||
usage = .IMMUTABLE,
|
||||
pixel_format = .DEPTH,
|
||||
sample_count = app_env.defaults.sample_count,
|
||||
sample_count = 1,
|
||||
})
|
||||
verify( sokol_gfx.query_image_state(atlas_rt_depth) < ResourceState.FAILED, "Failed to make atlas_rt_depth")
|
||||
|
||||
@ -352,7 +354,7 @@ font_provider_startup :: proc()
|
||||
0 = {
|
||||
load_action = .LOAD,
|
||||
store_action = .STORE,
|
||||
clear_value = {0,0,0,1.0},
|
||||
clear_value = {0, 0, 0, 0.0},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -386,7 +388,7 @@ font_provider_startup :: proc()
|
||||
}
|
||||
|
||||
color_target := ColorTargetState {
|
||||
// pixel_format = .R8,
|
||||
pixel_format = app_env.defaults.color_format,
|
||||
// write_mask =
|
||||
blend = BlendState {
|
||||
enabled = true,
|
||||
@ -408,6 +410,11 @@ font_provider_startup :: proc()
|
||||
},
|
||||
color_count = 1,
|
||||
sample_count = 1,
|
||||
depth = {
|
||||
pixel_format = app_env.defaults.depth_format,
|
||||
compare = .ALWAYS,
|
||||
},
|
||||
cull_mode = .NONE,
|
||||
})
|
||||
verify( sokol_gfx.query_pipeline_state(screen_pipeline) < ResourceState.FAILED, "Failed to make screen_pipeline" )
|
||||
}
|
||||
@ -417,9 +424,9 @@ font_provider_startup :: proc()
|
||||
screen_action := PassAction {
|
||||
colors = {
|
||||
0 = {
|
||||
load_action = .CLEAR,
|
||||
load_action = .LOAD,
|
||||
store_action = .STORE,
|
||||
clear_value = {1.0,0.0,0.0,1.0},
|
||||
clear_value = {0.0,0.0,0.0,0.0},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user