forked from bartvdbraak/blender
Cycles: Stop lamp sampling if the lamp isn't visible
Both spot and area light have large areas where they're not visible. Therefore, this patch stops the light sampling code when one of these cases (outside of the spotlight cone or behind the area light) occurs, before the lamp shader is evaluated. In the case of the area light, the solid angle sampling can also be skipped. In a test scene with Sample All Lights and 18 Area lamps and 9 Spot lamps that all point away from the area that the camera sees, render time drops from 12sec to 5sec. Reviewers: brecht, sergey, dingto, juicyfruit Differential Revision: https://developer.blender.org/D2216
This commit is contained in:
parent
aae2cea28d
commit
b459d9f46c
@ -510,7 +510,7 @@ ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3
|
|||||||
return t*t/cos_pi;
|
return t*t/cos_pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
|
ccl_device_inline bool lamp_light_sample(KernelGlobals *kg,
|
||||||
int lamp,
|
int lamp,
|
||||||
float randu, float randv,
|
float randu, float randv,
|
||||||
float3 P,
|
float3 P,
|
||||||
@ -581,6 +581,9 @@ ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
|
|||||||
/* spot light attenuation */
|
/* spot light attenuation */
|
||||||
float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2);
|
float4 data2 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 2);
|
||||||
ls->eval_fac *= spot_light_attenuation(data1, data2, ls);
|
ls->eval_fac *= spot_light_attenuation(data1, data2, ls);
|
||||||
|
if(ls->eval_fac == 0.0f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
|
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
|
||||||
}
|
}
|
||||||
@ -593,6 +596,10 @@ ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
|
|||||||
float3 axisv = make_float3(data2.y, data2.z, data2.w);
|
float3 axisv = make_float3(data2.y, data2.z, data2.w);
|
||||||
float3 D = make_float3(data3.y, data3.z, data3.w);
|
float3 D = make_float3(data3.y, data3.z, data3.w);
|
||||||
|
|
||||||
|
if(dot(ls->P - P, D) > 0.0f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ls->pdf = area_light_sample(P, &ls->P,
|
ls->pdf = area_light_sample(P, &ls->P,
|
||||||
axisu, axisv,
|
axisu, axisv,
|
||||||
randu, randv,
|
randu, randv,
|
||||||
@ -603,13 +610,12 @@ ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
|
|||||||
|
|
||||||
float invarea = data2.x;
|
float invarea = data2.x;
|
||||||
ls->eval_fac = 0.25f*invarea;
|
ls->eval_fac = 0.25f*invarea;
|
||||||
|
|
||||||
if(dot(ls->D, D) > 0.0f)
|
|
||||||
ls->pdf = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ls->eval_fac *= kernel_data.integrator.inv_pdf_lights;
|
ls->eval_fac *= kernel_data.integrator.inv_pdf_lights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (ls->pdf > 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls)
|
ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls)
|
||||||
@ -836,7 +842,7 @@ ccl_device bool light_select_reached_max_bounces(KernelGlobals *kg, int index, i
|
|||||||
return (bounce > __float_as_int(data4.x));
|
return (bounce > __float_as_int(data4.x));
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device_noinline void light_sample(KernelGlobals *kg,
|
ccl_device_noinline bool light_sample(KernelGlobals *kg,
|
||||||
float randt,
|
float randt,
|
||||||
float randu,
|
float randu,
|
||||||
float randv,
|
float randv,
|
||||||
@ -857,21 +863,20 @@ ccl_device_noinline void light_sample(KernelGlobals *kg,
|
|||||||
int shader_flag = __float_as_int(l.z);
|
int shader_flag = __float_as_int(l.z);
|
||||||
|
|
||||||
triangle_light_sample(kg, prim, object, randu, randv, time, ls);
|
triangle_light_sample(kg, prim, object, randu, randv, time, ls);
|
||||||
|
|
||||||
/* compute incoming direction, distance and pdf */
|
/* compute incoming direction, distance and pdf */
|
||||||
ls->D = normalize_len(ls->P - P, &ls->t);
|
ls->D = normalize_len(ls->P - P, &ls->t);
|
||||||
ls->pdf = triangle_light_pdf(kg, ls->Ng, -ls->D, ls->t);
|
ls->pdf = triangle_light_pdf(kg, ls->Ng, -ls->D, ls->t);
|
||||||
ls->shader |= shader_flag;
|
ls->shader |= shader_flag;
|
||||||
|
return (ls->pdf > 0.0f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int lamp = -prim-1;
|
int lamp = -prim-1;
|
||||||
|
|
||||||
if(UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
|
if(UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
|
||||||
ls->pdf = 0.0f;
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lamp_light_sample(kg, lamp, randu, randv, P, ls);
|
return lamp_light_sample(kg, lamp, randu, randv, P, ls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,15 +54,15 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
|
|||||||
path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
lamp_light_sample(kg, i, light_u, light_v, ccl_fetch(sd, P), &ls);
|
if(lamp_light_sample(kg, i, light_u, light_v, ccl_fetch(sd, P), &ls)) {
|
||||||
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
/* trace shadow ray */
|
/* accumulate */
|
||||||
float3 shadow;
|
path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
||||||
|
}
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,15 +86,15 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
|
|||||||
light_t = 0.5f*light_t;
|
light_t = 0.5f*light_t;
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls);
|
if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
|
||||||
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
/* trace shadow ray */
|
/* accumulate */
|
||||||
float3 shadow;
|
path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
||||||
|
}
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,16 +107,16 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
|
|||||||
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
|
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls);
|
if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
|
||||||
|
/* sample random light */
|
||||||
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
/* sample random light */
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
/* accumulate */
|
||||||
/* trace shadow ray */
|
path_radiance_accum_light(L, throughput*num_samples_adjust, &L_light, shadow, num_samples_adjust, state->bounce, is_lamp);
|
||||||
float3 shadow;
|
}
|
||||||
|
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, throughput*num_samples_adjust, &L_light, shadow, num_samples_adjust, state->bounce, is_lamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,15 +206,15 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, ccl_
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls);
|
if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
|
||||||
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
/* trace shadow ray */
|
/* accumulate */
|
||||||
float3 shadow;
|
path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
|
||||||
|
}
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -46,17 +46,16 @@ ccl_device_inline void kernel_path_volume_connect_light(
|
|||||||
light_ray.time = sd->time;
|
light_ray.time = sd->time;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls);
|
if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls))
|
||||||
if(ls.pdf == 0.0f)
|
{
|
||||||
return;
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
float3 shadow;
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
/* accumulate */
|
/* accumulate */
|
||||||
path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
|
path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -146,7 +145,7 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
|
|||||||
path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
lamp_light_sample(kg, i, light_u, light_v, ray->P, &ls);
|
lamp_light_sample(kg, i, light_u, light_v, ray->P, &ls);
|
||||||
|
|
||||||
float3 tp = throughput;
|
float3 tp = throughput;
|
||||||
|
|
||||||
@ -156,23 +155,20 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
|
|||||||
|
|
||||||
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
|
VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
|
||||||
state, ray, sd, &tp, rphase, rscatter, segment, (ls.t != FLT_MAX)? &ls.P: NULL, false);
|
state, ray, sd, &tp, rphase, rscatter, segment, (ls.t != FLT_MAX)? &ls.P: NULL, false);
|
||||||
|
|
||||||
(void)result;
|
(void)result;
|
||||||
kernel_assert(result == VOLUME_PATH_SCATTERED);
|
kernel_assert(result == VOLUME_PATH_SCATTERED);
|
||||||
|
|
||||||
/* todo: split up light_sample so we don't have to call it again with new position */
|
/* todo: split up light_sample so we don't have to call it again with new position */
|
||||||
lamp_light_sample(kg, i, light_u, light_v, sd->P, &ls);
|
if(lamp_light_sample(kg, i, light_u, light_v, sd->P, &ls)) {
|
||||||
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
if(ls.pdf == 0.0f)
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
continue;
|
/* accumulate */
|
||||||
|
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
}
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,18 +208,15 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
|
|||||||
kernel_assert(result == VOLUME_PATH_SCATTERED);
|
kernel_assert(result == VOLUME_PATH_SCATTERED);
|
||||||
|
|
||||||
/* todo: split up light_sample so we don't have to call it again with new position */
|
/* todo: split up light_sample so we don't have to call it again with new position */
|
||||||
light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls);
|
if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
||||||
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
if(ls.pdf == 0.0f)
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
continue;
|
/* accumulate */
|
||||||
|
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
}
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,19 +244,16 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
|
|||||||
kernel_assert(result == VOLUME_PATH_SCATTERED);
|
kernel_assert(result == VOLUME_PATH_SCATTERED);
|
||||||
|
|
||||||
/* todo: split up light_sample so we don't have to call it again with new position */
|
/* todo: split up light_sample so we don't have to call it again with new position */
|
||||||
light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls);
|
if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
|
||||||
|
/* sample random light */
|
||||||
|
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
|
/* trace shadow ray */
|
||||||
|
float3 shadow;
|
||||||
|
|
||||||
if(ls.pdf == 0.0f)
|
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
||||||
return;
|
/* accumulate */
|
||||||
|
path_radiance_accum_light(L, tp, &L_light, shadow, 1.0f, state->bounce, is_lamp);
|
||||||
/* sample random light */
|
}
|
||||||
if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
|
||||||
/* trace shadow ray */
|
|
||||||
float3 shadow;
|
|
||||||
|
|
||||||
if(!shadow_blocked(kg, emission_sd, state, &light_ray, &shadow)) {
|
|
||||||
/* accumulate */
|
|
||||||
path_radiance_accum_light(L, tp, &L_light, shadow, 1.0f, state->bounce, is_lamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,30 +74,31 @@ ccl_device char kernel_direct_lighting(
|
|||||||
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
|
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
|
||||||
|
|
||||||
LightSample ls;
|
LightSample ls;
|
||||||
light_sample(kg,
|
if(light_sample(kg,
|
||||||
light_t, light_u, light_v,
|
light_t, light_u, light_v,
|
||||||
ccl_fetch(sd, time),
|
ccl_fetch(sd, time),
|
||||||
ccl_fetch(sd, P),
|
ccl_fetch(sd, P),
|
||||||
state->bounce,
|
state->bounce,
|
||||||
&ls);
|
&ls)) {
|
||||||
|
|
||||||
Ray light_ray;
|
Ray light_ray;
|
||||||
#ifdef __OBJECT_MOTION__
|
#ifdef __OBJECT_MOTION__
|
||||||
light_ray.time = ccl_fetch(sd, time);
|
light_ray.time = ccl_fetch(sd, time);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BsdfEval L_light;
|
BsdfEval L_light;
|
||||||
bool is_lamp;
|
bool is_lamp;
|
||||||
if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp)) {
|
||||||
/* Write intermediate data to global memory to access from
|
/* Write intermediate data to global memory to access from
|
||||||
* the next kernel.
|
* the next kernel.
|
||||||
*/
|
*/
|
||||||
LightRay_coop[ray_index] = light_ray;
|
LightRay_coop[ray_index] = light_ray;
|
||||||
BSDFEval_coop[ray_index] = L_light;
|
BSDFEval_coop[ray_index] = L_light;
|
||||||
ISLamp_coop[ray_index] = is_lamp;
|
ISLamp_coop[ray_index] = is_lamp;
|
||||||
/* Mark ray state for next shadow kernel. */
|
/* Mark ray state for next shadow kernel. */
|
||||||
ADD_RAY_FLAG(ray_state, ray_index, RAY_SHADOW_RAY_CAST_DL);
|
ADD_RAY_FLAG(ray_state, ray_index, RAY_SHADOW_RAY_CAST_DL);
|
||||||
enqueue_flag = 1;
|
enqueue_flag = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* __EMISSION__ */
|
#endif /* __EMISSION__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user