/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2021 Blender Foundation. */ /** \file * \ingroup eevee * * An instance contains all structures needed to do a complete render. */ #include #include "BKE_global.h" #include "BKE_object.h" #include "BLI_rect.h" #include "DEG_depsgraph_query.h" #include "DNA_ID.h" #include "DNA_lightprobe_types.h" #include "DNA_modifier_types.h" #include "RE_pipeline.h" #include "eevee_instance.hh" namespace blender::eevee { /* -------------------------------------------------------------------- */ /** \name Initialization * * Initialization functions need to be called once at the start of a frame. * Active camera, render extent and enabled render passes are immutable until next init. * This takes care of resizing output buffers and view in case a parameter changed. * IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources. * Any attempt to do so will likely produce use after free situations. * \{ */ void Instance::init(const int2 &output_res, const rcti *output_rect, RenderEngine *render_, Depsgraph *depsgraph_, const LightProbe *light_probe_, Object *camera_object_, const RenderLayer *render_layer_, const DRWView *drw_view_, const View3D *v3d_, const RegionView3D *rv3d_) { UNUSED_VARS(light_probe_); render = render_; depsgraph = depsgraph_; camera_orig_object = camera_object_; render_layer = render_layer_; drw_view = drw_view_; v3d = v3d_; rv3d = rv3d_; info = ""; update_eval_members(); sampling.init(scene); camera.init(); film.init(output_res, output_rect); main_view.init(); } void Instance::set_time(float time) { BLI_assert(render); DRW_render_set_time(render, depsgraph, floorf(time), fractf(time)); update_eval_members(); } void Instance::update_eval_members() { scene = DEG_get_evaluated_scene(depsgraph); view_layer = DEG_get_evaluated_view_layer(depsgraph); camera_eval_object = (camera_orig_object) ? DEG_get_evaluated_object(depsgraph, camera_orig_object) : nullptr; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Sync * * Sync will gather data from the scene that can change over a time step (i.e: motion steps). * IMPORTANT: xxx.sync() functions area responsible for creating DRW resources (i.e: DRWView) as * well as querying temp texture pool. All DRWPasses should be ready by the end end_sync(). * \{ */ void Instance::begin_sync() { materials.begin_sync(); velocity.begin_sync(); gpencil_engine_enabled = false; render_buffers.sync(); pipelines.sync(); main_view.sync(); world.sync(); camera.sync(); film.sync(); } void Instance::object_sync(Object *ob) { const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL, OB_MESH); const int ob_visibility = DRW_object_visibility_in_active_context(ob); const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 && (ob->type == OB_MESH); const bool object_is_visible = DRW_object_is_renderable(ob) && (ob_visibility & OB_VISIBLE_SELF) != 0; if (!is_renderable_type || (!partsys_is_visible && !object_is_visible)) { return; } ObjectHandle &ob_handle = sync.sync_object(ob); if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) { LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == eModifierType_ParticleSystem) { sync.sync_curves(ob, ob_handle, md); } } } if (object_is_visible) { switch (ob->type) { case OB_LAMP: break; case OB_MESH: case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: case OB_MBALL: { sync.sync_mesh(ob, ob_handle); break; } case OB_VOLUME: break; case OB_CURVES: sync.sync_curves(ob, ob_handle); break; case OB_GPENCIL: sync.sync_gpencil(ob, ob_handle); break; default: break; } } ob_handle.reset_recalc_flag(); } /* Wrapper to use with DRW_render_object_iter. */ void Instance::object_sync_render(void *instance_, Object *ob, RenderEngine *engine, Depsgraph *depsgraph) { UNUSED_VARS(engine, depsgraph); Instance &inst = *reinterpret_cast(instance_); inst.object_sync(ob); } void Instance::end_sync() { velocity.end_sync(); sampling.end_sync(); film.end_sync(); } void Instance::render_sync() { DRW_cache_restart(); begin_sync(); DRW_render_object_iter(this, render, depsgraph, object_sync_render); end_sync(); DRW_render_instance_buffer_finish(); /* Also we weed to have a correct FBO bound for #DRW_hair_update */ // GPU_framebuffer_bind(); // DRW_hair_update(); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Rendering * \{ */ /** * Conceptually renders one sample per pixel. * Everything based on random sampling should be done here (i.e: DRWViews jitter) **/ void Instance::render_sample() { if (sampling.finished_viewport()) { film.display(); return; } /* Motion blur may need to do re-sync after a certain number of sample. */ if (!is_viewport() && sampling.do_render_sync()) { render_sync(); } sampling.step(); main_view.render(); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Interface * \{ */ void Instance::render_frame(RenderLayer *render_layer, const char *view_name) { while (!sampling.finished()) { this->render_sample(); /* TODO(fclem) print progression. */ } /* Read Results. */ eViewLayerEEVEEPassType pass_bits = film.enabled_passes_get(); for (auto i : IndexRange(EEVEE_RENDER_PASS_MAX_BIT)) { eViewLayerEEVEEPassType pass_type = eViewLayerEEVEEPassType(pass_bits & (1 << i)); if (pass_type == 0) { continue; } const char *pass_name = Film::pass_to_render_pass_name(pass_type); RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name); if (rp) { float *result = film.read_pass(pass_type); if (result) { std::cout << "read " << pass_name << std::endl; BLI_mutex_lock(&render->update_render_passes_mutex); /* WORKAROUND: We use texture read to avoid using a framebuffer to get the render result. * However, on some implementation, we need a buffer with a few extra bytes for the read to * happen correctly (see GLTexture::read()). So we need a custom memory allocation. */ /* Avoid memcpy(), replace the pointer directly. */ MEM_SAFE_FREE(rp->rect); rp->rect = result; BLI_mutex_unlock(&render->update_render_passes_mutex); } } } } void Instance::draw_viewport(DefaultFramebufferList *dfbl) { UNUSED_VARS(dfbl); render_sample(); velocity.step_swap(); if (!sampling.finished_viewport()) { DRW_viewport_request_redraw(); } if (materials.queued_shaders_count > 0) { std::stringstream ss; ss << "Compiling Shaders " << materials.queued_shaders_count; info = ss.str(); } } /** \} */ } // namespace blender::eevee