Cycles: Add support for OSL texture intrinsic on the GPU
This makes it possible to use `texture` and `texture3d` in custom OSL shaders with a constant image file name as argument on the GPU, where previously texturing was only possible through Cycles nodes. For constant file name arguments, OSL calls `OSL::RendererServices::get_texture_handle()` with the file name string to convert it into an opaque handle for use on the GPU. That is now used to load the respective image file using the Cycles image manager and generate a SVM handle that can be used on the GPU. Some care is necessary as the renderer services class is shared across multiple Cycles instances, whereas the Cycles image manager is local to each. Maniphest Tasks: T101222 Differential Revision: https://developer.blender.org/D17032
This commit is contained in:
parent
e270a198a5
commit
9066f2e043
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "kernel/osl/globals.h"
|
||||
#include "kernel/osl/services.h"
|
||||
#include "kernel/osl/types.h"
|
||||
|
||||
#include "util/foreach.h"
|
||||
#include "util/log.h"
|
||||
|
@ -119,6 +120,8 @@ ustring OSLRenderServices::u_u("u");
|
|||
ustring OSLRenderServices::u_v("v");
|
||||
ustring OSLRenderServices::u_empty;
|
||||
|
||||
ImageManager *OSLRenderServices::image_manager = nullptr;
|
||||
|
||||
OSLRenderServices::OSLRenderServices(OSL::TextureSystem *texture_system, int device_type)
|
||||
: OSL::RendererServices(texture_system), device_type_(device_type)
|
||||
{
|
||||
|
@ -1154,7 +1157,7 @@ TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring file
|
|||
/* For non-OIIO textures, just return a pointer to our own OSLTextureHandle. */
|
||||
if (it != textures.end()) {
|
||||
if (it->second->type != OSLTextureHandle::OIIO) {
|
||||
return (TextureSystem::TextureHandle *)it->second.get();
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(it->second.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1173,16 +1176,53 @@ TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring file
|
|||
|
||||
/* Assign OIIO texture handle and return. */
|
||||
it->second->oiio_handle = handle;
|
||||
return (TextureSystem::TextureHandle *)it->second.get();
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(it->second.get());
|
||||
}
|
||||
else {
|
||||
if (it != textures.end() && it->second->type == OSLTextureHandle::SVM &&
|
||||
it->second->svm_slots[0].w == -1) {
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(
|
||||
static_cast<uintptr_t>(it->second->svm_slots[0].y + 1));
|
||||
/* Construct GPU texture handle for existing textures. */
|
||||
if (it != textures.end()) {
|
||||
switch (it->second->type) {
|
||||
case OSLTextureHandle::OIIO:
|
||||
return NULL;
|
||||
case OSLTextureHandle::SVM:
|
||||
if (!it->second->handle.empty() && it->second->handle.get_manager() != image_manager) {
|
||||
it.clear();
|
||||
break;
|
||||
}
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_SVM |
|
||||
it->second->svm_slots[0].y);
|
||||
case OSLTextureHandle::IES:
|
||||
if (!it->second->handle.empty() && it->second->handle.get_manager() != image_manager) {
|
||||
it.clear();
|
||||
break;
|
||||
}
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_IES |
|
||||
it->second->svm_slots[0].y);
|
||||
case OSLTextureHandle::AO:
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(
|
||||
OSL_TEXTURE_HANDLE_TYPE_AO_OR_BEVEL | 1);
|
||||
case OSLTextureHandle::BEVEL:
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(
|
||||
OSL_TEXTURE_HANDLE_TYPE_AO_OR_BEVEL | 2);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
if (!image_manager) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Load new textures using SVM image manager. */
|
||||
ImageHandle handle = image_manager->add_image(filename.string(), ImageParams());
|
||||
if (handle.empty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!textures.insert(filename, new OSLTextureHandle(handle))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return reinterpret_cast<TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_SVM |
|
||||
handle.svm_slot());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <OSL/oslexec.h>
|
||||
#include <OSL/rendererservices.h>
|
||||
|
||||
#include "scene/image.h"
|
||||
|
||||
#ifdef WITH_PTEX
|
||||
class PtexCache;
|
||||
#endif
|
||||
|
@ -54,10 +56,20 @@ struct OSLTextureHandle : public OIIO::RefCnt {
|
|||
{
|
||||
}
|
||||
|
||||
OSLTextureHandle(const ImageHandle &handle)
|
||||
: type(SVM),
|
||||
svm_slots(handle.get_svm_slots()),
|
||||
oiio_handle(nullptr),
|
||||
processor(nullptr),
|
||||
handle(handle)
|
||||
{
|
||||
}
|
||||
|
||||
Type type;
|
||||
vector<int4> svm_slots;
|
||||
OSL::TextureSystem::TextureHandle *oiio_handle;
|
||||
ColorSpaceProcessor *processor;
|
||||
ImageHandle handle;
|
||||
};
|
||||
|
||||
typedef OIIO::intrusive_ptr<OSLTextureHandle> OSLTextureHandleRef;
|
||||
|
@ -324,6 +336,8 @@ class OSLRenderServices : public OSL::RendererServices {
|
|||
* shading system. */
|
||||
OSLTextureHandleMap textures;
|
||||
|
||||
static ImageManager *image_manager;
|
||||
|
||||
private:
|
||||
int device_type_;
|
||||
};
|
||||
|
|
|
@ -1443,6 +1443,8 @@ OSL_NOISE_IMPL(osl_snoise, snoise)
|
|||
|
||||
/* Texturing */
|
||||
|
||||
#include "kernel/svm/ies.h"
|
||||
|
||||
ccl_device_extern ccl_private OSLTextureOptions *osl_get_texture_options(
|
||||
ccl_private ShaderGlobals *sg)
|
||||
{
|
||||
|
@ -1548,25 +1550,31 @@ ccl_device_extern bool osl_texture(ccl_private ShaderGlobals *sg,
|
|||
ccl_private float *dalphady,
|
||||
ccl_private void *errormessage)
|
||||
{
|
||||
if (!texture_handle) {
|
||||
return false;
|
||||
const unsigned int type = OSL_TEXTURE_HANDLE_TYPE(texture_handle);
|
||||
const unsigned int slot = OSL_TEXTURE_HANDLE_SLOT(texture_handle);
|
||||
|
||||
switch (type) {
|
||||
case OSL_TEXTURE_HANDLE_TYPE_SVM: {
|
||||
const float4 rgba = kernel_tex_image_interp(nullptr, slot, s, 1.0f - t);
|
||||
if (nchannels > 0)
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 1)
|
||||
result[1] = rgba.y;
|
||||
if (nchannels > 2)
|
||||
result[2] = rgba.z;
|
||||
if (alpha)
|
||||
*alpha = rgba.w;
|
||||
return true;
|
||||
}
|
||||
case OSL_TEXTURE_HANDLE_TYPE_IES: {
|
||||
if (nchannels > 0)
|
||||
result[0] = kernel_ies_interp(nullptr, slot, s, t);
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only SVM textures are supported. */
|
||||
int id = static_cast<int>(reinterpret_cast<size_t>(texture_handle) - 1);
|
||||
|
||||
const float4 rgba = kernel_tex_image_interp(nullptr, id, s, 1.0f - t);
|
||||
|
||||
if (nchannels > 0)
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 1)
|
||||
result[1] = rgba.y;
|
||||
if (nchannels > 2)
|
||||
result[2] = rgba.z;
|
||||
if (alpha)
|
||||
*alpha = rgba.w;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_device_extern bool osl_texture3d(ccl_private ShaderGlobals *sg,
|
||||
|
@ -1586,25 +1594,26 @@ ccl_device_extern bool osl_texture3d(ccl_private ShaderGlobals *sg,
|
|||
ccl_private float *dalphady,
|
||||
ccl_private void *errormessage)
|
||||
{
|
||||
if (!texture_handle) {
|
||||
return false;
|
||||
const unsigned int type = OSL_TEXTURE_HANDLE_TYPE(texture_handle);
|
||||
const unsigned int slot = OSL_TEXTURE_HANDLE_SLOT(texture_handle);
|
||||
|
||||
switch (type) {
|
||||
case OSL_TEXTURE_HANDLE_TYPE_SVM: {
|
||||
const float4 rgba = kernel_tex_image_interp_3d(nullptr, slot, *P, INTERPOLATION_NONE);
|
||||
if (nchannels > 0)
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 1)
|
||||
result[1] = rgba.y;
|
||||
if (nchannels > 2)
|
||||
result[2] = rgba.z;
|
||||
if (alpha)
|
||||
*alpha = rgba.w;
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only SVM textures are supported. */
|
||||
int id = static_cast<int>(reinterpret_cast<size_t>(texture_handle) - 1);
|
||||
|
||||
const float4 rgba = kernel_tex_image_interp_3d(nullptr, id, *P, INTERPOLATION_NONE);
|
||||
|
||||
if (nchannels > 0)
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 1)
|
||||
result[1] = rgba.y;
|
||||
if (nchannels > 2)
|
||||
result[2] = rgba.z;
|
||||
if (alpha)
|
||||
*alpha = rgba.w;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_device_extern bool osl_environment(ccl_private ShaderGlobals *sg,
|
||||
|
|
|
@ -90,10 +90,17 @@ struct ShaderGlobals {
|
|||
int backfacing;
|
||||
};
|
||||
|
||||
struct OSLNoiseOptions {
|
||||
};
|
||||
struct OSLNoiseOptions {};
|
||||
|
||||
struct OSLTextureOptions {
|
||||
};
|
||||
struct OSLTextureOptions {};
|
||||
|
||||
#define OSL_TEXTURE_HANDLE_TYPE_IES ((uintptr_t)0x2 << 30)
|
||||
#define OSL_TEXTURE_HANDLE_TYPE_SVM ((uintptr_t)0x1 << 30)
|
||||
#define OSL_TEXTURE_HANDLE_TYPE_AO_OR_BEVEL ((uintptr_t)0x3 << 30)
|
||||
|
||||
#define OSL_TEXTURE_HANDLE_TYPE(handle) \
|
||||
((unsigned int)((uintptr_t)(handle) & ((uintptr_t)0x3 << 30)))
|
||||
#define OSL_TEXTURE_HANDLE_SLOT(handle) \
|
||||
((unsigned int)((uintptr_t)(handle) & ((uintptr_t)0x3FFFFFFF)))
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -84,6 +84,7 @@ ccl_device_inline float kernel_ies_interp(KernelGlobals kg, int slot, float h_an
|
|||
return max(cubic_interp(a, b, c, d, h_frac), 0.0f);
|
||||
}
|
||||
|
||||
#ifdef __SVM__
|
||||
ccl_device_noinline void svm_node_ies(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
ccl_private float *stack,
|
||||
|
@ -105,5 +106,6 @@ ccl_device_noinline void svm_node_ies(KernelGlobals kg,
|
|||
stack_store_float(stack, fac_offset, fac);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -222,6 +222,11 @@ VDBImageLoader *ImageHandle::vdb_loader(const int tile_index) const
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ImageManager *ImageHandle::get_manager() const
|
||||
{
|
||||
return manager;
|
||||
}
|
||||
|
||||
bool ImageHandle::operator==(const ImageHandle &other) const
|
||||
{
|
||||
return manager == other.manager && tile_slots == other.tile_slots;
|
||||
|
|
|
@ -153,6 +153,8 @@ class ImageHandle {
|
|||
|
||||
VDBImageLoader *vdb_loader(const int tile_index = 0) const;
|
||||
|
||||
ImageManager *get_manager() const;
|
||||
|
||||
protected:
|
||||
vector<int> tile_slots;
|
||||
ImageManager *manager;
|
||||
|
|
|
@ -184,9 +184,19 @@ void OSLShaderManager::device_update_specific(Device *device,
|
|||
* is being freed after the Session is freed.
|
||||
*/
|
||||
thread_scoped_lock lock(ss_shared_mutex);
|
||||
|
||||
/* Set current image manager during the lock, so that there is no conflict with other shader
|
||||
* manager instances.
|
||||
*
|
||||
* It is used in "OSLRenderServices::get_texture_handle" called during optimization below to
|
||||
* load images for the GPU. */
|
||||
OSLRenderServices::image_manager = scene->image_manager;
|
||||
|
||||
for (const auto &[device_type, ss] : ss_shared) {
|
||||
ss->optimize_all_groups();
|
||||
}
|
||||
|
||||
OSLRenderServices::image_manager = nullptr;
|
||||
}
|
||||
|
||||
/* load kernels */
|
||||
|
@ -213,6 +223,22 @@ void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s
|
|||
og->bump_state.clear();
|
||||
og->background_state.reset();
|
||||
});
|
||||
|
||||
/* Remove any textures specific to an image manager from shared render services textures, since
|
||||
* the image manager may get destroyed next. */
|
||||
for (const auto &[device_type, ss] : ss_shared) {
|
||||
OSLRenderServices *services = static_cast<OSLRenderServices *>(ss->renderer());
|
||||
|
||||
for (auto it = services->textures.begin(); it != services->textures.end(); ++it) {
|
||||
if (it->second->handle.get_manager() == scene->image_manager) {
|
||||
/* Don't lock again, since the iterator already did so. */
|
||||
services->textures.erase(it->first, false);
|
||||
it.clear();
|
||||
/* Iterator was invalidated, start from the beginning again. */
|
||||
it = services->textures.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSLShaderManager::texture_system_init()
|
||||
|
|
Loading…
Reference in New Issue