Skip to content

Commit

Permalink
Fix some assertions when using a canvas without a layer with webgpu
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=270705
rdar://124213314

Reviewed by Mike Wyrzykowski.

The test in this PR hit several issues:
1. PresentationContextIOSurface::present asserted, which I changed to an early return.
2. I handle errors when creating objects by returning nullptr instead of an object that
   has no corresponding object in the GPU process, and propogate those errors to JS.
3. I added a WeakPtr check in the lambda in GPUCanvasContextCocoa::prepareForDisplay
4. In order to keep Windows building after this change, I needed to change
   FontPlatformData::Attributes to FontPlatformDataAttributes in WebKit.

* LayoutTests/fast/webgpu/use-canvas-without-layer-expected.txt: Added.
* LayoutTests/fast/webgpu/use-canvas-without-layer.html: Added.
* Source/WebGPU/WebGPU/PresentationContextIOSurface.mm:
* Source/WebKit/Platform/IPC/StreamServerConnection.cpp:
(IPC::StreamServerConnection::dispatchStreamMessages):
* Source/WebKit/WebProcess/GPU/graphics/WebGPU/RemoteTextureProxy.cpp:
(WebKit::WebGPU::RemoteTextureProxy::~RemoteTextureProxy):
(WebKit::WebGPU::RemoteTextureProxy::createView):
(WebKit::WebGPU::RemoteTextureProxy::destroy):
(WebKit::WebGPU::RemoteTextureProxy::setLabelInternal):
* Source/WebKit/WebProcess/GPU/graphics/WebGPU/RemoteTextureProxy.h:

Canonical link: https://commits.webkit.org/276005@main
  • Loading branch information
achristensen07 committed Mar 12, 2024
1 parent 09186d9 commit f816a17
Show file tree
Hide file tree
Showing 62 changed files with 469 additions and 390 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This test passes if it does not crash.
23 changes: 23 additions & 0 deletions LayoutTests/fast/webgpu/use-canvas-without-layer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script>
if (window.testRunner) { testRunner.waitUntilDone(); testRunner.dumpAsText() }
onload = async () => {
try {
let adapter12 = await navigator.gpu.requestAdapter({ });
let device10 = await adapter12.requestDevice({ label: '\u0678' } );
let canvas25 = document.createElement('canvas');
canvas25.height = 1419621282;
let gpuCanvasContext17 = canvas25.getContext('webgpu');
gpuCanvasContext17.configure(
{
device: device10,
format: 'rgba16float',
colorSpace: 'srgb',
alphaMode: 'premultiplied',
}
);
let texture95 = gpuCanvasContext17.getCurrentTexture();
} catch (e) { }
if (window.testRunner) { testRunner.notifyDone() }
};
</script>
This test passes if it does not crash.
14 changes: 10 additions & 4 deletions Source/WebCore/Modules/WebGPU/GPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,20 @@ Ref<WGSLLanguageFeatures> GPU::wgslLanguageFeatures() const
return m_wgslLanguageFeatures;
}

Ref<GPUPresentationContext> GPU::createPresentationContext(const GPUPresentationContextDescriptor& presentationContextDescriptor)
RefPtr<GPUPresentationContext> GPU::createPresentationContext(const GPUPresentationContextDescriptor& presentationContextDescriptor)
{
return GPUPresentationContext::create(m_backing->createPresentationContext(presentationContextDescriptor.convertToBacking()));
RefPtr context = m_backing->createPresentationContext(presentationContextDescriptor.convertToBacking());
if (!context)
return nullptr;
return GPUPresentationContext::create(context.releaseNonNull());
}

Ref<GPUCompositorIntegration> GPU::createCompositorIntegration()
RefPtr<GPUCompositorIntegration> GPU::createCompositorIntegration()
{
return GPUCompositorIntegration::create(m_backing->createCompositorIntegration());
RefPtr integration = m_backing->createCompositorIntegration();
if (!integration)
return nullptr;
return GPUCompositorIntegration::create(integration.releaseNonNull());
}

} // namespace WebCore
4 changes: 2 additions & 2 deletions Source/WebCore/Modules/WebGPU/GPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ class GPU : public RefCounted<GPU> {
GPUTextureFormat getPreferredCanvasFormat() const;
Ref<WGSLLanguageFeatures> wgslLanguageFeatures() const;

Ref<GPUPresentationContext> createPresentationContext(const GPUPresentationContextDescriptor&);
RefPtr<GPUPresentationContext> createPresentationContext(const GPUPresentationContextDescriptor&);

Ref<GPUCompositorIntegration> createCompositorIntegration();
RefPtr<GPUCompositorIntegration> createCompositorIntegration();

void paintToCanvas(NativeImage&, const IntSize&, GraphicsContext&);

Expand Down
21 changes: 15 additions & 6 deletions Source/WebCore/Modules/WebGPU/GPUCommandEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,20 @@ void GPUCommandEncoder::setLabel(String&& label)
m_backing->setLabel(WTFMove(label));
}

Ref<GPURenderPassEncoder> GPUCommandEncoder::beginRenderPass(const GPURenderPassDescriptor& renderPassDescriptor)
ExceptionOr<Ref<GPURenderPassEncoder>> GPUCommandEncoder::beginRenderPass(const GPURenderPassDescriptor& renderPassDescriptor)
{
return GPURenderPassEncoder::create(m_backing->beginRenderPass(renderPassDescriptor.convertToBacking()));
RefPtr encoder = m_backing->beginRenderPass(renderPassDescriptor.convertToBacking());
if (!encoder)
return Exception { ExceptionCode::InvalidStateError, "GPUCommandEncoder.beginRenderPass: Unable to begin render pass."_s };
return GPURenderPassEncoder::create(encoder.releaseNonNull());
}

Ref<GPUComputePassEncoder> GPUCommandEncoder::beginComputePass(const std::optional<GPUComputePassDescriptor>& computePassDescriptor)
ExceptionOr<Ref<GPUComputePassEncoder>> GPUCommandEncoder::beginComputePass(const std::optional<GPUComputePassDescriptor>& computePassDescriptor)
{
return GPUComputePassEncoder::create(m_backing->beginComputePass(computePassDescriptor ? std::optional { computePassDescriptor->convertToBacking() } : std::nullopt));
RefPtr computePass = m_backing->beginComputePass(computePassDescriptor ? std::optional { computePassDescriptor->convertToBacking() } : std::nullopt);
if (!computePass)
return Exception { ExceptionCode::InvalidStateError, "GPUCommandEncoder.beginComputePass: Unable to begin compute pass."_s };
return GPUComputePassEncoder::create(computePass.releaseNonNull());
}

void GPUCommandEncoder::copyBufferToBuffer(
Expand Down Expand Up @@ -132,9 +138,12 @@ static WebGPU::CommandBufferDescriptor convertToBacking(const std::optional<GPUC
return commandBufferDescriptor->convertToBacking();
}

Ref<GPUCommandBuffer> GPUCommandEncoder::finish(const std::optional<GPUCommandBufferDescriptor>& commandBufferDescriptor)
ExceptionOr<Ref<GPUCommandBuffer>> GPUCommandEncoder::finish(const std::optional<GPUCommandBufferDescriptor>& commandBufferDescriptor)
{
return GPUCommandBuffer::create(m_backing->finish(convertToBacking(commandBufferDescriptor)));
RefPtr buffer = m_backing->finish(convertToBacking(commandBufferDescriptor));
if (!buffer)
return Exception { ExceptionCode::InvalidStateError, "GPUCommandEncoder.finish: Unable to finish."_s };
return GPUCommandBuffer::create(buffer.releaseNonNull());
}

}
6 changes: 3 additions & 3 deletions Source/WebCore/Modules/WebGPU/GPUCommandEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class GPUCommandEncoder : public RefCounted<GPUCommandEncoder> {
String label() const;
void setLabel(String&&);

Ref<GPURenderPassEncoder> beginRenderPass(const GPURenderPassDescriptor&);
Ref<GPUComputePassEncoder> beginComputePass(const std::optional<GPUComputePassDescriptor>&);
ExceptionOr<Ref<GPURenderPassEncoder>> beginRenderPass(const GPURenderPassDescriptor&);
ExceptionOr<Ref<GPUComputePassEncoder>> beginComputePass(const std::optional<GPUComputePassDescriptor>&);

void copyBufferToBuffer(
const GPUBuffer& source,
Expand Down Expand Up @@ -99,7 +99,7 @@ class GPUCommandEncoder : public RefCounted<GPUCommandEncoder> {
const GPUBuffer& destination,
GPUSize64 destinationOffset);

Ref<GPUCommandBuffer> finish(const std::optional<GPUCommandBufferDescriptor>&);
ExceptionOr<Ref<GPUCommandBuffer>> finish(const std::optional<GPUCommandBufferDescriptor>&);

WebGPU::CommandEncoder& backing() { return m_backing; }
const WebGPU::CommandEncoder& backing() const { return m_backing; }
Expand Down
115 changes: 87 additions & 28 deletions Source/WebCore/Modules/WebGPU/GPUDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,11 @@ ExceptionOr<Ref<GPUBuffer>> GPUDevice::createBuffer(const GPUBufferDescriptor& b

auto usage = bufferDescriptor.usage;
auto mappedAtCreation = bufferDescriptor.mappedAtCreation;
return GPUBuffer::create(m_backing->createBuffer(bufferDescriptor.convertToBacking()), bufferSize, usage, mappedAtCreation, *this);
RefPtr buffer = m_backing->createBuffer(bufferDescriptor.convertToBacking());
if (!buffer)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createBuffer: Unable to create buffer."_s };

return GPUBuffer::create(buffer.releaseNonNull(), bufferSize, usage, mappedAtCreation, *this);
}

bool GPUDevice::isSupportedFormat(GPUTextureFormat format) const
Expand Down Expand Up @@ -241,7 +245,11 @@ ExceptionOr<Ref<GPUTexture>> GPUDevice::createTexture(const GPUTextureDescriptor
if (!isSupportedFormat(textureDescriptor.format))
return Exception { ExceptionCode::TypeError, "GPUDevice.createTexture: Unsupported texture format."_s };

return GPUTexture::create(m_backing->createTexture(textureDescriptor.convertToBacking()), textureDescriptor, *this);
RefPtr texture = m_backing->createTexture(textureDescriptor.convertToBacking());
if (!texture)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createTexture: Unable to create texture."_s };

return GPUTexture::create(texture.releaseNonNull(), textureDescriptor, *this);
}

static WebGPU::SamplerDescriptor convertToBacking(const std::optional<GPUSamplerDescriptor>& samplerDescriptor)
Expand All @@ -265,9 +273,12 @@ static WebGPU::SamplerDescriptor convertToBacking(const std::optional<GPUSampler
return samplerDescriptor->convertToBacking();
}

Ref<GPUSampler> GPUDevice::createSampler(const std::optional<GPUSamplerDescriptor>& samplerDescriptor)
ExceptionOr<Ref<GPUSampler>> GPUDevice::createSampler(const std::optional<GPUSamplerDescriptor>& samplerDescriptor)
{
return GPUSampler::create(m_backing->createSampler(convertToBacking(samplerDescriptor)));
RefPtr sampler = m_backing->createSampler(convertToBacking(samplerDescriptor));
if (!sampler)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createSampler: Unable to create sampler."_s };
return GPUSampler::create(sampler.releaseNonNull());
}

GPUExternalTexture* GPUDevice::externalTextureForDescriptor(const GPUExternalTextureDescriptor& descriptor)
Expand Down Expand Up @@ -323,19 +334,22 @@ class GPUDeviceVideoFrameRequestCallback final : public VideoFrameRequestCallbac
WeakPtr<GPUDevice, WeakPtrImplWithEventTargetData> m_gpuDevice;
};

Ref<GPUExternalTexture> GPUDevice::importExternalTexture(const GPUExternalTextureDescriptor& externalTextureDescriptor)
ExceptionOr<Ref<GPUExternalTexture>> GPUDevice::importExternalTexture(const GPUExternalTextureDescriptor& externalTextureDescriptor)
{
if (auto* externalTexture = externalTextureForDescriptor(externalTextureDescriptor)) {
if (RefPtr externalTexture = externalTextureForDescriptor(externalTextureDescriptor)) {
externalTexture->undestroy();
#if ENABLE(WEB_CODECS)
auto& videoElement = std::get<RefPtr<HTMLVideoElement>>(externalTextureDescriptor.source);
#else
auto& videoElement = externalTextureDescriptor.source;
#endif
m_videoElementToExternalTextureMap.remove(*videoElement.get());
return *externalTexture;
return externalTexture.releaseNonNull();
}
auto externalTexture = GPUExternalTexture::create(m_backing->importExternalTexture(externalTextureDescriptor.convertToBacking()));
RefPtr texture = m_backing->importExternalTexture(externalTextureDescriptor.convertToBacking());
if (!texture)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.importExternalTexture: Unable to import texture."_s };
auto externalTexture = GPUExternalTexture::create(texture.releaseNonNull());
#if ENABLE(VIDEO)
#if ENABLE(WEB_CODECS)
if (auto* videoElement = std::get_if<RefPtr<HTMLVideoElement>>(&externalTextureDescriptor.source); videoElement && videoElement->get()) {
Expand Down Expand Up @@ -367,35 +381,58 @@ ExceptionOr<Ref<GPUBindGroupLayout>> GPUDevice::createBindGroupLayout(const GPUB
return Exception { ExceptionCode::TypeError, "GPUDevice.createBindGroupLayout: Unsupported texture format."_s };
}
}
return GPUBindGroupLayout::create(m_backing->createBindGroupLayout(bindGroupLayoutDescriptor.convertToBacking()));

RefPtr layout = m_backing->createBindGroupLayout(bindGroupLayoutDescriptor.convertToBacking());
if (!layout)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createBindGroupLayout: Unable to create bind group layout."_s };
return GPUBindGroupLayout::create(layout.releaseNonNull());
}

Ref<GPUPipelineLayout> GPUDevice::createAutoPipelineLayout()
RefPtr<GPUPipelineLayout> GPUDevice::createAutoPipelineLayout()
{
return GPUPipelineLayout::create(m_backing->createPipelineLayout(WebGPU::PipelineLayoutDescriptor {
RefPtr layout = m_backing->createPipelineLayout(WebGPU::PipelineLayoutDescriptor {
{ "autoLayout"_s, },
std::nullopt
}));
});
if (!layout)
return nullptr;
return GPUPipelineLayout::create(layout.releaseNonNull());
}

Ref<GPUPipelineLayout> GPUDevice::createPipelineLayout(const GPUPipelineLayoutDescriptor& pipelineLayoutDescriptor)
ExceptionOr<Ref<GPUPipelineLayout>> GPUDevice::createPipelineLayout(const GPUPipelineLayoutDescriptor& pipelineLayoutDescriptor)
{
return GPUPipelineLayout::create(m_backing->createPipelineLayout(pipelineLayoutDescriptor.convertToBacking()));
RefPtr pipelineLayout = m_backing->createPipelineLayout(pipelineLayoutDescriptor.convertToBacking());
if (!pipelineLayout)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createPipelineLayout: Unable to make pipeline layout."_s };
return GPUPipelineLayout::create(pipelineLayout.releaseNonNull());
}

Ref<GPUBindGroup> GPUDevice::createBindGroup(const GPUBindGroupDescriptor& bindGroupDescriptor)
ExceptionOr<Ref<GPUBindGroup>> GPUDevice::createBindGroup(const GPUBindGroupDescriptor& bindGroupDescriptor)
{
return GPUBindGroup::create(m_backing->createBindGroup(bindGroupDescriptor.convertToBacking()));
RefPtr group = m_backing->createBindGroup(bindGroupDescriptor.convertToBacking());
if (!group)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createBindGroup: Unable to make bind group."_s };
return GPUBindGroup::create(group.releaseNonNull());
}

Ref<GPUShaderModule> GPUDevice::createShaderModule(const GPUShaderModuleDescriptor& shaderModuleDescriptor)
ExceptionOr<Ref<GPUShaderModule>> GPUDevice::createShaderModule(const GPUShaderModuleDescriptor& shaderModuleDescriptor)
{
return GPUShaderModule::create(m_backing->createShaderModule(shaderModuleDescriptor.convertToBacking(m_autoPipelineLayout)));
if (!m_autoPipelineLayout)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createShaderModule: Unable to make shader module."_s };
RefPtr shaderModule = m_backing->createShaderModule(shaderModuleDescriptor.convertToBacking(*m_autoPipelineLayout));
if (!shaderModule)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createShaderModule: Unable to make shader module."_s };
return GPUShaderModule::create(shaderModule.releaseNonNull());
}

Ref<GPUComputePipeline> GPUDevice::createComputePipeline(const GPUComputePipelineDescriptor& computePipelineDescriptor)
ExceptionOr<Ref<GPUComputePipeline>> GPUDevice::createComputePipeline(const GPUComputePipelineDescriptor& computePipelineDescriptor)
{
return GPUComputePipeline::create(m_backing->createComputePipeline(computePipelineDescriptor.convertToBacking(m_autoPipelineLayout)));
if (!m_autoPipelineLayout)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createComputePipeline: Unable to make pipeline."_s };
RefPtr pipeline = m_backing->createComputePipeline(computePipelineDescriptor.convertToBacking(*m_autoPipelineLayout));
if (!pipeline)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createComputePipeline: Unable to make pipeline."_s };
return GPUComputePipeline::create(pipeline.releaseNonNull());
}

ExceptionOr<Ref<GPURenderPipeline>> GPUDevice::createRenderPipeline(const GPURenderPipelineDescriptor& renderPipelineDescriptor)
Expand All @@ -413,13 +450,22 @@ ExceptionOr<Ref<GPURenderPipeline>> GPUDevice::createRenderPipeline(const GPURen
return Exception { ExceptionCode::TypeError, "GPUDevice.createRenderPipeline: Unsupported texture format for depth target."_s };
}

return GPURenderPipeline::create(m_backing->createRenderPipeline(renderPipelineDescriptor.convertToBacking(m_autoPipelineLayout)));
if (!m_autoPipelineLayout)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createRenderPipeline: Unable to make pipeline."_s };
RefPtr renderPipeline = m_backing->createRenderPipeline(renderPipelineDescriptor.convertToBacking(*m_autoPipelineLayout));
if (!renderPipeline)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createRenderPipeline: Unable to make pipeline."_s };
return GPURenderPipeline::create(renderPipeline.releaseNonNull());
}

void GPUDevice::createComputePipelineAsync(const GPUComputePipelineDescriptor& computePipelineDescriptor, CreateComputePipelineAsyncPromise&& promise)
{
m_backing->createComputePipelineAsync(computePipelineDescriptor.convertToBacking(m_autoPipelineLayout), [promise = WTFMove(promise)](RefPtr<WebGPU::ComputePipeline>&& computePipeline) mutable {
if (computePipeline.get())
if (!m_autoPipelineLayout) {
promise.rejectType<IDLInterface<GPUPipelineError>>(GPUPipelineError::create(""_s, { GPUPipelineErrorReason::Internal }));
return;
}
m_backing->createComputePipelineAsync(computePipelineDescriptor.convertToBacking(*m_autoPipelineLayout), [promise = WTFMove(promise)](RefPtr<WebGPU::ComputePipeline>&& computePipeline) mutable {
if (computePipeline)
promise.resolve(GPUComputePipeline::create(computePipeline.releaseNonNull()));
else
promise.rejectType<IDLInterface<GPUPipelineError>>(GPUPipelineError::create(""_s, { GPUPipelineErrorReason::Validation }));
Expand All @@ -441,7 +487,10 @@ ExceptionOr<void> GPUDevice::createRenderPipelineAsync(const GPURenderPipelineDe
return Exception { ExceptionCode::TypeError, "GPUDevice.createRenderBundleEncoder: Unsupported texture format for color format."_s };
}

m_backing->createRenderPipelineAsync(renderPipelineDescriptor.convertToBacking(m_autoPipelineLayout), [promise = WTFMove(promise)](RefPtr<WebGPU::RenderPipeline>&& renderPipeline) mutable {
if (!m_autoPipelineLayout)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createRenderBundleEncoder: Unable to make encoder."_s };

m_backing->createRenderPipelineAsync(renderPipelineDescriptor.convertToBacking(*m_autoPipelineLayout), [promise = WTFMove(promise)](RefPtr<WebGPU::RenderPipeline>&& renderPipeline) mutable {
if (renderPipeline.get())
promise.resolve(GPURenderPipeline::create(renderPipeline.releaseNonNull()));
else
Expand All @@ -458,9 +507,12 @@ static WebGPU::CommandEncoderDescriptor convertToBacking(const std::optional<GPU
return commandEncoderDescriptor->convertToBacking();
}

Ref<GPUCommandEncoder> GPUDevice::createCommandEncoder(const std::optional<GPUCommandEncoderDescriptor>& commandEncoderDescriptor)
ExceptionOr<Ref<GPUCommandEncoder>> GPUDevice::createCommandEncoder(const std::optional<GPUCommandEncoderDescriptor>& commandEncoderDescriptor)
{
return GPUCommandEncoder::create(m_backing->createCommandEncoder(convertToBacking(commandEncoderDescriptor)));
RefPtr encoder = m_backing->createCommandEncoder(convertToBacking(commandEncoderDescriptor));
if (!encoder)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createCommandEncoder: Unable to make command encoder."_s };
return GPUCommandEncoder::create(encoder.releaseNonNull());
}

ExceptionOr<Ref<GPURenderBundleEncoder>> GPUDevice::createRenderBundleEncoder(const GPURenderBundleEncoderDescriptor& renderBundleEncoderDescriptor)
Expand All @@ -476,7 +528,10 @@ ExceptionOr<Ref<GPURenderBundleEncoder>> GPUDevice::createRenderBundleEncoder(co
return Exception { ExceptionCode::TypeError, "GPUDevice.createRenderBundleEncoder: Unsupported texture format for depth format."_s };
}

return GPURenderBundleEncoder::create(m_backing->createRenderBundleEncoder(renderBundleEncoderDescriptor.convertToBacking()));
RefPtr encoder = m_backing->createRenderBundleEncoder(renderBundleEncoderDescriptor.convertToBacking());
if (!encoder)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createRenderBundleEncoder: Unable to make encoder."_s };
return GPURenderBundleEncoder::create(encoder.releaseNonNull());
}

ExceptionOr<Ref<GPUQuerySet>> GPUDevice::createQuerySet(const GPUQuerySetDescriptor& querySetDescriptor)
Expand All @@ -486,7 +541,11 @@ ExceptionOr<Ref<GPUQuerySet>> GPUDevice::createQuerySet(const GPUQuerySetDescrip
return Exception { ExceptionCode::TypeError, "Timestamp queries are not supported."_s };
}

return GPUQuerySet::create(m_backing->createQuerySet(querySetDescriptor.convertToBacking()), querySetDescriptor);
RefPtr querySet = m_backing->createQuerySet(querySetDescriptor.convertToBacking());
if (!querySet)
return Exception { ExceptionCode::InvalidStateError, "GPUDevice.createQuerySet: Unable to make query set."_s };

return GPUQuerySet::create(querySet.releaseNonNull(), querySetDescriptor);
}

void GPUDevice::pushErrorScope(GPUErrorFilter errorFilter)
Expand Down
Loading

0 comments on commit f816a17

Please sign in to comment.