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:
committed by
Clément Foucault
parent
f98bf61eac
commit
da92505c7d
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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){};
|
||||
|
||||
Reference in New Issue
Block a user