Overlay-Next: Initial implementation #107045

Closed
Clément Foucault wants to merge 28 commits from fclem/blender:overlay-next into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
12 changed files with 148 additions and 79 deletions
Showing only changes of commit a9397b5458 - Show all commits

View File

@ -597,6 +597,8 @@ set(GLSL_SRC
engines/select/shaders/select_id_frag.glsl
engines/select/shaders/select_lib.glsl
engines/select/select_shader_shared.hh
engines/basic/shaders/basic_conservative_depth_geom.glsl
engines/basic/shaders/basic_depth_vert.glsl
engines/basic/shaders/basic_depth_vert_conservative_no_geom.glsl

View File

@ -15,8 +15,7 @@
#include "overlay_prepass.hh"
#include "overlay_shape.hh"
#include "../select/select_empty.hh"
#include "../select/select_object.hh"
#include "../select/select_instance.hh"
namespace blender::draw::overlay {
@ -25,7 +24,7 @@ template<
* selectable component and using a special shaders for drawing.
* Making the select engine templated makes it easier to phase out any overhead of the
* selection for the regular non-selection case.*/
typename SelectEngineT = select::EngineEmpty>
typename SelectEngineT = select::Instance>
class Instance {
public:
/* WORKAROUND: Legacy. Move to grid pass. */

View File

@ -88,6 +88,8 @@ template<typename SelectEngineT> class Metaballs {
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
state.clipping_state);
/* NOTE: Use armature sphere outline shader to have perspective correct outline instead of
* just a circle facing the camera. */
pass.shader_set(res.shaders.armature_sphere_outline);
pass.bind_ubo("globalsBlock", &res.globals_buf);
res.select_bind(pass);

View File

@ -46,7 +46,7 @@ template<typename SelectEngineT> class Prepass {
GPUBatch *geom = DRW_cache_object_surface_get(ob_ref.object);
if (geom) {
ResourceHandle res_handle = manager.resource_handle(ob_ref);
pass.draw(geom, res_handle, res.select_id(ob_ref).value);
pass.draw(geom, res_handle, res.select_id(ob_ref).get());
}
}

View File

@ -1236,15 +1236,16 @@ void OVERLAY_shader_free(void)
namespace blender::draw::overlay {
template<>
ShaderModule<select::EngineEmpty> *ShaderModule<select::EngineEmpty>::g_shader_module = nullptr;
ShaderModule<select::Instance> *ShaderModule<select::Instance>::g_shader_module = nullptr;
template<>
ShaderModule<select::EngineObject> *ShaderModule<select::EngineObject>::g_shader_module = nullptr;
ShaderModule<select::InstanceDummy> *ShaderModule<select::InstanceDummy>::g_shader_module =
nullptr;
void shader_module_free()
{
ShaderModule<select::EngineEmpty>::module_free();
ShaderModule<select::EngineObject>::module_free();
ShaderModule<select::Instance>::module_free();
ShaderModule<select::InstanceDummy>::module_free();
}
} // namespace blender::draw::overlay

View File

@ -10,8 +10,7 @@
#include "gpu_shader_create_info.hh"
#include "../select/select_empty.hh"
#include "../select/select_object.hh"
#include "../select/select_instance.hh"
namespace blender::draw::overlay {

View File

@ -6,5 +6,6 @@
#pragma once
#define SELECT_DATA 4
#define SELECT_ID_IN 5
#define SELECT_ID_OUT 6

View File

@ -1,50 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. */
/** \file
* \ingroup draw_engine
*
* Dummy implementation of the select engine types to avoid any overhead.
*/
#pragma once
#include "draw_manager.hh"
#include "gpu_shader_create_info.hh"
namespace blender::draw::select {
struct EngineEmpty {
/* Add type safety to selection ID. Only the select engine should provide them. */
struct ID {};
struct SelectShader {
static void patch(gpu::shader::ShaderCreateInfo &){};
};
struct SelectBuf {
void select_clear(){};
void select_append(ID){};
void select_bind(PassSimple &){};
};
struct SelectMap {
[[nodiscard]] const ID select_id(const ObjectRef &, uint = 0)
{
return {};
}
void begin_sync(){};
void select_bind(PassSimple &){};
void select_bind(PassMain &){};
void end_sync(){};
void read_result(){};
};
};
} // namespace blender::draw::select

View File

@ -4,8 +4,6 @@
/** \file
* \ingroup draw_engine
*
* This is an implementation of the Select engine specialized for selecting object.
* Should plug seamlessly inside the overlay engine logic.
*/
#pragma once
@ -18,19 +16,72 @@
#include "gpu_shader_create_info.hh"
#include "select_defines.h"
#include "select_shader_shared.hh"
namespace blender::draw::select {
struct EngineObject {
/**
* Dummy implementation of the select engine types to avoid any overhead.
* Bypass any selection logic.
*/
struct InstanceDummy {
/* Add type safety to selection ID. Only the select engine should provide them. */
struct ID {
class ID {
public:
constexpr uint32_t get() const
{
return 0;
}
};
struct SelectShader {
static void patch(gpu::shader::ShaderCreateInfo &){};
};
struct SelectBuf {
void select_clear(){};
void select_append(ID){};
void select_bind(PassSimple &){};
};
struct SelectMap {
[[nodiscard]] const ID select_id(const ObjectRef &, uint = 0)
{
return {};
}
void begin_sync(){};
void select_bind(PassSimple &){};
void select_bind(PassMain &){};
void end_sync(){};
void read_result(){};
};
};
/**
* This is an implementation of the Select engine specialized for selecting object.
* Should plug seamlessly inside the overlay engine logic.
*/
struct Instance {
/* Add type safety to selection ID. Only the select engine should provide them. */
class ID {
public:
uint32_t value;
uint32_t get() const
{
return value;
}
};
struct SelectShader {
static void patch(gpu::shader::ShaderCreateInfo &info)
{
info.define("SELECT_UNORDERED");
info.define("SELECT_ENABLE");
/* Replace additional info. */
for (StringRefNull &str : info.additional_infos_) {
if (str == "draw_modelmat_new") {
@ -43,7 +94,7 @@ struct EngineObject {
/**
* Add a dedicated selection id buffer to a pass.
* Use this when not using a #PassMain which can pass the select ID via CustomID.
* To be used when not using a #PassMain which can pass the select ID via CustomID.
*/
struct SelectBuf {
StorageVectorBuffer<uint32_t> select_buf = {"select_buf"};
@ -55,7 +106,7 @@ struct EngineObject {
void select_append(ID select_id)
{
select_buf.append(select_id.value);
select_buf.append(select_id.get());
}
void select_bind(PassSimple &pass)
@ -80,6 +131,8 @@ struct EngineObject {
StorageArrayBuffer<uint> select_output_buf = {"select_output_buf"};
/** Dummy buffer. Might be better to remove, but simplify the shader create info patching. */
StorageArrayBuffer<uint, 4, true> dummy_select_buf = {"dummy_select_buf"};
/** Uniform buffer to bind to all passes to pass information about the selection state. */
UniformBuffer<SelectInfoData> info_buf;
/* TODO(fclem): The sub_object_id id should eventually become some enum or take a sub-object
* reference directly. This would isolate the selection logic to this class. */
@ -93,6 +146,12 @@ struct EngineObject {
return {id};
}
/* Load an invalid index that will not write to the output (not selectable). */
[[nodiscard]] const ID select_invalid_id()
{
return {uint32_t(-1)};
}
void begin_sync()
{
select_id_map.clear();
@ -101,14 +160,18 @@ struct EngineObject {
#endif
}
/** IMPORTANT: Changes the draw state. Need to be called after the pass own state. */
void select_bind(PassSimple &pass)
{
pass.bind_ubo(SELECT_DATA, &info_buf);
pass.bind_ssbo(SELECT_ID_OUT, &select_output_buf);
}
/** IMPORTANT: Changes the draw state. Need to be called after the pass own state. */
void select_bind(PassMain &pass)
{
pass.use_custom_ids = true;
pass.bind_ubo(SELECT_DATA, &info_buf);
/* IMPORTANT: This binds a dummy buffer `in_select_buf` but it is not supposed to be used. */
pass.bind_ssbo(SELECT_ID_IN, &dummy_select_buf);
pass.bind_ssbo(SELECT_ID_OUT, &select_output_buf);
@ -116,6 +179,11 @@ struct EngineObject {
void end_sync()
{
info_buf.mode = SelectType::SELECT_ALL;
/* TODO: Should be select rect center. */
info_buf.cursor = int2(512, 512);
info_buf.push_update();
select_output_buf.resize(ceil_to_multiple_u(select_id_map.size(), 4));
select_output_buf.push_update();
select_output_buf.clear_to_zero();

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef GPU_SHADER
# pragma once
# include "GPU_shader_shared_utils.h"
namespace blender::draw::select {
#endif
/* Matches eV3DSelectMode */
enum SelectType : uint32_t {
SELECT_ALL = 0u,
SELECT_PICK_ALL = 1u,
SELECT_PICK_NEAREST = 2u,
};
struct SelectInfoData {
int2 cursor;
SelectType mode;
uint _pad0;
};
BLI_STATIC_ASSERT_ALIGN(SelectInfoData, 16)
#ifndef GPU_SHADER
} // namespace blender::draw::select
#endif

View File

@ -44,7 +44,11 @@ GPU_SHADER_CREATE_INFO(select_id_uniform_clipped)
/* Used to patch overlay shaders. */
GPU_SHADER_CREATE_INFO(select_id_patch)
.typedef_source("select_shader_shared.hh")
.vertex_out(select_id_iface)
/* Need to make sure the depth & stencil comparison runs before the fragment shader. */
.early_fragment_test(true)
.uniform_buf(SELECT_DATA, "SelectInfoData", "select_info_buf")
/* Select IDs for instanced draw-calls not using #PassMain. */
.storage_buf(SELECT_ID_IN, Qualifier::READ, "int", "in_select_buf[]")
/* Stores the result of the whole selection drawing. Content depends on selection mode. */

View File

@ -1,5 +1,5 @@
#if !(defined(SELECT_UNORDERED) || defined(SELECT_DEPTH_PICKING))
#ifndef SELECT_ENABLE
/* Avoid requesting the select_id when not in selection mode. */
# define select_id_set(select_id)
# define select_id_output(select_id)
@ -16,19 +16,34 @@ void select_id_set(int id)
void select_id_output(int id)
{
# if defined(SELECT_UNORDERED)
/* Used by rectangle selection.
* Set the bit of the select id in the bitmap. */
atomicOr(out_select_buf[id / 32u], 1u << (uint(id) % 32u));
if (id == -1) {
/* Invalid index */
return;
}
# elif defined(SELECT_DEPTH_PICKING)
/* Used by mouse-clicking selection.
* Stores the nearest depth for this select id. */
atomicMin(out_select_buf[id], floatBitsToUint(gl_FragCoord.z));
if (select_info_buf.mode == SELECT_ALL) {
/* Set the bit of the select id in the bitmap. */
atomicOr(out_select_buf[id / 32u], 1u << (uint(id) % 32u));
}
else if (select_info_buf.mode == SELECT_PICK_ALL) {
/* Stores the nearest depth for this select id. */
atomicMin(out_select_buf[id], floatBitsToUint(gl_FragCoord.z));
}
else if (select_info_buf.mode == SELECT_PICK_NEAREST) {
/* Stores the nearest depth with the distance to the cursor. */
# else
# error
# endif
/* Distance function to the cursor. Currently a simple pixel ring distance. */
ivec2 coord = abs(ivec2(gl_FragCoord.xy) - select_info_buf.cursor);
uint dist = uint(max(coord.x, coord.y));
uint depth = uint(gl_FragCoord.z * float(0x00FFFFFFu));
/* Reject hits outside of valid range. */
if (dist < 0xFFu) {
/* Packed values to ensure the atomicMin is performed on the whole result. */
atomicMin(out_select_buf[id], (depth << 8u) | dist);
}
}
}
#endif