Refactor: DRW: Make DrawEngine a virtual class

This removes the old `DrawEngineType` and use the new `DrawEngine`
virtual class instead.

This removes a lot of boilerplate functions that were only there for
legacy reason.

To this end, some engines that were based on static functions have been
refactored into `Instance` classes. This was particularly cumbersome
for the Grease pencil engine which needed some more refactoring.

The `Engine` class that is in each namespace is a workaround to isolate
the internal implementation (i.e. the `Instance`) to the engine
modules. Without this, the whole engine is getting included in each
compile unit that includes the `Instance` class. Eventually, if we get
rid of these intricate dependencies, we could remove the `Engine` class.

Pull Request: https://projects.blender.org/blender/blender/pulls/136001
This commit is contained in:
Clément Foucault
2025-03-17 10:31:22 +01:00
committed by Clément Foucault
parent 070a4cc9ee
commit 92968c23fe
40 changed files with 1601 additions and 2305 deletions

View File

@@ -140,7 +140,6 @@ set(SRC
engines/gpencil/gpencil_draw_data.cc
engines/gpencil/gpencil_engine_c.cc
engines/gpencil/gpencil_render.cc
engines/gpencil/gpencil_shader_c.cc
engines/gpencil/gpencil_shader_fx.cc
engines/select/select_engine.cc
engines/select/select_instance.cc
@@ -220,7 +219,9 @@ set(SRC
engines/eevee_next/eevee_volume.hh
engines/eevee_next/eevee_world.hh
engines/external/external_engine.h
engines/gpencil/gpencil_engine.h
engines/gpencil/gpencil_engine_private.hh
engines/gpencil/gpencil_engine.hh
engines/gpencil/gpencil_shader.hh
engines/image/image_batches.hh
engines/image/image_buffer_cache.hh
engines/image/image_drawing_mode.hh
@@ -281,7 +282,6 @@ set(SRC
engines/select/select_defines.hh
engines/select/select_engine.hh
engines/select/select_instance.hh
engines/select/select_private.hh
engines/workbench/workbench_defines.hh
engines/workbench/workbench_engine.h
engines/workbench/workbench_enums.hh

View File

@@ -13,7 +13,6 @@ struct DRWData;
struct DRWInstanceDataList;
struct Depsgraph;
struct DrawDataList;
struct DrawEngineType;
struct GPUMaterial;
struct GPUOffScreen;
struct GPUVertFormat;
@@ -109,10 +108,8 @@ bool DRW_draw_in_progress();
*/
bool DRW_render_check_grease_pencil(Depsgraph *depsgraph);
/**
* Render grease pencil on top of other render engine output (but only for non-draw-engine).
* Render grease pencil on top of other render engine output.
* This function creates a DRWContext.
* `DRW_render_to_image()` applies grease pencil using `DRW_render_gpencil_to_image` as it
* already has a DRWContext setup.
*/
void DRW_render_gpencil(RenderEngine *engine, Depsgraph *depsgraph);

View File

@@ -196,90 +196,63 @@ class Context : public compositor::Context {
}
};
class Engine {
class Instance : public DrawEngine {
private:
Context context_;
public:
Engine(char *info_message) : context_(info_message) {}
Instance() : context_(this->info) {}
void draw()
StringRefNull name_get() final
{
compositor::Evaluator evaluator(context_);
evaluator.evaluate();
return "Compositor";
}
void init() final{};
void begin_sync() final{};
void object_sync(blender::draw::ObjectRef & /*ob_ref*/,
blender::draw::Manager & /*manager*/) final{};
void end_sync() final{};
void draw(Manager & /*manager*/) final
{
DRW_submission_start();
#if defined(__APPLE__)
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
/* NOTE(Metal): Isolate Compositor compute work in individual command buffer to improve
* workload scheduling. When expensive compositor nodes are in the graph, these can stall out
* the GPU for extended periods of time and sub-optimally schedule work for execution. */
GPU_flush();
}
#endif
/* Execute Compositor render commands. */
{
compositor::Evaluator evaluator(context_);
evaluator.evaluate();
}
#if defined(__APPLE__)
/* NOTE(Metal): Following previous flush to break command stream, with compositor command
* buffers potentially being heavy, we avoid issuing subsequent commands until compositor work
* has completed. If subsequent work is prematurely queued up, the subsequent command buffers
* will be blocked behind compositor work and may trigger a command buffer time-out error. As a
* result, we should wait for compositor work to complete.
*
* This is not an efficient approach for peak performance, but a catch-all to prevent command
* buffer failure, until the offending cases can be resolved. */
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
GPU_finish();
}
#endif
DRW_submission_end();
}
};
DrawEngine *Engine::create_instance()
{
return new Instance();
}
} // namespace blender::draw::compositor_engine
using namespace blender::draw::compositor_engine;
struct COMPOSITOR_Data {
DrawEngineType *engine_type;
Engine *instance_data;
char info[GPU_INFO_SIZE];
};
static void compositor_engine_init(void *data)
{
COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
if (!compositor_data->instance_data) {
compositor_data->instance_data = new Engine(compositor_data->info);
}
}
static void compositor_engine_free(void *instance_data)
{
Engine *engine = static_cast<Engine *>(instance_data);
delete engine;
}
static void compositor_engine_draw(void *data)
{
COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
DRW_submission_start();
#if defined(__APPLE__)
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
/* NOTE(Metal): Isolate Compositor compute work in individual command buffer to improve
* workload scheduling. When expensive compositor nodes are in the graph, these can stall out
* the GPU for extended periods of time and sub-optimally schedule work for execution. */
GPU_flush();
}
#endif
/* Execute Compositor render commands. */
compositor_data->instance_data->draw();
#if defined(__APPLE__)
/* NOTE(Metal): Following previous flush to break command stream, with compositor command
* buffers potentially being heavy, we avoid issuing subsequent commands until compositor work
* has completed. If subsequent work is prematurely queued up, the subsequent command buffers
* will be blocked behind compositor work and may trigger a command buffer time-out error. As a
* result, we should wait for compositor work to complete.
*
* This is not an efficient approach for peak performance, but a catch-all to prevent command
* buffer failure, until the offending cases can be resolved. */
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
GPU_finish();
}
#endif
DRW_submission_end();
}
DrawEngineType draw_engine_compositor_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Compositor"),
/*engine_init*/ &compositor_engine_init,
/*engine_free*/ nullptr,
/*instance_free*/ &compositor_engine_free,
/*cache_init*/ nullptr,
/*cache_populate*/ nullptr,
/*cache_finish*/ nullptr,
/*draw_scene*/ &compositor_engine_draw,
/*render_to_image*/ nullptr,
/*store_metadata*/ nullptr,
};

View File

@@ -4,6 +4,12 @@
#pragma once
struct DrawEngineType;
#include "DRW_render.hh"
extern DrawEngineType draw_engine_compositor_type;
namespace blender::draw::compositor_engine {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
};
} // namespace blender::draw::compositor_engine

View File

@@ -19,177 +19,60 @@
#include "eevee_instance.hh"
using namespace blender;
namespace blender::eevee {
struct EEVEE_Data {
DrawEngineType *engine_type;
eevee::Instance *instance;
char info[GPU_INFO_SIZE];
};
static void eevee_engine_init(void *vedata)
DrawEngine *Engine::create_instance()
{
EEVEE_Data *ved = reinterpret_cast<EEVEE_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new eevee::Instance();
}
const DRWContext *ctx_state = DRW_context_get();
Depsgraph *depsgraph = ctx_state->depsgraph;
Scene *scene = ctx_state->scene;
View3D *v3d = ctx_state->v3d;
ARegion *region = ctx_state->region;
RegionView3D *rv3d = ctx_state->rv3d;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
int2 size = int2(GPU_texture_width(dtxl->color), GPU_texture_height(dtxl->color));
draw::View &default_view = draw::View::default_get();
Object *camera = nullptr;
/* Get render borders. */
rcti rect;
BLI_rcti_init(&rect, 0, size[0], 0, size[1]);
rcti visible_rect = rect;
if (v3d) {
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
camera = v3d->camera;
}
if (camera) {
rctf default_border;
BLI_rctf_init(&default_border, 0.0f, 1.0f, 0.0f, 1.0f);
bool is_default_border = BLI_rctf_compare(&scene->r.border, &default_border, 0.0f);
bool use_border = scene->r.mode & R_BORDER;
if (!is_default_border && use_border) {
rctf viewborder;
/* TODO(fclem) Might be better to get it from DRW. */
ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, false, &viewborder);
float viewborder_sizex = BLI_rctf_size_x(&viewborder);
float viewborder_sizey = BLI_rctf_size_y(&viewborder);
rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex));
rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey));
rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex));
rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey));
}
}
else if (v3d->flag2 & V3D_RENDER_BORDER) {
rect.xmin = v3d->render_border.xmin * size[0];
rect.ymin = v3d->render_border.ymin * size[1];
rect.xmax = v3d->render_border.xmax * size[0];
rect.ymax = v3d->render_border.ymax * size[1];
}
if (DRW_state_is_viewport_image_render()) {
const float2 vp_size = DRW_viewport_size_get();
visible_rect.xmax = vp_size[0];
visible_rect.ymax = vp_size[1];
visible_rect.xmin = visible_rect.ymin = 0;
}
else {
visible_rect = *ED_region_visible_rect(region);
}
}
ved->instance->init(
size, &rect, &visible_rect, nullptr, depsgraph, camera, nullptr, &default_view, v3d, rv3d);
return new Instance();
}
static void eevee_draw_scene(void *vedata)
void Engine::free_static()
{
EEVEE_Data *ved = reinterpret_cast<EEVEE_Data *>(vedata);
if (DRW_state_is_viewport_image_render()) {
ved->instance->draw_viewport_image_render();
}
else {
ved->instance->draw_viewport();
}
STRNCPY(ved->info, ved->instance->info_get());
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_viewport_reset(dfbl->default_fb);
ShaderModule::module_free();
}
static void eevee_cache_init(void *vedata)
} // namespace blender::eevee
using namespace blender::eevee;
void eevee_render(RenderEngine *engine, Depsgraph *depsgraph)
{
reinterpret_cast<EEVEE_Data *>(vedata)->instance->begin_sync();
}
Instance *instance = nullptr;
static void eevee_cache_populate(void *vedata, draw::ObjectRef &ob_ref)
{
reinterpret_cast<EEVEE_Data *>(vedata)->instance->object_sync(ob_ref);
}
auto eevee_render_to_image = [&](RenderEngine *engine, RenderLayer *layer, const rcti /*rect*/) {
Render *render = engine->re;
Depsgraph *depsgraph = DRW_context_get()->depsgraph;
Object *camera_original_ob = RE_GetCamera(engine->re);
const char *viewname = RE_GetActiveRenderView(engine->re);
int size[2] = {engine->resolution_x, engine->resolution_y};
static void eevee_cache_finish(void *vedata)
{
reinterpret_cast<EEVEE_Data *>(vedata)->instance->end_sync();
}
/* WORKAROUND: Fails if created in the parent scope. Must be because of lack of active
* `DRWContext`. To be revisited. */
instance = new Instance();
static void eevee_engine_free()
{
eevee::ShaderModule::module_free();
}
rctf view_rect;
rcti rect;
RE_GetViewPlane(render, &view_rect, &rect);
rcti visible_rect = rect;
static void eevee_instance_free(void *instance)
{
delete reinterpret_cast<eevee::Instance *>(instance);
}
instance->init(size, &rect, &visible_rect, engine, depsgraph, camera_original_ob, layer);
instance->render_frame(engine, layer, viewname);
};
static void eevee_render_to_image(void *vedata,
RenderEngine *engine,
RenderLayer *layer,
const rcti * /*rect*/)
{
eevee::Instance *instance = new eevee::Instance();
auto eevee_store_metadata = [&](RenderResult *render_result) {
instance->store_metadata(render_result);
};
Render *render = engine->re;
Depsgraph *depsgraph = DRW_context_get()->depsgraph;
Object *camera_original_ob = RE_GetCamera(engine->re);
const char *viewname = RE_GetActiveRenderView(engine->re);
int size[2] = {engine->resolution_x, engine->resolution_y};
DRW_render_to_image(engine, depsgraph, eevee_render_to_image, eevee_store_metadata);
rctf view_rect;
rcti rect;
RE_GetViewPlane(render, &view_rect, &rect);
rcti visible_rect = rect;
instance->init(size, &rect, &visible_rect, engine, depsgraph, camera_original_ob, layer);
instance->render_frame(engine, layer, viewname);
EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
delete ved->instance;
ved->instance = instance;
}
static void eevee_store_metadata(void *vedata, RenderResult *render_result)
{
EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
eevee::Instance *instance = ved->instance;
instance->store_metadata(render_result);
delete instance;
ved->instance = nullptr;
}
static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
eevee::Instance::update_passes(engine, scene, view_layer);
Instance::update_passes(engine, scene, view_layer);
}
DrawEngineType draw_engine_eevee_next_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("EEVEE"),
/*engine_init*/ &eevee_engine_init,
/*engine_free*/ &eevee_engine_free,
/*instance_free*/ &eevee_instance_free,
/*cache_init*/ &eevee_cache_init,
/*cache_populate*/ &eevee_cache_populate,
/*cache_finish*/ &eevee_cache_finish,
/*draw_scene*/ &eevee_draw_scene,
/*render_to_image*/ &eevee_render_to_image,
/*store_metadata*/ &eevee_store_metadata,
};
RenderEngineType DRW_engine_viewport_eevee_next_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
@@ -197,7 +80,7 @@ RenderEngineType DRW_engine_viewport_eevee_next_type = {
/*name*/ N_("EEVEE"),
/*flag*/ RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
/*update*/ nullptr,
/*render*/ &DRW_render_to_image,
/*render*/ &eevee_render,
/*render_frame_finish*/ nullptr,
/*draw*/ nullptr,
/*bake*/ nullptr,
@@ -205,7 +88,7 @@ RenderEngineType DRW_engine_viewport_eevee_next_type = {
/*view_draw*/ nullptr,
/*update_script_node*/ nullptr,
/*update_render_passes*/ &eevee_render_update_passes,
/*draw_engine*/ &draw_engine_eevee_next_type,
/*draw_engine*/ nullptr,
/*rna_ext*/
{
/*data*/ nullptr,

View File

@@ -11,5 +11,14 @@
#include "DRW_render.hh"
#include "RE_engine.h"
extern DrawEngineType draw_engine_eevee_next_type;
extern RenderEngineType DRW_engine_viewport_eevee_next_type;
namespace blender::eevee {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
static void free_static();
};
} // namespace blender::eevee

View File

@@ -22,6 +22,8 @@
#include "DNA_lightprobe_types.h"
#include "DNA_modifier_types.h"
#include "ED_screen.hh"
#include "ED_view3d.hh"
#include "GPU_context.hh"
#include "IMB_imbuf_types.hh"
@@ -52,6 +54,68 @@ void *Instance::debug_scope_irradiance_sample = nullptr;
* Any attempt to do so will likely produce use after free situations.
* \{ */
void Instance::init()
{
const DRWContext *ctx_state = DRW_context_get();
Depsgraph *depsgraph = ctx_state->depsgraph;
Scene *scene = ctx_state->scene;
View3D *v3d = ctx_state->v3d;
ARegion *region = ctx_state->region;
RegionView3D *rv3d = ctx_state->rv3d;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
int2 size = int2(GPU_texture_width(dtxl->color), GPU_texture_height(dtxl->color));
draw::View &default_view = draw::View::default_get();
Object *camera = nullptr;
/* Get render borders. */
rcti rect;
BLI_rcti_init(&rect, 0, size[0], 0, size[1]);
rcti visible_rect = rect;
if (v3d) {
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
camera = v3d->camera;
}
if (camera) {
rctf default_border;
BLI_rctf_init(&default_border, 0.0f, 1.0f, 0.0f, 1.0f);
bool is_default_border = BLI_rctf_compare(&scene->r.border, &default_border, 0.0f);
bool use_border = scene->r.mode & R_BORDER;
if (!is_default_border && use_border) {
rctf viewborder;
/* TODO(fclem) Might be better to get it from DRW. */
ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, false, &viewborder);
float viewborder_sizex = BLI_rctf_size_x(&viewborder);
float viewborder_sizey = BLI_rctf_size_y(&viewborder);
rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex));
rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey));
rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex));
rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey));
}
}
else if (v3d->flag2 & V3D_RENDER_BORDER) {
rect.xmin = v3d->render_border.xmin * size[0];
rect.ymin = v3d->render_border.ymin * size[1];
rect.xmax = v3d->render_border.xmax * size[0];
rect.ymax = v3d->render_border.ymax * size[1];
}
if (DRW_state_is_viewport_image_render()) {
const float2 vp_size = DRW_viewport_size_get();
visible_rect.xmax = vp_size[0];
visible_rect.ymax = vp_size[1];
visible_rect.xmin = visible_rect.ymin = 0;
}
else {
visible_rect = *ED_region_visible_rect(region);
}
}
init(size, &rect, &visible_rect, nullptr, depsgraph, camera, nullptr, &default_view, v3d, rv3d);
}
void Instance::init(const int2 &output_res,
const rcti *output_rect,
const rcti *visible_rect,
@@ -249,7 +313,7 @@ void Instance::begin_sync()
}
}
void Instance::object_sync(ObjectRef &ob_ref)
void Instance::object_sync(ObjectRef &ob_ref, Manager & /*manager*/)
{
if (skip_render_) {
return;
@@ -314,16 +378,6 @@ void Instance::object_sync(ObjectRef &ob_ref)
}
}
void Instance::object_sync_render(void *instance_,
ObjectRef &ob_ref,
RenderEngine *engine,
Depsgraph *depsgraph)
{
UNUSED_VARS(engine, depsgraph);
Instance &inst = *reinterpret_cast<Instance *>(instance_);
inst.object_sync(ob_ref);
}
void Instance::end_sync()
{
if (skip_render_) {
@@ -354,7 +408,10 @@ void Instance::render_sync()
begin_sync();
DRW_render_object_iter(this, render, depsgraph, object_sync_render);
DRW_render_object_iter(
render, depsgraph, [this](blender::draw::ObjectRef &ob_ref, RenderEngine *, Depsgraph *) {
this->object_sync(ob_ref, *this->manager);
});
velocity.geometry_steps_fill();
@@ -621,6 +678,19 @@ void Instance::draw_viewport_image_render()
}
}
void Instance::draw(Manager & /*manager*/)
{
if (DRW_state_is_viewport_image_render()) {
draw_viewport_image_render();
}
else {
draw_viewport();
}
STRNCPY(info, info_get());
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_viewport_reset(dfbl->default_fb);
}
void Instance::store_metadata(RenderResult *render_result)
{
if (skip_render_) {
@@ -710,7 +780,7 @@ void Instance::light_bake_irradiance(
auto custom_pipeline_wrapper = [&](FunctionRef<void()> callback) {
context_enable();
DRW_custom_pipeline_begin(draw_ctx, &draw_engine_eevee_next_type, depsgraph);
DRW_custom_pipeline_begin(draw_ctx, depsgraph);
callback();
DRW_custom_pipeline_end(draw_ctx);
context_disable();

View File

@@ -72,7 +72,7 @@ struct UniformDataModule {
* \class Instance
* \brief A running instance of the engine.
*/
class Instance {
class Instance : public DrawEngine {
friend VelocityModule;
friend MotionBlurModule;
@@ -180,6 +180,11 @@ class Instance {
volume(*this, uniform_data.data.volumes){};
~Instance(){};
blender::StringRefNull name_get() final
{
return "EEVEE";
}
/* Render & Viewport. */
/* TODO(fclem): Split for clarity. */
void init(const int2 &output_res,
@@ -193,9 +198,11 @@ class Instance {
const View3D *v3d = nullptr,
const RegionView3D *rv3d = nullptr);
void begin_sync();
void object_sync(ObjectRef &ob_ref);
void end_sync();
void init() final;
void begin_sync() final;
void object_sync(ObjectRef &ob_ref, Manager &manager) final;
void end_sync() final;
/**
* Return true when probe pipeline is used during this sample.
@@ -221,6 +228,8 @@ class Instance {
void draw_viewport();
void draw_viewport_image_render();
void draw(Manager &manager) final;
/* Light bake. */
void init_light_bake(Depsgraph *depsgraph, draw::Manager *manager);
@@ -370,11 +379,6 @@ class Instance {
}
private:
/** Wrapper to use with #DRW_render_object_iter. */
static void object_sync_render(void *instance_,
ObjectRef &ob_ref,
RenderEngine *engine,
Depsgraph *depsgraph);
/**
* Conceptually renders one sample per pixel.
* Everything based on random sampling should be done here (i.e: DRWViews jitter)

View File

@@ -60,13 +60,8 @@ void VelocityModule::init()
}
/* Similar to Instance::object_sync, but only syncs velocity. */
static void step_object_sync_render(void *instance,
ObjectRef &ob_ref,
RenderEngine * /*engine*/,
Depsgraph * /*depsgraph*/)
static void step_object_sync_render(Instance &inst, ObjectRef &ob_ref)
{
Instance &inst = *reinterpret_cast<Instance *>(instance);
Object *ob = ob_ref.object;
const bool is_velocity_type = ELEM(ob->type, OB_CURVES, OB_MESH, OB_POINTCLOUD);
@@ -107,7 +102,11 @@ void VelocityModule::step_sync(eVelocityStep step, float time)
object_steps_usage[step_] = 0;
step_camera_sync();
DRW_render_object_iter(&inst_, inst_.render, inst_.depsgraph, step_object_sync_render);
DRW_render_object_iter(inst_.render,
inst_.depsgraph,
[&](blender::draw::ObjectRef &ob_ref, RenderEngine *, Depsgraph *) {
step_object_sync_render(inst_, ob_ref);
});
geometry_steps_fill();
}

View File

@@ -38,226 +38,227 @@
/* Shaders */
#define EXTERNAL_ENGINE "BLENDER_EXTERNAL"
namespace blender::draw::external {
class Instance : public DrawEngine {
blender::StringRefNull name_get() final
{
return "External";
}
struct EXTERNAL_Data {
void *engine_type;
void *instance_data;
void init() final {}
char info[GPU_INFO_SIZE];
};
void begin_sync() final {}
/* Functions */
void object_sync(blender::draw::ObjectRef & /*ob_ref*/,
blender::draw::Manager & /*manager*/) final
{
}
static void external_draw_scene_do_v3d(void *vedata)
{
const DRWContext *draw_ctx = DRW_context_get();
RegionView3D *rv3d = draw_ctx->rv3d;
ARegion *region = draw_ctx->region;
void end_sync() final {}
blender::draw::command::StateSet::set(DRW_STATE_WRITE_COLOR);
void draw_scene_do_v3d()
{
const DRWContext *draw_ctx = DRW_context_get();
RegionView3D *rv3d = draw_ctx->rv3d;
ARegion *region = draw_ctx->region;
/* The external engine can use the OpenGL rendering API directly, so make sure the state is
* already applied. */
GPU_apply_state();
blender::draw::command::StateSet::set(DRW_STATE_WRITE_COLOR);
/* Create render engine. */
RenderEngine *render_engine = nullptr;
if (!rv3d->view_render) {
RenderEngineType *engine_type = ED_view3d_engine_type(draw_ctx->scene,
draw_ctx->v3d->shading.type);
/* The external engine can use the OpenGL rendering API directly, so make sure the state is
* already applied. */
GPU_apply_state();
if (!(engine_type->view_update && engine_type->view_draw)) {
/* Create render engine. */
RenderEngine *render_engine = nullptr;
if (!rv3d->view_render) {
RenderEngineType *engine_type = ED_view3d_engine_type(draw_ctx->scene,
draw_ctx->v3d->shading.type);
if (!(engine_type->view_update && engine_type->view_draw)) {
return;
}
rv3d->view_render = RE_NewViewRender(engine_type);
render_engine = RE_view_engine_get(rv3d->view_render);
engine_type->view_update(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
}
else {
render_engine = RE_view_engine_get(rv3d->view_render);
}
/* Rendered draw. */
GPU_matrix_push_projection();
GPU_matrix_push();
ED_region_pixelspace(region);
/* Render result draw. */
const RenderEngineType *type = render_engine->type;
type->view_draw(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
GPU_bgl_end();
GPU_matrix_pop();
GPU_matrix_pop_projection();
/* Set render info. */
if (render_engine->text[0] != '\0') {
STRNCPY(info, render_engine->text);
}
else {
info[0] = '\0';
}
}
/* Configure current matrix stack so that the external engine can use the same drawing code for
* both viewport and image editor drawing.
*
* The engine draws result in the pixel space, and is applying render offset. For image editor we
* need to switch from normalized space to pixel space, and "un-apply" offset. */
static void external_image_space_matrix_set(const RenderEngine *engine)
{
BLI_assert(engine != nullptr);
const DRWContext *draw_ctx = DRW_context_get();
SpaceImage *space_image = (SpaceImage *)draw_ctx->space_data;
/* Apply current view as transformation matrix.
* This will configure drawing for normalized space with current zoom and pan applied. */
float4x4 view_matrix = blender::draw::View::default_get().viewmat();
float4x4 projection_matrix = blender::draw::View::default_get().winmat();
GPU_matrix_projection_set(projection_matrix.ptr());
GPU_matrix_set(view_matrix.ptr());
/* Switch from normalized space to pixel space. */
{
int width, height;
ED_space_image_get_size(space_image, &width, &height);
const float width_inv = width ? 1.0f / width : 0.0f;
const float height_inv = height ? 1.0f / height : 0.0f;
GPU_matrix_scale_2f(width_inv, height_inv);
}
/* Un-apply render offset. */
{
Render *render = engine->re;
rctf view_rect;
rcti render_rect;
RE_GetViewPlane(render, &view_rect, &render_rect);
GPU_matrix_translate_2f(-render_rect.xmin, -render_rect.ymin);
}
}
void draw_scene_do_image()
{
const DRWContext *draw_ctx = DRW_context_get();
Scene *scene = draw_ctx->scene;
Render *re = RE_GetSceneRender(scene);
RenderEngine *engine = RE_engine_get(re);
/* Is tested before enabling the drawing engine. */
BLI_assert(re != nullptr);
BLI_assert(engine != nullptr);
blender::draw::command::StateSet::set(DRW_STATE_WRITE_COLOR);
/* The external engine can use the OpenGL rendering API directly, so make sure the state is
* already applied. */
GPU_apply_state();
const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
/* Clear the depth buffer to the value used by the background overlay so that the overlay is
* not happening outside of the drawn image.
*
* NOTE: The external engine only draws color. The depth is taken care of using the depth pass
* which initialized the depth to the values expected by the background overlay. */
GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f);
GPU_matrix_push_projection();
GPU_matrix_push();
external_image_space_matrix_set(engine);
GPU_debug_group_begin("External Engine");
const RenderEngineType *engine_type = engine->type;
BLI_assert(engine_type != nullptr);
BLI_assert(engine_type->draw != nullptr);
engine_type->draw(engine, draw_ctx->evil_C, draw_ctx->depsgraph);
GPU_debug_group_end();
GPU_matrix_pop();
GPU_matrix_pop_projection();
blender::draw::command::StateSet::set();
GPU_bgl_end();
RE_engine_draw_release(re);
}
void draw_scene_do()
{
const DRWContext *draw_ctx = DRW_context_get();
if (draw_ctx->v3d != nullptr) {
draw_scene_do_v3d();
return;
}
rv3d->view_render = RE_NewViewRender(engine_type);
render_engine = RE_view_engine_get(rv3d->view_render);
engine_type->view_update(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
}
else {
render_engine = RE_view_engine_get(rv3d->view_render);
if (draw_ctx->space_data == nullptr) {
return;
}
const eSpace_Type space_type = eSpace_Type(draw_ctx->space_data->spacetype);
if (space_type == SPACE_IMAGE) {
draw_scene_do_image();
return;
}
}
/* Rendered draw. */
GPU_matrix_push_projection();
GPU_matrix_push();
ED_region_pixelspace(region);
/* Render result draw. */
const RenderEngineType *type = render_engine->type;
type->view_draw(render_engine, draw_ctx->evil_C, draw_ctx->depsgraph);
GPU_bgl_end();
GPU_matrix_pop();
GPU_matrix_pop_projection();
/* Set render info. */
EXTERNAL_Data *data = static_cast<EXTERNAL_Data *>(vedata);
if (render_engine->text[0] != '\0') {
STRNCPY(data->info, render_engine->text);
}
else {
data->info[0] = '\0';
}
}
/* Configure current matrix stack so that the external engine can use the same drawing code for
* both viewport and image editor drawing.
*
* The engine draws result in the pixel space, and is applying render offset. For image editor we
* need to switch from normalized space to pixel space, and "un-apply" offset. */
static void external_image_space_matrix_set(const RenderEngine *engine)
{
BLI_assert(engine != nullptr);
const DRWContext *draw_ctx = DRW_context_get();
SpaceImage *space_image = (SpaceImage *)draw_ctx->space_data;
/* Apply current view as transformation matrix.
* This will configure drawing for normalized space with current zoom and pan applied. */
float4x4 view_matrix = blender::draw::View::default_get().viewmat();
float4x4 projection_matrix = blender::draw::View::default_get().winmat();
GPU_matrix_projection_set(projection_matrix.ptr());
GPU_matrix_set(view_matrix.ptr());
/* Switch from normalized space to pixel space. */
void draw(blender::draw::Manager & /*manager*/) final
{
int width, height;
ED_space_image_get_size(space_image, &width, &height);
const DRWContext *draw_ctx = DRW_context_get();
const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
const float width_inv = width ? 1.0f / width : 0.0f;
const float height_inv = height ? 1.0f / height : 0.0f;
GPU_matrix_scale_2f(width_inv, height_inv);
/* Will be nullptr during OpenGL render.
* OpenGL render is used for quick preview (thumbnails or sequencer preview)
* where using the rendering engine to preview doesn't make so much sense. */
if (draw_ctx->evil_C) {
const float clear_col[4] = {0, 0, 0, 0};
/* This is to keep compatibility with external engine. */
/* TODO(fclem): remove it eventually. */
GPU_framebuffer_bind(dfbl->default_fb);
GPU_framebuffer_clear_color(dfbl->default_fb, clear_col);
DRW_submission_start();
draw_scene_do();
DRW_submission_end();
}
}
/* Un-apply render offset. */
{
Render *render = engine->re;
rctf view_rect;
rcti render_rect;
RE_GetViewPlane(render, &view_rect, &render_rect);
GPU_matrix_translate_2f(-render_rect.xmin, -render_rect.ymin);
}
}
static void external_draw_scene_do_image(void * /*vedata*/)
{
const DRWContext *draw_ctx = DRW_context_get();
Scene *scene = draw_ctx->scene;
Render *re = RE_GetSceneRender(scene);
RenderEngine *engine = RE_engine_get(re);
/* Is tested before enabling the drawing engine. */
BLI_assert(re != nullptr);
BLI_assert(engine != nullptr);
blender::draw::command::StateSet::set(DRW_STATE_WRITE_COLOR);
/* The external engine can use the OpenGL rendering API directly, so make sure the state is
* already applied. */
GPU_apply_state();
const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
/* Clear the depth buffer to the value used by the background overlay so that the overlay is not
* happening outside of the drawn image.
*
* NOTE: The external engine only draws color. The depth is taken care of using the depth pass
* which initialized the depth to the values expected by the background overlay. */
GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f);
GPU_matrix_push_projection();
GPU_matrix_push();
external_image_space_matrix_set(engine);
GPU_debug_group_begin("External Engine");
const RenderEngineType *engine_type = engine->type;
BLI_assert(engine_type != nullptr);
BLI_assert(engine_type->draw != nullptr);
engine_type->draw(engine, draw_ctx->evil_C, draw_ctx->depsgraph);
GPU_debug_group_end();
GPU_matrix_pop();
GPU_matrix_pop_projection();
blender::draw::command::StateSet::set();
GPU_bgl_end();
RE_engine_draw_release(re);
}
static void external_draw_scene_do(void *vedata)
{
const DRWContext *draw_ctx = DRW_context_get();
if (draw_ctx->v3d != nullptr) {
external_draw_scene_do_v3d(vedata);
return;
}
if (draw_ctx->space_data == nullptr) {
return;
}
const eSpace_Type space_type = eSpace_Type(draw_ctx->space_data->spacetype);
if (space_type == SPACE_IMAGE) {
external_draw_scene_do_image(vedata);
return;
}
}
static void external_draw_scene(void *vedata)
{
const DRWContext *draw_ctx = DRW_context_get();
const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
/* Will be nullptr during OpenGL render.
* OpenGL render is used for quick preview (thumbnails or sequencer preview)
* where using the rendering engine to preview doesn't make so much sense. */
if (draw_ctx->evil_C) {
const float clear_col[4] = {0, 0, 0, 0};
/* This is to keep compatibility with external engine. */
/* TODO(fclem): remove it eventually. */
GPU_framebuffer_bind(dfbl->default_fb);
GPU_framebuffer_clear_color(dfbl->default_fb, clear_col);
DRW_submission_start();
external_draw_scene_do(vedata);
DRW_submission_end();
}
}
DrawEngineType draw_engine_external_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("External"),
/*engine_init*/ nullptr,
/*engine_free*/ nullptr,
/*instance_free*/ nullptr,
/*cache_init*/ nullptr,
/*cache_populate*/ nullptr,
/*cache_finish*/ nullptr,
/*draw_scene*/ &external_draw_scene,
/*render_to_image*/ nullptr,
/*store_metadata*/ nullptr,
};
DrawEngine *Engine::create_instance()
{
return new Instance();
}
} // namespace blender::draw::external
/* Functions */
/* NOTE: currently unused,
* we should not register unless we want to see this when debugging the view. */
RenderEngineType DRW_engine_viewport_external_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ EXTERNAL_ENGINE,
/*idname*/ "BLENDER_EXTERNAL",
/*name*/ N_("External"),
/*flag*/ RE_INTERNAL | RE_USE_STEREO_VIEWPORT,
/*update*/ nullptr,
@@ -269,7 +270,7 @@ RenderEngineType DRW_engine_viewport_external_type = {
/*view_draw*/ nullptr,
/*update_script_node*/ nullptr,
/*update_render_passes*/ nullptr,
/*draw_engine*/ &draw_engine_external_type,
/*draw_engine*/ nullptr,
/*rna_ext*/
{
/*data*/ nullptr,
@@ -324,5 +325,3 @@ void DRW_engine_external_free(RegionView3D *rv3d)
DRW_gpu_context_disable_ex(true);
}
}
#undef EXTERNAL_ENGINE

View File

@@ -8,10 +8,10 @@
#pragma once
struct DrawEngineType;
#include "DRW_render.hh"
struct RenderEngineType;
extern DrawEngineType draw_engine_external_type;
extern RenderEngineType DRW_engine_viewport_external_type;
/* Check whether an external engine is to be used to draw content of an image editor.
@@ -20,3 +20,11 @@ extern RenderEngineType DRW_engine_viewport_external_type;
*
* NOTE: Released by the draw engine when it is done drawing. */
bool DRW_engine_external_acquire_for_image_editor(void);
namespace blender::draw::external {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
};
} // namespace blender::draw::external

View File

@@ -9,11 +9,13 @@
#include "DNA_scene_types.h"
#include "DRW_render.hh"
#include "gpencil_engine.h"
#include "gpencil_engine_private.hh"
#include "BLI_smaa_textures.h"
void GPENCIL_antialiasing_init(GPENCIL_Instance *inst)
namespace blender::draw::gpencil {
void GPENCIL_antialiasing_init(Instance *inst)
{
const float2 size_f = DRW_viewport_size_get();
const int2 size(size_f[0], size_f[1]);
@@ -24,7 +26,7 @@ void GPENCIL_antialiasing_init(GPENCIL_Instance *inst)
blender::draw::PassSimple &pass = inst->smaa_resolve_ps;
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
pass.shader_set(GPENCIL_shader_antialiasing(2));
pass.shader_set(ShaderCache::get().antialiasing[2].get());
pass.bind_texture("blendTex", &inst->color_tx);
pass.bind_texture("colorTex", &inst->color_tx);
pass.bind_texture("revealTex", &inst->reveal_tx);
@@ -61,7 +63,7 @@ void GPENCIL_antialiasing_init(GPENCIL_Instance *inst)
blender::draw::PassSimple &pass = inst->smaa_edge_ps;
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR);
pass.shader_set(GPENCIL_shader_antialiasing(0));
pass.shader_set(ShaderCache::get().antialiasing[0].get());
pass.bind_texture("colorTex", &inst->color_tx);
pass.bind_texture("revealTex", &inst->reveal_tx);
pass.push_constant("viewportMetrics", metrics);
@@ -74,7 +76,7 @@ void GPENCIL_antialiasing_init(GPENCIL_Instance *inst)
blender::draw::PassSimple &pass = inst->smaa_weight_ps;
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR);
pass.shader_set(GPENCIL_shader_antialiasing(1));
pass.shader_set(ShaderCache::get().antialiasing[1].get());
pass.bind_texture("edgesTex", &inst->smaa_edge_tx);
pass.bind_texture("areaTex", &inst->smaa_area_tx);
pass.bind_texture("searchTex", &inst->smaa_search_tx);
@@ -87,7 +89,7 @@ void GPENCIL_antialiasing_init(GPENCIL_Instance *inst)
blender::draw::PassSimple &pass = inst->smaa_resolve_ps;
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
pass.shader_set(GPENCIL_shader_antialiasing(2));
pass.shader_set(ShaderCache::get().antialiasing[2].get());
pass.bind_texture("blendTex", &inst->smaa_weight_tx);
pass.bind_texture("colorTex", &inst->color_tx);
pass.bind_texture("revealTex", &inst->reveal_tx);
@@ -98,10 +100,8 @@ void GPENCIL_antialiasing_init(GPENCIL_Instance *inst)
}
}
void GPENCIL_antialiasing_draw(GPENCIL_Data *vedata)
void GPENCIL_antialiasing_draw(Instance *inst)
{
GPENCIL_Instance *inst = vedata->instance;
blender::draw::Manager *manager = DRW_manager_get();
if (!inst->simplify_antialias) {
@@ -115,3 +115,5 @@ void GPENCIL_antialiasing_draw(GPENCIL_Data *vedata)
GPU_framebuffer_bind(inst->scene_fb);
manager->submit(inst->smaa_resolve_ps);
}
} // namespace blender::draw::gpencil

View File

@@ -27,17 +27,19 @@
#include "BLI_math_vector.hh"
#include "BLI_memblock.h"
#include "gpencil_engine.h"
#include "gpencil_engine_private.hh"
#include "DEG_depsgraph.hh"
#include "UI_resources.hh"
namespace blender::draw::gpencil {
/* -------------------------------------------------------------------- */
/** \name Object
* \{ */
GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_Instance *inst,
GPENCIL_tObject *gpencil_object_cache_add(Instance *inst,
Object *ob,
const bool is_stroke_order_3d,
const blender::Bounds<float3> bounds)
@@ -144,7 +146,7 @@ static int gpencil_tobject_dist_sort(const void *a, const void *b)
return 0;
}
void gpencil_object_cache_sort(GPENCIL_Instance *inst)
void gpencil_object_cache_sort(Instance *inst)
{
/* Sort object by distance to the camera. */
if (inst->tobjects.first) {
@@ -184,7 +186,7 @@ void gpencil_object_cache_sort(GPENCIL_Instance *inst)
/** \name Layer
* \{ */
static float grease_pencil_layer_final_opacity_get(const GPENCIL_Instance *inst,
static float grease_pencil_layer_final_opacity_get(const Instance *inst,
const Object *ob,
const GreasePencil &grease_pencil,
const blender::bke::greasepencil::Layer &layer)
@@ -206,7 +208,7 @@ static float grease_pencil_layer_final_opacity_get(const GPENCIL_Instance *inst,
return layer.opacity;
}
static float4 grease_pencil_layer_final_tint_and_alpha_get(const GPENCIL_Instance *inst,
static float4 grease_pencil_layer_final_tint_and_alpha_get(const Instance *inst,
const GreasePencil &grease_pencil,
const int onion_id,
float *r_alpha)
@@ -286,7 +288,7 @@ GPENCIL_tLayer *grease_pencil_layer_cache_get(GPENCIL_tObject *tgp_ob,
return nullptr;
}
GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_Instance *inst,
GPENCIL_tLayer *grease_pencil_layer_cache_add(Instance *inst,
const Object *ob,
const blender::bke::greasepencil::Layer &layer,
const int onion_id,
@@ -407,7 +409,7 @@ GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_Instance *inst,
PassSimple &pass = *tgp_layer->blend_ps;
pass.init();
pass.state_set(state);
pass.shader_set(GPENCIL_shader_layer_blend_get());
pass.shader_set(ShaderCache::get().layer_blend.get());
pass.push_constant("blendMode", int(layer.blend_mode));
pass.push_constant("blendOpacity", layer_opacity);
pass.bind_texture("colorBuf", &inst->color_layer_tx);
@@ -445,7 +447,7 @@ GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_Instance *inst,
state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
pass.state_set(state);
pass.shader_set(GPENCIL_shader_geometry_get());
pass.shader_set(ShaderCache::get().geometry.get());
pass.bind_texture("gpSceneDepthTexture", depth_tex);
pass.bind_texture("gpMaskTexture", mask_tex);
pass.push_constant("gpNormal", tgp_ob->plane_normal);
@@ -471,3 +473,5 @@ GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_Instance *inst,
return tgp_layer;
}
/** \} */
} // namespace blender::draw::gpencil

View File

@@ -22,13 +22,15 @@
#include "IMB_imbuf_types.hh"
#include "gpencil_engine.h"
#include "gpencil_engine_private.hh"
namespace blender::draw::gpencil {
/* -------------------------------------------------------------------- */
/** \name Material
* \{ */
static GPENCIL_MaterialPool *gpencil_material_pool_add(GPENCIL_Instance *inst)
static GPENCIL_MaterialPool *gpencil_material_pool_add(Instance *inst)
{
GPENCIL_MaterialPool *matpool = static_cast<GPENCIL_MaterialPool *>(
BLI_memblock_alloc(inst->gp_material_pool));
@@ -41,7 +43,7 @@ static GPENCIL_MaterialPool *gpencil_material_pool_add(GPENCIL_Instance *inst)
return matpool;
}
static GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_premult)
static GPUTexture *gpencil_image_texture_get(::Image *image, bool *r_alpha_premult)
{
ImageUser iuser = {nullptr};
GPUTexture *gpu_tex = nullptr;
@@ -88,7 +90,7 @@ static void gpencil_shade_color(float color[3])
/* Apply all overrides from the solid viewport mode to the GPencil material. */
static MaterialGPencilStyle *gpencil_viewport_material_overrides(
GPENCIL_Instance *inst,
Instance *inst,
Object *ob,
int color_type,
MaterialGPencilStyle *gp_style,
@@ -159,7 +161,7 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(
return gp_style;
}
GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_Instance *inst,
GPENCIL_MaterialPool *gpencil_material_pool_create(Instance *inst,
Object *ob,
int *ofs,
const bool is_vertex_mode)
@@ -327,7 +329,7 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool,
/** \name Lights
* \{ */
GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_Instance *inst)
GPENCIL_LightPool *gpencil_light_pool_add(Instance *inst)
{
GPENCIL_LightPool *lightpool = static_cast<GPENCIL_LightPool *>(
BLI_memblock_alloc(inst->gp_light_pool));
@@ -414,7 +416,7 @@ void gpencil_light_pool_populate(GPENCIL_LightPool *lightpool, Object *ob)
}
}
GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_Instance *inst, Object * /*ob*/)
GPENCIL_LightPool *gpencil_light_pool_create(Instance *inst, Object * /*ob*/)
{
GPENCIL_LightPool *lightpool = inst->last_light_pool;
@@ -427,57 +429,6 @@ GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_Instance *inst, Object * /*
return lightpool;
}
void gpencil_material_pool_free(void *storage)
{
GPENCIL_MaterialPool *matpool = (GPENCIL_MaterialPool *)storage;
GPU_UBO_FREE_SAFE(matpool->ubo);
}
void gpencil_light_pool_free(void *storage)
{
GPENCIL_LightPool *lightpool = (GPENCIL_LightPool *)storage;
GPU_UBO_FREE_SAFE(lightpool->ubo);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name View Layer Data
* \{ */
static void gpencil_view_layer_data_free(void *storage)
{
GPENCIL_ViewLayerData *vldata = (GPENCIL_ViewLayerData *)storage;
BLI_memblock_destroy(vldata->gp_light_pool, gpencil_light_pool_free);
BLI_memblock_destroy(vldata->gp_material_pool, gpencil_material_pool_free);
BLI_memblock_destroy(vldata->gp_maskbit_pool, nullptr);
BLI_memblock_destroy(vldata->gp_object_pool, nullptr);
delete vldata->gp_layer_pool;
delete vldata->gp_vfx_pool;
}
GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure()
{
GPENCIL_ViewLayerData **vldata = (GPENCIL_ViewLayerData **)DRW_view_layer_engine_data_ensure(
&draw_engine_gpencil_type, gpencil_view_layer_data_free);
/* NOTE(@fclem): Putting this stuff in view-layer means it is shared by all viewports.
* For now it is ok, but in the future, it could become a problem if we implement
* the caching system. */
if (*vldata == nullptr) {
*vldata = static_cast<GPENCIL_ViewLayerData *>(
MEM_callocN(sizeof(**vldata), "GPENCIL_ViewLayerData"));
(*vldata)->gp_light_pool = BLI_memblock_create(sizeof(GPENCIL_LightPool));
(*vldata)->gp_material_pool = BLI_memblock_create(sizeof(GPENCIL_MaterialPool));
(*vldata)->gp_maskbit_pool = BLI_memblock_create(BLI_BITMAP_SIZE(GP_MAX_MASKBITS));
(*vldata)->gp_object_pool = BLI_memblock_create(sizeof(GPENCIL_tObject));
(*vldata)->gp_layer_pool = new GPENCIL_tLayer_Pool();
(*vldata)->gp_vfx_pool = new GPENCIL_tVfx_Pool();
}
return *vldata;
}
/** \} */
} // namespace blender::draw::gpencil

View File

@@ -0,0 +1,22 @@
/* SPDX-FileCopyrightText: 2017 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "DRW_render.hh"
namespace blender::draw::gpencil {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
static void render_to_image(RenderEngine *engine, RenderLayer *render_layer, const rcti rect);
static void free_static();
};
} // namespace blender::draw::gpencil

View File

@@ -39,7 +39,8 @@
#include "draw_manager.hh"
#include "draw_view.hh"
#include "gpencil_engine.h"
#include "gpencil_engine.hh"
#include "gpencil_engine_private.hh"
#include "DEG_depsgraph_query.hh"
@@ -49,67 +50,62 @@
#include "GPU_debug.hh"
/* *********** FUNCTIONS *********** */
namespace blender::draw::gpencil {
void GPENCIL_engine_init(void *ved)
using namespace blender::draw;
void Instance::init()
{
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
const DRWContext *ctx = DRW_context_get();
const View3D *v3d = ctx->v3d;
if (vedata->instance == nullptr) {
vedata->instance = new GPENCIL_Instance();
}
GPENCIL_Instance &inst = *vedata->instance;
if (!inst.dummy_texture.is_valid()) {
if (!dummy_texture.is_valid()) {
const float pixels[1][4] = {{1.0f, 0.0f, 1.0f, 1.0f}};
inst.dummy_texture.ensure_2d(GPU_RGBA8, int2(1), GPU_TEXTURE_USAGE_SHADER_READ, &pixels[0][0]);
dummy_texture.ensure_2d(GPU_RGBA8, int2(1), GPU_TEXTURE_USAGE_SHADER_READ, &pixels[0][0]);
}
if (!inst.dummy_depth.is_valid()) {
if (!dummy_depth.is_valid()) {
const float pixels[1] = {1.0f};
inst.dummy_depth.ensure_2d(
dummy_depth.ensure_2d(
GPU_DEPTH_COMPONENT24, int2(1), GPU_TEXTURE_USAGE_SHADER_READ, &pixels[0]);
}
GPENCIL_ViewLayerData *vldata = GPENCIL_view_layer_data_ensure();
/* Resize and reset memory-blocks. */
BLI_memblock_clear(vldata->gp_light_pool, gpencil_light_pool_free);
BLI_memblock_clear(vldata->gp_material_pool, gpencil_material_pool_free);
BLI_memblock_clear(vldata->gp_object_pool, nullptr);
vldata->gp_layer_pool->clear();
vldata->gp_vfx_pool->clear();
BLI_memblock_clear(vldata->gp_maskbit_pool, nullptr);
BLI_memblock_clear(vldata.gp_light_pool, ViewLayerData::light_pool_free);
BLI_memblock_clear(vldata.gp_material_pool, ViewLayerData::material_pool_free);
BLI_memblock_clear(vldata.gp_object_pool, nullptr);
vldata.gp_layer_pool->clear();
vldata.gp_vfx_pool->clear();
BLI_memblock_clear(vldata.gp_maskbit_pool, nullptr);
inst.gp_light_pool = vldata->gp_light_pool;
inst.gp_material_pool = vldata->gp_material_pool;
inst.gp_maskbit_pool = vldata->gp_maskbit_pool;
inst.gp_object_pool = vldata->gp_object_pool;
inst.gp_layer_pool = vldata->gp_layer_pool;
inst.gp_vfx_pool = vldata->gp_vfx_pool;
inst.view_layer = ctx->view_layer;
inst.scene = ctx->scene;
inst.v3d = ctx->v3d;
inst.last_light_pool = nullptr;
inst.last_material_pool = nullptr;
inst.tobjects.first = nullptr;
inst.tobjects.last = nullptr;
inst.tobjects_infront.first = nullptr;
inst.tobjects_infront.last = nullptr;
inst.sbuffer_tobjects.first = nullptr;
inst.sbuffer_tobjects.last = nullptr;
inst.dummy_tx = inst.dummy_texture;
inst.draw_wireframe = (v3d && v3d->shading.type == OB_WIRE);
inst.scene_depth_tx = nullptr;
inst.scene_fb = nullptr;
inst.is_render = inst.render_depth_tx.is_valid() || (v3d && v3d->shading.type == OB_RENDER);
inst.is_viewport = (v3d != nullptr);
inst.global_light_pool = gpencil_light_pool_add(&inst);
inst.shadeless_light_pool = gpencil_light_pool_add(&inst);
/* TODO remove */
this->gp_light_pool = vldata.gp_light_pool;
this->gp_material_pool = vldata.gp_material_pool;
this->gp_maskbit_pool = vldata.gp_maskbit_pool;
this->gp_object_pool = vldata.gp_object_pool;
this->gp_layer_pool = vldata.gp_layer_pool;
this->gp_vfx_pool = vldata.gp_vfx_pool;
this->view_layer = ctx->view_layer;
this->scene = ctx->scene;
this->v3d = ctx->v3d;
this->last_light_pool = nullptr;
this->last_material_pool = nullptr;
this->tobjects.first = nullptr;
this->tobjects.last = nullptr;
this->tobjects_infront.first = nullptr;
this->tobjects_infront.last = nullptr;
this->sbuffer_tobjects.first = nullptr;
this->sbuffer_tobjects.last = nullptr;
this->dummy_tx = this->dummy_texture;
this->draw_wireframe = (v3d && v3d->shading.type == OB_WIRE);
this->scene_depth_tx = nullptr;
this->scene_fb = nullptr;
this->is_render = this->render_depth_tx.is_valid() || (v3d && v3d->shading.type == OB_RENDER);
this->is_viewport = (v3d != nullptr);
this->global_light_pool = gpencil_light_pool_add(this);
this->shadeless_light_pool = gpencil_light_pool_add(this);
/* Small HACK: we don't want the global pool to be reused,
* so we set the last light pool to nullptr. */
inst.last_light_pool = nullptr;
this->last_light_pool = nullptr;
bool use_scene_lights = false;
bool use_scene_world = false;
@@ -119,76 +115,72 @@ void GPENCIL_engine_init(void *ved)
use_scene_world = V3D_USES_SCENE_WORLD(v3d);
inst.v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1;
this->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1;
/* Special case: If we're in Vertex Paint mode, enforce #V3D_SHADING_VERTEX_COLOR setting. */
if (v3d->shading.type == OB_SOLID && ctx->obact &&
(ctx->obact->mode & OB_MODE_VERTEX_GREASE_PENCIL) != 0)
{
inst.v3d_color_type = V3D_SHADING_VERTEX_COLOR;
this->v3d_color_type = V3D_SHADING_VERTEX_COLOR;
}
copy_v3_v3(inst.v3d_single_color, v3d->shading.single_color);
copy_v3_v3(this->v3d_single_color, v3d->shading.single_color);
/* For non active frame, use only lines in multiedit mode. */
const bool overlays_on = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0;
inst.use_multiedit_lines_only = overlays_on &&
(v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0;
this->use_multiedit_lines_only = overlays_on &&
(v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0;
const bool shmode_xray_support = v3d->shading.type <= OB_SOLID;
inst.xray_alpha = (shmode_xray_support && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f;
inst.force_stroke_order_3d = v3d->gp_flag & V3D_GP_FORCE_STROKE_ORDER_3D;
this->xray_alpha = (shmode_xray_support && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f;
this->force_stroke_order_3d = v3d->gp_flag & V3D_GP_FORCE_STROKE_ORDER_3D;
}
else if (inst.is_render) {
else if (this->is_render) {
use_scene_lights = true;
use_scene_world = true;
inst.use_multiedit_lines_only = false;
inst.xray_alpha = 1.0f;
inst.v3d_color_type = -1;
inst.force_stroke_order_3d = false;
this->use_multiedit_lines_only = false;
this->xray_alpha = 1.0f;
this->v3d_color_type = -1;
this->force_stroke_order_3d = false;
}
inst.use_lighting = (v3d && v3d->shading.type > OB_SOLID) || inst.is_render;
inst.use_lights = use_scene_lights;
this->use_lighting = (v3d && v3d->shading.type > OB_SOLID) || this->is_render;
this->use_lights = use_scene_lights;
gpencil_light_ambient_add(inst.shadeless_light_pool, blender::float3{1.0f, 1.0f, 1.0f});
gpencil_light_ambient_add(this->shadeless_light_pool, blender::float3{1.0f, 1.0f, 1.0f});
World *world = ctx->scene->world;
if (world != nullptr && use_scene_world) {
gpencil_light_ambient_add(inst.global_light_pool, &world->horr);
gpencil_light_ambient_add(this->global_light_pool, &world->horr);
}
else if (v3d) {
float world_light[3];
copy_v3_fl(world_light, v3d->shading.studiolight_intensity);
gpencil_light_ambient_add(inst.global_light_pool, world_light);
gpencil_light_ambient_add(this->global_light_pool, world_light);
}
float4x4 viewmatinv = blender::draw::View::default_get().viewinv();
copy_v3_v3(inst.camera_z_axis, viewmatinv[2]);
copy_v3_v3(inst.camera_pos, viewmatinv[3]);
inst.camera_z_offset = dot_v3v3(viewmatinv[3], viewmatinv[2]);
copy_v3_v3(this->camera_z_axis, viewmatinv[2]);
copy_v3_v3(this->camera_pos, viewmatinv[3]);
this->camera_z_offset = dot_v3v3(viewmatinv[3], viewmatinv[2]);
if (ctx && ctx->rv3d && v3d) {
inst.camera = (ctx->rv3d->persp == RV3D_CAMOB) ? v3d->camera : nullptr;
this->camera = (ctx->rv3d->persp == RV3D_CAMOB) ? v3d->camera : nullptr;
}
else {
inst.camera = nullptr;
this->camera = nullptr;
}
}
void GPENCIL_cache_init(void *ved)
void Instance::begin_sync()
{
using namespace blender::draw;
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
GPENCIL_Instance *inst = vedata->instance;
const DRWContext *draw_ctx = DRW_context_get();
inst->cfra = int(DEG_get_ctime(draw_ctx->depsgraph));
inst->simplify_antialias = GPENCIL_SIMPLIFY_AA(draw_ctx->scene);
inst->use_layer_fb = false;
inst->use_object_fb = false;
inst->use_mask_fb = false;
this->cfra = int(DEG_get_ctime(draw_ctx->depsgraph));
this->simplify_antialias = GPENCIL_SIMPLIFY_AA(draw_ctx->scene);
this->use_layer_fb = false;
this->use_object_fb = false;
this->use_mask_fb = false;
/* Always use high precision for render. */
inst->use_signed_fb = !inst->is_viewport;
this->use_signed_fb = !this->is_viewport;
if (draw_ctx->v3d) {
const bool hide_overlay = ((draw_ctx->v3d->flag2 & V3D_HIDE_OVERLAYS) != 0);
@@ -197,95 +189,95 @@ void GPENCIL_cache_init(void *ved)
ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) !=
nullptr :
false;
inst->do_onion = show_onion && !hide_overlay && !playing;
inst->playing = playing;
this->do_onion = show_onion && !hide_overlay && !playing;
this->playing = playing;
/* Save simplify flags (can change while drawing, so it's better to save). */
Scene *scene = draw_ctx->scene;
inst->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, playing);
inst->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, playing) ||
this->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, playing);
this->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, playing) ||
(draw_ctx->v3d->shading.type < OB_RENDER);
/* Fade Layer. */
const bool is_fade_layer = ((!hide_overlay) && (!inst->is_render) &&
const bool is_fade_layer = ((!hide_overlay) && (!this->is_render) &&
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS));
inst->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f;
inst->vertex_paint_opacity = draw_ctx->v3d->overlay.gpencil_vertex_paint_opacity;
this->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f;
this->vertex_paint_opacity = draw_ctx->v3d->overlay.gpencil_vertex_paint_opacity;
/* Fade GPencil Objects. */
const bool is_fade_object = ((!hide_overlay) && (!inst->is_render) &&
const bool is_fade_object = ((!hide_overlay) && (!this->is_render) &&
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS) &&
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_GPENCIL));
inst->fade_gp_object_opacity = (is_fade_object) ?
this->fade_gp_object_opacity = (is_fade_object) ?
draw_ctx->v3d->overlay.gpencil_paper_opacity :
-1.0f;
inst->fade_3d_object_opacity = ((!hide_overlay) && (!inst->is_render) &&
this->fade_3d_object_opacity = ((!hide_overlay) && (!this->is_render) &&
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS)) ?
draw_ctx->v3d->overlay.gpencil_paper_opacity :
-1.0f;
}
else {
inst->do_onion = true;
this->do_onion = true;
Scene *scene = draw_ctx->scene;
inst->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, false);
inst->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, false);
inst->fade_layer_opacity = -1.0f;
inst->playing = false;
this->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, false);
this->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, false);
this->fade_layer_opacity = -1.0f;
this->playing = false;
}
{
inst->stroke_batch = nullptr;
inst->fill_batch = nullptr;
inst->do_fast_drawing = false;
this->stroke_batch = nullptr;
this->fill_batch = nullptr;
this->do_fast_drawing = false;
inst->obact = draw_ctx->obact;
this->obact = draw_ctx->obact;
}
if (inst->do_fast_drawing) {
inst->snapshot_buffer_dirty = !inst->snapshot_depth_tx.is_valid();
if (this->do_fast_drawing) {
this->snapshot_buffer_dirty = !this->snapshot_depth_tx.is_valid();
const float2 size = DRW_viewport_size_get();
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT;
inst->snapshot_depth_tx.ensure_2d(GPU_DEPTH24_STENCIL8, int2(size), usage);
inst->snapshot_color_tx.ensure_2d(GPU_R11F_G11F_B10F, int2(size), usage);
inst->snapshot_reveal_tx.ensure_2d(GPU_R11F_G11F_B10F, int2(size), usage);
this->snapshot_depth_tx.ensure_2d(GPU_DEPTH24_STENCIL8, int2(size), usage);
this->snapshot_color_tx.ensure_2d(GPU_R11F_G11F_B10F, int2(size), usage);
this->snapshot_reveal_tx.ensure_2d(GPU_R11F_G11F_B10F, int2(size), usage);
inst->snapshot_fb.ensure(GPU_ATTACHMENT_TEXTURE(inst->snapshot_depth_tx),
GPU_ATTACHMENT_TEXTURE(inst->snapshot_color_tx),
GPU_ATTACHMENT_TEXTURE(inst->snapshot_reveal_tx));
this->snapshot_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->snapshot_depth_tx),
GPU_ATTACHMENT_TEXTURE(this->snapshot_color_tx),
GPU_ATTACHMENT_TEXTURE(this->snapshot_reveal_tx));
}
else {
/* Free unneeded buffers. */
inst->snapshot_depth_tx.free();
inst->snapshot_color_tx.free();
inst->snapshot_reveal_tx.free();
this->snapshot_depth_tx.free();
this->snapshot_color_tx.free();
this->snapshot_reveal_tx.free();
}
{
blender::draw::PassSimple &pass = inst->merge_depth_ps;
blender::draw::PassSimple &pass = this->merge_depth_ps;
pass.init();
pass.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
pass.shader_set(GPENCIL_shader_depth_merge_get());
pass.bind_texture("depthBuf", &inst->depth_tx);
pass.push_constant("strokeOrder3d", &inst->is_stroke_order_3d);
pass.push_constant("gpModelMatrix", &inst->object_bound_mat);
pass.shader_set(ShaderCache::get().depth_merge.get());
pass.bind_texture("depthBuf", &this->depth_tx);
pass.push_constant("strokeOrder3d", &this->is_stroke_order_3d);
pass.push_constant("gpModelMatrix", &this->object_bound_mat);
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
{
blender::draw::PassSimple &pass = inst->mask_invert_ps;
blender::draw::PassSimple &pass = this->mask_invert_ps;
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_LOGIC_INVERT);
pass.shader_set(GPENCIL_shader_mask_invert_get());
pass.shader_set(ShaderCache::get().mask_invert.get());
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
Camera *cam = static_cast<Camera *>(
(inst->camera != nullptr && inst->camera->type == OB_CAMERA) ? inst->camera->data : nullptr);
(this->camera != nullptr && this->camera->type == OB_CAMERA) ? this->camera->data : nullptr);
/* Pseudo DOF setup. */
if (cam && (cam->dof.flag & CAM_DOF_ENABLED)) {
const float2 vp_size = DRW_viewport_size_get();
float fstop = cam->dof.aperture_fstop;
float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
float focus_dist = BKE_camera_object_dof_distance(inst->camera);
float focus_dist = BKE_camera_object_dof_distance(this->camera);
float focal_len = cam->lens;
const float scale_camera = 0.001f;
@@ -298,13 +290,13 @@ void GPENCIL_cache_init(void *ved)
sensor_scaled *= draw_ctx->rv3d->viewcamtexcofac[0];
}
inst->dof_params[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
inst->dof_params[1] *= vp_size[0] / sensor_scaled;
inst->dof_params[0] = -focus_dist * inst->dof_params[1];
this->dof_params[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
this->dof_params[1] *= vp_size[0] / sensor_scaled;
this->dof_params[0] = -focus_dist * this->dof_params[1];
}
else {
/* Disable DoF blur scaling. */
inst->camera = nullptr;
this->camera = nullptr;
}
}
@@ -358,7 +350,7 @@ static bool use_layer_in_render(const GreasePencil &grease_pencil,
}
static GPENCIL_tObject *grease_pencil_object_cache_populate(
GPENCIL_Instance *inst, Object *ob, blender::draw::ResourceHandle res_handle)
Instance *inst, Object *ob, blender::draw::ResourceHandle res_handle)
{
using namespace blender;
using namespace blender::ed::greasepencil;
@@ -590,10 +582,8 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(
return tgp_ob;
}
void GPENCIL_cache_populate(void *ved, blender::draw::ObjectRef &ob_ref)
void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
{
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
GPENCIL_Instance *inst = vedata->instance;
Object *ob = ob_ref.object;
/* object must be visible */
@@ -602,43 +592,39 @@ void GPENCIL_cache_populate(void *ved, blender::draw::ObjectRef &ob_ref)
}
if (ob->data && (ob->type == OB_GREASE_PENCIL) && (ob->dt >= OB_SOLID)) {
blender::draw::Manager *manager = DRW_manager_get();
blender::draw::ResourceHandle res_handle = manager->unique_handle(ob_ref);
blender::draw::ResourceHandle res_handle = manager.unique_handle(ob_ref);
GPENCIL_tObject *tgp_ob = grease_pencil_object_cache_populate(inst, ob, res_handle);
GPENCIL_tObject *tgp_ob = grease_pencil_object_cache_populate(this, ob, res_handle);
gpencil_vfx_cache_populate(
vedata,
this,
ob,
tgp_ob,
ELEM(ob->mode, OB_MODE_EDIT, OB_MODE_SCULPT_GREASE_PENCIL, OB_MODE_WEIGHT_GREASE_PENCIL));
}
if (ob->type == OB_LAMP && inst->use_lights) {
gpencil_light_pool_populate(inst->global_light_pool, ob);
if (ob->type == OB_LAMP && this->use_lights) {
gpencil_light_pool_populate(this->global_light_pool, ob);
}
}
void GPENCIL_cache_finish(void *ved)
void Instance::end_sync()
{
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
GPENCIL_Instance *inst = vedata->instance;
/* Upload UBO data. */
BLI_memblock_iter iter;
BLI_memblock_iternew(inst->gp_material_pool, &iter);
BLI_memblock_iternew(this->gp_material_pool, &iter);
GPENCIL_MaterialPool *pool;
while ((pool = (GPENCIL_MaterialPool *)BLI_memblock_iterstep(&iter))) {
GPU_uniformbuf_update(pool->ubo, pool->mat_data);
}
BLI_memblock_iternew(inst->gp_light_pool, &iter);
BLI_memblock_iternew(this->gp_light_pool, &iter);
GPENCIL_LightPool *lpool;
while ((lpool = (GPENCIL_LightPool *)BLI_memblock_iterstep(&iter))) {
GPU_uniformbuf_update(lpool->ubo, lpool->light_data);
}
}
void GPENCIL_Instance::acquire_resources()
void Instance::acquire_resources()
{
/* Create frame-buffers only if needed. */
if (this->tobjects.first == nullptr) {
@@ -690,7 +676,7 @@ void GPENCIL_Instance::acquire_resources()
}
}
void GPENCIL_Instance::release_resources()
void Instance::release_resources()
{
this->depth_tx.release();
this->color_tx.release();
@@ -706,12 +692,11 @@ void GPENCIL_Instance::release_resources()
this->smaa_weight_tx.release();
}
static void gpencil_draw_mask(GPENCIL_Data *vedata,
static void gpencil_draw_mask(Instance *inst,
blender::draw::View &view,
GPENCIL_tObject *ob,
GPENCIL_tLayer *layer)
{
GPENCIL_Instance *inst = vedata->instance;
blender::draw::Manager *manager = DRW_manager_get();
const float clear_col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
@@ -759,11 +744,8 @@ static void gpencil_draw_mask(GPENCIL_Data *vedata,
GPU_debug_group_end();
}
static void GPENCIL_draw_object(GPENCIL_Data *vedata,
blender::draw::View &view,
GPENCIL_tObject *ob)
static void GPENCIL_draw_object(Instance *inst, blender::draw::View &view, GPENCIL_tObject *ob)
{
GPENCIL_Instance *inst = vedata->instance;
blender::draw::Manager *manager = DRW_manager_get();
const float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}};
@@ -781,7 +763,7 @@ static void GPENCIL_draw_object(GPENCIL_Data *vedata,
LISTBASE_FOREACH (GPENCIL_tLayer *, layer, &ob->layers) {
if (layer->mask_bits) {
gpencil_draw_mask(vedata, view, ob, layer);
gpencil_draw_mask(inst, view, ob, layer);
}
if (layer->blend_ps) {
@@ -816,10 +798,8 @@ static void GPENCIL_draw_object(GPENCIL_Data *vedata,
GPU_debug_group_end();
}
static void GPENCIL_fast_draw_start(GPENCIL_Data *vedata)
static void GPENCIL_fast_draw_start(Instance *inst)
{
GPENCIL_Instance *inst = vedata->instance;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (!inst->snapshot_buffer_dirty) {
@@ -832,10 +812,8 @@ static void GPENCIL_fast_draw_start(GPENCIL_Data *vedata)
}
}
static void GPENCIL_fast_draw_end(GPENCIL_Data *vedata, blender::draw::View &view)
static void GPENCIL_fast_draw_end(Instance *inst, blender::draw::View &view)
{
GPENCIL_Instance *inst = vedata->instance;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (inst->snapshot_buffer_dirty) {
@@ -847,109 +825,92 @@ static void GPENCIL_fast_draw_end(GPENCIL_Data *vedata, blender::draw::View &vie
}
/* Draw the sbuffer stroke(s). */
LISTBASE_FOREACH (GPENCIL_tObject *, ob, &inst->sbuffer_tobjects) {
GPENCIL_draw_object(vedata, view, ob);
GPENCIL_draw_object(inst, view, ob);
}
}
void GPENCIL_draw_scene(void *ved)
void Instance::draw(Manager & /*manager*/)
{
using namespace blender::draw;
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
GPENCIL_Instance &inst = *vedata->instance;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (inst.render_depth_tx.is_valid()) {
inst.scene_depth_tx = inst.render_depth_tx;
inst.scene_fb = inst.render_fb;
if (this->render_depth_tx.is_valid()) {
this->scene_depth_tx = this->render_depth_tx;
this->scene_fb = this->render_fb;
}
else {
inst.scene_fb = dfbl->default_fb;
inst.scene_depth_tx = dtxl->depth;
this->scene_fb = dfbl->default_fb;
this->scene_depth_tx = dtxl->depth;
}
BLI_assert(inst.scene_depth_tx);
BLI_assert(this->scene_depth_tx);
float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}};
/* Fade 3D objects. */
if ((!inst.is_render) && (inst.fade_3d_object_opacity > -1.0f) && (inst.obact != nullptr) &&
ELEM(inst.obact->type, OB_GREASE_PENCIL))
if ((!this->is_render) && (this->fade_3d_object_opacity > -1.0f) && (this->obact != nullptr) &&
ELEM(this->obact->type, OB_GREASE_PENCIL))
{
float background_color[3];
ED_view3d_background_color_get(inst.scene, inst.v3d, background_color);
ED_view3d_background_color_get(this->scene, this->v3d, background_color);
/* Blend color. */
interp_v3_v3v3(clear_cols[0], background_color, clear_cols[0], inst.fade_3d_object_opacity);
interp_v3_v3v3(clear_cols[0], background_color, clear_cols[0], this->fade_3d_object_opacity);
mul_v4_fl(clear_cols[1], inst.fade_3d_object_opacity);
mul_v4_fl(clear_cols[1], this->fade_3d_object_opacity);
}
/* Sort object by decreasing Z to avoid most of alpha ordering issues. */
gpencil_object_cache_sort(&inst);
gpencil_object_cache_sort(this);
if (inst.tobjects.first == nullptr) {
if (this->tobjects.first == nullptr) {
return;
}
DRW_submission_start();
GPENCIL_antialiasing_init(&inst);
GPENCIL_antialiasing_init(this);
inst.acquire_resources();
this->acquire_resources();
if (inst.do_fast_drawing) {
GPENCIL_fast_draw_start(vedata);
if (this->do_fast_drawing) {
GPENCIL_fast_draw_start(this);
}
if (inst.tobjects.first) {
GPU_framebuffer_bind(inst.gpencil_fb);
GPU_framebuffer_multi_clear(inst.gpencil_fb, clear_cols);
if (this->tobjects.first) {
GPU_framebuffer_bind(this->gpencil_fb);
GPU_framebuffer_multi_clear(this->gpencil_fb, clear_cols);
}
blender::draw::View &view = blender::draw::View::default_get();
LISTBASE_FOREACH (GPENCIL_tObject *, ob, &inst.tobjects) {
GPENCIL_draw_object(vedata, view, ob);
LISTBASE_FOREACH (GPENCIL_tObject *, ob, &this->tobjects) {
GPENCIL_draw_object(this, view, ob);
}
if (inst.do_fast_drawing) {
GPENCIL_fast_draw_end(vedata, view);
if (this->do_fast_drawing) {
GPENCIL_fast_draw_end(this, view);
}
if (inst.scene_fb) {
GPENCIL_antialiasing_draw(vedata);
if (this->scene_fb) {
GPENCIL_antialiasing_draw(this);
}
inst.gp_object_pool = inst.gp_maskbit_pool = nullptr;
inst.gp_vfx_pool = nullptr;
inst.gp_layer_pool = nullptr;
this->gp_object_pool = this->gp_maskbit_pool = nullptr;
this->gp_vfx_pool = nullptr;
this->gp_layer_pool = nullptr;
inst.release_resources();
this->release_resources();
DRW_submission_end();
}
static void GPENCIL_engine_free()
DrawEngine *Engine::create_instance()
{
GPENCIL_shader_free();
return new Instance();
}
static void GPENCIL_instance_free(void *instance)
void Engine::free_static()
{
delete reinterpret_cast<GPENCIL_Instance *>(instance);
ShaderCache::release();
}
DrawEngineType draw_engine_gpencil_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("GpencilMode"),
/*engine_init*/ &GPENCIL_engine_init,
/*engine_free*/ &GPENCIL_engine_free,
/*instance_free*/ &GPENCIL_instance_free,
/*cache_init*/ &GPENCIL_cache_init,
/*cache_populate*/ &GPENCIL_cache_populate,
/*cache_finish*/ &GPENCIL_cache_finish,
/*draw_scene*/ &GPENCIL_draw_scene,
/*render_to_image*/ &GPENCIL_render_to_image,
/*store_metadata*/ nullptr,
};
} // namespace blender::draw::gpencil

View File

@@ -8,6 +8,7 @@
#pragma once
#include "BLI_memblock.h"
#include "DRW_render.hh"
#include "BLI_bitmap.h"
@@ -22,10 +23,9 @@
#define GP_LIGHT
#include "gpencil_defines.h"
#include "gpencil_shader.hh"
#include "gpencil_shader_shared.h"
extern DrawEngineType draw_engine_gpencil_type;
struct GPENCIL_Data;
struct GPENCIL_StorageList;
namespace blender::gpu {
@@ -45,13 +45,13 @@ struct View3D;
#define GP_MAX_MASKBITS 256
namespace blender::draw::gpencil {
using namespace blender::draw;
struct GPENCIL_tVfx;
struct GPENCIL_tLayer;
using PassSimple = blender::draw::PassSimple;
using Texture = blender::draw::Texture;
using TextureFromPool = blender::draw::TextureFromPool;
using Framebuffer = blender::draw::Framebuffer;
/* NOTE: These do not preserve the PassSimple memory across frames.
* If that becomes a bottleneck, these containers can be improved. */
using GPENCIL_tVfx_Pool = blender::draw::detail::SubPassVector<GPENCIL_tVfx>;
@@ -81,21 +81,6 @@ typedef struct GPENCIL_LightPool {
int light_used;
} GPENCIL_LightPool;
struct GPENCIL_ViewLayerData {
/* GPENCIL_tObject */
struct BLI_memblock *gp_object_pool;
/* GPENCIL_tLayer */
GPENCIL_tLayer_Pool *gp_layer_pool;
/* GPENCIL_tVfx */
GPENCIL_tVfx_Pool *gp_vfx_pool;
/* GPENCIL_MaterialPool */
struct BLI_memblock *gp_material_pool;
/* GPENCIL_LightPool */
struct BLI_memblock *gp_light_pool;
/* BLI_bitmap */
struct BLI_memblock *gp_maskbit_pool;
};
/* *********** GPencil *********** */
struct GPENCIL_tVfx {
@@ -148,12 +133,46 @@ typedef struct GPENCIL_tObject {
} GPENCIL_tObject;
/* *********** LISTS *********** */
typedef struct GPENCIL_StorageList {
struct GPENCIL_Instance *inst;
} GPENCIL_StorageList;
struct ViewLayerData {
/* GPENCIL_tObject */
struct BLI_memblock *gp_object_pool = BLI_memblock_create(sizeof(GPENCIL_tObject));
/* GPENCIL_tLayer */
GPENCIL_tLayer_Pool *gp_layer_pool = new GPENCIL_tLayer_Pool();
/* GPENCIL_tVfx */
GPENCIL_tVfx_Pool *gp_vfx_pool = new GPENCIL_tVfx_Pool();
/* GPENCIL_MaterialPool */
struct BLI_memblock *gp_material_pool = BLI_memblock_create(sizeof(GPENCIL_MaterialPool));
/* GPENCIL_LightPool */
struct BLI_memblock *gp_light_pool = BLI_memblock_create(sizeof(GPENCIL_LightPool));
/* BLI_bitmap */
struct BLI_memblock *gp_maskbit_pool = BLI_memblock_create(BLI_BITMAP_SIZE(GP_MAX_MASKBITS));
struct GPENCIL_Instance {
~ViewLayerData()
{
BLI_memblock_destroy(gp_light_pool, light_pool_free);
BLI_memblock_destroy(gp_material_pool, material_pool_free);
BLI_memblock_destroy(gp_maskbit_pool, nullptr);
BLI_memblock_destroy(gp_object_pool, nullptr);
delete gp_layer_pool;
delete gp_vfx_pool;
}
static void material_pool_free(void *storage)
{
GPENCIL_MaterialPool *matpool = (GPENCIL_MaterialPool *)storage;
GPU_UBO_FREE_SAFE(matpool->ubo);
}
static void light_pool_free(void *storage)
{
GPENCIL_LightPool *lightpool = (GPENCIL_LightPool *)storage;
GPU_UBO_FREE_SAFE(lightpool->ubo);
}
};
/* *********** LISTS *********** */
struct Instance : public DrawEngine {
PassSimple smaa_edge_ps = {"smaa_edge"};
PassSimple smaa_weight_ps = {"smaa_weight"};
PassSimple smaa_resolve_ps = {"smaa_resolve"};
@@ -204,7 +223,9 @@ struct GPENCIL_Instance {
Framebuffer smaa_edge_fb = {"smaa_edge_fb"};
Framebuffer smaa_weight_fb = {"smaa_weight_fb"};
/* Pointers copied from GPENCIL_ViewLayerData. */
ViewLayerData vldata;
/* Pointers copied from blender::draw::gpencil::ViewLayerData. */
struct BLI_memblock *gp_object_pool;
GPENCIL_tLayer_Pool *gp_layer_pool;
GPENCIL_tVfx_Pool *gp_vfx_pool;
@@ -310,11 +331,24 @@ struct GPENCIL_Instance {
void acquire_resources();
void release_resources();
blender::StringRefNull name_get() final
{
return "Grease Pencil";
}
void init() final;
void begin_sync() final;
void object_sync(blender::draw::ObjectRef &ob_ref, blender::draw::Manager &manager) final;
void end_sync() final;
void draw(blender::draw::Manager &manager) final;
};
struct GPENCIL_Data {
void *engine_type; /* Required */
struct GPENCIL_Instance *instance;
struct Instance *instance;
char info[GPU_INFO_SIZE];
};
@@ -322,17 +356,17 @@ struct GPENCIL_Data {
/* geometry batch cache functions */
struct GpencilBatchCache *gpencil_batch_cache_get(struct Object *ob, int cfra);
GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_Instance *inst,
GPENCIL_tObject *gpencil_object_cache_add(Instance *inst,
Object *ob,
bool is_stroke_order_3d,
blender::Bounds<float3> bounds);
void gpencil_object_cache_sort(GPENCIL_Instance *inst);
void gpencil_object_cache_sort(Instance *inst);
GPENCIL_tLayer *grease_pencil_layer_cache_get(GPENCIL_tObject *tgp_ob,
int layer_id,
bool skip_onion);
GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_Instance *inst,
GPENCIL_tLayer *grease_pencil_layer_cache_add(Instance *inst,
const Object *ob,
const blender::bke::greasepencil::Layer &layer,
int onion_id,
@@ -343,7 +377,7 @@ GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_Instance *inst,
* We merge the material pools together if object does not contain a huge amount of materials.
* Also return an offset to the first material of the object in the UBO.
*/
GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_Instance *inst,
GPENCIL_MaterialPool *gpencil_material_pool_create(Instance *inst,
Object *ob,
int *ofs,
bool is_vertex_mode);
@@ -355,62 +389,22 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool,
void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]);
void gpencil_light_pool_populate(GPENCIL_LightPool *lightpool, Object *ob);
GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_Instance *inst);
GPENCIL_LightPool *gpencil_light_pool_add(Instance *inst);
/**
* Creates a single pool containing all lights assigned (light linked) for a given object.
*/
GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_Instance *inst, Object *ob);
GPENCIL_LightPool *gpencil_light_pool_create(Instance *inst, Object *ob);
/* effects */
void gpencil_vfx_cache_populate(GPENCIL_Data *vedata,
void gpencil_vfx_cache_populate(Instance *inst,
Object *ob,
GPENCIL_tObject *tgp_ob,
const bool is_edit_mode);
/* Shaders */
struct GPUShader *GPENCIL_shader_antialiasing(int stage);
struct GPUShader *GPENCIL_shader_geometry_get(void);
struct GPUShader *GPENCIL_shader_layer_blend_get(void);
struct GPUShader *GPENCIL_shader_mask_invert_get(void);
struct GPUShader *GPENCIL_shader_depth_merge_get(void);
struct GPUShader *GPENCIL_shader_fx_blur_get(void);
struct GPUShader *GPENCIL_shader_fx_colorize_get(void);
struct GPUShader *GPENCIL_shader_fx_composite_get(void);
struct GPUShader *GPENCIL_shader_fx_transform_get(void);
struct GPUShader *GPENCIL_shader_fx_glow_get(void);
struct GPUShader *GPENCIL_shader_fx_pixelize_get(void);
struct GPUShader *GPENCIL_shader_fx_rim_get(void);
struct GPUShader *GPENCIL_shader_fx_shadow_get(void);
void GPENCIL_shader_free(void);
/* Antialiasing */
void GPENCIL_antialiasing_init(GPENCIL_Instance *inst);
void GPENCIL_antialiasing_draw(struct GPENCIL_Data *vedata);
/* main functions */
void GPENCIL_engine_init(void *vedata);
void GPENCIL_cache_init(void *vedata);
void GPENCIL_cache_populate(void *vedata, blender::draw::ObjectRef &ob_ref);
void GPENCIL_cache_finish(void *vedata);
void GPENCIL_draw_scene(void *vedata);
void GPENCIL_antialiasing_init(Instance *inst);
void GPENCIL_antialiasing_draw(Instance *inst);
/* render */
/**
* Initialize render data.
*/
void GPENCIL_render_init(struct GPENCIL_Data *ved,
struct RenderEngine *engine,
struct RenderLayer *render_layer,
const struct Depsgraph *depsgraph,
const rcti *rect);
void GPENCIL_render_to_image(void *vedata,
struct RenderEngine *engine,
struct RenderLayer *render_layer,
const rcti *rect);
/* Draw Data. */
void gpencil_light_pool_free(void *storage);
void gpencil_material_pool_free(void *storage);
GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void);
} // namespace blender::draw::gpencil

View File

@@ -19,19 +19,16 @@
#include "IMB_imbuf_types.hh"
#include "gpencil_engine.h"
#include "gpencil_engine_private.hh"
void GPENCIL_render_init(GPENCIL_Data *vedata,
RenderEngine *engine,
RenderLayer *render_layer,
const Depsgraph *depsgraph,
const rcti *rect)
namespace blender::draw::gpencil {
static void render_init(Instance &inst,
RenderEngine *engine,
RenderLayer *render_layer,
const Depsgraph *depsgraph,
const rcti *rect)
{
if (vedata->instance == nullptr) {
vedata->instance = new GPENCIL_Instance();
}
GPENCIL_Instance &inst = *vedata->instance;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
const int2 size = int2(DRW_viewport_size_get());
@@ -140,25 +137,10 @@ void GPENCIL_render_init(GPENCIL_Data *vedata,
MEM_SAFE_FREE(pix_z);
}
/* render all objects and select only grease pencil */
static void GPENCIL_render_cache(void *vedata,
blender::draw::ObjectRef &ob_ref,
RenderEngine * /*engine*/,
Depsgraph * /*depsgraph*/)
{
if (!ELEM(ob_ref.object->type, OB_GREASE_PENCIL, OB_LAMP)) {
return;
}
if (!(DRW_object_visibility_in_active_context(ob_ref.object) & OB_VISIBLE_SELF)) {
return;
}
GPENCIL_cache_populate(vedata, ob_ref);
}
static void GPENCIL_render_result_z(RenderLayer *rl,
const char *viewname,
GPENCIL_Data *vedata,
const rcti *rect)
static void render_result_z(RenderLayer *rl,
const char *viewname,
Instance &instance,
const rcti *rect)
{
const DRWContext *draw_ctx = DRW_context_get();
ViewLayer *view_layer = draw_ctx->view_layer;
@@ -172,7 +154,7 @@ static void GPENCIL_render_result_z(RenderLayer *rl,
float *ro_buffer_data = rp->ibuf->float_buffer.data;
GPU_framebuffer_read_depth(vedata->instance->render_fb,
GPU_framebuffer_read_depth(instance.render_fb,
rect->xmin,
rect->ymin,
BLI_rcti_size_x(rect),
@@ -213,15 +195,15 @@ static void GPENCIL_render_result_z(RenderLayer *rl,
}
}
static void GPENCIL_render_result_combined(RenderLayer *rl,
const char *viewname,
GPENCIL_Data *vedata,
const rcti *rect)
static void render_result_combined(RenderLayer *rl,
const char *viewname,
Instance &instance,
const rcti *rect)
{
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname);
GPU_framebuffer_bind(vedata->instance->render_fb);
GPU_framebuffer_read_color(vedata->instance->render_fb,
GPU_framebuffer_bind(instance.render_fb);
GPU_framebuffer_read_color(instance.render_fb,
rect->xmin,
rect->ymin,
BLI_rcti_size_x(rect),
@@ -232,33 +214,45 @@ static void GPENCIL_render_result_combined(RenderLayer *rl,
rp->ibuf->float_buffer.data);
}
void GPENCIL_render_to_image(void *ved,
RenderEngine *engine,
RenderLayer *render_layer,
const rcti *rect)
void Engine::render_to_image(RenderEngine *engine, RenderLayer *render_layer, const rcti rect)
{
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
const char *viewname = RE_GetActiveRenderView(engine->re);
const DRWContext *draw_ctx = DRW_context_get();
Depsgraph *depsgraph = draw_ctx->depsgraph;
DRW_manager_get()->begin_sync();
blender::draw::gpencil::Instance inst;
GPENCIL_render_init(vedata, engine, render_layer, depsgraph, rect);
GPENCIL_engine_init(vedata);
Manager &manager = *DRW_manager_get();
vedata->instance->camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
render_init(inst, engine, render_layer, depsgraph, &rect);
inst.init();
inst.camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
manager.begin_sync();
/* Loop over all objects and create draw structure. */
GPENCIL_cache_init(vedata);
DRW_render_object_iter(vedata, engine, depsgraph, GPENCIL_render_cache);
GPENCIL_cache_finish(vedata);
inst.begin_sync();
DRW_render_object_iter(
engine, depsgraph, [&](blender::draw::ObjectRef &ob_ref, RenderEngine *, Depsgraph *) {
if (!ELEM(ob_ref.object->type, OB_GREASE_PENCIL, OB_LAMP)) {
return;
}
if (!(DRW_object_visibility_in_active_context(ob_ref.object) & OB_VISIBLE_SELF)) {
return;
}
inst.object_sync(ob_ref, manager);
});
inst.end_sync();
DRW_manager_get()->end_sync();
manager.end_sync();
/* Render the gpencil object and merge the result to the underlying render. */
GPENCIL_draw_scene(vedata);
inst.draw(manager);
GPENCIL_render_result_combined(render_layer, viewname, vedata, rect);
GPENCIL_render_result_z(render_layer, viewname, vedata, rect);
render_result_combined(render_layer, viewname, inst, &rect);
render_result_z(render_layer, viewname, inst, &rect);
}
} // namespace blender::draw::gpencil

View File

@@ -5,11 +5,10 @@
/** \file
* \ingroup draw
*/
#include "DRW_render.hh"
#include "BLI_string.h"
#pragma once
#include "gpencil_engine.h"
#include "GPU_shader.hh"
namespace blender::draw::gpencil {
@@ -57,78 +56,3 @@ class ShaderCache {
};
} // namespace blender::draw::gpencil
using namespace blender::draw::gpencil;
void GPENCIL_shader_free()
{
ShaderCache::get().release();
}
GPUShader *GPENCIL_shader_antialiasing(int stage)
{
BLI_assert(stage < 3);
return ShaderCache::get().antialiasing[stage].get();
}
GPUShader *GPENCIL_shader_geometry_get()
{
return ShaderCache::get().geometry.get();
}
GPUShader *GPENCIL_shader_layer_blend_get()
{
return ShaderCache::get().layer_blend.get();
}
GPUShader *GPENCIL_shader_mask_invert_get()
{
return ShaderCache::get().mask_invert.get();
}
GPUShader *GPENCIL_shader_depth_merge_get()
{
return ShaderCache::get().depth_merge.get();
}
/* ------- FX Shaders --------- */
GPUShader *GPENCIL_shader_fx_blur_get()
{
return ShaderCache::get().fx_blur.get();
}
GPUShader *GPENCIL_shader_fx_colorize_get()
{
return ShaderCache::get().fx_colorize.get();
}
GPUShader *GPENCIL_shader_fx_composite_get()
{
return ShaderCache::get().fx_composite.get();
}
GPUShader *GPENCIL_shader_fx_glow_get()
{
return ShaderCache::get().fx_glow.get();
}
GPUShader *GPENCIL_shader_fx_pixelize_get()
{
return ShaderCache::get().fx_pixelize.get();
}
GPUShader *GPENCIL_shader_fx_rim_get()
{
return ShaderCache::get().fx_rim.get();
}
GPUShader *GPENCIL_shader_fx_shadow_get()
{
return ShaderCache::get().fx_shadow.get();
}
GPUShader *GPENCIL_shader_fx_transform_get()
{
return ShaderCache::get().fx_transform.get();
}

View File

@@ -20,7 +20,9 @@
#include "BKE_camera.h"
#include "gpencil_engine.h"
#include "gpencil_engine_private.hh"
namespace blender::draw::gpencil {
using namespace blender::draw;
@@ -45,7 +47,7 @@ static bool effect_is_active(ShaderFxData *fx, bool is_edit, bool is_viewport)
}
struct gpIterVfxData {
GPENCIL_Instance *inst;
Instance *inst;
GPENCIL_tObject *tgp_ob;
GPUFrameBuffer **target_fb;
GPUFrameBuffer **source_fb;
@@ -118,7 +120,7 @@ static void gpencil_vfx_blur(BlurShaderFxData *fx, Object *ob, gpIterVfxData *it
mul_v2_fl(blur_size, distance_factor);
}
GPUShader *sh = GPENCIL_shader_fx_blur_get();
GPUShader *sh = ShaderCache::get().fx_blur.get();
DRWState state = DRW_STATE_WRITE_COLOR;
if (blur_size[0] > 0.0f) {
@@ -137,7 +139,7 @@ static void gpencil_vfx_blur(BlurShaderFxData *fx, Object *ob, gpIterVfxData *it
static void gpencil_vfx_colorize(ColorizeShaderFxData *fx, Object * /*ob*/, gpIterVfxData *iter)
{
GPUShader *sh = GPENCIL_shader_fx_colorize_get();
GPUShader *sh = ShaderCache::get().fx_colorize.get();
DRWState state = DRW_STATE_WRITE_COLOR;
auto &grp = gpencil_vfx_pass_create("Fx Colorize", state, iter, sh);
@@ -154,7 +156,7 @@ static void gpencil_vfx_flip(FlipShaderFxData *fx, Object * /*ob*/, gpIterVfxDat
axis_flip[0] = (fx->flag & FX_FLIP_HORIZONTAL) ? -1.0f : 1.0f;
axis_flip[1] = (fx->flag & FX_FLIP_VERTICAL) ? -1.0f : 1.0f;
GPUShader *sh = GPENCIL_shader_fx_transform_get();
GPUShader *sh = ShaderCache::get().fx_transform.get();
DRWState state = DRW_STATE_WRITE_COLOR;
auto &grp = gpencil_vfx_pass_create("Fx Flip", state, iter, sh);
@@ -184,7 +186,7 @@ static void gpencil_vfx_rim(RimShaderFxData *fx, Object *ob, gpIterVfxData *iter
mul_v2_v2(offset, vp_size_inv);
mul_v2_fl(blur_size, distance_factor);
GPUShader *sh = GPENCIL_shader_fx_rim_get();
GPUShader *sh = ShaderCache::get().fx_rim.get();
{
DRWState state = DRW_STATE_WRITE_COLOR;
@@ -267,7 +269,7 @@ static void gpencil_vfx_pixelize(PixelShaderFxData *fx, Object *ob, gpIterVfxDat
/* Center to texel */
madd_v2_v2fl(ob_center, pixel_size, -0.5f);
GPUShader *sh = GPENCIL_shader_fx_pixelize_get();
GPUShader *sh = ShaderCache::get().fx_pixelize.get();
DRWState state = DRW_STATE_WRITE_COLOR;
@@ -374,7 +376,7 @@ static void gpencil_vfx_shadow(ShadowShaderFxData *fx, Object *ob, gpIterVfxData
wave_phase = 0.0f;
}
GPUShader *sh = GPENCIL_shader_fx_shadow_get();
GPUShader *sh = ShaderCache::get().fx_shadow.get();
copy_v2_fl2(blur_dir, blur_size[0] * vp_size_inv[0], 0.0f);
@@ -422,7 +424,7 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object * /*ob*/, gpIterVfxDat
const float s = sin(fx->rotation);
const float c = cos(fx->rotation);
GPUShader *sh = GPENCIL_shader_fx_glow_get();
GPUShader *sh = ShaderCache::get().fx_glow.get();
float ref_col[4];
@@ -528,7 +530,7 @@ static void gpencil_vfx_wave(WaveShaderFxData *fx, Object *ob, gpIterVfxData *it
/* Phase start at shadow center. */
wave_phase = fx->phase - dot_v2v2(wave_center, wave_dir);
GPUShader *sh = GPENCIL_shader_fx_transform_get();
GPUShader *sh = ShaderCache::get().fx_transform.get();
DRWState state = DRW_STATE_WRITE_COLOR;
auto &grp = gpencil_vfx_pass_create("Fx Wave", state, iter, sh);
@@ -572,7 +574,7 @@ static void gpencil_vfx_swirl(SwirlShaderFxData *fx, Object * /*ob*/, gpIterVfxD
return;
}
GPUShader *sh = GPENCIL_shader_fx_transform_get();
GPUShader *sh = ShaderCache::get().fx_transform.get();
DRWState state = DRW_STATE_WRITE_COLOR;
auto &grp = gpencil_vfx_pass_create("Fx Flip", state, iter, sh);
@@ -584,13 +586,11 @@ static void gpencil_vfx_swirl(SwirlShaderFxData *fx, Object * /*ob*/, gpIterVfxD
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
void gpencil_vfx_cache_populate(GPENCIL_Data *vedata,
void gpencil_vfx_cache_populate(Instance *inst,
Object *ob,
GPENCIL_tObject *tgp_ob,
const bool is_edit_mode)
{
GPENCIL_Instance *inst = vedata->instance;
/* These may not be allocated yet, use address of future pointer. */
gpIterVfxData iter{};
iter.inst = inst;
@@ -645,7 +645,7 @@ void gpencil_vfx_cache_populate(GPENCIL_Data *vedata,
/* We need an extra pass to combine result to main buffer. */
iter.target_fb = &inst->gpencil_fb;
GPUShader *sh = GPENCIL_shader_fx_composite_get();
GPUShader *sh = ShaderCache::get().fx_composite.get();
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL;
auto &grp = gpencil_vfx_pass_create("GPencil Object Compose", state, &iter, sh);
@@ -662,3 +662,5 @@ void gpencil_vfx_cache_populate(GPENCIL_Data *vedata,
inst->use_layer_fb = true;
}
}
} // namespace blender::draw::gpencil

View File

@@ -8,96 +8,20 @@
* Draw engine to draw the Image/UV editor
*/
#include "DRW_render.hh"
#include "BLT_translation.hh"
#include "BKE_context.hh"
#include "BKE_image.hh"
#include "BKE_main.hh"
#include "BKE_object.hh"
#include "ED_image.hh"
#include "draw_view_data.hh"
#include "image_drawing_mode.hh"
#include "image_engine.h"
#include "image_instance.hh"
#include "image_shader.hh"
namespace blender::image_engine {
struct IMAGE_Data {
void *engine_type;
Instance *instance;
char info[GPU_INFO_SIZE];
};
/* -------------------------------------------------------------------- */
/** \name Engine Callbacks
* \{ */
static void IMAGE_engine_init(void *vedata)
DrawEngine *Engine::create_instance()
{
IMAGE_Data *ved = reinterpret_cast<IMAGE_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new image_engine::Instance();
}
const DRWContext *ctx_state = DRW_context_get();
Main *bmain = CTX_data_main(ctx_state->evil_C);
ved->instance->init(bmain, ctx_state->space_data, ctx_state->region);
return new Instance();
}
static void IMAGE_cache_init(void *vedata)
{
IMAGE_Data *ved = reinterpret_cast<IMAGE_Data *>(vedata);
ved->instance->begin_sync();
ved->instance->image_sync();
}
static void IMAGE_cache_populate(void * /*vedata*/, blender::draw::ObjectRef & /*ob_ref*/)
{
/* Function intentional left empty. `cache_populate` is required to be implemented. */
}
static void IMAGE_draw_scene(void *vedata)
{
IMAGE_Data *ved = reinterpret_cast<IMAGE_Data *>(vedata);
DRW_submission_start();
ved->instance->draw_viewport();
ved->instance->draw_finish();
DRW_submission_end();
}
static void IMAGE_engine_free()
void Engine::free_static()
{
ShaderModule::module_free();
}
static void IMAGE_instance_free(void *instance)
{
delete reinterpret_cast<image_engine::Instance *>(instance);
}
/** \} */
} // namespace blender::image_engine
using namespace blender::image_engine;
DrawEngineType draw_engine_image_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("UV/Image"),
/*engine_init*/ &IMAGE_engine_init,
/*engine_free*/ &IMAGE_engine_free,
/*instance_free*/ &IMAGE_instance_free,
/*cache_init*/ &IMAGE_cache_init,
/*cache_populate*/ &IMAGE_cache_populate,
/*cache_finish*/ nullptr,
/*draw_scene*/ &IMAGE_draw_scene,
/*render_to_image*/ nullptr,
/*store_metadata*/ nullptr,
};

View File

@@ -8,6 +8,14 @@
#pragma once
struct DrawEngineType;
#include "DRW_render.hh"
extern DrawEngineType draw_engine_image_type;
namespace blender::image_engine {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
static void free_static();
};
} // namespace blender::image_engine

View File

@@ -6,6 +6,10 @@
#include <DRW_render.hh>
#include "BKE_context.hh"
#include "DRW_engine.hh"
#include "image_drawing_mode.hh"
#include "image_private.hh"
#include "image_space.hh"
@@ -33,7 +37,7 @@ static inline std::unique_ptr<AbstractSpaceAccessor> space_accessor_from_space(
return nullptr;
}
class Instance {
class Instance : public DrawEngine {
private:
std::unique_ptr<AbstractSpaceAccessor> space_;
Main *main_;
@@ -48,17 +52,23 @@ class Instance {
public:
Instance() : drawing_mode_(*this) {}
void init(Main *main, SpaceLink *space_link, const ARegion *_region)
virtual ~Instance() = default;
StringRefNull name_get() final
{
main_ = main;
region = _region;
space_ = space_accessor_from_space(space_link);
return "UV/Image";
}
void init() final
{
const DRWContext *ctx_state = DRW_context_get();
main_ = CTX_data_main(ctx_state->evil_C);
region = ctx_state->region;
space_ = space_accessor_from_space(ctx_state->space_data);
manager = DRW_manager_get();
}
virtual ~Instance() = default;
void begin_sync()
void begin_sync() final
{
drawing_mode_.begin_sync();
@@ -68,6 +78,8 @@ class Instance {
float4x4 winmat = float4x4::identity();
state.view.sync(viewmat, winmat);
state.flags.do_tile_drawing = false;
image_sync();
}
void image_sync()
@@ -102,15 +114,17 @@ class Instance {
drawing_mode_.image_sync(state.image, iuser);
}
void draw_finish()
void object_sync(ObjectRef & /*obref*/, Manager & /*manager*/) final {}
void end_sync() final {}
void draw(Manager & /*manager */) final
{
DRW_submission_start();
drawing_mode_.draw_viewport();
drawing_mode_.draw_finish();
state.image = nullptr;
}
void draw_viewport()
{
drawing_mode_.draw_viewport();
DRW_submission_end();
}
};
} // namespace blender::image_engine

View File

@@ -8,6 +8,14 @@
#pragma once
struct DrawEngineType;
#include "DRW_render.hh"
extern DrawEngineType draw_engine_overlay_next_type;
namespace blender::draw::overlay {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
static void free_static();
};
} // namespace blender::draw::overlay

View File

@@ -6,6 +6,8 @@
* \ingroup draw_engine
*/
#pragma once
#include "BLI_math_quaternion_types.hh"
#include "BLI_string.h"

View File

@@ -8,89 +8,20 @@
* Engine for drawing a selection map where the pixels indicate the selection indices.
*/
#include "DRW_engine.hh"
#include "DRW_render.hh"
#include "BLT_translation.hh"
#include "draw_manager.hh"
#include "overlay_next_instance.hh"
#include "overlay_engine.h"
#include "overlay_next_private.hh"
using namespace blender::draw;
namespace blender::draw::overlay {
using Instance = blender::draw::overlay::Instance;
/* -------------------------------------------------------------------- */
/** \name Engine Instance
* \{ */
static void OVERLAY_next_engine_init(void *vedata)
DrawEngine *Engine::create_instance()
{
OVERLAY_Data *ved = reinterpret_cast<OVERLAY_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new Instance(select::SelectionType::DISABLED);
}
reinterpret_cast<Instance *>(ved->instance)->init();
return new Instance();
}
static void OVERLAY_next_cache_init(void *vedata)
void Engine::free_static()
{
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)->begin_sync();
ShaderModule::module_free();
}
static void OVERLAY_next_cache_populate(void *vedata, blender::draw::ObjectRef &ob_ref)
{
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)
->object_sync(ob_ref, *DRW_manager_get());
}
static void OVERLAY_next_cache_finish(void *vedata)
{
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)->end_sync();
}
static void OVERLAY_next_draw_scene(void *vedata)
{
DRW_submission_start();
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)
->draw(*DRW_manager_get());
DRW_submission_end();
}
static void OVERLAY_next_instance_free(void *instance_)
{
Instance *instance = (Instance *)instance_;
delete instance;
}
static void OVERLAY_next_engine_free()
{
overlay::ShaderModule::module_free();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Type
* \{ */
DrawEngineType draw_engine_overlay_next_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Overlay"),
/*engine_init*/ &OVERLAY_next_engine_init,
/*engine_free*/ &OVERLAY_next_engine_free,
/*instance_free*/ &OVERLAY_next_instance_free,
/*cache_init*/ &OVERLAY_next_cache_init,
/*cache_populate*/ &OVERLAY_next_cache_populate,
/*cache_finish*/ &OVERLAY_next_cache_finish,
/*draw_scene*/ &OVERLAY_next_draw_scene,
/*render_to_image*/ nullptr,
/*store_metadata*/ nullptr,
};
/** \} */
} // namespace blender::draw::overlay

View File

@@ -54,14 +54,11 @@ namespace blender::draw::overlay {
* Selection engine reuse most of the Overlay engine by creating selection IDs for each
* selectable component and using a special shaders for drawing.
*/
class Instance {
class Instance : public DrawEngine {
const SelectionType selection_type_;
bool clipping_enabled_;
public:
/* WORKAROUND: Legacy. Move to grid pass. */
GPUUniformBuf *grid_ubo = nullptr;
ShapeCache shapes;
/** Global types. */
@@ -115,18 +112,19 @@ class Instance {
AntiAliasing anti_aliasing;
XrayFade xray_fade;
Instance() : selection_type_(select::SelectionType::DISABLED){};
Instance(const SelectionType selection_type) : selection_type_(selection_type){};
~Instance()
blender::StringRefNull name_get() final
{
GPU_UBO_FREE_SAFE(grid_ubo);
return "Overlay";
}
void init();
void begin_sync();
void object_sync(ObjectRef &ob_ref, Manager &manager);
void end_sync();
void draw(Manager &manager);
void init() final;
void begin_sync() final;
void object_sync(ObjectRef &ob_ref, Manager &manager) final;
void end_sync() final;
void draw(Manager &manager) final;
private:
bool object_is_selected(const ObjectRef &ob_ref);

View File

@@ -20,21 +20,15 @@
#include "select_engine.hh"
#define SELECT_DEBUG_ENGINE "SELECT_DEBUG_ENGINE"
/* -------------------------------------------------------------------- */
/** \name Structs and static variables
* \{ */
struct SELECTIDDEBUG_Data {
void *engine_type;
};
namespace blender::draw::SelectDebug {
using StaticShader = gpu::StaticShader;
namespace blender::draw::edit_select_debug {
class ShaderCache {
using StaticShader = gpu::StaticShader;
private:
static gpu::StaticShaderCache<ShaderCache> &get_static_cache()
{
@@ -55,63 +49,48 @@ class ShaderCache {
StaticShader select_debug = {"select_debug_fullscreen"};
};
} // namespace blender::draw::SelectDebug
using namespace blender::draw::SelectDebug;
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Functions
* \{ */
static void select_debug_draw_scene(void * /*vedata*/)
{
GPUTexture *texture_u32 = DRW_engine_select_texture_get();
if (texture_u32 == nullptr) {
return;
class Instance : public DrawEngine {
StringRefNull name_get() final
{
return "Select ID Debug";
}
using namespace blender::draw;
PassSimple pass = {"SelectEngineDebug"};
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
pass.shader_set(ShaderCache::get().select_debug.get());
pass.bind_texture("image", texture_u32);
pass.bind_texture("image", texture_u32);
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
void init() final{};
void begin_sync() final{};
void object_sync(blender::draw::ObjectRef & /*ob_ref*/,
blender::draw::Manager & /*manager*/) final{};
void end_sync() final{};
DRW_submission_start();
DRW_manager_get()->submit(pass);
DRW_submission_end();
void draw(blender::draw::Manager &manager) final
{
GPUTexture *texture_u32 = DRW_engine_select_texture_get();
if (texture_u32 == nullptr) {
return;
}
using namespace blender::draw;
PassSimple pass = {"SelectEngineDebug"};
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
pass.shader_set(ShaderCache::get().select_debug.get());
pass.bind_texture("image", texture_u32);
pass.bind_texture("image", texture_u32);
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
DRW_submission_start();
manager.submit(pass);
DRW_submission_end();
}
};
DrawEngine *Engine::create_instance()
{
return new Instance();
}
static void select_debug_engine_free()
void Engine::free_static()
{
ShaderCache::release();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Type
* \{ */
DrawEngineType draw_engine_debug_select_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Select ID Debug"),
/*engine_init*/ nullptr,
/*engine_free*/ &select_debug_engine_free,
/*instance_free*/ nullptr,
/*cache_init*/ nullptr,
/*cache_populate*/ nullptr,
/*cache_finish*/ nullptr,
/*draw_scene*/ &select_debug_draw_scene,
/*render_to_image*/ nullptr,
/*store_metadata*/ nullptr,
};
/** \} */
#undef SELECT_DEBUG_ENGINE
} // namespace blender::draw::edit_select_debug

View File

@@ -25,453 +25,465 @@
#include "draw_cache_impl.hh"
#include "draw_common_c.hh"
#include "draw_context_private.hh"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "draw_view_data.hh"
#include "../overlay/overlay_next_private.hh"
#include "select_engine.hh"
#include "select_private.hh"
#define SELECT_ENGINE "SELECT_ENGINE"
namespace blender::draw::edit_select {
/* *********** STATIC *********** */
#define USE_CAGE_OCCLUSION
struct SelectEngineData {
GPUFrameBuffer *framebuffer_select_id;
GPUTexture *texture_u32;
struct Instance : public DrawEngine {
private:
PassSimple depth_only_ps = {"depth_only_ps"};
PassSimple::Sub *depth_only = nullptr;
PassSimple::Sub *depth_occlude = nullptr;
SELECTID_Shaders sh_data[GPU_SHADER_CFG_LEN];
SELECTID_Context context;
};
PassSimple select_edge_ps = {"select_id_edge_ps"};
PassSimple::Sub *select_edge = nullptr;
static SelectEngineData &get_engine_data()
{
static SelectEngineData data = {};
return data;
}
PassSimple select_id_vert_ps = {"select_id_vert_ps"};
PassSimple::Sub *select_vert = nullptr;
/* -------------------------------------------------------------------- */
/** \name Utils
* \{ */
PassSimple select_face_ps = {"select_id_face_ps"};
PassSimple::Sub *select_face_uniform = nullptr;
PassSimple::Sub *select_face_flat = nullptr;
static void select_engine_framebuffer_setup()
{
SelectEngineData &e_data = get_engine_data();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
int size[2];
size[0] = GPU_texture_width(dtxl->depth);
size[1] = GPU_texture_height(dtxl->depth);
View view_faces = {"view_faces"};
View view_edges = {"view_edges"};
View view_verts = {"view_verts"};
if (e_data.framebuffer_select_id == nullptr) {
e_data.framebuffer_select_id = GPU_framebuffer_create("framebuffer_select_id");
}
public:
struct StaticData {
GPUFrameBuffer *framebuffer_select_id;
GPUTexture *texture_u32;
if ((e_data.texture_u32 != nullptr) && ((GPU_texture_width(e_data.texture_u32) != size[0]) ||
(GPU_texture_height(e_data.texture_u32) != size[1])))
{
GPU_texture_free(e_data.texture_u32);
e_data.texture_u32 = nullptr;
}
struct Shaders {
/* Depth Pre Pass */
GPUShader *select_id_flat;
GPUShader *select_id_uniform;
} sh_data[GPU_SHADER_CFG_LEN];
/* Make sure the depth texture is attached.
* It may disappear when loading another Blender session. */
GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, dtxl->depth, 0, 0);
SELECTID_Context context;
if (e_data.texture_u32 == nullptr) {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
e_data.texture_u32 = GPU_texture_create_2d(
"select_buf_ids", size[0], size[1], 1, GPU_R32UI, usage, nullptr);
GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, e_data.texture_u32, 0, 0);
GPU_framebuffer_check_valid(e_data.framebuffer_select_id, nullptr);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Functions
* \{ */
static void select_engine_init(void *vedata)
{
SelectEngineData &e_data = get_engine_data();
const DRWContext *draw_ctx = DRW_context_get();
eGPUShaderConfig sh_cfg = (RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d)) ?
GPU_SHADER_CFG_CLIPPED :
GPU_SHADER_CFG_DEFAULT;
SELECTID_Data *ved = reinterpret_cast<SELECTID_Data *>(vedata);
SELECTID_Shaders *sh_data = &e_data.sh_data[sh_cfg];
if (ved->instance == nullptr) {
ved->instance = new SELECTID_Instance();
}
/* Prepass */
if (!sh_data->select_id_flat) {
sh_data->select_id_flat = GPU_shader_create_from_info_name(
sh_cfg == GPU_SHADER_CFG_CLIPPED ? "select_id_flat_clipped" : "select_id_flat");
}
if (!sh_data->select_id_uniform) {
sh_data->select_id_uniform = GPU_shader_create_from_info_name(
sh_cfg == GPU_SHADER_CFG_CLIPPED ? "select_id_uniform_clipped" : "select_id_uniform");
}
}
static short select_id_get_object_select_mode(Scene *scene, Object *ob)
{
short r_select_mode = 0;
if (ob->mode & (OB_MODE_WEIGHT_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) {
/* In order to sample flat colors for vertex weights / texture-paint / vertex-paint
* we need to be in SCE_SELECT_FACE mode so select_cache_init() correctly sets up
* a shgroup with select_id_flat.
* Note this is not working correctly for vertex-paint (yet), but has been discussed
* in #66645 and there is a solution by @mano-wii in P1032.
* So OB_MODE_VERTEX_PAINT is already included here [required for P1032 I guess]. */
Mesh *me_orig = static_cast<Mesh *>(DEG_get_original_object(ob)->data);
if (me_orig->editflag & ME_EDIT_PAINT_VERT_SEL) {
r_select_mode = SCE_SELECT_VERTEX;
}
else {
r_select_mode = SCE_SELECT_FACE;
}
}
else {
r_select_mode = scene->toolsettings->selectmode;
}
return r_select_mode;
}
static void select_cache_init(void *vedata)
{
SELECTID_Instance &inst = *reinterpret_cast<SELECTID_Data *>(vedata)->instance;
SelectEngineData &e_data = get_engine_data();
const DRWContext *draw_ctx = DRW_context_get();
eGPUShaderConfig sh_cfg = (RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d)) ?
GPU_SHADER_CFG_CLIPPED :
GPU_SHADER_CFG_DEFAULT;
SELECTID_Shaders *sh = &e_data.sh_data[sh_cfg];
if (e_data.context.select_mode == -1) {
e_data.context.select_mode = select_id_get_object_select_mode(draw_ctx->scene,
draw_ctx->obact);
BLI_assert(e_data.context.select_mode != 0);
}
DRWState state = DRW_STATE_DEFAULT;
if (RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d)) {
state |= DRW_STATE_CLIP_PLANES;
}
bool retopology_occlusion = RETOPOLOGY_ENABLED(draw_ctx->v3d) && !XRAY_ENABLED(draw_ctx->v3d);
float retopology_offset = RETOPOLOGY_OFFSET(draw_ctx->v3d);
/* Note there might be less than 6 planes, but we always compute the 6 of them for simplicity. */
int clipping_plane_count = RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d) ? 6 : 0;
{
inst.depth_only_ps.init();
inst.depth_only_ps.state_set(state, clipping_plane_count);
inst.depth_only = nullptr;
inst.depth_occlude = nullptr;
static StaticData &get()
{
auto &sub = inst.depth_only_ps.sub("DepthOnly");
sub.shader_set(sh->select_id_uniform);
sub.push_constant("retopologyOffset", retopology_offset);
sub.push_constant("select_id", 0);
inst.depth_only = &sub;
static StaticData data = {};
return data;
}
if (retopology_occlusion) {
auto &sub = inst.depth_only_ps.sub("Occlusion");
sub.shader_set(sh->select_id_uniform);
sub.push_constant("retopologyOffset", 0.0f);
sub.push_constant("select_id", 0);
inst.depth_occlude = &sub;
};
blender::StringRefNull name_get() final
{
return "SelectID";
}
void init() final
{
StaticData &e_data = StaticData::get();
const DRWContext *draw_ctx = DRW_context_get();
eGPUShaderConfig sh_cfg = (RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d)) ?
GPU_SHADER_CFG_CLIPPED :
GPU_SHADER_CFG_DEFAULT;
StaticData::Shaders *sh_data = &e_data.sh_data[sh_cfg];
/* Prepass */
if (!sh_data->select_id_flat) {
sh_data->select_id_flat = GPU_shader_create_from_info_name(
sh_cfg == GPU_SHADER_CFG_CLIPPED ? "select_id_flat_clipped" : "select_id_flat");
}
if (!sh_data->select_id_uniform) {
sh_data->select_id_uniform = GPU_shader_create_from_info_name(
sh_cfg == GPU_SHADER_CFG_CLIPPED ? "select_id_uniform_clipped" : "select_id_uniform");
}
}
void begin_sync() final
{
StaticData &e_data = StaticData::get();
const DRWContext *draw_ctx = DRW_context_get();
eGPUShaderConfig sh_cfg = (RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d)) ?
GPU_SHADER_CFG_CLIPPED :
GPU_SHADER_CFG_DEFAULT;
StaticData::Shaders *sh = &e_data.sh_data[sh_cfg];
if (e_data.context.select_mode == -1) {
e_data.context.select_mode = get_object_select_mode(draw_ctx->scene, draw_ctx->obact);
BLI_assert(e_data.context.select_mode != 0);
}
inst.select_face_ps.init();
inst.select_face_ps.state_set(state, clipping_plane_count);
inst.select_face_uniform = nullptr;
inst.select_face_flat = nullptr;
if (e_data.context.select_mode & SCE_SELECT_FACE) {
auto &sub = inst.select_face_ps.sub("Face");
sub.shader_set(sh->select_id_flat);
sub.push_constant("retopologyOffset", retopology_offset);
inst.select_face_flat = &sub;
DRWState state = DRW_STATE_DEFAULT;
if (RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d)) {
state |= DRW_STATE_CLIP_PLANES;
}
bool retopology_occlusion = RETOPOLOGY_ENABLED(draw_ctx->v3d) && !XRAY_ENABLED(draw_ctx->v3d);
float retopology_offset = RETOPOLOGY_OFFSET(draw_ctx->v3d);
/* Note there might be less than 6 planes, but we always compute the 6 of them for simplicity.
*/
int clipping_plane_count = RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d) ? 6 : 0;
{
depth_only_ps.init();
depth_only_ps.state_set(state, clipping_plane_count);
depth_only = nullptr;
depth_occlude = nullptr;
{
auto &sub = depth_only_ps.sub("DepthOnly");
sub.shader_set(sh->select_id_uniform);
sub.push_constant("retopologyOffset", retopology_offset);
sub.push_constant("select_id", 0);
depth_only = &sub;
}
if (retopology_occlusion) {
auto &sub = depth_only_ps.sub("Occlusion");
sub.shader_set(sh->select_id_uniform);
sub.push_constant("retopologyOffset", 0.0f);
sub.push_constant("select_id", 0);
depth_occlude = &sub;
}
select_face_ps.init();
select_face_ps.state_set(state, clipping_plane_count);
select_face_uniform = nullptr;
select_face_flat = nullptr;
if (e_data.context.select_mode & SCE_SELECT_FACE) {
auto &sub = select_face_ps.sub("Face");
sub.shader_set(sh->select_id_flat);
sub.push_constant("retopologyOffset", retopology_offset);
select_face_flat = &sub;
}
else {
auto &sub = select_face_ps.sub("FaceNoSelect");
sub.shader_set(sh->select_id_uniform);
sub.push_constant("select_id", 0);
sub.push_constant("retopologyOffset", retopology_offset);
select_face_uniform = &sub;
}
select_edge_ps.init();
select_edge = nullptr;
if (e_data.context.select_mode & SCE_SELECT_EDGE) {
auto &sub = select_edge_ps.sub("Sub");
sub.state_set(state | DRW_STATE_FIRST_VERTEX_CONVENTION, clipping_plane_count);
sub.shader_set(sh->select_id_flat);
sub.push_constant("retopologyOffset", retopology_offset);
select_edge = &sub;
}
select_id_vert_ps.init();
select_vert = nullptr;
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
const float vertex_size = blender::draw::overlay::Resources::vertex_size_get();
auto &sub = select_id_vert_ps.sub("Sub");
sub.state_set(state, clipping_plane_count);
sub.shader_set(sh->select_id_flat);
sub.push_constant("vertex_size", float(2 * vertex_size));
sub.push_constant("retopologyOffset", retopology_offset);
select_vert = &sub;
}
}
e_data.context.elem_ranges.clear();
e_data.context.persmat = float4x4(draw_ctx->rv3d->persmat);
e_data.context.max_index_drawn_len = 1;
framebuffer_setup();
GPU_framebuffer_bind(e_data.framebuffer_select_id);
GPU_framebuffer_clear_color_depth(e_data.framebuffer_select_id, blender::float4{0.0f}, 1.0f);
}
ElemIndexRanges edit_mesh_sync(Object *ob,
ResourceHandle res_handle,
short select_mode,
bool draw_facedot,
const uint initial_index)
{
using namespace blender::draw;
using namespace blender;
Mesh &mesh = *static_cast<Mesh *>(ob->data);
BMEditMesh *em = mesh.runtime->edit_mesh.get();
ElemIndexRanges ranges{};
ranges.total = IndexRange::from_begin_size(initial_index, 0);
BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE | BM_FACE);
if (select_mode & SCE_SELECT_FACE) {
ranges.face = alloc_range(ranges.total, em->bm->totface);
gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
PassSimple::Sub *face_sub = select_face_flat;
face_sub->push_constant("offset", int(ranges.face.start()));
face_sub->draw(geom_faces, res_handle);
if (draw_facedot) {
gpu::Batch *geom_facedots = DRW_mesh_batch_cache_get_facedots_with_select_id(mesh);
face_sub->draw(geom_facedots, res_handle);
}
}
else {
auto &sub = inst.select_face_ps.sub("FaceNoSelect");
sub.shader_set(sh->select_id_uniform);
sub.push_constant("select_id", 0);
sub.push_constant("retopologyOffset", retopology_offset);
inst.select_face_uniform = &sub;
if (ob->dt >= OB_SOLID) {
#ifdef USE_CAGE_OCCLUSION
gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
#else
gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_surface(mesh);
#endif
select_face_uniform->draw(geom_faces, res_handle);
}
}
inst.select_edge_ps.init();
inst.select_edge = nullptr;
if (e_data.context.select_mode & SCE_SELECT_EDGE) {
auto &sub = inst.select_edge_ps.sub("Sub");
sub.state_set(state | DRW_STATE_FIRST_VERTEX_CONVENTION, clipping_plane_count);
sub.shader_set(sh->select_id_flat);
sub.push_constant("retopologyOffset", retopology_offset);
inst.select_edge = &sub;
/* Unlike faces, only draw edges if edge select mode. */
if (select_mode & SCE_SELECT_EDGE) {
ranges.edge = alloc_range(ranges.total, em->bm->totedge);
gpu::Batch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(mesh);
select_edge->push_constant("offset", int(ranges.edge.start()));
select_edge->draw(geom_edges, res_handle);
}
inst.select_id_vert_ps.init();
inst.select_vert = nullptr;
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
const float vertex_size = blender::draw::overlay::Resources::vertex_size_get();
auto &sub = inst.select_id_vert_ps.sub("Sub");
sub.state_set(state, clipping_plane_count);
sub.shader_set(sh->select_id_flat);
sub.push_constant("vertex_size", float(2 * vertex_size));
sub.push_constant("retopologyOffset", retopology_offset);
inst.select_vert = &sub;
/* Unlike faces, only verts if vert select mode. */
if (select_mode & SCE_SELECT_VERTEX) {
ranges.vert = alloc_range(ranges.total, em->bm->totvert);
gpu::Batch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(mesh);
select_vert->push_constant("offset", int(ranges.vert.start()));
select_vert->draw(geom_verts, res_handle);
}
return ranges;
}
e_data.context.elem_ranges.clear();
ElemIndexRanges mesh_sync(Object *ob,
ResourceHandle res_handle,
short select_mode,
const uint initial_index)
{
using namespace blender::draw;
using namespace blender;
Mesh &mesh = *static_cast<Mesh *>(ob->data);
e_data.context.persmat = float4x4(draw_ctx->rv3d->persmat);
e_data.context.max_index_drawn_len = 1;
select_engine_framebuffer_setup();
GPU_framebuffer_bind(e_data.framebuffer_select_id);
GPU_framebuffer_clear_color_depth(e_data.framebuffer_select_id, blender::float4{0.0f}, 1.0f);
}
static bool check_ob_drawface_dot(short select_mode, const View3D *v3d, eDrawType dt)
{
if (select_mode & SCE_SELECT_FACE) {
if ((dt < OB_SOLID) || XRAY_FLAG_ENABLED(v3d)) {
return true;
}
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) {
return true;
}
}
return false;
}
namespace blender {
/* Return a new range if size `n` after `total_range` and grow `total_range` by the same amount. */
static IndexRange alloc_range(IndexRange &total_range, uint size)
{
const IndexRange indices = total_range.after(size);
total_range = IndexRange::from_begin_size(total_range.start(), total_range.size() + size);
return indices;
}
} // namespace blender
static ElemIndexRanges select_id_edit_mesh_sync(SELECTID_Instance &inst,
Object *ob,
ResourceHandle res_handle,
short select_mode,
bool draw_facedot,
const uint initial_index)
{
using namespace blender::draw;
using namespace blender;
Mesh &mesh = *static_cast<Mesh *>(ob->data);
BMEditMesh *em = mesh.runtime->edit_mesh.get();
ElemIndexRanges ranges{};
ranges.total = IndexRange::from_begin_size(initial_index, 0);
BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE | BM_FACE);
if (select_mode & SCE_SELECT_FACE) {
ranges.face = alloc_range(ranges.total, em->bm->totface);
ElemIndexRanges ranges{};
ranges.total = IndexRange::from_begin_size(initial_index, 0);
gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
PassSimple::Sub *face_sub = inst.select_face_flat;
face_sub->push_constant("offset", int(ranges.face.start()));
face_sub->draw(geom_faces, res_handle);
if (select_mode & SCE_SELECT_FACE) {
ranges.face = alloc_range(ranges.total, mesh.faces_num);
if (draw_facedot) {
gpu::Batch *geom_facedots = DRW_mesh_batch_cache_get_facedots_with_select_id(mesh);
face_sub->draw(geom_facedots, res_handle);
select_face_flat->push_constant("offset", int(ranges.face.start()));
select_face_flat->draw(geom_faces, res_handle);
}
}
else {
if (ob->dt >= OB_SOLID) {
#ifdef USE_CAGE_OCCLUSION
gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
#else
gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_surface(mesh);
#endif
inst.select_face_uniform->draw(geom_faces, res_handle);
else {
/* Only draw faces to mask out verts, we don't want their selection ID's. */
select_face_uniform->draw(geom_faces, res_handle);
}
if (select_mode & SCE_SELECT_EDGE) {
ranges.edge = alloc_range(ranges.total, mesh.edges_num);
gpu::Batch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(mesh);
select_edge->push_constant("offset", int(ranges.edge.start()));
select_edge->draw(geom_edges, res_handle);
}
if (select_mode & SCE_SELECT_VERTEX) {
ranges.vert = alloc_range(ranges.total, mesh.verts_num);
gpu::Batch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(mesh);
select_vert->push_constant("offset", int(ranges.vert.start()));
select_vert->draw(geom_verts, res_handle);
}
return ranges;
}
/* Unlike faces, only draw edges if edge select mode. */
if (select_mode & SCE_SELECT_EDGE) {
ranges.edge = alloc_range(ranges.total, em->bm->totedge);
ElemIndexRanges object_sync(
View3D *v3d, Object *ob, ResourceHandle res_handle, short select_mode, uint index_start)
{
BLI_assert_msg(index_start > 0, "Index 0 is reserved for no selection");
gpu::Batch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(mesh);
inst.select_edge->push_constant("offset", int(ranges.edge.start()));
inst.select_edge->draw(geom_edges, res_handle);
}
/* Unlike faces, only verts if vert select mode. */
if (select_mode & SCE_SELECT_VERTEX) {
ranges.vert = alloc_range(ranges.total, em->bm->totvert);
gpu::Batch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(mesh);
inst.select_vert->push_constant("offset", int(ranges.vert.start()));
inst.select_vert->draw(geom_verts, res_handle);
}
return ranges;
}
static ElemIndexRanges select_id_mesh_sync(SELECTID_Instance &inst,
Object *ob,
ResourceHandle res_handle,
short select_mode,
const uint initial_index)
{
using namespace blender::draw;
using namespace blender;
Mesh &mesh = *static_cast<Mesh *>(ob->data);
ElemIndexRanges ranges{};
ranges.total = IndexRange::from_begin_size(initial_index, 0);
gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(mesh);
if (select_mode & SCE_SELECT_FACE) {
ranges.face = alloc_range(ranges.total, mesh.faces_num);
inst.select_face_flat->push_constant("offset", int(ranges.face.start()));
inst.select_face_flat->draw(geom_faces, res_handle);
}
else {
/* Only draw faces to mask out verts, we don't want their selection ID's. */
inst.select_face_uniform->draw(geom_faces, res_handle);
}
if (select_mode & SCE_SELECT_EDGE) {
ranges.edge = alloc_range(ranges.total, mesh.edges_num);
gpu::Batch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(mesh);
inst.select_edge->push_constant("offset", int(ranges.edge.start()));
inst.select_edge->draw(geom_edges, res_handle);
}
if (select_mode & SCE_SELECT_VERTEX) {
ranges.vert = alloc_range(ranges.total, mesh.verts_num);
gpu::Batch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(mesh);
inst.select_vert->push_constant("offset", int(ranges.vert.start()));
inst.select_vert->draw(geom_verts, res_handle);
}
return ranges;
}
static ElemIndexRanges select_id_object_sync(SELECTID_Instance &inst,
View3D *v3d,
Object *ob,
ResourceHandle res_handle,
short select_mode,
uint index_start)
{
BLI_assert_msg(index_start > 0, "Index 0 is reserved for no selection");
switch (ob->type) {
case OB_MESH: {
const Mesh &mesh = *static_cast<const Mesh *>(ob->data);
if (mesh.runtime->edit_mesh) {
bool draw_facedot = check_ob_drawface_dot(select_mode, v3d, eDrawType(ob->dt));
return select_id_edit_mesh_sync(
inst, ob, res_handle, select_mode, draw_facedot, index_start);
switch (ob->type) {
case OB_MESH: {
const Mesh &mesh = *static_cast<const Mesh *>(ob->data);
if (mesh.runtime->edit_mesh) {
bool draw_facedot = check_ob_drawface_dot(select_mode, v3d, eDrawType(ob->dt));
return edit_mesh_sync(ob, res_handle, select_mode, draw_facedot, index_start);
}
return mesh_sync(ob, res_handle, select_mode, index_start);
}
return select_id_mesh_sync(inst, ob, res_handle, select_mode, index_start);
case OB_CURVES_LEGACY:
case OB_SURF:
break;
}
case OB_CURVES_LEGACY:
case OB_SURF:
break;
}
BLI_assert_unreachable();
return ElemIndexRanges{};
}
static void select_cache_populate(void *vedata, blender::draw::ObjectRef &ob_ref)
{
Manager &manager = *DRW_manager_get();
Object *ob = ob_ref.object;
SelectEngineData &e_data = get_engine_data();
SELECTID_Context &sel_ctx = e_data.context;
SELECTID_Instance &inst = *reinterpret_cast<SELECTID_Data *>(vedata)->instance;
const DRWContext *draw_ctx = DRW_context_get();
if (!sel_ctx.objects.contains(ob) && ob->dt >= OB_SOLID) {
/* This object is not selectable. It is here to participate in occlusion.
* This is the case in retopology mode. */
blender::gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_surface(
*static_cast<Mesh *>(ob->data));
inst.depth_occlude->draw(geom_faces, manager.resource_handle(ob_ref));
return;
BLI_assert_unreachable();
return ElemIndexRanges{};
}
/* Only sync selectable object once.
* This can happen in retopology mode where there is two sync loop. */
sel_ctx.elem_ranges.lookup_or_add_cb(ob, [&]() {
ResourceHandle res_handle = manager.resource_handle(ob_ref);
ElemIndexRanges elem_ranges = select_id_object_sync(
inst, draw_ctx->v3d, ob, res_handle, sel_ctx.select_mode, sel_ctx.max_index_drawn_len);
sel_ctx.max_index_drawn_len = elem_ranges.total.one_after_last();
return elem_ranges;
});
}
static void select_draw_scene(void *vedata)
{
Manager &manager = *DRW_manager_get();
SelectEngineData &e_data = get_engine_data();
SELECTID_Instance &inst = *reinterpret_cast<SELECTID_Data *>(vedata)->instance;
DRW_submission_start();
void object_sync(ObjectRef &ob_ref, Manager &manager) final
{
Object *ob = ob_ref.object;
StaticData &e_data = StaticData::get();
SELECTID_Context &sel_ctx = e_data.context;
const DRWContext *draw_ctx = DRW_context_get();
View::OffsetData offset_data(*draw_ctx->rv3d);
/* Create view with depth offset */
const View &view = View::default_get();
inst.view_faces.sync(view.viewmat(), view.winmat());
inst.view_edges.sync(view.viewmat(), offset_data.winmat_polygon_offset(view.winmat(), 1.0f));
inst.view_verts.sync(view.viewmat(), offset_data.winmat_polygon_offset(view.winmat(), 1.1f));
if (!sel_ctx.objects.contains(ob) && ob->dt >= OB_SOLID) {
/* This object is not selectable. It is here to participate in occlusion.
* This is the case in retopology mode. */
blender::gpu::Batch *geom_faces = DRW_mesh_batch_cache_get_surface(
*static_cast<Mesh *>(ob->data));
depth_occlude->draw(geom_faces, manager.resource_handle(ob_ref));
return;
}
/* Only sync selectable object once.
* This can happen in retopology mode where there is two sync loop. */
sel_ctx.elem_ranges.lookup_or_add_cb(ob, [&]() {
ResourceHandle res_handle = manager.resource_handle(ob_ref);
ElemIndexRanges elem_ranges = object_sync(
draw_ctx->v3d, ob, res_handle, sel_ctx.select_mode, sel_ctx.max_index_drawn_len);
sel_ctx.max_index_drawn_len = elem_ranges.total.one_after_last();
return elem_ranges;
});
}
void end_sync() final {}
void draw(Manager &manager) final
{
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->depth_only_fb);
GPU_framebuffer_clear_depth(dfbl->depth_only_fb, 1.0f);
manager.submit(inst.depth_only_ps, inst.view_faces);
StaticData &e_data = StaticData::get();
DRW_submission_start();
{
const DRWContext *draw_ctx = DRW_context_get();
View::OffsetData offset_data(*draw_ctx->rv3d);
/* Create view with depth offset */
const View &view = View::default_get();
view_faces.sync(view.viewmat(), view.winmat());
view_edges.sync(view.viewmat(), offset_data.winmat_polygon_offset(view.winmat(), 1.0f));
view_verts.sync(view.viewmat(), offset_data.winmat_polygon_offset(view.winmat(), 1.1f));
}
{
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->depth_only_fb);
GPU_framebuffer_clear_depth(dfbl->depth_only_fb, 1.0f);
manager.submit(depth_only_ps, view_faces);
}
/* Setup framebuffer */
GPU_framebuffer_bind(e_data.framebuffer_select_id);
manager.submit(select_face_ps, view_faces);
if (e_data.context.select_mode & SCE_SELECT_EDGE) {
manager.submit(select_edge_ps, view_edges);
}
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
manager.submit(select_id_vert_ps, view_verts);
}
DRW_submission_end();
}
/* Setup framebuffer */
GPU_framebuffer_bind(e_data.framebuffer_select_id);
private:
void framebuffer_setup()
{
StaticData &e_data = StaticData::get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
int size[2];
size[0] = GPU_texture_width(dtxl->depth);
size[1] = GPU_texture_height(dtxl->depth);
manager.submit(inst.select_face_ps, inst.view_faces);
if (e_data.framebuffer_select_id == nullptr) {
e_data.framebuffer_select_id = GPU_framebuffer_create("framebuffer_select_id");
}
if (e_data.context.select_mode & SCE_SELECT_EDGE) {
manager.submit(inst.select_edge_ps, inst.view_edges);
if ((e_data.texture_u32 != nullptr) && ((GPU_texture_width(e_data.texture_u32) != size[0]) ||
(GPU_texture_height(e_data.texture_u32) != size[1])))
{
GPU_texture_free(e_data.texture_u32);
e_data.texture_u32 = nullptr;
}
/* Make sure the depth texture is attached.
* It may disappear when loading another Blender session. */
GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, dtxl->depth, 0, 0);
if (e_data.texture_u32 == nullptr) {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
e_data.texture_u32 = GPU_texture_create_2d(
"select_buf_ids", size[0], size[1], 1, GPU_R32UI, usage, nullptr);
GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, e_data.texture_u32, 0, 0);
GPU_framebuffer_check_valid(e_data.framebuffer_select_id, nullptr);
}
}
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
manager.submit(inst.select_id_vert_ps, inst.view_verts);
short get_object_select_mode(Scene *scene, Object *ob)
{
short r_select_mode = 0;
if (ob->mode & (OB_MODE_WEIGHT_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) {
/* In order to sample flat colors for vertex weights / texture-paint / vertex-paint
* we need to be in SCE_SELECT_FACE mode so select_cache_init() correctly sets up
* a shgroup with select_id_flat.
* Note this is not working correctly for vertex-paint (yet), but has been discussed
* in #66645 and there is a solution by @mano-wii in P1032.
* So OB_MODE_VERTEX_PAINT is already included here [required for P1032 I guess]. */
Mesh *me_orig = static_cast<Mesh *>(DEG_get_original_object(ob)->data);
if (me_orig->editflag & ME_EDIT_PAINT_VERT_SEL) {
r_select_mode = SCE_SELECT_VERTEX;
}
else {
r_select_mode = SCE_SELECT_FACE;
}
}
else {
r_select_mode = scene->toolsettings->selectmode;
}
return r_select_mode;
}
DRW_submission_end();
bool check_ob_drawface_dot(short select_mode, const View3D *v3d, eDrawType dt)
{
if (select_mode & SCE_SELECT_FACE) {
if ((dt < OB_SOLID) || XRAY_FLAG_ENABLED(v3d)) {
return true;
}
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) {
return true;
}
}
return false;
}
/* Return a new range if size `n` after `total_range` and grow `total_range` by the same amount.
*/
IndexRange alloc_range(IndexRange &total_range, uint size)
{
const IndexRange indices = total_range.after(size);
total_range = IndexRange::from_begin_size(total_range.start(), total_range.size() + size);
return indices;
}
};
DrawEngine *Engine::create_instance()
{
return new Instance();
}
static void select_engine_free()
void Engine::free_static()
{
SelectEngineData &e_data = get_engine_data();
Instance::StaticData &e_data = Instance::StaticData::get();
for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) {
SELECTID_Shaders *sh_data = &e_data.sh_data[sh_data_index];
Instance::StaticData::Shaders *sh_data = &e_data.sh_data[sh_data_index];
GPU_SHADER_FREE_SAFE(sh_data->select_id_flat);
GPU_SHADER_FREE_SAFE(sh_data->select_id_uniform);
}
@@ -480,57 +492,7 @@ static void select_engine_free()
GPU_FRAMEBUFFER_FREE_SAFE(e_data.framebuffer_select_id);
}
static void select_instance_free(void *instance)
{
delete reinterpret_cast<SELECTID_Instance *>(instance);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Type
* \{ */
DrawEngineType draw_engine_select_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Select ID"),
/*engine_init*/ &select_engine_init,
/*engine_free*/ &select_engine_free,
/*instance_free*/ select_instance_free,
/*cache_init*/ &select_cache_init,
/*cache_populate*/ &select_cache_populate,
/*cache_finish*/ nullptr,
/*draw_scene*/ &select_draw_scene,
/*render_to_image*/ nullptr,
/*store_metadata*/ nullptr,
};
/* NOTE: currently unused, we may want to register so we can see this when debugging the view. */
RenderEngineType DRW_engine_viewport_select_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ SELECT_ENGINE,
/*name*/ N_("Select ID"),
/*flag*/ RE_INTERNAL | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
/*update*/ nullptr,
/*render*/ nullptr,
/*render_frame_finish*/ nullptr,
/*draw*/ nullptr,
/*bake*/ nullptr,
/*view_update*/ nullptr,
/*view_draw*/ nullptr,
/*update_script_node*/ nullptr,
/*update_render_passes*/ nullptr,
/*draw_engine*/ &draw_engine_select_type,
/*rna_ext*/
{
/*data*/ nullptr,
/*srna*/ nullptr,
/*call*/ nullptr,
},
};
} // namespace blender::draw::edit_select
/** \} */
@@ -538,24 +500,24 @@ RenderEngineType DRW_engine_viewport_select_type = {
/** \name Exposed `select_private.h` functions
* \{ */
using namespace blender::draw::edit_select;
SELECTID_Context *DRW_select_engine_context_get()
{
SelectEngineData &e_data = get_engine_data();
Instance::StaticData &e_data = Instance::StaticData::get();
return &e_data.context;
}
GPUFrameBuffer *DRW_engine_select_framebuffer_get()
{
SelectEngineData &e_data = get_engine_data();
Instance::StaticData &e_data = Instance::StaticData::get();
return e_data.framebuffer_select_id;
}
GPUTexture *DRW_engine_select_texture_get()
{
SelectEngineData &e_data = get_engine_data();
Instance::StaticData &e_data = Instance::StaticData::get();
return e_data.texture_u32;
}
/** \} */
#undef SELECT_ENGINE

View File

@@ -8,15 +8,23 @@
#pragma once
/* `select_engine.cc` */
#include "DRW_render.hh"
extern DrawEngineType draw_engine_select_type;
extern RenderEngineType DRW_engine_viewport_select_type;
/* `select_engine.cc` */
#ifdef WITH_DRAW_DEBUG
/* `select_debug_engine.cc` */
extern DrawEngineType draw_engine_debug_select_type;
namespace blender::draw::edit_select_debug {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
static void free_static();
};
} // namespace blender::draw::edit_select_debug
#endif
struct SELECTID_Context *DRW_select_engine_context_get();
@@ -25,4 +33,20 @@ struct GPUTexture *DRW_engine_select_texture_get();
/* select_instance.cc */
extern DrawEngineType draw_engine_select_next_type;
namespace blender::draw::select {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
};
} // namespace blender::draw::select
namespace blender::draw::edit_select {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
static void free_static();
};
} // namespace blender::draw::edit_select

View File

@@ -8,79 +8,21 @@
#include "DRW_render.hh"
#include "BLT_translation.hh"
#include "select_engine.hh"
#include "../overlay/overlay_next_instance.hh"
#include "select_instance.hh"
using namespace blender::draw;
namespace blender::draw::select {
/* -------------------------------------------------------------------- */
/** \name Select-Next Engine
* \{ */
using Instance = overlay::Instance;
struct SELECT_NextData {
void *engine_type;
Instance *instance;
class Instance : public overlay::Instance {
public:
Instance() : overlay::Instance(SelectionType::ENABLED){};
};
static void SELECT_next_engine_init(void *vedata)
DrawEngine *Engine::create_instance()
{
OVERLAY_Data *ved = reinterpret_cast<OVERLAY_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new Instance(select::SelectionType::ENABLED);
}
reinterpret_cast<Instance *>(ved->instance)->init();
return new Instance();
}
static void SELECT_next_cache_init(void *vedata)
{
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)->begin_sync();
}
static void SELECT_next_cache_populate(void *vedata, blender::draw::ObjectRef &ob_ref)
{
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)
->object_sync(ob_ref, *DRW_manager_get());
}
static void SELECT_next_cache_finish(void *vedata)
{
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)->end_sync();
}
static void SELECT_next_draw_scene(void *vedata)
{
DRW_submission_start();
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)
->draw(*DRW_manager_get());
DRW_submission_end();
}
static void SELECT_next_instance_free(void *instance_)
{
Instance *instance = (Instance *)instance_;
delete instance;
}
DrawEngineType draw_engine_select_next_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Select-Next"),
/*engine_init*/ &SELECT_next_engine_init,
/*engine_free*/ nullptr,
/*instance_free*/ &SELECT_next_instance_free,
/*cache_init*/ &SELECT_next_cache_init,
/*cache_populate*/ &SELECT_next_cache_populate,
/*cache_finish*/ &SELECT_next_cache_finish,
/*draw_scene*/ &SELECT_next_draw_scene,
/*render_to_image*/ nullptr,
/*store_metadata*/ nullptr,
};
/** \} */
} // namespace blender::draw::select

View File

@@ -1,59 +0,0 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#pragma once
#define USE_CAGE_OCCLUSION
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "draw_view_data.hh"
#include "DRW_render.hh"
#include "DRW_select_buffer.hh"
using namespace blender::draw;
/* GPUViewport.storage
* Is freed every time the viewport engine changes. */
struct SELECTID_StorageList {
struct SELECTID_PrivateData *g_data;
};
struct SELECTID_Instance {
PassSimple depth_only_ps = {"depth_only_ps"};
PassSimple::Sub *depth_only = nullptr;
PassSimple::Sub *depth_occlude = nullptr;
PassSimple select_edge_ps = {"select_id_edge_ps"};
PassSimple::Sub *select_edge = nullptr;
PassSimple select_id_vert_ps = {"select_id_vert_ps"};
PassSimple::Sub *select_vert = nullptr;
PassSimple select_face_ps = {"select_id_face_ps"};
PassSimple::Sub *select_face_uniform = nullptr;
PassSimple::Sub *select_face_flat = nullptr;
View view_faces = {"view_faces"};
View view_edges = {"view_edges"};
View view_verts = {"view_verts"};
};
struct SELECTID_Data {
void *engine_type;
SELECTID_Instance *instance;
char info[GPU_INFO_SIZE];
};
struct SELECTID_Shaders {
/* Depth Pre Pass */
GPUShader *select_id_flat;
GPUShader *select_id_uniform;
};

View File

@@ -41,7 +41,7 @@ namespace blender::workbench {
using namespace draw;
class Instance {
class Instance : public DrawEngine {
private:
View view_ = {"DefaultView"};
@@ -67,6 +67,11 @@ class Instance {
uint64_t depsgraph_last_update_ = 0;
public:
blender::StringRefNull name_get() final
{
return "Workbench";
}
Span<const GPUMaterial *> get_dummy_gpu_materials(int material_count)
{
if (material_count > dummy_gpu_materials_.size()) {
@@ -75,6 +80,11 @@ class Instance {
return dummy_gpu_materials_.as_span().slice(IndexRange(material_count));
};
void init() final
{
init(DRW_context_get()->depsgraph);
}
void init(Depsgraph *depsgraph, Object *camera_ob = nullptr)
{
bool scene_updated = assign_if_different(depsgraph_last_update_,
@@ -89,7 +99,7 @@ class Instance {
anti_aliasing_ps_.init(scene_state_);
}
void begin_sync()
void begin_sync() final
{
resources_.material_buf.clear_and_trim();
@@ -104,7 +114,7 @@ class Instance {
anti_aliasing_ps_.sync(scene_state_, resources_);
}
void end_sync()
void end_sync() final
{
resources_.material_buf.push_update();
}
@@ -132,7 +142,7 @@ class Instance {
}
}
void object_sync(Manager &manager, ObjectRef &ob_ref)
void object_sync(ObjectRef &ob_ref, Manager &manager) final
{
if (scene_state_.render_finished) {
return;
@@ -494,6 +504,20 @@ class Instance {
}
}
void draw(Manager &manager) final
{
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRW_submission_start();
if (DRW_state_is_viewport_image_render()) {
draw_image_render(manager, dtxl->depth, dtxl->depth_in_front, dtxl->color);
}
else {
draw_viewport(manager, dtxl->depth, dtxl->depth_in_front, dtxl->color);
}
DRW_submission_end();
}
void draw_image_render(Manager &manager,
GPUTexture *depth_tx,
GPUTexture *depth_in_front_tx,
@@ -523,6 +547,16 @@ class Instance {
}
};
DrawEngine *Engine::create_instance()
{
return new Instance();
}
void Engine::free_static()
{
ShaderCache::release();
}
} // namespace blender::workbench
/* -------------------------------------------------------------------- */
@@ -531,66 +565,6 @@ class Instance {
using namespace blender;
struct WORKBENCH_Data {
DrawEngineType *engine_type;
workbench::Instance *instance;
char info[GPU_INFO_SIZE];
};
static void workbench_engine_init(void *vedata)
{
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new workbench::Instance();
}
ved->instance->init(DRW_context_get()->depsgraph);
}
static void workbench_cache_init(void *vedata)
{
reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->begin_sync();
}
static void workbench_cache_populate(void *vedata, blender::draw::ObjectRef &ob_ref)
{
draw::Manager *manager = DRW_manager_get();
reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->object_sync(*manager, ob_ref);
}
static void workbench_cache_finish(void *vedata)
{
reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->end_sync();
}
static void workbench_draw_scene(void *vedata)
{
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
draw::Manager *manager = DRW_manager_get();
DRW_submission_start();
if (DRW_state_is_viewport_image_render()) {
ved->instance->draw_image_render(*manager, dtxl->depth, dtxl->depth_in_front, dtxl->color);
}
else {
ved->instance->draw_viewport(*manager, dtxl->depth, dtxl->depth_in_front, dtxl->color);
}
DRW_submission_end();
}
static void workbench_instance_free(void *instance)
{
delete reinterpret_cast<workbench::Instance *>(instance);
}
static void workbench_engine_free()
{
workbench::ShaderCache::release();
}
/* RENDER */
static bool workbench_render_framebuffers_init()
@@ -704,10 +678,7 @@ static void write_render_z_output(RenderLayer *layer,
}
}
static void workbench_render_to_image(void *vedata,
RenderEngine *engine,
RenderLayer *layer,
const rcti *rect)
static void workbench_render_to_image(RenderEngine *engine, RenderLayer *layer, const rcti rect)
{
using namespace blender::draw;
if (!workbench_render_framebuffers_init()) {
@@ -720,10 +691,7 @@ static void workbench_render_to_image(void *vedata,
const DRWContext *draw_ctx = DRW_context_get();
Depsgraph *depsgraph = draw_ctx->depsgraph;
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new workbench::Instance();
}
workbench::Instance instance;
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
Object *camera_ob = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
@@ -739,34 +707,33 @@ static void workbench_render_to_image(void *vedata,
DRW_cache_restart();
blender::draw::View::default_set(float4x4(viewmat), float4x4(winmat));
ved->instance->init(depsgraph, camera_ob);
instance.init(depsgraph, camera_ob);
draw::Manager &manager = *DRW_manager_get();
manager.begin_sync();
workbench_cache_init(vedata);
auto workbench_render_cache = [](void *vedata,
blender::draw::ObjectRef &ob_ref,
RenderEngine * /*engine*/,
Depsgraph * /*depsgraph*/) {
workbench_cache_populate(vedata, ob_ref);
};
DRW_render_object_iter(vedata, engine, depsgraph, workbench_render_cache);
workbench_cache_finish(vedata);
instance.begin_sync();
DRW_render_object_iter(
engine,
depsgraph,
[&](blender::draw::ObjectRef &ob_ref, RenderEngine * /*engine*/, Depsgraph * /*depsgraph*/) {
instance.object_sync(ob_ref, manager);
});
instance.end_sync();
manager.end_sync();
DRW_submission_start();
DefaultTextureList &dtxl = *DRW_viewport_texture_list_get();
ved->instance->draw_image_render(manager, dtxl.depth, dtxl.depth_in_front, dtxl.color, engine);
instance.draw_image_render(manager, dtxl.depth, dtxl.depth_in_front, dtxl.color, engine);
DRW_submission_end();
/* Write image */
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);
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,
@@ -781,20 +748,10 @@ static void workbench_render_update_passes(RenderEngine *engine,
}
}
DrawEngineType draw_engine_workbench = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Workbench"),
/*engine_init*/ &workbench_engine_init,
/*engine_free*/ &workbench_engine_free,
/*instance_free*/ &workbench_instance_free,
/*cache_init*/ &workbench_cache_init,
/*cache_populate*/ &workbench_cache_populate,
/*cache_finish*/ &workbench_cache_finish,
/*draw_scene*/ &workbench_draw_scene,
/*render_to_image*/ &workbench_render_to_image,
/*store_metadata*/ nullptr,
};
void workbench_render(RenderEngine *engine, Depsgraph *depsgraph)
{
DRW_render_to_image(engine, depsgraph, workbench_render_to_image, [](RenderResult *) {});
}
RenderEngineType DRW_engine_viewport_workbench_type = {
/*next*/ nullptr,
@@ -803,7 +760,7 @@ RenderEngineType DRW_engine_viewport_workbench_type = {
/*name*/ N_("Workbench"),
/*flag*/ RE_INTERNAL | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
/*update*/ nullptr,
/*render*/ &DRW_render_to_image,
/*render*/ &workbench_render,
/*render_frame_finish*/ nullptr,
/*draw*/ nullptr,
/*bake*/ nullptr,
@@ -811,7 +768,7 @@ RenderEngineType DRW_engine_viewport_workbench_type = {
/*view_draw*/ nullptr,
/*update_script_node*/ nullptr,
/*update_render_passes*/ &workbench_render_update_passes,
/*draw_engine*/ &draw_engine_workbench,
/*draw_engine*/ nullptr,
/*rna_ext*/
{
/*data*/ nullptr,

View File

@@ -8,6 +8,18 @@
#pragma once
#include "DRW_render.hh"
struct RenderEngineType;
extern RenderEngineType DRW_engine_viewport_workbench_type;
namespace blender::workbench {
struct Engine : public DrawEngine::Pointer {
DrawEngine *create_instance() final;
static void free_static();
};
} // namespace blender::workbench

View File

@@ -16,8 +16,6 @@
#include "GPU_capabilities.hh"
extern "C" DrawEngineType draw_engine_workbench;
namespace blender::workbench {
using namespace draw;

View File

@@ -54,6 +54,7 @@ struct GPUViewport;
namespace blender::draw {
class TextureFromPool;
struct ObjectRef;
class Manager;
} // namespace blender::draw
typedef struct DRWPass DRWPass;
@@ -65,27 +66,63 @@ struct BoundSphere {
float center[3], radius;
};
struct DrawEngineType {
DrawEngineType *next, *prev;
struct DrawEngine {
static constexpr int GPU_INFO_SIZE = 512; /* IMA_MAX_RENDER_TEXT_SIZE */
char idname[32];
char info[GPU_INFO_SIZE] = {'\0'};
DRWTextStore *text_draw_cache = nullptr;
void (*engine_init)(void *vedata);
void (*engine_free)();
bool used = false;
void (*instance_free)(void *instance_data);
virtual ~DrawEngine() = default;
void (*cache_init)(void *vedata);
void (*cache_populate)(void *vedata, blender::draw::ObjectRef &ob_ref);
void (*cache_finish)(void *vedata);
virtual blender::StringRefNull name_get() = 0;
void (*draw_scene)(void *vedata);
/* Functions called for viewport. */
void (*render_to_image)(void *vedata,
RenderEngine *engine,
RenderLayer *layer,
const rcti *rect);
void (*store_metadata)(void *vedata, RenderResult *render_result);
/* Init engine. Run first and for every redraw. */
virtual void init() = 0;
/* Scene synchronization. Command buffers building. */
virtual void begin_sync() = 0;
virtual void object_sync(blender::draw::ObjectRef &ob_ref, blender::draw::Manager &manager) = 0;
virtual void end_sync() = 0;
/* Command Submission. */
virtual void draw(blender::draw::Manager &manager) = 0;
/* Called when closing blender.
* Cleanup all lazily initialized static members that have GPU resources.
* Implemented on a case by case basis and called directly. */
// static void exit(){};
struct Pointer {
DrawEngine *instance = nullptr;
~Pointer()
{
free_instance();
}
void free_instance()
{
delete instance;
instance = nullptr;
}
void set_used(bool used)
{
if (used) {
if (instance == nullptr) {
instance = create_instance();
}
instance->used = true;
}
else if (instance) {
instance->used = false;
}
}
virtual DrawEngine *create_instance() = 0;
};
};
/* Shaders */
@@ -120,19 +157,24 @@ blender::float2 DRW_viewport_size_get();
DefaultFramebufferList *DRW_viewport_framebuffer_list_get();
DefaultTextureList *DRW_viewport_texture_list_get();
/* See DRW_viewport_pass_texture_get. */
/* Returns a TextureFromPool stored in the given view data for the pass identified by the given
* pass name. Engines should call this function for each of the passes needed by the viewport
* compositor in every redraw, then it should allocate the texture and write the pass data to it.
* The texture should cover the entire viewport. */
blender::draw::TextureFromPool &DRW_viewport_pass_texture_get(const char *pass_name);
void DRW_viewport_request_redraw();
void DRW_render_to_image(RenderEngine *engine, Depsgraph *depsgraph);
void DRW_render_object_iter(void *vedata,
RenderEngine *engine,
Depsgraph *depsgraph,
void (*callback)(void *vedata,
blender::draw::ObjectRef &ob_ref,
RenderEngine *engine,
Depsgraph *depsgraph));
void DRW_render_to_image(
RenderEngine *engine,
Depsgraph *depsgraph,
std::function<void(RenderEngine *, RenderLayer *, const rcti)> render_view_cb,
std::function<void(RenderResult *)> store_metadata_cb);
void DRW_render_object_iter(
RenderEngine *engine,
Depsgraph *depsgraph,
std::function<void(blender::draw::ObjectRef &, RenderEngine *, Depsgraph *)>);
/**
* \warning Changing frame might free the #ViewLayerEngineData.
@@ -144,9 +186,7 @@ void DRW_render_set_time(RenderEngine *engine, Depsgraph *depsgraph, int frame,
* This function only setup DST and execute the given function.
* \warning similar to DRW_render_to_image you cannot use default lists (`dfbl` & `dtxl`).
*/
void DRW_custom_pipeline_begin(DRWContext &draw_ctx,
DrawEngineType *draw_engine_type,
Depsgraph *depsgraph);
void DRW_custom_pipeline_begin(DRWContext &draw_ctx, Depsgraph *depsgraph);
void DRW_custom_pipeline_end(DRWContext &draw_ctx);
/**
@@ -155,24 +195,6 @@ void DRW_custom_pipeline_end(DRWContext &draw_ctx);
*/
void DRW_cache_restart();
/* ViewLayers */
void *DRW_view_layer_engine_data_get(DrawEngineType *engine_type);
void **DRW_view_layer_engine_data_ensure_ex(ViewLayer *view_layer,
DrawEngineType *engine_type,
void (*callback)(void *storage));
void **DRW_view_layer_engine_data_ensure(DrawEngineType *engine_type,
void (*callback)(void *storage));
/* DrawData */
DrawData *DRW_drawdata_get(ID *id, DrawEngineType *engine_type);
DrawData *DRW_drawdata_ensure(ID *id,
DrawEngineType *engine_type,
size_t size,
DrawDataInitCb init_cb,
DrawDataFreeCb free_cb);
/* Settings. */
bool DRW_object_is_renderable(const Object *ob);

View File

@@ -88,7 +88,7 @@
#include "engines/compositor/compositor_engine.h"
#include "engines/eevee_next/eevee_engine.h"
#include "engines/external/external_engine.h"
#include "engines/gpencil/gpencil_engine.h"
#include "engines/gpencil/gpencil_engine.hh"
#include "engines/image/image_engine.h"
#include "engines/overlay/overlay_engine.h"
#include "engines/select/select_engine.hh"
@@ -197,8 +197,7 @@ DRWContext::~DRWContext()
GPUFrameBuffer *DRWContext::default_framebuffer()
{
DefaultFramebufferList *dfbl = DRW_view_data_default_framebuffer_list_get(view_data_active);
return dfbl->default_fb;
return view_data_active->dfbl.default_fb;
}
static bool draw_show_annotation()
@@ -496,17 +495,18 @@ void DRWContext::release_data()
DefaultFramebufferList *DRW_viewport_framebuffer_list_get()
{
return DRW_view_data_default_framebuffer_list_get(drw_get().view_data_active);
return &drw_get().view_data_active->dfbl;
}
DefaultTextureList *DRW_viewport_texture_list_get()
{
return DRW_view_data_default_texture_list_get(drw_get().view_data_active);
return &drw_get().view_data_active->dtxl;
}
blender::draw::TextureFromPool &DRW_viewport_pass_texture_get(const char *pass_name)
{
return DRW_view_data_pass_texture_get(drw_get().view_data_active, pass_name);
return *drw_get().view_data_active->viewport_compositor_passes.lookup_or_add_cb(
pass_name, [&]() { return std::make_unique<blender::draw::TextureFromPool>(pass_name); });
}
void DRW_viewport_request_redraw()
@@ -634,49 +634,6 @@ void DupliCacheManager::extract_all(ExtractionGraph &extraction)
/** \} */
/* -------------------------------------------------------------------- */
/** \name ViewLayers (DRW_scenelayer)
* \{ */
void *DRW_view_layer_engine_data_get(DrawEngineType *engine_type)
{
LISTBASE_FOREACH (ViewLayerEngineData *, sled, &drw_get().view_layer->drawdata) {
if (sled->engine_type == engine_type) {
return sled->storage;
}
}
return nullptr;
}
void **DRW_view_layer_engine_data_ensure_ex(ViewLayer *view_layer,
DrawEngineType *engine_type,
void (*callback)(void *storage))
{
ViewLayerEngineData *sled;
LISTBASE_FOREACH (ViewLayerEngineData *, sled, &view_layer->drawdata) {
if (sled->engine_type == engine_type) {
return &sled->storage;
}
}
sled = static_cast<ViewLayerEngineData *>(
MEM_callocN(sizeof(ViewLayerEngineData), "ViewLayerEngineData"));
sled->engine_type = engine_type;
sled->free = callback;
BLI_addtail(&view_layer->drawdata, sled);
return &sled->storage;
}
void **DRW_view_layer_engine_data_ensure(DrawEngineType *engine_type,
void (*callback)(void *storage))
{
return DRW_view_layer_engine_data_ensure_ex(drw_get().view_layer, engine_type, callback);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Draw Data (DRW_drawdata)
* \{ */
@@ -737,56 +694,6 @@ DrawDataList *DRW_drawdatalist_from_id(ID *id)
return nullptr;
}
DrawData *DRW_drawdata_get(ID *id, DrawEngineType *engine_type)
{
DrawDataList *drawdata = DRW_drawdatalist_from_id(id);
if (drawdata == nullptr) {
return nullptr;
}
LISTBASE_FOREACH (DrawData *, dd, drawdata) {
if (dd->engine_type == engine_type) {
return dd;
}
}
return nullptr;
}
DrawData *DRW_drawdata_ensure(ID *id,
DrawEngineType *engine_type,
size_t size,
DrawDataInitCb init_cb,
DrawDataFreeCb free_cb)
{
BLI_assert(size >= sizeof(DrawData));
BLI_assert(id_can_have_drawdata(id));
BLI_assert_msg(
GS(id->name) != ID_OB,
"Objects should not use DrawData anymore. Use last_update instead for update detection");
/* Try to re-use existing data. */
DrawData *dd = DRW_drawdata_get(id, engine_type);
if (dd != nullptr) {
return dd;
}
DrawDataList *drawdata = DRW_drawdatalist_from_id(id);
/* Allocate new data. */
{
dd = static_cast<DrawData *>(MEM_callocN(size, "DrawData"));
}
dd->engine_type = engine_type;
dd->free = free_cb;
/* Perform user-side initialization, if needed. */
if (init_cb != nullptr) {
init_cb(dd);
}
/* Register in the list. */
BLI_addtail((ListBase *)drawdata, dd);
return dd;
}
void DRW_drawdata_free(ID *id)
{
DrawDataList *drawdata = DRW_drawdatalist_from_id(id);
@@ -908,11 +815,7 @@ static void drw_engines_cache_populate(blender::draw::ObjectRef &ref, Extraction
DRWContext &ctx = drw_get();
ctx.view_data_active->foreach_enabled_engine(
[&](ViewportEngineData *data, DrawEngineType *engine) {
if (engine->cache_populate) {
engine->cache_populate(data, ref);
}
});
[&](DrawEngine &instance) { instance.object_sync(ref, *DRW_manager_get()); });
/* TODO: in the future it would be nice to generate once for all viewports.
* But we need threaded DRW manager first. */
@@ -947,34 +850,24 @@ void DRWContext::sync(iter_callback_t iter_callback)
void DRWContext::engines_init_and_sync(iter_callback_t iter_callback)
{
view_data_active->foreach_enabled_engine([&](ViewportEngineData *data, DrawEngineType *engine) {
if (engine->engine_init) {
engine->engine_init(data);
}
});
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) { instance.init(); });
view_data_active->foreach_enabled_engine([&](ViewportEngineData *data, DrawEngineType *engine) {
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) {
/* TODO(fclem): Remove. Only there for overlay engine. */
if (data->text_draw_cache) {
DRW_text_cache_destroy(data->text_draw_cache);
data->text_draw_cache = nullptr;
if (instance.text_draw_cache) {
DRW_text_cache_destroy(instance.text_draw_cache);
instance.text_draw_cache = nullptr;
}
if (drw_get().text_store_p == nullptr) {
drw_get().text_store_p = &data->text_draw_cache;
drw_get().text_store_p = &instance.text_draw_cache;
}
if (engine->cache_init) {
engine->cache_init(data);
}
instance.begin_sync();
});
sync(iter_callback);
view_data_active->foreach_enabled_engine([&](ViewportEngineData *data, DrawEngineType *engine) {
if (engine->cache_finish) {
engine->cache_finish(data);
}
});
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) { instance.end_sync(); });
}
void DRWContext::engines_draw_scene()
@@ -982,12 +875,10 @@ void DRWContext::engines_draw_scene()
/* Start Drawing */
blender::draw::command::StateSet::set();
view_data_active->foreach_enabled_engine([&](ViewportEngineData *data, DrawEngineType *engine) {
if (engine->draw_scene) {
GPU_debug_group_begin(engine->idname);
engine->draw_scene(data);
GPU_debug_group_end();
}
view_data_active->foreach_enabled_engine([&](DrawEngine &instance) {
GPU_debug_group_begin(instance.name_get().c_str());
instance.draw(*DRW_manager_get());
GPU_debug_group_end();
});
/* Reset state after drawing */
@@ -1002,30 +893,28 @@ void DRWContext::engines_draw_scene()
static void drw_engines_draw_text()
{
DRWContext &ctx = drw_get();
ctx.view_data_active->foreach_enabled_engine(
[&](ViewportEngineData *data, DrawEngineType * /*engine*/) {
if (data->text_draw_cache) {
DRW_text_cache_draw(data->text_draw_cache, ctx.region, ctx.v3d);
}
});
ctx.view_data_active->foreach_enabled_engine([&](DrawEngine &instance) {
if (instance.text_draw_cache) {
DRW_text_cache_draw(instance.text_draw_cache, ctx.region, ctx.v3d);
}
});
}
void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height)
{
DRWContext &ctx = drw_get();
ctx.view_data_active->foreach_enabled_engine(
[&](ViewportEngineData *data, DrawEngineType * /*engine*/) {
if (data->info[0] != '\0') {
const char *buf_step = IFACE_(data->info);
do {
const char *buf = buf_step;
buf_step = BLI_strchr_or_end(buf, '\n');
const int buf_len = buf_step - buf;
*yoffset -= line_height;
BLF_draw_default(xoffset, *yoffset, 0.0f, buf, buf_len);
} while (*buf_step ? ((void)buf_step++, true) : false);
}
});
ctx.view_data_active->foreach_enabled_engine([&](DrawEngine &instance) {
if (instance.info[0] != '\0') {
const char *buf_step = IFACE_(instance.info);
do {
const char *buf = buf_step;
buf_step = BLI_strchr_or_end(buf, '\n');
const int buf_len = buf_step - buf;
*yoffset -= line_height;
BLF_draw_default(xoffset, *yoffset, 0.0f, buf, buf_len);
} while (*buf_step ? ((void)buf_step++, true) : false);
}
});
}
void DRWContext::enable_engines(bool gpencil_engine_needed, RenderEngineType *render_engine_type)
@@ -1035,12 +924,12 @@ void DRWContext::enable_engines(bool gpencil_engine_needed, RenderEngineType *re
SpaceLink *space_data = this->space_data;
if (space_data && space_data->spacetype == SPACE_IMAGE) {
if (DRW_engine_external_acquire_for_image_editor()) {
view_data.external.used = true;
view_data.external.set_used(true);
}
else {
view_data.image.used = true;
view_data.image.set_used(true);
}
view_data.overlay.used = true;
view_data.overlay.set_used(true);
return;
}
@@ -1048,26 +937,26 @@ void DRWContext::enable_engines(bool gpencil_engine_needed, RenderEngineType *re
/* Only enable when drawing the space image backdrop. */
SpaceNode *snode = (SpaceNode *)space_data;
if ((snode->flag & SNODE_BACKDRAW) != 0) {
view_data.image.used = true;
view_data.overlay.used = true;
view_data.image.set_used(true);
view_data.overlay.set_used(true);
}
return;
}
if (ELEM(this->mode, DRWContext::SELECT_OBJECT, DRWContext::SELECT_OBJECT_MATERIAL)) {
this->view_data_active->grease_pencil.used = gpencil_engine_needed;
this->view_data_active->object_select.used = true;
view_data.grease_pencil.set_used(gpencil_engine_needed);
view_data.object_select.set_used(true);
return;
}
if (ELEM(this->mode, DRWContext::SELECT_EDIT_MESH)) {
this->view_data_active->edit_select.used = true;
view_data.edit_select.set_used(true);
return;
}
if (ELEM(this->mode, DRWContext::DEPTH)) {
this->view_data_active->grease_pencil.used = gpencil_engine_needed;
this->view_data_active->overlay.used = true;
view_data.grease_pencil.set_used(gpencil_engine_needed);
view_data.overlay.set_used(true);
return;
}
@@ -1080,41 +969,39 @@ void DRWContext::enable_engines(bool gpencil_engine_needed, RenderEngineType *re
switch (drawtype) {
case OB_WIRE:
case OB_SOLID:
view_data.workbench.used = true;
view_data.workbench.set_used(true);
break;
case OB_MATERIAL:
case OB_RENDER:
default:
if (render_engine_type->draw_engine != nullptr) {
if (render_engine_type == &DRW_engine_viewport_eevee_next_type) {
view_data.eevee.used = true;
}
else if (render_engine_type == &DRW_engine_viewport_workbench_type) {
view_data.workbench.used = true;
}
else {
BLI_assert_unreachable();
}
if (render_engine_type == &DRW_engine_viewport_eevee_next_type) {
view_data.eevee.set_used(true);
}
else if (render_engine_type == &DRW_engine_viewport_workbench_type) {
view_data.workbench.set_used(true);
}
else if ((render_engine_type->flag & RE_INTERNAL) == 0) {
view_data.external.used = true;
view_data.external.set_used(true);
}
else {
BLI_assert_unreachable();
}
break;
}
if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
view_data.grease_pencil.used = true;
if ((drawtype >= OB_SOLID) || !use_xray) {
view_data.grease_pencil.set_used(gpencil_engine_needed);
}
if (DRW_state_viewport_compositor_enabled()) {
view_data.compositor.used = true;
view_data.compositor.set_used(true);
}
view_data.overlay.used = true;
view_data.overlay.set_used(true);
#ifdef WITH_DRAW_DEBUG
if (G.debug_value == 31) {
view_data_active.edit_select_debug.used = true;
view_data.edit_select_debug.set_used(true);
}
#endif
}
@@ -1605,21 +1492,9 @@ bool DRW_render_check_grease_pencil(Depsgraph *depsgraph)
return false;
}
static void drw_render_gpencil_to_image(RenderEngine *engine,
RenderLayer *render_layer,
const rcti *rect)
{
DrawEngineType *draw_engine = &draw_engine_gpencil_type;
if (draw_engine->render_to_image) {
ViewportEngineData *gpdata = DRW_view_data_engine_data_get_ensure(drw_get().view_data_active,
draw_engine);
draw_engine->render_to_image(gpdata, engine, render_layer, rect);
}
}
void DRW_render_gpencil(RenderEngine *engine, Depsgraph *depsgraph)
{
using namespace blender::draw;
/* This function should only be called if there are grease pencil objects,
* especially important to avoid failing in background renders without GPU context. */
BLI_assert(DRW_render_check_grease_pencil(depsgraph));
@@ -1655,10 +1530,10 @@ void DRW_render_gpencil(RenderEngine *engine, Depsgraph *depsgraph)
render_view = render_view->next)
{
RE_SetActiveRenderView(render, render_view->name);
drw_render_gpencil_to_image(engine, render_layer, &render_rect);
gpencil::Engine::render_to_image(engine, render_layer, render_rect);
}
blender::draw::command::StateSet::set();
command::StateSet::set();
GPU_depth_test(GPU_DEPTH_NONE);
@@ -1672,13 +1547,15 @@ void DRW_render_gpencil(RenderEngine *engine, Depsgraph *depsgraph)
DRW_render_context_disable(render);
}
void DRW_render_to_image(RenderEngine *engine, Depsgraph *depsgraph)
void DRW_render_to_image(
RenderEngine *engine,
Depsgraph *depsgraph,
std::function<void(RenderEngine *, RenderLayer *, const rcti)> render_view_cb,
std::function<void(RenderResult *)> store_metadata_cb)
{
using namespace blender::draw;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
RenderEngineType *engine_type = engine->type;
DrawEngineType *draw_engine_type = engine_type->draw_engine;
Render *render = engine->re;
/* IMPORTANT: We don't support immediate mode in render mode!
@@ -1694,9 +1571,6 @@ void DRW_render_to_image(RenderEngine *engine, Depsgraph *depsgraph)
/* Init modules ahead of time because the begin_sync happens before DRW_render_object_iter. */
draw_ctx.data->modules_init();
ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(drw_get().view_data_active,
draw_engine_type);
/* Main rendering. */
rctf view_rect;
rcti render_rect;
@@ -1706,7 +1580,7 @@ void DRW_render_to_image(RenderEngine *engine, Depsgraph *depsgraph)
}
/* Reset state before drawing */
blender::draw::command::StateSet::set();
command::StateSet::set();
/* set default viewport */
GPU_viewport(0, 0, draw_ctx.size[0], draw_ctx.size[1]);
@@ -1725,15 +1599,12 @@ void DRW_render_to_image(RenderEngine *engine, Depsgraph *depsgraph)
render_view = render_view->next)
{
RE_SetActiveRenderView(render, render_view->name);
engine_type->draw_engine->render_to_image(data, engine, render_layer, &render_rect);
render_view_cb(engine, render_layer, render_rect);
}
RE_engine_end_result(engine, render_result, false, false, false);
if (engine_type->draw_engine->store_metadata) {
RenderResult *final_render_result = RE_engine_get_result(engine);
engine_type->draw_engine->store_metadata(data, final_render_result);
}
store_metadata_cb(RE_engine_get_result(engine));
GPU_framebuffer_restore();
@@ -1746,13 +1617,10 @@ void DRW_render_to_image(RenderEngine *engine, Depsgraph *depsgraph)
GPU_render_end();
}
void DRW_render_object_iter(void *vedata,
RenderEngine *engine,
Depsgraph *depsgraph,
void (*callback)(void *vedata,
blender::draw::ObjectRef &ob_ref,
RenderEngine *engine,
Depsgraph *depsgraph))
void DRW_render_object_iter(
RenderEngine *engine,
Depsgraph *depsgraph,
std::function<void(blender::draw::ObjectRef &, RenderEngine *, Depsgraph *)> callback)
{
DRWContext &draw_ctx = drw_get();
@@ -1772,7 +1640,7 @@ void DRW_render_object_iter(void *vedata,
if (ob_ref.is_dupli() == false) {
drw_batch_cache_validate(ob);
}
callback(vedata, ob_ref, engine, depsgraph);
callback(ob_ref, engine, depsgraph);
if (ob_ref.is_dupli() == false) {
drw_batch_cache_generate_requested(ob, *extraction.graph);
}
@@ -1782,9 +1650,7 @@ void DRW_render_object_iter(void *vedata,
});
}
void DRW_custom_pipeline_begin(DRWContext &draw_ctx,
DrawEngineType * /*draw_engine_type*/,
Depsgraph * /*depsgraph*/)
void DRW_custom_pipeline_begin(DRWContext &draw_ctx, Depsgraph * /*depsgraph*/)
{
draw_ctx.acquire_data();
draw_ctx.data->modules_init();
@@ -2245,15 +2111,15 @@ void DRW_engines_register()
void DRW_engines_free()
{
DRW_engine_viewport_eevee_next_type.draw_engine->engine_free();
DRW_engine_viewport_workbench_type.draw_engine->engine_free();
draw_engine_gpencil_type.engine_free();
draw_engine_image_type.engine_free();
draw_engine_overlay_next_type.engine_free();
blender::eevee::Engine::free_static();
blender::workbench::Engine::free_static();
blender::draw::gpencil::Engine::free_static();
blender::image_engine::Engine::free_static();
blender::draw::overlay::Engine::free_static();
blender::draw::edit_select::Engine::free_static();
#ifdef WITH_DRAW_DEBUG
draw_engine_debug_select_type.engine_free();
blender::draw::edit_select_debug::Engine::free_static();
#endif
draw_engine_select_type.engine_free();
}
/** \} */

View File

@@ -81,6 +81,9 @@ DRWTextStore *DRW_text_cache_create()
void DRW_text_cache_destroy(DRWTextStore *dt)
{
if (dt == nullptr) {
return;
}
BLI_memiter_destroy(dt->cache_strings);
MEM_freeN(dt);
}

View File

@@ -23,30 +23,9 @@
#include "draw_manager.hh"
#include "draw_view_data.hh"
#include "engines/compositor/compositor_engine.h"
#include "engines/eevee_next/eevee_engine.h"
#include "engines/external/external_engine.h"
#include "engines/gpencil/gpencil_engine.h"
#include "engines/image/image_engine.h"
#include "engines/overlay/overlay_engine.h"
#include "engines/select/select_engine.hh"
#include "engines/workbench/workbench_engine.h"
using namespace blender;
DRWViewData::DRWViewData()
: eevee(DRW_engine_viewport_eevee_next_type.draw_engine),
workbench(DRW_engine_viewport_workbench_type.draw_engine),
external(&draw_engine_external_type),
image(&draw_engine_image_type),
grease_pencil(&draw_engine_gpencil_type),
overlay(&draw_engine_overlay_next_type),
object_select(&draw_engine_select_next_type),
edit_select(&draw_engine_select_type),
#ifdef WITH_DRAW_DEBUG
edit_select_debug(DRW_engine_viewport_select_type.draw_engine),
#endif
compositor(&draw_engine_compositor_type)
{
manager = new draw::Manager();
};
@@ -57,13 +36,6 @@ DRWViewData::~DRWViewData()
delete manager;
};
draw::TextureFromPool &DRW_view_data_pass_texture_get(DRWViewData *view_data,
const char *pass_name)
{
return *view_data->viewport_compositor_passes.lookup_or_add_cb(
pass_name, [&]() { return std::make_unique<draw::TextureFromPool>(pass_name); });
}
void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewport *viewport)
{
int active_view = GPU_viewport_active_view_get(viewport);
@@ -103,22 +75,6 @@ void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewpo
});
}
static void draw_viewport_engines_data_clear(ViewportEngineData *data, bool clear_instance_data)
{
DrawEngineType *engine_type = data->draw_engine;
if (clear_instance_data && data->instance_data) {
BLI_assert(engine_type->instance_free != nullptr);
engine_type->instance_free(data->instance_data);
data->instance_data = nullptr;
}
if (data->text_draw_cache) {
DRW_text_cache_destroy(data->text_draw_cache);
data->text_draw_cache = nullptr;
}
}
void DRWViewData::clear(bool free_instance_data)
{
GPU_FRAMEBUFFER_FREE_SAFE(this->dfbl.default_fb);
@@ -135,9 +91,15 @@ void DRWViewData::clear(bool free_instance_data)
}
GPU_TEXTURE_FREE_SAFE(this->dtxl.depth_in_front);
foreach_engine([&](ViewportEngineData *data, DrawEngineType * /*engine*/) {
draw_viewport_engines_data_clear(data, free_instance_data);
});
if (free_instance_data) {
foreach_engine([&](DrawEngine::Pointer &ptr) {
if (ptr.instance) {
/* TODO Move where it belongs. */
DRW_text_cache_destroy(ptr.instance->text_draw_cache);
ptr.free_instance();
}
});
}
}
void DRWViewData::texture_list_size_validate(const blender::int2 &size)
@@ -148,28 +110,9 @@ void DRWViewData::texture_list_size_validate(const blender::int2 &size)
}
}
ViewportEngineData *DRW_view_data_engine_data_get_ensure(DRWViewData *view_data,
DrawEngineType *engine_type)
{
ViewportEngineData *result = nullptr;
view_data->foreach_engine([&](ViewportEngineData *data, DrawEngineType *engine) {
if (engine_type == engine) {
result = data;
}
});
return result;
}
void DRW_view_data_use_engine(DRWViewData *view_data, DrawEngineType *engine_type)
{
ViewportEngineData *engine = DRW_view_data_engine_data_get_ensure(view_data, engine_type);
engine->used = true;
}
void DRW_view_data_reset(DRWViewData *view_data)
{
view_data->foreach_enabled_engine(
[&](ViewportEngineData *data, DrawEngineType * /*engine*/) { data->used = false; });
view_data->foreach_enabled_engine([&](DrawEngine &instance) { instance.used = false; });
for (std::unique_ptr<draw::TextureFromPool> &texture :
view_data->viewport_compositor_passes.values())
@@ -181,23 +124,15 @@ void DRW_view_data_reset(DRWViewData *view_data)
void DRW_view_data_free_unused(DRWViewData *view_data)
{
view_data->foreach_engine([&](ViewportEngineData *data, DrawEngineType * /*engine*/) {
if (data->used == false) {
draw_viewport_engines_data_clear(data, false);
view_data->foreach_engine([&](DrawEngine::Pointer &ptr) {
if (ptr.instance && ptr.instance->used == false) {
/* TODO Move where it belongs. */
DRW_text_cache_destroy(ptr.instance->text_draw_cache);
ptr.free_instance();
}
});
}
DefaultFramebufferList *DRW_view_data_default_framebuffer_list_get(DRWViewData *view_data)
{
return &view_data->dfbl;
}
DefaultTextureList *DRW_view_data_default_texture_list_get(DRWViewData *view_data)
{
return &view_data->dtxl;
}
draw::Manager *DRW_manager_get()
{
BLI_assert(drw_get().view_data_active->manager);

View File

@@ -17,6 +17,15 @@
#include "DRW_render.hh"
#include "draw_context_private.hh"
#include "engines/compositor/compositor_engine.h"
#include "engines/eevee_next/eevee_engine.h"
#include "engines/external/external_engine.h"
#include "engines/gpencil/gpencil_engine.hh"
#include "engines/image/image_engine.h"
#include "engines/overlay/overlay_engine.h"
#include "engines/select/select_engine.hh"
#include "engines/workbench/workbench_engine.h"
#define GPU_INFO_SIZE 512 /* IMA_MAX_RENDER_TEXT_SIZE */
namespace blender::draw {
@@ -24,40 +33,12 @@ class TextureFromPool;
class Manager;
} // namespace blender::draw
struct DrawEngineType;
struct DRWTextStore;
struct GPUFrameBuffer;
struct GPUTexture;
struct GPUViewport;
struct ListBase;
struct ViewportEngineData {
/* Not owning pointer to the draw engine. */
DrawEngineType *draw_engine;
/**
* \brief Memory block that can be freely used by the draw engine.
* When used the draw engine must implement #DrawEngineType.instance_free callback.
*/
void *instance_data = nullptr;
char info[GPU_INFO_SIZE] = {'\0'};
/* we may want to put this elsewhere */
DRWTextStore *text_draw_cache = nullptr;
bool used = false;
ViewportEngineData(DrawEngineType *engine_type) : draw_engine(engine_type) {}
};
struct ViewportEngineData_Info {
int fbl_len;
int txl_len;
int psl_len;
int stl_len;
};
/* Buffer and textures used by the viewport by default */
struct DefaultFramebufferList {
GPUFrameBuffer *default_fb;
@@ -86,19 +67,18 @@ struct DRWViewData {
blender::int2 texture_list_size = {0, 0};
/* Engines running for this viewport. nullptr if not enabled. */
/* TODO(fclem): Directly use each engine class. */
ViewportEngineData eevee;
ViewportEngineData workbench;
ViewportEngineData external;
ViewportEngineData image;
ViewportEngineData grease_pencil;
ViewportEngineData overlay;
ViewportEngineData object_select;
ViewportEngineData edit_select;
blender::eevee::Engine eevee;
blender::workbench::Engine workbench;
blender::draw::external::Engine external;
blender::image_engine::Engine image;
blender::draw::gpencil::Engine grease_pencil;
blender::draw::overlay::Engine overlay;
blender::draw::select::Engine object_select;
blender::draw::edit_select::Engine edit_select;
#ifdef WITH_DRAW_DEBUG
ViewportEngineData edit_select_debug;
blender::draw::edit_select_debug::Engine edit_select_debug;
#endif
ViewportEngineData compositor;
blender::draw::compositor_engine::Engine compositor;
/* Stores passes needed by the viewport compositor. Engines are expected to populate those in
* every redraw using calls to the DRW_viewport_pass_texture_get function. The compositor can
@@ -121,32 +101,32 @@ struct DRWViewData {
/* IMPORTANT: Order here defines the draw order. */
/* Render engines. Output to the render result framebuffer. Mutually exclusive. */
callback(&eevee, eevee.draw_engine);
callback(&workbench, workbench.draw_engine);
callback(&external, external.draw_engine);
callback(&image, image.draw_engine);
callback(eevee);
callback(workbench);
callback(external);
callback(image);
#ifdef WITH_DRAW_DEBUG
callback(&edit_select_debug, edit_select_debug.draw_engine);
callback(edit_select_debug);
#endif
/* Grease pencil. Merge its output to the render result framebuffer. */
callback(&grease_pencil, grease_pencil.draw_engine);
callback(grease_pencil);
/* GPU compositor. Processes render result and output to the render result framebuffer. */
callback(&compositor, compositor.draw_engine);
callback(compositor);
/* Overlays. Draw on a separate overlay framebuffer. Can read render result. */
callback(&overlay, overlay.draw_engine);
callback(overlay);
/* Selection. Are always enabled alone and have no interaction with other engines. */
callback(&object_select, object_select.draw_engine);
callback(&edit_select, edit_select.draw_engine);
callback(object_select);
callback(edit_select);
}
template<typename CallbackT> void foreach_enabled_engine(CallbackT callback)
{
foreach_engine([&](ViewportEngineData *data, DrawEngineType *engine) {
if (!data->used) {
foreach_engine([&](DrawEngine::Pointer &ptr) {
if (ptr.instance == nullptr || ptr.instance->used == false) {
return;
}
callback(data, engine);
callback(*ptr.instance);
});
}
@@ -154,18 +134,6 @@ struct DRWViewData {
void clear(bool free_instance_data);
};
/* Returns a TextureFromPool stored in the given view data for the pass identified by the given
* pass name. Engines should call this function for each of the passes needed by the viewport
* compositor in every redraw, then it should allocate the texture and write the pass data to it.
* The texture should cover the entire viewport. */
blender::draw::TextureFromPool &DRW_view_data_pass_texture_get(DRWViewData *view_data,
const char *pass_name);
void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewport *viewport);
ViewportEngineData *DRW_view_data_engine_data_get_ensure(DRWViewData *view_data,
DrawEngineType *engine_type);
void DRW_view_data_use_engine(DRWViewData *view_data, DrawEngineType *engine_type);
void DRW_view_data_reset(DRWViewData *view_data);
void DRW_view_data_free_unused(DRWViewData *view_data);
DefaultFramebufferList *DRW_view_data_default_framebuffer_list_get(DRWViewData *view_data);
DefaultTextureList *DRW_view_data_default_texture_list_get(DRWViewData *view_data);