Compare commits
14 Commits
tmp-workbe
...
tmp-worben
Author | SHA1 | Date | |
---|---|---|---|
2da005902f | |||
693dffb7b7 | |||
aef43d1461 | |||
a9d716fa0f | |||
6f28259ea3 | |||
a6b383bd69 | |||
c2022d6d36 | |||
0ea4baa94d | |||
49e9d105f0 | |||
89e114fa70 | |||
a53e560ca5 | |||
64b87737d6 | |||
b33634f8fa | |||
1b20a9d383 |
@@ -269,7 +269,10 @@ class ShadowPass {
|
||||
ShadowView();
|
||||
|
||||
protected:
|
||||
virtual void compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze);
|
||||
virtual void compute_visibility(ObjectMatricesBuf &matrices,
|
||||
ObjectBoundsBuf &bounds,
|
||||
uint resource_len,
|
||||
bool debug_freeze);
|
||||
virtual VisibilityBuf &get_visibility_buffer();
|
||||
} view_ = {};
|
||||
|
||||
|
@@ -201,7 +201,8 @@ void ShadowPass::ShadowView::set_mode(ShadowPass::PassType type)
|
||||
current_pass_type_ = type;
|
||||
}
|
||||
|
||||
void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
|
||||
void ShadowPass::ShadowView::compute_visibility(ObjectMatricesBuf &matrices,
|
||||
ObjectBoundsBuf &bounds,
|
||||
uint resource_len,
|
||||
bool debug_freeze)
|
||||
{
|
||||
@@ -231,6 +232,8 @@ void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
|
||||
GPU_storagebuf_clear(visibility_buf_, GPU_R32UI, GPU_DATA_UINT, &data);
|
||||
}
|
||||
|
||||
/* TODO(Miguel Pozo): Disabled in the optimization branch */
|
||||
do_visibility_ = false;
|
||||
if (do_visibility_) {
|
||||
/* TODO(Miguel Pozo): Use regular culling for the caps pass */
|
||||
|
||||
@@ -249,6 +252,7 @@ void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
|
||||
GPU_shader_uniform_3fv(shader, "shadow_direction", light_direction_);
|
||||
GPU_uniformbuf_bind(extruded_frustum_,
|
||||
GPU_shader_get_uniform_block(shader, "extruded_frustum"));
|
||||
GPU_storagebuf_bind(matrices, GPU_shader_get_ssbo(shader, "matrix_buf"));
|
||||
GPU_storagebuf_bind(bounds, GPU_shader_get_ssbo(shader, "bounds_buf"));
|
||||
if (current_pass_type_ == ShadowPass::FORCED_FAIL) {
|
||||
GPU_storagebuf_bind(visibility_buf_, GPU_shader_get_ssbo(shader, "visibility_buf"));
|
||||
|
@@ -1007,8 +1007,10 @@ void DRW_cache_free_old_batches(Main *bmain)
|
||||
|
||||
static void drw_engines_init(void)
|
||||
{
|
||||
DRW_stats_group_start("drw_engines.engines_init");
|
||||
DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) {
|
||||
PROFILE_START(stime);
|
||||
DRW_stats_group_start(engine->idname);
|
||||
|
||||
const DrawEngineDataSize *data_size = engine->vedata_size;
|
||||
memset(data->psl->passes, 0, sizeof(*data->psl->passes) * data_size->psl_len);
|
||||
@@ -1017,15 +1019,19 @@ static void drw_engines_init(void)
|
||||
engine->engine_init(data);
|
||||
}
|
||||
|
||||
DRW_stats_group_end();
|
||||
PROFILE_END_UPDATE(data->init_time, stime);
|
||||
}
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
static void drw_engines_cache_init(void)
|
||||
{
|
||||
DRW_stats_group_start("drw_engines.cache_init");
|
||||
DRW_manager_begin_sync();
|
||||
|
||||
DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) {
|
||||
DRW_stats_group_start(engine->idname);
|
||||
if (data->text_draw_cache) {
|
||||
DRW_text_cache_destroy(data->text_draw_cache);
|
||||
data->text_draw_cache = NULL;
|
||||
@@ -1037,7 +1043,9 @@ static void drw_engines_cache_init(void)
|
||||
if (engine->cache_init) {
|
||||
engine->cache_init(data);
|
||||
}
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
static void drw_engines_world_update(Scene *scene)
|
||||
@@ -1045,12 +1053,16 @@ static void drw_engines_world_update(Scene *scene)
|
||||
if (scene->world == NULL) {
|
||||
return;
|
||||
}
|
||||
DRW_stats_group_start("drw_engines.world_update");
|
||||
|
||||
DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) {
|
||||
if (engine->id_update) {
|
||||
DRW_stats_group_start(engine->idname);
|
||||
engine->id_update(data, &scene->world->id);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
}
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
static void drw_engines_cache_populate(Object *ob)
|
||||
@@ -1091,17 +1103,22 @@ static void drw_engines_cache_populate(Object *ob)
|
||||
|
||||
static void drw_engines_cache_finish(void)
|
||||
{
|
||||
DRW_stats_group_start("drw_engines.cache_finish");
|
||||
DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) {
|
||||
if (engine->cache_finish) {
|
||||
DRW_stats_group_start(engine->idname);
|
||||
engine->cache_finish(data);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
}
|
||||
|
||||
DRW_manager_end_sync();
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
static void drw_engines_draw_scene(void)
|
||||
{
|
||||
DRW_stats_group_start("drw_engines.draw_scene");
|
||||
DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) {
|
||||
PROFILE_START(stime);
|
||||
if (engine->draw_scene) {
|
||||
@@ -1117,6 +1134,7 @@ static void drw_engines_draw_scene(void)
|
||||
}
|
||||
/* Reset state after drawing */
|
||||
DRW_state_reset();
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
static void drw_engines_draw_text(void)
|
||||
@@ -1674,6 +1692,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
|
||||
drw_context_state_init();
|
||||
|
||||
drw_manager_init(&DST, viewport, NULL);
|
||||
|
||||
DRW_viewport_colormanagement_set(viewport);
|
||||
|
||||
const int object_type_exclude_viewport = v3d->object_type_exclude_viewport;
|
||||
@@ -1712,6 +1731,8 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
|
||||
|
||||
/* Only iterate over objects for internal engines or when overlays are enabled */
|
||||
if (do_populate_loop) {
|
||||
DRW_stats_group_start("drw_render.populate_loop");
|
||||
|
||||
DST.dupli_origin = NULL;
|
||||
DST.dupli_origin_data = NULL;
|
||||
DEGObjectIterSettings deg_iter_settings = {0};
|
||||
@@ -1733,6 +1754,8 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
|
||||
drw_engines_cache_populate(ob);
|
||||
}
|
||||
DEG_OBJECT_ITER_END;
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
drw_duplidata_free();
|
||||
|
@@ -166,7 +166,7 @@ void Manager::submit(PassMain &pass, View &view)
|
||||
bool freeze_culling = (U.experimental.use_viewport_debug && DST.draw_ctx.v3d &&
|
||||
(DST.draw_ctx.v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0);
|
||||
|
||||
view.compute_visibility(bounds_buf, resource_len_, freeze_culling);
|
||||
view.compute_visibility(matrix_buf, bounds_buf, resource_len_, freeze_culling);
|
||||
|
||||
command::RecordingState state;
|
||||
state.inverted_view = view.is_inverted();
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_wrapper.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_pbvh.h"
|
||||
@@ -695,7 +696,7 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob)
|
||||
drw_call_calc_orco(ob, ob_infos->orcotexfac);
|
||||
/* Random float value. */
|
||||
uint random = (DST.dupli_source) ?
|
||||
DST.dupli_source->random_id :
|
||||
DST.dupli_source->random_id :
|
||||
/* TODO(fclem): this is rather costly to do at runtime. Maybe we can
|
||||
* put it in ob->runtime and make depsgraph ensure it is up to date. */
|
||||
BLI_hash_int_2d(BLI_hash_string(ob->id.name + 2), 0);
|
||||
@@ -719,26 +720,41 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob)
|
||||
|
||||
static void drw_call_culling_init(DRWCullingState *cull, Object *ob)
|
||||
{
|
||||
const BoundBox *bbox;
|
||||
if (ob != nullptr && (bbox = BKE_object_boundbox_get(ob))) {
|
||||
float corner[3];
|
||||
/* Get BoundSphere center and radius from the BoundBox. */
|
||||
mid_v3_v3v3(cull->bsphere.center, bbox->vec[0], bbox->vec[6]);
|
||||
mul_v3_m4v3(corner, ob->object_to_world, bbox->vec[0]);
|
||||
mul_m4_v3(ob->object_to_world, cull->bsphere.center);
|
||||
cull->bsphere.radius = len_v3v3(cull->bsphere.center, corner);
|
||||
/* Bypass test */
|
||||
cull->bsphere.radius = -1.0f;
|
||||
/* Reset user data */
|
||||
cull->user_data = nullptr;
|
||||
|
||||
if (ob != nullptr) {
|
||||
if (ob->type == OB_MESH) {
|
||||
/* Optimization: Retrieve the mesh cached min max directly.
|
||||
* Avoids allocating a BoundBox on every sample for each DupliObject instance.
|
||||
* TODO(Miguel Pozo): Remove once T92963 or T96968 are done */
|
||||
float3 min, max;
|
||||
INIT_MINMAX(min, max);
|
||||
BKE_mesh_wrapper_minmax(static_cast<Mesh *>(ob->data), min, max);
|
||||
|
||||
/* Get BoundSphere center and radius from min/max. */
|
||||
float3 min_world = float4x4(ob->object_to_world) * min;
|
||||
float3 max_world = float4x4(ob->object_to_world) * max;
|
||||
|
||||
mid_v3_v3v3(cull->bsphere.center, min_world, max_world);
|
||||
cull->bsphere.radius = len_v3v3(cull->bsphere.center, max_world);
|
||||
}
|
||||
else if (const BoundBox *bbox = BKE_object_boundbox_get(ob)) {
|
||||
float corner[3];
|
||||
/* Get BoundSphere center and radius from the BoundBox. */
|
||||
mid_v3_v3v3(cull->bsphere.center, bbox->vec[0], bbox->vec[6]);
|
||||
mul_v3_m4v3(corner, ob->object_to_world, bbox->vec[0]);
|
||||
mul_m4_v3(ob->object_to_world, cull->bsphere.center);
|
||||
cull->bsphere.radius = len_v3v3(cull->bsphere.center, corner);
|
||||
}
|
||||
|
||||
/* Bypass test for very large objects (see T67319). */
|
||||
if (UNLIKELY(cull->bsphere.radius > 1e12)) {
|
||||
cull->bsphere.radius = -1.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Bypass test. */
|
||||
cull->bsphere.radius = -1.0f;
|
||||
}
|
||||
/* Reset user data */
|
||||
cull->user_data = nullptr;
|
||||
}
|
||||
|
||||
static DRWResourceHandle drw_resource_handle_new(float (*obmat)[4], Object *ob)
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_duplilist.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_wrapper.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_volume.h"
|
||||
#include "BLI_hash.h"
|
||||
@@ -82,15 +83,6 @@ inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active
|
||||
SET_FLAG_FROM_TEST(
|
||||
flag, ref.object->transflag & OB_NEG_SCALE, eObjectInfoFlag::OBJECT_NEGATIVE_SCALE);
|
||||
|
||||
if (ref.dupli_object == nullptr) {
|
||||
/* TODO(fclem): this is rather costly to do at draw time. Maybe we can
|
||||
* put it in ob->runtime and make depsgraph ensure it is up to date. */
|
||||
random = BLI_hash_int_2d(BLI_hash_string(ref.object->id.name + 2), 0) *
|
||||
(1.0f / (float)0xFFFFFFFF);
|
||||
}
|
||||
else {
|
||||
random = ref.dupli_object->random_id * (1.0f / (float)0xFFFFFFFF);
|
||||
}
|
||||
/* Default values. Set if needed. */
|
||||
random = 0.0f;
|
||||
|
||||
@@ -155,51 +147,54 @@ inline std::ostream &operator<<(std::ostream &stream, const ObjectInfos &infos)
|
||||
|
||||
inline void ObjectBounds::sync()
|
||||
{
|
||||
bounding_sphere.w = -1.0f; /* Disable test. */
|
||||
test_enabled = false;
|
||||
}
|
||||
|
||||
inline void ObjectBounds::sync(Object &ob)
|
||||
{
|
||||
const BoundBox *bbox = BKE_object_boundbox_get(&ob);
|
||||
if (bbox == nullptr) {
|
||||
bounding_sphere.w = -1.0f; /* Disable test. */
|
||||
return;
|
||||
float3 min, max;
|
||||
INIT_MINMAX(min, max);
|
||||
|
||||
if (ob.type == OB_MESH) {
|
||||
/* Optimization: Retrieve the mesh cached min max directly.
|
||||
* Avoids allocating a BoundBox on every sample for each DupliObject instance.
|
||||
* TODO(Miguel Pozo): Remove once T92963 or T96968 are done */
|
||||
BKE_mesh_wrapper_minmax(static_cast<Mesh *>(ob.data), min, max);
|
||||
}
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[0]) = bbox->vec[0];
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[1]) = bbox->vec[4];
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[2]) = bbox->vec[3];
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[3]) = bbox->vec[1];
|
||||
bounding_sphere.w = 0.0f; /* Enable test. */
|
||||
else {
|
||||
const BoundBox *bbox = BKE_object_boundbox_get(&ob);
|
||||
if (bbox == nullptr) {
|
||||
test_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const float3 &corner : bbox->vec) {
|
||||
minmax_v3v3_v3(min, max, corner);
|
||||
}
|
||||
}
|
||||
|
||||
size = (max - min) / 2.0f;
|
||||
center = min + size;
|
||||
test_enabled = true;
|
||||
}
|
||||
|
||||
inline void ObjectBounds::sync(const float3 ¢er, const float3 &size)
|
||||
{
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[0]) = center - size;
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[1]) = center + float3(+size.x, -size.y, -size.z);
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[2]) = center + float3(-size.x, +size.y, -size.z);
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[3]) = center + float3(-size.x, -size.y, +size.z);
|
||||
bounding_sphere.w = 0.0; /* Enable test. */
|
||||
this->center = center;
|
||||
this->size = size;
|
||||
test_enabled = true;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const ObjectBounds &bounds)
|
||||
{
|
||||
stream << "ObjectBounds(";
|
||||
if (bounds.bounding_sphere.w == -1.0f) {
|
||||
if (!bounds.test_enabled) {
|
||||
stream << "skipped)" << std::endl;
|
||||
return stream;
|
||||
}
|
||||
stream << std::endl;
|
||||
stream << ".bounding_corners[0]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[0]) << std::endl;
|
||||
stream << ".bounding_corners[1]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[1]) << std::endl;
|
||||
stream << ".bounding_corners[2]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[2]) << std::endl;
|
||||
stream << ".bounding_corners[3]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[3]) << std::endl;
|
||||
stream << ".sphere=(pos=" << float3(bounds.bounding_sphere)
|
||||
<< ", rad=" << bounds.bounding_sphere.w << std::endl;
|
||||
stream << ")" << std::endl;
|
||||
stream << ".center" << bounds.center << std::endl;
|
||||
stream << ".size" << bounds.size << std::endl;
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
@@ -175,15 +175,11 @@ struct ObjectInfos {
|
||||
BLI_STATIC_ASSERT_ALIGN(ObjectInfos, 16)
|
||||
|
||||
struct ObjectBounds {
|
||||
/**
|
||||
* Uploaded as vertex (0, 4, 3, 1) of the bbox in local space, matching XYZ axis order.
|
||||
* Then processed by GPU and stored as (0, 4-0, 3-0, 1-0) in world space for faster culling.
|
||||
*/
|
||||
float4 bounding_corners[4];
|
||||
/** Bounding sphere derived from the bounding corner. Computed on GPU. */
|
||||
float4 bounding_sphere;
|
||||
/** Radius of the inscribed sphere derived from the bounding corner. Computed on GPU. */
|
||||
#define _inner_sphere_radius bounding_corners[3].w
|
||||
float3 center;
|
||||
bool test_enabled;
|
||||
/* TODO(Miguel Pozo): This is actually half size */
|
||||
float3 size;
|
||||
uint _pad;
|
||||
|
||||
#if !defined(GPU_SHADER) && defined(__cplusplus)
|
||||
void sync();
|
||||
|
@@ -227,7 +227,10 @@ void View::bind()
|
||||
GPU_uniformbuf_bind(culling_, DRW_VIEW_CULLING_UBO_SLOT);
|
||||
}
|
||||
|
||||
void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze)
|
||||
void View::compute_visibility(ObjectMatricesBuf &matrices,
|
||||
ObjectBoundsBuf &bounds,
|
||||
uint resource_len,
|
||||
bool debug_freeze)
|
||||
{
|
||||
if (debug_freeze && frozen_ == false) {
|
||||
data_freeze_[0] = static_cast<ViewMatrices>(data_[0]);
|
||||
@@ -264,6 +267,7 @@ void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool d
|
||||
GPU_shader_uniform_1i(shader, "resource_len", resource_len);
|
||||
GPU_shader_uniform_1i(shader, "view_len", view_len_);
|
||||
GPU_shader_uniform_1i(shader, "visibility_word_per_draw", word_per_draw);
|
||||
GPU_storagebuf_bind(matrices, GPU_shader_get_ssbo(shader, "matrix_buf"));
|
||||
GPU_storagebuf_bind(bounds, GPU_shader_get_ssbo(shader, "bounds_buf"));
|
||||
GPU_storagebuf_bind(visibility_buf_, GPU_shader_get_ssbo(shader, "visibility_buf"));
|
||||
GPU_uniformbuf_bind(frozen_ ? data_freeze_ : data_, DRW_VIEW_UBO_SLOT);
|
||||
|
@@ -26,6 +26,7 @@ namespace blender::draw {
|
||||
class Manager;
|
||||
|
||||
/* TODO: de-duplicate. */
|
||||
using ObjectMatricesBuf = StorageArrayBuffer<ObjectMatrices, 128>;
|
||||
using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
|
||||
using VisibilityBuf = StorageArrayBuffer<uint, 4, true>;
|
||||
|
||||
@@ -135,7 +136,10 @@ class View {
|
||||
protected:
|
||||
/** Called from draw manager. */
|
||||
void bind();
|
||||
virtual void compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze);
|
||||
virtual void compute_visibility(ObjectMatricesBuf &matrices,
|
||||
ObjectBoundsBuf &bounds,
|
||||
uint resource_len,
|
||||
bool debug_freeze);
|
||||
virtual VisibilityBuf &get_visibility_buffer();
|
||||
|
||||
void update_viewport_size();
|
||||
|
@@ -123,312 +123,138 @@ IsectFrustum isect_data_setup(Frustum shape)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name View Intersection functions.
|
||||
* \{ */
|
||||
|
||||
bool intersect_view(Pyramid pyramid)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Pyramid vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 5; ++v) {
|
||||
float test = dot(drw_view_culling.planes[p], vec4(pyramid.corners[v], 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Pyramid planes. */
|
||||
IsectPyramid i_pyramid = isect_data_setup(pyramid);
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_pyramid.planes[p], vec4(drw_view_culling.corners[v].xyz, 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect_view(Box box)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Box vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(drw_view_culling.planes[p], vec4(box.corners[v], 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Box planes. */
|
||||
IsectBox i_box = isect_data_setup(box);
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_box.planes[p], vec4(drw_view_culling.corners[v].xyz, 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect_view(IsectBox i_box)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Box vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(drw_view_culling.planes[p], vec4(i_box.corners[v], 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_box.planes[p], vec4(drw_view_culling.corners[v].xyz, 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect_view(Sphere sphere)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
for (int p = 0; p < 6 && intersects; ++p) {
|
||||
float dist_to_plane = dot(drw_view_culling.planes[p], vec4(sphere.center, 1.0));
|
||||
if (dist_to_plane < -sphere.radius) {
|
||||
intersects = false;
|
||||
}
|
||||
}
|
||||
/* TODO reject false positive. */
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Shape vs. Shape Intersection functions.
|
||||
* \{ */
|
||||
|
||||
bool intersect(IsectPyramid i_pyramid, Box box)
|
||||
bool intersect(IsectPyramid i_pyramid, IsectBox i_box)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Box vertices vs Pyramid planes. */
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
bool separating_axis = true;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_pyramid.planes[p], vec4(box.corners[v], 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
float signed_distance = point_plane_projection_dist(i_box.corners[v], i_pyramid.planes[p]);
|
||||
if (signed_distance <= 0.0) {
|
||||
separating_axis = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
if (separating_axis) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Pyramid vertices vs Box planes. */
|
||||
IsectBox i_box = isect_data_setup(box);
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
bool separating_axis = true;
|
||||
for (int v = 0; v < 5; ++v) {
|
||||
float test = dot(i_box.planes[p], vec4(i_pyramid.corners[v], 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
float signed_distance = point_plane_projection_dist(i_pyramid.corners[v], i_box.planes[p]);
|
||||
if (signed_distance <= 0.0) {
|
||||
separating_axis = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
if (separating_axis) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool intersect(IsectPyramid i_pyramid, Box box)
|
||||
{
|
||||
return intersect(i_pyramid, isect_data_setup(box));
|
||||
}
|
||||
|
||||
bool intersect(IsectFrustum i_frustum, Pyramid pyramid)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Pyramid vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
bool separating_axis = true;
|
||||
for (int v = 0; v < 5; ++v) {
|
||||
float test = dot(i_frustum.planes[p], vec4(pyramid.corners[v], 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
float signed_distance = point_plane_projection_dist(pyramid.corners[v], i_frustum.planes[p]);
|
||||
if (signed_distance <= 0.0) {
|
||||
separating_axis = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
if (separating_axis) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Pyramid planes. */
|
||||
IsectPyramid i_pyramid = isect_data_setup(pyramid);
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
bool separating_axis = true;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_pyramid.planes[p], vec4(i_frustum.corners[v].xyz, 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
float signed_distance = point_plane_projection_dist(i_frustum.corners[v].xyz,
|
||||
i_pyramid.planes[p]);
|
||||
if (signed_distance <= 0.0) {
|
||||
separating_axis = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
if (separating_axis) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool intersect(IsectFrustum i_frustum, IsectBox i_box)
|
||||
{
|
||||
/* Do Box vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool separating_axis = true;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float signed_distance = point_plane_projection_dist(i_box.corners[v], i_frustum.planes[p]);
|
||||
if (signed_distance <= 0.0) {
|
||||
separating_axis = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (separating_axis) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Box planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool separating_axis = true;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float signed_distance = point_plane_projection_dist(i_frustum.corners[v].xyz,
|
||||
i_box.planes[p]);
|
||||
if (signed_distance <= 0.0) {
|
||||
separating_axis = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (separating_axis) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool intersect(IsectFrustum i_frustum, Box box)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Box vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_frustum.planes[p], vec4(box.corners[v], 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Box planes. */
|
||||
IsectBox i_box = isect_data_setup(box);
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_box.planes[p], vec4(i_frustum.corners[v].xyz, 1.0));
|
||||
if (test > 0.0) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return intersects;
|
||||
return intersect(i_frustum, isect_data_setup(box));
|
||||
}
|
||||
|
||||
bool intersect(IsectFrustum i_frustum, Sphere sphere)
|
||||
{
|
||||
bool intersects = true;
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
float dist_to_plane = dot(i_frustum.planes[p], vec4(sphere.center, 1.0));
|
||||
if (dist_to_plane < -sphere.radius) {
|
||||
intersects = false;
|
||||
break;
|
||||
float signed_distance = point_plane_projection_dist(sphere.center, i_frustum.planes[p]);
|
||||
if (signed_distance > sphere.radius) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool intersect(Cone cone, Sphere sphere)
|
||||
@@ -454,7 +280,7 @@ bool intersect(Cone cone, Sphere sphere)
|
||||
* only in the monotonic region [0 .. M_PI / 2]. This saves costly acos() calls. */
|
||||
bool intersects = (cone_sphere_center_cos >= cone_sphere_angle_sum_cos);
|
||||
|
||||
return intersects;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool intersect(Circle circle_a, Circle circle_b)
|
||||
@@ -464,3 +290,41 @@ bool intersect(Circle circle_a, Circle circle_b)
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name View Intersection functions.
|
||||
* \{ */
|
||||
|
||||
IsectFrustum IsectFrustum_from_view()
|
||||
{
|
||||
IsectFrustum result;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
result.corners[i] = drw_view_culling.corners[i].xyz;
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
result.planes[i] = drw_view_culling.planes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool intersect_view(Pyramid pyramid)
|
||||
{
|
||||
return intersect(IsectFrustum_from_view(), pyramid);
|
||||
}
|
||||
|
||||
bool intersect_view(Box box)
|
||||
{
|
||||
return intersect(IsectFrustum_from_view(), box);
|
||||
}
|
||||
|
||||
bool intersect_view(IsectBox i_box)
|
||||
{
|
||||
return intersect(IsectFrustum_from_view(), i_box);
|
||||
}
|
||||
|
||||
bool intersect_view(Sphere sphere)
|
||||
{
|
||||
return intersect(IsectFrustum_from_view(), sphere);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -5,6 +5,9 @@
|
||||
/** \name Math intersection & projection functions.
|
||||
* \{ */
|
||||
|
||||
/* WARNING: For legacy reasons,
|
||||
* planes in Blender are represented with the projection component (w) flipped. */
|
||||
|
||||
vec4 plane_from_quad(vec3 v0, vec3 v1, vec3 v2, vec3 v3)
|
||||
{
|
||||
vec3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3));
|
||||
@@ -17,6 +20,11 @@ vec4 plane_from_tri(vec3 v0, vec3 v1, vec3 v2)
|
||||
return vec4(nor, -dot(nor, v2));
|
||||
}
|
||||
|
||||
float point_plane_projection_dist(vec3 point, vec4 plane)
|
||||
{
|
||||
return -dot(vec4(point, 1), plane);
|
||||
}
|
||||
|
||||
float point_plane_projection_dist(vec3 line_origin, vec3 plane_origin, vec3 plane_normal)
|
||||
{
|
||||
return dot(plane_normal, plane_origin - line_origin);
|
||||
|
@@ -14,6 +14,8 @@ void main()
|
||||
|
||||
mat4 model_mat = matrix_buf[resource_id].model;
|
||||
ObjectInfos infos = infos_buf[resource_id];
|
||||
|
||||
#if 0
|
||||
ObjectBounds bounds = bounds_buf[resource_id];
|
||||
|
||||
if (bounds.bounding_sphere.w != -1.0) {
|
||||
@@ -51,6 +53,7 @@ void main()
|
||||
bounds_buf[resource_id].bounding_sphere.w = -1.0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 loc = infos.orco_add; /* Box center. */
|
||||
vec3 size = infos.orco_mul; /* Box half-extent. */
|
||||
|
@@ -160,8 +160,9 @@ GPU_SHADER_CREATE_INFO(draw_visibility_compute)
|
||||
.do_static_compilation(true)
|
||||
.local_group_size(DRW_VISIBILITY_GROUP_SIZE)
|
||||
.define("DRW_VIEW_LEN", "64")
|
||||
.storage_buf(0, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "uint", "visibility_buf[]")
|
||||
.storage_buf(0, Qualifier::READ, "ObjectMatrices", "matrix_buf[]")
|
||||
.storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "uint", "visibility_buf[]")
|
||||
.push_constant(Type::INT, "resource_len")
|
||||
.push_constant(Type::INT, "view_len")
|
||||
.push_constant(Type::INT, "visibility_word_per_draw")
|
||||
|
@@ -26,25 +26,31 @@ void main()
|
||||
return;
|
||||
}
|
||||
|
||||
mat4 model_mat = matrix_buf[gl_GlobalInvocationID.x].model;
|
||||
ObjectBounds bounds = bounds_buf[gl_GlobalInvocationID.x];
|
||||
|
||||
if (bounds.bounding_sphere.w != -1.0) {
|
||||
IsectBox box = isect_data_setup(bounds.bounding_corners[0].xyz,
|
||||
bounds.bounding_corners[1].xyz,
|
||||
bounds.bounding_corners[2].xyz,
|
||||
bounds.bounding_corners[3].xyz);
|
||||
Sphere bounding_sphere = Sphere(bounds.bounding_sphere.xyz, bounds.bounding_sphere.w);
|
||||
Sphere inscribed_sphere = Sphere(bounds.bounding_sphere.xyz, bounds._inner_sphere_radius);
|
||||
if (bounds.test_enabled) {
|
||||
vec3 origin = transform_point(model_mat, bounds.center - bounds.size);
|
||||
vec3 side_x = transform_point(model_mat, bounds.center + bounds.size * vec3(1, -1, -1)) -
|
||||
origin;
|
||||
vec3 side_y = transform_point(model_mat, bounds.center + bounds.size * vec3(-1, 1, -1)) -
|
||||
origin;
|
||||
vec3 side_z = transform_point(model_mat, bounds.center + bounds.size * vec3(-1, -1, 1)) -
|
||||
origin;
|
||||
|
||||
vec3 center = origin + ((side_x + side_y + side_z) / 2.0f);
|
||||
float radius = distance(origin, center);
|
||||
float inscribed_radius = min(min(bounds.size.x, bounds.size.y), bounds.size.z);
|
||||
|
||||
IsectBox box = isect_data_setup(origin, side_x, side_y, side_z);
|
||||
Sphere bounding_sphere = Sphere(center, radius);
|
||||
Sphere inscribed_sphere = Sphere(center, inscribed_radius);
|
||||
|
||||
for (drw_view_id = 0; drw_view_id < view_len; drw_view_id++) {
|
||||
if (intersect_view(inscribed_sphere) == true) {
|
||||
/* Visible. */
|
||||
if (intersect_view(inscribed_sphere)) {
|
||||
/* Visible */
|
||||
}
|
||||
else if (intersect_view(bounding_sphere) == false) {
|
||||
/* Not visible. */
|
||||
mask_visibility_bit(drw_view_id);
|
||||
}
|
||||
else if (intersect_view(box) == false) {
|
||||
else if (intersect_view(bounding_sphere) == false || intersect_view(box) == false) {
|
||||
/* Not visible. */
|
||||
mask_visibility_bit(drw_view_id);
|
||||
}
|
||||
|
@@ -141,6 +141,8 @@ void GLContext::activate()
|
||||
bound_ubo_slots = 0;
|
||||
|
||||
immActivate();
|
||||
|
||||
process_frame_timings();
|
||||
}
|
||||
|
||||
void GLContext::deactivate()
|
||||
|
@@ -95,6 +95,21 @@ class GLContext : public Context {
|
||||
/** #GLBackend owns this data. */
|
||||
GLSharedOrphanLists &shared_orphan_list_;
|
||||
|
||||
struct TimeQuery {
|
||||
std::string name;
|
||||
GLuint handles[2];
|
||||
int stack_depth;
|
||||
bool finished;
|
||||
int64_t cpu_start;
|
||||
float cpu_time;
|
||||
};
|
||||
struct FrameQueries {
|
||||
Vector<TimeQuery> queries;
|
||||
};
|
||||
Vector<FrameQueries> frame_timings;
|
||||
|
||||
void process_frame_timings();
|
||||
|
||||
public:
|
||||
GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list);
|
||||
~GLContext();
|
||||
|
@@ -80,7 +80,7 @@ static void APIENTRY debug_callback(GLenum /*source*/,
|
||||
const bool use_color = CLG_color_support_get(&LOG);
|
||||
|
||||
if (ELEM(severity, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION)) {
|
||||
if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= CLG_SEVERITY_INFO)) {
|
||||
if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level <= CLG_SEVERITY_INFO)) {
|
||||
const char *format = use_color ? "\033[2m%s\033[0m" : "%s";
|
||||
CLG_logf(LOG.type, CLG_SEVERITY_INFO, "Notification", "", format, message);
|
||||
}
|
||||
@@ -131,6 +131,10 @@ static void APIENTRY debug_callback(GLenum /*source*/,
|
||||
|
||||
void init_gl_callbacks()
|
||||
{
|
||||
if (G.log.level >= CLG_SEVERITY_LEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
CLOG_ENSURE(&LOG);
|
||||
|
||||
char msg[256] = "";
|
||||
@@ -362,6 +366,9 @@ namespace blender::gpu {
|
||||
* Useful for debugging through render-doc. This makes all the API calls grouped into "passes".
|
||||
* \{ */
|
||||
|
||||
#define PROFILE_DEBUG_GROUPS 0
|
||||
#define MAX_DEBUG_GROUPS_STACK_DEPTH 4
|
||||
|
||||
void GLContext::debug_group_begin(const char *name, int index)
|
||||
{
|
||||
if ((G.debug & G_DEBUG_GPU) &&
|
||||
@@ -369,6 +376,23 @@ void GLContext::debug_group_begin(const char *name, int index)
|
||||
/* Add 10 to avoid collision with other indices from other possible callback layers. */
|
||||
index += 10;
|
||||
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, index, -1, name);
|
||||
|
||||
#if PROFILE_DEBUG_GROUPS
|
||||
if (frame_timings.is_empty()) {
|
||||
frame_timings.append({});
|
||||
}
|
||||
|
||||
TimeQuery query = {};
|
||||
query.finished = false;
|
||||
query.name = name;
|
||||
query.stack_depth = debug_stack.size();
|
||||
glGetInteger64v(GL_TIMESTAMP, &query.cpu_start);
|
||||
|
||||
/* Use GL_TIMESTAMP instead of GL_ELAPSED_TIME to support nested debug groups */
|
||||
glGenQueries(2, query.handles);
|
||||
glQueryCounter(query.handles[0], GL_TIMESTAMP);
|
||||
frame_timings.last().queries.append(query);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,9 +401,96 @@ void GLContext::debug_group_end()
|
||||
if ((G.debug & G_DEBUG_GPU) &&
|
||||
(epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug"))) {
|
||||
glPopDebugGroup();
|
||||
|
||||
#if PROFILE_DEBUG_GROUPS
|
||||
Vector<TimeQuery> &queries = frame_timings.last().queries;
|
||||
for (int i = queries.size() - 1; i >= 0; i--) {
|
||||
TimeQuery &query = queries[i];
|
||||
if (!query.finished) {
|
||||
glQueryCounter(query.handles[1], GL_TIMESTAMP);
|
||||
query.finished = true;
|
||||
int64_t cpu_end;
|
||||
glGetInteger64v(GL_TIMESTAMP, &cpu_end);
|
||||
query.cpu_time = (cpu_end - query.cpu_start) / 1000000.0;
|
||||
break;
|
||||
}
|
||||
BLI_assert(i != 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GLContext::process_frame_timings()
|
||||
{
|
||||
#if PROFILE_DEBUG_GROUPS
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
for (int frame_i = 0; frame_i < frame_timings.size(); frame_i++) {
|
||||
Vector<TimeQuery> &queries = frame_timings[frame_i].queries;
|
||||
if (queries.is_empty() || !queries.last().finished /* Group begin/end mismatch */) {
|
||||
frame_timings.remove(frame_i--);
|
||||
continue;
|
||||
}
|
||||
|
||||
GLint ready = 0;
|
||||
glGetQueryObjectiv(queries.last().handles[1], GL_QUERY_RESULT_AVAILABLE, &ready);
|
||||
if (!ready) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::stringstream result;
|
||||
result << "\n";
|
||||
// clang-format off
|
||||
result << " Group | GPU | CPU | Latency\n";
|
||||
result << "--------------------------------|------|------|--------\n";
|
||||
result << " Total | ";
|
||||
// clang-format on
|
||||
GLuint64 begin_timestamp = 0;
|
||||
GLuint64 end_timestamp = 0;
|
||||
glGetQueryObjectui64v(queries.first().handles[0], GL_QUERY_RESULT, &begin_timestamp);
|
||||
glGetQueryObjectui64v(queries.last().handles[1], GL_QUERY_RESULT, &end_timestamp);
|
||||
|
||||
float gpu_total_time = (end_timestamp - begin_timestamp) / 1000000.0;
|
||||
result << std::to_string(gpu_total_time).substr(0, 4) << " | ";
|
||||
|
||||
float cpu_total_time = (queries.last().cpu_start - queries.first().cpu_start) / 1000000.0 +
|
||||
queries.last().cpu_time;
|
||||
result << std::to_string(cpu_total_time).substr(0, 4) << " | \n";
|
||||
|
||||
for (TimeQuery &query : queries) {
|
||||
if (query.stack_depth >= MAX_DEBUG_GROUPS_STACK_DEPTH) {
|
||||
glDeleteQueries(2, query.handles);
|
||||
continue;
|
||||
}
|
||||
GLuint64 begin_timestamp = 0;
|
||||
GLuint64 end_timestamp = 0;
|
||||
glGetQueryObjectui64v(query.handles[0], GL_QUERY_RESULT, &begin_timestamp);
|
||||
glGetQueryObjectui64v(query.handles[1], GL_QUERY_RESULT, &end_timestamp);
|
||||
glDeleteQueries(2, query.handles);
|
||||
|
||||
result << std::string(query.stack_depth, '.');
|
||||
result << " " << query.name
|
||||
<< std::string(max_ii(0, 30 - query.stack_depth - query.name.length()), ' ')
|
||||
<< " | ";
|
||||
|
||||
float gpu_time = (end_timestamp - begin_timestamp) / 1000000.0;
|
||||
|
||||
result << std::to_string(gpu_time).substr(0, 4) << " | ";
|
||||
result << std::to_string(query.cpu_time).substr(0, 4) << " | ";
|
||||
result << std::to_string((begin_timestamp - query.cpu_start) / 1000000.0).substr(0, 4)
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
std::string print = result.str();
|
||||
printf("%s", print.c_str());
|
||||
|
||||
frame_timings.remove(frame_i--);
|
||||
}
|
||||
|
||||
frame_timings.append({});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
@@ -385,7 +385,9 @@ void GLTexture::clear(eGPUDataFormat data_format, const void *data)
|
||||
{
|
||||
BLI_assert(validate_data_format(format_, data_format));
|
||||
|
||||
if (GLContext::clear_texture_support) {
|
||||
/* ClearTexImage can be up to 10 times slower */
|
||||
const bool USE_CLEAR_TEX_IMAGE = false;
|
||||
if (USE_CLEAR_TEX_IMAGE && GLContext::clear_texture_support) {
|
||||
int mip = 0;
|
||||
GLenum gl_format = to_gl_data_format(format_);
|
||||
GLenum gl_type = to_gl(data_format);
|
||||
|
Reference in New Issue
Block a user