FreeCAD / FreeCAD-render

The Render workbench
Other
163 stars 35 forks source link

Unable to use self-compiled pbrt for rendering on Windows #465

Open 7sDream opened 4 days ago

7sDream commented 4 days ago

A few days ago, I successfully compiled pbrt, and if I run it directly, it works well:

But if I try to use it to render in FreeCAD, it fails during testing:

The exit code is 3221225785.

If I ignore the test results and proceed directly to rendering, the opened image is blank, and there are no image files under the displayed path. The console errors are basically the same as testing:

00:51:54  [Render][Objstrings] STARTING OBJECTS EXPORT
00:51:54  [Render][Objstrings] 1 chunks: 0 heavy - 1 light (size: 1)
00:51:54  [Render][Objstrings] '_D_297_render#Body001': Exporting
00:51:54  [Render][Object] 'BodyForRender': Processing
00:51:54  [Render][Object] 'BodyForRender': 'Part::Feature' detected
00:51:54  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Begin meshing
00:51:54  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Autosmooth
00:51:54  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Compute connected components (np)
00:51:54  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Compute vertex normals (np)
00:51:54  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': End meshing (0.1295325756072998s)
00:51:54  [Render][Material] ''_D_297_render#Body001' <None>': Fallback to default material
00:51:54  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Write mesh file
00:51:54  [Render][Objstrings] ENDING OBJECTS EXPORT - TIME: 0.15353798866271973
00:51:54  Starting rendering...
"D:/Cloud/Github/pbrt-v4/build/pbrt.exe"  --outfile "D:\Cloud\App\FreeCAD\Config\temp\FreeCAD_Doc_8326dd1b-ffbf-4b30-9abd-a87aa7d11348_82a4d4_577723\Project_output.png"  --spp 128  "D:\Cloud\App\FreeCAD\Config\temp\FreeCAD_Doc_8326dd1b-ffbf-4b30-9abd-a87aa7d11348_82a4d4_577723\Project.pbrt"
00:51:54  Exiting rendering - Return code: 3221225785
00:51:54  D:\Cloud\App\FreeCAD\Config\Mod\Render\.\Render\rdrexecutor.py:125: RuntimeWarning: libshiboken: Overflow: Value 3221225785 exceeds limits of type  [signed] "int" (4bytes).
  self.finished.emit(rcode)
00:51:54  OverflowError

Any ideas?

7sDream commented 4 days ago

I notice that the console shows the command it executed, and I try to run it by hand, it fails, with an error of:

❯ D:\Cloud\Github\pbrt-v4\build\pbrt.exe  --outfile "D:\Cloud\App\FreeCAD\Config\temp\FreeCAD_Doc_8326dd1b-ffbf-4b30-9abd-a87aa7d11348_82a4d4_369147\Project_output.png"  --spp 128  "D:\Cloud\App\FreeCAD\Config\temp\FreeCAD_Doc_8326dd1b-ffbf-4b30-9abd-a87aa7d11348_82a4d4_369147\Project.pbrt"
pbrt version 4 (built Nov 27 2024 at 23:26:26)
Copyright (c)1998-2021 Matt Pharr, Wenzel Jakob, and Greg Humphreys.
The source code to pbrt (but *not* the book contents) is covered by the Apache 2.0 License.
See the file LICENSE.txt for the conditions of the license.
Error: D:\Cloud\App\FreeCAD\Config\temp\FreeCAD_Doc_8326dd1b-ffbf-4b30-9abd-a87aa7d11348_82a4d4_369147\Project.pbrt:44:24: unexpected escaped character "_"

So I open the generated pbrt file, and found the error source:

looks like pbrt think the path separator \ is an escape character.

So I edit the file, change it to \\, and the command just works:

But in FreeCAD, the render command still doesn't work. And this escape issue doesn't explain why test command --help fails too...

7sDream commented 4 days ago

Although this path escaping is not the root of the issue, it should also be a potential problem; I will attempt to send a PR to resolve it.

Then I will continue to delve into the root cause of the test command failure.

7sDream commented 4 days ago

The exit code 3221225785 is 0xC0000139, and it means (from windows error code tool):

<?xml version="1.0" standalone="no"?>
<ErrV1>
<err n='0xc0000139' name='STATUS_ENTRYPOINT_NOT_FOUND' src='ntstatus.h'>{Entry Point Not Found}&#10;The procedure entry point %hs could not be located in the dynamic link library %hs.</err>
</ErrV1>

Which means something is wrong with the dll it use. So I checked it:

Everything look fine for me, no unresolved function name etc at least. This is expected through, because irectly running this exe doesn't seem to have any issues. Is there any other info I can provide?

7sDream commented 4 days ago

I came up with a idea, write a wrapper to launch pbrt with a debugger, to see what exactly happens during runtime.

@echo off
pushd "%~dp0"
D:\scoop\apps\x64dbg\current\release\x64\x64dbg.exe "%~dp0pbrt.exe" "%*" "%~dp0"
popd

Use this bat file as the Pbrt executable path.

With the help of x64dbg's awesome user dll loaded break point.

It shows pbrt uses some dll in FreeCAD's bin folder when running? Upon inspecting the PATH environment variables, I found a bunch of paths related to FreeCAD:

So I changed the bat file and shows the PATH env var directlly, to get a clear result:

I'm not sure if these paths were deliberately added to the PATH environment variables. Is there a way to disable this behavior? I think it should be the cause of my pbrt exiting with dll error.

7sDream commented 4 days ago

I just realized that since the wrapper bat can be used, I can directly delete the unnecessary FreeCAD path in PATH.

But I must go to sleep now... I can try doing it tomorrow. Wish me luck.

7sDream commented 3 days ago

The horror story continues.

I wrote the following script to clean up FreeCAD's modifications to PATH:

@echo off
pushd "%~dp0"
call :removeFreeCADInPath
echo After clean, PATH = %PATH%
pbrt.exe %*
popd
exit /b %ERRORLEVEL%

REM ===== Functions =====
REM set new PATH env var, remove all paths start with "D:\Cloud\App\FreeCAD\"
:removeFreeCADInPath
setlocal enabledelayedexpansion
    set newPATH=
    for %%A in ("%PATH:;=";"%") do (
        set "item=%%A"
        set "unquote=!item:~1,-1!"
        set "prefix=!unquote:~0,21!"
        if not "!prefix!"=="D:\Cloud\App\FreeCAD\" (
            set "newPATH=!newPATH!;!unquote!"
        ) else (
            echo Remove !unquote! from PATH
        )
    )
    REM remove the first semicolon character
    set newPATH=%newPATH:~1%
endlocal && set "PATH=%newPATH%"
exit /b 0

From the logs, the script has achieved its intended purpose, and the PATH is now normal before running pbrt. Here is the log:

02:36:52  [Render][Objstrings] STARTING OBJECTS EXPORT
02:36:52  [Render][Objstrings] 1 chunks: 0 heavy - 1 light (size: 1)
02:36:52  [Render][Objstrings] '_D_297_render#Body001': Exporting
02:36:52  [Render][Object] 'BodyForRender': Processing
02:36:52  [Render][Object] 'BodyForRender': 'Part::Feature' detected
02:36:52  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Begin meshing
02:36:52  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Autosmooth
02:36:52  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Compute connected components (np)
02:36:52  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Compute vertex normals (np)
02:36:52  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': End meshing (0.11773252487182617s)
02:36:52  [Render][Material] ''_D_297_render#Body001' <None>': Fallback to default material
02:36:52  [Render][Object] ''BodyForRender' ('_D_297_render#Body001')': Write mesh file
02:36:52  [Render][Objstrings] ENDING OBJECTS EXPORT - TIME: 0.1390519142150879
02:36:52  Starting rendering...
"D:/Cloud/Github/pbrt-v4/build/pbrt_fixpath.bat"  --outfile "D:\Cloud\App\FreeCAD\Config\temp\FreeCAD_Doc_8326dd1b-ffbf-4b30-9abd-a87aa7d11348_82a4d4_60475\Project_output.png"  --spp 128  "D:\Cloud\App\FreeCAD\Config\temp\FreeCAD_Doc_8326dd1b-ffbf-4b30-9abd-a87aa7d11348_82a4d4_60475\Project.pbrt"
02:36:52  Remove D:\Cloud\App\FreeCAD\App\bin\Lib\site-packages\PySide2 from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\bin from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\AddonManager from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Assembly from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\BIM from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\CAM from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Draft from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Fem from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Help from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Idf from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Import from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Inspection from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Material from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Measure from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Mesh from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\MeshPart from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\OpenSCAD from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Part from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\PartDesign from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Plot from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Points from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\ReverseEngineering from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Robot from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Show from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Sketcher from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Spreadsheet from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Start from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Surface from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\TechDraw from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Test from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Tux from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\App\Mod\Web from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\Curves from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\Curves\.\ from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\fasteners from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\fasteners\.\ from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\OpticsWorkbench from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\OpticsWorkbench\.\ from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\PieMenu from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\PieMenu\.\ from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\Render from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\Render\.\ from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\sheetmetal from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\sheetmetal\.\ from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\Silk from PATH
02:36:52  Remove D:\Cloud\App\FreeCAD\Config\Mod\Silk\.\ from PATH
02:36:52  After clean, PATH = C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\dotnet\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;D:\scoop\apps\gpg4win\current\Gpg4win\..\GnuPG\bin;D:\Program Files\PowerShell\7\;C:\Program Files\Docker\Docker\resources\bin;C:\Program Files\NVIDIA Corporation\NVIDIA app\NvDLISR;C:\Windows\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps;D:\scoop\apps\vscode\current\bin;D:\scoop\apps\imagemagick\current;D:\scoop\apps\msys2\current\ucrt64\bin;D:\scoop\apps\gpg4win\current\GnuPG\bin;D:\scoop\apps\gpg4win\current\Gpg4win\bin;D:\scoop\apps\mpv\current;D:\scoop\apps\rustup-msvc\current\.cargo\bin;D:\scoop\shims;D:\Cloud\Script;C:\Users\7sDream\AppData\Local\Microsoft\WindowsApps;C:\Users\7sDream\.local\bin
02:37:10  Exiting rendering - Return code: 3221225785
02:37:10  D:\Cloud\App\FreeCAD\Config\Mod\Render\.\Render\rdrexecutor.py:125: RuntimeWarning: libshiboken: Overflow: Value 3221225785 exceeds limits of type  [signed] "int" (4bytes).
  self.finished.emit(rcode)
02:37:10  OverflowError

But strangely, pbrt still cannot start, with the same error code.

It seems that things have begun to develop in a direction I cannot understand...

7sDream commented 3 days ago

After carefully reading Microsoft's documentation on DLL search order, I found that the SetDllDirectory function is likely the cause of the problem:

The standard search order of the process will also be affected by calling the SetDllDirectory function in the parent process before the start of the current process.

I tried hooking these functions related to DLL search to see if FreeCAD would use them, and the results were as follows:

Sure enough, it calls SetDllDirectory with the bin directory as an argument upon startup.

Then I suddenly realized that FreeCAD itself is also open-source software, and I can directly look up the code. Right here

7sDream commented 3 days ago

It seems that the truth has emerged. Since the previous comments were basically written while testing, they are quite messy, so I will summarize them briefly.

I use msys2(ucrt64) as the development environment on Windows, so I also use it to compile pbrt. The msys2 bin path has been in my PATH, so when the environment is ok, this exe can be used normally.

When calling from FreeCAD, there are three issues:

  1. The string fields in the pbrt files generated by FreeCAD-render will have unescaped \ which causes pbrt to fail to parse. (I believe this has been solved by #466.)
  2. FreeCAD modify the PATH environment variable, adding its own bin directory and many directories of addons, which will affect the DLL search path. (This can be solved by cleaning up PATH in the bat wrapper, see the comment above)
  3. FreeCAD uses the SetDllDirectory win32 API to set the bin directory as the DLL search directory when it starts. The directorie set in this way will also affect child processes. (There is no good solution for the time being)

For issue 3, it might be possible to solve it by replacing the bat wrapper with a real program that calls SetDllDirectory again before start pbrt, but this seems to be a bit too troublesome...even for developers...

luzpaz commented 21 hours ago

Great sleuthing here. I think points 2 and 3 are interesting and maybe worth discussing further