| // Copyright 2018 The Fuchsia 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 "zircon_platform_connection_client.h" |
| |
| #include <vector> |
| |
| #include "magma_common_defs.h" |
| #include "magma_util/dlog.h" |
| #include "platform_connection_client.h" |
| #include "zircon_platform_handle.h" |
| #include "zircon_platform_status.h" |
| |
| // clang-format off |
| using fuchsia_gpu_magma::wire::QueryId; |
| static_assert(static_cast<uint32_t>(QueryId::kVendorId) == MAGMA_QUERY_VENDOR_ID, "mismatch"); |
| static_assert(static_cast<uint32_t>(QueryId::kDeviceId) == MAGMA_QUERY_DEVICE_ID, "mismatch"); |
| static_assert(static_cast<uint32_t>(QueryId::kIsTotalTimeSupported) == MAGMA_QUERY_IS_TOTAL_TIME_SUPPORTED, "mismatch"); |
| static_assert(static_cast<uint32_t>(QueryId::kMaximumInflightParams) == MAGMA_QUERY_MAXIMUM_INFLIGHT_PARAMS, "mismatch"); |
| using fuchsia_gpu_magma::wire::MapFlags; |
| static_assert(static_cast<uint64_t>(MapFlags::kRead) == MAGMA_GPU_MAP_FLAG_READ, "mismatch"); |
| static_assert(static_cast<uint64_t>(MapFlags::kWrite) == MAGMA_GPU_MAP_FLAG_WRITE, "mismatch"); |
| static_assert(static_cast<uint64_t>(MapFlags::kExecute) == MAGMA_GPU_MAP_FLAG_EXECUTE, "mismatch"); |
| static_assert(static_cast<uint64_t>(MapFlags::kGrowable) == MAGMA_GPU_MAP_FLAG_GROWABLE, "mismatch"); |
| static_assert(static_cast<uint64_t>(MapFlags::kVendorFlag0) == MAGMA_GPU_MAP_FLAG_VENDOR_0, "mismatch"); |
| |
| // clang-format on |
| |
| namespace magma { |
| |
| class ZirconPlatformPerfCountPoolClient : public PlatformPerfCountPoolClient { |
| public: |
| zx_status_t Initialize() { |
| static std::atomic_uint64_t ids; |
| pool_id_ = ids++; |
| zx::channel client_endpoint; |
| zx_status_t status = zx::channel::create(0, &client_endpoint, &server_endpoint_); |
| if (status == ZX_OK) |
| perf_counter_events_ = fidl::WireSyncClient( |
| fidl::ClientEnd<fuchsia_gpu_magma::PerformanceCounterEvents>(std::move(client_endpoint))); |
| return status; |
| } |
| uint64_t pool_id() override { return pool_id_; } |
| zx::channel TakeServerEndpoint() { |
| DASSERT(server_endpoint_); |
| return std::move(server_endpoint_); |
| } |
| |
| magma_handle_t handle() override { return perf_counter_events_.client_end().channel().get(); } |
| magma::Status ReadPerformanceCounterCompletion(uint32_t* trigger_id_out, uint64_t* buffer_id_out, |
| uint32_t* buffer_offset_out, uint64_t* time_out, |
| uint32_t* result_flags_out) override { |
| zx_signals_t pending; |
| zx_status_t status = perf_counter_events_.client_end().channel().wait_one( |
| ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, zx::time(), &pending); |
| if (status != ZX_OK) { |
| magma_status_t magma_status = magma::FromZxStatus(status).get(); |
| return DRET(magma_status); |
| } |
| if (!(pending & ZX_CHANNEL_READABLE)) { |
| // If none of the signals were active then wait_one should have returned ZX_ERR_TIMED_OUT. |
| DASSERT(pending & ZX_CHANNEL_PEER_CLOSED); |
| return DRET(MAGMA_STATUS_CONNECTION_LOST); |
| } |
| |
| class EventHandler |
| : public fidl::WireSyncEventHandler<fuchsia_gpu_magma::PerformanceCounterEvents> { |
| public: |
| EventHandler(uint32_t* trigger_id_out, uint64_t* buffer_id_out, uint32_t* buffer_offset_out, |
| uint64_t* time_out, uint32_t* result_flags_out) |
| : trigger_id_out_(trigger_id_out), |
| buffer_id_out_(buffer_id_out), |
| buffer_offset_out_(buffer_offset_out), |
| time_out_(time_out), |
| result_flags_out_(result_flags_out) {} |
| |
| void OnPerformanceCounterReadCompleted( |
| fidl::WireEvent< |
| fuchsia_gpu_magma::PerformanceCounterEvents::OnPerformanceCounterReadCompleted>* |
| event) override { |
| if (!event->has_trigger_id() || !event->has_buffer_id() || !event->has_buffer_offset() || |
| !event->has_timestamp() || !event->has_flags()) { |
| return; |
| } |
| *trigger_id_out_ = event->trigger_id(); |
| *buffer_id_out_ = event->buffer_id(); |
| *buffer_offset_out_ = event->buffer_offset(); |
| *time_out_ = event->timestamp(); |
| *result_flags_out_ = static_cast<uint32_t>(event->flags()); |
| } |
| |
| private: |
| uint32_t* const trigger_id_out_; |
| uint64_t* const buffer_id_out_; |
| uint32_t* const buffer_offset_out_; |
| uint64_t* const time_out_; |
| uint32_t* const result_flags_out_; |
| }; |
| |
| EventHandler event_handler(trigger_id_out, buffer_id_out, buffer_offset_out, time_out, |
| result_flags_out); |
| fidl::Status event_status = perf_counter_events_.HandleOneEvent(event_handler); |
| if (!event_status.ok() && event_status.reason() == fidl::Reason::kUnexpectedMessage) { |
| status = ZX_ERR_INTERNAL; |
| } else { |
| status = event_status.status(); |
| } |
| magma_status_t magma_status = magma::FromZxStatus(status).get(); |
| return DRET(magma_status); |
| } |
| |
| private: |
| uint64_t pool_id_; |
| |
| fidl::WireSyncClient<fuchsia_gpu_magma::PerformanceCounterEvents> perf_counter_events_; |
| zx::channel server_endpoint_; |
| }; |
| |
| void PrimaryWrapper::on_fidl_error(::fidl::UnbindInfo info) { |
| unbind_info_ = info; |
| loop_.Quit(); |
| } |
| |
| void PrimaryWrapper::OnNotifyMessagesConsumed( |
| ::fidl::WireEvent<::fuchsia_gpu_magma::Primary::OnNotifyMessagesConsumed>* event) { |
| inflight_count_ -= event->count; |
| } |
| |
| void PrimaryWrapper::OnNotifyMemoryImported( |
| ::fidl::WireEvent<::fuchsia_gpu_magma::Primary::OnNotifyMemoryImported>* event) { |
| inflight_bytes_ -= event->bytes; |
| } |
| |
| PrimaryWrapper::PrimaryWrapper(zx::channel channel, uint64_t max_inflight_messages, |
| uint64_t max_inflight_bytes) |
| : loop_(&kAsyncLoopConfigNeverAttachToThread), |
| client_(fidl::ClientEnd<fuchsia_gpu_magma::Primary>(std::move(channel)), loop_.dispatcher(), |
| this), |
| max_inflight_messages_(max_inflight_messages), |
| max_inflight_bytes_(max_inflight_bytes) { |
| if (max_inflight_messages == 0 || max_inflight_bytes == 0) |
| return; |
| |
| zx_status_t status = client_->EnableFlowControl().status(); |
| if (status == ZX_OK) { |
| flow_control_enabled_ = true; |
| } else { |
| MAGMA_LOG(ERROR, "EnableFlowControl failed: %d", status); |
| } |
| } |
| |
| static_assert(static_cast<uint32_t>(magma::PlatformObject::SEMAPHORE) == |
| static_cast<uint32_t>(fuchsia_gpu_magma::wire::ObjectType::kEvent)); |
| static_assert(static_cast<uint32_t>(magma::PlatformObject::BUFFER) == |
| static_cast<uint32_t>(fuchsia_gpu_magma::wire::ObjectType::kBuffer)); |
| |
| magma_status_t PrimaryWrapper::ImportObject(zx::handle handle, |
| magma::PlatformObject::Type object_type) { |
| uint64_t size = 0; |
| |
| if (object_type == magma::PlatformObject::BUFFER) { |
| zx::unowned_vmo vmo(handle.get()); |
| zx_status_t status = vmo->get_size(&size); |
| if (status != ZX_OK) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "get_size failed: %d", status); |
| } |
| |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(size); |
| |
| auto wire_object_type = static_cast<fuchsia_gpu_magma::wire::ObjectType>(object_type); |
| |
| zx_status_t status = client_->ImportObject(std::move(handle), wire_object_type).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(size); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::ImportObject(zx::handle handle, |
| magma::PlatformObject::Type object_type, |
| uint64_t object_id) { |
| uint64_t size = 0; |
| |
| if (object_type == magma::PlatformObject::BUFFER) { |
| zx::unowned_vmo vmo(handle.get()); |
| zx_status_t status = vmo->get_size(&size); |
| if (status != ZX_OK) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "get_size failed: %d", status); |
| } |
| |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(size); |
| |
| auto wire_object_type = static_cast<fuchsia_gpu_magma::wire::ObjectType>(object_type); |
| |
| zx_status_t status = |
| client_->ImportObject2(std::move(handle), wire_object_type, object_id).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(size); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::ReleaseObject(uint64_t object_id, |
| magma::PlatformObject::Type object_type) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| |
| auto wire_object_type = static_cast<fuchsia_gpu_magma::wire::ObjectType>(object_type); |
| |
| zx_status_t status = client_->ReleaseObject(object_id, wire_object_type).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::CreateContext(uint32_t context_id) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->CreateContext(context_id).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::DestroyContext(uint32_t context_id) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->DestroyContext(context_id).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| static_assert(static_cast<uint64_t>(fuchsia_gpu_magma::wire::CommandBufferFlags::kVendorFlag0) == |
| MAGMA_COMMAND_BUFFER_VENDOR_FLAGS_0); |
| |
| // DEPRECATED - TODO(fxb/86670) remove |
| magma_status_t PrimaryWrapper::ExecuteCommandBufferWithResources2( |
| uint32_t context_id, fuchsia_gpu_magma::wire::CommandBuffer2 command_buffer, |
| ::fidl::VectorView<fuchsia_gpu_magma::wire::BufferRange> resources, |
| ::fidl::VectorView<uint64_t> wait_semaphores, ::fidl::VectorView<uint64_t> signal_semaphores) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_ |
| ->ExecuteCommandBufferWithResources2( |
| context_id, std::move(command_buffer), std::move(resources), |
| std::move(wait_semaphores), std::move(signal_semaphores)) |
| .status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::ExecuteCommand( |
| uint32_t context_id, ::fidl::VectorView<fuchsia_gpu_magma::wire::BufferRange> resources, |
| ::fidl::VectorView<fuchsia_gpu_magma::wire::CommandBuffer> command_buffers, |
| ::fidl::VectorView<uint64_t> wait_semaphores, ::fidl::VectorView<uint64_t> signal_semaphores, |
| fuchsia_gpu_magma::wire::CommandBufferFlags flags) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = |
| client_ |
| ->ExecuteCommand(context_id, std::move(resources), std::move(command_buffers), |
| std::move(wait_semaphores), std::move(signal_semaphores), flags) |
| .status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::ExecuteImmediateCommands(uint32_t context_id, |
| ::fidl::VectorView<uint8_t> command_data, |
| ::fidl::VectorView<uint64_t> semaphores) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = |
| client_->ExecuteImmediateCommands(context_id, std::move(command_data), std::move(semaphores)) |
| .status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::MapBufferGpu(uint64_t buffer_id, uint64_t gpu_va, |
| uint64_t page_offset, uint64_t page_count, |
| fuchsia_gpu_magma::wire::MapFlags flags) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = |
| client_->MapBufferGpu(buffer_id, gpu_va, page_offset, page_count, flags).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::UnmapBufferGpu(uint64_t buffer_id, uint64_t gpu_va) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->UnmapBufferGpu(buffer_id, gpu_va).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::BufferRangeOp(uint64_t buffer_id, |
| fuchsia_gpu_magma::wire::BufferOp op, uint64_t start, |
| uint64_t length) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->BufferRangeOp(buffer_id, op, start, length).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::EnablePerformanceCounterAccess(zx::event event) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->EnablePerformanceCounterAccess(std::move(event)).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::EnablePerformanceCounters(fidl::VectorView<uint64_t> counters) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->EnablePerformanceCounters(std::move(counters)).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::CreatePerformanceCounterBufferPool(uint64_t pool_id, |
| zx::channel event_channel) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = |
| client_ |
| ->CreatePerformanceCounterBufferPool( |
| pool_id, fidl::ServerEnd<fuchsia_gpu_magma::PerformanceCounterEvents>( |
| std::move(event_channel))) |
| .status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::ReleasePerformanceCounterBufferPool(uint64_t pool_id) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->ReleasePerformanceCounterBufferPool(pool_id).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::AddPerformanceCounterBufferOffsetsToPool( |
| uint64_t pool_id, fidl::VectorView<fuchsia_gpu_magma::wire::BufferRange> offsets) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = |
| client_->AddPerformanceCounterBufferOffsetsToPool(pool_id, std::move(offsets)).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::RemovePerformanceCounterBufferFromPool(uint64_t pool_id, |
| uint64_t buffer_id) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->RemovePerformanceCounterBufferFromPool(pool_id, buffer_id).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::DumpPerformanceCounters(uint64_t pool_id, uint32_t trigger_id) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->DumpPerformanceCounters(pool_id, trigger_id).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::ClearPerformanceCounters(fidl::VectorView<uint64_t> counters) { |
| std::lock_guard<std::mutex> lock(flow_control_mutex_); |
| FlowControl(); |
| zx_status_t status = client_->ClearPerformanceCounters(std::move(counters)).status(); |
| if (status == ZX_OK) { |
| UpdateFlowControl(); |
| } |
| return magma::FromZxStatus(status).get(); |
| } |
| |
| std::tuple<bool, uint64_t, uint64_t> PrimaryWrapper::ShouldWait(uint64_t new_bytes) { |
| uint64_t count = inflight_count_ + 1; |
| uint64_t bytes = inflight_bytes_ + new_bytes; |
| |
| if (count > max_inflight_messages_) { |
| return {true, count, bytes}; |
| } |
| |
| if (new_bytes && inflight_bytes_ < max_inflight_bytes_ / 2) { |
| // Don't block because we won't get a return message. |
| // Its ok to exceed the max inflight bytes in order to get very large messages through. |
| return {false, count, bytes}; |
| } |
| |
| return {new_bytes && bytes > max_inflight_bytes_, count, bytes}; |
| } |
| |
| void PrimaryWrapper::FlowControl(uint64_t new_bytes) { |
| if (!flow_control_enabled_) |
| return; |
| |
| auto [wait, count, bytes] = ShouldWait(new_bytes); |
| |
| const auto wait_time_start = std::chrono::steady_clock::now(); |
| |
| while (true) { |
| if (wait) { |
| DLOG("Flow control: waiting message count %lu (max %lu) bytes %lu (max %lu) new_bytes %lu", |
| count, max_inflight_messages_, bytes, max_inflight_bytes_, new_bytes); |
| } |
| |
| // Flow control messages are handled in the PrimaryWrapper::AsyncHandler. |
| zx_status_t status = loop_.Run( |
| wait ? zx::deadline_after(zx::sec(5)) : zx::deadline_after(zx::duration(0)), true /*once*/); |
| if (status == ZX_ERR_BAD_STATE || status == ZX_ERR_CANCELED) { |
| DLOG("Loop was shutdown, client is unbound (channel closed)"); |
| return; |
| } |
| if (wait && status == ZX_ERR_TIMED_OUT) { |
| MAGMA_LOG(WARNING, "Flow control: timed out messages %lu bytes %lu", count, bytes); |
| continue; |
| } |
| if (status != ZX_OK && status != ZX_ERR_TIMED_OUT) { |
| MAGMA_LOG(ERROR, "Flow control: error waiting for message: %d", status); |
| return; |
| } |
| if (wait) { |
| DLOG("Flow control: waited %lld us message count %lu (max %lu) imported bytes %lu (max %lu)", |
| std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - |
| wait_time_start) |
| .count(), |
| count, max_inflight_messages_, bytes, max_inflight_bytes_); |
| } |
| |
| std::tie(wait, count, bytes) = ShouldWait(new_bytes); |
| if (!wait) |
| break; |
| } |
| } |
| |
| void PrimaryWrapper::UpdateFlowControl(uint64_t new_bytes) { |
| if (!flow_control_enabled_) |
| return; |
| inflight_count_ += 1; |
| inflight_bytes_ += new_bytes; |
| } |
| |
| magma_status_t PrimaryWrapper::Flush() { |
| auto result = client_.sync()->Flush(); |
| return magma::FromZxStatus(result.status()).get(); |
| } |
| |
| magma_status_t PrimaryWrapper::GetError() { |
| std::lock_guard<std::mutex> lock(get_error_lock_); |
| if (error_ != MAGMA_STATUS_OK) |
| return error_; |
| |
| auto result = client_.sync()->Flush(); |
| |
| if (!result.ok()) { |
| // Only run the loop if the channel has been closed - we don't want to process any |
| // incoming flow control events here. |
| DASSERT(result.status() == ZX_ERR_PEER_CLOSED || result.status() == ZX_ERR_CANCELED); |
| |
| // Ensure no other threads are waiting for async events via flow control |
| std::lock_guard<std::mutex> loop_lock(flow_control_mutex_); |
| |
| // Run the loop to process the unbind |
| loop_.RunUntilIdle(); |
| |
| auto unbind_info = unbind_info_; |
| DASSERT(unbind_info); |
| if (unbind_info) { |
| DMESSAGE("Primary protocol unbind_info: %s", unbind_info->FormatDescription().c_str()); |
| error_ = MAGMA_STATUS_INTERNAL_ERROR; |
| if (unbind_info->is_peer_closed()) { |
| error_ = magma::FromZxStatus(unbind_info->status()).get(); |
| } |
| } |
| } |
| |
| return error_; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| class ZirconPlatformConnectionClient : public PlatformConnectionClient { |
| public: |
| ZirconPlatformConnectionClient(zx::channel channel, zx::channel notification_channel, |
| uint64_t max_inflight_messages, uint64_t max_inflight_bytes) |
| : client_(std::move(channel), max_inflight_messages, max_inflight_bytes), |
| notification_channel_(std::move(notification_channel)) {} |
| |
| magma_status_t ImportObject(uint32_t handle, PlatformObject::Type object_type) override { |
| DLOG("ZirconPlatformConnectionClient: ImportObject"); |
| magma_status_t result = client_.ImportObject(zx::handle(handle), object_type); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET_MSG(result, "failed to write to channel"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma_status_t ImportObject(uint32_t handle, PlatformObject::Type object_type, |
| uint64_t object_id) override { |
| DLOG("ZirconPlatformConnectionClient: ImportObject"); |
| magma_status_t result = client_.ImportObject(zx::handle(handle), object_type, object_id); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET_MSG(result, "failed to write to channel"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma_status_t ReleaseObject(uint64_t object_id, PlatformObject::Type object_type) override { |
| DLOG("ZirconPlatformConnectionClient: ReleaseObject"); |
| magma_status_t result = client_.ReleaseObject(object_id, object_type); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET_MSG(result, "failed to write to channel"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| // Creates a context and returns the context id |
| magma_status_t CreateContext(uint32_t* context_id_out) override { |
| DLOG("ZirconPlatformConnectionClient: CreateContext"); |
| auto context_id = next_context_id_++; |
| *context_id_out = context_id; |
| magma_status_t result = client_.CreateContext(context_id); |
| return result; |
| } |
| |
| // Destroys a context for the given id |
| magma_status_t DestroyContext(uint32_t context_id) override { |
| DLOG("ZirconPlatformConnectionClient: DestroyContext"); |
| magma_status_t result = client_.DestroyContext(context_id); |
| return result; |
| } |
| |
| magma_status_t ExecuteCommandBufferWithResources(uint32_t context_id, |
| magma_command_buffer* command_buffer, |
| magma_exec_resource* resources, |
| uint64_t* semaphores) override { |
| fuchsia_gpu_magma::wire::CommandBuffer2 fidl_command_buffer = { |
| .resource_index = command_buffer->batch_buffer_resource_index, |
| .start_offset = command_buffer->batch_start_offset, |
| .flags = static_cast<fuchsia_gpu_magma::wire::CommandBufferFlags>(command_buffer->flags)}; |
| |
| std::vector<fuchsia_gpu_magma::wire::BufferRange> fidl_resources; |
| fidl_resources.reserve(command_buffer->resource_count); |
| |
| for (uint32_t i = 0; i < command_buffer->resource_count; i++) { |
| fidl_resources.push_back({.buffer_id = resources[i].buffer_id, |
| .offset = resources[i].offset, |
| .size = resources[i].length}); |
| } |
| |
| uint64_t* wait_semaphores = semaphores; |
| uint64_t* signal_semaphores = semaphores + command_buffer->wait_semaphore_count; |
| |
| magma_status_t result = client_.ExecuteCommandBufferWithResources2( |
| context_id, std::move(fidl_command_buffer), |
| fidl::VectorView<fuchsia_gpu_magma::wire::BufferRange>::FromExternal(fidl_resources), |
| fidl::VectorView<uint64_t>::FromExternal(wait_semaphores, |
| command_buffer->wait_semaphore_count), |
| fidl::VectorView<uint64_t>::FromExternal(signal_semaphores, |
| command_buffer->signal_semaphore_count)); |
| return result; |
| } |
| |
| magma_status_t ExecuteCommand(uint32_t context_id, |
| magma_command_descriptor* descriptor) override { |
| std::vector<fuchsia_gpu_magma::wire::BufferRange> fidl_resources; |
| fidl_resources.reserve(descriptor->resource_count); |
| |
| for (uint32_t i = 0; i < descriptor->resource_count; i++) { |
| fidl_resources.push_back({.buffer_id = descriptor->resources[i].buffer_id, |
| .offset = descriptor->resources[i].offset, |
| .size = descriptor->resources[i].length}); |
| } |
| |
| std::vector<fuchsia_gpu_magma::wire::CommandBuffer> command_buffers; |
| command_buffers.reserve(descriptor->command_buffer_count); |
| for (uint32_t i = 0; i < descriptor->command_buffer_count; i++) { |
| command_buffers.push_back({.resource_index = descriptor->command_buffers[i].resource_index, |
| .start_offset = descriptor->command_buffers[i].start_offset}); |
| } |
| |
| uint64_t* wait_semaphores = descriptor->semaphore_ids; |
| uint64_t* signal_semaphores = descriptor->semaphore_ids + descriptor->wait_semaphore_count; |
| |
| magma_status_t result = client_.ExecuteCommand( |
| context_id, |
| fidl::VectorView<fuchsia_gpu_magma::wire::BufferRange>::FromExternal(fidl_resources), |
| fidl::VectorView<fuchsia_gpu_magma::wire::CommandBuffer>::FromExternal(command_buffers), |
| fidl::VectorView<uint64_t>::FromExternal(wait_semaphores, descriptor->wait_semaphore_count), |
| fidl::VectorView<uint64_t>::FromExternal(signal_semaphores, |
| descriptor->signal_semaphore_count), |
| static_cast<fuchsia_gpu_magma::wire::CommandBufferFlags>(descriptor->flags)); |
| |
| return result; |
| } |
| |
| // Returns the number of commands that will fit within |max_bytes|. |
| static int FitCommands(const size_t max_bytes, const uint64_t num_buffers, |
| const magma_inline_command_buffer* buffers, const uint64_t starting_index, |
| uint64_t* command_bytes, uint32_t* num_semaphores) { |
| int buffer_count = 0; |
| uint64_t bytes_used = 0; |
| *command_bytes = 0; |
| *num_semaphores = 0; |
| while (starting_index + buffer_count < num_buffers && bytes_used < max_bytes) { |
| *command_bytes += buffers[starting_index + buffer_count].size; |
| *num_semaphores += buffers[starting_index + buffer_count].semaphore_count; |
| bytes_used = *command_bytes + *num_semaphores * sizeof(uint64_t); |
| buffer_count++; |
| } |
| if (bytes_used > max_bytes) |
| return (buffer_count - 1); |
| return buffer_count; |
| } |
| |
| magma_status_t ExecuteImmediateCommands(uint32_t context_id, uint64_t num_buffers, |
| magma_inline_command_buffer* buffers, |
| uint64_t* messages_sent_out) override { |
| DLOG("ZirconPlatformConnectionClient: ExecuteImmediateCommands"); |
| uint64_t buffers_sent = 0; |
| uint64_t messages_sent = 0; |
| |
| while (buffers_sent < num_buffers) { |
| // Tally up the number of commands to send in this batch. |
| uint64_t command_bytes = 0; |
| uint32_t num_semaphores = 0; |
| int buffers_to_send = |
| FitCommands(fuchsia_gpu_magma::wire::kMaxImmediateCommandsDataSize, num_buffers, buffers, |
| buffers_sent, &command_bytes, &num_semaphores); |
| |
| // TODO(fxbug.dev/13144): Figure out how to move command and semaphore bytes across the FIDL |
| // interface without copying. |
| std::vector<uint8_t> command_vec; |
| command_vec.reserve(command_bytes); |
| std::vector<uint64_t> semaphore_vec; |
| semaphore_vec.reserve(num_semaphores); |
| |
| for (int i = 0; i < buffers_to_send; ++i) { |
| const auto& buffer = buffers[buffers_sent + i]; |
| const auto buffer_data = static_cast<uint8_t*>(buffer.data); |
| std::copy(buffer_data, buffer_data + buffer.size, std::back_inserter(command_vec)); |
| std::copy(buffer.semaphore_ids, buffer.semaphore_ids + buffer.semaphore_count, |
| std::back_inserter(semaphore_vec)); |
| } |
| magma_status_t result = client_.ExecuteImmediateCommands( |
| context_id, fidl::VectorView<uint8_t>::FromExternal(command_vec), |
| fidl::VectorView<uint64_t>::FromExternal(semaphore_vec)); |
| if (result != MAGMA_STATUS_OK) { |
| return result; |
| } |
| buffers_sent += buffers_to_send; |
| messages_sent += 1; |
| } |
| |
| *messages_sent_out = messages_sent; |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma_status_t GetError() override { |
| DLOG("ZirconPlatformConnectionClient: GetError"); |
| return client_.GetError(); |
| } |
| |
| magma_status_t Flush() override { |
| DLOG("ZirconPlatformConnectionClient: Flush"); |
| return client_.Flush(); |
| } |
| |
| magma_status_t MapBufferGpu(uint64_t buffer_id, uint64_t gpu_va, uint64_t page_offset, |
| uint64_t page_count, uint64_t flags) override { |
| DLOG("ZirconPlatformConnectionClient: MapBufferGpu"); |
| magma_status_t result = |
| client_.MapBufferGpu(buffer_id, gpu_va, page_offset, page_count, |
| static_cast<fuchsia_gpu_magma::wire::MapFlags>(flags)); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET_MSG(result, "failed to write to channel"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma_status_t UnmapBufferGpu(uint64_t buffer_id, uint64_t gpu_va) override { |
| DLOG("ZirconPlatformConnectionClient: UnmapBufferGpu"); |
| magma_status_t result = client_.UnmapBufferGpu(buffer_id, gpu_va); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET_MSG(result, "failed to write to channel"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma_status_t BufferRangeOp(uint64_t buffer_id, uint32_t options, uint64_t start, |
| uint64_t length) override { |
| DLOG("ZirconPlatformConnectionClient::BufferOpRange"); |
| fuchsia_gpu_magma::wire::BufferOp op; |
| switch (options) { |
| case MAGMA_BUFFER_RANGE_OP_DEPOPULATE_TABLES: |
| op = fuchsia_gpu_magma::wire::BufferOp::kDepopulateTables; |
| break; |
| case MAGMA_BUFFER_RANGE_OP_POPULATE_TABLES: |
| op = fuchsia_gpu_magma::wire::BufferOp::kPopulateTables; |
| break; |
| default: |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Invalid buffer op %d", options); |
| } |
| |
| magma_status_t result = client_.BufferRangeOp(buffer_id, op, start, length); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET_MSG(result, "failed to write to channel"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma_status_t EnablePerformanceCounterAccess( |
| std::unique_ptr<magma::PlatformHandle> handle) override { |
| if (!handle) |
| return DRET(MAGMA_STATUS_INVALID_ARGS); |
| |
| zx::event event(static_cast<ZirconPlatformHandle*>(handle.get())->release()); |
| |
| magma_status_t result = client_.EnablePerformanceCounterAccess(std::move(event)); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET_MSG(result, "failed to write to channel"); |
| |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma_status_t IsPerformanceCounterAccessAllowed(bool* enabled_out) override { |
| auto rsp = client_.IsPerformanceCounterAccessAllowed(); |
| if (!rsp.ok()) |
| return DRET_MSG(magma::FromZxStatus(rsp.status()).get(), "failed to write to channel"); |
| |
| *enabled_out = rsp->enabled; |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status EnablePerformanceCounters(uint64_t* counters, uint64_t counter_count) override { |
| magma_status_t result = client_.EnablePerformanceCounters( |
| fidl::VectorView<uint64_t>::FromExternal(counters, counter_count)); |
| |
| if (result != MAGMA_STATUS_OK) |
| return DRET(result); |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status CreatePerformanceCounterBufferPool( |
| std::unique_ptr<PlatformPerfCountPoolClient>* pool_out) override { |
| auto zircon_pool = std::make_unique<ZirconPlatformPerfCountPoolClient>(); |
| zx_status_t status = zircon_pool->Initialize(); |
| if (status != ZX_OK) |
| return magma::FromZxStatus(status).get(); |
| |
| magma_status_t result = client_.CreatePerformanceCounterBufferPool( |
| zircon_pool->pool_id(), zircon_pool->TakeServerEndpoint()); |
| if (result != MAGMA_STATUS_OK) |
| return DRET(result); |
| *pool_out = std::move(zircon_pool); |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status ReleasePerformanceCounterBufferPool(uint64_t pool_id) override { |
| magma_status_t result = client_.ReleasePerformanceCounterBufferPool(pool_id); |
| if (result != MAGMA_STATUS_OK) |
| return DRET(result); |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status AddPerformanceCounterBufferOffsetsToPool(uint64_t pool_id, |
| const magma_buffer_offset* offsets, |
| uint64_t offset_count) override { |
| DASSERT(sizeof(*offsets) == sizeof(fuchsia_gpu_magma::wire::BufferRange)); |
| // The LLCPP FIDL bindings don't take const pointers, but they don't modify the data unless it |
| // contains handles. |
| auto fidl_offsets = const_cast<fuchsia_gpu_magma::wire::BufferRange*>( |
| reinterpret_cast<const fuchsia_gpu_magma::wire::BufferRange*>(offsets)); |
| magma_status_t result = client_.AddPerformanceCounterBufferOffsetsToPool( |
| pool_id, fidl::VectorView<fuchsia_gpu_magma::wire::BufferRange>::FromExternal( |
| fidl_offsets, offset_count)); |
| if (result != MAGMA_STATUS_OK) |
| return DRET(result); |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status RemovePerformanceCounterBufferFromPool(uint64_t pool_id, |
| uint64_t buffer_id) override { |
| magma_status_t result = client_.RemovePerformanceCounterBufferFromPool(pool_id, buffer_id); |
| if (result != MAGMA_STATUS_OK) |
| return DRET(result); |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status DumpPerformanceCounters(uint64_t pool_id, uint32_t trigger_id) override { |
| magma_status_t result = client_.DumpPerformanceCounters(pool_id, trigger_id); |
| if (result != MAGMA_STATUS_OK) |
| return DRET(result); |
| return MAGMA_STATUS_OK; |
| } |
| |
| magma::Status ClearPerformanceCounters(uint64_t* counters, uint64_t counter_count) override { |
| magma_status_t result = client_.ClearPerformanceCounters( |
| fidl::VectorView<uint64_t>::FromExternal(counters, counter_count)); |
| if (result != MAGMA_STATUS_OK) |
| return DRET(result); |
| return MAGMA_STATUS_OK; |
| } |
| |
| uint32_t GetNotificationChannelHandle() override { return notification_channel_.get(); } |
| |
| magma_status_t ReadNotificationChannel(void* buffer, size_t buffer_size, size_t* buffer_size_out, |
| magma_bool_t* more_data_out) override { |
| uint32_t buffer_actual_size; |
| |
| zx_status_t status = notification_channel_.read( |
| 0, buffer, nullptr, magma::to_uint32(buffer_size), 0, &buffer_actual_size, nullptr); |
| switch (status) { |
| case ZX_ERR_SHOULD_WAIT: |
| *buffer_size_out = 0; |
| *more_data_out = false; |
| return MAGMA_STATUS_OK; |
| case ZX_OK: { |
| *buffer_size_out = buffer_actual_size; |
| uint32_t null_buffer_size = 0; |
| status = notification_channel_.read(0, buffer, nullptr, null_buffer_size, 0, |
| &null_buffer_size, nullptr); |
| *more_data_out = (status == ZX_ERR_BUFFER_TOO_SMALL); |
| return MAGMA_STATUS_OK; |
| } |
| case ZX_ERR_PEER_CLOSED: |
| return DRET_MSG(MAGMA_STATUS_CONNECTION_LOST, "notification channel, closed"); |
| case ZX_ERR_BUFFER_TOO_SMALL: |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "buffer_size %zd buffer_actual_size %u", |
| buffer_size, buffer_actual_size); |
| default: |
| return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "notification channel read failed, status %d", |
| status); |
| } |
| } |
| |
| std::pair<uint64_t, uint64_t> GetFlowControlCounts() override { |
| return {client_.inflight_count(), client_.inflight_bytes()}; |
| } |
| |
| private: |
| PrimaryWrapper client_; |
| zx::channel notification_channel_; |
| uint32_t next_context_id_ = 1; |
| }; |
| |
| std::unique_ptr<PlatformConnectionClient> PlatformConnectionClient::Create( |
| uint32_t device_handle, uint32_t device_notification_handle, uint64_t max_inflight_messages, |
| uint64_t max_inflight_bytes) { |
| return std::unique_ptr<ZirconPlatformConnectionClient>(new ZirconPlatformConnectionClient( |
| zx::channel(device_handle), zx::channel(device_notification_handle), max_inflight_messages, |
| max_inflight_bytes)); |
| } |
| |
| // static |
| std::unique_ptr<magma::PlatformHandle> PlatformConnectionClient::RetrieveAccessToken( |
| magma::PlatformHandle* channel) { |
| if (!channel) |
| return DRETP(nullptr, "No channel"); |
| auto rsp = fidl::WireCall( |
| fidl::UnownedClientEnd<fuchsia_gpu_magma::PerformanceCounterAccess>( |
| zx::unowned_channel(static_cast<const ZirconPlatformHandle*>(channel)->get()))) |
| ->GetPerformanceCountToken(); |
| if (!rsp.ok()) { |
| return DRETP(nullptr, "GetPerformanceCountToken failed"); |
| } |
| if (!rsp->access_token) { |
| return DRETP(nullptr, "GetPerformanceCountToken retrieved no event."); |
| } |
| return magma::PlatformHandle::Create(rsp->access_token.release()); |
| } |
| |
| } // namespace magma |