Blender Cycles调用自定义函数崩溃问题(set函数引发)
从你贴的代码来看,调用update_bcd_inputs里的set函数导致崩溃,大概率是以下几个原因之一,咱们来逐个排查:
1. 多线程并发读写的数据竞争问题
你提到sStats是CPUDevice的属性,thread_render负责初始化它,而path_trace在渲染循环里调用update_bcd_inputs修改它的m_meanImage。Cycles默认是多线程渲染的,多个渲染线程可能会同时访问和修改同一个sStats->m_meanImage实例——但getValue和set操作完全没有同步机制,这会引发数据竞争,破坏内存结构,最终导致崩溃。
解决思路:
给update_bcd_inputs加个线程同步锁,比如用std::mutex:
// 先在CPUDevice类里加一个mutex成员变量 std::mutex stats_mutex; void update_bcd_inputs(int x, int y, float sampleR, float sampleG, float sampleB){ std::lock_guard<std::mutex> lock(stats_mutex); // 加锁,保证同一时间只有一个线程操作图像 float current_valueR = sStats->m_meanImage.getValue(bcd::PixelPosition(x, y), 0); float current_valueG = sStats->m_meanImage.getValue(bcd::PixelPosition(x, y), 1); float current_valueB = sStats->m_meanImage.getValue(bcd::PixelPosition(x, y), 2); sStats->m_meanImage.set(x, y, 0, current_valueR + sampleR); sStats->m_meanImage.set(x, y, 1, current_valueG + sampleG); sStats->m_meanImage.set(x, y, 2, current_valueB + sampleB); }
2. 坐标越界访问DeepImage
你在thread_render里用task.w, task.h初始化sStats的图像,但path_trace里的x,y是基于tile的坐标:x从tile.x到tile.x + tile.w,y从tile.y到tile.y + tile.h。如果tile的范围超出了task.w和task.h(比如tile的右下角坐标超过了图像尺寸),调用set(x,y,...)就会越界访问m_meanImage的内存,直接触发崩溃。
解决思路:
在调用update_bcd_inputs前先检查坐标是否在有效范围内:
// 在path_trace的x/y循环里加个判断 if(x < task.w && y < task.h) { update_bcd_inputs(x, y, sampleR*inv_weight, sampleG*inv_weight, sampleB*inv_weight); }
或者确认task.w和task.h是整个渲染图像的尺寸,tile的坐标不会超出这个范围。
3. sStats指针的生命周期问题
有没有可能path_trace执行的时候,sStats还没被初始化(还是nullptr),或者已经被释放了?比如:
thread_render还没完成sStats的初始化,path_trace就开始调用update_bcd_inputs,此时sStats是空指针,访问它的成员会直接崩溃。- 渲染任务结束后
sStats被销毁,但某个线程还在执行path_trace的循环,继续访问已释放的内存。
解决思路:
- 确保
sStats在path_trace执行前完成初始化,比如在thread_render里初始化完成后再启动path_trace任务。 - 在销毁
sStats前,确保所有渲染线程都已经结束,不再访问它。
4. DeepImage的通道数不匹配
m_meanImage是DeepImage<float>,如果SamplesStatisticsImages的构造函数没有把它初始化为3通道(对应R/G/B),那么调用set(x,y,2,...)访问第3个通道(索引从0开始)就会越界,导致崩溃。
解决思路:
检查SamplesStatisticsImages的构造函数,确认m_meanImage被初始化为3通道:
SamplesStatisticsImages::SamplesStatisticsImages(int i_width, int i_height, int i_nbOfBins) : m_nbOfSamplesImage(i_width, i_height, 1), // 1通道:记录每个像素的采样数 m_meanImage(i_width, i_height, 3), // 3通道:分别存储R/G/B的累计值 m_covarImage(i_width, i_height, 6), // 协方差通常是6个值(3通道的两两组合) m_histoImage(i_width, i_height, i_nbOfBins * 3) // 每个通道对应一组直方图 {}
如果构造函数里m_meanImage的通道数不是3,就会导致访问越界。
内容的提问来源于stack exchange,提问作者Blobixx

