<commit_before><commit_msg>Resolves: fdo#65501 ensure configured backup dir exists before using it<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2012 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 "webkit/plugins/ppapi/ppb_image_data_impl.h"

#include <algorithm>
#include <limits>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "skia/ext/platform_canvas.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/ppb_image_data.h"
#include "ppapi/c/trusted/ppb_image_data_trusted.h"
#include "ppapi/thunk/thunk.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "webkit/plugins/ppapi/common.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/resource_helper.h"

using ::ppapi::thunk::PPB_ImageData_API;

namespace webkit {
namespace ppapi {

PPB_ImageData_Impl::PPB_ImageData_Impl(PP_Instance instance,
                                       ImageDataType type)
    : Resource(::ppapi::OBJECT_IS_IMPL, instance),
      format_(PP_IMAGEDATAFORMAT_BGRA_PREMUL),
      width_(0),
      height_(0) {
  switch (type) {
    case PLATFORM:
      backend_.reset(new ImageDataPlatformBackend);
      return;
    case NACL:
      backend_.reset(new ImageDataNaClBackend);
      return;
    // No default: so that we get a compiler warning if any types are added.
  }
  NOTREACHED();
}

PPB_ImageData_Impl::~PPB_ImageData_Impl() {
}

bool PPB_ImageData_Impl::Init(PP_ImageDataFormat format,
                              int width, int height,
                              bool init_to_zero) {
  // TODO(brettw) this should be called only on the main thread!
  if (!IsImageDataFormatSupported(format))
    return false;  // Only support this one format for now.
  if (width <= 0 || height <= 0)
    return false;
  if (static_cast<int64>(width) * static_cast<int64>(height) * 4 >=
      std::numeric_limits<int32>::max())
    return false;  // Prevent overflow of signed 32-bit ints.

  format_ = format;
  width_ = width;
  height_ = height;
  return backend_->Init(this, format, width, height, init_to_zero);
}

// static
PP_Resource PPB_ImageData_Impl::CreatePlatform(PP_Instance instance,
                                               PP_ImageDataFormat format,
                                               const PP_Size& size,
                                               PP_Bool init_to_zero) {
  scoped_refptr<PPB_ImageData_Impl>
      data(new PPB_ImageData_Impl(instance, PLATFORM));
  if (!data->Init(format, size.width, size.height, !!init_to_zero))
    return 0;
  return data->GetReference();
}

// static
PP_Resource PPB_ImageData_Impl::CreateNaCl(PP_Instance instance,
                                               PP_ImageDataFormat format,
                                               const PP_Size& size,
                                               PP_Bool init_to_zero) {
  scoped_refptr<PPB_ImageData_Impl>
      data(new PPB_ImageData_Impl(instance, NACL));
  if (!data->Init(format, size.width, size.height, !!init_to_zero))
    return 0;
  return data->GetReference();
}

PPB_ImageData_API* PPB_ImageData_Impl::AsPPB_ImageData_API() {
  return this;
}

bool PPB_ImageData_Impl::IsMapped() const {
  return backend_->IsMapped();
}

PluginDelegate::PlatformImage2D* PPB_ImageData_Impl::PlatformImage() const {
  return backend_->PlatformImage();
}

PP_Bool PPB_ImageData_Impl::Describe(PP_ImageDataDesc* desc) {
  desc->format = format_;
  desc->size.width = width_;
  desc->size.height = height_;
  desc->stride = width_ * 4;
  return PP_TRUE;
}

void* PPB_ImageData_Impl::Map() {
  return backend_->Map();
}

void PPB_ImageData_Impl::Unmap() {
  backend_->Unmap();
}

int32_t PPB_ImageData_Impl::GetSharedMemory(int* handle, uint32_t* byte_count) {
  return backend_->GetSharedMemory(handle, byte_count);
}

skia::PlatformCanvas* PPB_ImageData_Impl::GetPlatformCanvas() {
  return backend_->GetPlatformCanvas();
}

SkCanvas* PPB_ImageData_Impl::GetCanvas() {
  return backend_->GetCanvas();
}

const SkBitmap* PPB_ImageData_Impl::GetMappedBitmap() const {
  return backend_->GetMappedBitmap();
}

// ImageDataPlatformBackend --------------------------------------------------

ImageDataPlatformBackend::ImageDataPlatformBackend() {
}

ImageDataPlatformBackend::~ImageDataPlatformBackend() {
}

bool ImageDataPlatformBackend::Init(PPB_ImageData_Impl* impl,
                                     PP_ImageDataFormat format,
                                     int width, int height,
                                     bool init_to_zero) {
  PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(impl);
  if (!plugin_delegate)
    return false;

  // TODO(brettw) use init_to_zero when we implement caching.
  platform_image_.reset(plugin_delegate->CreateImage2D(width, height));
  return !!platform_image_.get();
}

bool ImageDataPlatformBackend::IsMapped() const {
  return !!mapped_canvas_.get();
}

PluginDelegate::PlatformImage2D*
ImageDataPlatformBackend::PlatformImage() const {
  return platform_image_.get();
}

void* ImageDataPlatformBackend::Map() {
  if (!mapped_canvas_.get()) {
    mapped_canvas_.reset(platform_image_->Map());
    if (!mapped_canvas_.get())
      return NULL;
  }
  const SkBitmap& bitmap =
      skia::GetTopDevice(*mapped_canvas_)->accessBitmap(true);

  // Our platform bitmaps are set to opaque by default, which we don't want.
  const_cast<SkBitmap&>(bitmap).setIsOpaque(false);

  bitmap.lockPixels();
  return bitmap.getAddr32(0, 0);
}

void ImageDataPlatformBackend::Unmap() {
  // This is currently unimplemented, which is OK. The data will just always
  // be around once it's mapped. Chrome's TransportDIB isn't currently
  // unmappable without freeing it, but this may be something we want to support
  // in the future to save some memory.
}

int32_t ImageDataPlatformBackend::GetSharedMemory(int* handle,
                                                   uint32_t* byte_count) {
  *handle = platform_image_->GetSharedMemoryHandle(byte_count);
  return PP_OK;
}

skia::PlatformCanvas* ImageDataPlatformBackend::GetPlatformCanvas() {
  return mapped_canvas_.get();
}

SkCanvas* ImageDataPlatformBackend::GetCanvas() {
  return mapped_canvas_.get();
}

const SkBitmap* ImageDataPlatformBackend::GetMappedBitmap() const {
  if (!mapped_canvas_.get())
    return NULL;
  return &skia::GetTopDevice(*mapped_canvas_)->accessBitmap(false);
}

// ImageDataNaClBackend ------------------------------------------------------

ImageDataNaClBackend::ImageDataNaClBackend()
    : map_count_(0) {
}

ImageDataNaClBackend::~ImageDataNaClBackend() {
}

bool ImageDataNaClBackend::Init(PPB_ImageData_Impl* impl,
                                 PP_ImageDataFormat format,
                                 int width, int height,
                                 bool init_to_zero) {
  skia_bitmap_.setConfig(SkBitmap::kARGB_8888_Config,
                         impl->width(), impl->height());
  PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(impl);
  if (!plugin_delegate)
    return false;
  shared_memory_.reset(
      plugin_delegate->CreateAnonymousSharedMemory(skia_bitmap_.getSize()));
  return !!shared_memory_.get();
}

bool ImageDataNaClBackend::IsMapped() const {
  return map_count_ > 0;
}

PluginDelegate::PlatformImage2D* ImageDataNaClBackend::PlatformImage() const {
  return NULL;
}

void* ImageDataNaClBackend::Map() {
  DCHECK(shared_memory_.get());
  if (map_count_++ == 0) {
    shared_memory_->Map(skia_bitmap_.getSize());
    skia_bitmap_.setPixels(shared_memory_->memory());
    // Our platform bitmaps are set to opaque by default, which we don't want.
    skia_bitmap_.setIsOpaque(false);
    skia_canvas_.reset(new SkCanvas(skia_bitmap_));
    return skia_bitmap_.getAddr32(0, 0);
  }
  return shared_memory_->memory();
}

void ImageDataNaClBackend::Unmap() {
  if (--map_count_ == 0)
    shared_memory_->Unmap();
}

int32_t ImageDataNaClBackend::GetSharedMemory(int* handle,
                                                uint32_t* byte_count) {
  *byte_count = skia_bitmap_.getSize();
#if defined(OS_POSIX)
  *handle = shared_memory_->handle().fd;
#elif defined(OS_WIN)
  *handle = reinterpret_cast<int>(shared_memory_->handle());
#else
#error "Platform not supported."
#endif
  return PP_OK;
}

skia::PlatformCanvas* ImageDataNaClBackend::GetPlatformCanvas() {
  return NULL;
}

SkCanvas* ImageDataNaClBackend::GetCanvas() {
  if (!IsMapped())
    return NULL;
  return skia_canvas_.get();
}

const SkBitmap* ImageDataNaClBackend::GetMappedBitmap() const {
  if (!IsMapped())
    return NULL;
  return &skia_bitmap_;
}

}  // namespace ppapi
}  // namespace webkit

<commit_msg>Security fix: integer overflow on checking image size<commit_after>// Copyright (c) 2012 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 "webkit/plugins/ppapi/ppb_image_data_impl.h"

#include <algorithm>
#include <limits>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "skia/ext/platform_canvas.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/ppb_image_data.h"
#include "ppapi/c/trusted/ppb_image_data_trusted.h"
#include "ppapi/thunk/thunk.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "webkit/plugins/ppapi/common.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/resource_helper.h"

using ::ppapi::thunk::PPB_ImageData_API;

namespace webkit {
namespace ppapi {

PPB_ImageData_Impl::PPB_ImageData_Impl(PP_Instance instance,
                                       ImageDataType type)
    : Resource(::ppapi::OBJECT_IS_IMPL, instance),
      format_(PP_IMAGEDATAFORMAT_BGRA_PREMUL),
      width_(0),
      height_(0) {
  switch (type) {
    case PLATFORM:
      backend_.reset(new ImageDataPlatformBackend);
      return;
    case NACL:
      backend_.reset(new ImageDataNaClBackend);
      return;
    // No default: so that we get a compiler warning if any types are added.
  }
  NOTREACHED();
}

PPB_ImageData_Impl::~PPB_ImageData_Impl() {
}

bool PPB_ImageData_Impl::Init(PP_ImageDataFormat format,
                              int width, int height,
                              bool init_to_zero) {
  // TODO(brettw) this should be called only on the main thread!
  if (!IsImageDataFormatSupported(format))
    return false;  // Only support this one format for now.
  if (width <= 0 || height <= 0)
    return false;
  if (static_cast<int64>(width) * static_cast<int64>(height) >=
      std::numeric_limits<int32>::max() / 4)
    return false;  // Prevent overflow of signed 32-bit ints.

  format_ = format;
  width_ = width;
  height_ = height;
  return backend_->Init(this, format, width, height, init_to_zero);
}

// static
PP_Resource PPB_ImageData_Impl::CreatePlatform(PP_Instance instance,
                                               PP_ImageDataFormat format,
                                               const PP_Size& size,
                                               PP_Bool init_to_zero) {
  scoped_refptr<PPB_ImageData_Impl>
      data(new PPB_ImageData_Impl(instance, PLATFORM));
  if (!data->Init(format, size.width, size.height, !!init_to_zero))
    return 0;
  return data->GetReference();
}

// static
PP_Resource PPB_ImageData_Impl::CreateNaCl(PP_Instance instance,
                                               PP_ImageDataFormat format,
                                               const PP_Size& size,
                                               PP_Bool init_to_zero) {
  scoped_refptr<PPB_ImageData_Impl>
      data(new PPB_ImageData_Impl(instance, NACL));
  if (!data->Init(format, size.width, size.height, !!init_to_zero))
    return 0;
  return data->GetReference();
}

PPB_ImageData_API* PPB_ImageData_Impl::AsPPB_ImageData_API() {
  return this;
}

bool PPB_ImageData_Impl::IsMapped() const {
  return backend_->IsMapped();
}

PluginDelegate::PlatformImage2D* PPB_ImageData_Impl::PlatformImage() const {
  return backend_->PlatformImage();
}

PP_Bool PPB_ImageData_Impl::Describe(PP_ImageDataDesc* desc) {
  desc->format = format_;
  desc->size.width = width_;
  desc->size.height = height_;
  desc->stride = width_ * 4;
  return PP_TRUE;
}

void* PPB_ImageData_Impl::Map() {
  return backend_->Map();
}

void PPB_ImageData_Impl::Unmap() {
  backend_->Unmap();
}

int32_t PPB_ImageData_Impl::GetSharedMemory(int* handle, uint32_t* byte_count) {
  return backend_->GetSharedMemory(handle, byte_count);
}

skia::PlatformCanvas* PPB_ImageData_Impl::GetPlatformCanvas() {
  return backend_->GetPlatformCanvas();
}

SkCanvas* PPB_ImageData_Impl::GetCanvas() {
  return backend_->GetCanvas();
}

const SkBitmap* PPB_ImageData_Impl::GetMappedBitmap() const {
  return backend_->GetMappedBitmap();
}

// ImageDataPlatformBackend --------------------------------------------------

ImageDataPlatformBackend::ImageDataPlatformBackend() {
}

ImageDataPlatformBackend::~ImageDataPlatformBackend() {
}

bool ImageDataPlatformBackend::Init(PPB_ImageData_Impl* impl,
                                     PP_ImageDataFormat format,
                                     int width, int height,
                                     bool init_to_zero) {
  PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(impl);
  if (!plugin_delegate)
    return false;

  // TODO(brettw) use init_to_zero when we implement caching.
  platform_image_.reset(plugin_delegate->CreateImage2D(width, height));
  return !!platform_image_.get();
}

bool ImageDataPlatformBackend::IsMapped() const {
  return !!mapped_canvas_.get();
}

PluginDelegate::PlatformImage2D*
ImageDataPlatformBackend::PlatformImage() const {
  return platform_image_.get();
}

void* ImageDataPlatformBackend::Map() {
  if (!mapped_canvas_.get()) {
    mapped_canvas_.reset(platform_image_->Map());
    if (!mapped_canvas_.get())
      return NULL;
  }
  const SkBitmap& bitmap =
      skia::GetTopDevice(*mapped_canvas_)->accessBitmap(true);

  // Our platform bitmaps are set to opaque by default, which we don't want.
  const_cast<SkBitmap&>(bitmap).setIsOpaque(false);

  bitmap.lockPixels();
  return bitmap.getAddr32(0, 0);
}

void ImageDataPlatformBackend::Unmap() {
  // This is currently unimplemented, which is OK. The data will just always
  // be around once it's mapped. Chrome's TransportDIB isn't currently
  // unmappable without freeing it, but this may be something we want to support
  // in the future to save some memory.
}

int32_t ImageDataPlatformBackend::GetSharedMemory(int* handle,
                                                   uint32_t* byte_count) {
  *handle = platform_image_->GetSharedMemoryHandle(byte_count);
  return PP_OK;
}

skia::PlatformCanvas* ImageDataPlatformBackend::GetPlatformCanvas() {
  return mapped_canvas_.get();
}

SkCanvas* ImageDataPlatformBackend::GetCanvas() {
  return mapped_canvas_.get();
}

const SkBitmap* ImageDataPlatformBackend::GetMappedBitmap() const {
  if (!mapped_canvas_.get())
    return NULL;
  return &skia::GetTopDevice(*mapped_canvas_)->accessBitmap(false);
}

// ImageDataNaClBackend ------------------------------------------------------

ImageDataNaClBackend::ImageDataNaClBackend()
    : map_count_(0) {
}

ImageDataNaClBackend::~ImageDataNaClBackend() {
}

bool ImageDataNaClBackend::Init(PPB_ImageData_Impl* impl,
                                 PP_ImageDataFormat format,
                                 int width, int height,
                                 bool init_to_zero) {
  skia_bitmap_.setConfig(SkBitmap::kARGB_8888_Config,
                         impl->width(), impl->height());
  PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(impl);
  if (!plugin_delegate)
    return false;
  shared_memory_.reset(
      plugin_delegate->CreateAnonymousSharedMemory(skia_bitmap_.getSize()));
  return !!shared_memory_.get();
}

bool ImageDataNaClBackend::IsMapped() const {
  return map_count_ > 0;
}

PluginDelegate::PlatformImage2D* ImageDataNaClBackend::PlatformImage() const {
  return NULL;
}

void* ImageDataNaClBackend::Map() {
  DCHECK(shared_memory_.get());
  if (map_count_++ == 0) {
    shared_memory_->Map(skia_bitmap_.getSize());
    skia_bitmap_.setPixels(shared_memory_->memory());
    // Our platform bitmaps are set to opaque by default, which we don't want.
    skia_bitmap_.setIsOpaque(false);
    skia_canvas_.reset(new SkCanvas(skia_bitmap_));
    return skia_bitmap_.getAddr32(0, 0);
  }
  return shared_memory_->memory();
}

void ImageDataNaClBackend::Unmap() {
  if (--map_count_ == 0)
    shared_memory_->Unmap();
}

int32_t ImageDataNaClBackend::GetSharedMemory(int* handle,
                                                uint32_t* byte_count) {
  *byte_count = skia_bitmap_.getSize();
#if defined(OS_POSIX)
  *handle = shared_memory_->handle().fd;
#elif defined(OS_WIN)
  *handle = reinterpret_cast<int>(shared_memory_->handle());
#else
#error "Platform not supported."
#endif
  return PP_OK;
}

skia::PlatformCanvas* ImageDataNaClBackend::GetPlatformCanvas() {
  return NULL;
}

SkCanvas* ImageDataNaClBackend::GetCanvas() {
  if (!IsMapped())
    return NULL;
  return skia_canvas_.get();
}

const SkBitmap* ImageDataNaClBackend::GetMappedBitmap() const {
  if (!IsMapped())
    return NULL;
  return &skia_bitmap_;
}

}  // namespace ppapi
}  // namespace webkit
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "formeditorscene.h"
#include "formeditorview.h"
#include "formeditorwidget.h"
#include "formeditoritem.h"
#include "qmldesignerplugin.h"
#include "designersettings.h"

#include <QGraphicsSceneDragDropEvent>

#include <QEvent>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsView>

#include <QDebug>
#include <QList>
#include <QTime>

namespace QmlDesigner {

FormEditorScene::FormEditorScene(FormEditorWidget *view, FormEditorView *editorView)
        : QGraphicsScene(),
        m_editorView(editorView),
        m_showBoundingRects(true)
{
    setupScene();
    view->setScene(this);
    setItemIndexMethod(QGraphicsScene::NoIndex);
}

FormEditorScene::~FormEditorScene()
{
    clear();  //FormEditorItems have to be cleared before destruction
              //Reason: FormEditorItems accesses FormEditorScene in destructor
}

void FormEditorScene::setupScene()
{
    m_formLayerItem = new LayerItem(this);
    m_manipulatorLayerItem = new LayerItem(this);
    m_manipulatorLayerItem->setZValue(1.0);
    m_manipulatorLayerItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
    m_formLayerItem->setZValue(0.0);
    m_formLayerItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
}

void FormEditorScene::resetScene()
{
    foreach (QGraphicsItem *item, m_manipulatorLayerItem->childItems())
       removeItem(item);

    setSceneRect(-canvasWidth()/2., -canvasHeight()/2., canvasWidth(), canvasHeight());
}

FormEditorItem* FormEditorScene::itemForQmlItemNode(const QmlItemNode &qmlItemNode) const
{
    Q_ASSERT(hasItemForQmlItemNode(qmlItemNode));
    return m_qmlItemNodeItemHash.value(qmlItemNode);
}

double FormEditorScene::canvasWidth() const
{
    DesignerSettings settings = QmlDesignerPlugin::instance()->settings();
    return settings.canvasWidth;
}

double FormEditorScene::canvasHeight() const
{
    DesignerSettings settings = QmlDesignerPlugin::instance()->settings();
    return settings.canvasHeight;
}

QList<FormEditorItem*> FormEditorScene::itemsForQmlItemNodes(const QList<QmlItemNode> &nodeList) const
{
    QList<FormEditorItem*> itemList;
    foreach (const QmlItemNode &node, nodeList) {
        if (hasItemForQmlItemNode(node))
            itemList += itemForQmlItemNode(node);
    }

    return itemList;
}

QList<FormEditorItem*> FormEditorScene::allFormEditorItems() const
{
    return m_qmlItemNodeItemHash.values();
}

void FormEditorScene::updateAllFormEditorItems()
{
    foreach (FormEditorItem *item, allFormEditorItems()) {
        item->update();
    }
}

bool FormEditorScene::hasItemForQmlItemNode(const QmlItemNode &qmlItemNode) const
{
    return m_qmlItemNodeItemHash.contains(qmlItemNode);
}

void FormEditorScene::removeItemFromHash(FormEditorItem *item)
{
    m_qmlItemNodeItemHash.remove(item->qmlItemNode());
}

AbstractFormEditorTool* FormEditorScene::currentTool() const
{
    return m_editorView->currentTool();
}

//This function calculates the possible parent for reparent
FormEditorItem* FormEditorScene::calulateNewParent(FormEditorItem *formEditorItem)
{
    if (formEditorItem->qmlItemNode().isValid()) {
        QList<QGraphicsItem *> list = items(formEditorItem->qmlItemNode().instanceBoundingRect().center());
        foreach (QGraphicsItem *graphicsItem, list) {
            if (qgraphicsitem_cast<FormEditorItem*>(graphicsItem) &&
                graphicsItem->collidesWithItem(formEditorItem, Qt::ContainsItemShape))
                return qgraphicsitem_cast<FormEditorItem*>(graphicsItem);
        }
    }

    return 0;
}

void FormEditorScene::synchronizeTransformation(const QmlItemNode &qmlItemNode)
{
    FormEditorItem *item = itemForQmlItemNode(qmlItemNode);
    item->updateGeometry();
    item->update();

    if (qmlItemNode.isRootNode()) {
        formLayerItem()->update();
        manipulatorLayerItem()->update();
    }
}

void FormEditorScene::synchronizeParent(const QmlItemNode &qmlItemNode)
{
    QmlItemNode parentNode = qmlItemNode.instanceParent().toQmlItemNode();
    reparentItem(qmlItemNode, parentNode);
}

void FormEditorScene::synchronizeOtherProperty(const QmlItemNode &qmlItemNode, const QString &propertyName)
{
    if (hasItemForQmlItemNode(qmlItemNode)) {
        FormEditorItem *item = itemForQmlItemNode(qmlItemNode);

        if (propertyName == "opacity")
            item->setOpacity(qmlItemNode.instanceValue("opacity").toDouble());

        if (propertyName == "clip")
            item->setFlag(QGraphicsItem::ItemClipsChildrenToShape, qmlItemNode.instanceValue("clip").toBool());

        if (propertyName == "z")
            item->setZValue(qmlItemNode.instanceValue("z").toDouble());

        if (propertyName == "visible")
            item->setContentVisible(qmlItemNode.instanceValue("visible").toBool());
    }
}

void FormEditorScene::synchronizeState(const QmlItemNode &qmlItemNode)
{
    if (hasItemForQmlItemNode(qmlItemNode)) {
        FormEditorItem *item = itemForQmlItemNode(qmlItemNode);
        if (item)
            item->update();
    }
}

FormEditorItem *FormEditorScene::addFormEditorItem(const QmlItemNode &qmlItemNode)
{
    FormEditorItem *formEditorItem = new FormEditorItem(qmlItemNode, this);
    Q_ASSERT(!m_qmlItemNodeItemHash.contains(qmlItemNode));

    m_qmlItemNodeItemHash.insert(qmlItemNode, formEditorItem);
    if (qmlItemNode.isRootNode()) {
        setSceneRect(-canvasWidth()/2., -canvasHeight()/2., canvasWidth(), canvasHeight());
        formLayerItem()->update();
        manipulatorLayerItem()->update();
    }

    return formEditorItem;
}

void FormEditorScene::dropEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dropEvent(event);

    if (views().first())
        views().first()->setFocus();
}

void FormEditorScene::dragEnterEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dragEnterEvent(event);
}

void FormEditorScene::dragLeaveEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dragLeaveEvent(event);

    return;
}

void FormEditorScene::dragMoveEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dragMoveEvent(event);
}

QList<QGraphicsItem *> FormEditorScene::removeLayerItems(const QList<QGraphicsItem *> &itemList)
{
    QList<QGraphicsItem *> itemListWithoutLayerItems;

    foreach (QGraphicsItem *item, itemList)
        if (item != manipulatorLayerItem() && item != formLayerItem())
            itemListWithoutLayerItems.append(item);

    return itemListWithoutLayerItems;
}

void FormEditorScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (editorView() && editorView()->model())
        currentTool()->mousePressEvent(removeLayerItems(items(event->scenePos())), event);
}

static QTime staticTimer()
{
    QTime timer;
    timer.start();
    return timer;
}

void FormEditorScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    static QTime time = staticTimer();

    if (time.elapsed() > 30) {
        time.restart();
        if (event->buttons())
            currentTool()->mouseMoveEvent(removeLayerItems(items(event->scenePos())), event);
        else
            currentTool()->hoverMoveEvent(removeLayerItems(items(event->scenePos())), event);

        event->accept();
    }
}

void FormEditorScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (editorView() && editorView()->model()) {
        currentTool()->mouseReleaseEvent(removeLayerItems(items(event->scenePos())), event);

        event->accept();
    }
 }

void FormEditorScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    if (editorView() && editorView()->model()) {
        currentTool()->mouseDoubleClickEvent(removeLayerItems(items(event->scenePos())), event);

        event->accept();
    }
}

void FormEditorScene::keyPressEvent(QKeyEvent *keyEvent)
{
    if (editorView() && editorView()->model())
        currentTool()->keyPressEvent(keyEvent);
}

void FormEditorScene::keyReleaseEvent(QKeyEvent *keyEvent)
{
    if (editorView() && editorView()->model())
        currentTool()->keyReleaseEvent(keyEvent);
}

FormEditorView *FormEditorScene::editorView() const
{
    return m_editorView;
}

LayerItem* FormEditorScene::manipulatorLayerItem() const
{
    return m_manipulatorLayerItem.data();
}

LayerItem* FormEditorScene::formLayerItem() const
{
    return m_formLayerItem.data();
}

bool FormEditorScene::event(QEvent * event)
{
    switch (event->type())
    {
        case QEvent::GraphicsSceneHoverEnter :
            hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
            return true;
        case QEvent::GraphicsSceneHoverMove :
            hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
            return true;
        case QEvent::GraphicsSceneHoverLeave :
            hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
            return true;
        case QEvent::ShortcutOverride :
            if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
                currentTool()->keyPressEvent(static_cast<QKeyEvent*>(event));
                return true;
            }
        default: return QGraphicsScene::event(event);
    }

}

void FormEditorScene::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/)
{
    qDebug() << __FUNCTION__;
}

void FormEditorScene::hoverMoveEvent(QGraphicsSceneHoverEvent * /*event*/)
{
    qDebug() << __FUNCTION__;
}

void FormEditorScene::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
{
    qDebug() << __FUNCTION__;
}

void FormEditorScene::reparentItem(const QmlItemNode &node, const QmlItemNode &newParent)
{
    Q_ASSERT(hasItemForQmlItemNode(node));
    FormEditorItem *item = itemForQmlItemNode(node);
    FormEditorItem *parentItem = 0;
    if (newParent.isValid() && hasItemForQmlItemNode(newParent))
        parentItem = itemForQmlItemNode(newParent);

    item->setParentItem(0);
    item->setParentItem(parentItem);
}

FormEditorItem* FormEditorScene::rootFormEditorItem() const
{
     if (hasItemForQmlItemNode(editorView()->rootModelNode()))
         return itemForQmlItemNode(editorView()->rootModelNode());
    return 0;
}

void FormEditorScene::clearFormEditorItems()
{
    QList<QGraphicsItem*> itemList(items());

    foreach (QGraphicsItem *item, itemList) {
        if (qgraphicsitem_cast<FormEditorItem* >(item))
            item->setParentItem(0);
    }

    foreach (QGraphicsItem *item, itemList) {
        if (qgraphicsitem_cast<FormEditorItem* >(item))
            delete item;
    }
}

void FormEditorScene::highlightBoundingRect(FormEditorItem *highlighItem)
{
    foreach (FormEditorItem *item, allFormEditorItems()) {
        if (item == highlighItem)
            item->setHighlightBoundingRect(true);
        else
            item->setHighlightBoundingRect(false);
    }
}

void FormEditorScene::setShowBoundingRects(bool show)
{
    m_showBoundingRects = show;
    updateAllFormEditorItems();
}

bool FormEditorScene::showBoundingRects() const
{
    return m_showBoundingRects;
}

}

<commit_msg>QmlDesigner: Delete manipulator items<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "formeditorscene.h"
#include "formeditorview.h"
#include "formeditorwidget.h"
#include "formeditoritem.h"
#include "qmldesignerplugin.h"
#include "designersettings.h"

#include <QGraphicsSceneDragDropEvent>

#include <QEvent>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsView>

#include <QDebug>
#include <QList>
#include <QTime>

namespace QmlDesigner {

FormEditorScene::FormEditorScene(FormEditorWidget *view, FormEditorView *editorView)
        : QGraphicsScene(),
        m_editorView(editorView),
        m_showBoundingRects(true)
{
    setupScene();
    view->setScene(this);
    setItemIndexMethod(QGraphicsScene::NoIndex);
}

FormEditorScene::~FormEditorScene()
{
    clear();  //FormEditorItems have to be cleared before destruction
              //Reason: FormEditorItems accesses FormEditorScene in destructor
}

void FormEditorScene::setupScene()
{
    m_formLayerItem = new LayerItem(this);
    m_manipulatorLayerItem = new LayerItem(this);
    m_manipulatorLayerItem->setZValue(1.0);
    m_manipulatorLayerItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
    m_formLayerItem->setZValue(0.0);
    m_formLayerItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
}

void FormEditorScene::resetScene()
{
    foreach (QGraphicsItem *item, m_manipulatorLayerItem->childItems()) {
       removeItem(item);
       delete item;
    }

    setSceneRect(-canvasWidth()/2., -canvasHeight()/2., canvasWidth(), canvasHeight());
}

FormEditorItem* FormEditorScene::itemForQmlItemNode(const QmlItemNode &qmlItemNode) const
{
    Q_ASSERT(hasItemForQmlItemNode(qmlItemNode));
    return m_qmlItemNodeItemHash.value(qmlItemNode);
}

double FormEditorScene::canvasWidth() const
{
    DesignerSettings settings = QmlDesignerPlugin::instance()->settings();
    return settings.canvasWidth;
}

double FormEditorScene::canvasHeight() const
{
    DesignerSettings settings = QmlDesignerPlugin::instance()->settings();
    return settings.canvasHeight;
}

QList<FormEditorItem*> FormEditorScene::itemsForQmlItemNodes(const QList<QmlItemNode> &nodeList) const
{
    QList<FormEditorItem*> itemList;
    foreach (const QmlItemNode &node, nodeList) {
        if (hasItemForQmlItemNode(node))
            itemList += itemForQmlItemNode(node);
    }

    return itemList;
}

QList<FormEditorItem*> FormEditorScene::allFormEditorItems() const
{
    return m_qmlItemNodeItemHash.values();
}

void FormEditorScene::updateAllFormEditorItems()
{
    foreach (FormEditorItem *item, allFormEditorItems()) {
        item->update();
    }
}

bool FormEditorScene::hasItemForQmlItemNode(const QmlItemNode &qmlItemNode) const
{
    return m_qmlItemNodeItemHash.contains(qmlItemNode);
}

void FormEditorScene::removeItemFromHash(FormEditorItem *item)
{
    m_qmlItemNodeItemHash.remove(item->qmlItemNode());
}

AbstractFormEditorTool* FormEditorScene::currentTool() const
{
    return m_editorView->currentTool();
}

//This function calculates the possible parent for reparent
FormEditorItem* FormEditorScene::calulateNewParent(FormEditorItem *formEditorItem)
{
    if (formEditorItem->qmlItemNode().isValid()) {
        QList<QGraphicsItem *> list = items(formEditorItem->qmlItemNode().instanceBoundingRect().center());
        foreach (QGraphicsItem *graphicsItem, list) {
            if (qgraphicsitem_cast<FormEditorItem*>(graphicsItem) &&
                graphicsItem->collidesWithItem(formEditorItem, Qt::ContainsItemShape))
                return qgraphicsitem_cast<FormEditorItem*>(graphicsItem);
        }
    }

    return 0;
}

void FormEditorScene::synchronizeTransformation(const QmlItemNode &qmlItemNode)
{
    FormEditorItem *item = itemForQmlItemNode(qmlItemNode);
    item->updateGeometry();
    item->update();

    if (qmlItemNode.isRootNode()) {
        formLayerItem()->update();
        manipulatorLayerItem()->update();
    }
}

void FormEditorScene::synchronizeParent(const QmlItemNode &qmlItemNode)
{
    QmlItemNode parentNode = qmlItemNode.instanceParent().toQmlItemNode();
    reparentItem(qmlItemNode, parentNode);
}

void FormEditorScene::synchronizeOtherProperty(const QmlItemNode &qmlItemNode, const QString &propertyName)
{
    if (hasItemForQmlItemNode(qmlItemNode)) {
        FormEditorItem *item = itemForQmlItemNode(qmlItemNode);

        if (propertyName == "opacity")
            item->setOpacity(qmlItemNode.instanceValue("opacity").toDouble());

        if (propertyName == "clip")
            item->setFlag(QGraphicsItem::ItemClipsChildrenToShape, qmlItemNode.instanceValue("clip").toBool());

        if (propertyName == "z")
            item->setZValue(qmlItemNode.instanceValue("z").toDouble());

        if (propertyName == "visible")
            item->setContentVisible(qmlItemNode.instanceValue("visible").toBool());
    }
}

void FormEditorScene::synchronizeState(const QmlItemNode &qmlItemNode)
{
    if (hasItemForQmlItemNode(qmlItemNode)) {
        FormEditorItem *item = itemForQmlItemNode(qmlItemNode);
        if (item)
            item->update();
    }
}

FormEditorItem *FormEditorScene::addFormEditorItem(const QmlItemNode &qmlItemNode)
{
    FormEditorItem *formEditorItem = new FormEditorItem(qmlItemNode, this);
    Q_ASSERT(!m_qmlItemNodeItemHash.contains(qmlItemNode));

    m_qmlItemNodeItemHash.insert(qmlItemNode, formEditorItem);
    if (qmlItemNode.isRootNode()) {
        setSceneRect(-canvasWidth()/2., -canvasHeight()/2., canvasWidth(), canvasHeight());
        formLayerItem()->update();
        manipulatorLayerItem()->update();
    }

    return formEditorItem;
}

void FormEditorScene::dropEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dropEvent(event);

    if (views().first())
        views().first()->setFocus();
}

void FormEditorScene::dragEnterEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dragEnterEvent(event);
}

void FormEditorScene::dragLeaveEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dragLeaveEvent(event);

    return;
}

void FormEditorScene::dragMoveEvent(QGraphicsSceneDragDropEvent * event)
{
    currentTool()->dragMoveEvent(event);
}

QList<QGraphicsItem *> FormEditorScene::removeLayerItems(const QList<QGraphicsItem *> &itemList)
{
    QList<QGraphicsItem *> itemListWithoutLayerItems;

    foreach (QGraphicsItem *item, itemList)
        if (item != manipulatorLayerItem() && item != formLayerItem())
            itemListWithoutLayerItems.append(item);

    return itemListWithoutLayerItems;
}

void FormEditorScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (editorView() && editorView()->model())
        currentTool()->mousePressEvent(removeLayerItems(items(event->scenePos())), event);
}

static QTime staticTimer()
{
    QTime timer;
    timer.start();
    return timer;
}

void FormEditorScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    static QTime time = staticTimer();

    if (time.elapsed() > 30) {
        time.restart();
        if (event->buttons())
            currentTool()->mouseMoveEvent(removeLayerItems(items(event->scenePos())), event);
        else
            currentTool()->hoverMoveEvent(removeLayerItems(items(event->scenePos())), event);

        event->accept();
    }
}

void FormEditorScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (editorView() && editorView()->model()) {
        currentTool()->mouseReleaseEvent(removeLayerItems(items(event->scenePos())), event);

        event->accept();
    }
 }

void FormEditorScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    if (editorView() && editorView()->model()) {
        currentTool()->mouseDoubleClickEvent(removeLayerItems(items(event->scenePos())), event);

        event->accept();
    }
}

void FormEditorScene::keyPressEvent(QKeyEvent *keyEvent)
{
    if (editorView() && editorView()->model())
        currentTool()->keyPressEvent(keyEvent);
}

void FormEditorScene::keyReleaseEvent(QKeyEvent *keyEvent)
{
    if (editorView() && editorView()->model())
        currentTool()->keyReleaseEvent(keyEvent);
}

FormEditorView *FormEditorScene::editorView() const
{
    return m_editorView;
}

LayerItem* FormEditorScene::manipulatorLayerItem() const
{
    return m_manipulatorLayerItem.data();
}

LayerItem* FormEditorScene::formLayerItem() const
{
    return m_formLayerItem.data();
}

bool FormEditorScene::event(QEvent * event)
{
    switch (event->type())
    {
        case QEvent::GraphicsSceneHoverEnter :
            hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
            return true;
        case QEvent::GraphicsSceneHoverMove :
            hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
            return true;
        case QEvent::GraphicsSceneHoverLeave :
            hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
            return true;
        case QEvent::ShortcutOverride :
            if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
                currentTool()->keyPressEvent(static_cast<QKeyEvent*>(event));
                return true;
            }
        default: return QGraphicsScene::event(event);
    }

}

void FormEditorScene::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/)
{
    qDebug() << __FUNCTION__;
}

void FormEditorScene::hoverMoveEvent(QGraphicsSceneHoverEvent * /*event*/)
{
    qDebug() << __FUNCTION__;
}

void FormEditorScene::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
{
    qDebug() << __FUNCTION__;
}

void FormEditorScene::reparentItem(const QmlItemNode &node, const QmlItemNode &newParent)
{
    Q_ASSERT(hasItemForQmlItemNode(node));
    FormEditorItem *item = itemForQmlItemNode(node);
    FormEditorItem *parentItem = 0;
    if (newParent.isValid() && hasItemForQmlItemNode(newParent))
        parentItem = itemForQmlItemNode(newParent);

    item->setParentItem(0);
    item->setParentItem(parentItem);
}

FormEditorItem* FormEditorScene::rootFormEditorItem() const
{
     if (hasItemForQmlItemNode(editorView()->rootModelNode()))
         return itemForQmlItemNode(editorView()->rootModelNode());
    return 0;
}

void FormEditorScene::clearFormEditorItems()
{
    QList<QGraphicsItem*> itemList(items());

    foreach (QGraphicsItem *item, itemList) {
        if (qgraphicsitem_cast<FormEditorItem* >(item))
            item->setParentItem(0);
    }

    foreach (QGraphicsItem *item, itemList) {
        if (qgraphicsitem_cast<FormEditorItem* >(item))
            delete item;
    }
}

void FormEditorScene::highlightBoundingRect(FormEditorItem *highlighItem)
{
    foreach (FormEditorItem *item, allFormEditorItems()) {
        if (item == highlighItem)
            item->setHighlightBoundingRect(true);
        else
            item->setHighlightBoundingRect(false);
    }
}

void FormEditorScene::setShowBoundingRects(bool show)
{
    m_showBoundingRects = show;
    updateAllFormEditorItems();
}

bool FormEditorScene::showBoundingRects() const
{
    return m_showBoundingRects;
}

}

<|endoftext|>


<commit_before>#include "../../../../Block_Diagonal_Matrix.hxx"

// Minimum eigenvalue of A.  A is assumed to be symmetric.

// Annoyingly, El::HermitianEig modifies 'block'.  It is OK, because
// it is only called from step_length(), which passes in a temporary.
// Still ugly.
El::BigFloat min_eigenvalue(Block_Diagonal_Matrix &A)
{
  El::BigFloat local_min(El::limits::Max<El::BigFloat>());

  for(auto &block : A.blocks)
    {
      El::DistMatrix<El::BigFloat, El::VR, El::STAR> eigenvalues(block.Grid());
      /// There is a bug in El::HermitianEig when there is more than
      /// one level of recursion when computing eigenvalues.  One fix
      /// is to increase the cutoff so that there is no more than one
      /// level of recursion.

      /// An alternate workaround is to compute both eigenvalues and
      /// eigenvectors, but that seems to be significantly slower.
      El::HermitianEigCtrl<El::BigFloat> hermitian_eig_ctrl;
      hermitian_eig_ctrl.tridiagEigCtrl.dcCtrl.cutoff = block.Height() / 2 + 1;

      /// The default number of iterations is 40.  That is sometimes
      /// not enough, so we bump it up significantly.
      hermitian_eig_ctrl.tridiagEigCtrl.dcCtrl.secularCtrl.maxIterations = 400;
      El::HermitianEig(El::UpperOrLowerNS::LOWER, block, eigenvalues,
                       hermitian_eig_ctrl);
      local_min = El::Min(local_min, El::Min(eigenvalues));
    }
  return El::mpi::AllReduce(local_min, El::mpi::MIN, El::mpi::COMM_WORLD);
}
<commit_msg>Increase maxIterations for step length eigenvalues to 16384<commit_after>#include "../../../../Block_Diagonal_Matrix.hxx"

// Minimum eigenvalue of A.  A is assumed to be symmetric.

// Annoyingly, El::HermitianEig modifies 'block'.  It is OK, because
// it is only called from step_length(), which passes in a temporary.
// Still ugly.
El::BigFloat min_eigenvalue(Block_Diagonal_Matrix &A)
{
  El::BigFloat local_min(El::limits::Max<El::BigFloat>());

  for(auto &block : A.blocks)
    {
      El::DistMatrix<El::BigFloat, El::VR, El::STAR> eigenvalues(block.Grid());
      /// There is a bug in El::HermitianEig when there is more than
      /// one level of recursion when computing eigenvalues.  One fix
      /// is to increase the cutoff so that there is no more than one
      /// level of recursion.

      /// An alternate workaround is to compute both eigenvalues and
      /// eigenvectors, but that seems to be significantly slower.
      El::HermitianEigCtrl<El::BigFloat> hermitian_eig_ctrl;
      hermitian_eig_ctrl.tridiagEigCtrl.dcCtrl.cutoff = block.Height() / 2 + 1;

      /// The default number of iterations is 40.  That is sometimes
      /// not enough, so we bump it up significantly.
      hermitian_eig_ctrl.tridiagEigCtrl.dcCtrl.secularCtrl.maxIterations = 16384;
      El::HermitianEig(El::UpperOrLowerNS::LOWER, block, eigenvalues,
                       hermitian_eig_ctrl);
      local_min = El::Min(local_min, El::Min(eigenvalues));
    }
  return El::mpi::AllReduce(local_min, El::mpi::MIN, El::mpi::COMM_WORLD);
}
<|endoftext|>


<commit_before>//
// Copyright 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RobustBufferAccessBehaviorTest:
//   Various tests related for GL_KHR_robust_buffer_access_behavior.
//

#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"

#include <array>

using namespace angle;

namespace
{

class RobustBufferAccessBehaviorTest : public ANGLETest
{
  protected:
    RobustBufferAccessBehaviorTest() : mProgram(0), mTestAttrib(-1)
    {
        setWindowWidth(128);
        setWindowHeight(128);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
    }

    void TearDown() override
    {
        glDeleteProgram(mProgram);
        ANGLETest::TearDown();
    }

    bool initExtension()
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
        if (!eglDisplayExtensionEnabled(display, "EGL_EXT_create_context_robustness"))
        {
            return false;
        }

        ANGLETest::TearDown();
        setRobustAccess(true);
        ANGLETest::SetUp();
        if (!extensionEnabled("GL_KHR_robust_buffer_access_behavior"))
        {
            return false;
        }
        return true;
    }

    void initBasicProgram()
    {
        const std::string &vsCheckOutOfBounds =
            "precision mediump float;\n"
            "attribute vec4 position;\n"
            "attribute vec4 vecRandom;\n"
            "varying vec4 v_color;\n"
            "bool testFloatComponent(float component) {\n"
            "    return (component == 0.2 || component == 0.0);\n"
            "}\n"
            "bool testLastFloatComponent(float component) {\n"
            "    return testFloatComponent(component) || component == 1.0;\n"
            "}\n"
            "void main() {\n"
            "    if (testFloatComponent(vecRandom.x) &&\n"
            "        testFloatComponent(vecRandom.y) &&\n"
            "        testFloatComponent(vecRandom.z) &&\n"
            "        testLastFloatComponent(vecRandom.w)) {\n"
            "        v_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
            "    } else {\n"
            "        v_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
            "    }\n"
            "    gl_Position = position;\n"
            "}\n";

        const std::string &fragmentShaderSource =
            "precision mediump float;\n"
            "varying vec4 v_color;\n"
            "void main() {\n"
            "    gl_FragColor = v_color;\n"
            "}\n";

        mProgram = CompileProgram(vsCheckOutOfBounds, fragmentShaderSource);
        ASSERT_NE(0u, mProgram);

        mTestAttrib = glGetAttribLocation(mProgram, "vecRandom");
        ASSERT_NE(-1, mTestAttrib);

        glUseProgram(mProgram);
    }

    void runIndexOutOfRangeTests(GLenum drawType)
    {
        if (mProgram == 0)
        {
            initBasicProgram();
        }

        GLBuffer bufferIncomplete;
        glBindBuffer(GL_ARRAY_BUFFER, bufferIncomplete);
        std::array<GLfloat, 12> randomData = {
            {0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f}};
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * randomData.size(), randomData.data(),
                     drawType);

        glEnableVertexAttribArray(mTestAttrib);
        glVertexAttribPointer(mTestAttrib, 4, GL_FLOAT, GL_FALSE, 0, nullptr);

        glClearColor(0.0, 0.0, 1.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        drawIndexedQuad(mProgram, "position", 0.5f);

        int width     = getWindowWidth();
        int height    = getWindowHeight();
        GLenum result = glGetError();
        // For D3D dynamic draw, we still return invalid operation. Once we force the index buffer
        // to clamp any out of range indices instead of invalid operation, this part can be removed.
        // We can always get GL_NO_ERROR.
        if (result == GL_INVALID_OPERATION)
        {
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::blue);
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::blue);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::blue);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::blue);
        }
        else
        {
            EXPECT_GLENUM_EQ(GL_NO_ERROR, result);
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::green);
        }
    }

    GLuint mProgram;
    GLint mTestAttrib;
};

// Test that static draw with out-of-bounds reads will not read outside of the data store of the
// buffer object and will not result in GL interruption or termination when
// GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithStaticDraw)
{
    ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
    ANGLE_SKIP_TEST_IF(!initExtension());

    runIndexOutOfRangeTests(GL_STATIC_DRAW);
}

// Test that dynamic draw with out-of-bounds reads will not read outside of the data store of the
// buffer object and will not result in GL interruption or termination when
// GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithDynamicDraw)
{
    ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
    ANGLE_SKIP_TEST_IF(!initExtension());

    runIndexOutOfRangeTests(GL_DYNAMIC_DRAW);
}

// Test that vertex buffers are rebound with the correct offsets in subsequent calls in the D3D11
// backend.  http://crbug.com/837002
TEST_P(RobustBufferAccessBehaviorTest, D3D11StateSynchronizationOrderBug)
{
    ANGLE_SKIP_TEST_IF(!initExtension());

    glDisable(GL_DEPTH_TEST);

    // 2 quads, the first one red, the second one green
    const std::array<angle::Vector4, 16> vertices{
        angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v0
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c0
        angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v1
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c1
        angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v2
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c2
        angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v3
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c3

        angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v4
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c4
        angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v5
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c5
        angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v6
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c6
        angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v7
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c7
    };

    GLBuffer vb;
    glBindBuffer(GL_ARRAY_BUFFER, vb);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);

    const std::array<GLushort, 12> indicies{
        0, 1, 2, 0, 2, 3,  // quad0
        4, 5, 6, 4, 6, 7,  // quad1
    };

    GLBuffer ib;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies.data(), GL_STATIC_DRAW);

    constexpr char kVS[] = R"(
precision highp float;
attribute vec4 a_position;
attribute vec4 a_color;

varying vec4 v_color;

void main()
{
    gl_Position = a_position;
    v_color = a_color;
})";

    constexpr char kFS[] = R"(
precision highp float;
varying vec4 v_color;

void main()
{
    gl_FragColor = v_color;
})";

    ANGLE_GL_PROGRAM(program, kVS, kFS);
    glUseProgram(program);

    GLint positionLocation = glGetAttribLocation(program, "a_position");
    glEnableVertexAttribArray(positionLocation);
    glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2, 0);

    GLint colorLocation = glGetAttribLocation(program, "a_color");
    glEnableVertexAttribArray(colorLocation);
    glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2,
                          reinterpret_cast<const void *>(sizeof(angle::Vector4)));

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
                   reinterpret_cast<const void *>(sizeof(GLshort) * 6));
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
                   reinterpret_cast<const void *>(sizeof(GLshort) * 6));
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}

// Covers drawing with a very large vertex range which overflows GLsizei. http://crbug.com/842028
TEST_P(RobustBufferAccessBehaviorTest, VeryLargeVertexCountWithDynamicVertexData)
{
    ANGLE_SKIP_TEST_IF(!initExtension());
    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_element_index_uint"));

    constexpr GLsizei kIndexCount           = 32;
    std::array<GLuint, kIndexCount> indices = {{}};
    for (GLsizei index = 0; index < kIndexCount; ++index)
    {
        indices[index] = ((std::numeric_limits<GLuint>::max() - 2) / kIndexCount) * index;
    }

    GLBuffer indexBuffer;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(),
                 GL_STATIC_DRAW);

    std::array<GLfloat, 256> vertexData = {{}};

    GLBuffer vertexBuffer;
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(GLfloat), vertexData.data(),
                 GL_DYNAMIC_DRAW);

    ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
    glUseProgram(program);

    GLint attribLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
    ASSERT_NE(-1, attribLoc);

    glVertexAttribPointer(attribLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(attribLoc);
    ASSERT_GL_NO_ERROR();

    glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_INT, nullptr);

    // This may or may not generate an error, but it should not crash.
}

ANGLE_INSTANTIATE_TEST(RobustBufferAccessBehaviorTest,
                       ES2_D3D9(),
                       ES2_D3D11_FL9_3(),
                       ES2_D3D11(),
                       ES3_D3D11(),
                       ES31_D3D11(),
                       ES2_OPENGL(),
                       ES3_OPENGL(),
                       ES31_OPENGL(),
                       ES2_OPENGLES(),
                       ES3_OPENGLES(),
                       ES31_OPENGLES());

}  // namespace
<commit_msg>Skip DrawElementsIndexOutOfRangeWithStaticDraw on NVIDIA D3D11 FL9.3.<commit_after>//
// Copyright 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RobustBufferAccessBehaviorTest:
//   Various tests related for GL_KHR_robust_buffer_access_behavior.
//

#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"

#include <array>

using namespace angle;

namespace
{

class RobustBufferAccessBehaviorTest : public ANGLETest
{
  protected:
    RobustBufferAccessBehaviorTest() : mProgram(0), mTestAttrib(-1)
    {
        setWindowWidth(128);
        setWindowHeight(128);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
    }

    void TearDown() override
    {
        glDeleteProgram(mProgram);
        ANGLETest::TearDown();
    }

    bool initExtension()
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
        if (!eglDisplayExtensionEnabled(display, "EGL_EXT_create_context_robustness"))
        {
            return false;
        }

        ANGLETest::TearDown();
        setRobustAccess(true);
        ANGLETest::SetUp();
        if (!extensionEnabled("GL_KHR_robust_buffer_access_behavior"))
        {
            return false;
        }
        return true;
    }

    void initBasicProgram()
    {
        const std::string &vsCheckOutOfBounds =
            "precision mediump float;\n"
            "attribute vec4 position;\n"
            "attribute vec4 vecRandom;\n"
            "varying vec4 v_color;\n"
            "bool testFloatComponent(float component) {\n"
            "    return (component == 0.2 || component == 0.0);\n"
            "}\n"
            "bool testLastFloatComponent(float component) {\n"
            "    return testFloatComponent(component) || component == 1.0;\n"
            "}\n"
            "void main() {\n"
            "    if (testFloatComponent(vecRandom.x) &&\n"
            "        testFloatComponent(vecRandom.y) &&\n"
            "        testFloatComponent(vecRandom.z) &&\n"
            "        testLastFloatComponent(vecRandom.w)) {\n"
            "        v_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
            "    } else {\n"
            "        v_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
            "    }\n"
            "    gl_Position = position;\n"
            "}\n";

        const std::string &fragmentShaderSource =
            "precision mediump float;\n"
            "varying vec4 v_color;\n"
            "void main() {\n"
            "    gl_FragColor = v_color;\n"
            "}\n";

        mProgram = CompileProgram(vsCheckOutOfBounds, fragmentShaderSource);
        ASSERT_NE(0u, mProgram);

        mTestAttrib = glGetAttribLocation(mProgram, "vecRandom");
        ASSERT_NE(-1, mTestAttrib);

        glUseProgram(mProgram);
    }

    void runIndexOutOfRangeTests(GLenum drawType)
    {
        if (mProgram == 0)
        {
            initBasicProgram();
        }

        GLBuffer bufferIncomplete;
        glBindBuffer(GL_ARRAY_BUFFER, bufferIncomplete);
        std::array<GLfloat, 12> randomData = {
            {0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f}};
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * randomData.size(), randomData.data(),
                     drawType);

        glEnableVertexAttribArray(mTestAttrib);
        glVertexAttribPointer(mTestAttrib, 4, GL_FLOAT, GL_FALSE, 0, nullptr);

        glClearColor(0.0, 0.0, 1.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        drawIndexedQuad(mProgram, "position", 0.5f);

        int width     = getWindowWidth();
        int height    = getWindowHeight();
        GLenum result = glGetError();
        // For D3D dynamic draw, we still return invalid operation. Once we force the index buffer
        // to clamp any out of range indices instead of invalid operation, this part can be removed.
        // We can always get GL_NO_ERROR.
        if (result == GL_INVALID_OPERATION)
        {
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::blue);
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::blue);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::blue);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::blue);
        }
        else
        {
            EXPECT_GLENUM_EQ(GL_NO_ERROR, result);
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 1 / 4, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(width * 1 / 4, height * 3 / 4, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 1 / 4, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(width * 3 / 4, height * 3 / 4, GLColor::green);
        }
    }

    GLuint mProgram;
    GLint mTestAttrib;
};

// Test that static draw with out-of-bounds reads will not read outside of the data store of the
// buffer object and will not result in GL interruption or termination when
// GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithStaticDraw)
{
    ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());

    // Failing after changing the shard count of angle_end2end_tests. http://anglebug.com/2799
    ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsD3D11_FL93());

    ANGLE_SKIP_TEST_IF(!initExtension());

    runIndexOutOfRangeTests(GL_STATIC_DRAW);
}

// Test that dynamic draw with out-of-bounds reads will not read outside of the data store of the
// buffer object and will not result in GL interruption or termination when
// GL_KHR_robust_buffer_access_behavior is supported.
TEST_P(RobustBufferAccessBehaviorTest, DrawElementsIndexOutOfRangeWithDynamicDraw)
{
    ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsWindows() && IsOpenGL());
    ANGLE_SKIP_TEST_IF(!initExtension());

    runIndexOutOfRangeTests(GL_DYNAMIC_DRAW);
}

// Test that vertex buffers are rebound with the correct offsets in subsequent calls in the D3D11
// backend.  http://crbug.com/837002
TEST_P(RobustBufferAccessBehaviorTest, D3D11StateSynchronizationOrderBug)
{
    ANGLE_SKIP_TEST_IF(!initExtension());

    glDisable(GL_DEPTH_TEST);

    // 2 quads, the first one red, the second one green
    const std::array<angle::Vector4, 16> vertices{
        angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v0
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c0
        angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v1
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c1
        angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v2
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c2
        angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v3
        angle::Vector4(1.0f, 0.0f, 0.0f, 1.0f),    // c3

        angle::Vector4(-1.0f, 1.0f, 0.5f, 1.0f),   // v4
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c4
        angle::Vector4(-1.0f, -1.0f, 0.5f, 1.0f),  // v5
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c5
        angle::Vector4(1.0f, -1.0f, 0.5f, 1.0f),   // v6
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c6
        angle::Vector4(1.0f, 1.0f, 0.5f, 1.0f),    // v7
        angle::Vector4(0.0f, 1.0f, 0.0f, 1.0f),    // c7
    };

    GLBuffer vb;
    glBindBuffer(GL_ARRAY_BUFFER, vb);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);

    const std::array<GLushort, 12> indicies{
        0, 1, 2, 0, 2, 3,  // quad0
        4, 5, 6, 4, 6, 7,  // quad1
    };

    GLBuffer ib;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies.data(), GL_STATIC_DRAW);

    constexpr char kVS[] = R"(
precision highp float;
attribute vec4 a_position;
attribute vec4 a_color;

varying vec4 v_color;

void main()
{
    gl_Position = a_position;
    v_color = a_color;
})";

    constexpr char kFS[] = R"(
precision highp float;
varying vec4 v_color;

void main()
{
    gl_FragColor = v_color;
})";

    ANGLE_GL_PROGRAM(program, kVS, kFS);
    glUseProgram(program);

    GLint positionLocation = glGetAttribLocation(program, "a_position");
    glEnableVertexAttribArray(positionLocation);
    glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2, 0);

    GLint colorLocation = glGetAttribLocation(program, "a_color");
    glEnableVertexAttribArray(colorLocation);
    glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(angle::Vector4) * 2,
                          reinterpret_cast<const void *>(sizeof(angle::Vector4)));

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
                   reinterpret_cast<const void *>(sizeof(GLshort) * 6));
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
                   reinterpret_cast<const void *>(sizeof(GLshort) * 6));
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}

// Covers drawing with a very large vertex range which overflows GLsizei. http://crbug.com/842028
TEST_P(RobustBufferAccessBehaviorTest, VeryLargeVertexCountWithDynamicVertexData)
{
    ANGLE_SKIP_TEST_IF(!initExtension());
    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_element_index_uint"));

    constexpr GLsizei kIndexCount           = 32;
    std::array<GLuint, kIndexCount> indices = {{}};
    for (GLsizei index = 0; index < kIndexCount; ++index)
    {
        indices[index] = ((std::numeric_limits<GLuint>::max() - 2) / kIndexCount) * index;
    }

    GLBuffer indexBuffer;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(),
                 GL_STATIC_DRAW);

    std::array<GLfloat, 256> vertexData = {{}};

    GLBuffer vertexBuffer;
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(GLfloat), vertexData.data(),
                 GL_DYNAMIC_DRAW);

    ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
    glUseProgram(program);

    GLint attribLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
    ASSERT_NE(-1, attribLoc);

    glVertexAttribPointer(attribLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(attribLoc);
    ASSERT_GL_NO_ERROR();

    glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_INT, nullptr);

    // This may or may not generate an error, but it should not crash.
}

ANGLE_INSTANTIATE_TEST(RobustBufferAccessBehaviorTest,
                       ES2_D3D9(),
                       ES2_D3D11_FL9_3(),
                       ES2_D3D11(),
                       ES3_D3D11(),
                       ES31_D3D11(),
                       ES2_OPENGL(),
                       ES3_OPENGL(),
                       ES31_OPENGL(),
                       ES2_OPENGLES(),
                       ES3_OPENGLES(),
                       ES31_OPENGLES());

}  // namespace
<|endoftext|>


<commit_before>#include "../../local/include/gtest/gtest.h"

#include <vector>

#include "genetic.h"
#include "genetic_circuit.h"
#include "circuit.h"
#include "utility.h"

TEST(InnocuousAttempt, NumberTwo) {
  BooleanTable expected_o = {{true, true, true},
                             {true, true, false},
                             {true, false, true},
                             {true, false, false},
                             {false, true, true},
                             {false, true, false},
                             {false, false, true},
                             {false, false, false}};
  std::mt19937 rand(std::random_device{}());
  // std::minstd_rand0 rand(std::random_device{}());
  GeneticCircuit* c = new GeneticCircuit(3, 3, &rand);
  while (c->evaluateAllInputs() != expected_o) {
    delete c;
    c = new GeneticCircuit(3, 3, &rand);
  }
}
<commit_msg>add xor test<commit_after>#include "../../local/include/gtest/gtest.h"

#include <vector>

// uncomment to use threads
//#define THREADED

#ifdef THREADED

// Tron
//#define NUM_CORES 4
// Clue
#define NUM_CORES 12

#include <thread>
#include <future>

#endif  // THREADED

#include "genetic.h"
#include "genetic_circuit.h"
#include "circuit.h"
#include "utility.h"

TEST(InnocuousAttempt, XOR) {
  BooleanTable expected_o = {{false, false},
                             {false, true},
                             {false, true},
                             {true, false},
                             {false, true},
                             {true, false},
                             {true, false},
                             {true, true}};
  BooleanTable expected_o_bar = {{false, false},
                                 {true, false},
                                 {true, false},
                                 {false, true},
                                 {true, false},
                                 {false, true},
                                 {false, true},
                                 {true, true}};
  std::mt19937 rand(std::random_device{}());
  // std::minstd_rand0 rand(std::random_device{}());
  GeneticCircuit* c = new GeneticCircuit(3, 2, &rand);
  BooleanTable answer = c->evaluateAllInputs();
  int i = 0;

  // #ifdef THREADED
  // std::vector<std::future<BooleanTable>> boolean_table_futures;
  // #endif THREADED

  while ((answer != expected_o) || (answer != expected_o_bar)) {
    // #ifdef THREADED
    // for (int i = 0; i < (NUM_CORES - 1); ++i) {
    //   boolean_table_futures.push_back(
    //       std::async(std::launch::async, , points));
    // }
    // #endif THREADED

    // std::vector<Path> paths;  // vector of possible paths
    // // Calculate a path on main thread while waiting for other threads
    // // If we're doing more than 1 set, do NUM_SETS random path calculations
    // on
    // // main thread
    // for (int i = 0; i < NUM_SETS; i++) {
    //   paths.push_back(FindRandomPath(points));
    // }

    // // Get Paths from threads
    // for (auto& pathfuture : pathfutures) {
    //   paths.push_back(pathfuture.get());
    // }

    delete c;
    c = new GeneticCircuit(3, 2, &rand);
    std::cerr << "Iteration: " + std::to_string(++i) << std::endl;
    // std::cerr << *c << std::endl;
    answer = c->evaluateAllInputs();
  }
}

// TEST(InnocuousAttempt, InvertInputs) {
//   BooleanTable expected_o = {{true, true, true},
//                              {true, true, false},
//                              {true, false, true},
//                              {true, false, false},
//                              {false, true, true},
//                              {false, true, false},
//                              {false, false, true},
//                              {false, false, false}};
//   std::mt19937 rand(std::random_device{}());
//   // std::minstd_rand0 rand(std::random_device{}());
//   GeneticCircuit* c = new GeneticCircuit(3, 3, &rand);
//   while (c->evaluateAllInputs() != expected_o) {
//     delete c;
//     c = new GeneticCircuit(3, 3, &rand);
//     std::cerr << *c << std::endl;
//   }
// }
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/trace_processor/importers/common/clock_tracker.h"

#include <inttypes.h>

#include <algorithm>
#include <atomic>
#include <queue>

#include "perfetto/base/logging.h"
#include "perfetto/ext/base/hash.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"

#include "protos/perfetto/common/builtin_clock.pbzero.h"
#include "protos/perfetto/trace/clock_snapshot.pbzero.h"

namespace perfetto {
namespace trace_processor {

using Clock = protos::pbzero::ClockSnapshot::Clock;

ClockTracker::ClockTracker(TraceProcessorContext* ctx)
    : context_(ctx),
      trace_time_clock_id_(protos::pbzero::BUILTIN_CLOCK_BOOTTIME) {}

ClockTracker::~ClockTracker() = default;

void ClockTracker::AddSnapshot(const std::vector<ClockValue>& clocks) {
  const auto snapshot_id = cur_snapshot_id_++;

  // Clear the cache
  cache_.fill({});

  // Compute the fingerprint of the snapshot by hashing all clock ids. This is
  // used by the clock pathfinding logic.
  base::Hash hasher;
  for (const auto& clock : clocks)
    hasher.Update(clock.clock_id);
  const auto snapshot_hash = static_cast<SnapshotHash>(hasher.digest());

  // Add a new entry in each clock's snapshot vector.
  for (const auto& clock : clocks) {
    ClockId clock_id = clock.clock_id;
    ClockDomain& domain = clocks_[clock_id];
    if (domain.snapshots.empty()) {
      if (clock.is_incremental && !ClockIsSeqScoped(clock_id)) {
        PERFETTO_ELOG("Clock sync error: the global clock with id=%" PRIu64
                      " cannot use incremental encoding; this is only "
                      "supported for sequence-scoped clocks.",
                      clock_id);
        context_->storage->IncrementStats(stats::invalid_clock_snapshots);
        return;
      }
      domain.unit_multiplier_ns = clock.unit_multiplier_ns;
      domain.is_incremental = clock.is_incremental;
    } else if (PERFETTO_UNLIKELY(
                   domain.unit_multiplier_ns != clock.unit_multiplier_ns ||
                   domain.is_incremental != clock.is_incremental)) {
      PERFETTO_ELOG("Clock sync error: the clock domain with id=%" PRIu64
                    " (unit=%" PRIu64
                    ", incremental=%d), was previously registered with "
                    "different properties (unit=%" PRIu64 ", incremental=%d).",
                    clock_id, clock.unit_multiplier_ns, clock.is_incremental,
                    domain.unit_multiplier_ns, domain.is_incremental);
      context_->storage->IncrementStats(stats::invalid_clock_snapshots);
      return;
    }
    const int64_t timestamp_ns =
        clock.absolute_timestamp * domain.unit_multiplier_ns;
    domain.last_timestamp_ns = timestamp_ns;

    ClockSnapshots& vect = domain.snapshots[snapshot_hash];
    if (!vect.snapshot_ids.empty() &&
        PERFETTO_UNLIKELY(vect.snapshot_ids.back() == snapshot_id)) {
      PERFETTO_ELOG("Clock sync error: duplicate clock domain with id=%" PRIu64
                    " at snapshot %" PRIu32 ".",
                    clock_id, snapshot_id);
      context_->storage->IncrementStats(stats::invalid_clock_snapshots);
      return;
    }

    // Clock ids in the range [64, 128) are sequence-scoped and must be
    // translated to global ids via SeqScopedClockIdToGlobal() before calling
    // this function.
    PERFETTO_DCHECK(!IsReservedSeqScopedClockId(clock_id));

    // Snapshot IDs must be always monotonic.
    PERFETTO_DCHECK(vect.snapshot_ids.empty() ||
                    vect.snapshot_ids.back() < snapshot_id);

    if (!vect.timestamps_ns.empty() &&
        timestamp_ns < vect.timestamps_ns.back()) {
      // Clock is not monotonic.

      if (clock_id == trace_time_clock_id_) {
        // The trace clock cannot be non-monotonic.
        PERFETTO_ELOG("Clock sync error: the trace clock (id=%" PRIu64
                      ") is not monotonic at snapshot %" PRIu32 ". %" PRId64
                      " not >= %" PRId64 ".",
                      clock_id, snapshot_id, timestamp_ns,
                      vect.timestamps_ns.back());
        context_->storage->IncrementStats(stats::invalid_clock_snapshots);
        return;
      }

      PERFETTO_DLOG("Detected non-monotonic clock with ID %" PRIu64, clock_id);

      // For the other clocks the best thing we can do is mark it as
      // non-monotonic and refuse to use it as a source clock in the resolution
      // graph. We can still use it as a target clock, but not viceversa.
      // The concrete example is the CLOCK_REALTIME going 1h backwards during
      // daylight saving. We can still answer the question "what was the
      // REALTIME timestamp when BOOTTIME was X?" but we can't answer the
      // opposite question because there can be two valid BOOTTIME(s) for the
      // same REALTIME instant because of the 1:many relationship.
      non_monotonic_clocks_.insert(clock_id);

      // Erase all edges from the graph that start from this clock (but keep the
      // ones that end on this clock).
      auto begin = graph_.lower_bound(ClockGraphEdge{clock_id, 0, 0});
      auto end = graph_.lower_bound(ClockGraphEdge{clock_id + 1, 0, 0});
      graph_.erase(begin, end);
    }
    vect.snapshot_ids.emplace_back(snapshot_id);
    vect.timestamps_ns.emplace_back(timestamp_ns);
  }

  // Create graph edges for all the possible tuples of clocks in this snapshot.
  // If the snapshot contains clock a, b, c, d create edges [ab, ac, ad, bc, bd,
  // cd] and the symmetrical ones [ba, ca, da, bc, db, dc].
  // This is to store the information: Clock A is syncable to Clock B via the
  // snapshots of type (hash).
  // Clocks that were previously marked as non-monotonic won't be added as
  // valid sources.
  for (auto it1 = clocks.begin(); it1 != clocks.end(); ++it1) {
    auto it2 = it1;
    ++it2;
    for (; it2 != clocks.end(); ++it2) {
      if (!non_monotonic_clocks_.count(it1->clock_id))
        graph_.emplace(it1->clock_id, it2->clock_id, snapshot_hash);

      if (!non_monotonic_clocks_.count(it2->clock_id))
        graph_.emplace(it2->clock_id, it1->clock_id, snapshot_hash);
    }
  }
}

// Finds the shortest clock resolution path in the graph that allows to
// translate a timestamp from |src| to |target| clocks.
// The return value looks like the following: "If you want to convert a
// timestamp from clock C1 to C2 you need to first convert C1 -> C3 using the
// snapshot hash A, then convert C3 -> C2 via snapshot hash B".
ClockTracker::ClockPath ClockTracker::FindPath(ClockId src, ClockId target) {
  // This is a classic breadth-first search. Each node in the queue holds also
  // the full path to reach that node.
  // We assume the graph is acyclic, if it isn't the ClockPath::kMaxLen will
  // stop the search anyways.
  PERFETTO_CHECK(src != target);
  std::queue<ClockPath> queue;
  queue.emplace(src);

  while (!queue.empty()) {
    ClockPath cur_path = queue.front();
    queue.pop();

    const ClockId cur_clock_id = cur_path.last;
    if (cur_clock_id == target)
      return cur_path;

    if (cur_path.len >= ClockPath::kMaxLen)
      continue;

    // Expore all the adjacent clocks.
    // The lower_bound() below returns an iterator to the first edge that starts
    // on |cur_clock_id|. The edges are sorted by (src, target, hash).
    for (auto it = std::lower_bound(graph_.begin(), graph_.end(),
                                    ClockGraphEdge(cur_clock_id, 0, 0));
         it != graph_.end() && std::get<0>(*it) == cur_clock_id; ++it) {
      ClockId next_clock_id = std::get<1>(*it);
      SnapshotHash hash = std::get<2>(*it);
      queue.push(ClockPath(cur_path, next_clock_id, hash));
    }
  }
  return ClockPath();  // invalid path.
}

base::Optional<int64_t> ClockTracker::ConvertSlowpath(ClockId src_clock_id,
                                                      int64_t src_timestamp,
                                                      ClockId target_clock_id) {
  PERFETTO_DCHECK(!IsReservedSeqScopedClockId(src_clock_id));
  PERFETTO_DCHECK(!IsReservedSeqScopedClockId(target_clock_id));

  context_->storage->IncrementStats(stats::clock_sync_cache_miss);

  ClockPath path = FindPath(src_clock_id, target_clock_id);
  if (!path.valid()) {
    // Too many logs maybe emitted when path is invalid.
    static std::atomic<uint32_t> dlog_count = 0;
    if (dlog_count++ < 10) {
      PERFETTO_DLOG("No path from clock %" PRIu64 " to %" PRIu64
                    " at timestamp %" PRId64,
                    src_clock_id, target_clock_id, src_timestamp);
    }
    context_->storage->IncrementStats(stats::clock_sync_failure);
    return base::nullopt;
  }

  // We can cache only single-path resolutions between two clocks.
  // Caching multi-path resolutions is harder because the (src,target) tuple
  // is not enough as a cache key: at any step the |ns| value can yield to a
  // different choice of the next snapshot. Multi-path resolutions don't seem
  // too frequent these days, so we focus only on caching the more frequent
  // one-step resolutions (typically from any clock to the trace clock).
  const bool cacheable = path.len == 1;
  CachedClockPath cache_entry{};

  // Iterate trough the path found and translate timestamps onto the new clock
  // domain on each step, until the target domain is reached.
  ClockDomain* src_domain = GetClock(src_clock_id);
  int64_t ns = src_domain->ToNs(src_timestamp);
  for (uint32_t i = 0; i < path.len; ++i) {
    const ClockGraphEdge edge = path.at(i);
    ClockDomain* cur_clock = GetClock(std::get<0>(edge));
    ClockDomain* next_clock = GetClock(std::get<1>(edge));
    const SnapshotHash hash = std::get<2>(edge);

    // Find the closest timestamp within the snapshots of the source clock.
    const ClockSnapshots& cur_snap = cur_clock->GetSnapshot(hash);
    const auto& ts_vec = cur_snap.timestamps_ns;
    auto it = std::upper_bound(ts_vec.begin(), ts_vec.end(), ns);
    if (it != ts_vec.begin())
      --it;

    // Now lookup the snapshot id that matches the closest timestamp.
    size_t index = static_cast<size_t>(std::distance(ts_vec.begin(), it));
    PERFETTO_DCHECK(index < ts_vec.size());
    PERFETTO_DCHECK(cur_snap.snapshot_ids.size() == ts_vec.size());
    uint32_t snapshot_id = cur_snap.snapshot_ids[index];

    // And use that to retrieve the corresponding time in the next clock domain.
    // The snapshot id must exist in the target clock domain. If it doesn't
    // either the hash logic or the pathfinding logic are bugged.
    // This can also happen if the checks in AddSnapshot fail and we skip part
    // of the snapshot.
    const ClockSnapshots& next_snap = next_clock->GetSnapshot(hash);

    // Using std::lower_bound because snapshot_ids is sorted, so we can do
    // a binary search. std::find would do a linear scan.
    auto next_it = std::lower_bound(next_snap.snapshot_ids.begin(),
                                    next_snap.snapshot_ids.end(), snapshot_id);
    if (next_it == next_snap.snapshot_ids.end() || *next_it != snapshot_id) {
      PERFETTO_DFATAL("Snapshot does not exist in clock domain.");
      continue;
    }
    size_t next_index = static_cast<size_t>(
        std::distance(next_snap.snapshot_ids.begin(), next_it));
    PERFETTO_DCHECK(next_index < next_snap.snapshot_ids.size());
    int64_t next_timestamp_ns = next_snap.timestamps_ns[next_index];

    // The translated timestamp is the relative delta of the source timestamp
    // from the closest snapshot found (ns - *it), plus the timestamp in
    // the new clock domain for the same snapshot id.
    const int64_t adj = next_timestamp_ns - *it;
    ns += adj;

    // On the first iteration, keep track of the bounds for the cache entry.
    // This will allow future Convert() calls to skip the pathfinder logic
    // as long as the query stays within the bound.
    if (cacheable) {
      PERFETTO_DCHECK(i == 0);
      const int64_t kInt64Min = std::numeric_limits<int64_t>::min();
      const int64_t kInt64Max = std::numeric_limits<int64_t>::max();
      cache_entry.min_ts_ns = it == ts_vec.begin() ? kInt64Min : *it;
      auto ubound = it + 1;
      cache_entry.max_ts_ns = ubound == ts_vec.end() ? kInt64Max : *ubound;
      cache_entry.translation_ns = adj;
    }

    // The last clock in the path must be the target clock.
    PERFETTO_DCHECK(i < path.len - 1 || std::get<1>(edge) == target_clock_id);
  }

  if (cacheable) {
    cache_entry.src = src_clock_id;
    cache_entry.src_domain = src_domain;
    cache_entry.target = target_clock_id;
    cache_[rnd_() % cache_.size()] = cache_entry;
  }

  return ns;
}

}  // namespace trace_processor
}  // namespace perfetto
<commit_msg>Fix clock_tracker.cc am: 5dc147f282 am: f2faa3dd19 am: caf328c24a am: 55af9a1d07<commit_after>/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/trace_processor/importers/common/clock_tracker.h"

#include <inttypes.h>

#include <algorithm>
#include <atomic>
#include <queue>

#include "perfetto/base/logging.h"
#include "perfetto/ext/base/hash.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"

#include "protos/perfetto/common/builtin_clock.pbzero.h"
#include "protos/perfetto/trace/clock_snapshot.pbzero.h"

namespace perfetto {
namespace trace_processor {

using Clock = protos::pbzero::ClockSnapshot::Clock;

ClockTracker::ClockTracker(TraceProcessorContext* ctx)
    : context_(ctx),
      trace_time_clock_id_(protos::pbzero::BUILTIN_CLOCK_BOOTTIME) {}

ClockTracker::~ClockTracker() = default;

void ClockTracker::AddSnapshot(const std::vector<ClockValue>& clocks) {
  const auto snapshot_id = cur_snapshot_id_++;

  // Clear the cache
  cache_.fill({});

  // Compute the fingerprint of the snapshot by hashing all clock ids. This is
  // used by the clock pathfinding logic.
  base::Hash hasher;
  for (const auto& clock : clocks)
    hasher.Update(clock.clock_id);
  const auto snapshot_hash = static_cast<SnapshotHash>(hasher.digest());

  // Add a new entry in each clock's snapshot vector.
  for (const auto& clock : clocks) {
    ClockId clock_id = clock.clock_id;
    ClockDomain& domain = clocks_[clock_id];
    if (domain.snapshots.empty()) {
      if (clock.is_incremental && !ClockIsSeqScoped(clock_id)) {
        PERFETTO_ELOG("Clock sync error: the global clock with id=%" PRIu64
                      " cannot use incremental encoding; this is only "
                      "supported for sequence-scoped clocks.",
                      clock_id);
        context_->storage->IncrementStats(stats::invalid_clock_snapshots);
        return;
      }
      domain.unit_multiplier_ns = clock.unit_multiplier_ns;
      domain.is_incremental = clock.is_incremental;
    } else if (PERFETTO_UNLIKELY(
                   domain.unit_multiplier_ns != clock.unit_multiplier_ns ||
                   domain.is_incremental != clock.is_incremental)) {
      PERFETTO_ELOG("Clock sync error: the clock domain with id=%" PRIu64
                    " (unit=%" PRIu64
                    ", incremental=%d), was previously registered with "
                    "different properties (unit=%" PRIu64 ", incremental=%d).",
                    clock_id, clock.unit_multiplier_ns, clock.is_incremental,
                    domain.unit_multiplier_ns, domain.is_incremental);
      context_->storage->IncrementStats(stats::invalid_clock_snapshots);
      return;
    }
    const int64_t timestamp_ns =
        clock.absolute_timestamp * domain.unit_multiplier_ns;
    domain.last_timestamp_ns = timestamp_ns;

    ClockSnapshots& vect = domain.snapshots[snapshot_hash];
    if (!vect.snapshot_ids.empty() &&
        PERFETTO_UNLIKELY(vect.snapshot_ids.back() == snapshot_id)) {
      PERFETTO_ELOG("Clock sync error: duplicate clock domain with id=%" PRIu64
                    " at snapshot %" PRIu32 ".",
                    clock_id, snapshot_id);
      context_->storage->IncrementStats(stats::invalid_clock_snapshots);
      return;
    }

    // Clock ids in the range [64, 128) are sequence-scoped and must be
    // translated to global ids via SeqScopedClockIdToGlobal() before calling
    // this function.
    PERFETTO_DCHECK(!IsReservedSeqScopedClockId(clock_id));

    // Snapshot IDs must be always monotonic.
    PERFETTO_DCHECK(vect.snapshot_ids.empty() ||
                    vect.snapshot_ids.back() < snapshot_id);

    if (!vect.timestamps_ns.empty() &&
        timestamp_ns < vect.timestamps_ns.back()) {
      // Clock is not monotonic.

      if (clock_id == trace_time_clock_id_) {
        // The trace clock cannot be non-monotonic.
        PERFETTO_ELOG("Clock sync error: the trace clock (id=%" PRIu64
                      ") is not monotonic at snapshot %" PRIu32 ". %" PRId64
                      " not >= %" PRId64 ".",
                      clock_id, snapshot_id, timestamp_ns,
                      vect.timestamps_ns.back());
        context_->storage->IncrementStats(stats::invalid_clock_snapshots);
        return;
      }

      PERFETTO_DLOG("Detected non-monotonic clock with ID %" PRIu64, clock_id);

      // For the other clocks the best thing we can do is mark it as
      // non-monotonic and refuse to use it as a source clock in the resolution
      // graph. We can still use it as a target clock, but not viceversa.
      // The concrete example is the CLOCK_REALTIME going 1h backwards during
      // daylight saving. We can still answer the question "what was the
      // REALTIME timestamp when BOOTTIME was X?" but we can't answer the
      // opposite question because there can be two valid BOOTTIME(s) for the
      // same REALTIME instant because of the 1:many relationship.
      non_monotonic_clocks_.insert(clock_id);

      // Erase all edges from the graph that start from this clock (but keep the
      // ones that end on this clock).
      auto begin = graph_.lower_bound(ClockGraphEdge{clock_id, 0, 0});
      auto end = graph_.lower_bound(ClockGraphEdge{clock_id + 1, 0, 0});
      graph_.erase(begin, end);
    }
    vect.snapshot_ids.emplace_back(snapshot_id);
    vect.timestamps_ns.emplace_back(timestamp_ns);
  }

  // Create graph edges for all the possible tuples of clocks in this snapshot.
  // If the snapshot contains clock a, b, c, d create edges [ab, ac, ad, bc, bd,
  // cd] and the symmetrical ones [ba, ca, da, bc, db, dc].
  // This is to store the information: Clock A is syncable to Clock B via the
  // snapshots of type (hash).
  // Clocks that were previously marked as non-monotonic won't be added as
  // valid sources.
  for (auto it1 = clocks.begin(); it1 != clocks.end(); ++it1) {
    auto it2 = it1;
    ++it2;
    for (; it2 != clocks.end(); ++it2) {
      if (!non_monotonic_clocks_.count(it1->clock_id))
        graph_.emplace(it1->clock_id, it2->clock_id, snapshot_hash);

      if (!non_monotonic_clocks_.count(it2->clock_id))
        graph_.emplace(it2->clock_id, it1->clock_id, snapshot_hash);
    }
  }
}

// Finds the shortest clock resolution path in the graph that allows to
// translate a timestamp from |src| to |target| clocks.
// The return value looks like the following: "If you want to convert a
// timestamp from clock C1 to C2 you need to first convert C1 -> C3 using the
// snapshot hash A, then convert C3 -> C2 via snapshot hash B".
ClockTracker::ClockPath ClockTracker::FindPath(ClockId src, ClockId target) {
  // This is a classic breadth-first search. Each node in the queue holds also
  // the full path to reach that node.
  // We assume the graph is acyclic, if it isn't the ClockPath::kMaxLen will
  // stop the search anyways.
  PERFETTO_CHECK(src != target);
  std::queue<ClockPath> queue;
  queue.emplace(src);

  while (!queue.empty()) {
    ClockPath cur_path = queue.front();
    queue.pop();

    const ClockId cur_clock_id = cur_path.last;
    if (cur_clock_id == target)
      return cur_path;

    if (cur_path.len >= ClockPath::kMaxLen)
      continue;

    // Expore all the adjacent clocks.
    // The lower_bound() below returns an iterator to the first edge that starts
    // on |cur_clock_id|. The edges are sorted by (src, target, hash).
    for (auto it = std::lower_bound(graph_.begin(), graph_.end(),
                                    ClockGraphEdge(cur_clock_id, 0, 0));
         it != graph_.end() && std::get<0>(*it) == cur_clock_id; ++it) {
      ClockId next_clock_id = std::get<1>(*it);
      SnapshotHash hash = std::get<2>(*it);
      queue.push(ClockPath(cur_path, next_clock_id, hash));
    }
  }
  return ClockPath();  // invalid path.
}

base::Optional<int64_t> ClockTracker::ConvertSlowpath(ClockId src_clock_id,
                                                      int64_t src_timestamp,
                                                      ClockId target_clock_id) {
  PERFETTO_DCHECK(!IsReservedSeqScopedClockId(src_clock_id));
  PERFETTO_DCHECK(!IsReservedSeqScopedClockId(target_clock_id));

  context_->storage->IncrementStats(stats::clock_sync_cache_miss);

  ClockPath path = FindPath(src_clock_id, target_clock_id);
  if (!path.valid()) {
    // Too many logs maybe emitted when path is invalid.
    static std::atomic<uint32_t> dlog_count(0);
    if (dlog_count++ < 10) {
      PERFETTO_DLOG("No path from clock %" PRIu64 " to %" PRIu64
                    " at timestamp %" PRId64,
                    src_clock_id, target_clock_id, src_timestamp);
    }
    context_->storage->IncrementStats(stats::clock_sync_failure);
    return base::nullopt;
  }

  // We can cache only single-path resolutions between two clocks.
  // Caching multi-path resolutions is harder because the (src,target) tuple
  // is not enough as a cache key: at any step the |ns| value can yield to a
  // different choice of the next snapshot. Multi-path resolutions don't seem
  // too frequent these days, so we focus only on caching the more frequent
  // one-step resolutions (typically from any clock to the trace clock).
  const bool cacheable = path.len == 1;
  CachedClockPath cache_entry{};

  // Iterate trough the path found and translate timestamps onto the new clock
  // domain on each step, until the target domain is reached.
  ClockDomain* src_domain = GetClock(src_clock_id);
  int64_t ns = src_domain->ToNs(src_timestamp);
  for (uint32_t i = 0; i < path.len; ++i) {
    const ClockGraphEdge edge = path.at(i);
    ClockDomain* cur_clock = GetClock(std::get<0>(edge));
    ClockDomain* next_clock = GetClock(std::get<1>(edge));
    const SnapshotHash hash = std::get<2>(edge);

    // Find the closest timestamp within the snapshots of the source clock.
    const ClockSnapshots& cur_snap = cur_clock->GetSnapshot(hash);
    const auto& ts_vec = cur_snap.timestamps_ns;
    auto it = std::upper_bound(ts_vec.begin(), ts_vec.end(), ns);
    if (it != ts_vec.begin())
      --it;

    // Now lookup the snapshot id that matches the closest timestamp.
    size_t index = static_cast<size_t>(std::distance(ts_vec.begin(), it));
    PERFETTO_DCHECK(index < ts_vec.size());
    PERFETTO_DCHECK(cur_snap.snapshot_ids.size() == ts_vec.size());
    uint32_t snapshot_id = cur_snap.snapshot_ids[index];

    // And use that to retrieve the corresponding time in the next clock domain.
    // The snapshot id must exist in the target clock domain. If it doesn't
    // either the hash logic or the pathfinding logic are bugged.
    // This can also happen if the checks in AddSnapshot fail and we skip part
    // of the snapshot.
    const ClockSnapshots& next_snap = next_clock->GetSnapshot(hash);

    // Using std::lower_bound because snapshot_ids is sorted, so we can do
    // a binary search. std::find would do a linear scan.
    auto next_it = std::lower_bound(next_snap.snapshot_ids.begin(),
                                    next_snap.snapshot_ids.end(), snapshot_id);
    if (next_it == next_snap.snapshot_ids.end() || *next_it != snapshot_id) {
      PERFETTO_DFATAL("Snapshot does not exist in clock domain.");
      continue;
    }
    size_t next_index = static_cast<size_t>(
        std::distance(next_snap.snapshot_ids.begin(), next_it));
    PERFETTO_DCHECK(next_index < next_snap.snapshot_ids.size());
    int64_t next_timestamp_ns = next_snap.timestamps_ns[next_index];

    // The translated timestamp is the relative delta of the source timestamp
    // from the closest snapshot found (ns - *it), plus the timestamp in
    // the new clock domain for the same snapshot id.
    const int64_t adj = next_timestamp_ns - *it;
    ns += adj;

    // On the first iteration, keep track of the bounds for the cache entry.
    // This will allow future Convert() calls to skip the pathfinder logic
    // as long as the query stays within the bound.
    if (cacheable) {
      PERFETTO_DCHECK(i == 0);
      const int64_t kInt64Min = std::numeric_limits<int64_t>::min();
      const int64_t kInt64Max = std::numeric_limits<int64_t>::max();
      cache_entry.min_ts_ns = it == ts_vec.begin() ? kInt64Min : *it;
      auto ubound = it + 1;
      cache_entry.max_ts_ns = ubound == ts_vec.end() ? kInt64Max : *ubound;
      cache_entry.translation_ns = adj;
    }

    // The last clock in the path must be the target clock.
    PERFETTO_DCHECK(i < path.len - 1 || std::get<1>(edge) == target_clock_id);
  }

  if (cacheable) {
    cache_entry.src = src_clock_id;
    cache_entry.src_domain = src_domain;
    cache_entry.target = target_clock_id;
    cache_[rnd_() % cache_.size()] = cache_entry;
  }

  return ns;
}

}  // namespace trace_processor
}  // namespace perfetto
<|endoftext|>