blob: 1d78f850f5f0c8d7139ddaee712f99dac4be037c [file] [log] [blame]
// Copyright 2014 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 "mojo/gles2/command_buffer_client_impl.h"
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <utility>
#include "base/logging.h"
#include "base/process/process_handle.h"
#include "base/threading/thread_restrictions.h"
#include "components/mus/gles2/command_buffer_type_conversions.h"
#include "components/mus/gles2/mojo_buffer_backing.h"
#include "components/mus/gles2/mojo_gpu_memory_buffer.h"
#include "gpu/command_buffer/client/gpu_control_client.h"
#include "gpu/command_buffer/common/command_buffer_id.h"
#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "mojo/platform_handle/platform_handle_functions.h"
namespace gles2 {
namespace {
bool CreateMapAndDupSharedBuffer(size_t size,
void** memory,
mojo::ScopedSharedBufferHandle* handle,
mojo::ScopedSharedBufferHandle* duped) {
MojoResult result = mojo::CreateSharedBuffer(NULL, size, handle);
if (result != MOJO_RESULT_OK)
return false;
DCHECK(handle->is_valid());
result = mojo::DuplicateBuffer(handle->get(), NULL, duped);
if (result != MOJO_RESULT_OK)
return false;
DCHECK(duped->is_valid());
result = mojo::MapBuffer(
handle->get(), 0, size, memory, MOJO_MAP_BUFFER_FLAG_NONE);
if (result != MOJO_RESULT_OK)
return false;
DCHECK(*memory);
return true;
}
void MakeProgressCallback(
gpu::CommandBuffer::State* output,
const gpu::CommandBuffer::State& input) {
*output = input;
}
void InitializeCallback(mus::mojom::CommandBufferInitializeResultPtr* output,
mus::mojom::CommandBufferInitializeResultPtr input) {
*output = std::move(input);
}
} // namespace
CommandBufferClientImpl::CommandBufferClientImpl(
const std::vector<int32_t>& attribs,
mojo::ScopedMessagePipeHandle command_buffer_handle)
: gpu_control_client_(nullptr),
destroyed_(false),
attribs_(attribs),
client_binding_(this),
command_buffer_id_(),
shared_state_(NULL),
last_put_offset_(-1),
next_transfer_buffer_id_(0),
next_image_id_(0),
next_fence_sync_release_(1),
flushed_fence_sync_release_(0) {
command_buffer_.Bind(mojo::InterfacePtrInfo<mus::mojom::CommandBuffer>(
std::move(command_buffer_handle), 0u));
command_buffer_.set_connection_error_handler(
[this]() { Destroyed(gpu::error::kUnknown, gpu::error::kLostContext); });
}
CommandBufferClientImpl::~CommandBufferClientImpl() {}
bool CommandBufferClientImpl::Initialize() {
const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState);
void* memory = NULL;
mojo::ScopedSharedBufferHandle duped;
bool result = CreateMapAndDupSharedBuffer(
kSharedStateSize, &memory, &shared_state_handle_, &duped);
if (!result)
return false;
shared_state_ = static_cast<gpu::CommandBufferSharedState*>(memory);
shared_state()->Initialize();
mus::mojom::CommandBufferClientPtr client_ptr;
client_binding_.Bind(GetProxy(&client_ptr));
mus::mojom::CommandBufferInitializeResultPtr initialize_result;
command_buffer_->Initialize(
std::move(client_ptr), std::move(duped),
mojo::Array<int32_t>::From(attribs_),
base::Bind(&InitializeCallback, &initialize_result));
base::ThreadRestrictions::ScopedAllowWait wait;
if (!command_buffer_.WaitForIncomingResponse()) {
VLOG(1) << "Channel encountered error while creating command buffer.";
return false;
}
if (!initialize_result) {
VLOG(1) << "Command buffer cannot be initialized successfully.";
return false;
}
DCHECK_EQ(gpu::CommandBufferNamespace::MOJO,
initialize_result->command_buffer_namespace);
command_buffer_id_ = gpu::CommandBufferId::FromUnsafeValue(
initialize_result->command_buffer_id);
capabilities_ = initialize_result->capabilities;
return true;
}
gpu::CommandBuffer::State CommandBufferClientImpl::GetLastState() {
return last_state_;
}
int32_t CommandBufferClientImpl::GetLastToken() {
TryUpdateState();
return last_state_.token;
}
void CommandBufferClientImpl::Flush(int32_t put_offset) {
if (last_put_offset_ == put_offset)
return;
last_put_offset_ = put_offset;
command_buffer_->Flush(put_offset);
flushed_fence_sync_release_ = next_fence_sync_release_ - 1;
}
void CommandBufferClientImpl::OrderingBarrier(int32_t put_offset) {
// TODO(jamesr): Implement this more efficiently.
Flush(put_offset);
}
void CommandBufferClientImpl::WaitForTokenInRange(int32_t start, int32_t end) {
TryUpdateState();
while (!InRange(start, end, last_state_.token) &&
last_state_.error == gpu::error::kNoError) {
MakeProgressAndUpdateState();
}
}
void CommandBufferClientImpl::WaitForGetOffsetInRange(int32_t start,
int32_t end) {
TryUpdateState();
while (!InRange(start, end, last_state_.get_offset) &&
last_state_.error == gpu::error::kNoError) {
MakeProgressAndUpdateState();
}
}
void CommandBufferClientImpl::SetGetBuffer(int32_t shm_id) {
command_buffer_->SetGetBuffer(shm_id);
last_put_offset_ = -1;
}
scoped_refptr<gpu::Buffer> CommandBufferClientImpl::CreateTransferBuffer(
size_t size,
int32_t* id) {
if (size >= std::numeric_limits<uint32_t>::max())
return NULL;
void* memory = NULL;
mojo::ScopedSharedBufferHandle handle;
mojo::ScopedSharedBufferHandle duped;
if (!CreateMapAndDupSharedBuffer(size, &memory, &handle, &duped)) {
if (last_state_.error == gpu::error::kNoError)
last_state_.error = gpu::error::kLostContext;
return NULL;
}
*id = ++next_transfer_buffer_id_;
command_buffer_->RegisterTransferBuffer(*id, std::move(duped),
static_cast<uint32_t>(size));
std::unique_ptr<gpu::BufferBacking> backing(
new mus::MojoBufferBacking(std::move(handle), memory, size));
scoped_refptr<gpu::Buffer> buffer(new gpu::Buffer(std::move(backing)));
return buffer;
}
void CommandBufferClientImpl::DestroyTransferBuffer(int32_t id) {
command_buffer_->DestroyTransferBuffer(id);
}
void CommandBufferClientImpl::SetGpuControlClient(gpu::GpuControlClient* c) {
gpu_control_client_ = c;
}
gpu::Capabilities CommandBufferClientImpl::GetCapabilities() {
return capabilities_;
}
int32_t CommandBufferClientImpl::CreateImage(ClientBuffer buffer,
size_t width,
size_t height,
unsigned internalformat) {
int32_t new_id = ++next_image_id_;
mojo::SizePtr size = mojo::Size::New();
size->width = static_cast<int32_t>(width);
size->height = static_cast<int32_t>(height);
mus::MojoGpuMemoryBufferImpl* gpu_memory_buffer =
mus::MojoGpuMemoryBufferImpl::FromClientBuffer(buffer);
gfx::GpuMemoryBufferHandle handle = gpu_memory_buffer->GetHandle();
bool requires_sync_point = false;
if (handle.type != gfx::SHARED_MEMORY_BUFFER) {
requires_sync_point = true;
NOTIMPLEMENTED();
return -1;
}
base::SharedMemoryHandle dupd_handle =
base::SharedMemory::DuplicateHandle(handle.handle);
#if defined(OS_WIN)
HANDLE platform_handle = dupd_handle.GetHandle();
#else
int platform_handle = dupd_handle.fd;
#endif
MojoHandle mojo_handle = MOJO_HANDLE_INVALID;
MojoResult create_result = MojoCreatePlatformHandleWrapper(
platform_handle, &mojo_handle);
if (create_result != MOJO_RESULT_OK) {
NOTIMPLEMENTED();
return -1;
}
mojo::ScopedHandle scoped_handle;
scoped_handle.reset(mojo::Handle(mojo_handle));
command_buffer_->CreateImage(
new_id, std::move(scoped_handle), handle.type, std::move(size),
static_cast<int32_t>(gpu_memory_buffer->GetFormat()), internalformat);
if (requires_sync_point) {
NOTIMPLEMENTED();
// TODO(jam): need to support this if we support types other than
// SHARED_MEMORY_BUFFER.
//gpu_memory_buffer_manager->SetDestructionSyncPoint(gpu_memory_buffer,
// InsertSyncPoint());
}
return new_id;
}
void CommandBufferClientImpl::DestroyImage(int32_t id) {
command_buffer_->DestroyImage(id);
}
int32_t CommandBufferClientImpl::CreateGpuMemoryBufferImage(
size_t width,
size_t height,
unsigned internalformat,
unsigned usage) {
std::unique_ptr<gfx::GpuMemoryBuffer> buffer(
mus::MojoGpuMemoryBufferImpl::Create(
gfx::Size(static_cast<int>(width), static_cast<int>(height)),
gpu::DefaultBufferFormatForImageFormat(internalformat),
gfx::BufferUsage::SCANOUT));
if (!buffer)
return -1;
return CreateImage(buffer->AsClientBuffer(), width, height, internalformat);
}
void CommandBufferClientImpl::SignalQuery(uint32_t query,
const base::Closure& callback) {
// TODO(piman)
NOTIMPLEMENTED();
}
void CommandBufferClientImpl::Destroyed(int32_t lost_reason, int32_t error) {
if (destroyed_)
return;
last_state_.context_lost_reason =
static_cast<gpu::error::ContextLostReason>(lost_reason);
last_state_.error = static_cast<gpu::error::Error>(error);
if (gpu_control_client_)
gpu_control_client_->OnGpuControlLostContext();
destroyed_ = true;
}
void CommandBufferClientImpl::SignalAck(uint32_t id) {
}
void CommandBufferClientImpl::SwapBuffersCompleted(int32_t result) {
}
void CommandBufferClientImpl::UpdateState(
const gpu::CommandBuffer::State& state) {
}
void CommandBufferClientImpl::UpdateVSyncParameters(int64_t timebase,
int64_t interval) {
}
void CommandBufferClientImpl::TryUpdateState() {
if (last_state_.error == gpu::error::kNoError)
shared_state()->Read(&last_state_);
}
void CommandBufferClientImpl::MakeProgressAndUpdateState() {
gpu::CommandBuffer::State state;
command_buffer_->MakeProgress(
last_state_.get_offset,
base::Bind(&MakeProgressCallback, &state));
base::ThreadRestrictions::ScopedAllowWait wait;
if (!command_buffer_.WaitForIncomingResponse()) {
VLOG(1) << "Channel encountered error while waiting for command buffer.";
// TODO(piman): is it ok for this to re-enter?
Destroyed(gpu::error::kUnknown, gpu::error::kLostContext);
return;
}
if (state.generation - last_state_.generation < 0x80000000U)
last_state_ = state;
}
void CommandBufferClientImpl::SetLock(base::Lock* lock) {
}
void CommandBufferClientImpl::EnsureWorkVisible() {
// This is only relevant for out-of-process command buffers.
}
gpu::CommandBufferNamespace CommandBufferClientImpl::GetNamespaceID() const {
return gpu::CommandBufferNamespace::MOJO;
}
gpu::CommandBufferId CommandBufferClientImpl::GetCommandBufferID() const {
return command_buffer_id_;
}
int32_t CommandBufferClientImpl::GetExtraCommandBufferData() const {
return 0;
}
uint64_t CommandBufferClientImpl::GenerateFenceSyncRelease() {
return next_fence_sync_release_++;
}
bool CommandBufferClientImpl::IsFenceSyncRelease(uint64_t release) {
return release != 0 && release < next_fence_sync_release_;
}
bool CommandBufferClientImpl::IsFenceSyncFlushed(uint64_t release) {
return release != 0 && release <= flushed_fence_sync_release_;
}
bool CommandBufferClientImpl::IsFenceSyncFlushReceived(uint64_t release) {
return IsFenceSyncFlushed(release);
}
void CommandBufferClientImpl::SignalSyncToken(const gpu::SyncToken& sync_token,
const base::Closure& callback) {
// TODO(dyen)
NOTIMPLEMENTED();
}
bool CommandBufferClientImpl::CanWaitUnverifiedSyncToken(
const gpu::SyncToken* sync_token) {
// Right now, MOJO_LOCAL is only used by trusted code, so it is safe to wait
// on a sync token in MOJO_LOCAL command buffer.
if (sync_token->namespace_id() == gpu::CommandBufferNamespace::MOJO_LOCAL)
return true;
// It is also safe to wait on the same context.
if (sync_token->namespace_id() == gpu::CommandBufferNamespace::MOJO &&
sync_token->command_buffer_id() == GetCommandBufferID())
return true;
return false;
}
} // namespace gles2