blob: a2d3ba2d927867769474ad83bb09a65f3d869801 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
#include "gpu/command_buffer/client/webgpu_interface.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/modules/event_target_modules.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_bind_group.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_compute_pipeline.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_device_descriptor.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_device_lost_info.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_queue.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_sampler.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_shader_module.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event_init.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
namespace blink {
// TODO(enga): Handle adapter options and device descriptor
GPUDevice::GPUDevice(ExecutionContext* execution_context,
scoped_refptr<DawnControlClientHolder> dawn_control_client,
GPUAdapter* adapter,
const GPUDeviceDescriptor* descriptor)
: ContextClient(execution_context),
DawnObject(dawn_control_client,
dawn_control_client->GetInterface()->GetDefaultDevice()),
adapter_(adapter),
queue_(MakeGarbageCollected<GPUQueue>(
this,
GetProcs().deviceCreateQueue(GetHandle()))),
lost_property_(MakeGarbageCollected<LostProperty>(execution_context,
this,
LostProperty::kLost)),
error_callback_(
BindRepeatingDawnCallback(&GPUDevice::OnUncapturedError,
WrapWeakPersistent(this),
WrapWeakPersistent(execution_context))) {
GetProcs().deviceSetUncapturedErrorCallback(
GetHandle(), error_callback_->UnboundRepeatingCallback(),
error_callback_->AsUserdata());
}
GPUDevice::~GPUDevice() {
if (IsDawnControlClientDestroyed()) {
return;
}
GetProcs().deviceRelease(GetHandle());
}
void GPUDevice::OnUncapturedError(ExecutionContext* execution_context,
WGPUErrorType errorType,
const char* message) {
if (execution_context) {
DCHECK_NE(errorType, WGPUErrorType_NoError);
LOG(ERROR) << "GPUDevice: " << message;
ConsoleMessage* console_message =
ConsoleMessage::Create(mojom::ConsoleMessageSource::kRendering,
mojom::ConsoleMessageLevel::kWarning, message);
execution_context->AddConsoleMessage(console_message);
}
// TODO: Use device lost callback instead of uncaptured error callback.
if (errorType == WGPUErrorType_DeviceLost &&
lost_property_->GetState() == ScriptPromisePropertyBase::kPending) {
auto* device_lost_info = MakeGarbageCollected<GPUDeviceLostInfo>(message);
lost_property_->Resolve(device_lost_info);
}
GPUUncapturedErrorEventInit* init = GPUUncapturedErrorEventInit::Create();
if (errorType == WGPUErrorType_Validation) {
auto* error = MakeGarbageCollected<GPUValidationError>(message);
init->setError(
GPUOutOfMemoryErrorOrGPUValidationError::FromGPUValidationError(error));
} else if (errorType == WGPUErrorType_OutOfMemory) {
GPUOutOfMemoryError* error = GPUOutOfMemoryError::Create();
init->setError(
GPUOutOfMemoryErrorOrGPUValidationError::FromGPUOutOfMemoryError(
error));
} else {
return;
}
this->DispatchEvent(*GPUUncapturedErrorEvent::Create(
event_type_names::kUncapturederror, init));
}
GPUAdapter* GPUDevice::adapter() const {
return adapter_;
}
ScriptPromise GPUDevice::lost(ScriptState* script_state) {
return lost_property_->Promise(script_state->World());
}
GPUQueue* GPUDevice::defaultQueue() {
return queue_;
}
GPUBuffer* GPUDevice::createBuffer(const GPUBufferDescriptor* descriptor) {
return GPUBuffer::Create(this, descriptor);
}
HeapVector<GPUBufferOrArrayBuffer> GPUDevice::createBufferMapped(
const GPUBufferDescriptor* descriptor,
ExceptionState& exception_state) {
GPUBuffer* gpu_buffer;
DOMArrayBuffer* array_buffer;
std::tie(gpu_buffer, array_buffer) =
GPUBuffer::CreateMapped(this, descriptor, exception_state);
return HeapVector<GPUBufferOrArrayBuffer>(
{GPUBufferOrArrayBuffer::FromGPUBuffer(gpu_buffer),
GPUBufferOrArrayBuffer::FromArrayBuffer(array_buffer)});
}
GPUTexture* GPUDevice::createTexture(const GPUTextureDescriptor* descriptor,
ExceptionState& exception_state) {
return GPUTexture::Create(this, descriptor, exception_state);
}
GPUSampler* GPUDevice::createSampler(const GPUSamplerDescriptor* descriptor) {
return GPUSampler::Create(this, descriptor);
}
GPUBindGroup* GPUDevice::createBindGroup(
const GPUBindGroupDescriptor* descriptor) {
return GPUBindGroup::Create(this, descriptor);
}
GPUBindGroupLayout* GPUDevice::createBindGroupLayout(
const GPUBindGroupLayoutDescriptor* descriptor) {
return GPUBindGroupLayout::Create(this, descriptor);
}
GPUPipelineLayout* GPUDevice::createPipelineLayout(
const GPUPipelineLayoutDescriptor* descriptor) {
return GPUPipelineLayout::Create(this, descriptor);
}
GPUShaderModule* GPUDevice::createShaderModule(
const GPUShaderModuleDescriptor* descriptor) {
return GPUShaderModule::Create(this, descriptor);
}
GPURenderPipeline* GPUDevice::createRenderPipeline(
ScriptState* script_state,
const GPURenderPipelineDescriptor* descriptor) {
return GPURenderPipeline::Create(script_state, this, descriptor);
}
GPUComputePipeline* GPUDevice::createComputePipeline(
const GPUComputePipelineDescriptor* descriptor) {
return GPUComputePipeline::Create(this, descriptor);
}
GPUCommandEncoder* GPUDevice::createCommandEncoder(
const GPUCommandEncoderDescriptor* descriptor) {
return GPUCommandEncoder::Create(this, descriptor);
}
GPURenderBundleEncoder* GPUDevice::createRenderBundleEncoder(
const GPURenderBundleEncoderDescriptor* descriptor) {
return GPURenderBundleEncoder::Create(this, descriptor);
}
void GPUDevice::pushErrorScope(const WTF::String& filter) {
GetProcs().devicePushErrorScope(GetHandle(),
AsDawnEnum<WGPUErrorFilter>(filter));
}
ScriptPromise GPUDevice::popErrorScope(ScriptState* script_state) {
ScriptPromiseResolver* resolver =
MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
auto* callback =
BindDawnCallback(&GPUDevice::OnPopErrorScopeCallback,
WrapPersistent(this), WrapPersistent(resolver));
if (!GetProcs().devicePopErrorScope(GetHandle(), callback->UnboundCallback(),
callback->AsUserdata())) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kOperationError, "No error scopes to pop."));
delete callback;
return promise;
}
// WebGPU guarantees callbacks complete in finite time. Flush now so that
// commands reach the GPU process. TODO(enga): This should happen at the end
// of the task.
GetInterface()->FlushCommands();
return promise;
}
void GPUDevice::OnPopErrorScopeCallback(ScriptPromiseResolver* resolver,
WGPUErrorType type,
const char* message) {
v8::Isolate* isolate = resolver->GetScriptState()->GetIsolate();
switch (type) {
case WGPUErrorType_NoError:
resolver->Resolve(v8::Null(isolate));
break;
case WGPUErrorType_OutOfMemory:
resolver->Resolve(GPUOutOfMemoryError::Create());
break;
case WGPUErrorType_Validation:
resolver->Resolve(MakeGarbageCollected<GPUValidationError>(message));
break;
case WGPUErrorType_Unknown:
case WGPUErrorType_DeviceLost:
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kOperationError));
break;
default:
NOTREACHED();
}
}
ExecutionContext* GPUDevice::GetExecutionContext() const {
return ContextClient::GetExecutionContext();
}
const AtomicString& GPUDevice::InterfaceName() const {
return event_target_names::kGPUDevice;
}
void GPUDevice::Trace(blink::Visitor* visitor) {
visitor->Trace(adapter_);
visitor->Trace(queue_);
visitor->Trace(lost_property_);
ContextClient::Trace(visitor);
EventTargetWithInlineData::Trace(visitor);
}
} // namespace blink