gfx-rs / wgpu-native

Native WebGPU implementation based on wgpu-core
Apache License 2.0
843 stars 96 forks source link

How to use callback function while calling instance.requestAdapter #385

Closed xrui94 closed 3 months ago

xrui94 commented 3 months ago

I'm using wgpu-native to write a wasm application by emscripten, but an problem prevented me from further working. This problem is:

wgpu::RequestAdapterCallback adapterCallback = [](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, char const* message) {
        if (status == wgpu::RequestAdapterStatus::Success) {
            // 设备请求成功,执行相应操作
            std::cout << "adapter request succeeded!" << std::endl;
            // 这里可以对设备进行操作,比如设置错误回调、执行其他操作等
        } else {
            // 设备请求失败,执行相应操作
            std::cout << "adapter request failed!" << std::endl;
            // 这里可以处理设备请求失败的情况
        }
    };

    wgpu::RequestAdapterOptions descriptor{};
    std::cout << "@@@@@@@@@@@@@@4" << std::endl;
    std::unique_ptr<wgpu::RequestAdapterCallback> callbackPtr = m_Instance.requestAdapter(descriptor, std::move(adapterCallback));

it's not woring. In my terminal, the function has returned after program printed "@@@@@@@@@@@@@@4", and give me an error, just like the image below: image

almarklein commented 3 months ago

I'm not very knowledgeable in C/C++, but it looks like it does not recognize your function. See the example code for reference:

https://github.com/gfx-rs/wgpu-native/blob/33133da4ec5a0174cb21539ef2d3346f75200411/examples/triangle/main.c#L28-L38

https://github.com/gfx-rs/wgpu-native/blob/33133da4ec5a0174cb21539ef2d3346f75200411/examples/triangle/main.c#L184-L188

xrui94 commented 3 months ago

I'm not very knowledgeable in C/C++, but it looks like it does not recognize your function. See the example code for reference:

https://github.com/gfx-rs/wgpu-native/blob/33133da4ec5a0174cb21539ef2d3346f75200411/examples/triangle/main.c#L28-L38

https://github.com/gfx-rs/wgpu-native/blob/33133da4ec5a0174cb21539ef2d3346f75200411/examples/triangle/main.c#L184-L188

Thanks for your reply. However, there is not an parameter called "void userdata" in "requestAdapter" function stored in webgpu/webgpu.hpp file, and it expects only 2 parameters, not 3 parameters. At the same time, there is not an parameter called "void userdata" in "requestDevice" function. Is it a bug of webgpu.hpp? image

image

almarklein commented 3 months ago

However, there is not an parameter called "void *userdata" in "requestAdapter" function stored in webgpu/webgpu.hpp file

What webgpu.hpp are you using? In webgpu.h its there: https://github.com/webgpu-native/webgpu-headers/blob/aef5e428a1fdab2ea770581ae7c95d8779984e0a/webgpu.h#L730

xrui94 commented 3 months ago

However, there is not an parameter called "void *userdata" in "requestAdapter" function stored in webgpu/webgpu.hpp file

What webgpu.hpp are you using? In webgpu.h its there: https://github.com/webgpu-native/webgpu-headers/blob/aef5e428a1fdab2ea770581ae7c95d8779984e0a/webgpu.h#L730

It coms from this: https://github.com/gfx-rs/wgpu-native

image

“webgpu.hpp” is automatically generated by this project: https://github.com/eliemichel/WebGPU-Cpp/tree/main/wgpu-native

I did it following this tutorial: https://eliemichel.github.io/LearnWebGPU/basic-3d-rendering/hello-triangle.html

almarklein commented 3 months ago

Does it run when compiled to native code (i.e. not wasm)? Perhaps the callback does not survive emscriptem. I'm just guessing here tho.

xrui94 commented 3 months ago

Does it run when compiled to native code (i.e. not wasm)? Perhaps the callback does not survive emscriptem. I'm just guessing here tho.

Your guessing is right !!!

The following same code can be successfully compiled and run in Windows desktop, in a browser, it can be successfully compiled into a wasm program through emscripten, but an error occurs when running it. This error is the one I first mentioned above: "Uncaught (in promise) RuntimeError: null function or function signature mismatch..."

    std::cout << "@@@@@@@@@@@@@@2" << std::endl;
    auto requestAdapterCallback = [this, callback](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, char const *message)
    {
        std::cout << "@@@@@@@@@@@@@@3" << std::endl;
        if (status != wgpu::RequestAdapterStatus::Success)
        {
            std::cerr << "Could not get WebGPU adapter: " << message << std::endl;
            return;
        }

        #ifdef WEBGPU_BACKEND_WGPU
            m_SwapChainFormat = m_Surface.getPreferredFormat(adapter);
        #else
            m_SwapChainFormat = wgpu::TextureFormat::BGRA8Unorm;
        #endif

        auto requestDeviceCallback = [callback](wgpu::RequestDeviceStatus status, wgpu::Device device, char const *message)
        {
            if (status != wgpu::RequestDeviceStatus::Success)
            {
                std::cerr << "Could not get WebGPU device: " << message << std::endl;
                return;
            }

            device.setUncapturedErrorCallback(
                [](WGPUErrorType type, const char *message)
                {
                    std::cout << "Error: " << type << " , message: " << message;
                }
            );
            // m_Device = device;

            std::cout << "@@@@@@@@@@@@@@4" << std::endl;
            // AdapterAndDeviceCallback callback = *reinterpret_cast<AdapterAndDeviceCallback*>(pUserData);
            callback(device);
        };

        //
        wgpu::DeviceDescriptor deviceDesc{};
        deviceDesc.label = "Device for Web";
        deviceDesc.requiredFeaturesCount = 0;
        // deviceDesc.requiredLimits = &requiredLimits;
        deviceDesc.defaultQueue.label = "The default queue";
        adapter.requestDevice(deviceDesc, requestDeviceCallback);
    };

    wgpu::RequestAdapterOptions adapterOpts{};
    adapterOpts.compatibleSurface = m_Surface;
    m_Instance.requestAdapter(adapterOpts, requestAdapterCallback);  // Not Working

However, I found that in the implementation logic of the requestAdapter and requestDevice functions (specifically referring to functions that only require one parameter) in webgpu.hpp, when the code needs to be compiled into a "wasm" program through emscripten, it already includes asynchronous processing (by using the "_EMSCRIPTEN__" macro). Therefore, I can directly reuse the desktop code without the need for callback functions to handle the logic of obtaining adapter and device asynchronously. However, when compiling the wasm program using the "emcc" toolchain, need to add the "- SASYNCIFY" parameter!

image

Finally,the following code can be successfully compiled and run in both Windows desktop programs and browser WASM (with "- SASYNCIFY" parameter )programs:

    std::cout << "Requesting adapter..." << std::endl;
    wgpu::RequestAdapterOptions adapterOpts{};
    adapterOpts.compatibleSurface = m_Surface;
    wgpu::Adapter adapter = m_Instance.requestAdapter(adapterOpts);
    std::cout << "Got adapter: " << adapter << std::endl;

    //
    #ifdef WEBGPU_BACKEND_WGPU
        m_SwapChainFormat = m_Surface.getPreferredFormat(adapter);
    #else
        m_SwapChainFormat = wgpu::TextureFormat::BGRA8Unorm;
    #endif

    //
    std::cout << "Requesting device..." << std::endl;

    wgpu::DeviceDescriptor deviceDesc;
    deviceDesc.label = "My Device";
    deviceDesc.requiredFeaturesCount = 0;
    // deviceDesc.requiredLimits = &requiredLimits;
    deviceDesc.defaultQueue.label = "The default queue";
    m_Device = adapter.requestDevice(deviceDesc);
    std::cout << "Got device: " << m_Device << std::endl;

    // Add an error callback for more debug info
    m_ErrorCallbackHandle = m_Device.setUncapturedErrorCallback([](wgpu::ErrorType type, char const* message) {
        std::cout << "Device error: type " << type;
        if (message) std::cout << " (message: " << message << ")";
        std::cout << std::endl;
    });

    //
    m_Queue = m_Device.getQueue();

    //
    adapter.release();

    // swap chain
    CreateSwapChain(m_Width, m_Height);

    m_IsInitialized = true;