HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TIL_RadeonFilter.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE 'AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 #include "TIL_RadeonFilter.h"
28 #include <FS/UT_DSO.h>
29 #include <UT/UT_EnvControl.h>
30 #include <UT/UT_ErrorLog.h>
31 #include <UT/UT_Exit.h>
32 #include <UT/UT_Debug.h>
33 #include <UT/UT_StopWatch.h>
34 #include <UT/UT_ParallelUtil.h>
35 #include <UT/UT_DSOVersion.h>
36 #include <UT/UT_WorkArgs.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 
40 #if defined(RIF_USE_METAL)
41  #define BACKEND_TYPE RIF_BACKEND_API_METAL
42 #else
43  #define BACKEND_TYPE RIF_BACKEND_API_OPENCL
44 #endif
45 
46 namespace
47 {
48  // Keywords
49  static constexpr UT_StringLit theRadius("radius");
50  static constexpr UT_StringLit theThreshold("threshold");
51  static constexpr UT_StringLit theWeight("weight");
52  static constexpr UT_StringLit theUseHDR("useHDR");
53  static constexpr UT_StringLit theModelPath("modelPath");
54 
55  static constexpr UT_StringLit theNName("N");
56  static constexpr UT_StringLit thePzName("Pz");
57  static constexpr UT_StringLit theDefaultModelPath("./models");
58 }
59 
60 // Uncomment this to get more information when running filters
61 //#define TRACE_CALLS
62 #if defined(TRACE_CALLS)
63  #define DEBUG_PRINT(...) do { \
64  UT_WorkBuffer tmp; \
65  tmp.format(__VA_ARGS__); \
66  fprintf(stderr, "%s(%d): %s\n", __FILE__, __LINE__, tmp.buffer()); \
67  } while (false) \
68  /* end macro */
69 #else
70  #define DEBUG_PRINT(...) ((void)0)
71 #endif
72 
73 namespace
74 {
75  static void SYS_PRINTF_CHECK_ATTRIBUTE(2, 3)
76  error(UT_StringHolder &msg, const char *fmt, ...)
77  {
78  UT_WorkBuffer tmp;
79  va_list args;
80  va_start(args, fmt);
81  tmp.vsprintf(fmt, args);
82  va_end(args);
83  msg = UT_StringHolder(tmp);
84  DEBUG_PRINT("{}", msg);
85  }
86 
87  static constexpr inline bool
88  isError(rif_int status)
89  {
90  return status != RIF_SUCCESS;
91  }
92 
93 #define RIF_CALL(status, msg, call) \
94  { status = call; \
95  DEBUG_PRINT("CALL: {} = {}", int(status), #call); \
96  if (status != RIF_SUCCESS) { \
97  error(msg, "RadeonPro Error: %s\n %s\n", \
98  rifGetErrorCodeString(status), \
99  rifGetErrorCodeDescription(status)); \
100  UT_ASSERT(0); \
101  } \
102  } \
103  /* end macro */
104 
105 #define RIF_CALL_FAIL(status, msg, call) \
106  { RIF_CALL(status, msg, call) \
107  if (isError(status)) return false; } \
108  /* end of macro */
109 
110  static rif_context
111  getRIFContext(UT_StringHolder &msg, int devnum)
112  {
113  rif_int status = RIF_SUCCESS;
114  int ndev = 0;
115  RIF_CALL(status, msg, rifGetDeviceCount(BACKEND_TYPE, &ndev));
116  if (!ndev || isError(status))
117  return nullptr;
118  DEBUG_PRINT("Radeon {} devices", ndev);
119  rif_context context = nullptr;
120  RIF_CALL(status, msg, rifCreateContext(RIF_API_VERSION, BACKEND_TYPE,
121  devnum, nullptr, &context));
122 
123  return context;
124  };
125 }
126 
128  : myFilterType(type)
129  , myDeviceNum(0)
130  , myRadius(0.01)
131  , myThreshold(0)
132  , myWeight(0.3)
133 {
134 }
135 
136 bool
138 {
139  if (!o.importOption(theRadius.asRef(), myRadius))
140  myRadius = 0.01f;
141  if (!o.importOption(theThreshold.asRef(), myThreshold))
142  myThreshold = 0;
143  if (!o.importOption(theWeight.asRef(), myWeight))
144  myWeight = 0.3;
145  if (!o.importOption(theModelPath.asRef(), myModelPath))
146  myModelPath = theDefaultModelPath.asHolder();
147 
148  if (myFilterType == RADEON_FILTER_TYPE::DENOISE)
149  {
150  // Tell the caller what extra AOVs we might want to read
151  myAuxPlaneNames.append(theNName.asHolder()); // Normals
152  myAuxPlaneNames.append(thePzName.asHolder()); // Depth
153  }
154 
155  return true;
156 }
157 
159 {
160 }
161 
162 template <typename DT, typename ST, uint DNC, uint SNC, uint32 ISCALE, bool INVERT>
163 static void
164 copyPixels(DT *dest, const ST *src, exint npixels)
165 {
166  float scale = 1;
167  constexpr uint NC = DNC < SNC ? DNC : SNC;
168  if (ISCALE)
169  scale = INVERT ? 1.0/ISCALE : ISCALE;
171  [&](const UT_BlockedRange<exint> &r)
172  {
173  exint n = r.end() - r.begin();
174  DT *d = dest + r.begin() * DNC;
175  const ST *s = src + r.begin() * SNC;
176  if (!ISCALE && DNC == SNC)
177  {
178  std::copy(s, s+SNC*n, d);
179  }
180  else
181  {
182  for (exint i = 0; i < n; ++i, d += DNC, s += SNC)
183  {
184  if (!ISCALE)
185  std::copy(s, s+NC, d);
186  else
187  {
188  for (int ch = 0; ch < NC; ++ch)
189  d[ch] = s[ch] * scale;
190  }
191  }
192  }
193  }
194  );
195 }
196 
197 template <typename DT, typename ST, uint32 ISCALE, bool INVERT>
198 static void
199 doCopyRaster(void *d, int dnc, const void *s, int snc, exint npix)
200 {
201  if (dnc == 3)
202  {
203  if (snc == 3)
204  copyPixels<DT, ST, 3, 3, ISCALE, INVERT>((DT *)d, (const ST *)s, npix);
205  else
206  copyPixels<DT, ST, 3, 4, ISCALE, INVERT>((DT *)d, (const ST *)s, npix);
207  }
208  else
209  {
210  if (snc == 3)
211  copyPixels<DT, ST, 4, 3, ISCALE, INVERT>((DT *)d, (const ST *)s, npix);
212  else
213  copyPixels<DT, ST, 4, 4, ISCALE, INVERT>((DT *)d, (const ST *)s, npix);
214  }
215 }
216 
217 static const PXL_Raster &
218 makeFloatRaster(const PXL_Raster &src, PXL_Raster &storage)
219 {
220  if (src.getFormat() == PXL_FLOAT32)
221  return src;
222 
223  storage.setPacking(src.getPacking());
224  storage.setFormat(PXL_FLOAT32);
225  storage.setRes(src.getXres(), src.getYres());
226  storage.init();
227 
228  float *f = (float *)storage.getPixels();
229  const void *s = src.getPixels();
230  exint npix = src.getNumPixels();
231  int nchan = PXLpackingComponents(src.getPacking());
232 
233  switch (src.getFormat())
234  {
235  case PXL_INT8:
236  doCopyRaster<float, uint8, 0xff, true>(f, nchan, s, nchan, npix);
237  break;
238  case PXL_INT16:
239  doCopyRaster<float, uint16, 0xffff, true>(f, nchan, s, nchan, npix);
240  break;
241  case PXL_INT32:
242  doCopyRaster<float, uint32, 0xffffffff, true>(f, nchan, s, nchan, npix);
243  break;
244  case PXL_FLOAT16:
245  doCopyRaster<float, fpreal16, 0, false>(f, nchan, s, nchan, npix);
246  break;
247  case PXL_FLOAT32:
248  doCopyRaster<float, fpreal32, 0, false>(f, nchan, s, nchan, npix);
249  break;
250  case PXL_MAX_DATA_FORMAT:
251  UT_ASSERT(0);
252  break;
253  }
254  return storage;
255 }
256 
257 bool
258 TIL_RadeonFilter::downloadRaster(PXL_Raster &dest, const RIFImage &src)
259 {
260  rif_int status;
261  rif_image_desc desc;
262  size_t rsize;
263 
264  RIF_CALL_FAIL(status, myErrorString, rifImageGetInfo(*src, RIF_IMAGE_DESC,
265  sizeof(desc), &desc, &rsize));
266  rif_uchar *data;
267  RIF_CALL_FAIL(status, myErrorString, rifImageMap(*src, RIF_IMAGE_MAP_READ,
268  (void **)&data));
269  if (!data)
270  return false;
271 
272  int devSize = desc.num_components;
273  int destSize = PXLpackingComponents(dest.getPacking());
274  UT_ASSERT(desc.image_width == dest.getXres());
275  UT_ASSERT(desc.image_height == dest.getYres());
276  exint npix = dest.getNumPixels();
277  switch (dest.getFormat())
278  {
279  case PXL_INT8:
280  doCopyRaster<uint8, float, 0xff, false>(
281  dest.getPixels(), destSize, data, devSize, npix);
282  break;
283  case PXL_INT16:
284  doCopyRaster<uint16, float, 0xffff, false>(
285  dest.getPixels(), destSize, data, devSize, npix);
286  break;
287  case PXL_INT32:
288  doCopyRaster<uint32, float, 0xffffffff, false>(
289  dest.getPixels(), destSize, data, devSize, npix);
290  break;
291  case PXL_FLOAT16:
292  doCopyRaster<fpreal16, float, 0, false>(
293  dest.getPixels(), destSize, data, devSize, npix);
294  break;
295  case PXL_FLOAT32:
296  doCopyRaster<fpreal32, float, 0, false>(
297  dest.getPixels(), destSize, data, devSize, npix);
298  break;
299  default:
300  UT_ASSERT(0);
301  break;
302  }
303  RIF_CALL_FAIL(status, myErrorString, rifImageUnmap(*src, data));
304  return true;
305 }
306 
307 bool
308 TIL_RadeonFilter::uploadRaster(RIFContext &ctx,
309  RIFImage &img,
310  const char *label,
311  const PXL_Raster &src,
312  PXL_Raster &storage)
313 {
314  const PXL_Raster &rp = makeFloatRaster(src, storage);
315  const void *data = rp.getPixels();
317 
318  rif_image_desc desc;
319  rif_int status;
320  memset(&desc, 0, sizeof(desc));
321  desc.type = RIF_COMPONENT_TYPE_FLOAT32;
322  desc.image_width = rp.getXres();
323  desc.image_height = rp.getYres();
324  desc.num_components = PXLpackingComponents(rp.getPacking());
325  RIF_CALL(status, myErrorString, rifContextCreateImage(*ctx, &desc, data, img.ptr()));
326  if (img && status != RIF_SUCCESS)
327  img = nullptr;
328  return img.isValid();
329 }
330 
331 bool
333 {
334  RIFContext ctx;
335  ctx = getRIFContext(myErrorString, myDeviceNum);
336  UT_ASSERT(ctx);
337  if (!ctx)
338  return false;
339  DEBUG_PRINT("Apply RadeonPro");
340  UT_StopWatch timer; timer.start();
341  int nchan = PXLpackingComponents(raster->getPacking());
342  if (nchan != 3 && nchan != 4)
343  return false; // Only filter 3&4 channel images
344 
345  DEBUG_PRINT("Init: {}", timer.lap());
346  rif_int status;
349  rifContextCreateCommandQueue(*ctx, queue.ptr()));
350  if (!queue)
351  return false;
352 
353  RIFImage rgb;
355  const char *colorName = (myFilterType == RADEON_FILTER_TYPE::BLOOM)
356  ? "color" : "colorImg";
357  if (!uploadRaster(ctx, rgb, colorName, *raster, storage))
358  return false;
359 
360  size_t out_size;
361  rif_image_desc out_desc;
362  rifImageGetInfo(*rgb, RIF_IMAGE_DESC, sizeof(out_desc), &out_desc, &out_size);
363 
364  RIFImage out;
365  RIFImage normalsImg;
366  RIFImage depthImg;
367  PXL_Raster normalsImg_store;
368  PXL_Raster depthImg_store;
369  out_desc.num_components = 3;
370  out_desc.type = RIF_COMPONENT_TYPE_FLOAT32;
371  RIF_CALL_FAIL(status, myErrorString, rifContextCreateImage(*ctx,
372  &out_desc, nullptr, out.ptr()));
373 
375  if (myFilterType == RADEON_FILTER_TYPE::DENOISE)
376  {
377  RIF_CALL_FAIL(status, myErrorString, rifContextCreateImageFilter(*ctx,
378  RIF_IMAGE_FILTER_EAW_DENOISE, filter.ptr()));
379  auto it = myAuxPlanes.find(theNName.asRef());
380  if (it != myAuxPlanes.end())
381  {
382  if (uploadRaster(ctx, normalsImg, "normalsImg",
383  *it->second, normalsImg_store))
384  {
386  rifImageFilterSetParameterImage(*filter, "normalsImg", *normalsImg));
387  }
388  }
389  it = myAuxPlanes.find(thePzName.asRef());
390  if (it != myAuxPlanes.end())
391  {
392  if (uploadRaster(ctx, depthImg, "depthImg",
393  *it->second, depthImg_store))
394  {
396  rifImageFilterSetParameterImage(*filter, "depthImg", *depthImg));
397  }
398  }
399  RIF_CALL_FAIL(status, myErrorString, rifCommandQueueAttachImageFilter(*queue,
400  *filter, *rgb, *out));
401  }
402  else if (myFilterType == RADEON_FILTER_TYPE::AIDENOISE)
403  {
404  RIF_CALL_FAIL(status, myErrorString, rifContextCreateImageFilter(*ctx,
405  RIF_IMAGE_FILTER_AI_DENOISE, filter.ptr()));
406  // Specifies if the color and albedo inputs are in the HDR or LDR format
407  RIF_CALL_FAIL(status, myErrorString, rifImageFilterSetParameter1u(
408  *filter, theUseHDR.c_str(), 1));
409  RIF_CALL_FAIL(status, myErrorString, rifImageFilterSetParameterString(
410  *filter, theModelPath.c_str(), myModelPath.c_str()));
411  RIF_CALL_FAIL(status, myErrorString, rifImageFilterSetParameterImage(
412  *filter, colorName, *rgb));
413  RIF_CALL_FAIL(status, myErrorString, rifCommandQueueAttachImageFilter(*queue,
414  *filter, *rgb, *out));
415  }
416  else if (myFilterType == RADEON_FILTER_TYPE::BLOOM)
417  {
418  RIF_CALL_FAIL(status, myErrorString, rifContextCreateImageFilter(*ctx,
419  RIF_IMAGE_FILTER_BLOOM, filter.ptr()));
420  RIF_CALL_FAIL(status, myErrorString, rifImageFilterSetParameter1f(
421  *filter, theRadius.c_str(), myRadius));
422  RIF_CALL_FAIL(status, myErrorString, rifImageFilterSetParameter1f(
423  *filter, theThreshold.c_str(), myThreshold));
424  RIF_CALL_FAIL(status, myErrorString, rifImageFilterSetParameter1f(
425  *filter, theWeight.c_str(), myWeight));
426  RIF_CALL_FAIL(status, myErrorString, rifCommandQueueAttachImageFilter(*queue,
427  *filter, *rgb, *out));
428  }
429  RIF_CALL_FAIL(status, myErrorString, rifContextExecuteCommandQueue(
430  *ctx, *queue, nullptr, nullptr, nullptr));
431 
432  DEBUG_PRINT("Run Time: {}", timer.lap());
433  downloadRaster(*raster, out);
434 
435  rifCommandQueueDetachImageFilter(*queue, *filter);
436 
437  return true;
438 }
439 
440 namespace
441 {
442  struct Factory final : public TIL_RasterFilter::Factory
443  {
444  Factory(RADEON_FILTER_TYPE type)
445  : myType(type)
446  {
447  }
448  const UT_StringHolder &name() const override
449  {
450  static constexpr UT_StringLit theBloom ("radeon_bloom");
451  static constexpr UT_StringLit theDenoise("radeon_denoise");
452  static constexpr UT_StringLit theAIDenoise("radeon_aidenoise");
453  switch (myType)
454  {
456  return theBloom.asHolder();
458  return theDenoise.asHolder();
460  return theAIDenoise.asHolder();
461  }
463  }
464  const UT_StringHolder &label() const override
465  {
466  static constexpr UT_StringLit theBloom ("AMD Radeon Bloom");
467  static constexpr UT_StringLit theDenoise("AMD Radeon Denoise");
468  static constexpr UT_StringLit theAIDenoise("AMD Radeon AI Denoise");
469  switch (myType)
470  {
472  return theBloom.asHolder();
474  return theDenoise.asHolder();
476  return theAIDenoise.asHolder();
477  }
479  }
481  {
482  return UTmakeUnique<TIL_RadeonFilter>(myType);
483  }
484 
485  RADEON_FILTER_TYPE myType;
486  };
487 }
488 
489 void
491 {
492  // Register different filters
493  DEBUG_PRINT("Registering Radeon Image Filters");
495  UTmakeUnique<Factory>(RADEON_FILTER_TYPE::DENOISE));
497  UTmakeUnique<Factory>(RADEON_FILTER_TYPE::BLOOM));
499  UTmakeUnique<Factory>(RADEON_FILTER_TYPE::AIDENOISE));
500 }
GLdouble s
Definition: glew.h:1390
void * getPixels()
Definition: PXL_Raster.h:125
virtual const UT_StringHolder & name() const =0
UT_StringHolder myErrorString
int PXLpackingComponents(PXL_Packing p)
Definition: PXL_Common.h:118
GLenum src
Definition: glew.h:2410
exint getYres() const
Definition: PXL_Raster.h:90
UT_StringArray myAuxPlaneNames
fpreal64 lap() const
const Args & args
Definition: printf.h:628
GLenum GLenum GLenum GLenum GLenum scale
Definition: glew.h:13880
int int int vsprintf(const char *fmt, va_list ap)
int64 exint
Definition: SYS_Types.h:125
virtual const UT_StringHolder & label() const =0
GLint void * img
Definition: glew.h:1385
PXL_DataFormat getFormat() const
Definition: PXL_Raster.h:94
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:33
#define RIF_CALL(status, msg, call)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:2981
bool setOptions(const UT_Options &argstr) override
PXL_Packing getPacking() const
Definition: PXL_Raster.h:95
GLclampf f
Definition: glew.h:3499
void setPacking(PXL_Packing p)
GLint GLenum GLsizei GLint GLsizei const void * data
Definition: glew.h:1379
exint getNumPixels() const
Definition: PXL_Raster.h:91
static const UT_StringHolder theEmptyString
#define SYS_PRINTF_CHECK_ATTRIBUTE(string_index, first_to_check)
Definition: SYS_Types.h:433
virtual ~TIL_RadeonFilter()
GLsizei n
Definition: glew.h:4040
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the queue
Definition: thread.h:643
SYS_FORCE_INLINE const char * c_str() const
static void registerFactory(UT_UniquePtr< Factory > factory)
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1253
virtual TIL_RasterFilterPtr newFilter() const =0
exint append()
Definition: UT_Array.h:95
void newRasterFilter()
GLuint GLsizei GLsizei GLchar * label
Definition: glew.h:8986
bool apply(PXL_Raster *raster) override
A map of string to various well defined value types.
Definition: UT_Options.h:84
The factory to define a filter.
#define DEBUG_PRINT(...)
basic_printf_context_t< buffer >::type context
Definition: printf.h:631
TIL_RadeonFilter(RADEON_FILTER_TYPE type)
GLdouble GLdouble GLdouble r
Definition: glew.h:1406
OIIO_API bool copy(string_view from, string_view to, std::string &err)
#define BACKEND_TYPE
#define RIF_CALL_FAIL(status, msg, call)
RADEON_FILTER_TYPE
getOption("OpenEXR.storage") storage
Definition: HDK_Image.dox:276
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:135
#define const
Definition: zconf.h:214
Declare prior to use.
exint getXres() const
Definition: PXL_Raster.h:89
unsigned int uint
Definition: SYS_Types.h:45
UT_StringMap< const PXL_Raster * > myAuxPlanes
bool importOption(const UT_StringRef &name, int &value) const
void UTparallelFor(const Range &range, const Body &body, const int subscribe_ratio=2, const int min_grain_size=1)