blob: c1d13fc15a54458517123f7cbe341738b3615fcd [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"
namespace blink {
// static
GPUDevice* GPUDevice::Create(
ExecutionContext* execution_context,
scoped_refptr<DawnControlClientHolder> dawn_control_client,
GPUAdapter* adapter,
const GPUDeviceDescriptor* descriptor) {
return MakeGarbageCollected<GPUDevice>(
execution_context, std::move(dawn_control_client), adapter, descriptor);
}
// 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_(GPUQueue::Create(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) {
GPUDeviceLostInfo* device_lost_info = GPUDeviceLostInfo::Create(message);
lost_property_->Resolve(device_lost_info);
}
GPUUncapturedErrorEventInit* init = GPUUncapturedErrorEventInit::Create();
if (errorType == WGPUErrorType_Validation) {
GPUValidationError* error = GPUValidationError::Create(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());
}
GPUBuffer* GPUDevice::createBuffer(const GPUBufferDescriptor* descriptor) {
return GPUBuffer::Create(this, descriptor);
}
HeapVector<ScriptValue> GPUDevice::createBufferMapped(
ScriptState* script_state,
const GPUBufferDescriptor* descriptor,
ExceptionState& exception_state) {
GPUBuffer* gpu_buffer;
DOMArrayBuffer* array_buffer;
std::tie(gpu_buffer, array_buffer) =
GPUBuffer::CreateMapped(this, descriptor, exception_state);
v8::Isolate* isolate = script_state->GetIsolate();
v8::Local<v8::Object> creation_context = script_state->GetContext()->Global();
return HeapVector<ScriptValue>({
ScriptValue(isolate, ToV8(gpu_buffer, creation_context, isolate)),
ScriptValue(isolate, ToV8(array_buffer, creation_context, isolate)),
});
}
ScriptPromise GPUDevice::createBufferMappedAsync(
ScriptState* script_state,
const GPUBufferDescriptor* descriptor,
ExceptionState& exception_state) {
GPUBuffer* gpu_buffer;
DOMArrayBuffer* array_buffer;
std::tie(gpu_buffer, array_buffer) =
GPUBuffer::CreateMapped(this, descriptor, exception_state);
v8::Isolate* isolate = script_state->GetIsolate();
v8::Local<v8::Object> creation_context = script_state->GetContext()->Global();
v8::Local<v8::Value> elements[] = {
ToV8(gpu_buffer, creation_context, isolate),
ToV8(array_buffer, creation_context, isolate),
};
ScriptValue result(isolate, v8::Array::New(isolate, elements, 2));
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// TODO(enga): CreateBufferMappedAsync is intended to spend more time to
// create an optimal mapping for the buffer. It resolves the promise when the
// mapping is complete. Currently, there is always a staging buffer in the
// wire so this is already the optimal path and the promise is immediately
// resolved. When we can create a buffer such that the memory is mapped
// directly in the renderer process, this promise should be resolved
// asynchronously.
if (exception_state.HadException()) {
resolver->Reject(exception_state);
} else {
resolver->Resolve(result);
}
return promise;
}
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);
}
GPUQueue* GPUDevice::getQueue() {
return queue_;
}
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(GPUValidationError::Create(message));
break;
case WGPUErrorType_Unknown:
case WGPUErrorType_DeviceLost:
resolver->Reject(
MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
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