Direct3D 12 Memory Allocation Crash
Hello everyone, I'm really stumped on this sporadic crash that happens in my app and I'm running out of ideas. I keep getting a memory access violation error, my stack trace has a couple of variations but always ends up with D3D12DescriptorCache::Allocate.
In short my app is passing 2 textures as native array pointers to a custom c++ dll that then writes in them before I call Texture2D.Apply in unity. This was something I ended up doing cause I needed to handle multiple devices at a high framerate that was not possible with unity's standard LoadData function. here is my code:
Here is a snippet of my unity code:
void UpdateFrame() { try { int newFrameResult = 0;
if(!isDisabled)
{
texDataColor = colTex.GetRawTextureData<Color32>();
texDataDepth = depthTex.GetRawTextureData<Color32>();
unsafe
{
IntPtr colorPtr = (IntPtr)NativeArrayUnsafeUtility.GetUnsafePtr(texDataColor);
IntPtr depthPtr = (IntPtr)NativeArrayUnsafeUtility.GetUnsafePtr(texDataDepth);
if(colorPtr != IntPtr.Zero && depthPtr != IntPtr.Zero)
{
newFrameResult = GetLatestLiveFrames(colorPtr, depthPtr, kinectIndex, resolution.x, resolution.y, offset, limitIR, maxDepthMM);
}
else
{
newFrameResult = 4;
}
}
if(newFrameResult == 1)
{
colTex.Apply();
depthTex.Apply();
cShader.SetInt("w", resolution.x);
cShader.SetInt("h", resolution.y);
cShader.SetBool("flipX", flipX);
cShader.SetBool("flipY", flipY);
cShader.SetFloat("maxDepth", maxDepth);
cShader.Dispatch(0, resolution.x / 8, resolution.y / 8, 1);
cShader.Dispatch(1, resolution.x / 8, resolution.y / 8, 1);
}
}
else
{
RenderTexture.active = colRT;
GL.Clear(true, true, Color.black);
RenderTexture.active = depthRT;
GL.Clear(true, true, Color.black);
RenderTexture.active = null;
newFrameResult = 1;
}
if(texDataColor.IsCreated)
{
texDataColor.Dispose();
}
if(texDataDepth.IsCreated)
{
texDataDepth.Dispose();
}
if(newFrameResult == 1)
{
threeSlicePipeline.SendTexture(areaID, colRT, depthRT, this, !isDisabled);
}
else
{
switch(newFrameResult)
{
case 0:
Debug.LogWarning($"{gameObject.name}: DLL Error Maybe null pointer! Skipping frame!");
break;
case 2:
if(useRecording)
{
Debug.LogWarning($"{gameObject.name}: DLL Error EOF! Skipping frame!");
}
else
{
Debug.LogWarning($"{gameObject.name}: DLL Error Timeout! Skipping frame!");
}
break;
case 3:
Debug.LogWarning($"{gameObject.name}: DLL Error Failed to pull capture! Skipping frame!");
break;
case 4:
Debug.LogWarning($"{gameObject.name}: Unity Error NULL pointer found! Skipping frame!");
break;
}
ReadyForNextFrame();
}
}
catch
{
Debug.LogWarning($"{gameObject.name}: Error caught while pulling frame, skipping!");
if(texDataColor.IsCreated)
{
texDataColor.Dispose();
}
if(texDataDepth.IsCreated)
{
texDataDepth.Dispose();
}
ReadyForNextFrame();
}
}
Here is a snippet of my DLL code:
void MemCopyImage(uint8_t* _buffer, uint8_t* _dst, int _dstW, int _dstH, int _srcXOffset, size_t _stride, int _pixelSizeInBytes) { uint8_t* ptr = _dst; uint8_t* sourcePtr = _buffer;
int chunkHeight = _dstH / 12; //denominator MUST cleanly divide the height
sourcePtr += _srcXOffset * _pixelSizeInBytes;
if (sourcePtr == NULL || *sourcePtr == NULL) {
OutputDebugString(L"Bad source pointer");
return;
}
if (ptr == NULL || *ptr == NULL) {
OutputDebugString(L"Bad destination pointer");
return;
}
std::vector<std::thread> threads;
std::mutex mtx;
for (int i = 0; i < 12; i++) {
threads.push_back(std::thread(CopyImageInThread, sourcePtr, ptr, _dstW, _dstH, _stride, _pixelSizeInBytes, i, chunkHeight, std::ref(mtx)));
}
for (auto& th : threads) {
th.join();
}
}
void CopyImageInThread(uint8_t* _sourceStart, uint8_t* _dstStart, int _dstW, int _dstH, size_t _stride, int _pixelSizeInBytes, int _index, int _chunkHeight, std::mutex& _mtx)
{
//image 0,0 position
uint8_t* sourcePtr = _sourceStart;
uint8_t* dstPtr = _dstStart;
//strides
size_t destinationStride = _dstW * _pixelSizeInBytes;
size_t srcStride = _stride;
//starting position for this chunk
sourcePtr += srcStride * _index * _chunkHeight;
dstPtr += destinationStride * _index * _chunkHeight;
if (sourcePtr == NULL || *sourcePtr == NULL) {
OutputDebugString(L"Bad source pointer");
return;
}
if (dstPtr == NULL || *dstPtr == NULL) {
OutputDebugString(L"Bad destination pointer");
return;
}
if (IsBadHugeReadPtr(sourcePtr, destinationStride)) {
OutputDebugString(L"Cant Read source pointer");
return;
}
if (IsBadHugeWritePtr(dstPtr, destinationStride)) {
OutputDebugString(L"Cant Read destination pointer");
return;
}
_mtx.lock();
for (int j = 0; j < _chunkHeight; j++)
{
try {
memcpy(dstPtr, sourcePtr, destinationStride);
}
catch (...) {
OutputDebugString(L"Problem writing line");
}
//move for the next copy
sourcePtr += srcStride; //move by 1 row
dstPtr += destinationStride;
}
_mtx.unlock();
}
I attached the last couple of crash logs I got. As you can see on the code I have added plenty of null pointer checks and I designed the code so that there should never be any type of memory overlaps. The C# function only runs once the DLL is completely done with the previous cycle.
Any help on this issue would be much appreciated! link text