Refactor: Cycles: Move UDIM code out of SVM, refactor ImageHandle

Refactor ImageHandle to used pointers to a new ImageSlot istead of slot
indices. ImageSlot has subclasses ImageSingle and ImageUDIM, for
individual images and UDIMs. ImageUDIM has a list of handles for
ImageSingle.

UDIM tile lookup was moved out of SVM and OSL code, and is now shared.

Early loading of images for displacement and volumes was refactored.
Now a set of ImageSingle pointers is collected, which are then loaded
by the image manager.

Pull Request: https://projects.blender.org/blender/blender/pulls/154668
This commit is contained in:
Brecht Van Lommel 2026-02-15 02:37:52 +01:00
parent 84d4a2e9cf
commit 1ffb43fc69
29 changed files with 511 additions and 452 deletions

View file

@ -238,6 +238,7 @@ set(SRC_KERNEL_UTIL_HEADERS
util/colorspace.h
util/differential.h
util/ies.h
util/image_2d.h
util/image_3d.h
util/lookup_table.h
util/nanovdb.h

View file

@ -88,7 +88,8 @@ KERNEL_DATA_ARRAY(int, volume_tree_root_ids)
KERNEL_DATA_ARRAY(float, volume_step_size)
/* image textures */
KERNEL_DATA_ARRAY(KernelImageInfo, image_info)
KERNEL_DATA_ARRAY(KernelImageTexture, image_textures)
KERNEL_DATA_ARRAY(KernelImageUDIM, image_texture_udims)
KERNEL_DATA_ARRAY(KernelImageInfo, image_info)
#undef KERNEL_DATA_ARRAY

View file

@ -6,6 +6,7 @@
#include "kernel/device/cpu/compat.h"
#include "kernel/device/cpu/globals.h"
#include "kernel/util/image_2d.h"
#include "util/half.h"
#include "util/types_image.h"
@ -372,6 +373,19 @@ ccl_device float4 kernel_image_interp(KernelGlobals kg,
}
}
ccl_device_forceinline float4 kernel_image_interp_with_udim(KernelGlobals kg,
ShaderData * /*sd*/,
const int udim_id,
float2 uv)
{
const int image_texture_id = kernel_image_udim_map(kg, udim_id, uv);
if (image_texture_id == KERNEL_IMAGE_NONE) {
return IMAGE_MISSING_RGBA;
}
return kernel_image_interp(kg, image_texture_id, uv.x, uv.y);
}
} /* Namespace. */
CCL_NAMESPACE_END

View file

@ -5,6 +5,7 @@
#pragma once
#include "kernel/globals.h"
#include "kernel/util/image_2d.h"
CCL_NAMESPACE_BEGIN
@ -126,4 +127,17 @@ ccl_device float4 kernel_image_interp(KernelGlobals kg,
}
}
ccl_device_forceinline float4 kernel_image_interp_with_udim(KernelGlobals kg,
ccl_private ShaderData * /*sd*/,
const int udim_id,
float2 uv)
{
const int image_texture_id = kernel_image_udim_map(kg, udim_id, uv);
if (image_texture_id == KERNEL_IMAGE_NONE) {
return IMAGE_MISSING_RGBA;
}
return kernel_image_interp(kg, image_texture_id, uv.x, uv.y);
}
CCL_NAMESPACE_END

View file

@ -313,15 +313,15 @@ OSL::TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(
it.clear();
break;
}
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(
OSL_TEXTURE_HANDLE_TYPE_SVM | it->second.svm_image_texture_ids[0].y);
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_SVM |
it->second.id);
case OSLTextureHandle::IES:
if (!it->second.handle.empty() && it->second.handle.get_manager() != image_manager) {
it.clear();
break;
}
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(
OSL_TEXTURE_HANDLE_TYPE_IES | it->second.svm_image_texture_ids[0].y);
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_IES |
it->second.id);
case OSLTextureHandle::AO:
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(
OSL_TEXTURE_HANDLE_TYPE_AO_OR_BEVEL | 1);
@ -346,7 +346,7 @@ OSL::TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(
}
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_SVM |
handle.svm_image_texture_id());
handle.kernel_id());
}
bool OSLRenderServices::good(OSL::TextureSystem::TextureHandle *texture_handle)
@ -425,37 +425,8 @@ bool OSLRenderServices::texture(OSLUStringHash filename,
break;
}
case OSLTextureHandle::SVM: {
int id = -1;
if (handle->svm_image_texture_ids[0].w == -1) {
/* Packed single texture. */
id = handle->svm_image_texture_ids[0].y;
}
else {
/* Packed tiled texture. */
const int tx = (int)s;
const int ty = (int)t;
const int tile = 1001 + 10 * ty + tx;
for (const int4 &tile_node : handle->svm_image_texture_ids) {
if (tile_node.x == tile) {
id = tile_node.y;
break;
}
if (tile_node.z == tile) {
id = tile_node.w;
break;
}
}
s -= tx;
t -= ty;
}
float4 rgba;
if (id == -1) {
rgba = IMAGE_MISSING_RGBA;
}
else {
rgba = kernel_image_interp(kernel_globals, id, s, 1.0f - t);
}
const float4 rgba = kernel_image_interp_with_udim(
kernel_globals, sd, handle->id, make_float2(s, 1.0f - t));
result[0] = rgba[0];
if (nchannels > 1) {
@ -472,7 +443,7 @@ bool OSLRenderServices::texture(OSLUStringHash filename,
}
case OSLTextureHandle::IES: {
/* IES light. */
result[0] = kernel_ies_interp(kernel_globals, handle->svm_image_texture_ids[0].y, s, t);
result[0] = kernel_ies_interp(kernel_globals, handle->id, s, t);
status = true;
break;
}
@ -558,10 +529,9 @@ bool OSLRenderServices::texture3d(OSLUStringHash filename,
switch (texture_type) {
case OSLTextureHandle::SVM: {
/* Packed texture. */
const int image_texture_id = handle->svm_image_texture_ids[0].y;
const float3 P_float3 = make_float3(P.x, P.y, P.z);
float4 rgba = kernel_image_interp_3d(
kernel_globals, globals->sd, image_texture_id, P_float3, INTERPOLATION_NONE, false);
kernel_globals, globals->sd, handle->id, P_float3, INTERPOLATION_NONE, false);
result[0] = rgba[0];
if (nchannels > 1) {

View file

@ -48,23 +48,14 @@ struct ThreadKernelGlobalsCPU;
struct OSLTextureHandle {
enum Type { OIIO, SVM, IES, BEVEL, AO };
OSLTextureHandle(Type type, const vector<int4> &svm_image_texture_ids)
: type(type), svm_image_texture_ids(svm_image_texture_ids)
{
}
OSLTextureHandle(Type type, const int id = -1) : type(type), id(id) {}
OSLTextureHandle(Type type = OIIO, const int svm_image_texture_ids = -1)
: OSLTextureHandle(type, {make_int4(0, svm_image_texture_ids, -1, -1)})
{
}
OSLTextureHandle(const ImageHandle &handle)
: type(SVM), svm_image_texture_ids(handle.get_svm_image_texture_ids()), handle(handle)
OSLTextureHandle(const ImageHandle &handle) : type(SVM), id(handle.kernel_id()), handle(handle)
{
}
Type type;
vector<int4> svm_image_texture_ids;
int id = -1;
OSL::TextureSystem::TextureHandle *oiio_handle = nullptr;
ColorSpaceProcessor *processor = nullptr;
ImageHandle handle;

View file

@ -283,7 +283,7 @@ ccl_device_extern bool rend_get_userdata(RSDeviceString name,
return false;
}
ccl_device_extern bool rs_texture(ccl_private ShaderGlobals * /*sg*/,
ccl_device_extern bool rs_texture(ccl_private ShaderGlobals *sg,
RSDeviceString /*filename*/,
ccl_private void *texture_handle,
ccl_private void * /*texture_thread_info*/,
@ -305,7 +305,9 @@ ccl_device_extern bool rs_texture(ccl_private ShaderGlobals * /*sg*/,
switch (type) {
case OSL_TEXTURE_HANDLE_TYPE_SVM: {
const float4 rgba = kernel_image_interp(nullptr, image_texture_id, s, 1.0f - t);
ccl_private ShaderData *sd = sg->sd;
const float4 rgba = kernel_image_interp_with_udim(
nullptr, sd, image_texture_id, make_float2(s, 1.0f - t));
if (nchannels > 0) {
result[0] = rgba.x;
}

View file

@ -18,14 +18,10 @@
CCL_NAMESPACE_BEGIN
ccl_device float4
svm_image_texture(KernelGlobals kg, const int id, const float x, float y, const uint flags)
ccl_device float4 svm_image_texture(
KernelGlobals kg, ccl_private ShaderData *sd, const int id, const float2 uv, const uint flags)
{
if (id == -1) {
return IMAGE_MISSING_RGBA;
}
float4 r = kernel_image_interp(kg, id, x, y);
float4 r = kernel_image_interp_with_udim(kg, sd, id, uv);
const float alpha = r.w;
if ((flags & NODE_IMAGE_ALPHA_UNASSOCIATE) && alpha != 1.0f && alpha != 0.0f) {
@ -47,7 +43,7 @@ ccl_device_inline float3 texco_remap_square(const float3 co)
}
ccl_device_noinline int svm_node_tex_image(KernelGlobals kg,
ccl_private ShaderData * /*sd*/,
ccl_private ShaderData *sd,
ccl_private float *stack,
const uint4 node,
int offset)
@ -73,50 +69,8 @@ ccl_device_noinline int svm_node_tex_image(KernelGlobals kg,
tex_co = make_float2(co.x, co.y);
}
/* TODO(lukas): Consider moving tile information out of the SVM node.
* KernelImageInfo seems a reasonable candidate. */
int id = -1;
const int num_nodes = (int)node.y;
if (num_nodes > 0) {
/* Remember the offset of the node following the tile nodes. */
const int next_offset = offset + num_nodes;
/* Find the tile that the UV lies in. */
const int tx = (int)tex_co.x;
const int ty = (int)tex_co.y;
/* Check that we're within a legitimate tile. */
if (tx >= 0 && ty >= 0 && tx < 10) {
const int tile = 1001 + 10 * ty + tx;
/* Find the index of the tile. */
for (int i = 0; i < num_nodes; i++) {
const uint4 tile_node = read_node(kg, &offset);
if (tile_node.x == tile) {
id = tile_node.y;
break;
}
if (tile_node.z == tile) {
id = tile_node.w;
break;
}
}
/* If we found the tile, offset the UVs to be relative to it. */
if (id != -1) {
tex_co.x -= tx;
tex_co.y -= ty;
}
}
/* Skip over the remaining nodes. */
offset = next_offset;
}
else {
id = -num_nodes;
}
const float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, flags);
const int id = node.y;
const float4 f = svm_image_texture(kg, sd, id, tex_co, flags);
if (stack_valid(out_offset)) {
stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z));
@ -212,15 +166,15 @@ ccl_device_noinline void svm_node_tex_image_box(KernelGlobals kg,
/* Map so that no textures are flipped, rotation is somewhat arbitrary. */
if (weight.x > 0.0f) {
const float2 uv = make_float2((signed_N.x < 0.0f) ? 1.0f - co.y : co.y, co.z);
f += weight.x * svm_image_texture(kg, id, uv.x, uv.y, flags);
f += weight.x * svm_image_texture(kg, sd, id, uv, flags);
}
if (weight.y > 0.0f) {
const float2 uv = make_float2((signed_N.y > 0.0f) ? 1.0f - co.x : co.x, co.z);
f += weight.y * svm_image_texture(kg, id, uv.x, uv.y, flags);
f += weight.y * svm_image_texture(kg, sd, id, uv, flags);
}
if (weight.z > 0.0f) {
const float2 uv = make_float2((signed_N.z > 0.0f) ? 1.0f - co.y : co.y, co.x);
f += weight.z * svm_image_texture(kg, id, uv.x, uv.y, flags);
f += weight.z * svm_image_texture(kg, sd, id, uv, flags);
}
if (stack_valid(out_offset)) {
@ -232,7 +186,7 @@ ccl_device_noinline void svm_node_tex_image_box(KernelGlobals kg,
}
ccl_device_noinline void svm_node_tex_environment(KernelGlobals kg,
ccl_private ShaderData * /*sd*/,
ccl_private ShaderData *sd,
ccl_private float *stack,
const uint4 node)
{
@ -257,7 +211,7 @@ ccl_device_noinline void svm_node_tex_environment(KernelGlobals kg,
uv = direction_to_mirrorball(co);
}
const float4 f = svm_image_texture(kg, id, uv.x, uv.y, flags);
const float4 f = svm_image_texture(kg, sd, id, uv, flags);
if (stack_valid(out_offset)) {
stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z));

View file

@ -0,0 +1,48 @@
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/sample/lcg.h"
#include "util/atomic.h"
#include "util/defines.h"
#include "util/math_fast.h"
#include "util/types_image.h"
CCL_NAMESPACE_BEGIN
ccl_device_forceinline int kernel_image_udim_map(KernelGlobals kg,
const int id,
ccl_private float2 &uv)
{
if (id >= 0) {
return id;
}
const int tx = (int)uv.x;
const int ty = (int)uv.y;
if (tx < 0 || ty < 0 || tx >= 10) {
return KERNEL_IMAGE_NONE;
}
int udim_id = -id - 1;
const int num_udims = kernel_data_fetch(image_texture_udims, udim_id++).tile;
const int tile = 1001 + 10 * ty + tx;
for (int i = 0; i < num_udims; i++) {
const KernelImageUDIM udim = kernel_data_fetch(image_texture_udims, udim_id++);
if (udim.tile == tile) {
/* If we found the tile, offset the UVs to be relative to it. */
uv.x -= tx;
uv.y -= ty;
return udim.image_texture_id;
}
}
return KERNEL_IMAGE_NONE;
}
CCL_NAMESPACE_END

View file

@ -250,7 +250,7 @@ ccl_device float4 kernel_image_interp_3d(KernelGlobals kg,
#else
(void)kg;
(void)sd;
(void)id;
(void)image_texture_id;
(void)P;
(void)interp;
(void)stochastic;

View file

@ -58,7 +58,8 @@ DeviceScene::DeviceScene(Device *device)
volume_tree_roots(device, "volume_tree_roots", MEM_GLOBAL),
volume_tree_root_ids(device, "volume_tree_root_ids", MEM_GLOBAL),
volume_step_size(device, "volume_step_size", MEM_GLOBAL),
image_textures(device, "image_textures", MEM_GLOBAL)
image_textures(device, "image_textures", MEM_GLOBAL),
image_texture_udims(device, "image_texture_udims", MEM_GLOBAL)
{
memset((void *)&data, 0, sizeof(data));
}

View file

@ -95,6 +95,7 @@ class DeviceScene {
/* Image textures */
device_vector<KernelImageTexture> image_textures;
device_vector<KernelImageUDIM> image_texture_udims;
KernelData data;

View file

@ -620,9 +620,8 @@ void GeometryManager::device_update_displacement_images(Device *device,
Progress &progress)
{
progress.set_status("Updating Displacement Images");
TaskPool pool;
ImageManager *image_manager = scene->image_manager.get();
set<int> bump_images;
set<const ImageSingle *> bump_images;
#ifdef WITH_OSL
bool has_osl_node = false;
#endif
@ -655,11 +654,8 @@ void GeometryManager::device_update_displacement_images(Device *device,
}
ImageSlotTextureNode *image_node = static_cast<ImageSlotTextureNode *>(node);
for (int i = 0; i < image_node->handle.num_svm_image_texture_ids(); i++) {
const int image_texture_id = image_node->handle.svm_image_texture_id(i);
if (image_texture_id != -1) {
bump_images.insert(image_texture_id);
}
if (!image_node->handle.empty()) {
image_node->handle.add_to_set(bump_images);
}
}
}
@ -670,16 +666,11 @@ void GeometryManager::device_update_displacement_images(Device *device,
/* If any OSL node is used for displacement, it may reference a texture. But it's
* unknown which ones, so have to load them all. */
if (has_osl_node) {
OSLShaderManager::osl_image_slots(device, image_manager, bump_images);
OSLShaderManager::osl_image_handles(device, image_manager, bump_images);
}
#endif
for (const int image_texture_id : bump_images) {
pool.push([image_manager, device, scene, image_texture_id, &progress] {
image_manager->device_update_image_texture_id(device, scene, image_texture_id, progress);
});
}
pool.wait_work();
image_manager->device_load_images(device, scene, progress, bump_images);
}
void GeometryManager::device_update_volume_images(Device *device, Scene *scene, Progress &progress)
@ -687,7 +678,7 @@ void GeometryManager::device_update_volume_images(Device *device, Scene *scene,
progress.set_status("Updating Volume Images");
TaskPool pool;
ImageManager *image_manager = scene->image_manager.get();
set<int> volume_images;
set<const ImageSingle *> volume_images;
for (Geometry *geom : scene->geometry) {
if (!geom->is_modified()) {
@ -700,19 +691,13 @@ void GeometryManager::device_update_volume_images(Device *device, Scene *scene,
}
const ImageHandle &handle = attr.data_voxel();
const int image_texture_id = handle.svm_image_texture_id();
if (image_texture_id != -1) {
volume_images.insert(image_texture_id);
if (!handle.empty()) {
handle.add_to_set(volume_images);
}
}
}
for (const int image_texture_id : volume_images) {
pool.push([image_manager, device, scene, image_texture_id, &progress] {
image_manager->device_update_image_texture_id(device, scene, image_texture_id, progress);
});
}
pool.wait_work();
image_manager->device_load_images(device, scene, progress, volume_images);
}
void GeometryManager::device_update(Device *device,

View file

@ -334,7 +334,7 @@ class AttributeTableBuilder {
if (mattr->element & ATTR_ELEMENT_VOXEL) {
/* store slot in offset value */
const ImageHandle &handle = mattr->data_voxel();
offset = handle.svm_image_texture_id();
offset = handle.kernel_id();
}
else if (mattr->element & ATTR_ELEMENT_IS_BYTE) {
offset = attr_uchar4.add(mattr->data_uchar4(), size, mattr->modified);

View file

@ -11,9 +11,6 @@
#include "scene/stats.h"
#include "util/colorspace.h"
#include "util/image.h"
#include "util/image_impl.h"
#include "util/log.h"
#include "util/progress.h"
#include "util/task.h"
#include "util/types_image.h"
@ -26,31 +23,56 @@ CCL_NAMESPACE_BEGIN
/* Image Handle */
ImageHandle::ImageHandle() : manager(nullptr) {}
ImageHandle::ImageHandle() = default;
ImageHandle::ImageHandle(ImageTexture *image_texture, ImageManager *manager)
: image_texture(image_texture), manager(manager)
{
if (image_texture) {
image_texture->users++;
}
}
ImageHandle::ImageHandle(const ImageHandle &other)
: image_texture_ids(other.image_texture_ids), is_tiled(other.is_tiled), manager(other.manager)
: image_texture(other.image_texture), manager(other.manager)
{
/* Increase image user count. */
for (const size_t image_texture_id : image_texture_ids) {
manager->add_image_user(image_texture_id);
if (image_texture) {
image_texture->users++;
}
}
ImageHandle::ImageHandle(ImageHandle &&other) noexcept
: image_texture(other.image_texture), manager(other.manager)
{
other.image_texture = nullptr;
other.manager = nullptr;
}
ImageHandle &ImageHandle::operator=(const ImageHandle &other)
{
clear();
image_texture = other.image_texture;
manager = other.manager;
is_tiled = other.is_tiled;
image_texture_ids = other.image_texture_ids;
for (const size_t image_texture_id : image_texture_ids) {
manager->add_image_user(image_texture_id);
if (image_texture) {
image_texture->users++;
}
return *this;
}
ImageHandle &ImageHandle::operator=(ImageHandle &&other) noexcept
{
if (this != &other) {
clear();
image_texture = other.image_texture;
manager = other.manager;
other.image_texture = nullptr;
other.manager = nullptr;
}
return *this;
}
ImageHandle::~ImageHandle()
{
clear();
@ -58,110 +80,82 @@ ImageHandle::~ImageHandle()
void ImageHandle::clear()
{
for (const size_t image_texture_id : image_texture_ids) {
manager->remove_image_user(image_texture_id);
/* 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. */
if (image_texture) {
assert(image_texture->users >= 1);
image_texture->users--;
if (image_texture->users == 0) {
manager->tag_update();
}
image_texture = nullptr;
}
image_texture_ids.clear();
manager = nullptr;
}
bool ImageHandle::empty() const
{
return image_texture_ids.empty();
return image_texture == nullptr;
}
int ImageHandle::num_tiles() const
{
return (is_tiled) ? image_texture_ids.size() : 0;
}
int ImageHandle::num_svm_image_texture_ids() const
{
return image_texture_ids.size();
}
ImageMetaData ImageHandle::metadata()
{
if (image_texture_ids.empty()) {
return ImageMetaData();
if (image_texture && image_texture->type == ImageTexture::UDIM) {
ImageUDIM *udim = static_cast<ImageUDIM *>(image_texture);
return udim->tiles.size();
}
ImageManager::Image *img = manager->get_image_texture(image_texture_ids.front());
manager->load_image_metadata(img);
return img->metadata;
return 0;
}
int ImageHandle::svm_image_texture_id(const int image_texture_id_index) const
ImageMetaData ImageHandle::metadata(Progress &progress)
{
if (image_texture_id_index >= image_texture_ids.size()) {
return -1;
}
if (manager->osl_texture_system) {
ImageManager::Image *img = manager->get_image_texture(
image_texture_ids[image_texture_id_index]);
if (!img->loader->osl_filepath().empty()) {
return -1;
if (image_texture) {
if (image_texture->type == ImageTexture::SINGLE) {
ImageSingle *img = static_cast<ImageSingle *>(image_texture);
manager->load_image_metadata(img, progress);
return img->metadata;
}
if (image_texture->type == ImageTexture::UDIM) {
ImageUDIM *udim = static_cast<ImageUDIM *>(image_texture);
return udim->tiles[0].second.metadata(progress);
}
}
return image_texture_ids[image_texture_id_index];
return ImageMetaData();
}
vector<int4> ImageHandle::get_svm_image_texture_ids() const
int ImageHandle::kernel_id() const
{
const size_t num_nodes = divide_up(image_texture_ids.size(), 2);
vector<int4> svm_image_texture_ids;
svm_image_texture_ids.reserve(num_nodes);
for (size_t i = 0; i < num_nodes; i++) {
int4 node;
size_t image_texture_id = image_texture_ids[2 * i];
node.x = manager->get_image_texture(image_texture_id)->loader->get_tile_number();
node.y = image_texture_id;
if ((2 * i + 1) < image_texture_ids.size()) {
image_texture_id = image_texture_ids[2 * i + 1];
node.z = manager->get_image_texture(image_texture_id)->loader->get_tile_number();
node.w = image_texture_id;
}
else {
node.z = -1;
node.w = -1;
}
svm_image_texture_ids.push_back(node);
if (!image_texture) {
return KERNEL_IMAGE_NONE;
}
return svm_image_texture_ids;
if (image_texture->type == ImageTexture::SINGLE) {
return static_cast<const ImageSingle *>(image_texture)->image_texture_id;
}
return static_cast<const ImageUDIM *>(image_texture)->id;
}
device_image *ImageHandle::image_memory() const
device_image *ImageHandle::vdb_image_memory() const
{
if (image_texture_ids.empty()) {
if (image_texture == nullptr || image_texture->type != ImageTexture::SINGLE) {
return nullptr;
}
ImageManager::Image *img = manager->get_image_texture(image_texture_ids[0]);
return img ? img->mem : nullptr;
ImageSingle *img = static_cast<ImageSingle *>(image_texture);
return img->vdb_memory;
}
VDBImageLoader *ImageHandle::vdb_loader() const
{
if (image_texture_ids.empty()) {
return nullptr;
}
ImageManager::Image *img = manager->get_image_texture(image_texture_ids[0]);
if (img == nullptr) {
if (image_texture == nullptr || image_texture->type != ImageTexture::SINGLE) {
return nullptr;
}
ImageSingle *img = static_cast<ImageSingle *>(image_texture);
ImageLoader *loader = img->loader.get();
if (loader == nullptr) {
return nullptr;
}
@ -180,10 +174,29 @@ ImageManager *ImageHandle::get_manager() const
bool ImageHandle::operator==(const ImageHandle &other) const
{
return manager == other.manager && is_tiled == other.is_tiled &&
image_texture_ids == other.image_texture_ids;
return image_texture == other.image_texture && manager == other.manager;
}
void ImageHandle::add_to_set(set<const ImageSingle *> &images) const
{
if (empty()) {
return;
}
if (image_texture->type == ImageTexture::SINGLE) {
images.insert(static_cast<const ImageSingle *>(image_texture));
}
else {
for (const auto &tile : static_cast<const ImageUDIM *>(image_texture)->tiles) {
images.insert(static_cast<const ImageSingle *>(tile.second.image_texture));
}
}
}
/* Image Single */
ImageSingle::~ImageSingle() = default;
/* Image Manager */
ImageManager::ImageManager(const DeviceInfo & /*info*/)
@ -195,8 +208,9 @@ ImageManager::ImageManager(const DeviceInfo & /*info*/)
ImageManager::~ImageManager()
{
for (size_t image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
assert(!images[image_texture_id]);
for (ImageSingle *img : images) {
assert(!img);
(void)img;
}
}
@ -211,8 +225,8 @@ bool ImageManager::set_animation_frame_update(const int frame)
const thread_scoped_lock device_lock(images_mutex);
animation_frame = frame;
for (size_t image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
if (images[image_texture_id] && images[image_texture_id]->params.animated) {
for (ImageSingle *img : images) {
if (img && img->params.animated) {
return true;
}
}
@ -221,7 +235,7 @@ bool ImageManager::set_animation_frame_update(const int frame)
return false;
}
void ImageManager::load_image_metadata(Image *img)
void ImageManager::load_image_metadata(ImageSingle *img, Progress & /*progress*/)
{
if (!img->need_metadata) {
return;
@ -255,30 +269,19 @@ void ImageManager::load_image_metadata(Image *img)
ImageHandle ImageManager::add_image(const string &filename, const ImageParams &params)
{
const size_t image_texture_id = add_image_texture(
make_unique<OIIOImageLoader>(filename), params, false);
ImageHandle handle;
handle.image_texture_ids.push_back(image_texture_id);
handle.manager = this;
return handle;
ImageSingle *image = add_image_texture(make_unique<OIIOImageLoader>(filename), params, false);
return ImageHandle(image, this);
}
ImageHandle ImageManager::add_image(const string &filename,
const ImageParams &params,
const array<int> &tiles)
{
ImageHandle handle;
handle.manager = this;
handle.is_tiled = !tiles.empty();
if (!handle.is_tiled) {
const size_t image_texture_id = add_image_texture(
make_unique<OIIOImageLoader>(filename), params, false);
handle.image_texture_ids.push_back(image_texture_id);
return handle;
if (tiles.empty()) {
return add_image(filename, params);
}
vector<std::pair<int, ImageHandle>> udim_tiles;
for (const int tile : tiles) {
string tile_filename = filename;
@ -290,57 +293,51 @@ ImageHandle ImageManager::add_image(const string &filename,
const int v = ((tile - 1001) / 10);
string_replace(tile_filename, "<UVTILE>", string_printf("u%d_v%d", u + 1, v + 1));
const size_t image_texture_id = add_image_texture(
ImageSingle *image = add_image_texture(
make_unique<OIIOImageLoader>(tile_filename), params, false);
handle.image_texture_ids.push_back(image_texture_id);
udim_tiles.emplace_back(tile, ImageHandle(image, this));
}
return handle;
ImageUDIM *udim = add_image_texture(std::move(udim_tiles));
return ImageHandle(udim, this);
}
ImageHandle ImageManager::add_image(unique_ptr<ImageLoader> &&loader,
const ImageParams &params,
const bool builtin)
{
const size_t image_texture_id = add_image_texture(std::move(loader), params, builtin);
ImageHandle handle;
handle.image_texture_ids.push_back(image_texture_id);
handle.manager = this;
return handle;
ImageSingle *image = add_image_texture(std::move(loader), params, builtin);
return ImageHandle(image, this);
}
ImageHandle ImageManager::add_image(vector<unique_ptr<ImageLoader>> &&loaders,
const ImageParams &params)
{
ImageHandle handle;
handle.is_tiled = true;
vector<std::pair<int, ImageHandle>> udim_tiles;
for (unique_ptr<ImageLoader> &loader : loaders) {
unique_ptr<ImageLoader> local_loader;
std::swap(loader, local_loader);
const size_t image_texture_id = add_image_texture(std::move(local_loader), params, true);
handle.image_texture_ids.push_back(image_texture_id);
ImageSingle *image = add_image_texture(std::move(local_loader), params, true);
udim_tiles.emplace_back(image->loader->get_tile_number(), ImageHandle(image, this));
}
handle.manager = this;
return handle;
ImageUDIM *udim = add_image_texture(std::move(udim_tiles));
return ImageHandle(udim, this);
}
size_t ImageManager::add_image_texture(unique_ptr<ImageLoader> &&loader,
const ImageParams &params,
const bool builtin)
ImageSingle *ImageManager::add_image_texture(unique_ptr<ImageLoader> &&loader,
const ImageParams &params,
const bool builtin)
{
size_t image_texture_id;
const thread_scoped_lock device_lock(images_mutex);
/* Find existing image. */
size_t image_texture_id;
for (image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
Image *img = images[image_texture_id].get();
ImageSingle *img = images[image_texture_id];
if (img && ImageLoader::equals(img->loader.get(), loader.get()) && img->params == params) {
img->users++;
return image_texture_id;
return img;
}
}
@ -356,53 +353,58 @@ size_t ImageManager::add_image_texture(unique_ptr<ImageLoader> &&loader,
}
/* Add new image. */
unique_ptr<Image> img = make_unique<Image>();
unique_ptr<ImageSingle> img = make_unique<ImageSingle>();
img->type = ImageTexture::SINGLE;
img->params = params;
img->loader = std::move(loader);
img->need_metadata = true;
img->need_load = !(osl_texture_system && !img->loader->osl_filepath().empty());
img->image_texture_id = (img->need_load) ? image_texture_id : KERNEL_IMAGE_NONE;
img->builtin = builtin;
img->users = 1;
img->mem = nullptr;
images[image_texture_id] = std::move(img);
images.replace(image_texture_id, std::move(img));
need_update_ = true;
tag_update();
return image_texture_id;
return images[image_texture_id];
}
void ImageManager::add_image_user(const size_t image_texture_id)
ImageUDIM *ImageManager::add_image_texture(vector<std::pair<int, ImageHandle>> &&tiles)
{
const thread_scoped_lock device_lock(images_mutex);
Image *image = images[image_texture_id].get();
assert(image && image->users >= 1);
image->users++;
}
void ImageManager::remove_image_user(const size_t image_texture_id)
{
const thread_scoped_lock device_lock(images_mutex);
Image *image = images[image_texture_id].get();
assert(image && image->users >= 1);
/* decrement user count */
image->users--;
/* 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. */
if (image->users == 0) {
need_update_ = true;
/* Find existing UDIM. */
size_t image_texture_id;
for (image_texture_id = 0; image_texture_id < image_udims.size(); image_texture_id++) {
ImageUDIM *udim = image_udims[image_texture_id];
if (udim && udim->tiles == tiles) {
return udim;
}
}
}
ImageManager::Image *ImageManager::get_image_texture(const size_t image_texture_id)
{
/* Need mutex lock, images vector might get resized by another thread. */
const thread_scoped_lock device_lock(images_mutex);
return images[image_texture_id].get();
/* Find free image_texture_id. */
for (image_texture_id = 0; image_texture_id < image_udims.size(); image_texture_id++) {
if (!image_udims[image_texture_id]) {
break;
}
}
if (image_texture_id == image_udims.size()) {
image_udims.resize(image_udims.size() + 1);
}
/* Add new image. */
unique_ptr<ImageUDIM> img = make_unique<ImageUDIM>();
img->type = ImageTexture::UDIM;
img->id = -num_udim_tiles - 1;
img->tiles = std::move(tiles);
num_udim_tiles += img->tiles.size() + 1;
image_udims.replace(image_texture_id, std::move(img));
tag_update();
return image_udims[image_texture_id];
}
void ImageManager::device_resize_image_textures(Scene *scene)
@ -423,6 +425,7 @@ void ImageManager::device_copy_image_textures(Scene *scene)
DeviceScene &dscene = scene->dscene;
dscene.image_textures.copy_to_device_if_modified();
dscene.image_texture_udims.copy_to_device_if_modified();
}
void ImageManager::device_load_image(Device *device,
@ -434,15 +437,11 @@ void ImageManager::device_load_image(Device *device,
return;
}
Image *img = images[image_texture_id].get();
if (img->users == 0) {
return;
}
ImageSingle *img = images[image_texture_id];
progress.set_status("Updating Images", "Loading " + img->loader->name());
load_image_metadata(img);
load_image_metadata(img, progress);
KernelImageTexture tex;
tex.width = img->metadata.width;
@ -452,7 +451,7 @@ void ImageManager::device_load_image(Device *device,
tex.use_transform_3d = img->metadata.use_transform_3d;
tex.transform_3d = img->metadata.transform_3d;
img->mem = image_cache.load_image_full(
img->vdb_memory = image_cache.load_image_full(
*device, *img->loader, img->metadata, scene->params.texture_limit, tex);
/* Update image texture device data. */
@ -466,7 +465,7 @@ void ImageManager::device_load_image(Device *device,
void ImageManager::device_free_image(Scene *scene, size_t image_texture_id)
{
Image *img = images[image_texture_id].get();
ImageSingle *img = images[image_texture_id];
if (img == nullptr) {
return;
}
@ -480,13 +479,44 @@ void ImageManager::device_free_image(Scene *scene, size_t image_texture_id)
#endif
}
if (img->mem) {
const KernelImageTexture &tex = scene->dscene.image_textures[image_texture_id];
image_cache.free_image(scene->dscene, tex);
img->mem = nullptr;
const KernelImageTexture &tex = scene->dscene.image_textures[image_texture_id];
image_cache.free_image(scene->dscene, tex);
img->vdb_memory = nullptr;
images.steal(image_texture_id);
}
void ImageManager::device_update_udims(Device * /*device*/, Scene *scene)
{
const thread_scoped_lock device_lock(device_mutex);
device_vector<KernelImageUDIM> &device_udims = scene->dscene.image_texture_udims;
if (device_udims.size() == num_udim_tiles) {
return;
}
images[image_texture_id].reset();
device_udims.resize(num_udim_tiles);
for (auto [udim_id, udim] : image_udims.enumerate()) {
if (udim == nullptr) {
continue;
}
if (udim->users == 0) {
image_udims.replace(udim_id, nullptr);
}
else if (udim->need_load) {
const uint udim_offset = -udim->id - 1;
KernelImageUDIM *udim_data = device_udims.data() + udim_offset;
udim_data[0] = KernelImageUDIM{.tile = int(udim->tiles.size()), .image_texture_id = 0};
for (int i = 0; i < udim->tiles.size(); i++) {
const auto &tile = udim->tiles[i];
udim_data[i + 1] = KernelImageUDIM{.tile = tile.first,
.image_texture_id = tile.second.kernel_id()};
}
udim->need_load = false;
}
}
}
void ImageManager::device_update(Device *device, Scene *scene, Progress &progress)
@ -501,13 +531,15 @@ void ImageManager::device_update(Device *device, Scene *scene, Progress &progres
}
});
/* Update UDIM ids. */
device_update_udims(device, scene);
/* Resize devices arrays to match. */
device_resize_image_textures(scene);
/* Free and load images. */
TaskPool pool;
for (size_t image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
Image *img = images[image_texture_id].get();
for (auto [image_texture_id, img] : images.enumerate()) {
if (img && img->users == 0) {
device_free_image(scene, image_texture_id);
}
@ -526,22 +558,31 @@ void ImageManager::device_update(Device *device, Scene *scene, Progress &progres
need_update_ = false;
}
void ImageManager::device_update_image_texture_id(Device *device,
Scene *scene,
const size_t image_texture_id,
Progress &progress)
void ImageManager::device_load_images(Device *device,
Scene *scene,
Progress &progress,
const set<const ImageSingle *> &images)
{
Image *img = images[image_texture_id].get();
assert(img != nullptr);
/* Update UDIM ids. */
device_update_udims(device, scene);
if (img->users == 0) {
/* Resize devices arrays to match number of images. */
device_resize_image_textures(scene);
device_free_image(scene, image_texture_id);
}
else if (img->need_load) {
device_load_image(device, scene, image_texture_id, progress);
/* Load handles. */
TaskPool pool;
for (const ImageSingle *img : images) {
pool.push([this, device, scene, img, &progress] {
assert(img != nullptr);
if (img->users == 0) {
device_free_image(scene, img->image_texture_id);
}
else if (img->need_load) {
device_load_image(device, scene, img->image_texture_id, progress);
}
});
}
pool.wait_work();
/* Copy device arrays. */
device_copy_image_textures(scene);
@ -558,8 +599,7 @@ void ImageManager::device_load_builtin(Device *device, Scene *scene, Progress &p
device_resize_image_textures(scene);
TaskPool pool;
for (size_t image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
Image *img = images[image_texture_id].get();
for (auto [image_texture_id, img] : images.enumerate()) {
if (img && img->need_load && img->builtin) {
pool.push([this, device, scene, image_texture_id, &progress] {
device_load_image(device, scene, image_texture_id, progress);
@ -572,8 +612,8 @@ void ImageManager::device_load_builtin(Device *device, Scene *scene, Progress &p
void ImageManager::device_free_builtin(Scene *scene)
{
for (size_t image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
Image *img = images[image_texture_id].get();
image_udims.clear();
for (auto [image_texture_id, img] : images.enumerate()) {
if (img && img->builtin) {
device_free_image(scene, image_texture_id);
}
@ -582,27 +622,25 @@ void ImageManager::device_free_builtin(Scene *scene)
void ImageManager::device_free(Scene *scene)
{
for (size_t image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
image_udims.clear();
for (auto [image_texture_id, img] : images.enumerate()) {
device_free_image(scene, image_texture_id);
}
images.clear();
const thread_scoped_lock device_lock(device_mutex);
image_cache.device_free(scene->dscene);
scene->dscene.image_textures.free();
scene->dscene.image_texture_udims.free();
}
void ImageManager::collect_statistics(RenderStats *stats)
void ImageManager::collect_statistics(RenderStats *stats, Scene * /*scene*/)
{
for (size_t image_texture_id = 0; image_texture_id < images.size(); image_texture_id++) {
Image *image = images[image_texture_id].get();
if (!image || !image->mem) {
/* Image may have been freed due to lack of users. */
for (auto [image_texture_id, image] : images.enumerate()) {
if (!image) {
continue;
}
stats->image.textures.add_entry(
NamedSizeEntry(image->loader->name(), image->mem->memory_size()));
NamedSizeEntry(image->loader->name(), image->metadata.memory_size()));
}
}

View file

@ -21,10 +21,13 @@ CCL_NAMESPACE_BEGIN
class Device;
class DeviceInfo;
class ImageLoader;
class ImageSingle;
class ImageHandle;
class ImageKey;
class ImageMetaData;
class ImageManager;
class ImageUDIM;
class ImageTexture;
class Progress;
class RenderStats;
class Scene;
@ -54,12 +57,17 @@ class ImageParams {
/* Image Handle
*
* Access handle for image in the image manager. Multiple shader nodes may
* share the same image, and this class handles reference counting for that. */
* share the same image, and this class handles reference counting for that.
*
* This may reference a single image, or a UDIM with multiple images. */
class ImageHandle {
public:
ImageHandle();
ImageHandle(ImageTexture *image_texture, ImageManager *manager);
ImageHandle(const ImageHandle &other);
ImageHandle(ImageHandle &&other) noexcept;
ImageHandle &operator=(const ImageHandle &other);
ImageHandle &operator=(ImageHandle &&other) noexcept;
~ImageHandle();
bool operator==(const ImageHandle &other) const;
@ -68,25 +76,68 @@ class ImageHandle {
bool empty() const;
int num_tiles() const;
int num_svm_image_texture_ids() const;
ImageMetaData metadata();
int svm_image_texture_id(const int image_texture_id_index = 0) const;
vector<int4> get_svm_image_texture_ids() const;
device_image *image_memory() const;
ImageMetaData metadata(Progress &progress);
int kernel_id() const;
device_image *vdb_image_memory() const;
VDBImageLoader *vdb_loader() const;
ImageManager *get_manager() const;
void add_to_set(set<const ImageSingle *> &images) const;
protected:
vector<size_t> image_texture_ids;
bool is_tiled = false;
ImageManager *manager;
ImageTexture *image_texture = nullptr;
ImageManager *manager = nullptr;
friend class ImageManager;
};
/* Image Texture
*
* Base class for an entry in the image manager, which can either be
* a single image or a UDIM. */
class ImageTexture {
public:
std::atomic<int> users = 0;
enum { SINGLE, UDIM } type = SINGLE;
bool need_load = true;
};
/* Image Single
*
* Representation of single image texture in the image manager. */
class ImageSingle : public ImageTexture {
public:
~ImageSingle();
/* Index into ImageManager::images and DeviceScene::image_textures. */
int image_texture_id = KERNEL_IMAGE_NONE;
ImageParams params;
ImageMetaData metadata;
unique_ptr<ImageLoader> loader;
bool need_metadata = true;
bool builtin = false;
thread_mutex mutex;
device_image *vdb_memory = nullptr;
};
/* Image UDIM
*
* Representation of an UDIM image in the image manager. */
class ImageUDIM : public ImageTexture {
public:
/* Negative kernel ID encoding offset into DeviceScene::image_texture_udims. */
int id = KERNEL_IMAGE_NONE;
vector<std::pair<int, ImageHandle>> tiles;
};
/* Image Manager
*
* Handles loading and storage of all images in the scene. This includes 2D
@ -106,41 +157,25 @@ class ImageManager {
ImageHandle add_image(vector<unique_ptr<ImageLoader>> &&loaders, const ImageParams &params);
void device_update(Device *device, Scene *scene, Progress &progress);
void device_update_image_texture_id(Device *device,
Scene *scene,
const size_t image_texture_id,
Progress &progress);
void device_free(Scene *scene);
void device_load_builtin(Device *device, Scene *scene, Progress &progress);
void device_free_builtin(Scene *scene);
void device_load_images(Device *device,
Scene *scene,
Progress &progress,
const set<const ImageSingle *> &images);
void set_osl_texture_system(void *texture_system);
bool set_animation_frame_update(const int frame);
void collect_statistics(RenderStats *stats);
void collect_statistics(RenderStats *stats, Scene *scene);
void tag_update();
bool need_update() const;
ImageCache image_cache;
struct Image {
ImageParams params;
ImageMetaData metadata;
unique_ptr<ImageLoader> loader;
bool need_metadata;
bool need_load;
bool builtin;
device_image *mem = nullptr;
int users;
thread_mutex mutex;
};
private:
bool need_update_;
@ -148,17 +183,20 @@ class ImageManager {
thread_mutex images_mutex;
int animation_frame;
vector<unique_ptr<Image>> images;
unique_ptr_vector<ImageSingle> images;
unique_ptr_vector<ImageUDIM> image_udims;
int num_udim_tiles = 0;
ImageCache image_cache;
void *osl_texture_system;
size_t add_image_texture(unique_ptr<ImageLoader> &&loader,
const ImageParams &params,
const bool builtin);
void add_image_user(const size_t image_texture_id);
void remove_image_user(const size_t image_texture_id);
Image *get_image_texture(const size_t image_texture_id);
ImageSingle *add_image_texture(unique_ptr<ImageLoader> &&loader,
const ImageParams &params,
const bool builtin);
ImageUDIM *add_image_texture(vector<std::pair<int, ImageHandle>> &&tiles);
void load_image_metadata(Image *img);
void load_image_metadata(ImageSingle *img, Progress &progress);
void device_load_image(Device *device,
Scene *scene,
@ -166,6 +204,8 @@ class ImageManager {
Progress &progress);
void device_free_image(Scene *scene, const size_t image_texture_id);
void device_update_udims(Device *device, Scene *scene);
void device_resize_image_textures(Scene *scene);
void device_copy_image_textures(Scene *scene);

View file

@ -1014,7 +1014,7 @@ void LightManager::device_update_background(Device *device,
if (node->type == EnvironmentTextureNode::get_node_type()) {
EnvironmentTextureNode *env = (EnvironmentTextureNode *)node;
if (!env->handle.empty()) {
const ImageMetaData metadata = env->handle.metadata();
const ImageMetaData metadata = env->handle.metadata(progress);
environment_res.x = max(environment_res.x, (int)metadata.width);
environment_res.y = max(environment_res.y, (int)metadata.height);
}

View file

@ -293,7 +293,7 @@ uint Object::visibility_for_tracing() const
return SHADOW_CATCHER_OBJECT_VISIBILITY(is_shadow_catcher, visibility & PATH_RAY_ALL_VISIBILITY);
}
float Object::compute_volume_step_size() const
float Object::compute_volume_step_size(Progress &progress) const
{
if (geometry->is_light()) {
/* World volume. */
@ -343,7 +343,7 @@ float Object::compute_volume_step_size() const
for (Attribute &attr : volume->attributes.attributes) {
if (attr.element == ATTR_ELEMENT_VOXEL) {
ImageHandle &handle = attr.data_voxel();
const ImageMetaData &metadata = handle.metadata();
const ImageMetaData &metadata = handle.metadata(progress);
if (metadata.nanovdb_byte_size == 0) {
continue;
}

View file

@ -114,7 +114,7 @@ class Object : public Node {
int get_device_index() const;
/* Compute step size from attributes, shaders, transforms. */
float compute_volume_step_size() const;
float compute_volume_step_size(Progress &progress) const;
/* Check whether this object can be used as light-emissive. */
bool usable_as_light() const;

View file

@ -669,10 +669,11 @@ void OSLShaderManager::device_update_specific(Device *device,
for (Shader *shader : scene->shaders) {
assert(shader->graph);
auto compile = [scene, shader, background_shader](Device *sub_device, OSLGlobals *) {
auto compile = [scene, &progress, shader, background_shader](Device *sub_device,
OSLGlobals *) {
OSL::ShadingSystem *ss = scene->osl_manager->get_shading_system(sub_device);
OSLCompiler compiler(ss, scene, sub_device);
OSLCompiler compiler(ss, scene, progress, sub_device);
compiler.background = (shader == background_shader);
compiler.compile(shader);
};
@ -923,10 +924,9 @@ OSLNode *OSLShaderManager::osl_node(ShaderGraph *graph,
return node;
}
/* Static function, so only this file needs to be compile with RTTT. */
void OSLShaderManager::osl_image_slots(Device *device,
ImageManager *image_manager,
set<int> &image_slots)
void OSLShaderManager::osl_image_handles(Device *device,
ImageManager *image_manager,
set<const ImageSingle *> &handles)
{
set<OSLRenderServices *> services_shared;
device->foreach_device([&services_shared](Device *sub_device) {
@ -936,9 +936,9 @@ void OSLShaderManager::osl_image_slots(Device *device,
for (OSLRenderServices *services : services_shared) {
for (auto it = services->textures.begin(); it != services->textures.end(); ++it) {
if (it->second.handle.get_manager() == image_manager) {
const int slot = it->second.handle.svm_image_texture_id();
image_slots.insert(slot);
const ImageHandle &handle = it->second.handle;
if (handle.get_manager() == image_manager && !handle.empty()) {
handle.add_to_set(handles);
}
}
}
@ -946,8 +946,9 @@ void OSLShaderManager::osl_image_slots(Device *device,
/* Graph Compiler */
OSLCompiler::OSLCompiler(OSL::ShadingSystem *ss, Scene *scene, Device *device)
OSLCompiler::OSLCompiler(OSL::ShadingSystem *ss, Scene *scene, Progress &progress, Device *device)
: scene(scene),
progress(progress),
services(static_cast<OSLRenderServices *>(ss->renderer())),
ss(ss),
device(device)
@ -1590,9 +1591,8 @@ void OSLCompiler::parameter_texture(const char *name, const ImageHandle &handle)
* to get handle again. Note that this name must be unique between multiple
* render sessions as the render services are shared. */
const ustring filename(string_printf("@svm%d", texture_shared_unique_id++).c_str());
services->textures.insert(
OSLUStringHash(filename),
OSLTextureHandle(OSLTextureHandle::SVM, handle.get_svm_image_texture_ids()));
services->textures.insert(OSLUStringHash(filename),
OSLTextureHandle(OSLTextureHandle::SVM, handle.kernel_id()));
parameter(name, filename);
}

View file

@ -140,8 +140,10 @@ class OSLShaderManager : public ShaderManager {
const std::string &bytecode_hash = "",
const std::string &bytecode = "");
/* Get image slots used by OSL services on device. */
static void osl_image_slots(Device *device, ImageManager *image_manager, set<int> &image_slots);
/* Get image handles used by OSL services on device. */
static void osl_image_handles(Device *device,
ImageManager *image_manager,
set<const ImageSingle *> &image_handles);
};
#endif
@ -151,7 +153,7 @@ class OSLShaderManager : public ShaderManager {
class OSLCompiler {
public:
#ifdef WITH_OSL
OSLCompiler(OSL::ShadingSystem *ss, Scene *scene, Device *device);
OSLCompiler(OSL::ShadingSystem *ss, Scene *scene, Progress &progress, Device *device);
#endif
void compile(Shader *shader);
@ -185,6 +187,7 @@ class OSLCompiler {
bool background;
Scene *scene;
Progress &progress;
private:
#ifdef WITH_OSL

View file

@ -500,7 +500,7 @@ void Scene::device_free()
void Scene::collect_statistics(RenderStats *stats)
{
geometry_manager->collect_statistics(this, stats);
image_manager->collect_statistics(stats);
image_manager->collect_statistics(stats, this);
}
void Scene::enable_update_stats()

View file

@ -368,20 +368,25 @@ void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attribute
ShaderNode::attributes(shader, attributes);
}
void ImageTextureNode::update_images(const SVMCompiler &compiler)
{
if (handle.empty()) {
cull_tiles(compiler.scene, compiler.current_graph);
ImageManager *image_manager = compiler.scene->image_manager.get();
handle = image_manager->add_image(filename.string(), image_params(), tiles);
}
}
void ImageTextureNode::compile(SVMCompiler &compiler)
{
ShaderInput *vector_in = input("Vector");
ShaderOutput *color_out = output("Color");
ShaderOutput *alpha_out = output("Alpha");
if (handle.empty()) {
cull_tiles(compiler.scene, compiler.current_graph);
ImageManager *image_manager = compiler.scene->image_manager.get();
handle = image_manager->add_image(filename.string(), image_params(), tiles);
}
update_images(compiler);
/* All tiles have the same metadata. */
const ImageMetaData metadata = handle.metadata();
const ImageMetaData metadata = handle.metadata(compiler.progress);
const bool compress_as_srgb = metadata.is_compressible_as_srgb;
const int vector_offset = tex_mapping.compile_begin(compiler, vector_in);
@ -401,44 +406,17 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
}
if (projection != NODE_IMAGE_PROJ_BOX) {
/* If there only is one image (a very common case), we encode it as a negative value. */
int num_nodes;
if (handle.num_tiles() == 0) {
num_nodes = -handle.svm_image_texture_id();
}
else {
num_nodes = divide_up(handle.num_tiles(), 2);
}
compiler.add_node(NODE_TEX_IMAGE,
num_nodes,
handle.kernel_id(),
compiler.encode_uchar4(vector_offset,
compiler.stack_assign_if_linked(color_out),
compiler.stack_assign_if_linked(alpha_out),
flags),
projection);
if (num_nodes > 0) {
for (int i = 0; i < num_nodes; i++) {
int4 node;
node.x = tiles[2 * i];
node.y = handle.svm_image_texture_id(2 * i);
if (2 * i + 1 < tiles.size()) {
node.z = tiles[2 * i + 1];
node.w = handle.svm_image_texture_id(2 * i + 1);
}
else {
node.z = -1;
node.w = -1;
}
compiler.add_node(node.x, node.y, node.z, node.w);
}
}
}
else {
assert(handle.num_svm_image_texture_ids() == 1);
compiler.add_node(NODE_TEX_IMAGE_BOX,
handle.svm_image_texture_id(),
handle.kernel_id(),
compiler.encode_uchar4(vector_offset,
compiler.stack_assign_if_linked(color_out),
compiler.stack_assign_if_linked(alpha_out),
@ -460,12 +438,12 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
handle = image_manager->add_image(filename.string(), image_params());
}
const ImageMetaData metadata = handle.metadata();
const ImageMetaData metadata = handle.metadata(compiler.progress);
const bool is_float = metadata.is_float();
const bool compress_as_srgb = metadata.is_compressible_as_srgb;
const ustring known_colorspace = metadata.colorspace;
if (handle.svm_image_texture_id() == -1) {
if (handle.kernel_id() == KERNEL_IMAGE_NONE) {
compiler.parameter_texture(
"filename", filename, compress_as_srgb ? u_colorspace_scene_linear : known_colorspace);
}
@ -571,18 +549,23 @@ void EnvironmentTextureNode::attributes(Shader *shader, AttributeRequestSet *att
ShaderNode::attributes(shader, attributes);
}
void EnvironmentTextureNode::update_images(const SVMCompiler &compiler)
{
if (handle.empty()) {
ImageManager *image_manager = compiler.scene->image_manager.get();
handle = image_manager->add_image(filename.string(), image_params());
}
}
void EnvironmentTextureNode::compile(SVMCompiler &compiler)
{
ShaderInput *vector_in = input("Vector");
ShaderOutput *color_out = output("Color");
ShaderOutput *alpha_out = output("Alpha");
if (handle.empty()) {
ImageManager *image_manager = compiler.scene->image_manager.get();
handle = image_manager->add_image(filename.string(), image_params());
}
update_images(compiler);
const ImageMetaData metadata = handle.metadata();
const ImageMetaData metadata = handle.metadata(compiler.progress);
const bool compress_as_srgb = metadata.is_compressible_as_srgb;
const int vector_offset = tex_mapping.compile_begin(compiler, vector_in);
@ -593,7 +576,7 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler)
}
compiler.add_node(NODE_TEX_ENVIRONMENT,
handle.svm_image_texture_id(),
handle.kernel_id(),
compiler.encode_uchar4(vector_offset,
compiler.stack_assign_if_linked(color_out),
compiler.stack_assign_if_linked(alpha_out),
@ -612,12 +595,12 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler)
tex_mapping.compile(compiler);
const ImageMetaData metadata = handle.metadata();
const ImageMetaData metadata = handle.metadata(compiler.progress);
const bool is_float = metadata.is_float();
const bool compress_as_srgb = metadata.is_compressible_as_srgb;
const ustring known_colorspace = metadata.colorspace;
if (handle.svm_image_texture_id() == -1) {
if (handle.kernel_id() == KERNEL_IMAGE_NONE) {
compiler.parameter_texture(
"filename", filename, compress_as_srgb ? u_colorspace_scene_linear : known_colorspace);
}
@ -1027,7 +1010,7 @@ void SkyTextureNode::compile(SVMCompiler &compiler)
compiler.add_node(__float_as_uint(sunsky.nishita_data[8]),
__float_as_uint(sunsky.nishita_data[9]),
__float_as_uint(sunsky.nishita_data[10]),
handle.svm_image_texture_id());
handle.kernel_id());
}
tex_mapping.compile_end(compiler, vector_in, vector_offset);

View file

@ -84,6 +84,8 @@ class ImageSlotTextureNode : public TextureNode {
return TextureNode::equals(other) && handle == other_node.handle;
}
virtual void update_images(const SVMCompiler &compiler) = 0;
ImageHandle handle;
};
@ -105,6 +107,8 @@ class ImageTextureNode : public ImageSlotTextureNode {
ImageParams image_params() const;
void update_images(const SVMCompiler &compiler) override;
/* Parameters. */
NODE_SOCKET_API(ustring, filename)
NODE_SOCKET_API(ustring, colorspace)
@ -139,6 +143,8 @@ class EnvironmentTextureNode : public ImageSlotTextureNode {
ImageParams image_params() const;
void update_images(const SVMCompiler &compiler) override;
/* Parameters. */
NODE_SOCKET_API(ustring, filename)
NODE_SOCKET_API(ustring, colorspace)

View file

@ -39,7 +39,7 @@ void SVMShaderManager::device_update_shader(Scene *scene,
assert(shader->graph);
SVMCompiler::Summary summary;
SVMCompiler compiler(scene);
SVMCompiler compiler(scene, progress);
compiler.background = (shader == scene->background->get_shader(scene));
compiler.compile(shader, *svm_nodes, 0, &summary);
@ -149,7 +149,7 @@ void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s
/* Graph Compiler */
SVMCompiler::SVMCompiler(Scene *scene) : scene(scene)
SVMCompiler::SVMCompiler(Scene *scene, Progress &progress) : scene(scene), progress(progress)
{
max_stack_use = 0;
current_type = SHADER_TYPE_SURFACE;

View file

@ -75,7 +75,7 @@ class SVMCompiler {
string full_report() const;
};
SVMCompiler(Scene *scene);
SVMCompiler(Scene *scene, Progress &progress);
void compile(Shader *shader,
array<int4> &svm_nodes,
const int index,
@ -116,6 +116,7 @@ class SVMCompiler {
}
Scene *scene;
Progress &progress;
ShaderGraph *current_graph;
bool background;

View file

@ -645,7 +645,7 @@ void GeometryManager::create_volume_mesh(const Scene *scene, Volume *volume, Pro
}
/* Create NanoVDB grid handle from image memory. */
device_image *image = handle.image_memory();
device_image *image = handle.vdb_image_memory();
if (image == nullptr || image->host_pointer == nullptr ||
image->info.data_type == IMAGE_DATA_TYPE_NANOVDB_EMPTY ||
!is_nanovdb_type(image->info.data_type))
@ -1181,7 +1181,7 @@ std::string VolumeManager::visualize_octree(const char *filename) const
return filename_full;
}
void VolumeManager::update_step_size(const Scene *scene, DeviceScene *dscene)
void VolumeManager::update_step_size(const Scene *scene, DeviceScene *dscene, Progress &progress)
{
assert(scene->integrator->get_volume_ray_marching());
@ -1204,7 +1204,7 @@ void VolumeManager::update_step_size(const Scene *scene, DeviceScene *dscene)
}
volume_step_size[object->index] = scene->integrator->get_volume_step_rate() *
object->compute_volume_step_size();
object->compute_volume_step_size(progress);
}
dscene->volume_step_size.copy_to_device();
@ -1224,7 +1224,7 @@ void VolumeManager::device_update(Device *device,
dscene->volume_tree_roots.free();
dscene->volume_tree_root_ids.free();
}
update_step_size(scene, dscene);
update_step_size(scene, dscene, progress);
algorithm_modified_ = false;
return;
}

View file

@ -78,7 +78,7 @@ class VolumeManager {
std::string visualize_octree(const char *filename) const;
/* Step size for ray marching. */
void update_step_size(const Scene *, DeviceScene *);
void update_step_size(const Scene *, DeviceScene *, Progress &progress);
/* One octree per object per shader. */
std::map<std::pair<const Object *, const Shader *>, std::shared_ptr<Octree>> object_octrees_;

View file

@ -102,6 +102,12 @@ struct KernelImageInfo {
uint height = 0;
};
/* KernelImageTexture index for UDIM tile. */
struct KernelImageUDIM {
int tile;
int image_texture_id;
};
/* Kernel data structure for image textures.
*
* This describes a logical image texture for the shading system, that may be stored
@ -109,7 +115,7 @@ struct KernelImageInfo {
* support on demand loading of tiles. */
struct KernelImageTexture {
/* Index into image object map. */
uint image_info_id = 0;
uint image_info_id = KERNEL_IMAGE_NONE;
/* Image dimensions */
uint width = 0;
uint height = 0;