Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
414b8b7628
|
@ -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")
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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]);
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -281,6 +281,11 @@ class CompositorContext {
|
|||
{
|
||||
return m_rd->size * 0.01f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active execution model.
|
||||
*/
|
||||
eExecutionModel get_execution_model() const;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "COM_SharedOperationBuffers.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "COM_NodeOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
SharedOperationBuffers::BufferData::BufferData()
|
||||
: buffer(nullptr), registered_reads(0), received_reads(0)
|
||||
{
|
||||
}
|
||||
|
||||
SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op)
|
||||
{
|
||||
return buffers_.lookup_or_add_cb(op, []() { return BufferData(); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether given operation area to render is already registered.
|
||||
* TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial
|
||||
* overlapping, etc. Leading to more rendering than necessary.
|
||||
*/
|
||||
bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render)
|
||||
{
|
||||
BufferData &buf_data = get_buffer_data(op);
|
||||
for (rcti ®_rect : buf_data.render_areas) {
|
||||
if (BLI_rcti_inside_rcti(®_rect, &area_to_render)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an operation area to render.
|
||||
*/
|
||||
void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render)
|
||||
{
|
||||
get_buffer_data(op).render_areas.append(area_to_render);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether given operation has any registered reads (other operation registered it depends on given
|
||||
* operation).
|
||||
*/
|
||||
bool SharedOperationBuffers::has_registered_reads(NodeOperation *op)
|
||||
{
|
||||
return get_buffer_data(op).registered_reads > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an operation read (other operation depends on given operation).
|
||||
*/
|
||||
void SharedOperationBuffers::register_read(NodeOperation *read_op)
|
||||
{
|
||||
get_buffer_data(read_op).registered_reads++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered areas given operation needs to render.
|
||||
*/
|
||||
blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
|
||||
{
|
||||
return get_buffer_data(op).render_areas.as_span();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this operation buffer has already been rendered.
|
||||
*/
|
||||
bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op)
|
||||
{
|
||||
return get_buffer_data(op).buffer != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores given operation rendered buffer.
|
||||
*/
|
||||
void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op,
|
||||
std::unique_ptr<MemoryBuffer> buffer)
|
||||
{
|
||||
BufferData &buf_data = get_buffer_data(op);
|
||||
BLI_assert(buf_data.received_reads == 0);
|
||||
BLI_assert(buf_data.buffer == nullptr);
|
||||
buf_data.buffer = std::move(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get given operation rendered buffer.
|
||||
*/
|
||||
MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op)
|
||||
{
|
||||
BLI_assert(is_operation_rendered(op));
|
||||
return get_buffer_data(op).buffer.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an operation has finished reading given operation. If all given operation dependencies
|
||||
* have finished its buffer will be disposed.
|
||||
*/
|
||||
void SharedOperationBuffers::read_finished(NodeOperation *read_op)
|
||||
{
|
||||
BufferData &buf_data = get_buffer_data(read_op);
|
||||
buf_data.received_reads++;
|
||||
BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads);
|
||||
if (buf_data.received_reads == buf_data.registered_reads) {
|
||||
/* Dispose buffer. */
|
||||
buf_data.buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -42,6 +42,7 @@ struct SpaceImage;
|
|||
struct ToolSettings;
|
||||
struct ViewLayer;
|
||||
struct bNode;
|
||||
struct bNodeTree;
|
||||
struct wmKeyConfig;
|
||||
|
||||
/* uvedit_ops.c */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue