2023-06-14 16:52:36 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/image.h"
|
2019-05-02 15:45:31 +02:00
|
|
|
#include "device/device.h"
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/colorspace.h"
|
|
|
|
|
#include "scene/image_oiio.h"
|
|
|
|
|
#include "scene/image_vdb.h"
|
|
|
|
|
#include "scene/scene.h"
|
|
|
|
|
#include "scene/stats.h"
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "util/image.h"
|
|
|
|
|
#include "util/image_impl.h"
|
|
|
|
|
#include "util/log.h"
|
|
|
|
|
#include "util/progress.h"
|
|
|
|
|
#include "util/task.h"
|
|
|
|
|
#include "util/texture.h"
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2011-05-31 16:21:30 +00:00
|
|
|
#ifdef WITH_OSL
|
|
|
|
|
# include <OSL/oslexec.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
|
2018-07-27 10:24:03 +02:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
const char *name_from_type(ImageDataType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case IMAGE_DATA_TYPE_FLOAT4:
|
|
|
|
|
return "float4";
|
|
|
|
|
case IMAGE_DATA_TYPE_BYTE4:
|
|
|
|
|
return "byte4";
|
|
|
|
|
case IMAGE_DATA_TYPE_HALF4:
|
|
|
|
|
return "half4";
|
|
|
|
|
case IMAGE_DATA_TYPE_FLOAT:
|
|
|
|
|
return "float";
|
|
|
|
|
case IMAGE_DATA_TYPE_BYTE:
|
|
|
|
|
return "byte";
|
|
|
|
|
case IMAGE_DATA_TYPE_HALF:
|
|
|
|
|
return "half";
|
|
|
|
|
case IMAGE_DATA_TYPE_USHORT4:
|
|
|
|
|
return "ushort4";
|
|
|
|
|
case IMAGE_DATA_TYPE_USHORT:
|
|
|
|
|
return "ushort";
|
2020-10-02 17:40:28 +02:00
|
|
|
case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
|
|
|
|
|
return "nanovdb_float";
|
|
|
|
|
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
|
|
|
|
|
return "nanovdb_float3";
|
2022-05-20 18:01:26 +02:00
|
|
|
case IMAGE_DATA_TYPE_NANOVDB_FPN:
|
|
|
|
|
return "nanovdb_fpn";
|
|
|
|
|
case IMAGE_DATA_TYPE_NANOVDB_FP16:
|
|
|
|
|
return "nanovdb_fp16";
|
2018-07-27 10:24:03 +02:00
|
|
|
case IMAGE_DATA_NUM_TYPES:
|
|
|
|
|
assert(!"System enumerator type, should never be used");
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
assert(!"Unhandled image data type");
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2020-03-08 10:42:11 +01:00
|
|
|
/* Image Handle */
|
|
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
ImageHandle::ImageHandle() : manager(nullptr) {}
|
2020-03-08 10:42:11 +01:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
ImageHandle::ImageHandle(const ImageHandle &other)
|
2025-01-23 21:20:01 +01:00
|
|
|
: slots(other.slots), is_tiled(other.is_tiled), manager(other.manager)
|
2020-03-08 10:42:11 +01:00
|
|
|
{
|
|
|
|
|
/* Increase image user count. */
|
2025-01-23 21:20:01 +01:00
|
|
|
for (const size_t slot : slots) {
|
2020-03-08 10:42:11 +01:00
|
|
|
manager->add_image_user(slot);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImageHandle &ImageHandle::operator=(const ImageHandle &other)
|
|
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
manager = other.manager;
|
2025-01-23 21:20:01 +01:00
|
|
|
is_tiled = other.is_tiled;
|
|
|
|
|
slots = other.slots;
|
2020-03-08 10:42:11 +01:00
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
for (const size_t slot : slots) {
|
2020-03-08 10:42:11 +01:00
|
|
|
manager->add_image_user(slot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImageHandle::~ImageHandle()
|
|
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ImageHandle::clear()
|
|
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
for (const size_t slot : slots) {
|
2020-03-08 10:42:11 +01:00
|
|
|
manager->remove_image_user(slot);
|
|
|
|
|
}
|
2020-03-19 19:50:34 +01:00
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
slots.clear();
|
2024-12-26 17:53:55 +01:00
|
|
|
manager = nullptr;
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-11 20:11:44 -07:00
|
|
|
bool ImageHandle::empty() const
|
2020-03-08 10:42:11 +01:00
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
return slots.empty();
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-11 20:11:44 -07:00
|
|
|
int ImageHandle::num_tiles() const
|
2020-03-08 10:42:11 +01:00
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
return (is_tiled) ? slots.size() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ImageHandle::num_svm_slots() const
|
|
|
|
|
{
|
|
|
|
|
return slots.size();
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImageMetaData ImageHandle::metadata()
|
|
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
if (slots.empty()) {
|
2020-03-08 10:42:11 +01:00
|
|
|
return ImageMetaData();
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
ImageManager::Image *img = manager->get_image_slot(slots.front());
|
2020-03-08 14:21:29 +01:00
|
|
|
manager->load_image_metadata(img);
|
|
|
|
|
return img->metadata;
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
int ImageHandle::svm_slot(const int slot_index) const
|
2020-03-08 10:42:11 +01:00
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
if (slot_index >= slots.size()) {
|
2020-03-08 10:42:11 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (manager->osl_texture_system) {
|
2025-01-23 21:20:01 +01:00
|
|
|
ImageManager::Image *img = manager->get_image_slot(slots[slot_index]);
|
2020-03-12 17:19:30 +01:00
|
|
|
if (!img->loader->osl_filepath().empty()) {
|
2020-03-08 10:42:11 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
return slots[slot_index];
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-11 20:11:44 -07:00
|
|
|
vector<int4> ImageHandle::get_svm_slots() const
|
|
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
const size_t num_nodes = divide_up(slots.size(), 2);
|
2022-05-11 20:11:44 -07:00
|
|
|
|
|
|
|
|
vector<int4> svm_slots;
|
|
|
|
|
svm_slots.reserve(num_nodes);
|
|
|
|
|
for (size_t i = 0; i < num_nodes; i++) {
|
|
|
|
|
int4 node;
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
size_t slot = slots[2 * i];
|
2025-01-08 20:32:31 +01:00
|
|
|
node.x = manager->get_image_slot(slot)->loader->get_tile_number();
|
2022-05-11 20:11:44 -07:00
|
|
|
node.y = slot;
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
if ((2 * i + 1) < slots.size()) {
|
|
|
|
|
slot = slots[2 * i + 1];
|
2025-01-08 20:32:31 +01:00
|
|
|
node.z = manager->get_image_slot(slot)->loader->get_tile_number();
|
2022-05-11 20:11:44 -07:00
|
|
|
node.w = slot;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
node.z = -1;
|
|
|
|
|
node.w = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
svm_slots.push_back(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return svm_slots;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
device_texture *ImageHandle::image_memory() const
|
2020-03-08 10:42:11 +01:00
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
if (slots.empty()) {
|
2024-12-26 17:53:55 +01:00
|
|
|
return nullptr;
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
ImageManager::Image *img = manager->get_image_slot(slots[0]);
|
2024-12-29 23:13:45 +01:00
|
|
|
return img ? img->mem.get() : nullptr;
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
VDBImageLoader *ImageHandle::vdb_loader() const
|
2020-08-11 17:34:34 +02:00
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
if (slots.empty()) {
|
2024-12-26 17:53:55 +01:00
|
|
|
return nullptr;
|
2020-08-11 17:34:34 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-23 21:20:01 +01:00
|
|
|
ImageManager::Image *img = manager->get_image_slot(slots[0]);
|
2020-08-11 17:34:34 +02:00
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
if (img == nullptr) {
|
|
|
|
|
return nullptr;
|
2020-08-11 17:34:34 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
ImageLoader *loader = img->loader.get();
|
2020-08-11 17:34:34 +02:00
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
if (loader == nullptr) {
|
|
|
|
|
return nullptr;
|
2020-08-11 17:34:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loader->is_vdb_loader()) {
|
|
|
|
|
return dynamic_cast<VDBImageLoader *>(loader);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
return nullptr;
|
2020-08-11 17:34:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-18 17:28:03 +01:00
|
|
|
ImageManager *ImageHandle::get_manager() const
|
|
|
|
|
{
|
|
|
|
|
return manager;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
bool ImageHandle::operator==(const ImageHandle &other) const
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
2025-01-23 21:20:01 +01:00
|
|
|
return manager == other.manager && is_tiled == other.is_tiled && slots == other.slots;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
/* Image MetaData */
|
|
|
|
|
|
|
|
|
|
ImageMetaData::ImageMetaData()
|
|
|
|
|
: channels(0),
|
|
|
|
|
width(0),
|
|
|
|
|
height(0),
|
|
|
|
|
depth(0),
|
2020-10-02 17:40:28 +02:00
|
|
|
byte_size(0),
|
2020-03-08 14:21:29 +01:00
|
|
|
type(IMAGE_DATA_NUM_TYPES),
|
|
|
|
|
colorspace(u_colorspace_raw),
|
|
|
|
|
colorspace_file_format(""),
|
2020-03-17 16:48:00 +01:00
|
|
|
use_transform_3d(false),
|
2020-03-08 14:21:29 +01:00
|
|
|
compress_as_srgb(false)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
bool ImageMetaData::operator==(const ImageMetaData &other) const
|
2011-05-31 16:21:30 +00:00
|
|
|
{
|
2020-03-08 14:21:29 +01:00
|
|
|
return channels == other.channels && width == other.width && height == other.height &&
|
2020-03-17 16:48:00 +01:00
|
|
|
depth == other.depth && use_transform_3d == other.use_transform_3d &&
|
|
|
|
|
(!use_transform_3d || transform_3d == other.transform_3d) && type == other.type &&
|
|
|
|
|
colorspace == other.colorspace && compress_as_srgb == other.compress_as_srgb;
|
2011-05-31 16:21:30 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
bool ImageMetaData::is_float() const
|
2012-11-21 13:00:51 +00:00
|
|
|
{
|
2020-03-08 14:21:29 +01:00
|
|
|
return (type == IMAGE_DATA_TYPE_FLOAT || type == IMAGE_DATA_TYPE_FLOAT4 ||
|
|
|
|
|
type == IMAGE_DATA_TYPE_HALF || type == IMAGE_DATA_TYPE_HALF4);
|
2012-11-21 13:00:51 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
void ImageMetaData::detect_colorspace()
|
2019-05-02 15:45:31 +02:00
|
|
|
{
|
|
|
|
|
/* Convert used specified color spaces to one we know how to handle. */
|
2020-03-08 14:21:29 +01:00
|
|
|
colorspace = ColorSpaceManager::detect_known_colorspace(
|
2022-11-14 18:09:45 +01:00
|
|
|
colorspace, colorspace_file_hint.c_str(), colorspace_file_format, is_float());
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
if (colorspace == u_colorspace_raw) {
|
2019-05-02 15:45:31 +02:00
|
|
|
/* Nothing to do. */
|
|
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
else if (colorspace == u_colorspace_srgb) {
|
2019-05-02 15:45:31 +02:00
|
|
|
/* Keep sRGB colorspace stored as sRGB, to save memory and/or loading time
|
|
|
|
|
* for the common case of 8bit sRGB images like PNG. */
|
2020-03-08 14:21:29 +01:00
|
|
|
compress_as_srgb = true;
|
2019-05-02 15:45:31 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2019-05-14 11:22:07 +02:00
|
|
|
/* If colorspace conversion needed, use half instead of short so we can
|
|
|
|
|
* represent HDR values that might result from conversion. */
|
2022-01-21 18:57:00 +01:00
|
|
|
if (type == IMAGE_DATA_TYPE_BYTE || type == IMAGE_DATA_TYPE_USHORT) {
|
2020-03-08 14:21:29 +01:00
|
|
|
type = IMAGE_DATA_TYPE_HALF;
|
2019-05-14 11:22:07 +02:00
|
|
|
}
|
2022-01-21 18:57:00 +01:00
|
|
|
else if (type == IMAGE_DATA_TYPE_BYTE4 || type == IMAGE_DATA_TYPE_USHORT4) {
|
2020-03-08 14:21:29 +01:00
|
|
|
type = IMAGE_DATA_TYPE_HALF4;
|
2019-05-14 11:22:07 +02:00
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
/* Image Loader */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
ImageLoader::ImageLoader() = default;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
ustring ImageLoader::osl_filepath() const
|
|
|
|
|
{
|
|
|
|
|
return ustring();
|
|
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2022-05-11 20:11:44 -07:00
|
|
|
int ImageLoader::get_tile_number() const
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
bool ImageLoader::equals(const ImageLoader *a, const ImageLoader *b)
|
|
|
|
|
{
|
2024-12-26 17:53:55 +01:00
|
|
|
if (a == nullptr && b == nullptr) {
|
2018-02-27 22:16:45 +01:00
|
|
|
return true;
|
Packed and generated images support for Cycles
This commit adds support of packed and generated images
for Cycles when using SVM backend. Movies are still not
supported. This changes also doesn't touch OSL which is
much less trivial to adopt for any images which are not
saved to disk.
Implementation details:
- When adding images to Image Manager is now possible
to mark image as builtin. Builtin images will bypass
OIIO loader and will use special loading callbacks.
- Callbacks are set by Blender Session and they're
using C++ RNA interface to obtain needed data (pixels,
dimensions, is_float flag).
- Image Manager assumes file path is used as reference
to a builtin images, but in fact currently image
datablock name is used for reference. This makes it
easy to find an image in BlendData database.
- Added some extra properties to Image RNA:
* channels, which denotes actual number of channels
in ImBuf. This is needed to treat image's pixels
correct (before it wasn't possible because API
used internal number of channels for pixels which
is in fact doesn't correlate with image depth)
* is_float, which is truth if image is stored in
float buffer of ImBuf.
- Implemented string lookup for C++ RNA collections
for cases there's no manual lookup function.
OSL is not supported because it used own image loading
and filtering routines and there's seems to be no API
to feed pre-loaded pixels directly to the library.
Think we'll either need to add some API to support
such kind of feeding or consider OSL does not have
support of packed images at all.
Movies are not supported at this moment because of lack
of RNA API to load specified frame. It's not difficult
to solve, just need to consider what to best here:
* Either write some general python interface for ImBuf
and use it via C++ API, or
* Write a PY API function which will return pixels for
given frame, or
* Use bad-level BKE_* call
Anyway, small steps, further improvements later.
Reviewed by Brecht, thanks!
2013-01-12 10:59:13 +00:00
|
|
|
}
|
2024-12-26 17:53:59 +01:00
|
|
|
return (a && b && typeid(*a) == typeid(*b) && a->equals(*b));
|
2020-03-08 14:21:29 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-11 17:34:34 +02:00
|
|
|
bool ImageLoader::is_vdb_loader() const
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
/* Image Manager */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
ImageManager::ImageManager(const DeviceInfo &info)
|
|
|
|
|
{
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
need_update_ = true;
|
2024-12-26 17:53:55 +01:00
|
|
|
osl_texture_system = nullptr;
|
2020-03-08 14:21:29 +01:00
|
|
|
animation_frame = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
/* Set image limits */
|
2021-03-29 22:58:19 +02:00
|
|
|
features.has_nanovdb = info.has_nanovdb;
|
2020-03-08 14:21:29 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
ImageManager::~ImageManager()
|
|
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
for (size_t slot = 0; slot < images.size(); slot++) {
|
2020-03-08 14:21:29 +01:00
|
|
|
assert(!images[slot]);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
void ImageManager::set_osl_texture_system(void *texture_system)
|
|
|
|
|
{
|
|
|
|
|
osl_texture_system = texture_system;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
bool ImageManager::set_animation_frame_update(const int frame)
|
2020-03-08 14:21:29 +01:00
|
|
|
{
|
|
|
|
|
if (frame != animation_frame) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(images_mutex);
|
2020-03-08 14:21:29 +01:00
|
|
|
animation_frame = frame;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
for (size_t slot = 0; slot < images.size(); slot++) {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (images[slot] && images[slot]->params.animated) {
|
2020-03-08 14:21:29 +01:00
|
|
|
return true;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2018-02-27 22:16:45 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
void ImageManager::load_image_metadata(Image *img)
|
|
|
|
|
{
|
|
|
|
|
if (!img->need_metadata) {
|
|
|
|
|
return;
|
2018-07-05 12:37:52 +02:00
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock image_lock(img->mutex);
|
2020-03-08 14:21:29 +01:00
|
|
|
if (!img->need_metadata) {
|
|
|
|
|
return;
|
2016-05-12 14:51:42 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
ImageMetaData &metadata = img->metadata;
|
|
|
|
|
metadata = ImageMetaData();
|
|
|
|
|
metadata.colorspace = img->params.colorspace;
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2021-03-29 22:58:19 +02:00
|
|
|
if (img->loader->load_metadata(features, metadata)) {
|
2020-03-16 16:00:59 +01:00
|
|
|
assert(metadata.type != IMAGE_DATA_NUM_TYPES);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
metadata.type = IMAGE_DATA_TYPE_BYTE4;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
metadata.detect_colorspace();
|
|
|
|
|
|
2021-03-29 22:58:19 +02:00
|
|
|
assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT ||
|
2022-05-20 18:01:26 +02:00
|
|
|
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 ||
|
|
|
|
|
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FPN ||
|
|
|
|
|
metadata.type != IMAGE_DATA_TYPE_NANOVDB_FP16));
|
2020-03-08 14:21:29 +01:00
|
|
|
|
|
|
|
|
img->need_metadata = false;
|
2012-03-07 12:27:18 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
ImageHandle ImageManager::add_image(const string &filename, const ImageParams ¶ms)
|
2020-03-08 10:42:11 +01:00
|
|
|
{
|
2024-12-29 23:13:45 +01:00
|
|
|
const size_t slot = add_image_slot(make_unique<OIIOImageLoader>(filename), params, false);
|
2020-03-08 14:21:29 +01:00
|
|
|
|
2020-03-08 10:42:11 +01:00
|
|
|
ImageHandle handle;
|
2025-01-23 21:20:01 +01:00
|
|
|
handle.slots.push_back(slot);
|
2020-03-08 10:42:11 +01:00
|
|
|
handle.manager = this;
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
ImageHandle ImageManager::add_image(const string &filename,
|
|
|
|
|
const ImageParams ¶ms,
|
2020-11-04 11:17:38 +01:00
|
|
|
const array<int> &tiles)
|
2020-03-08 10:42:11 +01:00
|
|
|
{
|
|
|
|
|
ImageHandle handle;
|
|
|
|
|
handle.manager = this;
|
2025-01-23 21:20:01 +01:00
|
|
|
handle.is_tiled = !tiles.empty();
|
|
|
|
|
|
|
|
|
|
if (!handle.is_tiled) {
|
|
|
|
|
const size_t slot = add_image_slot(make_unique<OIIOImageLoader>(filename), params, false);
|
|
|
|
|
handle.slots.push_back(slot);
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
2020-03-08 10:42:11 +01:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
for (const int tile : tiles) {
|
2020-03-08 14:21:29 +01:00
|
|
|
string tile_filename = filename;
|
UDIM: Support virtual filenames
This implements the design detailed in T92696 to support virtual
filenames for UDIM textures. Currently, the following 2 substitution
tokens are supported:
| Token | Meaning |
| ----- | ---- |
| <UDIM> | 1001 + u-tile + v-tile * 10 |
| <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> |
Example for u-tile of 3 and v-tile of 1:
filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png
filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png
For image loading, the existing workflow is unchanged. A user can select
one or more image files, belonging to one or more UDIM tile sets, and
have Blender load them all as it does today. Now the <UVTILE> format is
"guessed" just as the <UDIM> format was guessed before.
If guessing fails, the user can simply go into the Image Editor and type
the proper substitution in the filename. Once typing is complete,
Blender will reload the files and correctly fill the tiles. This
workflow is new as attempting to fix the guessing in current versions
did not really work, and the user was often stuck with a confusing
situation.
For image saving, the existing workflow is changed slightly. Currently,
when saving, a user has to be sure to type the filename of the first
tile (e.g. filename.1001.png) to save the entire UDIM set. The number
could differ if they start at a different tile etc. This is confusing.
Now, the user should type a filename containing the appropriate
substitution token. By default Blender will fill in a default name using
the <UDIM> token but the user is free to save out images using <UVTILE>
if they wish.
Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
|
|
|
|
|
|
|
|
/* Since we don't have information about the exact tile format used in this code location,
|
|
|
|
|
* just attempt all replacement patterns that Blender supports. */
|
2025-01-23 21:20:01 +01:00
|
|
|
string_replace(tile_filename, "<UDIM>", string_printf("%04d", tile));
|
|
|
|
|
|
|
|
|
|
const int u = ((tile - 1001) % 10);
|
|
|
|
|
const int v = ((tile - 1001) / 10);
|
|
|
|
|
string_replace(tile_filename, "<UVTILE>", string_printf("u%d_v%d", u + 1, v + 1));
|
UDIM: Support virtual filenames
This implements the design detailed in T92696 to support virtual
filenames for UDIM textures. Currently, the following 2 substitution
tokens are supported:
| Token | Meaning |
| ----- | ---- |
| <UDIM> | 1001 + u-tile + v-tile * 10 |
| <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> |
Example for u-tile of 3 and v-tile of 1:
filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png
filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png
For image loading, the existing workflow is unchanged. A user can select
one or more image files, belonging to one or more UDIM tile sets, and
have Blender load them all as it does today. Now the <UVTILE> format is
"guessed" just as the <UDIM> format was guessed before.
If guessing fails, the user can simply go into the Image Editor and type
the proper substitution in the filename. Once typing is complete,
Blender will reload the files and correctly fill the tiles. This
workflow is new as attempting to fix the guessing in current versions
did not really work, and the user was often stuck with a confusing
situation.
For image saving, the existing workflow is changed slightly. Currently,
when saving, a user has to be sure to type the filename of the first
tile (e.g. filename.1001.png) to save the entire UDIM set. The number
could differ if they start at a different tile etc. This is confusing.
Now, the user should type a filename containing the appropriate
substitution token. By default Blender will fill in a default name using
the <UDIM> token but the user is free to save out images using <UVTILE>
if they wish.
Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
const size_t slot = add_image_slot(make_unique<OIIOImageLoader>(tile_filename), params, false);
|
2025-01-23 21:20:01 +01:00
|
|
|
handle.slots.push_back(slot);
|
2020-03-08 10:42:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
ImageHandle ImageManager::add_image(unique_ptr<ImageLoader> &&loader,
|
2020-08-06 13:55:50 +02:00
|
|
|
const ImageParams ¶ms,
|
|
|
|
|
const bool builtin)
|
2020-03-08 14:21:29 +01:00
|
|
|
{
|
2024-12-29 23:13:45 +01:00
|
|
|
const size_t slot = add_image_slot(std::move(loader), params, builtin);
|
2020-03-08 14:21:29 +01:00
|
|
|
|
|
|
|
|
ImageHandle handle;
|
2025-01-23 21:20:01 +01:00
|
|
|
handle.slots.push_back(slot);
|
2020-03-08 14:21:29 +01:00
|
|
|
handle.manager = this;
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
ImageHandle ImageManager::add_image(vector<unique_ptr<ImageLoader>> &&loaders,
|
2022-05-11 20:11:44 -07:00
|
|
|
const ImageParams ¶ms)
|
|
|
|
|
{
|
|
|
|
|
ImageHandle handle;
|
2025-01-23 21:20:01 +01:00
|
|
|
handle.is_tiled = true;
|
|
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
for (unique_ptr<ImageLoader> &loader : loaders) {
|
|
|
|
|
unique_ptr<ImageLoader> local_loader;
|
|
|
|
|
std::swap(loader, local_loader);
|
|
|
|
|
const size_t slot = add_image_slot(std::move(local_loader), params, true);
|
2025-01-23 21:20:01 +01:00
|
|
|
handle.slots.push_back(slot);
|
2022-05-11 20:11:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handle.manager = this;
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
size_t ImageManager::add_image_slot(unique_ptr<ImageLoader> &&loader,
|
2023-04-04 20:00:56 +02:00
|
|
|
const ImageParams ¶ms,
|
|
|
|
|
const bool builtin)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
size_t slot;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(images_mutex);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-05 16:23:34 +11:00
|
|
|
/* Find existing image. */
|
2020-02-26 17:31:33 +01:00
|
|
|
for (slot = 0; slot < images.size(); slot++) {
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *img = images[slot].get();
|
|
|
|
|
if (img && ImageLoader::equals(img->loader.get(), loader.get()) && img->params == params) {
|
2016-05-06 11:57:30 +02:00
|
|
|
img->users++;
|
2020-02-26 17:31:33 +01:00
|
|
|
return slot;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-03-07 12:27:18 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-06 11:57:30 +02:00
|
|
|
/* Find free slot. */
|
2020-02-26 17:31:33 +01:00
|
|
|
for (slot = 0; slot < images.size(); slot++) {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (!images[slot]) {
|
2016-05-06 11:57:30 +02:00
|
|
|
break;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2016-05-06 11:57:30 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-02-26 17:31:33 +01:00
|
|
|
if (slot == images.size()) {
|
|
|
|
|
images.resize(images.size() + 1);
|
2016-05-06 11:57:30 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-06 11:57:30 +02:00
|
|
|
/* Add new image. */
|
2024-12-29 23:13:45 +01:00
|
|
|
unique_ptr<Image> img = make_unique<Image>();
|
2020-03-08 14:21:29 +01:00
|
|
|
img->params = params;
|
2024-12-29 23:13:45 +01:00
|
|
|
img->loader = std::move(loader);
|
2020-03-08 14:21:29 +01:00
|
|
|
img->need_metadata = true;
|
2020-03-12 17:19:30 +01:00
|
|
|
img->need_load = !(osl_texture_system && !img->loader->osl_filepath().empty());
|
2020-03-08 14:21:29 +01:00
|
|
|
img->builtin = builtin;
|
2016-05-06 11:57:30 +02:00
|
|
|
img->users = 1;
|
2024-12-26 17:53:55 +01:00
|
|
|
img->mem = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
images[slot] = std::move(img);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
need_update_ = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-02-26 17:31:33 +01:00
|
|
|
return slot;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
void ImageManager::add_image_user(const size_t slot)
|
2019-05-15 00:42:51 +02:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(images_mutex);
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *image = images[slot].get();
|
2019-05-15 00:42:51 +02:00
|
|
|
assert(image && image->users >= 1);
|
|
|
|
|
|
|
|
|
|
image->users++;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
void ImageManager::remove_image_user(const size_t slot)
|
2014-03-29 13:03:48 +01:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(images_mutex);
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *image = images[slot].get();
|
2016-05-08 20:18:52 +02:00
|
|
|
assert(image && image->users >= 1);
|
2014-03-29 13:03:48 +01:00
|
|
|
|
2016-05-06 11:57:30 +02:00
|
|
|
/* decrement user count */
|
2016-05-08 20:18:52 +02:00
|
|
|
image->users--;
|
2014-03-29 13:03:48 +01:00
|
|
|
|
2016-05-06 11:57:30 +02:00
|
|
|
/* don't remove immediately, rather do it all together later on. one of
|
|
|
|
|
* the reasons for this is that on shader changes we add and remove nodes
|
|
|
|
|
* that use them, but we do not want to reload the image all the time. */
|
2023-09-17 09:01:48 +10:00
|
|
|
if (image->users == 0) {
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
need_update_ = true;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2014-03-29 13:03:48 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-08 20:32:31 +01:00
|
|
|
ImageManager::Image *ImageManager::get_image_slot(const size_t slot)
|
|
|
|
|
{
|
|
|
|
|
/* Need mutex lock, images vector might get resized by another thread. */
|
|
|
|
|
const thread_scoped_lock device_lock(images_mutex);
|
|
|
|
|
return images[slot].get();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-26 12:16:58 +02:00
|
|
|
static bool image_associate_alpha(ImageManager::Image *img)
|
|
|
|
|
{
|
|
|
|
|
/* For typical RGBA images we let OIIO convert to associated alpha,
|
|
|
|
|
* but some types we want to leave the RGB channels untouched. */
|
2020-03-08 14:21:29 +01:00
|
|
|
return !(ColorSpaceManager::colorspace_is_data(img->params.colorspace) ||
|
|
|
|
|
img->params.alpha_type == IMAGE_ALPHA_IGNORE ||
|
|
|
|
|
img->params.alpha_type == IMAGE_ALPHA_CHANNEL_PACKED);
|
2016-05-09 12:51:42 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-12 15:22:18 +01:00
|
|
|
template<TypeDesc::BASETYPE FileFormat, typename StorageType>
|
2025-01-01 18:15:54 +01:00
|
|
|
bool ImageManager::file_load_image(Image *img, const int texture_limit)
|
2016-05-09 12:51:42 +02:00
|
|
|
{
|
2020-12-11 19:08:45 +01:00
|
|
|
/* Ignore empty images. */
|
|
|
|
|
if (!(img->metadata.channels > 0)) {
|
2015-09-04 12:38:10 +05:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-14 16:18:34 +02:00
|
|
|
/* Get metadata. */
|
2024-12-29 17:32:00 +01:00
|
|
|
const int width = img->metadata.width;
|
|
|
|
|
const int height = img->metadata.height;
|
|
|
|
|
const int depth = img->metadata.depth;
|
|
|
|
|
const int components = img->metadata.channels;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-05-02 15:45:31 +02:00
|
|
|
/* Read pixels. */
|
2016-11-17 12:13:22 +01:00
|
|
|
vector<StorageType> pixels_storage;
|
|
|
|
|
StorageType *pixels;
|
|
|
|
|
const size_t max_size = max(max(width, height), depth);
|
2017-09-04 13:14:54 +02:00
|
|
|
if (max_size == 0) {
|
2019-05-02 15:45:31 +02:00
|
|
|
/* Don't bother with empty images. */
|
2017-09-04 13:14:54 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
|
|
|
|
/* Allocate memory as needed, may be smaller to resize down. */
|
2016-11-17 12:13:22 +01:00
|
|
|
if (texture_limit > 0 && max_size > texture_limit) {
|
|
|
|
|
pixels_storage.resize(((size_t)width) * height * depth * 4);
|
|
|
|
|
pixels = &pixels_storage[0];
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
pixels = (StorageType *)img->mem->alloc(width, height, depth);
|
2015-09-04 12:38:10 +05:00
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
if (pixels == nullptr) {
|
2017-09-04 13:14:54 +02:00
|
|
|
/* Could be that we've run out of memory. */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2016-11-29 11:03:11 +01:00
|
|
|
const size_t num_pixels = ((size_t)width) * height * depth;
|
2020-03-08 14:21:29 +01:00
|
|
|
img->loader->load_pixels(
|
|
|
|
|
img->metadata, pixels, num_pixels * components, image_associate_alpha(img));
|
2019-05-02 15:45:31 +02:00
|
|
|
|
|
|
|
|
/* The kernel can handle 1 and 4 channel images. Anything that is not a single
|
|
|
|
|
* channel image is converted to RGBA format. */
|
2024-12-29 17:32:00 +01:00
|
|
|
const bool is_rgba = (img->metadata.type == IMAGE_DATA_TYPE_FLOAT4 ||
|
|
|
|
|
img->metadata.type == IMAGE_DATA_TYPE_HALF4 ||
|
|
|
|
|
img->metadata.type == IMAGE_DATA_TYPE_BYTE4 ||
|
|
|
|
|
img->metadata.type == IMAGE_DATA_TYPE_USHORT4);
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2016-11-17 12:13:22 +01:00
|
|
|
if (is_rgba) {
|
2018-07-15 18:34:31 +02:00
|
|
|
const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
if (components == 2) {
|
2019-05-02 15:45:31 +02:00
|
|
|
/* Grayscale + alpha to RGBA. */
|
2016-05-22 23:24:26 +02:00
|
|
|
for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
|
|
|
|
|
pixels[i * 4 + 3] = pixels[i * 2 + 1];
|
|
|
|
|
pixels[i * 4 + 2] = pixels[i * 2 + 0];
|
|
|
|
|
pixels[i * 4 + 1] = pixels[i * 2 + 0];
|
|
|
|
|
pixels[i * 4 + 0] = pixels[i * 2 + 0];
|
2012-03-07 12:27:18 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2016-05-22 23:24:26 +02:00
|
|
|
else if (components == 3) {
|
2019-05-02 15:45:31 +02:00
|
|
|
/* RGB to RGBA. */
|
2016-05-22 23:24:26 +02:00
|
|
|
for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
|
2018-07-15 18:34:31 +02:00
|
|
|
pixels[i * 4 + 3] = one;
|
2016-05-22 23:24:26 +02:00
|
|
|
pixels[i * 4 + 2] = pixels[i * 3 + 2];
|
|
|
|
|
pixels[i * 4 + 1] = pixels[i * 3 + 1];
|
|
|
|
|
pixels[i * 4 + 0] = pixels[i * 3 + 0];
|
2012-03-07 12:27:18 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2016-05-22 23:24:26 +02:00
|
|
|
else if (components == 1) {
|
2019-05-02 15:45:31 +02:00
|
|
|
/* Grayscale to RGBA. */
|
2016-05-22 23:24:26 +02:00
|
|
|
for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
|
2018-07-15 18:34:31 +02:00
|
|
|
pixels[i * 4 + 3] = one;
|
2016-05-22 23:24:26 +02:00
|
|
|
pixels[i * 4 + 2] = pixels[i];
|
|
|
|
|
pixels[i * 4 + 1] = pixels[i];
|
|
|
|
|
pixels[i * 4 + 0] = pixels[i];
|
2014-05-07 16:36:44 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
|
|
|
|
/* Disable alpha if requested by the user. */
|
2020-03-08 14:21:29 +01:00
|
|
|
if (img->params.alpha_type == IMAGE_ALPHA_IGNORE) {
|
2016-05-22 23:24:26 +02:00
|
|
|
for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
|
2018-07-15 18:34:31 +02:00
|
|
|
pixels[i * 4 + 3] = one;
|
2016-05-09 12:51:42 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2022-01-20 19:09:41 +01:00
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2022-01-20 19:09:41 +01:00
|
|
|
if (img->metadata.colorspace != u_colorspace_raw &&
|
2024-01-02 18:12:54 +01:00
|
|
|
img->metadata.colorspace != u_colorspace_srgb)
|
|
|
|
|
{
|
2022-01-20 19:09:41 +01:00
|
|
|
/* Convert to scene linear. */
|
|
|
|
|
ColorSpaceManager::to_scene_linear(
|
|
|
|
|
img->metadata.colorspace, pixels, num_pixels, is_rgba, img->metadata.compress_as_srgb);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2017-05-17 15:29:47 +02:00
|
|
|
/* Make sure we don't have buggy values. */
|
2024-08-14 19:52:48 +02:00
|
|
|
if constexpr (FileFormat == TypeDesc::FLOAT) {
|
2017-05-17 15:29:47 +02:00
|
|
|
/* For RGBA buffers we put all channels to 0 if either of them is not
|
|
|
|
|
* finite. This way we avoid possible artifacts caused by fully changed
|
2019-05-02 15:45:31 +02:00
|
|
|
* hue. */
|
2017-05-17 15:29:47 +02:00
|
|
|
if (is_rgba) {
|
|
|
|
|
for (size_t i = 0; i < num_pixels; i += 4) {
|
|
|
|
|
StorageType *pixel = &pixels[i * 4];
|
|
|
|
|
if (!isfinite(pixel[0]) || !isfinite(pixel[1]) || !isfinite(pixel[2]) ||
|
2024-01-02 18:12:54 +01:00
|
|
|
!isfinite(pixel[3]))
|
|
|
|
|
{
|
2017-05-17 15:29:47 +02:00
|
|
|
pixel[0] = 0;
|
|
|
|
|
pixel[1] = 0;
|
|
|
|
|
pixel[2] = 0;
|
|
|
|
|
pixel[3] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (size_t i = 0; i < num_pixels; ++i) {
|
2017-05-18 15:49:03 +02:00
|
|
|
StorageType *pixel = &pixels[i];
|
2017-05-17 15:29:47 +02:00
|
|
|
if (!isfinite(pixel[0])) {
|
|
|
|
|
pixel[0] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2017-05-17 15:29:47 +02:00
|
|
|
/* Scale image down if needed. */
|
2024-12-26 17:53:59 +01:00
|
|
|
if (!pixels_storage.empty()) {
|
2016-11-17 12:13:22 +01:00
|
|
|
float scale_factor = 1.0f;
|
|
|
|
|
while (max_size * scale_factor > texture_limit) {
|
|
|
|
|
scale_factor *= 0.5f;
|
|
|
|
|
}
|
2022-06-16 19:39:13 +02:00
|
|
|
VLOG_WORK << "Scaling image " << img->loader->name() << " by a factor of " << scale_factor
|
|
|
|
|
<< ".";
|
2016-11-17 12:13:22 +01:00
|
|
|
vector<StorageType> scaled_pixels;
|
2024-12-29 17:32:00 +01:00
|
|
|
size_t scaled_width;
|
|
|
|
|
size_t scaled_height;
|
|
|
|
|
size_t scaled_depth;
|
2016-11-17 12:13:22 +01:00
|
|
|
util_image_resize_pixels(pixels_storage,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
depth,
|
|
|
|
|
is_rgba ? 4 : 1,
|
|
|
|
|
scale_factor,
|
|
|
|
|
&scaled_pixels,
|
|
|
|
|
&scaled_width,
|
|
|
|
|
&scaled_height,
|
|
|
|
|
&scaled_depth);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-10-21 01:09:59 +02:00
|
|
|
StorageType *texture_pixels;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-10-21 01:09:59 +02:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
texture_pixels = (StorageType *)img->mem->alloc(scaled_width, scaled_height, scaled_depth);
|
2017-10-21 01:09:59 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-11-17 12:13:22 +01:00
|
|
|
memcpy(texture_pixels, &scaled_pixels[0], scaled_pixels.size() * sizeof(StorageType));
|
|
|
|
|
}
|
2019-05-02 15:45:31 +02:00
|
|
|
|
2016-06-19 17:31:16 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
void ImageManager::device_load_image(Device *device,
|
|
|
|
|
Scene *scene,
|
|
|
|
|
const size_t slot,
|
|
|
|
|
Progress &progress)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
2024-12-26 17:53:55 +01:00
|
|
|
if (progress.get_cancel()) {
|
2012-05-05 19:44:33 +00:00
|
|
|
return;
|
2020-03-08 14:21:29 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *img = images[slot].get();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
progress.set_status("Updating Images", "Loading " + img->loader->name());
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-11-17 12:13:22 +01:00
|
|
|
const int texture_limit = scene->params.texture_limit;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
load_image_metadata(img);
|
2024-12-29 17:32:00 +01:00
|
|
|
const ImageDataType type = img->metadata.type;
|
2020-02-26 17:31:33 +01:00
|
|
|
|
2020-03-12 15:22:18 +01:00
|
|
|
/* Name for debugging. */
|
2023-04-04 20:00:56 +02:00
|
|
|
img->mem_name = string_printf("tex_image_%s_%03d", name_from_type(type), (int)slot);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-10-20 04:20:37 +02:00
|
|
|
/* Free previous texture in slot. */
|
|
|
|
|
if (img->mem) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2024-12-29 23:13:45 +01:00
|
|
|
img->mem.reset();
|
2017-10-20 04:20:37 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
img->mem = make_unique<device_texture>(
|
2020-03-12 15:22:18 +01:00
|
|
|
device, img->mem_name.c_str(), slot, type, img->params.interpolation, img->params.extension);
|
2020-03-17 16:48:00 +01:00
|
|
|
img->mem->info.use_transform_3d = img->metadata.use_transform_3d;
|
|
|
|
|
img->mem->info.transform_3d = img->metadata.transform_3d;
|
2020-03-12 15:22:18 +01:00
|
|
|
|
2017-10-20 04:20:37 +02:00
|
|
|
/* Create new texture. */
|
|
|
|
|
if (type == IMAGE_DATA_TYPE_FLOAT4) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::FLOAT, float>(img, texture_limit)) {
|
2012-07-04 11:48:42 +00:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
float *pixels = (float *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-04 11:48:42 +00:00
|
|
|
pixels[0] = TEX_IMAGE_MISSING_R;
|
|
|
|
|
pixels[1] = TEX_IMAGE_MISSING_G;
|
|
|
|
|
pixels[2] = TEX_IMAGE_MISSING_B;
|
|
|
|
|
pixels[3] = TEX_IMAGE_MISSING_A;
|
2012-03-07 12:27:18 +00:00
|
|
|
}
|
2016-05-09 12:51:42 +02:00
|
|
|
}
|
|
|
|
|
else if (type == IMAGE_DATA_TYPE_FLOAT) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::FLOAT, float>(img, texture_limit)) {
|
2016-05-09 12:51:42 +02:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
float *pixels = (float *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-09 12:51:42 +02:00
|
|
|
pixels[0] = TEX_IMAGE_MISSING_R;
|
|
|
|
|
}
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
2016-05-16 10:55:50 +02:00
|
|
|
else if (type == IMAGE_DATA_TYPE_BYTE4) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::UINT8, uchar>(img, texture_limit)) {
|
2012-07-04 11:48:42 +00:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
uchar *pixels = (uchar *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-04 11:48:42 +00:00
|
|
|
pixels[0] = (TEX_IMAGE_MISSING_R * 255);
|
|
|
|
|
pixels[1] = (TEX_IMAGE_MISSING_G * 255);
|
|
|
|
|
pixels[2] = (TEX_IMAGE_MISSING_B * 255);
|
|
|
|
|
pixels[3] = (TEX_IMAGE_MISSING_A * 255);
|
2012-03-07 12:27:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
2017-10-20 04:20:37 +02:00
|
|
|
else if (type == IMAGE_DATA_TYPE_BYTE) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::UINT8, uchar>(img, texture_limit)) {
|
2016-05-12 14:51:42 +02:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
uchar *pixels = (uchar *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-12 14:51:42 +02:00
|
|
|
pixels[0] = (TEX_IMAGE_MISSING_R * 255);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-20 04:20:37 +02:00
|
|
|
else if (type == IMAGE_DATA_TYPE_HALF4) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::HALF, half>(img, texture_limit)) {
|
2016-06-19 17:31:16 +02:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
half *pixels = (half *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-06-19 17:31:16 +02:00
|
|
|
pixels[0] = TEX_IMAGE_MISSING_R;
|
|
|
|
|
pixels[1] = TEX_IMAGE_MISSING_G;
|
|
|
|
|
pixels[2] = TEX_IMAGE_MISSING_B;
|
|
|
|
|
pixels[3] = TEX_IMAGE_MISSING_A;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-05 12:37:52 +02:00
|
|
|
else if (type == IMAGE_DATA_TYPE_USHORT) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::USHORT, uint16_t>(img, texture_limit)) {
|
2018-07-05 12:37:52 +02:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
uint16_t *pixels = (uint16_t *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-15 18:34:31 +02:00
|
|
|
pixels[0] = (TEX_IMAGE_MISSING_R * 65535);
|
2018-07-05 12:37:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (type == IMAGE_DATA_TYPE_USHORT4) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::USHORT, uint16_t>(img, texture_limit)) {
|
2018-07-05 12:37:52 +02:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
uint16_t *pixels = (uint16_t *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-15 18:34:31 +02:00
|
|
|
pixels[0] = (TEX_IMAGE_MISSING_R * 65535);
|
|
|
|
|
pixels[1] = (TEX_IMAGE_MISSING_G * 65535);
|
|
|
|
|
pixels[2] = (TEX_IMAGE_MISSING_B * 65535);
|
|
|
|
|
pixels[3] = (TEX_IMAGE_MISSING_A * 65535);
|
2018-07-05 12:37:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
2017-10-20 04:20:37 +02:00
|
|
|
else if (type == IMAGE_DATA_TYPE_HALF) {
|
2020-03-12 15:22:18 +01:00
|
|
|
if (!file_load_image<TypeDesc::HALF, half>(img, texture_limit)) {
|
2016-06-19 17:31:16 +02:00
|
|
|
/* on failure to load, we set a 1x1 pixels pink image */
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
half *pixels = (half *)img->mem->alloc(1, 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-06-19 17:31:16 +02:00
|
|
|
pixels[0] = TEX_IMAGE_MISSING_R;
|
|
|
|
|
}
|
2020-03-12 15:22:18 +01:00
|
|
|
}
|
2020-10-02 17:40:28 +02:00
|
|
|
#ifdef WITH_NANOVDB
|
2022-05-20 18:01:26 +02:00
|
|
|
else if (type == IMAGE_DATA_TYPE_NANOVDB_FLOAT || type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3 ||
|
|
|
|
|
type == IMAGE_DATA_TYPE_NANOVDB_FPN || type == IMAGE_DATA_TYPE_NANOVDB_FP16)
|
|
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-10-02 17:40:28 +02:00
|
|
|
void *pixels = img->mem->alloc(img->metadata.byte_size, 0);
|
|
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
if (pixels != nullptr) {
|
2020-10-02 17:40:28 +02:00
|
|
|
img->loader->load_pixels(img->metadata, pixels, img->metadata.byte_size, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-12 15:22:18 +01:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2020-03-12 15:22:18 +01:00
|
|
|
img->mem->copy_to_device();
|
2017-10-20 04:20:37 +02:00
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
|
|
|
|
|
/* Cleanup memory in image loader. */
|
|
|
|
|
img->loader->cleanup();
|
2012-05-05 19:44:33 +00:00
|
|
|
img->need_load = false;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
void ImageManager::device_free_image(Device * /*unused*/, size_t slot)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *img = images[slot].get();
|
2024-12-26 17:53:55 +01:00
|
|
|
if (img == nullptr) {
|
2020-03-08 14:21:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2012-03-07 12:27:18 +00:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
if (osl_texture_system) {
|
2011-05-31 16:21:30 +00:00
|
|
|
#ifdef WITH_OSL
|
2024-12-29 17:32:00 +01:00
|
|
|
const ustring filepath = img->loader->osl_filepath();
|
2020-03-12 17:19:30 +01:00
|
|
|
if (!filepath.empty()) {
|
2020-03-08 14:21:29 +01:00
|
|
|
((OSL::TextureSystem *)osl_texture_system)->invalidate(filepath);
|
2016-06-19 17:31:16 +02:00
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
2016-05-12 14:51:42 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
if (img->mem) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const thread_scoped_lock device_lock(device_mutex);
|
2024-12-29 23:13:45 +01:00
|
|
|
img->mem.reset();
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
|
2024-12-29 23:13:45 +01:00
|
|
|
images[slot].reset();
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-04 16:27:31 +02:00
|
|
|
void ImageManager::device_update(Device *device, Scene *scene, Progress &progress)
|
|
|
|
|
{
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
if (!need_update()) {
|
2017-05-04 16:27:31 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const scoped_callback_timer timer([scene](double time) {
|
2020-10-01 23:16:01 +02:00
|
|
|
if (scene->update_stats) {
|
|
|
|
|
scene->update_stats->image.times.add_entry({"device_update", time});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2017-05-04 16:27:31 +02:00
|
|
|
TaskPool pool;
|
2020-02-26 17:31:33 +01:00
|
|
|
for (size_t slot = 0; slot < images.size(); slot++) {
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *img = images[slot].get();
|
2020-03-08 14:21:29 +01:00
|
|
|
if (img && img->users == 0) {
|
2020-02-26 17:31:33 +01:00
|
|
|
device_free_image(device, slot);
|
|
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
else if (img && img->need_load) {
|
2024-12-26 17:53:55 +01:00
|
|
|
pool.push([this, device, scene, slot, &progress] {
|
|
|
|
|
device_load_image(device, scene, slot, progress);
|
|
|
|
|
});
|
2012-03-07 12:27:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-05 19:44:33 +00:00
|
|
|
pool.wait_work();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
need_update_ = false;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-04 20:00:56 +02:00
|
|
|
void ImageManager::device_update_slot(Device *device,
|
|
|
|
|
Scene *scene,
|
2025-01-01 18:15:54 +01:00
|
|
|
const size_t slot,
|
2024-12-26 17:53:55 +01:00
|
|
|
Progress &progress)
|
2015-04-02 20:37:57 +05:00
|
|
|
{
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *img = images[slot].get();
|
2024-12-26 17:53:55 +01:00
|
|
|
assert(img != nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-08 14:21:29 +01:00
|
|
|
if (img->users == 0) {
|
2020-02-26 17:31:33 +01:00
|
|
|
device_free_image(device, slot);
|
2015-04-02 20:37:57 +05:00
|
|
|
}
|
2020-03-08 14:21:29 +01:00
|
|
|
else if (img->need_load) {
|
|
|
|
|
device_load_image(device, scene, slot, progress);
|
2015-04-02 20:37:57 +05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 16:29:49 +02:00
|
|
|
void ImageManager::device_load_builtin(Device *device, Scene *scene, Progress &progress)
|
|
|
|
|
{
|
|
|
|
|
/* Load only builtin images, Blender needs this to load evaluated
|
|
|
|
|
* scene data from depsgraph before it is freed. */
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
if (!need_update()) {
|
2018-06-11 16:29:49 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-11 16:29:49 +02:00
|
|
|
TaskPool pool;
|
2020-02-26 17:31:33 +01:00
|
|
|
for (size_t slot = 0; slot < images.size(); slot++) {
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *img = images[slot].get();
|
2020-03-08 14:21:29 +01:00
|
|
|
if (img && img->need_load && img->builtin) {
|
2024-12-26 17:53:55 +01:00
|
|
|
pool.push([this, device, scene, slot, &progress] {
|
|
|
|
|
device_load_image(device, scene, slot, progress);
|
|
|
|
|
});
|
2018-06-11 16:29:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-11 16:29:49 +02:00
|
|
|
pool.wait_work();
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-20 04:20:37 +02:00
|
|
|
void ImageManager::device_free_builtin(Device *device)
|
2014-05-19 14:45:52 +02:00
|
|
|
{
|
2020-02-26 17:31:33 +01:00
|
|
|
for (size_t slot = 0; slot < images.size(); slot++) {
|
2024-12-29 23:13:45 +01:00
|
|
|
Image *img = images[slot].get();
|
2020-03-08 14:21:29 +01:00
|
|
|
if (img && img->builtin) {
|
2020-02-26 17:31:33 +01:00
|
|
|
device_free_image(device, slot);
|
2020-03-08 14:21:29 +01:00
|
|
|
}
|
2016-05-06 11:57:30 +02:00
|
|
|
}
|
2014-05-19 14:45:52 +02:00
|
|
|
}
|
|
|
|
|
|
2017-10-20 04:20:37 +02:00
|
|
|
void ImageManager::device_free(Device *device)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
2020-02-26 17:31:33 +01:00
|
|
|
for (size_t slot = 0; slot < images.size(); slot++) {
|
|
|
|
|
device_free_image(device, slot);
|
2016-05-06 11:57:30 +02:00
|
|
|
}
|
2020-02-26 17:31:33 +01:00
|
|
|
images.clear();
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-27 15:46:13 +02:00
|
|
|
void ImageManager::collect_statistics(RenderStats *stats)
|
|
|
|
|
{
|
2024-12-29 23:13:45 +01:00
|
|
|
for (const unique_ptr<Image> &image : images) {
|
2022-01-23 16:30:57 +01:00
|
|
|
if (!image) {
|
|
|
|
|
/* Image may have been freed due to lack of users. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-02-26 17:31:33 +01:00
|
|
|
stats->image.textures.add_entry(
|
2020-03-08 14:21:29 +01:00
|
|
|
NamedSizeEntry(image->loader->name(), image->mem->memory_size()));
|
2018-07-27 15:46:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
void ImageManager::tag_update()
|
|
|
|
|
{
|
|
|
|
|
need_update_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ImageManager::need_update() const
|
|
|
|
|
{
|
|
|
|
|
return need_update_;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
CCL_NAMESPACE_END
|