diff options
author | Campbell Barton <ideasman42@gmail.com> | 2017-11-04 13:45:52 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2017-11-04 13:45:52 +0300 |
commit | d4fe083b356d7967ce6d9716cd0e6ba3039d1655 (patch) | |
tree | 49eb05d3589f180c1056691efa5dd7c67b28dca8 | |
parent | 10024603ee904a4201ae35b87643012543274d0f (diff) | |
parent | 01a3c9560938c98793a2d2a0b61e574ed5c27e4a (diff) |
Merge branch 'master' into blender2.8
-rw-r--r-- | intern/cycles/device/device.cpp | 29 | ||||
-rw-r--r-- | intern/cycles/device/device.h | 18 | ||||
-rw-r--r-- | intern/cycles/device/device_cpu.cpp | 1 | ||||
-rw-r--r-- | intern/cycles/device/device_cuda.cpp | 363 | ||||
-rw-r--r-- | intern/cycles/render/image.cpp | 26 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_region_tooltip.c | 356 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_widgets.c | 19 |
7 files changed, 458 insertions, 354 deletions
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 923b9bd45bf..6a666eb946e 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -507,39 +507,48 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo>& subdevices, int th info.description = "Multi Device"; info.num = 0; - info.has_bindless_textures = true; + info.has_fermi_limits = false; + info.has_half_images = true; info.has_volume_decoupled = true; info.has_qbvh = true; info.has_osl = true; foreach(const DeviceInfo &device, subdevices) { - info.has_bindless_textures &= device.has_bindless_textures; - info.has_volume_decoupled &= device.has_volume_decoupled; - info.has_qbvh &= device.has_qbvh; - info.has_osl &= device.has_osl; - + /* Ensure CPU device does not slow down GPU. */ if(device.type == DEVICE_CPU && subdevices.size() > 1) { if(background) { int orig_cpu_threads = (threads)? threads: system_cpu_thread_count(); int cpu_threads = max(orig_cpu_threads - (subdevices.size() - 1), 0); + VLOG(1) << "CPU render threads reduced from " + << orig_cpu_threads << " to " << cpu_threads + << ", to dedicate to GPU."; + if(cpu_threads >= 1) { DeviceInfo cpu_device = device; cpu_device.cpu_threads = cpu_threads; info.multi_devices.push_back(cpu_device); } - - VLOG(1) << "CPU render threads reduced from " - << orig_cpu_threads << " to " << cpu_threads - << ", to dedicate to GPU."; + else { + continue; + } } else { VLOG(1) << "CPU render threads disabled for interactive render."; + continue; } } else { info.multi_devices.push_back(device); } + + /* Accumulate device info. */ + info.has_fermi_limits = info.has_fermi_limits || + device.has_fermi_limits; + info.has_half_images &= device.has_half_images; + info.has_volume_decoupled &= device.has_volume_decoupled; + info.has_qbvh &= device.has_qbvh; + info.has_osl &= device.has_osl; } return info; diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index c79f086fc2d..17d9abb4a33 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -52,13 +52,14 @@ public: string description; string id; /* used for user preferences, should stay fixed with changing hardware config */ int num; - bool display_device; - bool advanced_shading; - bool has_bindless_textures; /* flag for GPU and Multi device */ - bool has_volume_decoupled; - bool has_qbvh; - bool has_osl; - bool use_split_kernel; /* Denotes if the device is going to run cycles using split-kernel */ + bool display_device; /* GPU is used as a display device. */ + bool advanced_shading; /* Supports full shading system. */ + bool has_fermi_limits; /* Fixed number of textures limit. */ + bool has_half_images; /* Support half-float textures. */ + bool has_volume_decoupled; /* Decoupled volume shading. */ + bool has_qbvh; /* Supports both BVH2 and BVH4 raytracing. */ + bool has_osl; /* Support Open Shading Language. */ + bool use_split_kernel; /* Use split or mega kernel. */ int cpu_threads; vector<DeviceInfo> multi_devices; @@ -70,7 +71,8 @@ public: cpu_threads = 0; display_device = false; advanced_shading = true; - has_bindless_textures = false; + has_fermi_limits = false; + has_half_images = false; has_volume_decoupled = false; has_qbvh = false; has_osl = false; diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index 32ab18fe164..0c0e6af7eb4 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -1047,6 +1047,7 @@ void device_cpu_info(vector<DeviceInfo>& devices) info.has_qbvh = system_cpu_support_sse2(); info.has_volume_decoupled = true; info.has_osl = true; + info.has_half_images = true; devices.insert(devices.begin(), info); } diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp index c742e91c561..62d8a92a024 100644 --- a/intern/cycles/device/device_cuda.cpp +++ b/intern/cycles/device/device_cuda.cpp @@ -274,7 +274,7 @@ public: delete split_kernel; - if(info.has_bindless_textures) { + if(!info.has_fermi_limits) { texture_info.free(); } @@ -547,13 +547,13 @@ public: void load_texture_info() { - if(info.has_bindless_textures && need_texture_info) { + if(!info.has_fermi_limits && need_texture_info) { texture_info.copy_to_device(); need_texture_info = false; } } - void generic_alloc(device_memory& mem) + void generic_alloc(device_memory& mem, size_t padding = 0) { CUDAContextScope scope(this); @@ -565,7 +565,7 @@ public: CUdeviceptr device_pointer; size_t size = mem.memory_size(); - cuda_assert(cuMemAlloc(&device_pointer, size)); + cuda_assert(cuMemAlloc(&device_pointer, size + padding)); mem.device_pointer = (device_ptr)device_pointer; mem.device_size = size; stats.mem_alloc(size); @@ -701,7 +701,7 @@ public: << string_human_readable_size(mem.memory_size()) << ")"; /* Check if we are on sm_30 or above, for bindless textures. */ - bool has_bindless_textures = info.has_bindless_textures; + bool has_fermi_limits = info.has_fermi_limits; /* General variables for both architectures */ string bind_name = mem.name; @@ -732,30 +732,8 @@ public: filter_mode = CU_TR_FILTER_MODE_LINEAR; } - /* General variables for Fermi */ - CUtexref texref = NULL; - - if(!has_bindless_textures && mem.interpolation != INTERPOLATION_NONE) { - if(mem.data_depth > 1) { - /* Kernel uses different bind names for 2d and 3d float textures, - * so we have to adjust couple of things here. - */ - vector<string> tokens; - string_split(tokens, mem.name, "_"); - bind_name = string_printf("__tex_image_%s_3d_%s", - tokens[2].c_str(), - tokens[3].c_str()); - } - - cuda_assert(cuModuleGetTexRef(&texref, cuModule, bind_name.c_str())); - - if(!texref) { - return; - } - } - + /* Data Storage */ if(mem.interpolation == INTERPOLATION_NONE) { - /* Data Storage */ generic_alloc(mem); generic_copy_to(mem); @@ -774,179 +752,243 @@ public: uint32_t ptr = (uint32_t)mem.device_pointer; cuda_assert(cuMemcpyHtoD(cumem, (void*)&ptr, cubytes)); } + + tex_interp_map[mem.device_pointer] = false; + return; } - else { - /* Texture Storage */ - CUarray handle = NULL; - - CUarray_format_enum format; - switch(mem.data_type) { - case TYPE_UCHAR: format = CU_AD_FORMAT_UNSIGNED_INT8; break; - case TYPE_UINT: format = CU_AD_FORMAT_UNSIGNED_INT32; break; - case TYPE_INT: format = CU_AD_FORMAT_SIGNED_INT32; break; - case TYPE_FLOAT: format = CU_AD_FORMAT_FLOAT; break; - case TYPE_HALF: format = CU_AD_FORMAT_HALF; break; - default: assert(0); return; - } + /* Image Texture Storage */ + CUtexref texref = NULL; + + if(has_fermi_limits) { if(mem.data_depth > 1) { - CUDA_ARRAY3D_DESCRIPTOR desc; + /* Kernel uses different bind names for 2d and 3d float textures, + * so we have to adjust couple of things here. + */ + vector<string> tokens; + string_split(tokens, mem.name, "_"); + bind_name = string_printf("__tex_image_%s_3d_%s", + tokens[2].c_str(), + tokens[3].c_str()); + } - desc.Width = mem.data_width; - desc.Height = mem.data_height; - desc.Depth = mem.data_depth; - desc.Format = format; - desc.NumChannels = mem.data_elements; - desc.Flags = 0; + cuda_assert(cuModuleGetTexRef(&texref, cuModule, bind_name.c_str())); - cuda_assert(cuArray3DCreate(&handle, &desc)); + if(!texref) { + return; } - else { - CUDA_ARRAY_DESCRIPTOR desc; + } - desc.Width = mem.data_width; - desc.Height = mem.data_height; - desc.Format = format; - desc.NumChannels = mem.data_elements; + CUarray_format_enum format; + switch(mem.data_type) { + case TYPE_UCHAR: format = CU_AD_FORMAT_UNSIGNED_INT8; break; + case TYPE_UINT: format = CU_AD_FORMAT_UNSIGNED_INT32; break; + case TYPE_INT: format = CU_AD_FORMAT_SIGNED_INT32; break; + case TYPE_FLOAT: format = CU_AD_FORMAT_FLOAT; break; + case TYPE_HALF: format = CU_AD_FORMAT_HALF; break; + default: assert(0); return; + } - cuda_assert(cuArrayCreate(&handle, &desc)); - } - if(!handle) { + CUarray array_3d = NULL; + size_t src_pitch = mem.data_width * dsize * mem.data_elements; + size_t dst_pitch = src_pitch; + + if(mem.data_depth > 1) { + /* 3D texture using array, there is no API for linear memory. */ + CUDA_ARRAY3D_DESCRIPTOR desc; + + desc.Width = mem.data_width; + desc.Height = mem.data_height; + desc.Depth = mem.data_depth; + desc.Format = format; + desc.NumChannels = mem.data_elements; + desc.Flags = 0; + + cuda_assert(cuArray3DCreate(&array_3d, &desc)); + + if(!array_3d) { return; } - /* Allocate 3D, 2D or 1D memory */ - if(mem.data_depth > 1) { - CUDA_MEMCPY3D param; - memset(¶m, 0, sizeof(param)); - param.dstMemoryType = CU_MEMORYTYPE_ARRAY; - param.dstArray = handle; - param.srcMemoryType = CU_MEMORYTYPE_HOST; - param.srcHost = (void*)mem.data_pointer; - param.srcPitch = mem.data_width*dsize*mem.data_elements; - param.WidthInBytes = param.srcPitch; - param.Height = mem.data_height; - param.Depth = mem.data_depth; - - cuda_assert(cuMemcpy3D(¶m)); - } - else if(mem.data_height > 1) { - CUDA_MEMCPY2D param; - memset(¶m, 0, sizeof(param)); - param.dstMemoryType = CU_MEMORYTYPE_ARRAY; - param.dstArray = handle; - param.srcMemoryType = CU_MEMORYTYPE_HOST; - param.srcHost = (void*)mem.data_pointer; - param.srcPitch = mem.data_width*dsize*mem.data_elements; - param.WidthInBytes = param.srcPitch; - param.Height = mem.data_height; - - cuda_assert(cuMemcpy2D(¶m)); - } - else - cuda_assert(cuMemcpyHtoA(handle, 0, (void*)mem.data_pointer, size)); + CUDA_MEMCPY3D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = CU_MEMORYTYPE_ARRAY; + param.dstArray = array_3d; + param.srcMemoryType = CU_MEMORYTYPE_HOST; + param.srcHost = (void*)mem.data_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + param.Depth = mem.data_depth; - /* Fermi and Kepler */ - mem.device_pointer = (device_ptr)handle; - mem.device_size = size; + cuda_assert(cuMemcpy3D(¶m)); + mem.device_pointer = (device_ptr)array_3d; + mem.device_size = size; stats.mem_alloc(size); + } + else if(mem.data_height > 1) { + /* 2D texture, using pitch aligned linear memory. */ + int alignment = 0; + cuda_assert(cuDeviceGetAttribute(&alignment, CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT, cuDevice)); + dst_pitch = align_up(src_pitch, alignment); + size_t dst_size = dst_pitch * mem.data_height; - if(has_bindless_textures) { - /* Bindless Textures - Kepler */ - int flat_slot = 0; - if(string_startswith(mem.name, "__tex_image")) { - int pos = string(mem.name).rfind("_"); - flat_slot = atoi(mem.name + pos + 1); - } - else { - assert(0); - } + generic_alloc(mem, dst_size - mem.memory_size()); + + CUDA_MEMCPY2D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = CU_MEMORYTYPE_DEVICE; + param.dstDevice = mem.device_pointer; + param.dstPitch = dst_pitch; + param.srcMemoryType = CU_MEMORYTYPE_HOST; + param.srcHost = (void*)mem.data_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; - CUDA_RESOURCE_DESC resDesc; - memset(&resDesc, 0, sizeof(resDesc)); + cuda_assert(cuMemcpy2DUnaligned(¶m)); + } + else { + /* 1D texture, using linear memory. */ + generic_alloc(mem); + cuda_assert(cuMemcpyHtoD(mem.device_pointer, (void*)mem.data_pointer, size)); + } + + if(!has_fermi_limits) { + /* Kepler+, bindless textures. */ + int flat_slot = 0; + if(string_startswith(mem.name, "__tex_image")) { + int pos = string(mem.name).rfind("_"); + flat_slot = atoi(mem.name + pos + 1); + } + else { + assert(0); + } + + CUDA_RESOURCE_DESC resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + + if(mem.data_depth > 1) { resDesc.resType = CU_RESOURCE_TYPE_ARRAY; - resDesc.res.array.hArray = handle; + resDesc.res.array.hArray = array_3d; resDesc.flags = 0; + } + else if(mem.data_height > 1) { + resDesc.resType = CU_RESOURCE_TYPE_PITCH2D; + resDesc.res.pitch2D.devPtr = mem.device_pointer; + resDesc.res.pitch2D.format = format; + resDesc.res.pitch2D.numChannels = mem.data_elements; + resDesc.res.pitch2D.height = mem.data_height; + resDesc.res.pitch2D.width = mem.data_width; + resDesc.res.pitch2D.pitchInBytes = dst_pitch; + } + else { + resDesc.resType = CU_RESOURCE_TYPE_LINEAR; + resDesc.res.linear.devPtr = mem.device_pointer; + resDesc.res.linear.format = format; + resDesc.res.linear.numChannels = mem.data_elements; + resDesc.res.linear.sizeInBytes = mem.device_size; + } - CUDA_TEXTURE_DESC texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - texDesc.addressMode[0] = address_mode; - texDesc.addressMode[1] = address_mode; - texDesc.addressMode[2] = address_mode; - texDesc.filterMode = filter_mode; - texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; + CUDA_TEXTURE_DESC texDesc; + memset(&texDesc, 0, sizeof(texDesc)); + texDesc.addressMode[0] = address_mode; + texDesc.addressMode[1] = address_mode; + texDesc.addressMode[2] = address_mode; + texDesc.filterMode = filter_mode; + texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; - CUtexObject tex = 0; - cuda_assert(cuTexObjectCreate(&tex, &resDesc, &texDesc, NULL)); + CUtexObject tex = 0; + cuda_assert(cuTexObjectCreate(&tex, &resDesc, &texDesc, NULL)); - /* Safety check */ - if((uint)tex > UINT_MAX) { - assert(0); - } + /* Safety check */ + if((uint)tex > UINT_MAX) { + assert(0); + } - /* Resize once */ - if(flat_slot >= texture_info.size()) { - /* Allocate some slots in advance, to reduce amount - * of re-allocations. */ - texture_info.resize(flat_slot + 128); - } + /* Resize once */ + if(flat_slot >= texture_info.size()) { + /* Allocate some slots in advance, to reduce amount + * of re-allocations. */ + texture_info.resize(flat_slot + 128); + } - /* Set Mapping and tag that we need to (re-)upload to device */ - TextureInfo& info = texture_info[flat_slot]; - info.data = (uint64_t)tex; - info.cl_buffer = 0; - info.interpolation = mem.interpolation; - info.extension = mem.extension; - info.width = mem.data_width; - info.height = mem.data_height; - info.depth = mem.data_depth; - - tex_bindless_map[mem.device_pointer] = tex; - need_texture_info = true; + /* Set Mapping and tag that we need to (re-)upload to device */ + TextureInfo& info = texture_info[flat_slot]; + info.data = (uint64_t)tex; + info.cl_buffer = 0; + info.interpolation = mem.interpolation; + info.extension = mem.extension; + info.width = mem.data_width; + info.height = mem.data_height; + info.depth = mem.data_depth; + + tex_bindless_map[mem.device_pointer] = tex; + need_texture_info = true; + } + else { + /* Fermi, fixed texture slots. */ + if(mem.data_depth > 1) { + cuda_assert(cuTexRefSetArray(texref, array_3d, CU_TRSA_OVERRIDE_FORMAT)); + } + else if(mem.data_height > 1) { + CUDA_ARRAY_DESCRIPTOR array_desc; + array_desc.Format = format; + array_desc.Height = mem.data_height; + array_desc.Width = mem.data_width; + array_desc.NumChannels = mem.data_elements; + cuda_assert(cuTexRefSetAddress2D_v3(texref, &array_desc, mem.device_pointer, dst_pitch)); } else { - /* Regular Textures - Fermi */ - cuda_assert(cuTexRefSetArray(texref, handle, CU_TRSA_OVERRIDE_FORMAT)); - cuda_assert(cuTexRefSetFilterMode(texref, filter_mode)); - cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_NORMALIZED_COORDINATES)); - - cuda_assert(cuTexRefSetAddressMode(texref, 0, address_mode)); - cuda_assert(cuTexRefSetAddressMode(texref, 1, address_mode)); - if(mem.data_depth > 1) { - cuda_assert(cuTexRefSetAddressMode(texref, 2, address_mode)); - } + cuda_assert(cuTexRefSetAddress(NULL, texref, cuda_device_ptr(mem.device_pointer), size)); + } - cuda_assert(cuTexRefSetFormat(texref, format, mem.data_elements)); + /* Attach to texture reference. */ + cuda_assert(cuTexRefSetFilterMode(texref, filter_mode)); + cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_NORMALIZED_COORDINATES)); + cuda_assert(cuTexRefSetFormat(texref, format, mem.data_elements)); + cuda_assert(cuTexRefSetAddressMode(texref, 0, address_mode)); + cuda_assert(cuTexRefSetAddressMode(texref, 1, address_mode)); + if(mem.data_depth > 1) { + cuda_assert(cuTexRefSetAddressMode(texref, 2, address_mode)); } } /* Fermi and Kepler */ - tex_interp_map[mem.device_pointer] = (mem.interpolation != INTERPOLATION_NONE); + tex_interp_map[mem.device_pointer] = true; } void tex_free(device_memory& mem) { if(mem.device_pointer) { - if(tex_interp_map[mem.device_pointer]) { + bool interp = tex_interp_map[mem.device_pointer]; + tex_interp_map.erase(tex_interp_map.find(mem.device_pointer)); + + if(interp) { CUDAContextScope scope(this); - cuArrayDestroy((CUarray)mem.device_pointer); - /* Free CUtexObject (Bindless Textures) */ - if(info.has_bindless_textures && tex_bindless_map[mem.device_pointer]) { - CUtexObject tex = tex_bindless_map[mem.device_pointer]; - cuTexObjectDestroy(tex); + if(!info.has_fermi_limits) { + /* Free bindless texture. */ + if(tex_bindless_map[mem.device_pointer]) { + CUtexObject tex = tex_bindless_map[mem.device_pointer]; + cuTexObjectDestroy(tex); + } } - tex_interp_map.erase(tex_interp_map.find(mem.device_pointer)); - mem.device_pointer = 0; - - stats.mem_free(mem.device_size); - mem.device_size = 0; + if(mem.data_depth > 1) { + /* Free array. */ + cuArrayDestroy((CUarray)mem.device_pointer); + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + } + else { + generic_free(mem); + } } else { - tex_interp_map.erase(tex_interp_map.find(mem.device_pointer)); generic_free(mem); } } @@ -2195,7 +2237,8 @@ void device_cuda_info(vector<DeviceInfo>& devices) info.num = num; info.advanced_shading = (major >= 2); - info.has_bindless_textures = (major >= 3); + info.has_fermi_limits = !(major >= 3); + info.has_half_images = (major >= 3); info.has_volume_decoupled = false; info.has_qbvh = false; diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 625901ff258..9358b40a689 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -46,32 +46,10 @@ ImageManager::ImageManager(const DeviceInfo& info) osl_texture_system = NULL; animation_frame = 0; - /* In case of multiple devices used we need to know type of an actual - * compute device. - * - * NOTE: We assume that all the devices are same type, otherwise we'll - * be screwed on so many levels.. - */ - DeviceType device_type = info.type; - if(device_type == DEVICE_MULTI) { - device_type = info.multi_devices[0].type; - } - /* Set image limits */ max_num_images = TEX_NUM_MAX; - has_half_images = true; - cuda_fermi_limits = false; - - if(device_type == DEVICE_CUDA) { - if(!info.has_bindless_textures) { - /* CUDA Fermi hardware (SM 2.x) has a hard limit on the number of textures */ - cuda_fermi_limits = true; - has_half_images = false; - } - } - else if(device_type == DEVICE_OPENCL) { - has_half_images = false; - } + has_half_images = info.has_half_images; + cuda_fermi_limits = info.has_fermi_limits; for(size_t type = 0; type < IMAGE_DATA_NUM_TYPES; type++) { tex_num_images[type] = 0; diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 601a2d2e11a..6d45c5bd02b 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -68,44 +68,66 @@ #define UI_TIP_PADDING (int)(UI_TIP_PAD_FAC * UI_UNIT_Y) #define UI_TIP_MAXWIDTH 600 -#define MAX_TOOLTIP_LINES 8 -typedef struct uiTooltipData { - rcti bbox; - uiFontStyle fstyle; - char lines[MAX_TOOLTIP_LINES][2048]; - char header[2048], active_info[2048]; - struct { - enum { - UI_TIP_STYLE_NORMAL = 0, - UI_TIP_STYLE_HEADER, - UI_TIP_STYLE_MONO, - } style : 3; - enum { - UI_TIP_LC_MAIN = 0, /* primary text */ - UI_TIP_LC_VALUE, /* the value of buttons (also shortcuts) */ - UI_TIP_LC_ACTIVE, /* titles of active enum values */ - UI_TIP_LC_NORMAL, /* regular text */ - UI_TIP_LC_PYTHON, /* Python snippet */ - UI_TIP_LC_ALERT, /* description of why operator can't run */ - } color_id : 4; - int is_pad : 1; - } format[MAX_TOOLTIP_LINES]; +typedef struct uiTooltipFormat { + enum { + UI_TIP_STYLE_NORMAL = 0, + UI_TIP_STYLE_HEADER, + UI_TIP_STYLE_MONO, + } style : 3; + enum { + UI_TIP_LC_MAIN = 0, /* primary text */ + UI_TIP_LC_VALUE, /* the value of buttons (also shortcuts) */ + UI_TIP_LC_ACTIVE, /* titles of active enum values */ + UI_TIP_LC_NORMAL, /* regular text */ + UI_TIP_LC_PYTHON, /* Python snippet */ + UI_TIP_LC_ALERT, /* description of why operator can't run */ + } color_id : 4; + int is_pad : 1; +} uiTooltipFormat; + +typedef struct uiTooltipField { + char *text; + char *text_suffix; struct { uint x_pos; /* x cursor position at the end of the last line */ uint lines; /* number of lines, 1 or more with word-wrap */ - } line_geom[MAX_TOOLTIP_LINES]; + } geom; + uiTooltipFormat format; - int wrap_width; +} uiTooltipField; - int totline; +#define MAX_TOOLTIP_LINES 8 +typedef struct uiTooltipData { + rcti bbox; + uiTooltipField *fields; + uint fields_len; + uiFontStyle fstyle; + int wrap_width; int toth, lineh; } uiTooltipData; #define UI_TIP_LC_MAX 6 BLI_STATIC_ASSERT(UI_TIP_LC_MAX == UI_TIP_LC_ALERT + 1, "invalid lc-max"); -BLI_STATIC_ASSERT(sizeof(((uiTooltipData *)NULL)->format[0]) <= sizeof(int), "oversize"); +BLI_STATIC_ASSERT(sizeof(uiTooltipFormat) <= sizeof(int), "oversize"); + +static uiTooltipField *text_field_add_only( + uiTooltipData *data) +{ + data->fields_len += 1; + data->fields = MEM_recallocN(data->fields, sizeof(*data->fields) * data->fields_len); + return &data->fields[data->fields_len - 1]; +} + +static uiTooltipField *text_field_add( + uiTooltipData *data, + const uiTooltipFormat *format) +{ + uiTooltipField *field = text_field_add_only(data); + field->format = *format; + return field; +} /* -------------------------------------------------------------------- */ /** \name ToolTip Callbacks (Draw & Free) @@ -188,12 +210,14 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) bbox.xmin += 0.5f * pad_px; /* add padding to the text */ bbox.ymax -= 0.25f * pad_px; - for (i = 0; i < data->totline; i++) { - bbox.ymin = bbox.ymax - (data->lineh * data->line_geom[i].lines); - if (data->format[i].style == UI_TIP_STYLE_HEADER) { + for (i = 0; i < data->fields_len; i++) { + const uiTooltipField *field = &data->fields[i]; + const uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : NULL; + + bbox.ymin = bbox.ymax - (data->lineh * field->geom.lines); + if (field->format.style == UI_TIP_STYLE_HEADER) { /* draw header and active data (is done here to be able to change color) */ uiFontStyle fstyle_header = data->fstyle; - float xofs, yofs; /* override text-style */ fstyle_header.shadow = 1; @@ -204,23 +228,26 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]); UI_fontstyle_set(&fstyle_header); - UI_fontstyle_draw(&fstyle_header, &bbox, data->header, drawcol); - - /* offset to the end of the last line */ - xofs = data->line_geom[i].x_pos; - yofs = data->lineh * (data->line_geom[i].lines - 1); - bbox.xmin += xofs; - bbox.ymax -= yofs; + UI_fontstyle_draw(&fstyle_header, &bbox, field->text, drawcol); fstyle_header.shadow = 0; - rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]); - UI_fontstyle_draw(&fstyle_header, &bbox, data->active_info, drawcol); - /* undo offset */ - bbox.xmin -= xofs; - bbox.ymax += yofs; + /* offset to the end of the last line */ + if (field->text_suffix) { + float xofs = field->geom.x_pos; + float yofs = data->lineh * (field->geom.lines - 1); + bbox.xmin += xofs; + bbox.ymax -= yofs; + + rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]); + UI_fontstyle_draw(&fstyle_header, &bbox, field->text_suffix, drawcol); + + /* undo offset */ + bbox.xmin -= xofs; + bbox.ymax += yofs; + } } - else if (data->format[i].style == UI_TIP_STYLE_MONO) { + else if (field->format.style == UI_TIP_STYLE_MONO) { uiFontStyle fstyle_mono = data->fstyle; fstyle_mono.uifont_id = blf_mono_font; fstyle_mono.word_wrap = true; @@ -228,23 +255,23 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) UI_fontstyle_set(&fstyle_mono); /* XXX, needed because we dont have mono in 'U.uifonts' */ BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi); - rgb_float_to_uchar(drawcol, tip_colors[data->format[i].color_id]); - UI_fontstyle_draw(&fstyle_mono, &bbox, data->lines[i], drawcol); + rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]); + UI_fontstyle_draw(&fstyle_mono, &bbox, field->text, drawcol); } else { uiFontStyle fstyle_normal = data->fstyle; - BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL); + BLI_assert(field->format.style == UI_TIP_STYLE_NORMAL); fstyle_normal.word_wrap = true; /* draw remaining data */ - rgb_float_to_uchar(drawcol, tip_colors[data->format[i].color_id]); + rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]); UI_fontstyle_set(&fstyle_normal); - UI_fontstyle_draw(&fstyle_normal, &bbox, data->lines[i], drawcol); + UI_fontstyle_draw(&fstyle_normal, &bbox, field->text, drawcol); } - bbox.ymax -= data->lineh * data->line_geom[i].lines; + bbox.ymax -= data->lineh * field->geom.lines; - if ((i + 1 != data->totline) && data->format[i + 1].is_pad) { + if (field_next && field_next->format.is_pad) { bbox.ymax -= data->lineh * (UI_TIP_PAD_FAC - 1); } } @@ -261,6 +288,15 @@ static void ui_tooltip_region_free_cb(ARegion *ar) uiTooltipData *data; data = ar->regiondata; + + for (int i = 0; i < data->fields_len; i++) { + const uiTooltipField *field = &data->fields[i]; + MEM_freeN(field->text); + if (field->text_suffix) { + MEM_freeN(field->text_suffix); + } + } + MEM_freeN(data->fields); MEM_freeN(data); ar->regiondata = NULL; } @@ -290,48 +326,63 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* Tip */ if (but_tip.strinfo) { - if (enum_label.strinfo) { - BLI_snprintf(data->header, sizeof(data->header), "%s: ", but_tip.strinfo); - BLI_strncpy(data->active_info, enum_label.strinfo, sizeof(data->lines[0])); - } - else { - BLI_snprintf(data->header, sizeof(data->header), "%s.", but_tip.strinfo); + { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_HEADER, + .color_id = UI_TIP_LC_NORMAL, + }); + if (enum_label.strinfo) { + field->text = BLI_sprintfN("%s: ", but_tip.strinfo); + field->text_suffix = BLI_strdup(enum_label.strinfo); + } + else { + field->text = BLI_sprintfN("%s.", but_tip.strinfo); + } } - data->format[data->totline].style = UI_TIP_STYLE_HEADER; - data->totline++; /* special case enum rna buttons */ if ((but->type & UI_BTYPE_ROW) && but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) { - BLI_strncpy(data->lines[data->totline], IFACE_("(Shift-Click/Drag to select multiple)"), - sizeof(data->lines[0])); - - data->format[data->totline].color_id = UI_TIP_LC_NORMAL; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_NORMAL, + }); + field->text = BLI_strdup(IFACE_("(Shift-Click/Drag to select multiple)")); } } - /* Enum item label & tip */ + /* Enum field label & tip */ if (enum_tip.strinfo) { - BLI_strncpy(data->lines[data->totline], enum_tip.strinfo, sizeof(data->lines[0])); - data->format[data->totline].is_pad = true; - data->format[data->totline].color_id = UI_TIP_LC_VALUE; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = BLI_strdup(enum_tip.strinfo); } /* Op shortcut */ if (op_keymap.strinfo) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), op_keymap.strinfo); - data->format[data->totline].is_pad = true; - data->format[data->totline].color_id = UI_TIP_LC_VALUE; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = BLI_sprintfN(TIP_("Shortcut: %s"), op_keymap.strinfo); } /* Property context-toggle shortcut */ if (prop_keymap.strinfo) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), prop_keymap.strinfo); - data->format[data->totline].is_pad = true; - data->format[data->totline].color_id = UI_TIP_LC_VALUE; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = BLI_sprintfN(TIP_("Shortcut: %s"), prop_keymap.strinfo); } if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { @@ -340,10 +391,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* full string */ ui_but_string_get(but, buf, sizeof(buf)); if (buf[0]) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf); - data->format[data->totline].is_pad = true; - data->format[data->totline].color_id = UI_TIP_LC_VALUE; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = BLI_sprintfN(TIP_("Value: %s"), buf); } } } @@ -356,27 +410,36 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) float value = RNA_property_array_check(but->rnaprop) ? RNA_property_float_get_index(&but->rnapoin, but->rnaprop, but->rnaindex) : RNA_property_float_get(&but->rnapoin, but->rnaprop); - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value); - data->format[data->totline].color_id = UI_TIP_LC_NORMAL; - data->totline++; + + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + }); + field->text = BLI_sprintfN(TIP_("Radians: %f"), value); } } if (but->flag & UI_BUT_DRIVEN) { if (ui_but_anim_expression_get(but, buf, sizeof(buf))) { - /* expression */ - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf); - data->format[data->totline].color_id = UI_TIP_LC_NORMAL; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_NORMAL, + }); + field->text = BLI_sprintfN(TIP_("Expression: %s"), buf); } } if (but->rnapoin.id.data) { - ID *id = but->rnapoin.id.data; + const ID *id = but->rnapoin.id.data; if (ID_IS_LINKED_DATABLOCK(id)) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name); - data->format[data->totline].color_id = UI_TIP_LC_NORMAL; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_NORMAL, + }); + field->text = BLI_sprintfN(TIP_("Library: %s"), id->lib->name); } } } @@ -385,7 +448,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) char *str; opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */ - /* so the context is passed to itemf functions (some py itemf functions use it) */ + /* so the context is passed to fieldf functions (some py fieldf functions use it) */ WM_operator_properties_sanitize(opptr, false); str = WM_operator_pystring_ex(C, NULL, false, false, but->optype, opptr); @@ -395,11 +458,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* operator info */ if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str); - data->format[data->totline].style = UI_TIP_STYLE_MONO; - data->format[data->totline].is_pad = true; - data->format[data->totline].color_id = UI_TIP_LC_PYTHON; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_MONO, + .color_id = UI_TIP_LC_PYTHON, + .is_pad = true, + }); + field->text = BLI_sprintfN(TIP_("Python: %s"), str); } MEM_freeN(str); @@ -421,48 +486,51 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } if (disabled_msg && disabled_msg[0]) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), disabled_msg); - data->format[data->totline].color_id = UI_TIP_LC_ALERT; - data->totline++; + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_ALERT, + }); + field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg); } } if ((U.flag & USER_TOOLTIPS_PYTHON) == 0 && !but->optype && rna_struct.strinfo) { - if (rna_prop.strinfo) { - /* Struct and prop */ - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), - TIP_("Python: %s.%s"), - rna_struct.strinfo, rna_prop.strinfo); - } - else { - /* Only struct (e.g. menus) */ - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), - TIP_("Python: %s"), rna_struct.strinfo); + { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_MONO, + .color_id = UI_TIP_LC_PYTHON, + .is_pad = true, + }); + if (rna_prop.strinfo) { + /* Struct and prop */ + field->text = BLI_sprintfN(TIP_("Python: %s.%s"), rna_struct.strinfo, rna_prop.strinfo); + } + else { + /* Only struct (e.g. menus) */ + field->text = BLI_sprintfN(TIP_("Python: %s"), rna_struct.strinfo); + } } - data->format[data->totline].style = UI_TIP_STYLE_MONO; - data->format[data->totline].is_pad = true; - data->format[data->totline].color_id = UI_TIP_LC_PYTHON; - data->totline++; if (but->rnapoin.id.data) { + uiTooltipField *field = text_field_add( + data, &(uiTooltipFormat){ + .style = UI_TIP_STYLE_MONO, + .color_id = UI_TIP_LC_PYTHON, + }); + /* this could get its own 'BUT_GET_...' type */ /* never fails */ - char *id_path; - + /* move ownership (no need for re-alloc) */ if (but->rnaprop) { - id_path = RNA_path_full_property_py_ex(&but->rnapoin, but->rnaprop, but->rnaindex, true); + field->text = RNA_path_full_property_py_ex(&but->rnapoin, but->rnaprop, but->rnaindex, true); } else { - id_path = RNA_path_full_struct_py(&but->rnapoin); + field->text = RNA_path_full_struct_py(&but->rnapoin); } - BLI_strncat_utf8(data->lines[data->totline], id_path, sizeof(data->lines[0])); - MEM_freeN(id_path); - - data->format[data->totline].style = UI_TIP_STYLE_MONO; - data->format[data->totline].color_id = UI_TIP_LC_PYTHON; - data->totline++; } } @@ -482,9 +550,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) if (rna_prop.strinfo) MEM_freeN(rna_prop.strinfo); - BLI_assert(data->totline < MAX_TOOLTIP_LINES); + BLI_assert(data->fields_len < MAX_TOOLTIP_LINES); - if (data->totline == 0) { + if (data->fields_len == 0) { MEM_freeN(data); return NULL; } @@ -557,38 +625,38 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) h = BLF_height_max(data->fstyle.uifont_id); - for (i = 0, fontw = 0, fonth = 0; i < data->totline; i++) { + for (i = 0, fontw = 0, fonth = 0; i < data->fields_len; i++) { + uiTooltipField *field = &data->fields[i]; + uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : NULL; + struct ResultBLF info; int w, x_pos = 0; + int font_id; - if (data->format[i].style == UI_TIP_STYLE_HEADER) { - w = BLF_width_ex(data->fstyle.uifont_id, data->header, sizeof(data->header), &info); - /* check for enum label */ - if (data->active_info[0]) { - x_pos = info.width; - w = max_ii(w, x_pos + BLF_width(data->fstyle.uifont_id, data->active_info, sizeof(data->active_info))); - } - } - else if (data->format[i].style == UI_TIP_STYLE_MONO) { + if (field->format.style == UI_TIP_STYLE_MONO) { BLF_size(blf_mono_font, data->fstyle.points * U.pixelsize, U.dpi); - - w = BLF_width_ex(blf_mono_font, data->lines[i], sizeof(data->lines[i]), &info); + font_id = blf_mono_font; } else { - BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL); - - w = BLF_width_ex(data->fstyle.uifont_id, data->lines[i], sizeof(data->lines[i]), &info); + BLI_assert(ELEM(field->format.style, UI_TIP_STYLE_NORMAL, UI_TIP_STYLE_HEADER)); + font_id = data->fstyle.uifont_id; } + w = BLF_width_ex(font_id, field->text, BLF_DRAW_STR_DUMMY_MAX, &info); + /* check for suffix (enum label) */ + if (field->text_suffix && field->text_suffix[0]) { + x_pos = info.width; + w = max_ii(w, x_pos + BLF_width(font_id, field->text_suffix, BLF_DRAW_STR_DUMMY_MAX)); + } fontw = max_ii(fontw, w); fonth += h * info.lines; - if ((i + 1 != data->totline) && data->format[i + 1].is_pad) { + if (field_next && field_next->format.is_pad) { fonth += h * (UI_TIP_PAD_FAC - 1); } - data->line_geom[i].lines = info.lines; - data->line_geom[i].x_pos = x_pos; + field->geom.lines = info.lines; + field->geom.x_pos = x_pos; } //fontw *= aspect; diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 75cab383f35..2f996eb7e39 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -66,16 +66,18 @@ /* icons are 80% of height of button (16 pixels inside 20 height) */ #define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect)) -#define UI_BUT_FLAGS_PUBLIC \ - (UI_SELECT | UI_SCROLLED | UI_ACTIVE | UI_HAS_ICON | UI_HIDDEN | UI_SELECT_DRAW) - -/* Don't overlap w/ UI_BUT_FLAGS_PUBLIC bits. */ +/* Button state argument shares bits with 'uiBut.flag'. + * reuse flags that aren't needed for drawing to avoid collision. */ enum { /* Show that holding the button opens a menu. */ - UI_STATE_HOLD_ACTION = (1 << 6), - UI_STATE_TEXT_INPUT = (1 << 7), -}; + UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY, + UI_STATE_TEXT_INPUT = UI_BUT_UNDO, + UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT), +}; +/* Prevent accidental use. */ +#define UI_BUT_UPDATE_DELAY ((void)0) +#define UI_BUT_UNDO ((void)0) /* ************** widget base functions ************** */ /** @@ -4066,7 +4068,8 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct roundboxalign = widget_roundbox_set(but, rect); - state = but->flag & UI_BUT_FLAGS_PUBLIC; + /* Mask out flags re-used for local state. */ + state = but->flag & ~UI_STATE_FLAGS_ALL; if (state & UI_SELECT_DRAW) { state |= UI_SELECT; |