Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2021-06-01 12:52:07 +02:00
commit 414b8b7628
116 changed files with 8942 additions and 6911 deletions

View File

@ -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")

View File

@ -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 */

View File

@ -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) {

View File

@ -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,

View File

@ -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;
}

View File

@ -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 {

View File

@ -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();

View File

@ -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;
}

View File

@ -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;

View File

@ -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()

View File

@ -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")

View File

@ -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):

View File

@ -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"),
),
)

View File

@ -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>

View File

@ -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;

View File

@ -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. */

View File

@ -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);
/** \} */

View File

@ -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)

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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

View File

@ -428,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,
@ -468,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.
@ -480,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);
@ -501,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;
}
}
@ -522,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;
@ -587,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;
@ -613,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;
@ -665,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;
@ -683,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);
@ -941,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);
@ -1284,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
@ -1319,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);
@ -1403,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;
@ -1557,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);

View File

@ -518,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,
@ -548,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;
}
@ -562,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;
@ -588,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 {
@ -627,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) {
@ -649,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) {
@ -714,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);
@ -777,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;
@ -806,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,
@ -852,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);
@ -1636,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");
}

View File

@ -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;

View File

@ -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;

View File

@ -1695,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) {
@ -1705,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;
@ -1717,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;
@ -1730,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;
@ -1756,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 {
@ -1837,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;
@ -1861,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) {

View File

@ -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)
static inline mpq3 tti_interp(
const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf)
{
mpq3 ab = a - b;
mpq_class den = mpq3::dot(ab, n);
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) {

View File

@ -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) {

View File

@ -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

View File

@ -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"

View File

@ -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);
}
/** \} */

View File

@ -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]);

View File

@ -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);
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
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);
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

View File

@ -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

View File

@ -281,6 +281,11 @@ class CompositorContext {
{
return m_rd->size * 0.01f;
}
/**
* Get active execution model.
*/
eExecutionModel get_execution_model() const;
};
} // namespace blender::compositor

View File

@ -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");

View File

@ -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);
};

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,12 +116,20 @@ 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
*/
@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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 &reg_rect : buf_data.render_areas) {
if (BLI_rcti_inside_rcti(&reg_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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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(

View File

@ -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());

View File

@ -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],

View File

@ -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;

View File

@ -51,6 +51,8 @@ set(INC
set(SRC
intern/draw_cache.c
intern/draw_cache_extract_mesh_extractors.c
intern/draw_cache_extract_mesh_render_data.c
intern/draw_cache_extract_mesh.c
intern/draw_cache_impl_curve.cc
intern/draw_cache_impl_displist.c

View File

@ -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;
@ -80,12 +84,6 @@ typedef enum eMRDataType {
MR_DATA_TAN_LOOP_NOR = 1 << 4,
} eMRDataType;
typedef enum eMRExtractType {
MR_EXTRACT_BMESH,
MR_EXTRACT_MAPPED,
MR_EXTRACT_MESH,
} eMRExtractType;
BLI_INLINE int mesh_render_mat_len_get(Mesh *me)
{
/* In edit mode, the displayed mesh is stored in the edit-mesh. */
@ -286,7 +284,7 @@ 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,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,509 @@
/*
* 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"
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_flag;
/** 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 DRW_MeshCDMask *cd_used,
const ToolSettings *ts,
const eMRIterType iter_type);
void mesh_render_data_free(MeshRenderData *mr);
void mesh_render_data_update_normals(MeshRenderData *mr,
const eMRIterType iter_type,
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;

View File

@ -0,0 +1,374 @@
/*
* 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 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.
*/
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 DRW_MeshCDMask *UNUSED(cd_used),
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);
}
/** \} */

View File

@ -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;

View File

@ -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) {

View File

@ -1559,7 +1559,7 @@ 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,
@ -1578,7 +1578,7 @@ 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,
@ -1596,7 +1596,7 @@ 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,

View File

@ -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. */

View File

@ -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))
{

View File

@ -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);

View File

@ -42,6 +42,7 @@ struct SpaceImage;
struct ToolSettings;
struct ViewLayer;
struct bNode;
struct bNodeTree;
struct wmKeyConfig;
/* uvedit_ops.c */

View File

@ -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 */

View File

@ -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;
}
/**

View File

@ -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);

View File

@ -530,7 +530,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]);
@ -539,7 +539,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. */

View File

@ -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];

View File

@ -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;

View File

@ -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(

View File

@ -1281,10 +1281,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;
@ -1332,7 +1331,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);

View File

@ -852,7 +852,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) {
@ -862,7 +862,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;
@ -1246,7 +1246,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;

View File

@ -1900,7 +1900,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);
}
@ -4703,7 +4703,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])

View File

@ -284,7 +284,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]);

View File

@ -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;
}

View File

@ -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 {

View File

@ -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;

View File

@ -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(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
GPU_matrix_project_3fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
}
bool ED_view3d_unproject(
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_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);
}
/** \} */

View File

@ -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)

View File

@ -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]);
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]);
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_project_2fv(const float world[3],
const float model[4][4],
const float proj[4][4],
const int view[4],
float r_win[2]);
void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
const float win[3],
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]);
void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
const float win[3],
float r_world[3]);
/* 2D Projection Matrix */

View File

@ -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;
}

View File

@ -494,13 +494,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);
@ -515,6 +508,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];
}

View File

@ -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
*/

Some files were not shown because too many files have changed in this diff Show More