diff options
146 files changed, 6297 insertions, 3706 deletions
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index b6ec016aa95..497fd179aaf 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -119,7 +119,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099") list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi - pathcch + pathcch Shcore ) if(WITH_INPUT_IME) @@ -144,8 +144,8 @@ add_definitions(-D_ALLOW_KEYWORD_MACROS) # that both /GR and /GR- are specified. remove_cc_flag("/GR") -# We want to support Windows 7 level ABI -add_definitions(-D_WIN32_WINNT=0x601) +# Make the Windows 8.1 API available for use. +add_definitions(-D_WIN32_WINNT=0x603) include(build_files/cmake/platform/platform_win32_bundle_crt.cmake) remove_cc_flag("/MDd" "/MD" "/Zi") diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 032f8a86bd5..4be27e0f0e8 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -954,7 +954,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): # constant, not much fun we can do here except to list it. # TODO, figure out some way to document these! fw(".. data:: %s\n\n" % attribute) - write_indented_lines(" ", fw, "constant value %s" % repr(value), False) + write_indented_lines(" ", fw, "Constant value %s" % repr(value), False) fw("\n") else: BPY_LOGGER.debug("\tnot documenting %s.%s of %r type" % (module_name, attribute, value_type.__name__)) @@ -1246,7 +1246,7 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False): "%s.\n" % ( identifier, # Account for multi-line enum descriptions, allowing this to be a block of text. - indent(", ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), + indent(" -- ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), ) for identifier, name, description in prop.enum_items ]) diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index b31841801d8..6954c5c2f26 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -83,6 +83,8 @@ struct BlenderCamera { BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; + float passepartout_alpha; + Transform matrix; float offscreen_dicing_scale; @@ -125,6 +127,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->pano_viewplane.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; bcam->viewport_camera_border.top = 1.0f; + bcam->passepartout_alpha = 0.5f; bcam->offscreen_dicing_scale = 1.0f; bcam->matrix = transform_identity(); @@ -212,6 +215,8 @@ static void blender_camera_from_object(BlenderCamera *bcam, bcam->lens = b_camera.lens(); + bcam->passepartout_alpha = b_camera.show_passepartout() ? b_camera.passepartout_alpha() : 0.0f; + if (b_camera.dof().use_dof()) { /* allow f/stop number to change aperture_size but still * give manual control over aperture radius */ @@ -834,15 +839,19 @@ static void blender_camera_border(BlenderCamera *bcam, full_border, &bcam->viewport_camera_border); - if (!b_render.use_border()) { + if (b_render.use_border()) { + bcam->border.left = b_render.border_min_x(); + bcam->border.right = b_render.border_max_x(); + bcam->border.bottom = b_render.border_min_y(); + bcam->border.top = b_render.border_max_y(); + } + else if (bcam->passepartout_alpha == 1.0f) { + bcam->border = full_border; + } + else { return; } - bcam->border.left = b_render.border_min_x(); - bcam->border.right = b_render.border_max_x(); - bcam->border.bottom = b_render.border_min_y(); - bcam->border.top = b_render.border_max_y(); - /* Determine viewport subset matching camera border. */ blender_camera_border_subset(b_engine, b_render, @@ -885,8 +894,7 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d, } } -BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, +BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, @@ -902,7 +910,8 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, if (b_v3d && b_rv3d && b_rv3d.view_perspective() != BL::RegionView3D::view_perspective_CAMERA) use_border = b_v3d.use_render_border(); else - use_border = b_render.use_border(); + /* the camera can always have a passepartout */ + use_border = true; if (use_border) { /* border render */ diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 89854f6a0e5..29de886e4ff 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -155,7 +155,7 @@ void BlenderSession::create_session() /* set buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); session->reset(buffer_params, session_params.samples); b_engine.use_highlight_tiles(session_params.progressive_refine == false); @@ -242,8 +242,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL); BL::RegionView3D b_null_region_view3d(PointerRNA_NULL); - BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, - b_null_space_view3d, + BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d, b_null_region_view3d, scene->camera, width, @@ -486,7 +485,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background, b_view_layer); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); /* temporary render result to find needed passes and views */ BL::RenderResult b_rr = begin_render_result( @@ -810,7 +809,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* get buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); if (!buffer_params.denoising_data_pass) { session_params.denoising.use = false; @@ -889,7 +888,7 @@ bool BlenderSession::draw(int w, int h) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session_pause == false) { @@ -907,7 +906,7 @@ bool BlenderSession::draw(int w, int h) /* draw */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); DeviceDrawParams draw_params; if (session->params.display_buffer_linear) { diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 8cd65f13f70..1c98e529190 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -104,8 +104,7 @@ class BlenderSync { bool background, BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL)); static bool get_session_pause(BL::Scene &b_scene, bool background); - static BufferParams get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, + static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 1c4b360750f..ce76658acb6 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -2063,7 +2063,7 @@ void GeometryManager::device_update(Device *device, * for meshes with correct bounding boxes. * * This wouldn't cause wrong results, just true - * displacement might be less optimal ot calculate. + * displacement might be less optimal to calculate. */ scene->object_manager->need_flags_update = old_need_object_flags_update; } diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/util_task.h index 7c39ed675b5..906bf420756 100644 --- a/intern/cycles/util/util_task.h +++ b/intern/cycles/util/util_task.h @@ -32,7 +32,7 @@ typedef function<void(void)> TaskRunFunction; /* Task Pool * - * Pool of tasks that will be executed by the central TaskScheduler.For each + * Pool of tasks that will be executed by the central TaskScheduler. For each * pool, we can wait for all tasks to be done, or cancel them before they are * done. * @@ -77,7 +77,7 @@ class TaskPool { /* Task Scheduler * - * Central scheduler that holds running threads ready to execute tasks. A singe + * Central scheduler that holds running threads ready to execute tasks. A single * queue holds the task from all pools. */ class TaskScheduler { diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 54a004d53e2..0c22cf82688 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -43,6 +43,55 @@ # define FFMPEG_INLINE static inline #endif +#if (LIBAVFORMAT_VERSION_MAJOR < 58) || \ + ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR < 76)) +# define FFMPEG_USE_DURATION_WORKAROUND 1 + +/* Before ffmpeg 4.4, package duration calculation used depricated variables to calculate the + * packet duration. Use the function from commit + * github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586 + * to calculate the correct framerate for ffmpeg < 4.4. + */ + +FFMPEG_INLINE +void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + if (pkt->duration < 0 && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { + av_log(s, + AV_LOG_WARNING, + "Packet with invalid duration %" PRId64 " in stream %d\n", + pkt->duration, + pkt->stream_index); + pkt->duration = 0; + } + + if (pkt->duration) { + return; + } + + switch (st->codecpar->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) { + pkt->duration = av_rescale_q(1, av_inv_q(st->avg_frame_rate), st->time_base); + } + else if (st->time_base.num * 1000LL > st->time_base.den) { + pkt->duration = 1; + } + break; + case AVMEDIA_TYPE_AUDIO: { + int frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size); + if (frame_size && st->codecpar->sample_rate) { + pkt->duration = av_rescale_q( + frame_size, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + } + break; + } + default: + break; + } +} +#endif + FFMPEG_INLINE void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) { diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 38dea9c1142..fc3cb81e26a 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -32,6 +32,7 @@ #include <commctrl.h> #include <psapi.h> #include <shellapi.h> +#include <shellscalingapi.h> #include <shlobj.h> #include <tlhelp32.h> #include <windowsx.h> @@ -97,41 +98,6 @@ # define VK_GR_LESS 0xE2 #endif // VK_GR_LESS -#ifndef VK_MEDIA_NEXT_TRACK -# define VK_MEDIA_NEXT_TRACK 0xB0 -#endif // VK_MEDIA_NEXT_TRACK -#ifndef VK_MEDIA_PREV_TRACK -# define VK_MEDIA_PREV_TRACK 0xB1 -#endif // VK_MEDIA_PREV_TRACK -#ifndef VK_MEDIA_STOP -# define VK_MEDIA_STOP 0xB2 -#endif // VK_MEDIA_STOP -#ifndef VK_MEDIA_PLAY_PAUSE -# define VK_MEDIA_PLAY_PAUSE 0xB3 -#endif // VK_MEDIA_PLAY_PAUSE - -// Window message newer than Windows 7 -#ifndef WM_DPICHANGED -# define WM_DPICHANGED 0x02E0 -#endif // WM_DPICHANGED - -// WM_POINTER API messages minimum Windows 7 -#ifndef WM_POINTERENTER -# define WM_POINTERENTER 0x0249 -#endif // WM_POINTERENTER -#ifndef WM_POINTERDOWN -# define WM_POINTERDOWN 0x0246 -#endif // WM_POINTERDOWN -#ifndef WM_POINTERUPDATE -# define WM_POINTERUPDATE 0x0245 -#endif // WM_POINTERUPDATE -#ifndef WM_POINTERUP -# define WM_POINTERUP 0x0247 -#endif // WM_POINTERUP -#ifndef WM_POINTERLEAVE -# define WM_POINTERLEAVE 0x024A -#endif // WM_POINTERLEAVE - /* Workaround for some laptop touchpads, some of which seems to * have driver issues which makes it so window function receives * the message, but PeekMessage doesn't pick those messages for @@ -173,24 +139,6 @@ static void initRawInput() #undef DEVICE_COUNT } -#ifndef DPI_ENUMS_DECLARED -typedef enum PROCESS_DPI_AWARENESS { - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 -} PROCESS_DPI_AWARENESS; - -typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; - -# define USER_DEFAULT_SCREEN_DPI 96 - -# define DPI_ENUMS_DECLARED -#endif typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND); @@ -205,15 +153,7 @@ GHOST_SystemWin32::GHOST_SystemWin32() // Tell Windows we are per monitor DPI aware. This disables the default // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. - HMODULE m_shcore = ::LoadLibrary("Shcore.dll"); - if (m_shcore) { - GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness = - (GHOST_WIN32_SetProcessDpiAwareness)::GetProcAddress(m_shcore, "SetProcessDpiAwareness"); - - if (fpSetProcessDpiAwareness) { - fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - } - } + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); // Check if current keyboard layout uses AltGr and save keylayout ID for // specialized handling if keys like VK_OEM_*. I.e. french keylayout @@ -581,14 +521,7 @@ GHOST_TSuccess GHOST_SystemWin32::init() InitCommonControls(); /* Disable scaling on high DPI displays on Vista */ - HMODULE - user32 = ::LoadLibraryA("user32.dll"); - typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)(); - LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress( - user32, "SetProcessDPIAware"); - if (SetProcessDPIAware) - SetProcessDPIAware(); - FreeLibrary(user32); + SetProcessDPIAware(); initRawInput(); m_lfstart = ::GetTickCount(); diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 3af92153e8b..eeafe333633 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -84,9 +84,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_wantAlphaBackground(alphaBackground), m_normal_state(GHOST_kWindowStateNormal), m_user32(NULL), - m_fpGetPointerInfoHistory(NULL), - m_fpGetPointerPenInfoHistory(NULL), - m_fpGetPointerTouchInfoHistory(NULL), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), m_debug_context(is_debug) { @@ -153,19 +150,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_user32 = ::LoadLibrary("user32.dll"); if (m_hWnd) { - if (m_user32) { - // Touch enabled screens with pen support by default have gestures - // enabled, which results in a delay between the pointer down event - // and the first move when using the stylus. RegisterTouchWindow - // disables the new gesture architecture enabling the events to be - // sent immediately to the application rather than being absorbed by - // the gesture API. - GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow = (GHOST_WIN32_RegisterTouchWindow) - GetProcAddress(m_user32, "RegisterTouchWindow"); - if (pRegisterTouchWindow) { - pRegisterTouchWindow(m_hWnd, 0); - } - } + RegisterTouchWindow(m_hWnd, 0); // Register this window as a droptarget. Requires m_hWnd to be valid. // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32. @@ -232,16 +217,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, } } - // Initialize Windows Ink - if (m_user32) { - m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress( - m_user32, "GetPointerInfoHistory"); - m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress( - m_user32, "GetPointerPenInfoHistory"); - m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress( - m_user32, "GetPointerTouchInfoHistory"); - } - // Initialize Wintab m_wintab.handle = ::LoadLibrary("Wintab32.dll"); if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) { @@ -326,9 +301,6 @@ GHOST_WindowWin32::~GHOST_WindowWin32() if (m_user32) { FreeLibrary(m_user32); m_user32 = NULL; - m_fpGetPointerInfoHistory = NULL; - m_fpGetPointerPenInfoHistory = NULL; - m_fpGetPointerTouchInfoHistory = NULL; } if (m_customCursor) { @@ -950,15 +922,14 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); GHOST_TUns32 outCount; - if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) { + if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) { return GHOST_kFailure; } auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount); outPointerInfo.resize(outCount); - if (!(m_fpGetPointerPenInfoHistory && - m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { + if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { return GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index b004f7e7b19..a13bd876667 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -53,177 +53,12 @@ typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID); typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL); typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL); -// typedef to user32 functions to disable gestures on windows -typedef BOOL(API *GHOST_WIN32_RegisterTouchWindow)(HWND hwnd, ULONG ulFlags); - // typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND); #ifndef USER_DEFAULT_SCREEN_DPI # define USER_DEFAULT_SCREEN_DPI 96 #endif // USER_DEFAULT_SCREEN_DPI -// typedefs for user32 functions to allow pointer functions -enum tagPOINTER_INPUT_TYPE { - PT_POINTER = 1, // Generic pointer - PT_TOUCH = 2, // Touch - PT_PEN = 3, // Pen - PT_MOUSE = 4, // Mouse -#if (WINVER >= 0x0603) - PT_TOUCHPAD = 5, // Touchpad -#endif /* WINVER >= 0x0603 */ -}; - -typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { - POINTER_CHANGE_NONE, - POINTER_CHANGE_FIRSTBUTTON_DOWN, - POINTER_CHANGE_FIRSTBUTTON_UP, - POINTER_CHANGE_SECONDBUTTON_DOWN, - POINTER_CHANGE_SECONDBUTTON_UP, - POINTER_CHANGE_THIRDBUTTON_DOWN, - POINTER_CHANGE_THIRDBUTTON_UP, - POINTER_CHANGE_FOURTHBUTTON_DOWN, - POINTER_CHANGE_FOURTHBUTTON_UP, - POINTER_CHANGE_FIFTHBUTTON_DOWN, - POINTER_CHANGE_FIFTHBUTTON_UP, -} POINTER_BUTTON_CHANGE_TYPE; - -typedef DWORD POINTER_INPUT_TYPE; -typedef UINT32 POINTER_FLAGS; - -#define POINTER_FLAG_NONE 0x00000000 -#define POINTER_FLAG_NEW 0x00000001 -#define POINTER_FLAG_INRANGE 0x00000002 -#define POINTER_FLAG_INCONTACT 0x00000004 -#define POINTER_FLAG_FIRSTBUTTON 0x00000010 -#define POINTER_FLAG_SECONDBUTTON 0x00000020 -#define POINTER_FLAG_THIRDBUTTON 0x00000040 -#define POINTER_FLAG_FOURTHBUTTON 0x00000080 -#define POINTER_FLAG_FIFTHBUTTON 0x00000100 -#define POINTER_FLAG_PRIMARY 0x00002000 -#define POINTER_FLAG_CONFIDENCE 0x000004000 -#define POINTER_FLAG_CANCELED 0x000008000 -#define POINTER_FLAG_DOWN 0x00010000 -#define POINTER_FLAG_UPDATE 0x00020000 -#define POINTER_FLAG_UP 0x00040000 -#define POINTER_FLAG_WHEEL 0x00080000 -#define POINTER_FLAG_HWHEEL 0x00100000 -#define POINTER_FLAG_CAPTURECHANGED 0x00200000 -#define POINTER_FLAG_HASTRANSFORM 0x00400000 - -typedef struct tagPOINTER_INFO { - POINTER_INPUT_TYPE pointerType; - UINT32 pointerId; - UINT32 frameId; - POINTER_FLAGS pointerFlags; - HANDLE sourceDevice; - HWND hwndTarget; - POINT ptPixelLocation; - POINT ptHimetricLocation; - POINT ptPixelLocationRaw; - POINT ptHimetricLocationRaw; - DWORD dwTime; - UINT32 historyCount; - INT32 InputData; - DWORD dwKeyStates; - UINT64 PerformanceCount; - POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; -} POINTER_INFO; - -typedef UINT32 PEN_FLAGS; -#define PEN_FLAG_NONE 0x00000000 // Default -#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed -#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted -#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed - -typedef UINT32 PEN_MASK; -#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid -#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid -#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid -#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid - -typedef struct tagPOINTER_PEN_INFO { - POINTER_INFO pointerInfo; - PEN_FLAGS penFlags; - PEN_MASK penMask; - UINT32 pressure; - UINT32 rotation; - INT32 tiltX; - INT32 tiltY; -} POINTER_PEN_INFO; - -/* - * Flags that appear in pointer input message parameters - */ -#define POINTER_MESSAGE_FLAG_NEW 0x00000001 // New pointer -#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002 // Pointer has not departed -#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004 // Pointer is in contact -#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 // Primary action -#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020 // Secondary action -#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040 // Third button -#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080 // Fourth button -#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100 // Fifth button -#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000 // Pointer is primary -#define POINTER_MESSAGE_FLAG_CONFIDENCE \ - 0x00004000 // Pointer is considered unlikely to be accidental -#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner - -typedef UINT32 TOUCH_FLAGS; -#define TOUCH_FLAG_NONE 0x00000000 // Default - -typedef UINT32 TOUCH_MASK; -#define TOUCH_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define TOUCH_MASK_CONTACTAREA 0x00000001 // The rcContact field is valid -#define TOUCH_MASK_ORIENTATION 0x00000002 // The orientation field is valid -#define TOUCH_MASK_PRESSURE 0x00000004 // The pressure field is valid - -typedef struct tagPOINTER_TOUCH_INFO { - POINTER_INFO pointerInfo; - TOUCH_FLAGS touchFlags; - TOUCH_MASK touchMask; - RECT rcContact; - RECT rcContactRaw; - UINT32 orientation; - UINT32 pressure; -} POINTER_TOUCH_INFO; - -/* - * Macros to retrieve information from pointer input message parameters - */ -#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) -#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag)) -#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW) -#define IS_POINTER_INRANGE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE) -#define IS_POINTER_INCONTACT_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT) -#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON) -#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON) -#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON) -#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON) -#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON) -#define IS_POINTER_PRIMARY_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY) -#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE) -#define IS_POINTER_CANCELED_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED) - -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_INFO *pointerInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_PEN_INFO *penInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_TOUCH_INFO *touchInfo); - struct GHOST_PointerInfoWin32 { GHOST_TInt32 pointerId; GHOST_TInt32 isPrimary; @@ -576,9 +411,6 @@ class GHOST_WindowWin32 : public GHOST_Window { /** `user32.dll` handle */ HMODULE m_user32; - GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory; - GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory; - GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory; HWND m_parentWindowHwnd; diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd +Subproject 27fe7f3a4f964b53af436c4da4ddea337eff0c7 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 7d78c8a63f2f4b146f9327ddc0d567a5921b94e +Subproject 5a82baad9f986722104280e8354a4427d8e9eab diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 3969386bad7..dd84dfa2df8 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -153,7 +153,7 @@ class PREFERENCES_OT_copy_prev(Operator): def execute(self, _context): import shutil - shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True) + shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True, symlinks=True) # reload preferences and recent-files.txt bpy.ops.wm.read_userpref() diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 29c17711c2a..6ba8750e9df 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -628,7 +628,7 @@ class LightMapPack(Operator): name="New Image", description=( "Assign new images for every mesh (only one if " - "shared tex space enabled)" + "Share Texture Space is enabled)" ), default=False, ) diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 89ce742b81e..1208ca0a64a 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -660,8 +660,12 @@ class NODE_PT_quality(bpy.types.Panel): snode = context.space_data tree = snode.node_tree + prefs = bpy.context.preferences col = layout.column() + if prefs.experimental.use_full_frame_compositor: + col.prop(tree, "execution_mode") + col.prop(tree, "render_quality", text="Render") col.prop(tree, "edit_quality", text="Edit") col.prop(tree, "chunk_size") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index b24b6e84939..e9bfe6cd4e2 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1398,8 +1398,8 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): box.template_image_stereo_3d(strip.stereo_3d_format) # Resolution. - col = layout.column(align=True) - col = col.box() + col = layout.box() + col = col.column(align=True) split = col.split(factor=0.5, align=False) split.alignment = 'RIGHT' split.label(text="Resolution") @@ -1409,6 +1409,14 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): split.label(text="%dx%d" % size, translate=False) else: split.label(text="None") + #FPS + if elem.orig_fps: + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="FPS") + split.alignment = 'LEFT' + split.label(text="%.2f" % elem.orig_fps, translate=False) + class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index de78b88c0f6..d85fe16d654 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2256,6 +2256,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): context, ( ({"property": "use_new_hair_type"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), + ({"property": "use_full_frame_compositor"}, "T88150"), ), ) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 4bff18cd1be..05c7ef756c7 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -503,6 +503,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_CURVE", "Curve", items=[ NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), + NodeItem("GeometryNodeMeshToCurve"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), diff --git a/release/windows/manifest/blender.exe.manifest.in b/release/windows/manifest/blender.exe.manifest.in index e73ddf3267b..b516efe24cb 100644 --- a/release/windows/manifest/blender.exe.manifest.in +++ b/release/windows/manifest/blender.exe.manifest.in @@ -13,12 +13,6 @@ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- Windows 8.1 --> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> - <!-- Windows 8 --> - <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> - <!-- Windows 7 --> - <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> - <!-- Windows Vista --> - <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> </application> </compatibility> <dependency> diff --git a/source/blender/blenfont/intern/blf_font_win32_compat.c b/source/blender/blenfont/intern/blf_font_win32_compat.c index 7d130204c07..e573d3bd224 100644 --- a/source/blender/blenfont/intern/blf_font_win32_compat.c +++ b/source/blender/blenfont/intern/blf_font_win32_compat.c @@ -66,7 +66,7 @@ static unsigned long ft_ansi_stream_io(FT_Stream stream, file = STREAM_FILE(stream); if (stream->pos != offset) { - fseek(file, offset, SEEK_SET); + BLI_fseek(file, offset, SEEK_SET); } return fread(buffer, 1, count, file); @@ -93,7 +93,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Resource); } - fseek(file, 0, SEEK_END); + BLI_fseek(file, 0LL, SEEK_END); stream->size = ftell(file); if (!stream->size) { fprintf(stderr, @@ -104,7 +104,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Stream); } - fseek(file, 0, SEEK_SET); + BLI_fseek(file, 0LL, SEEK_SET); stream->descriptor.pointer = file; stream->read = ft_ansi_stream_io; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 0bab980cfcd..eb937ac9608 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 1 +#define BLENDER_FILE_SUBVERSION 2 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 7ac45ac4883..e16507bf3cc 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -258,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); -bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name) - ATTR_NONNULL(1, 2); +bool BKE_id_new_name_validate(struct ListBase *lb, + struct ID *id, + const char *name, + const bool do_linked_data) ATTR_NONNULL(1, 2); void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); /* Affect whole Main database. */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 448f4ae48ad..fb6647cb68d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1305,15 +1305,18 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteSyncFromAdd(bNode *node); +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); void ntreeCompositCryptomatteSyncFromRemove(bNode *node); bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len); +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len); /* Update the runtime layer names with the cryptomatte layer names of the references * render layer or image. */ -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node); -struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node); +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); +struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); /** \} */ @@ -1426,6 +1429,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 +#define GEO_NODE_MESH_TO_CURVE 1052 /** \} */ diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index d8fbdf26d93..be827cd338d 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1423,7 +1423,8 @@ static bool collection_instance_find_recursive(Collection *collection, } LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - if (collection_instance_find_recursive(collection_child->collection, instance_collection)) { + if (collection_child->collection != NULL && + collection_instance_find_recursive(collection_child->collection, instance_collection)) { return true; } } diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index 470c2f2d246..4aaecc26eff 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -167,7 +167,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += header->structbytes; header->structbytes = sizeof(CDataFileHeader); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -201,7 +201,7 @@ static bool cdf_read_header(CDataFile *cdf) mesh->structbytes = sizeof(CDataFileMeshHeader); } - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -233,7 +233,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += layer->structbytes; layer->structbytes = sizeof(CDataFileLayer); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } } @@ -321,7 +321,7 @@ bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) offset += cdf->layer[a].datasize; } - return (fseek(cdf->readf, offset, SEEK_SET) == 0); + return (BLI_fseek(cdf->readf, offset, SEEK_SET) == 0); } bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data) diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 0fa3bb29ccd..bd76357617a 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -127,9 +127,10 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) } em->looptris = looptris; + em->tottri = looptris_tot; /* after allocating the em->looptris, we're ready to tessellate */ - BM_mesh_calc_tessellation(em->bm, em->looptris, &em->tottri); + BM_mesh_calc_tessellation(em->bm, em->looptris); } void BKE_editmesh_looptri_calc(BMEditMesh *em) diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 9abd00c2b4f..69840ba1612 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -544,9 +544,9 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, } } -static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result) +static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups) { - CurveEval *new_curve = new CurveEval(); + Vector<SplinePtr> new_splines; for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; if (!set.has_curve()) { @@ -558,10 +558,18 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp for (const float4x4 &transform : set_group.transforms) { SplinePtr new_spline = source_spline->copy(); new_spline->transform(transform); - new_curve->add_spline(std::move(new_spline)); + new_splines.append(std::move(new_spline)); } } } + if (new_splines.is_empty()) { + return nullptr; + } + + CurveEval *new_curve = new CurveEval(); + for (SplinePtr &new_spline : new_splines) { + new_curve->add_spline(std::move(new_spline)); + } for (SplinePtr &spline : new_curve->splines()) { /* Spline instances should have no custom attributes, since they always come @@ -573,8 +581,7 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp } new_curve->attributes.reallocate(new_curve->splines().size()); - - result.replace(new_curve); + return new_curve; } static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, @@ -639,14 +646,17 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, { /* Not yet supported. Joining volume grids with the same name requires resampling of at least * one of the grids. The cell size of the resulting volume has to be determined somehow. */ - VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>(); - UNUSED_VARS(set_groups, dst_component); + UNUSED_VARS(set_groups, result); } static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) { + CurveEval *curve = join_curve_splines(set_groups); + if (curve == nullptr) { + return; + } CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); - join_curve_splines(set_groups, dst_component); + dst_component.replace(curve); } GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index f93bf494ee9..490abe05169 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); id->flag &= ~LIB_INDIRECT_WEAK_LINK; if (id_in_mainlist) { - if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) { + if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) { bmain->is_memfile_undo_written = false; } } @@ -833,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv) ListBase *lb = which_libbase(bmain, GS(id->name)); BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, NULL); + /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new + * overrides for recursive resync. */ + BKE_id_new_name_validate(lb, id, NULL, true); /* alphabetic insertion: is in new_id */ id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); bmain->is_memfile_undo_written = false; @@ -989,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) } for (i = 0; i < lb_len; i++) { if (!BLI_gset_add(gset, id_array[i]->name + 2)) { - BKE_id_new_name_validate(lb, id_array[i], NULL); + BKE_id_new_name_validate(lb, id_array[i], NULL, false); } } BLI_gset_free(gset, NULL); @@ -1092,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); bmain->is_memfile_undo_written = false; /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); @@ -1557,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * and that current one is not. */ bool is_valid = false; for (id_test = lb->first; id_test; id_test = id_test->next) { - if (id != id_test && !ID_IS_LINKED(id_test)) { + if (id != id_test && id_test->lib == id->lib) { if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { /* We expect final_name to not be already used, so this is a failure. */ is_valid = false; @@ -1613,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ for (id_test = lb->first; id_test; id_test = id_test->next) { char base_name_test[MAX_ID_NAME - 2]; int number_test; - if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && + if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) && (ELEM(id_test->name[base_name_len + 2], '.', '\0')) && STREQLEN(name, id_test->name + 2, base_name_len) && (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == @@ -1702,15 +1704,18 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * * Only for local IDs (linked ones already have a unique ID in their library). * + * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked + * (otherwise, just ensure that it is properly sorted). + * * \return true if a new name had to be created. */ -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) +bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data) { bool result = false; char name[MAX_ID_NAME - 2]; - /* If library, don't rename, but do ensure proper sorting. */ - if (ID_IS_LINKED(id)) { + /* If library, don't rename (unless explicitely required), but do ensure proper sorting. */ + if (!do_linked_data && ID_IS_LINKED(id)) { id_sort_by_name(lb, id, NULL); return result; @@ -2193,9 +2198,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) /* search for id */ idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); - if (idtest != NULL) { + if (idtest != NULL && !ID_IS_LINKED(idtest)) { /* BKE_id_new_name_validate also takes care of sorting. */ - BKE_id_new_name_validate(lb, idtest, NULL); + BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; } } @@ -2205,8 +2210,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) */ void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { + BLI_assert(!ID_IS_LINKED(id)); ListBase *lb = which_libbase(bmain, GS(id->name)); - if (BKE_id_new_name_validate(lb, id, name)) { + if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; } } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index fbe4a15da1c..8e21ae88aa6 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -20,6 +20,7 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -110,4 +111,63 @@ TEST(lib_id_main_sort, linked_ids_1) test_lib_id_main_sort_free(&ctx); } +TEST(lib_id_main_unique_name, local_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + test_lib_id_main_sort_check_order({id_a, id_b, id_c}); + + BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false); + EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_a); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_a, id_c, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + +TEST(lib_id_main_unique_name, linked_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); + Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + + id_a->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); + id_b->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + id_b->lib = lib_b; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 0e2317c72de..4d5085d6ad5 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -252,6 +252,13 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id) return false; } + /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just + * assume they are never user-edited, actual proper detection will happen from their owner check. + */ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + return false; + } + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) { @@ -421,7 +428,8 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, /* If other ID is a linked one, but not from the same library as our reference, then we * consider we should also remap it, as part of recursive resync. */ - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib) { + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib && + other_id != local_id) { BKE_libblock_relink_ex(bmain, other_id, reference_id, @@ -461,6 +469,8 @@ typedef struct LibOverrideGroupTagData { ID *id_root; uint tag; uint missing_tag; + /* Whether we are looping on override data, or their references (linked) one. */ + bool is_override; } LibOverrideGroupTagData; /* Tag all IDs in dependency relationships within an override hierarchy/group. @@ -473,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa { Main *bmain = data->bmain; ID *id = data->id_root; + const bool is_override = data->is_override; MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); BLI_assert(entry != NULL); @@ -494,16 +505,16 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa } /* We only consider IDs from the same library. */ ID *to_id = *to_id_entry->id_pointer.to; - if (!ID_IS_LINKED(to_id) && !ID_IS_OVERRIDE_LIBRARY(to_id)) { - /* Pure local data is a barrier of dependency in override cases. */ + if (to_id == NULL || to_id->lib != id->lib || + (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) { + /* IDs from different libraries, or non-override IDs in case we are processing overrides, are + * both barriers of dependency. */ continue; } - if (to_id != NULL && to_id->lib == id->lib) { - LibOverrideGroupTagData sub_data = *data; - sub_data.id_root = to_id; - if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { - id->tag |= data->tag; - } + LibOverrideGroupTagData sub_data = *data; + sub_data.id_root = to_id; + if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { + id->tag |= data->tag; } } @@ -515,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_LINKED(id_owner)); + BLI_assert(!data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -580,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_root = data->id_root; + BLI_assert(!data->is_override); if ((id_root->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -606,11 +619,12 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) } } -static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner)); + BLI_assert(data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -658,15 +672,16 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data /* Recursively process the dependencies. */ LibOverrideGroupTagData sub_data = *data; sub_data.id_root = to_id; - lib_override_local_group_tag_recursive(&sub_data); + lib_override_overrides_group_tag_recursive(&sub_data); } } /* This will tag all override IDs of an override group defined by the given `id_root`. */ -static void lib_override_local_group_tag(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + BLI_assert(data->is_override); if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -676,14 +691,17 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) } /* Tag all local overrides in id_root's group. */ - lib_override_local_group_tag_recursive(data); + lib_override_overrides_group_tag_recursive(data); } static bool lib_override_library_create_do(Main *bmain, ID *id_root) { BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -934,12 +952,16 @@ bool BKE_lib_override_library_resync(Main *bmain, ID *id_root_reference = id_root->override_library->reference; BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; - lib_override_local_group_tag(&data); + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; + lib_override_overrides_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); data.id_root = id_root_reference; + data.is_override = false; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1277,6 +1299,58 @@ bool BKE_lib_override_library_resync(Main *bmain, return success; } +/* Ensures parent collection (or objects) in the same override group are also tagged for resync. + * + * This is needed since otherwise, some (new) ID added in one sub-collection might be used in + * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those + * sub-collections would be unaware that this is the same ID, and would re-generate several + * overrides for it. + * + * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to + * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and + * resync the whole hierarchy. */ +static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level) +{ + (*level)++; + ID *return_id = id; + + switch (GS(id->name)) { + case ID_GR: { + /* Find the highest valid collection in the parenting hierarchy. + * Note that in practice, in any decent common case there is only one well defined root + * collection anyway. */ + int max_level = *level; + Collection *collection = (Collection *)id; + LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) { + Collection *collection_parent = collection_parent_iter->collection; + if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) && + collection_parent->id.lib == id->lib) { + int tmp_level = *level; + ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id, + &tmp_level); + if (tmp_level > max_level) { + max_level = tmp_level; + return_id = tmp_id; + } + } + } + break; + } + case ID_OB: { + Object *object = (Object *)id; + if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) && + object->parent->id.lib == id->lib) { + return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level); + } + break; + } + default: + break; + } + + return return_id; +} + /* Ensure resync of all overrides at one level of indirect usage. * * We need to handle each level independently, since an override at level n may be affected by @@ -1312,7 +1386,8 @@ static void lib_override_library_main_resync_on_library_indirect_level( LibOverrideGroupTagData data = {.bmain = bmain, .id_root = id->override_library->reference, .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING}; + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); @@ -1396,6 +1471,9 @@ static void lib_override_library_main_resync_on_library_indirect_level( (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } + + int level = 0; + id = lib_override_library_main_resync_find_root_recurse(id, &level); BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); do_continue = true; @@ -1550,9 +1628,12 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) /* Tag all library overrides in the chains of dependencies from the given root one. */ BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; - lib_override_local_group_tag(&data); + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; + lib_override_overrides_group_tag(&data); BKE_main_relations_free(bmain); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3377f5c69dc..d0864e85373 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5066,6 +5066,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_primitive_ico_sphere(); register_node_type_geo_mesh_primitive_line(); register_node_type_geo_mesh_primitive_uv_sphere(); + register_node_type_geo_mesh_to_curve(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 39f65d76e3c..60c216a8401 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -205,12 +205,11 @@ static int write_audio_frame(FFMpegContext *context) success = -1; } - av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); - if (pkt->duration > 0) { - pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base); - } - pkt->stream_index = context->audio_stream->index; + av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->audio_stream, pkt); +# endif pkt->flags |= AV_PKT_FLAG_KEY; @@ -349,6 +348,10 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif + if (av_interleaved_write_frame(context->outfile, packet) != 0) { success = -1; break; @@ -515,6 +518,48 @@ static void set_ffmpeg_properties(RenderData *rd, } } +static AVRational calc_time_base(uint den, double num, int codec_id) +{ + /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer + * (within a floating point error range). + * For example if we have `den = 3` and `num = 0.1` then the fps is: `den/num = 30` fps. + * When converting this to a FFMPEG time base, we want num to be an integer. + * So we simply move the decimal places of both numbers. i.e. `den = 30`, `num = 1`. */ + float eps = FLT_EPSILON; + const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; + + /* Calculate the precision of the initial floating point number. */ + if (num > 1.0) { + const uint num_integer_bits = log2_floor_u((unsigned int)num); + + /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits) + * For example, a float has 23 mantissa bits and the float value 3.5f as a pow2 range of + * (4-2=2): + * (2) / pow2(23) = floating point precision for 3.5f + */ + eps = (float)(1 << num_integer_bits) * FLT_EPSILON; + } + + /* Calculate how many decimal shifts we can do until we run out of precision. */ + const int max_num_shift = fabsf(log10f(eps)); + /* Calculate how many times we can shift the denominator. */ + const int max_den_shift = log10f(DENUM_MAX) - log10f(den); + const int max_iter = min_ii(max_num_shift, max_den_shift); + + for (int i = 0; i < max_iter && fabs(num - round(num)) > eps; i++) { + /* Increase the number and denominator until both are integers. */ + num *= 10; + den *= 10; + eps *= 10; + } + + AVRational time_base; + time_base.den = den; + time_base.num = (int)num; + + return time_base; +} + /* prepare a video stream for the output file */ static AVStream *alloc_video_stream(FFMpegContext *context, @@ -545,13 +590,24 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_VIDEO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid video codec\n"); + avcodec_free_context(&c); + context->video_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + /* Get some values from the current render settings */ c->width = rectx; c->height = recty; - /* FIXME: Really bad hack (tm) for NTSC support */ if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + /* FIXME: Really bad hack (tm) for NTSC support */ c->time_base.den = 2997; c->time_base.num = 100; } @@ -559,21 +615,23 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->time_base.den = rd->frs_sec; c->time_base.num = (int)rd->frs_sec_base; } - else if (compare_ff(rd->frs_sec_base, 1.001f, 0.000001f)) { - /* This converts xx/1.001 (which is used in presets) to xx000/1001 (which is used in the rest - * of the world, including FFmpeg). */ - c->time_base.den = (int)(rd->frs_sec * 1000); - c->time_base.num = (int)(rd->frs_sec_base * 1000); - } else { - /* This calculates a fraction (DENUM_MAX / num) which approximates the scene frame rate - * (frs_sec / frs_sec_base). It uses the maximum denominator allowed by FFmpeg. - */ - const double DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; - const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base; + c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id); + } - c->time_base.den = (int)DENUM_MAX; - c->time_base.num = (int)num; + /* As per the time-base documentation here: + * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options + * We want to set the time base to (1 / fps) for fixed frame rate video. + * If it is not possible, we want to set the time-base numbers to something as + * small as possible. + */ + if (c->time_base.num != 1) { + AVRational new_time_base; + if (av_reduce( + &new_time_base.num, &new_time_base.den, c->time_base.num, c->time_base.den, INT_MAX)) { + /* Exact reduction was possible. Use the new value. */ + c->time_base = new_time_base; + } } st->time_base = c->time_base; @@ -585,6 +643,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context, ffmpeg_dict_set_int(&opts, "lossless", 1); } else if (context->ffmpeg_crf >= 0) { + /* As per https://trac.ffmpeg.org/wiki/Encode/VP9 we must set the bit rate to zero when + * encoding with vp9 in crf mode. + * Set this to always be zero for other codecs as well. + * We don't care about bit rate in crf mode. */ + c->bit_rate = 0; ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf); } else { @@ -624,12 +687,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - avcodec_free_context(&c); - return NULL; - } - /* Be sure to use the correct pixel format(e.g. RGB, YUV) */ if (codec->pix_fmts) { @@ -646,12 +703,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); } - if (codec_id == AV_CODEC_ID_H264) { - /* correct wrong default ffmpeg param which crash x264 */ - c->qmin = 10; - c->qmax = 51; - } - /* Keep lossless encodes in the RGB domain. */ if (codec_id == AV_CODEC_ID_HUFFYUV) { if (rd->im_format.planes == R_IMF_PLANES_RGBA) { @@ -711,10 +762,14 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->thread_type = FF_THREAD_SLICE; } - if (avcodec_open2(c, codec, &opts) < 0) { + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize video codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->video_codec = NULL; return NULL; } av_dict_free(&opts); @@ -774,6 +829,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_AUDIO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid audio codec\n"); + avcodec_free_context(&c); + context->audio_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + c->sample_rate = rd->ffcodecdata.audio_mixrate; c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; @@ -803,13 +869,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->sample_fmt = AV_SAMPLE_FMT_FLT; } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - // XXX error("Couldn't find a valid audio codec"); - avcodec_free_context(&c); - return NULL; - } - if (codec->sample_fmts) { /* Check if the preferred sample format for this codec is supported. * this is because, depending on the version of libav, @@ -849,11 +908,14 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, set_ffmpeg_properties(rd, c, "audio", &opts); - if (avcodec_open2(c, codec, &opts) < 0) { - // XXX error("Couldn't initialize audio codec"); + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->audio_codec = NULL; return NULL; } av_dict_free(&opts); @@ -1181,6 +1243,9 @@ static void flush_ffmpeg(FFMpegContext *context) packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif int write_ret = av_interleaved_write_frame(context->outfile, packet); if (write_ret != 0) { @@ -1630,49 +1695,7 @@ static void ffmpeg_set_expert_options(RenderData *rd) IDP_FreePropertyContent(rd->ffcodecdata.properties); } - if (codec_id == AV_CODEC_ID_H264) { - /* - * All options here are for x264, but must be set via ffmpeg. - * The names are therefore different - Search for "x264 to FFmpeg option mapping" - * to get a list. - */ - - /* - * Use CABAC coder. Using "coder:1", which should be equivalent, - * crashes Blender for some reason. Either way - this is no big deal. - */ - BKE_ffmpeg_property_add_string(rd, "video", "coder:vlc"); - - /* - * The other options were taken from the libx264-default.preset - * included in the ffmpeg distribution. - */ - - /* This breaks compatibility for QT. */ - // BKE_ffmpeg_property_add_string(rd, "video", "flags:loop"); - BKE_ffmpeg_property_add_string(rd, "video", "cmp:chroma"); - BKE_ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "me:hex"); - BKE_ffmpeg_property_add_string(rd, "video", "subq:6"); - BKE_ffmpeg_property_add_string(rd, "video", "me_range:16"); - BKE_ffmpeg_property_add_string(rd, "video", "qdiff:4"); - BKE_ffmpeg_property_add_string(rd, "video", "keyint_min:25"); - BKE_ffmpeg_property_add_string(rd, "video", "sc_threshold:40"); - BKE_ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71"); - BKE_ffmpeg_property_add_string(rd, "video", "b_strategy:1"); - BKE_ffmpeg_property_add_string(rd, "video", "bf:3"); - BKE_ffmpeg_property_add_string(rd, "video", "refs:2"); - BKE_ffmpeg_property_add_string(rd, "video", "qcomp:0.6"); - - BKE_ffmpeg_property_add_string(rd, "video", "trellis:0"); - BKE_ffmpeg_property_add_string(rd, "video", "weightb:1"); - BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1"); - BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1"); - BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2"); - } - else if (codec_id == AV_CODEC_ID_DNXHD) { + if (codec_id == AV_CODEC_ID_DNXHD) { if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd"); } diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh index fb5e2b61cdb..b9eda2ad7e1 100644 --- a/source/blender/blenlib/BLI_mpq3.hh +++ b/source/blender/blenlib/BLI_mpq3.hh @@ -218,6 +218,15 @@ struct mpq3 { return a.x * b.x + a.y * b.y + a.z * b.z; } + static mpq_class dot_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer *= b; + buffer.x += buffer.y; + buffer.x += buffer.z; + return buffer.x; + } + static mpq3 cross(const mpq3 &a, const mpq3 &b) { return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); @@ -246,6 +255,13 @@ struct mpq3 { return mpq3::dot(diff, diff); } + static mpq_class distance_squared_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer -= b; + return mpq3::dot(buffer, buffer); + } + static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t) { mpq_class s = 1 - t; diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 106bd5bc793..107c27da6a2 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -171,7 +171,7 @@ size_t BLI_gzip_mem_to_file_at_pos( z_stream strm; unsigned char out[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; @@ -217,7 +217,7 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g size_t chunk = 256 * 1024; unsigned char in[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 25291b8c3b0..cc27e9238c3 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -41,6 +41,7 @@ # include "BLI_set.hh" # include "BLI_span.hh" # include "BLI_stack.hh" +# include "BLI_task.hh" # include "BLI_vector.hh" # include "BLI_vector_set.hh" @@ -48,6 +49,10 @@ # include "BLI_mesh_boolean.hh" +# ifdef WITH_TBB +# include "tbb/spin_mutex.h" +# endif + // # define PERFDEBUG namespace blender::meshintersect { @@ -1690,9 +1695,24 @@ static int find_containing_cell(const Vert *v, * If the closest point is on an edge, return 0, 1, or 2 * for edges ab, bc, or ca in *r_edge; else -1. * (Adapted from #closest_on_tri_to_point_v3()). + * The arguments ab, ac, ..., r are used as temporaries + * in this routine. Passing them in from the caller can + * avoid many allocs and frees of temporary mpq3 values + * and the mpq_class values within them. */ -static mpq_class closest_on_tri_to_point( - const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert) +static mpq_class closest_on_tri_to_point(const mpq3 &p, + const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + mpq3 &ab, + mpq3 &ac, + mpq3 &ap, + mpq3 &bp, + mpq3 &cp, + mpq3 &m, + mpq3 &r, + int *r_edge, + int *r_vert) { constexpr int dbg_level = 0; if (dbg_level > 0) { @@ -1700,11 +1720,15 @@ static mpq_class closest_on_tri_to_point( std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n"; } /* Check if p in vertex region outside a. */ - mpq3 ab = b - a; - mpq3 ac = c - a; - mpq3 ap = p - a; - mpq_class d1 = mpq3::dot(ab, ap); - mpq_class d2 = mpq3::dot(ac, ap); + ab = b; + ab -= a; + ac = c; + ac -= a; + ap = p; + ap -= a; + + mpq_class d1 = mpq3::dot_with_buffer(ab, ap, m); + mpq_class d2 = mpq3::dot_with_buffer(ac, ap, m); if (d1 <= 0 && d2 <= 0) { /* Barycentric coordinates (1,0,0). */ *r_edge = -1; @@ -1712,12 +1736,13 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = a\n"; } - return mpq3::distance_squared(p, a); + return mpq3::distance_squared_with_buffer(p, a, m); } /* Check if p in vertex region outside b. */ - mpq3 bp = p - b; - mpq_class d3 = mpq3::dot(ab, bp); - mpq_class d4 = mpq3::dot(ac, bp); + bp = p; + bp -= b; + mpq_class d3 = mpq3::dot_with_buffer(ab, bp, m); + mpq_class d4 = mpq3::dot_with_buffer(ac, bp, m); if (d3 >= 0 && d4 <= d3) { /* Barycentric coordinates (0,1,0). */ *r_edge = -1; @@ -1725,25 +1750,28 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = b\n"; } - return mpq3::distance_squared(p, b); + return mpq3::distance_squared_with_buffer(p, b, m); } /* Check if p in region of ab. */ mpq_class vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { mpq_class v = d1 / (d1 - d3); /* Barycentric coordinates (1-v,v,0). */ - mpq3 r = a + v * ab; + r = ab; + r *= v; + r += a; *r_vert = -1; *r_edge = 0; if (dbg_level > 0) { std::cout << " answer = on ab at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in vertex region outside c. */ - mpq3 cp = p - c; - mpq_class d5 = mpq3::dot(ab, cp); - mpq_class d6 = mpq3::dot(ac, cp); + cp = p; + cp -= c; + mpq_class d5 = mpq3::dot_with_buffer(ab, cp, m); + mpq_class d6 = mpq3::dot_with_buffer(ac, cp, m); if (d6 >= 0 && d5 <= d6) { /* Barycentric coordinates (0,0,1). */ *r_edge = -1; @@ -1751,49 +1779,54 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = c\n"; } - return mpq3::distance_squared(p, c); + return mpq3::distance_squared_with_buffer(p, c, m); } /* Check if p in edge region of ac. */ mpq_class vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { mpq_class w = d2 / (d2 - d6); /* Barycentric coordinates (1-w,0,w). */ - mpq3 r = a + w * ac; + r = ac; + r *= w; + r += a; *r_vert = -1; *r_edge = 2; if (dbg_level > 0) { std::cout << " answer = on ac at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in edge region of bc. */ mpq_class va = d3 * d6 - d5 * d4; if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) { mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); /* Barycentric coordinates (0,1-w,w). */ - mpq3 r = c - b; - r = w * r; - r = r + b; + r = c; + r -= b; + r *= w; + r += b; *r_vert = -1; *r_edge = 1; if (dbg_level > 0) { std::cout << " answer = on bc at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* p inside face region. Compute barycentric coordinates (u,v,w). */ mpq_class denom = 1 / (va + vb + vc); mpq_class v = vb * denom; mpq_class w = vc * denom; - ac = w * ac; - mpq3 r = a + v * ab; - r = r + ac; + ac *= w; + r = ab; + r *= v; + r += a; + r += ac; *r_vert = -1; *r_edge = -1; if (dbg_level > 0) { std::cout << " answer = inside at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } struct ComponentContainer { @@ -1832,6 +1865,9 @@ static Vector<ComponentContainer> find_component_containers(int comp, if (dbg_level > 0) { std::cout << "test vertex in comp: " << test_v << "\n"; } + + mpq3 buf[7]; + for (int comp_other : components.index_range()) { if (comp == comp_other) { continue; @@ -1856,6 +1892,13 @@ static Vector<ComponentContainer> find_component_containers(int comp, tri[0]->co_exact, tri[1]->co_exact, tri[2]->co_exact, + buf[0], + buf[1], + buf[2], + buf[3], + buf[4], + buf[5], + buf[6], &close_edge, &close_vert); if (dbg_level > 1) { @@ -2567,47 +2610,58 @@ static IMesh raycast_tris_boolean(const IMesh &tm, BVHTree *tree = raycast_tree(tm); Vector<Face *> out_faces; out_faces.reserve(tm.face_size()); - Array<float> in_shape(nshapes, 0); - Array<int> winding(nshapes, 0); - for (int t : tm.face_index_range()) { - Face &tri = *tm.face(t); - int shape = shape_fn(tri.orig); - if (dbg_level > 0) { - std::cout << "process triangle " << t << " = " << &tri << "\n"; - std::cout << "shape = " << shape << "\n"; - } - test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); - for (int other_shape = 0; other_shape < nshapes; ++other_shape) { - if (other_shape == shape) { - continue; - } - /* The in_shape array has a confidence value for "insideness". - * For most operations, even a hint of being inside - * gives good results, but when shape is a cutter in a Difference - * operation, we want to be pretty sure that the point is inside other_shape. - * E.g., T75827. - * Also, when the operation is intersection, we also want high confidence. - */ - bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || - op == BoolOpType::Intersect; - bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); +# ifdef WITH_TBB + tbb::spin_mutex mtx; +# endif + const int grainsize = 256; + parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) { + Array<float> in_shape(nshapes, 0); + Array<int> winding(nshapes, 0); + for (int t : range) { + Face &tri = *tm.face(t); + int shape = shape_fn(tri.orig); if (dbg_level > 0) { - std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " - << other_shape << " val = " << in_shape[other_shape] << "\n"; + std::cout << "process triangle " << t << " = " << &tri << "\n"; + std::cout << "shape = " << shape << "\n"; } - winding[other_shape] = inside; - } - bool do_flip; - bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); - if (!do_remove) { - if (!do_flip) { - out_faces.append(&tri); + test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); + for (int other_shape = 0; other_shape < nshapes; ++other_shape) { + if (other_shape == shape) { + continue; + } + /* The in_shape array has a confidence value for "insideness". + * For most operations, even a hint of being inside + * gives good results, but when shape is a cutter in a Difference + * operation, we want to be pretty sure that the point is inside other_shape. + * E.g., T75827. + * Also, when the operation is intersection, we also want high confidence. + */ + bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || + op == BoolOpType::Intersect; + bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); + if (dbg_level > 0) { + std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " + << other_shape << " val = " << in_shape[other_shape] << "\n"; + } + winding[other_shape] = inside; } - else { - raycast_add_flipped(out_faces, tri, arena); + bool do_flip; + bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); + { +# ifdef WITH_TBB + tbb::spin_mutex::scoped_lock lock(mtx); +# endif + if (!do_remove) { + if (!do_flip) { + out_faces.append(&tri); + } + else { + raycast_add_flipped(out_faces, tri, arena); + } + } } } - } + }); BLI_bvhtree_free(tree); ans.set_faces(out_faces); return ans; diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 636209883c3..97f856476c5 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1165,13 +1165,19 @@ static int filter_plane_side(const double3 &p, * Assumes ab is not perpendicular to n. * This works because the ratio of the projections of ab and ac onto n is the same as * the ratio along the line ab of the intersection point to the whole of ab. + * The ab, ac, and dotbuf arguments are used as a temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n) -{ - mpq3 ab = a - b; - mpq_class den = mpq3::dot(ab, n); +static inline mpq3 tti_interp( + const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf) +{ + ab = a; + ab -= b; + ac = a; + ac -= c; + mpq_class den = mpq3::dot_with_buffer(ab, n, dotbuf); BLI_assert(den != 0); - mpq_class alpha = mpq3::dot(a - c, n) / den; + mpq_class alpha = mpq3::dot_with_buffer(ac, n, dotbuf) / den; return a - alpha * ab; } @@ -1179,11 +1185,28 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const * Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW * order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations. * TODO: change arguments to `const Vert *` and use floating filters. + * The ba, ca, n, and dotbuf arguments are used as temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad) +static inline int tti_above(const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + const mpq3 &ad, + mpq3 &ba, + mpq3 &ca, + mpq3 &n, + mpq3 &dotbuf) { - mpq3 n = mpq3::cross(b - a, c - a); - return sgn(mpq3::dot(ad, n)); + ba = b; + ba -= a; + ca = c; + ca -= a; + + n.x = ba.y * ca.z - ba.z * ca.y; + n.y = ba.z * ca.x - ba.x * ca.z; + n.z = ba.x * ca.y - ba.y * ca.x; + + return sgn(mpq3::dot_with_buffer(ad, n, dotbuf)); } /** @@ -1227,20 +1250,21 @@ static ITT_value itt_canon2(const mpq3 &p1, mpq3 p1p2 = p2 - p1; mpq3 intersect_1; mpq3 intersect_2; + mpq3 buf[4]; bool no_overlap = false; /* Top test in classification tree. */ - if (tti_above(p1, q1, r2, p1p2) > 0) { + if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Middle right test in classification tree. */ - if (tti_above(p1, r1, r2, p1p2) <= 0) { + if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) { /* Bottom right test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) > 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Overlap is [k [i l] j]. */ if (dbg_level > 0) { std::cout << "overlap [k [i l] j]\n"; } /* i is intersect with p1r1. l is intersect with p2r2. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k l] j]. */ @@ -1248,8 +1272,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k l] j]\n"; } /* k is intersect with p2q2. l is intersect is p2r2. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } } else { @@ -1262,7 +1286,7 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Middle left test in classification tree. */ - if (tti_above(p1, q1, q2, p1p2) < 0) { + if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) { /* No overlap: [i j] [k l]. */ if (dbg_level > 0) { std::cout << "no overlap: [i j] [k l]\n"; @@ -1271,14 +1295,14 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Bottom left test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) >= 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) { /* Overlap is [k [i j] l]. */ if (dbg_level > 0) { std::cout << "overlap [k [i j] l]\n"; } /* i is intersect with p1r1. j is intersect with p1q1. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k j] l]. */ @@ -1286,8 +1310,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k j] l]\n"; } /* k is intersect with p2q2. j is intersect with p1q1. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } } } @@ -1438,6 +1462,7 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) return ITT_value(INONE); } + mpq3 buf[2]; const mpq3 &p1 = vp1->co_exact; const mpq3 &q1 = vq1->co_exact; const mpq3 &r1 = vr1->co_exact; @@ -1447,13 +1472,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) const mpq3 &n2 = tri2.plane->norm_exact; if (sp1 == 0) { - sp1 = sgn(mpq3::dot(p1 - r2, n2)); + buf[0] = p1; + buf[0] -= r2; + sp1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sq1 == 0) { - sq1 = sgn(mpq3::dot(q1 - r2, n2)); + buf[0] = q1; + buf[0] -= r2; + sq1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sr1 == 0) { - sr1 = sgn(mpq3::dot(r1 - r2, n2)); + buf[0] = r1; + buf[0] -= r2; + sr1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (dbg_level > 1) { @@ -1473,13 +1504,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) /* Repeat for signs of t2's vertices with respect to plane of t1. */ const mpq3 &n1 = tri1.plane->norm_exact; if (sp2 == 0) { - sp2 = sgn(mpq3::dot(p2 - r1, n1)); + buf[0] = p2; + buf[0] -= r1; + sp2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sq2 == 0) { - sq2 = sgn(mpq3::dot(q2 - r1, n1)); + buf[0] = q2; + buf[0] -= r1; + sq2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sr2 == 0) { - sr2 = sgn(mpq3::dot(r2 - r1, n1)); + buf[0] = r2; + buf[0] -= r1; + sr2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (dbg_level > 1) { diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 6338dc95aba..990fc1d65d7 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -446,7 +446,7 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name) id->flag = LIB_FAKEUSER; *((short *)id->name) = ID_GD; - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); /* alphabetic insertion: is in BKE_id_new_name_validate */ if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 8c5e86eadd3..c175714c537 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -22,6 +22,7 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" @@ -85,6 +86,32 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } +static void version_switch_node_input_prefix(Main *bmain) +{ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_SWITCH) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + /* Skip the "switch" socket. */ + if (socket == node->inputs.first) { + continue; + } + strcpy(socket->name, socket->name[0] == 'A' ? "False" : "True"); + + /* Replace "A" and "B", but keep the unique number suffix at the end. */ + char number_suffix[8]; + BLI_strncpy(number_suffix, socket->identifier + 1, sizeof(number_suffix)); + strcpy(socket->identifier, socket->name); + strcat(socket->identifier, number_suffix); + } + } + } + } + } + FOREACH_NODETREE_END; +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -110,6 +137,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) { + version_switch_node_input_prefix(bmain); + + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose == NULL) { + continue; + } + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -121,15 +164,5 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ - if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->pose == NULL) { - continue; - } - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); - } - } - } } } diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index c215cf69e3a..af47b9557ef 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -102,6 +102,8 @@ set(SRC intern/bmesh_mesh_convert.h intern/bmesh_mesh_duplicate.c intern/bmesh_mesh_duplicate.h + intern/bmesh_mesh_tessellate.c + intern/bmesh_mesh_tessellate.h intern/bmesh_mesh_validate.c intern/bmesh_mesh_validate.h intern/bmesh_mods.c @@ -182,10 +184,6 @@ set(LIB extern_rangetree ) -if(MSVC AND NOT MSVC_CLANG) - string(APPEND CMAKE_C_FLAGS " /WX /wd4101") -endif() - if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} @@ -225,6 +223,10 @@ endif() blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +if(MSVC AND NOT MSVC_CLANG) + target_compile_options(bf_bmesh PRIVATE /WX /wd4101) +endif() + if(WITH_GTESTS) set(TEST_SRC tests/bmesh_core_test.cc diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 4441ccc0c88..a7e4e011ae2 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -215,6 +215,7 @@ extern "C" { #include "intern/bmesh_mesh.h" #include "intern/bmesh_mesh_convert.h" #include "intern/bmesh_mesh_duplicate.h" +#include "intern/bmesh_mesh_tessellate.h" #include "intern/bmesh_mesh_validate.h" #include "intern/bmesh_mods.h" #include "intern/bmesh_operators.h" diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c new file mode 100644 index 00000000000..c8ea9429145 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -0,0 +1,305 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bmesh + * + * This file contains code for polygon tessellation + * (creating triangles from polygons). + */ + +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_heap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_polyfill_2d_beautify.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +/* -------------------------------------------------------------------- */ +/** \name Default Mesh Tessellation + * \{ */ + +static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p) +{ + switch (efa->len) { + case 3: { + /* `0 1 2` -> `0 1 2` */ + BMLoop *l; + BMLoop **l_ptr = looptris[0]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + return 1; + } + case 4: { + /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ + BMLoop *l; + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; + (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); + (l_ptr_a[1] = l = l->next); + (l_ptr_a[2] = l_ptr_b[1] = l = l->next); + (l_ptr_b[2] = l->next); + + if (UNLIKELY(is_quad_flip_v3_first_third_fast( + l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { + /* Flip out of degenerate 0-2 state. */ + l_ptr_a[2] = l_ptr_b[2]; + l_ptr_b[0] = l_ptr_a[1]; + } + return 2; + } + default: { + BMLoop *l_iter, *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const int tris_len = efa->len - 2; + + MemArena *pf_arena = *pf_arena_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + int i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); + + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(pf_arena); + return tris_len; + } + } +} + +/** + * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh + * \param looptris: + * + * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count + */ +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena); + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + pf_arena = NULL; + } + + BLI_assert(i <= looptris_tot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Beauty Mesh Tessellation + * + * Avoid degenerate triangles. + * \{ */ + +static int mesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p, + Heap **pf_heap_p) +{ + switch (efa->len) { + case 3: { + BMLoop *l; + BMLoop **l_ptr = looptris[0]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + return 1; + } + case 4: { + BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); + BMLoop *l_v2 = l_v1->next; + BMLoop *l_v3 = l_v2->next; + BMLoop *l_v4 = l_v1->prev; + + /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! + * It's meant for rotating edges, it also calculates a new normal. + * + * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. + */ +#if 0 + const bool split_13 = (BM_verts_calc_rotate_beauty( + l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); +#else + float axis_mat[3][3], v_quad[4][2]; + axis_dominant_v3_to_m3(axis_mat, efa->no); + mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); + mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); + mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); + mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); + + const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( + v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; +#endif + + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; + if (split_13) { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v3; + + l_ptr_b[0] = l_v1; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + else { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v4; + + l_ptr_b[0] = l_v2; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + return 2; + } + default: { + MemArena *pf_arena = *pf_arena_p; + Heap *pf_heap = *pf_heap_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + } + + BMLoop *l_iter, *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const int tris_len = efa->len - 2; + + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + int i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); + + BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); + + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(pf_arena); + + return tris_len; + } + } +} + +/** + * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + */ +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + /* use_beauty */ + Heap *pf_heap = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap); + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + + BLI_heap_free(pf_heap, NULL); + } + + BLI_assert(i <= looptris_tot); +} + +/** \} */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h new file mode 100644 index 00000000000..5606a5a7e02 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h @@ -0,0 +1,24 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bmesh + */ + +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 4ae2cc67140..5397098a7f3 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -18,8 +18,7 @@ * \ingroup bmesh * * This file contains code for dealing - * with polygons (normal/area calculation, - * tessellation, etc) + * with polygons (normal/area calculation, tessellation, etc) */ #include "DNA_listBase.h" @@ -1523,289 +1522,3 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) l = l->next; r_loops[3] = l; } - -/** - * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh - * \param looptris: - * - * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count - */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) -{ - /* use this to avoid locking pthread for _every_ polygon - * and calling the fill function */ -#define USE_TESSFACE_SPEEDUP - - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *arena = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ - } - -#ifdef USE_TESSFACE_SPEEDUP - - /* no need to ensure the loop order, we know its ok */ - - else if (efa->len == 3) { -# if 0 - int j; - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - looptris[i][j] = l; - } - i += 1; -# else - /* more cryptic but faster */ - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; -# endif - } - else if (efa->len == 4) { -# if 0 - BMLoop *ltmp[4]; - int j; - BLI_array_grow_items(looptris, 2); - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - ltmp[j] = l; - } - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[1]; - looptris[i][2] = ltmp[2]; - i += 1; - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[2]; - looptris[i][2] = ltmp[3]; - i += 1; -# else - /* more cryptic but faster */ - BMLoop *l; - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); - (l_ptr_a[1] = l = l->next); - (l_ptr_a[2] = l_ptr_b[1] = l = l->next); - (l_ptr_b[2] = l->next); -# endif - - if (UNLIKELY(is_quad_flip_v3_first_third_fast( - l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { - /* flip out of degenerate 0-2 state. */ - l_ptr_a[2] = l_ptr_b[2]; - l_ptr_b[0] = l_ptr_a[1]; - } - } - -#endif /* USE_TESSFACE_SPEEDUP */ - - else { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - uint(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - uint *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(arena); - } - } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - *r_looptris_tot = i; - - BLI_assert(i <= looptris_tot); - -#undef USE_TESSFACE_SPEEDUP -} - -/** - * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. - */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) -{ - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *pf_arena = NULL; - - /* use_beauty */ - Heap *pf_heap = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ - } - else if (efa->len == 3) { - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; - } - else if (efa->len == 4) { - BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); - BMLoop *l_v2 = l_v1->next; - BMLoop *l_v3 = l_v2->next; - BMLoop *l_v4 = l_v1->prev; - - /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! - * It's meant for rotating edges, it also calculates a new normal. - * - * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. - */ -#if 0 - const bool split_13 = (BM_verts_calc_rotate_beauty( - l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); -#else - float axis_mat[3][3], v_quad[4][2]; - axis_dominant_v3_to_m3(axis_mat, efa->no); - mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); - mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); - mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); - mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); - - const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( - v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; -#endif - - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - if (split_13) { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v3; - - l_ptr_b[0] = l_v1; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - else { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v4; - - l_ptr_b[0] = l_v2; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - } - else { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - unsigned int(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(pf_arena == NULL)) { - pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); - } - - tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); - - BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - unsigned int *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(pf_arena); - } - } - - if (pf_arena) { - BLI_memarena_free(pf_arena); - - BLI_heap_free(pf_heap, NULL); - } - - *r_looptris_tot = i; - - BLI_assert(i <= looptris_tot); -} diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 8c2b9ee0bff..e7d5cb2f89d 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -24,9 +24,6 @@ struct Heap; #include "BLI_compiler_attrs.h" -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); - void BM_face_calc_tessellation(const BMFace *f, const bool use_fixed_quad, BMLoop **r_loops, diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 65391794c12..ac59d832013 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -49,6 +49,8 @@ set(SRC COM_compositor.h COM_defines.h + intern/COM_BufferOperation.cc + intern/COM_BufferOperation.h intern/COM_CPUDevice.cc intern/COM_CPUDevice.h intern/COM_ChunkOrder.cc @@ -66,14 +68,20 @@ set(SRC intern/COM_Enums.cc intern/COM_ExecutionGroup.cc intern/COM_ExecutionGroup.h + intern/COM_ExecutionModel.cc + intern/COM_ExecutionModel.h intern/COM_ExecutionSystem.cc intern/COM_ExecutionSystem.h + intern/COM_FullFrameExecutionModel.cc + intern/COM_FullFrameExecutionModel.h intern/COM_MemoryBuffer.cc intern/COM_MemoryBuffer.h intern/COM_MemoryProxy.cc intern/COM_MemoryProxy.h intern/COM_MetaData.cc intern/COM_MetaData.h + intern/COM_MultiThreadedOperation.cc + intern/COM_MultiThreadedOperation.h intern/COM_Node.cc intern/COM_Node.h intern/COM_NodeConverter.cc @@ -86,8 +94,12 @@ set(SRC intern/COM_NodeOperationBuilder.h intern/COM_OpenCLDevice.cc intern/COM_OpenCLDevice.h + intern/COM_SharedOperationBuffers.cc + intern/COM_SharedOperationBuffers.h intern/COM_SingleThreadedOperation.cc intern/COM_SingleThreadedOperation.h + intern/COM_TiledExecutionModel.cc + intern/COM_TiledExecutionModel.h intern/COM_WorkPackage.cc intern/COM_WorkPackage.h intern/COM_WorkScheduler.cc diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index ef889807030..5a52d216117 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -20,6 +20,16 @@ namespace blender::compositor { +enum class eExecutionModel { + /** + * Operations are executed from outputs to inputs grouped in execution groups and rendered + * in tiles. + */ + Tiled, + /** Operations are fully rendered in order from inputs to outputs. */ + FullFrame +}; + /** * \brief possible data types for sockets * \ingroup Model diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc new file mode 100644 index 00000000000..8464d01801f --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_BufferOperation.h" + +namespace blender::compositor { + +BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) +{ + buffer_ = buffer; + /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following + * code to: set_resolution(buffer.get_size()) */ + unsigned int resolution[2]; + resolution[0] = buffer->getWidth(); + resolution[1] = buffer->getHeight(); + setResolution(resolution); + addOutputSocket(data_type); +} + +void *BufferOperation::initializeTileData(rcti * /*rect*/) +{ + return buffer_; +} + +void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +{ + switch (sampler) { + case PixelSampler::Nearest: + buffer_->read(output, x, y); + break; + case PixelSampler::Bilinear: + default: + buffer_->readBilinear(output, x, y); + break; + case PixelSampler::Bicubic: + /* No bicubic. Same implementation as ReadBufferOperation. */ + buffer_->readBilinear(output, x, y); + break; + } +} + +void BufferOperation::executePixelFiltered( + float output[4], float x, float y, float dx[2], float dy[2]) +{ + const float uv[2] = {x, y}; + const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; + buffer_->readEWA(output, uv, deriv); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h new file mode 100644 index 00000000000..f87cd4db94e --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +class BufferOperation : public NodeOperation { + private: + MemoryBuffer *buffer_; + + public: + BufferOperation(MemoryBuffer *buffer, DataType data_type); + + void *initializeTileData(rcti *rect) override; + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc index 29a82bec636..2ca5557e278 100644 --- a/source/blender/compositor/intern/COM_CPUDevice.cc +++ b/source/blender/compositor/intern/COM_CPUDevice.cc @@ -30,11 +30,24 @@ CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id) void CPUDevice::execute(WorkPackage *work_package) { - const unsigned int chunkNumber = work_package->chunk_number; - ExecutionGroup *executionGroup = work_package->execution_group; - - executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); - executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + switch (work_package->type) { + case eWorkPackageType::Tile: { + const unsigned int chunkNumber = work_package->chunk_number; + ExecutionGroup *executionGroup = work_package->execution_group; + + executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); + executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + break; + } + case eWorkPackageType::CustomFunction: { + work_package->execute_fn(); + break; + } + } + + if (work_package->executed_fn) { + work_package->executed_fn(); + } } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index f70f3a8ebfc..61e299c045e 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -21,6 +21,7 @@ #include <cstdio> #include "BLI_assert.h" +#include "DNA_userdef_types.h" namespace blender::compositor { @@ -33,6 +34,7 @@ CompositorContext::CompositorContext() this->m_fastCalculation = false; this->m_viewSettings = nullptr; this->m_displaySettings = nullptr; + this->m_bnodetree = nullptr; } int CompositorContext::getFramenumber() const @@ -41,4 +43,20 @@ int CompositorContext::getFramenumber() const return m_rd->cfra; } +eExecutionModel CompositorContext::get_execution_model() const +{ + if (U.experimental.use_full_frame_compositor) { + BLI_assert(m_bnodetree != nullptr); + switch (m_bnodetree->execution_mode) { + case 1: + return eExecutionModel::FullFrame; + case 0: + return eExecutionModel::Tiled; + default: + BLI_assert(!"Invalid execution mode"); + } + } + return eExecutionModel::Tiled; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index e6164246bdd..56251511576 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -281,6 +281,11 @@ class CompositorContext { { return m_rd->size * 0.01f; } + + /** + * Get active execution model. + */ + eExecutionModel get_execution_model() const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index dfb4f53fee5..4cf7e09a7d8 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -211,12 +211,14 @@ int DebugInfo::graphviz_legend_group( return len; } -int DebugInfo::graphviz_legend(char *str, int maxlen) +int DebugInfo::graphviz_legend(char *str, int maxlen, const bool has_execution_groups) { int len = 0; len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n"); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + } len += snprintf( str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n"); @@ -236,21 +238,24 @@ int DebugInfo::graphviz_legend(char *str, int maxlen) "Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0); len += graphviz_legend_color( "Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += graphviz_legend_color( + "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_color( + "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + } len += graphviz_legend_color( "Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n"); - - len += graphviz_legend_group( - "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n"); + len += graphviz_legend_group( + "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "</TABLE>\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n"); @@ -387,7 +392,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } } - len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0); + const bool has_execution_groups = system->getContext().get_execution_model() == + eExecutionModel::Tiled; + len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n"); diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index e1aea69e481..0de3a5e39dc 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -129,7 +129,7 @@ class DebugInfo { const char *name, const char *color, const char *style, char *str, int maxlen); static int graphviz_legend_group( const char *name, const char *color, const char *style, char *str, int maxlen); - static int graphviz_legend(char *str, int maxlen); + static int graphviz_legend(char *str, int maxlen, bool has_execution_groups); static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen); }; diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h index f65ce3e856e..519e7df940e 100644 --- a/source/blender/compositor/intern/COM_Enums.h +++ b/source/blender/compositor/intern/COM_Enums.h @@ -70,6 +70,21 @@ enum class eWorkPackageState { Executed = 2, }; +/** + * \brief Work type to execute. + * \ingroup Execution + */ +enum class eWorkPackageType { + /** + * \brief Executes an execution group tile. + */ + Tile = 0, + /** + * \brief Executes a custom function. + */ + CustomFunction = 1 +}; + std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority); std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state); diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index 80d453bf7f9..68bda8c70d6 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -157,6 +157,7 @@ void ExecutionGroup::init_work_packages() if (this->m_chunks_len != 0) { m_work_packages.resize(this->m_chunks_len); for (unsigned int index = 0; index < m_chunks_len; index++) { + m_work_packages[index].type = eWorkPackageType::Tile; m_work_packages[index].state = eWorkPackageState::NotScheduled; m_work_packages[index].execution_group = this; m_work_packages[index].chunk_number = index; diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc new file mode 100644 index 00000000000..4d7f62e091b --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.cc @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_ExecutionModel.h" + +namespace blender::compositor { + +ExecutionModel::ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations) + : context_(context), operations_(operations) +{ + const bNodeTree *node_tree = context_.getbNodeTree(); + + const rctf *viewer_border = &node_tree->viewer_border; + border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) && + viewer_border->xmin < viewer_border->xmax && + viewer_border->ymin < viewer_border->ymax; + border_.viewer_border = viewer_border; + + const RenderData *rd = context_.getRenderData(); + /* Case when cropping to render border happens is handled in + * compositor output and render layer nodes. */ + border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) && + !(rd->mode & R_CROP); + border_.render_border = &rd->border; +} + +bool ExecutionModel::is_breaked() const +{ + const bNodeTree *btree = context_.getbNodeTree(); + return btree->test_break(btree->tbh); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h new file mode 100644 index 00000000000..9e8466b9282 --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.h @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_rect.h" +#include "BLI_vector.hh" + +#include "COM_ExecutionSystem.h" + +#include <functional> + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class NodeOperation; + +/** + * Base class for execution models. Contains shared implementation. + */ +class ExecutionModel { + protected: + /** + * Render and viewer border info. Coordinates are normalized. + */ + struct { + bool use_render_border; + const rctf *render_border; + bool use_viewer_border; + const rctf *viewer_border; + } border_; + + /** + * Context used during execution. + */ + CompositorContext &context_; + + /** + * All operations being executed. + */ + Span<NodeOperation *> operations_; + + public: + ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations); + + virtual ~ExecutionModel() + { + } + + virtual void execute(ExecutionSystem &exec_system) = 0; + + virtual void execute_work(const rcti &UNUSED(work_rect), + std::function<void(const rcti &split_rect)> UNUSED(work_func)) + { + BLI_assert(!"Method not supported by current execution model"); + } + + protected: + bool is_breaked() const; + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index e22dc17837b..a12ec774032 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -21,16 +21,11 @@ #include "BLI_utildefines.h" #include "PIL_time.h" -#include "BKE_node.h" - -#include "BLT_translation.h" - -#include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_ExecutionGroup.h" +#include "COM_FullFrameExecutionModel.h" #include "COM_NodeOperation.h" #include "COM_NodeOperationBuilder.h" -#include "COM_ReadBufferOperation.h" +#include "COM_TiledExecutionModel.h" #include "COM_WorkScheduler.h" #ifdef WITH_CXX_GUARDEDALLOC @@ -73,41 +68,23 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, builder.convertToOperations(this); } - unsigned int resolution[2]; - - rctf *viewer_border = &editingtree->viewer_border; - bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) && - viewer_border->xmin < viewer_border->xmax && - viewer_border->ymin < viewer_border->ymax; - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Determining resolution")); - - for (ExecutionGroup *executionGroup : m_groups) { - resolution[0] = 0; - resolution[1] = 0; - executionGroup->determineResolution(resolution); - - if (rendering) { - /* case when cropping to render border happens is handled in - * compositor output and render layer nodes - */ - if ((rd->mode & R_BORDER) && !(rd->mode & R_CROP)) { - executionGroup->setRenderBorder( - rd->border.xmin, rd->border.xmax, rd->border.ymin, rd->border.ymax); - } - } - - if (use_viewer_border) { - executionGroup->setViewerBorder( - viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); - } + switch (m_context.get_execution_model()) { + case eExecutionModel::Tiled: + execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups); + break; + case eExecutionModel::FullFrame: + execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations); + break; + default: + BLI_assert(!"Non implemented execution model"); + break; } - - // DebugInfo::graphviz(this); } ExecutionSystem::~ExecutionSystem() { + delete execution_model_; + for (NodeOperation *operation : m_operations) { delete operation; } @@ -126,100 +103,16 @@ void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations, m_groups = groups; } -static void update_read_buffer_offset(Vector<NodeOperation *> &operations) -{ - unsigned int order = 0; - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; - readOperation->setOffset(order); - order++; - } - } -} - -static void init_write_operations_for_execution(Vector<NodeOperation *> &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void link_write_buffers(Vector<NodeOperation *> &operations) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation); - readOperation->updateMemoryBuffer(); - } - } -} - -static void init_non_write_operations_for_execution(Vector<NodeOperation *> &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (!operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void init_execution_groups_for_execution(Vector<ExecutionGroup *> &groups, - const int chunk_size) -{ - for (ExecutionGroup *execution_group : groups) { - execution_group->setChunksize(chunk_size); - execution_group->initExecution(); - } -} - void ExecutionSystem::execute() { - const bNodeTree *editingtree = this->m_context.getbNodeTree(); - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); - DebugInfo::execute_started(this); - update_read_buffer_offset(m_operations); - - init_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - link_write_buffers(m_operations); - init_non_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - init_execution_groups_for_execution(m_groups, m_context.getChunksize()); - - WorkScheduler::start(this->m_context); - execute_groups(eCompositorPriority::High); - if (!this->getContext().isFastCalculation()) { - execute_groups(eCompositorPriority::Medium); - execute_groups(eCompositorPriority::Low); - } - WorkScheduler::finish(); - WorkScheduler::stop(); - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); - - for (NodeOperation *operation : m_operations) { - operation->deinitExecution(); - } - - for (ExecutionGroup *execution_group : m_groups) { - execution_group->deinitExecution(); - } + execution_model_->execute(*this); } -void ExecutionSystem::execute_groups(eCompositorPriority priority) +void ExecutionSystem::execute_work(const rcti &work_rect, + std::function<void(const rcti &split_rect)> work_func) { - for (ExecutionGroup *execution_group : m_groups) { - if (execution_group->get_flags().is_output && - execution_group->getRenderPriority() == priority) { - execution_group->execute(this); - } - } + execution_model_->execute_work(work_rect, work_func); } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index e6170c48778..e106209651c 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -25,6 +25,7 @@ class ExecutionGroup; #include "COM_ExecutionGroup.h" #include "COM_Node.h" #include "COM_NodeOperation.h" +#include "COM_SharedOperationBuffers.h" #include "DNA_color_types.h" #include "DNA_node_types.h" @@ -115,13 +116,21 @@ namespace blender::compositor { * \see ExecutionGroup class representing the ExecutionGroup */ +/* Forward declarations. */ +class ExecutionModel; + /** * \brief the ExecutionSystem contains the whole compositor tree. */ class ExecutionSystem { - private: /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers active_buffers_; + + /** * \brief the context used during execution */ CompositorContext m_context; @@ -136,6 +145,11 @@ class ExecutionSystem { */ Vector<ExecutionGroup *> m_groups; + /** + * Active execution model implementation. + */ + ExecutionModel *execution_model_; + private: // methods public: /** @@ -178,9 +192,14 @@ class ExecutionSystem { return this->m_context; } - private: - void execute_groups(eCompositorPriority priority); + SharedOperationBuffers &get_active_buffers() + { + return active_buffers_; + } + + void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func); + private: /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc new file mode 100644 index 00000000000..396aa2fcf6f --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -0,0 +1,327 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_FullFrameExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span<NodeOperation *> operations) + : ExecutionModel(context, operations), + active_buffers_(shared_buffers), + num_operations_finished_(0), + work_mutex_(), + work_finished_cond_() +{ + priorities_.append(eCompositorPriority::High); + if (!context.isFastCalculation()) { + priorities_.append(eCompositorPriority::Medium); + priorities_.append(eCompositorPriority::Low); + } + + BLI_mutex_init(&work_mutex_); + BLI_condition_init(&work_finished_cond_); +} + +FullFrameExecutionModel::~FullFrameExecutionModel() +{ + BLI_condition_end(&work_finished_cond_); + BLI_mutex_end(&work_mutex_); +} + +void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *node_tree = this->context_.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution")); + + DebugInfo::graphviz(&exec_system); + + determine_areas_to_render_and_reads(); + render_operations(exec_system); +} + +void FullFrameExecutionModel::determine_areas_to_render_and_reads() +{ + const bool is_rendering = context_.isRendering(); + const bNodeTree *node_tree = context_.getbNodeTree(); + + rcti area; + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + op->setbNodeTree(node_tree); + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + get_output_render_area(op, area); + determine_areas_to_render(op, area); + determine_reads(op); + } + } + } +} + +void FullFrameExecutionModel::ensure_inputs_rendered(NodeOperation *op, + ExecutionSystem &exec_system) +{ + const int num_inputs = op->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + if (!active_buffers_.is_operation_rendered(input_op)) { + render_operation(input_op, exec_system); + } + } +} + +Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op) +{ + const int num_inputs = op->getNumberOfInputSockets(); + Vector<MemoryBuffer *> inputs_buffers(num_inputs); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op); + } + return inputs_buffers; +} + +MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op) +{ + rcti op_rect; + BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight()); + + const DataType data_type = op->getOutputSocket(0)->getDataType(); + /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way + * to know if an operation is constant has to be implemented yet. */ + const bool is_a_single_elem = op->get_flags().is_set_operation; + return new MemoryBuffer(data_type, op_rect, is_a_single_elem); +} + +void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system) +{ + if (active_buffers_.is_operation_rendered(op)) { + return; + } + + ensure_inputs_rendered(op, exec_system); + Vector<MemoryBuffer *> input_bufs = get_input_buffers(op); + + const bool has_outputs = op->getNumberOfOutputSockets() > 0; + MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; + Span<rcti> areas = active_buffers_.get_areas_to_render(op); + op->render(op_buf, areas, input_bufs, exec_system); + active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf)); + + operation_finished(op); +} + +/** + * Render output operations in order of priority. + */ +void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system) +{ + const bool is_rendering = context_.isRendering(); + + WorkScheduler::start(this->context_); + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + render_operation(op, exec_system); + } + } + } + WorkScheduler::stop(); +} + +/** + * Determines all input operations areas needed to render given operation area. + * \param operation: Renderer operation. + * \param render_area: Area within given operation bounds to render. + */ +void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *operation, + const rcti &render_area) +{ + if (active_buffers_.is_area_registered(operation, render_area)) { + return; + } + + active_buffers_.register_area(operation, render_area); + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + rcti input_op_rect, input_area; + BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight()); + operation->get_area_of_interest(input_op, render_area, input_area); + + /* Ensure area of interest is within operation bounds, cropping areas outside. */ + BLI_rcti_isect(&input_area, &input_op_rect, &input_area); + + determine_areas_to_render(input_op, input_area); + } +} + +/** + * Determines the reads given operation and its inputs will receive (i.e: Number of dependent + * operations each operation has). + */ +void FullFrameExecutionModel::determine_reads(NodeOperation *operation) +{ + if (active_buffers_.has_registered_reads(operation)) { + return; + } + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + determine_reads(input_op); + active_buffers_.register_read(input_op); + } +} + +/** + * Calculates given output operation area to be rendered taking into account viewer and render + * borders. + */ +void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area) +{ + BLI_assert(output_op->isOutputOperation(context_.isRendering())); + + /* By default return operation bounds (no border). */ + const int op_width = output_op->getWidth(); + const int op_height = output_op->getHeight(); + BLI_rcti_init(&r_area, 0, op_width, 0, op_height); + + const bool has_viewer_border = border_.use_viewer_border && + (output_op->get_flags().is_viewer_operation || + output_op->get_flags().is_preview_operation); + const bool has_render_border = border_.use_render_border; + if (has_viewer_border || has_render_border) { + /* Get border with normalized coordinates. */ + const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border; + + /* Return de-normalized border. */ + BLI_rcti_init(&r_area, + norm_border->xmin * op_width, + norm_border->xmax * op_width, + norm_border->ymin * op_height, + norm_border->ymax * op_height); + } +} + +/** + * Multi-threadedly execute given work function passing work_rect splits as argument. + */ +void FullFrameExecutionModel::execute_work(const rcti &work_rect, + std::function<void(const rcti &split_rect)> work_func) +{ + if (is_breaked()) { + return; + } + + /* Split work vertically to maximize continuous memory. */ + const int work_height = BLI_rcti_size_y(&work_rect); + const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height); + const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works; + int remaining_height = work_height - split_height * num_sub_works; + + Vector<WorkPackage> sub_works(num_sub_works); + int sub_work_y = work_rect.ymin; + int num_sub_works_finished = 0; + for (int i = 0; i < num_sub_works; i++) { + int sub_work_height = split_height; + + /* Distribute remaining height between sub-works. */ + if (remaining_height > 0) { + sub_work_height++; + remaining_height--; + } + + WorkPackage &sub_work = sub_works[i]; + sub_work.type = eWorkPackageType::CustomFunction; + sub_work.execute_fn = [=, &work_func, &work_rect]() { + if (is_breaked()) { + return; + } + rcti split_rect; + BLI_rcti_init( + &split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height); + work_func(split_rect); + }; + sub_work.executed_fn = [&]() { + BLI_mutex_lock(&work_mutex_); + num_sub_works_finished++; + if (num_sub_works_finished == num_sub_works) { + BLI_condition_notify_one(&work_finished_cond_); + } + BLI_mutex_unlock(&work_mutex_); + }; + WorkScheduler::schedule(&sub_work); + sub_work_y += sub_work_height; + } + BLI_assert(sub_work_y == work_rect.ymax); + + WorkScheduler::finish(); + + /* Ensure all sub-works finished. + * TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading + * model. Sync code should be removed once it's fixed. */ + BLI_mutex_lock(&work_mutex_); + if (num_sub_works_finished < num_sub_works) { + BLI_condition_wait(&work_finished_cond_, &work_mutex_); + } + BLI_mutex_unlock(&work_mutex_); +} + +void FullFrameExecutionModel::operation_finished(NodeOperation *operation) +{ + /* Report inputs reads so that buffers may be freed/reused. */ + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + active_buffers_.read_finished(operation->get_input_operation(i)); + } + + num_operations_finished_++; + update_progress_bar(); +} + +void FullFrameExecutionModel::update_progress_bar() +{ + const bNodeTree *tree = context_.getbNodeTree(); + if (tree) { + const float progress = num_operations_finished_ / static_cast<float>(operations_.size()); + tree->progress(tree->prh, progress); + + char buf[128]; + BLI_snprintf(buf, + sizeof(buf), + TIP_("Compositing | Operation %i-%li"), + num_operations_finished_ + 1, + operations_.size()); + tree->stats_draw(tree->sdh, buf); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h new file mode 100644 index 00000000000..2c0d5e0460a --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -0,0 +1,89 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +/* Forward declarations. */ +class ExecutionGroup; + +/** + * Fully renders operations in order from inputs to outputs. + */ +class FullFrameExecutionModel : public ExecutionModel { + private: + /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers &active_buffers_; + + /** + * Number of operations finished. + */ + int num_operations_finished_; + + /** + * Order of priorities for output operations execution. + */ + Vector<eCompositorPriority> priorities_; + + ThreadMutex work_mutex_; + ThreadCondition work_finished_cond_; + + public: + FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span<NodeOperation *> operations); + ~FullFrameExecutionModel(); + + void execute(ExecutionSystem &exec_system) override; + + void execute_work(const rcti &work_rect, + std::function<void(const rcti &split_rect)> work_func) override; + + private: + void determine_areas_to_render_and_reads(); + void render_operations(ExecutionSystem &exec_system); + + void ensure_inputs_rendered(NodeOperation *op, ExecutionSystem &exec_system); + Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op); + MemoryBuffer *create_operation_buffer(NodeOperation *op); + void render_operation(NodeOperation *op, ExecutionSystem &exec_system); + + void operation_finished(NodeOperation *operation); + + void get_output_render_area(NodeOperation *output_op, rcti &r_area); + void determine_areas_to_render(NodeOperation *operation, const rcti &render_area); + void determine_reads(NodeOperation *operation); + + void update_progress_bar(); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:FullFrameExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc new file mode 100644 index 00000000000..c54c2edccb0 --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -0,0 +1,26 @@ +#include "COM_MultiThreadedOperation.h" +#include "COM_ExecutionSystem.h" + +namespace blender::compositor { + +MultiThreadedOperation::MultiThreadedOperation() +{ + m_num_passes = 1; + flags.is_fullframe_operation = true; +} + +void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &output_area, + blender::Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system) +{ + for (int current_pass = 0; current_pass < m_num_passes; current_pass++) { + update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass); + exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) { + update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass); + }); + update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h new file mode 100644 index 00000000000..97c5fba4ead --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +class MultiThreadedOperation : public NodeOperation { + protected: + /** + * Number of execution passes. + */ + int m_num_passes; + + protected: + MultiThreadedOperation(); + + /** + * Called before an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + /** + * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls. + */ + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &output_rect, + blender::Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system, + int current_pass) = 0; + + /** + * Called after an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + private: + void update_memory_buffer(MemoryBuffer *output, + const rcti &output_area, + blender::Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index be3ea59efa5..83de8a751c4 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -17,8 +17,10 @@ */ #include <cstdio> +#include <memory> #include <typeinfo> +#include "COM_BufferOperation.h" #include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" #include "COM_defines.h" @@ -175,6 +177,177 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input, return !first; } +/* -------------------------------------------------------------------- */ +/** \name Full Frame Methods + * \{ */ + +/** + * \brief Get input operation area being read by this operation on rendering given output area. + * + * Implementation don't need to ensure r_input_area is within input operation bounds. The + * caller must clamp it. + * TODO: See if it's possible to use parameter overloading (input_id for example). + * + * \param input_op_idx: Input operation index for which we want to calculate the area being read. + * \param output_area: Area being rendered by this operation. + * \param r_input_area: Returned input operation area that needs to be read in order to render + * given output area. + */ +void NodeOperation::get_area_of_interest(const int input_op_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (get_flags().is_fullframe_operation) { + r_input_area = output_area; + } + else { + /* Non full-frame operations never implement this method. To ensure correctness assume + * whole area is used. */ + NodeOperation *input_op = getInputOperation(input_op_idx); + BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight()); + } +} + +void NodeOperation::get_area_of_interest(NodeOperation *input_op, + const rcti &output_area, + rcti &r_input_area) +{ + for (int i = 0; i < getNumberOfInputSockets(); i++) { + if (input_op == getInputOperation(i)) { + get_area_of_interest(i, output_area, r_input_area); + return; + } + } + BLI_assert(!"input_op is not an input operation."); +} + +/** + * Executes operation image manipulation algorithm rendering given areas. + * \param output_buf: Buffer to write result to. + * \param areas: Areas within this operation bounds to render. + * \param inputs_bufs: Inputs operations buffers. + * \param exec_system: Execution system. + */ +void NodeOperation::render(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system) +{ + if (get_flags().is_fullframe_operation) { + render_full_frame(output_buf, areas, inputs_bufs, exec_system); + } + else { + render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system); + } +} + +/** + * Renders given areas using operations full frame implementation. + */ +void NodeOperation::render_full_frame(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system) +{ + initExecution(); + for (const rcti &area : areas) { + update_memory_buffer(output_buf, area, inputs_bufs, exec_system); + } + deinitExecution(); +} + +/** + * Renders given areas using operations tiled implementation. + */ +void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system) +{ + Vector<NodeOperationOutput *> orig_input_links = replace_inputs_with_buffers(inputs_bufs); + + initExecution(); + const bool is_output_operation = getNumberOfOutputSockets() == 0; + if (!is_output_operation && output_buf->is_a_single_elem()) { + float *output_elem = output_buf->get_elem(0, 0); + readSampled(output_elem, 0, 0, PixelSampler::Nearest); + } + else { + for (const rcti &rect : areas) { + exec_system.execute_work(rect, [=](const rcti &split_rect) { + rcti tile_rect = split_rect; + if (is_output_operation) { + executeRegion(&tile_rect, 0); + } + else { + render_tile(output_buf, &tile_rect); + } + }); + } + } + deinitExecution(); + + remove_buffers_and_restore_original_inputs(orig_input_links); +} + +void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) +{ + const bool is_complex = get_flags().complex; + void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr; + const int elem_stride = output_buf->elem_stride; + for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) { + float *output_elem = output_buf->get_elem(tile_rect->xmin, y); + if (is_complex) { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + read(output_elem, x, y, tile_data); + output_elem += elem_stride; + } + } + else { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + readSampled(output_elem, x, y, PixelSampler::Nearest); + output_elem += elem_stride; + } + } + } + if (tile_data) { + deinitializeTileData(tile_rect, tile_data); + } +} + +/** + * \return Replaced inputs links. + */ +Vector<NodeOperationOutput *> NodeOperation::replace_inputs_with_buffers( + Span<MemoryBuffer *> inputs_bufs) +{ + BLI_assert(inputs_bufs.size() == getNumberOfInputSockets()); + Vector<NodeOperationOutput *> orig_links(inputs_bufs.size()); + for (int i = 0; i < inputs_bufs.size(); i++) { + NodeOperationInput *input_socket = getInputSocket(i); + BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType()); + orig_links[i] = input_socket->getLink(); + input_socket->setLink(buffer_op->getOutputSocket()); + } + return orig_links; +} + +void NodeOperation::remove_buffers_and_restore_original_inputs( + Span<NodeOperationOutput *> original_inputs_links) +{ + BLI_assert(original_inputs_links.size() == getNumberOfInputSockets()); + for (int i = 0; i < original_inputs_links.size(); i++) { + NodeOperation *buffer_op = get_input_operation(i); + BLI_assert(buffer_op != nullptr); + BLI_assert(typeid(*buffer_op) == typeid(BufferOperation)); + NodeOperationInput *input_socket = getInputSocket(i); + input_socket->setLink(original_inputs_links[i]); + delete buffer_op; + } +} + +/** \} */ + /***************** **** OpInput **** *****************/ @@ -267,6 +440,9 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat if (!node_operation_flags.use_datatype_conversion) { os << "no_conversion,"; } + if (node_operation_flags.is_fullframe_operation) { + os << "full_frame,"; + } return os; } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index baf3a0878b9..01068c7f812 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -39,6 +39,7 @@ namespace blender::compositor { class OpenCLDevice; class ReadBufferOperation; class WriteBufferOperation; +class ExecutionSystem; class NodeOperation; typedef NodeOperation SocketReader; @@ -190,6 +191,10 @@ struct NodeOperationFlags { */ bool open_cl : 1; + /** + * TODO: Remove this flag and SingleThreadedOperation if tiled implemention is removed. + * Full-frame implemention doesn't need it. + */ bool single_threaded : 1; /** @@ -232,6 +237,11 @@ struct NodeOperationFlags { */ bool use_datatype_conversion : 1; + /** + * Has this operation fullframe implementation. + */ + bool is_fullframe_operation : 1; + NodeOperationFlags() { complex = false; @@ -247,6 +257,7 @@ struct NodeOperationFlags { is_viewer_operation = false; is_preview_operation = false; use_datatype_conversion = true; + is_fullframe_operation = false; } }; @@ -341,6 +352,13 @@ class NodeOperation { NodeOperationOutput *getOutputSocket(unsigned int index = 0); NodeOperationInput *getInputSocket(unsigned int index); + NodeOperation *get_input_operation(int index) + { + /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing + * this method. */ + return getInputOperation(index); + } + /** * \brief determine the resolution of this node * \note this method will not set the resolution, this is the responsibility of the caller @@ -537,6 +555,33 @@ class NodeOperation { return std::unique_ptr<MetaData>(); } + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system); + + /** + * Executes operation updating output memory buffer. Single-threaded calls. + */ + virtual void update_memory_buffer(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_area), + Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system)) + { + } + + /** + * Get input operation area being read by this operation on rendering given output area. + */ + virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area); + void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area); + + /** \} */ + protected: NodeOperation(); @@ -616,6 +661,27 @@ class NodeOperation { { } + private: + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render_full_frame(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system); + + void render_full_frame_fallback(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system); + void render_tile(MemoryBuffer *output_buf, rcti *tile_rect); + Vector<NodeOperationOutput *> replace_inputs_with_buffers(Span<MemoryBuffer *> inputs_bufs); + void remove_buffers_and_restore_original_inputs( + Span<NodeOperationOutput *> original_inputs_links); + + /** \} */ + /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 82eb969b752..c81a5a2bd98 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -99,8 +99,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) determineResolutions(); - /* surround complex ops with read/write buffer */ - add_complex_operation_buffers(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* surround complex ops with read/write buffer */ + add_complex_operation_buffers(); + } /* links not available from here on */ /* XXX make m_links a local variable to avoid confusion! */ @@ -111,8 +113,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) /* ensure topological (link-based) order of nodes */ /*sort_operations();*/ /* not needed yet */ - /* create execution groups */ - group_operations(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* create execution groups */ + group_operations(); + } /* transfer resulting operations to the system */ system->set_operations(m_operations, m_groups); diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc new file mode 100644 index 00000000000..4ce674a1c25 --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -0,0 +1,128 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_SharedOperationBuffers.h" +#include "BLI_rect.h" +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +SharedOperationBuffers::BufferData::BufferData() + : buffer(nullptr), registered_reads(0), received_reads(0) +{ +} + +SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op) +{ + return buffers_.lookup_or_add_cb(op, []() { return BufferData(); }); +} + +/** + * Whether given operation area to render is already registered. + * TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial + * overlapping, etc. Leading to more rendering than necessary. + */ +bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render) +{ + BufferData &buf_data = get_buffer_data(op); + for (rcti ®_rect : buf_data.render_areas) { + if (BLI_rcti_inside_rcti(®_rect, &area_to_render)) { + return true; + } + } + return false; +} + +/** + * Registers an operation area to render. + */ +void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render) +{ + get_buffer_data(op).render_areas.append(area_to_render); +} + +/** + * Whether given operation has any registered reads (other operation registered it depends on given + * operation). + */ +bool SharedOperationBuffers::has_registered_reads(NodeOperation *op) +{ + return get_buffer_data(op).registered_reads > 0; +} + +/** + * Registers an operation read (other operation depends on given operation). + */ +void SharedOperationBuffers::register_read(NodeOperation *read_op) +{ + get_buffer_data(read_op).registered_reads++; +} + +/** + * Get registered areas given operation needs to render. + */ +blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op) +{ + return get_buffer_data(op).render_areas.as_span(); +} + +/** + * Whether this operation buffer has already been rendered. + */ +bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op) +{ + return get_buffer_data(op).buffer != nullptr; +} + +/** + * Stores given operation rendered buffer. + */ +void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op, + std::unique_ptr<MemoryBuffer> buffer) +{ + BufferData &buf_data = get_buffer_data(op); + BLI_assert(buf_data.received_reads == 0); + BLI_assert(buf_data.buffer == nullptr); + buf_data.buffer = std::move(buffer); +} + +/** + * Get given operation rendered buffer. + */ +MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op) +{ + BLI_assert(is_operation_rendered(op)); + return get_buffer_data(op).buffer.get(); +} + +/** + * Reports an operation has finished reading given operation. If all given operation dependencies + * have finished its buffer will be disposed. + */ +void SharedOperationBuffers::read_finished(NodeOperation *read_op) +{ + BufferData &buf_data = get_buffer_data(read_op); + buf_data.received_reads++; + BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads); + if (buf_data.received_reads == buf_data.registered_reads) { + /* Dispose buffer. */ + buf_data.buffer = nullptr; + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h new file mode 100644 index 00000000000..480a799d89f --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_map.hh" +#include "BLI_span.hh" +#include "BLI_vector.hh" +#include "COM_MemoryBuffer.h" +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif +#include <memory> + +namespace blender::compositor { + +/** + * Stores and shares operations rendered buffers including render data. Buffers are + * disposed once all dependent operations have finished reading them. + */ +class SharedOperationBuffers { + private: + typedef struct BufferData { + public: + BufferData(); + std::unique_ptr<MemoryBuffer> buffer; + blender::Vector<rcti> render_areas; + int registered_reads; + int received_reads; + } BufferData; + blender::Map<NodeOperation *, BufferData> buffers_; + + public: + bool is_area_registered(NodeOperation *op, const rcti &area_to_render); + void register_area(NodeOperation *op, const rcti &area_to_render); + + bool has_registered_reads(NodeOperation *op); + void register_read(NodeOperation *read_op); + + blender::Span<rcti> get_areas_to_render(NodeOperation *op); + bool is_operation_rendered(NodeOperation *op); + void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer); + MemoryBuffer *get_rendered_buffer(NodeOperation *op); + + void read_finished(NodeOperation *read_op); + + private: + BufferData &get_buffer_data(NodeOperation *op); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:SharedOperationBuffers") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc new file mode 100644 index 00000000000..d025ce53330 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_TiledExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +TiledExecutionModel::TiledExecutionModel(CompositorContext &context, + Span<NodeOperation *> operations, + Span<ExecutionGroup *> groups) + : ExecutionModel(context, operations), groups_(groups) +{ + const bNodeTree *node_tree = context.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution")); + + unsigned int resolution[2]; + for (ExecutionGroup *group : groups_) { + resolution[0] = 0; + resolution[1] = 0; + group->determineResolution(resolution); + + if (border_.use_render_border) { + const rctf *render_border = border_.viewer_border; + group->setRenderBorder( + render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); + } + + if (border_.use_viewer_border) { + const rctf *viewer_border = border_.viewer_border; + group->setViewerBorder( + viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); + } + } +} + +static void update_read_buffer_offset(Span<NodeOperation *> operations) +{ + unsigned int order = 0; + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; + readOperation->setOffset(order); + order++; + } + } +} + +static void init_write_operations_for_execution(Span<NodeOperation *> operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void link_write_buffers(Span<NodeOperation *> operations) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation); + readOperation->updateMemoryBuffer(); + } + } +} + +static void init_non_write_operations_for_execution(Span<NodeOperation *> operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (!operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void init_execution_groups_for_execution(Span<ExecutionGroup *> groups, + const int chunk_size) +{ + for (ExecutionGroup *execution_group : groups) { + execution_group->setChunksize(chunk_size); + execution_group->initExecution(); + } +} + +void TiledExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *editingtree = this->context_.getbNodeTree(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); + + update_read_buffer_offset(operations_); + + init_write_operations_for_execution(operations_, context_.getbNodeTree()); + link_write_buffers(operations_); + init_non_write_operations_for_execution(operations_, context_.getbNodeTree()); + init_execution_groups_for_execution(groups_, context_.getChunksize()); + + WorkScheduler::start(context_); + execute_groups(eCompositorPriority::High, exec_system); + if (!context_.isFastCalculation()) { + execute_groups(eCompositorPriority::Medium, exec_system); + execute_groups(eCompositorPriority::Low, exec_system); + } + WorkScheduler::finish(); + WorkScheduler::stop(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); + + for (NodeOperation *operation : operations_) { + operation->deinitExecution(); + } + + for (ExecutionGroup *execution_group : groups_) { + execution_group->deinitExecution(); + } +} + +void TiledExecutionModel::execute_groups(eCompositorPriority priority, + ExecutionSystem &exec_system) +{ + for (ExecutionGroup *execution_group : groups_) { + if (execution_group->get_flags().is_output && + execution_group->getRenderPriority() == priority) { + execution_group->execute(&exec_system); + } + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h new file mode 100644 index 00000000000..05a795b9f07 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class ExecutionGroup; + +/** + * Operations are executed from outputs to inputs grouped in execution groups and rendered in + * tiles. + */ +class TiledExecutionModel : public ExecutionModel { + private: + Span<ExecutionGroup *> groups_; + + public: + TiledExecutionModel(CompositorContext &context, + Span<NodeOperation *> operations, + Span<ExecutionGroup *> groups); + + void execute(ExecutionSystem &exec_system) override; + + private: + void execute_groups(eCompositorPriority priority, ExecutionSystem &exec_system); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:TiledExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 28aa746fdc4..4d503022120 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -22,6 +22,7 @@ #include "BLI_rect.h" +#include <functional> #include <ostream> namespace blender::compositor { @@ -33,6 +34,8 @@ class ExecutionGroup; * \see WorkScheduler */ struct WorkPackage { + eWorkPackageType type; + eWorkPackageState state = eWorkPackageState::NotScheduled; /** @@ -50,6 +53,16 @@ struct WorkPackage { */ rcti rect; + /** + * Custom function to execute when work package type is CustomFunction. + */ + std::function<void()> execute_fn; + + /** + * Called when work execution is finished. + */ + std::function<void()> executed_fn; + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkPackage") #endif diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index d578ac24a4a..157ded943d6 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -98,6 +98,8 @@ static struct { bool active = false; bool initialized = false; } opencl; + + int num_cpu_threads; } g_work_scheduler; /* -------------------------------------------------------------------- */ @@ -143,7 +145,8 @@ static void opencl_start(CompositorContext &context) static bool opencl_schedule(WorkPackage *package) { - if (package->execution_group->get_flags().open_cl && g_work_scheduler.opencl.active) { + if (package->type == eWorkPackageType::Tile && package->execution_group->get_flags().open_cl && + g_work_scheduler.opencl.active) { BLI_thread_queue_push(g_work_scheduler.opencl.queue, package); return true; } @@ -532,11 +535,12 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads) opencl_initialize(use_opencl); } + g_work_scheduler.num_cpu_threads = num_cpu_threads; switch (COM_threading_model()) { case ThreadingModel::SingleThreaded: + g_work_scheduler.num_cpu_threads = 1; /* Nothing to do. */ break; - case ThreadingModel::Queue: threading_model_queue_initialize(num_cpu_threads); break; @@ -568,6 +572,11 @@ void WorkScheduler::deinitialize() } } +int WorkScheduler::get_num_cpu_threads() +{ + return g_work_scheduler.num_cpu_threads; +} + int WorkScheduler::current_thread_id() { if (COM_threading_model() == ThreadingModel::SingleThreaded) { diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index 85b1d7e2ebf..be88859be7c 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -87,6 +87,8 @@ struct WorkScheduler { */ static bool has_gpu_devices(); + static int get_num_cpu_threads(); + static int current_thread_id(); #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index d97e4371dc6..3beb3aa2917 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -77,10 +77,10 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, /** \name Cryptomatte V2 * \{ */ -static std::string prefix_from_node(const bNode &node) +static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; - ntreeCompositCryptomatteLayerPrefix(&node, prefix, sizeof(prefix)); + ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix)); return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix))); } @@ -119,7 +119,7 @@ void CryptomatteNode::input_operations_from_render_source( } const short cryptomatte_layer_id = 0; - const std::string prefix = prefix_from_node(node); + const std::string prefix = prefix_from_node(context, node); LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { @@ -177,7 +177,7 @@ void CryptomatteNode::input_operations_from_image_source( } } - const std::string prefix = prefix_from_node(node); + const std::string prefix = prefix_from_node(context, node); int layer_index; LISTBASE_FOREACH_INDEX (RenderLayer *, render_layer, &image->rr->layers, layer_index) { if (!blender::StringRef(prefix).startswith(blender::StringRef( diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 922393f006a..1b2ce341a66 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -56,7 +56,10 @@ void TranslateNode::convertToOperations(NodeConverter &converter, converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); - if (data->wrap_axis) { + /* FullFrame does not support using WriteBufferOperation. + * TODO: Implement TranslateOperation with wrap support in FullFrame. + */ + if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) { WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color); WrapOperation *wrapOperation = new WrapOperation(DataType::Color); wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index e94c457f981..7517ff8a137 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -75,16 +75,13 @@ void TextureBaseOperation::deinitExecution() void TextureBaseOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { - if (preferredResolution[0] == 0 || preferredResolution[1] == 0) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; - resolution[0] = width; - resolution[1] = height; - } - else { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; - } + /* Determine inputs resolutions. */ + unsigned int temp[2]; + NodeOperation::determineResolution(temp, preferredResolution); + + /* We don't use inputs resolutions because they are only used as parameters, not image data. */ + resolution[0] = preferredResolution[0]; + resolution[1] = preferredResolution[1]; } void TextureAlphaOperation::executePixelSampled(float output[4], diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index e1e04611c6c..e5f56673694 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -44,7 +44,7 @@ class TextureBaseOperation : public NodeOperation { protected: /** - * Determine the output resolution. The resolution is retrieved from the Renderer + * Determine the output resolution. */ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 0541aa982f3..d6598bf79b0 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -51,7 +51,9 @@ set(INC set(SRC intern/draw_cache.c - intern/draw_cache_extract_mesh.c + intern/draw_cache_extract_mesh_extractors.c + intern/draw_cache_extract_mesh_render_data.c + intern/draw_cache_extract_mesh.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_displist.c intern/draw_cache_impl_gpencil.c diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index c8eaa06094e..05496ad4ab0 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -164,8 +164,8 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) vec2 N_new; if (valid1 && valid2) { /* If both are possible, do the expensive reflection-based check. */ - vec2 N1 = vec2(sqrt(1.0 - N1_z2), sqrt(N1_z2)); - vec2 N2 = vec2(sqrt(1.0 - N2_z2), sqrt(N2_z2)); + vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2)); + vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2)); float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; @@ -181,7 +181,7 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) } else if (valid1 || valid2) { float Nz2 = valid1 ? N1_z2 : N2_z2; - N_new = vec2(sqrt(1.0 - Nz2), sqrt(Nz2)); + N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2)); } else { return Ng; diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 7412959a30b..ac48b94fea9 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -442,6 +442,10 @@ void stroke_vertex() if (is_dot) { # ifdef GP_MATERIAL_BUFFER_LEN int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; + /* For one point strokes use object aligment. */ + if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { + alignement = GP_STROKE_ALIGNMENT_OBJECT; + } # endif vec2 x_axis; diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a3e43ff2ec5..810deaec349 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "GPU_batch.h" +#include "GPU_index_buffer.h" +#include "GPU_vertex_buffer.h" + /* Vertex Group Selection and display options */ typedef struct DRW_MeshWeightState { int defgroup_active; @@ -64,13 +68,13 @@ typedef struct DRW_MeshCDMask { * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") - typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, MR_ITER_LEDGE = 1 << 2, MR_ITER_LVERT = 1 << 3, } eMRIterType; +ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) typedef enum eMRDataType { MR_DATA_POLY_NOR = 1 << 1, @@ -79,12 +83,11 @@ typedef enum eMRDataType { /** Force loop normals calculation. */ MR_DATA_TAN_LOOP_NOR = 1 << 4, } eMRDataType; +ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR) -typedef enum eMRExtractType { - MR_EXTRACT_BMESH, - MR_EXTRACT_MAPPED, - MR_EXTRACT_MESH, -} eMRExtractType; +#ifdef __cplusplus +extern "C" { +#endif BLI_INLINE int mesh_render_mat_len_get(Mesh *me) { @@ -150,6 +153,18 @@ typedef struct MeshBufferCache { GPUIndexBuf **tris_per_mat; } MeshBufferCache; +/** + * Data that are kept around between extractions to reduce rebuilding time. + * + * - Loose geometry. + */ +typedef struct MeshBufferExtractionCache { + int edge_loose_len; + int vert_loose_len; + int *lverts; + int *ledges; +} MeshBufferExtractionCache; + typedef enum DRWBatchFlag { MBC_SURFACE = (1 << 0), MBC_SURFACE_WEIGHTS = (1 << 1), @@ -195,6 +210,10 @@ typedef enum DRWBatchFlag { typedef struct MeshBatchCache { MeshBufferCache final, cage, uv_cage; + MeshBufferExtractionCache final_extraction_cache; + MeshBufferExtractionCache cage_extraction_cache; + MeshBufferExtractionCache uv_cage_extraction_cache; + struct { /* Surfaces / Render */ GPUBatch *surface; @@ -270,7 +289,8 @@ typedef struct MeshBatchCache { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache mbc, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, const bool is_paint_mode, @@ -279,7 +299,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc new file mode 100644 index 00000000000..622ea191a0a --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -0,0 +1,996 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ +#include "MEM_guardedalloc.h" + +#include "atomic_ops.h" + +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" + +#include "BLI_task.h" +#include "BLI_vector.hh" + +#include "BKE_editmesh.h" + +#include "GPU_capabilities.h" + +#include "draw_cache_extract.h" +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_inline.h" + +// #define DEBUG_TIME + +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif + +#define CHUNK_SIZE 8192 + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +struct ExtractorRunData { + /* Extractor where this run data belongs to. */ + const MeshExtract *extractor; + /* During iteration the VBO/IBO that is being build. */ + void *buffer = nullptr; + /* User data during iteration. Created in MeshExtract.init and passed along to other MeshExtract + * functions. */ + void *user_data = nullptr; + + ExtractorRunData(const MeshExtract *extractor) : extractor(extractor) + { + } +}; + +class ExtractorRunDatas : public Vector<ExtractorRunData> { + public: + void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + result.append(data); + continue; + } + } + } + + void filter_threaded_extractors_into(ExtractorRunDatas &result) + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if (extractor->use_threading) { + result.append(extractor); + } + } + } + + eMRIterType iter_types() + { + eMRIterType iter_type = static_cast<eMRIterType>(0); + + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + iter_type |= mesh_extract_iter_type(extractor); + } + return iter_type; + } + + eMRDataType data_types() + { + eMRDataType data_type = static_cast<eMRDataType>(0); + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + data_type |= extractor->data_type; + } + return data_type; + } +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract + * \{ */ + +BLI_INLINE void extract_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + ExtractorRunDatas &extractors, + MeshBufferCache *mbc) +{ + /* Multi thread. */ + for (ExtractorRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + run_data.buffer = mesh_extract_buffer_get(extractor, mbc); + run_data.user_data = extractor->init(mr, cache, run_data.buffer); + } +} + +BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, + const ExtractTriBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, + const ExtractTriMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, + const ExtractPolyBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, + const ExtractPolyMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, + const ExtractLEdgeBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, + const ExtractLEdgeMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, + const ExtractLVertBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, + const ExtractLVertMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_MESH_END; +} + +BLI_INLINE void extract_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + const ExtractorRunDatas &extractors) +{ + for (const ExtractorRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (extractor->finish) { + extractor->finish(mr, cache, run_data.buffer, run_data.user_data); + } + } +} + +/* Single Thread. */ +BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + ExtractorRunDatas &extractors, + eMRIterType iter_type, + MeshBufferCache *mbc) +{ + extract_init(mr, cache, extractors, mbc); + + bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; + if (iter_type & MR_ITER_LOOPTRI) { + if (is_mesh) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + else { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_POLY) { + if (is_mesh) { + ExtractPolyMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + else { + ExtractPolyBMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LEDGE) { + if (is_mesh) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + else { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LVERT) { + if (is_mesh) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + else { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + } + extract_finish(mr, cache, extractors); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name ExtractTaskData + * \{ */ +struct ExtractTaskData { + void *next = nullptr; + void *prev = nullptr; + + const MeshRenderData *mr = nullptr; + MeshBatchCache *cache = nullptr; + /* #UserData is shared between the iterations as it holds counters to detect if the + * extraction is finished. To make sure the duplication of the user_data does not create a new + * instance of the counters we allocate the user_data in its own container. + * + * This structure makes sure that when extract_init is called, that the user data of all + * iterations are updated. */ + + ExtractorRunDatas *extractors = nullptr; + MeshBufferCache *mbc = nullptr; + int32_t *task_counter = nullptr; + + eMRIterType iter_type; + int start = 0; + int end = INT_MAX; + /** Decremented each time a task is finished. */ + + ExtractTaskData(const MeshRenderData *mr, + struct MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + : mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter) + { + iter_type = extractors->iter_types(); + }; + + ExtractTaskData(const ExtractTaskData &src) = default; + + ~ExtractTaskData() + { + delete extractors; + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData") +#endif +}; + +static ExtractTaskData *extract_extract_iter_task_data_create_mesh(const MeshRenderData *mr, + MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + +{ + ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter); + return taskdata; +} + +static void extract_task_data_free(void *data) +{ + ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data); + delete task_data; +} + +static void extract_task_data_free_ex(void *data) +{ + ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data); + task_data->extractors = nullptr; + delete task_data; +} + +BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, + const eMRIterType iter_type, + int start, + int end, + ExtractorRunDatas &extractors) +{ + switch (mr->extract_type) { + case MR_EXTRACT_BMESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyBMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + break; + case MR_EXTRACT_MAPPED: + case MR_EXTRACT_MESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + break; + } +} + +static void extract_task_init(ExtractTaskData *data) +{ + extract_init(data->mr, data->cache, *data->extractors, data->mbc); +} + +static void extract_task_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, *data->extractors); + + /* If this is the last task, we do the finish function. */ + int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); + if (remainin_tasks == 0) { + extract_finish(data->mr, data->cache, *data->extractors); + } +} + +static void extract_task_init_and_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + extract_run_and_finish_init( + data->mr, data->cache, *data->extractors, data->iter_type, data->mbc); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Update Mesh Render Data + * \{ */ +struct MeshRenderDataUpdateTaskData { + MeshRenderData *mr = nullptr; + eMRIterType iter_type; + eMRDataType data_flag; + + ~MeshRenderDataUpdateTaskData() + { + mesh_render_data_free(mr); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData") +#endif +}; + +static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) +{ + BLI_assert(taskdata); + delete taskdata; +} + +static void mesh_extract_render_data_node_exec(void *__restrict task_data) +{ + MeshRenderDataUpdateTaskData *update_task_data = static_cast<MeshRenderDataUpdateTaskData *>( + task_data); + MeshRenderData *mr = update_task_data->mr; + const eMRIterType iter_type = update_task_data->iter_type; + const eMRDataType data_flag = update_task_data->data_flag; + + mesh_render_data_update_normals(mr, data_flag); + mesh_render_data_update_looptris(mr, iter_type, data_flag); +} + +static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, + MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + MeshRenderDataUpdateTaskData *task_data = new (MeshRenderDataUpdateTaskData); + task_data->mr = mr; + task_data->iter_type = iter_type; + task_data->data_flag = data_flag; + + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + mesh_extract_render_data_node_exec, + task_data, + (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Extract Single Threaded + * \{ */ + +static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, + ExtractTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + extract_task_init_and_run, + task_data, + (TaskGraphNodeFreeFunction)extract_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - UserData Initializer + * \{ */ +struct UserDataInitTaskData { + ExtractTaskData *td; + int32_t task_counter = 0; + + ~UserDataInitTaskData() + { + extract_task_data_free(td); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:UserDataInitTaskData") +#endif +}; + +static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) +{ + delete taskdata; +} + +static void user_data_init_task_data_exec(void *__restrict task_data) +{ + UserDataInitTaskData *extract_task_data = static_cast<UserDataInitTaskData *>(task_data); + ExtractTaskData *taskdata_base = extract_task_data->td; + extract_task_init(taskdata_base); +} + +static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, + UserDataInitTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + user_data_init_task_data_exec, + task_data, + (TaskGraphNodeFreeFunction)user_data_init_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop + * \{ */ + +static void extract_range_task_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata, + const eMRIterType type, + int start, + int length) +{ + taskdata = new ExtractTaskData(*taskdata); + atomic_add_and_fetch_int32(taskdata->task_counter, 1); + taskdata->iter_type = type; + taskdata->start = start; + taskdata->end = start + length; + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, extract_task_run, taskdata, extract_task_data_free_ex); + BLI_task_graph_edge_create(task_node_user_data_init, task_node); +} + +static int extract_range_task_num_elements_get(const MeshRenderData *mr, + const eMRIterType iter_type) +{ + /* Divide task into sensible chunks. */ + int iter_len = 0; + if (iter_type & MR_ITER_LOOPTRI) { + iter_len += mr->tri_len; + } + if (iter_type & MR_ITER_POLY) { + iter_len += mr->poly_len; + } + if (iter_type & MR_ITER_LEDGE) { + iter_len += mr->edge_loose_len; + } + if (iter_type & MR_ITER_LVERT) { + iter_len += mr->vert_loose_len; + } + return iter_len; +} + +static int extract_range_task_chunk_size_get(const MeshRenderData *mr, + const eMRIterType iter_type, + const int num_threads) +{ + /* Divide task into sensible chunks. */ + const int num_elements = extract_range_task_num_elements_get(mr, iter_type); + int range_len = (num_elements + num_threads) / num_threads; + CLAMP_MIN(range_len, CHUNK_SIZE); + return range_len; +} + +static void extract_task_in_ranges_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata_base, + const int num_threads) +{ + const MeshRenderData *mr = taskdata_base->mr; + const int range_len = extract_range_task_chunk_size_get( + mr, taskdata_base->iter_type, num_threads); + + if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { + for (int i = 0; i < mr->tri_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_POLY) { + for (int i = 0; i < mr->poly_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LEDGE) { + for (int i = 0; i < mr->edge_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LVERT) { + for (int i = 0; i < mr->vert_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); + } + } +} + +static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. + * This sub-graph starts with an extract_render_data_node. This fills/converts the required + * data from Mesh. + * + * Small extractions and extractions that can't be multi-threaded are grouped in a single + * `extract_single_threaded_task_node`. + * + * Other extractions will create a node for each loop exceeding 8192 items. these nodes are + * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the + * user_data needed for the extraction based on the data extracted from the mesh. + * counters are used to check if the finalize of a task has to be called. + * + * Mesh extraction sub graph + * + * +----------------------+ + * +-----> | extract_task1_loop_1 | + * | +----------------------+ + * +------------------+ +----------------------+ +----------------------+ + * | mesh_render_data | --> | | --> | extract_task1_loop_2 | + * +------------------+ | | +----------------------+ + * | | | +----------------------+ + * | | user_data_init | --> | extract_task2_loop_1 | + * v | | +----------------------+ + * +------------------+ | | +----------------------+ + * | single_threaded | | | --> | extract_task2_loop_2 | + * +------------------+ +----------------------+ +----------------------+ + * | +----------------------+ + * +-----> | extract_task2_loop_3 | + * +----------------------+ + */ + const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != nullptr; + const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || + GPU_use_hq_normals_workaround(); + + /* Create an array containing all the extractors that needs to be executed. */ + ExtractorRunDatas extractors; + +#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ + do { \ + if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ + const MeshExtract *extractor = mesh_extract_override_get( \ + &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ + extractors.append(extractor); \ + } \ + } while (0) + + EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); + EXTRACT_ADD_REQUESTED(VBO, vbo, uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, tan); + EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); + EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, orco); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); + EXTRACT_ADD_REQUESTED(VBO, vbo, weights); + EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); + EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); + + EXTRACT_ADD_REQUESTED(IBO, ibo, tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, points); + EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); + +#undef EXTRACT_ADD_REQUESTED + + if (extractors.is_empty()) { + return; + } + +#ifdef DEBUG_TIME + double rdata_start = PIL_check_seconds_timer(); +#endif + + eMRIterType iter_type = extractors.iter_types(); + eMRDataType data_flag = extractors.data_types(); + + MeshRenderData *mr = mesh_render_data_create(me, + extraction_cache, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + ts, + iter_type); + mr->use_hide = use_hide; + mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_final_mesh = do_final; + +#ifdef DEBUG_TIME + double rdata_end = PIL_check_seconds_timer(); +#endif + + struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( + task_graph, mr, iter_type, data_flag); + + /* Simple heuristic. */ + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; + + if (use_thread) { + uint threads_to_use = 0; + + /* First run the requested extractors that do not support asynchronous ranges. */ + for (const ExtractorRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (!extractor->use_threading) { + ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); + single_threaded_extractors->append(extractor); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, single_threaded_extractors, mbc, nullptr); + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, + taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + threads_to_use++; + } + + /* Distribute the remaining extractors into ranges per core. */ + ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas(); + extractors.filter_threaded_extractors_into(*multi_threaded_extractors); + if (!multi_threaded_extractors->is_empty()) { + /* + * Determine the number of thread to use for multithreading. + * Thread can be used for single threaded tasks. These typically take longer to execute so + * fill the rest of the threads for range operations. + */ + int num_threads = BLI_task_scheduler_num_threads(); + if (threads_to_use < num_threads) { + num_threads -= threads_to_use; + } + + UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData(); + struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( + task_graph, user_data_init_task_data); + + user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( + mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); + + extract_task_in_ranges_create( + task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); + + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); + } + else { + /* No tasks created freeing extractors list. */ + delete multi_threaded_extractors; + } + } + else { + /* Run all requests on the same thread. */ + ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, extractors_copy, mbc, nullptr); + + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + + /* Trigger the sub-graph for this mesh. */ + BLI_task_graph_node_push_work(task_node_mesh_render_data); + +#ifdef DEBUG_TIME + BLI_task_graph_work_and_wait(task_graph); + double end = PIL_check_seconds_timer(); + + static double avg = 0; + static double avg_fps = 0; + static double avg_rdata = 0; + static double end_prev = 0; + + if (end_prev == 0) { + end_prev = end; + } + + avg = avg * 0.95 + (end - rdata_end) * 0.05; + avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; + avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; + + printf( + "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); + + end_prev = end; +#endif +} + +} // namespace blender::draw + +extern "C" { +void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + blender::draw::mesh_buffer_cache_create_requested(task_graph, + cache, + mbc, + extraction_cache, + me, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + use_subsurf_fdots, + scene, + ts, + use_hide); +} + +} // extern "C" + +/** \} */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index f167ea3d540..324ebd2ec38 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -13,7 +13,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * The Original Code is Copyright (C) 2017 by Blender Foundation. + * The Original Code is Copyright (C) 2021 by Blender Foundation. * All rights reserved. */ @@ -25,773 +25,94 @@ #include "MEM_guardedalloc.h" -#include "BLI_alloca.h" -#include "BLI_bitmap.h" -#include "BLI_buffer.h" +#include "atomic_ops.h" + +#include "DNA_object_types.h" + #include "BLI_edgehash.h" #include "BLI_jitter_2d.h" -#include "BLI_math_bits.h" -#include "BLI_math_vector.h" +#include "BLI_kdopbvh.h" #include "BLI_string.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "BKE_bvhutils.h" -#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" #include "BKE_editmesh_cache.h" #include "BKE_editmesh_tangent.h" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_mesh_tangent.h" -#include "BKE_modifier.h" -#include "BKE_object_deform.h" #include "BKE_paint.h" -#include "atomic_ops.h" - -#include "bmesh.h" +#include "ED_uvedit.h" -#include "GPU_batch.h" #include "GPU_capabilities.h" -#include "DRW_render.h" - -#include "ED_mesh.h" -#include "ED_uvedit.h" - +#include "draw_cache_extract_mesh_private.h" #include "draw_cache_impl.h" -#include "draw_cache_inline.h" - -#include "draw_cache_extract.h" - -// #define DEBUG_TIME -#ifdef DEBUG_TIME -# include "PIL_time_utildefines.h" -#endif - -/* ---------------------------------------------------------------------- */ -/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). - * \{ */ - -typedef struct MeshRenderData { - eMRExtractType extract_type; - - int poly_len, edge_len, vert_len, loop_len; - int edge_loose_len; - int vert_loose_len; - int loop_loose_len; - int tri_len; - int mat_len; - - bool use_hide; - bool use_subsurf_fdots; - bool use_final_mesh; - - /** Use for #MeshStatVis calculation which use world-space coords. */ - float obmat[4][4]; - - const ToolSettings *toolsettings; - /** Edit Mesh */ - BMEditMesh *edit_bmesh; - BMesh *bm; - EditMeshData *edit_data; - - /* For deformed edit-mesh data. */ - /* Use for #ME_WRAPPER_TYPE_BMESH. */ - const float (*bm_vert_coords)[3]; - const float (*bm_vert_normals)[3]; - const float (*bm_poly_normals)[3]; - const float (*bm_poly_centers)[3]; - - int *v_origindex, *e_origindex, *p_origindex; - int crease_ofs; - int bweight_ofs; - int freestyle_edge_ofs; - int freestyle_face_ofs; - /** Mesh */ - Mesh *me; - const MVert *mvert; - const MEdge *medge; - const MLoop *mloop; - const MPoly *mpoly; - BMVert *eve_act; - BMEdge *eed_act; - BMFace *efa_act; - BMFace *efa_act_uv; - /* Data created on-demand (usually not for #BMesh based data). */ - MLoopTri *mlooptri; - float (*loop_normals)[3]; - float (*poly_normals)[3]; - int *lverts, *ledges; -} MeshRenderData; - -static void mesh_render_data_update_loose_geom(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType UNUSED(data_flag)) -{ - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - - BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - mr->ledges[mr->edge_loose_len++] = med_index; - } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); - } - - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - for (int v = 0; v < mr->vert_len; v++) { - if (!BLI_BITMAP_TEST(lvert_map, v)) { - mr->lverts[mr->vert_loose_len++] = v; - } - } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } - - MEM_freeN(lvert_map); - - mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); - } - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - int elem_id; - BMIter iter; - BMVert *eve; - BMEdge *ede; - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { - mr->lverts[mr->vert_loose_len++] = elem_id; - } - } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } - - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__); - BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { - mr->ledges[mr->edge_loose_len++] = elem_id; - } - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); - } - - mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; - } - } -} - -/** - * Part of the creation of the #MeshRenderData that happens in a thread. - */ -static void mesh_render_data_update_looptris(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); - } - } - else { - /* #BMesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - /* Edit mode ensures this is valid, no need to calculate. */ - BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); - } - } -} - -static void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType UNUSED(iter_type), - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; - const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { - mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); - BKE_mesh_calc_normals_poly((MVert *)mr->mvert, - NULL, - mr->vert_len, - mr->mloop, - mr->mpoly, - mr->loop_len, - mr->poly_len, - mr->poly_normals, - true); - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); - BKE_mesh_normals_loop_split(mr->me->mvert, - mr->vert_len, - mr->me->medge, - mr->edge_len, - mr->me->mloop, - mr->loop_normals, - mr->loop_len, - mr->me->mpoly, - mr->poly_normals, - mr->poly_len, - is_auto_smooth, - split_angle, - NULL, - clnors, - NULL); - } - } - else { - /* #BMesh */ - if (data_flag & MR_DATA_POLY_NOR) { - /* Use #BMFace.no instead. */ - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - - const float(*vert_coords)[3] = NULL; - const float(*vert_normals)[3] = NULL; - const float(*poly_normals)[3] = NULL; - - if (mr->edit_data && mr->edit_data->vertexCos) { - vert_coords = mr->bm_vert_coords; - vert_normals = mr->bm_vert_normals; - poly_normals = mr->bm_poly_normals; - } - - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); - BM_loops_calc_normal_vcos(mr->bm, - vert_coords, - vert_normals, - poly_normals, - is_auto_smooth, - split_angle, - mr->loop_normals, - NULL, - NULL, - clnors_offset, - false); - } - } -} - -/** - * \param is_mode_active: When true, use the modifiers from the edit-data, - * otherwise don't use modifiers as they are not from this object. - */ -static MeshRenderData *mesh_render_data_create(Mesh *me, - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const DRW_MeshCDMask *UNUSED(cd_used), - const ToolSettings *ts, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); - mr->toolsettings = ts; - mr->mat_len = mesh_render_mat_len_get(me); - - copy_m4_m4(mr->obmat, obmat); - - if (is_editmode) { - BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); - mr->bm = me->edit_mesh->bm; - mr->edit_bmesh = me->edit_mesh; - mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; - mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; - - if (mr->edit_data) { - EditMeshData *emd = mr->edit_data; - if (emd->vertexCos) { - BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); - BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); - } - - mr->bm_vert_coords = mr->edit_data->vertexCos; - mr->bm_vert_normals = mr->edit_data->vertexNos; - mr->bm_poly_normals = mr->edit_data->polyNos; - mr->bm_poly_centers = mr->edit_data->polyCos; - } - - bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); - bool use_mapped = is_mode_active && - (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); - - int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; - - BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); - BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); - - mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); - mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); - mr->eed_act = BM_mesh_active_edge_get(mr->bm); - mr->eve_act = BM_mesh_active_vert_get(mr->bm); - - mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); - mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); -#ifdef WITH_FREESTYLE - mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); - mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); -#endif - - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; - - /* Seems like the mesh_eval_final do not have the right origin indices. - * Force not mapped in this case. */ - if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { - // mr->edit_bmesh = NULL; - mr->extract_type = MR_EXTRACT_MESH; - } - } - else { - mr->me = me; - mr->edit_bmesh = NULL; - - bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; - } - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - mr->vert_len = mr->me->totvert; - mr->edge_len = mr->me->totedge; - mr->loop_len = mr->me->totloop; - mr->poly_len = mr->me->totpoly; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - - mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); - mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); - mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); - mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); - - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - - mr->vert_len = bm->totvert; - mr->edge_len = bm->totedge; - mr->loop_len = bm->totloop; - mr->poly_len = bm->totface; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - } - mesh_render_data_update_loose_geom(mr, iter_type, data_flag); - - return mr; -} - -static void mesh_render_data_free(MeshRenderData *mr) -{ - MEM_SAFE_FREE(mr->mlooptri); - MEM_SAFE_FREE(mr->poly_normals); - MEM_SAFE_FREE(mr->loop_normals); - - MEM_SAFE_FREE(mr->lverts); - MEM_SAFE_FREE(mr->ledges); - - MEM_freeN(mr); -} - -BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) { - return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_face_at_index(mr->bm, mr->p_origindex[idx]) : - NULL; + /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to + * `MeshBufferCache *`. What shows a different usage versus intent. */ + void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); + void *buffer = *buffer_ptr; + BLI_assert(buffer); + return buffer; } -BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) +eMRIterType mesh_extract_iter_type(const MeshExtract *ext) { - return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : - NULL; + eMRIterType type = 0; + SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); + SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); + SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); + SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); + return type; } -BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) -{ - return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : - NULL; -} +/* ---------------------------------------------------------------------- */ +/** \name Override extractors + * Extractors can be overridden. When overridden a specialized version is used. The next functions + * would check for any needed overrides and usage of the specialized version. + * \{ */ -BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) +static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) { - const float(*vert_coords)[3] = mr->bm_vert_coords; - if (vert_coords != NULL) { - return vert_coords[BM_elem_index_get(eve)]; + if (extractor == &extract_pos_nor) { + return &extract_pos_nor_hq; } - - UNUSED_VARS(mr); - return eve->co; -} - -BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) -{ - const float(*vert_normals)[3] = mr->bm_vert_normals; - if (vert_normals != NULL) { - return vert_normals[BM_elem_index_get(eve)]; + if (extractor == &extract_lnor) { + return &extract_lnor_hq; } - - UNUSED_VARS(mr); - return eve->no; + if (extractor == &extract_tan) { + return &extract_tan_hq; + } + if (extractor == &extract_fdots_nor) { + return &extract_fdots_nor_hq; + } + return extractor; } -BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) +static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) { - const float(*poly_normals)[3] = mr->bm_poly_normals; - if (poly_normals != NULL) { - return poly_normals[BM_elem_index_get(efa)]; + if (extractor == &extract_lines) { + return &extract_lines_with_lines_loose; } - - UNUSED_VARS(mr); - return efa->no; + return extractor; } -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loop Triangles - * \{ */ - -typedef struct ExtractTriBMesh_Params { - BMLoop *(*looptris)[3]; - int tri_range[2]; -} ExtractTriBMesh_Params; -typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, - const ExtractTriBMesh_Params *params, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 3) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } - -typedef struct ExtractTriMesh_Params { - const MLoopTri *mlooptri; - int tri_range[2]; -} ExtractTriMesh_Params; -typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, - const ExtractTriMesh_Params *params, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 1) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Polygons, Loops - * \{ */ - -typedef struct ExtractPolyBMesh_Params { - BMLoop *(*looptris)[3]; - int poly_range[2]; -} ExtractPolyBMesh_Params; -typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, - void *data); - -#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_poly = _ftable[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_BM_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_face = _ftable[index_poly]; \ - BMLoop *elem_loop, *l_first; \ - elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ - do { \ - const int index_loop = BM_elem_index_get(elem_loop); \ - (void)index_loop; /* Quiet warning when unused. */ - -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ - } \ - while ((elem_loop = elem_loop->next) != l_first) \ - ; \ - } \ - } - -typedef struct ExtractPolyMesh_Params { - int poly_range[2]; -} ExtractPolyMesh_Params; -typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, - void *data); - -#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_MESH_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ - elem_poly, index_poly, elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const MLoop *_mloop = mr->mloop; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ - for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ - const MLoop *elem_loop = &_mloop[index_loop]; \ - (void)elem_loop; - -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Edges - * \{ */ - -typedef struct ExtractLEdgeBMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeBMesh_Params; -typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, - void *data); - -#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ - CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ - BMEdge **_etable = mr->bm->etable; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLEdgeMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeMesh_Params; -typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, - void *data); - -#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ - CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ - { \ - const MEdge *_medge = mr->medge; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_MESH_END \ - } \ - } \ +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer) +{ + if (do_hq_normals) { + extractor = mesh_extract_override_hq_normals(extractor); } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Vertices - * \{ */ - -typedef struct ExtractLVertBMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertBMesh_Params; -typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, - void *data); - -#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ - CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMVert **vtable = mr->bm->vtable; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - BMVert *elem_vert = vtable[lverts[index_lvert]]; \ - (void)elem_vert; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLVertMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertMesh_Params; -typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, - void *data); - -#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ - CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ - { \ - const MVert *mvert = mr->mvert; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - const MVert *elem = &mvert[lverts[index_lvert]]; \ - (void)elem; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_MESH_END \ - } \ - } \ + if (do_lines_loose_subbuffer) { + extractor = mesh_extract_override_loose_lines(extractor); } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract Struct - * \{ */ - -typedef void *(ExtractInitFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer); -typedef void(ExtractFinishFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer, - void *data); - -typedef struct MeshExtract { - /** Executed on main thread and return user data for iteration functions. */ - ExtractInitFn *init; - /** Executed on one (or more if use_threading) worker thread(s). */ - ExtractTriBMeshFn *iter_looptri_bm; - ExtractTriMeshFn *iter_looptri_mesh; - ExtractPolyBMeshFn *iter_poly_bm; - ExtractPolyMeshFn *iter_poly_mesh; - ExtractLEdgeBMeshFn *iter_ledge_bm; - ExtractLEdgeMeshFn *iter_ledge_mesh; - ExtractLVertBMeshFn *iter_lvert_bm; - ExtractLVertMeshFn *iter_lvert_mesh; - /** Executed on one worker thread after all elements iterations. */ - ExtractFinishFn *finish; - /** Used to request common data. */ - const eMRDataType data_flag; - /** Used to know if the element callbacks are thread-safe and can be parallelized. */ - const bool use_threading; -} MeshExtract; - -BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext) -{ - eMRIterType type = 0; - SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); - SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); - SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); - SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); - return type; + return extractor; } /** \} */ @@ -855,69 +176,65 @@ static void *extract_tris_init(const MeshRenderData *mr, } static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(elt[0]->f->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts(&data->elb, - mat_tri_ofs[mat]++, - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } + + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(elt[0]->f->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts(&data->elb, + mat_tri_ofs[mat]++, + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(mp->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts( - &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(mp->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts( + &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_tris_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_Tri_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch * is created before the surfaces-per-material. */ if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc = &cache->final; + MeshBufferCache *mbc_final = &cache->final; for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc->tris_per_mat[i] == NULL) { - mbc->tris_per_mat[i] = GPU_indexbuf_calloc(); + if (mbc_final->tris_per_mat[i] == NULL) { + mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); } /* Multiply by 3 because these are triangle indices. */ const int mat_start = data->tri_mat_start[i]; const int mat_end = data->tri_mat_end[i]; const int start = mat_start * 3; const int len = (mat_end - mat_start) * 3; - GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, start, len); + GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); } } MEM_freeN(data->tri_mat_start); @@ -925,14 +242,13 @@ static void extract_tris_finish(const MeshRenderData *mr, MEM_freeN(data); } -static const MeshExtract extract_tris = { - .init = extract_tris_init, - .iter_looptri_bm = extract_tris_iter_looptri_bm, - .iter_looptri_mesh = extract_tris_iter_looptri_mesh, - .finish = extract_tris_finish, - .data_flag = 0, - .use_threading = false, -}; +const MeshExtract extract_tris = {.init = extract_tris_init, + .iter_looptri_bm = extract_tris_iter_looptri_bm, + .iter_looptri_mesh = extract_tris_iter_looptri_mesh, + .finish = extract_tris_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; /** \} */ @@ -951,134 +267,118 @@ static void *extract_lines_init(const MeshRenderData *mr, return elb; } -static void extract_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - /* Using poly & loop iterator would complicate accessing the adjacent loop. */ - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - BMLoop *l_iter, *l_first; - /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_line_verts(elb, - BM_elem_index_get(l_iter->e), - BM_elem_index_get(l_iter), - BM_elem_index_get(l_iter->next)); - } - else { - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); - } - } while ((l_iter = l_iter->next) != l_first); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; + do { + if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_line_verts(elb, + BM_elem_index_get(l_iter->e), + BM_elem_index_get(l_iter), + BM_elem_index_get(l_iter->next)); + } + else { + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); + } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { /* Using poly & loop iterator would complicate accessing the adjacent loop. */ const MLoop *mloop = mr->mloop; const MEdge *medge = mr->medge; if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } - else { - GPU_indexbuf_set_line_restart(elb, ml->e); - } - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + const MEdge *med = &medge[ml->e]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } + else { + GPU_indexbuf_set_line_restart(elb, ml->e); + } + } while ((ml_index = ml_index_next++) != ml_index_last); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } while ((ml_index = ml_index_next++) != ml_index_last); } } static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - const int l_index_offset = mr->edge_len + ledge_index; - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); + const int l_index_offset = mr->edge_len + ledge_index; + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); } - EXTRACT_LEDGE_FOREACH_BM_END; + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); } static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int l_index_offset = mr->edge_len + ledge_index; - const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, e_index); + const int l_index_offset = mr->edge_len + ledge_index; + const int e_index = mr->ledges[ledge_index]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); } - EXTRACT_LEDGE_FOREACH_MESH_END; + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, e_index); } static void extract_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } -static const MeshExtract extract_lines = { - .init = extract_lines_init, - .iter_poly_bm = extract_lines_iter_poly_bm, - .iter_poly_mesh = extract_lines_iter_poly_mesh, - .iter_ledge_bm = extract_lines_iter_ledge_bm, - .iter_ledge_mesh = extract_lines_iter_ledge_mesh, - .finish = extract_lines_finish, - .data_flag = 0, - .use_threading = false, -}; +const MeshExtract extract_lines = {.init = extract_lines_init, + .iter_poly_bm = extract_lines_iter_poly_bm, + .iter_poly_mesh = extract_lines_iter_poly_mesh, + .iter_ledge_bm = extract_lines_iter_ledge_bm, + .iter_ledge_mesh = extract_lines_iter_ledge_mesh, + .finish = extract_lines_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -1098,24 +398,25 @@ static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshB static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); extract_lines_loose_subbuffer(mr, cache); MEM_freeN(elb); } -static const MeshExtract extract_lines_with_lines_loose = { +const MeshExtract extract_lines_with_lines_loose = { .init = extract_lines_init, .iter_poly_bm = extract_lines_iter_poly_bm, .iter_poly_mesh = extract_lines_iter_poly_mesh, .iter_ledge_bm = extract_lines_iter_ledge_bm, .iter_ledge_mesh = extract_lines_iter_ledge_mesh, .finish = extract_lines_with_lines_loose_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; /** \} */ @@ -1159,97 +460,91 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, } } -static void extract_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - vert_set_bm(elb, l->v, l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + vert_set_bm(elb, l_iter->v, l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; vert_set_mesh(elb, mr, ml->v, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_points_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); - vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_BM_END; + vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); + vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); - vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_MESH_END; + vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); + vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - vert_set_bm(elb, eve, offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_BM_END; + vert_set_bm(elb, eve, offset + lvert_index); } static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_MESH_END; + + vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); } static void extract_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } -static const MeshExtract extract_points = { - .init = extract_points_init, - .iter_poly_bm = extract_points_iter_poly_bm, - .iter_poly_mesh = extract_points_iter_poly_mesh, - .iter_ledge_bm = extract_points_iter_ledge_bm, - .iter_ledge_mesh = extract_points_iter_ledge_mesh, - .iter_lvert_bm = extract_points_iter_lvert_bm, - .iter_lvert_mesh = extract_points_iter_lvert_mesh, - .finish = extract_points_finish, - .data_flag = 0, - .use_threading = false, -}; +const MeshExtract extract_points = {.init = extract_points_init, + .iter_poly_bm = extract_points_iter_poly_bm, + .iter_poly_mesh = extract_points_iter_poly_mesh, + .iter_ledge_bm = extract_points_iter_ledge_bm, + .iter_ledge_mesh = extract_points_iter_ledge_mesh, + .iter_lvert_bm = extract_points_iter_lvert_bm, + .iter_lvert_mesh = extract_points_iter_lvert_mesh, + .finish = extract_points_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; /** \} */ @@ -1266,71 +561,65 @@ static void *extract_fdots_init(const MeshRenderData *mr, return elb; } -static void extract_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *elb) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, f_index, f_index); - } - else { - GPU_indexbuf_set_point_restart(elb, f_index); - } + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, f_index, f_index); + } + else { + GPU_indexbuf_set_point_restart(elb, f_index); } - EXTRACT_POLY_FOREACH_BM_END; } static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *elb) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; const MVert *mv = &mr->mvert[ml->v]; if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); + return; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; + GPU_indexbuf_set_point_restart(elb, mp_index); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); - } + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + } + else { + GPU_indexbuf_set_point_restart(elb, mp_index); } - EXTRACT_POLY_FOREACH_MESH_END; } } static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } -static const MeshExtract extract_fdots = { - .init = extract_fdots_init, - .iter_poly_bm = extract_fdots_iter_poly_bm, - .iter_poly_mesh = extract_fdots_iter_poly_mesh, - .finish = extract_fdots_finish, - .data_flag = 0, - .use_threading = false, -}; +const MeshExtract extract_fdots = {.init = extract_fdots_init, + .iter_poly_bm = extract_fdots_iter_poly_bm, + .iter_poly_mesh = extract_fdots_iter_poly_mesh, + .finish = extract_fdots_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; /** \} */ @@ -1346,7 +635,7 @@ typedef struct MeshExtract_LinePaintMask_Data { static void *extract_lines_paint_mask_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) + void *UNUSED(ibo)) { size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); @@ -1355,12 +644,16 @@ static void *extract_lines_paint_mask_init(const MeshRenderData *mr, } static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_LinePaintMask_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int e_index = ml->e; const MEdge *me = &mr->medge[e_index]; if (!((mr->use_hide && (me->flag & ME_HIDE)) || @@ -1390,26 +683,26 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, GPU_indexbuf_set_line_restart(&data->elb, e_index); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } + static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LinePaintMask_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); } -static const MeshExtract extract_lines_paint_mask = { +const MeshExtract extract_lines_paint_mask = { .init = extract_lines_paint_mask_init, .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, .finish = extract_lines_paint_mask_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; /** \} */ @@ -1487,49 +780,44 @@ BLI_INLINE void lines_adjacency_triangle( } static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), - BM_elem_index_get(elt[1]->v), - BM_elem_index_get(elt[2]->v), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2]), - data); - } + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LineAdjacency_Data *data = _data; /* Create edges for remaining non manifold edges. */ EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); @@ -1559,14 +847,14 @@ static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), #undef NO_EDGE -static const MeshExtract extract_lines_adjacency = { +const MeshExtract extract_lines_adjacency = { .init = extract_lines_adjacency_init, .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, .finish = extract_lines_adjacency_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; /** \} */ @@ -1598,56 +886,51 @@ BLI_INLINE void edituv_tri_add( } static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - edituv_tri_add(data, - BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, - (mp->flag & ME_FACE_SEL) != 0, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2]); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; + const MPoly *mp = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mp->flag & ME_HIDE) != 0, + (mp->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); } static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); } -static const MeshExtract extract_edituv_tris = { +const MeshExtract extract_edituv_tris = { .init = extract_edituv_tris_init, .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, .finish = extract_edituv_tris_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; /** \} */ @@ -1674,27 +957,34 @@ BLI_INLINE void edituv_edge_add( } } -static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + edituv_edge_add(data, - BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(loop->f, BM_ELEM_SELECT), + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), l_index, - BM_elem_index_get(loop->next)); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + BM_elem_index_get(l_iter->next)); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int ml_index_last = mp->totloop + mp->loopstart - 1; const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); @@ -1704,27 +994,27 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, ml_index, ml_index_next); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); } -static const MeshExtract extract_edituv_lines = { +const MeshExtract extract_edituv_lines = { .init = extract_edituv_lines_init, .iter_poly_bm = extract_edituv_lines_iter_poly_bm, .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, .finish = extract_edituv_lines_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; /** \} */ @@ -1753,52 +1043,57 @@ BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - edituv_point_add(data, - BM_elem_flag_test(l->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(l->f, BM_ELEM_SELECT), - l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_point_add( + data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && mr->v_origindex[ml->v] != ORIGINDEX_NONE); edituv_point_add( data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); } -static const MeshExtract extract_edituv_points = { +const MeshExtract extract_edituv_points = { .init = extract_edituv_points_init, .iter_poly_bm = extract_edituv_points_iter_poly_bm, .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, .finish = extract_edituv_points_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; /** \} */ @@ -1830,26 +1125,29 @@ BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - edituv_facedot_add( - data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), f_index); - } - EXTRACT_POLY_FOREACH_BM_END; + edituv_facedot_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + f_index); } static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && mr->p_origindex[mp_index] != ORIGINDEX_NONE); const bool subd_fdot = (!mr->use_subsurf_fdots || @@ -1859,40 +1157,34 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, (mp->flag & ME_FACE_SEL) != 0, mp_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); - } - EXTRACT_POLY_FOREACH_MESH_END; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + edituv_facedot_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); } -static const MeshExtract extract_edituv_fdots = { +const MeshExtract extract_edituv_fdots = { .init = extract_edituv_fdots_init, .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, .finish = extract_edituv_fdots_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; /** \} */ @@ -1914,6 +1206,7 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorLoop struct accordingly. */ @@ -1921,7 +1214,6 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -1949,28 +1241,34 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, } static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - vert->nor = data->normals[BM_elem_index_get(l->v)].low; - BMFace *efa = l->f; - vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; + vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -1988,96 +1286,86 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, vert->nor.w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; - vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; - } - EXTRACT_LEDGE_FOREACH_BM_END; + + int l_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; + vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; } static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - vert[0].nor = data->normals[med->v1].low; - vert[1].nor = data->normals[med->v2].low; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + vert[0].nor = data->normals[med->v1].low; + vert[1].nor = data->normals[med->v2].low; } static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - vert->nor = data->normals[BM_elem_index_get(eve)].low; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + vert->nor = data->normals[BM_elem_index_get(eve)].low; } static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[v_index].low; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[v_index].low; } static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); } -static const MeshExtract extract_pos_nor = { - .init = extract_pos_nor_init, - .iter_poly_bm = extract_pos_nor_iter_poly_bm, - .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, - .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, - .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, - .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, - .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, - .finish = extract_pos_nor_finish, - .data_flag = 0, - .use_threading = true, -}; +const MeshExtract extract_pos_nor = {.init = extract_pos_nor_init, + .iter_poly_bm = extract_pos_nor_iter_poly_bm, + .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, + .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, + .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, + .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, + .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, + .finish = extract_pos_nor_finish, + .data_type = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -2098,6 +1386,7 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorHQLoop struct accordingly. */ @@ -2105,7 +1394,6 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -2133,29 +1421,35 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, } static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); - BMFace *efa = l->f; + BMFace *efa = l_iter->f; vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -2174,91 +1468,80 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, vert->nor[3] = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_BM_END; + int l_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[v_index].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[v_index].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); } -static const MeshExtract extract_pos_nor_hq = { +const MeshExtract extract_pos_nor_hq = { .init = extract_pos_nor_hq_init, .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm, .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh, @@ -2267,9 +1550,9 @@ static const MeshExtract extract_pos_nor_hq = { .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, .finish = extract_pos_nor_hq_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -2284,12 +1567,12 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2297,36 +1580,37 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, } static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(_l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); } else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, l->f)); + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); } } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; if (mr->loop_normals) { normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); @@ -2352,16 +1636,14 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static const MeshExtract extract_lnor_hq = { - .init = extract_lnor_hq_init, - .iter_poly_bm = extract_lnor_hq_iter_poly_bm, - .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, - .use_threading = true, -}; +const MeshExtract extract_lnor_hq = {.init = extract_lnor_hq_init, + .iter_poly_bm = extract_lnor_hq_iter_poly_bm, + .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, + .data_type = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -2372,12 +1654,12 @@ static void *extract_lnor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2385,40 +1667,39 @@ static void *extract_lnor_init(const MeshRenderData *mr, } static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( + bm_vert_no_get(mr, l_iter->v)); } else { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, l->f)); + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); } - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; if (mr->loop_normals) { *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); @@ -2444,16 +1725,14 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static const MeshExtract extract_lnor = { - .init = extract_lnor_init, - .iter_poly_bm = extract_lnor_iter_poly_bm, - .iter_poly_mesh = extract_lnor_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, - .use_threading = true, -}; +const MeshExtract extract_lnor = {.init = extract_lnor_init, + .iter_poly_bm = extract_lnor_iter_poly_bm, + .iter_poly_mesh = extract_lnor_iter_poly_mesh, + .data_type = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; /** \} */ @@ -2463,6 +1742,7 @@ static const MeshExtract extract_lnor = { static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2512,7 +1792,6 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca v_len = 1; } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -2545,11 +1824,10 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca return NULL; } -static const MeshExtract extract_uv = { - .init = extract_uv_init, - .data_flag = 0, - .use_threading = false, -}; +const MeshExtract extract_uv = {.init = extract_uv_init, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; /** \} */ @@ -2557,10 +1835,10 @@ static const MeshExtract extract_uv = { /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) +static void extract_tan_ex_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) { GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; @@ -2731,15 +2009,15 @@ static void extract_tan_ex(const MeshRenderData *mr, static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, false); + extract_tan_ex_init(mr, cache, buf, false); return NULL; } -static const MeshExtract extract_tan = { - .init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, - .use_threading = false, -}; +const MeshExtract extract_tan = {.init = extract_tan_init, + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | + MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; /** \} */ @@ -2749,13 +2027,13 @@ static const MeshExtract extract_tan = { static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, true); + extract_tan_ex_init(mr, cache, buf, true); return NULL; } -static const MeshExtract extract_tan_hq = { +const MeshExtract extract_tan_hq = { .init = extract_tan_hq_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, .use_threading = false, }; @@ -2769,6 +2047,7 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; @@ -2783,7 +2062,6 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2852,12 +2130,12 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, return NULL; } -static const MeshExtract extract_sculpt_data = { +const MeshExtract extract_sculpt_data = { .init = extract_sculpt_data_init, - .data_flag = 0, + .data_type = 0, /* TODO: enable threading. */ .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; /** \} */ @@ -2867,6 +2145,7 @@ static const MeshExtract extract_sculpt_data = { static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2928,7 +2207,6 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * } } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3001,11 +2279,10 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * return NULL; } -static const MeshExtract extract_vcol = { - .init = extract_vcol_init, - .data_flag = 0, - .use_threading = false, -}; +const MeshExtract extract_vcol = {.init = extract_vcol_init, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; /** \} */ @@ -3022,6 +2299,7 @@ static void *extract_orco_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex @@ -3031,7 +2309,6 @@ static void *extract_orco_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3045,32 +2322,36 @@ static void *extract_orco_init(const MeshRenderData *mr, return data; } -static void extract_orco_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); float *loop_orco = orco_data->vbo_data[l_index]; - copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]); + copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + } while ((l_iter = l_iter->next) != l_first); } static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; float *loop_orco = orco_data->vbo_data[ml_index]; copy_v3_v3(loop_orco, orco_data->orco[ml->v]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_orco_finish(const MeshRenderData *UNUSED(mr), @@ -3081,14 +2362,13 @@ static void extract_orco_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_orco = { - .init = extract_orco_init, - .iter_poly_bm = extract_orco_iter_poly_bm, - .iter_poly_mesh = extract_orco_iter_poly_mesh, - .finish = extract_orco_finish, - .data_flag = 0, - .use_threading = true, -}; +const MeshExtract extract_orco = {.init = extract_orco_init, + .iter_poly_bm = extract_orco_iter_poly_bm, + .iter_poly_mesh = extract_orco_iter_poly_mesh, + .finish = extract_orco_finish, + .data_type = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; /** \} */ @@ -3124,11 +2404,12 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3159,43 +2440,47 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, } static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_edge_is_manifold(l->e)) { - float ratio = loop_edge_factor_get(bm_face_no_get(mr, l->f), - bm_vert_co_get(mr, l->v), - bm_vert_no_get(mr, l->v), - bm_vert_co_get(mr, l->next->v)); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + if (BM_edge_is_manifold(l_iter->e)) { + float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), + bm_vert_co_get(mr, l_iter->v), + bm_vert_no_get(mr, l_iter->v), + bm_vert_co_get(mr, l_iter->next->v)); data->vbo_data[l_index] = ratio * 253 + 1; } else { data->vbo_data[l_index] = 255; } - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; - if (data->use_edge_render) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + if (data->use_edge_render) { const MEdge *med = &mr->medge[ml->e]; data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + /* Count loop per edge to detect non-manifold. */ if (data->edge_loop_count[ml->e] < 3) { data->edge_loop_count[ml->e]++; @@ -3217,34 +2502,28 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, data->vbo_data[ml_index] = 255; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *UNUSED(eed), + const int ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_BM_END; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; } static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; - data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + + data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; + data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; } static void extract_edge_fac_finish(const MeshRenderData *mr, @@ -3252,10 +2531,10 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data) { + GPUVertBuf *vbo = buf; MeshExtract_EdgeFac_Data *data = _data; if (GPU_crappy_amd_driver()) { - GPUVertBuf *vbo = (GPUVertBuf *)buf; /* Some AMD drivers strangely crash with VBO's with a one byte format. * To workaround we reinitialize the VBO with another format and convert * all bytes to floats. */ @@ -3281,16 +2560,16 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, MEM_freeN(data); } -static const MeshExtract extract_edge_fac = { +const MeshExtract extract_edge_fac = { .init = extract_edge_fac_init, .iter_poly_bm = extract_edge_fac_iter_poly_bm, .iter_poly_mesh = extract_edge_fac_iter_poly_mesh, .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, .finish = extract_edge_fac_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -3361,11 +2640,11 @@ static void *extract_weights_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3389,48 +2668,44 @@ static void *extract_weights_init(const MeshRenderData *mr, return data; } -static void extract_weights_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->cd_ofs != -1) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l->v, data->cd_ofs); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (data->cd_ofs != -1) { + const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + else { data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->dvert != NULL) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (data->dvert != NULL) { const MDeformVert *dvert = &data->dvert[ml->v]; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - const MDeformVert *dvert = NULL; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + const MDeformVert *dvert = NULL; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -3442,14 +2717,13 @@ static void extract_weights_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_weights = { - .init = extract_weights_init, - .iter_poly_bm = extract_weights_iter_poly_bm, - .iter_poly_mesh = extract_weights_iter_poly_mesh, - .finish = extract_weights_finish, - .data_flag = 0, - .use_threading = true, -}; +const MeshExtract extract_weights = {.init = extract_weights_init, + .iter_poly_bm = extract_weights_iter_poly_bm, + .iter_poly_mesh = extract_weights_iter_poly_mesh, + .finish = extract_weights_finish, + .data_type = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; /** \} */ @@ -3599,40 +2873,45 @@ static void *extract_edit_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); } static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { EditLoopData *data = (EditLoopData *)_data + l_index; memset(data, 0x0, sizeof(*data)); - mesh_render_data_face_flag(mr, l->f, -1, data); - mesh_render_data_edge_flag(mr, l->e, data); - mesh_render_data_vert_flag(mr, l->v, data); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_face_flag(mr, f, -1, data); + mesh_render_data_edge_flag(mr, l_iter->e, data); + mesh_render_data_vert_flag(mr, l_iter->v, data); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; EditLoopData *data = (EditLoopData *)_data + ml_index; memset(data, 0x0, sizeof(*data)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3648,84 +2927,72 @@ static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, mesh_render_data_vert_flag(mr, eve, data); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); - memset(data, 0x0, sizeof(*data) * 2); - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - mesh_render_data_vert_flag(mr, eed->v1, &data[0]); - mesh_render_data_vert_flag(mr, eed->v2, &data[1]); - } - EXTRACT_LEDGE_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); + memset(data, 0x0, sizeof(*data) * 2); + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + mesh_render_data_vert_flag(mr, eed->v1, &data[0]); + mesh_render_data_vert_flag(mr, eed->v2, &data[1]); } static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; - memset(data, 0x0, sizeof(*data) * 2); - const int e_index = mr->ledges[ledge_index]; - BMEdge *eed = bm_original_edge_get(mr, e_index); - BMVert *eve1 = bm_original_vert_get(mr, med->v1); - BMVert *eve2 = bm_original_vert_get(mr, med->v2); - if (eed) { - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - } - if (eve1) { - mesh_render_data_vert_flag(mr, eve1, &data[0]); - } - if (eve2) { - mesh_render_data_vert_flag(mr, eve2, &data[1]); - } + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; + memset(data, 0x0, sizeof(*data) * 2); + const int e_index = mr->ledges[ledge_index]; + BMEdge *eed = bm_original_edge_get(mr, e_index); + BMVert *eve1 = bm_original_vert_get(mr, med->v1); + BMVert *eve2 = bm_original_vert_get(mr, med->v2); + if (eed) { + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + } + if (eve1) { + mesh_render_data_vert_flag(mr, eve1, &data[0]); + } + if (eve2) { + mesh_render_data_vert_flag(mr, eve2, &data[1]); } - EXTRACT_LEDGE_FOREACH_MESH_END; } static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_vert_flag(mr, eve, data); - } - EXTRACT_LVERT_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_vert_flag(mr, eve, data); } static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - const int v_index = mr->lverts[lvert_index]; - BMVert *eve = bm_original_vert_get(mr, v_index); - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } + + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + const int v_index = mr->lverts[lvert_index]; + BMVert *eve = bm_original_vert_get(mr, v_index); + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); } - EXTRACT_LVERT_FOREACH_MESH_END; } -static const MeshExtract extract_edit_data = { +const MeshExtract extract_edit_data = { .init = extract_edit_data_init, .iter_poly_bm = extract_edit_data_iter_poly_bm, .iter_poly_mesh = extract_edit_data_iter_poly_mesh, @@ -3733,9 +3000,9 @@ static const MeshExtract extract_edit_data = { .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, .iter_lvert_bm = extract_edit_data_iter_lvert_bm, .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; /** \} */ @@ -3752,6 +3019,7 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ @@ -3759,7 +3027,6 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3772,28 +3039,34 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, } static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); MeshExtract_EditUVData_Data *data = _data; EditLoopData *eldata = &data->vbo_data[l_index]; memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); - mesh_render_data_face_flag(mr, l->f, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EditUVData_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + EditLoopData *eldata = &data->vbo_data[ml_index]; memset(eldata, 0x0, sizeof(*eldata)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3823,7 +3096,6 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -3834,14 +3106,14 @@ static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_edituv_data = { +const MeshExtract extract_edituv_data = { .init = extract_edituv_data_init, .iter_poly_bm = extract_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, .finish = extract_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; /** \} */ @@ -3853,12 +3125,12 @@ static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3880,11 +3152,12 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t return (ratio > 1.0f) ? (1.0f / ratio) : ratio; } -static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *UNUSED(data)) +static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; float tot_area = 0.0f, tot_uv_area = 0.0f; float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); @@ -3926,7 +3199,6 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, } /* Copy face data for each loop. */ - GPUVertBuf *vbo = buf; uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); if (mr->extract_type == MR_EXTRACT_BMESH) { @@ -3952,12 +3224,12 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, MEM_freeN(area_ratio); } -static const MeshExtract extract_edituv_stretch_area = { +const MeshExtract extract_edituv_stretch_area = { .init = extract_edituv_stretch_area_init, - .finish = mesh_edituv_stretch_area_finish, - .data_flag = 0, + .finish = extract_edituv_stretch_area_finish, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; /** \} */ @@ -4023,6 +3295,7 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* Waning: adjust #UVStretchAngle struct accordingly. */ @@ -4030,7 +3303,6 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4049,21 +3321,24 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, } static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + const MLoopUV *luv, *luv_next; - BMLoop *l_next = l->next; - BMFace *efa = l->f; - if (l == BM_FACE_FIRST_LOOP(efa)) { + BMLoop *l_next = l_iter->next; + if (l_iter == BM_FACE_FIRST_LOOP(f)) { /* First loop in face. */ - BMLoop *l_tmp = l->prev; - BMLoop *l_next_tmp = l; + BMLoop *l_tmp = l_iter->prev; + BMLoop *l_next_tmp = l_iter; luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); compute_normalize_edge_vectors(auv, @@ -4076,7 +3351,7 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v2_v2(last_auv, auv[1]); copy_v3_v3(last_av, av[1]); } - if (l_next == BM_FACE_FIRST_LOOP(efa)) { + if (l_next == BM_FACE_FIRST_LOOP(f)) { /* Move previous edge. */ copy_v2_v2(auv[0], auv[1]); copy_v3_v3(av[0], av[1]); @@ -4085,27 +3360,31 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v3_v3(av[1], last_av); } else { - luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); + luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); - compute_normalize_edge_vectors( - auv, av, luv->uv, luv_next->uv, bm_vert_co_get(mr, l->v), bm_vert_co_get(mr, l_next->v)); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_iter->v), + bm_vert_co_get(mr, l_next->v)); } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - int l_next = ml_index + 1, ml_index_end = mp->loopstart + mp->totloop; + int l_next = ml_index + 1; const MVert *v, *v_next; if (ml_index == mp->loopstart) { /* First loop in face. */ @@ -4136,7 +3415,6 @@ static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), @@ -4147,14 +3425,14 @@ static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr) MEM_freeN(data); } -static const MeshExtract extract_edituv_stretch_angle = { +const MeshExtract extract_edituv_stretch_angle = { .init = extract_edituv_stretch_angle_init, .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, .finish = extract_edituv_stretch_angle_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; /** \} */ @@ -4166,12 +3444,12 @@ static void *extract_mesh_analysis_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4725,14 +4003,14 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) MEM_freeN(vert_angles); } -static void extract_mesh_analysis_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) +static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; BLI_assert(mr->edit_bmesh); - GPUVertBuf *vbo = buf; float *l_weight = (float *)GPU_vertbuf_get_data(vbo); switch (mr->toolsettings->statvis.type) { @@ -4754,14 +4032,14 @@ static void extract_mesh_analysis_finish(const MeshRenderData *mr, } } -static const MeshExtract extract_mesh_analysis = { +const MeshExtract extract_mesh_analysis = { .init = extract_mesh_analysis_init, - .finish = extract_mesh_analysis_finish, + .finish = extract_analysis_iter_finish_mesh, /* This is not needed for all visualization types. * * Maybe split into different extract. */ - .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; /** \} */ @@ -4773,79 +4051,75 @@ static void *extract_fdots_pos_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int f_index, void *data) { float(*center)[3] = data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - float *co = center[f_index]; - zero_v3(co); + float *co = center[f_index]; + zero_v3(co); - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); - } while ((l_iter = l_iter->next) != l_first); - mul_v3_fl(co, 1.0f / (float)f->len); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); + } while ((l_iter = l_iter->next) != l_first); + mul_v3_fl(co, 1.0f / (float)f->len); } static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { float(*center)[3] = (float(*)[3])data; + float *co = center[mp_index]; + zero_v3(co); + const MVert *mvert = mr->mvert; const MLoop *mloop = mr->mloop; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v3_v3(center[mp_index], mv->co); + break; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - float *co = center[mp_index]; - zero_v3(co); - - const MLoop *ml = &mloop[mp->loopstart]; - for (int i = 0; i < mp->totloop; i++, ml++) { - const MVert *mv = &mvert[ml->v]; - add_v3_v3(center[mp_index], mv->co); - } - mul_v3_fl(co, 1.0f / (float)mp->totloop); + else { + const MVert *mv = &mvert[ml->v]; + add_v3_v3(center[mp_index], mv->co); } - EXTRACT_POLY_FOREACH_MESH_END; + } + + if (!mr->use_subsurf_fdots) { + mul_v3_fl(co, 1.0f / (float)mp->totloop); } } -static const MeshExtract extract_fdots_pos = { +const MeshExtract extract_fdots_pos = { .init = extract_fdots_pos_init, .iter_poly_bm = extract_fdots_pos_iter_poly_bm, .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; /** \} */ @@ -4861,11 +4135,12 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4877,8 +4152,8 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -4921,12 +4196,12 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, } } -static const MeshExtract extract_fdots_nor = { +const MeshExtract extract_fdots_nor = { .init = extract_fdots_nor_init, .finish = extract_fdots_nor_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ @@ -4937,11 +4212,12 @@ static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4953,8 +4229,8 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; short *nor = (short *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -4997,12 +4273,12 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, } } -static const MeshExtract extract_fdots_nor_hq = { +const MeshExtract extract_fdots_nor_hq = { .init = extract_fdots_nor_hq_init, .finish = extract_fdots_nor_hq_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ @@ -5020,13 +4296,14 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "au"); GPU_vertformat_alias_add(&format, "pos"); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5047,42 +4324,41 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, return data; } -static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_FdotUV_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - float w = 1.0f / (float)l->f->len; - const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); - madd_v2_v2fl(data->vbo_data[BM_elem_index_get(l->f)], luv->uv, w); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float w = 1.0f / (float)f->len; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); + } while ((l_iter = l_iter->next) != l_first); } static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_FdotUV_Data *data = _data; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { float w = 1.0f / (float)mp->totloop; madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -5094,14 +4370,15 @@ static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_fdots_uv = { +const MeshExtract extract_fdots_uv = { .init = extract_fdots_uv_init, .iter_poly_bm = extract_fdots_uv_iter_poly_bm, .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, .finish = extract_fdots_uv_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -5117,11 +4394,12 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5132,34 +4410,28 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, } static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); - } - EXTRACT_POLY_FOREACH_BM_END; + EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); } static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[mp_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); - } + EditLoopData *eldata = &data->vbo_data[mp_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); } - EXTRACT_POLY_FOREACH_MESH_END; } static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -5170,14 +4442,15 @@ static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_fdots_edituv_data = { +const MeshExtract extract_fdots_edituv_data = { .init = extract_fdots_edituv_data_init, .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, .finish = extract_fdots_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -5193,6 +4466,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; /* Exclusively for edit mode. */ BLI_assert(mr->bm); @@ -5201,7 +4475,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); @@ -5228,11 +4502,11 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, return NULL; } -static const MeshExtract extract_skin_roots = { +const MeshExtract extract_skin_roots = { .init = extract_skin_roots_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; /** \} */ @@ -5244,12 +4518,12 @@ static void *extract_select_idx_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); @@ -5260,169 +4534,163 @@ static void *extract_select_idx_init(const MeshRenderData *mr, * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the * shader to output original index. */ -static void extract_poly_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->f); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = f_index; + } while ((l_iter = l_iter->next) != l_first); } -static void extract_edge_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->e); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); } -static void extract_vert_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->v); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); } static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); } static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); - } - EXTRACT_LVERT_FOREACH_BM_END; + + ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); } static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int e_index = mr->ledges[ledge_index]; - const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int e_index = mr->ledges[ledge_index]; + const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; } static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; - int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; + int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; } static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(med, lvert_index, params, mr) - { - const int v_index = mr->lverts[lvert_index]; - const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; - ((uint32_t *)data)[offset + lvert_index] = v_orig; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int v_index = mr->lverts[lvert_index]; + const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; + ((uint32_t *)data)[offset + lvert_index] = v_orig; } -static const MeshExtract extract_poly_idx = { +const MeshExtract extract_poly_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_poly_idx_iter_poly_bm, .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; -static const MeshExtract extract_edge_idx = { +const MeshExtract extract_edge_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_edge_idx_iter_poly_bm, .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; -static const MeshExtract extract_vert_idx = { +const MeshExtract extract_vert_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_vert_idx_iter_poly_bm, .iter_poly_mesh = extract_vert_idx_iter_poly_mesh, @@ -5430,736 +4698,51 @@ static const MeshExtract extract_vert_idx = { .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; -static void *extract_select_fdot_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void *extract_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } -static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *UNUSED(f), + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - ((uint32_t *)data)[f_index] = f_index; - } - EXTRACT_POLY_FOREACH_BM_END; + ((uint32_t *)data)[f_index] = f_index; } static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *data) { if (mr->p_origindex != NULL) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mp_index; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mp_index; } } -static const MeshExtract extract_fdot_idx = { - .init = extract_select_fdot_idx_init, +const MeshExtract extract_fdot_idx = { + .init = extract_fdot_idx_init, .iter_poly_bm = extract_fdot_idx_iter_poly_bm, .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name ExtractTaskData - * \{ */ -typedef struct ExtractUserData { - void *user_data; -} ExtractUserData; - -typedef enum ExtractTaskDataType { - EXTRACT_MESH_EXTRACT, - EXTRACT_LINES_LOOSE, -} ExtractTaskDataType; - -typedef struct ExtractTaskData { - void *next, *prev; - const MeshRenderData *mr; - struct MeshBatchCache *cache; - const MeshExtract *extract; - ExtractTaskDataType tasktype; - eMRIterType iter_type; - int start, end; - /** Decremented each time a task is finished. */ - int32_t *task_counter; - void *buf; - ExtractUserData *user_data; -} ExtractTaskData; - -static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr, - struct MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) -{ - ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__); - taskdata->next = NULL; - taskdata->prev = NULL; - taskdata->tasktype = EXTRACT_MESH_EXTRACT; - taskdata->mr = mr; - taskdata->cache = cache; - taskdata->extract = extract; - taskdata->buf = buf; - - /* #ExtractUserData is shared between the iterations as it holds counters to detect if the - * extraction is finished. To make sure the duplication of the user_data does not create a new - * instance of the counters we allocate the user_data in its own container. - * - * This structure makes sure that when extract_init is called, that the user data of all - * iterations are updated. */ - taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__); - taskdata->iter_type = mesh_extract_iter_type(extract); - taskdata->task_counter = task_counter; - taskdata->start = 0; - taskdata->end = INT_MAX; - return taskdata; -} - -static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr, - struct MeshBatchCache *cache) -{ - ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__); - taskdata->tasktype = EXTRACT_LINES_LOOSE; - taskdata->mr = mr; - taskdata->cache = cache; - return taskdata; -} - -static void extract_task_data_free(void *data) -{ - ExtractTaskData *task_data = data; - MEM_SAFE_FREE(task_data->user_data); - MEM_freeN(task_data); -} - -BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, - const eMRIterType iter_type, - int start, - int end, - const MeshExtract *extract, - void *user_data) -{ - switch (mr->extract_type) { - case MR_EXTRACT_BMESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_POLY) { - extract->iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); - } - break; - case MR_EXTRACT_MAPPED: - case MR_EXTRACT_MESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_POLY) { - extract->iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); - } - break; - } -} - -static void extract_init(ExtractTaskData *data) -{ - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf); - } -} - -static void extract_run(void *__restrict taskdata) -{ - ExtractTaskData *data = (ExtractTaskData *)taskdata; - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - mesh_extract_iter(data->mr, - data->iter_type, - data->start, - data->end, - data->extract, - data->user_data->user_data); - - /* If this is the last task, we do the finish function. */ - int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); - if (remainin_tasks == 0 && data->extract->finish != NULL) { - data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data); - } - } - else if (data->tasktype == EXTRACT_LINES_LOOSE) { - extract_lines_loose_subbuffer(data->mr, data->cache); - } -} - -static void extract_init_and_run(void *__restrict taskdata) -{ - extract_init((ExtractTaskData *)taskdata); - extract_run(taskdata); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Update Mesh Render Data - * \{ */ -typedef struct MeshRenderDataUpdateTaskData { - MeshRenderData *mr; - eMRIterType iter_type; - eMRDataType data_flag; -} MeshRenderDataUpdateTaskData; - -static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) -{ - BLI_assert(taskdata); - MeshRenderData *mr = taskdata->mr; - mesh_render_data_free(mr); - MEM_freeN(taskdata); -} - -static void mesh_extract_render_data_node_exec(void *__restrict task_data) -{ - MeshRenderDataUpdateTaskData *update_task_data = task_data; - MeshRenderData *mr = update_task_data->mr; - const eMRIterType iter_type = update_task_data->iter_type; - const eMRDataType data_flag = update_task_data->data_flag; - - mesh_render_data_update_normals(mr, iter_type, data_flag); - mesh_render_data_update_looptris(mr, iter_type, data_flag); -} - -static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, - MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - MeshRenderDataUpdateTaskData *task_data = MEM_mallocN(sizeof(MeshRenderDataUpdateTaskData), - __func__); - task_data->mr = mr; - task_data->iter_type = iter_type; - task_data->data_flag = data_flag; - - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - mesh_extract_render_data_node_exec, - task_data, - (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Extract Single Threaded - * \{ */ -typedef struct ExtractSingleThreadedTaskData { - ListBase task_datas; -} ExtractSingleThreadedTaskData; - -static void extract_single_threaded_task_data_free(ExtractSingleThreadedTaskData *taskdata) -{ - BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_freeN(taskdata); -} - -static void extract_single_threaded_task_node_exec(void *__restrict task_data) -{ - ExtractSingleThreadedTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init_and_run(td); - } -} - -static struct TaskNode *extract_single_threaded_task_node_create( - struct TaskGraph *task_graph, ExtractSingleThreadedTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - extract_single_threaded_task_node_exec, - task_data, - (TaskGraphNodeFreeFunction)extract_single_threaded_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - UserData Initializer - * \{ */ -typedef struct UserDataInitTaskData { - ListBase task_datas; - int32_t *task_counters; - -} UserDataInitTaskData; - -static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) -{ - BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_SAFE_FREE(taskdata->task_counters); - MEM_freeN(taskdata); -} - -static void user_data_init_task_data_exec(void *__restrict task_data) -{ - UserDataInitTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init(td); - } -} - -static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, - UserDataInitTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - user_data_init_task_data_exec, - task_data, - (TaskGraphNodeFreeFunction)user_data_init_task_data_free); - return task_node; -} - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Loop - * \{ */ - -static void extract_range_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata, - const eMRIterType type, - int start, - int length) -{ - taskdata = MEM_dupallocN(taskdata); - atomic_add_and_fetch_int32(taskdata->task_counter, 1); - taskdata->iter_type = type; - taskdata->start = start; - taskdata->end = start + length; - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, extract_run, taskdata, MEM_freeN); - BLI_task_graph_edge_create(task_node_user_data_init, task_node); -} - -static void extract_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_mesh_render_data, - struct TaskNode *task_node_user_data_init, - ListBase *single_threaded_task_datas, - ListBase *user_data_init_task_datas, - const Scene *scene, - const MeshRenderData *mr, - MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) -{ - BLI_assert(scene != NULL); - const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || - GPU_use_hq_normals_workaround(); - if (do_hq_normals) { - if (extract == &extract_lnor) { - extract = &extract_lnor_hq; - } - else if (extract == &extract_pos_nor) { - extract = &extract_pos_nor_hq; - } - else if (extract == &extract_tan) { - extract = &extract_tan_hq; - } - else if (extract == &extract_fdots_nor) { - extract = &extract_fdots_nor_hq; - } - } - - /* Divide extraction of the VBO/IBO into sensible chunks of works. */ - ExtractTaskData *taskdata = extract_task_data_create_mesh_extract( - mr, cache, extract, buf, task_counter); - - /* Simple heuristic. */ - const int chunk_size = 8192; - const bool use_thread = (mr->loop_len + mr->loop_loose_len) > chunk_size; - if (use_thread && extract->use_threading) { - - /* Divide task into sensible chunks. */ - if (taskdata->iter_type & MR_ITER_LOOPTRI) { - for (int i = 0; i < mr->tri_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_POLY) { - for (int i = 0; i < mr->poly_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_POLY, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_LEDGE) { - for (int i = 0; i < mr->edge_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LEDGE, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_LVERT) { - for (int i = 0; i < mr->vert_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LVERT, i, chunk_size); - } - } - BLI_addtail(user_data_init_task_datas, taskdata); - } - else if (use_thread) { - /* One task for the whole VBO. */ - (*task_counter)++; - struct TaskNode *one_task = BLI_task_graph_node_create( - task_graph, extract_init_and_run, taskdata, extract_task_data_free); - BLI_task_graph_edge_create(task_node_mesh_render_data, one_task); - } - else { - /* Single threaded extraction. */ - (*task_counter)++; - BLI_addtail(single_threaded_task_datas, taskdata); - } -} - -void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, - MeshBatchCache *cache, - MeshBufferCache mbc, - Mesh *me, - - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, - const Scene *scene, - const ToolSettings *ts, - const bool use_hide) -{ - /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. - * This sub-graph starts with an extract_render_data_node. This fills/converts the required data - * from Mesh. - * - * Small extractions and extractions that can't be multi-threaded are grouped in a single - * `extract_single_threaded_task_node`. - * - * Other extractions will create a node for each loop exceeding 8192 items. these nodes are - * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the - * user_data needed for the extraction based on the data extracted from the mesh. - * counters are used to check if the finalize of a task has to be called. - * - * Mesh extraction sub graph - * - * +----------------------+ - * +-----> | extract_task1_loop_1 | - * | +----------------------+ - * +------------------+ +----------------------+ +----------------------+ - * | mesh_render_data | --> | | --> | extract_task1_loop_2 | - * +------------------+ | | +----------------------+ - * | | | +----------------------+ - * | | user_data_init | --> | extract_task2_loop_1 | - * v | | +----------------------+ - * +------------------+ | | +----------------------+ - * | single_threaded | | | --> | extract_task2_loop_2 | - * +------------------+ +----------------------+ +----------------------+ - * | +----------------------+ - * +-----> | extract_task2_loop_3 | - * +----------------------+ - */ - eMRIterType iter_flag = 0; - eMRDataType data_flag = 0; - - const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL; - -#define TEST_ASSIGN(type, type_lowercase, name) \ - do { \ - if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ - iter_flag |= mesh_extract_iter_type(&extract_##name); \ - data_flag |= extract_##name.data_flag; \ - } \ - } while (0) - - TEST_ASSIGN(VBO, vbo, pos_nor); - TEST_ASSIGN(VBO, vbo, lnor); - TEST_ASSIGN(VBO, vbo, uv); - TEST_ASSIGN(VBO, vbo, tan); - TEST_ASSIGN(VBO, vbo, vcol); - TEST_ASSIGN(VBO, vbo, sculpt_data); - TEST_ASSIGN(VBO, vbo, orco); - TEST_ASSIGN(VBO, vbo, edge_fac); - TEST_ASSIGN(VBO, vbo, weights); - TEST_ASSIGN(VBO, vbo, edit_data); - TEST_ASSIGN(VBO, vbo, edituv_data); - TEST_ASSIGN(VBO, vbo, edituv_stretch_area); - TEST_ASSIGN(VBO, vbo, edituv_stretch_angle); - TEST_ASSIGN(VBO, vbo, mesh_analysis); - TEST_ASSIGN(VBO, vbo, fdots_pos); - TEST_ASSIGN(VBO, vbo, fdots_nor); - TEST_ASSIGN(VBO, vbo, fdots_uv); - TEST_ASSIGN(VBO, vbo, fdots_edituv_data); - TEST_ASSIGN(VBO, vbo, poly_idx); - TEST_ASSIGN(VBO, vbo, edge_idx); - TEST_ASSIGN(VBO, vbo, vert_idx); - TEST_ASSIGN(VBO, vbo, fdot_idx); - TEST_ASSIGN(VBO, vbo, skin_roots); - - TEST_ASSIGN(IBO, ibo, tris); - TEST_ASSIGN(IBO, ibo, lines); - TEST_ASSIGN(IBO, ibo, points); - TEST_ASSIGN(IBO, ibo, fdots); - TEST_ASSIGN(IBO, ibo, lines_paint_mask); - TEST_ASSIGN(IBO, ibo, lines_adjacency); - TEST_ASSIGN(IBO, ibo, edituv_tris); - TEST_ASSIGN(IBO, ibo, edituv_lines); - TEST_ASSIGN(IBO, ibo, edituv_points); - TEST_ASSIGN(IBO, ibo, edituv_fdots); - - if (do_lines_loose_subbuffer) { - iter_flag |= MR_ITER_LEDGE; - } - -#undef TEST_ASSIGN - -#ifdef DEBUG_TIME - double rdata_start = PIL_check_seconds_timer(); -#endif - - MeshRenderData *mr = mesh_render_data_create(me, - is_editmode, - is_paint_mode, - is_mode_active, - obmat, - do_final, - do_uvedit, - cd_layer_used, - ts, - iter_flag, - data_flag); - mr->use_hide = use_hide; - mr->use_subsurf_fdots = use_subsurf_fdots; - mr->use_final_mesh = do_final; - -#ifdef DEBUG_TIME - double rdata_end = PIL_check_seconds_timer(); -#endif - - size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); - int32_t *task_counters = MEM_callocN(counters_size, __func__); - int counter_used = 0; - - struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, iter_flag, data_flag); - ExtractSingleThreadedTaskData *single_threaded_task_data = MEM_callocN( - sizeof(ExtractSingleThreadedTaskData), __func__); - UserDataInitTaskData *user_data_init_task_data = MEM_callocN(sizeof(UserDataInitTaskData), - __func__); - user_data_init_task_data->task_counters = task_counters; - struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( - task_graph, user_data_init_task_data); - -#define EXTRACT(buf, name) \ - if (mbc.buf.name) { \ - extract_task_create(task_graph, \ - task_node_mesh_render_data, \ - task_node_user_data_init, \ - &single_threaded_task_data->task_datas, \ - &user_data_init_task_data->task_datas, \ - scene, \ - mr, \ - cache, \ - &extract_##name, \ - mbc.buf.name, \ - &task_counters[counter_used++]); \ - } \ - ((void)0) - - EXTRACT(vbo, pos_nor); - EXTRACT(vbo, lnor); - EXTRACT(vbo, uv); - EXTRACT(vbo, tan); - EXTRACT(vbo, vcol); - EXTRACT(vbo, sculpt_data); - EXTRACT(vbo, orco); - EXTRACT(vbo, edge_fac); - EXTRACT(vbo, weights); - EXTRACT(vbo, edit_data); - EXTRACT(vbo, edituv_data); - EXTRACT(vbo, edituv_stretch_area); - EXTRACT(vbo, edituv_stretch_angle); - EXTRACT(vbo, mesh_analysis); - EXTRACT(vbo, fdots_pos); - EXTRACT(vbo, fdots_nor); - EXTRACT(vbo, fdots_uv); - EXTRACT(vbo, fdots_edituv_data); - EXTRACT(vbo, poly_idx); - EXTRACT(vbo, edge_idx); - EXTRACT(vbo, vert_idx); - EXTRACT(vbo, fdot_idx); - EXTRACT(vbo, skin_roots); - - EXTRACT(ibo, tris); - if (mbc.ibo.lines) { - /* When `lines` and `lines_loose` are requested, schedule lines extraction that also creates - * the `lines_loose` sub-buffer. */ - const MeshExtract *lines_extractor = do_lines_loose_subbuffer ? - &extract_lines_with_lines_loose : - &extract_lines; - extract_task_create(task_graph, - task_node_mesh_render_data, - task_node_user_data_init, - &single_threaded_task_data->task_datas, - &user_data_init_task_data->task_datas, - scene, - mr, - cache, - lines_extractor, - mbc.ibo.lines, - &task_counters[counter_used++]); - } - else { - if (do_lines_loose_subbuffer) { - ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache); - BLI_addtail(&single_threaded_task_data->task_datas, taskdata); - } - } - EXTRACT(ibo, points); - EXTRACT(ibo, fdots); - EXTRACT(ibo, lines_paint_mask); - EXTRACT(ibo, lines_adjacency); - EXTRACT(ibo, edituv_tris); - EXTRACT(ibo, edituv_lines); - EXTRACT(ibo, edituv_points); - EXTRACT(ibo, edituv_fdots); - - /* Only create the edge when there is user data that needs to be initialized. - * The task is still part of the graph so the task_data will be freed when the graph is freed. - */ - if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) { - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); - } - - if (!BLI_listbase_is_empty(&single_threaded_task_data->task_datas)) { - struct TaskNode *task_node = extract_single_threaded_task_node_create( - task_graph, single_threaded_task_data); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); - } - else { - extract_single_threaded_task_data_free(single_threaded_task_data); - } - - /* Trigger the sub-graph for this mesh. */ - BLI_task_graph_node_push_work(task_node_mesh_render_data); - -#undef EXTRACT - -#ifdef DEBUG_TIME - BLI_task_graph_work_and_wait(task_graph); - double end = PIL_check_seconds_timer(); - - static double avg = 0; - static double avg_fps = 0; - static double avg_rdata = 0; - static double end_prev = 0; - - if (end_prev == 0) { - end_prev = end; - } - - avg = avg * 0.95 + (end - rdata_end) * 0.05; - avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; - avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; - - printf( - "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); - - end_prev = end; -#endif -} - -/** \} */ + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)};
\ No newline at end of file diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h new file mode 100644 index 00000000000..d3acf10b72c --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -0,0 +1,515 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#pragma once + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_editmesh.h" + +#include "draw_cache_extract.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum eMRExtractType { + MR_EXTRACT_BMESH, + MR_EXTRACT_MAPPED, + MR_EXTRACT_MESH, +} eMRExtractType; + +typedef struct MeshRenderData { + eMRExtractType extract_type; + + int poly_len, edge_len, vert_len, loop_len; + int edge_loose_len; + int vert_loose_len; + int loop_loose_len; + int tri_len; + int mat_len; + + bool use_hide; + bool use_subsurf_fdots; + bool use_final_mesh; + + /** Use for #MeshStatVis calculation which use world-space coords. */ + float obmat[4][4]; + + const ToolSettings *toolsettings; + /** Edit Mesh */ + BMEditMesh *edit_bmesh; + BMesh *bm; + EditMeshData *edit_data; + + /* For deformed edit-mesh data. */ + /* Use for #ME_WRAPPER_TYPE_BMESH. */ + const float (*bm_vert_coords)[3]; + const float (*bm_vert_normals)[3]; + const float (*bm_poly_normals)[3]; + const float (*bm_poly_centers)[3]; + + int *v_origindex, *e_origindex, *p_origindex; + int crease_ofs; + int bweight_ofs; + int freestyle_edge_ofs; + int freestyle_face_ofs; + /** Mesh */ + Mesh *me; + const MVert *mvert; + const MEdge *medge; + const MLoop *mloop; + const MPoly *mpoly; + BMVert *eve_act; + BMEdge *eed_act; + BMFace *efa_act; + BMFace *efa_act_uv; + /* Data created on-demand (usually not for #BMesh based data). */ + MLoopTri *mlooptri; + float (*loop_normals)[3]; + float (*poly_normals)[3]; + int *lverts, *ledges; +} MeshRenderData; + +BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) +{ + return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_face_at_index(mr->bm, mr->p_origindex[idx]) : + NULL; +} + +BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) +{ + return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : + NULL; +} + +BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) +{ + return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : + NULL; +} + +BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_coords)[3] = mr->bm_vert_coords; + if (vert_coords != NULL) { + return vert_coords[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->co; +} + +BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_normals)[3] = mr->bm_vert_normals; + if (vert_normals != NULL) { + return vert_normals[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->no; +} + +BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) +{ + const float(*poly_normals)[3] = mr->bm_poly_normals; + if (poly_normals != NULL) { + return poly_normals[BM_elem_index_get(efa)]; + } + + UNUSED_VARS(mr); + return efa->no; +} + +/* TODO(jbakker): phase out batch iteration macros as they are only used once. */ +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loop Triangles + * \{ */ + +typedef struct ExtractTriBMesh_Params { + BMLoop *(*looptris)[3]; + int tri_range[2]; +} ExtractTriBMesh_Params; +typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, + BMLoop **elt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 3) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } + +typedef struct ExtractTriMesh_Params { + const MLoopTri *mlooptri; + int tri_range[2]; +} ExtractTriMesh_Params; +typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, + const MLoopTri *mlt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 1) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Polygons, Loops + * \{ */ + +typedef struct ExtractPolyBMesh_Params { + BMLoop *(*looptris)[3]; + int poly_range[2]; +} ExtractPolyBMesh_Params; +typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, + BMFace *f, + const int f_index, + void *data); + +#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_poly = _ftable[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_BM_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_face = _ftable[index_poly]; \ + BMLoop *elem_loop, *l_first; \ + elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ + do { \ + const int index_loop = BM_elem_index_get(elem_loop); \ + (void)index_loop; /* Quiet warning when unused. */ + +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ + } \ + while ((elem_loop = elem_loop->next) != l_first) \ + ; \ + } \ + } + +typedef struct ExtractPolyMesh_Params { + int poly_range[2]; +} ExtractPolyMesh_Params; +typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data); + +#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_MESH_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ + elem_poly, index_poly, elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const MLoop *_mloop = mr->mloop; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ + for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ + const MLoop *elem_loop = &_mloop[index_loop]; \ + (void)elem_loop; + +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Edges + * \{ */ + +typedef struct ExtractLEdgeBMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeBMesh_Params; +typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ + CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ + BMEdge **_etable = mr->bm->etable; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLEdgeMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeMesh_Params; +typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ + CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ + { \ + const MEdge *_medge = mr->medge; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Vertices + * \{ */ + +typedef struct ExtractLVertBMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertBMesh_Params; +typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ + CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMVert **vtable = mr->bm->vtable; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + BMVert *elem_vert = vtable[lverts[index_lvert]]; \ + (void)elem_vert; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLVertMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertMesh_Params; +typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ + CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ + { \ + const MVert *mvert = mr->mvert; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + const MVert *elem = &mvert[lverts[index_lvert]]; \ + (void)elem; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +typedef void *(ExtractInitFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer); +typedef void(ExtractFinishFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *data); + +typedef struct MeshExtract { + /** Executed on main thread and return user data for iteration functions. */ + ExtractInitFn *init; + /** Executed on one (or more if use_threading) worker thread(s). */ + ExtractTriBMeshFn *iter_looptri_bm; + ExtractTriMeshFn *iter_looptri_mesh; + ExtractPolyBMeshFn *iter_poly_bm; + ExtractPolyMeshFn *iter_poly_mesh; + ExtractLEdgeBMeshFn *iter_ledge_bm; + ExtractLEdgeMeshFn *iter_ledge_mesh; + ExtractLVertBMeshFn *iter_lvert_bm; + ExtractLVertMeshFn *iter_lvert_mesh; + /** Executed on one worker thread after all elements iterations. */ + ExtractFinishFn *finish; + /** Used to request common data. */ + const eMRDataType data_type; + /** Used to know if the element callbacks are thread-safe and can be parallelized. */ + const bool use_threading; + /** + * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index + * buffer. + */ + const size_t mesh_buffer_offset; +} MeshExtract; + +/** \} */ + +/* draw_cache_extract_mesh_render_data.c */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const ToolSettings *ts, + const eMRIterType iter_type); +void mesh_render_data_free(MeshRenderData *mr); +void mesh_render_data_update_normals(MeshRenderData *mr, + const eMRDataType data_flag); +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag); + +/* draw_cache_extract_mesh_extractors.c */ +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc); +eMRIterType mesh_extract_iter_type(const MeshExtract *ext); +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer); +/* + * Total number of extractions types. + */ +#define M_EXTRACT_LEN 38 + +extern const MeshExtract extract_tris; +extern const MeshExtract extract_lines; +extern const MeshExtract extract_lines_with_lines_loose; +extern const MeshExtract extract_points; +extern const MeshExtract extract_fdots; +extern const MeshExtract extract_lines_paint_mask; +extern const MeshExtract extract_lines_adjacency; +extern const MeshExtract extract_edituv_tris; +extern const MeshExtract extract_edituv_lines; +extern const MeshExtract extract_edituv_points; +extern const MeshExtract extract_edituv_fdots; +extern const MeshExtract extract_pos_nor; +extern const MeshExtract extract_pos_nor_hq; +extern const MeshExtract extract_lnor_hq; +extern const MeshExtract extract_lnor; +extern const MeshExtract extract_uv; +extern const MeshExtract extract_tan; +extern const MeshExtract extract_tan_hq; +extern const MeshExtract extract_sculpt_data; +extern const MeshExtract extract_vcol; +extern const MeshExtract extract_orco; +extern const MeshExtract extract_edge_fac; +extern const MeshExtract extract_weights; +extern const MeshExtract extract_edit_data; +extern const MeshExtract extract_edituv_data; +extern const MeshExtract extract_edituv_stretch_area; +extern const MeshExtract extract_edituv_stretch_angle; +extern const MeshExtract extract_mesh_analysis; +extern const MeshExtract extract_fdots_pos; +extern const MeshExtract extract_fdots_nor; +extern const MeshExtract extract_fdots_nor_hq; +extern const MeshExtract extract_fdots_uv; +extern const MeshExtract extract_fdots_edituv_data; +extern const MeshExtract extract_skin_roots; +extern const MeshExtract extract_poly_idx; +extern const MeshExtract extract_edge_idx; +extern const MeshExtract extract_vert_idx; +extern const MeshExtract extract_fdot_idx; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c new file mode 100644 index 00000000000..db28e4f3ba3 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -0,0 +1,372 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" + +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_mesh.h" + +#include "GPU_batch.h" + +#include "ED_mesh.h" + +#include "draw_cache_extract_mesh_private.h" + +/* ---------------------------------------------------------------------- */ +/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). + * \{ */ + +static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) +{ + mr->ledges = cache->ledges; + mr->lverts = cache->lverts; + mr->vert_loose_len = cache->vert_loose_len; + mr->edge_loose_len = cache->edge_loose_len; + + mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); +} + +static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges + * and verts are calculated at the same time.*/ + if (cache->lverts) { + return; + } + + cache->vert_loose_len = 0; + cache->edge_loose_len = 0; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + const MEdge *med = mr->medge; + for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { + if (med->flag & ME_LOOSEEDGE) { + cache->ledges[cache->edge_loose_len++] = med_index; + } + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, med->v1); + BLI_BITMAP_ENABLE(lvert_map, med->v2); + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + cache->lverts[cache->vert_loose_len++] = v; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + MEM_freeN(lvert_map); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + int elem_id; + BMIter iter; + BMVert *eve; + BMEdge *ede; + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + cache->lverts[cache->vert_loose_len++] = elem_id; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + cache->ledges[cache->edge_loose_len++] = elem_id; + } + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + } +} + +/** + * Part of the creation of the #MeshRenderData that happens in a thread. + */ +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + Mesh *me = mr->me; + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); + BKE_mesh_recalc_looptri( + me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); + } + } + else { + /* #BMesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + /* Edit mode ensures this is valid, no need to calculate. */ + BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); + } + } +} + +void mesh_render_data_update_normals(MeshRenderData *mr, + const eMRDataType data_flag) +{ + Mesh *me = mr->me; + const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; + const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { + mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); + BKE_mesh_calc_normals_poly((MVert *)mr->mvert, + NULL, + mr->vert_len, + mr->mloop, + mr->mpoly, + mr->loop_len, + mr->poly_len, + mr->poly_normals, + true); + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); + BKE_mesh_normals_loop_split(mr->me->mvert, + mr->vert_len, + mr->me->medge, + mr->edge_len, + mr->me->mloop, + mr->loop_normals, + mr->loop_len, + mr->me->mpoly, + mr->poly_normals, + mr->poly_len, + is_auto_smooth, + split_angle, + NULL, + clnors, + NULL); + } + } + else { + /* #BMesh */ + if (data_flag & MR_DATA_POLY_NOR) { + /* Use #BMFace.no instead. */ + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + + const float(*vert_coords)[3] = NULL; + const float(*vert_normals)[3] = NULL; + const float(*poly_normals)[3] = NULL; + + if (mr->edit_data && mr->edit_data->vertexCos) { + vert_coords = mr->bm_vert_coords; + vert_normals = mr->bm_vert_normals; + poly_normals = mr->bm_poly_normals; + } + + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); + BM_loops_calc_normal_vcos(mr->bm, + vert_coords, + vert_normals, + poly_normals, + is_auto_smooth, + split_angle, + mr->loop_normals, + NULL, + NULL, + clnors_offset, + false); + } + } +} + +/** + * \param is_mode_active: When true, use the modifiers from the edit-data, + * otherwise don't use modifiers as they are not from this object. + */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const ToolSettings *ts, + const eMRIterType iter_type) +{ + MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); + mr->toolsettings = ts; + mr->mat_len = mesh_render_mat_len_get(me); + + copy_m4_m4(mr->obmat, obmat); + + if (is_editmode) { + BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); + mr->bm = me->edit_mesh->bm; + mr->edit_bmesh = me->edit_mesh; + mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; + mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; + + if (mr->edit_data) { + EditMeshData *emd = mr->edit_data; + if (emd->vertexCos) { + BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); + BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); + } + + mr->bm_vert_coords = mr->edit_data->vertexCos; + mr->bm_vert_normals = mr->edit_data->vertexNos; + mr->bm_poly_normals = mr->edit_data->polyNos; + mr->bm_poly_centers = mr->edit_data->polyCos; + } + + bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + bool use_mapped = is_mode_active && + (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); + + int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; + + BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); + BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); + + mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); + mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); + mr->eed_act = BM_mesh_active_edge_get(mr->bm); + mr->eve_act = BM_mesh_active_vert_get(mr->bm); + + mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); + mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); +#ifdef WITH_FREESTYLE + mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); + mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); +#endif + + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; + + /* Seems like the mesh_eval_final do not have the right origin indices. + * Force not mapped in this case. */ + if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { + // mr->edit_bmesh = NULL; + mr->extract_type = MR_EXTRACT_MESH; + } + } + else { + mr->me = me; + mr->edit_bmesh = NULL; + + bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; + } + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + mr->vert_len = mr->me->totvert; + mr->edge_len = mr->me->totedge; + mr->loop_len = mr->me->totloop; + mr->poly_len = mr->me->totpoly; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + + mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); + mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); + mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); + mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); + + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + + mr->vert_len = bm->totvert; + mr->edge_len = bm->totedge; + mr->loop_len = bm->totloop; + mr->poly_len = bm->totface; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + } + + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + mesh_render_data_loose_geom_ensure(mr, cache); + mesh_render_data_loose_geom_load(mr, cache); + } + + return mr; +} + +void mesh_render_data_free(MeshRenderData *mr) +{ + MEM_SAFE_FREE(mr->mlooptri); + MEM_SAFE_FREE(mr->poly_normals); + MEM_SAFE_FREE(mr->loop_normals); + + /* Loose geometry are owned by MeshBufferExtractionCache. */ + mr->ledges = NULL; + mr->lverts = NULL; + + MEM_freeN(mr); +} + +/** \} */
\ No newline at end of file diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 5cf99db5485..ee6a47e3dc6 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -843,6 +843,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2; int vbo_len_used = 0; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) +#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos)) { GPU_vertbuf_init_with_format(vbo_pos, &format_pos); GPU_vertbuf_data_alloc(vbo_pos, verts_len_capacity); @@ -863,6 +866,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity); } +#undef DRW_TEST_ASSIGN_VBO +#undef DRW_TEST_ASSIGN_IBO + int nu_id = 0; for (Nurb *nu = (Nurb *)rdata->nurbs->first; nu; nu = nu->next, nu_id++) { const BezTriple *bezt = nu->bezt; diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index d606f70db9e..ee16cb1a022 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -532,6 +532,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPUVertBufRaw uv_step = {0}; GPUVertBufRaw tan_step = {0}; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) { GPU_vertbuf_init_with_format(vbo_pos_nor, do_hq_normals ? &format_pos_nor_hq : &format_pos_nor); @@ -550,6 +552,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPU_vertbuf_attr_get_raw_data(vbo_tan, tan_id, &tan_step); } +#undef DRW_TEST_ASSIGN_VBO + BKE_displist_normals_add(lb); LISTBASE_FOREACH (const DispList *, dl, lb) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 8d5fdcc5276..3cc71e47f28 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -707,6 +707,26 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) } } +static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache) +{ + GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; + GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; + for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { + GPU_VERTBUF_DISCARD_SAFE(vbos[i]); + } + for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { + GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); + } +} + +static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache) +{ + MEM_SAFE_FREE(extraction_cache->lverts); + MEM_SAFE_FREE(extraction_cache->ledges); + extraction_cache->edge_loose_len = 0; + extraction_cache->vert_loose_len = 0; +} + static void mesh_batch_cache_clear(Mesh *me) { MeshBatchCache *cache = me->runtime.batch_cache; @@ -714,16 +734,13 @@ static void mesh_batch_cache_clear(Mesh *me) return; } FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; - GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; - for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { - GPU_VERTBUF_DISCARD_SAFE(vbos[i]); - } - for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { - GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); - } + mesh_buffer_cache_clear(mbufcache); } + mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache); + for (int i = 0; i < cache->mat_len; i++) { GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]); } @@ -1542,7 +1559,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_uvcage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->uv_cage, + &cache->uv_cage, + &cache->uv_cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1551,7 +1569,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, true, false, - &cache->cd_used, scene, ts, true); @@ -1560,7 +1577,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_cage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->cage, + &cache->cage, + &cache->cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1569,7 +1587,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, true); @@ -1577,7 +1594,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, - cache->final, + &cache->final, + &cache->final_extraction_cache, me, is_editmode, is_paint_mode, @@ -1586,7 +1604,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, true, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h index bfc714e5d6a..6e537a3bffa 100644 --- a/source/blender/draw/intern/draw_cache_inline.h +++ b/source/blender/draw/intern/draw_cache_inline.h @@ -40,10 +40,6 @@ (flag |= DRW_ibo_requested(ibo) ? (value) : 0) #endif -/* Test and assign NULL if test fails */ -#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) -#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) - BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch) { /* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index bf3b10bccd3..585e171adc5 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -88,8 +88,8 @@ extern char datatoc_common_hair_refine_comp_glsl[]; extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; /* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */ -/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OSs. Currently the - * APPLE codepath does not compile on other platforms and vice versa. */ +/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OS's. + * Currently the `__APPLE__` code-path does not compile on other platforms and vice versa. */ #ifdef USE_COMPUTE_SHADERS static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement)) { diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 33deae0b0a1..479f9cd1827 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -86,6 +86,8 @@ float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; } vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } +float safe_sqrt(float a) { return sqrt(max(a, 0.0)); } + float sqr(float a) { return a * a; } vec2 sqr(vec2 a) { return a * a; } vec3 sqr(vec3 a) { return a * a; } diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 8842274e017..e4f2de1f741 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -210,7 +210,7 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd, ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_i, depth, r_location_world)) { + if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) { is_location_world_set = true; if (r_normal_world) { zero_v3(r_normal_world); diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 4de97411059..ea3d921f2c5 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -42,6 +42,7 @@ struct SpaceImage; struct ToolSettings; struct ViewLayer; struct bNode; +struct bNodeTree; struct wmKeyConfig; /* uvedit_ops.c */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 66ec57c8a31..52d69d12253 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -162,10 +162,10 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, const int mval[2], float r_normal[3]); -bool ED_view3d_depth_unproject(const struct ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]); +bool ED_view3d_depth_unproject_v3(const struct ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]); void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); /* Projection */ @@ -410,8 +410,13 @@ void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d, const float obmat[4][4], float r_pmat[4][4]); -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]); -bool ED_view3d_unproject( +void ED_view3d_project_v3(const struct ARegion *region, + const float world[3], + float r_region_co[3]); +void ED_view3d_project_v2(const struct ARegion *region, + const float world[3], + float r_region_co[2]); +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]); /* end */ diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index d5fb0e4e744..6583fa31dbf 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -118,7 +118,8 @@ static bool eyedropper_init(bContext *C, wmOperator *op) RNA_property_float_get_array(&eye->ptr, eye->prop, col); if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) { eye->crypto_node = (bNode *)eye->ptr.data; - eye->cryptomatte_session = ntreeCompositCryptomatteSession(eye->crypto_node); + eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C), + eye->crypto_node); eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye); } @@ -199,6 +200,57 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay return false; } +static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Scene *scene = (Scene *)node->id; + BLI_assert(GS(scene->id.name) == ID_SCE); + Render *re = RE_GetSceneRender(scene); + + if (re) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + RE_ReleaseResult(re); + } + return success; +} + +static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node, + NodeCryptomatte *crypto, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Image *image = (Image *)node->id; + BLI_assert(GS(image->id.name) == ID_IM); + ImageUser *iuser = &crypto->iuser; + + if (image && image->type == IMA_TYPE_MULTILAYER) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); + if (image->rr) { + LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + BKE_image_release_ibuf(image, ibuf, NULL); + } + return success; +} static bool eyedropper_cryptomatte_sample_fl( bContext *C, Eyedropper *eye, int mx, int my, float r_col[3]) @@ -255,53 +307,19 @@ static bool eyedropper_cryptomatte_sample_fl( return false; } - bool success = false; /* TODO(jbakker): Migrate this file to cc and use std::string as return param. */ char prefix[MAX_NAME + 1]; - ntreeCompositCryptomatteLayerPrefix(node, prefix, sizeof(prefix) - 1); + const Scene *scene = CTX_data_scene(C); + ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1); prefix[MAX_NAME] = '\0'; if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { - Scene *scene = (Scene *)node->id; - BLI_assert(GS(scene->id.name) == ID_SCE); - Render *re = RE_GetSceneRender(scene); - - if (re) { - RenderResult *rr = RE_AcquireResultRead(re); - if (rr) { - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - RE_ReleaseResult(re); - } + return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); } - else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { - Image *image = (Image *)node->id; - BLI_assert(GS(image->id.name) == ID_IM); - ImageUser *iuser = &crypto->iuser; - - if (image && image->type == IMA_TYPE_MULTILAYER) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); - if (image->rr) { - LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - BKE_image_release_ibuf(image, ibuf, NULL); - } + if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { + return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); } - - return success; + return false; } /** diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index b5cd9c7f60d..825b7d11aef 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -578,8 +578,8 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, float r_origin_ofs[3]) { /* unproject to find view ray */ - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); /* transform into object space */ mul_m4_v3(kcd->ob_imat, r_origin); @@ -1745,7 +1745,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, float view[3], p_ofs[3]; /* TODO: I think there's a simpler way to get the required raycast ray */ - ED_view3d_unproject(kcd->vc.region, s[0], s[1], 0.0f, view); + ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view); mul_m4_v3(kcd->ob_imat, view); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 9ef2cce875f..1ff576504ce 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -525,7 +525,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev float d_a[3], d_b[3]; float d_a_proj[2], d_b_proj[2]; - float preview_plane_proj[4][3]; + float preview_plane_proj[4][2]; const float y_axis_proj[2] = {0.0f, 1.0f}; mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]); @@ -534,7 +534,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev for (int i = 0; i < 4; i++) { float preview_plane_world_space[3]; mul_v3_m4v3(preview_plane_world_space, active_object->obmat, cd->preview_plane[i]); - ED_view3d_project(region, preview_plane_world_space, preview_plane_proj[i]); + ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]); } /* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index a87b5054efa..b9a3bc87e19 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1654,7 +1654,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x if (center_tot) { mul_v3_fl(center, 1.0f / center_tot); float center_proj[3]; - ED_view3d_project(xfd->vc.region, center, center_proj); + ED_view3d_project_v3(xfd->vc.region, center, center_proj); xfd->prev.depth = center_proj[2]; xfd->prev.is_depth_valid = true; } @@ -1890,7 +1890,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { xfd->prev.depth = depth_fl; xfd->prev.is_depth_valid = true; - if (ED_view3d_depth_unproject(region, event->mval, depth, location_world)) { + if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) { if (is_translate) { float normal[3]; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 5b545784e5b..97994b65f40 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -609,7 +609,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre } float win[3]; - ED_view3d_project(data->vc.region, co, win); + ED_view3d_project_v3(data->vc.region, co, win); if (win[2] - 0.00001f > depth) { return 0; diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 3829aeebbeb..7e111905883 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1039,7 +1039,7 @@ static void cursor_draw_point_screen_space(const uint gpuattr, float translation_vertex_cursor[3], location[3]; copy_v3_v3(location, true_location); mul_m4_v3(obmat, location); - ED_view3d_project(region, location, translation_vertex_cursor); + ED_view3d_project_v3(region, location, translation_vertex_cursor); /* Do not draw points behind the view. Z [near, far] is mapped to [-1, 1]. */ if (translation_vertex_cursor[2] <= 1.0f) { imm_draw_circle_fill_3d( diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b6ae6f8bee7..da34723eed4 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1239,10 +1239,9 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) })); const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3]; looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); BMIter iter; int i; @@ -1290,7 +1289,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) break; } BM_mesh_boolean( - bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); + bm, looptris, looptris_tot, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); } MEM_freeN(looptris); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index b093f07226e..59a5ad63f0e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -846,7 +846,7 @@ static int paint_space_stroke(bContext *C, while (length > 0.0f) { float spacing = paint_space_stroke_spacing_variable( C, scene, stroke, pressure, dpressure, length); - float mouse[3]; + float mouse[2]; if (length >= spacing) { if (use_scene_spacing) { @@ -856,7 +856,7 @@ static int paint_space_stroke(bContext *C, add_v3_v3v3(final_world_space_position, stroke->last_world_space_position, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; @@ -1240,7 +1240,7 @@ static void paint_line_strokes_spacing(bContext *C, mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final); add_v3_v3v3( final_world_space_position, world_space_position_old, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 2e1dd928f96..d6d54a1985d 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1854,7 +1854,7 @@ static void flip_v3(float v[3], const ePaintSymmetryFlags symm) flip_v3_v3(v, v, symm); } -static void flip_qt(float quat[3], const ePaintSymmetryFlags symm) +static void flip_qt(float quat[4], const ePaintSymmetryFlags symm) { flip_qt_qt(quat, quat, symm); } @@ -4196,7 +4196,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], } } -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 087cb6dd94a..e2ee4c9fed3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -278,7 +278,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 6b4366b2966..f11f1c01019 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -2753,11 +2753,10 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po #else /* Always supported through Accelerate framework BNNS on macOS. */ # ifndef __APPLE__ - if (!BLI_cpu_support_sse41()) -# endif - { + if (!BLI_cpu_support_sse41()) { uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); } +# endif #endif uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index ebd4c0090b4..53ddc818cd1 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -3053,7 +3053,7 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op) scene->r.efra = efra; } - WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME_RANGE, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index cfc11afce13..867b8e3d40a 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -807,7 +807,7 @@ static bool select_linked_internal(Scene *scene) bool changed = false; LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT) != 0) { + if ((seq->flag & SELECT) == 0) { continue; } /* Only get unselected neighbors. */ diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 967ad966320..8b6d0e9ee04 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -3666,8 +3666,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* convert border to 3d coordinates */ - if ((!ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) || - (!ED_view3d_unproject(region, rect.xmin, rect.ymin, depth_close, p_corner))) { + if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) || + (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) { return OPERATOR_CANCELLED; } @@ -3690,7 +3690,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) new_dist = rv3d->dist; /* convert the drawn rectangle into 3d space */ - if (depth_close != FLT_MAX && ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) { + if (depth_close != FLT_MAX && + ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) { negate_v3_v3(new_ofs, p); } else { diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index 298a2a7a824..07c3b6bd1d8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -154,10 +154,10 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int * Only pre-select a vertex when the cursor is really close to it. */ if (eve_test) { BMVert *vert = (BMVert *)eve_test; - float vert_p_co[3], vert_co[3]; + float vert_p_co[2], vert_co[3]; const float mval_f[2] = {UNPACK2(vc.mval)}; mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); - ED_view3d_project(vc.region, vert_co, vert_p_co); + ED_view3d_project_v2(vc.region, vert_co, vert_p_co); float len = len_v2v2(vert_p_co, mval_f); if (len < 35) { best.ele = (BMElem *)eve_test; diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 24d34e514c5..7547f8ee434 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -809,23 +809,30 @@ void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, /** * Convert between region relative coordinates (x,y) and depth component z and * a point in world space. */ -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]) +void ED_view3d_project_v3(const struct ARegion *region, const float world[3], float r_region_co[3]) { /* Viewport is set up to make coordinates relative to the region, not window. */ RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_3fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +} - GPU_matrix_project(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +void ED_view3d_project_v2(const struct ARegion *region, const float world[3], float r_region_co[2]) +{ + /* Viewport is set up to make coordinates relative to the region, not window. */ + RegionView3D *rv3d = region->regiondata; + const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_2fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); } -bool ED_view3d_unproject( +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]) { RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; const float region_co[3] = {regionx, regiony, regionz}; - return GPU_matrix_unproject(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); + return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index f96c17d7cff..8ae5d4a29e9 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -299,8 +299,8 @@ void ED_view3d_clipping_calc( float xs = (ELEM(val, 0, 3)) ? rect->xmin : rect->xmax; float ys = (ELEM(val, 0, 1)) ? rect->ymin : rect->ymax; - ED_view3d_unproject(region, xs, ys, 0.0, bb->vec[val]); - ED_view3d_unproject(region, xs, ys, 1.0, bb->vec[4 + val]); + ED_view3d_unproject_v3(region, xs, ys, 0.0, bb->vec[val]); + ED_view3d_unproject_v3(region, xs, ys, 1.0, bb->vec[4 + val]); } /* optionally transform to object space */ @@ -1057,7 +1057,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - if (ED_view3d_unproject(region, centx, centy, depth_close, mouse_worldloc)) { + if (ED_view3d_unproject_v3(region, centx, centy, depth_close, mouse_worldloc)) { return true; } } @@ -1091,7 +1091,7 @@ bool ED_view3d_autodist_simple(ARegion *region, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, mouse_worldloc); + return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc); } bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth) @@ -1716,7 +1716,7 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, ED_view3d_depth_read_cached(depths, mval_ofs, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_ofs, depth, coords[i])) { + if (ED_view3d_depth_unproject_v3(region, mval_ofs, depth, coords[i])) { depths_valid[i] = true; } } @@ -1751,14 +1751,14 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, return false; } -bool ED_view3d_depth_unproject(const ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]) +bool ED_view3d_depth_unproject_v3(const ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]) { float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, r_location_world); + return ED_view3d_unproject_v3(region, centx, centy, depth, r_location_world); } void ED_view3d_depth_tag_update(RegionView3D *rv3d) diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index aad6ae9e2ba..e073263f352 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -118,21 +118,27 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_prec const float proj[4][4], const int view[4]); -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_win[3]); - -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]); - -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, - const float win[3], - float r_world[3]); +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[3]); + +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[2]); + +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]); + +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, + const float win[3], + float r_world[3]); /* 2D Projection Matrix */ diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 569b51a407a..6eb9cb823d5 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -474,11 +474,11 @@ void GPU_matrix_look_at(float eyeX, GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ); } -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float win[3]) +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[3]) { float v[4]; @@ -494,6 +494,25 @@ void GPU_matrix_project(const float world[3], win[2] = (v[2] + 1) * 0.5f; } +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[2]) +{ + float v[4]; + + mul_v4_m4v3(v, model, world); + mul_m4_v4(proj, v); + + if (v[3] != 0.0f) { + mul_v2_fl(v, 1.0f / v[3]); + } + + win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f; + win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; +} + /** * The same result could be obtained as follows: * @@ -556,9 +575,9 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, return true; } -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, - const float win[3], - float r_world[3]) +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, + const float win[3], + float r_world[3]) { float in[3] = { (win[0] - precalc->view[0]) / precalc->view[2], @@ -569,18 +588,18 @@ void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc * mul_v3_m4v3(r_world, precalc->model_inverted, in); } -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]) +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]) { struct GPUMatrixUnproject_Precalc precalc; if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) { zero_v3(r_world); return false; } - GPU_matrix_unproject_with_precalc(&precalc, win, r_world); + GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world); return true; } diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 11ce77e3091..e1e6cc677ed 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -466,13 +466,6 @@ struct proxy_output_ctx { struct anim *anim; }; -// work around stupid swscaler 16 bytes alignment bug... - -static int round_up(int x, int mod) -{ - return x + ((mod - (x % mod)) % mod); -} - static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality) { @@ -499,13 +492,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c = avcodec_alloc_context3(NULL); rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = AV_CODEC_ID_H264; - rv->c->width = width; - rv->c->height = height; - rv->c->gop_size = 10; - rv->c->max_b_frames = 0; - /* Correct wrong default ffmpeg param which crash x264. */ - rv->c->qmin = 10; - rv->c->qmax = 51; rv->of->oformat->video_codec = rv->c->codec_id; rv->codec = avcodec_find_encoder(rv->c->codec_id); @@ -520,6 +506,13 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( return NULL; } + avcodec_get_context_defaults3(rv->c, rv->codec); + + rv->c->width = width; + rv->c->height = height; + rv->c->gop_size = 10; + rv->c->max_b_frames = 0; + if (rv->codec->pix_fmts) { rv->c->pix_fmt = rv->codec->pix_fmts[0]; } @@ -595,15 +588,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( if (st->codecpar->width != width || st->codecpar->height != height || st->codecpar->format != rv->c->pix_fmt) { rv->frame = av_frame_alloc(); - av_image_fill_arrays( - rv->frame->data, - rv->frame->linesize, - MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1), - "alloc proxy output frame"), - rv->c->pix_fmt, - round_up(width, 16), - height, - 1); + + av_image_fill_arrays(rv->frame->data, + rv->frame->linesize, + MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1), + "alloc proxy output frame"), + rv->c->pix_fmt, + width, + height, + 1); + + rv->frame->format = rv->c->pix_fmt; + rv->frame->width = width; + rv->frame->height = height; rv->sws_ctx = sws_getContext(st->codecpar->width, rv->orig_height, @@ -686,6 +683,9 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr packet->stream_index = ctx->st->index; av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(ctx->of, ctx->st, packet); +# endif int write_ret = av_interleaved_write_frame(ctx->of, packet); if (write_ret != 0) { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 58c94b6f369..0fa1d1a74ac 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -461,7 +461,6 @@ typedef struct bNodeTree { short is_updating; /** Generic temporary flag for recursion check (DFS/BFS). */ short done; - char _pad2[4]; /** Specific node type this tree is used for. */ int nodetype DNA_DEPRECATED; @@ -472,6 +471,8 @@ typedef struct bNodeTree { short render_quality; /** Tile size for compositor engine. */ int chunksize; + /** Execution mode to use for compositor engine. */ + int execution_mode; rctf viewer_border; @@ -545,6 +546,12 @@ typedef enum eNodeTreeUpdate { NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), } eNodeTreeUpdate; +/* tree->execution_mode */ +typedef enum eNodeTreeExecutionMode { + NTREE_EXECUTION_MODE_TILED = 0, + NTREE_EXECUTION_MODE_FULL_FRAME = 1, +} eNodeTreeExecutionMode; + /* socket value structs for input buttons * DEPRECATED now using ID properties */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 4b95dd41b30..f59f51ea28a 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -57,6 +57,7 @@ typedef struct StripElem { char name[256]; /** Ignore when zeroed. */ int orig_width, orig_height; + float orig_fps; } StripElem; typedef struct StripCrop { diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 1fed8e14bd6..61d2c04d98b 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -642,11 +642,12 @@ typedef struct UserDef_Experimental { * when the release cycle is not alpha. */ char use_new_hair_type; char use_new_point_cloud_type; + char use_full_frame_compositor; char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; char use_asset_browser; char use_override_templates; - char _pad[6]; + char _pad[5]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 150a455f1c7..948fef1b51e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6115,13 +6115,25 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id) path = ""; } - char id_esc[(sizeof(id->name) - 2) * 2]; + char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4]; + if (id->lib != NULL) { + int ofs = 0; + memcpy(lib_filepath_esc, ", \"", 3); + ofs += 3; + ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc)); + memcpy(lib_filepath_esc + ofs, "\"", 2); + } + else { + lib_filepath_esc[0] = '\0'; + } + char id_esc[(sizeof(id->name) - 2) * 2]; BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc)); - return BLI_sprintfN("bpy.data.%s[\"%s\"]%s%s", + return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s", BKE_idtype_idcode_to_name_plural(GS(id->name)), id_esc, + lib_filepath_esc, path[0] ? "." : "", path); } diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 206ebc2cb14..54f9a93d90a 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -650,7 +650,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, seq->strip->proxy->anim = NULL; } - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + SEQ_relations_invalidate_cache_raw(scene, seq); } else { SEQ_ALL_BEGIN (scene->ed, seq) { diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index abc96ddc820..e5e7564f578 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1376,7 +1376,7 @@ static void rna_def_charinfo(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); // RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this character"); RNA_def_property_int_funcs(prop, "rna_ChariInfo_material_index_get", "rna_ChariInfo_material_index_set", @@ -2068,7 +2068,7 @@ static void rna_def_curve_nurb(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this curve"); RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Curve_material_index_range"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 5f865f9d4cd..19ed5f960cf 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -1625,7 +1625,7 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) /* Material Index */ prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", "Index of material used in this stroke"); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this stroke"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Settings */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index f772f9b5573..d5a1047d287 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1823,7 +1823,7 @@ static void rna_def_mlooptri(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_int_funcs(prop, "rna_MeshLoopTriangle_material_index_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this triangle"); prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1933,7 +1933,7 @@ static void rna_def_mpolygon(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this polygon"); # if 0 RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MeshPoly_material_index_range"); # endif diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 11f5ff0441a..f5cbb694554 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -127,6 +127,20 @@ static const EnumPropertyItem node_chunksize_items[] = { }; #endif +static const EnumPropertyItem rna_enum_execution_mode_items[] = { + {NTREE_EXECUTION_MODE_TILED, + "TILED", + 0, + "Tiled", + "Compositing is tiled, having as priority to display first tiles as fast as possible"}, + {NTREE_EXECUTION_MODE_FULL_FRAME, + "FULL_FRAME", + 0, + "Full Frame", + "Composites full image result as fast as possible"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_mapping_type_items[] = { {NODE_MAPPING_TYPE_POINT, "POINT", 0, "Point", "Transform a point"}, {NODE_MAPPING_TYPE_TEXTURE, @@ -3973,7 +3987,7 @@ static void rna_NodeCryptomatte_layer_name_set(PointerRNA *ptr, int new_value) } } -static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UNUSED(C), +static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) @@ -3984,7 +3998,7 @@ static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UN EnumPropertyItem template = {0, "", 0, "", ""}; int totitem = 0; - ntreeCompositCryptomatteUpdateLayerNames(node); + ntreeCompositCryptomatteUpdateLayerNames(CTX_data_scene(C), node); int layer_index; LISTBASE_FOREACH_INDEX (CryptomatteLayer *, layer, &storage->runtime.layers, layer_index) { template.value = layer_index; @@ -4076,7 +4090,7 @@ static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value) static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr) { - ntreeCompositCryptomatteSyncFromAdd(ptr->data); + ntreeCompositCryptomatteSyncFromAdd(scene, ptr->data); rna_Node_update(bmain, scene, ptr); } @@ -9236,12 +9250,12 @@ static void def_geo_attribute_attribute_compare(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type A", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type B", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } @@ -11671,6 +11685,12 @@ static void rna_def_composite_nodetree(BlenderRNA *brna) RNA_def_struct_sdna(srna, "bNodeTree"); RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS); + prop = RNA_def_property(srna, "execution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "execution_mode"); + RNA_def_property_enum_items(prop, rna_enum_execution_mode_items); + RNA_def_property_ui_text(prop, "Execution Mode", "Set how compositing is executed"); + RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update"); + prop = RNA_def_property(srna, "render_quality", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "render_quality"); RNA_def_property_enum_items(prop, node_quality_items); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 9ba92431723..8fbad449cf6 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1346,6 +1346,11 @@ static void rna_def_strip_element(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "orig_height"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Orig Height", "Original image height"); + + prop = RNA_def_property(srna, "orig_fps", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "orig_fps"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second"); } static void rna_def_strip_crop(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index d744f67c6f6..0af2572a4bd 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5466,7 +5466,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXIES); RNA_def_property_ui_text( prop, "Use Proxies", "Use optimized files for faster scrubbing when available"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache"); /* grease pencil */ prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index bacd3943141..6005ec96255 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6276,6 +6276,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "New Point Cloud Type", "Enable the new point cloud type in the ui"); + prop = RNA_def_property(srna, "use_full_frame_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_full_frame_compositor", 1); + RNA_def_property_ui_text(prop, + "Full Frame Compositor", + "Enable compositor full frame execution mode option (no tiling, " + "reduces execution time and memory usage)"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1); RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui"); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 91327b97fe4..0138dd0c3ad 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -195,7 +195,11 @@ endif() if(WITH_TBB) add_definitions(-DWITH_TBB) - + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} ) diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 9b8782737c3..4b9b24e4e47 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -283,11 +283,10 @@ static void BMD_mesh_intersection(BMesh *bm, /* main bmesh intersection setup */ /* create tessface & intersect */ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3] = (BMLoop * (*)[3]) MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); /* postpone this until after tessellating * so we can use the original normals before the vertex are moved */ @@ -364,7 +363,7 @@ static void BMD_mesh_intersection(BMesh *bm, BM_mesh_intersect(bm, looptris, - tottri, + looptris_tot, bm_face_isect_pair, nullptr, false, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index d2c011a21d3..bfd4cd81803 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -184,13 +184,17 @@ static void deformVerts(ModifierData *md, surmd->cfra = cfra; - surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); + const bool has_poly = surmd->mesh->totpoly > 0; + const bool has_edge = surmd->mesh->totedge > 0; + if (has_poly || has_edge) { + surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); - if (surmd->mesh->totpoly) { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); - } - else { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + if (has_poly) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); + } + else if (has_edge) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + } } } } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9d21ff19f46..24085b31fc3 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -178,6 +178,7 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc + geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d2a702c30a6..eadfed26be1 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -66,6 +66,7 @@ void register_node_type_geo_mesh_primitive_grid(void); void register_node_type_geo_mesh_primitive_ico_sphere(void); void register_node_type_geo_mesh_primitive_line(void); void register_node_type_geo_mesh_primitive_uv_sphere(void); +void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index ce1813fdac3..ef5f25e7b57 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -305,6 +305,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index d9b36924516..dca6dc59ca2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -40,61 +40,74 @@ /** \name Cryptomatte * \{ */ +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render( + const bNode &node, const bool use_meta_data) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + + Scene *scene = (Scene *)node.id; + if (!scene) { + return session; + } + BLI_assert(GS(scene->id.name) == ID_SCE); + + if (use_meta_data) { + Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; + RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + if (render) { + RE_ReleaseResult(render); + } + } + + if (session == nullptr) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_scene(scene)); + } + return session; +} + +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_image( + const Scene &scene, const bNode &node) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + Image *image = (Image *)node.id; + if (!image) { + return session; + } + BLI_assert(GS(image->id.name) == ID_IM); + + NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage); + ImageUser *iuser = &node_cryptomatte->iuser; + BKE_image_user_frame_calc(image, iuser, scene.r.cfra); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); + RenderResult *render_result = image->rr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + BKE_image_release_ibuf(image, ibuf, nullptr); + return session; +} static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node( - const bNode &node, const int frame_number, const bool use_meta_data) + const Scene &scene, const bNode &node, const bool use_meta_data) { blender::bke::cryptomatte::CryptomatteSessionPtr session; if (node.type != CMP_NODE_CRYPTOMATTE) { return session; } - NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage); switch (node.custom1) { case CMP_CRYPTOMATTE_SRC_RENDER: { - Scene *scene = (Scene *)node.id; - if (!scene) { - return session; - } - BLI_assert(GS(scene->id.name) == ID_SCE); - - if (use_meta_data) { - Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; - RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - if (render) { - RE_ReleaseResult(render); - } - } - - if (session == nullptr) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_scene(scene)); - } - - break; + return cryptomatte_init_from_node_render(node, use_meta_data); } case CMP_CRYPTOMATTE_SRC_IMAGE: { - Image *image = (Image *)node.id; - if (!image) { - break; - } - BLI_assert(GS(image->id.name) == ID_IM); - - ImageUser *iuser = &node_cryptomatte->iuser; - BKE_image_user_frame_calc(image, iuser, frame_number); - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); - RenderResult *render_result = image->rr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - BKE_image_release_ibuf(image, ibuf, nullptr); - break; + return cryptomatte_init_from_node_image(scene, node); } } return session; @@ -111,7 +124,10 @@ static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encode return nullptr; } -static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, float encoded_hash) +static void cryptomatte_add(const Scene &scene, + bNode &node, + NodeCryptomatte &node_cryptomatte, + float encoded_hash) { /* Check if entry already exist. */ if (cryptomatte_find(node_cryptomatte, encoded_hash)) { @@ -121,9 +137,8 @@ static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, floa CryptomatteEntry *entry = static_cast<CryptomatteEntry *>( MEM_callocN(sizeof(CryptomatteEntry), __func__)); entry->encoded_hash = encoded_hash; - /* TODO(jbakker): Get current frame from scene. */ blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - node, 0, true); + scene, node, true); if (session) { BKE_cryptomatte_find_name(session.get(), encoded_hash, entry->name, sizeof(entry->name)); } @@ -151,12 +166,12 @@ static bNodeSocketTemplate cmp_node_cryptomatte_out[] = { {-1, ""}, }; -void ntreeCompositCryptomatteSyncFromAdd(bNode *node) +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node) { BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY)); NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage); if (n->runtime.add[0] != 0.0f) { - cryptomatte_add(*node, *n, n->runtime.add[0]); + cryptomatte_add(*scene, *node, *n, n->runtime.add[0]); zero_v3(n->runtime.add); } } @@ -170,14 +185,14 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node) zero_v3(n->runtime.remove); } } -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage); BLI_freelistN(&n->runtime.layers); blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); if (session) { for (blender::StringRef layer_name : @@ -190,12 +205,15 @@ void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) } } -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len) +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *node_cryptomatte = (NodeCryptomatte *)node->storage; blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); std::string first_layer_name; if (session) { @@ -216,10 +234,10 @@ void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size BLI_strncpy(r_prefix, cstr, prefix_len); } -CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node) +CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node) { blender::bke::cryptomatte::CryptomatteSessionPtr session_ptr = cryptomatte_init_from_node( - *node, 0, true); + *scene, *node, true); return session_ptr.release(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc new file mode 100644 index 00000000000..b852f929b5f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -0,0 +1,318 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::Array; + +static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +namespace blender::nodes { + +template<typename T> +static void copy_attribute_to_points(const VArray<T> &source_data, + Span<int> map, + MutableSpan<T> dest_data) +{ + for (const int point_index : map.index_range()) { + const int vert_index = map[point_index]; + dest_data[point_index] = source_data[vert_index]; + } +} + +static void copy_attributes_to_points(CurveEval &curve, + const MeshComponent &mesh_component, + Span<Vector<int>> point_to_vert_maps) +{ + MutableSpan<SplinePtr> splines = curve.splines(); + Set<std::string> source_attribute_names = mesh_component.attribute_names(); + + /* Copy builtin control point attributes. */ + if (source_attribute_names.contains_as("tilt")) { + const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( + "tilt", ATTR_DOMAIN_POINT, 0.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points<float>( + *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); + } + }); + source_attribute_names.remove_contained_as("tilt"); + } + if (source_attribute_names.contains_as("radius")) { + const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( + "radius", ATTR_DOMAIN_POINT, 1.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points<float>( + *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); + } + }); + source_attribute_names.remove_contained_as("radius"); + } + + /* Don't copy other builtin control point attributes. */ + source_attribute_names.remove_as("position"); + + /* Copy dynamic control point attributes. */ + for (const StringRef name : source_attribute_names) { + const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name, + ATTR_DOMAIN_POINT); + /* Some attributes might not exist if they were builtin attribute on domains that don't + * have any elements, i.e. a face attribute on the output of the line primitive node. */ + if (!mesh_attribute) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); + + parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + /* Create attribute on the spline points. */ + splines[i]->attributes.create(name, data_type); + std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name); + BLI_assert(spline_attribute); + + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points<T>( + mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>()); + }); + } + }); + } + + curve.assert_valid_point_attributes(); +} + +struct CurveFromEdgesOutput { + std::unique_ptr<CurveEval> curve; + Vector<Vector<int>> point_to_vert_maps; +}; + +static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + Vector<Vector<int>> point_to_vert_maps; + + /* Compute the number of edges connecting to each vertex. */ + Array<int> neighbor_count(verts.size(), 0); + for (const std::pair<int, int> &edge : edges) { + neighbor_count[edge.first]++; + neighbor_count[edge.second]++; + } + + /* Compute an offset into the array of neighbor edges based on the counts. */ + Array<int> neighbor_offsets(verts.size()); + int start = 0; + for (const int i : verts.index_range()) { + neighbor_offsets[i] = start; + start += neighbor_count[i]; + } + + /* Use as an index into the "neighbor group" for each vertex. */ + Array<int> used_slots(verts.size(), 0); + /* Calculate the indices of each vertex's neighboring edges. */ + Array<int> neighbors(edges.size() * 2); + for (const int i : edges.index_range()) { + const int v1 = edges[i].first; + const int v2 = edges[i].second; + neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; + neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; + used_slots[v1]++; + used_slots[v2]++; + } + + /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ + Array<int> unused_edges = std::move(used_slots); + + for (const int start_vert : verts.index_range()) { + /* The vertex will be part of a cyclic spline. */ + if (neighbor_count[start_vert] == 2) { + continue; + } + + /* The vertex has no connected edges, or they were already used. */ + if (unused_edges[start_vert] == 0) { + continue; + } + + for (const int i : IndexRange(neighbor_count[start_vert])) { + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert] + i]; + + if (unused_edges[next_vert] == 0) { + continue; + } + + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Vector<int> point_to_vert_map; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we read a vertex with more than two connected edges. */ + while (true) { + int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + if (neighbor_count[current_vert] != 2) { + break; + } + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + } + + /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + for (const int start_vert : verts.index_range()) { + if (unused_edges[start_vert] != 2) { + continue; + } + + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert]]; + + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Vector<int> point_to_vert_map; + spline->set_cyclic(true); + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we loop back to the start vertex. */ + while (next_vert != start_vert) { + const int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + + curve->attributes.reallocate(curve->splines().size()); + return {std::move(curve), std::move(point_to_vert_maps)}; +} + +/** + * Get a separate array of the indices for edges in a selection (a boolean attribute). + * This helps to make the above algorithm simpler by removing the need to check for selection + * in many places. + */ +static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params, + const MeshComponent &component) +{ + const Mesh &mesh = *component.get_for_read(); + const std::string selection_name = params.extract_input<std::string>("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector<std::pair<int, int>> selected_edges; + for (const int i : IndexRange(mesh.totedge)) { + if (selection[i]) { + selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); + } + } + + return selected_edges; +} + +static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Curve", GeometrySet()); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *component.get_for_read(); + Span<MVert> verts = Span{mesh.mvert, mesh.totvert}; + Span<MEdge> edges = Span{mesh.medge, mesh.totedge}; + if (edges.size() == 0) { + params.set_output("Curve", GeometrySet()); + return; + } + + Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component); + + CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); + copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); + + params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_to_curve() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 049ba5d3143..742fafba9e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -22,24 +22,24 @@ static bNodeSocketTemplate geo_node_switch_in[] = { {SOCK_BOOLEAN, N_("Switch")}, - {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_BOOLEAN, N_("A")}, - {SOCK_BOOLEAN, N_("B")}, - {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_STRING, N_("A")}, - {SOCK_STRING, N_("B")}, - {SOCK_GEOMETRY, N_("A")}, - {SOCK_GEOMETRY, N_("B")}, - {SOCK_OBJECT, N_("A")}, - {SOCK_OBJECT, N_("B")}, - {SOCK_COLLECTION, N_("A")}, - {SOCK_COLLECTION, N_("B")}, + {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_BOOLEAN, N_("False")}, + {SOCK_BOOLEAN, N_("True")}, + {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_STRING, N_("False")}, + {SOCK_STRING, N_("True")}, + {SOCK_GEOMETRY, N_("False")}, + {SOCK_GEOMETRY, N_("True")}, + {SOCK_OBJECT, N_("False")}, + {SOCK_OBJECT, N_("True")}, + {SOCK_COLLECTION, N_("False")}, + {SOCK_COLLECTION, N_("True")}, {-1, ""}, }; @@ -64,7 +64,7 @@ static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), Pointe static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) { NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); - data->input_type = SOCK_FLOAT; + data->input_type = SOCK_GEOMETRY; node->storage = data; } @@ -91,8 +91,8 @@ static void output_input(GeoNodeExecParams ¶ms, const StringRef input_suffix, const StringRef output_identifier) { - const std::string name_a = "A" + input_suffix; - const std::string name_b = "B" + input_suffix; + const std::string name_a = "False" + input_suffix; + const std::string name_b = "True" + input_suffix; if (input) { params.set_input_unused(name_a); if (params.lazy_require_input(name_b)) { @@ -165,7 +165,7 @@ void register_node_type_geo_switch() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); node_type_init(&ntype, geo_node_switch_init); node_type_update(&ntype, blender::nodes::geo_node_switch_update); diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index f1a8d450ea5..598640f8f68 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1385,7 +1385,6 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) BMesh *bm; int looptris_tot; - int tottri; BMLoop *(*looptris)[3]; PyObject *ret; @@ -1398,10 +1397,10 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); looptris = PyMem_MALLOC(sizeof(*looptris) * looptris_tot); - BM_mesh_calc_tessellation(bm, looptris, &tottri); + BM_mesh_calc_tessellation(bm, looptris); - ret = PyList_New(tottri); - for (i = 0; i < tottri; i++) { + ret = PyList_New(looptris_tot); + for (i = 0; i < looptris_tot; i++) { PyList_SET_ITEM(ret, i, BPy_BMLoop_Array_As_Tuple(bm, looptris[i], 3)); } diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 927ec11c376..4de6063098b 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -82,7 +82,10 @@ static PyTypeObject BlenderAppType; static PyStructSequence_Field app_info_fields[] = { {"version", "The Blender version as a tuple of 3 numbers. eg. (2, 83, 1)"}, - {"version_file", "The blend file version, compatible with ``bpy.data.version``"}, + {"version_file", + "The Blender version, as a tuple, last used to save a .blend file, compatible with " + "``bpy.data.version``. This value should be used for handling compatibility changes between " + "Blender versions"}, {"version_string", "The Blender version formatted as a string"}, {"version_cycle", "The release status of this build alpha/beta/rc/release"}, {"version_char", "Deprecated, always an empty string"}, diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 1acbcc006ca..79ed9e68420 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -961,8 +961,6 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb /* Get data for tessellation */ { - int tris_len_dummy; - coords_len = (uint)bm->totvert; tris_len = (uint)poly_to_tri_count(bm->totface, bm->totloop); @@ -971,8 +969,7 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb looptris = MEM_mallocN(sizeof(*looptris) * (size_t)tris_len, __func__); - BM_mesh_calc_tessellation(bm, looptris, &tris_len_dummy); - BLI_assert(tris_len_dummy == (int)tris_len); + BM_mesh_calc_tessellation(bm, looptris); } { diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index a0c95c1c197..5ccf2a027b0 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -518,7 +518,7 @@ static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntr static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); const size_t num_items_read = fread(header, sizeof(*header), 1, file); if (num_items_read < 1) { BLI_assert(!"unable to read disk cache header"); @@ -540,7 +540,7 @@ static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); return fwrite(header, sizeof(*header), 1, file); } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index d881c90a1e0..8ed769880a4 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1237,6 +1237,12 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, } if (*r_is_proxy_image == false) { + if (sanim && sanim->anim) { + short fps_denom; + float fps_num; + IMB_anim_get_fps(sanim->anim, &fps_denom, &fps_num, true); + seq->strip->stripdata->orig_fps = fps_denom / fps_num; + } seq->strip->stripdata->orig_width = ibuf->x; seq->strip->stripdata->orig_height = ibuf->y; } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 5ec2269b993..64671aeb265 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -548,15 +548,25 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ + float video_fps = 0.0f; + if (anim_arr[0] != NULL) { seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); IMB_anim_load_metadata(anim_arr[0]); + short fps_denom; + float fps_num; + + IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); + + video_fps = fps_denom / fps_num; + /* Adjust scene's frame rate settings to match. */ if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { - IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true); + scene->r.frs_sec = fps_denom; + scene->r.frs_sec_base = fps_num; } /* Set initial scale based on load_data->fit_method. */ @@ -577,6 +587,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem"); strip->stripdata->orig_width = orig_width; strip->stripdata->orig_height = orig_height; + strip->stripdata->orig_fps = video_fps; BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name)); seq_add_set_view_transform(scene, seq, load_data); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 611a9cba000..2ffa04bd8ae 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -620,7 +620,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, struct GPUMatrixUnproject_Precalc unproj_precalc; GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin); uint *buf_iter = buffer; int hit_found = -1; @@ -631,7 +631,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8]; float co_3d[3]; co_screen[2] = int_as_float(buf_iter[1]); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d); float select_bias = gz->select_bias; if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { select_bias *= gz->scale_final; diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 6a768106d9e..92cc4ae297a 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -119,6 +119,10 @@ if(WITH_GMP) add_definitions(-DWITH_GMP) endif() +if(WITH_OPENCOLORIO) + add_definitions(-DWITH_OCIO) +endif() + # Setup the exe sources and buildinfo set(SRC creator.c diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 36fdaef507b..8b1ac05f086 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -674,6 +674,9 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo printf(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n"); printf(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n"); printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n"); +# ifdef WITH_OCIO + printf(" $OCIO Path to override the OpenColorIO config file.\n"); +# endif # ifdef WIN32 printf(" $TEMP Store temporary files here.\n"); # else diff --git a/source/tools b/source/tools -Subproject f99d29ae3e6ad44d45d79309454c45f8088781a +Subproject 01f51a0e551ab730f0934dc6488613690ac4bf8 |