Open9

DX8 → wined3d → gl4es → ANGLE実験

okuokuokuoku

とりあえずクラッシュしないところまで行ったので、後は何とか出画しない原因を探したい。。

SwiftShaderを使う

毎度おなじみSwiftShaderは32bitアプリでも使用できる。メモリ足りるのかな。。SwiftShaderはgitでチェックアウト後CMakeで直接ビルドできる。ビルド後、 Windows\vk_swiftshader_icd.json を環境変数 VK_ICD_FILENAMES に指定する。 ...当然、ターゲットアプリを起動するときの環境変数に設定する必要がある

VK_ICD_FILENAMES=F:\swiftshader32\Windows\vk_swiftshader_icd.json

IntelのVulkanは自分自身のOpenGLに依存しているため使えなかった。(今回はOpenGLをgl4esを使用して自前で実装しているので、OpenGLに依存している外部ライブラリは一切使えない)

prev

https://zenn.dev/okuoku/scraps/90791dc3419681

okuokuokuoku

RenderDoc → クラッシュする

Vulkanアプリのデバッグと言えばRenderDocだね! ただ、RenderDocはOpenGLもhookしようとしてしまうため諸々を破壊してしまう。最初のWined3d初期化の部分までは正常に処理できるものの、ゲームに入ると即死する。

RenderDoc読み込み時のクラッシュは前DebugDiagを使って頑張って調べたけど、単に環境変数でimplicit layerとして読まれるRenderDocを有効化すれば十分だった。

https://zenn.dev/okuoku/scraps/c105b9fa7c96e7

ENABLE_VULKAN_RENDERDOC_CAPTURE=1

環境変数 ENABLE_VULKAN_RENDERDOC_CAPTURE を指定してVulkanアプリを起動すると、RenderDocから接続できるようになる。

okuokuokuoku

gfxreconstruct → キャプチャはできるがフレームがない

余計なAPI hookが無い gfxreconstruct を試してみることにする。

バイナリはVulkan SDKに収録されていて、Layerについては32bit版が提供されている。

VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_gfxreconstruct
GFXRECON_CAPTURE_FILE=f:\capture.gfxr
GFXRECON_CAPTURE_FILE_FLUSH=true
GFXRECON_LOG_LEVEL=debug
GFXRECON_LOG_OUTPUT_TO_OS_DEBUG_STRING=true
VK_LAYER_PATH=C:\VulkanSDK\1.3.236.0\Bin32

キャプチャはできたものの、フレームが無い。このため、ANGLEから見たEGL的なコンテキストが正常にflipできていない可能性が高いと見られる。... まぁ正常にキャプチャできてない可能性も考える必要があるが。。

C:\VulkanSDK\1.3.236.0\Bin>gfxrecon-info f:\capture_20221218T153529.gfxr
File info:
        Compression format: LZ4
        Total frames: 0

Application info:
        Application name: ZWEI2P.exe
        Application version: 1
        Engine name: ANGLE
        Engine version: 1
        Target API version: 4198400 (1.1.0)

Physical device info:
        Device name: SwiftShader Device (LLVM 10.0.0)
        Device ID: 0xc0de
        Vendor ID: 0x1ae0
        Driver version: 20971520 (0x1400000)
        API version: 4206592 (1.3.0)

Physical device info:
        Device name: SwiftShader Device (LLVM 10.0.0)
        Device ID: 0xc0de
        Vendor ID: 0x1ae0
        Driver version: 20971520 (0x1400000)
        API version: 4206592 (1.3.0)

Device memory allocation info:
        Total allocations: 556
        Min allocation size: 16
        Max allocation size: 5592416

Pipeline info:
        Total graphics pipelines: 121
        Total compute pipelines: 0

Annotation info:
        Total annotations: 1
        Operation annotations: 1

        {
            "tool": "capture",
            "timestamp": "2022-11-18T06:35:29Z",
            "gfxrecon-version": "0.9.16 (e4b9092)",
            "vulkan-version": "1.3.236"
        }

File did not contain any frames
okuokuokuoku

確かにFlipしていない

とりあえずgfxrecon-convertでjsonlに変換してみて中身を見ると、

{"index":196,"vkFunc":{"name":"vkGetPhysicalDeviceSurfaceFormats2KHR","return":"VK_SUCCESS","args":{"physicalDevice":2,"pSurfaceInfo":{"sType":"VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR","pNext":null,"surface":14},"pSurfaceFormatCount":2,"pSurfaceFormats":[{"sType":"VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR","pNext":null,"surfaceFormat":{"format":"VK_FORMAT_B8G8R8A8_UNORM","colorSpace":"VK_COLOR_SPACE_SRGB_NONLINEAR_KHR"}},{"sType":"VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR","pNext":null,"surfaceFormat":{"format":"VK_FORMAT_B8G8R8A8_SRGB","colorSpace":"VK_COLOR_SPACE_SRGB_NONLINEAR_KHR"}}]}}}
{"index":198,"vkFunc":{"name":"vkCreateSwapchainKHR","return":"VK_SUCCESS","args":{"device":3,"pCreateInfo":{"sType":"VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR","pNext":null,"flags":0,"surface":14,"minImageCount":3,"imageFormat":"VK_FORMAT_B8G8R8A8_UNORM","imageColorSpace":"VK_COLOR_SPACE_SRGB_NONLINEAR_KHR","imageExtent":{"width":120,"height":1},"imageArrayLayers":1,"imageUsage":151,"imageSharingMode":"VK_SHARING_MODE_EXCLUSIVE","queueFamilyIndexCount":0,"pQueueFamilyIndices":null,"preTransform":"VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR","compositeAlpha":"VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR","presentMode":"VK_PRESENT_MODE_FIFO_KHR","clipped":1,"oldSwapchain":"VK_NULL_HANDLE"},"pAllocator":null,"pSwapchain":15}}}
{"index":199,"vkFunc":{"name":"vkGetSwapchainImagesKHR","return":"VK_SUCCESS","args":{"device":3,"swapchain":15,"pSwapchainImageCount":3,"pSwapchainImages":null}}}
{"index":200,"vkFunc":{"name":"vkGetSwapchainImagesKHR","return":"VK_SUCCESS","args":{"device":3,"swapchain":15,"pSwapchainImageCount":3,"pSwapchainImages":[16,17,18]}}}
{"index":201,"vkFunc":{"name":"vkCreateSemaphore","return":"VK_SUCCESS","args":{"device":3,"pCreateInfo":{"sType":"VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO","pNext":null,"flags":0},"pAllocator":null,"pSemaphore":19}}}
{"index":202,"vkFunc":{"name":"vkCreateSemaphore","return":"VK_SUCCESS","args":{"device":3,"pCreateInfo":{"sType":"VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO","pNext":null,"flags":0},"pAllocator":null,"pSemaphore":20}}}
{"index":203,"vkFunc":{"name":"vkCreateSemaphore","return":"VK_SUCCESS","args":{"device":3,"pCreateInfo":{"sType":"VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO","pNext":null,"flags":0},"pAllocator":null,"pSemaphore":21}}}
{"index":204,"vkFunc":{"name":"vkCreateFence","return":"VK_SUCCESS","args":{"device":3,"pCreateInfo":{"sType":"VK_STRUCTURE_TYPE_FENCE_CREATE_INFO","pNext":null,"flags":0},"pAllocator":null,"pFence":22}}}
{"index":205,"vkFunc":{"name":"vkAcquireNextImageKHR","return":"VK_SUCCESS","args":{"device":3,"swapchain":15,"timeout":18446744073709551615,"semaphore":19,"fence":22,"pImageIndex":0}}}

vkGetSwapchainImagesKHR でswapchain上のimageを拾っているのに、 vkQueuePresentKHR の類がダンプにない。

{"index":64339,"vkFunc":{"name":"vkCmdSetStencilTestEnableEXT","args":{"commandBuffer":4282,"stencilTestEnable":0}}}
{"index":64340,"vkFunc":{"name":"vkCmdSetStencilOpEXT","args":{"commandBuffer":4282,"faceMask":1,"failOp":"VK_STENCIL_OP_KEEP","passOp":"VK_STENCIL_OP_KEEP","depthFailOp":"VK_STENCIL_OP_KEEP","compareOp":"VK_COMPARE_OP_ALWAYS"}}}
{"index":64341,"vkFunc":{"name":"vkCmdSetStencilOpEXT","args":{"commandBuffer":4282,"faceMask":2,"failOp":"VK_STENCIL_OP_KEEP","passOp":"VK_STENCIL_OP_KEEP","depthFailOp":"VK_STENCIL_OP_KEEP","compareOp":"VK_COMPARE_OP_ALWAYS"}}}
{"index":64342,"vkFunc":{"name":"vkCmdDraw","args":{"commandBuffer":4282,"vertexCount":4,"instanceCount":1,"firstVertex":0,"firstInstance":0}}}

vkCmdDraw はあるのでコマンドバッファ自体は構築している。上位層で glFinish なり glFlush しないとダメとかあるんだろうか。。

okuokuokuoku

ANGLE側を追う

とりあえず、SwapBuffer自体は呼べているようなので、その処理がどこまで到達しているかを確認する。

EVENT: glGetError(context = 1)
DUMMY: wglSwapBuffers
EVENT: eglPrepareSwapBuffersANGLE(dpy = 0x000000000d627288, surface = 0x0000000000000001)
DUMMY: wglGetPixelFormat (did nothing)
EVENT: glGetError(context = 1)

ねっとりとステップ実行したところ、

https://github.com/google/angle/blob/251ba5cb119ff2fed0e861cbc9b096c45004c1fa/src/libANGLE/validationEGL.cpp#L4211-L4231

ここの val->eglThread->getCurrentDrawSurface() != eglSurface チェックに失敗していることがわかった。つまり wglMakeCurrent の実装があやしいな。。

okuokuokuoku

黒画になった

どうもEGL contextに紐付いているdisplayが変えられないようなので、 eglGetDisplay(EGL_DEFAULT_DISPLAY); を代わりに使うようにしたら黒画になった。

これで再度APIトレースを取って問題分析だな。。

okuokuokuoku

APIトレースにはフレームはある

C:\VulkanSDK\1.3.236.0\Bin>gfxrecon-info.exe f:\capture_20221218T170917.gfxr
File info:
        Compression format: LZ4
        Total frames: 311

ただリプレイできるデバイスが無い。。一応 --remove-unsupported オプションはあるものの、やっぱりクラッシュしてしまう。

C:\VulkanSDK\1.3.236.0\Bin>gfxrecon-replay.exe --pause-frame 300 --remove-unsupported -m remap f:\capture_20221218T170917.gfxr
[gfxrecon] WARNING - The replay device differs from the original capture device; replay may fail due to device incompatibilities:
[gfxrecon] WARNING -   Capture device info:     [vendorID = 0x1ae0, deviceId = 0xc0de, deviceName = SwiftShader Device (LLVM 10.0.0)]
[gfxrecon] WARNING -   Replay device info:      [vendorID = 0x8086, deviceId = 0x1912, deviceName = Intel(R) HD Graphics 530]
[gfxrecon] WARNING - Extension VK_EXT_load_store_op_none, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Extension VK_KHR_sampler_ycbcr_conversion, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Extension VK_EXT_blend_operation_advanced, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Extension VK_KHR_pipeline_library, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Extension VK_EXT_graphics_pipeline_library, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Extension VK_EXT_pipeline_robustness, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Extension VK_EXT_rasterization_order_attachment_access, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Feature rasterizationOrderColorAttachmentAccess, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Feature rasterizationOrderDepthAttachmentAccess, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Feature rasterizationOrderStencilAttachmentAccess, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Feature samplerYcbcrConversion, which is not supported by the replay device, will not be enabled
[gfxrecon] WARNING - Feature rectangularLines, which is not supported by the replay device, will not be enabled
[gfxrecon] INFO - Replay adjusted the vkGetPhysicalDeviceSurfaceFormatsKHR array count: capture count = 2, replay count = 4
[gfxrecon] WARNING - API call vkAcquireNextImageKHR returned value VK_SUBOPTIMAL_KHR that does not match return value from capture file: VK_SUCCESS.
[gfxrecon] WARNING - Failed to map handle for object id 110

とりあえず gfxrecon-replay を手元でもビルドしてみて確認してみたところ、SwiftShader内部でクラッシュしていた。

https://github.com/google/swiftshader/blob/aac11f84562f3e8c0581d9efda0ca308e8aa8334/src/Device/Context.cpp#L1271-L1280

一旦Vulkan Profilesで機能制限を掛けたうえで実行できないか検討した方が良いかな。。

https://vulkan.lunarg.com/doc/view/1.3.204.1/windows/profiles_layer.html

https://vulkan.lunarg.com/doc/sdk/1.3.236.0/windows/profiles_definitions.html

okuokuokuoku

リプレイできるキャプチャが取れた

VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_gfxreconstruct;VK_LAYER_KHRONOS_profiles
GFXRECON_CAPTURE_FILE=f:\capture.gfxr
GFXRECON_CAPTURE_FILE_FLUSH=true
GFXRECON_LOG_LEVEL=debug
GFXRECON_LOG_OUTPUT_TO_OS_DEBUG_STRING=true
VK_LAYER_PATH=C:\VulkanSDK\1.3.236.0\Bin32;F:\vulkanprofiles32\bin\Debug
VK_KHRONOS_PROFILES_EXCLUDE_DEVICE_EXTENSIONS=VK_EXT_load_store_op_none;VK_KHR_sampler_ycbcr_conversion;VK_EXT_blend_operation_advanced;VK_KHR_pipeline_library;VK_EXT_graphics_pipeline_library;VK_EXT_pipeline_robustness;VK_EXT_rasterization_order_attachment_access;VK_EXT_provoking_vertex;VK_EXT_external_memory_host
VK_KHRONOS_PROFILES_DEBUG_ACTIONS=DEBUG_ACTION_OUTPUT_BIT
VK_ICD_FILENAMES=F:\swiftshader32\Windows\vk_swiftshader_icd.json
WINEDEBUG=+all

ここまでしないといけないの辛いすぎるな。。 VK_EXT_provoking_vertex VK_EXT_external_memory_host の2つはRenderDocのキャプチャを入れると無効化されるようなので最初から無効化しておく必要があった。(gfxreconstructはリプレイできるがRenderDocがリプレイできない)

Wined3dは起動時にレンダリングのテストをするので、それが1枚目のWindowとしてリプレイされてしまう。実際のゲームは2枚目のWindowになるので、接続したあとCycleボタンを押してactive windowを切り替える必要がある。

リプレイは直接RenderDoc上で64bitのSwiftShaderを使ったら普通にクラッシュしたので、32bit版のリプレイサーバを使ってみる。

C:\Program Files\RenderDoc\x86>renderdoccmd remoteserver
Spawning a replay host listening on *...

GUIのRenderDocは64bit版しか提供されないものの、 renderdoccmd は32bit版がある。

JIT session error: Symbols not found: [ __chkstk ]

... Debug版のCRTはダメなのか。。全部をリリース版にしてキャプチャを取りなおす。

okuokuokuoku

キャプチャの分析

タイトル画面の部分をキャプチャしているのでQuadのドローばかりなのは多分正しい。UIテクスチャも正常にアップロードされているようなので、それらの描画を追うことで間違ったパラメーターを発見できそう。

... こういう描画されるべきものが見えない時はPixel historyを使うのが定石な気がするけど、今回はどうもRenderDocがクラッシュしてしまうようなのでPipeline stateを地道に追うことにする。

まず、Mesh viewerで頂点属性、いわゆる gl_Position の場所を確認する。今回の場合は _input7 が入力、 _sig63._child0 が出力っぽいので、それぞれカラムを右クリックしてPositionに設定する。設定することでワイヤフレームが描画されるようになる。

... が、出力がオールゼロで明かにおかしい。これはgl4esのfixed pipelineエミュレータのバグっぽい気がする。。