Files
test/source/blender/draw/intern/draw_view.hh

264 lines
7.7 KiB
C++

/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup draw
*
* View description and states.
*
* A `draw::View` object is required for drawing geometry using the DRW API and its internal
* culling system.
*
* One `View` object can actually contain multiple view matrices if the template parameter
* `view_len` is greater than 1. This is called multi-view rendering and the vertex shader must
* setting `drw_view_id` accordingly.
*/
#include "DNA_view3d_types.h"
#include "DRW_gpu_wrapper.hh"
#include "GPU_matrix.hh"
#include "draw_shader_shared.hh"
#include <atomic>
#include <cstdint>
namespace blender::draw {
class Manager;
/* TODO: de-duplicate. */
using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
using ObjectInfosBuf = StorageArrayBuffer<ObjectInfos, 128>;
using VisibilityBuf = StorageArrayBuffer<uint, 4, true>;
class View {
friend Manager;
/** Number of sync done by views. Used for fingerprint. */
static std::atomic<uint32_t> global_sync_counter_;
/* Local sync counter. Used for fingerprint. */
uint32_t sync_counter_ = 0;
protected:
/** TODO(fclem): Maybe try to reduce the minimum cost if the number of view is lower. */
UniformArrayBuffer<ViewMatrices, DRW_VIEW_MAX> data_;
UniformArrayBuffer<ViewCullingData, DRW_VIEW_MAX> culling_;
/** Frozen version of data_ used for debugging culling. */
UniformArrayBuffer<ViewMatrices, DRW_VIEW_MAX> data_freeze_;
UniformArrayBuffer<ViewCullingData, DRW_VIEW_MAX> culling_freeze_;
/** Result of the visibility computation. 1 bit or 1 or 2 word per resource ID per view. */
VisibilityBuf visibility_buf_;
/* Fingerprint of the manager state when visibility was computed. */
uint64_t manager_fingerprint_ = 0;
const char *debug_name_;
int view_len_ = 0;
bool is_inverted_ = false;
bool do_visibility_ = true;
bool dirty_ = true;
bool frozen_ = false;
bool procedural_ = false;
public:
View(const char *name, int view_len = 1, bool procedural = false)
: visibility_buf_(name), debug_name_(name), view_len_(view_len), procedural_(procedural)
{
BLI_assert(view_len <= DRW_VIEW_MAX);
}
virtual ~View() = default;
void sync(const float4x4 &view_mat, const float4x4 &win_mat, int view_id = 0);
/** Enable or disable every visibility test (frustum culling, HiZ culling). */
void visibility_test(bool enable)
{
do_visibility_ = enable;
}
/**
* Update culling data using a compute shader.
* This is to be used if the matrices were updated externally
* on the GPU (not using the `sync()` method).
*/
void compute_procedural_bounds();
bool is_persp(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].winmat[3][3] == 0.0f;
}
bool is_inverted(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
UNUSED_VARS_NDEBUG(view_id);
return is_inverted_;
}
float far_clip(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
if (is_persp(view_id)) {
return -data_[view_id].winmat[3][2] / (data_[view_id].winmat[2][2] + 1.0f);
}
return -(data_[view_id].winmat[3][2] - 1.0f) / data_[view_id].winmat[2][2];
}
float near_clip(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
if (is_persp(view_id)) {
return -data_[view_id].winmat[3][2] / (data_[view_id].winmat[2][2] - 1.0f);
}
return -(data_[view_id].winmat[3][2] + 1.0f) / data_[view_id].winmat[2][2];
}
const float3 &location(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].viewinv.location();
}
const float3 &forward(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].viewinv.z_axis();
}
const float4x4 &viewmat(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].viewmat;
}
const float4x4 &viewinv(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].viewinv;
}
const float4x4 &winmat(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].winmat;
}
const float4x4 &wininv(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].wininv;
}
/* Compute and return the perspective matrix. */
const float4x4 persmat(int view_id = 0) const
{
BLI_assert(view_id < view_len_);
return data_[view_id].winmat * data_[view_id].viewmat;
}
int visibility_word_per_draw() const
{
return (view_len_ == 1) ? 0 : divide_ceil_u(view_len_, 32);
}
UniformArrayBuffer<ViewMatrices, DRW_VIEW_MAX> &matrices_ubo_get()
{
return data_;
}
/* TODO(fclem): Remove. Global DST access. */
static View &default_get();
static void default_set(const float4x4 &view_mat, const float4x4 &win_mat);
/* Data to save per overlay to not rely on rv3d for rendering.
* TODO(fclem): Compute offset directly from the view. */
struct OffsetData {
/* Copy of rv3d->dist. */
float dist = 0.0f;
/* Copy of rv3d->persp. */
char persp = 0;
/* Copy of rv3d->is_persp. */
bool is_persp = false;
OffsetData() = default;
OffsetData(const RegionView3D &rv3d)
: dist(rv3d.dist), persp(rv3d.persp), is_persp(rv3d.is_persp != 0)
{
}
float4x4 winmat_polygon_offset(const float4x4 &winmat, float offset)
{
float view_dist = dist;
/* Special exception for orthographic camera:
* `view_dist` isn't used as the depth range isn't the same. */
if (persp == RV3D_CAMOB && is_persp == false) {
view_dist = 1.0f / max_ff(fabsf(winmat[0][0]), fabsf(winmat[1][1]));
}
float4x4 result = winmat;
result[3][2] -= GPU_polygon_offset_calc(winmat.ptr(), view_dist, offset);
return result;
}
/* Return unit offset to apply to `gl_Position.z`. To be scaled depending on purpose. */
float polygon_offset_factor(const float4x4 &winmat)
{
float view_dist = dist;
/* Special exception for orthographic camera:
* `view_dist` isn't used as the depth range isn't the same. */
if (persp == RV3D_CAMOB && is_persp == false) {
view_dist = 1.0f / max_ff(fabsf(winmat[0][0]), fabsf(winmat[1][1]));
}
return GPU_polygon_offset_calc(winmat.ptr(), view_dist, 1.0);
}
};
/* Returns frustum planes equations. Available only after sync. */
std::array<float4, 6> frustum_planes_get(int view_id = 0);
/* Returns frustum corners positions in world space. Available only after sync. */
std::array<float3, 8> frustum_corners_get(int view_id = 0);
protected:
/** Called from draw manager. */
void bind();
virtual void compute_visibility(ObjectBoundsBuf &bounds,
ObjectInfosBuf &infos,
uint resource_len,
bool debug_freeze);
virtual VisibilityBuf &get_visibility_buffer();
bool has_computed_visibility() const
{
/* NOTE: Even though manager fingerprint is not enough to check for update, it is still
* guaranteed to not be 0. So we can check weather or not this view has computed visibility
* after sync. Asserts will catch invalid usage . */
return manager_fingerprint_ != 0;
}
/* Fingerprint of the view for the current state.
* Not reliable enough for general update detection. Only to be used for debugging assertion. */
uint64_t fingerprint_get() const
{
BLI_assert_msg(sync_counter_ != 0, "View should be synced at least once before use");
return sync_counter_;
}
void update_viewport_size();
/* WARNING: These 3 functions must be called in order */
void frustum_boundbox_calc(int view_id);
void frustum_culling_planes_calc(int view_id);
void frustum_culling_sphere_calc(int view_id);
};
} // namespace blender::draw