From 18110744a26f5eaf9665a5175376cfcf949bbcf4 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 2 May 2025 09:49:09 +0200 Subject: [PATCH] Sequencer: Support HDR in Sequencer Preview The title is pretty self-explanatory: this change brings support of displaying HDR content in the sequencer preview. Before this change it was clamped to the 0..1 range, now it is unclamped sRGB, similar to how image editor, viewport, and nodes backdrop works. The general idea is to draw the sequencer content on a non-overlay frame-buffer and tag viewport as having non-standard input color space as the sequencer operates in a different space. The way it is done mimics what happens from the draw manager side for the nodes backdrop, but bypassing the image engine. Partially because the image engine expects the Image data-block to be displayed, but also because of performance: there are a lot of things going on like float buffer creation, clamping etc. Overall the image engine is not fast enough for the sequencer needs. Code-side changes that worth mentioning to highlight the overall direction for the possible future refactors in the area: - Decouple arguments from the scene: editing, render data, color management etc are now passed as individual arguments. This is an anticipation of story tools project where this data might be coming from a different place. - Move the entire preview region drawing to sequencer_preview_draw.cc Previously logic was split across sequencer_preview_draw.cc and space_sequencer.cc which was quite tricky to know what should go where. - Split functions which had boolean argument to define their behavior into individual functions. Generally if a function has boolean argument used in a way if(foo) { do_something_(); } else { do_something_else() } it is a good indication that the function is to be split. Pull Request: https://projects.blender.org/blender/blender/pulls/138094 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_400.cc | 27 +- .../space_sequencer/sequencer_intern.hh | 20 +- .../space_sequencer/sequencer_preview_draw.cc | 1022 ++++++++++++----- .../space_sequencer/space_sequencer.cc | 175 +-- 5 files changed, 765 insertions(+), 481 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index a2419770354..73a71589b00 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 65 +#define BLENDER_FILE_SUBVERSION 66 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index ebca1e44b39..bb1d28697dc 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -10633,6 +10633,20 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 66)) { + /* Clear unused draw flag (used to be SEQ_DRAW_BACKDROP). */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *space_sequencer = (SpaceSeq *)sl; + space_sequencer->draw_flag &= ~SEQ_DRAW_UNUSED_0; + } + } + } + } + } + /* Always run this versioning (keep at the bottom of the function). Meshes are written with the * legacy format which always needs to be converted to the new format on file load. To be moved * to a subversion check in 5.0. */ @@ -10642,19 +10656,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) rename_mesh_uv_seam_attribute(*mesh); } - /* Clear unused darw flag (used to be SEQ_DRAW_BACKDROP). */ - // TODO: Move into a dedicated subversion check. - LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - if (sl->spacetype == SPACE_SEQ) { - SpaceSeq *space_sequencer = (SpaceSeq *)sl; - space_sequencer->draw_flag &= ~SEQ_DRAW_UNUSED_0; - } - } - } - } - /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/editors/space_sequencer/sequencer_intern.hh b/source/blender/editors/space_sequencer/sequencer_intern.hh index ac7a48dc399..64e5129b170 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.hh +++ b/source/blender/editors/space_sequencer/sequencer_intern.hh @@ -9,6 +9,7 @@ #pragma once #include "BLI_map.hh" +#include "BLI_rect.h" #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_utility_mixins.hh" @@ -29,6 +30,8 @@ struct ARegion; struct ARegionType; +struct ColorManagedViewSettings; +struct ColorManagedDisplaySettings; struct Scene; struct SeqRetimingKey; struct Strip; @@ -127,15 +130,16 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region); /* `sequencer_preview_draw.cc` */ -void sequencer_draw_preview(const bContext *C, - Scene *scene, - ARegion *region, - SpaceSeq *sseq, - int timeline_frame, - int offset, - bool draw_overlay); +/** + * Draw callback for the sequencer preview region. + * + * It is supposed to be set as the draw function of the ARegionType corresponding to the preview + * region. + */ +void sequencer_preview_region_draw(const bContext *C, ARegion *region); + bool sequencer_draw_get_transform_preview(SpaceSeq *sseq, Scene *scene); -int sequencer_draw_get_transform_preview_frame(Scene *scene); +int sequencer_draw_get_transform_preview_frame(const Scene *scene); void sequencer_special_update_set(Strip *strip); /* Get handle width in 2d-View space. */ diff --git a/source/blender/editors/space_sequencer/sequencer_preview_draw.cc b/source/blender/editors/space_sequencer/sequencer_preview_draw.cc index a47eaed906f..d639b89da92 100644 --- a/source/blender/editors/space_sequencer/sequencer_preview_draw.cc +++ b/source/blender/editors/space_sequencer/sequencer_preview_draw.cc @@ -31,6 +31,7 @@ #include "BKE_context.hh" #include "BKE_global.hh" #include "BKE_scene.hh" +#include "BKE_screen.hh" #include "IMB_colormanagement.hh" #include "IMB_imbuf.hh" @@ -49,6 +50,7 @@ #include "ED_sequencer.hh" #include "ED_space_api.hh" #include "ED_util.hh" +#include "ED_view3d.hh" #include "BIF_glutil.hh" @@ -75,7 +77,6 @@ #include "sequencer_scopes.hh" namespace blender::ed::vse { - static Strip *special_seq_update = nullptr; void sequencer_special_update_set(Strip *strip) @@ -177,15 +178,15 @@ ImBuf *sequencer_ibuf_get(const bContext *C, const int timeline_frame, const cha return ibuf; } -static ImBuf *sequencer_make_scope(Scene *scene, - ImBuf *ibuf, +static ImBuf *sequencer_make_scope(const ColorManagedViewSettings &view_settings, + const ColorManagedDisplaySettings &display_settings, + const ImBuf &ibuf, ImBuf *(*make_scope_fn)(const ImBuf *ibuf)) { - ImBuf *display_ibuf = IMB_dupImBuf(ibuf); + ImBuf *display_ibuf = IMB_dupImBuf(&ibuf); ImBuf *scope; - IMB_colormanagement_imbuf_make_display_space( - display_ibuf, &scene->view_settings, &scene->display_settings); + IMB_colormanagement_imbuf_make_display_space(display_ibuf, &view_settings, &display_settings); scope = make_scope_fn(display_ibuf); @@ -194,12 +195,12 @@ static ImBuf *sequencer_make_scope(Scene *scene, return scope; } -static void sequencer_display_size(Scene *scene, float r_viewrect[2]) +static void sequencer_display_size(const RenderData &render_data, float r_viewrect[2]) { - r_viewrect[0] = float(scene->r.xsch); - r_viewrect[1] = float(scene->r.ysch); + r_viewrect[0] = float(render_data.xsch); + r_viewrect[1] = float(render_data.ysch); - r_viewrect[0] *= scene->r.xasp / scene->r.yasp; + r_viewrect[0] *= render_data.xasp / render_data.yasp; } static void sequencer_draw_gpencil_overlay(const bContext *C) @@ -217,14 +218,14 @@ static void sequencer_draw_gpencil_overlay(const bContext *C) /** * Draw content and safety borders. */ -static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, - const View2D *v2d, +static void sequencer_draw_borders_overlay(const SpaceSeq &sseq, + const View2D &v2d, const Scene *scene) { - float x1 = v2d->tot.xmin; - float y1 = v2d->tot.ymin; - float x2 = v2d->tot.xmax; - float y2 = v2d->tot.ymax; + const float x1 = v2d.tot.xmin; + const float y1 = v2d.tot.ymin; + const float x2 = v2d.tot.xmax; + const float y2 = v2d.tot.ymax; GPU_line_width(1.0f); @@ -246,7 +247,7 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, imm_draw_box_wire_2d(shdr_pos, x1 - 0.5f, y1 - 0.5f, x2 + 0.5f, y2 + 0.5f); /* Draw safety border. */ - if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_MARGINS) { + if (sseq.preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_MARGINS) { immUniformThemeColorBlend(TH_VIEW_OVERLAY, TH_BACK, 0.25f); rctf rect; rect.xmin = x1; @@ -255,7 +256,7 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, rect.ymax = y2; UI_draw_safe_areas(shdr_pos, &rect, scene->safe_areas.title, scene->safe_areas.action); - if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_CENTER) { + if (sseq.preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_CENTER) { UI_draw_safe_areas( shdr_pos, &rect, scene->safe_areas.title_center, scene->safe_areas.action_center); @@ -309,73 +310,6 @@ static void seq_prefetch_wm_notify(const bContext *C, Scene *scene) } } -static void *sequencer_OCIO_transform_ibuf(const bContext *C, - ImBuf *ibuf, - bool *r_glsl_used, - eGPUTextureFormat *r_format, - eGPUDataFormat *r_data, - void **r_buffer_cache_handle) -{ - void *display_buffer; - bool force_fallback = false; - *r_glsl_used = false; - force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL); - force_fallback |= (ibuf->dither != 0.0f); - - /* Default */ - *r_format = GPU_RGBA8; - *r_data = GPU_DATA_UBYTE; - - /* Fallback to CPU based color space conversion. */ - if (force_fallback) { - *r_glsl_used = false; - display_buffer = nullptr; - } - else if (ibuf->float_buffer.data) { - display_buffer = ibuf->float_buffer.data; - - *r_data = GPU_DATA_FLOAT; - if (ibuf->channels == 4) { - *r_format = GPU_RGBA32F; - } - else if (ibuf->channels == 3) { - /* Alpha is implicitly 1. */ - *r_format = GPU_RGB32F; - } - else { - BLI_assert_msg(0, "Incompatible number of channels for float buffer in sequencer"); - *r_format = GPU_RGBA32F; - display_buffer = nullptr; - } - - if (ibuf->float_buffer.colorspace) { - *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space_ctx( - C, ibuf->float_buffer.colorspace, ibuf->dither, true); - } - else { - *r_glsl_used = IMB_colormanagement_setup_glsl_draw_ctx(C, ibuf->dither, true); - } - } - else if (ibuf->byte_buffer.data) { - display_buffer = ibuf->byte_buffer.data; - - *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space_ctx( - C, ibuf->byte_buffer.colorspace, ibuf->dither, false); - } - else { - display_buffer = nullptr; - } - - /* If we need to fallback to CPU based display transform, do that here. */ - if ((ibuf->byte_buffer.data || ibuf->float_buffer.data) && !*r_glsl_used) { - display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, r_buffer_cache_handle); - *r_format = GPU_RGBA8; - *r_data = GPU_DATA_UBYTE; - } - - return display_buffer; -} - static void sequencer_stop_running_jobs(const bContext *C, Scene *scene) { if (G.is_rendering == false && (scene->r.seq_prev_type) == OB_RENDER) { @@ -394,102 +328,87 @@ static void sequencer_preview_clear() UI_ThemeClearColor(TH_SEQ_PREVIEW); } -static void sequencer_preview_get_rect( - rctf *preview, Scene *scene, ARegion *region, SpaceSeq *sseq, bool draw_overlay) +/* Semantic utility to get a rectangle with positions that correspond to a full frame drawn in the + * preview region. */ +static rctf preview_get_full_position(const ARegion ®ion) { - View2D *v2d = ®ion->v2d; - float viewrect[2]; - - sequencer_display_size(scene, viewrect); - BLI_rctf_init(preview, -1.0f, 1.0f, -1.0f, 1.0f); - - if (draw_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT)) { - preview->xmax = v2d->tot.xmin + - (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->overlay_frame_rect.xmax); - preview->xmin = v2d->tot.xmin + - (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->overlay_frame_rect.xmin); - preview->ymax = v2d->tot.ymin + - (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->overlay_frame_rect.ymax); - preview->ymin = v2d->tot.ymin + - (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->overlay_frame_rect.ymin); - } - else { - *preview = v2d->tot; - } + return region.v2d.tot; } -static void sequencer_draw_display_buffer(const bContext *C, - Scene *scene, - ARegion *region, - SpaceSeq *sseq, - ImBuf *ibuf, - bool draw_overlay) +/* Semantic utility to generate rectangle with UV coordinates that cover an entire 0 .. 1 + * rectangle. */ +static rctf preview_get_full_texture_coord() { - void *buffer_cache_handle = nullptr; - - if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) { - GPU_blend(GPU_BLEND_ALPHA); - } - - /* Format needs to be created prior to any #immBindShader call. - * Do it here because OCIO binds its own shader. */ - eGPUTextureFormat format; - eGPUDataFormat data; - bool glsl_used = false; - GPUVertFormat *imm_format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint texCoord = GPU_vertformat_attr_add( - imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - void *display_buffer = sequencer_OCIO_transform_ibuf( - C, ibuf, &glsl_used, &format, &data, &buffer_cache_handle); - - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT; - GPUTexture *texture = GPU_texture_create_2d( - "seq_display_buf", ibuf->x, ibuf->y, 1, format, usage, nullptr); - GPU_texture_update(texture, data, display_buffer); - GPU_texture_filter_mode(texture, false); - - GPU_texture_bind(texture, 0); - - if (!glsl_used) { - immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR); - immUniformColor3f(1.0f, 1.0f, 1.0f); - } - - rctf preview; - rctf canvas; - sequencer_preview_get_rect(&preview, scene, region, sseq, draw_overlay); - - if (draw_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT)) { - canvas = scene->ed->overlay_frame_rect; - } - else { - BLI_rctf_init(&canvas, 0.0f, 1.0f, 0.0f, 1.0f); - } - - immRectf_with_texco(pos, texCoord, preview, canvas); - - GPU_texture_unbind(texture); - GPU_texture_free(texture); - - if (!glsl_used) { - immUnbindProgram(); - } - else { - IMB_colormanagement_finish_glsl_draw(); - } - - if (buffer_cache_handle) { - IMB_display_buffer_release(buffer_cache_handle); - } - - if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) { - GPU_blend(GPU_BLEND_NONE); - } + rctf texture_coord; + BLI_rctf_init(&texture_coord, 0.0f, 1.0f, 0.0f, 1.0f); + return texture_coord; } -static void draw_histogram(ARegion *region, +/* Get rectangle positions within preview region that are to be used to draw reference frame. + * + * If the frame overlay is set to RECTANGLE this function returns coordinates of the rectangle + * where partial reference frame is to be drawn. + * + * If the frame overlay is set to REFERENCE this function returns full-frame rectangle, same as + * preview_get_full_position(). + * + * If the frame overlay is set to REFERENCE or is disabled the return value is valid but + * corresponds to an undefined state. + */ +static rctf preview_get_reference_position(const SpaceSeq &space_sequencer, + const Editing &editing, + const ARegion ®ion) +{ + const View2D &v2d = region.v2d; + + BLI_assert(ELEM(space_sequencer.overlay_frame_type, + SEQ_OVERLAY_FRAME_TYPE_RECT, + SEQ_OVERLAY_FRAME_TYPE_REFERENCE)); + + if (space_sequencer.overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT) { + rctf position; + const float xmin = v2d.tot.xmin; + const float ymin = v2d.tot.ymin; + + const float width = BLI_rctf_size_x(&v2d.tot); + const float height = BLI_rctf_size_y(&v2d.tot); + + position.xmax = xmin + width * editing.overlay_frame_rect.xmax; + position.xmin = xmin + width * editing.overlay_frame_rect.xmin; + position.ymax = ymin + height * editing.overlay_frame_rect.ymax; + position.ymin = ymin + height * editing.overlay_frame_rect.ymin; + + return position; + } + + return v2d.tot; +} + +/* Return rectangle with UV coordinates that are to be used to draw reference frame. + * + * If the frame overlay is set to rectangle the return value contains vUV coordinates of the + * rectangle within the reference frame. + * + * If the frame overlay is set to REFERENCE this function returns full-frame UV rectangle, same as + * preview_get_full_texture_coord(). + * + * If the frame overlay is set to REFERENCE or is disabled the return value is valid but + * corresponds to an undefined state. + */ +static rctf preview_get_reference_texture_coord(const SpaceSeq &space_sequencer, + const Editing &editing) +{ + if (space_sequencer.overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT) { + return editing.overlay_frame_rect; + } + + rctf texture_coord; + BLI_rctf_init(&texture_coord, 0.0f, 1.0f, 0.0f, 1.0f); + + return texture_coord; +} + +static void draw_histogram(ARegion ®ion, const ScopeHistogram &hist, SeqQuadsBatch &quads, const rctf &area) @@ -510,9 +429,9 @@ static void draw_histogram(ARegion *region, grid_x_1 = area.xmin + (area.xmax - area.xmin) * ratio_1; } - View2D *v2d = ®ion->v2d; + View2D &v2d = region.v2d; float text_scale_x, text_scale_y; - UI_view2d_scale_get_inverse(v2d, &text_scale_x, &text_scale_y); + UI_view2d_scale_get_inverse(&v2d, &text_scale_x, &text_scale_y); for (int line = 0; line <= 4; line++) { float val = float(line) / 4; @@ -528,7 +447,7 @@ static void draw_histogram(ARegion *region, text_width *= text_scale_x; text_height *= text_scale_y; UI_view2d_text_cache_add( - v2d, x - text_width / 2, area.ymax - text_height * 1.3f, buf, buf_len, col_grid); + &v2d, x - text_width / 2, area.ymax - text_height * 1.3f, buf, buf_len, col_grid); } /* Border. */ @@ -562,7 +481,7 @@ static void draw_histogram(ARegion *region, quads.draw(); GPU_blend(GPU_BLEND_ALPHA); - UI_view2d_text_cache_draw(region); + UI_view2d_text_cache_draw(®ion); } static blender::float2 rgb_to_uv_scaled(const blender::float3 &rgb) @@ -754,15 +673,14 @@ static void draw_vectorscope_graticule(ARegion *region, SeqQuadsBatch &quads, co UI_view2d_text_cache_draw(region); } -static void sequencer_draw_scopes(Scene *scene, ARegion *region, SpaceSeq *sseq) +static void sequencer_draw_scopes(const SpaceSeq &space_sequencer, ARegion ®ion) { /* Figure out draw coordinates. */ - rctf preview; - sequencer_preview_get_rect(&preview, scene, region, sseq, false); + const rctf preview = preview_get_full_position(region); rctf uv; BLI_rctf_init(&uv, 0.0f, 1.0f, 0.0f, 1.0f); - const bool keep_aspect = sseq->mainb == SEQ_DRAW_IMG_VECTORSCOPE; + const bool keep_aspect = space_sequencer.mainb == SEQ_DRAW_IMG_VECTORSCOPE; float vecscope_aspect = 1.0f; if (keep_aspect) { float width = std::max(BLI_rctf_size_x(&preview), 0.1f); @@ -777,12 +695,13 @@ static void sequencer_draw_scopes(Scene *scene, ARegion *region, SpaceSeq *sseq) } SeqQuadsBatch quads; - SeqScopes *scopes = &sseq->runtime->scopes; + const SeqScopes *scopes = &space_sequencer.runtime->scopes; - bool use_blend = sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA; + bool use_blend = space_sequencer.mainb == SEQ_DRAW_IMG_IMBUF && + space_sequencer.flag & SEQ_USE_ALPHA; /* Draw black rectangle over scopes area. */ - if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + if (space_sequencer.mainb != SEQ_DRAW_IMG_IMBUF) { GPU_blend(GPU_BLEND_NONE); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uchar black[4] = {0, 0, 0, 255}; @@ -794,16 +713,16 @@ static void sequencer_draw_scopes(Scene *scene, ARegion *region, SpaceSeq *sseq) /* Draw scope image if there is one. */ ImBuf *scope_image = nullptr; - if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { + if (space_sequencer.mainb == SEQ_DRAW_IMG_IMBUF) { scope_image = scopes->zebra_ibuf; } - else if (sseq->mainb == SEQ_DRAW_IMG_WAVEFORM) { + else if (space_sequencer.mainb == SEQ_DRAW_IMG_WAVEFORM) { scope_image = scopes->waveform_ibuf; } - else if (sseq->mainb == SEQ_DRAW_IMG_VECTORSCOPE) { + else if (space_sequencer.mainb == SEQ_DRAW_IMG_VECTORSCOPE) { scope_image = scopes->vector_ibuf; } - else if (sseq->mainb == SEQ_DRAW_IMG_RGBPARADE) { + else if (space_sequencer.mainb == SEQ_DRAW_IMG_RGBPARADE) { scope_image = scopes->sep_waveform_ibuf; } @@ -856,16 +775,16 @@ static void sequencer_draw_scopes(Scene *scene, ARegion *region, SpaceSeq *sseq) immUnbindProgram(); } - if (sseq->mainb == SEQ_DRAW_IMG_HISTOGRAM) { + if (space_sequencer.mainb == SEQ_DRAW_IMG_HISTOGRAM) { draw_histogram(region, scopes->histogram, quads, preview); } - if (ELEM(sseq->mainb, SEQ_DRAW_IMG_WAVEFORM, SEQ_DRAW_IMG_RGBPARADE)) { + if (ELEM(space_sequencer.mainb, SEQ_DRAW_IMG_WAVEFORM, SEQ_DRAW_IMG_RGBPARADE)) { use_blend = true; - draw_waveform_graticule(region, quads, preview); + draw_waveform_graticule(®ion, quads, preview); } - if (sseq->mainb == SEQ_DRAW_IMG_VECTORSCOPE) { + if (space_sequencer.mainb == SEQ_DRAW_IMG_VECTORSCOPE) { use_blend = true; - draw_vectorscope_graticule(region, quads, preview); + draw_vectorscope_graticule(®ion, quads, preview); } quads.draw(); @@ -875,76 +794,81 @@ static void sequencer_draw_scopes(Scene *scene, ARegion *region, SpaceSeq *sseq) } } -static bool sequencer_calc_scopes(Scene *scene, SpaceSeq *sseq, ImBuf *ibuf) +static bool sequencer_calc_scopes(const SpaceSeq &space_sequencer, + const ColorManagedViewSettings &view_settings, + const ColorManagedDisplaySettings &display_settings, + const ImBuf &ibuf) + { - if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->zebra == 0) { + if (space_sequencer.mainb == SEQ_DRAW_IMG_IMBUF && space_sequencer.zebra == 0) { return false; /* Not drawing any scopes. */ } - SeqScopes *scopes = &sseq->runtime->scopes; - if (scopes->reference_ibuf != ibuf) { + SeqScopes *scopes = &space_sequencer.runtime->scopes; + if (scopes->reference_ibuf != &ibuf) { scopes->cleanup(); } - switch (sseq->mainb) { + switch (space_sequencer.mainb) { case SEQ_DRAW_IMG_IMBUF: if (!scopes->zebra_ibuf) { - - if (ibuf->float_buffer.data) { - ImBuf *display_ibuf = IMB_dupImBuf(ibuf); + if (ibuf.float_buffer.data) { + ImBuf *display_ibuf = IMB_dupImBuf(&ibuf); IMB_colormanagement_imbuf_make_display_space( - display_ibuf, &scene->view_settings, &scene->display_settings); - scopes->zebra_ibuf = make_zebra_view_from_ibuf(display_ibuf, sseq->zebra); + display_ibuf, &view_settings, &display_settings); + scopes->zebra_ibuf = make_zebra_view_from_ibuf(display_ibuf, space_sequencer.zebra); IMB_freeImBuf(display_ibuf); } else { - scopes->zebra_ibuf = make_zebra_view_from_ibuf(ibuf, sseq->zebra); + scopes->zebra_ibuf = make_zebra_view_from_ibuf(&ibuf, space_sequencer.zebra); } } break; case SEQ_DRAW_IMG_WAVEFORM: if (!scopes->waveform_ibuf) { - scopes->waveform_ibuf = sequencer_make_scope(scene, ibuf, make_waveform_view_from_ibuf); + scopes->waveform_ibuf = sequencer_make_scope( + view_settings, display_settings, ibuf, make_waveform_view_from_ibuf); } break; case SEQ_DRAW_IMG_VECTORSCOPE: if (!scopes->vector_ibuf) { - scopes->vector_ibuf = sequencer_make_scope(scene, ibuf, make_vectorscope_view_from_ibuf); + scopes->vector_ibuf = sequencer_make_scope( + view_settings, display_settings, ibuf, make_vectorscope_view_from_ibuf); } break; case SEQ_DRAW_IMG_HISTOGRAM: { - ImBuf *display_ibuf = IMB_dupImBuf(ibuf); + ImBuf *display_ibuf = IMB_dupImBuf(&ibuf); IMB_colormanagement_imbuf_make_display_space( - display_ibuf, &scene->view_settings, &scene->display_settings); + display_ibuf, &view_settings, &display_settings); scopes->histogram.calc_from_ibuf(display_ibuf); IMB_freeImBuf(display_ibuf); } break; case SEQ_DRAW_IMG_RGBPARADE: if (!scopes->sep_waveform_ibuf) { scopes->sep_waveform_ibuf = sequencer_make_scope( - scene, ibuf, make_sep_waveform_view_from_ibuf); + view_settings, display_settings, ibuf, make_sep_waveform_view_from_ibuf); } break; default: /* Future files might have scopes we don't know about. */ return false; } - scopes->reference_ibuf = ibuf; + scopes->reference_ibuf = &ibuf; return true; } -bool sequencer_draw_get_transform_preview(SpaceSeq *sseq, Scene *scene) +bool sequencer_draw_get_transform_preview(const SpaceSeq &sseq, const Scene &scene) { - Strip *last_seq = seq::select_active_get(scene); + Strip *last_seq = seq::select_active_get(&scene); if (last_seq == nullptr) { return false; } return (G.moving & G_TRANSFORM_SEQ) && (last_seq->flag & SELECT) && ((last_seq->flag & SEQ_LEFTSEL) || (last_seq->flag & SEQ_RIGHTSEL)) && - (sseq->draw_flag & SEQ_DRAW_TRANSFORM_PREVIEW); + (sseq.draw_flag & SEQ_DRAW_TRANSFORM_PREVIEW); } -int sequencer_draw_get_transform_preview_frame(Scene *scene) +int sequencer_draw_get_transform_preview_frame(const Scene *scene) { Strip *last_seq = seq::select_active_get(scene); /* #sequencer_draw_get_transform_preview must already have been called. */ @@ -1214,112 +1138,632 @@ static void text_edit_draw(const bContext *C) text_edit_draw_box(C, strip, pos); } -void sequencer_draw_preview(const bContext *C, - Scene *scene, - ARegion *region, - SpaceSeq *sseq, - int timeline_frame, - int offset, - bool draw_overlay) +/* Draw empty preview region. + * The entire region is cleared with the TH_SEQ_PREVIEW color. + * + * Used in cases when there is no editing, or when the display is set to NONE. */ +static void sequencer_preview_draw_empty(ARegion ®ion) { - View2D *v2d = ®ion->v2d; - ImBuf *ibuf = nullptr; - float viewrect[2]; - const bool show_imbuf = check_show_imbuf(*sseq); - const bool draw_gpencil = ((sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_GPENCIL) && sseq->gpd); - const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + GPUViewport *viewport = WM_draw_region_get_bound_viewport(®ion); + BLI_assert(viewport); - sequencer_stop_running_jobs(C, scene); + GPUFrameBuffer *overlay_fb = GPU_viewport_framebuffer_overlay_get(viewport); + GPU_framebuffer_bind_no_srgb(overlay_fb); + + sequencer_preview_clear(); +} + +/* Begin drawing the sequence preview region. + * Initializes the drawing state which is common for color render and overlay drawing. + * + * Returns true if the region is to be drawn. Example is when it is not to be drawn is when there + * is ongoing offline rendering (to avoid possible threading conflict). + * + * If the function returns true preview_draw_end() is to be called after drawing is done. */ +static bool preview_draw_begin(const bContext *C, + const RenderData &render_data, + const ColorManagedViewSettings &view_settings, + const ColorManagedDisplaySettings &display_settings, + ARegion ®ion) +{ + sequencer_stop_running_jobs(C, CTX_data_scene(C)); if (G.is_rendering) { - return; + return false; } - int preview_frame = timeline_frame; - if (sequencer_draw_get_transform_preview(sseq, scene)) { - preview_frame = sequencer_draw_get_transform_preview_frame(scene); - } + GPUViewport *viewport = WM_draw_region_get_bound_viewport(®ion); + BLI_assert(viewport); - /* Get image. */ - ibuf = sequencer_ibuf_get(C, preview_frame + offset, names[sseq->multiview_eye]); + /* Configure color space used by the viewport. + * This also checks for HDR support and enables it for the viewport when found and needed. */ + GPU_viewport_colorspace_set( + viewport, &view_settings, &display_settings, render_data.dither_intensity); - /* Setup off-screen buffers. */ - GPUViewport *viewport = WM_draw_region_get_viewport(region); - GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport); - GPU_framebuffer_bind_no_srgb(framebuffer_overlay); GPU_depth_test(GPU_DEPTH_NONE); - if (sseq->render_size == SEQ_RENDER_SIZE_NONE) { - sequencer_preview_clear(); + /* Setup view. */ + View2D &v2d = region.v2d; + float viewrect[2]; + sequencer_display_size(render_data, viewrect); + UI_view2d_totRect_set(&v2d, roundf(viewrect[0]), roundf(viewrect[1])); + UI_view2d_curRect_validate(&v2d); + UI_view2d_view_ortho(&v2d); + + return true; +} + +static void preview_draw_end(const bContext *C) +{ + UI_view2d_view_restore(C); + seq_prefetch_wm_notify(C, CTX_data_scene(C)); +} + +/* Configure current GPU state to draw on the color render frame-buffer of the viewport. */ +static void preview_draw_color_render_begin(ARegion ®ion) +{ + GPUViewport *viewport = WM_draw_region_get_bound_viewport(®ion); + BLI_assert(viewport); + + GPUFrameBuffer *render_fb = GPU_viewport_framebuffer_render_get(viewport); + GPU_framebuffer_bind(render_fb); + + float col[4] = {0, 0, 0, 0}; + GPU_framebuffer_clear_color(render_fb, col); +} + +/* Configure current GPU state to draw on the overlay frame-buffer of the viewport. */ +static void preview_draw_overlay_begin(ARegion ®ion) +{ + GPUViewport *viewport = WM_draw_region_get_bound_viewport(®ion); + BLI_assert(viewport); + + GPUFrameBuffer *overlay_fb = GPU_viewport_framebuffer_overlay_get(viewport); + GPU_framebuffer_bind_no_srgb(overlay_fb); + + sequencer_preview_clear(); +} + +/* Draw the given texture on the currently bound frame-buffer without any changes to its pixels + * colors. + * + * The position denotes coordinates of a rectangle used to display the texture. + * The texture_coord contains UV coordinates of the input texture which are mapped to the corners + * of the rectangle. */ +void preview_draw_texture_simple(GPUTexture &texture, + const rctf &position, + const rctf &texture_coord) +{ + GPUVertFormat *imm_format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint tex_coord = GPU_vertformat_attr_add( + imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR); + immUniformColor3f(1.0f, 1.0f, 1.0f); + + GPU_texture_bind(&texture, 0); + + immRectf_with_texco(pos, tex_coord, position, texture_coord); + + GPU_texture_unbind(&texture); + immUnbindProgram(); +} + +/* Draw the given texture on the currently bound frame-buffer and convert its colors to linear + * space in the fragment shader. This makes it suitable to be further processed by a GPUViewport + * + * The position denotes coordinates of a rectangle used to display the texture. + * The texture_coord contains UV coordinates of the input texture which are mapped to the corners + * of the rectangle. */ +void preview_draw_texture_to_linear(GPUTexture &texture, + const char *texture_colorspace_name, + const bool predivide, + const rctf &position, + const rctf &texture_coord) +{ + GPUVertFormat *imm_format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint tex_coord = GPU_vertformat_attr_add( + imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + if (!IMB_colormanagement_setup_glsl_draw_to_scene_linear(texture_colorspace_name, predivide)) { + /* An error happened when configuring GPU side color space conversion. Return and allow the + * view to be black, so that it is obvious something went wrong and that a bug report is to + * be submitted. + * + * Note that fallback OCIO implementation is handled on a higher level. */ return; } - /* Setup view. */ - sequencer_display_size(scene, viewrect); - UI_view2d_totRect_set(v2d, roundf(viewrect[0]), roundf(viewrect[1])); - UI_view2d_curRect_validate(v2d); - UI_view2d_view_ortho(v2d); + GPU_texture_bind(&texture, 0); - /* Draw background. */ - if (!draw_overlay || (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE)) { - sequencer_preview_clear(); + immRectf_with_texco(pos, tex_coord, position, texture_coord); - if (sseq->flag & SEQ_USE_ALPHA) { - imm_draw_box_checker_2d(v2d->tot.xmin, v2d->tot.ymin, v2d->tot.xmax, v2d->tot.ymax); + GPU_texture_unbind(&texture); + + IMB_colormanagement_finish_glsl_draw(); +} + +/* Draw overlays for the currently displayed images in the preview. */ +void preview_draw_all_image_overlays(const bContext *C, + const Scene *scene, + const Editing &editing, + const int timeline_frame) +{ + ListBase *channels = seq::channels_displayed_get(&editing); + VectorSet strips = seq::query_rendered_strips( + scene, channels, editing.seqbasep, timeline_frame, 0); + Strip *active_seq = seq::select_active_get(scene); + for (Strip *strip : strips) { + /* TODO(sergey): Avoid having per-strip strip-independent checks. */ + strip_draw_image_origin_and_outline(C, strip, strip == active_seq); + text_edit_draw(C); + } +} + +static bool is_cursor_visible(const SpaceSeq &sseq) +{ + if (G.moving & G_TRANSFORM_CURSOR) { + return true; + } + + if ((sseq.flag & SEQ_SHOW_OVERLAY) && + (sseq.preview_overlay.flag & SEQ_PREVIEW_SHOW_2D_CURSOR) != 0) + { + return true; + } + return false; +} + +/** + * We may want to move this into a more general location. + */ +static void draw_cursor_2d(const ARegion *region, const blender::float2 &cursor) +{ + int co[2]; + UI_view2d_view_to_region(®ion->v2d, cursor[0], cursor[1], &co[0], &co[1]); + + /* Draw nice Anti Aliased cursor. */ + GPU_blend(GPU_BLEND_ALPHA); + + /* Draw lines */ + float original_proj[4][4]; + GPU_matrix_projection_get(original_proj); + GPU_matrix_push(); + ED_region_pixelspace(region); + GPU_matrix_translate_2f(co[0] + 0.5f, co[1] + 0.5f); + GPU_matrix_scale_2f(U.widget_unit, U.widget_unit); + + float viewport[4]; + GPU_viewport_size_get_f(viewport); + + GPUVertFormat *format = immVertexFormat(); + struct { + uint pos, col; + } attr_id{}; + attr_id.pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + attr_id.col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR); + immUniform2fv("viewportSize", &viewport[2]); + immUniform1f("lineWidth", U.pixelsize); + + const float f5 = 0.25f; + const float f10 = 0.5f; + const float f20 = 1.0f; + + const float red[3] = {1.0f, 0.0f, 0.0f}; + const float white[3] = {1.0f, 1.0f, 1.0f}; + + const int segments = 16; + immBegin(GPU_PRIM_LINE_STRIP, segments + 1); + for (int i = 0; i < segments + 1; i++) { + float angle = float(2 * M_PI) * (float(i) / float(segments)); + float x = f10 * cosf(angle); + float y = f10 * sinf(angle); + + immAttr3fv(attr_id.col, (i % 2 == 0) ? red : white); + immVertex2f(attr_id.pos, x, y); + } + immEnd(); + + float crosshair_color[3]; + UI_GetThemeColor3fv(TH_VIEW_OVERLAY, crosshair_color); + + immBegin(GPU_PRIM_LINES, 8); + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, -f20, 0); + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, -f5, 0); + + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, +f20, 0); + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, +f5, 0); + + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, 0, -f20); + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, 0, -f5); + + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, 0, +f20); + immAttr3fv(attr_id.col, crosshair_color); + immVertex2f(attr_id.pos, 0, +f5); + immEnd(); + + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + + GPU_matrix_pop(); + GPU_matrix_projection_set(original_proj); +} + +/* Get offset in frame numbers of the reference frame relative to the current frame. */ +static int get_reference_frame_offset(const Editing &editing, const RenderData &render_data) +{ + if (editing.overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { + return editing.overlay_frame_abs - render_data.cfra; + } + return editing.overlay_frame_ofs; +} + +/* Create GPUTexture from the given image buffer for drawing rendered sequencer frame on the + * color render frame buffer. + * + * The texture format and color space matches the CPU-side buffer. + * + * If both float and byte buffers are missing nullptr is returned. + * If channel configuration is incompatible with the texture nullptr is returned. */ +static GPUTexture *create_texture(const ImBuf &ibuf) +{ + const eGPUTextureUsage texture_usage = GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_ATTACHMENT; + + GPUTexture *texture = nullptr; + + if (ibuf.float_buffer.data) { + eGPUTextureFormat texture_format; + switch (ibuf.channels) { + case 1: + texture_format = GPU_R32F; + break; + case 3: + texture_format = GPU_RGB32F; + break; + case 4: + texture_format = GPU_RGBA32F; + break; + default: + BLI_assert_msg(0, "Incompatible number of channels for float buffer in sequencer"); + return nullptr; + } + + texture = GPU_texture_create_2d( + "seq_display_buf", ibuf.x, ibuf.y, 1, texture_format, texture_usage, nullptr); + GPU_texture_update(texture, GPU_DATA_FLOAT, ibuf.float_buffer.data); + } + else if (ibuf.byte_buffer.data) { + texture = GPU_texture_create_2d( + "seq_display_buf", ibuf.x, ibuf.y, 1, GPU_RGBA8, texture_usage, nullptr); + GPU_texture_update(texture, GPU_DATA_UBYTE, ibuf.byte_buffer.data); + } + + if (texture) { + GPU_texture_filter_mode(texture, false); + } + + return texture; +} + +/* Get colorspace name of the image buffer used to create GPU texture. + * + * Needs to be kept in sync with create_texture() w.r.t which buffers are used to create the + * texture. If the image buffer does not specify color space explicitly scene linear is returned if + * there is a float buffer, and default byte space is returned if there is a byte buffer. + * + * If there are no buffers at all scene linear space is returned. */ +static const char *get_texture_colorspace_name(const ImBuf &ibuf) +{ + if (ibuf.float_buffer.data) { + if (ibuf.byte_buffer.colorspace) { + return IMB_colormanagement_colorspace_get_name(ibuf.float_buffer.colorspace); + } + return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + } + + if (ibuf.byte_buffer.data) { + if (ibuf.byte_buffer.colorspace) { + return IMB_colormanagement_colorspace_get_name(ibuf.byte_buffer.colorspace); + } + return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); + } + + /* Fail-safe fallback. */ + return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); +} + +/* Part of the sequencer preview region drawing which renders images to the viewport's color render + * frame-buffer. */ +static void sequencer_preview_draw_color_render(const SpaceSeq &space_sequencer, + const Editing &editing, + ARegion ®ion, + const ImBuf *current_ibuf, + GPUTexture *current_texture, + const ImBuf *reference_ibuf, + GPUTexture *reference_texture) +{ + preview_draw_color_render_begin(region); + + if (current_texture) { + BLI_assert(current_ibuf); + const rctf position = preview_get_full_position(region); + const rctf texture_coord = preview_get_full_texture_coord(); + const char *texture_colorspace = get_texture_colorspace_name(*current_ibuf); + const bool predivide = (current_ibuf->float_buffer.data != nullptr); + preview_draw_texture_to_linear( + *current_texture, texture_colorspace, predivide, position, texture_coord); + } + + if (reference_texture) { + BLI_assert(reference_ibuf); + const rctf position = preview_get_reference_position(space_sequencer, editing, region); + const rctf texture_coord = preview_get_reference_texture_coord(space_sequencer, editing); + const char *texture_colorspace = get_texture_colorspace_name(*reference_ibuf); + const bool predivide = (reference_ibuf->float_buffer.data != nullptr); + preview_draw_texture_to_linear( + *reference_texture, texture_colorspace, predivide, position, texture_coord); + } +} + +static void draw_registered_callbacks(const bContext *C, ARegion ®ion) +{ + GPUViewport *viewport = WM_draw_region_get_bound_viewport(®ion); + BLI_assert(viewport); + + GPUFrameBuffer *overlay_fb = GPU_viewport_framebuffer_overlay_get(viewport); + + GPU_framebuffer_bind(overlay_fb); + ED_region_draw_cb_draw(C, ®ion, REGION_DRAW_POST_VIEW); + GPU_framebuffer_bind_no_srgb(overlay_fb); +} + +/* Part of the sequencer preview region drawing which renders information overlays to the + * viewport's overlay frame-buffer. */ +static void sequencer_preview_draw_overlays(const bContext *C, + const wmWindowManager &wm, + const Scene *scene, + const SpaceSeq &space_sequencer, + const Editing &editing, + const ColorManagedViewSettings &view_settings, + const ColorManagedDisplaySettings &display_settings, + ARegion ®ion, + GPUTexture *current_texture, + GPUTexture *reference_texture, + const ImBuf *overlay_ibuf, + const int timeline_frame) +{ + const bool is_playing = ED_screen_animation_playing(&wm); + const bool show_imbuf = check_show_imbuf(space_sequencer); + + preview_draw_overlay_begin(region); + + bool has_scopes = false; + if (overlay_ibuf && + sequencer_calc_scopes(space_sequencer, view_settings, display_settings, *overlay_ibuf)) + { + /* Draw scope. */ + sequencer_draw_scopes(space_sequencer, region); + has_scopes = true; + } + else if (space_sequencer.flag & SEQ_USE_ALPHA) { + /* Draw checked-board. */ + const View2D &v2d = region.v2d; + imm_draw_box_checker_2d(v2d.tot.xmin, v2d.tot.ymin, v2d.tot.xmax, v2d.tot.ymax); + + /* Draw current and preview textures in a special way to pierce a hole in the overlay to make + * the actual image visible. */ + GPU_blend(GPU_BLEND_OVERLAY_MASK_FROM_ALPHA); + if (current_texture) { + const rctf position = preview_get_full_position(region); + const rctf texture_coord = preview_get_full_texture_coord(); + preview_draw_texture_simple(*current_texture, position, texture_coord); + } + if (reference_texture) { + const rctf position = preview_get_reference_position(space_sequencer, editing, region); + const rctf texture_coord = preview_get_reference_texture_coord(space_sequencer, editing); + preview_draw_texture_simple(*reference_texture, position, texture_coord); + } + GPU_blend(GPU_BLEND_NONE); + } + else { + /* The overlay framebuffer is fully cleared. Need to draw a full-frame transparent rectangle in + * it to make sequencer result visible. */ + + const rctf position = preview_get_full_position(region); + + GPUVertFormat *imm_format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + GPU_blend(GPU_BLEND_OVERLAY_MASK_FROM_ALPHA); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor3f(-.0f, 1.0f, 1.0f); + immRectf(pos, position.xmin, position.ymin, position.xmax, position.ymax); + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + } + + /* Draw metadata. */ + if (!has_scopes && overlay_ibuf) { + if ((space_sequencer.preview_overlay.flag & SEQ_PREVIEW_SHOW_METADATA) && + (space_sequencer.flag & SEQ_SHOW_OVERLAY)) + { + const View2D &v2d = region.v2d; + ED_region_image_metadata_draw(0.0, 0.0, overlay_ibuf, &v2d.tot, 1.0, 1.0); } } - if (ibuf) { - bool has_scope = sequencer_calc_scopes(scene, sseq, ibuf); - if (has_scope) { - /* Draw scope. */ - sequencer_draw_scopes(scene, region, sseq); - } - else { - /* Draw image. */ - sequencer_draw_display_buffer(C, scene, region, sseq, ibuf, draw_overlay); - } + if (show_imbuf && (space_sequencer.flag & SEQ_SHOW_OVERLAY)) { + sequencer_draw_borders_overlay(space_sequencer, region.v2d, scene); - /* Draw metadata. */ - if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_METADATA && sseq->flag & SEQ_SHOW_OVERLAY) { - ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0); + /* Various overlays like stirp selection and text editing. */ + preview_draw_all_image_overlays(C, scene, editing, timeline_frame); + + if ((space_sequencer.preview_overlay.flag & SEQ_PREVIEW_SHOW_GPENCIL) && space_sequencer.gpd) { + sequencer_draw_gpencil_overlay(C); } } - if (show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { - sequencer_draw_borders_overlay(sseq, v2d, scene); + draw_registered_callbacks(C, region); + + UI_view2d_view_restore(C); + + /* No need to show the cursor for scopes. */ + if ((is_playing == false) && (space_sequencer.mainb == SEQ_DRAW_IMG_IMBUF) && + is_cursor_visible(space_sequencer)) + { + GPU_color_mask(true, true, true, true); + GPU_depth_mask(false); + GPU_depth_test(GPU_DEPTH_NONE); + + const float2 cursor_pixel = seq::image_preview_unit_to_px(scene, space_sequencer.cursor); + draw_cursor_2d(®ion, cursor_pixel); } - if (scene->ed != nullptr) { - Editing *ed = seq::editing_get(scene); - ListBase *channels = seq::channels_displayed_get(ed); - blender::VectorSet strips = seq::query_rendered_strips( - scene, channels, ed->seqbasep, timeline_frame, 0); - Strip *active_seq = seq::select_active_get(scene); - for (Strip *strip : strips) { - strip_draw_image_origin_and_outline(C, strip, strip == active_seq); - text_edit_draw(C); + /* Gizmos. */ + if ((is_playing == false) && (space_sequencer.gizmo_flag & SEQ_GIZMO_HIDE) == 0) { + WM_gizmomap_draw(region.runtime->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); + } + + /* FPS counter. */ + if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(&wm)) { + const rcti *rect = ED_region_visible_rect(®ion); + int xoffset = rect->xmin + U.widget_unit; + int yoffset = rect->ymax; + + /* #ED_scene_draw_fps does not set text/shadow colors, except when frame-rate is too low, then + * it sets text color to red. Make sure the "normal case" also has legible colors. */ + const int font_id = BLF_default(); + float text_color[4] = {1, 1, 1, 1}, shadow_color[4] = {0, 0, 0, 0.8f}; + BLF_color4fv(font_id, text_color); + BLF_enable(font_id, BLF_SHADOW); + BLF_shadow_offset(font_id, 0, 0); + BLF_shadow(font_id, FontShadowType::Outline, shadow_color); + + ED_scene_draw_fps(scene, xoffset, &yoffset); + + BLF_disable(font_id, BLF_SHADOW); + } +} + +void sequencer_preview_region_draw(const bContext *C, ARegion *region) +{ + const char *view_names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + const ScrArea *area = CTX_wm_area(C); + const SpaceSeq &space_sequencer = *static_cast(area->spacedata.first); + const Scene *scene = CTX_data_scene(C); + + if (!scene->ed || space_sequencer.render_size == SEQ_RENDER_SIZE_NONE) { + sequencer_preview_draw_empty(*region); + return; + } + + const Editing &editing = *scene->ed; + const RenderData &render_data = scene->r; + + if (!preview_draw_begin(C, render_data, scene->view_settings, scene->display_settings, *region)) + { + sequencer_preview_draw_empty(*region); + return; + } + + const bool show_imbuf = check_show_imbuf(space_sequencer); + + const bool draw_overlay = (space_sequencer.flag & SEQ_SHOW_OVERLAY); + const bool draw_frame_overlay = (editing.overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) && + draw_overlay; + const bool need_current_frame = !(draw_frame_overlay && (space_sequencer.overlay_frame_type == + SEQ_OVERLAY_FRAME_TYPE_REFERENCE)); + const bool need_reference_frame = draw_frame_overlay && space_sequencer.overlay_frame_type != + SEQ_OVERLAY_FRAME_TYPE_CURRENT; + + int timeline_frame = render_data.cfra; + if (sequencer_draw_get_transform_preview(space_sequencer, *scene)) { + timeline_frame = sequencer_draw_get_transform_preview_frame(scene); + } + + /* GPU textures for the current and reference frames. + * + * When non-nullptr they are to be drawn (in other words, when they are non-nullptr the + * corresponding draw_current_frame and draw_reference_frame is true). */ + GPUTexture *current_texture = nullptr; + GPUTexture *reference_texture = nullptr; + + /* Get image buffers before setting up GPU state for drawing. This is because + * sequencer_ibuf_get() might not properly restore the state. + * Additionally, some image buffers might be needed for both color render and overlay drawing. */ + ImBuf *current_ibuf = nullptr; + ImBuf *reference_ibuf = nullptr; + if (need_current_frame) { + current_ibuf = sequencer_ibuf_get( + C, timeline_frame, view_names[space_sequencer.multiview_eye]); + if (show_imbuf && current_ibuf) { + current_texture = create_texture(*current_ibuf); + } + } + if (need_reference_frame) { + const int offset = get_reference_frame_offset(editing, render_data); + reference_ibuf = sequencer_ibuf_get( + C, timeline_frame + offset, view_names[space_sequencer.multiview_eye]); + if (show_imbuf && reference_ibuf) { + reference_texture = create_texture(*reference_ibuf); } } - if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { - sequencer_draw_gpencil_overlay(C); - } + /* Image buffer used for overlays: scopes, metadata etc. */ + ImBuf *overlay_ibuf = need_current_frame ? current_ibuf : reference_ibuf; + + /* Draw parts of the preview region to the corresponding frame buffers. */ + sequencer_preview_draw_color_render(space_sequencer, + editing, + *region, + current_ibuf, + current_texture, + reference_ibuf, + reference_texture); + sequencer_preview_draw_overlays(C, + *CTX_wm_manager(C), + scene, + space_sequencer, + editing, + scene->view_settings, + scene->display_settings, + *region, + current_texture, + reference_texture, + overlay_ibuf, + timeline_frame); #if 0 sequencer_draw_maskedit(C, scene, region, sseq); #endif - /* Draw registered callbacks. */ - GPU_framebuffer_bind(framebuffer_overlay); - ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW); - GPU_framebuffer_bind_no_srgb(framebuffer_overlay); - - if (ibuf) { - IMB_freeImBuf(ibuf); + /* Free textures. */ + if (current_texture) { + GPU_texture_free(current_texture); + } + if (reference_texture) { + GPU_texture_free(reference_texture); } - UI_view2d_view_restore(C); - seq_prefetch_wm_notify(C, scene); + /* Free CPU side resources. */ + IMB_freeImBuf(current_ibuf); + IMB_freeImBuf(reference_ibuf); + + preview_draw_end(C); } } // namespace blender::ed::vse diff --git a/source/blender/editors/space_sequencer/space_sequencer.cc b/source/blender/editors/space_sequencer/space_sequencer.cc index 608bf475d19..4b95b057e64 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.cc +++ b/source/blender/editors/space_sequencer/space_sequencer.cc @@ -20,6 +20,7 @@ #include "BLI_listbase.h" #include "BLI_math_base.h" +#include "BLI_rect.h" #include "BLI_string.h" #include "BLF_api.hh" @@ -29,6 +30,9 @@ #include "BKE_lib_remap.hh" #include "BKE_screen.hh" +#include "IMB_colormanagement.hh" +#include "IMB_imbuf.hh" + #include "GPU_state.hh" #include "ED_markers.hh" @@ -37,7 +41,7 @@ #include "ED_space_api.hh" #include "ED_time_scrub_ui.hh" #include "ED_transform.hh" -#include "ED_view3d.hh" +#include "ED_util.hh" #include "ED_view3d_offscreen.hh" /* Only for sequencer view3d drawing callback. */ #include "WM_api.hh" @@ -56,9 +60,6 @@ #include "BLO_read_write.hh" -/* Only for cursor drawing. */ -#include "DRW_engine.hh" - /* Own include. */ #include "sequencer_intern.hh" @@ -842,172 +843,6 @@ static void sequencer_preview_region_view2d_changed(const bContext *C, ARegion * sseq->flag &= ~SEQ_ZOOM_TO_FIT; } -static bool is_cursor_visible(const SpaceSeq &sseq) -{ - if (G.moving & G_TRANSFORM_CURSOR) { - return true; - } - - if ((sseq.flag & SEQ_SHOW_OVERLAY) && - (sseq.preview_overlay.flag & SEQ_PREVIEW_SHOW_2D_CURSOR) != 0) - { - return true; - } - return false; -} - -/** - * We may want to move this into a more general location. - */ -static void draw_cursor_2d(const ARegion *region, const blender::float2 &cursor) -{ - int co[2]; - UI_view2d_view_to_region(®ion->v2d, cursor[0], cursor[1], &co[0], &co[1]); - - /* Draw nice Anti Aliased cursor. */ - GPU_blend(GPU_BLEND_ALPHA); - - /* Draw lines */ - float original_proj[4][4]; - GPU_matrix_projection_get(original_proj); - GPU_matrix_push(); - ED_region_pixelspace(region); - GPU_matrix_translate_2f(co[0] + 0.5f, co[1] + 0.5f); - GPU_matrix_scale_2f(U.widget_unit, U.widget_unit); - - float viewport[4]; - GPU_viewport_size_get_f(viewport); - - GPUVertFormat *format = immVertexFormat(); - struct { - uint pos, col; - } attr_id{}; - attr_id.pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - attr_id.col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR); - immUniform2fv("viewportSize", &viewport[2]); - immUniform1f("lineWidth", U.pixelsize); - - const float f5 = 0.25f; - const float f10 = 0.5f; - const float f20 = 1.0f; - - const float red[3] = {1.0f, 0.0f, 0.0f}; - const float white[3] = {1.0f, 1.0f, 1.0f}; - - const int segments = 16; - immBegin(GPU_PRIM_LINE_STRIP, segments + 1); - for (int i = 0; i < segments + 1; i++) { - float angle = float(2 * M_PI) * (float(i) / float(segments)); - float x = f10 * cosf(angle); - float y = f10 * sinf(angle); - - immAttr3fv(attr_id.col, (i % 2 == 0) ? red : white); - immVertex2f(attr_id.pos, x, y); - } - immEnd(); - - float crosshair_color[3]; - UI_GetThemeColor3fv(TH_VIEW_OVERLAY, crosshair_color); - - immBegin(GPU_PRIM_LINES, 8); - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, -f20, 0); - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, -f5, 0); - - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, +f20, 0); - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, +f5, 0); - - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, 0, -f20); - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, 0, -f5); - - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, 0, +f20); - immAttr3fv(attr_id.col, crosshair_color); - immVertex2f(attr_id.pos, 0, +f5); - immEnd(); - - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); - - GPU_matrix_pop(); - GPU_matrix_projection_set(original_proj); -} - -static void sequencer_preview_region_draw(const bContext *C, ARegion *region) -{ - ScrArea *area = CTX_wm_area(C); - SpaceSeq *sseq = static_cast(area->spacedata.first); - Scene *scene = CTX_data_scene(C); - wmWindowManager *wm = CTX_wm_manager(C); - const bool draw_overlay = sseq->flag & SEQ_SHOW_OVERLAY; - const bool draw_frame_overlay = (scene->ed && - (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) && - draw_overlay); - const bool is_playing = ED_screen_animation_playing(wm); - - if (!(draw_frame_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE))) { - sequencer_draw_preview(C, scene, region, sseq, scene->r.cfra, 0, false); - } - - if (draw_frame_overlay && sseq->overlay_frame_type != SEQ_OVERLAY_FRAME_TYPE_CURRENT) { - int over_cfra; - - if (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { - over_cfra = scene->ed->overlay_frame_abs; - } - else { - over_cfra = scene->r.cfra + scene->ed->overlay_frame_ofs; - } - - if ((over_cfra != scene->r.cfra) || (sseq->overlay_frame_type != SEQ_OVERLAY_FRAME_TYPE_RECT)) - { - sequencer_draw_preview( - C, scene, region, sseq, scene->r.cfra, over_cfra - scene->r.cfra, true); - } - } - - /* No need to show the cursor for scopes. */ - if ((is_playing == false) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF) && is_cursor_visible(*sseq)) { - GPU_color_mask(true, true, true, true); - GPU_depth_mask(false); - GPU_depth_test(GPU_DEPTH_NONE); - - const blender::float2 cursor_pixel = seq::image_preview_unit_to_px(scene, sseq->cursor); - draw_cursor_2d(region, cursor_pixel); - } - - if ((is_playing == false) && (sseq->gizmo_flag & SEQ_GIZMO_HIDE) == 0) { - WM_gizmomap_draw(region->runtime->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); - } - - if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) { - const rcti *rect = ED_region_visible_rect(region); - int xoffset = rect->xmin + U.widget_unit; - int yoffset = rect->ymax; - - /* #ED_scene_draw_fps does not set text/shadow colors, except when - * frame-rate is too low, then it sets text color to red. - * Make sure the "normal case" also has legible colors. */ - const int font_id = BLF_default(); - float text_color[4] = {1, 1, 1, 1}, shadow_color[4] = {0, 0, 0, 0.8f}; - BLF_color4fv(font_id, text_color); - BLF_enable(font_id, BLF_SHADOW); - BLF_shadow_offset(font_id, 0, 0); - BLF_shadow(font_id, FontShadowType::Outline, shadow_color); - - ED_scene_draw_fps(scene, xoffset, &yoffset); - - BLF_disable(font_id, BLF_SHADOW); - } -} - static void sequencer_preview_region_listener(const wmRegionListenerParams *params) { ARegion *region = params->region;