beaufortfrancois / webgpu-cross-platform-app

WebGPU cross-platform app with CMake/Emscripten
https://developer.chrome.com/docs/web-platform/webgpu/build-app
80 stars 5 forks source link

TextureFormat::BGRA8Unorm is not supported by the adapter #9

Open amirsojoodi opened 2 weeks ago

amirsojoodi commented 2 weeks ago

Running the example, I get this error:

Error: 1 - message: Present mode (TextureFormat::BGRA8Unorm) is not supported by the adapter ([Adapter "NVIDIA GeForce GTX 1060 3GB"]) for this surface.
 - While calling [Surface].Configure().

I tried to add DeviceDescriptor to request the device with the required feature. But still no luck:

void GetDevice(void (*callback)(wgpu::Device)) {
  wgpu::DeviceDescriptor deviceDescriptor{};
  wgpu::FeatureName requiredFeatures[] = {wgpu::FeatureName::BGRA8UnormStorage};
  deviceDescriptor.requiredFeatures = requiredFeatures;
  deviceDescriptor.requiredFeatureCount = 1;

  adapter.RequestDevice(
      &deviceDescriptor,
      // TODO(https://bugs.chromium.org/p/dawn/issues/detail?id=1892): Use
      // wgpu::RequestDeviceStatus and wgpu::Device.
      [](WGPURequestDeviceStatus status, WGPUDevice cDevice,
          const char* message, void* userdata) {
        if (message) {
          printf("RequestDevice: %s\n", message);
        }
        wgpu::Device device = wgpu::Device::Acquire(cDevice);
        device.SetUncapturedErrorCallback(
            [](WGPUErrorType type, const char* message, void* userdata) {
              std::cout << "Error: " << type << " - message: " << message;
            },
            nullptr);
        reinterpret_cast<void (*)(wgpu::Device)>(userdata)(device);
  }, reinterpret_cast<void*>(callback));
}

The only way I could make this code work is to explicitly create the SwapChain object instead of surface configuration. I couldn't figure out what causes this issue, though. What's more interesting is that with the SwapChain method, I didn't even need to pass the DeviceDescriptor to RequestDevice.

I could have created a pull request out of the following code, but I was not sure whether surface configuration meant be done for this example or not.

#include <GLFW/glfw3.h>
#include <iostream>
#include <webgpu/webgpu_cpp.h>
#include <iostream>
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#else
#include <webgpu/webgpu_glfw.h>
#endif

wgpu::Instance instance;
wgpu::Adapter adapter;
wgpu::Device device;
wgpu::RenderPipeline pipeline;
wgpu::SwapChain swapChain;

wgpu::Surface surface;
wgpu::TextureFormat format = wgpu::TextureFormat::BGRA8Unorm;
const uint32_t kWidth = 512;
const uint32_t kHeight = 512;

void ConfigureSwapChain() {
  wgpu::SwapChainDescriptor swapChainDesc{};
  swapChainDesc.usage = wgpu::TextureUsage::RenderAttachment;
  swapChainDesc.format = format;
  swapChainDesc.width = kWidth;
  swapChainDesc.height = kHeight;
  swapChainDesc.presentMode = wgpu::PresentMode::Fifo;

  swapChain = device.CreateSwapChain(surface, &swapChainDesc);
}

void GetAdapter(void (*callback)(wgpu::Adapter)) {
  instance.RequestAdapter(
      nullptr,
      // TODO(https://bugs.chromium.org/p/dawn/issues/detail?id=1892): Use
      // wgpu::RequestAdapterStatus and wgpu::Adapter.
      [](WGPURequestAdapterStatus status, WGPUAdapter cAdapter,
         const char* message, void* userdata) {
        if (message) {
          printf("RequestAdapter: %s\n", message);
        }
        if (status != WGPURequestAdapterStatus_Success) {
          exit(0);
        }
        wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
        reinterpret_cast<void (*)(wgpu::Adapter)>(userdata)(adapter);
  }, reinterpret_cast<void*>(callback));
}

void GetDevice(void (*callback)(wgpu::Device)) {

  adapter.RequestDevice(
      nullptr,
      // TODO(https://bugs.chromium.org/p/dawn/issues/detail?id=1892): Use
      // wgpu::RequestDeviceStatus and wgpu::Device.
      [](WGPURequestDeviceStatus status, WGPUDevice cDevice,
          const char* message, void* userdata) {
        if (message) {
          printf("RequestDevice: %s\n", message);
        }
        wgpu::Device device = wgpu::Device::Acquire(cDevice);
        device.SetUncapturedErrorCallback(
            [](WGPUErrorType type, const char* message, void* userdata) {
              std::cout << "Error: " << type << " - message: " << message;
            },
            nullptr);
        reinterpret_cast<void (*)(wgpu::Device)>(userdata)(device);
  }, reinterpret_cast<void*>(callback));
}

const char shaderCode[] = R"(
    @vertex fn vertexMain(@builtin(vertex_index) i : u32) ->
      @builtin(position) vec4f {
        const pos = array(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1));
        return vec4f(pos[i], 0, 1);
    }
    @fragment fn fragmentMain() -> @location(0) vec4f {
        return vec4f(1, 0, 0, 1);
    }
)";

void CreateRenderPipeline() {
  wgpu::ShaderModuleWGSLDescriptor wgslDesc{};
  wgslDesc.code = shaderCode;

  wgpu::ShaderModuleDescriptor shaderModuleDescriptor{
      .nextInChain = &wgslDesc};
  wgpu::ShaderModule shaderModule =
      device.CreateShaderModule(&shaderModuleDescriptor);

  wgpu::ColorTargetState colorTargetState{.format = format};

  wgpu::FragmentState fragmentState{.module = shaderModule,
                                    .targetCount = 1,
                                    .targets = &colorTargetState};

  wgpu::RenderPipelineDescriptor descriptor{
      .vertex = {.module = shaderModule},
      .fragment = &fragmentState};
  pipeline = device.CreateRenderPipeline(&descriptor);
}

void Render() {
  wgpu::TextureView backbuffer = swapChain.GetCurrentTextureView();

  wgpu::RenderPassColorAttachment attachment{
      .view = backbuffer,
      .loadOp = wgpu::LoadOp::Clear,
      .storeOp = wgpu::StoreOp::Store,
      .clearValue = {0.3f, 0.3f, 0.3f, 1.0f}};

  wgpu::RenderPassDescriptor renderpass{.colorAttachmentCount = 1,
                                        .colorAttachments = &attachment};

  wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
  wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderpass);
  pass.SetPipeline(pipeline);
  pass.Draw(3);
  pass.End();
  wgpu::CommandBuffer commands = encoder.Finish();
  device.GetQueue().Submit(1, &commands);
  swapChain.Present();
}

void InitGraphics() {
  ConfigureSwapChain();
  CreateRenderPipeline();
}

void Start() {
  if (!glfwInit()) {
    return;
  }

  glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
  GLFWwindow* window =
      glfwCreateWindow(kWidth, kHeight, "WebGPU window", nullptr, nullptr);

#if defined(__EMSCRIPTEN__)
  wgpu::SurfaceDescriptorFromCanvasHTMLSelector canvasDesc{};
  canvasDesc.selector = "#canvas";

  wgpu::SurfaceDescriptor surfaceDesc{.nextInChain = &canvasDesc};
  surface = instance.CreateSurface(&surfaceDesc);
#else
  surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
#endif

  InitGraphics();

#if defined(__EMSCRIPTEN__)
  emscripten_set_main_loop(Render, 0, false);
#else
  while (!glfwWindowShouldClose(window)) {
    glfwPollEvents();
    Render();
    instance.ProcessEvents();
  }
#endif
}

int main() {
  instance = wgpu::CreateInstance();
  GetAdapter([](wgpu::Adapter a) {
    adapter = a;
    GetDevice([](wgpu::Device d) {
      device = d;
      Start();
    });
  });
}
kainino0x commented 2 weeks ago

This is very strange, bgra8unorm should be the preferred format on everything except Android. (bgra8unorm-storage isn't relevant here, that's only needed if you use STORAGE usage, aka SSBO.) It sounds like a Dawn bug.

Could you please provide the chrome://gpu report from a Chromium browser on your system?

amirsojoodi commented 2 weeks ago

Sure. Here it is: about-gpu-2024-06-21T19-12-30-905Z.txt

kainino0x commented 2 weeks ago

Wait... I reread the error message and it doesn't make any sense. Present mode (TextureFormat::BGRA8Unorm) is not supported by the adapter BGRA8Unorm is not a present mode, it's a format. The error is erroneously printing the format when it should be printing the present mode. Filed https://crbug.com/348590029 about that.

Anyway, I still don't know what the problem is because you are using Fifo and Fifo is supposed to be always supported. Maybe something weird is happening and Dawn is not actually receiving Fifo but some other present mode. You could try fixing the error messages in your Dawn checkout and see what it prints.

amirsojoodi commented 2 weeks ago

Thanks. Upvoted.

Yeah, I put Fifo after I got the error, and it doesn't make any changes.

I saw your filed bug, and I commented those three lines out of curiosity, and I rebuilt dawn. Of course, the original source worked like a charm!

    // DAWN_INVALID_IF(presentModeIt == capabilities.presentModes.end(),
    //                 "Present mode (%s) is not supported by the adapter (%s) for this surface.",
    //                 config->format, config->device->GetAdapter());

Maybe, capabilities doesn't get populated correctly, or maybe the whole error checking is erroneous? 🤔

kainino0x commented 2 weeks ago

Maybe, capabilities doesn't get populated correctly

I think this is the most likely. Not sure why though.

kainino0x commented 2 weeks ago

Filed https://crbug.com/348699454 for that too.

Kangz commented 1 week ago

Re: PresentMode::Fifo not being present, that mode is required to be supported in Vulkan and Dawn should translate it correctly. Can you provide the output of vulkaninfo?

amirsojoodi commented 1 week ago

@Kangz Sure: vulkaninfo.txt

Kangz commented 1 week ago

Thank you!

GPU id : 0 (NVIDIA GeForce GTX 1060 3GB):
        Surface types: count = 2
                VK_KHR_xcb_surface
                VK_KHR_xlib_surface
        Formats: count = 2
                SurfaceFormat[0]:
                        format = FORMAT_B8G8R8A8_UNORM
                        colorSpace = COLOR_SPACE_SRGB_NONLINEAR_KHR
                SurfaceFormat[1]:
                        format = FORMAT_B8G8R8A8_SRGB
                        colorSpace = COLOR_SPACE_SRGB_NONLINEAR_KHR
        Present Modes: count = 3
                PRESENT_MODE_FIFO_KHR
                PRESENT_MODE_FIFO_RELAXED_KHR
                PRESENT_MODE_IMMEDIATE_KHR
        VkSurfaceCapabilitiesKHR:
        -------------------------
                minImageCount = 2
                maxImageCount = 8
                currentExtent:
                        width  = 256
                        height = 256
                minImageExtent:
                        width  = 256
                        height = 256
                maxImageExtent:
                        width  = 256
                        height = 256
                maxImageArrayLayers = 1
                supportedTransforms: count = 1
                        SURFACE_TRANSFORM_IDENTITY_BIT_KHR
                currentTransform = SURFACE_TRANSFORM_IDENTITY_BIT_KHR
                supportedCompositeAlpha: count = 1
                        COMPOSITE_ALPHA_OPAQUE_BIT_KHR
                supportedUsageFlags: count = 6
                        IMAGE_USAGE_TRANSFER_SRC_BIT
                        IMAGE_USAGE_TRANSFER_DST_BIT
                        IMAGE_USAGE_SAMPLED_BIT
                        IMAGE_USAGE_STORAGE_BIT
                        IMAGE_USAGE_COLOR_ATTACHMENT_BIT
                        IMAGE_USAGE_INPUT_ATTACHMENT_BIT
        VkSharedPresentSurfaceCapabilitiesKHR:
        --------------------------------------
                sharedPresentSupportedUsageFlags: count = 6
                        IMAGE_USAGE_TRANSFER_SRC_BIT
                        IMAGE_USAGE_TRANSFER_DST_BIT
                        IMAGE_USAGE_SAMPLED_BIT
                        IMAGE_USAGE_STORAGE_BIT
                        IMAGE_USAGE_COLOR_ATTACHMENT_BIT
                        IMAGE_USAGE_INPUT_ATTACHMENT_BIT

        VkSurfaceProtectedCapabilitiesKHR:
        ----------------------------------
                supportsProtected = false

and

GPU id : 1 (llvmpipe (LLVM 12.0.0, 256 bits)):
        Surface types: count = 2
                VK_KHR_xcb_surface
                VK_KHR_xlib_surface
        Formats: count = 2
                SurfaceFormat[0]:
                        format = FORMAT_B8G8R8A8_SRGB
                        colorSpace = COLOR_SPACE_SRGB_NONLINEAR_KHR
                SurfaceFormat[1]:
                        format = FORMAT_B8G8R8A8_UNORM
                        colorSpace = COLOR_SPACE_SRGB_NONLINEAR_KHR
        Present Modes: count = 4
                PRESENT_MODE_IMMEDIATE_KHR
                PRESENT_MODE_MAILBOX_KHR
                PRESENT_MODE_FIFO_KHR
                PRESENT_MODE_FIFO_RELAXED_KHR
        VkSurfaceCapabilitiesKHR:
        -------------------------
                minImageCount = 3
                maxImageCount = 0
                currentExtent:
                        width  = 256
                        height = 256
                minImageExtent:
                        width  = 256
                        height = 256
                maxImageExtent:
                        width  = 256
                        height = 256
                maxImageArrayLayers = 1
                supportedTransforms: count = 1
                        SURFACE_TRANSFORM_IDENTITY_BIT_KHR
                currentTransform = SURFACE_TRANSFORM_IDENTITY_BIT_KHR
                supportedCompositeAlpha: count = 2
                        COMPOSITE_ALPHA_OPAQUE_BIT_KHR
                        COMPOSITE_ALPHA_INHERIT_BIT_KHR
                supportedUsageFlags: count = 5
                        IMAGE_USAGE_TRANSFER_SRC_BIT
                        IMAGE_USAGE_TRANSFER_DST_BIT
                        IMAGE_USAGE_SAMPLED_BIT
                        IMAGE_USAGE_STORAGE_BIT
                        IMAGE_USAGE_COLOR_ATTACHMENT_BIT
        VkSurfaceProtectedCapabilitiesKHR:
        ----------------------------------
                supportsProtected = false

Fifo is indeed present. The code in Dawn should translate it correctly to wgpu::PresentMode::Fifo so I'm really not sure why the validation triggers.

amirsojoodi commented 1 week ago

@Kangz Thanks for the double check.

I checked out dawn to the main branch and tried without the fix:

    // DAWN_INVALID_IF(presentModeIt == capabilities.presentModes.end(),
    //                 "Present mode (%s) is not supported by the adapter (%s) for this surface.",
    //                 config->format, config->device->GetAdapter());

And now there is no issue anymore. 🤔 Maybe the dawn branch should be updated in the .gitmodule then?

beaufortfrancois commented 6 days ago

I use chromium/6478 branch in .gitmodule which is Chrome 126. I now wonder which dawn change actually fixed the error you're seeing.