Overlay-Next: Anti-Aliasing

Straight-forward port except for the frame-buffers and textures
management. This will be described inside the documentation.

Rel #102179

Pull Request: https://projects.blender.org/blender/blender/pulls/125984
This commit is contained in:
Clément Foucault
2024-08-07 17:35:24 +02:00
committed by Clément Foucault
parent f98bf61eac
commit da92505c7d
6 changed files with 166 additions and 41 deletions

View File

@@ -289,6 +289,7 @@ set(SRC
engines/image/image_texture_info.hh
engines/image/image_usage.hh
engines/overlay/overlay_engine.h
engines/overlay/overlay_next_antialiasing.hh
engines/overlay/overlay_next_background.hh
engines/overlay/overlay_next_bounds.hh
engines/overlay/overlay_next_empty.hh

View File

@@ -0,0 +1,84 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Overlay antialiasing:
*
* Most of the overlays are wires which causes a lot of flickering in motions
* due to aliasing problems.
*
* Our goal is to have a technique that works with single sample per pixel
* to avoid extra cost of managing MSAA or additional texture buffers and jitters.
*
* To solve this we use a simple and effective post-process AA. The technique
* goes like this:
*
* - During wireframe rendering, we output the line color, the line direction
* and the distance from the line for the pixel center.
*
* - Then, in a post process pass, for each pixels we gather all lines in a search area
* that could cover (even partially) the center pixel.
* We compute the coverage of each line and do a sorted alpha compositing of them.
*
* This technique has one major shortcoming compared to MSAA:
* - It handles (initial) partial visibility poorly (because of single sample). This makes
* overlapping / crossing wires a bit too thin at their intersection.
* Wireframe meshes overlaid over solid meshes can have half of the edge missing due to
* z-fighting (this has workaround).
* Another manifestation of this, is flickering of really dense wireframe if using small
* line thickness (also has workaround).
*
* The pros of this approach are many:
* - Works without geometry shader.
* - Can inflate line thickness.
* - Coverage is very close to perfect and can even be filtered (Blackman-Harris, gaussian).
* - Wires can "bleed" / overlap non-line objects since the filter is in screen-space.
* - Only uses one additional lightweight full-screen buffer (compared to MSAA/SMAA).
* - No convergence time (compared to TAA).
*/
#pragma once
#include "overlay_next_private.hh"
namespace blender::draw::overlay {
class AntiAliasing {
private:
PassSimple anti_aliasing_ps_ = {"AntiAliasing"};
public:
void begin_sync(Resources &res)
{
if (res.selection_type != SelectionType::DISABLED) {
anti_aliasing_ps_.init();
return;
}
const bool do_smooth_lines = (U.gpu_flag & USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE) != 0;
{
PassSimple &pass = anti_aliasing_ps_;
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL);
pass.shader_set(res.shaders.anti_aliasing.get());
pass.framebuffer_set(&res.overlay_output_fb);
pass.bind_ubo("globalsBlock", &res.globals_buf);
pass.bind_texture("depthTex", &res.depth_tx);
pass.bind_texture("colorTex", &res.overlay_tx);
pass.bind_texture("lineTex", &res.line_tx);
pass.push_constant("doSmoothLines", do_smooth_lines);
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
}
void draw(Manager &manager)
{
manager.submit(anti_aliasing_ps_);
}
};
} // namespace blender::draw::overlay

View File

@@ -67,6 +67,12 @@ class Background {
}
bg_ps_.init();
bg_ps_.framebuffer_set(&res.overlay_output_fb);
/* Don't clear background for the node editor. The node editor draws the background and we
* need to mask out the image from the already drawn overlay color buffer. */
if (state.space_type != SPACE_NODE) {
bg_ps_.clear_color(float4(0.0f));
}
bg_ps_.state_set(pass_state);
bg_ps_.shader_set(res.shaders.background_fill.get());
bg_ps_.bind_ubo("globalsBlock", &res.globals_buf);
@@ -85,9 +91,8 @@ class Background {
}
}
void draw(Resources &res, Manager &manager)
void draw(Manager &manager)
{
GPU_framebuffer_bind(res.overlay_color_only_fb);
manager.submit(bg_ps_);
}
};

View File

@@ -93,6 +93,8 @@ void Instance::begin_sync()
begin_sync_layer(infront);
grid.begin_sync(resources, state, view);
anti_aliasing.begin_sync(resources);
}
void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
@@ -188,6 +190,21 @@ void Instance::end_sync()
};
end_sync_layer(regular);
end_sync_layer(infront);
/* WORKAROUND: This prevents bad frame-buffer config inside workbench when xray is enabled.
* Better find a solution to this chicken-egg problem. */
{
/* HACK we allocate the in front depth here to avoid the overhead when if is not needed. */
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRW_texture_ensure_fullscreen_2d(
&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, DRWTextureFlag(0));
GPU_framebuffer_ensure_config(
&dfbl->in_front_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)});
}
}
void Instance::draw(Manager &manager)
@@ -202,56 +219,59 @@ void Instance::draw(Manager &manager)
const DRWView *view_legacy = DRW_view_default_get();
View view("OverlayView", view_legacy);
/* TODO: Better semantics using a switch? */
if (!resources.color_overlay_tx.is_valid()) {
/* Likely to be the selection case. Allocate dummy texture and bind only depth buffer. */
resources.line_tx.acquire(int2(1, 1), GPU_RGBA8);
resources.color_overlay_alloc_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
resources.color_render_alloc_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
resources.color_overlay_tx.wrap(resources.color_overlay_alloc_tx);
resources.color_render_tx.wrap(resources.color_render_alloc_tx);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
/* Create it but shouldn't even be used. */
resources.overlay_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
}
else {
resources.line_tx.acquire(render_size, GPU_RGBA8);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx),
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
resources.overlay_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
}
/* TODO(fclem): Remove mandatory allocation. */
if (!resources.depth_in_front_tx.is_valid()) {
resources.depth_in_front_alloc_tx.acquire(render_size, GPU_DEPTH_COMPONENT24);
resources.depth_in_front_tx.wrap(resources.depth_in_front_alloc_tx);
}
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
resources.overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx),
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
/* TODO: Better semantics using a switch? */
if (!resources.color_overlay_tx.is_valid()) {
/* Likely to be the selection case. Allocate dummy texture and bind only depth buffer. */
resources.color_overlay_alloc_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
resources.color_render_alloc_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
GPU_framebuffer_bind(resources.overlay_color_only_fb);
resources.color_overlay_tx.wrap(resources.color_overlay_alloc_tx);
resources.color_render_tx.wrap(resources.color_render_alloc_tx);
resources.line_tx.acquire(int2(1, 1), GPU_RGBA8);
resources.overlay_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
}
else {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
GPU_TEXTURE_USAGE_ATTACHMENT;
resources.line_tx.acquire(render_size, GPU_RGBA8, usage);
resources.overlay_tx.acquire(render_size, GPU_SRGB8_A8, usage);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx),
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
resources.overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx),
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
}
resources.overlay_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx));
resources.overlay_output_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
float4 clear_color(0.0f);
GPU_framebuffer_clear_color(resources.overlay_color_only_fb, clear_color);
GPU_framebuffer_bind(resources.overlay_line_fb);
GPU_framebuffer_clear_color(resources.overlay_line_fb, clear_color);
regular.prepass.draw(resources.overlay_line_fb, manager, view);
infront.prepass.draw(resources.overlay_line_in_front_fb, manager, view);
background.draw(resources, manager);
auto draw_layer = [&](OverlayLayer &layer, Framebuffer &framebuffer) {
layer.bounds.draw(framebuffer, manager, view);
layer.cameras.draw(framebuffer, manager, view);
@@ -270,9 +290,12 @@ void Instance::draw(Manager &manager)
/* TODO(: Breaks selection on M1 Max. */
// infront.lattices.draw(resources.overlay_line_in_front_fb, manager, view);
// anti_aliasing.draw(resources, manager, view);
/* Drawn onto the output framebuffer. */
background.draw(manager);
anti_aliasing.draw(manager);
resources.line_tx.release();
resources.overlay_tx.release();
resources.depth_in_front_alloc_tx.release();
resources.color_overlay_alloc_tx.release();
resources.color_render_alloc_tx.release();

View File

@@ -10,6 +10,7 @@
#include "overlay_next_private.hh"
#include "overlay_next_antialiasing.hh"
#include "overlay_next_background.hh"
#include "overlay_next_bounds.hh"
#include "overlay_next_camera.hh"
@@ -60,6 +61,8 @@ class Instance {
Grid grid;
AntiAliasing anti_aliasing;
Instance(const SelectionType selection_type) : selection_type_(selection_type){};
~Instance()

View File

@@ -143,6 +143,7 @@ class ShaderModule {
ShaderPtr grid = shader("overlay_grid");
ShaderPtr background_fill = shader("overlay_background");
ShaderPtr background_clip_bound = shader("overlay_clipbound");
ShaderPtr anti_aliasing = shader("overlay_antialiasing");
/** Selectable Shaders */
ShaderPtr armature_sphere_outline;
@@ -180,8 +181,15 @@ struct Resources : public select::SelectMap {
Framebuffer overlay_color_only_fb = {"overlay_color_only_fb"};
Framebuffer overlay_line_fb = {"overlay_line_fb"};
Framebuffer overlay_line_in_front_fb = {"overlay_line_in_front_fb"};
Framebuffer overlay_output_fb = {"overlay_output_fb"};
/* Target containing line direction and data for line expansion and anti-aliasing. */
TextureFromPool line_tx = {"line_tx"};
/* Target containing overlay color before anti-aliasing. */
TextureFromPool overlay_tx = {"overlay_tx"};
/* Texture that are usually allocated inside. These are fallback when they aren't.
* They are then wrapped inside the #TextureRefs below. */
TextureFromPool depth_in_front_alloc_tx = {"overlay_depth_in_front_tx"};
TextureFromPool color_overlay_alloc_tx = {"overlay_color_overlay_alloc_tx"};
TextureFromPool color_render_alloc_tx = {"overlay_color_render_alloc_tx"};
@@ -191,11 +199,12 @@ struct Resources : public select::SelectMap {
GlobalsUboStorage theme_settings;
/* References, not owned. */
GPUUniformBuf *globals_buf;
TextureRef weight_ramp_tx;
/* Wrappers around #DefaultTextureList members. */
TextureRef depth_tx;
TextureRef depth_in_front_tx;
TextureRef color_overlay_tx;
TextureRef color_render_tx;
TextureRef weight_ramp_tx;
Resources(const SelectionType selection_type_, ShaderModule &shader_module)
: select::SelectMap(selection_type_), shaders(shader_module){};