Code cleanup: remove shader context.
This was needed when we accessed OSL closure memory after shader evaluation, which could get overwritten by another shader evaluation. But all closures are immediatley converted to ShaderClosure now, so no longer needed.
This commit is contained in:
@@ -52,7 +52,7 @@ ccl_device_inline void compute_light_pass(KernelGlobals *kg,
|
||||
|
||||
/* evaluate surface shader */
|
||||
float rbsdf = path_state_rng_1D(kg, &state, PRNG_BSDF);
|
||||
shader_eval_surface(kg, sd, &state, rbsdf, state.flag, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_surface(kg, sd, &state, rbsdf, state.flag);
|
||||
|
||||
/* TODO, disable more closures we don't need besides transparent */
|
||||
shader_bsdf_disable_transparency(kg, sd);
|
||||
@@ -242,12 +242,12 @@ ccl_device float3 kernel_bake_evaluate_direct_indirect(KernelGlobals *kg,
|
||||
}
|
||||
else {
|
||||
/* surface color of the pass only */
|
||||
shader_eval_surface(kg, sd, state, 0.0f, 0, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_surface(kg, sd, state, 0.0f, 0);
|
||||
return kernel_bake_shader_bsdf(kg, sd, type);
|
||||
}
|
||||
}
|
||||
else {
|
||||
shader_eval_surface(kg, sd, state, 0.0f, 0, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_surface(kg, sd, state, 0.0f, 0);
|
||||
color = kernel_bake_shader_bsdf(kg, sd, type);
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
|
||||
case SHADER_EVAL_NORMAL:
|
||||
{
|
||||
if((sd.flag & SD_HAS_BUMP)) {
|
||||
shader_eval_surface(kg, &sd, &state, 0.f, 0, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_surface(kg, &sd, &state, 0.f, 0);
|
||||
}
|
||||
|
||||
/* encoding: normal = (2 * color) - 1 */
|
||||
@@ -353,7 +353,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
|
||||
}
|
||||
case SHADER_EVAL_EMISSION:
|
||||
{
|
||||
shader_eval_surface(kg, &sd, &state, 0.f, 0, SHADER_CONTEXT_EMISSION);
|
||||
shader_eval_surface(kg, &sd, &state, 0.f, 0);
|
||||
out = shader_emissive_eval(kg, &sd);
|
||||
break;
|
||||
}
|
||||
@@ -473,7 +473,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
|
||||
|
||||
/* evaluate */
|
||||
int flag = 0; /* we can't know which type of BSDF this is for */
|
||||
out = shader_eval_background(kg, &sd, &state, flag, SHADER_CONTEXT_MAIN);
|
||||
out = shader_eval_background(kg, &sd, &state, flag);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -517,7 +517,7 @@ ccl_device void kernel_shader_evaluate(KernelGlobals *kg,
|
||||
|
||||
/* evaluate */
|
||||
float3 P = sd.P;
|
||||
shader_eval_displacement(kg, &sd, &state, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_displacement(kg, &sd, &state);
|
||||
out = sd.P - P;
|
||||
|
||||
object_inverse_dir_transform(kg, &sd, &out);
|
||||
@@ -545,7 +545,7 @@ ccl_device void kernel_shader_evaluate(KernelGlobals *kg,
|
||||
|
||||
/* evaluate */
|
||||
int flag = 0; /* we can't know which type of BSDF this is for */
|
||||
out = shader_eval_background(kg, &sd, &state, flag, SHADER_CONTEXT_MAIN);
|
||||
out = shader_eval_background(kg, &sd, &state, flag);
|
||||
}
|
||||
|
||||
/* write output */
|
||||
|
@@ -46,7 +46,7 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg,
|
||||
shader_setup_from_background(kg, emission_sd, &ray);
|
||||
|
||||
path_state_modify_bounce(state, true);
|
||||
eval = shader_eval_background(kg, emission_sd, state, 0, SHADER_CONTEXT_EMISSION);
|
||||
eval = shader_eval_background(kg, emission_sd, state, 0);
|
||||
path_state_modify_bounce(state, false);
|
||||
}
|
||||
else
|
||||
@@ -72,7 +72,7 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg,
|
||||
/* no path flag, we're evaluating this for all closures. that's weak but
|
||||
* we'd have to do multiple evaluations otherwise */
|
||||
path_state_modify_bounce(state, true);
|
||||
shader_eval_surface(kg, emission_sd, state, 0.0f, 0, SHADER_CONTEXT_EMISSION);
|
||||
shader_eval_surface(kg, emission_sd, state, 0.0f, 0);
|
||||
path_state_modify_bounce(state, false);
|
||||
|
||||
/* evaluate emissive closure */
|
||||
@@ -319,7 +319,7 @@ ccl_device_noinline float3 indirect_background(KernelGlobals *kg,
|
||||
# endif
|
||||
|
||||
path_state_modify_bounce(state, true);
|
||||
float3 L = shader_eval_background(kg, emission_sd, state, state->flag, SHADER_CONTEXT_EMISSION);
|
||||
float3 L = shader_eval_background(kg, emission_sd, state, state->flag);
|
||||
path_state_modify_bounce(state, false);
|
||||
|
||||
#ifdef __BACKGROUND_MIS__
|
||||
|
@@ -304,7 +304,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
|
||||
&isect,
|
||||
ray);
|
||||
float rbsdf = path_state_rng_1D_for_decision(kg, state, PRNG_BSDF);
|
||||
shader_eval_surface(kg, sd, state, rbsdf, state->flag, SHADER_CONTEXT_INDIRECT);
|
||||
shader_eval_surface(kg, sd, state, rbsdf, state->flag);
|
||||
#ifdef __BRANCHED_PATH__
|
||||
shader_merge_closures(sd);
|
||||
#endif /* __BRANCHED_PATH__ */
|
||||
@@ -627,7 +627,7 @@ ccl_device_inline void kernel_path_integrate(KernelGlobals *kg,
|
||||
/* setup shading */
|
||||
shader_setup_from_ray(kg, &sd, &isect, &ray);
|
||||
float rbsdf = path_state_rng_1D_for_decision(kg, &state, PRNG_BSDF);
|
||||
shader_eval_surface(kg, &sd, &state, rbsdf, state.flag, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_surface(kg, &sd, &state, rbsdf, state.flag);
|
||||
|
||||
#ifdef __SHADOW_TRICKS__
|
||||
if((sd.object_flag & SD_OBJECT_SHADOW_CATCHER)) {
|
||||
|
@@ -488,7 +488,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
|
||||
|
||||
/* setup shading */
|
||||
shader_setup_from_ray(kg, &sd, &isect, &ray);
|
||||
shader_eval_surface(kg, &sd, &state, 0.0f, state.flag, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_surface(kg, &sd, &state, 0.0f, state.flag);
|
||||
shader_merge_closures(&sd);
|
||||
|
||||
#ifdef __SHADOW_TRICKS__
|
||||
|
@@ -872,7 +872,7 @@ ccl_device float3 shader_holdout_eval(KernelGlobals *kg, ShaderData *sd)
|
||||
/* Surface Evaluation */
|
||||
|
||||
ccl_device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd,
|
||||
ccl_addr_space PathState *state, float randb, int path_flag, ShaderContext ctx)
|
||||
ccl_addr_space PathState *state, float randb, int path_flag)
|
||||
{
|
||||
sd->num_closure = 0;
|
||||
sd->num_closure_extra = 0;
|
||||
@@ -880,7 +880,7 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd,
|
||||
|
||||
#ifdef __OSL__
|
||||
if(kg->osl)
|
||||
OSLShader::eval_surface(kg, sd, state, path_flag, ctx);
|
||||
OSLShader::eval_surface(kg, sd, state, path_flag);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
@@ -903,7 +903,7 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd,
|
||||
/* Background Evaluation */
|
||||
|
||||
ccl_device float3 shader_eval_background(KernelGlobals *kg, ShaderData *sd,
|
||||
ccl_addr_space PathState *state, int path_flag, ShaderContext ctx)
|
||||
ccl_addr_space PathState *state, int path_flag)
|
||||
{
|
||||
sd->num_closure = 0;
|
||||
sd->num_closure_extra = 0;
|
||||
@@ -912,7 +912,7 @@ ccl_device float3 shader_eval_background(KernelGlobals *kg, ShaderData *sd,
|
||||
#ifdef __SVM__
|
||||
#ifdef __OSL__
|
||||
if(kg->osl) {
|
||||
OSLShader::eval_background(kg, sd, state, path_flag, ctx);
|
||||
OSLShader::eval_background(kg, sd, state, path_flag);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -1047,8 +1047,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg,
|
||||
ShaderData *sd,
|
||||
ccl_addr_space PathState *state,
|
||||
ccl_addr_space VolumeStack *stack,
|
||||
int path_flag,
|
||||
ShaderContext ctx)
|
||||
int path_flag)
|
||||
{
|
||||
/* reset closures once at the start, we will be accumulating the closures
|
||||
* for all volumes in the stack into a single array of closures */
|
||||
@@ -1081,7 +1080,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg,
|
||||
#ifdef __SVM__
|
||||
# ifdef __OSL__
|
||||
if(kg->osl) {
|
||||
OSLShader::eval_volume(kg, sd, state, path_flag, ctx);
|
||||
OSLShader::eval_volume(kg, sd, state, path_flag);
|
||||
}
|
||||
else
|
||||
# endif
|
||||
@@ -1100,7 +1099,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg,
|
||||
|
||||
/* Displacement Evaluation */
|
||||
|
||||
ccl_device void shader_eval_displacement(KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, ShaderContext ctx)
|
||||
ccl_device void shader_eval_displacement(KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state)
|
||||
{
|
||||
sd->num_closure = 0;
|
||||
sd->num_closure_extra = 0;
|
||||
@@ -1110,7 +1109,7 @@ ccl_device void shader_eval_displacement(KernelGlobals *kg, ShaderData *sd, ccl_
|
||||
#ifdef __SVM__
|
||||
# ifdef __OSL__
|
||||
if(kg->osl)
|
||||
OSLShader::eval_displacement(kg, sd, ctx);
|
||||
OSLShader::eval_displacement(kg, sd);
|
||||
else
|
||||
# endif
|
||||
{
|
||||
|
@@ -51,8 +51,7 @@ ccl_device_forceinline bool shadow_handle_transparent_isect(
|
||||
shadow_sd,
|
||||
state,
|
||||
0.0f,
|
||||
PATH_RAY_SHADOW,
|
||||
SHADER_CONTEXT_SHADOW);
|
||||
PATH_RAY_SHADOW);
|
||||
path_state_modify_bounce(state, false);
|
||||
*throughput *= shader_bsdf_transparency(kg, shadow_sd);
|
||||
}
|
||||
|
@@ -219,7 +219,7 @@ ccl_device void subsurface_color_bump_blur(KernelGlobals *kg,
|
||||
|
||||
if(bump || texture_blur > 0.0f) {
|
||||
/* average color and normal at incoming point */
|
||||
shader_eval_surface(kg, sd, state, 0.0f, state_flag, SHADER_CONTEXT_SSS);
|
||||
shader_eval_surface(kg, sd, state, 0.0f, state_flag);
|
||||
float3 in_color = shader_bssrdf_sum(sd, (bump)? N: NULL, NULL);
|
||||
|
||||
/* we simply divide out the average color and multiply with the average
|
||||
|
@@ -802,20 +802,6 @@ typedef ccl_addr_space struct ccl_align(16) ShaderClosure {
|
||||
float data[10]; /* pad to 80 bytes */
|
||||
} ShaderClosure;
|
||||
|
||||
/* Shader Context
|
||||
*
|
||||
* For OSL we recycle a fixed number of contexts for speed */
|
||||
|
||||
typedef enum ShaderContext {
|
||||
SHADER_CONTEXT_MAIN = 0,
|
||||
SHADER_CONTEXT_INDIRECT = 1,
|
||||
SHADER_CONTEXT_EMISSION = 2,
|
||||
SHADER_CONTEXT_SHADOW = 3,
|
||||
SHADER_CONTEXT_SSS = 4,
|
||||
SHADER_CONTEXT_VOLUME = 5,
|
||||
SHADER_CONTEXT_NUM = 6
|
||||
} ShaderContext;
|
||||
|
||||
/* Shader Data
|
||||
*
|
||||
* Main shader state at a point on the surface or in a volume. All coordinates
|
||||
|
@@ -43,7 +43,7 @@ ccl_device_inline bool volume_shader_extinction_sample(KernelGlobals *kg,
|
||||
float3 *extinction)
|
||||
{
|
||||
sd->P = P;
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW);
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW);
|
||||
|
||||
if(!(sd->flag & (SD_ABSORPTION|SD_SCATTER)))
|
||||
return false;
|
||||
@@ -69,7 +69,7 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals *kg,
|
||||
VolumeShaderCoefficients *coeff)
|
||||
{
|
||||
sd->P = P;
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, state->flag, SHADER_CONTEXT_VOLUME);
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, state->flag);
|
||||
|
||||
if(!(sd->flag & (SD_ABSORPTION|SD_SCATTER|SD_EMISSION)))
|
||||
return false;
|
||||
|
@@ -86,7 +86,7 @@ struct OSLThreadData {
|
||||
OSL::ShaderGlobals globals;
|
||||
OSL::PerThreadInfo *osl_thread_info;
|
||||
OSLTraceData tracedata;
|
||||
OSL::ShadingContext *context[SHADER_CONTEXT_NUM];
|
||||
OSL::ShadingContext *context;
|
||||
OIIO::TextureSystem::Perthread *oiio_thread_info;
|
||||
};
|
||||
|
||||
|
@@ -57,9 +57,7 @@ void OSLShader::thread_init(KernelGlobals *kg, KernelGlobals *kernel_globals, OS
|
||||
tdata->globals.tracedata = &tdata->tracedata;
|
||||
tdata->globals.flipHandedness = false;
|
||||
tdata->osl_thread_info = ss->create_thread_info();
|
||||
|
||||
for(int i = 0; i < SHADER_CONTEXT_NUM; i++)
|
||||
tdata->context[i] = ss->get_context(tdata->osl_thread_info);
|
||||
tdata->context = ss->get_context(tdata->osl_thread_info);
|
||||
|
||||
tdata->oiio_thread_info = osl_globals->ts->get_perthread_info();
|
||||
|
||||
@@ -74,9 +72,7 @@ void OSLShader::thread_free(KernelGlobals *kg)
|
||||
|
||||
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
||||
OSLThreadData *tdata = kg->osl_tdata;
|
||||
|
||||
for(int i = 0; i < SHADER_CONTEXT_NUM; i++)
|
||||
ss->release_context(tdata->context[i]);
|
||||
ss->release_context(tdata->context);
|
||||
|
||||
ss->destroy_thread_info(tdata->osl_thread_info);
|
||||
|
||||
@@ -173,7 +169,7 @@ static void flatten_surface_closure_tree(ShaderData *sd,
|
||||
}
|
||||
}
|
||||
|
||||
void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx)
|
||||
void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag)
|
||||
{
|
||||
/* setup shader globals from shader data */
|
||||
OSLThreadData *tdata = kg->osl_tdata;
|
||||
@@ -182,7 +178,7 @@ void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state
|
||||
/* execute shader for this point */
|
||||
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
||||
OSL::ShaderGlobals *globals = &tdata->globals;
|
||||
OSL::ShadingContext *octx = tdata->context[(int)ctx];
|
||||
OSL::ShadingContext *octx = tdata->context;
|
||||
int shader = sd->shader & SHADER_MASK;
|
||||
|
||||
/* automatic bump shader */
|
||||
@@ -274,7 +270,7 @@ static void flatten_background_closure_tree(ShaderData *sd,
|
||||
}
|
||||
}
|
||||
|
||||
void OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx)
|
||||
void OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag)
|
||||
{
|
||||
/* setup shader globals from shader data */
|
||||
OSLThreadData *tdata = kg->osl_tdata;
|
||||
@@ -283,7 +279,7 @@ void OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *st
|
||||
/* execute shader for this point */
|
||||
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
||||
OSL::ShaderGlobals *globals = &tdata->globals;
|
||||
OSL::ShadingContext *octx = tdata->context[(int)ctx];
|
||||
OSL::ShadingContext *octx = tdata->context;
|
||||
|
||||
if(kg->osl->background_state) {
|
||||
ss->execute(octx, *(kg->osl->background_state), *globals);
|
||||
@@ -329,7 +325,7 @@ static void flatten_volume_closure_tree(ShaderData *sd,
|
||||
}
|
||||
}
|
||||
|
||||
void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx)
|
||||
void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag)
|
||||
{
|
||||
/* setup shader globals from shader data */
|
||||
OSLThreadData *tdata = kg->osl_tdata;
|
||||
@@ -338,7 +334,7 @@ void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state,
|
||||
/* execute shader */
|
||||
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
||||
OSL::ShaderGlobals *globals = &tdata->globals;
|
||||
OSL::ShadingContext *octx = tdata->context[(int)ctx];
|
||||
OSL::ShadingContext *octx = tdata->context;
|
||||
int shader = sd->shader & SHADER_MASK;
|
||||
|
||||
if(kg->osl->volume_state[shader]) {
|
||||
@@ -352,7 +348,7 @@ void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state,
|
||||
|
||||
/* Displacement */
|
||||
|
||||
void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderContext ctx)
|
||||
void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd)
|
||||
{
|
||||
/* setup shader globals from shader data */
|
||||
OSLThreadData *tdata = kg->osl_tdata;
|
||||
@@ -364,7 +360,7 @@ void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderConte
|
||||
/* execute shader */
|
||||
OSL::ShadingSystem *ss = (OSL::ShadingSystem*)kg->osl_ss;
|
||||
OSL::ShaderGlobals *globals = &tdata->globals;
|
||||
OSL::ShadingContext *octx = tdata->context[(int)ctx];
|
||||
OSL::ShadingContext *octx = tdata->context;
|
||||
int shader = sd->shader & SHADER_MASK;
|
||||
|
||||
if(kg->osl->displacement_state[shader]) {
|
||||
|
@@ -53,10 +53,10 @@ public:
|
||||
static void thread_free(KernelGlobals *kg);
|
||||
|
||||
/* eval */
|
||||
static void eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx);
|
||||
static void eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx);
|
||||
static void eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx);
|
||||
static void eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderContext ctx);
|
||||
static void eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag);
|
||||
static void eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag);
|
||||
static void eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag);
|
||||
static void eval_displacement(KernelGlobals *kg, ShaderData *sd);
|
||||
|
||||
/* attributes */
|
||||
static int find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc);
|
||||
|
@@ -52,9 +52,8 @@ ccl_device void kernel_shader_eval(KernelGlobals *kg)
|
||||
|
||||
#ifndef __BRANCHED_PATH__
|
||||
float rbsdf = path_state_rng_1D_for_decision(kg, state, PRNG_BSDF);
|
||||
shader_eval_surface(kg, &kernel_split_state.sd[ray_index], state, rbsdf, state->flag, SHADER_CONTEXT_MAIN);
|
||||
shader_eval_surface(kg, &kernel_split_state.sd[ray_index], state, rbsdf, state->flag);
|
||||
#else
|
||||
ShaderContext ctx = SHADER_CONTEXT_MAIN;
|
||||
float rbsdf = 0.0f;
|
||||
|
||||
if(!kernel_data.integrator.branched || IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) {
|
||||
@@ -62,11 +61,7 @@ ccl_device void kernel_shader_eval(KernelGlobals *kg)
|
||||
|
||||
}
|
||||
|
||||
if(IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) {
|
||||
ctx = SHADER_CONTEXT_INDIRECT;
|
||||
}
|
||||
|
||||
shader_eval_surface(kg, &kernel_split_state.sd[ray_index], state, rbsdf, state->flag, ctx);
|
||||
shader_eval_surface(kg, &kernel_split_state.sd[ray_index], state, rbsdf, state->flag);
|
||||
shader_merge_closures(&kernel_split_state.sd[ray_index]);
|
||||
#endif /* __BRANCHED_PATH__ */
|
||||
}
|
||||
|
Reference in New Issue
Block a user