eliemichel / LearnWebGPU-Code

The accompanying code of the Learn WebGPU C++ programming guide
https://eliemichel.github.io/LearnWebGPU
MIT License
114 stars 30 forks source link

step017 onQueueWorkDone callback don't trigger, utill i close window onQueueWorkDone called #34

Open guijiyang opened 9 months ago

guijiyang commented 9 months ago

I download code and build with dawn.

but onQueueWorkDone don't called, as I close the window down, onQueueWorkDone called by release adapter, and got wrong status: "Queued work finished with status: 3", is there anything wrong? this is the output

Requesting adapter...
Got adapter: 00000265796CB290
Requesting device...
Got device: 00000265796CE550
Device features:
Device limits:
 - maxTextureDimension1D: 8192
 - maxTextureDimension2D: 8192
 - maxTextureDimension3D: 2048
 - maxTextureArrayLayers: 256
 - maxBindGroups: 4
 - maxDynamicUniformBuffersPerPipelineLayout: 8
 - maxDynamicStorageBuffersPerPipelineLayout: 4
 - maxSampledTexturesPerShaderStage: 16        
 - maxSamplersPerShaderStage: 16
 - maxStorageBuffersPerShaderStage: 8
 - maxStorageTexturesPerShaderStage: 4
 - maxUniformBuffersPerShaderStage: 12
 - maxUniformBufferBindingSize: 65536
 - maxStorageBufferBindingSize: 134217728      
 - minUniformBufferOffsetAlignment: 256        
 - minStorageBufferOffsetAlignment: 256        
 - maxVertexBuffers: 8
 - maxVertexAttributes: 16
 - maxVertexBufferArrayStride: 2048
 - maxInterStageShaderComponents: 60
 - maxComputeWorkgroupStorageSize: 16384       
 - maxComputeInvocationsPerWorkgroup: 256      
 - maxComputeWorkgroupSizeX: 256
 - maxComputeWorkgroupSizeY: 256
 - maxComputeWorkgroupSizeZ: 64
 - maxComputeWorkgroupsPerDimension: 65535     
Submitting command...
Queued work finished with status: 3
Warning: No Dawn device lost callback was set. This is probably not intended. If you really want to ignore device lost and suppress this message, set the callback to null.
williamhCode commented 9 months ago

I think wgpuInstanceProcessEvents(instance) has to be called to fire the callback. So could put that in the main loop, or u could block with a loop right after with a bool condition.

guijiyang commented 9 months ago

I think wgpuInstanceProcessEvents(instance) has to be called to fire the callback. So could put that in the main loop, or u could block with a loop right after with a bool condition.

yeah, wgpuInstanceProcessEvents can trigger callbackmanager flush function to call query callback. but it doesn't make sense, if put this behind wgpuQueueOnSubmittedWorkDone(queue, onQueueWorkDone, nullptr /* pUserData */);, it will trigger before wgpuQueueSubmit(queue, 1, &command) called.

Device features:
Device limits:
 - maxTextureDimension1D: 8192
 - maxTextureDimension2D: 8192
 - maxTextureDimension3D: 2048
 - maxTextureArrayLayers: 256
 - maxBindGroups: 4
 - maxDynamicUniformBuffersPerPipelineLayout: 8
 - maxDynamicStorageBuffersPerPipelineLayout: 4
 - maxSampledTexturesPerShaderStage: 16
 - maxSamplersPerShaderStage: 16
 - maxStorageBuffersPerShaderStage: 8
 - maxStorageTexturesPerShaderStage: 4
 - maxUniformBuffersPerShaderStage: 12
 - maxUniformBufferBindingSize: 65536
 - maxStorageBufferBindingSize: 134217728
 - minUniformBufferOffsetAlignment: 256
 - minStorageBufferOffsetAlignment: 256
 - maxVertexBuffers: 8
 - maxVertexAttributes: 16
 - maxVertexBufferArrayStride: 2048
 - maxInterStageShaderComponents: 60
 - maxComputeWorkgroupStorageSize: 16384
 - maxComputeInvocationsPerWorkgroup: 256
 - maxComputeWorkgroupSizeX: 256
 - maxComputeWorkgroupSizeY: 256
 - maxComputeWorkgroupSizeZ: 64
 - maxComputeWorkgroupsPerDimension: 65535
Queued work finished with status: 0
Submitting command...
Warning: No Dawn device lost callback was set. This is probably not intended. If you really want to ignore device lost and suppress this message, set the callback to null.
williamhCode commented 9 months ago

yeah my bad, i mean that u could put a blocking loop after the submit, so it can wait for the submit command to finish. or u could just put it in the main loop since u would probably need to call wgpuQueueSubmit in the main loop when u start actually rendering stuff. Something like this:

queue.Submit(1, &commandBuffer);
bool workDone = false;
queue.OnSubmittedWorkDone(
  [](WGPUQueueWorkDoneStatus status, void* workDone) { 
    std::cout << "work done" << std::endl;
    *(bool*)workDone = true;
  },
  &workDone
);
using namespace std::chrono_literals;
while (!workDone) {
  instance.ProcessEvents();
  std::this_thread::sleep_for(1ms);
}
guijiyang commented 9 months ago

yeah my bad, i mean that u could put a blocking loop after the submit, so it can wait for the submit command to finish. or u could just put it in the main loop since u would probably need to call wgpuQueueSubmit in the main loop when u start actually rendering stuff. Something like this:

queue.Submit(1, &commandBuffer);
bool workDone = false;
queue.OnSubmittedWorkDone(
  [](WGPUQueueWorkDoneStatus status, void* workDone) { 
    std::cout << "work done" << std::endl;
    *(bool*)workDone = true;
  },
  &workDone
);
using namespace std::chrono_literals;
while (!workDone) {
  instance.ProcessEvents();
  std::this_thread::sleep_for(1ms);
}

this is my code:

// Add a callback to monitor the moment queued work winished
  bool queuedWorkFinished = false;
  auto onQueueWorkDone = [](WGPUQueueWorkDoneStatus status,
                            void * pQueuedWorkFinished) {
    std::cout << "Queued work finished with status: " << status << std::endl;
    *static_cast<bool *>(pQueuedWorkFinished) = true;
  };
  wgpuQueueOnSubmittedWorkDone(queue, onQueueWorkDone, static_cast<void*>(&queuedWorkFinished));
  while (!queuedWorkFinished) {
    dawn::native::InstanceProcessEvents(instance);
    std::this_thread::sleep_for(std::chrono::milliseconds(16));
  }
  // dawn::native::InstanceProcessEvents(instance);

  // We create a command encoder to be able to create command buffers
  WGPUCommandEncoderDescriptor encoderDesc = {};
  encoderDesc.nextInChain = nullptr;
  encoderDesc.label = "My command encoder";
  WGPUCommandEncoder encoder =
      wgpuDeviceCreateCommandEncoder(device, &encoderDesc);

  // Encode some mock commands
  wgpuCommandEncoderInsertDebugMarker(encoder, "Do one thing");
  wgpuCommandEncoderInsertDebugMarker(encoder, "Do another thing");

  // Encode commands into a command buffer
  WGPUCommandBufferDescriptor cmdBufferDescriptor = {};
  cmdBufferDescriptor.nextInChain = nullptr;
  cmdBufferDescriptor.label = "Command buffer";
  WGPUCommandBuffer command =
      wgpuCommandEncoderFinish(encoder, &cmdBufferDescriptor);
  wgpuCommandEncoderRelease(encoder); // release encoder after it's finished

  // wgpuDeviceTick(device);
  // Finally submit the command queue
  std::cout << "Submitting command..." << std::endl;
  wgpuQueueSubmit(queue, 1, &command);

output:

Queued work finished with status: 0
Submitting command...
Swap chain : 000001D8CA9B8050

as I put this function before wgpuQueueSubmit, it will get above result, because callback called immediately by callbackmanager flush function, which is called by instance processevents . I think this callback maybe not triggered by main thread, it must be other thread to fire it after wgpuQueueSubmit finish, as a async callback, is that right?

williamhCode commented 9 months ago

Yes so you have to put the blocking loop after the queuesubmit function. The point of the blocking loop is to busy wait (block the thread and wait) for the callback.

guijiyang commented 9 months ago

Yes so you have to put the blocking loop after the queuesubmit function. The point of the blocking loop is to busy wait (block the thread and wait) for the callback.

ok, think you for you reply, now I add wgpuDeviceTick in the end of loop, which is work for queue workdone callback

williamhCode commented 9 months ago

no problem, I think u can keep the issue open though because this should probably be mentioned in the tutorial