Chromium Code Reviews| Index: content/common/gpu/client/gpu_memory_buffer_impl_intel_drm.cc |
| diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_intel_drm.cc b/content/common/gpu/client/gpu_memory_buffer_impl_intel_drm.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5be371be7486905c744b0f1b751ddde23566f7d4 |
| --- /dev/null |
| +++ b/content/common/gpu/client/gpu_memory_buffer_impl_intel_drm.cc |
| @@ -0,0 +1,291 @@ |
| +// 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 "content/common/gpu/client/gpu_memory_buffer_impl_intel_drm.h" |
| + |
| +extern "C" { |
| +#include <fcntl.h> |
| +#include <libdrm/i915_drm.h> |
| +#include <libdrm/intel_bufmgr.h> |
| +#include <xf86drm.h> |
| +#if defined(USE_X11) |
| +#include <X11/Xlib.h> |
| +#include <X11/Xlibint.h> |
| +#include <X11/extensions/Xext.h> |
| +#include <X11/extensions/extutil.h> |
| +#include <X11/extensions/dri2proto.h> |
| +// X11/Xlibint.h define min/max macros which will conflict with std::min/max |
| +// unless we undefine them here. |
| +#undef min |
| +#undef max |
| +#endif |
| +} |
| + |
| +#include "base/debug/trace_event.h" |
| +#include "base/files/scoped_file.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/logging.h" |
| +#include "base/strings/stringprintf.h" |
| +#include "base/threading/thread_checker.h" |
| +#include "ui/gl/gl_bindings.h" |
| + |
| +#if defined(USE_X11) |
| +#include "ui/gfx/x/x11_types.h" |
| +#endif |
| + |
| +namespace content { |
| +namespace { |
| + |
| +#if defined(USE_X11) |
| +static char g_dri2_extension_name[] = DRI2_NAME; |
| +static XExtensionInfo* g_dri2_info; |
| +static XExtensionHooks g_dri2_extension_hooks = {NULL, // create_gc |
| + NULL, // copy_gc |
| + NULL, // flush_gc |
| + NULL, // free_gc |
| + NULL, // create_font |
| + NULL, // free_font |
| + NULL, // close_display |
| + NULL, // wire_to_event |
| + NULL, // event_to_wire |
| + NULL, // error |
| + NULL, // error_string |
| +}; |
| +static XEXT_GENERATE_FIND_DISPLAY(DRI2FindDisplay, |
| + g_dri2_info, |
| + g_dri2_extension_name, |
| + &g_dri2_extension_hooks, |
| + 0, |
| + NULL); |
| + |
| +bool X11DRI2Authenticate(drm_magic_t magic) { |
| + Display* dpy = gfx::GetXDisplay(); |
| + XExtDisplayInfo* info = DRI2FindDisplay(dpy); |
| + XextCheckExtension(dpy, info, g_dri2_extension_name, false); |
| + LockDisplay(dpy); |
| + xDRI2AuthenticateReq* req; |
| + GetReq(DRI2Authenticate, req); |
| + req->reqType = info->codes->major_opcode; |
| + req->dri2ReqType = X_DRI2Authenticate; |
| + req->window = DefaultRootWindow(dpy); |
| + req->magic = magic; |
| + xDRI2AuthenticateReply rep; |
| + int status = _XReply(dpy, reinterpret_cast<xReply*>(&rep), 0, xFalse); |
| + UnlockDisplay(dpy); |
| + SyncHandle(); |
| + return status && rep.authenticated; |
| +} |
| +#endif // USE_X11 |
| + |
| +const int kDeviceId = 0; |
| +const int kBatchSize = 2048; |
| + |
| +int WriteEnable(gfx::GpuMemoryBuffer::AccessMode mode) { |
| + switch (mode) { |
| + case gfx::GpuMemoryBuffer::READ_ONLY: |
| + return 0; |
| + case gfx::GpuMemoryBuffer::WRITE_ONLY: |
| + case gfx::GpuMemoryBuffer::READ_WRITE: |
| + return 1; |
| + } |
| + |
| + NOTREACHED(); |
| + return 0; |
| +} |
| + |
| +void ExportBufferObject(drm_intel_bo* buffer_object, |
| + base::SharedMemoryHandle* handle) { |
| + int prime_fd = -1; |
| + drm_intel_bo_gem_export_to_prime(buffer_object, &prime_fd); |
| + *handle = base::FileDescriptor(prime_fd, true); |
| +} |
| + |
| +uint64 BufferObjectPitch(gfx::Size size, unsigned internalformat) { |
| + return static_cast<uint64>(size.width()) * |
| + GpuMemoryBufferImpl::BytesPerPixel(internalformat); |
| +} |
| + |
| +class BufferObjectFactory { |
| + public: |
| + BufferObjectFactory() : buffer_manager_(NULL) { |
| + std::string dev_name( |
| + base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, kDeviceId)); |
| + base::ScopedFD dri_fd(open(dev_name.c_str(), O_RDWR)); |
| + if (dri_fd.get() < 0) { |
| + LOG(ERROR) << "Failed to open: " << dev_name; |
| + return; |
| + } |
| + |
| +#if defined(USE_X11) |
| + drm_magic_t magic; |
| + if (drmGetMagic(dri_fd.get(), &magic)) |
| + return; |
| + |
| + if (!X11DRI2Authenticate(magic)) { |
| + LOG(ERROR) << "DRI2Authenticate failed."; |
| + return; |
| + } |
| +#endif |
| + |
| + buffer_manager_ = drm_intel_bufmgr_gem_init(dri_fd.get(), kBatchSize); |
| + if (!buffer_manager_) |
| + LOG(ERROR) << "drm_intel_bufmgr_gem_init failed."; |
| + } |
| + ~BufferObjectFactory() { |
| + if (buffer_manager_) |
| + drm_intel_bufmgr_destroy(buffer_manager_); |
| + } |
| + |
| + drm_intel_bo* CreateBufferObject(gfx::Size size, unsigned internalformat) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (!buffer_manager_) |
| + return NULL; |
| + |
| + uint64 expected_pitch = BufferObjectPitch(size, internalformat); |
| + if (expected_pitch > std::numeric_limits<uint32>::max()) |
| + return NULL; |
| + |
| + uint32_t tiling_mode = I915_TILING_NONE; |
| + unsigned long pitch = 0; |
| + drm_intel_bo* buffer_object = drm_intel_bo_alloc_tiled( |
| + buffer_manager_, |
| + "ChromiumGpuMemoryBuffer", |
| + size.width(), |
| + size.height(), |
| + GpuMemoryBufferImpl::BytesPerPixel(internalformat), |
| + &tiling_mode, |
| + &pitch, |
| + 0); |
| + if (!buffer_object) { |
| + LOG(ERROR) << "drm_intel_bo_alloc_tiled failed."; |
| + return NULL; |
| + } |
| + |
| + // |pitch| must match what we expect. |
| + if (static_cast<uint64>(pitch) != expected_pitch) { |
| + LOG(ERROR) << "Unexpected pitch: " << pitch << " vs " << expected_pitch; |
| + drm_intel_bo_unreference(buffer_object); |
| + return NULL; |
| + } |
| + |
| + return buffer_object; |
| + } |
| + |
| + drm_intel_bo* CreateBufferObjectFromPrimeFd(gfx::Size size, |
| + unsigned internalformat, |
| + int prime_fd) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (!buffer_manager_) |
| + return NULL; |
| + |
| + uint64 pitch = BufferObjectPitch(size, internalformat); |
| + if (pitch > std::numeric_limits<uint32>::max()) |
| + return NULL; |
| + |
| + uint64 buffer_size = pitch * size.height(); |
| + if (buffer_size > std::numeric_limits<int32>::max()) |
|
fjhenigman
2014/04/09 16:33:52
should be uint32
reveman
2014/04/09 18:22:22
Fixed by using base::IsValueInRangeForNumericType
|
| + return NULL; |
| + |
| + return drm_intel_bo_gem_create_from_prime( |
| + buffer_manager_, prime_fd, buffer_size); |
| + } |
| + |
| + private: |
| + base::ThreadChecker thread_checker_; |
| + drm_intel_bufmgr* buffer_manager_; |
| +}; |
| +base::LazyInstance<BufferObjectFactory>::Leaky g_buffer_object_factory = |
| + LAZY_INSTANCE_INITIALIZER; |
| + |
| +} // namespace |
| + |
| +GpuMemoryBufferImplIntelDRM::GpuMemoryBufferImplIntelDRM( |
| + gfx::Size size, |
| + unsigned internalformat) |
| + : GpuMemoryBufferImpl(size, internalformat), buffer_object_(NULL) {} |
| + |
| +GpuMemoryBufferImplIntelDRM::~GpuMemoryBufferImplIntelDRM() { |
| + if (buffer_object_) |
| + drm_intel_bo_unreference(buffer_object_); |
| +} |
| + |
| +// static |
| +bool GpuMemoryBufferImplIntelDRM::IsFormatSupported(unsigned internalformat) { |
| + switch (internalformat) { |
| + case GL_BGRA8_EXT: |
| + return true; |
| + default: |
| + return false; |
| + } |
| +} |
| + |
| +// static |
| +drm_intel_bo* GpuMemoryBufferImplIntelDRM::CreateBufferObject( |
| + gfx::Size size, |
| + unsigned internalformat) { |
| + return g_buffer_object_factory.Pointer()->CreateBufferObject(size, |
| + internalformat); |
| +} |
| + |
| +// static |
| +bool GpuMemoryBufferImplIntelDRM::AllocateBufferObject( |
| + gfx::Size size, |
| + unsigned internalformat, |
| + base::SharedMemoryHandle* handle) { |
| + drm_intel_bo* buffer_object = CreateBufferObject(size, internalformat); |
| + if (!buffer_object) |
| + return false; |
| + |
| + ExportBufferObject(buffer_object, handle); |
| + drm_intel_bo_unreference(buffer_object); |
| + return true; |
| +} |
| + |
| +bool GpuMemoryBufferImplIntelDRM::Initialize( |
| + gfx::GpuMemoryBufferHandle handle) { |
| + TRACE_EVENT0("gpu", "GpuMemoryBufferImplIntelDRM::Initialize"); |
| + |
| + DCHECK(!buffer_object_); |
| + buffer_object_ = |
| + g_buffer_object_factory.Pointer()->CreateBufferObjectFromPrimeFd( |
| + size_, internalformat_, handle.handle.fd); |
| + return !!buffer_object_; |
| +} |
| + |
| +bool GpuMemoryBufferImplIntelDRM::InitializeFromBufferObject( |
| + drm_intel_bo* buffer_object) { |
| + DCHECK(!buffer_object_); |
| + buffer_object_ = buffer_object; |
| + return true; |
| +} |
| + |
| +void* GpuMemoryBufferImplIntelDRM::Map(AccessMode mode) { |
| + TRACE_EVENT0("gpu", "GpuMemoryBufferImplIntelDRM::Map"); |
| + |
| + DCHECK(!mapped_); |
| + DCHECK(buffer_object_); |
| + drm_intel_bo_map(buffer_object_, WriteEnable(mode)); |
| + mapped_ = true; |
| + return buffer_object_->virt; |
| +} |
| + |
| +void GpuMemoryBufferImplIntelDRM::Unmap() { |
| + TRACE_EVENT0("gpu", "GpuMemoryBufferImplIntelDRM::Unmap"); |
| + |
| + DCHECK(mapped_); |
| + drm_intel_bo_unmap(buffer_object_); |
| + mapped_ = false; |
| +} |
| + |
| +gfx::GpuMemoryBufferHandle GpuMemoryBufferImplIntelDRM::GetHandle() const { |
| + gfx::GpuMemoryBufferHandle handle; |
| + handle.type = gfx::INTEL_DRM_BUFFER; |
| + DCHECK(buffer_object_); |
| + ExportBufferObject(buffer_object_, &handle.handle); |
| + return handle; |
| +} |
| + |
| +} // namespace content |