static void basic_cache_init(void *vedata) { BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl; BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl; const DRWContextState *draw_ctx = DRW_context_state_get(); BASIC_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; if (!stl->g_data) { /* Alloc transient pointers */ stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); } { psl->depth_pass = DRW_pass_create("Depth Pass", DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL); stl->g_data->depth_shgrp = DRW_shgroup_create(sh_data->depth, psl->depth_pass); if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { DRW_shgroup_state_enable(stl->g_data->depth_shgrp, DRW_STATE_CLIP_PLANES); } psl->depth_pass_cull = DRW_pass_create("Depth Pass Cull", DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK); stl->g_data->depth_shgrp_cull = DRW_shgroup_create(sh_data->depth, psl->depth_pass_cull); if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { DRW_shgroup_state_enable(stl->g_data->depth_shgrp_cull, DRW_STATE_CLIP_PLANES); } } }
/* Here init all passes and shading groups * Assume that all Passes are NULL */ static void EDIT_TEXT_cache_init(void *vedata) { const DRWContextState *draw_ctx = DRW_context_state_get(); EDIT_TEXT_PassList *psl = ((EDIT_TEXT_Data *)vedata)->psl; EDIT_TEXT_StorageList *stl = ((EDIT_TEXT_Data *)vedata)->stl; if (!stl->g_data) { /* Alloc transient pointers */ stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); } { /* Text outline (fast drawing!) */ psl->wire_pass = DRW_pass_create( "Font Wire", DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL); stl->g_data->wire_shgrp = DRW_shgroup_create(e_data.wire_sh, psl->wire_pass); psl->overlay_select_pass = DRW_pass_create("Font Select", DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH); stl->g_data->overlay_select_shgrp = DRW_shgroup_create(e_data.overlay_select_sh, psl->overlay_select_pass); psl->overlay_cursor_pass = DRW_pass_create("Font Cursor", DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH); stl->g_data->overlay_cursor_shgrp = DRW_shgroup_create(e_data.overlay_cursor_sh, psl->overlay_cursor_pass); psl->text_box_pass = DRW_pass_create("Font Text Boxes", DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH); stl->g_data->box_shgrp = buffer_dynlines_dashed_uniform_color( psl->text_box_pass, G_draw.block.colorWire, draw_ctx->sh_cfg); stl->g_data->box_active_shgrp = buffer_dynlines_dashed_uniform_color( psl->text_box_pass, G_draw.block.colorActive, draw_ctx->sh_cfg); } }
/* Add geometry to shadingGroups. Execute for each objects */ static void EDIT_TEXT_cache_populate(void *vedata, Object *ob) { EDIT_TEXT_PassList *psl = ((EDIT_TEXT_Data *)vedata)->psl; EDIT_TEXT_StorageList *stl = ((EDIT_TEXT_Data *)vedata)->stl; const DRWContextState *draw_ctx = DRW_context_state_get(); UNUSED_VARS(psl, stl); if (ob->type == OB_FONT) { if (ob == draw_ctx->object_edit) { const Curve *cu = ob->data; /* Get geometry cache */ struct GPUBatch *geom; bool has_surface = (cu->flag & (CU_FRONT | CU_BACK)) || cu->ext1 != 0.0f || cu->ext2 != 0.0f; if ((cu->flag & CU_FAST) || !has_surface) { geom = DRW_cache_text_edge_wire_get(ob); if (geom) { DRW_shgroup_call(stl->g_data->wire_shgrp, geom, ob); } } else { /* object mode draws */ } edit_text_cache_populate_select(vedata, ob); edit_text_cache_populate_cursor(vedata, ob); edit_text_cache_populate_boxes(vedata, ob); } } }
int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; const DRWContextState *draw_ctx = DRW_context_state_get(); const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) { const float *viewport_size = DRW_viewport_size_get(); const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; /* Shaders */ if (!e_data.gtao_sh) { eevee_create_shader_occlusion(); } common_data->ao_dist = scene_eval->eevee.gtao_distance; common_data->ao_factor = scene_eval->eevee.gtao_factor; common_data->ao_quality = 1.0f - scene_eval->eevee.gtao_quality; common_data->ao_settings = 1.0f; /* USE_AO */ if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BENT_NORMALS) { common_data->ao_settings += 2.0f; /* USE_BENT_NORMAL */ } if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) { common_data->ao_settings += 4.0f; /* USE_DENOISE */ } common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f; effects->gtao_horizons = DRW_texture_pool_query_2d( fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type); GPU_framebuffer_ensure_config( &fbl->gtao_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons)}); if (G.debug_value == 6) { effects->gtao_horizons_debug = DRW_texture_pool_query_2d( fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type); GPU_framebuffer_ensure_config( &fbl->gtao_debug_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_debug)}); } else { effects->gtao_horizons_debug = NULL; } return EFFECT_GTAO | EFFECT_NORMAL_BUFFER; } /* Cleanup */ effects->gtao_horizons = NULL; GPU_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb); common_data->ao_settings = 0.0f; return 0; }
void DRW_draw_region_info(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); ARegion *ar = draw_ctx->ar; DRW_draw_cursor(); view3d_draw_region_info(draw_ctx->evil_C, ar); }
static void basic_engine_init(void *UNUSED(vedata)) { const DRWContextState *draw_ctx = DRW_context_state_get(); BASIC_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; /* Depth prepass */ if (!sh_data->depth) { sh_data->depth = DRW_shader_create_3d_depth_only(draw_ctx->sh_cfg); } }
static void basic_cache_populate(void *vedata, Object *ob) { BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl; /* TODO(fclem) fix selection of smoke domains. */ if (!DRW_object_is_renderable(ob) || (ob->dt < OB_SOLID)) { return; } const DRWContextState *draw_ctx = DRW_context_state_get(); if (ob != draw_ctx->object_edit) { for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) { if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { continue; } ParticleSettings *part = psys->part; const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; if (draw_as == PART_DRAW_PATH) { struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL); DRW_shgroup_call(stl->g_data->depth_shgrp, hairs, NULL); } } } /* Make flat object selectable in ortho view if wireframe is enabled. */ if ((draw_ctx->v3d->overlay.flag & V3D_OVERLAY_WIREFRAMES) || (draw_ctx->v3d->shading.type == OB_WIRE) || (ob->dtx & OB_DRAWWIRE) || (ob->dt == OB_WIRE)) { int flat_axis = 0; bool is_flat_object_viewed_from_side = ((draw_ctx->rv3d->persp == RV3D_ORTHO) && DRW_object_is_flat(ob, &flat_axis) && DRW_object_axis_orthogonal_to_view(ob, flat_axis)); if (is_flat_object_viewed_from_side) { /* Avoid losing flat objects when in ortho views (see T56549) */ struct GPUBatch *geom = DRW_cache_object_all_edges_get(ob); if (geom) { DRW_shgroup_call(stl->g_data->depth_shgrp, geom, ob); } return; } } struct GPUBatch *geom = DRW_cache_object_surface_get(ob); if (geom) { const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)); /* Depth Prepass */ DRW_shgroup_call( (do_cull) ? stl->g_data->depth_shgrp_cull : stl->g_data->depth_shgrp, geom, ob); } }
void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_TextureList *txl = vedata->txl; EEVEE_StorageList *stl = vedata->stl; EEVEE_PassList *psl = vedata->psl; EEVEE_EffectsInfo *effects = stl->effects; const DRWContextState *draw_ctx = DRW_context_state_get(); const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) { DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; DRW_texture_ensure_fullscreen_2d( &txl->ao_accum, GPU_R32F, 0); /* Should be enough precision for many samples. */ GPU_framebuffer_ensure_config(&fbl->ao_accum_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)}); /* Clear texture. */ GPU_framebuffer_bind(fbl->ao_accum_fb); GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear); /* Accumulation pass */ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE; psl->ao_accum_ps = DRW_pass_create("AO Accum", state); DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_accum_ps); DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } else { /* Cleanup to release memory */ DRW_TEXTURE_FREE_SAFE(txl->ao_accum); GPU_FRAMEBUFFER_FREE_SAFE(fbl->ao_accum_fb); } }
/* verify if exist a non instanced version of the object */ static bool gpencil_has_noninstanced_object(Object *ob_instance) { const DRWContextState *draw_ctx = DRW_context_state_get(); const ViewLayer *view_layer = draw_ctx->view_layer; Object *ob = NULL; for (Base *base = view_layer->object_bases.first; base; base = base->next) { ob = base->object; if (ob->type != OB_GPENCIL) { continue; } /* object must be visible (invisible objects don't create VBO data) */ if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { continue; } /* is not duplicated and the name is equals */ if ((ob->base_flag & BASE_FROM_DUPLI) == 0) { if (STREQ(ob->id.name, ob_instance->id.name)) { return true; } } } return false; }
void DRW_draw_cursor(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); ARegion *ar = draw_ctx->ar; Scene *scene = draw_ctx->scene; ViewLayer *view_layer = draw_ctx->view_layer; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_FALSE); glDisable(GL_DEPTH_TEST); if (is_cursor_visible(draw_ctx, scene, view_layer)) { int co[2]; /* Get cursor data into quaternion form */ const View3DCursor *cursor = &scene->cursor; if (ED_view3d_project_int_global( ar, cursor->location, co, V3D_PROJ_TEST_NOP | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) { RegionView3D *rv3d = ar->regiondata; float cursor_quat[4]; BKE_scene_cursor_rot_to_quat(cursor, cursor_quat); /* Draw nice Anti Aliased cursor. */ GPU_line_width(1.0f); GPU_blend(true); GPU_line_smooth(true); float eps = 1e-5f; rv3d->viewquat[0] = -rv3d->viewquat[0]; bool is_aligned = compare_v4v4(cursor_quat, rv3d->viewquat, eps); if (is_aligned == false) { float tquat[4]; rotation_between_quats_to_quat(tquat, rv3d->viewquat, cursor_quat); is_aligned = tquat[0] - eps < -1.0f; } rv3d->viewquat[0] = -rv3d->viewquat[0]; /* Draw lines */ if (is_aligned == false) { uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformThemeColor3(TH_VIEW_OVERLAY); immBegin(GPU_PRIM_LINES, 12); const float scale = ED_view3d_pixel_size_no_ui_scale(rv3d, cursor->location) * U.widget_unit; #define CURSOR_VERT(axis_vec, axis, fac) \ immVertex3f(pos, \ cursor->location[0] + axis_vec[0] * (fac), \ cursor->location[1] + axis_vec[1] * (fac), \ cursor->location[2] + axis_vec[2] * (fac)) #define CURSOR_EDGE(axis_vec, axis, sign) \ { \ CURSOR_VERT(axis_vec, axis, sign 1.0f); \ CURSOR_VERT(axis_vec, axis, sign 0.25f); \ } \ ((void)0) for (int axis = 0; axis < 3; axis++) { float axis_vec[3] = {0}; axis_vec[axis] = scale; mul_qt_v3(cursor_quat, axis_vec); CURSOR_EDGE(axis_vec, axis, +); CURSOR_EDGE(axis_vec, axis, -); } #undef CURSOR_VERT #undef CURSOR_EDGE immEnd(); immUnbindProgram(); } float original_proj[4][4]; GPU_matrix_projection_get(original_proj); GPU_matrix_push(); ED_region_pixelspace(ar); GPU_matrix_translate_2f(co[0] + 0.5f, co[1] + 0.5f); GPU_matrix_scale_2f(U.widget_unit, U.widget_unit); GPUBatch *cursor_batch = DRW_cache_cursor_get(is_aligned); GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR); GPU_batch_program_set( cursor_batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader)); GPU_batch_draw(cursor_batch); GPU_blend(false); GPU_line_smooth(false); GPU_matrix_pop(); GPU_matrix_projection_set(original_proj); }
/* add a gpencil object to cache to defer drawing */ tGPencilObjectCache *gpencil_object_cache_add(tGPencilObjectCache *cache_array, Object *ob, int *gp_cache_size, int *gp_cache_used) { const DRWContextState *draw_ctx = DRW_context_state_get(); tGPencilObjectCache *cache_elem = NULL; RegionView3D *rv3d = draw_ctx->rv3d; View3D *v3d = draw_ctx->v3d; tGPencilObjectCache *p = NULL; /* By default a cache is created with one block with a predefined number of free slots, * if the size is not enough, the cache is reallocated adding a new block of free slots. * This is done in order to keep cache small. */ if (*gp_cache_used + 1 > *gp_cache_size) { if ((*gp_cache_size == 0) || (cache_array == NULL)) { p = MEM_callocN(sizeof(struct tGPencilObjectCache) * GP_CACHE_BLOCK_SIZE, "tGPencilObjectCache"); *gp_cache_size = GP_CACHE_BLOCK_SIZE; } else { *gp_cache_size += GP_CACHE_BLOCK_SIZE; p = MEM_recallocN(cache_array, sizeof(struct tGPencilObjectCache) * *gp_cache_size); } cache_array = p; } /* zero out all pointers */ cache_elem = &cache_array[*gp_cache_used]; memset(cache_elem, 0, sizeof(*cache_elem)); cache_elem->ob = ob; cache_elem->gpd = (bGPdata *)ob->data; cache_elem->name = BKE_id_to_unique_string_key(&ob->id); copy_v3_v3(cache_elem->loc, ob->obmat[3]); copy_m4_m4(cache_elem->obmat, ob->obmat); cache_elem->idx = *gp_cache_used; /* object is duplicated (particle) */ if (ob->base_flag & BASE_FROM_DUPLI) { /* Check if the original object is not in the viewlayer * and cannot be managed as dupli. This is slower, but required to keep * the particle drawing FPS and display instanced objects in scene * without the original object */ bool has_original = gpencil_has_noninstanced_object(ob); cache_elem->is_dup_ob = (has_original) ? ob->base_flag & BASE_FROM_DUPLI : false; } else { cache_elem->is_dup_ob = false; } cache_elem->scale = mat4_to_scale(ob->obmat); /* save FXs */ cache_elem->pixfactor = cache_elem->gpd->pixfactor; cache_elem->shader_fx = ob->shader_fx; /* save wire mode (object mode is always primary option) */ if (ob->dt == OB_WIRE) { cache_elem->shading_type[0] = (int)OB_WIRE; } else { if (v3d) { cache_elem->shading_type[0] = (int)v3d->shading.type; } } /* shgrp array */ cache_elem->tot_layers = 0; int totgpl = BLI_listbase_count(&cache_elem->gpd->layers); if (totgpl > 0) { cache_elem->shgrp_array = MEM_callocN(sizeof(tGPencilObjectCache_shgrp) * totgpl, __func__); } /* calculate zdepth from point of view */ float zdepth = 0.0; if (rv3d) { if (rv3d->is_persp) { zdepth = ED_view3d_calc_zfac(rv3d, ob->obmat[3], NULL); } else { zdepth = -dot_v3v3(rv3d->viewinv[2], ob->obmat[3]); } } else { /* In render mode, rv3d is not available, so use the distance to camera. * The real distance is not important, but the relative distance to the camera plane * in order to sort by z_depth of the objects */ float vn[3] = {0.0f, 0.0f, -1.0f}; /* always face down */ float plane_cam[4]; struct Object *camera = draw_ctx->scene->camera; if (camera) { mul_m4_v3(camera->obmat, vn); normalize_v3(vn); plane_from_point_normal_v3(plane_cam, camera->loc, vn); zdepth = dist_squared_to_plane_v3(ob->obmat[3], plane_cam); } } cache_elem->zdepth = zdepth; /* increase slots used in cache */ (*gp_cache_used)++; return cache_array; }
int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; EEVEE_StorageList *stl = vedata->stl; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_TextureList *txl = vedata->txl; EEVEE_EffectsInfo *effects = stl->effects; const float *viewport_size = DRW_viewport_size_get(); const DRWContextState *draw_ctx = DRW_context_state_get(); const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); /* Compute pixel size, (shared with contact shadows) */ copy_v2_v2(common_data->ssr_pixelsize, viewport_size); invert_v2(common_data->ssr_pixelsize); if (scene_eval->eevee.flag & SCE_EEVEE_SSR_ENABLED) { const bool use_refraction = (scene_eval->eevee.flag & SCE_EEVEE_SSR_REFRACTION) != 0; if (use_refraction) { /* TODO: Opti: Could be shared. */ DRW_texture_ensure_fullscreen_2d( &txl->refract_color, GPU_R11F_G11F_B10F, DRW_TEX_FILTER | DRW_TEX_MIPMAP); GPU_framebuffer_ensure_config( &fbl->refract_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->refract_color)}); } const bool is_persp = DRW_view_is_persp_get(NULL); if (effects->ssr_was_persp != is_persp) { effects->ssr_was_persp = is_persp; DRW_viewport_request_redraw(); EEVEE_temporal_sampling_reset(vedata); stl->g_data->valid_double_buffer = false; } effects->reflection_trace_full = (scene_eval->eevee.flag & SCE_EEVEE_SSR_HALF_RESOLUTION) == 0; common_data->ssr_thickness = scene_eval->eevee.ssr_thickness; common_data->ssr_border_fac = scene_eval->eevee.ssr_border_fade; common_data->ssr_firefly_fac = scene_eval->eevee.ssr_firefly_fac; common_data->ssr_max_roughness = scene_eval->eevee.ssr_max_roughness; common_data->ssr_quality = 1.0f - 0.95f * scene_eval->eevee.ssr_quality; common_data->ssr_brdf_bias = 0.1f + common_data->ssr_quality * 0.6f; /* Range [0.1, 0.7]. */ if (common_data->ssr_firefly_fac < 1e-8f) { common_data->ssr_firefly_fac = FLT_MAX; } const int divisor = (effects->reflection_trace_full) ? 1 : 2; int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor}; int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]}; const bool high_qual_input = true; /* TODO dither low quality input */ const eGPUTextureFormat format = (high_qual_input) ? GPU_RGBA16F : GPU_RGBA8; /* MRT for the shading pass in order to output needed data for the SSR pass. */ effects->ssr_specrough_input = DRW_texture_pool_query_2d( size_fs[0], size_fs[1], format, &draw_engine_eevee_type); GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_specrough_input, 2, 0); /* Raytracing output */ effects->ssr_hit_output = DRW_texture_pool_query_2d( tracing_res[0], tracing_res[1], GPU_RG16I, &draw_engine_eevee_type); effects->ssr_pdf_output = DRW_texture_pool_query_2d( tracing_res[0], tracing_res[1], GPU_R16F, &draw_engine_eevee_type); GPU_framebuffer_ensure_config(&fbl->screen_tracing_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output), GPU_ATTACHMENT_TEXTURE(effects->ssr_pdf_output)}); /* Enable double buffering to be able to read previous frame color */ return EFFECT_SSR | EFFECT_NORMAL_BUFFER | EFFECT_DOUBLE_BUFFER | ((use_refraction) ? EFFECT_REFRACT : 0); } /* Cleanup to release memory */ GPU_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb); effects->ssr_specrough_input = NULL; effects->ssr_hit_output = NULL; effects->ssr_pdf_output = NULL; return 0; }