Changeset View
Changeset View
Standalone View
Standalone View
source/blender/draw/engines/workbench/workbench_engine.cc
- This file was added.
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |||||
| #include "BKE_editmesh.h" | |||||
| #include "BKE_modifier.h" | |||||
| #include "BKE_object.h" | |||||
| #include "BKE_paint.h" | |||||
| #include "BKE_particle.h" | |||||
| #include "BKE_pbvh.h" | |||||
| #include "BKE_report.h" | |||||
| #include "DEG_depsgraph_query.h" | |||||
| #include "DNA_fluid_types.h" | |||||
| #include "ED_paint.h" | |||||
| #include "ED_view3d.h" | |||||
| #include "GPU_capabilities.h" | |||||
| #include "workbench_private.hh" | |||||
| namespace blender::workbench { | |||||
| using namespace draw; | |||||
| class Instance { | |||||
| public: | |||||
| View view = {"DefaultView"}; | |||||
| SceneState scene_state; | |||||
| SceneResources resources; | |||||
| OpaquePass opaque_ps; | |||||
| TransparentPass transparent_ps; | |||||
| TransparentDepthPass transparent_depth_ps; | |||||
| ShadowPass shadow_ps; | |||||
| OutlinePass outline_ps; | |||||
| DofPass dof_ps; | |||||
| AntiAliasingPass anti_aliasing_ps; | |||||
| /* An array of nullptr GPUMaterial pointers so we can call DRW_cache_object_surface_material_get. | |||||
| * They never get actually used. */ | |||||
| Vector<GPUMaterial *> dummy_gpu_materials = {1, nullptr, {}}; | |||||
| GPUMaterial **get_dummy_gpu_materials(int material_count) | |||||
| { | |||||
| if (material_count > dummy_gpu_materials.size()) { | |||||
| dummy_gpu_materials.resize(material_count, nullptr); | |||||
| } | |||||
| return dummy_gpu_materials.begin(); | |||||
| }; | |||||
| void init(Object *camera_ob = nullptr) | |||||
| { | |||||
| scene_state.init(camera_ob); | |||||
| shadow_ps.init(scene_state, resources); | |||||
| resources.init(scene_state); | |||||
| outline_ps.init(scene_state); | |||||
| dof_ps.init(scene_state); | |||||
| anti_aliasing_ps.init(scene_state); | |||||
| } | |||||
| void begin_sync() | |||||
| { | |||||
| const float2 viewport_size = DRW_viewport_size_get(); | |||||
| const int2 resolution = {int(viewport_size.x), int(viewport_size.y)}; | |||||
| resources.depth_tx.ensure_2d(GPU_DEPTH24_STENCIL8, | |||||
| resolution, | |||||
| GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | | |||||
| GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW); | |||||
| opaque_ps.sync(scene_state, resources); | |||||
| transparent_ps.sync(scene_state, resources); | |||||
| transparent_depth_ps.sync(scene_state, resources); | |||||
| shadow_ps.sync(); | |||||
| outline_ps.sync(resources); | |||||
| dof_ps.sync(resources); | |||||
| anti_aliasing_ps.sync(resources, scene_state.resolution); | |||||
| } | |||||
| void end_sync() | |||||
| { | |||||
| resources.material_buf.push_update(); | |||||
| } | |||||
| void object_sync(Manager &manager, ObjectRef &ob_ref) | |||||
| { | |||||
| if (scene_state.render_finished) { | |||||
| return; | |||||
| } | |||||
| Object *ob = ob_ref.object; | |||||
| if (!DRW_object_is_renderable(ob)) { | |||||
| return; | |||||
| } | |||||
| const ObjectState object_state = ObjectState(scene_state, ob); | |||||
| /* Needed for mesh cache validation, to prevent two copies of | |||||
| * of vertex color arrays from being sent to the GPU (e.g. | |||||
| * when switching from eevee to workbench). | |||||
| */ | |||||
| if (ob_ref.object->sculpt && ob_ref.object->sculpt->pbvh) { | |||||
| BKE_pbvh_is_drawing_set(ob_ref.object->sculpt->pbvh, object_state.sculpt_pbvh); | |||||
| } | |||||
| if (ob->type == OB_MESH && ob->modifiers.first != nullptr) { | |||||
| LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { | |||||
| if (md->type != eModifierType_ParticleSystem) { | |||||
| continue; | |||||
| } | |||||
| ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; | |||||
| 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) { | |||||
| /* TODO(Miguel Pozo): | |||||
| workbench_cache_hair_populate( | |||||
| wpd, ob, psys, md, object_state.color_type, object_state.texture_paint_mode, | |||||
| part->omat); | |||||
| */ | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!(ob->base_flag & BASE_FROM_DUPLI)) { | |||||
| ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluid); | |||||
| if (md && BKE_modifier_is_enabled(scene_state.scene, md, eModifierMode_Realtime)) { | |||||
| FluidModifierData *fmd = (FluidModifierData *)md; | |||||
| if (fmd->domain) { | |||||
| /* TODO(Miguel Pozo): | |||||
| workbench_volume_cache_populate(vedata, wpd->scene, ob, md, V3D_SHADING_SINGLE_COLOR); | |||||
| */ | |||||
| if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { | |||||
| return; /* Do not draw solid in this case. */ | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { | |||||
| return; | |||||
| } | |||||
| if ((ob->dt < OB_SOLID) && !DRW_state_is_scene_render()) { | |||||
| return; | |||||
| } | |||||
| if (ELEM(ob->type, OB_MESH, OB_POINTCLOUD)) { | |||||
| mesh_sync(manager, ob_ref, object_state); | |||||
| } | |||||
| else if (ob->type == OB_CURVES) { | |||||
| /* TODO(Miguel Pozo): | |||||
| DRWShadingGroup *grp = workbench_material_hair_setup( | |||||
| wpd, ob, CURVES_MATERIAL_NR, object_state.color_type); | |||||
| DRW_shgroup_curves_create_sub(ob, grp, NULL); | |||||
| */ | |||||
| } | |||||
| else if (ob->type == OB_VOLUME) { | |||||
| if (scene_state.shading.type != OB_WIRE) { | |||||
| /* TODO(Miguel Pozo): | |||||
| workbench_volume_cache_populate(vedata, wpd->scene, ob, NULL, object_state.color_type); | |||||
| */ | |||||
| } | |||||
| } | |||||
| } | |||||
| void mesh_sync(Manager &manager, ObjectRef &ob_ref, const ObjectState &object_state) | |||||
| { | |||||
| ResourceHandle handle = manager.resource_handle(ob_ref); | |||||
| bool has_transparent_material = false; | |||||
| if (object_state.sculpt_pbvh) { | |||||
| /* TODO(Miguel Pozo): | |||||
| workbench_cache_sculpt_populate(wpd, ob, object_state.color_type); | |||||
| */ | |||||
| } | |||||
| else { | |||||
| if (object_state.use_per_material_batches) { | |||||
| const int material_count = DRW_cache_object_material_count_get(ob_ref.object); | |||||
| struct GPUBatch **batches; | |||||
| if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) { | |||||
| batches = DRW_cache_mesh_surface_texpaint_get(ob_ref.object); | |||||
| } | |||||
| else { | |||||
| batches = DRW_cache_object_surface_material_get( | |||||
| ob_ref.object, get_dummy_gpu_materials(material_count), material_count); | |||||
| } | |||||
| if (batches) { | |||||
| for (auto i : IndexRange(material_count)) { | |||||
| if (batches[i] == nullptr) { | |||||
| continue; | |||||
| } | |||||
| /* TODO(fclem): This create a cull-able instance for each sub-object. This is done | |||||
| * for simplicity to reduce complexity. But this increase the overhead per object. | |||||
| * Instead, we should use an indirection buffer to the material buffer. */ | |||||
| ResourceHandle _handle = i == 0 ? handle : manager.resource_handle(ob_ref); | |||||
| Material &mat = resources.material_buf.get_or_resize(_handle.resource_index()); | |||||
| if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, i + 1)) { | |||||
| mat = Material(*_mat); | |||||
| } | |||||
| else { | |||||
| mat = Material(*BKE_material_default_empty()); | |||||
| } | |||||
| has_transparent_material = has_transparent_material || mat.is_transparent(); | |||||
| ::Image *image = nullptr; | |||||
| ImageUser *iuser = nullptr; | |||||
| eGPUSamplerState sampler_state = eGPUSamplerState::GPU_SAMPLER_DEFAULT; | |||||
| if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) { | |||||
| get_material_image(ob_ref.object, i + 1, image, iuser, sampler_state); | |||||
| } | |||||
| draw_mesh(ob_ref, mat, batches[i], _handle, image, sampler_state, iuser); | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| struct GPUBatch *batch; | |||||
| if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) { | |||||
| batch = DRW_cache_mesh_surface_texpaint_single_get(ob_ref.object); | |||||
| } | |||||
| else if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) { | |||||
| if (ob_ref.object->mode & OB_MODE_VERTEX_PAINT) { | |||||
| batch = DRW_cache_mesh_surface_vertpaint_get(ob_ref.object); | |||||
| } | |||||
| else { | |||||
| batch = DRW_cache_mesh_surface_sculptcolors_get(ob_ref.object); | |||||
| } | |||||
| } | |||||
| else { | |||||
| batch = DRW_cache_object_surface_get(ob_ref.object); | |||||
| } | |||||
| if (batch) { | |||||
| Material &mat = resources.material_buf.get_or_resize(handle.resource_index()); | |||||
| if (object_state.color_type == V3D_SHADING_OBJECT_COLOR) { | |||||
| mat = Material(*ob_ref.object); | |||||
| } | |||||
| else if (object_state.color_type == V3D_SHADING_RANDOM_COLOR) { | |||||
| mat = Material(*ob_ref.object, true); | |||||
| } | |||||
| else if (object_state.color_type == V3D_SHADING_SINGLE_COLOR) { | |||||
| mat = scene_state.material_override; | |||||
| } | |||||
| else if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) { | |||||
| mat = scene_state.material_attribute_color; | |||||
| } | |||||
| else { | |||||
| mat = Material(*BKE_material_default_empty()); | |||||
| } | |||||
| has_transparent_material = has_transparent_material || mat.is_transparent(); | |||||
| draw_mesh(ob_ref, | |||||
| mat, | |||||
| batch, | |||||
| handle, | |||||
| object_state.image_paint_override, | |||||
| object_state.override_sampler_state); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (object_state.draw_shadow) { | |||||
| shadow_ps.object_sync(manager, scene_state, ob_ref, handle, has_transparent_material); | |||||
| } | |||||
| } | |||||
| void draw_mesh(ObjectRef &ob_ref, | |||||
| Material &material, | |||||
| GPUBatch *batch, | |||||
| ResourceHandle handle, | |||||
| ::Image *image = nullptr, | |||||
| eGPUSamplerState sampler_state = GPU_SAMPLER_DEFAULT, | |||||
| ImageUser *iuser = nullptr) | |||||
| { | |||||
| const bool in_front = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0; | |||||
| auto draw = [&](MeshPass &pass) { | |||||
| pass.draw(ob_ref, batch, handle, image, sampler_state, iuser); | |||||
| }; | |||||
| if (scene_state.xray_mode || material.is_transparent()) { | |||||
| if (in_front) { | |||||
| draw(transparent_ps.accumulation_in_front_ps_); | |||||
| if (scene_state.draw_transparent_depth) { | |||||
| draw(transparent_depth_ps.in_front_ps_); | |||||
| } | |||||
| } | |||||
| else { | |||||
| draw(transparent_ps.accumulation_ps_); | |||||
| if (scene_state.draw_transparent_depth) { | |||||
| draw(transparent_depth_ps.main_ps_); | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| if (in_front) { | |||||
| draw(opaque_ps.gbuffer_in_front_ps_); | |||||
| } | |||||
| else { | |||||
| draw(opaque_ps.gbuffer_ps_); | |||||
| } | |||||
| } | |||||
| } | |||||
| void draw(Manager &manager, GPUTexture *depth_tx, GPUTexture *color_tx) | |||||
| { | |||||
| view.sync(DRW_view_default_get()); | |||||
| int2 resolution = scene_state.resolution; | |||||
| if (scene_state.render_finished) { | |||||
fclem: This is commented but not needed. It isn't clear why you changed `depth_tx` into a `draw… | |||||
Done Inline ActionsI just removed it. pragma37: I just removed it.
I had to convert it to a Texture to use stencil_view. | |||||
| /* Just copy back the already rendered result */ | |||||
| anti_aliasing_ps.draw(manager, view, resources, resolution, depth_tx, color_tx); | |||||
| return; | |||||
| } | |||||
| anti_aliasing_ps.setup_view(view, resolution); | |||||
| resources.color_tx.acquire( | |||||
| resolution, GPU_RGBA16F, GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT); | |||||
| resources.color_tx.clear(resources.world_buf.background_color); | |||||
| if (scene_state.draw_object_id) { | |||||
| resources.object_id_tx.acquire( | |||||
| resolution, GPU_R16UI, GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT); | |||||
| resources.object_id_tx.clear(uint4(0)); | |||||
| } | |||||
| Framebuffer fb = Framebuffer("Workbench.Clear"); | |||||
| fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx)); | |||||
| fb.bind(); | |||||
| GPU_framebuffer_clear_depth_stencil(fb, 1.0f, 0x00); | |||||
| if (!transparent_ps.accumulation_in_front_ps_.is_empty()) { | |||||
| resources.depth_in_front_tx.acquire(resolution, | |||||
| GPU_DEPTH24_STENCIL8, | |||||
| GPU_TEXTURE_USAGE_SHADER_READ | | |||||
| GPU_TEXTURE_USAGE_ATTACHMENT); | |||||
| if (opaque_ps.gbuffer_in_front_ps_.is_empty()) { | |||||
| /* Clear only if it wont be overwitten by opaque_ps */ | |||||
| Framebuffer fb = Framebuffer("Workbench.Clear"); | |||||
| fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx)); | |||||
| fb.bind(); | |||||
| GPU_framebuffer_clear_depth_stencil(fb, 1.0f, 0x00); | |||||
| } | |||||
| } | |||||
| opaque_ps.draw(manager, | |||||
| view, | |||||
| resources, | |||||
| resolution, | |||||
| &shadow_ps, | |||||
| transparent_ps.accumulation_ps_.is_empty()); | |||||
| transparent_ps.draw(manager, view, resources, resolution); | |||||
| transparent_depth_ps.draw(manager, view, resources, resolution); | |||||
| // volume_ps.draw_prepass(manager, view, resources.depth_tx); | |||||
| outline_ps.draw(manager, view, resources, resolution); | |||||
| dof_ps.draw(manager, view, resources, resolution); | |||||
| anti_aliasing_ps.draw(manager, view, resources, resolution, depth_tx, color_tx); | |||||
| resources.color_tx.release(); | |||||
| resources.object_id_tx.release(); | |||||
| resources.depth_in_front_tx.release(); | |||||
Done Inline ActionsDon't use a static. Move it to a member of Instance. Also this function should also be part of the Instance class. fclem: Don't use a static. Move it to a member of `Instance`. Also this function should also be part… | |||||
Done Inline ActionsOk, I had just put it aside since it's basically irrelevant noise. pragma37: Ok, I had just put it aside since it's basically irrelevant noise.
Are in-function static… | |||||
| } | |||||
| void draw_viewport(Manager &manager, GPUTexture *depth_tx, GPUTexture *color_tx) | |||||
| { | |||||
| this->draw(manager, depth_tx, color_tx); | |||||
| if (scene_state.sample + 1 < scene_state.samples_len) { | |||||
| DRW_viewport_request_redraw(); | |||||
| } | |||||
| } | |||||
| }; | |||||
| } // namespace blender::workbench | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Interface with legacy C DRW manager | |||||
| * \{ */ | |||||
| using namespace blender; | |||||
| struct WORKBENCH_Data { | |||||
| DrawEngineType *engine_type; | |||||
| DRWViewportEmptyList *fbl; | |||||
| DRWViewportEmptyList *txl; | |||||
| DRWViewportEmptyList *psl; | |||||
| DRWViewportEmptyList *stl; | |||||
| workbench::Instance *instance; | |||||
| char info[GPU_INFO_SIZE]; | |||||
| }; | |||||
| static void workbench_engine_init(void *vedata) | |||||
| { | |||||
| /* TODO(fclem): Remove once it is minimum required. */ | |||||
| if (!GPU_shader_storage_buffer_objects_support()) { | |||||
| return; | |||||
| } | |||||
| WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata); | |||||
| if (ved->instance == nullptr) { | |||||
| ved->instance = new workbench::Instance(); | |||||
| } | |||||
| ved->instance->init(); | |||||
| } | |||||
| static void workbench_cache_init(void *vedata) | |||||
| { | |||||
| if (!GPU_shader_storage_buffer_objects_support()) { | |||||
| return; | |||||
| } | |||||
| reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->begin_sync(); | |||||
| } | |||||
| static void workbench_cache_populate(void *vedata, Object *object) | |||||
| { | |||||
| if (!GPU_shader_storage_buffer_objects_support()) { | |||||
| return; | |||||
| } | |||||
| draw::Manager *manager = DRW_manager_get(); | |||||
| draw::ObjectRef ref; | |||||
| ref.object = object; | |||||
| ref.dupli_object = DRW_object_get_dupli(object); | |||||
| ref.dupli_parent = DRW_object_get_dupli_parent(object); | |||||
| reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->object_sync(*manager, ref); | |||||
| } | |||||
| static void workbench_cache_finish(void *vedata) | |||||
| { | |||||
| if (!GPU_shader_storage_buffer_objects_support()) { | |||||
| return; | |||||
| } | |||||
| reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->end_sync(); | |||||
| } | |||||
| static void workbench_draw_scene(void *vedata) | |||||
| { | |||||
| WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata); | |||||
| if (!GPU_shader_storage_buffer_objects_support()) { | |||||
| STRNCPY(ved->info, "Error: No shader storage buffer support"); | |||||
| return; | |||||
| } | |||||
| DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); | |||||
Not Done Inline Actionsved->info Needs to be cleared in the normal case. fclem: `ved->info` Needs to be cleared in the normal case. | |||||
| draw::Manager *manager = DRW_manager_get(); | |||||
| ved->instance->draw_viewport(*manager, dtxl->depth, dtxl->color); | |||||
| } | |||||
| static void workbench_instance_free(void *instance) | |||||
| { | |||||
| if (!GPU_shader_storage_buffer_objects_support()) { | |||||
| return; | |||||
| } | |||||
| delete reinterpret_cast<workbench::Instance *>(instance); | |||||
| } | |||||
| static void workbench_view_update(void *vedata) | |||||
| { | |||||
| WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata); | |||||
| if (ved->instance) { | |||||
| ved->instance->scene_state.reset_taa_next_sample = true; | |||||
| } | |||||
| } | |||||
| static void workbench_id_update(void *vedata, struct ID *id) | |||||
| { | |||||
| UNUSED_VARS(vedata, id); | |||||
| } | |||||
| /* RENDER */ | |||||
| static bool workbench_render_framebuffers_init(void) | |||||
| { | |||||
| /* For image render, allocate own buffers because we don't have a viewport. */ | |||||
| const float2 viewport_size = DRW_viewport_size_get(); | |||||
| const int2 size = {int(viewport_size.x), int(viewport_size.y)}; | |||||
| DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); | |||||
| /* When doing a multi view rendering the first view will allocate the buffers | |||||
| * the other views will reuse these buffers */ | |||||
| if (dtxl->color == nullptr) { | |||||
| BLI_assert(dtxl->depth == nullptr); | |||||
| dtxl->color = GPU_texture_create_2d("txl.color", size.x, size.y, 1, GPU_RGBA16F, nullptr); | |||||
| dtxl->depth = GPU_texture_create_2d( | |||||
| "txl.depth", size.x, size.y, 1, GPU_DEPTH24_STENCIL8, nullptr); | |||||
| } | |||||
| if (!(dtxl->depth && dtxl->color)) { | |||||
| return false; | |||||
| } | |||||
| DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); | |||||
| GPU_framebuffer_ensure_config( | |||||
| &dfbl->default_fb, | |||||
| {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); | |||||
| GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, | |||||
| {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_NONE}); | |||||
| GPU_framebuffer_ensure_config(&dfbl->color_only_fb, | |||||
| {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(dtxl->color)}); | |||||
| return GPU_framebuffer_check_valid(dfbl->default_fb, nullptr) && | |||||
| GPU_framebuffer_check_valid(dfbl->color_only_fb, nullptr) && | |||||
| GPU_framebuffer_check_valid(dfbl->depth_only_fb, nullptr); | |||||
| } | |||||
| #ifdef DEBUG | |||||
| /* This is just to ease GPU debugging when the frame delimiter is set to Finish */ | |||||
| # define GPU_FINISH_DELIMITER() GPU_finish() | |||||
| #else | |||||
| # define GPU_FINISH_DELIMITER() | |||||
| #endif | |||||
| static void write_render_color_output(struct RenderLayer *layer, | |||||
| const char *viewname, | |||||
| GPUFrameBuffer *fb, | |||||
| const struct rcti *rect) | |||||
| { | |||||
| RenderPass *rp = RE_pass_find_by_name(layer, RE_PASSNAME_COMBINED, viewname); | |||||
| if (rp) { | |||||
| GPU_framebuffer_bind(fb); | |||||
| GPU_framebuffer_read_color(fb, | |||||
| rect->xmin, | |||||
| rect->ymin, | |||||
| BLI_rcti_size_x(rect), | |||||
| BLI_rcti_size_y(rect), | |||||
| 4, | |||||
| 0, | |||||
| GPU_DATA_FLOAT, | |||||
| rp->rect); | |||||
| } | |||||
| } | |||||
| static void write_render_z_output(struct RenderLayer *layer, | |||||
| const char *viewname, | |||||
| GPUFrameBuffer *fb, | |||||
| const struct rcti *rect, | |||||
| float4x4 winmat) | |||||
| { | |||||
| RenderPass *rp = RE_pass_find_by_name(layer, RE_PASSNAME_Z, viewname); | |||||
| if (rp) { | |||||
| GPU_framebuffer_bind(fb); | |||||
| GPU_framebuffer_read_depth(fb, | |||||
| rect->xmin, | |||||
| rect->ymin, | |||||
| BLI_rcti_size_x(rect), | |||||
| BLI_rcti_size_y(rect), | |||||
| GPU_DATA_FLOAT, | |||||
| rp->rect); | |||||
| int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); | |||||
| /* Convert ogl depth [0..1] to view Z [near..far] */ | |||||
| if (DRW_view_is_persp_get(nullptr)) { | |||||
| for (float &z : MutableSpan(rp->rect, pix_num)) { | |||||
| if (z == 1.0f) { | |||||
| z = 1e10f; /* Background */ | |||||
| } | |||||
| else { | |||||
| z = z * 2.0f - 1.0f; | |||||
| z = winmat[3][2] / (z + winmat[2][2]); | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* Keep in mind, near and far distance are negatives. */ | |||||
| float near = DRW_view_near_distance_get(nullptr); | |||||
| float far = DRW_view_far_distance_get(nullptr); | |||||
| float range = fabsf(far - near); | |||||
| for (float &z : MutableSpan(rp->rect, pix_num)) { | |||||
| if (z == 1.0f) { | |||||
| z = 1e10f; /* Background */ | |||||
| } | |||||
| else { | |||||
| z = z * range - near; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static void workbench_render_to_image(void *vedata, | |||||
| struct RenderEngine *engine, | |||||
| struct RenderLayer *layer, | |||||
| const struct rcti *rect) | |||||
| { | |||||
Done Inline ActionsSplit to write functions. fclem: Split to write functions. | |||||
| /* TODO(fclem): Remove once it is minimum required. */ | |||||
| if (!GPU_shader_storage_buffer_objects_support()) { | |||||
| return; | |||||
| } | |||||
| if (!workbench_render_framebuffers_init()) { | |||||
| RE_engine_report(engine, RPT_ERROR, "Failed to allocate GPU buffers"); | |||||
| return; | |||||
| } | |||||
| GPU_FINISH_DELIMITER(); | |||||
| /* Setup */ | |||||
| DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| Depsgraph *depsgraph = draw_ctx->depsgraph; | |||||
| WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata); | |||||
| if (ved->instance == nullptr) { | |||||
| ved->instance = new workbench::Instance(); | |||||
| } | |||||
| /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */ | |||||
| Object *camera_ob = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); | |||||
| /* Set the perspective, view and window matrix. */ | |||||
| float4x4 winmat, viewmat, viewinv; | |||||
| RE_GetCameraWindow(engine->re, camera_ob, winmat.ptr()); | |||||
| RE_GetCameraModelMatrix(engine->re, camera_ob, viewinv.ptr()); | |||||
| viewmat = viewinv.inverted(); | |||||
| DRWView *view = DRW_view_create(viewmat.ptr(), winmat.ptr(), nullptr, nullptr, nullptr); | |||||
| DRW_view_default_set(view); | |||||
| DRW_view_set_active(view); | |||||
| /* Render */ | |||||
| do { | |||||
| if (RE_engine_test_break(engine)) { | |||||
| break; | |||||
| } | |||||
| ved->instance->init(camera_ob); | |||||
| DRW_manager_get()->begin_sync(); | |||||
| workbench_cache_init(vedata); | |||||
| auto workbench_render_cache = [](void *vedata, | |||||
| struct Object *ob, | |||||
| struct RenderEngine * /*engine*/, | |||||
| struct Depsgraph * /*depsgraph*/) { | |||||
| workbench_cache_populate(vedata, ob); | |||||
| }; | |||||
| DRW_render_object_iter(vedata, engine, depsgraph, workbench_render_cache); | |||||
| workbench_cache_finish(vedata); | |||||
| DRW_manager_get()->end_sync(); | |||||
| /* Also we weed to have a correct FBO bound for #DRW_curves_update */ | |||||
| // GPU_framebuffer_bind(dfbl->default_fb); | |||||
| // DRW_curves_update(); /* TODO(Miguel Pozo): Check this once curves are implemented */ | |||||
| workbench_draw_scene(vedata); | |||||
| /* Perform render step between samples to allow | |||||
| * flushing of freed GPUBackend resources. */ | |||||
| GPU_render_step(); | |||||
| GPU_FINISH_DELIMITER(); | |||||
| } while (ved->instance->scene_state.sample + 1 < ved->instance->scene_state.samples_len); | |||||
| const char *viewname = RE_GetActiveRenderView(engine->re); | |||||
| write_render_color_output(layer, viewname, dfbl->default_fb, rect); | |||||
| write_render_z_output(layer, viewname, dfbl->default_fb, rect, winmat); | |||||
| } | |||||
| static void workbench_render_update_passes(RenderEngine *engine, | |||||
| Scene *scene, | |||||
| ViewLayer *view_layer) | |||||
| { | |||||
| if (view_layer->passflag & SCE_PASS_COMBINED) { | |||||
| RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA); | |||||
| } | |||||
| if (view_layer->passflag & SCE_PASS_Z) { | |||||
| RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_Z, 1, "Z", SOCK_FLOAT); | |||||
| } | |||||
| } | |||||
| extern "C" { | |||||
| static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data); | |||||
| DrawEngineType draw_engine_workbench_next = { | |||||
| nullptr, | |||||
| nullptr, | |||||
| N_("Workbench"), | |||||
| &workbench_data_size, | |||||
| &workbench_engine_init, | |||||
| nullptr, | |||||
| &workbench_instance_free, | |||||
| &workbench_cache_init, | |||||
| &workbench_cache_populate, | |||||
| &workbench_cache_finish, | |||||
| &workbench_draw_scene, | |||||
| &workbench_view_update, | |||||
| &workbench_id_update, | |||||
| &workbench_render_to_image, | |||||
| nullptr, | |||||
| }; | |||||
| RenderEngineType DRW_engine_viewport_workbench_next_type = { | |||||
| nullptr, | |||||
| nullptr, | |||||
| "BLENDER_WORKBENCH_NEXT", | |||||
| N_("Workbench Next"), | |||||
| RE_INTERNAL | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT, | |||||
| nullptr, | |||||
| &DRW_render_to_image, | |||||
| nullptr, | |||||
| nullptr, | |||||
| nullptr, | |||||
| nullptr, | |||||
| nullptr, | |||||
| nullptr, | |||||
| &workbench_render_update_passes, | |||||
| &draw_engine_workbench_next, | |||||
| {nullptr, nullptr, nullptr}, | |||||
| }; | |||||
| } | |||||
| /** \} */ | |||||
This is commented but not needed. It isn't clear why you changed depth_tx into a draw::Textiure. Maybe a comment on top of its declaration would make sense.